      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 (function (window, document, undefined) {
     13
     14     Ozone.eventing.Container = function () {
     15
     16         var cbMap = {
     17                 widgetEventingReady:[],
     18                 onRoute:[],
     19                 onPublish:[],
     20                 onSubscribe:[],
     21                 onUnsubscribe:[]
     22             },
     23             iframeIds = {},
     24             config = {};
     25
     26         function containerInit(sender, message) {
     27             //Handler used by container to parse out incoming initiation messages from gadgets
     28             //and set their relay URLs for communications downward
     29
     30             if ((window.name === "undefined") || (window.name === "")) {
     31                 window.name = "ContainerWindowName" + Math.random();
     32             }
     33
     34             var initMessage = gadgets.json.parse(message);
     35             var useMultiPartMessagesForIFPC = initMessage.useMultiPartMessagesForIFPC;
     36             var idString = null;
     37             if (initMessage.id.charAt(0) != '{') {
     38                 idString = initMessage.id;
     39             }
     40             else {
     41                 var obj = gadgets.json.parse(initMessage.id);
     42                 var id = obj.id;
     43                 idString = gadgets.json.stringify({id:obj.id});
     44             }
     45
     46             gadgets.rpc.setRelayUrl(idString, initMessage.relayUrl, false, useMultiPartMessagesForIFPC);
     47             gadgets.rpc.setAuthToken(idString, 0);
     48
     49             //execute any widgetEventingReady callbacks
     50             for (var i = 0, len = cbMap.widgetEventingReady.length; i < len; i++) {
     51                 cbMap.widgetEventingReady[i].fn.call(cbMap.widgetEventingReady[i].scope, sender, message);
     52             }
     53
     54             //sent after_container_init message
     55             var jsonString = '{\"id\":\"' + window.name + '\"}';
     56             gadgets.rpc.call(idString, 'after_container_init', null, window.name, jsonString);
     57         }
     58
     59
     60         //public
     61         return /** @lends Ozone.eventing.Container */ {
     62
     63             name:'OWF',
     64             version:Ozone.version.owfversion + Ozone.version.eventing,
     65
     66             init:function (cfg) {
     67                 config = cfg || {};
     68
     69                 //determine containerRelay
     70                 this.setContainerRelay(config.containerRelay != null ? config.containerRelay : Ozone.util.getContainerRelay());
     71
     72                 //setup any widgetEventingReady callbacks
     73                 if (config.widgetEventingReady != null) {
     74                     cbMap.widgetEventingReady.push(config.widgetEventingReady);
     75                 }
     76                 if (config.onRoute != null) {
     77                     cbMap.onRoute.push(config.onRoute);
     78                 }
     79                 if (config.onPublish != null) {
     80                     cbMap.onPublish.push(config.onPublish);
     81                 }
     82                 if (config.onSubscribe != null) {
     83                     cbMap.onSubscribe.push(config.onSubscribe);
     84                 }
     85                 if (config.onUnsubscribe != null) {
     86                     cbMap.onUnsubscribe.push(config.onUnsubscribe);
     87                 }
     88
     89                 //check for overrides
     90                 if (config.getIframeId != null) {
     91                     this.getIframeId = owfdojo.hitch(config.getIframeId.scope, config.getIframeId.fn);
     92                 }
     93                 if (config.getOpenedWidgets != null) {
     94                     this.getOpenedWidgets = owfdojo.hitch(config.getOpenedWidgets.scope, config.getOpenedWidgets.fn);
     95                 }
     96
     97                 //initialize Ozone.eventing.rpc
     98                 gadgets.rpc.register("LIST_WIDGETS", this.getOpenedWidgets);
     99                 Ozone.eventing.rpc.init({
    100                     getIframeId:this.getIframeId
    101                 });
    102
    103                 //hook containerInit for when widgets initialize with the container
    104                 gadgets.rpc.register("container_init", function (sender, message, functions) {
    105                     //need to hook eventing.rpc to container_init
    106                     Ozone.eventing.rpc.priv.clientToldUsFunctionsHandler(sender, message, functions);
    107
    108                     //initialize the widget with the container
    109                     containerInit(sender, message);
    110                 });
    111
    112                 //initialize shindig pubsub
    113                 gadgets.pubsubrouter.init(function (id) {
    114                     return id;
    115                 }, {
    116                     onRoute:function (sender, subscriber, channel, message) {
    117                         var returnValue = true;
    118
    119                         //execute any callbacks if any return false then stop the message
    120                         for (var i = 0, len = cbMap.onRoute.length; i < len; i++) {
    121                             returnValue = returnValue && !(cbMap.onRoute[i].fn.call(cbMap.onRoute[i].scope, sender, subscriber, channel, message) === false);
    122                         }
    123
    124                         //shindig pubsubrouter expects true to means don't send the message
    125                         //so we reverse the value here so false means don't allow the action which makes more sense
    126                         return !returnValue;
    127                     },
    128                     onPublish:function (sender, channel, message) {
    129                         var returnValue = true;
    130
    131                         //execute any callbacks if any return false then stop the message
    132                         for (var i = 0, len = cbMap.onPublish.length; i < len; i++) {
    133                             returnValue = returnValue && !(cbMap.onPublish[i].fn.call(cbMap.onPublish[i].scope, sender, channel, message) === false);
    134                         }
    135
    136                         //shindig pubsubrouter expects true to means don't send the message
    137                         //so we reverse the value here so false means don't allow the action which makes more sense
    138                         return !returnValue;
    139                     },
    140                     onSubscribe:function (sender, message) {
    141                         var returnValue = true;
    142
    143                         //execute any callbacks if any return false then stop the message
    144                         for (var i = 0, len = cbMap.onSubscribe.length; i < len; i++) {
    145                             returnValue = returnValue && !(cbMap.onSubscribe[i].fn.call(cbMap.onSubscribe[i].scope, sender, message) === false);
    146                         }
    147
    148                         //shindig pubsubrouter expects true to means don't send the message
    149                         //so we reverse the value here so false means don't allow the action which makes more sense
    150                         return !returnValue;
    151                     },
    152                     onUnsubscribe:function (sender, message) {
    153                         var returnValue = true;
    154
    155                         //execute any callbacks if any return false then stop the message
    156                         for (var i = 0, len = cbMap.onUnsubscribe.length; i < len; i++) {
    157                             returnValue = returnValue && !(cbMap.onUnsubscribe[i].fn.call(cbMap.onUnsubscribe[i].scope, sender, message) === false);
    158                         }
    159
    160                         //shindig pubsubrouter expects true to means don't send the message
    161                         //so we reverse the value here so false means don't allow the action which makes more sense
    162                         return !returnValue;
    163                     }
    164                 });
    165
    166             },
    167
    168             /**
    169              * @description Adds a widgetEventingReady handler function.  Each function will be executed for each widget that initializes
    170              * in the container
    171              * @param handler
    172              * @param scope
    173              */
    174             widgetEventingReady:function (handler, scope) {
    175                 cbMap.widgetEventingReady.push({fn:handler, scope:scope});
    176             },
    177             onRoute:function (handler, scope) {
    178                 cbMap.onRoute.push({fn:handler, scope:scope});
    179             },
    180             onPublish:function (handler, scope) {
    181                 cbMap.onPublish.push({fn:handler, scope:scope});
    182             },
    183             onSubscribe:function (handler, scope) {
    184                 cbMap.onSubscribe.push({fn:handler, scope:scope});
    185             },
    186             onUnsubscribe:function (handler, scope) {
    187                 cbMap.onUnsubscribe.push({fn:handler, scope:scope});
    188             },
    189             addListener:function (event, handler, scope) {
    190                 if (cbMap[event] != null) {
    191                     cbMap[event].push({fn:handler, scope:scope});
    192                 }
    193             },
    194             removeListener:function (event, handler, scope) {
    195                 if (cbMap[event] != null) {
    196                   for (var i = 0 ; i < cbMap[event].length ; i++) {
    197                       var cb = cbMap[event][i];
    198                       if (cb.fn == handler && cb.scope == scope) {
    199                           cbMap[event].splice(i,1);
    200                       }
    201                   }
    202                 }
    203             },
    204             clearListeners:function (event) {
    205                 if (cbMap[event] != null) {
    206                     cbMap[event] = [];
    207                 }
    208             },
    209             clearAllListeners:function () {
    210                 cbMap = {
    211                     widgetEventingReady:[],
    212                     onRoute:[],
    213                     onPublish:[],
    214                     onSubscribe:[],
    215                     onUnsubscribe:[]
    216                 };
    217             },
    218
    219             /**
    220              *
    221              * @param uniqueId
    222              */
    223             getIframeId:function (uniqueId) {
    224                 return iframeIds[uniqueId];
    225             },
    226
    227             getOpenedWidgets:function () {
    228                 return iframeIds.length != null ? iframeIds : 0;
    229             },
    230
    231             /**
    232              * @ignore
    233              * @description This method returns a string which contains the name and src parameters for an HTML iframe. Rather than setting these properties directly, call this method to autogenerate.
    234              * @param url
    235              * @param id
    236              */
    237             getIframeProperties:function (url, id, widget, paneType, launchData, isLocked) {
    238                 url || (url = widget.url);
    239
    240                 var generatedIdText = Ozone.util.toString(this.generateIframeId(id));
    241                 var generatedNameObj = this.generateIframeName(url, id, widget, paneType, launchData, isLocked);
    242                 var generatedNameText = Ozone.util.toString(generatedNameObj);
    243                 var htmlEncodedId = Ozone.util.HTMLEncodeReservedJS(generatedIdText);
    244                 var htmlEncodedName = Ozone.util.HTMLEncodeReservedJS(generatedNameText);
    245                 var a = url.split('?');
    246                 var base = Ozone.util.HTMLEncodeReservedJS(a[0]);
    247                 var qstring = '';
    248                 if (a[1]) {
    249                     qstring = '&' + a[1];
    250                 }
    251                 qstring = Ozone.util.HTMLEncodeReservedJS(qstring);
    252
    253                 //put extra parameters into the frame src and use the generatedid for the id and name of the frame
    254                 var ret = "id = \"" + htmlEncodedId + "\" name = \"" + htmlEncodedName + "\" src= \"" + base + "?lang=" + Ozone.lang.getLanguage() + "&owf=true" + qstring;
    255                 if (generatedNameObj.currentTheme) {
    256                     var currentThemeName = Ozone.util.HTMLEncodeReservedJS(generatedNameObj.currentTheme.themeName);
    257                     var currentThemeContrast = Ozone.util.HTMLEncodeReservedJS(generatedNameObj.currentTheme.themeContrast);
    258                     var currentThemeFontSize = generatedNameObj.currentTheme.themeFontSize;
    259
    260                     ret += '&themeName=' + currentThemeName;
    261                     ret += '&themeContrast=' + currentThemeContrast;
    262                     ret += '&themeFontSize=' + currentThemeFontSize;
    263                 }
    264                 ret += "\" ";
    265
    266                 iframeIds[id] = generatedIdText;
    267                 iframeIds.length != null ? iframeIds.length++ : iframeIds.length = 0;
    268
    269                 return ret;
    270             },
    271
    272             generateIframeName:function (url, objectId, widget, paneType, launchData, isLocked) {
    273                 var iframeNameObj = {
    274                     id:objectId,
    275
    276                     containerVersion:Ozone.version.owfversion,
    277                     webContextPath:Ozone.config.webContextPath,
    278                     preferenceLocation:Ozone.config.prefsLocation,
    279
    280                     relayUrl:this.getContainerRelay(),
    281                     lang:Ozone.lang.getLanguage(),
    282                     currentTheme:Ozone.config.currentTheme,
    283                     owf:true,
    284                     layout:paneType || "",
    285                     url:url,
    286                     guid:widget.widgetGuid,
    287                     version:widget.version || 1,
    288                     locked: isLocked || false
    289                 };
    290
    291                 if (launchData) {
    292                     iframeNameObj.data = launchData;
    293                 }
    294
    295                 //return this json object as formatted string which is appropriate for being put in the name attribute of the
    296                 //iframe tag.
    297                 return iframeNameObj;
    298
    299             },
    300             //Called by setIframeId function to reference a unique IframeId inside the container object
    301             /**
    302              * @ignore
    303              * @description This function is used to generate a unique id for use when instantiating an iframe. Do not call it from your code.
    304              * @private
    305              */
    306             generateIframeId:function (objectId) {
    307                 return {
    308                     id:objectId
    309                 };
    310             },
    311
    312             /**
    313              * @returns A string which represents the address where the container relay file is located.
    314              */
    315             getContainerRelay:function () {
    316                 return this.containerRelay;
    317             },
    318             /**
    319              * @ignore
    320              * @param A string representing the full or relative URL to the container relay file.
    321              * @description Call this method to set the location of the container relay file. You may specify either a full or a relative URL. <strong>An incorrectly set URL will result in relaying not working, but no error message will display.</strong>
    322              */
    323             setContainerRelay:function (relaypath) {
    324
    325                 if (relaypath === undefined || relaypath === '') {
    326                     throw 'Ozone.eventing.Container.setContainerRelay was called with a null or empty string. Please provide a valid URL.';
    327                 }
    328
    329                 //For purposes of basic proxy safeguards the container relay is pulled from the current
    330                 //window.location parameters and the relay path passed in by the manager object.
    331
    332                 //We should handle people passing in a full URL instead of a relative path. Let's accept both.
    333
    334                 var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
    335
    336                 // parse_url.exec will give us an array with entries of
    337                 // ['url', 'scheme', 'slash', 'host', 'port','path', 'query', 'hash'];
    338                 var result = parse_url.exec(relaypath);
    339                 if (result[1] === undefined || result[1] === null || result[1] === '') {
    340                     // Meaning there is no scheme, so we can just use the relaypath that was passed in.
    341                     // (Extra regexp at the end ensures no double slashes, save for the one after protocol.)
    342                     this.containerRelay = window.location.protocol + "//" + (window.location.host + "/" + relaypath).replace(/\/{2,}/, '/');
    343                 }
    344                 else {
    345                     // There is a scheme, therefore this is a full url. Let's just use the path
    346                     // (Extra regexp at the end ensures no double slashes, save for the one after protocol.)
    347                     this.containerRelay = window.location.protocol + "//" + (window.location.host + "/" + result[5]).replace(/\/{2,}/, '/');
    348                 }
    349             },
    350
    351             /**
    352              * @description Use this method to register functions against the container.
    353              * @param handlerName The key to use for later making calls to this function.
    354              * @param handlerObject The function
    355              */
    356             registerHandler:function (handlerName, handlerObject) {
    357                 //Simple wrapper for manager objects to register handler functions
    358                 if (this.containerRelay === null) {
    359                     throw 'A call was made to Ozone.eventing.Container.registerHandler before the container relay was set. Please call setContainerRelay before attempting to register any handlers.';
    360                 }
    361                 gadgets.rpc.register(handlerName, handlerObject);
    362             },
    363
    364             /**
    365              * Wraps gadgets.rpc.call.  This function calls a shindig service on a widget
    366              */
    367             send:function () {
    368                 gadgets.rpc.call.apply(gadgets.rpc, arguments);
    369             },
    370
    371             /**
    372              * @description Subscribe to a named channel for a given function.
    373              * @param channelName The channel to subscribe to.
    374              * @param handlerObject The function you wish to subscribe
    375              */
    376             subscribe:function (channelName, handlerObject) {
    377                 gadgets.pubsubrouter.subscribe(channelName, handlerObject);
    378             },
    379
    380             /**
    381              * @description UnSubscribe to a named channel
    382              * @param {String} channelName The channel to unsubscribe to.
    383              */
    384             unsubscribe:function (channelName) {
    385                 gadgets.pubsubrouter.unsubscribe(channelName);
    386             },
    387
    388             /**
    389              * @ignore
    390              * @description Publish a message to a given channel
    391              * @param {String} channelName The name of the channel to publish to
    392              * @param {Object} message The message to publish to the channel.
    393              * @param {String} [dest] The id of a particular destination.  Defaults to null which sends to all
    394              *                 subscribers on the channel
    395              */
    396             publish:function (channelName, message, dest) {
    397                 gadgets.pubsubrouter.publish(channelName, message, dest);
    398             }
    399         };
    400     }();
    401
    402 }(window, document));