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

JavaScript

开发平台:

JavaScript

  1. /*!  * Ext JS Library 3.1.0  * Copyright(c) 2006-2009 Ext JS, LLC  * licensing@extjs.com  * http://www.extjs.com/license  */ Ext.ns('Ext.ux.grid'); /**  * @class Ext.ux.grid.BufferView  * @extends Ext.grid.GridView  * A custom GridView which renders rows on an as-needed basis.  */ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { /**  * @cfg {Number} rowHeight  * The height of a row in the grid.  */ rowHeight: 19, /**  * @cfg {Number} borderHeight  * The combined height of border-top and border-bottom of a row.  */ borderHeight: 2, /**  * @cfg {Boolean/Number} scrollDelay  * The number of milliseconds before rendering rows out of the visible  * viewing area. Defaults to 100. Rows will render immediately with a config  * of false.  */ scrollDelay: 100, /**  * @cfg {Number} cacheSize  * The number of rows to look forward and backwards from the currently viewable  * area.  The cache applies only to rows that have been rendered already.  */ cacheSize: 20, /**  * @cfg {Number} cleanDelay  * The number of milliseconds to buffer cleaning of extra rows not in the  * cache.  */ cleanDelay: 500, initTemplates : function(){ Ext.ux.grid.BufferView.superclass.initTemplates.call(this); var ts = this.templates; // empty div to act as a place holder for a row         ts.rowHolder = new Ext.Template(         '<div class="x-grid3-row {alt}" style="{tstyle}"></div>' ); ts.rowHolder.disableFormats = true; ts.rowHolder.compile(); ts.rowBody = new Ext.Template(         '<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">', '<tbody><tr>{cells}</tr>', (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''), '</tbody></table>' ); ts.rowBody.disableFormats = true; ts.rowBody.compile(); }, getStyleRowHeight : function(){ return Ext.isBorderBox ? (this.rowHeight + this.borderHeight) : this.rowHeight; }, getCalculatedRowHeight : function(){ return this.rowHeight + this.borderHeight; }, getVisibleRowCount : function(){ var rh = this.getCalculatedRowHeight(); var visibleHeight = this.scroller.dom.clientHeight; return (visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh); }, getVisibleRows: function(){ var count = this.getVisibleRowCount(); var sc = this.scroller.dom.scrollTop; var start = (sc == 0 ? 0 : Math.floor(sc/this.getCalculatedRowHeight())-1); return { first: Math.max(start, 0), last: Math.min(start + count + 2, this.ds.getCount()-1) }; }, doRender : function(cs, rs, ds, startRow, colCount, stripe, onlyBody){ var ts = this.templates, ct = ts.cell, rt = ts.row, rb = ts.rowBody, last = colCount-1; var rh = this.getStyleRowHeight(); var vr = this.getVisibleRows(); var tstyle = 'width:'+this.getTotalWidth()+';height:'+rh+'px;'; // buffers var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r; for (var j = 0, len = rs.length; j < len; j++) { r = rs[j]; cb = []; var rowIndex = (j+startRow); var visible = rowIndex >= vr.first && rowIndex <= vr.last; if (visible) { for (var i = 0; i < colCount; i++) { c = cs[i]; p.id = c.id; p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : ''); p.attr = p.cellAttr = ""; p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds); p.style = c.style; if (p.value == undefined || p.value === "") { p.value = "&#160;"; } if (r.dirty && typeof r.modified[c.name] !== 'undefined') { p.css += ' x-grid3-dirty-cell'; } cb[cb.length] = ct.apply(p); } } var alt = []; if(stripe && ((rowIndex+1) % 2 == 0)){     alt[0] = "x-grid3-row-alt"; } if(r.dirty){     alt[1] = " x-grid3-dirty-row"; } rp.cols = colCount; if(this.getRowClass){     alt[2] = this.getRowClass(r, rowIndex, rp, ds); } rp.alt = alt.join(" "); rp.cells = cb.join(""); buf[buf.length] =  !visible ? ts.rowHolder.apply(rp) : (onlyBody ? rb.apply(rp) : rt.apply(rp)); } return buf.join(""); }, isRowRendered: function(index){ var row = this.getRow(index); return row && row.childNodes.length > 0; }, syncScroll: function(){ Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments); this.update(); }, // a (optionally) buffered method to update contents of gridview update: function(){ if (this.scrollDelay) { if (!this.renderTask) { this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this); } this.renderTask.delay(this.scrollDelay); }else{ this.doUpdate(); } },          onRemove : function(ds, record, index, isUpdate){         Ext.ux.grid.BufferView.superclass.onRemove.apply(this, arguments);         if(isUpdate !== true){             this.update();         }     }, doUpdate: function(){ if (this.getVisibleRowCount() > 0) { var g = this.grid, cm = g.colModel, ds = g.store;         var cs = this.getColumnData();         var vr = this.getVisibleRows(); for (var i = vr.first; i <= vr.last; i++) { // if row is NOT rendered and is visible, render it if(!this.isRowRendered(i)){ var html = this.doRender(cs, [ds.getAt(i)], ds, i, cm.getColumnCount(), g.stripeRows, true); this.getRow(i).innerHTML = html; } } this.clean(); } }, // a buffered method to clean rows clean : function(){ if(!this.cleanTask){ this.cleanTask = new Ext.util.DelayedTask(this.doClean, this); } this.cleanTask.delay(this.cleanDelay); }, doClean: function(){ if (this.getVisibleRowCount() > 0) { var vr = this.getVisibleRows(); vr.first -= this.cacheSize; vr.last += this.cacheSize; var i = 0, rows = this.getRows(); // if first is less than 0, all rows have been rendered // so lets clean the end... if(vr.first <= 0){ i = vr.last + 1; } for(var len = this.ds.getCount(); i < len; i++){ // if current row is outside of first and last and // has content, update the innerHTML to nothing if ((i < vr.first || i > vr.last) && rows[i].innerHTML) { rows[i].innerHTML = ''; } } } }, layout: function(){ Ext.ux.grid.BufferView.superclass.layout.call(this); this.update(); } }); // We are adding these custom layouts to a namespace that does not // exist by default in Ext, so we have to add the namespace first: Ext.ns('Ext.ux.layout'); /**  * @class Ext.ux.layout.CenterLayout  * @extends Ext.layout.FitLayout  * <p>This is a very simple layout style used to center contents within a container.  This layout works within  * nested containers and can also be used as expected as a Viewport layout to center the page layout.</p>  * <p>As a subclass of FitLayout, CenterLayout expects to have a single child panel of the container that uses  * the layout.  The layout does not require any config options, although the child panel contained within the  * layout must provide a fixed or percentage width.  The child panel's height will fit to the container by  * default, but you can specify <tt>autoHeight:true</tt> to allow it to autosize based on its content height.  * Example usage:</p>  * <pre><code> // The content panel is centered in the container var p = new Ext.Panel({     title: 'Center Layout',     layout: 'ux.center',     items: [{         title: 'Centered Content',         width: '75%',         html: 'Some content'     }] }); // If you leave the title blank and specify no border // you'll create a non-visual, structural panel just // for centering the contents in the main container. var p = new Ext.Panel({     layout: 'ux.center',     border: false,     items: [{         title: 'Centered Content',         width: 300,         autoHeight: true,         html: 'Some content'     }] }); </code></pre>  */ Ext.ux.layout.CenterLayout = Ext.extend(Ext.layout.FitLayout, { // private     setItemSize : function(item, size){         this.container.addClass('ux-layout-center');         item.addClass('ux-layout-center-item');         if(item && size.height > 0){             if(item.width){                 size.width = item.width;             }             item.setSize(size);         }     } }); Ext.Container.LAYOUTS['ux.center'] = Ext.ux.layout.CenterLayout; Ext.ns('Ext.ux.grid');
  2. /**
  3.  * @class Ext.ux.grid.CheckColumn
  4.  * @extends Object
  5.  * GridPanel plugin to add a column with check boxes to a grid.
  6.  * <p>Example usage:</p>
  7.  * <pre><code>
  8. // create the column
  9. var checkColumn = new Ext.grid.CheckColumn({
  10.    header: 'Indoor?',
  11.    dataIndex: 'indoor',
  12.    id: 'check',
  13.    width: 55
  14. });
  15. // add the column to the column model
  16. var cm = new Ext.grid.ColumnModel([{
  17.        header: 'Foo',
  18.        ...
  19.     },
  20.     checkColumn
  21. ]);
  22. // create the grid
  23. var grid = new Ext.grid.EditorGridPanel({
  24.     ...
  25.     cm: cm,
  26.     plugins: [checkColumn], // include plugin
  27.     ...
  28. });
  29.  * </code></pre>
  30.  * In addition to storing a Boolean value within the record data, this
  31.  * class toggles a css class between <tt>'x-grid3-check-col'</tt> and
  32.  * <tt>'x-grid3-check-col-on'</tt> to alter the background image used for
  33.  * a column.
  34.  */
  35. Ext.ux.grid.CheckColumn = function(config){
  36.     Ext.apply(this, config);
  37.     if(!this.id){
  38.         this.id = Ext.id();
  39.     }
  40.     this.renderer = this.renderer.createDelegate(this);
  41. };
  42. Ext.ux.grid.CheckColumn.prototype ={
  43.     init : function(grid){
  44.         this.grid = grid;
  45.         this.grid.on('render', function(){
  46.             var view = this.grid.getView();
  47.             view.mainBody.on('mousedown', this.onMouseDown, this);
  48.         }, this);
  49.     },
  50.     onMouseDown : function(e, t){
  51.         if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
  52.             e.stopEvent();
  53.             var index = this.grid.getView().findRowIndex(t);
  54.             var record = this.grid.store.getAt(index);
  55.             record.set(this.dataIndex, !record.data[this.dataIndex]);
  56.         }
  57.     },
  58.     renderer : function(v, p, record){
  59.         p.css += ' x-grid3-check-col-td'; 
  60.         return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>';
  61.     }
  62. };
  63. // register ptype
  64. Ext.preg('checkcolumn', Ext.ux.grid.CheckColumn);
  65. // backwards compat
  66. Ext.grid.CheckColumn = Ext.ux.grid.CheckColumn;Ext.ns('Ext.ux.grid');
  67. if(Ext.isWebKit){
  68.     Ext.grid.GridView.prototype.borderWidth = 0;
  69. }
  70. Ext.ux.grid.ColumnHeaderGroup = Ext.extend(Ext.util.Observable, {
  71.     constructor: function(config){
  72.         this.config = config;
  73.     },
  74.     init: function(grid){
  75.         Ext.applyIf(grid.colModel, this.config);
  76.         Ext.apply(grid.getView(), this.viewConfig);
  77.     },
  78.     viewConfig: {
  79.         initTemplates: function(){
  80.             this.constructor.prototype.initTemplates.apply(this, arguments);
  81.             var ts = this.templates || {};
  82.             if(!ts.gcell){
  83.                 ts.gcell = new Ext.XTemplate('<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} {cls}" style="{style}">', '<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '', '{value}</div></td>');
  84.             }
  85.             this.templates = ts;
  86.             this.hrowRe = new RegExp("ux-grid-hd-group-row-(\d+)", "");
  87.         },
  88.         renderHeaders: function(){
  89.             var ts = this.templates, headers = [], cm = this.cm, rows = cm.rows, tstyle = 'width:' + this.getTotalWidth() + ';';
  90.             for(var row = 0, rlen = rows.length; row < rlen; row++){
  91.                 var r = rows[row], cells = [];
  92.                 for(var i = 0, gcol = 0, len = r.length; i < len; i++){
  93.                     var group = r[i];
  94.                     group.colspan = group.colspan || 1;
  95.                     var id = this.getColumnId(group.dataIndex ? cm.findColumnIndex(group.dataIndex) : gcol), gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);
  96.                     cells[i] = ts.gcell.apply({
  97.                         cls: 'ux-grid-hd-group-cell',
  98.                         id: id,
  99.                         row: row,
  100.                         style: 'width:' + gs.width + ';' + (gs.hidden ? 'display:none;' : '') + (group.align ? 'text-align:' + group.align + ';' : ''),
  101.                         tooltip: group.tooltip ? (Ext.QuickTips.isEnabled() ? 'ext:qtip' : 'title') + '="' + group.tooltip + '"' : '',
  102.                         istyle: group.align == 'right' ? 'padding-right:16px' : '',
  103.                         btn: this.grid.enableHdMenu && group.header,
  104.                         value: group.header || '&nbsp;'
  105.                     });
  106.                     gcol += group.colspan;
  107.                 }
  108.                 headers[row] = ts.header.apply({
  109.                     tstyle: tstyle,
  110.                     cells: cells.join('')
  111.                 });
  112.             }
  113.             headers.push(this.constructor.prototype.renderHeaders.apply(this, arguments));
  114.             return headers.join('');
  115.         },
  116.         onColumnWidthUpdated: function(){
  117.             this.constructor.prototype.onColumnWidthUpdated.apply(this, arguments);
  118.             Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
  119.         },
  120.         onAllColumnWidthsUpdated: function(){
  121.             this.constructor.prototype.onAllColumnWidthsUpdated.apply(this, arguments);
  122.             Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
  123.         },
  124.         onColumnHiddenUpdated: function(){
  125.             this.constructor.prototype.onColumnHiddenUpdated.apply(this, arguments);
  126.             Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
  127.         },
  128.         getHeaderCell: function(index){
  129.             return this.mainHd.query(this.cellSelector)[index];
  130.         },
  131.         findHeaderCell: function(el){
  132.             return el ? this.fly(el).findParent('td.x-grid3-hd', this.cellSelectorDepth) : false;
  133.         },
  134.         findHeaderIndex: function(el){
  135.             var cell = this.findHeaderCell(el);
  136.             return cell ? this.getCellIndex(cell) : false;
  137.         },
  138.         updateSortIcon: function(col, dir){
  139.             var sc = this.sortClasses, hds = this.mainHd.select(this.cellSelector).removeClass(sc);
  140.             hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
  141.         },
  142.         handleHdDown: function(e, t){
  143.             var el = Ext.get(t);
  144.             if(el.hasClass('x-grid3-hd-btn')){
  145.                 e.stopEvent();
  146.                 var hd = this.findHeaderCell(t);
  147.                 Ext.fly(hd).addClass('x-grid3-hd-menu-open');
  148.                 var index = this.getCellIndex(hd);
  149.                 this.hdCtxIndex = index;
  150.                 var ms = this.hmenu.items, cm = this.cm;
  151.                 ms.get('asc').setDisabled(!cm.isSortable(index));
  152.                 ms.get('desc').setDisabled(!cm.isSortable(index));
  153.                 this.hmenu.on('hide', function(){
  154.                     Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
  155.                 }, this, {
  156.                     single: true
  157.                 });
  158.                 this.hmenu.show(t, 'tl-bl?');
  159.             }else if(el.hasClass('ux-grid-hd-group-cell') || Ext.fly(t).up('.ux-grid-hd-group-cell')){
  160.                 e.stopEvent();
  161.             }
  162.         },
  163.         handleHdMove: function(e, t){
  164.             var hd = this.findHeaderCell(this.activeHdRef);
  165.             if(hd && !this.headersDisabled && !Ext.fly(hd).hasClass('ux-grid-hd-group-cell')){
  166.                 var hw = this.splitHandleWidth || 5, r = this.activeHdRegion, x = e.getPageX(), ss = hd.style, cur = '';
  167.                 if(this.grid.enableColumnResize !== false){
  168.                     if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex - 1)){
  169.                         cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize
  170.                                                                                                 // not
  171.                                                                                                 // always
  172.                                                                                                 // supported
  173.                     }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
  174.                         cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
  175.                     }
  176.                 }
  177.                 ss.cursor = cur;
  178.             }
  179.         },
  180.         handleHdOver: function(e, t){
  181.             var hd = this.findHeaderCell(t);
  182.             if(hd && !this.headersDisabled){
  183.                 this.activeHdRef = t;
  184.                 this.activeHdIndex = this.getCellIndex(hd);
  185.                 var fly = this.fly(hd);
  186.                 this.activeHdRegion = fly.getRegion();
  187.                 if(!(this.cm.isMenuDisabled(this.activeHdIndex) || fly.hasClass('ux-grid-hd-group-cell'))){
  188.                     fly.addClass('x-grid3-hd-over');
  189.                     this.activeHdBtn = fly.child('.x-grid3-hd-btn');
  190.                     if(this.activeHdBtn){
  191.                         this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight - 1) + 'px';
  192.                     }
  193.                 }
  194.             }
  195.         },
  196.         handleHdOut: function(e, t){
  197.             var hd = this.findHeaderCell(t);
  198.             if(hd && (!Ext.isIE || !e.within(hd, true))){
  199.                 this.activeHdRef = null;
  200.                 this.fly(hd).removeClass('x-grid3-hd-over');
  201.                 hd.style.cursor = '';
  202.             }
  203.         },
  204.         handleHdMenuClick: function(item){
  205.             var index = this.hdCtxIndex, cm = this.cm, ds = this.ds, id = item.getItemId();
  206.             switch(id){
  207.                 case 'asc':
  208.                     ds.sort(cm.getDataIndex(index), 'ASC');
  209.                     break;
  210.                 case 'desc':
  211.                     ds.sort(cm.getDataIndex(index), 'DESC');
  212.                     break;
  213.                 default:
  214.                     if(id.substr(0, 5) == 'group'){
  215.                         var i = id.split('-'), row = parseInt(i[1], 10), col = parseInt(i[2], 10), r = this.cm.rows[row], group, gcol = 0;
  216.                         for(var i = 0, len = r.length; i < len; i++){
  217.                             group = r[i];
  218.                             if(col >= gcol && col < gcol + group.colspan){
  219.                                 break;
  220.                             }
  221.                             gcol += group.colspan;
  222.                         }
  223.                         if(item.checked){
  224.                             var max = cm.getColumnsBy(this.isHideableColumn, this).length;
  225.                             for(var i = gcol, len = gcol + group.colspan; i < len; i++){
  226.                                 if(!cm.isHidden(i)){
  227.                                     max--;
  228.                                 }
  229.                             }
  230.                             if(max < 1){
  231.                                 this.onDenyColumnHide();
  232.                                 return false;
  233.                             }
  234.                         }
  235.                         for(var i = gcol, len = gcol + group.colspan; i < len; i++){
  236.                             if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
  237.                                 cm.setHidden(i, item.checked);
  238.                             }
  239.                         }
  240.                     }else{
  241.                         index = cm.getIndexById(id.substr(4));
  242.                         if(index != -1){
  243.                             if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
  244.                                 this.onDenyColumnHide();
  245.                                 return false;
  246.                             }
  247.                             cm.setHidden(index, item.checked);
  248.                         }
  249.                     }
  250.                     item.checked = !item.checked;
  251.                     if(item.menu){
  252.                         var updateChildren = function(menu){
  253.                             menu.items.each(function(childItem){
  254.                                 if(!childItem.disabled){
  255.                                     childItem.setChecked(item.checked, false);
  256.                                     if(childItem.menu){
  257.                                         updateChildren(childItem.menu);
  258.                                     }
  259.                                 }
  260.                             });
  261.                         }
  262.                         updateChildren(item.menu);
  263.                     }
  264.                     var parentMenu = item, parentItem;
  265.                     while(parentMenu = parentMenu.parentMenu){
  266.                         if(!parentMenu.parentMenu || !(parentItem = parentMenu.parentMenu.items.get(parentMenu.getItemId())) || !parentItem.setChecked){
  267.                             break;
  268.                         }
  269.                         var checked = parentMenu.items.findIndexBy(function(m){
  270.                             return m.checked;
  271.                         }) >= 0;
  272.                         parentItem.setChecked(checked, true);
  273.                     }
  274.                     item.checked = !item.checked;
  275.             }
  276.             return true;
  277.         },
  278.         beforeColMenuShow: function(){
  279.             var cm = this.cm, rows = this.cm.rows;
  280.             this.colMenu.removeAll();
  281.             for(var col = 0, clen = cm.getColumnCount(); col < clen; col++){
  282.                 var menu = this.colMenu, title = cm.getColumnHeader(col), text = [];
  283.                 if(cm.config[col].fixed !== true && cm.config[col].hideable !== false){
  284.                     for(var row = 0, rlen = rows.length; row < rlen; row++){
  285.                         var r = rows[row], group, gcol = 0;
  286.                         for(var i = 0, len = r.length; i < len; i++){
  287.                             group = r[i];
  288.                             if(col >= gcol && col < gcol + group.colspan){
  289.                                 break;
  290.                             }
  291.                             gcol += group.colspan;
  292.                         }
  293.                         if(group && group.header){
  294.                             if(cm.hierarchicalColMenu){
  295.                                 var gid = 'group-' + row + '-' + gcol;
  296.                                 var item = menu.items.item(gid);
  297.                                 var submenu = item ? item.menu : null;
  298.                                 if(!submenu){
  299.                                     submenu = new Ext.menu.Menu({
  300.                                         itemId: gid
  301.                                     });
  302.                                     submenu.on("itemclick", this.handleHdMenuClick, this);
  303.                                     var checked = false, disabled = true;
  304.                                     for(var c = gcol, lc = gcol + group.colspan; c < lc; c++){
  305.                                         if(!cm.isHidden(c)){
  306.                                             checked = true;
  307.                                         }
  308.                                         if(cm.config[c].hideable !== false){
  309.                                             disabled = false;
  310.                                         }
  311.                                     }
  312.                                     menu.add({
  313.                                         itemId: gid,
  314.                                         text: group.header,
  315.                                         menu: submenu,
  316.                                         hideOnClick: false,
  317.                                         checked: checked,
  318.                                         disabled: disabled
  319.                                     });
  320.                                 }
  321.                                 menu = submenu;
  322.                             }else{
  323.                                 text.push(group.header);
  324.                             }
  325.                         }
  326.                     }
  327.                     text.push(title);
  328.                     menu.add(new Ext.menu.CheckItem({
  329.                         itemId: "col-" + cm.getColumnId(col),
  330.                         text: text.join(' '),
  331.                         checked: !cm.isHidden(col),
  332.                         hideOnClick: false,
  333.                         disabled: cm.config[col].hideable === false
  334.                     }));
  335.                 }
  336.             }
  337.         },
  338.         renderUI: function(){
  339.             this.constructor.prototype.renderUI.apply(this, arguments);
  340.             Ext.apply(this.columnDrop, Ext.ux.grid.ColumnHeaderGroup.prototype.columnDropConfig);
  341.             Ext.apply(this.splitZone, Ext.ux.grid.ColumnHeaderGroup.prototype.splitZoneConfig);
  342.         }
  343.     },
  344.     splitZoneConfig: {
  345.         allowHeaderDrag: function(e){
  346.             return !e.getTarget(null, null, true).hasClass('ux-grid-hd-group-cell');
  347.         }
  348.     },
  349.     columnDropConfig: {
  350.         getTargetFromEvent: function(e){
  351.             var t = Ext.lib.Event.getTarget(e);
  352.             return this.view.findHeaderCell(t);
  353.         },
  354.         positionIndicator: function(h, n, e){
  355.             var data = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);
  356.             if(data === false){
  357.                 return false;
  358.             }
  359.             var px = data.px + this.proxyOffsets[0];
  360.             this.proxyTop.setLeftTop(px, data.r.top + this.proxyOffsets[1]);
  361.             this.proxyTop.show();
  362.             this.proxyBottom.setLeftTop(px, data.r.bottom);
  363.             this.proxyBottom.show();
  364.             return data.pt;
  365.         },
  366.         onNodeDrop: function(n, dd, e, data){
  367.             var h = data.header;
  368.             if(h != n){
  369.                 var d = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);
  370.                 if(d === false){
  371.                     return false;
  372.                 }
  373.                 var cm = this.grid.colModel, right = d.oldIndex < d.newIndex, rows = cm.rows;
  374.                 for(var row = d.row, rlen = rows.length; row < rlen; row++){
  375.                     var r = rows[row], len = r.length, fromIx = 0, span = 1, toIx = len;
  376.                     for(var i = 0, gcol = 0; i < len; i++){
  377.                         var group = r[i];
  378.                         if(d.oldIndex >= gcol && d.oldIndex < gcol + group.colspan){
  379.                             fromIx = i;
  380.                         }
  381.                         if(d.oldIndex + d.colspan - 1 >= gcol && d.oldIndex + d.colspan - 1 < gcol + group.colspan){
  382.                             span = i - fromIx + 1;
  383.                         }
  384.                         if(d.newIndex >= gcol && d.newIndex < gcol + group.colspan){
  385.                             toIx = i;
  386.                         }
  387.                         gcol += group.colspan;
  388.                     }
  389.                     var groups = r.splice(fromIx, span);
  390.                     rows[row] = r.splice(0, toIx - (right ? span : 0)).concat(groups).concat(r);
  391.                 }
  392.                 for(var c = 0; c < d.colspan; c++){
  393.                     var oldIx = d.oldIndex + (right ? 0 : c), newIx = d.newIndex + (right ? -1 : c);
  394.                     cm.moveColumn(oldIx, newIx);
  395.                     this.grid.fireEvent("columnmove", oldIx, newIx);
  396.                 }
  397.                 return true;
  398.             }
  399.             return false;
  400.         }
  401.     },
  402.     getGroupStyle: function(group, gcol){
  403.         var width = 0, hidden = true;
  404.         for(var i = gcol, len = gcol + group.colspan; i < len; i++){
  405.             if(!this.cm.isHidden(i)){
  406.                 var cw = this.cm.getColumnWidth(i);
  407.                 if(typeof cw == 'number'){
  408.                     width += cw;
  409.                 }
  410.                 hidden = false;
  411.             }
  412.         }
  413.         return {
  414.             width: (Ext.isBorderBox ? width : Math.max(width - this.borderWidth, 0)) + 'px',
  415.             hidden: hidden
  416.         };
  417.     },
  418.     updateGroupStyles: function(col){
  419.         var tables = this.mainHd.query('.x-grid3-header-offset > table'), tw = this.getTotalWidth(), rows = this.cm.rows;
  420.         for(var row = 0; row < tables.length; row++){
  421.             tables[row].style.width = tw;
  422.             if(row < rows.length){
  423.                 var cells = tables[row].firstChild.firstChild.childNodes;
  424.                 for(var i = 0, gcol = 0; i < cells.length; i++){
  425.                     var group = rows[row][i];
  426.                     if((typeof col != 'number') || (col >= gcol && col < gcol + group.colspan)){
  427.                         var gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);
  428.                         cells[i].style.width = gs.width;
  429.                         cells[i].style.display = gs.hidden ? 'none' : '';
  430.                     }
  431.                     gcol += group.colspan;
  432.                 }
  433.             }
  434.         }
  435.     },
  436.     getGroupRowIndex: function(el){
  437.         if(el){
  438.             var m = el.className.match(this.hrowRe);
  439.             if(m && m[1]){
  440.                 return parseInt(m[1], 10);
  441.             }
  442.         }
  443.         return this.cm.rows.length;
  444.     },
  445.     getGroupSpan: function(row, col){
  446.         if(row < 0){
  447.             return {
  448.                 col: 0,
  449.                 colspan: this.cm.getColumnCount()
  450.             };
  451.         }
  452.         var r = this.cm.rows[row];
  453.         if(r){
  454.             for(var i = 0, gcol = 0, len = r.length; i < len; i++){
  455.                 var group = r[i];
  456.                 if(col >= gcol && col < gcol + group.colspan){
  457.                     return {
  458.                         col: gcol,
  459.                         colspan: group.colspan
  460.                     };
  461.                 }
  462.                 gcol += group.colspan;
  463.             }
  464.             return {
  465.                 col: gcol,
  466.                 colspan: 0
  467.             };
  468.         }
  469.         return {
  470.             col: col,
  471.             colspan: 1
  472.         };
  473.     },
  474.     getDragDropData: function(h, n, e){
  475.         if(h.parentNode != n.parentNode){
  476.             return false;
  477.         }
  478.         var cm = this.grid.colModel, x = Ext.lib.Event.getPageX(e), r = Ext.lib.Dom.getRegion(n.firstChild), px, pt;
  479.         if((r.right - x) <= (r.right - r.left) / 2){
  480.             px = r.right + this.view.borderWidth;
  481.             pt = "after";
  482.         }else{
  483.             px = r.left;
  484.             pt = "before";
  485.         }
  486.         var oldIndex = this.view.getCellIndex(h), newIndex = this.view.getCellIndex(n);
  487.         if(cm.isFixed(newIndex)){
  488.             return false;
  489.         }
  490.         var row = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupRowIndex.call(this.view, h), 
  491.             oldGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row, oldIndex), 
  492.             newGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row, newIndex),
  493.             oldIndex = oldGroup.col;
  494.             newIndex = newGroup.col + (pt == "after" ? newGroup.colspan : 0);
  495.         if(newIndex >= oldGroup.col && newIndex <= oldGroup.col + oldGroup.colspan){
  496.             return false;
  497.         }
  498.         var parentGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row - 1, oldIndex);
  499.         if(newIndex < parentGroup.col || newIndex > parentGroup.col + parentGroup.colspan){
  500.             return false;
  501.         }
  502.         return {
  503.             r: r,
  504.             px: px,
  505.             pt: pt,
  506.             row: row,
  507.             oldIndex: oldIndex,
  508.             newIndex: newIndex,
  509.             colspan: oldGroup.colspan
  510.         };
  511.     }
  512. });Ext.ns('Ext.ux.tree');
  513. /**
  514.  * @class Ext.ux.tree.ColumnTree
  515.  * @extends Ext.tree.TreePanel
  516.  * 
  517.  * @xtype columntree
  518.  */
  519. Ext.ux.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, {
  520.     lines : false,
  521.     borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
  522.     cls : 'x-column-tree',
  523.     onRender : function(){
  524.         Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments);
  525.         this.headers = this.header.createChild({cls:'x-tree-headers'});
  526.         var cols = this.columns, c;
  527.         var totalWidth = 0;
  528.         var scrollOffset = 19; // similar to Ext.grid.GridView default
  529.         for(var i = 0, len = cols.length; i < len; i++){
  530.              c = cols[i];
  531.              totalWidth += c.width;
  532.              this.headers.createChild({
  533.                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
  534.                  cn: {
  535.                      cls:'x-tree-hd-text',
  536.                      html: c.header
  537.                  },
  538.                  style:'width:'+(c.width-this.borderWidth)+'px;'
  539.              });
  540.         }
  541.         this.headers.createChild({cls:'x-clear'});
  542.         // prevent floats from wrapping when clipped
  543.         this.headers.setWidth(totalWidth+scrollOffset);
  544.         this.innerCt.setWidth(totalWidth);
  545.     }
  546. });
  547. Ext.reg('columntree', Ext.ux.tree.ColumnTree);
  548. //backwards compat
  549. Ext.tree.ColumnTree = Ext.ux.tree.ColumnTree;
  550. /**
  551.  * @class Ext.ux.tree.ColumnNodeUI
  552.  * @extends Ext.tree.TreeNodeUI
  553.  */
  554. Ext.ux.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
  555.     focus: Ext.emptyFn, // prevent odd scrolling behavior
  556.     renderElements : function(n, a, targetNode, bulkRender){
  557.         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
  558.         var t = n.getOwnerTree();
  559.         var cols = t.columns;
  560.         var bw = t.borderWidth;
  561.         var c = cols[0];
  562.         var buf = [
  563.              '<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf ', a.cls,'">',
  564.                 '<div class="x-tree-col" style="width:',c.width-bw,'px;">',
  565.                     '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
  566.                     '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',
  567.                     '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">',
  568.                     '<a hidefocus="on" class="x-tree-node-anchor" href="',a.href ? a.href : "#",'" tabIndex="1" ',
  569.                     a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>',
  570.                     '<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>",
  571.                 "</div>"];
  572.          for(var i = 1, len = cols.length; i < len; i++){
  573.              c = cols[i];
  574.              buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
  575.                         '<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>",
  576.                       "</div>");
  577.          }
  578.          buf.push(
  579.             '<div class="x-clear"></div></div>',
  580.             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
  581.             "</li>");
  582.         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
  583.             this.wrap = Ext.DomHelper.insertHtml("beforeBegin",
  584.                                 n.nextSibling.ui.getEl(), buf.join(""));
  585.         }else{
  586.             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
  587.         }
  588.         this.elNode = this.wrap.childNodes[0];
  589.         this.ctNode = this.wrap.childNodes[1];
  590.         var cs = this.elNode.firstChild.childNodes;
  591.         this.indentNode = cs[0];
  592.         this.ecNode = cs[1];
  593.         this.iconNode = cs[2];
  594.         this.anchor = cs[3];
  595.         this.textNode = cs[3].firstChild;
  596.     }
  597. });
  598. //backwards compat
  599. Ext.tree.ColumnNodeUI = Ext.ux.tree.ColumnNodeUI;
  600. /**
  601.  * @class Ext.DataView.LabelEditor
  602.  * @extends Ext.Editor
  603.  * 
  604.  */
  605. Ext.DataView.LabelEditor = Ext.extend(Ext.Editor, {
  606.     alignment: "tl-tl",
  607.     hideEl : false,
  608.     cls: "x-small-editor",
  609.     shim: false,
  610.     completeOnEnter: true,
  611.     cancelOnEsc: true,
  612.     labelSelector: 'span.x-editable',
  613.     
  614.     constructor: function(cfg, field){
  615.         Ext.DataView.LabelEditor.superclass.constructor.call(this,
  616.             field || new Ext.form.TextField({
  617.                 allowBlank: false,
  618.                 growMin:90,
  619.                 growMax:240,
  620.                 grow:true,
  621.                 selectOnFocus:true
  622.             }), cfg
  623.         );
  624.     },
  625.     
  626.     init : function(view){
  627.         this.view = view;
  628.         view.on('render', this.initEditor, this);
  629.         this.on('complete', this.onSave, this);
  630.     },
  631.     initEditor : function(){
  632.         this.view.on({
  633.             scope: this,
  634.             containerclick: this.doBlur,
  635.             click: this.doBlur
  636.         });
  637.         this.view.getEl().on('mousedown', this.onMouseDown, this, {delegate: this.labelSelector});
  638.     },
  639.     
  640.     doBlur: function(){
  641.         if(this.editing){
  642.             this.field.blur();
  643.         }
  644.     },
  645.     onMouseDown : function(e, target){
  646.         if(!e.ctrlKey && !e.shiftKey){
  647.             var item = this.view.findItemFromChild(target);
  648.             e.stopEvent();
  649.             var record = this.view.store.getAt(this.view.indexOf(item));
  650.             this.startEdit(target, record.data[this.dataIndex]);
  651.             this.activeRecord = record;
  652.         }else{
  653.             e.preventDefault();
  654.         }
  655.     },
  656.     onSave : function(ed, value){
  657.         this.activeRecord.set(this.dataIndex, value);
  658.     }
  659. });
  660. Ext.DataView.DragSelector = function(cfg){
  661.     cfg = cfg || {};
  662.     var view, proxy, tracker;
  663.     var rs, bodyRegion, dragRegion = new Ext.lib.Region(0,0,0,0);
  664.     var dragSafe = cfg.dragSafe === true;
  665.     this.init = function(dataView){
  666.         view = dataView;
  667.         view.on('render', onRender);
  668.     };
  669.     function fillRegions(){
  670.         rs = [];
  671.         view.all.each(function(el){
  672.             rs[rs.length] = el.getRegion();
  673.         });
  674.         bodyRegion = view.el.getRegion();
  675.     }
  676.     function cancelClick(){
  677.         return false;
  678.     }
  679.     function onBeforeStart(e){
  680.         return !dragSafe || e.target == view.el.dom;
  681.     }
  682.     function onStart(e){
  683.         view.on('containerclick', cancelClick, view, {single:true});
  684.         if(!proxy){
  685.             proxy = view.el.createChild({cls:'x-view-selector'});
  686.         }else{
  687.             if(proxy.dom.parentNode !== view.el.dom){
  688.                 view.el.dom.appendChild(proxy.dom);
  689.             }
  690.             proxy.setDisplayed('block');
  691.         }
  692.         fillRegions();
  693.         view.clearSelections();
  694.     }
  695.     function onDrag(e){
  696.         var startXY = tracker.startXY;
  697.         var xy = tracker.getXY();
  698.         var x = Math.min(startXY[0], xy[0]);
  699.         var y = Math.min(startXY[1], xy[1]);
  700.         var w = Math.abs(startXY[0] - xy[0]);
  701.         var h = Math.abs(startXY[1] - xy[1]);
  702.         dragRegion.left = x;
  703.         dragRegion.top = y;
  704.         dragRegion.right = x+w;
  705.         dragRegion.bottom = y+h;
  706.         dragRegion.constrainTo(bodyRegion);
  707.         proxy.setRegion(dragRegion);
  708.         for(var i = 0, len = rs.length; i < len; i++){
  709.             var r = rs[i], sel = dragRegion.intersect(r);
  710.             if(sel && !r.selected){
  711.                 r.selected = true;
  712.                 view.select(i, true);
  713.             }else if(!sel && r.selected){
  714.                 r.selected = false;
  715.                 view.deselect(i);
  716.             }
  717.         }
  718.     }
  719.     function onEnd(e){
  720.         if (!Ext.isIE) {
  721.             view.un('containerclick', cancelClick, view);    
  722.         }        
  723.         if(proxy){
  724.             proxy.setDisplayed(false);
  725.         }
  726.     }
  727.     function onRender(view){
  728.         tracker = new Ext.dd.DragTracker({
  729.             onBeforeStart: onBeforeStart,
  730.             onStart: onStart,
  731.             onDrag: onDrag,
  732.             onEnd: onEnd
  733.         });
  734.         tracker.initEl(view.el);
  735.     }
  736. };Ext.ns('Ext.ux.form'); /**  * @class Ext.ux.form.FileUploadField  * @extends Ext.form.TextField  * Creates a file upload field.  * @xtype fileuploadfield  */ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField,  {     /**      * @cfg {String} buttonText The button text to display on the upload button (defaults to      * 'Browse...').  Note that if you supply a value for {@link #buttonCfg}, the buttonCfg.text      * value will be used instead if available.      */     buttonText: 'Browse...',     /**      * @cfg {Boolean} buttonOnly True to display the file upload field as a button with no visible      * text field (defaults to false).  If true, all inherited TextField members will still be available.      */     buttonOnly: false,     /**      * @cfg {Number} buttonOffset The number of pixels of space reserved between the button and the text field      * (defaults to 3).  Note that this only applies if {@link #buttonOnly} = false.      */     buttonOffset: 3,     /**      * @cfg {Object} buttonCfg A standard {@link Ext.Button} config object.      */     // private     readOnly: true,     /**      * @hide      * @method autoSize      */     autoSize: Ext.emptyFn,     // private     initComponent: function(){         Ext.ux.form.FileUploadField.superclass.initComponent.call(this);         this.addEvents(             /**              * @event fileselected              * Fires when the underlying file input field's value has changed from the user              * selecting a new file from the system file selection dialog.              * @param {Ext.ux.form.FileUploadField} this              * @param {String} value The file value returned by the underlying file input field              */             'fileselected'         );     },     // private     onRender : function(ct, position){         Ext.ux.form.FileUploadField.superclass.onRender.call(this, ct, position);         this.wrap = this.el.wrap({cls:'x-form-field-wrap x-form-file-wrap'});         this.el.addClass('x-form-file-text');         this.el.dom.removeAttribute('name');         this.createFileInput();         var btnCfg = Ext.applyIf(this.buttonCfg || {}, {             text: this.buttonText         });         this.button = new Ext.Button(Ext.apply(btnCfg, {             renderTo: this.wrap,             cls: 'x-form-file-btn' + (btnCfg.iconCls ? ' x-btn-icon' : '')         }));         if(this.buttonOnly){             this.el.hide();             this.wrap.setWidth(this.button.getEl().getWidth());         }         this.bindListeners();         this.resizeEl = this.positionEl = this.wrap;     },          bindListeners: function(){         this.fileInput.on({             scope: this,             mouseenter: function() {                 this.button.addClass(['x-btn-over','x-btn-focus'])             },             mouseleave: function(){                 this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click'])             },             mousedown: function(){                 this.button.addClass('x-btn-click')             },             mouseup: function(){                 this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click'])             },             change: function(){                 var v = this.fileInput.dom.value;                 this.setValue(v);                 this.fireEvent('fileselected', this, v);                 }         });      },          createFileInput : function() {         this.fileInput = this.wrap.createChild({             id: this.getFileInputId(),             name: this.name||this.getId(),             cls: 'x-form-file',             tag: 'input',             type: 'file',             size: 1         });     },          reset : function(){         this.fileInput.remove();         this.createFileInput();         this.bindListeners();         Ext.ux.form.FileUploadField.superclass.reset.call(this);     },     // private     getFileInputId: function(){         return this.id + '-file';     },     // private     onResize : function(w, h){         Ext.ux.form.FileUploadField.superclass.onResize.call(this, w, h);         this.wrap.setWidth(w);         if(!this.buttonOnly){             var w = this.wrap.getWidth() - this.button.getEl().getWidth() - this.buttonOffset;             this.el.setWidth(w);         }     },     // private     onDestroy: function(){         Ext.ux.form.FileUploadField.superclass.onDestroy.call(this);         Ext.destroy(this.fileInput, this.button, this.wrap);     },          onDisable: function(){         Ext.ux.form.FileUploadField.superclass.onDisable.call(this);         this.doDisable(true);     },          onEnable: function(){         Ext.ux.form.FileUploadField.superclass.onEnable.call(this);         this.doDisable(false);     },          // private     doDisable: function(disabled){         this.fileInput.dom.disabled = disabled;         this.button.setDisabled(disabled);     },     // private     preFocus : Ext.emptyFn,     // private     alignErrorIcon : function(){         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);     } }); Ext.reg('fileuploadfield', Ext.ux.form.FileUploadField); // backwards compat Ext.form.FileUploadField = Ext.ux.form.FileUploadField; /**
  737.  * @class Ext.ux.GMapPanel
  738.  * @extends Ext.Panel
  739.  * @author Shea Frederick
  740.  */
  741. Ext.ux.GMapPanel = Ext.extend(Ext.Panel, {
  742.     initComponent : function(){
  743.         
  744.         var defConfig = {
  745.             plain: true,
  746.             zoomLevel: 3,
  747.             yaw: 180,
  748.             pitch: 0,
  749.             zoom: 0,
  750.             gmapType: 'map',
  751.             border: false
  752.         };
  753.         
  754.         Ext.applyIf(this,defConfig);
  755.         
  756.         Ext.ux.GMapPanel.superclass.initComponent.call(this);        
  757.     },
  758.     afterRender : function(){
  759.         
  760.         var wh = this.ownerCt.getSize();
  761.         Ext.applyIf(this, wh);
  762.         
  763.         Ext.ux.GMapPanel.superclass.afterRender.call(this);    
  764.         
  765.         if (this.gmapType === 'map'){
  766.             this.gmap = new GMap2(this.body.dom);
  767.         }
  768.         
  769.         if (this.gmapType === 'panorama'){
  770.             this.gmap = new GStreetviewPanorama(this.body.dom);
  771.         }
  772.         
  773.         if (typeof this.addControl == 'object' && this.gmapType === 'map') {
  774.             this.gmap.addControl(this.addControl);
  775.         }
  776.         
  777.         if (typeof this.setCenter === 'object') {
  778.             if (typeof this.setCenter.geoCodeAddr === 'string'){
  779.                 this.geoCodeLookup(this.setCenter.geoCodeAddr);
  780.             }else{
  781.                 if (this.gmapType === 'map'){
  782.                     var point = new GLatLng(this.setCenter.lat,this.setCenter.lng);
  783.                     this.gmap.setCenter(point, this.zoomLevel);    
  784.                 }
  785.                 if (typeof this.setCenter.marker === 'object' && typeof point === 'object'){
  786.                     this.addMarker(point,this.setCenter.marker,this.setCenter.marker.clear);
  787.                 }
  788.             }
  789.             if (this.gmapType === 'panorama'){
  790.                 this.gmap.setLocationAndPOV(new GLatLng(this.setCenter.lat,this.setCenter.lng), {yaw: this.yaw, pitch: this.pitch, zoom: this.zoom});
  791.             }
  792.         }
  793.         GEvent.bind(this.gmap, 'load', this, function(){
  794.             this.onMapReady();
  795.         });
  796.     },
  797.     onMapReady : function(){
  798.         this.addMarkers(this.markers);
  799.         this.addMapControls();
  800.         this.addOptions();  
  801.     },
  802.     onResize : function(w, h){
  803.         if (typeof this.getMap() == 'object') {
  804.             this.gmap.checkResize();
  805.         }
  806.         
  807.         Ext.ux.GMapPanel.superclass.onResize.call(this, w, h);
  808.     },
  809.     setSize : function(width, height, animate){
  810.         
  811.         if (typeof this.getMap() == 'object') {
  812.             this.gmap.checkResize();
  813.         }
  814.         
  815.         Ext.ux.GMapPanel.superclass.setSize.call(this, width, height, animate);
  816.         
  817.     },
  818.     getMap : function(){
  819.         
  820.         return this.gmap;
  821.         
  822.     },
  823.     getCenter : function(){
  824.         
  825.         return this.getMap().getCenter();
  826.         
  827.     },
  828.     getCenterLatLng : function(){
  829.         
  830.         var ll = this.getCenter();
  831.         return {lat: ll.lat(), lng: ll.lng()};
  832.         
  833.     },
  834.     addMarkers : function(markers) {
  835.         
  836.         if (Ext.isArray(markers)){
  837.             for (var i = 0; i < markers.length; i++) {
  838.                 var mkr_point = new GLatLng(markers[i].lat,markers[i].lng);
  839.                 this.addMarker(mkr_point,markers[i].marker,false,markers[i].setCenter, markers[i].listeners);
  840.             }
  841.         }
  842.         
  843.     },
  844.     addMarker : function(point, marker, clear, center, listeners){
  845.         
  846.         Ext.applyIf(marker,G_DEFAULT_ICON);
  847.         if (clear === true){
  848.             this.getMap().clearOverlays();
  849.         }
  850.         if (center === true) {
  851.             this.getMap().setCenter(point, this.zoomLevel);
  852.         }
  853.         var mark = new GMarker(point,marker);
  854.         if (typeof listeners === 'object'){
  855.             for (evt in listeners) {
  856.                 GEvent.bind(mark, evt, this, listeners[evt]);
  857.             }
  858.         }
  859.         this.getMap().addOverlay(mark);
  860.     },
  861.     addMapControls : function(){
  862.         
  863.         if (this.gmapType === 'map') {
  864.             if (Ext.isArray(this.mapControls)) {
  865.                 for(i=0;i<this.mapControls.length;i++){
  866.                     this.addMapControl(this.mapControls[i]);
  867.                 }
  868.             }else if(typeof this.mapControls === 'string'){
  869.                 this.addMapControl(this.mapControls);
  870.             }else if(typeof this.mapControls === 'object'){
  871.                 this.getMap().addControl(this.mapControls);
  872.             }
  873.         }
  874.         
  875.     },
  876.     addMapControl : function(mc){
  877.         
  878.         var mcf = window[mc];
  879.         if (typeof mcf === 'function') {
  880.             this.getMap().addControl(new mcf());
  881.         }    
  882.         
  883.     },
  884.     addOptions : function(){
  885.         
  886.         if (Ext.isArray(this.mapConfOpts)) {
  887.             var mc;
  888.             for(i=0;i<this.mapConfOpts.length;i++){
  889.                 this.addOption(this.mapConfOpts[i]);
  890.             }
  891.         }else if(typeof this.mapConfOpts === 'string'){
  892.             this.addOption(this.mapConfOpts);
  893.         }        
  894.         
  895.     },
  896.     addOption : function(mc){
  897.         
  898.         var mcf = this.getMap()[mc];
  899.         if (typeof mcf === 'function') {
  900.             this.getMap()[mc]();
  901.         }    
  902.         
  903.     },
  904.     geoCodeLookup : function(addr) {
  905.         
  906.         this.geocoder = new GClientGeocoder();
  907.         this.geocoder.getLocations(addr, this.addAddressToMap.createDelegate(this));
  908.         
  909.     },
  910.     addAddressToMap : function(response) {
  911.         
  912.         if (!response || response.Status.code != 200) {
  913.             Ext.MessageBox.alert('Error', 'Code '+response.Status.code+' Error Returned');
  914.         }else{
  915.             place = response.Placemark[0];
  916.             addressinfo = place.AddressDetails;
  917.             accuracy = addressinfo.Accuracy;
  918.             if (accuracy === 0) {
  919.                 Ext.MessageBox.alert('Unable to Locate Address', 'Unable to Locate the Address you provided');
  920.             }else{
  921.                 if (accuracy < 7) {
  922.                     Ext.MessageBox.alert('Address Accuracy', 'The address provided has a low accuracy.<br><br>Level '+accuracy+' Accuracy (8 = Exact Match, 1 = Vague Match)');
  923.                 }else{
  924.                     point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
  925.                     if (typeof this.setCenter.marker === 'object' && typeof point === 'object'){
  926.                         this.addMarker(point,this.setCenter.marker,this.setCenter.marker.clear,true, this.setCenter.listeners);
  927.                     }
  928.                 }
  929.             }
  930.         }
  931.         
  932.     }
  933.  
  934. });
  935. Ext.reg('gmappanel', Ext.ux.GMapPanel); Ext.namespace('Ext.ux.grid');
  936. /**
  937.  * @class Ext.ux.grid.GridFilters
  938.  * @extends Ext.util.Observable
  939.  * <p>GridFilter is a plugin (<code>ptype='gridfilters'</code>) for grids that
  940.  * allow for a slightly more robust representation of filtering than what is
  941.  * provided by the default store.</p>
  942.  * <p>Filtering is adjusted by the user using the grid's column header menu
  943.  * (this menu can be disabled through configuration). Through this menu users
  944.  * can configure, enable, and disable filters for each column.</p>
  945.  * <p><b><u>Features:</u></b></p>
  946.  * <div class="mdetail-params"><ul>
  947.  * <li><b>Filtering implementations</b> :
  948.  * <div class="sub-desc">
  949.  * Default filtering for Strings, Numeric Ranges, Date Ranges, Lists (which can
  950.  * be backed by a Ext.data.Store), and Boolean. Additional custom filter types
  951.  * and menus are easily created by extending Ext.ux.grid.filter.Filter.
  952.  * </div></li>
  953.  * <li><b>Graphical indicators</b> :
  954.  * <div class="sub-desc">
  955.  * Columns that are filtered have {@link #filterCls a configurable css class}
  956.  * applied to the column headers.
  957.  * </div></li>
  958.  * <li><b>Paging</b> :
  959.  * <div class="sub-desc">
  960.  * If specified as a plugin to the grid's configured PagingToolbar, the current page
  961.  * will be reset to page 1 whenever you update the filters.
  962.  * </div></li>
  963.  * <li><b>Automatic Reconfiguration</b> :
  964.  * <div class="sub-desc">
  965.  * Filters automatically reconfigure when the grid 'reconfigure' event fires.
  966.  * </div></li>
  967.  * <li><b>Stateful</b> :
  968.  * Filter information will be persisted across page loads by specifying a
  969.  * <code>stateId</code> in the Grid configuration.
  970.  * <div class="sub-desc">
  971.  * The filter collection binds to the
  972.  * <code>{@link Ext.grid.GridPanel#beforestaterestore beforestaterestore}</code>
  973.  * and <code>{@link Ext.grid.GridPanel#beforestatesave beforestatesave}</code>
  974.  * events in order to be stateful. 
  975.  * </div></li>
  976.  * <li><b>Grid Changes</b> :
  977.  * <div class="sub-desc"><ul>
  978.  * <li>A <code>filters</code> <i>property</i> is added to the grid pointing to
  979.  * this plugin.</li>
  980.  * <li>A <code>filterupdate</code> <i>event</i> is added to the grid and is
  981.  * fired upon onStateChange completion.</li>
  982.  * </ul></div></li>
  983.  * <li><b>Server side code examples</b> :
  984.  * <div class="sub-desc"><ul>
  985.  * <li><a href="http://www.vinylfox.com/extjs/grid-filter-php-backend-code.php">PHP</a> - (Thanks VinylFox)</li>
  986.  * <li><a href="http://extjs.com/forum/showthread.php?p=77326#post77326">Ruby on Rails</a> - (Thanks Zyclops)</li>
  987.  * <li><a href="http://extjs.com/forum/showthread.php?p=176596#post176596">Ruby on Rails</a> - (Thanks Rotomaul)</li>
  988.  * <li><a href="http://www.debatablybeta.com/posts/using-extjss-grid-filtering-with-django/">Python</a> - (Thanks Matt)</li>
  989.  * <li><a href="http://mcantrell.wordpress.com/2008/08/22/extjs-grids-and-grails/">Grails</a> - (Thanks Mike)</li>
  990.  * </ul></div></li>
  991.  * </ul></div>
  992.  * <p><b><u>Example usage:</u></b></p>
  993.  * <pre><code>    
  994. var store = new Ext.data.GroupingStore({
  995.     ...
  996. });
  997.  
  998. var filters = new Ext.ux.grid.GridFilters({
  999.     autoReload: false, //don&#39;t reload automatically
  1000.     local: true, //only filter locally
  1001.     // filters may be configured through the plugin,
  1002.     // or in the column definition within the column model configuration
  1003.     filters: [{
  1004.         type: 'numeric',
  1005.         dataIndex: 'id'
  1006.     }, {
  1007.         type: 'string',
  1008.         dataIndex: 'name'
  1009.     }, {
  1010.         type: 'numeric',
  1011.         dataIndex: 'price'
  1012.     }, {
  1013.         type: 'date',
  1014.         dataIndex: 'dateAdded'
  1015.     }, {
  1016.         type: 'list',
  1017.         dataIndex: 'size',
  1018.         options: ['extra small', 'small', 'medium', 'large', 'extra large'],
  1019.         phpMode: true
  1020.     }, {
  1021.         type: 'boolean',
  1022.         dataIndex: 'visible'
  1023.     }]
  1024. });
  1025. var cm = new Ext.grid.ColumnModel([{
  1026.     ...
  1027. }]);
  1028.  
  1029. var grid = new Ext.grid.GridPanel({
  1030.      ds: store,
  1031.      cm: cm,
  1032.      view: new Ext.grid.GroupingView(),
  1033.      plugins: [filters],
  1034.      height: 400,
  1035.      width: 700,
  1036.      bbar: new Ext.PagingToolbar({
  1037.          store: store,
  1038.          pageSize: 15,
  1039.          plugins: [filters] //reset page to page 1 if filters change
  1040.      })
  1041.  });
  1042. store.load({params: {start: 0, limit: 15}});
  1043. // a filters property is added to the grid
  1044. grid.filters
  1045.  * </code></pre>
  1046.  */
  1047. Ext.ux.grid.GridFilters = Ext.extend(Ext.util.Observable, {
  1048.     /**
  1049.      * @cfg {Boolean} autoReload
  1050.      * Defaults to true, reloading the datasource when a filter change happens.
  1051.      * Set this to false to prevent the datastore from being reloaded if there
  1052.      * are changes to the filters.  See <code>{@link updateBuffer}</code>.
  1053.      */
  1054.     autoReload : true,
  1055.     /**
  1056.      * @cfg {Boolean} encode
  1057.      * Specify true for {@link #buildQuery} to use Ext.util.JSON.encode to
  1058.      * encode the filter query parameter sent with a remote request.
  1059.      * Defaults to false.
  1060.      */
  1061.     /**
  1062.      * @cfg {Array} filters
  1063.      * An Array of filters config objects. Refer to each filter type class for
  1064.      * configuration details specific to each filter type. Filters for Strings,
  1065.      * Numeric Ranges, Date Ranges, Lists, and Boolean are the standard filters
  1066.      * available.
  1067.      */
  1068.     /**
  1069.      * @cfg {String} filterCls
  1070.      * The css class to be applied to column headers with active filters.
  1071.      * Defaults to <tt>'ux-filterd-column'</tt>.
  1072.      */
  1073.     filterCls : 'ux-filtered-column',
  1074.     /**
  1075.      * @cfg {Boolean} local
  1076.      * <tt>true</tt> to use Ext.data.Store filter functions (local filtering)
  1077.      * instead of the default (<tt>false</tt>) server side filtering.
  1078.      */
  1079.     local : false,
  1080.     /**
  1081.      * @cfg {String} menuFilterText
  1082.      * defaults to <tt>'Filters'</tt>.
  1083.      */
  1084.     menuFilterText : 'Filters',
  1085.     /**
  1086.      * @cfg {String} paramPrefix
  1087.      * The url parameter prefix for the filters.
  1088.      * Defaults to <tt>'filter'</tt>.
  1089.      */
  1090.     paramPrefix : 'filter',
  1091.     /**
  1092.      * @cfg {Boolean} showMenu
  1093.      * Defaults to true, including a filter submenu in the default header menu.
  1094.      */
  1095.     showMenu : true,
  1096.     /**
  1097.      * @cfg {String} stateId
  1098.      * Name of the value to be used to store state information.
  1099.      */
  1100.     stateId : undefined,
  1101.     /**
  1102.      * @cfg {Integer} updateBuffer
  1103.      * Number of milliseconds to defer store updates since the last filter change.
  1104.      */
  1105.     updateBuffer : 500,
  1106.     /** @private */
  1107.     constructor : function (config) {
  1108.         config = config || {};
  1109.         this.deferredUpdate = new Ext.util.DelayedTask(this.reload, this);
  1110.         this.filters = new Ext.util.MixedCollection();
  1111.         this.filters.getKey = function (o) {
  1112.             return o ? o.dataIndex : null;
  1113.         };
  1114.         this.addFilters(config.filters);
  1115.         delete config.filters;
  1116.         Ext.apply(this, config);
  1117.     },
  1118.     /** @private */
  1119.     init : function (grid) {
  1120.         if (grid instanceof Ext.grid.GridPanel) {
  1121.             this.grid = grid;
  1122.             
  1123.             this.bindStore(this.grid.getStore(), true);
  1124.             // assumes no filters were passed in the constructor, so try and use ones from the colModel
  1125.             if(this.filters.getCount() == 0){
  1126.                 this.addFilters(this.grid.getColumnModel());
  1127.             }
  1128.           
  1129.             this.grid.filters = this;
  1130.              
  1131.             this.grid.addEvents({'filterupdate': true});
  1132.               
  1133.             grid.on({
  1134.                 scope: this,
  1135.                 beforestaterestore: this.applyState,
  1136.                 beforestatesave: this.saveState,
  1137.                 beforedestroy: this.destroy,
  1138.                 reconfigure: this.onReconfigure
  1139.             });
  1140.             
  1141.             if (grid.rendered){
  1142.                 this.onRender();
  1143.             } else {
  1144.                 grid.on({
  1145.                     scope: this,
  1146.                     single: true,
  1147.                     render: this.onRender
  1148.                 });
  1149.             }
  1150.                       
  1151.         } else if (grid instanceof Ext.PagingToolbar) {
  1152.             this.toolbar = grid;
  1153.         }
  1154.     },
  1155.         
  1156.     /**
  1157.      * @private
  1158.      * Handler for the grid's beforestaterestore event (fires before the state of the
  1159.      * grid is restored).
  1160.      * @param {Object} grid The grid object
  1161.      * @param {Object} state The hash of state values returned from the StateProvider.
  1162.      */   
  1163.     applyState : function (grid, state) {
  1164.         var key, filter;
  1165.         this.applyingState = true;
  1166.         this.clearFilters();
  1167.         if (state.filters) {
  1168.             for (key in state.filters) {
  1169.                 filter = this.filters.get(key);
  1170.                 if (filter) {
  1171.                     filter.setValue(state.filters[key]);
  1172.                     filter.setActive(true);
  1173.                 }
  1174.             }
  1175.         }
  1176.         this.deferredUpdate.cancel();
  1177.         if (this.local) {
  1178.             this.reload();
  1179.         }
  1180.         delete this.applyingState;
  1181.     },
  1182.     
  1183.     /**
  1184.      * Saves the state of all active filters
  1185.      * @param {Object} grid
  1186.      * @param {Object} state
  1187.      * @return {Boolean}
  1188.      */
  1189.     saveState : function (grid, state) {
  1190.         var filters = {};
  1191.         this.filters.each(function (filter) {
  1192.             if (filter.active) {
  1193.                 filters[filter.dataIndex] = filter.getValue();
  1194.             }
  1195.         });
  1196.         return (state.filters = filters);
  1197.     },
  1198.     
  1199.     /**
  1200.      * @private
  1201.      * Handler called when the grid is rendered
  1202.      */    
  1203.     onRender : function () {
  1204.         this.grid.getView().on('refresh', this.onRefresh, this);
  1205.         this.createMenu();
  1206.     },
  1207.     /**
  1208.      * @private
  1209.      * Handler called by the grid 'beforedestroy' event
  1210.      */    
  1211.     destroy : function () {
  1212.         this.removeAll();
  1213.         this.purgeListeners();
  1214.         if(this.filterMenu){
  1215.             Ext.menu.MenuMgr.unregister(this.filterMenu);
  1216.             this.filterMenu.destroy();
  1217.              this.filterMenu = this.menu.menu = null;            
  1218.         }
  1219.     },
  1220.     /**
  1221.      * Remove all filters, permanently destroying them.
  1222.      */    
  1223.     removeAll : function () {
  1224.         if(this.filters){
  1225.             Ext.destroy.apply(Ext, this.filters.items);
  1226.             // remove all items from the collection 
  1227.             this.filters.clear();
  1228.         }
  1229.     },
  1230.     /**
  1231.      * Changes the data store bound to this view and refreshes it.
  1232.      * @param {Store} store The store to bind to this view
  1233.      */
  1234.     bindStore : function(store, initial){
  1235.         if(!initial && this.store){
  1236.             if (this.local) {
  1237.                 store.un('load', this.onLoad, this);
  1238.             } else {
  1239.                 store.un('beforeload', this.onBeforeLoad, this);
  1240.             }
  1241.         }
  1242.         if(store){
  1243.             if (this.local) {
  1244.                 store.on('load', this.onLoad, this);
  1245.             } else {
  1246.                 store.on('beforeload', this.onBeforeLoad, this);
  1247.             }
  1248.         }
  1249.         this.store = store;
  1250.     },
  1251.     /**
  1252.      * @private
  1253.      * Handler called when the grid reconfigure event fires
  1254.      */    
  1255.     onReconfigure : function () {
  1256.         this.bindStore(this.grid.getStore());
  1257.         this.store.clearFilter();
  1258.         this.removeAll();
  1259.         this.addFilters(this.grid.getColumnModel());
  1260.         this.updateColumnHeadings();
  1261.     },
  1262.     createMenu : function () {
  1263.         var view = this.grid.getView(),
  1264.             hmenu = view.hmenu;
  1265.         if (this.showMenu && hmenu) {
  1266.             
  1267.             this.sep  = hmenu.addSeparator();
  1268.             this.filterMenu = new Ext.menu.Menu({
  1269.                 id: this.grid.id + '-filters-menu'
  1270.             }); 
  1271.             this.menu = hmenu.add({
  1272.                 checked: false,
  1273.                 itemId: 'filters',
  1274.                 text: this.menuFilterText,
  1275.                 menu: this.filterMenu
  1276.             });
  1277.             this.menu.on({
  1278.                 scope: this,
  1279.                 checkchange: this.onCheckChange,
  1280.                 beforecheckchange: this.onBeforeCheck
  1281.             });
  1282.             hmenu.on('beforeshow', this.onMenu, this);
  1283.         }
  1284.         this.updateColumnHeadings();
  1285.     },
  1286.     /**
  1287.      * @private
  1288.      * Get the filter menu from the filters MixedCollection based on the clicked header
  1289.      */
  1290.     getMenuFilter : function () {
  1291.         var view = this.grid.getView();
  1292.         if (!view || view.hdCtxIndex === undefined) {
  1293.             return null;
  1294.         }
  1295.         return this.filters.get(
  1296.             view.cm.config[view.hdCtxIndex].dataIndex
  1297.         );
  1298.     },
  1299.     /**
  1300.      * @private
  1301.      * Handler called by the grid's hmenu beforeshow event
  1302.      */    
  1303.     onMenu : function (filterMenu) {
  1304.         var filter = this.getMenuFilter();
  1305.         if (filter) {
  1306. /*            
  1307. TODO: lazy rendering
  1308.             if (!filter.menu) {
  1309.                 filter.menu = filter.createMenu();
  1310.             }
  1311. */
  1312.             this.menu.menu = filter.menu;
  1313.             this.menu.setChecked(filter.active, false);
  1314.             // disable the menu if filter.disabled explicitly set to true
  1315.             this.menu.setDisabled(filter.disabled === true);
  1316.         }
  1317.         
  1318.         this.menu.setVisible(filter !== undefined);
  1319.         this.sep.setVisible(filter !== undefined);
  1320.     },
  1321.     
  1322.     /** @private */
  1323.     onCheckChange : function (item, value) {
  1324.         this.getMenuFilter().setActive(value);
  1325.     },
  1326.     
  1327.     /** @private */
  1328.     onBeforeCheck : function (check, value) {
  1329.         return !value || this.getMenuFilter().isActivatable();
  1330.     },
  1331.     /**
  1332.      * @private
  1333.      * Handler for all events on filters.
  1334.      * @param {String} event Event name
  1335.      * @param {Object} filter Standard signature of the event before the event is fired
  1336.      */   
  1337.     onStateChange : function (event, filter) {
  1338.         if (event === 'serialize') {
  1339.             return;
  1340.         }
  1341.         if (filter == this.getMenuFilter()) {
  1342.             this.menu.setChecked(filter.active, false);
  1343.         }
  1344.         if ((this.autoReload || this.local) && !this.applyingState) {
  1345.             this.deferredUpdate.delay(this.updateBuffer);
  1346.         }
  1347.         this.updateColumnHeadings();
  1348.             
  1349.         if (!this.applyingState) {
  1350.             this.grid.saveState();
  1351.         }    
  1352.         this.grid.fireEvent('filterupdate', this, filter);
  1353.     },
  1354.     
  1355.     /**
  1356.      * @private
  1357.      * Handler for store's beforeload event when configured for remote filtering
  1358.      * @param {Object} store
  1359.      * @param {Object} options
  1360.      */
  1361.     onBeforeLoad : function (store, options) {
  1362.         options.params = options.params || {};
  1363.         this.cleanParams(options.params);       
  1364.         var params = this.buildQuery(this.getFilterData());
  1365.         Ext.apply(options.params, params);
  1366.     },
  1367.     
  1368.     /**
  1369.      * @private
  1370.      * Handler for store's load event when configured for local filtering
  1371.      * @param {Object} store
  1372.      * @param {Object} options
  1373.      */
  1374.     onLoad : function (store, options) {
  1375.         store.filterBy(this.getRecordFilter());
  1376.     },
  1377.     /**
  1378.      * @private
  1379.      * Handler called when the grid's view is refreshed
  1380.      */    
  1381.     onRefresh : function () {
  1382.         this.updateColumnHeadings();
  1383.     },
  1384.     /**
  1385.      * Update the styles for the header row based on the active filters
  1386.      */    
  1387.     updateColumnHeadings : function () {
  1388.         var view = this.grid.getView(),
  1389.             hds, i, len, filter;
  1390.         if (view.mainHd) {
  1391.             hds = view.mainHd.select('td').removeClass(this.filterCls);
  1392.             for (i = 0, len = view.cm.config.length; i < len; i++) {
  1393.                 filter = this.getFilter(view.cm.config[i].dataIndex);
  1394.                 if (filter && filter.active) {
  1395.                     hds.item(i).addClass(this.filterCls);
  1396.                 }
  1397.             }
  1398.         }
  1399.     },
  1400.     
  1401.     /** @private */
  1402.     reload : function () {
  1403.         if (this.local) {
  1404.             this.grid.store.clearFilter(true);
  1405.             this.grid.store.filterBy(this.getRecordFilter());
  1406.         } else {
  1407.             var start,
  1408.                 store = this.grid.store;
  1409.             this.deferredUpdate.cancel();
  1410.             if (this.toolbar) {
  1411.                 start = store.paramNames.start;
  1412.                 if (store.lastOptions && store.lastOptions.params && store.lastOptions.params[start]) {
  1413.                     store.lastOptions.params[start] = 0;
  1414.                 }
  1415.             }
  1416.             store.reload();
  1417.         }
  1418.     },
  1419.     
  1420.     /**
  1421.      * Method factory that generates a record validator for the filters active at the time
  1422.      * of invokation.
  1423.      * @private
  1424.      */
  1425.     getRecordFilter : function () {
  1426.         var f = [], len, i;
  1427.         this.filters.each(function (filter) {
  1428.             if (filter.active) {
  1429.                 f.push(filter);
  1430.             }
  1431.         });
  1432.         
  1433.         len = f.length;
  1434.         return function (record) {
  1435.             for (i = 0; i < len; i++) {
  1436.                 if (!f[i].validateRecord(record)) {
  1437.                     return false;
  1438.                 }
  1439.             }
  1440.             return true;
  1441.         };
  1442.     },
  1443.     
  1444.     /**
  1445.      * Adds a filter to the collection and observes it for state change.
  1446.      * @param {Object/Ext.ux.grid.filter.Filter} config A filter configuration or a filter object.
  1447.      * @return {Ext.ux.grid.filter.Filter} The existing or newly created filter object.
  1448.      */
  1449.     addFilter : function (config) {
  1450.         var Cls = this.getFilterClass(config.type),
  1451.             filter = config.menu ? config : (new Cls(config));
  1452.         this.filters.add(filter);
  1453.         
  1454.         Ext.util.Observable.capture(filter, this.onStateChange, this);
  1455.         return filter;
  1456.     },
  1457.     /**
  1458.      * Adds filters to the collection.
  1459.      * @param {Array/Ext.grid.ColumnModel} filters Either an Array of
  1460.      * filter configuration objects or an Ext.grid.ColumnModel.  The columns
  1461.      * of a passed Ext.grid.ColumnModel will be examined for a <code>filter</code>
  1462.      * property and, if present, will be used as the filter configuration object.   
  1463.      */
  1464.     addFilters : function (filters) {
  1465.         if (filters) {
  1466.             var i, len, filter, cm = false, dI;
  1467.             if (filters instanceof Ext.grid.ColumnModel) {
  1468.                 filters = filters.config;
  1469.                 cm = true;
  1470.             }
  1471.             for (i = 0, len = filters.length; i < len; i++) {
  1472.                 filter = false;
  1473.                 if (cm) {
  1474.                     dI = filters[i].dataIndex;
  1475.                     filter = filters[i].filter || filters[i].filterable;
  1476.                     if (filter){
  1477.                         filter = (filter === true) ? {} : filter;
  1478.                         Ext.apply(filter, {dataIndex:dI});
  1479.                         // filter type is specified in order of preference:
  1480.                         //     filter type specified in config
  1481.                         //     type specified in store's field's type config
  1482.                         filter.type = filter.type || this.store.fields.get(dI).type;  
  1483.                     }
  1484.                 } else {
  1485.                     filter = filters[i];
  1486.                 }
  1487.                 // if filter config found add filter for the column 
  1488.                 if (filter) {
  1489.                     this.addFilter(filter);
  1490.                 }
  1491.             }
  1492.         }
  1493.     },
  1494.     
  1495.     /**
  1496.      * Returns a filter for the given dataIndex, if one exists.
  1497.      * @param {String} dataIndex The dataIndex of the desired filter object.
  1498.      * @return {Ext.ux.grid.filter.Filter}
  1499.      */
  1500.     getFilter : function (dataIndex) {
  1501.         return this.filters.get(dataIndex);
  1502.     },
  1503.     /**
  1504.      * Turns all filters off. This does not clear the configuration information
  1505.      * (see {@link #removeAll}).
  1506.      */
  1507.     clearFilters : function () {
  1508.         this.filters.each(function (filter) {
  1509.             filter.setActive(false);
  1510.         });
  1511.     },
  1512.     /**
  1513.      * Returns an Array of the currently active filters.
  1514.      * @return {Array} filters Array of the currently active filters.
  1515.      */
  1516.     getFilterData : function () {
  1517.         var filters = [], i, len;
  1518.         this.filters.each(function (f) {
  1519.             if (f.active) {
  1520.                 var d = [].concat(f.serialize());
  1521.                 for (i = 0, len = d.length; i < len; i++) {
  1522.                     filters.push({
  1523.                         field: f.dataIndex,
  1524.                         data: d[i]
  1525.                     });
  1526.                 }
  1527.             }
  1528.         });
  1529.         return filters;
  1530.     },
  1531.     
  1532.     /**
  1533.      * Function to take the active filters data and build it into a query.
  1534.      * The format of the query depends on the <code>{@link #encode}</code>
  1535.      * configuration:
  1536.      * <div class="mdetail-params"><ul>
  1537.      * 
  1538.      * <li><b><tt>false</tt></b> : <i>Default</i>
  1539.      * <div class="sub-desc">
  1540.      * Flatten into query string of the form (assuming <code>{@link #paramPrefix}='filters'</code>:
  1541.      * <pre><code>
  1542. filters[0][field]="someDataIndex"&
  1543. filters[0][data][comparison]="someValue1"&
  1544. filters[0][data][type]="someValue2"&
  1545. filters[0][data][value]="someValue3"&
  1546.      * </code></pre>
  1547.      * </div></li>
  1548.      * <li><b><tt>true</tt></b> : 
  1549.      * <div class="sub-desc">
  1550.      * JSON encode the filter data
  1551.      * <pre><code>
  1552. filters[0][field]="someDataIndex"&
  1553. filters[0][data][comparison]="someValue1"&
  1554. filters[0][data][type]="someValue2"&
  1555. filters[0][data][value]="someValue3"&
  1556.      * </code></pre>
  1557.      * </div></li>
  1558.      * </ul></div>
  1559.      * Override this method to customize the format of the filter query for remote requests.
  1560.      * @param {Array} filters A collection of objects representing active filters and their configuration.
  1561.      *    Each element will take the form of {field: dataIndex, data: filterConf}. dataIndex is not assured
  1562.      *    to be unique as any one filter may be a composite of more basic filters for the same dataIndex.
  1563.      * @return {Object} Query keys and values
  1564.      */
  1565.     buildQuery : function (filters) {
  1566.         var p = {}, i, f, root, dataPrefix, key, tmp,
  1567.             len = filters.length;
  1568.         if (!this.encode){
  1569.             for (i = 0; i < len; i++) {
  1570.                 f = filters[i];
  1571.                 root = [this.paramPrefix, '[', i, ']'].join('');
  1572.                 p[root + '[field]'] = f.field;
  1573.                 
  1574.                 dataPrefix = root + '[data]';
  1575.                 for (key in f.data) {
  1576.                     p[[dataPrefix, '[', key, ']'].join('')] = f.data[key];
  1577.                 }
  1578.             }
  1579.         } else {
  1580.             tmp = [];
  1581.             for (i = 0; i < len; i++) {
  1582.                 f = filters[i];
  1583.                 tmp.push(Ext.apply(
  1584.                     {},
  1585.                     {field: f.field},
  1586.                     f.data
  1587.                 ));
  1588.             }
  1589.             // only build if there is active filter 
  1590.             if (tmp.length > 0){
  1591.                 p[this.paramPrefix] = Ext.util.JSON.encode(tmp);
  1592.             }
  1593.         }
  1594.         return p;
  1595.     },
  1596.     
  1597.     /**
  1598.      * Removes filter related query parameters from the provided object.
  1599.      * @param {Object} p Query parameters that may contain filter related fields.
  1600.      */
  1601.     cleanParams : function (p) {
  1602.         // if encoding just delete the property
  1603.         if (this.encode) {
  1604.             delete p[this.paramPrefix];
  1605.         // otherwise scrub the object of filter data
  1606.         } else {
  1607.             var regex, key;
  1608.             regex = new RegExp('^' + this.paramPrefix + '[[0-9]+]');
  1609.             for (key in p) {
  1610.                 if (regex.test(key)) {
  1611.                     delete p[key];
  1612.                 }
  1613.             }
  1614.         }
  1615.     },
  1616.     
  1617.     /**
  1618.      * Function for locating filter classes, overwrite this with your favorite
  1619.      * loader to provide dynamic filter loading.
  1620.      * @param {String} type The type of filter to load ('Filter' is automatically
  1621.      * appended to the passed type; eg, 'string' becomes 'StringFilter').
  1622.      * @return {Class} The Ext.ux.grid.filter.Class 
  1623.      */
  1624.     getFilterClass : function (type) {
  1625.         // map the supported Ext.data.Field type values into a supported filter
  1626.         switch(type) {
  1627.             case 'auto':
  1628.               type = 'string';
  1629.               break;
  1630.             case 'int':
  1631.             case 'float':
  1632.               type = 'numeric';
  1633.               break;
  1634.         }
  1635.         return Ext.ux.grid.filter[type.substr(0, 1).toUpperCase() + type.substr(1) + 'Filter'];
  1636.     }
  1637. });
  1638. // register ptype
  1639. Ext.preg('gridfilters', Ext.ux.grid.GridFilters);
  1640. Ext.namespace('Ext.ux.grid.filter');
  1641. /** 
  1642.  * @class Ext.ux.grid.filter.Filter
  1643.  * @extends Ext.util.Observable
  1644.  * Abstract base class for filter implementations.
  1645.  */
  1646. Ext.ux.grid.filter.Filter = Ext.extend(Ext.util.Observable, {
  1647.     /**
  1648.      * @cfg {Boolean} active
  1649.      * Indicates the initial status of the filter (defaults to false).
  1650.      */
  1651.     active : false,
  1652.     /**
  1653.      * True if this filter is active.  Use setActive() to alter after configuration.
  1654.      * @type Boolean
  1655.      * @property active
  1656.      */
  1657.     /**
  1658.      * @cfg {String} dataIndex 
  1659.      * The {@link Ext.data.Store} dataIndex of the field this filter represents.
  1660.      * The dataIndex does not actually have to exist in the store.
  1661.      */
  1662.     dataIndex : null,
  1663.     /**
  1664.      * The filter configuration menu that will be installed into the filter submenu of a column menu.
  1665.      * @type Ext.menu.Menu
  1666.      * @property
  1667.      */
  1668.     menu : null,
  1669.     /**
  1670.      * @cfg {Number} updateBuffer
  1671.      * Number of milliseconds to wait after user interaction to fire an update. Only supported 
  1672.      * by filters: 'list', 'numeric', and 'string'. Defaults to 500.
  1673.      */
  1674.     updateBuffer : 500,
  1675.     constructor : function (config) {
  1676.         Ext.apply(this, config);
  1677.             
  1678.         this.addEvents(
  1679.             /**
  1680.              * @event activate
  1681.              * Fires when an inactive filter becomes active
  1682.              * @param {Ext.ux.grid.filter.Filter} this
  1683.              */
  1684.             'activate',
  1685.             /**
  1686.              * @event deactivate
  1687.              * Fires when an active filter becomes inactive
  1688.              * @param {Ext.ux.grid.filter.Filter} this
  1689.              */
  1690.             'deactivate',
  1691.             /**
  1692.              * @event serialize
  1693.              * Fires after the serialization process. Use this to attach additional parameters to serialization
  1694.              * data before it is encoded and sent to the server.
  1695.              * @param {Array/Object} data A map or collection of maps representing the current filter configuration.
  1696.              * @param {Ext.ux.grid.filter.Filter} filter The filter being serialized.
  1697.              */
  1698.             'serialize',
  1699.             /**
  1700.              * @event update
  1701.              * Fires when a filter configuration has changed
  1702.              * @param {Ext.ux.grid.filter.Filter} this The filter object.
  1703.              */
  1704.             'update'
  1705.         );
  1706.         Ext.ux.grid.filter.Filter.superclass.constructor.call(this);
  1707.         this.menu = new Ext.menu.Menu();
  1708.         this.init(config);
  1709.         if(config && config.value){
  1710.             this.setValue(config.value);
  1711.             this.setActive(config.active !== false, true);
  1712.             delete config.value;
  1713.         }
  1714.     },
  1715.     /**
  1716.      * Destroys this filter by purging any event listeners, and removing any menus.
  1717.      */
  1718.     destroy : function(){
  1719.         if (this.menu){
  1720.             this.menu.destroy();
  1721.         }
  1722.         this.purgeListeners();
  1723.     },
  1724.     /**
  1725.      * Template method to be implemented by all subclasses that is to
  1726.      * initialize the filter and install required menu items.
  1727.      * Defaults to Ext.emptyFn.
  1728.      */
  1729.     init : Ext.emptyFn,
  1730.     
  1731.     /**
  1732.      * Template method to be implemented by all subclasses that is to
  1733.      * get and return the value of the filter.
  1734.      * Defaults to Ext.emptyFn.
  1735.      * @return {Object} The 'serialized' form of this filter
  1736.      * @methodOf Ext.ux.grid.filter.Filter
  1737.      */
  1738.     getValue : Ext.emptyFn,
  1739.     
  1740.     /**
  1741.      * Template method to be implemented by all subclasses that is to
  1742.      * set the value of the filter and fire the 'update' event.
  1743.      * Defaults to Ext.emptyFn.
  1744.      * @param {Object} data The value to set the filter
  1745.      * @methodOf Ext.ux.grid.filter.Filter
  1746.      */
  1747.     setValue : Ext.emptyFn,
  1748.     
  1749.     /**
  1750.      * Template method to be implemented by all subclasses that is to
  1751.      * return <tt>true</tt> if the filter has enough configuration information to be activated.
  1752.      * Defaults to <tt>return true</tt>.
  1753.      * @return {Boolean}
  1754.      */
  1755.     isActivatable : function(){
  1756.         return true;
  1757.     },
  1758.     
  1759.     /**
  1760.      * Template method to be implemented by all subclasses that is to
  1761.      * get and return serialized filter data for transmission to the server.
  1762.      * Defaults to Ext.emptyFn.
  1763.      */
  1764.     getSerialArgs : Ext.emptyFn,
  1765.     /**
  1766.      * Template method to be implemented by all subclasses that is to
  1767.      * validates the provided Ext.data.Record against the filters configuration.
  1768.      * Defaults to <tt>return true</tt>.
  1769.      * @param {Ext.data.Record} record The record to validate
  1770.      * @return {Boolean} true if the record is valid within the bounds
  1771.      * of the filter, false otherwise.
  1772.      */
  1773.     validateRecord : function(){
  1774.         return true;
  1775.     },
  1776.     /**
  1777.      * Returns the serialized filter data for transmission to the server
  1778.      * and fires the 'serialize' event.
  1779.      * @return {Object/Array} An object or collection of objects containing
  1780.      * key value pairs representing the current configuration of the filter.
  1781.      * @methodOf Ext.ux.grid.filter.Filter
  1782.      */
  1783.     serialize : function(){
  1784.         var args = this.getSerialArgs();
  1785.         this.fireEvent('serialize', args, this);
  1786.         return args;
  1787.     },
  1788.     /** @private */
  1789.     fireUpdate : function(){
  1790.         if (this.active) {
  1791.             this.fireEvent('update', this);
  1792.         }
  1793.         this.setActive(this.isActivatable());
  1794.     },
  1795.     
  1796.     /**
  1797.      * Sets the status of the filter and fires the appropriate events.
  1798.      * @param {Boolean} active        The new filter state.
  1799.      * @param {Boolean} suppressEvent True to prevent events from being fired.
  1800.      * @methodOf Ext.ux.grid.filter.Filter
  1801.      */
  1802.     setActive : function(active, suppressEvent){
  1803.         if(this.active != active){
  1804.             this.active = active;
  1805.             if (suppressEvent !== true) {
  1806.                 this.fireEvent(active ? 'activate' : 'deactivate', this);
  1807.             }
  1808.         }
  1809.     }    
  1810. });/** 
  1811.  * @class Ext.ux.grid.filter.BooleanFilter
  1812.  * @extends Ext.ux.grid.filter.Filter
  1813.  * Boolean filters use unique radio group IDs (so you can have more than one!)
  1814.  * <p><b><u>Example Usage:</u></b></p>
  1815.  * <pre><code>    
  1816. var filters = new Ext.ux.grid.GridFilters({
  1817.     ...
  1818.     filters: [{
  1819.         // required configs
  1820.         type: 'boolean',
  1821.         dataIndex: 'visible'
  1822.         // optional configs
  1823.         defaultValue: null, // leave unselected (false selected by default)
  1824.         yesText: 'Yes',     // default
  1825.         noText: 'No'        // default
  1826.     }]
  1827. });
  1828.  * </code></pre>
  1829.  */
  1830. Ext.ux.grid.filter.BooleanFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
  1831. /**
  1832.  * @cfg {Boolean} defaultValue
  1833.  * Set this to null if you do not want either option to be checked by default. Defaults to false.
  1834.  */
  1835. defaultValue : false,
  1836. /**
  1837.  * @cfg {String} yesText
  1838.  * Defaults to 'Yes'.
  1839.  */
  1840. yesText : 'Yes',
  1841. /**
  1842.  * @cfg {String} noText
  1843.  * Defaults to 'No'.
  1844.  */
  1845. noText : 'No',
  1846.     /**  
  1847.      * @private
  1848.      * Template method that is to initialize the filter and install required menu items.
  1849.      */
  1850.     init : function (config) {
  1851.         var gId = Ext.id();
  1852. this.options = [
  1853. new Ext.menu.CheckItem({text: this.yesText, group: gId, checked: this.defaultValue === true}),
  1854. new Ext.menu.CheckItem({text: this.noText, group: gId, checked: this.defaultValue === false})];
  1855. this.menu.add(this.options[0], this.options[1]);
  1856. for(var i=0; i<this.options.length; i++){
  1857. this.options[i].on('click', this.fireUpdate, this);
  1858. this.options[i].on('checkchange', this.fireUpdate, this);
  1859. }
  1860. },
  1861.     /**
  1862.      * @private
  1863.      * Template method that is to get and return the value of the filter.
  1864.      * @return {String} The value of this filter
  1865.      */
  1866.     getValue : function () {
  1867. return this.options[0].checked;
  1868. },
  1869.     /**
  1870.      * @private
  1871.      * Template method that is to set the value of the filter.
  1872.      * @param {Object} value The value to set the filter
  1873.      */
  1874. setValue : function (value) {
  1875. this.options[value ? 0 : 1].setChecked(true);
  1876. },
  1877.     /**
  1878.      * @private
  1879.      * Template method that is to get and return serialized filter data for
  1880.      * transmission to the server.
  1881.      * @return {Object/Array} An object or collection of objects containing
  1882.      * key value pairs representing the current configuration of the filter.
  1883.      */
  1884.     getSerialArgs : function () {
  1885. var args = {type: 'boolean', value: this.getValue()};
  1886. return args;
  1887. },
  1888.     /**
  1889.      * Template method that is to validate the provided Ext.data.Record
  1890.      * against the filters configuration.
  1891.      * @param {Ext.data.Record} record The record to validate
  1892.      * @return {Boolean} true if the record is valid within the bounds
  1893.      * of the filter, false otherwise.
  1894.      */
  1895.     validateRecord : function (record) {
  1896. return record.get(this.dataIndex) == this.getValue();
  1897. }
  1898. });/** 
  1899.  * @class Ext.ux.grid.filter.DateFilter
  1900.  * @extends Ext.ux.grid.filter.Filter
  1901.  * Filter by a configurable Ext.menu.DateMenu
  1902.  * <p><b><u>Example Usage:</u></b></p>
  1903.  * <pre><code>    
  1904. var filters = new Ext.ux.grid.GridFilters({
  1905.     ...
  1906.     filters: [{
  1907.         // required configs
  1908.         type: 'date',
  1909.         dataIndex: 'dateAdded',
  1910.         
  1911.         // optional configs
  1912.         dateFormat: 'm/d/Y',  // default
  1913.         beforeText: 'Before', // default
  1914.         afterText: 'After',   // default
  1915.         onText: 'On',         // default
  1916.         pickerOpts: {
  1917.             // any DateMenu configs
  1918.         },
  1919.         active: true // default is false
  1920.     }]
  1921. });
  1922.  * </code></pre>
  1923.  */
  1924. Ext.ux.grid.filter.DateFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
  1925.     /**
  1926.      * @cfg {String} afterText
  1927.      * Defaults to 'After'.
  1928.      */
  1929.     afterText : 'After',
  1930.     /**
  1931.      * @cfg {String} beforeText
  1932.      * Defaults to 'Before'.
  1933.      */
  1934.     beforeText : 'Before',
  1935.     /**
  1936.      * @cfg {Object} compareMap
  1937.      * Map for assigning the comparison values used in serialization.
  1938.      */
  1939.     compareMap : {
  1940.         before: 'lt',
  1941.         after:  'gt',
  1942.         on:     'eq'
  1943.     },
  1944.     /**
  1945.      * @cfg {String} dateFormat
  1946.      * The date format to return when using getValue.
  1947.      * Defaults to 'm/d/Y'.
  1948.      */
  1949.     dateFormat : 'm/d/Y',
  1950.     /**
  1951.      * @cfg {Date} maxDate
  1952.      * Allowable date as passed to the Ext.DatePicker
  1953.      * Defaults to undefined.
  1954.      */
  1955.     /**
  1956.      * @cfg {Date} minDate
  1957.      * Allowable date as passed to the Ext.DatePicker
  1958.      * Defaults to undefined.
  1959.      */
  1960.     /**
  1961.      * @cfg {Array} menuItems
  1962.      * The items to be shown in this menu
  1963.      * Defaults to:<pre>
  1964.      * menuItems : ['before', 'after', '-', 'on'],
  1965.      * </pre>
  1966.      */
  1967.     menuItems : ['before', 'after', '-', 'on'],
  1968.     /**
  1969.      * @cfg {Object} menuItemCfgs
  1970.      * Default configuration options for each menu item
  1971.      */
  1972.     menuItemCfgs : {
  1973.         selectOnFocus: true,
  1974.         width: 125
  1975.     },
  1976.     /**
  1977.      * @cfg {String} onText
  1978.      * Defaults to 'On'.
  1979.      */
  1980.     onText : 'On',
  1981.     
  1982.     /**
  1983.      * @cfg {Object} pickerOpts
  1984.      * Configuration options for the date picker associated with each field.
  1985.      */
  1986.     pickerOpts : {},
  1987.     /**  
  1988.      * @private
  1989.      * Template method that is to initialize the filter and install required menu items.
  1990.      */
  1991.     init : function (config) {
  1992.         var menuCfg, i, len, item, cfg, Cls;
  1993.         menuCfg = Ext.apply(this.pickerOpts, {
  1994.             minDate: this.minDate, 
  1995.             maxDate: this.maxDate, 
  1996.             format:  this.dateFormat,
  1997.             listeners: {
  1998.                 scope: this,
  1999.                 select: this.onMenuSelect
  2000.             }
  2001.         });
  2002.         this.fields = {};
  2003.         for (i = 0, len = this.menuItems.length; i < len; i++) {
  2004.             item = this.menuItems[i];
  2005.             if (item !== '-') {
  2006.                 cfg = {
  2007.                     itemId: 'range-' + item,
  2008.                     text: this[item + 'Text'],
  2009.                     menu: new Ext.menu.DateMenu(
  2010.                         Ext.apply(menuCfg, {
  2011.                             itemId: item
  2012.                         })
  2013.                     ),
  2014.                     listeners: {
  2015.                         scope: this,
  2016.                         checkchange: this.onCheckChange
  2017.                     }
  2018.                 };
  2019.                 Cls = Ext.menu.CheckItem;
  2020.                 item = this.fields[item] = new Cls(cfg);
  2021.             }
  2022.             //this.add(item);
  2023.             this.menu.add(item);
  2024.         }
  2025.     },
  2026.     onCheckChange : function () {
  2027.         this.setActive(this.isActivatable());
  2028.         this.fireEvent('update', this);
  2029.     },
  2030.     /**  
  2031.      * @private
  2032.      * Handler method called when there is a keyup event on an input
  2033.      * item of this menu.
  2034.      */
  2035.     onInputKeyUp : function (field, e) {
  2036.         var k = e.getKey();
  2037.         if (k == e.RETURN && field.isValid()) {
  2038.             e.stopEvent();
  2039.             this.menu.hide(true);
  2040.             return;
  2041.         }
  2042.     },
  2043.     /**
  2044.      * Handler for when the menu for a field fires the 'select' event
  2045.      * @param {Object} date
  2046.      * @param {Object} menuItem
  2047.      * @param {Object} value
  2048.      * @param {Object} picker
  2049.      */
  2050.     onMenuSelect : function (menuItem, value, picker) {
  2051.         var fields = this.fields,
  2052.             field = this.fields[menuItem.itemId];
  2053.         
  2054.         field.setChecked(true);
  2055.         
  2056.         if (field == fields.on) {
  2057.             fields.before.setChecked(false, true);
  2058.             fields.after.setChecked(false, true);
  2059.         } else {
  2060.             fields.on.setChecked(false, true);
  2061.             if (field == fields.after && fields.before.menu.picker.value < value) {
  2062.                 fields.before.setChecked(false, true);
  2063.             } else if (field == fields.before && fields.after.menu.picker.value > value) {
  2064.                 fields.after.setChecked(false, true);
  2065.             }
  2066.         }
  2067.         this.fireEvent('update', this);
  2068.     },
  2069.     /**
  2070.      * @private
  2071.      * Template method that is to get and return the value of the filter.
  2072.      * @return {String} The value of this filter
  2073.      */
  2074.     getValue : function () {
  2075.         var key, result = {};
  2076.         for (key in this.fields) {
  2077.             if (this.fields[key].checked) {
  2078.                 result[key] = this.fields[key].menu.picker.getValue();
  2079.             }
  2080.         }
  2081.         return result;
  2082.     },
  2083.     /**
  2084.      * @private
  2085.      * Template method that is to set the value of the filter.
  2086.      * @param {Object} value The value to set the filter
  2087.      * @param {Boolean} preserve true to preserve the checked status
  2088.      * of the other fields.  Defaults to false, unchecking the
  2089.      * other fields
  2090.      */
  2091.     setValue : function (value, preserve) {
  2092.         var key;
  2093.         for (key in this.fields) {
  2094.             if(value[key]){
  2095.                 this.fields[key].menu.picker.setValue(value[key]);
  2096.                 this.fields[key].setChecked(true);
  2097.             } else if (!preserve) {
  2098.                 this.fields[key].setChecked(false);
  2099.             }
  2100.         }
  2101.         this.fireEvent('update', this);
  2102.     },
  2103.     /**
  2104.      * @private
  2105.      * Template method that is to return <tt>true</tt> if the filter
  2106.      * has enough configuration information to be activated.
  2107.      * @return {Boolean}
  2108.      */
  2109.     isActivatable : function () {
  2110.         var key;
  2111.         for (key in this.fields) {
  2112.             if (this.fields[key].checked) {
  2113.                 return true;
  2114.             }
  2115.         }
  2116.         return false;
  2117.     },
  2118.     /**
  2119.      * @private
  2120.      * Template method that is to get and return serialized filter data for
  2121.      * transmission to the server.
  2122.      * @return {Object/Array} An object or collection of objects containing
  2123.      * key value pairs representing the current configuration of the filter.
  2124.      */
  2125.     getSerialArgs : function () {
  2126.         var args = [];
  2127.         for (var key in this.fields) {
  2128.             if(this.fields[key].checked){
  2129.                 args.push({
  2130.                     type: 'date',
  2131.                     comparison: this.compareMap[key],
  2132.                     value: this.getFieldValue(key).format(this.dateFormat)
  2133.                 });
  2134.             }
  2135.         }
  2136.         return args;
  2137.     },
  2138.     /**
  2139.      * Get and return the date menu picker value
  2140.      * @param {String} item The field identifier ('before', 'after', 'on')
  2141.      * @return {Date} Gets the current selected value of the date field
  2142.      */
  2143.     getFieldValue : function(item){
  2144.         return this.fields[item].menu.picker.getValue();
  2145.     },
  2146.     
  2147.     /**
  2148.      * Gets the menu picker associated with the passed field
  2149.      * @param {String} item The field identifier ('before', 'after', 'on')
  2150.      * @return {Object} The menu picker
  2151.      */
  2152.     getPicker : function(item){
  2153.         return this.fields[item].menu.picker;
  2154.     },
  2155.     /**
  2156.      * Template method that is to validate the provided Ext.data.Record
  2157.      * against the filters configuration.
  2158.      * @param {Ext.data.Record} record The record to validate
  2159.      * @return {Boolean} true if the record is valid within the bounds
  2160.      * of the filter, false otherwise.
  2161.      */
  2162.     validateRecord : function (record) {
  2163.         var key,
  2164.             pickerValue,
  2165.             val = record.get(this.dataIndex);
  2166.             
  2167.         if(!Ext.isDate(val)){
  2168.             return false;
  2169.         }
  2170.         val = val.clearTime(true).getTime();
  2171.         
  2172.         for (key in this.fields) {
  2173.             if (this.fields[key].checked) {
  2174.                 pickerValue = this.getFieldValue(key).clearTime(true).getTime();
  2175.                 if (key == 'before' && pickerValue <= val) {
  2176.                     return false;
  2177.                 }
  2178.                 if (key == 'after' && pickerValue >= val) {
  2179.                     return false;
  2180.                 }
  2181.                 if (key == 'on' && pickerValue != val) {
  2182.                     return false;
  2183.                 }
  2184.             }
  2185.         }
  2186.         return true;
  2187.     }
  2188. });/** 
  2189.  * @class Ext.ux.grid.filter.ListFilter
  2190.  * @extends Ext.ux.grid.filter.Filter
  2191.  * <p>List filters are able to be preloaded/backed by an Ext.data.Store to load
  2192.  * their options the first time they are shown. ListFilter utilizes the
  2193.  * {@link Ext.ux.menu.ListMenu} component.</p>
  2194.  * <p>Although not shown here, this class accepts all configuration options
  2195.  * for {@link Ext.ux.menu.ListMenu}.</p>
  2196.  * 
  2197.  * <p><b><u>Example Usage:</u></b></p>
  2198.  * <pre><code>    
  2199. var filters = new Ext.ux.grid.GridFilters({
  2200.     ...
  2201.     filters: [{
  2202.         type: 'list',
  2203.         dataIndex: 'size',
  2204.         phpMode: true,
  2205.         // options will be used as data to implicitly creates an ArrayStore
  2206.         options: ['extra small', 'small', 'medium', 'large', 'extra large']
  2207.     }]
  2208. });
  2209.  * </code></pre>
  2210.  * 
  2211.  */
  2212. Ext.ux.grid.filter.ListFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
  2213.     /**
  2214.      * @cfg {Array} options
  2215.      * <p><code>data</code> to be used to implicitly create a data store
  2216.      * to back this list when the data source is <b>local</b>. If the
  2217.      * data for the list is remote, use the <code>{@link #store}</code>
  2218.      * config instead.</p>
  2219.      * <br><p>Each item within the provided array may be in one of the
  2220.      * following formats:</p>
  2221.      * <div class="mdetail-params"><ul>
  2222.      * <li><b>Array</b> :
  2223.      * <pre><code>
  2224. options: [
  2225.     [11, 'extra small'], 
  2226.     [18, 'small'],
  2227.     [22, 'medium'],
  2228.     [35, 'large'],
  2229.     [44, 'extra large']
  2230. ]
  2231.      * </code></pre>
  2232.      * </li>
  2233.      * <li><b>Object</b> :
  2234.      * <pre><code>
  2235. labelField: 'name', // override default of 'text'
  2236. options: [
  2237.     {id: 11, name:'extra small'}, 
  2238.     {id: 18, name:'small'}, 
  2239.     {id: 22, name:'medium'}, 
  2240.     {id: 35, name:'large'}, 
  2241.     {id: 44, name:'extra large'} 
  2242. ]
  2243.      * </code></pre>
  2244.      * </li>
  2245.      * <li><b>String</b> :
  2246.      * <pre><code>
  2247.      * options: ['extra small', 'small', 'medium', 'large', 'extra large']
  2248.      * </code></pre>
  2249.      * </li>
  2250.      */
  2251.     /**
  2252.      * @cfg {Boolean} phpMode
  2253.      * <p>Adjust the format of this filter. Defaults to false.</p>
  2254.      * <br><p>When GridFilters <code>@cfg encode = false</code> (default):</p>
  2255.      * <pre><code>