Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / docs / source / GridFilters.html
1 <html>
2 <head>
3   <title>The source code</title>
4     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
5     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
6 </head>
7 <body  onload="prettyPrint();">
8     <pre class="prettyprint lang-js">Ext.namespace('Ext.ux.grid');\r
9 \r
10 <div id="cls-Ext.ux.grid.GridFilters"></div>/**\r
11  * @class Ext.ux.grid.GridFilters\r
12  * @extends Ext.util.Observable\r
13  * <p>GridFilter is a plugin (<code>ptype='gridfilters'</code>) for grids that\r
14  * allow for a slightly more robust representation of filtering than what is\r
15  * provided by the default store.</p>\r
16  * <p>Filtering is adjusted by the user using the grid's column header menu\r
17  * (this menu can be disabled through configuration). Through this menu users\r
18  * can configure, enable, and disable filters for each column.</p>\r
19  * <p><b><u>Features:</u></b></p>\r
20  * <div class="mdetail-params"><ul>\r
21  * <li><b>Filtering implementations</b> :\r
22  * <div class="sub-desc">\r
23  * Default filtering for Strings, Numeric Ranges, Date Ranges, Lists (which can\r
24  * be backed by a Ext.data.Store), and Boolean. Additional custom filter types\r
25  * and menus are easily created by extending Ext.ux.grid.filter.Filter.\r
26  * </div></li>\r
27  * <li><b>Graphical indicators</b> :\r
28  * <div class="sub-desc">\r
29  * Columns that are filtered have {@link #filterCls a configurable css class}\r
30  * applied to the column headers.\r
31  * </div></li>\r
32  * <li><b>Paging</b> :\r
33  * <div class="sub-desc">\r
34  * If specified as a plugin to the grid's configured PagingToolbar, the current page\r
35  * will be reset to page 1 whenever you update the filters.\r
36  * </div></li>\r
37  * <li><b>Automatic Reconfiguration</b> :\r
38  * <div class="sub-desc">\r
39  * Filters automatically reconfigure when the grid 'reconfigure' event fires.\r
40  * </div></li>\r
41  * <li><b>Stateful</b> :\r
42  * Filter information will be persisted across page loads by specifying a\r
43  * <code>stateId</code> in the Grid configuration.\r
44  * <div class="sub-desc">\r
45  * The filter collection binds to the\r
46  * <code>{@link Ext.grid.GridPanel#beforestaterestore beforestaterestore}</code>\r
47  * and <code>{@link Ext.grid.GridPanel#beforestatesave beforestatesave}</code>\r
48  * events in order to be stateful. \r
49  * </div></li>\r
50  * <li><b>Grid Changes</b> :\r
51  * <div class="sub-desc"><ul>\r
52  * <li>A <code>filters</code> <i>property</i> is added to the grid pointing to\r
53  * this plugin.</li>\r
54  * <li>A <code>filterupdate</code> <i>event</i> is added to the grid and is\r
55  * fired upon onStateChange completion.</li>\r
56  * </ul></div></li>\r
57  * <li><b>Server side code examples</b> :\r
58  * <div class="sub-desc"><ul>\r
59  * <li><a href="http://www.vinylfox.com/extjs/grid-filter-php-backend-code.php">PHP</a> - (Thanks VinylFox)</li>\r
60  * <li><a href="http://extjs.com/forum/showthread.php?p=77326#post77326">Ruby on Rails</a> - (Thanks Zyclops)</li>\r
61  * <li><a href="http://extjs.com/forum/showthread.php?p=176596#post176596">Ruby on Rails</a> - (Thanks Rotomaul)</li>\r
62  * <li><a href="http://www.debatablybeta.com/posts/using-extjss-grid-filtering-with-django/">Python</a> - (Thanks Matt)</li>\r
63  * <li><a href="http://mcantrell.wordpress.com/2008/08/22/extjs-grids-and-grails/">Grails</a> - (Thanks Mike)</li>\r
64  * </ul></div></li>\r
65  * </ul></div>\r
66  * <p><b><u>Example usage:</u></b></p>\r
67  * <pre><code>    \r
68 var store = new Ext.data.GroupingStore({\r
69     ...\r
70 });\r
71  \r
72 var filters = new Ext.ux.grid.GridFilters({\r
73     autoReload: false, //don&#39;t reload automatically\r
74     local: true, //only filter locally\r
75     // filters may be configured through the plugin,\r
76     // or in the column definition within the column model configuration\r
77     filters: [{\r
78         type: 'numeric',\r
79         dataIndex: 'id'\r
80     }, {\r
81         type: 'string',\r
82         dataIndex: 'name'\r
83     }, {\r
84         type: 'numeric',\r
85         dataIndex: 'price'\r
86     }, {\r
87         type: 'date',\r
88         dataIndex: 'dateAdded'\r
89     }, {\r
90         type: 'list',\r
91         dataIndex: 'size',\r
92         options: ['extra small', 'small', 'medium', 'large', 'extra large'],\r
93         phpMode: true\r
94     }, {\r
95         type: 'boolean',\r
96         dataIndex: 'visible'\r
97     }]\r
98 });\r
99 var cm = new Ext.grid.ColumnModel([{\r
100     ...\r
101 }]);\r
102  \r
103 var grid = new Ext.grid.GridPanel({\r
104      ds: store,\r
105      cm: cm,\r
106      view: new Ext.grid.GroupingView(),\r
107      plugins: [filters],\r
108      height: 400,\r
109      width: 700,\r
110      bbar: new Ext.PagingToolbar({\r
111          store: store,\r
112          pageSize: 15,\r
113          plugins: [filters] //reset page to page 1 if filters change\r
114      })\r
115  });\r
116 \r
117 store.load({params: {start: 0, limit: 15}});\r
118 \r
119 // a filters property is added to the grid\r
120 grid.filters\r
121  * </code></pre>\r
122  */\r
123 Ext.ux.grid.GridFilters = Ext.extend(Ext.util.Observable, {\r
124     <div id="cfg-Ext.ux.grid.GridFilters-autoReload"></div>/**\r
125      * @cfg {Boolean} autoReload\r
126      * Defaults to true, reloading the datasource when a filter change happens.\r
127      * Set this to false to prevent the datastore from being reloaded if there\r
128      * are changes to the filters.  See <code>{@link updateBuffer}</code>.\r
129      */\r
130     autoReload : true,\r
131     <div id="cfg-Ext.ux.grid.GridFilters-encode"></div>/**\r
132      * @cfg {Boolean} encode\r
133      * Specify true for {@link #buildQuery} to use Ext.util.JSON.encode to\r
134      * encode the filter query parameter sent with a remote request.\r
135      * Defaults to false.\r
136      */\r
137     <div id="cfg-Ext.ux.grid.GridFilters-filters"></div>/**\r
138      * @cfg {Array} filters\r
139      * An Array of filters config objects. Refer to each filter type class for\r
140      * configuration details specific to each filter type. Filters for Strings,\r
141      * Numeric Ranges, Date Ranges, Lists, and Boolean are the standard filters\r
142      * available.\r
143      */\r
144     <div id="cfg-Ext.ux.grid.GridFilters-filterCls"></div>/**\r
145      * @cfg {String} filterCls\r
146      * The css class to be applied to column headers with active filters.\r
147      * Defaults to <tt>'ux-filterd-column'</tt>.\r
148      */\r
149     filterCls : 'ux-filtered-column',\r
150     <div id="cfg-Ext.ux.grid.GridFilters-local"></div>/**\r
151      * @cfg {Boolean} local\r
152      * <tt>true</tt> to use Ext.data.Store filter functions (local filtering)\r
153      * instead of the default (<tt>false</tt>) server side filtering.\r
154      */\r
155     local : false,\r
156     <div id="cfg-Ext.ux.grid.GridFilters-menuFilterText"></div>/**\r
157      * @cfg {String} menuFilterText\r
158      * defaults to <tt>'Filters'</tt>.\r
159      */\r
160     menuFilterText : 'Filters',\r
161     <div id="cfg-Ext.ux.grid.GridFilters-paramPrefix"></div>/**\r
162      * @cfg {String} paramPrefix\r
163      * The url parameter prefix for the filters.\r
164      * Defaults to <tt>'filter'</tt>.\r
165      */\r
166     paramPrefix : 'filter',\r
167     <div id="cfg-Ext.ux.grid.GridFilters-showMenu"></div>/**\r
168      * @cfg {Boolean} showMenu\r
169      * Defaults to true, including a filter submenu in the default header menu.\r
170      */\r
171     showMenu : true,\r
172     <div id="cfg-Ext.ux.grid.GridFilters-stateId"></div>/**\r
173      * @cfg {String} stateId\r
174      * Name of the value to be used to store state information.\r
175      */\r
176     stateId : undefined,\r
177     <div id="cfg-Ext.ux.grid.GridFilters-updateBuffer"></div>/**\r
178      * @cfg {Integer} updateBuffer\r
179      * Number of milliseconds to defer store updates since the last filter change.\r
180      */\r
181     updateBuffer : 500,\r
182 \r
183     /** @private */\r
184     constructor : function (config) {           \r
185         this.deferredUpdate = new Ext.util.DelayedTask(this.reload, this);\r
186         this.filters = new Ext.util.MixedCollection();\r
187         this.filters.getKey = function (o) {\r
188             return o ? o.dataIndex : null;\r
189         };\r
190         this.addFilters(config.filters);\r
191         delete config.filters;\r
192         Ext.apply(this, config);\r
193     },\r
194 \r
195     /** @private */\r
196     init : function (grid) {\r
197         if (grid instanceof Ext.grid.GridPanel) {\r
198             this.grid = grid;\r
199 \r
200             this.bindStore(this.grid.getStore(), true);\r
201           \r
202             this.grid.filters = this;\r
203              \r
204             this.grid.addEvents({'filterupdate': true});\r
205               \r
206             grid.on({\r
207                 scope: this,\r
208                 beforestaterestore: this.applyState,\r
209                 beforestatesave: this.saveState,\r
210                 beforedestroy: this.destroy,\r
211                 reconfigure: this.onReconfigure\r
212             });\r
213             \r
214             if (grid.rendered){\r
215                 this.onRender();\r
216             } else {\r
217                 grid.on({\r
218                     scope: this,\r
219                     single: true,\r
220                     render: this.onRender\r
221                 });\r
222             }\r
223                       \r
224         } else if (grid instanceof Ext.PagingToolbar) {\r
225             this.toolbar = grid;\r
226         }\r
227     },\r
228         \r
229     /**\r
230      * @private\r
231      * Handler for the grid's beforestaterestore event (fires before the state of the\r
232      * grid is restored).\r
233      * @param {Object} grid The grid object\r
234      * @param {Object} state The hash of state values returned from the StateProvider.\r
235      */   \r
236     applyState : function (grid, state) {\r
237         var key, filter;\r
238         this.applyingState = true;\r
239         this.clearFilters();\r
240         if (state.filters) {\r
241             for (key in state.filters) {\r
242                 filter = this.filters.get(key);\r
243                 if (filter) {\r
244                     filter.setValue(state.filters[key]);\r
245                     filter.setActive(true);\r
246                 }\r
247             }\r
248         }\r
249         this.deferredUpdate.cancel();\r
250         if (this.local) {\r
251             this.reload();\r
252         }\r
253         delete this.applyingState;\r
254     },\r
255     \r
256     <div id="method-Ext.ux.grid.GridFilters-saveState"></div>/**\r
257      * Saves the state of all active filters\r
258      * @param {Object} grid\r
259      * @param {Object} state\r
260      * @return {Boolean}\r
261      */\r
262     saveState : function (grid, state) {\r
263         var filters = {};\r
264         this.filters.each(function (filter) {\r
265             if (filter.active) {\r
266                 filters[filter.dataIndex] = filter.getValue();\r
267             }\r
268         });\r
269         return (state.filters = filters);\r
270     },\r
271     \r
272     /**\r
273      * @private\r
274      * Handler called when the grid is rendered\r
275      */    \r
276     onRender : function () {\r
277         this.grid.getView().on('refresh', this.onRefresh, this);\r
278         this.createMenu();\r
279     },\r
280 \r
281     /**\r
282      * @private\r
283      * Handler called by the grid 'beforedestroy' event\r
284      */    \r
285     destroy : function () {\r
286         this.removeAll();\r
287         this.purgeListeners();\r
288 \r
289         if(this.filterMenu){\r
290             Ext.menu.MenuMgr.unregister(this.filterMenu);\r
291             this.filterMenu.destroy();\r
292              this.filterMenu = this.menu.menu = null;            \r
293         }\r
294     },\r
295 \r
296     <div id="method-Ext.ux.grid.GridFilters-removeAll"></div>/**\r
297      * Remove all filters, permanently destroying them.\r
298      */    \r
299     removeAll : function () {\r
300         if(this.filters){\r
301             Ext.destroy.apply(Ext, this.filters.items);\r
302             // remove all items from the collection \r
303             this.filters.clear();\r
304         }\r
305     },\r
306 \r
307 \r
308     <div id="method-Ext.ux.grid.GridFilters-bindStore"></div>/**\r
309      * Changes the data store bound to this view and refreshes it.\r
310      * @param {Store} store The store to bind to this view\r
311      */\r
312     bindStore : function(store, initial){\r
313         if(!initial && this.store){\r
314             if (this.local) {\r
315                 store.un('load', this.onLoad, this);\r
316             } else {\r
317                 store.un('beforeload', this.onBeforeLoad, this);\r
318             }\r
319         }\r
320         if(store){\r
321             if (this.local) {\r
322                 store.on('load', this.onLoad, this);\r
323             } else {\r
324                 store.on('beforeload', this.onBeforeLoad, this);\r
325             }\r
326         }\r
327         this.store = store;\r
328     },\r
329 \r
330     /**\r
331      * @private\r
332      * Handler called when the grid reconfigure event fires\r
333      */    \r
334     onReconfigure : function () {\r
335         this.bindStore(this.grid.getStore());\r
336         this.store.clearFilter();\r
337         this.removeAll();\r
338         this.addFilters(this.grid.getColumnModel());\r
339         this.updateColumnHeadings();\r
340     },\r
341 \r
342     createMenu : function () {\r
343         var view = this.grid.getView(),\r
344             hmenu = view.hmenu;\r
345 \r
346         if (this.showMenu && hmenu) {\r
347             \r
348             this.sep  = hmenu.addSeparator();\r
349             this.filterMenu = new Ext.menu.Menu({\r
350                 id: this.grid.id + '-filters-menu'\r
351             }); \r
352             this.menu = hmenu.add({\r
353                 checked: false,\r
354                 itemId: 'filters',\r
355                 text: this.menuFilterText,\r
356                 menu: this.filterMenu\r
357             });\r
358 \r
359             this.menu.on({\r
360                 scope: this,\r
361                 checkchange: this.onCheckChange,\r
362                 beforecheckchange: this.onBeforeCheck\r
363             });\r
364             hmenu.on('beforeshow', this.onMenu, this);\r
365         }\r
366         this.updateColumnHeadings();\r
367     },\r
368 \r
369     /**\r
370      * @private\r
371      * Get the filter menu from the filters MixedCollection based on the clicked header\r
372      */\r
373     getMenuFilter : function () {\r
374         var view = this.grid.getView();\r
375         if (!view || view.hdCtxIndex === undefined) {\r
376             return null;\r
377         }\r
378         return this.filters.get(\r
379             view.cm.config[view.hdCtxIndex].dataIndex\r
380         );\r
381     },\r
382 \r
383     /**\r
384      * @private\r
385      * Handler called by the grid's hmenu beforeshow event\r
386      */    \r
387     onMenu : function (filterMenu) {\r
388         var filter = this.getMenuFilter();\r
389 \r
390         if (filter) {\r
391 /*            \r
392 TODO: lazy rendering\r
393             if (!filter.menu) {\r
394                 filter.menu = filter.createMenu();\r
395             }\r
396 */\r
397             this.menu.menu = filter.menu;\r
398             this.menu.setChecked(filter.active, false);\r
399             // disable the menu if filter.disabled explicitly set to true\r
400             this.menu.setDisabled(filter.disabled === true);\r
401         }\r
402         \r
403         this.menu.setVisible(filter !== undefined);\r
404         this.sep.setVisible(filter !== undefined);\r
405     },\r
406     \r
407     /** @private */\r
408     onCheckChange : function (item, value) {\r
409         this.getMenuFilter().setActive(value);\r
410     },\r
411     \r
412     /** @private */\r
413     onBeforeCheck : function (check, value) {\r
414         return !value || this.getMenuFilter().isActivatable();\r
415     },\r
416 \r
417     /**\r
418      * @private\r
419      * Handler for all events on filters.\r
420      * @param {String} event Event name\r
421      * @param {Object} filter Standard signature of the event before the event is fired\r
422      */   \r
423     onStateChange : function (event, filter) {\r
424         if (event === 'serialize') {\r
425             return;\r
426         }\r
427 \r
428         if (filter == this.getMenuFilter()) {\r
429             this.menu.setChecked(filter.active, false);\r
430         }\r
431 \r
432         if ((this.autoReload || this.local) && !this.applyingState) {\r
433             this.deferredUpdate.delay(this.updateBuffer);\r
434         }\r
435         this.updateColumnHeadings();\r
436             \r
437         if (!this.applyingState) {\r
438             this.grid.saveState();\r
439         }    \r
440         this.grid.fireEvent('filterupdate', this, filter);\r
441     },\r
442     \r
443     /**\r
444      * @private\r
445      * Handler for store's beforeload event when configured for remote filtering\r
446      * @param {Object} store\r
447      * @param {Object} options\r
448      */\r
449     onBeforeLoad : function (store, options) {\r
450         options.params = options.params || {};\r
451         this.cleanParams(options.params);               \r
452         var params = this.buildQuery(this.getFilterData());\r
453         Ext.apply(options.params, params);\r
454     },\r
455     \r
456     /**\r
457      * @private\r
458      * Handler for store's load event when configured for local filtering\r
459      * @param {Object} store\r
460      * @param {Object} options\r
461      */\r
462     onLoad : function (store, options) {\r
463         store.filterBy(this.getRecordFilter());\r
464     },\r
465 \r
466     /**\r
467      * @private\r
468      * Handler called when the grid's view is refreshed\r
469      */    \r
470     onRefresh : function () {\r
471         this.updateColumnHeadings();\r
472     },\r
473 \r
474     <div id="method-Ext.ux.grid.GridFilters-updateColumnHeadings"></div>/**\r
475      * Update the styles for the header row based on the active filters\r
476      */    \r
477     updateColumnHeadings : function () {\r
478         var view = this.grid.getView(),\r
479             hds, i, len, filter;\r
480         if (view.mainHd) {\r
481             hds = view.mainHd.select('td').removeClass(this.filterCls);\r
482             for (i = 0, len = view.cm.config.length; i < len; i++) {\r
483                 filter = this.getFilter(view.cm.config[i].dataIndex);\r
484                 if (filter && filter.active) {\r
485                     hds.item(i).addClass(this.filterCls);\r
486                 }\r
487             }\r
488         }\r
489     },\r
490     \r
491     /** @private */\r
492     reload : function () {\r
493         if (this.local) {\r
494             this.grid.store.clearFilter(true);\r
495             this.grid.store.filterBy(this.getRecordFilter());\r
496         } else {\r
497             var start,\r
498                 store = this.grid.store;\r
499             this.deferredUpdate.cancel();\r
500             if (this.toolbar) {\r
501                 start = store.paramNames.start;\r
502                 if (store.lastOptions && store.lastOptions.params && store.lastOptions.params[start]) {\r
503                     store.lastOptions.params[start] = 0;\r
504                 }\r
505             }\r
506             store.reload();\r
507         }\r
508     },\r
509     \r
510     /**\r
511      * Method factory that generates a record validator for the filters active at the time\r
512      * of invokation.\r
513      * @private\r
514      */\r
515     getRecordFilter : function () {\r
516         var f = [], len, i;\r
517         this.filters.each(function (filter) {\r
518             if (filter.active) {\r
519                 f.push(filter);\r
520             }\r
521         });\r
522         \r
523         len = f.length;\r
524         return function (record) {\r
525             for (i = 0; i < len; i++) {\r
526                 if (!f[i].validateRecord(record)) {\r
527                     return false;\r
528                 }\r
529             }\r
530             return true;\r
531         };\r
532     },\r
533     \r
534     <div id="method-Ext.ux.grid.GridFilters-addFilter"></div>/**\r
535      * Adds a filter to the collection and observes it for state change.\r
536      * @param {Object/Ext.ux.grid.filter.Filter} config A filter configuration or a filter object.\r
537      * @return {Ext.ux.grid.filter.Filter} The existing or newly created filter object.\r
538      */\r
539     addFilter : function (config) {\r
540         var Cls = this.getFilterClass(config.type),\r
541             filter = config.menu ? config : (new Cls(config));\r
542         this.filters.add(filter);\r
543         \r
544         Ext.util.Observable.capture(filter, this.onStateChange, this);\r
545         return filter;\r
546     },\r
547 \r
548     <div id="method-Ext.ux.grid.GridFilters-addFilters"></div>/**\r
549      * Adds filters to the collection.\r
550      * @param {Array/Ext.grid.ColumnModel} filters Either an Array of\r
551      * filter configuration objects or an Ext.grid.ColumnModel.  The columns\r
552      * of a passed Ext.grid.ColumnModel will be examined for a <code>filter</code>\r
553      * property and, if present, will be used as the filter configuration object.   \r
554      */\r
555     addFilters : function (filters) {\r
556         if (filters) {\r
557             var i, len, filter, cm = false, dI;\r
558             if (filters instanceof Ext.grid.ColumnModel) {\r
559                 filters = filters.config;\r
560                 cm = true;\r
561             }\r
562             for (i = 0, len = filters.length; i < len; i++) {\r
563                 filter = false;\r
564                 if (cm) {\r
565                     dI = filters[i].dataIndex;\r
566                     filter = filters[i].filter || filters[i].filterable;\r
567                     if (filter){\r
568                         filter = (filter === true) ? {} : filter;\r
569                         Ext.apply(filter, {dataIndex:dI});\r
570                         // filter type is specified in order of preference:\r
571                         //     filter type specified in config\r
572                         //     type specified in store's field's type config\r
573                         filter.type = filter.type || this.store.fields.get(dI).type;  \r
574                     }\r
575                 } else {\r
576                     filter = filters[i];\r
577                 }\r
578                 // if filter config found add filter for the column \r
579                 if (filter) {\r
580                     this.addFilter(filter);\r
581                 }\r
582             }\r
583         }\r
584     },\r
585     \r
586     <div id="method-Ext.ux.grid.GridFilters-getFilter"></div>/**\r
587      * Returns a filter for the given dataIndex, if one exists.\r
588      * @param {String} dataIndex The dataIndex of the desired filter object.\r
589      * @return {Ext.ux.grid.filter.Filter}\r
590      */\r
591     getFilter : function (dataIndex) {\r
592         return this.filters.get(dataIndex);\r
593     },\r
594 \r
595     <div id="method-Ext.ux.grid.GridFilters-clearFilters"></div>/**\r
596      * Turns all filters off. This does not clear the configuration information\r
597      * (see {@link #removeAll}).\r
598      */\r
599     clearFilters : function () {\r
600         this.filters.each(function (filter) {\r
601             filter.setActive(false);\r
602         });\r
603     },\r
604 \r
605     <div id="method-Ext.ux.grid.GridFilters-getFilterData"></div>/**\r
606      * Returns an Array of the currently active filters.\r
607      * @return {Array} filters Array of the currently active filters.\r
608      */\r
609     getFilterData : function () {\r
610         var filters = [], i, len;\r
611 \r
612         this.filters.each(function (f) {\r
613             if (f.active) {\r
614                 var d = [].concat(f.serialize());\r
615                 for (i = 0, len = d.length; i < len; i++) {\r
616                     filters.push({\r
617                         field: f.dataIndex,\r
618                         data: d[i]\r
619                     });\r
620                 }\r
621             }\r
622         });\r
623         return filters;\r
624     },\r
625     \r
626     <div id="method-Ext.ux.grid.GridFilters-buildQuery"></div>/**\r
627      * Function to take the active filters data and build it into a query.\r
628      * The format of the query depends on the <code>{@link #encode}</code>\r
629      * configuration:\r
630      * <div class="mdetail-params"><ul>\r
631      * \r
632      * <li><b><tt>false</tt></b> : <i>Default</i>\r
633      * <div class="sub-desc">\r
634      * Flatten into query string of the form (assuming <code>{@link #paramPrefix}='filters'</code>:\r
635      * <pre><code>\r
636 filters[0][field]="someDataIndex"&\r
637 filters[0][data][comparison]="someValue1"&\r
638 filters[0][data][type]="someValue2"&\r
639 filters[0][data][value]="someValue3"&\r
640      * </code></pre>\r
641      * </div></li>\r
642      * <li><b><tt>true</tt></b> : \r
643      * <div class="sub-desc">\r
644      * JSON encode the filter data\r
645      * <pre><code>\r
646 filters[0][field]="someDataIndex"&\r
647 filters[0][data][comparison]="someValue1"&\r
648 filters[0][data][type]="someValue2"&\r
649 filters[0][data][value]="someValue3"&\r
650      * </code></pre>\r
651      * </div></li>\r
652      * </ul></div>\r
653      * Override this method to customize the format of the filter query for remote requests.\r
654      * @param {Array} filters A collection of objects representing active filters and their configuration.\r
655      *    Each element will take the form of {field: dataIndex, data: filterConf}. dataIndex is not assured\r
656      *    to be unique as any one filter may be a composite of more basic filters for the same dataIndex.\r
657      * @return {Object} Query keys and values\r
658      */\r
659     buildQuery : function (filters) {\r
660         var p = {}, i, f, root, dataPrefix, key, tmp,\r
661             len = filters.length;\r
662 \r
663         if (!this.encode){\r
664             for (i = 0; i < len; i++) {\r
665                 f = filters[i];\r
666                 root = [this.paramPrefix, '[', i, ']'].join('');\r
667                 p[root + '[field]'] = f.field;\r
668                 \r
669                 dataPrefix = root + '[data]';\r
670                 for (key in f.data) {\r
671                     p[[dataPrefix, '[', key, ']'].join('')] = f.data[key];\r
672                 }\r
673             }\r
674         } else {\r
675             tmp = [];\r
676             for (i = 0; i < len; i++) {\r
677                 f = filters[i];\r
678                 tmp.push(Ext.apply(\r
679                     {},\r
680                     {field: f.field},\r
681                     f.data\r
682                 ));\r
683             }\r
684             // only build if there is active filter \r
685             if (tmp.length > 0){\r
686                 p[this.paramPrefix] = Ext.util.JSON.encode(tmp);\r
687             }\r
688         }\r
689         return p;\r
690     },\r
691     \r
692     <div id="method-Ext.ux.grid.GridFilters-cleanParams"></div>/**\r
693      * Removes filter related query parameters from the provided object.\r
694      * @param {Object} p Query parameters that may contain filter related fields.\r
695      */\r
696     cleanParams : function (p) {\r
697         // if encoding just delete the property\r
698         if (this.encode) {\r
699             delete p[this.paramPrefix];\r
700         // otherwise scrub the object of filter data\r
701         } else {\r
702             var regex, key;\r
703             regex = new RegExp('^' + this.paramPrefix + '\[[0-9]+\]');\r
704             for (key in p) {\r
705                 if (regex.test(key)) {\r
706                     delete p[key];\r
707                 }\r
708             }\r
709         }\r
710     },\r
711     \r
712     <div id="method-Ext.ux.grid.GridFilters-getFilterClass"></div>/**\r
713      * Function for locating filter classes, overwrite this with your favorite\r
714      * loader to provide dynamic filter loading.\r
715      * @param {String} type The type of filter to load ('Filter' is automatically\r
716      * appended to the passed type; eg, 'string' becomes 'StringFilter').\r
717      * @return {Class} The Ext.ux.grid.filter.Class \r
718      */\r
719     getFilterClass : function (type) {\r
720         // map the supported Ext.data.Field type values into a supported filter\r
721         switch(type) {\r
722             case 'auto':\r
723               type = 'string';\r
724               break;\r
725             case 'int':\r
726             case 'float':\r
727               type = 'numeric';\r
728               break;\r
729         }\r
730         return Ext.ux.grid.filter[type.substr(0, 1).toUpperCase() + type.substr(1) + 'Filter'];\r
731     }\r
732 });\r
733 \r
734 // register ptype\r
735 Ext.preg('gridfilters', Ext.ux.grid.GridFilters);\r
736 </pre>
737 </body>
738 </html>