      1 /**
      2  * @ignore
      3  */
      4 var Ozone = Ozone ? Ozone : {};
      5
      6 /**
      7  * @ignore
      8  * @namespace
      9  */
     10 Ozone.eventing = Ozone.eventing ? Ozone.eventing : {};
     11
     12 //-----------------------------------------------------------------
     13 //Ozone Eventing Widget Object
     14 //-----------------------------------------------------------------
     15 /**
     16  * @deprecated Since OWF 3.7.0  You should use <a href="#.getInstance">Ozone.eventing.Widget.getInstance</a>
     17  * @constructor
     18  * @param {String} widgetRelay The URL for the widget relay file. The relay file must be specified with full location details, but without a fully
     19  * qualified path. In the case where the relay is residing @ http://server/path/relay.html, the path used must be from the context root of the local
     20  * widget. In this case, it would be /path/relay.html.  Do not include the protocol.
     21  * @param {Function} afterInit - callback to be executed after the widget is finished initializing.
     22  * @description The Ozone.eventing.Widget object manages the eventing for an individual widget (Deprecated).  This constructor is deprecated.
     23  *  You should use <a href="#.getInstance">Ozone.eventing.Widget.getInstance</a>
     24  * @example
     25  * this.widgetEventingController = new Ozone.eventing.Widget(
     26  * 'owf-sample-html/js/eventing/rpc_relay.uncompressed.html', function() {
     27  *
     28  *  //put code here to execute after widget init - perhaps immediately publish to a channel
     29  *
     30  * });
     31  * @throws {Error} throws an error with a message if widget initialization fails
     32  */
     33 Ozone.eventing.Widget = function(widgetRelay, afterInit) {
     34   if (Ozone.eventing.Widget.instance == null) {
     35     Ozone.eventing.Widget.instance = this;
     36     this.isAfterInit = false;
     37     //connect passed in function to the internal callback function
     38     if (afterInit != null) {
     39       owfdojo.connect(
     40               this,'afterInitCallBack',
     41               this,afterInit);
     42     }
     43     this.setWidgetRelay(widgetRelay);
     44     try {
     45         this.widgetInit();
     46     } catch (error) {
     47         throw 'Widget relay init failed. Relaying will not work. Inner Exception: ' + error.name + ": " + error.message;
     48     }
     49   }
     50   else {
     51     if (afterInit != null) {
     52       if (this.isAfterInit === false) {
     53         //connect passed in function to the internal callback function
     54         owfdojo.connect(
     55                 this,'afterInitCallBack',
     56                 this,afterInit);
     57     }
     58       else {
     59         //already initialized just execute the supplied callback
     60         setTimeout(afterInit,50);
     61   }
     62     }
     63   }
     64   return Ozone.eventing.Widget.instance;
     65 };
     66
     67 /**
     68  * @description The location of the widget relay file.  The relay file should be defined
     69  *   globally for the entire widget by setting Ozone.eventing.Widget.widgetRelayURL to the relay file url, immediately after
     70  *   including the widget bundle javascript.  If the relay is not defined at all it is assumed to be at
     71  *   /[context]/js/eventing/rpc_relay.uncompressed.html. The relay file must be specified with full location details, but without a fully
     72  *   qualified path. In the case where the relay is residing @ http://server/path/relay.html, the path used must be from the context root of the local
     73  *   widget. In this case, it would be /path/relay.html.  Do not include the protocol.
     74  * @since OWF 3.7.0
     75  * @example
     76  * <script type="text/javascript" src="../../js-min/owf-widget-min.js"></script>
     77  * <script>
     78  *       //The location is assumed to be at /[context]/js/eventing/rpc_relay.uncompressed.html if it is not
     79  *       //set the path correctly below
     80  *       Ozone.eventing.Widget.widgetRelayURL = '/owf/js/eventing/rpc_relay.uncompressed.html';
     81  *       //...
     82  * </script>
     83  *
     84  */
     85 Ozone.eventing.Widget.widgetRelayURL = Ozone.eventing.Widget.widgetRelayURL ? Ozone.eventing.Widget.widgetRelayURL : null;
     86
     87 Ozone.eventing.Widget.prototype = {
     88
     89     version: Ozone.version.owfversion + Ozone.version.eventing,
     90
     91     /**
     92      * @ignore
     93      * @returns The URL for the widgetRelay
     94      * @description This should not be called from usercode.
     95      */
     96     getWidgetRelay : function() {
     97         return this.widgetRelay;
     98     },
     99     /**
    100      * @ignore
    101      * @param The relaypath to set.
    102      * @description This should not be called from usercode.
    103      */
    104     setWidgetRelay : function(relaypath) {
    105       //if null figure out path
    106       if (relaypath == null) {
    107         //check if global path variable was set
    108         if (Ozone.eventing.Widget.widgetRelayURL != null) {
    109           relaypath = Ozone.eventing.Widget.widgetRelayURL;
    110         }
    111         //else calculate a standard relative path
    112         else {
    113           //find root context - assume relay file is at /<context/js/eventing/rpc_relay.uncompressed.html
    114           var baseContextPath = window.location.pathname;
    115           var baseContextPathRegex = /^(\/[^\/]+\/).*$/i;
    116           var matches = baseContextPath.match(baseContextPathRegex);
    117           if (matches != null && matches[1] != null && matches[1].length > 0) {
    118             baseContextPath = matches[1];
    119             //remove final /
    120             baseContextPath = baseContextPath.substring(0,baseContextPath.length-1);
    121           }
    122           else {
    123             baseContextPath = '';
    124           }
    125           relaypath = baseContextPath + '/js/eventing/rpc_relay.uncompressed.html';
    126         }
    127       }
    128       this.widgetRelay = window.location.protocol + "//" + window.location.host + (relaypath.charAt(0) != '/'? ('/'+relaypath) : relaypath);
    129     },
    130     /**
    131      * @description Returns the Widget Id
    132      * @returns {String} The widgetId is a complex JSON encoded string which identifies a widget for Eventing.
    133      *   Embedded in this string is the widget's uniqueId as the 'id' attribute.  There is other data is in the string
    134      *   which is needed for Eventing and other APIs to function properly. This complex widgetId string may be used in
    135      *   the <a href="#publish">Ozone.eventing.Widget.publish</a> function to designate a specific recipient for a message.
    136      *   Additionally, once subscribed to a channel via <a href="#subscribe">Ozone.eventing.Widget.subscribe</a> during the
    137      *   receipt of a message, the sender's widgetId is made available as the first argument to the handler function.
    138      * @example
    139      * //decode and retrieve the widget's unique id
    140      * var complexIdString = this.eventingController.getWidgetId();
    141      * var complexIdObj = owfdojo.toJson(complexIdString);
    142      *
    143      * //complexIdObj will look like
    144      * // {
    145      * //  //widget's uniqueId
    146      * //  id:"49cd21f0-3110-8121-d905-18ffa81b442e"
    147      * // }
    148      *
    149      * //get Widget's uniqueId
    150      * alert('widget id = ' + complexIdObj.id);
    151      */
    152     getWidgetId : function() {
    153         return this.widgetId;
    154     },
    155     /**
    156      * @ignore
    157      * @returns The containerRelay
    158      * @description This should not be called from usercode.
    159      */
    160     getContainerRelay : function() {
    161         return this.containerRelay;
    162     },
    163     /**
    164      * @ignore
    165      * @description This method is called by the Widget's constructor. It should never be called from user code.
    166      */
    167     widgetInit : function() {
    168         var queryHash = {};
    169         var jsonString = null;
    170
    171         //check for data in window.name
    172         var configParams = Ozone.util.parseWindowNameData();
    173         if (configParams != null) {
    174
    175           //the id is the whole contents of the window.name
    176           this.widgetId = '{\"id\":\"' + configParams.id + '\"}';
    177           this.locked = configParams.locked;
    178
    179           //embedded in the id is the relay
    180           this.containerRelay = configParams.relayUrl;
    181         }
    182         else {
    183           throw {
    184             name :'WidgetInitException',
    185             message :'The call to container_init failed. Inner Exception: '
    186           };
    187         }
    188
    189         gadgets.rpc.setRelayUrl("..", this.containerRelay, false, true);
    190
    191
    192         var onClickHandler,
    193             onKeyDownHandler;
    194
    195         function activateWidget() {
    196
    197              var config = {
    198                  fn: "activateWidget",
    199                  params: {
    200                     guid: configParams.id,
    201                     focusIframe: document.activeElement === document.body
    202                  }
    203              };
    204
    205              var stateChannel = '_WIDGET_STATE_CHANNEL_' + configParams.id;
    206              if (!this.disableActivateWidget) {
    207                gadgets.rpc.call('..', stateChannel, null, this.widgetId, config);
    208              }
    209              else {
    210                this.disableActivateWidget = false;
    211              }
    212         }
    213
    214         //register for after_container_init
    215         gadgets.rpc.register("after_container_init", owfdojo.hitch(this,function() {
    216
    217             gadgets.rpc.unregister("after_container_init");
    218
    219             //attach mouse click and keydown listener to send activate calls for the widget
    220             if(!onClickHandler) {
    221                 onClickHandler = owfdojo.connect(document, 'click', owfdojo.hitch(this, activateWidget));
    222             }
    223
    224             if(!onKeyDownHandler) {
    225                 onKeyDownHandler = owfdojo.connect(document, 'onkeyup', owfdojo.hitch(this, activateWidget));
    226             }
    227
    228             //execute callback
    229             this.afterContainerInit();
    230
    231         }));
    232
    233         gadgets.rpc.register("_widget_activated", owfdojo.hitch(this,function() {
    234             //console.log("_widget_activated => " + configParams.id);
    235
    236             if(onClickHandler) {
    237                 owfdojo.disconnect(onClickHandler);
    238             }
    239             if(onClickHandler) {
    240                 owfdojo.disconnect(onKeyDownHandler);
    241             }
    242
    243             onClickHandler = null;
    244             onKeyDownHandler = null;
    245
    246         }));
    247
    248         gadgets.rpc.register("_widget_deactivated", owfdojo.hitch(this,function() {
    249             //console.log("_widget_deactivated => " + configParams.id);
    250
    251             if(!onClickHandler) {
    252                 onClickHandler = owfdojo.connect(document, 'click', owfdojo.hitch(this, activateWidget));
    253             }
    254             if(!onKeyDownHandler) {
    255                 onKeyDownHandler = owfdojo.connect(document, 'onkeyup', owfdojo.hitch(this, activateWidget));
    256             }
    257         }));
    258
    259         //register with container
    260         try {
    261             var idString = '{\"id\":\"' + configParams.id + '\"}';
    262             var data = {
    263               id: idString,
    264               version: this.version,
    265               useMultiPartMessagesForIFPC: true,
    266               relayUrl: this.widgetRelay
    267             };
    268
    269             if (Ozone.util.pageLoad.loadTime != null && Ozone.util.pageLoad.autoSend) {
    270               data.loadTime = Ozone.util.pageLoad.loadTime;
    271             }
    272
    273             //jsonString = gadgets.json.stringify(data);
    274             jsonString = Ozone.util.toString(data);
    275             gadgets.rpc.call('..', 'container_init', null, idString, jsonString);
    276
    277         } catch (error) {
    278             throw {
    279                 name :'WidgetInitException',
    280                 message :'The call to container_init failed. Inner Exception: ' + error
    281             };
    282         }
    283     },
    284
    285     isInitialized: function() {
    286       return this.isAfterInit;
    287     },
    288
    289     /**
    290      * @ignore
    291      * default noop callback
    292      */
    293     afterInitCallBack: function(widgetEventingController) {
    294
    295     },
    296
    297     /**
    298      * @ignore
    299      * @description This method is called by the Widget's constructor. It should never be called from user code.
    300      */
    301      afterContainerInit: function() {
    302        this.isAfterInit = true;
    303        if (this.afterInitCallBack != null) {
    304          this.afterInitCallBack(this);
    305        }
    306      },
    307
    308     /**
    309      * @ignore
    310      */
    311     registerHandler : function(handlerName, handlerObject) {
    312       //Simple wrapper for manager objects to register handler functions
    313       gadgets.rpc.register(handlerName, handlerObject);
    314     },
    315
    316     /**
    317      * @ignore
    318      * Wraps gadgets.rpc.call.
    319      */
    320     send:function () {
    321         gadgets.rpc.call.apply(gadgets.rpc, arguments);
    322     },
    323
    324     /**
    325      * @description Subscribe to a named channel for a given function.
    326      * @param {String} channelName The channel to subscribe to.
    327      * @param {Function} handler The function you wish to subscribe.  This function will be called with three
    328      *   arguments: sender, msg, channel.
    329      * @param {String} [handler.sender] The first argument passed to the handler function is the id of the sender
    330      *   of the message.  See <a href="#getWidgetId">Ozone.eventing.Widget.getWidgetId</a>
    331      *   for a description of this id.
    332      * @param {Object} [handler.msg] The second argument passed to the handler function is the message itself.
    333      * @param {String} [handler.channel] The third argument passed to the handler function is the channel the message was
    334      *   published on.
    335      * @example
    336      * this.widgetEventingController = Ozone.eventing.Widget.getInstance();
    337      * this.widgetEventingController.subscribe("ClockChannel", this.update);
    338      *
    339      * var update = function(sender, msg, channel) {
    340      *     document.getElementById('currentTime').innerHTML = msg;
    341      * }
    342      *
    343      */
    344     subscribe : function(channelName, handler) {
    345         gadgets.pubsub.subscribe(channelName, handler);
    346         return this;
    347     },
    348     /**
    349      * @description Unsubscribe to a named channel
    350      * @param {String} channelName The channel to unsubscribe to.
    351      * @example
    352      * this.widgetEventingController.unsubscribe("ClockChannel");
    353      */
    354     unsubscribe : function(channelName) {
    355         gadgets.pubsub.unsubscribe(channelName);
    356         return this;
    357     },
    358     /**
    359      * @description Publish a message to a given channel
    360      * @param {String} channelName The name of the channel to publish to
    361      * @param {Object} message The message to publish to the channel.
    362      * @param {String} [dest] The id of a particular destination.  Defaults to null which sends to all
    363      *                 subscribers on the channel.  See <a href="#getWidgetId">Ozone.eventing.Widget.getWidgetId</a>
    364      *                 for a description of the id.
    365      * @example
    366      * this.widgetEventingController = Ozone.eventing.Widget.getInstance();
    367      * this.widgetEventingController.publish("ClockChannel", currentTimeString);
    368      */
    369     publish : function(channelName, message, dest) {
    370         gadgets.pubsub.publish(channelName, message, dest);
    371         return this;
    372     }
    373 };
    374
    375 /**
    376  * @description Retrieves Ozone.eventing.Widget Singleton instance
    377  * @param {Function} [afterInit] callback function to be executed after the Ozone.eventing.Widget singleton is initialized
    378  * @param {String} [widgetRelay] Optionally redefine the location of the relay file.  The relay file should be defined
    379  *   globally for the entire widget by setting Ozone.eventing.Widget.widgetRelayURL to the relay file url, immediately after
    380  *   including the widget bundle javascript.  If the relay is not defined at all it is assumed to be at
    381  *   /[context]/js/eventing/rpc_relay.uncompressed.html.  The relay file must be specified with full location details, but without a fully
    382  *   qualified path. In the case where the relay is residing @ http://server/path/relay.html, the path used must be from the context root of the local
    383  *   widget. In this case, it would be /path/relay.html.  Do not include the protocol.
    384  * @since OWF 3.7.0
    385  * @throws {Error} throws an error with a message if widget initialization fails
    386  * @example
    387  * <script type="text/javascript" src="../../js-min/owf-widget-min.js"></script>
    388  * <script>
    389  *       //The location is assumed to be at /[context]/js/eventing/rpc_relay.uncompressed.html if it is not
    390  *       //set the path correctly below
    391  *       Ozone.eventing.Widget.widgetRelayURL = '/owf/js/eventing/rpc_relay.uncompressed.html';
    392  *
    393  *       owfdojo.addOnLoad(function() {
    394  *         //get widget instance
    395  *         var widgetEventingController = Ozone.eventing.Widget.getInstance();
    396  *         //do something
    397  *         widgetEventingController.publish("FooChannel", 'message goes here');
    398  *       });
    399  * </script>
    400  */
    401 Ozone.eventing.Widget.getInstance = function(afterInit, widgetRelay) {
    402   if (Ozone.eventing.Widget.instance == null) {
    403     Ozone.eventing.Widget.instance = new Ozone.eventing.Widget(widgetRelay, afterInit);
    404   }
    405   else {
    406     if (afterInit != null) {
    407       if (!Ozone.eventing.Widget.instance.isAfterInit) {
    408         //connect passed in function to the internal callback function
    409         owfdojo.connect(
    410                 Ozone.eventing.Widget.instance,'afterInitCallBack',
    411                 Ozone.eventing.Widget.instance,afterInit);
    412       }
    413       else {
    414         //already initialized just execute the supplied callback
    415       setTimeout(function() {
    416         afterInit(Ozone.eventing.Widget.instance)
    417       },50);
    418     }
    419   }
    420   }
    421   return Ozone.eventing.Widget.instance;
    422 };
    423