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));