Source: js/bus.js

/** @namespace Bus
    @desc      Centralized inter-starlet messaging.
               Main methods are ask/tell, others are curried aliases for specific actions.
*/
var Bus = typeof Bus !== 'undefined' ? Bus :
{
    /* properties */
    
    /** @member {Boolean} Bus.registered
        @readonly
        @desc   Whether Starlet has registered itself with Connection.
    */
    'registered': false,
    
    'requests'  : {},
    
    /* methods */
    
    // i/o
    
    /** @method   Bus.ask
        @readonly
        @desc     Request data with criteria, or send a data-related instruction expecting a result. Data on demand
                  (action), future updates (subscribe), or both can be acquired via this single method.
        
        @arg      {String}   packet           - Name of {@link OrionObjects.DataPacket} being requested.
        @arg      {Object}   meta             - Criteria and information about the request. Besides the properties
                                                defined below, each packet type expects varying additional criteria.
        @arg      {String}   [meta.action]    - Action to be performed immediately (e.g. 'list', 'get', etc.).
        @arg      {Boolean}  [meta.subscribe] - Whether to receive future updates.
        @arg      {Function} [meta.callback]  - Function to respond to incoming data. May also be associated via the
                                                promise's then method, though it will only resolve once. Remember to
                                                specify the context of the callback or include a closure so it can
                                                access its controller.
        @returns  {Object}   promise
        
        @see      {@link Bus.tell}
        @see      {@link Widget.ControllerClass#ask}
        @see      {@link Widget.ControllerClass#tell}
        @see      {@link http://api.jquery.com/category/deferred-object/}
        @example  Bus.ask('users',
{
    'action'   : 'list',
    'subscribe': true,
    'callback' : controller.onUserData.bind(controller)
});
        @example  Bus.ask('users', {'action': 'list'}).then(controller.onUserData.bind(controller));
    */
    'ask': function (packet, meta)
    {
        packet === 'logs' && meta.subscribe === true && (log.verbosity = {'min': L_OFF, 'max': L_OFF});
        
        log(L_DEBUG, 'Bus.ask', arguments);
        
        // init
        
        meta         = meta         || {};
        meta.starlet = meta.starlet || Starlet.name;
        meta.packet  = meta.packet  || packet;
        meta.ask     = new Date().getTime();
        meta.tell    = false;
        
        var persist  = ~$.inArray(meta.type, ['update', 'both']) || meta.update || meta.subscribe,
            deferred = $.Deferred();
        
        // prepare callback
        
        if (typeof meta.callback === 'function')
        {
			meta.callback  = linkCallback(meta.callback, window, persist, deferred);
            meta.namespace = 'window';
        }
        else
        {
			meta.callback  = meta.callback  || linkCallback(false, window, persist, deferred);
            meta.namespace = meta.namespace || 'window';
        }
        
        // send
        
        OrionCommunication.messageConnection('Distributor', 'onRequest', [meta]);
        
        //Bus.requests[new Date().getTime()] = meta;
        
        return deferred.promise();
    },
    
    /** @method   Bus.tell
        @readonly
        @desc     Send a data-related instruction, expecting no result.
        
        @arg      {String}   packet           - Name of {@link OrionObjects.DataPacket} being requested.
        @arg      {Object}   meta             - Criteria and information about the request. Besides action,
                                                each packet type expects varying additional criteria.
        @arg      {String}   [meta.action]    - Action to be performed (e.g. 'list', 'get', etc.).
        @returns  {Boolean} true
        
        @see      {@link Bus.ask}
        @see      {@link Widget.ControllerClass#ask}
        @see      {@link Widget.ControllerClass#tell}
        @example  Bus.tell('usergroups',
{
    'action': 'create',
    'name'  : 'Foo',
    'users' : [1, 2, 3]
});
    */
    'tell': function (packet, meta)
    {
        log(L_DEBUG, 'Bus.tell', arguments);
        
        // init
        
        meta         = meta         || {};
        meta.starlet = meta.starlet || Starlet.name;
        meta.packet  = meta.packet  || packet;
        meta.ask     = false;
        meta.tell    = new Date().getTime();
        
        // send
        
        OrionCommunication.messageConnection('Distributor', 'onRequest', [meta]);
        
        //Bus.requests[new Date().getTime()] = meta;
        
        return true;
    },
    
    // in development
    'askLocal': function (packet, callback, data)
    {
        log(L_DEBUG, 'Bus.askLocal', arguments);
        
        var deferred  = $.Deferred(),
            instances = Widget.instances();
        
        $.each(instances, function ()
        {
            this.$element.triggerHandler(packet, [data, callback, deferred]);
        });
        
        return deferred.promise();
    },
    
    // in development
    'tellLocal': function (packet, data)
    {
        log(L_DEBUG, 'Bus.tellLocal', arguments);
        
        var instances = Widget.instances();
        
        $.each(instances, function ()
        {
            this.$element.triggerHandler(packet, [data]);
        });
    },
    
    // curried aliases
    
    /** @method   Bus.console
        @readonly
        @desc     Open developer console for a Starlet.
        
        @arg      {String} [starlet=Starlet.name - Framework name of Starlet to open developer console for.
        @returns  {Boolean} true
    */
    'console': function (starlet)
    {
        starlet = starlet || Starlet.name;
        
        this.tell('starlets', {'action': 'inspect', 'name': starlet});
    },
    
    /** @method   Bus.register
        @readonly
        @desc     Registers Starlet with Connection.
        
        @returns  {Boolean} true
    */
    'register': function () { if (!Bus.registered)
    {
        log(L_DEBUG, 'Bus.register', arguments);
        
        Bus.registered = true;
        
        return this.tell('starlets',
        {
            'action'     : 'register',
            'identifier' : Starlet.identifier,
            'title'      : Starlet.title,
            'url'        : window.location.href,
            'tearoff'    : Starlet.states.tearoff,
            'minitearoff': Starlet.states.minitearoff,
            'overlay'    : Starlet.states.overlay
        });
    }},
    
    /** @method   Bus.unregister
        @readonly
        @desc     Unregisters Starlet with Connection.
        
        @returns  {Boolean} true
    */
    'unregister': function (name) { if (Bus.registered)
    {
        log(L_DEBUG, 'Bus.unregister', arguments);
        
        Bus.registered = false;
        
        return this.tell('starlets', {'action': 'unregister'});
    }},
    
    /** @method   Bus.reload
        @readonly
        @desc     Reloads Starlet's WebView.
        
        @arg      {Boolean} [nocache=true] - Whether Starlet's WebView should be reloaded from server.
        @returns  {Boolean} true
    */
    'reload': function (nocache)
    {
        log(L_DEBUG, 'Bus.reload', arguments);
        
        nocache = typeof nocache === 'undefined' ? true : !!nocache;
        
        return this.tell('starlets', {'action': 'reload', 'nocache': nocache});
    },
    
    // in development
    'options': function (key, a, b)
    {
        log(L_DEBUG, 'Bus.options', arguments);
        
        var caller   = arguments.callee.caller,
            widgetId = caller.controller ? caller.controller.uniqueId : 0;
        
        switch (arguments.length)
        {
            case 1: return       this.ask ('options', {'key': key, 'type': 'init'});                           // (key)                  get only via promise
            case 2: return typeof a == 'function'
                               ? this.ask ('options', {'key': key, 'type': 'update',           'callback': a}) // (key, callback)        get only via promise/callback
                               : this.tell('options', {'key': key,                 'value': a})                // (key, value)           set only
            case 3: return       this.ask ('options', {'key': key, 'type': 'both', 'value': a, 'callback': b}) // (key, value, callback) set/get via promise/callback
                
            default: log(L_ERROR, 'Bus.options', 'invalid arguments'); { return false; }
        }
    },
    
    // in development
    'subscribe': function (type, packet, callback, args)
    {
        log(L_DEBUG, 'Bus.subscribe', arguments);
        
        var newargs =
        {
            'type'     : type,
            'packet'   : packet,
            'callback' : callback
        };
        
        $.extend(newargs, args);
        
        return type == 'init' || type == 'both'
            ? this.ask ('subscribe_packet', newargs)
            : this.tell('subscribe_packet', newargs);
    },
    
    // in development
    'subscribeFilter': function (packet, callback, args)
    {
        log(L_DEBUG, 'Bus.subscribe', arguments);
        
        var newargs =
        {
            'packet'   : packet,
            'callback' : callback
        };
        
        $.extend(newargs, args);
        
        return this.ask('subscribe_filter_packet', newargs);
    },
	
    /** @method   Bus.nav
        @readonly
        @desc     Navigate framework to a specific Starlet.
        
        @arg      {String} starlet - Framework name of Starlet to navigate to.
    */
	'nav': function (starlet)
	{
		log(L_DEBUG, 'Bus.nav', arguments);
		
		OrionCommunication.messageConnection('Distributor', 'onNavigate', [starlet]);
	},
	
    /** @method   Bus.relay
        @readonly
        @desc     Sends a command to another Starlet, which must have the corresponding case defined.
        
        @arg      {String} starlet - Framework name of Starlet to send command.
        @arg      {String} command - Name of command being sent.
        @arg      {Object} options - Index of additional values to send.
    */
	'relay': function (starlet, command, options)
	{
		log(L_DEBUG, 'Bus.relay', arguments);
		
		OrionCommunication.messageConnection('Distributor', 'onRelay', [starlet, command, options]);
	},
	
    /** @method   Bus.navRelay
        @readonly
        @desc     Navigates to another Starlet while sending it a command.
                  Target Starlet must have the corresponding case defined.
        
        @arg      {String} starlet - Framework name of Starlet to navigate to and send command.
        @arg      {String} command - Name of command being sent.
        @arg      {Object} options - Index of additional values to send.
    */
	'navRelay': function (starlet, command, options)
	{
		log(L_DEBUG, 'Bus.navRelay', arguments);
		
		Bus.nav(starlet);
		Bus.relay(starlet, command, options);
	},
	
	/** @method   Bus.crypto
        @readonly
        @desc     Encrypts or decrypts data passed into it.
        
        @arg      {Boolean} encrypt - true - encrypt; false - decrypt
        @arg      {String} data - if encrypting, the string to encrypt; if decrypting, the base64 string to decrypt
        @arg      {String} salt - encryption salt
    */
	'crypto': function (encrypt, data, salt)
	{
		OrionCommunication.messageConnection('Distributor', 'onCrypto',
		{
			starlet: Starlet.name,
			encrypt: encrypt,
			data: data,
			salt: salt,
			callback: linkCallback(meta.callback, window, false, deferred)
		});
	}
};