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