ux-all-debug.js
上传用户:dawnssy
上传日期:2022-08-06
资源大小:9345k
文件大小:337k
源码类别:

JavaScript

开发平台:

JavaScript

  1. // phpMode == false (default):
  2. filter[0][data][type] list
  3. filter[0][data][value] value1
  4. filter[0][data][value] value2
  5. filter[0][field] prod 
  6. // phpMode == true:
  7. filter[0][data][type] list
  8. filter[0][data][value] value1, value2
  9. filter[0][field] prod 
  10.      * </code></pre>
  11.      * When GridFilters <code>@cfg encode = true</code>:
  12.      * <pre><code>
  13. // phpMode == false (default):
  14. filter : [{"type":"list","value":["small","medium"],"field":"size"}]
  15. // phpMode == true:
  16. filter : [{"type":"list","value":"small,medium","field":"size"}]
  17.      * </code></pre>
  18.      */
  19.     phpMode : false,
  20.     /**
  21.      * @cfg {Ext.data.Store} store
  22.      * The {@link Ext.data.Store} this list should use as its data source
  23.      * when the data source is <b>remote</b>. If the data for the list
  24.      * is local, use the <code>{@link #options}</code> config instead.
  25.      */
  26.     /**  
  27.      * @private
  28.      * Template method that is to initialize the filter and install required menu items.
  29.      * @param {Object} config
  30.      */
  31.     init : function (config) {
  32.         this.dt = new Ext.util.DelayedTask(this.fireUpdate, this);
  33.         // if a menu already existed, do clean up first
  34.         if (this.menu){
  35.             this.menu.destroy();
  36.         }
  37.         this.menu = new Ext.ux.menu.ListMenu(config);
  38.         this.menu.on('checkchange', this.onCheckChange, this);
  39.     },
  40.     
  41.     /**
  42.      * @private
  43.      * Template method that is to get and return the value of the filter.
  44.      * @return {String} The value of this filter
  45.      */
  46.     getValue : function () {
  47.         return this.menu.getSelected();
  48.     },
  49.     /**
  50.      * @private
  51.      * Template method that is to set the value of the filter.
  52.      * @param {Object} value The value to set the filter
  53.      */
  54.     setValue : function (value) {
  55.         this.menu.setSelected(value);
  56.         this.fireEvent('update', this);
  57.     },
  58.     /**
  59.      * @private
  60.      * Template method that is to return <tt>true</tt> if the filter
  61.      * has enough configuration information to be activated.
  62.      * @return {Boolean}
  63.      */
  64.     isActivatable : function () {
  65.         return this.getValue().length > 0;
  66.     },
  67.     
  68.     /**
  69.      * @private
  70.      * Template method that is to get and return serialized filter data for
  71.      * transmission to the server.
  72.      * @return {Object/Array} An object or collection of objects containing
  73.      * key value pairs representing the current configuration of the filter.
  74.      */
  75.     getSerialArgs : function () {
  76.         var args = {type: 'list', value: this.phpMode ? this.getValue().join(',') : this.getValue()};
  77.         return args;
  78.     },
  79.     /** @private */
  80.     onCheckChange : function(){
  81.         this.dt.delay(this.updateBuffer);
  82.     },
  83.     
  84.     
  85.     /**
  86.      * Template method that is to validate the provided Ext.data.Record
  87.      * against the filters configuration.
  88.      * @param {Ext.data.Record} record The record to validate
  89.      * @return {Boolean} true if the record is valid within the bounds
  90.      * of the filter, false otherwise.
  91.      */
  92.     validateRecord : function (record) {
  93.         return this.getValue().indexOf(record.get(this.dataIndex)) > -1;
  94.     }
  95. });/** 
  96.  * @class Ext.ux.grid.filter.NumericFilter
  97.  * @extends Ext.ux.grid.filter.Filter
  98.  * Filters using an Ext.ux.menu.RangeMenu.
  99.  * <p><b><u>Example Usage:</u></b></p>
  100.  * <pre><code>    
  101. var filters = new Ext.ux.grid.GridFilters({
  102.     ...
  103.     filters: [{
  104.         type: 'numeric',
  105.         dataIndex: 'price'
  106.     }]
  107. });
  108.  * </code></pre> 
  109.  */
  110. Ext.ux.grid.filter.NumericFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
  111.     /**
  112.      * @cfg {Object} fieldCls
  113.      * The Class to use to construct each field item within this menu
  114.      * Defaults to:<pre>
  115.      * fieldCls : Ext.form.NumberField
  116.      * </pre>
  117.      */
  118.     fieldCls : Ext.form.NumberField,
  119.     /**
  120.      * @cfg {Object} fieldCfg
  121.      * The default configuration options for any field item unless superseded
  122.      * by the <code>{@link #fields}</code> configuration.
  123.      * Defaults to:<pre>
  124.      * fieldCfg : {}
  125.      * </pre>
  126.      * Example usage:
  127.      * <pre><code>
  128. fieldCfg : {
  129.     width: 150,
  130. },
  131.      * </code></pre>
  132.      */
  133.     /**
  134.      * @cfg {Object} fields
  135.      * The field items may be configured individually
  136.      * Defaults to <tt>undefined</tt>.
  137.      * Example usage:
  138.      * <pre><code>
  139. fields : {
  140.     gt: { // override fieldCfg options
  141.         width: 200,
  142.         fieldCls: Ext.ux.form.CustomNumberField // to override default {@link #fieldCls}
  143.     }
  144. },
  145.      * </code></pre>
  146.      */
  147.     /**
  148.      * @cfg {Object} iconCls
  149.      * The iconCls to be applied to each comparator field item.
  150.      * Defaults to:<pre>
  151. iconCls : {
  152.     gt : 'ux-rangemenu-gt',
  153.     lt : 'ux-rangemenu-lt',
  154.     eq : 'ux-rangemenu-eq'
  155. }
  156.      * </pre>
  157.      */
  158.     iconCls : {
  159.         gt : 'ux-rangemenu-gt',
  160.         lt : 'ux-rangemenu-lt',
  161.         eq : 'ux-rangemenu-eq'
  162.     },
  163.     /**
  164.      * @cfg {Object} menuItemCfgs
  165.      * Default configuration options for each menu item
  166.      * Defaults to:<pre>
  167. menuItemCfgs : {
  168.     emptyText: 'Enter Filter Text...',
  169.     selectOnFocus: true,
  170.     width: 125
  171. }
  172.      * </pre>
  173.      */
  174.     menuItemCfgs : {
  175.         emptyText: 'Enter Filter Text...',
  176.         selectOnFocus: true,
  177.         width: 125
  178.     },
  179.     /**
  180.      * @cfg {Array} menuItems
  181.      * The items to be shown in this menu.  Items are added to the menu
  182.      * according to their position within this array. Defaults to:<pre>
  183.      * menuItems : ['lt','gt','-','eq']
  184.      * </pre>
  185.      */
  186.     menuItems : ['lt', 'gt', '-', 'eq'],
  187.     /**  
  188.      * @private
  189.      * Template method that is to initialize the filter and install required menu items.
  190.      */
  191.     init : function (config) {
  192.         // if a menu already existed, do clean up first
  193.         if (this.menu){
  194.             this.menu.destroy();
  195.         }        
  196.         this.menu = new Ext.ux.menu.RangeMenu(Ext.apply(config, {
  197.             // pass along filter configs to the menu
  198.             fieldCfg : this.fieldCfg || {},
  199.             fieldCls : this.fieldCls,
  200.             fields : this.fields || {},
  201.             iconCls: this.iconCls,
  202.             menuItemCfgs: this.menuItemCfgs,
  203.             menuItems: this.menuItems,
  204.             updateBuffer: this.updateBuffer
  205.         }));
  206.         // relay the event fired by the menu
  207.         this.menu.on('update', this.fireUpdate, this);
  208.     },
  209.     
  210.     /**
  211.      * @private
  212.      * Template method that is to get and return the value of the filter.
  213.      * @return {String} The value of this filter
  214.      */
  215.     getValue : function () {
  216.         return this.menu.getValue();
  217.     },
  218.     /**
  219.      * @private
  220.      * Template method that is to set the value of the filter.
  221.      * @param {Object} value The value to set the filter
  222.      */
  223.     setValue : function (value) {
  224.         this.menu.setValue(value);
  225.     },
  226.     /**
  227.      * @private
  228.      * Template method that is to return <tt>true</tt> if the filter
  229.      * has enough configuration information to be activated.
  230.      * @return {Boolean}
  231.      */
  232.     isActivatable : function () {
  233.         var values = this.getValue();
  234.         for (key in values) {
  235.             if (values[key] !== undefined) {
  236.                 return true;
  237.             }
  238.         }
  239.         return false;
  240.     },
  241.     
  242.     /**
  243.      * @private
  244.      * Template method that is to get and return serialized filter data for
  245.      * transmission to the server.
  246.      * @return {Object/Array} An object or collection of objects containing
  247.      * key value pairs representing the current configuration of the filter.
  248.      */
  249.     getSerialArgs : function () {
  250.         var key,
  251.             args = [],
  252.             values = this.menu.getValue();
  253.         for (key in values) {
  254.             args.push({
  255.                 type: 'numeric',
  256.                 comparison: key,
  257.                 value: values[key]
  258.             });
  259.         }
  260.         return args;
  261.     },
  262.     /**
  263.      * Template method that is to validate the provided Ext.data.Record
  264.      * against the filters configuration.
  265.      * @param {Ext.data.Record} record The record to validate
  266.      * @return {Boolean} true if the record is valid within the bounds
  267.      * of the filter, false otherwise.
  268.      */
  269.     validateRecord : function (record) {
  270.         var val = record.get(this.dataIndex),
  271.             values = this.getValue();
  272.         if (values.eq !== undefined && val != values.eq) {
  273.             return false;
  274.         }
  275.         if (values.lt !== undefined && val >= values.lt) {
  276.             return false;
  277.         }
  278.         if (values.gt !== undefined && val <= values.gt) {
  279.             return false;
  280.         }
  281.         return true;
  282.     }
  283. });/** 
  284.  * @class Ext.ux.grid.filter.StringFilter
  285.  * @extends Ext.ux.grid.filter.Filter
  286.  * Filter by a configurable Ext.form.TextField
  287.  * <p><b><u>Example Usage:</u></b></p>
  288.  * <pre><code>    
  289. var filters = new Ext.ux.grid.GridFilters({
  290.     ...
  291.     filters: [{
  292.         // required configs
  293.         type: 'string',
  294.         dataIndex: 'name',
  295.         
  296.         // optional configs
  297.         value: 'foo',
  298.         active: true, // default is false
  299.         iconCls: 'ux-gridfilter-text-icon' // default
  300.         // any Ext.form.TextField configs accepted
  301.     }]
  302. });
  303.  * </code></pre>
  304.  */
  305. Ext.ux.grid.filter.StringFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
  306.     /**
  307.      * @cfg {String} iconCls
  308.      * The iconCls to be applied to the menu item.
  309.      * Defaults to <tt>'ux-gridfilter-text-icon'</tt>.
  310.      */
  311.     iconCls : 'ux-gridfilter-text-icon',
  312.     emptyText: 'Enter Filter Text...',
  313.     selectOnFocus: true,
  314.     width: 125,
  315.     
  316.     /**  
  317.      * @private
  318.      * Template method that is to initialize the filter and install required menu items.
  319.      */
  320.     init : function (config) {
  321.         Ext.applyIf(config, {
  322.             enableKeyEvents: true,
  323.             iconCls: this.iconCls,
  324.             listeners: {
  325.                 scope: this,
  326.                 keyup: this.onInputKeyUp
  327.             }
  328.         });
  329.         this.inputItem = new Ext.form.TextField(config); 
  330.         this.menu.add(this.inputItem);
  331.         this.updateTask = new Ext.util.DelayedTask(this.fireUpdate, this);
  332.     },
  333.     
  334.     /**
  335.      * @private
  336.      * Template method that is to get and return the value of the filter.
  337.      * @return {String} The value of this filter
  338.      */
  339.     getValue : function () {
  340.         return this.inputItem.getValue();
  341.     },
  342.     
  343.     /**
  344.      * @private
  345.      * Template method that is to set the value of the filter.
  346.      * @param {Object} value The value to set the filter
  347.      */
  348.     setValue : function (value) {
  349.         this.inputItem.setValue(value);
  350.         this.fireEvent('update', this);
  351.     },
  352.     /**
  353.      * @private
  354.      * Template method that is to return <tt>true</tt> if the filter
  355.      * has enough configuration information to be activated.
  356.      * @return {Boolean}
  357.      */
  358.     isActivatable : function () {
  359.         return this.inputItem.getValue().length > 0;
  360.     },
  361.     /**
  362.      * @private
  363.      * Template method that is to get and return serialized filter data for
  364.      * transmission to the server.
  365.      * @return {Object/Array} An object or collection of objects containing
  366.      * key value pairs representing the current configuration of the filter.
  367.      */
  368.     getSerialArgs : function () {
  369.         return {type: 'string', value: this.getValue()};
  370.     },
  371.     /**
  372.      * Template method that is to validate the provided Ext.data.Record
  373.      * against the filters configuration.
  374.      * @param {Ext.data.Record} record The record to validate
  375.      * @return {Boolean} true if the record is valid within the bounds
  376.      * of the filter, false otherwise.
  377.      */
  378.     validateRecord : function (record) {
  379.         var val = record.get(this.dataIndex);
  380.         if(typeof val != 'string') {
  381.             return (this.getValue().length === 0);
  382.         }
  383.         return val.toLowerCase().indexOf(this.getValue().toLowerCase()) > -1;
  384.     },
  385.     
  386.     /**  
  387.      * @private
  388.      * Handler method called when there is a keyup event on this.inputItem
  389.      */
  390.     onInputKeyUp : function (field, e) {
  391.         var k = e.getKey();
  392.         if (k == e.RETURN && field.isValid()) {
  393.             e.stopEvent();
  394.             this.menu.hide(true);
  395.             return;
  396.         }
  397.         // restart the timer
  398.         this.updateTask.delay(this.updateBuffer);
  399.     }
  400. });
  401. Ext.namespace('Ext.ux.menu');
  402. /** 
  403.  * @class Ext.ux.menu.ListMenu
  404.  * @extends Ext.menu.Menu
  405.  * This is a supporting class for {@link Ext.ux.grid.filter.ListFilter}.
  406.  * Although not listed as configuration options for this class, this class
  407.  * also accepts all configuration options from {@link Ext.ux.grid.filter.ListFilter}.
  408.  */
  409. Ext.ux.menu.ListMenu = Ext.extend(Ext.menu.Menu, {
  410.     /**
  411.      * @cfg {String} labelField
  412.      * Defaults to 'text'.
  413.      */
  414.     labelField :  'text',
  415.     /**
  416.      * @cfg {String} paramPrefix
  417.      * Defaults to 'Loading...'.
  418.      */
  419.     loadingText : 'Loading...',
  420.     /**
  421.      * @cfg {Boolean} loadOnShow
  422.      * Defaults to true.
  423.      */
  424.     loadOnShow : true,
  425.     /**
  426.      * @cfg {Boolean} single
  427.      * Specify true to group all items in this list into a single-select
  428.      * radio button group. Defaults to false.
  429.      */
  430.     single : false,
  431.     constructor : function (cfg) {
  432.         this.selected = [];
  433.         this.addEvents(
  434.             /**
  435.              * @event checkchange
  436.              * Fires when there is a change in checked items from this list
  437.              * @param {Object} item Ext.menu.CheckItem
  438.              * @param {Object} checked The checked value that was set
  439.              */
  440.             'checkchange'
  441.         );
  442.       
  443.         Ext.ux.menu.ListMenu.superclass.constructor.call(this, cfg = cfg || {});
  444.     
  445.         if(!cfg.store && cfg.options){
  446.             var options = [];
  447.             for(var i=0, len=cfg.options.length; i<len; i++){
  448.                 var value = cfg.options[i];
  449.                 switch(Ext.type(value)){
  450.                     case 'array':  options.push(value); break;
  451.                     case 'object': options.push([value.id, value[this.labelField]]); break;
  452.                     case 'string': options.push([value, value]); break;
  453.                 }
  454.             }
  455.             
  456.             this.store = new Ext.data.Store({
  457.                 reader: new Ext.data.ArrayReader({id: 0}, ['id', this.labelField]),
  458.                 data:   options,
  459.                 listeners: {
  460.                     'load': this.onLoad,
  461.                     scope:  this
  462.                 }
  463.             });
  464.             this.loaded = true;
  465.         } else {
  466.             this.add({text: this.loadingText, iconCls: 'loading-indicator'});
  467.             this.store.on('load', this.onLoad, this);
  468.         }
  469.     },
  470.     destroy : function () {
  471.         if (this.store) {
  472.             this.store.destroy();    
  473.         }
  474.         Ext.ux.menu.ListMenu.superclass.destroy.call(this);
  475.     },
  476.     /**
  477.      * Lists will initially show a 'loading' item while the data is retrieved from the store.
  478.      * In some cases the loaded data will result in a list that goes off the screen to the
  479.      * right (as placement calculations were done with the loading item). This adapter will
  480.      * allow show to be called with no arguments to show with the previous arguments and
  481.      * thus recalculate the width and potentially hang the menu from the left.
  482.      */
  483.     show : function () {
  484.         var lastArgs = null;
  485.         return function(){
  486.             if(arguments.length === 0){
  487.                 Ext.ux.menu.ListMenu.superclass.show.apply(this, lastArgs);
  488.             } else {
  489.                 lastArgs = arguments;
  490.                 if (this.loadOnShow && !this.loaded) {
  491.                     this.store.load();
  492.                 }
  493.                 Ext.ux.menu.ListMenu.superclass.show.apply(this, arguments);
  494.             }
  495.         };
  496.     }(),
  497.     
  498.     /** @private */
  499.     onLoad : function (store, records) {
  500.         var visible = this.isVisible();
  501.         this.hide(false);
  502.         
  503.         this.removeAll(true);
  504.         
  505.         var gid = this.single ? Ext.id() : null;
  506.         for(var i=0, len=records.length; i<len; i++){
  507.             var item = new Ext.menu.CheckItem({
  508.                 text:    records[i].get(this.labelField), 
  509.                 group:   gid,
  510.                 checked: this.selected.indexOf(records[i].id) > -1,
  511.                 hideOnClick: false});
  512.             
  513.             item.itemId = records[i].id;
  514.             item.on('checkchange', this.checkChange, this);
  515.                         
  516.             this.add(item);
  517.         }
  518.         
  519.         this.loaded = true;
  520.         
  521.         if (visible) {
  522.             this.show();
  523.         }
  524.         this.fireEvent('load', this, records);
  525.     },
  526.     /**
  527.      * Get the selected items.
  528.      * @return {Array} selected
  529.      */
  530.     getSelected : function () {
  531.         return this.selected;
  532.     },
  533.     
  534.     /** @private */
  535.     setSelected : function (value) {
  536.         value = this.selected = [].concat(value);
  537.         if (this.loaded) {
  538.             this.items.each(function(item){
  539.                 item.setChecked(false, true);
  540.                 for (var i = 0, len = value.length; i < len; i++) {
  541.                     if (item.itemId == value[i]) {
  542.                         item.setChecked(true, true);
  543.                     }
  544.                 }
  545.             }, this);
  546.         }
  547.     },
  548.     
  549.     /**
  550.      * Handler for the 'checkchange' event from an check item in this menu
  551.      * @param {Object} item Ext.menu.CheckItem
  552.      * @param {Object} checked The checked value that was set
  553.      */
  554.     checkChange : function (item, checked) {
  555.         var value = [];
  556.         this.items.each(function(item){
  557.             if (item.checked) {
  558.                 value.push(item.itemId);
  559.             }
  560.         },this);
  561.         this.selected = value;
  562.         
  563.         this.fireEvent('checkchange', item, checked);
  564.     }    
  565. });Ext.ns('Ext.ux.menu');
  566. /** 
  567.  * @class Ext.ux.menu.RangeMenu
  568.  * @extends Ext.menu.Menu
  569.  * Custom implementation of Ext.menu.Menu that has preconfigured
  570.  * items for gt, lt, eq.
  571.  * <p><b><u>Example Usage:</u></b></p>
  572.  * <pre><code>    
  573.  * </code></pre> 
  574.  */
  575. Ext.ux.menu.RangeMenu = Ext.extend(Ext.menu.Menu, {
  576.     constructor : function (config) {
  577.         Ext.ux.menu.RangeMenu.superclass.constructor.call(this, config);
  578.         this.addEvents(
  579.             /**
  580.              * @event update
  581.              * Fires when a filter configuration has changed
  582.              * @param {Ext.ux.grid.filter.Filter} this The filter object.
  583.              */
  584.             'update'
  585.         );
  586.       
  587.         this.updateTask = new Ext.util.DelayedTask(this.fireUpdate, this);
  588.     
  589.         var i, len, item, cfg, Cls;
  590.         for (i = 0, len = this.menuItems.length; i < len; i++) {
  591.             item = this.menuItems[i];
  592.             if (item !== '-') {
  593.                 // defaults
  594.                 cfg = {
  595.                     itemId: 'range-' + item,
  596.                     enableKeyEvents: true,
  597.                     iconCls: this.iconCls[item] || 'no-icon',
  598.                     listeners: {
  599.                         scope: this,
  600.                         keyup: this.onInputKeyUp
  601.                     }
  602.                 };
  603.                 Ext.apply(
  604.                     cfg,
  605.                     // custom configs
  606.                     Ext.applyIf(this.fields[item] || {}, this.fieldCfg[item]),
  607.                     // configurable defaults
  608.                     this.menuItemCfgs
  609.                 );
  610.                 Cls = cfg.fieldCls || this.fieldCls;
  611.                 item = this.fields[item] = new Cls(cfg);
  612.             }
  613.             this.add(item);
  614.         }
  615.     },
  616.     /**
  617.      * @private
  618.      * called by this.updateTask
  619.      */
  620.     fireUpdate : function () {
  621.         this.fireEvent('update', this);
  622.     },
  623.     
  624.     /**
  625.      * Get and return the value of the filter.
  626.      * @return {String} The value of this filter
  627.      */
  628.     getValue : function () {
  629.         var result = {}, key, field;
  630.         for (key in this.fields) {
  631.             field = this.fields[key];
  632.             if (field.isValid() && String(field.getValue()).length > 0) {
  633.                 result[key] = field.getValue();
  634.             }
  635.         }
  636.         return result;
  637.     },
  638.   
  639.     /**
  640.      * Set the value of this menu and fires the 'update' event.
  641.      * @param {Object} data The data to assign to this menu
  642.      */
  643.     setValue : function (data) {
  644.         var key;
  645.         for (key in this.fields) {
  646.             this.fields[key].setValue(data[key] !== undefined ? data[key] : '');
  647.         }
  648.         this.fireEvent('update', this);
  649.     },
  650.     /**  
  651.      * @private
  652.      * Handler method called when there is a keyup event on an input
  653.      * item of this menu.
  654.      */
  655.     onInputKeyUp : function (field, e) {
  656.         var k = e.getKey();
  657.         if (k == e.RETURN && field.isValid()) {
  658.             e.stopEvent();
  659.             this.hide(true);
  660.             return;
  661.         }
  662.         
  663.         if (field == this.fields.eq) {
  664.             if (this.fields.gt) {
  665.                 this.fields.gt.setValue(null);
  666.             }
  667.             if (this.fields.lt) {
  668.                 this.fields.lt.setValue(null);
  669.             }
  670.         }
  671.         else {
  672.             this.fields.eq.setValue(null);
  673.         }
  674.         
  675.         // restart the timer
  676.         this.updateTask.delay(this.updateBuffer);
  677.     }
  678. });
  679. Ext.ns('Ext.ux.grid');
  680. /**
  681.  * @class Ext.ux.grid.GroupSummary
  682.  * @extends Ext.util.Observable
  683.  * A GridPanel plugin that enables dynamic column calculations and a dynamically
  684.  * updated grouped summary row.
  685.  */
  686. Ext.ux.grid.GroupSummary = Ext.extend(Ext.util.Observable, {
  687.     /**
  688.      * @cfg {Function} summaryRenderer Renderer example:<pre><code>
  689. summaryRenderer: function(v, params, data){
  690.     return ((v === 0 || v > 1) ? '(' + v +' Tasks)' : '(1 Task)');
  691. },
  692.      * </code></pre>
  693.      */
  694.     /**
  695.      * @cfg {String} summaryType (Optional) The type of
  696.      * calculation to be used for the column.  For options available see
  697.      * {@link #Calculations}.
  698.      */
  699.     constructor : function(config){
  700.         Ext.apply(this, config);
  701.         Ext.ux.grid.GroupSummary.superclass.constructor.call(this);
  702.     },
  703.     init : function(grid){
  704.         this.grid = grid;
  705.         var v = this.view = grid.getView();
  706.         v.doGroupEnd = this.doGroupEnd.createDelegate(this);
  707.         v.afterMethod('onColumnWidthUpdated', this.doWidth, this);
  708.         v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);
  709.         v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);
  710.         v.afterMethod('onUpdate', this.doUpdate, this);
  711.         v.afterMethod('onRemove', this.doRemove, this);
  712.         if(!this.rowTpl){
  713.             this.rowTpl = new Ext.Template(
  714.                 '<div class="x-grid3-summary-row" style="{tstyle}">',
  715.                 '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
  716.                     '<tbody><tr>{cells}</tr></tbody>',
  717.                 '</table></div>'
  718.             );
  719.             this.rowTpl.disableFormats = true;
  720.         }
  721.         this.rowTpl.compile();
  722.         if(!this.cellTpl){
  723.             this.cellTpl = new Ext.Template(
  724.                 '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
  725.                 '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on">{value}</div>',
  726.                 "</td>"
  727.             );
  728.             this.cellTpl.disableFormats = true;
  729.         }
  730.         this.cellTpl.compile();
  731.     },
  732.     /**
  733.      * Toggle the display of the summary row on/off
  734.      * @param {Boolean} visible <tt>true</tt> to show the summary, <tt>false</tt> to hide the summary.
  735.      */
  736.     toggleSummaries : function(visible){
  737.         var el = this.grid.getGridEl();
  738.         if(el){
  739.             if(visible === undefined){
  740.                 visible = el.hasClass('x-grid-hide-summary');
  741.             }
  742.             el[visible ? 'removeClass' : 'addClass']('x-grid-hide-summary');
  743.         }
  744.     },
  745.     renderSummary : function(o, cs){
  746.         cs = cs || this.view.getColumnData();
  747.         var cfg = this.grid.getColumnModel().config,
  748.             buf = [], c, p = {}, cf, last = cs.length-1;
  749.         for(var i = 0, len = cs.length; i < len; i++){
  750.             c = cs[i];
  751.             cf = cfg[i];
  752.             p.id = c.id;
  753.             p.style = c.style;
  754.             p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
  755.             if(cf.summaryType || cf.summaryRenderer){
  756.                 p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o);
  757.             }else{
  758.                 p.value = '';
  759.             }
  760.             if(p.value == undefined || p.value === "") p.value = "&#160;";
  761.             buf[buf.length] = this.cellTpl.apply(p);
  762.         }
  763.         return this.rowTpl.apply({
  764.             tstyle: 'width:'+this.view.getTotalWidth()+';',
  765.             cells: buf.join('')
  766.         });
  767.     },
  768.     /**
  769.      * @private
  770.      * @param {Object} rs
  771.      * @param {Object} cs
  772.      */
  773.     calculate : function(rs, cs){
  774.         var data = {}, r, c, cfg = this.grid.getColumnModel().config, cf;
  775.         for(var j = 0, jlen = rs.length; j < jlen; j++){
  776.             r = rs[j];
  777.             for(var i = 0, len = cs.length; i < len; i++){
  778.                 c = cs[i];
  779.                 cf = cfg[i];
  780.                 if(cf.summaryType){
  781.                     data[c.name] = Ext.ux.grid.GroupSummary.Calculations[cf.summaryType](data[c.name] || 0, r, c.name, data);
  782.                 }
  783.             }
  784.         }
  785.         return data;
  786.     },
  787.     doGroupEnd : function(buf, g, cs, ds, colCount){
  788.         var data = this.calculate(g.rs, cs);
  789.         buf.push('</div>', this.renderSummary({data: data}, cs), '</div>');
  790.     },
  791.     doWidth : function(col, w, tw){
  792.         var gs = this.view.getGroups(), s;
  793.         for(var i = 0, len = gs.length; i < len; i++){
  794.             s = gs[i].childNodes[2];
  795.             s.style.width = tw;
  796.             s.firstChild.style.width = tw;
  797.             s.firstChild.rows[0].childNodes[col].style.width = w;
  798.         }
  799.     },
  800.     doAllWidths : function(ws, tw){
  801.         var gs = this.view.getGroups(), s, cells, wlen = ws.length;
  802.         for(var i = 0, len = gs.length; i < len; i++){
  803.             s = gs[i].childNodes[2];
  804.             s.style.width = tw;
  805.             s.firstChild.style.width = tw;
  806.             cells = s.firstChild.rows[0].childNodes;
  807.             for(var j = 0; j < wlen; j++){
  808.                 cells[j].style.width = ws[j];
  809.             }
  810.         }
  811.     },
  812.     doHidden : function(col, hidden, tw){
  813.         var gs = this.view.getGroups(), s, display = hidden ? 'none' : '';
  814.         for(var i = 0, len = gs.length; i < len; i++){
  815.             s = gs[i].childNodes[2];
  816.             s.style.width = tw;
  817.             s.firstChild.style.width = tw;
  818.             s.firstChild.rows[0].childNodes[col].style.display = display;
  819.         }
  820.     },
  821.     // Note: requires that all (or the first) record in the
  822.     // group share the same group value. Returns false if the group
  823.     // could not be found.
  824.     refreshSummary : function(groupValue){
  825.         return this.refreshSummaryById(this.view.getGroupId(groupValue));
  826.     },
  827.     getSummaryNode : function(gid){
  828.         var g = Ext.fly(gid, '_gsummary');
  829.         if(g){
  830.             return g.down('.x-grid3-summary-row', true);
  831.         }
  832.         return null;
  833.     },
  834.     refreshSummaryById : function(gid){
  835.         var g = Ext.getDom(gid);
  836.         if(!g){
  837.             return false;
  838.         }
  839.         var rs = [];
  840.         this.grid.getStore().each(function(r){
  841.             if(r._groupId == gid){
  842.                 rs[rs.length] = r;
  843.             }
  844.         });
  845.         var cs = this.view.getColumnData(),
  846.             data = this.calculate(rs, cs),
  847.             markup = this.renderSummary({data: data}, cs),
  848.             existing = this.getSummaryNode(gid);
  849.             
  850.         if(existing){
  851.             g.removeChild(existing);
  852.         }
  853.         Ext.DomHelper.append(g, markup);
  854.         return true;
  855.     },
  856.     doUpdate : function(ds, record){
  857.         this.refreshSummaryById(record._groupId);
  858.     },
  859.     doRemove : function(ds, record, index, isUpdate){
  860.         if(!isUpdate){
  861.             this.refreshSummaryById(record._groupId);
  862.         }
  863.     },
  864.     /**
  865.      * Show a message in the summary row.
  866.      * <pre><code>
  867. grid.on('afteredit', function(){
  868.     var groupValue = 'Ext Forms: Field Anchoring';
  869.     summary.showSummaryMsg(groupValue, 'Updating Summary...');
  870. });
  871.      * </code></pre>
  872.      * @param {String} groupValue
  873.      * @param {String} msg Text to use as innerHTML for the summary row.
  874.      */
  875.     showSummaryMsg : function(groupValue, msg){
  876.         var gid = this.view.getGroupId(groupValue),
  877.              node = this.getSummaryNode(gid);
  878.         if(node){
  879.             node.innerHTML = '<div class="x-grid3-summary-msg">' + msg + '</div>';
  880.         }
  881.     }
  882. });
  883. //backwards compat
  884. Ext.grid.GroupSummary = Ext.ux.grid.GroupSummary;
  885. /**
  886.  * Calculation types for summary row:</p><div class="mdetail-params"><ul>
  887.  * <li><b><tt>sum</tt></b> : <div class="sub-desc"></div></li>
  888.  * <li><b><tt>count</tt></b> : <div class="sub-desc"></div></li>
  889.  * <li><b><tt>max</tt></b> : <div class="sub-desc"></div></li>
  890.  * <li><b><tt>min</tt></b> : <div class="sub-desc"></div></li>
  891.  * <li><b><tt>average</tt></b> : <div class="sub-desc"></div></li>
  892.  * </ul></div>
  893.  * <p>Custom calculations may be implemented.  An example of
  894.  * custom <code>summaryType=totalCost</code>:</p><pre><code>
  895. // define a custom summary function
  896. Ext.ux.grid.GroupSummary.Calculations['totalCost'] = function(v, record, field){
  897.     return v + (record.data.estimate * record.data.rate);
  898. };
  899.  * </code></pre>
  900.  * @property Calculations
  901.  */
  902. Ext.ux.grid.GroupSummary.Calculations = {
  903.     'sum' : function(v, record, field){
  904.         return v + (record.data[field]||0);
  905.     },
  906.     'count' : function(v, record, field, data){
  907.         return data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
  908.     },
  909.     'max' : function(v, record, field, data){
  910.         var v = record.data[field];
  911.         var max = data[field+'max'] === undefined ? (data[field+'max'] = v) : data[field+'max'];
  912.         return v > max ? (data[field+'max'] = v) : max;
  913.     },
  914.     'min' : function(v, record, field, data){
  915.         var v = record.data[field];
  916.         var min = data[field+'min'] === undefined ? (data[field+'min'] = v) : data[field+'min'];
  917.         return v < min ? (data[field+'min'] = v) : min;
  918.     },
  919.     'average' : function(v, record, field, data){
  920.         var c = data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
  921.         var t = (data[field+'total'] = ((data[field+'total']||0) + (record.data[field]||0)));
  922.         return t === 0 ? 0 : t / c;
  923.     }
  924. };
  925. Ext.grid.GroupSummary.Calculations = Ext.ux.grid.GroupSummary.Calculations;
  926. /**
  927.  * @class Ext.ux.grid.HybridSummary
  928.  * @extends Ext.ux.grid.GroupSummary
  929.  * Adds capability to specify the summary data for the group via json as illustrated here:
  930.  * <pre><code>
  931. {
  932.     data: [
  933.         {
  934.             projectId: 100,     project: 'House',
  935.             taskId:    112, description: 'Paint',
  936.             estimate:    6,        rate:     150,
  937.             due:'06/24/2007'
  938.         },
  939.         ...
  940.     ],
  941.     summaryData: {
  942.         'House': {
  943.             description: 14, estimate: 9,
  944.                    rate: 99, due: new Date(2009, 6, 29),
  945.                    cost: 999
  946.         }
  947.     }
  948. }
  949.  * </code></pre>
  950.  *
  951.  */
  952. Ext.ux.grid.HybridSummary = Ext.extend(Ext.ux.grid.GroupSummary, {
  953.     /**
  954.      * @private
  955.      * @param {Object} rs
  956.      * @param {Object} cs
  957.      */
  958.     calculate : function(rs, cs){
  959.         var gcol = this.view.getGroupField(),
  960.             gvalue = rs[0].data[gcol],
  961.             gdata = this.getSummaryData(gvalue);
  962.         return gdata || Ext.ux.grid.HybridSummary.superclass.calculate.call(this, rs, cs);
  963.     },
  964.     /**
  965.      * <pre><code>
  966. grid.on('afteredit', function(){
  967.     var groupValue = 'Ext Forms: Field Anchoring';
  968.     summary.showSummaryMsg(groupValue, 'Updating Summary...');
  969.     setTimeout(function(){ // simulate server call
  970.         // HybridSummary class implements updateSummaryData
  971.         summary.updateSummaryData(groupValue,
  972.             // create data object based on configured dataIndex
  973.             {description: 22, estimate: 888, rate: 888, due: new Date(), cost: 8});
  974.     }, 2000);
  975. });
  976.      * </code></pre>
  977.      * @param {String} groupValue
  978.      * @param {Object} data data object
  979.      * @param {Boolean} skipRefresh (Optional) Defaults to false
  980.      */
  981.     updateSummaryData : function(groupValue, data, skipRefresh){
  982.         var json = this.grid.getStore().reader.jsonData;
  983.         if(!json.summaryData){
  984.             json.summaryData = {};
  985.         }
  986.         json.summaryData[groupValue] = data;
  987.         if(!skipRefresh){
  988.             this.refreshSummary(groupValue);
  989.         }
  990.     },
  991.     /**
  992.      * Returns the summaryData for the specified groupValue or null.
  993.      * @param {String} groupValue
  994.      * @return {Object} summaryData
  995.      */
  996.     getSummaryData : function(groupValue){
  997.         var json = this.grid.getStore().reader.jsonData;
  998.         if(json && json.summaryData){
  999.             return json.summaryData[groupValue];
  1000.         }
  1001.         return null;
  1002.     }
  1003. });
  1004. //backwards compat
  1005. Ext.grid.HybridSummary = Ext.ux.grid.HybridSummary;
  1006. Ext.ux.GroupTab = Ext.extend(Ext.Container, {
  1007.     mainItem: 0,
  1008.     
  1009.     expanded: true,
  1010.     
  1011.     deferredRender: true,
  1012.     
  1013.     activeTab: null,
  1014.     
  1015.     idDelimiter: '__',
  1016.     
  1017.     headerAsText: false,
  1018.     
  1019.     frame: false,
  1020.     
  1021.     hideBorders: true,
  1022.     
  1023.     initComponent: function(config){
  1024.         Ext.apply(this, config);
  1025.         this.frame = false;
  1026.         
  1027.         Ext.ux.GroupTab.superclass.initComponent.call(this);
  1028.         
  1029.         this.addEvents('activate', 'deactivate', 'changemainitem', 'beforetabchange', 'tabchange');
  1030.         
  1031.         this.setLayout(new Ext.layout.CardLayout({
  1032.             deferredRender: this.deferredRender
  1033.         }));
  1034.         
  1035.         if (!this.stack) {
  1036.             this.stack = Ext.TabPanel.AccessStack();
  1037.         }
  1038.         
  1039.         this.initItems();
  1040.         
  1041.         this.on('beforerender', function(){
  1042.             this.groupEl = this.ownerCt.getGroupEl(this);
  1043.         }, this);
  1044.         
  1045.         this.on('add', this.onAdd, this, {
  1046.             target: this
  1047.         });
  1048.         this.on('remove', this.onRemove, this, {
  1049.             target: this
  1050.         });
  1051.         
  1052.         if (this.mainItem !== undefined) {
  1053.             var item = (typeof this.mainItem == 'object') ? this.mainItem : this.items.get(this.mainItem);
  1054.             delete this.mainItem;
  1055.             this.setMainItem(item);
  1056.         }
  1057.     },
  1058.     
  1059.     /**
  1060.      * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
  1061.      * can return false to cancel the tab change.
  1062.      * @param {String/Panel} tab The id or tab Panel to activate
  1063.      */
  1064.     setActiveTab : function(item){
  1065.         item = this.getComponent(item);
  1066.         if(!item){
  1067.             return false;
  1068.         }
  1069.         if(!this.rendered){
  1070.             this.activeTab = item;
  1071.             return true;
  1072.         }
  1073.         if(this.activeTab != item && this.fireEvent('beforetabchange', this, item, this.activeTab) !== false){
  1074.             if(this.activeTab && this.activeTab != this.mainItem){
  1075.                 var oldEl = this.getTabEl(this.activeTab);
  1076.                 if(oldEl){
  1077.                     Ext.fly(oldEl).removeClass('x-grouptabs-strip-active');
  1078.                 }
  1079.             }
  1080.             var el = this.getTabEl(item);
  1081.             Ext.fly(el).addClass('x-grouptabs-strip-active');
  1082.             this.activeTab = item;
  1083.             this.stack.add(item);
  1084.             this.layout.setActiveItem(item);
  1085.             if(this.layoutOnTabChange && item.doLayout){
  1086.                 item.doLayout();
  1087.             }
  1088.             if(this.scrolling){
  1089.                 this.scrollToTab(item, this.animScroll);
  1090.             }
  1091.             this.fireEvent('tabchange', this, item);
  1092.             return true;
  1093.         }
  1094.         return false;
  1095.     },
  1096.     
  1097.     getTabEl: function(item){
  1098.         if (item == this.mainItem) {
  1099.             return this.groupEl;
  1100.         }
  1101.         return Ext.TabPanel.prototype.getTabEl.call(this, item);
  1102.     },
  1103.     
  1104.     onRender: function(ct, position){
  1105.         Ext.ux.GroupTab.superclass.onRender.call(this, ct, position);
  1106.         
  1107.         this.strip = Ext.fly(this.groupEl).createChild({
  1108.             tag: 'ul',
  1109.             cls: 'x-grouptabs-sub'
  1110.         });
  1111.         this.tooltip = new Ext.ToolTip({
  1112.            target: this.groupEl,
  1113.            delegate: 'a.x-grouptabs-text',
  1114.            trackMouse: true,
  1115.            renderTo: document.body,
  1116.            listeners: {
  1117.                beforeshow: function(tip) {
  1118.                    var item = (tip.triggerElement.parentNode === this.mainItem.tabEl)
  1119.                        ? this.mainItem
  1120.                        : this.findById(tip.triggerElement.parentNode.id.split(this.idDelimiter)[1]);
  1121.                    if(!item.tabTip) {
  1122.                        return false;
  1123.                    }
  1124.                    tip.body.dom.innerHTML = item.tabTip;
  1125.                },
  1126.                scope: this
  1127.            }
  1128.         });
  1129.                 
  1130.         if (!this.itemTpl) {
  1131.             var tt = new Ext.Template('<li class="{cls}" id="{id}">', '<a onclick="return false;" class="x-grouptabs-text {iconCls}">{text}</a>', '</li>');
  1132.             tt.disableFormats = true;
  1133.             tt.compile();
  1134.             Ext.ux.GroupTab.prototype.itemTpl = tt;
  1135.         }
  1136.         
  1137.         this.items.each(this.initTab, this);
  1138.     },
  1139.     
  1140.     afterRender: function(){
  1141.         Ext.ux.GroupTab.superclass.afterRender.call(this);
  1142.         
  1143.         if (this.activeTab !== undefined) {
  1144.             var item = (typeof this.activeTab == 'object') ? this.activeTab : this.items.get(this.activeTab);
  1145.             delete this.activeTab;
  1146.             this.setActiveTab(item);
  1147.         }
  1148.     },
  1149.     
  1150.     // private
  1151.     initTab: function(item, index){
  1152.         var before = this.strip.dom.childNodes[index];
  1153.         var p = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);
  1154.         
  1155.         if (item === this.mainItem) {
  1156.             item.tabEl = this.groupEl;
  1157.             p.cls += ' x-grouptabs-main-item';
  1158.         }
  1159.         
  1160.         var el = before ? this.itemTpl.insertBefore(before, p) : this.itemTpl.append(this.strip, p);
  1161.         
  1162.         item.tabEl = item.tabEl || el;
  1163.                 
  1164.         item.on('disable', this.onItemDisabled, this);
  1165.         item.on('enable', this.onItemEnabled, this);
  1166.         item.on('titlechange', this.onItemTitleChanged, this);
  1167.         item.on('iconchange', this.onItemIconChanged, this);
  1168.         item.on('beforeshow', this.onBeforeShowItem, this);
  1169.     },
  1170.     
  1171.     setMainItem: function(item){
  1172.         item = this.getComponent(item);
  1173.         if (!item || this.fireEvent('changemainitem', this, item, this.mainItem) === false) {
  1174.             return;
  1175.         }
  1176.         
  1177.         this.mainItem = item;
  1178.     },
  1179.     
  1180.     getMainItem: function(){
  1181.         return this.mainItem || null;
  1182.     },
  1183.     
  1184.     // private
  1185.     onBeforeShowItem: function(item){
  1186.         if (item != this.activeTab) {
  1187.             this.setActiveTab(item);
  1188.             return false;
  1189.         }
  1190.     },
  1191.     
  1192.     // private
  1193.     onAdd: function(gt, item, index){
  1194.         if (this.rendered) {
  1195.             this.initTab.call(this, item, index);
  1196.         }
  1197.     },
  1198.     
  1199.     // private
  1200.     onRemove: function(tp, item){
  1201.         Ext.destroy(Ext.get(this.getTabEl(item)));
  1202.         this.stack.remove(item);
  1203.         item.un('disable', this.onItemDisabled, this);
  1204.         item.un('enable', this.onItemEnabled, this);
  1205.         item.un('titlechange', this.onItemTitleChanged, this);
  1206.         item.un('iconchange', this.onItemIconChanged, this);
  1207.         item.un('beforeshow', this.onBeforeShowItem, this);
  1208.         if (item == this.activeTab) {
  1209.             var next = this.stack.next();
  1210.             if (next) {
  1211.                 this.setActiveTab(next);
  1212.             }
  1213.             else if (this.items.getCount() > 0) {
  1214.                 this.setActiveTab(0);
  1215.             }
  1216.             else {
  1217.                 this.activeTab = null;
  1218.             }
  1219.         }
  1220.     },
  1221.     
  1222.     // private
  1223.     onBeforeAdd: function(item){
  1224.         var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
  1225.         if (existing) {
  1226.             this.setActiveTab(item);
  1227.             return false;
  1228.         }
  1229.         Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
  1230.         var es = item.elements;
  1231.         item.elements = es ? es.replace(',header', '') : es;
  1232.         item.border = (item.border === true);
  1233.     },
  1234.     
  1235.     // private
  1236.     onItemDisabled: Ext.TabPanel.prototype.onItemDisabled,
  1237.     onItemEnabled: Ext.TabPanel.prototype.onItemEnabled,
  1238.     
  1239.     // private
  1240.     onItemTitleChanged: function(item){
  1241.         var el = this.getTabEl(item);
  1242.         if (el) {
  1243.             Ext.fly(el).child('a.x-grouptabs-text', true).innerHTML = item.title;
  1244.         }
  1245.     },
  1246.     
  1247.     //private
  1248.     onItemIconChanged: function(item, iconCls, oldCls){
  1249.         var el = this.getTabEl(item);
  1250.         if (el) {
  1251.             Ext.fly(el).child('a.x-grouptabs-text').replaceClass(oldCls, iconCls);
  1252.         }
  1253.     },
  1254.     
  1255.     beforeDestroy: function(){
  1256.         Ext.TabPanel.prototype.beforeDestroy.call(this);
  1257.         this.tooltip.destroy();
  1258.     }
  1259. });
  1260. Ext.reg('grouptab', Ext.ux.GroupTab);
  1261. Ext.ns('Ext.ux');
  1262. Ext.ux.GroupTabPanel = Ext.extend(Ext.TabPanel, {
  1263.     tabPosition: 'left',
  1264.     
  1265.     alternateColor: false,
  1266.     
  1267.     alternateCls: 'x-grouptabs-panel-alt',
  1268.     
  1269.     defaultType: 'grouptab',
  1270.     
  1271.     deferredRender: false,
  1272.     
  1273.     activeGroup : null,
  1274.     
  1275.     initComponent: function(){
  1276.         Ext.ux.GroupTabPanel.superclass.initComponent.call(this);
  1277.         
  1278.         this.addEvents(
  1279.             'beforegroupchange',
  1280.             'groupchange'
  1281.         );
  1282.         this.elements = 'body,header';
  1283.         this.stripTarget = 'header';
  1284.         
  1285.         this.tabPosition = this.tabPosition == 'right' ? 'right' : 'left';
  1286.         
  1287.         this.addClass('x-grouptabs-panel');
  1288.         
  1289.         if (this.tabStyle && this.tabStyle != '') {
  1290.             this.addClass('x-grouptabs-panel-' + this.tabStyle);
  1291.         }
  1292.         
  1293.         if (this.alternateColor) {
  1294.             this.addClass(this.alternateCls);
  1295.         }
  1296.         
  1297.         this.on('beforeadd', function(gtp, item, index){
  1298.             this.initGroup(item, index);
  1299.         });      
  1300.     },
  1301.     
  1302.     initEvents : function() {
  1303.         this.mon(this.strip, 'mousedown', this.onStripMouseDown, this);
  1304.     },
  1305.         
  1306.     onRender: function(ct, position){
  1307.         Ext.TabPanel.superclass.onRender.call(this, ct, position);
  1308.         if(this.plain){
  1309.             var pos = this.tabPosition == 'top' ? 'header' : 'footer';
  1310.             this[pos].addClass('x-tab-panel-'+pos+'-plain');
  1311.         }
  1312.         var st = this[this.stripTarget];
  1313.         this.stripWrap = st.createChild({cls:'x-tab-strip-wrap ', cn:{
  1314.             tag:'ul', cls:'x-grouptabs-strip x-grouptabs-tab-strip-'+this.tabPosition}});
  1315.         var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
  1316.         this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
  1317. this.header.addClass('x-grouptabs-panel-header');
  1318. this.bwrap.addClass('x-grouptabs-bwrap');
  1319.         this.body.addClass('x-tab-panel-body-'+this.tabPosition + ' x-grouptabs-panel-body');
  1320.         if (!this.groupTpl) {
  1321.             var tt = new Ext.Template(
  1322.                 '<li class="{cls}" id="{id}">', 
  1323.                 '<a class="x-grouptabs-expand" onclick="return false;"></a>', 
  1324.                 '<a class="x-grouptabs-text {iconCls}" href="#" onclick="return false;">',
  1325.                 '<span>{text}</span></a>', 
  1326.                 '</li>'
  1327.             );
  1328.             tt.disableFormats = true;
  1329.             tt.compile();
  1330.             Ext.ux.GroupTabPanel.prototype.groupTpl = tt;
  1331.         }
  1332.         this.items.each(this.initGroup, this);
  1333.     },
  1334.     
  1335.     afterRender: function(){
  1336.         Ext.ux.GroupTabPanel.superclass.afterRender.call(this);
  1337.         
  1338.         this.tabJoint = Ext.fly(this.body.dom.parentNode).createChild({
  1339.             cls: 'x-tab-joint'
  1340.         });
  1341.         
  1342.         this.addClass('x-tab-panel-' + this.tabPosition);
  1343.         this.header.setWidth(this.tabWidth);
  1344.         
  1345.         if (this.activeGroup !== undefined) {
  1346.             var group = (typeof this.activeGroup == 'object') ? this.activeGroup : this.items.get(this.activeGroup);
  1347.             delete this.activeGroup;
  1348.             this.setActiveGroup(group);
  1349.             group.setActiveTab(group.getMainItem());
  1350.         }
  1351.     },
  1352.     getGroupEl : Ext.TabPanel.prototype.getTabEl,
  1353.         
  1354.     // private
  1355.     findTargets: function(e){
  1356.         var item = null,
  1357.             itemEl = e.getTarget('li', this.strip);
  1358.         if (itemEl) {
  1359.             item = this.findById(itemEl.id.split(this.idDelimiter)[1]);
  1360.             if (item.disabled) {
  1361.                 return {
  1362.                     expand: null,
  1363.                     item: null,
  1364.                     el: null
  1365.                 };
  1366.             }
  1367.         }
  1368.         return {
  1369.             expand: e.getTarget('.x-grouptabs-expand', this.strip),
  1370.             isGroup: !e.getTarget('ul.x-grouptabs-sub', this.strip),
  1371.             item: item,
  1372.             el: itemEl
  1373.         };
  1374.     },
  1375.     
  1376.     // private
  1377.     onStripMouseDown: function(e){
  1378.         if (e.button != 0) {
  1379.             return;
  1380.         }
  1381.         e.preventDefault();
  1382.         var t = this.findTargets(e);
  1383.         if (t.expand) {
  1384.             this.toggleGroup(t.el);
  1385.         }
  1386.         else if (t.item) {
  1387.             if(t.isGroup) {
  1388.                 t.item.setActiveTab(t.item.getMainItem());
  1389.             }
  1390.             else {
  1391.                 t.item.ownerCt.setActiveTab(t.item);
  1392.             }
  1393.         }
  1394.     },
  1395.     
  1396.     expandGroup: function(groupEl){
  1397.         if(groupEl.isXType) {
  1398.             groupEl = this.getGroupEl(groupEl);
  1399.         }
  1400.         Ext.fly(groupEl).addClass('x-grouptabs-expanded');
  1401. this.syncTabJoint();
  1402.     },
  1403.     
  1404.     toggleGroup: function(groupEl){
  1405.         if(groupEl.isXType) {
  1406.             groupEl = this.getGroupEl(groupEl);
  1407.         }        
  1408.         Ext.fly(groupEl).toggleClass('x-grouptabs-expanded');
  1409. this.syncTabJoint();
  1410.     },    
  1411.     collapseGroup: function(groupEl){
  1412.         if(groupEl.isXType) {
  1413.             groupEl = this.getGroupEl(groupEl);
  1414.         }
  1415.         Ext.fly(groupEl).removeClass('x-grouptabs-expanded');
  1416. this.syncTabJoint();
  1417.     },
  1418.         
  1419.     syncTabJoint: function(groupEl){
  1420.         if (!this.tabJoint) {
  1421.             return;
  1422.         }
  1423.         
  1424.         groupEl = groupEl || this.getGroupEl(this.activeGroup);
  1425.         if(groupEl) {
  1426.             this.tabJoint.setHeight(Ext.fly(groupEl).getHeight() - 2); 
  1427.             var y = Ext.isGecko2 ? 0 : 1;
  1428.             if (this.tabPosition == 'left'){
  1429.                 this.tabJoint.alignTo(groupEl, 'tl-tr', [-2,y]);
  1430.             }
  1431.             else {
  1432.                 this.tabJoint.alignTo(groupEl, 'tr-tl', [1,y]);
  1433.             }           
  1434.         }
  1435.         else {
  1436.             this.tabJoint.hide();
  1437.         }
  1438.     },
  1439.     
  1440.     getActiveTab : function() {
  1441.         if(!this.activeGroup) return null;
  1442.         return this.activeGroup.getTabEl(this.activeGroup.activeTab) || null;  
  1443.     },
  1444.     
  1445.     onResize: function(){
  1446.         Ext.ux.GroupTabPanel.superclass.onResize.apply(this, arguments);
  1447.         this.syncTabJoint();
  1448.     },
  1449.     
  1450.     createCorner: function(el, pos){
  1451.         return Ext.fly(el).createChild({
  1452.             cls: 'x-grouptabs-corner x-grouptabs-corner-' + pos
  1453.         });
  1454.     },
  1455.     
  1456.     initGroup: function(group, index){
  1457.         var before = this.strip.dom.childNodes[index],   
  1458.             p = this.getTemplateArgs(group);
  1459.         if (index === 0) {
  1460.             p.cls += ' x-tab-first';
  1461.         }
  1462.         p.cls += ' x-grouptabs-main';
  1463.         p.text = group.getMainItem().title;
  1464.         
  1465.         var el = before ? this.groupTpl.insertBefore(before, p) : this.groupTpl.append(this.strip, p),
  1466.             tl = this.createCorner(el, 'top-' + this.tabPosition),
  1467.             bl = this.createCorner(el, 'bottom-' + this.tabPosition);
  1468.         group.tabEl = el;
  1469.         if (group.expanded) {
  1470.             this.expandGroup(el);
  1471.         }
  1472.         if (Ext.isIE6 || (Ext.isIE && !Ext.isStrict)){
  1473.             bl.setLeft('-10px');
  1474.             bl.setBottom('-5px');
  1475.             tl.setLeft('-10px');
  1476.             tl.setTop('-5px');
  1477.         }
  1478.         this.mon(group, {
  1479.             scope: this,
  1480.             changemainitem: this.onGroupChangeMainItem,
  1481.             beforetabchange: this.onGroupBeforeTabChange
  1482.         });
  1483.     },
  1484.     
  1485.     setActiveGroup : function(group) {
  1486.         group = this.getComponent(group);
  1487.         if(!group){
  1488.             return false;
  1489.         }
  1490.         if(!this.rendered){
  1491.             this.activeGroup = group;
  1492.             return true;
  1493.         }
  1494.         if(this.activeGroup != group && this.fireEvent('beforegroupchange', this, group, this.activeGroup) !== false){
  1495.             if(this.activeGroup){
  1496.                 var oldEl = this.getGroupEl(this.activeGroup);
  1497.                 if(oldEl){
  1498.                     Ext.fly(oldEl).removeClass('x-grouptabs-strip-active');
  1499.                 }
  1500.             }
  1501.             var groupEl = this.getGroupEl(group);
  1502.             Ext.fly(groupEl).addClass('x-grouptabs-strip-active');
  1503.                         
  1504.             this.activeGroup = group;
  1505.             this.stack.add(group);
  1506.             this.layout.setActiveItem(group);
  1507.             this.syncTabJoint(groupEl);
  1508.             this.fireEvent('groupchange', this, group);
  1509.             return true;
  1510.         }
  1511.         return false; 
  1512.     },
  1513.     
  1514.     onGroupBeforeTabChange: function(group, newTab, oldTab){
  1515.         if(group !== this.activeGroup || newTab !== oldTab) {
  1516.             this.strip.select('.x-grouptabs-sub > li.x-grouptabs-strip-active', true).removeClass('x-grouptabs-strip-active');
  1517.         } 
  1518.         this.expandGroup(this.getGroupEl(group));
  1519.         if(group !== this.activeGroup) {
  1520.             return this.setActiveGroup(group);
  1521.         }        
  1522.     },
  1523.     
  1524.     getFrameHeight: function(){
  1525.         var h = this.el.getFrameWidth('tb');
  1526.         h += (this.tbar ? this.tbar.getHeight() : 0) +
  1527.         (this.bbar ? this.bbar.getHeight() : 0);
  1528.         
  1529.         return h;
  1530.     },
  1531.     
  1532.     adjustBodyWidth: function(w){
  1533.         return w - this.tabWidth;
  1534.     }
  1535. });
  1536. Ext.reg('grouptabpanel', Ext.ux.GroupTabPanel);/*
  1537.  * Note that this control will most likely remain as an example, and not as a core Ext form
  1538.  * control.  However, the API will be changing in a future release and so should not yet be
  1539.  * treated as a final, stable API at this time.
  1540.  */
  1541. /**
  1542.  * @class Ext.ux.form.ItemSelector
  1543.  * @extends Ext.form.Field
  1544.  * A control that allows selection of between two Ext.ux.form.MultiSelect controls.
  1545.  *
  1546.  *  @history
  1547.  *    2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams)
  1548.  *
  1549.  * @constructor
  1550.  * Create a new ItemSelector
  1551.  * @param {Object} config Configuration options
  1552.  * @xtype itemselector 
  1553.  */
  1554. Ext.ux.form.ItemSelector = Ext.extend(Ext.form.Field,  {
  1555.     hideNavIcons:false,
  1556.     imagePath:"",
  1557.     iconUp:"up2.gif",
  1558.     iconDown:"down2.gif",
  1559.     iconLeft:"left2.gif",
  1560.     iconRight:"right2.gif",
  1561.     iconTop:"top2.gif",
  1562.     iconBottom:"bottom2.gif",
  1563.     drawUpIcon:true,
  1564.     drawDownIcon:true,
  1565.     drawLeftIcon:true,
  1566.     drawRightIcon:true,
  1567.     drawTopIcon:true,
  1568.     drawBotIcon:true,
  1569.     delimiter:',',
  1570.     bodyStyle:null,
  1571.     border:false,
  1572.     defaultAutoCreate:{tag: "div"},
  1573.     /**
  1574.      * @cfg {Array} multiselects An array of {@link Ext.ux.form.MultiSelect} config objects, with at least all required parameters (e.g., store)
  1575.      */
  1576.     multiselects:null,
  1577.     initComponent: function(){
  1578.         Ext.ux.form.ItemSelector.superclass.initComponent.call(this);
  1579.         this.addEvents({
  1580.             'rowdblclick' : true,
  1581.             'change' : true
  1582.         });
  1583.     },
  1584.     onRender: function(ct, position){
  1585.         Ext.ux.form.ItemSelector.superclass.onRender.call(this, ct, position);
  1586.         // Internal default configuration for both multiselects
  1587.         var msConfig = [{
  1588.             legend: 'Available',
  1589.             draggable: true,
  1590.             droppable: true,
  1591.             width: 100,
  1592.             height: 100
  1593.         },{
  1594.             legend: 'Selected',
  1595.             droppable: true,
  1596.             draggable: true,
  1597.             width: 100,
  1598.             height: 100
  1599.         }];
  1600.         this.fromMultiselect = new Ext.ux.form.MultiSelect(Ext.applyIf(this.multiselects[0], msConfig[0]));
  1601.         this.fromMultiselect.on('dblclick', this.onRowDblClick, this);
  1602.         this.toMultiselect = new Ext.ux.form.MultiSelect(Ext.applyIf(this.multiselects[1], msConfig[1]));
  1603.         this.toMultiselect.on('dblclick', this.onRowDblClick, this);
  1604.         var p = new Ext.Panel({
  1605.             bodyStyle:this.bodyStyle,
  1606.             border:this.border,
  1607.             layout:"table",
  1608.             layoutConfig:{columns:3}
  1609.         });
  1610.         p.add(this.fromMultiselect);
  1611.         var icons = new Ext.Panel({header:false});
  1612.         p.add(icons);
  1613.         p.add(this.toMultiselect);
  1614.         p.render(this.el);
  1615.         icons.el.down('.'+icons.bwrapCls).remove();
  1616.         // ICON HELL!!!
  1617.         if (this.imagePath!="" && this.imagePath.charAt(this.imagePath.length-1)!="/")
  1618.             this.imagePath+="/";
  1619.         this.iconUp = this.imagePath + (this.iconUp || 'up2.gif');
  1620.         this.iconDown = this.imagePath + (this.iconDown || 'down2.gif');
  1621.         this.iconLeft = this.imagePath + (this.iconLeft || 'left2.gif');
  1622.         this.iconRight = this.imagePath + (this.iconRight || 'right2.gif');
  1623.         this.iconTop = this.imagePath + (this.iconTop || 'top2.gif');
  1624.         this.iconBottom = this.imagePath + (this.iconBottom || 'bottom2.gif');
  1625.         var el=icons.getEl();
  1626.         this.toTopIcon = el.createChild({tag:'img', src:this.iconTop, style:{cursor:'pointer', margin:'2px'}});
  1627.         el.createChild({tag: 'br'});
  1628.         this.upIcon = el.createChild({tag:'img', src:this.iconUp, style:{cursor:'pointer', margin:'2px'}});
  1629.         el.createChild({tag: 'br'});
  1630.         this.addIcon = el.createChild({tag:'img', src:this.iconRight, style:{cursor:'pointer', margin:'2px'}});
  1631.         el.createChild({tag: 'br'});
  1632.         this.removeIcon = el.createChild({tag:'img', src:this.iconLeft, style:{cursor:'pointer', margin:'2px'}});
  1633.         el.createChild({tag: 'br'});
  1634.         this.downIcon = el.createChild({tag:'img', src:this.iconDown, style:{cursor:'pointer', margin:'2px'}});
  1635.         el.createChild({tag: 'br'});
  1636.         this.toBottomIcon = el.createChild({tag:'img', src:this.iconBottom, style:{cursor:'pointer', margin:'2px'}});
  1637.         this.toTopIcon.on('click', this.toTop, this);
  1638.         this.upIcon.on('click', this.up, this);
  1639.         this.downIcon.on('click', this.down, this);
  1640.         this.toBottomIcon.on('click', this.toBottom, this);
  1641.         this.addIcon.on('click', this.fromTo, this);
  1642.         this.removeIcon.on('click', this.toFrom, this);
  1643.         if (!this.drawUpIcon || this.hideNavIcons) { this.upIcon.dom.style.display='none'; }
  1644.         if (!this.drawDownIcon || this.hideNavIcons) { this.downIcon.dom.style.display='none'; }
  1645.         if (!this.drawLeftIcon || this.hideNavIcons) { this.addIcon.dom.style.display='none'; }
  1646.         if (!this.drawRightIcon || this.hideNavIcons) { this.removeIcon.dom.style.display='none'; }
  1647.         if (!this.drawTopIcon || this.hideNavIcons) { this.toTopIcon.dom.style.display='none'; }
  1648.         if (!this.drawBotIcon || this.hideNavIcons) { this.toBottomIcon.dom.style.display='none'; }
  1649.         var tb = p.body.first();
  1650.         this.el.setWidth(p.body.first().getWidth());
  1651.         p.body.removeClass();
  1652.         this.hiddenName = this.name;
  1653.         var hiddenTag = {tag: "input", type: "hidden", value: "", name: this.name};
  1654.         this.hiddenField = this.el.createChild(hiddenTag);
  1655.     },
  1656.     
  1657.     doLayout: function(){
  1658.         if(this.rendered){
  1659.             this.fromMultiselect.fs.doLayout();
  1660.             this.toMultiselect.fs.doLayout();
  1661.         }
  1662.     },
  1663.     afterRender: function(){
  1664.         Ext.ux.form.ItemSelector.superclass.afterRender.call(this);
  1665.         this.toStore = this.toMultiselect.store;
  1666.         this.toStore.on('add', this.valueChanged, this);
  1667.         this.toStore.on('remove', this.valueChanged, this);
  1668.         this.toStore.on('load', this.valueChanged, this);
  1669.         this.valueChanged(this.toStore);
  1670.     },
  1671.     toTop : function() {
  1672.         var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
  1673.         var records = [];
  1674.         if (selectionsArray.length > 0) {
  1675.             selectionsArray.sort();
  1676.             for (var i=0; i<selectionsArray.length; i++) {
  1677.                 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
  1678.                 records.push(record);
  1679.             }
  1680.             selectionsArray = [];
  1681.             for (var i=records.length-1; i>-1; i--) {
  1682.                 record = records[i];
  1683.                 this.toMultiselect.view.store.remove(record);
  1684.                 this.toMultiselect.view.store.insert(0, record);
  1685.                 selectionsArray.push(((records.length - 1) - i));
  1686.             }
  1687.         }
  1688.         this.toMultiselect.view.refresh();
  1689.         this.toMultiselect.view.select(selectionsArray);
  1690.     },
  1691.     toBottom : function() {
  1692.         var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
  1693.         var records = [];
  1694.         if (selectionsArray.length > 0) {
  1695.             selectionsArray.sort();
  1696.             for (var i=0; i<selectionsArray.length; i++) {
  1697.                 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
  1698.                 records.push(record);
  1699.             }
  1700.             selectionsArray = [];
  1701.             for (var i=0; i<records.length; i++) {
  1702.                 record = records[i];
  1703.                 this.toMultiselect.view.store.remove(record);
  1704.                 this.toMultiselect.view.store.add(record);
  1705.                 selectionsArray.push((this.toMultiselect.view.store.getCount()) - (records.length - i));
  1706.             }
  1707.         }
  1708.         this.toMultiselect.view.refresh();
  1709.         this.toMultiselect.view.select(selectionsArray);
  1710.     },
  1711.     up : function() {
  1712.         var record = null;
  1713.         var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
  1714.         selectionsArray.sort();
  1715.         var newSelectionsArray = [];
  1716.         if (selectionsArray.length > 0) {
  1717.             for (var i=0; i<selectionsArray.length; i++) {
  1718.                 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
  1719.                 if ((selectionsArray[i] - 1) >= 0) {
  1720.                     this.toMultiselect.view.store.remove(record);
  1721.                     this.toMultiselect.view.store.insert(selectionsArray[i] - 1, record);
  1722.                     newSelectionsArray.push(selectionsArray[i] - 1);
  1723.                 }
  1724.             }
  1725.             this.toMultiselect.view.refresh();
  1726.             this.toMultiselect.view.select(newSelectionsArray);
  1727.         }
  1728.     },
  1729.     down : function() {
  1730.         var record = null;
  1731.         var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
  1732.         selectionsArray.sort();
  1733.         selectionsArray.reverse();
  1734.         var newSelectionsArray = [];
  1735.         if (selectionsArray.length > 0) {
  1736.             for (var i=0; i<selectionsArray.length; i++) {
  1737.                 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
  1738.                 if ((selectionsArray[i] + 1) < this.toMultiselect.view.store.getCount()) {
  1739.                     this.toMultiselect.view.store.remove(record);
  1740.                     this.toMultiselect.view.store.insert(selectionsArray[i] + 1, record);
  1741.                     newSelectionsArray.push(selectionsArray[i] + 1);
  1742.                 }
  1743.             }
  1744.             this.toMultiselect.view.refresh();
  1745.             this.toMultiselect.view.select(newSelectionsArray);
  1746.         }
  1747.     },
  1748.     fromTo : function() {
  1749.         var selectionsArray = this.fromMultiselect.view.getSelectedIndexes();
  1750.         var records = [];
  1751.         if (selectionsArray.length > 0) {
  1752.             for (var i=0; i<selectionsArray.length; i++) {
  1753.                 record = this.fromMultiselect.view.store.getAt(selectionsArray[i]);
  1754.                 records.push(record);
  1755.             }
  1756.             if(!this.allowDup)selectionsArray = [];
  1757.             for (var i=0; i<records.length; i++) {
  1758.                 record = records[i];
  1759.                 if(this.allowDup){
  1760.                     var x=new Ext.data.Record();
  1761.                     record.id=x.id;
  1762.                     delete x;
  1763.                     this.toMultiselect.view.store.add(record);
  1764.                 }else{
  1765.                     this.fromMultiselect.view.store.remove(record);
  1766.                     this.toMultiselect.view.store.add(record);
  1767.                     selectionsArray.push((this.toMultiselect.view.store.getCount() - 1));
  1768.                 }
  1769.             }
  1770.         }
  1771.         this.toMultiselect.view.refresh();
  1772.         this.fromMultiselect.view.refresh();
  1773.         var si = this.toMultiselect.store.sortInfo;
  1774.         if(si){
  1775.             this.toMultiselect.store.sort(si.field, si.direction);
  1776.         }
  1777.         this.toMultiselect.view.select(selectionsArray);
  1778.     },
  1779.     toFrom : function() {
  1780.         var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
  1781.         var records = [];
  1782.         if (selectionsArray.length > 0) {
  1783.             for (var i=0; i<selectionsArray.length; i++) {
  1784.                 record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
  1785.                 records.push(record);
  1786.             }
  1787.             selectionsArray = [];
  1788.             for (var i=0; i<records.length; i++) {
  1789.                 record = records[i];
  1790.                 this.toMultiselect.view.store.remove(record);
  1791.                 if(!this.allowDup){
  1792.                     this.fromMultiselect.view.store.add(record);
  1793.                     selectionsArray.push((this.fromMultiselect.view.store.getCount() - 1));
  1794.                 }
  1795.             }
  1796.         }
  1797.         this.fromMultiselect.view.refresh();
  1798.         this.toMultiselect.view.refresh();
  1799.         var si = this.fromMultiselect.store.sortInfo;
  1800.         if (si){
  1801.             this.fromMultiselect.store.sort(si.field, si.direction);
  1802.         }
  1803.         this.fromMultiselect.view.select(selectionsArray);
  1804.     },
  1805.     valueChanged: function(store) {
  1806.         var record = null;
  1807.         var values = [];
  1808.         for (var i=0; i<store.getCount(); i++) {
  1809.             record = store.getAt(i);
  1810.             values.push(record.get(this.toMultiselect.valueField));
  1811.         }
  1812.         this.hiddenField.dom.value = values.join(this.delimiter);
  1813.         this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);
  1814.     },
  1815.     getValue : function() {
  1816.         return this.hiddenField.dom.value;
  1817.     },
  1818.     onRowDblClick : function(vw, index, node, e) {
  1819.         if (vw == this.toMultiselect.view){
  1820.             this.toFrom();
  1821.         } else if (vw == this.fromMultiselect.view) {
  1822.             this.fromTo();
  1823.         }
  1824.         return this.fireEvent('rowdblclick', vw, index, node, e);
  1825.     },
  1826.     reset: function(){
  1827.         range = this.toMultiselect.store.getRange();
  1828.         this.toMultiselect.store.removeAll();
  1829.         this.fromMultiselect.store.add(range);
  1830.         var si = this.fromMultiselect.store.sortInfo;
  1831.         if (si){
  1832.             this.fromMultiselect.store.sort(si.field, si.direction);
  1833.         }
  1834.         this.valueChanged(this.toMultiselect.store);
  1835.     }
  1836. });
  1837. Ext.reg('itemselector', Ext.ux.form.ItemSelector);
  1838. //backwards compat
  1839. Ext.ux.ItemSelector = Ext.ux.form.ItemSelector;
  1840. Ext.ns('Ext.ux.grid');
  1841. Ext.ux.grid.LockingGridView = Ext.extend(Ext.grid.GridView, {
  1842.     lockText : 'Lock',
  1843.     unlockText : 'Unlock',
  1844.     rowBorderWidth : 1,
  1845.     lockedBorderWidth : 1,
  1846.     /*
  1847.      * This option ensures that height between the rows is synchronized
  1848.      * between the locked and unlocked sides. This option only needs to be used
  1849.      * when the row heights isn't predictable.
  1850.      */
  1851.     syncHeights: false,
  1852.     initTemplates : function(){
  1853.         var ts = this.templates || {};
  1854.         if(!ts.master){
  1855.             ts.master = new Ext.Template(
  1856.                 '<div class="x-grid3" hidefocus="true">',
  1857.                     '<div class="x-grid3-locked">',
  1858.                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{lstyle}">{lockedHeader}</div></div><div class="x-clear"></div></div>',
  1859.                         '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{lstyle}">{lockedBody}</div><div class="x-grid3-scroll-spacer"></div></div>',
  1860.                     '</div>',
  1861.                     '<div class="x-grid3-viewport x-grid3-unlocked">',
  1862.                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
  1863.                         '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
  1864.                     '</div>',
  1865.                     '<div class="x-grid3-resize-marker">&#160;</div>',
  1866.                     '<div class="x-grid3-resize-proxy">&#160;</div>',
  1867.                 '</div>'
  1868.             );
  1869.         }
  1870.         this.templates = ts;
  1871.         Ext.ux.grid.LockingGridView.superclass.initTemplates.call(this);
  1872.     },
  1873.     getEditorParent : function(ed){
  1874.         return this.el.dom;
  1875.     },
  1876.     initElements : function(){
  1877.         var E = Ext.Element;
  1878.         var el = this.grid.getGridEl().dom.firstChild;
  1879.         var cs = el.childNodes;
  1880.         this.el = new E(el);
  1881.         this.lockedWrap = new E(cs[0]);
  1882.         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
  1883.         this.lockedInnerHd = this.lockedHd.dom.firstChild;
  1884.         this.lockedScroller = new E(this.lockedWrap.dom.childNodes[1]);
  1885.         this.lockedBody = new E(this.lockedScroller.dom.firstChild);
  1886.         this.mainWrap = new E(cs[1]);
  1887.         this.mainHd = new E(this.mainWrap.dom.firstChild);
  1888.         if(this.grid.hideHeaders){
  1889.             this.lockedHd.setDisplayed(false);
  1890.             this.mainHd.setDisplayed(false);
  1891.         }
  1892.         this.innerHd = this.mainHd.dom.firstChild;
  1893.         this.scroller = new E(this.mainWrap.dom.childNodes[1]);
  1894.         if(this.forceFit){
  1895.             this.scroller.setStyle('overflow-x', 'hidden');
  1896.         }
  1897.         this.mainBody = new E(this.scroller.dom.firstChild);
  1898.         this.focusEl = new E(this.scroller.dom.childNodes[1]);
  1899.         this.focusEl.swallowEvent('click', true);
  1900.         this.resizeMarker = new E(cs[2]);
  1901.         this.resizeProxy = new E(cs[3]);
  1902.     },
  1903.     
  1904.     getLockedRows : function(){
  1905.         return this.hasRows() ? this.lockedBody.dom.childNodes : [];
  1906.     },
  1907.     
  1908.     getLockedRow : function(row){
  1909.         return this.getLockedRows()[row];
  1910.     },
  1911.     
  1912.     getCell : function(row, col){
  1913.         var llen = this.cm.getLockedCount();
  1914.         if(col < llen){
  1915.             return this.getLockedRow(row).getElementsByTagName('td')[col];
  1916.         }
  1917.         return Ext.ux.grid.LockingGridView.superclass.getCell.call(this, row, col - llen);
  1918.     },
  1919.     
  1920.     getHeaderCell : function(index){
  1921.         var llen = this.cm.getLockedCount();
  1922.         if(index < llen){
  1923.             return this.lockedHd.dom.getElementsByTagName('td')[index];
  1924.         }
  1925.         return Ext.ux.grid.LockingGridView.superclass.getHeaderCell.call(this, index - llen);
  1926.     },
  1927.     
  1928.     addRowClass : function(row, cls){
  1929.         var r = this.getLockedRow(row);
  1930.         if(r){
  1931.             this.fly(r).addClass(cls);
  1932.         }
  1933.         Ext.ux.grid.LockingGridView.superclass.addRowClass.call(this, row, cls);
  1934.     },
  1935.     
  1936.     removeRowClass : function(row, cls){
  1937.         var r = this.getLockedRow(row);
  1938.         if(r){
  1939.             this.fly(r).removeClass(cls);
  1940.         }
  1941.         Ext.ux.grid.LockingGridView.superclass.removeRowClass.call(this, row, cls);
  1942.     },
  1943.     
  1944.     removeRow : function(row) {
  1945.         Ext.removeNode(this.getLockedRow(row));
  1946.         Ext.ux.grid.LockingGridView.superclass.removeRow.call(this, row);
  1947.     },
  1948.     
  1949.     removeRows : function(firstRow, lastRow){
  1950.         var bd = this.lockedBody.dom;
  1951.         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
  1952.             Ext.removeNode(bd.childNodes[firstRow]);
  1953.         }
  1954.         Ext.ux.grid.LockingGridView.superclass.removeRows.call(this, firstRow, lastRow);
  1955.     },
  1956.     
  1957.     syncScroll : function(e){
  1958.         var mb = this.scroller.dom;
  1959.         this.lockedScroller.dom.scrollTop = mb.scrollTop;
  1960.         Ext.ux.grid.LockingGridView.superclass.syncScroll.call(this, e);
  1961.     },
  1962.     
  1963.     updateSortIcon : function(col, dir){
  1964.         var sc = this.sortClasses,
  1965.             lhds = this.lockedHd.select('td').removeClass(sc),
  1966.             hds = this.mainHd.select('td').removeClass(sc),
  1967.             llen = this.cm.getLockedCount(),
  1968.             cls = sc[dir == 'DESC' ? 1 : 0];
  1969.         if(col < llen){
  1970.             lhds.item(col).addClass(cls);
  1971.         }else{
  1972.             hds.item(col - llen).addClass(cls);
  1973.         }
  1974.     },
  1975.     
  1976.     updateAllColumnWidths : function(){
  1977.         var tw = this.getTotalWidth(),
  1978.             clen = this.cm.getColumnCount(),
  1979.             lw = this.getLockedWidth(),
  1980.             llen = this.cm.getLockedCount(),
  1981.             ws = [], len, i;
  1982.         this.updateLockedWidth();
  1983.         for(i = 0; i < clen; i++){
  1984.             ws[i] = this.getColumnWidth(i);
  1985.             var hd = this.getHeaderCell(i);
  1986.             hd.style.width = ws[i];
  1987.         }
  1988.         var lns = this.getLockedRows(), ns = this.getRows(), row, trow, j;
  1989.         for(i = 0, len = ns.length; i < len; i++){
  1990.             row = lns[i];
  1991.             row.style.width = lw;
  1992.             if(row.firstChild){
  1993.                 row.firstChild.style.width = lw;
  1994.                 trow = row.firstChild.rows[0];
  1995.                 for (j = 0; j < llen; j++) {
  1996.                    trow.childNodes[j].style.width = ws[j];
  1997.                 }
  1998.             }
  1999.             row = ns[i];
  2000.             row.style.width = tw;
  2001.             if(row.firstChild){
  2002.                 row.firstChild.style.width = tw;
  2003.                 trow = row.firstChild.rows[0];
  2004.                 for (j = llen; j < clen; j++) {
  2005.                    trow.childNodes[j - llen].style.width = ws[j];
  2006.                 }
  2007.             }
  2008.         }
  2009.         this.onAllColumnWidthsUpdated(ws, tw);
  2010.         this.syncHeaderHeight();
  2011.     },
  2012.     
  2013.     updateColumnWidth : function(col, width){
  2014.         var w = this.getColumnWidth(col),
  2015.             llen = this.cm.getLockedCount(),
  2016.             ns, rw, c, row;
  2017.         this.updateLockedWidth();
  2018.         if(col < llen){
  2019.             ns = this.getLockedRows();
  2020.             rw = this.getLockedWidth();
  2021.             c = col;
  2022.         }else{
  2023.             ns = this.getRows();
  2024.             rw = this.getTotalWidth();
  2025.             c = col - llen;
  2026.         }
  2027.         var hd = this.getHeaderCell(col);
  2028.         hd.style.width = w;
  2029.         for(var i = 0, len = ns.length; i < len; i++){
  2030.             row = ns[i];
  2031.             row.style.width = rw;
  2032.             if(row.firstChild){
  2033.                 row.firstChild.style.width = rw;
  2034.                 row.firstChild.rows[0].childNodes[c].style.width = w;
  2035.             }
  2036.         }
  2037.         this.onColumnWidthUpdated(col, w, this.getTotalWidth());
  2038.         this.syncHeaderHeight();
  2039.     },
  2040.     
  2041.     updateColumnHidden : function(col, hidden){
  2042.         var llen = this.cm.getLockedCount(),
  2043.             ns, rw, c, row,
  2044.             display = hidden ? 'none' : '';
  2045.         this.updateLockedWidth();
  2046.         if(col < llen){
  2047.             ns = this.getLockedRows();
  2048.             rw = this.getLockedWidth();
  2049.             c = col;
  2050.         }else{
  2051.             ns = this.getRows();
  2052.             rw = this.getTotalWidth();
  2053.             c = col - llen;
  2054.         }
  2055.         var hd = this.getHeaderCell(col);
  2056.         hd.style.display = display;
  2057.         for(var i = 0, len = ns.length; i < len; i++){
  2058.             row = ns[i];
  2059.             row.style.width = rw;
  2060.             if(row.firstChild){
  2061.                 row.firstChild.style.width = rw;
  2062.                 row.firstChild.rows[0].childNodes[c].style.display = display;
  2063.             }
  2064.         }
  2065.         this.onColumnHiddenUpdated(col, hidden, this.getTotalWidth());
  2066.         delete this.lastViewWidth;
  2067.         this.layout();
  2068.     },
  2069.     
  2070.     doRender : function(cs, rs, ds, startRow, colCount, stripe){
  2071.         var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1,
  2072.             tstyle = 'width:'+this.getTotalWidth()+';',
  2073.             lstyle = 'width:'+this.getLockedWidth()+';',
  2074.             buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r;
  2075.         for(var j = 0, len = rs.length; j < len; j++){
  2076.             r = rs[j]; cb = []; lcb = [];
  2077.             var rowIndex = (j+startRow);
  2078.             for(var i = 0; i < colCount; i++){
  2079.                 c = cs[i];
  2080.                 p.id = c.id;
  2081.                 p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +
  2082.                     (this.cm.config[i].cellCls ? ' ' + this.cm.config[i].cellCls : '');
  2083.                 p.attr = p.cellAttr = '';
  2084.                 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
  2085.                 p.style = c.style;
  2086.                 if(Ext.isEmpty(p.value)){
  2087.                     p.value = '&#160;';
  2088.                 }
  2089.                 if(this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])){
  2090.                     p.css += ' x-grid3-dirty-cell';
  2091.                 }
  2092.                 if(c.locked){
  2093.                     lcb[lcb.length] = ct.apply(p);
  2094.                 }else{
  2095.                     cb[cb.length] = ct.apply(p);
  2096.                 }
  2097.             }
  2098.             var alt = [];
  2099.             if(stripe && ((rowIndex+1) % 2 === 0)){
  2100.                 alt[0] = 'x-grid3-row-alt';
  2101.             }
  2102.             if(r.dirty){
  2103.                 alt[1] = ' x-grid3-dirty-row';
  2104.             }
  2105.             rp.cols = colCount;
  2106.             if(this.getRowClass){
  2107.                 alt[2] = this.getRowClass(r, rowIndex, rp, ds);
  2108.             }
  2109.             rp.alt = alt.join(' ');
  2110.             rp.cells = cb.join('');
  2111.             rp.tstyle = tstyle;
  2112.             buf[buf.length] = rt.apply(rp);
  2113.             rp.cells = lcb.join('');
  2114.             rp.tstyle = lstyle;
  2115.             lbuf[lbuf.length] = rt.apply(rp);
  2116.         }
  2117.         return [buf.join(''), lbuf.join('')];
  2118.     },
  2119.     processRows : function(startRow, skipStripe){
  2120.         if(!this.ds || this.ds.getCount() < 1){
  2121.             return;
  2122.         }
  2123.         var rows = this.getRows(),
  2124.             lrows = this.getLockedRows(),
  2125.             row, lrow;
  2126.         skipStripe = skipStripe || !this.grid.stripeRows;
  2127.         startRow = startRow || 0;
  2128.         for(var i = 0, len = rows.length; i < len; ++i){
  2129.             row = rows[i];
  2130.             lrow = lrows[i];
  2131.             row.rowIndex = i;
  2132.             lrow.rowIndex = i;
  2133.             if(!skipStripe){
  2134.                 row.className = row.className.replace(this.rowClsRe, ' ');
  2135.                 lrow.className = lrow.className.replace(this.rowClsRe, ' ');
  2136.                 if ((idx + 1) % 2 === 0){
  2137.                     row.className += ' x-grid3-row-alt';
  2138.                     lrow.className += ' x-grid3-row-alt';
  2139.                 }
  2140.             }
  2141.             if(this.syncHeights){
  2142.                 var el1 = Ext.get(row),
  2143.                     el2 = Ext.get(lrow),
  2144.                     h1 = el1.getHeight(),
  2145.                     h2 = el2.getHeight();
  2146.                 
  2147.                 if(h1 > h2){
  2148.                     el2.setHeight(h1);    
  2149.                 }else if(h2 > h1){
  2150.                     el1.setHeight(h2);
  2151.                 }
  2152.             }
  2153.         }
  2154.         if(startRow === 0){
  2155.             Ext.fly(rows[0]).addClass(this.firstRowCls);
  2156.             Ext.fly(lrows[0]).addClass(this.firstRowCls);
  2157.         }
  2158.         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
  2159.         Ext.fly(lrows[lrows.length - 1]).addClass(this.lastRowCls);
  2160.     },
  2161.     
  2162.     afterRender : function(){
  2163.         if(!this.ds || !this.cm){
  2164.             return;
  2165.         }
  2166.         var bd = this.renderRows() || ['&#160;', '&#160;'];
  2167.         this.mainBody.dom.innerHTML = bd[0];
  2168.         this.lockedBody.dom.innerHTML = bd[1];
  2169.         this.processRows(0, true);
  2170.         if(this.deferEmptyText !== true){
  2171.             this.applyEmptyText();
  2172.         }
  2173.     },
  2174.     
  2175.     renderUI : function(){
  2176.         var header = this.renderHeaders();
  2177.         var body = this.templates.body.apply({rows:'&#160;'});
  2178.         var html = this.templates.master.apply({
  2179.             body: body,
  2180.             header: header[0],
  2181.             ostyle: 'width:'+this.getOffsetWidth()+';',
  2182.             bstyle: 'width:'+this.getTotalWidth()+';',
  2183.             lockedBody: body,
  2184.             lockedHeader: header[1],
  2185.             lstyle: 'width:'+this.getLockedWidth()+';'
  2186.         });
  2187.         var g = this.grid;
  2188.         g.getGridEl().dom.innerHTML = html;
  2189.         this.initElements();
  2190.         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
  2191.         Ext.fly(this.lockedInnerHd).on('click', this.handleHdDown, this);
  2192.         this.mainHd.on({
  2193.             scope: this,
  2194.             mouseover: this.handleHdOver,
  2195.             mouseout: this.handleHdOut,
  2196.             mousemove: this.handleHdMove
  2197.         });
  2198.         this.lockedHd.on({
  2199.             scope: this,
  2200.             mouseover: this.handleHdOver,
  2201.             mouseout: this.handleHdOut,
  2202.             mousemove: this.handleHdMove
  2203.         });
  2204.         this.scroller.on('scroll', this.syncScroll,  this);
  2205.         if(g.enableColumnResize !== false){
  2206.             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
  2207.             this.splitZone.setOuterHandleElId(Ext.id(this.lockedHd.dom));
  2208.             this.splitZone.setOuterHandleElId(Ext.id(this.mainHd.dom));
  2209.         }
  2210.         if(g.enableColumnMove){
  2211.             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
  2212.             this.columnDrag.setOuterHandleElId(Ext.id(this.lockedInnerHd));
  2213.             this.columnDrag.setOuterHandleElId(Ext.id(this.innerHd));
  2214.             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
  2215.         }
  2216.         if(g.enableHdMenu !== false){
  2217.             this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
  2218.             this.hmenu.add(
  2219.                 {itemId: 'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
  2220.                 {itemId: 'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
  2221.             );
  2222.             if(this.grid.enableColLock !== false){
  2223.                 this.hmenu.add('-',
  2224.                     {itemId: 'lock', text: this.lockText, cls: 'xg-hmenu-lock'},
  2225.                     {itemId: 'unlock', text: this.unlockText, cls: 'xg-hmenu-unlock'}
  2226.                 );
  2227.             }
  2228.             if(g.enableColumnHide !== false){
  2229.                 this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
  2230.                 this.colMenu.on({
  2231.                     scope: this,
  2232.                     beforeshow: this.beforeColMenuShow,
  2233.                     itemclick: this.handleHdMenuClick
  2234.                 });
  2235.                 this.hmenu.add('-', {
  2236.                     itemId:'columns',
  2237.                     hideOnClick: false,
  2238.                     text: this.columnsText,
  2239.                     menu: this.colMenu,
  2240.                     iconCls: 'x-cols-icon'
  2241.                 });
  2242.             }
  2243.             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
  2244.         }
  2245.         if(g.trackMouseOver){
  2246.             this.mainBody.on({
  2247.                 scope: this,
  2248.                 mouseover: this.onRowOver,
  2249.                 mouseout: this.onRowOut
  2250.             });
  2251.             this.lockedBody.on({
  2252.                 scope: this,
  2253.                 mouseover: this.onRowOver,
  2254.                 mouseout: this.onRowOut
  2255.             });
  2256.         }
  2257.         
  2258.         if(g.enableDragDrop || g.enableDrag){
  2259.             this.dragZone = new Ext.grid.GridDragZone(g, {
  2260.                 ddGroup : g.ddGroup || 'GridDD'
  2261.             });
  2262.         }
  2263.         this.updateHeaderSortState();
  2264.     },
  2265.     
  2266.     layout : function(){
  2267.         if(!this.mainBody){
  2268.             return;
  2269.         }
  2270.         var g = this.grid;
  2271.         var c = g.getGridEl();
  2272.         var csize = c.getSize(true);
  2273.         var vw = csize.width;
  2274.         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){
  2275.             return;
  2276.         }
  2277.         this.syncHeaderHeight();
  2278.         if(g.autoHeight){
  2279.             this.scroller.dom.style.overflow = 'visible';
  2280.             this.lockedScroller.dom.style.overflow = 'visible';
  2281.             if(Ext.isWebKit){