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