1 /**
  2  * @ignore
  3  */
  4 var Ozone = Ozone ? Ozone : {};
  5 
  6 /**
  7  * @ignore
  8  * @namespace
  9  */
 10 Ozone.chrome = Ozone.chrome ? Ozone.chrome : {};
 11 
 12 /**
 13  *  @deprecated Since OWF 3.7.0  You should use <a href="#.getInstance">Ozone.chrome.WidgetChrome.getInstance</a>
 14  *  @constructor  widgetEventingController - Ozone.eventing.Widget object
 15  *  @param  {Object} config - config object with parameters
 16  *  @param  {Ozone.eventing.Widget} config.widgetEventingController - widget eventing object which handles eventing for the widget
 17  *  @description This object allows a widget to modify the button contained in the widget header (the chrome).  
 18  *  To do so it requires a widgetEventingController
 19  *  @example
 20  *    this.wc = new Ozone.chrome.WidgetChrome({
 21  *        widgetEventingController: this.widgetEventingController
 22  *    });
 23  *
 24  * @throws {Error} throws an error with a message if widget initialization fails
 25  */
 26 Ozone.chrome.WidgetChrome = function(config) {
 27   if (Ozone.chrome.WidgetChrome.instance == null) {
 28     var scope = this;
 29     scope.channelName = "Ozone._WidgetChromeChannel";
 30     scope.version = Ozone.version.owfversion + Ozone.version.widgetChrome;
 31     scope.items = {};
 32     scope.registerChromeMenu = function(menuConfig) {
 33   		// Regular menu item
 34        	scope.items[menuConfig.itemId != null ? menuConfig.itemId : menuConfig.id] = menuConfig;
 35        	
 36   	  	if (menuConfig.menu) {
 37   	    	for (var j = 0 ; j < menuConfig.menu.items.length ; j++) {
 38   	    		var menuItem = menuConfig.menu.items[j];
 39     			// Sub-menu
 40     			scope.registerChromeMenu(menuItem);
 41   	    	}
 42 	  	}
 43     };
 44 
 45     var pub = /** @lends Ozone.chrome.WidgetChrome.prototype */ {
 46 
 47       init : function(config) {
 48 
 49         config = config || {};
 50 
 51           scope.widgetEventingController = config.widgetEventingController || Ozone.eventing.Widget.instance;
 52           scope.widgetEventingController.registerHandler(scope.channelName, function(sender, msg) {
 53             var returnValue = true;
 54 
 55             //msg will always be a json string
 56             var data = Ozone.util.parseJson(msg);
 57 
 58             var id = data.itemId != null ? data.itemId : data.id;
 59             var handler = scope.items[id].handler;
 60             if (handler != null && owfdojo.isFunction(handler)) {
 61               returnValue = handler.apply(data.scope != null ? data.scope : window, data.args != null ? data.args : []);
 62             }
 63 
 64             return returnValue;
 65           });
 66       },
 67 
 68       /**
 69        * @description Checks to see if the Widget Chrome has already been modified.  This is useful if the widget iframe is reloaded
 70        * @param {Object} cfg config object see below for properties
 71        * @param {Function} cfg.callback The function which receives the results.
 72        * This method will be passed an object which has following properties. <br>
 73        * <br>
 74        *     {Boolean} success: true if the widget is currently opened on the dashboard, or else false. <br>
 75        *     {Boolean} modified: true if the widget chrome(header) is modified, or else false. <br>
 76        *
 77        * @example
 78        *    //this.wc is an already instantiated WidgetChrome obj
 79        *    this.wc.isModified({
 80        *     callback: function(msg) {
 81        *         //msg will always be a json string
 82        *         var res = Ozone.util.parseJson(msg);
 83        *         if (res.success) {
 84        *
 85        *             //if the chrome was never modified
 86        *             if (!res.modified) {
 87        *                //do something, perhaps add buttons
 88        *             }
 89        *             //if we already modified the chrome
 90        *             else {
 91        *               //do something or perhaps nothing if the buttons are already added
 92        *             }
 93        *         }
 94        *     }
 95        *   });
 96        */
 97       isModified: function(cfg) {
 98         var data = {
 99           action: 'isModified'
100         };
101         var jsonString = gadgets.json.stringify(data);
102         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
103       },
104 
105       /**
106        * @description Adds buttons to the Widget Chrome.  Buttons are added after existing buttons.
107        * @param {Object} cfg config object see below for properties
108        * @param {Object[]} cfg.items an array of buttons configurations to add to the chrome.  See example for button configs
109        * @param {String} cfg.items[*].itemId itemId is a unique id among all buttons that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.  If itemId is not unique this may result in duplicate buttons which may not be able to be removed properly.
110        * @param {String} cfg.items[*].xtype xtype is ExtJS-like property used to determine the component to create.  Currently the Widget Chrome API only supports two xtype values: ‘button’ and ‘widgettool’.  xtype is an optional field, if it is omitted ‘widgettool’ is used.
111        * @param {String} cfg.items[*].type Used only for ‘widgettool’ buttons.  It determines the standard icon to be used.  For a complete list of types please see the ExtJS 4.x API documentation, <a href='http://docs.sencha.com/ext-js/4-0/#/api/Ext.panel.Tool-cfg-type'>http://docs.sencha.com/ext-js/4-0/#/api/Ext.panel.Tool-cfg-type</a>
112        * @param {String} cfg.items[*].icon This property defines the URL of the image to be used for the button.  If the URL is a relative path, it will be relative to the /owf context.  This is useful if the desired image is hosted by the OWF web server.  Otherwise a fully qualified URL should be used.  If type is being used to determine the image, the icon property is optional
113        * @param {String} cfg.items[*].text This property defines text to appear alongside the button.  This property is only used if the xtype is ‘button.’  ‘widgettool’ will not show text.
114        * @param {Object} cfg.items[*].tooltip This property defines a tooltip.  It has two important sub properties, title and text.  tooltip is only used when the xtype is ‘button’
115        * @param {Function} cfg.items[*].handler The handler attribute defines a function to be executed when the button is pressed. This function is executed using Widget Eventing API from inside the widget.  The internal channel name used is the itemId attribute. This function’s parameter list contains the standard parameters for an Eventing callback function.
116        * @example
117        *     //this.wc is an already instantiated WidgetChrome obj
118        *     this.wc.addHeaderButtons({
119        *             items:
120        *              [
121        *               {
122        *                 xtype: 'button',
123        *                 //path to an image to use. this path should either be fully qualified or relative to the /owf context
124        *                 icon: './themes/common/images/skin/exclamation.png',
125        *                 text: 'Alert',
126        *                 itemId:'alert',
127        *                 tooltip:  {
128        *                   text: 'Alert!'
129        *                 },
130        *                 handler: function(sender, data) {
131        *                   //widgetState is an already instantiated WidgetState Obj
132        *                   if (widgetState) {
133        *                     widgetState.getWidgetState({
134        *                         callback: function(state) {
135        *
136        *                           //check if the widget is visible
137        *                           if (!state.collapsed && !state.minimized && state.active) {
138        *                             //only render visual content, perhaps popup a message box if the widget is visible
139        *                             //otherwise it may not render correctly
140        *                           }
141        *                         }
142        *                     });
143        *                   }
144        *                 }
145        *               },
146        *               {
147        *                 xtype: 'widgettool',
148        *                 //path to an image to use. this path should either be fully qualified or relative to the /owf context
149        *                 icon: './themes/common/images/skin/information.png',
150        *                 itemId:'help',
151        *                 handler: function(sender, data) {
152        *                   alert('About Button Pressed');
153        *                 }
154        *               },
155        *               {
156        *                 //gear is a standard ext tool type
157        *                 type: 'gear',
158        *                 itemId:'gear',
159        *                 handler: function(sender, data) {
160        *                   alert('Utility Button Pressed');
161        *                 }
162        *               }
163        *             ]
164        *     });
165        */
166       addHeaderButtons : function(cfg) {
167         var data = {};
168         var items = null;
169 
170         if (!owfdojo.isArray(cfg.items)) {
171           items = [cfg.items];
172         }
173         else {
174           items = cfg.items;
175         }
176 
177         data.action = 'addHeaderButtons';
178         data.type = 'button';
179         data.items = items;
180         for (var i = 0; i < items.length; i++) {
181           scope.items[items[i].itemId != null ? items[i].itemId : items[i].id] = items[i];
182         }
183 
184         var jsonString = gadgets.json.stringify(data);
185         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
186 
187       },
188 
189       /**
190        * @description Updates any existing buttons in the Widget Chrome based on the itemId.
191        * @param {Object} cfg config object see below for properties
192        * @param {Object[]} cfg.items an array of buttons configurations to add to the chrome.  See example below for button configs
193        * @param {String} cfg.items[*].itemId itemId is a unique id among all buttons that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.  If itemId is not unique this may result in duplicate buttons which may not be able to be removed properly.
194        * @param {String} cfg.items[*].xtype xtype is ExtJS-like property used to determine the component to create.  Currently the Widget Chrome API only supports two xtype values: ‘button’ and ‘widgettool’.  xtype is an optional field, if it is omitted ‘widgettool’ is used.
195        * @param {String} cfg.items[*].type Used only for ‘widgettool’ buttons.  It determines the standard icon to be used.  For a complete list of types please see the ExtJS 4.x API documentation, <a href='http://docs.sencha.com/ext-js/4-0/#/api/Ext.panel.Tool-cfg-type'>http://docs.sencha.com/ext-js/4-0/#/api/Ext.panel.Tool-cfg-type</a>
196        * @param {String} cfg.items[*].icon This property defines the URL of the image to be used for the button.  If the URL is a relative path, it will be relative to the /owf context.  This is useful if the desired image is hosted by the OWF web server.  Otherwise a fully qualified URL should be used.  If type is being used to determine the image, the icon property is optional
197        * @param {String} cfg.items[*].text This property defines text to appear alongside the button.  This property is only used if the xtype is ‘button.’  ‘widgettool’ will not show text.
198        * @param {Object} cfg.items[*].tooltip This property defines a tooltip.  It has two important sub properties, title and text.  tooltip is only used when the xtype is ‘button’
199        * @param {Function} cfg.items[*].handler The handler attribute defines a function to be executed when the button is pressed. This function is executed using Widget Eventing API from inside the widget.  The internal channel name used is the itemId attribute. This function’s parameter list contains the standard parameters for an Eventing callback function.
200        * @example
201        *     //this.wc is an already instantiated WidgetChrome obj
202        *     this.wc.updateHeaderButtons({
203        *             items:
204        *              [
205        *               {
206        *                 xtype: 'button',
207        *                 //path to an image to use. this path should either be fully qualified or relative to the /owf context
208        *                 icon: './themes/common/images/skin/exclamation.png',
209        *                 text: 'Alert',
210        *                 itemId:'alert',
211        *                 tooltip:  {
212        *                   text: 'Alert!'
213        *                 },
214        *                 handler: function(sender, data) {
215        *                   //widgetState is an already instantiated WidgetState Obj
216        *                   if (widgetState) {
217        *                     widgetState.getWidgetState({
218        *                         callback: function(state) {
219        *
220        *                           //check if the widget is visible
221        *                           if (!state.collapsed && !state.minimized && state.active) {
222        *                             //only render visual content, perhaps popup a message box if the widget is visible
223        *                             //otherwise it may not render correctly
224        *                           }
225        *                         }
226        *                     });
227        *                   }
228        *                 }
229        *               },
230        *               {
231        *                 xtype: 'widgettool',
232        *                 //path to an image to use. this path should either be fully qualified or relative to the /owf context
233        *                 icon: './themes/common/images/skin/information.png',
234        *                 itemId:'help',
235        *                 handler: function(sender, data) {
236        *                   alert('About Button Pressed');
237        *                 }
238        *               },
239        *               {
240        *                 //gear is a standard ext tool type
241        *                 type: 'gear',
242        *                 itemId:'gear',
243        *                 handler: function(sender, data) {
244        *                   alert('Utility Button Pressed');
245        *                 }
246        *               }
247        *             ]
248        *     });
249        */
250       updateHeaderButtons : function(cfg) {
251         var data = {};
252         var items = null;
253 
254         if (!owfdojo.isArray(cfg.items)) {
255           items = [cfg.items];
256         }
257         else {
258           items = cfg.items;
259         }
260 
261         data.action = 'updateHeaderButtons';
262         data.type = 'button';
263         data.items = items;
264         for (var i = 0; i < items.length; i++) {
265           //save data
266           var id = items[i].itemId != null ? items[i].itemId : items[i].id;
267           if (scope.items[id] != null) {
268             owfdojo.mixin(scope.items[id], items[i]);
269           }
270           else {
271             scope.items[id] = items[i];
272           }
273         }
274 
275         var jsonString = gadgets.json.stringify(data);
276         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
277 
278       },
279 
280       /**
281        * @description Inserts new buttons to the Widget Chrome.  Buttons are added to the same area as existing buttons.
282        * @param {Object} cfg config object see below for properties
283        * @param {Number} [cfg.pos=0] 0-based index of where buttons will be added, among any pre-existing buttons.
284        * @param {Object[]} cfg.items an array of buttons configurations to insert to the chrome.  See example below for button configs
285        * @param {String} cfg.items[*].itemId itemId is a unique id among all buttons that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.  If itemId is not unique this may result in duplicate buttons which may not be able to be removed properly.
286        * @param {String} cfg.items[*].xtype xtype is ExtJS-like property used to determine the component to create.  Currently the Widget Chrome API only supports two xtype values: ‘button’ and ‘widgettool’.  xtype is an optional field, if it is omitted ‘widgettool’ is used.
287        * @param {String} cfg.items[*].type Used only for ‘widgettool’ buttons.  It determines the standard icon to be used.  For a complete list of types please see the ExtJS 4.x API documentation, <a href='http://docs.sencha.com/ext-js/4-0/#/api/Ext.panel.Tool-cfg-type'>http://docs.sencha.com/ext-js/4-0/#/api/Ext.panel.Tool-cfg-type</a>
288        * @param {String} cfg.items[*].icon This property defines the URL of the image to be used for the button.  If the URL is a relative path, it will be relative to the /owf context.  This is useful if the desired image is hosted by the OWF web server.  Otherwise a fully qualified URL should be used.  If type is being used to determine the image, the icon property is optional
289        * @param {String} cfg.items[*].text This property defines text to appear alongside the button.  This property is only used if the xtype is ‘button.’  ‘widgettool’ will not show text.
290        * @param {Object} cfg.items[*].tooltip This property defines a tooltip.  It has two important sub properties, title and text.  tooltip is only used when the xtype is ‘button’
291        * @param {Function} cfg.items[*].handler The handler attribute defines a function to be executed when the button is pressed. This function is executed using Widget Eventing API from inside the widget.  The internal channel name used is the itemId attribute. This function’s parameter list contains the standard parameters for an Eventing callback function.
292        * @example
293        *     //this.wc is an already instantiated WidgetChrome obj
294        *     this.wc.insertHeaderButtons({
295        *             pos: 0,
296        *             items:
297        *              [
298        *               {
299        *                 xtype: 'button',
300        *                 //path to an image to use. this path should either be fully qualified or relative to the /owf context
301        *                 icon: './themes/common/images/skin/exclamation.png',
302        *                 text: 'Alert',
303        *                 itemId:'alert',
304        *                 tooltip:  {
305        *                   text: 'Alert!'
306        *                 },
307        *                 handler: function(sender, data) {
308        *                   //widgetState is an already instantiated WidgetState Obj
309        *                   if (widgetState) {
310        *                     widgetState.getWidgetState({
311        *                         callback: function(state) {
312        *
313        *                           //check if the widget is visible
314        *                           if (!state.collapsed && !state.minimized && state.active) {
315        *                             //only render visual content, perhaps popup a message box if the widget is visible
316        *                             //otherwise it may not render correctly
317        *                           }
318        *                         }
319        *                     });
320        *                   }
321        *                 }
322        *               },
323        *               {
324        *                 xtype: 'widgettool',
325        *                 //path to an image to use. this path should either be fully qualified or relative to the /owf context
326        *                 icon: './themes/common/images/skin/information.png',
327        *                 itemId:'help',
328        *                 handler: function(sender, data) {
329        *                   alert('About Button Pressed');
330        *                 }
331        *               },
332        *               {
333        *                 //gear is a standard ext tool type
334        *                 type: 'gear',
335        *                 itemId:'gear',
336        *                 handler: function(sender, data) {
337        *                   alert('Utility Button Pressed');
338        *                 }
339        *               }
340        *             ]
341        *     });
342        */
343       insertHeaderButtons : function(cfg) {
344         var data = {};
345         var items = null;
346 
347         if (!owfdojo.isArray(cfg.items)) {
348           items = [cfg.items];
349         }
350         else {
351           items = cfg.items;
352         }
353 
354         data.action = 'insertHeaderButtons';
355         data.type = 'button';
356         if (cfg.pos != null) {
357           data.pos = cfg.pos;
358         }
359         data.items = items;
360         for (var i = 0; i < items.length; i++) {
361           scope.items[items[i].itemId != null ? items[i].itemId : items[i].id] = items[i];
362         }
363 
364         var jsonString = gadgets.json.stringify(data);
365         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
366       },
367 
368       /**
369        * @description Removes existing buttons on the Widget Chrome based on itemId.
370        * @param {Object} cfg config object see below for properties
371        * @param {Object[]} cfg.items an array of buttons configurations to remove to the chrome.  Only itemId is required.
372        *   See example below for button configs
373        * @param {String} cfg.items[*].itemId itemId is a unique id among all buttons that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.  If itemId is not unique this may result in duplicate buttons which may not be able to be removed properly.
374        * @example
375        *     //this.wc is an already instantiated WidgetChrome obj
376        *     this.wc.removeHeaderButtons({
377        *             items:
378        *              [
379        *               {
380        *                 itemId:'alert'
381        *               },
382        *               {
383        *                 itemId:'help'
384        *               },
385        *               {
386        *                 itemId:'gear'
387        *               }
388        *             ]
389        *     });
390        */
391       removeHeaderButtons : function(cfg) {
392         var data = {};
393         var items = null;
394 
395         if (!owfdojo.isArray(cfg.items)) {
396           items = [cfg.items];
397         }
398         else {
399           items = cfg.items;
400         }
401 
402         data.action = 'removeHeaderButtons';
403         data.type = 'button';
404         data.items = items;
405         for (var i = 0; i < items.length; i++) {
406           var id = items[i].itemId != null ? items[i].itemId : items[i].id;
407           if (scope.items[id] != null) {
408             //delete saved data
409             delete scope.items[id];
410           }
411         }
412 
413         var jsonString = gadgets.json.stringify(data);
414         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
415       },
416      
417     
418     /**
419      * @description Lists all buttons that have been added to the widget chrome.
420      * @param {Object} cfg config object see below for properties
421      * @param {Function} cfg.callback The function which receives the results.
422      *
423      * @example
424      *    //this.wc is an already instantiated WidgetChrome obj
425      *    this.wc.listHeaderButtons({
426      *     callback: function(msg) {
427      *         //msg will always be a json string
428      *         var res = Ozone.util.parseJson(msg);
429      *         if (res.success) {
430      *             for (var i = 0; i < res.items.length; i++) {
431      *             	// do something with the buttons
432      *             }
433      *         }
434      *     }
435      *   });
436      */
437     listHeaderButtons: function(cfg) {
438       var data = {
439         action: 'listHeaderMenus',
440         type: 'button'
441       };
442       var jsonString = gadgets.json.stringify(data);
443       gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
444     },
445     
446       /**
447        * @description Adds menus to the Widget Chrome.  Menus are added after existing menus.
448        * @param {Object} cfg config object see below for properties
449        * @param {Object[]} cfg.items an array of menu configurations to add to the chrome.  See example for menu configs
450        * @param {String} cfg.items[*].parentId itemId is the itemId of the menu to which this configuration should be added as a sub-menu.  If omitted, the configuration will be added as a main menu on the menu toolbar.  
451        * @param {String} cfg.items[*].itemId itemId is a unique id among all menus that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.  If itemId is not unique this may result in duplicate menus which may not be able to be removed properly.
452        * @param {String} cfg.items[*].icon This property defines the URL of the image to be used for the menu.  If the URL is a relative path, it will be relative to the /owf context.  This is useful if the desired image is hosted by the OWF web server.  Otherwise a fully qualified URL should be used.  If type is being used to determine the image, the icon property is optional
453        * @param {String} cfg.items[*].text This property defines text to appear alongside the menu.  
454        * @param {Object} cfg.items[*].menu menu configuration object
455        * @param {Object[]} cfg.items[*].menu.items an array of menu item configurations to add to the chrome.  See example for menu item configs
456        * @param {String} cfg.items[*].menu.items[*].itemId itemId is a unique id among all menu items that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.
457        * @param {String} cfg.items[*].menu.items[*].xtype xtype is used to specify the type of menu item to add.  This attribute should be omitted unless specifying a menuseparator. Setting this value to "menuseparator" adds a separator bar to a menu, used to divide logical groups of menu items. If specified, only xtype should be specified.  Generally you will add one of these by using "-" in your items config rather than creating one directly using xtype.  See example below for usage. 
458        * @param {String} cfg.items[*].menu.items[*].icon This property defines the URL of the image to be used for the menu item.  If the URL is a relative path, it will be relative to the /owf context.  This is useful if the desired image is hosted by the OWF web server.  Otherwise a fully qualified URL should be used.  If type is being used to determine the image, the icon property is optional
459        * @param {String} cfg.items[*].menu.items[*].text This property defines text to appear for the menu item. 
460        * @param {Function} cfg.items[*].menu.items[*].handler The handler attribute defines a function to be executed when the menu item is clicked. This function is executed using Widget Eventing API from inside the widget.  The internal channel name used is the itemId attribute. This function's parameter list contains the standard parameters for an Eventing callback function.
461        * @param {Object} cfg.items[*].menu.items[*].menu sub-menu configuration object.  See example for sub-menu config.
462        * @example
463        *     //this.wc is an already instantiated WidgetChrome obj
464        *     this.wc.addHeaderMenus({
465        *             items:
466        *              [
467        *				{
468        *					itemId:'regularMenu',
469        *					icon: './themes/common/images/skin/exclamation.png',
470        *					text: 'Regular Menu',
471        *					menu: {
472        *						items: [
473        *						    {
474        *						    	itemId:'regularMenuItem1',
475        *						    	icon: './themes/common/images/skin/exclamation.png',
476        *						    	text: 'Regular Menu Item 1',
477        *						    	handler: function(sender, data) {
478        *						    		alert('You clicked the Regular Menu menu item.');
479        *						    	}
480        *						    }
481        *						]
482        *					}
483        *				},
484        *				{
485        *					itemId:'snacks',
486        *					icon: './themes/common/images/skin/exclamation.png',
487        *					text: 'Menu with Sub-Menu',
488        *					menu: {
489        *						items: [
490        *						    {
491        *						    	itemId:'fruits',
492        *						    	icon: './themes/common/images/skin/exclamation.png',
493        *						    	text: 'Fruits',
494        *						    	menu: {
495        *							    	items: [
496        *							    	    {
497        *							    	    	itemId:'apple',
498        *							    	    	icon: './themes/common/images/skin/exclamation.png',
499        *							    	    	text: 'Apple',
500        *							    	    	handler: function(sender, data) {
501        *							    	    		alert('Your snack will be an Apple.');
502        *							    	    	}
503        *							    	    },
504        *                                        {
505        *                                            xtype: 'menuseparator'
506        *                                        },
507        *							    	    {
508        *							    	    	itemId:'banana',
509        *							    	    	icon: './themes/common/images/skin/exclamation.png',
510        *							    	    	text: 'Banana',
511        *							    	    	handler: function(sender, data) {
512        *							    	    		alert('Your snack will be a Banana.');
513        *							    	    	}
514        *							    	    }, {
515        *							    	    	itemId:'cherry',
516        *							    	    	icon: './themes/common/images/skin/exclamation.png',
517        *							    	    	text: 'Cherries',
518        *							    	    	handler: function(sender, data) {
519        *							    	    		alert('Your snack will be Cherries.');
520        *							    	    	}
521        *							    	    }
522        *							    	]
523        *						    	}
524        *						    },
525        *							'-', // another way to add a menu separator 
526        *							{
527        *						    	itemId:'cupcake',
528        *						    	icon: './themes/common/images/skin/exclamation.png',
529        *						    	text: 'Cupcake',
530        *						    	handler: function(sender, data) {
531        *						    		alert('Your snack will be a Cupcake.');
532        *						    	}
533        *						    }, {
534        *						    	itemId:'chips',
535        *						    	icon: './themes/common/images/skin/exclamation.png',
536        *						    	text: 'Potato Chips',
537        *						    	handler: function(sender, data) {
538        *						    		alert('Your snack will be a Potato Chips.');
539        *						    	}
540        *						    }
541        *						]
542        *					}
543        *				}
544        *             ]
545        *     });
546        */
547       addHeaderMenus : function(cfg) {
548         var data = {};
549         var items = null;
550 
551         if (!owfdojo.isArray(cfg.items)) {
552           items = [cfg.items];
553         }
554         else {
555           items = cfg.items;
556         }
557 
558         data.action = 'addHeaderMenus';
559         data.type = 'menu';
560         data.items = items;
561         for (var i = 0; i < items.length; i++) {
562         	var item = items[i];
563         	scope.registerChromeMenu(item);
564         }
565 
566         var jsonString = gadgets.json.stringify(data);
567         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
568 
569       },
570       
571       /**
572        * @description Updates any existing menus in the Widget Chrome based on the itemId.
573        * @param {Object} cfg config object see below for properties
574        * @param {Object[]} cfg.items an array of menu configurations to add to the chrome.  See example for menu configs
575        * @param {String} cfg.items[*].itemId itemId is a unique id among all menus that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.  If itemId is not unique this may result in duplicate menus which may not be able to be removed properly.
576        * @param {String} cfg.items[*].icon This property defines the URL of the image to be used for the menu.  If the URL is a relative path, it will be relative to the /owf context.  This is useful if the desired image is hosted by the OWF web server.  Otherwise a fully qualified URL should be used.  If type is being used to determine the image, the icon property is optional
577        * @param {String} cfg.items[*].text This property defines text to appear alongside the menu.  
578        * @param {Object} cfg.items[*].menu menu configuration object
579        * @param {Object[]} cfg.items[*].menu.items an array of menu item configurations to add to the chrome.  See example for menu item configs
580        * @param {String} cfg.items[*].menu.items[*].itemId itemId is a unique id among all menu items that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.
581        * @param {String} cfg.items[*].menu.items[*].xtype xtype is used to specify the type of menu item to add.  This attribute should be omitted unless specifying a menuseparator. Setting this value to "menuseparator" adds a separator bar to a menu, used to divide logical groups of menu items. If specified, only xtype should be specified.  See example below for usage.
582        * @param {String} cfg.items[*].menu.items[*].icon This property defines the URL of the image to be used for the menu item.  If the URL is a relative path, it will be relative to the /owf context.  This is useful if the desired image is hosted by the OWF web server.  Otherwise a fully qualified URL should be used.  If type is being used to determine the image, the icon property is optional
583        * @param {String} cfg.items[*].menu.items[*].text This property defines text to appear for the menu item. 
584        * @param {Function} cfg.items[*].menu.items[*].handler The handler attribute defines a function to be executed when the menu item is clicked. This function is executed using Widget Eventing API from inside the widget.  The internal channel name used is the itemId attribute. This function's parameter list contains the standard parameters for an Eventing callback function.
585        * @param {Object} cfg.items[*].menu.items[*].menu sub-menu configuration object.  See example for sub-menu config.
586        * @example
587        *     //this.wc is an already instantiated WidgetChrome obj
588        *     this.wc.updateHeaderMenus({
589        *             items:
590        *              [
591        *				{
592        *					itemId:'regularMenu',
593        *					icon: './themes/common/images/skin/exclamation.png',
594        *					text: 'Regular Menu',
595        *					menu: {
596        *						items: [
597        *						    {
598        *						    	itemId:'regularMenuItem1',
599        *						    	icon: './themes/common/images/skin/exclamation.png',
600        *						    	text: 'Regular Menu Item 1',
601        *						    	handler: function(sender, data) {
602        *						    		alert('You clicked the Regular Menu menu item.');
603        *						    	}
604        *						    }
605        *						]
606        *					}
607        *				},
608        *				{
609        *					itemId:'snacks',
610        *					icon: './themes/common/images/skin/exclamation.png',
611        *					text: 'Menu with Sub-Menu',
612        *					menu: {
613        *						items: [
614        *						    {
615        *						    	itemId:'fruits',
616        *						    	icon: './themes/common/images/skin/exclamation.png',
617        *						    	text: 'Fruits',
618        *						    	menu: {
619        *							    	items: [
620        *							    	    {
621        *							    	    	itemId:'apple',
622        *							    	    	icon: './themes/common/images/skin/exclamation.png',
623        *							    	    	text: 'Apple',
624        *							    	    	handler: function(sender, data) {
625        *							    	    		alert('Your snack will be an Apple.');
626        *							    	    	}
627        *							    	    },
628        *                                        {
629        *                                            xtype: 'menuseparator'
630        *                                        },
631        *							    	    {
632        *							    	    	itemId:'banana',
633        *							    	    	icon: './themes/common/images/skin/exclamation.png',
634        *							    	    	text: 'Banana',
635        *							    	    	handler: function(sender, data) {
636        *							    	    		alert('Your snack will be a Banana.');
637        *							    	    	}
638        *							    	    }, {
639        *							    	    	itemId:'cherry',
640        *							    	    	icon: './themes/common/images/skin/exclamation.png',
641        *							    	    	text: 'Cherries',
642        *							    	    	handler: function(sender, data) {
643        *							    	    		alert('Your snack will be Cherries.');
644        *							    	    	}
645        *							    	    }
646        *							    	]
647        *						    	}
648        *						    },
649        *							'-', // another way to add a menu separator 
650        *							{
651        *						    	itemId:'cupcake',
652        *						    	icon: './themes/common/images/skin/exclamation.png',
653        *						    	text: 'Cupcake',
654        *						    	handler: function(sender, data) {
655        *						    		alert('Your snack will be a Cupcake.');
656        *						    	}
657        *						    }, {
658        *						    	itemId:'chips',
659        *						    	icon: './themes/common/images/skin/exclamation.png',
660        *						    	text: 'Potato Chips',
661        *						    	handler: function(sender, data) {
662        *						    		alert('Your snack will be a Potato Chips.');
663        *						    	}
664        *						    }
665        *						]
666        *					}
667        *				}
668        *             ]
669        *     });
670        */
671       updateHeaderMenus : function(cfg) {
672         var data = {};
673         var items = null;
674 
675         if (!owfdojo.isArray(cfg.items)) {
676           items = [cfg.items];
677         }
678         else {
679           items = cfg.items;
680         }
681 
682         data.action = 'updateHeaderMenus';
683         data.type = 'menu';
684         data.items = items;
685         
686         var merge = function(source, key, value) {
687             if (typeof key === 'string') {
688                 if (value && value.constructor === Object) {
689                     if (source[key] && source[key].constructor === Object) {
690                         merge(source[key], value);
691                     }
692                     else {
693                         source[key] = Ext.clone(value);
694                     }
695                 }
696                 else {
697                     source[key] = value;
698                 }
699 
700                 return source;
701             }
702 
703             var i = 1,
704                 ln = arguments.length,
705                 object, property;
706 
707             for (; i < ln; i++) {
708                 object = arguments[i];
709 
710                 for (property in object) {
711                     if (object.hasOwnProperty(property)) {
712                         merge(source, property, object[property]);
713                     }
714                 }
715             }
716 
717             return source;
718         };
719         
720         for (var i = 0; i < items.length; i++) {
721         	var item = items[i];
722         	// don't re-register menu if submenu or handler are unchanged
723         	if (item.menu || item.handler) {
724         		scope.registerChromeMenu(item);
725         	}
726         	if (item.menu) {
727 	        	for (var j = 0 ; j < item.menu.items.length ; j++) {
728 	        		var menuItem = item.menu.items[j];
729 	        		if (!menuItem.menu) {
730 	        			// Regular menu item
731 	        			// Merge with current menu item
732 	        			var key = menuItem.itemId != null ? menuItem.itemId : menuItem.id;
733 	        			scope.items[key] = scope.items[key] ? scope.items[key] : {};
734 	                	scope.items[key] = merge(scope.items[key], menuItem);
735 	        		} else {
736 	        			// Sub-menu 
737 	        			for (var k = 0 ; k < menuItem.menu.items.length ; k++) {
738 	        				var subMenuItem = menuItem.menu.items[k];
739 		        			// Merge with current sub-menu item
740 		        			var key = subMenuItem.itemId != null ? subMenuItem.itemId : subMenuItem.id;
741 		        			scope.items[key] = scope.items[key] ? scope.items[key] : {};
742 		                	scope.items[key] = merge(scope.items[key], subMenuItem);
743 	        			}
744 	        		}
745 	        	}
746         	} else {
747     			// Regular menu item
748     			// Merge with current menu item
749     			var key = item.itemId != null ? item.itemId : item.id;
750     			scope.items[key] = scope.items[key] ? scope.items[key] : {};
751             	scope.items[key] = merge(scope.items[key], item);
752         	}
753         	
754         	// Update data with merged item
755         	data.items[i] = scope.items[data.items[i].itemId];
756         }
757 
758         var jsonString = gadgets.json.stringify(data);
759         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
760 
761       },
762 
763       /**
764        * @description Inserts new menus into the Widget Chrome.  Menus are added to the same area as existing menus.
765        * @param {Object} cfg config object see below for properties
766        * @param {Number} [cfg.pos=0] 0-based index of where menus will be added, among any pre-existing menus.
767        * @param {Object[]} cfg.items an array of menu configurations to add to the chrome.  See example for menu configs
768        * @param {String} cfg.items[*].parentId itemId is the itemId of the menu to which this configuration should be added as a sub-menu.  If omitted, the configuration will be added as a main menu on the menu toolbar.  
769        * @param {String} cfg.items[*].itemId itemId is a unique id among all menus that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.  If itemId is not unique this may result in duplicate menus which may not be able to be removed properly.
770        * @param {String} cfg.items[*].icon This property defines the URL of the image to be used for the menu.  If the URL is a relative path, it will be relative to the /owf context.  This is useful if the desired image is hosted by the OWF web server.  Otherwise a fully qualified URL should be used.  If type is being used to determine the image, the icon property is optional
771        * @param {String} cfg.items[*].text This property defines text to appear alongside the menu.  This property is only used if the xtype is ‘menu.’  ‘widgettool’ will not show text.
772        * @param {Object} cfg.items[*].menu menu configuration object
773        * @param {Object[]} cfg.items[*].menu.items an array of menu item configurations to add to the chrome.  See example for menu item configs
774        * @param {String} cfg.items[*].menu.items[*].itemId itemId is a unique id among all menu items that are added.  It is a required property.  It is used for identification and defines the internal Eventing channel which is used to execute the handler function.
775        * @param {String} cfg.items[*].menu.items[*].xtype xtype is used to specify the type of menu item to add.  This attribute should be omitted unless specifying a menuseparator. Setting this value to "menuseparator" adds a separator bar to a menu, used to divide logical groups of menu items. If specified, only xtype should be specified.  See example below for usage.
776        * @param {String} cfg.items[*].menu.items[*].icon This property defines the URL of the image to be used for the menu item.  If the URL is a relative path, it will be relative to the /owf context.  This is useful if the desired image is hosted by the OWF web server.  Otherwise a fully qualified URL should be used.  If type is being used to determine the image, the icon property is optional
777        * @param {String} cfg.items[*].menu.items[*].text This property defines text to appear for the menu item. 
778        * @param {Function} cfg.items[*].menu.items[*].handler The handler attribute defines a function to be executed when the menu item is clicked. This function is executed using Widget Eventing API from inside the widget.  The internal channel name used is the itemId attribute. This function's parameter list contains the standard parameters for an Eventing callback function.
779        * @param {Object} cfg.items[*].menu.items[*].menu sub-menu configuration object.  See example for sub-menu config.
780        * @example
781        *     //this.wc is an already instantiated WidgetChrome obj
782        *     this.wc.insertHeaderMenus({
783        *             pos: 0,
784        *             items: [{
785        *			 itemId:'insertedMenu',
786        *				icon: './themes/common/images/skin/exclamation.png',
787        *				text: 'Inserted Menu',
788        *				menu: {
789        *					items: [
790        *					    {
791        *					    	itemId:'insertedMenuItem1',
792        *					    	icon: './themes/common/images/skin/exclamation.png',
793        *					    	text: 'Inserted Menu Item 1',
794        *					    	handler: function(sender, data) {
795        *					    		alert('You clicked the Inserted Menu menu item.');
796        *					    	}
797        *					    },
798        *					    {
799        *					    	xtype: 'menuseparator'
800        *					    },
801        *					    {
802        *					    	itemId:'insertedMenuItem2',
803        *					    	icon: './themes/common/images/skin/exclamation.png',
804        *					    	text: 'Inserted Menu Item 2',
805        *					    	handler: function(sender, data) {
806        *					    		alert('You clicked the Inserted Menu menu item.');
807        *					    	}
808        *						},
809        *						'-', // another way to add a menu separator 
810        *						{
811        *					    	itemId:'insertedMenuItem3',
812        *					    	icon: './themes/common/images/skin/exclamation.png',
813        *					    	text: 'Inserted Menu Item 3',
814        *					    	handler: function(sender, data) {
815        *					    		alert('You clicked the Inserted Menu menu item.');
816        *					    	}
817        *					    }
818        *					]
819        *				}
820        *			}]
821        *     });
822        */
823       insertHeaderMenus : function(cfg) {
824         var data = {};
825         var items = null;
826 
827         if (!owfdojo.isArray(cfg.items)) {
828           items = [cfg.items];
829         }
830         else {
831           items = cfg.items;
832         }
833 
834         data.action = 'insertHeaderMenus';
835         data.type = 'menu';
836         if (cfg.pos != null) {
837           data.pos = cfg.pos;
838         }
839         data.items = items;
840         for (var i = 0; i < items.length; i++) {
841         	var item = items[i];
842         	if (item.menu) {
843 	        	for (var j = 0 ; j < item.menu.items.length ; j++) {
844 	        		var menuItem = item.menu.items[j];
845 	        		if (!menuItem.menu) {
846 	        			// Regular menu item
847 	                	scope.items[menuItem.itemId != null ? menuItem.itemId : menuItem.id] = menuItem;
848 	        		} else {
849 	        			// Sub-menu 
850 	        			for (var k = 0 ; k < menuItem.menu.items.length ; k++) {
851 	        				var subMenuItem = menuItem.menu.items[k];
852 	                    	scope.items[subMenuItem.itemId != null ? subMenuItem.itemId : subMenuItem.id] = subMenuItem;
853 	        			}
854 	        		}
855 	        	}
856         	} else {
857     			// Regular menu item
858             	scope.items[item.itemId != null ? item.itemId : item.id] = item;
859         	}
860         }
861 
862         var jsonString = gadgets.json.stringify(data);
863         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
864       },
865 
866       /**
867        * @description Removes existing menus on the Widget Chrome based on itemId.
868        * @param {Object} cfg config object see below for properties
869        * @param {Object[]} cfg.items an array of objects containing itemIds for the menus to remove from the chrome.
870        *   See example below for button configs
871        * @example
872        *     //this.wc is an already instantiated WidgetChrome obj
873        *     this.wc.removeHeaderMenus({
874        *             items: [{
875        *            	 itemId: 'regularMenu'
876        *             }]
877        *     });
878        */
879       removeHeaderMenus : function(cfg) {
880         var data = {};
881         var items = null;
882 
883         if (!owfdojo.isArray(cfg.items)) {
884           items = [cfg.items];
885         }
886         else {
887           items = cfg.items;
888         }
889 
890         data.action = 'removeHeaderMenus';
891         data.type = 'menu';
892         data.items = items;
893         for (var i = 0; i < items.length; i++) {
894           var id = items[i];
895           if (scope.items[id] != null) {
896             //delete saved data
897             delete scope.items[id];
898           }
899         }
900 
901         var jsonString = gadgets.json.stringify(data);
902         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
903       },
904       
905       /**
906        * @description Lists all menus that have been added to the widget chrome.
907        * @param {Object} cfg config object see below for properties
908        * @param {Function} cfg.callback The function which receives the results.
909        *
910        * @example
911        *    //this.wc is an already instantiated WidgetChrome obj
912        *    this.wc.listHeaderMenus({
913        *     callback: function(msg) {
914        *         //msg will always be a json string
915        *         var res = Ozone.util.parseJson(msg);
916        *         if (res.success) {
917        *             for (var i = 0; i < res.items.length; i++) {
918        *             	// do something with the menus
919        *             }
920        *         }
921        *     }
922        *   });
923        */
924       listHeaderMenus: function(cfg) {
925         var data = {
926           action: 'listHeaderMenus',
927           type: 'menu'
928         };
929         var jsonString = gadgets.json.stringify(data);
930         gadgets.rpc.call('..', scope.channelName, cfg.callback, scope.widgetEventingController.getWidgetId(), jsonString);
931       }
932 
933     };
934 
935     pub.init(config);
936     Ozone.chrome.WidgetChrome.instance = pub;
937   }
938   return Ozone.chrome.WidgetChrome.instance;
939 };
940 
941 /**
942  *  @description Retrieves Ozone.chrome.WidgetChrome Singleton instance.  To do so it requires a widgetEventingController
943  *  @param  {Object} config - config object with parameters
944  *  @param  {Ozone.eventing.Widget} config.widgetEventingController - widget eventing object which handles eventing for the widget
945  *  @example
946  *    this.wc = Ozone.chrome.WidgetChrome.getInstance({
947  *        widgetEventingController: this.widgetEventingController
948  *    });
949  *
950  * @throws {Error} throws an error with a message if widget initialization fails
951  */
952 Ozone.chrome.WidgetChrome.getInstance = function(cfg) {
953   if (Ozone.chrome.WidgetChrome.instance == null) {
954     Ozone.chrome.WidgetChrome.instance = new Ozone.chrome.WidgetChrome(cfg);
955   }
956   return Ozone.chrome.WidgetChrome.instance;
957 };
958 
959 
960