      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