Commit cc806102ee31daab4943cdf5117630a49e393f00

Authored by Benjamin Renard
1 parent a443efbf
Exists in minimize

Fix errors detected by sencha cmd

js/app/controllers/SampModule.js
@@ -318,5 +318,5 @@ Ext.define('amdaDesktop.SampModule', { @@ -318,5 +318,5 @@ Ext.define('amdaDesktop.SampModule', {
318 }; 318 };
319 xobj.send(null); 319 xobj.send(null);
320 return true; 320 return true;
321 - }, 321 + }
322 }); 322 });
js/app/models/DerivedParamNode.js
@@ -36,7 +36,7 @@ Ext.define('amdaModel.DerivedParamComponentNode', { @@ -36,7 +36,7 @@ Ext.define('amdaModel.DerivedParamComponentNode', {
36 }, 36 },
37 getAllContextMenuItems: function(){ 37 getAllContextMenuItems: function(){
38 return this.localMenuItems (); 38 return this.localMenuItems ();
39 -}, 39 +}
40 }); 40 });
41 41
42 Ext.define('amdaModel.DerivedParamNode', { 42 Ext.define('amdaModel.DerivedParamNode', {
js/app/models/FilterDef.js
@@ -82,7 +82,7 @@ Ext.define('amdaModel.FilterDef', { @@ -82,7 +82,7 @@ Ext.define('amdaModel.FilterDef', {
82 reader: 82 reader:
83 { 83 {
84 type: 'json', 84 type: 'json',
85 - root: 'filters', 85 + root: 'filters'
86 }, 86 },
87 writer: new Ext.data.JsonWriter({ 87 writer: new Ext.data.JsonWriter({
88 encode: false, 88 encode: false,
@@ -116,4 +116,4 @@ Ext.define('amdaModel.FilterDef', { @@ -116,4 +116,4 @@ Ext.define('amdaModel.FilterDef', {
116 } 116 }
117 }) 117 })
118 } 118 }
119 -});  
120 \ No newline at end of file 119 \ No newline at end of file
  120 +});
js/app/models/FilterInfo.js
@@ -157,7 +157,7 @@ Ext.define('amdaModel.FilterInfo', { @@ -157,7 +157,7 @@ Ext.define('amdaModel.FilterInfo', {
157 157
158 fields : [ 158 fields : [
159 {name: 'id', mapping: '@id', type:'string'}, 159 {name: 'id', mapping: '@id', type:'string'},
160 - {name: 'name', mapping: '@name', type:'string'}, 160 + {name: 'name', mapping: '@name', type:'string'}
161 ], 161 ],
162 162
163 hasMany : { 163 hasMany : {
@@ -175,4 +175,4 @@ Ext.define('amdaModel.FilterInfo', { @@ -175,4 +175,4 @@ Ext.define('amdaModel.FilterInfo', {
175 record: 'filter' 175 record: 'filter'
176 } 176 }
177 } 177 }
178 - });  
179 \ No newline at end of file 178 \ No newline at end of file
  179 + });
js/app/models/PlotObjects/PlotBaseSerieObject.js
@@ -66,7 +66,7 @@ Ext.define('amdaPlotObj.PlotBaseSerieObject', { @@ -66,7 +66,7 @@ Ext.define('amdaPlotObj.PlotBaseSerieObject', {
66 {name: 'serie-intervaltick-font-name', type: 'string'}, 66 {name: 'serie-intervaltick-font-name', type: 'string'},
67 {name: 'serie-intervaltick-font-size', type: 'int'}, 67 {name: 'serie-intervaltick-font-size', type: 'int'},
68 {name: 'serie-intervaltick-font-bold', type: 'boolean'}, 68 {name: 'serie-intervaltick-font-bold', type: 'boolean'},
69 - {name: 'serie-intervaltick-font-italic', type: 'boolean'}, 69 + {name: 'serie-intervaltick-font-italic', type: 'boolean'}
70 ], 70 ],
71 71
72 constructor: function(){ 72 constructor: function(){
js/app/models/PlotTabNode.js
@@ -160,5 +160,5 @@ Ext.define('amdaModel.PlotTabNode', { @@ -160,5 +160,5 @@ Ext.define('amdaModel.PlotTabNode', {
160 } 160 }
161 }); 161 });
162 }); 162 });
163 - }, 163 + }
164 }); 164 });
js/app/models/RequestParamObject.js
@@ -37,7 +37,7 @@ Ext.define('amdaModel.RequestParamObject', { @@ -37,7 +37,7 @@ Ext.define('amdaModel.RequestParamObject', {
37 'dim2-max-index': 0, 37 'dim2-max-index': 0,
38 'template_args': {} 38 'template_args': {}
39 }; 39 };
40 - }, 40 + }
41 }, 41 },
42 42
43 idProperty: 'id', 43 idProperty: 'id',
js/app/views/FiltersUI.js
@@ -523,7 +523,7 @@ Ext.define('amdaUI.FilterGridPanel', { @@ -523,7 +523,7 @@ Ext.define('amdaUI.FilterGridPanel', {
523 viewConfig: { 523 viewConfig: {
524 getRowClass: function(record, rowIndex, rowParams, store){ 524 getRowClass: function(record, rowIndex, rowParams, store){
525 if (record.get('index') == -1) return 'x-hide-display'; 525 if (record.get('index') == -1) return 'x-hide-display';
526 - }, 526 + }
527 }, 527 },
528 528
529 listeners : { 529 listeners : {
@@ -1143,4 +1143,4 @@ Ext.define('amdaUI.FiltersUI', { @@ -1143,4 +1143,4 @@ Ext.define('amdaUI.FiltersUI', {
1143 } 1143 }
1144 }); 1144 });
1145 } 1145 }
1146 -});  
1147 \ No newline at end of file 1146 \ No newline at end of file
  1147 +});
js/app/views/ParamArgumentsUI.js
@@ -31,7 +31,7 @@ Ext.define('amdaUI.ParamArgumentsUI', { @@ -31,7 +31,7 @@ Ext.define('amdaUI.ParamArgumentsUI', {
31 layout: { 31 layout: {
32 type: 'vbox', 32 type: 'vbox',
33 align: 'stretch' 33 align: 'stretch'
34 - }, 34 + }
35 }; 35 };
36 36
37 Ext.apply (this , Ext.apply (arguments, myConf)); 37 Ext.apply (this , Ext.apply (arguments, myConf));
js/app/views/PlotComponents/PlotLegendSeriesForm.js
@@ -22,7 +22,7 @@ Ext.define('amdaPlotComp.PlotLegendSeriesForm', { @@ -22,7 +22,7 @@ Ext.define('amdaPlotComp.PlotLegendSeriesForm', {
22 22
23 getFormItems: function() { 23 getFormItems: function() {
24 var borderItems = [ 24 var borderItems = [
25 - this.addStandardColor('legend-series-border-color', 'Border Color', amdaPlotObj.PlotObjectConfig.availableColors), 25 + this.addStandardColor('legend-series-border-color', 'Border Color', amdaPlotObj.PlotObjectConfig.availableColors)
26 ]; 26 ];
27 27
28 var intervalInfoItems = [ 28 var intervalInfoItems = [
@@ -48,4 +48,4 @@ Ext.define('amdaPlotComp.PlotLegendSeriesForm', { @@ -48,4 +48,4 @@ Ext.define('amdaPlotComp.PlotLegendSeriesForm', {
48 }) 48 })
49 ]; 49 ];
50 } 50 }
51 -});  
52 \ No newline at end of file 51 \ No newline at end of file
  52 +});
js/app/views/PlotComponents/PlotOrbitSerieForm.js
@@ -20,7 +20,7 @@ Ext.define('amdaPlotComp.PlotOrbitSerieForm', { @@ -20,7 +20,7 @@ Ext.define('amdaPlotComp.PlotOrbitSerieForm', {
20 this.addStandardCombo('serie-projection', 'Projection', amdaPlotObj.PlotObjectConfig.availableOrbitSerieProjections, function(name, value, oldValue) { 20 this.addStandardCombo('serie-projection', 'Projection', amdaPlotObj.PlotObjectConfig.availableOrbitSerieProjections, function(name, value, oldValue) {
21 me.object.set('serie-projection', value); 21 me.object.set('serie-projection', value);
22 me.crtTree.refresh(); 22 me.crtTree.refresh();
23 - }), 23 + })
24 ]; 24 ];
25 25
26 Ext.each(baseSerieItems, function(item) { 26 Ext.each(baseSerieItems, function(item) {
@@ -29,4 +29,4 @@ Ext.define('amdaPlotComp.PlotOrbitSerieForm', { @@ -29,4 +29,4 @@ Ext.define('amdaPlotComp.PlotOrbitSerieForm', {
29 29
30 return orbitSerieItems; 30 return orbitSerieItems;
31 } 31 }
32 -});  
33 \ No newline at end of file 32 \ No newline at end of file
  33 +});
js/app/views/PlotUI.js
@@ -263,7 +263,7 @@ Ext.define('amdaUI.PlotUI', { @@ -263,7 +263,7 @@ Ext.define('amdaUI.PlotUI', {
263 }); 263 });
264 }, 264 },
265 265
266 - savePlotRequest : function(allTabs = false) { 266 + savePlotRequest : function(allTabs) {
267 var plotModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.plot.id); 267 var plotModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.plot.id);
268 if (!plotModule) 268 if (!plotModule)
269 return; 269 return;
@@ -425,7 +425,7 @@ Ext.define('amdaUI.PlotUI', { @@ -425,7 +425,7 @@ Ext.define('amdaUI.PlotUI', {
425 handler: function() { 425 handler: function() {
426 this.savePlotRequest(false); 426 this.savePlotRequest(false);
427 } 427 }
428 - }, 428 + }
429 ] 429 ]
430 }, 430 },
431 scope: this, 431 scope: this,
js/app/views/ResourcesMgrUI.js
@@ -309,7 +309,7 @@ Ext.define('amdaUI.ResourcesMgrUI', { @@ -309,7 +309,7 @@ Ext.define('amdaUI.ResourcesMgrUI', {
309 icon: 'js/resources/images/32x32/1309360076_misc_22.png', 309 icon: 'js/resources/images/32x32/1309360076_misc_22.png',
310 width: 32, 310 width: 32,
311 heigth: 32, 311 heigth: 32,
312 - tooltip: 'Add to my Ws', 312 + tooltip: 'Add to my Ws'
313 // width: 50 313 // width: 50
314 },{// remove from my Ws 314 },{// remove from my Ws
315 xtype: 'button', 315 xtype: 'button',
@@ -318,7 +318,7 @@ Ext.define('amdaUI.ResourcesMgrUI', { @@ -318,7 +318,7 @@ Ext.define('amdaUI.ResourcesMgrUI', {
318 // text: 'Gauche', 318 // text: 'Gauche',
319 // iconCls: 'icon-removeRsrc', 319 // iconCls: 'icon-removeRsrc',
320 icon: 'js/resources/images/32x32/1309360153_misc_21.png', 320 icon: 'js/resources/images/32x32/1309360153_misc_21.png',
321 - tooltip: 'remove from my Ws', 321 + tooltip: 'remove from my Ws'
322 // width: 50 322 // width: 50
323 }, 323 },
324 { 324 {
js/app/views/ShareObjectUI.js
@@ -92,7 +92,7 @@ Ext.define('amdaUI.ShareObjectUI',{ @@ -92,7 +92,7 @@ Ext.define('amdaUI.ShareObjectUI',{
92 this.infoTextArea = Ext.create('Ext.form.field.TextArea', { 92 this.infoTextArea = Ext.create('Ext.form.field.TextArea', {
93 xtype : 'textareafield', 93 xtype : 'textareafield',
94 hideLabel : true, 94 hideLabel : true,
95 - autoScroll : true, 95 + autoScroll : true
96 }); 96 });
97 97
98 this.folderStore = Ext.create('Ext.data.Store', { 98 this.folderStore = Ext.create('Ext.data.Store', {
@@ -226,4 +226,4 @@ Ext.define('amdaUI.ShareObjectUI',{ @@ -226,4 +226,4 @@ Ext.define('amdaUI.ShareObjectUI',{
226 226
227 Ext.apply (this , Ext.apply (arguments, myConf)); 227 Ext.apply (this , Ext.apply (arguments, myConf));
228 } 228 }
229 -});  
230 \ No newline at end of file 229 \ No newline at end of file
  230 +});
js/lib/ux/desktop/FitAllLayout.js deleted
@@ -1,63 +0,0 @@ @@ -1,63 +0,0 @@
1 -/*  
2 -  
3 -This file is part of Ext JS 4  
4 -  
5 -Copyright (c) 2011 Sencha Inc  
6 -  
7 -Contact: http://www.sencha.com/contact  
8 -  
9 -GNU General Public License Usage  
10 -This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.  
11 -  
12 -If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.  
13 -  
14 -*/  
15 -/*!  
16 - * Ext JS Library 4.0  
17 - * Copyright(c) 2006-2011 Sencha Inc.  
18 - * licensing@sencha.com  
19 - * http://www.sencha.com/license  
20 - */  
21 -  
22 -/**  
23 - * @class Ext.ux.desktop.FitAllLayout  
24 - * @extends Ext.layout.container.AbstractFit  
25 - * <p>This layout applies a "fit" layout to all items, overlaying them on top of each  
26 - * other.</p>  
27 - */  
28 -Ext.define('Ext.ux.desktop.FitAllLayout', {  
29 - extend: 'Ext.layout.container.AbstractFit',  
30 - alias: 'layout.fitall',  
31 -  
32 - // @private  
33 - onLayout : function() {  
34 - var me = this;  
35 - me.callParent();  
36 -  
37 - var size = me.getLayoutTargetSize();  
38 -  
39 - me.owner.items.each(function (item) {  
40 - me.setItemBox(item, size);  
41 - });  
42 - },  
43 -  
44 - getTargetBox : function() {  
45 - return this.getLayoutTargetSize();  
46 - },  
47 -  
48 - setItemBox : function(item, box) {  
49 - var me = this;  
50 - if (item && box.height > 0) {  
51 - if (item.layoutManagedWidth == 2) {  
52 - box.width = undefined;  
53 - }  
54 - if (item.layoutManagedHeight == 2) {  
55 - box.height = undefined;  
56 - }  
57 -  
58 - item.getEl().position('absolute', null, 0, 0);  
59 - me.setItemSize(item, box.width, box.height);  
60 - }  
61 - }  
62 -});  
63 -  
js/lib/ux/desktop/exampleModules/app.jsb3 deleted
@@ -1,1050 +0,0 @@ @@ -1,1050 +0,0 @@
1 -{  
2 - "projectName": "Example - Desktop",  
3 - "licenseText": "Copyright(c) 2011 Sencha Inc.\nlicensing@sencha.com",  
4 - "builds": [  
5 - {  
6 - "name": "All Classes Needed",  
7 - "target": "classes.js",  
8 - "compress": true,  
9 - "files": [  
10 - {  
11 - "path": "../../../platform/src/util/",  
12 - "name": "Observable.js"  
13 - },  
14 - {  
15 - "path": "js/",  
16 - "name": "Module.js"  
17 - },  
18 - {  
19 - "path": "../../../platform/src/chart/",  
20 - "name": "Callout.js"  
21 - },  
22 - {  
23 - "path": "../../../platform/src/chart/",  
24 - "name": "Mask.js"  
25 - },  
26 - {  
27 - "path": "../../../platform/src/chart/",  
28 - "name": "Navigation.js"  
29 - },  
30 - {  
31 - "path": "../../../platform/src/chart/",  
32 - "name": "Shape.js"  
33 - },  
34 - {  
35 - "path": "../../src/form/field/",  
36 - "name": "VTypes.js"  
37 - },  
38 - {  
39 - "path": "../../src/util/",  
40 - "name": "ClickRepeater.js"  
41 - },  
42 - {  
43 - "path": "../../src/util/",  
44 - "name": "TextMetrics.js"  
45 - },  
46 - {  
47 - "path": "../../src/util/",  
48 - "name": "KeyMap.js"  
49 - },  
50 - {  
51 - "path": "../../../platform/src/",  
52 - "name": "Template.js"  
53 - },  
54 - {  
55 - "path": "../../../platform/src/data/",  
56 - "name": "Operation.js"  
57 - },  
58 - {  
59 - "path": "../../../platform/src/data/",  
60 - "name": "validations.js"  
61 - },  
62 - {  
63 - "path": "../../../platform/src/draw/",  
64 - "name": "Color.js"  
65 - },  
66 - {  
67 - "path": "../../../platform/src/draw/",  
68 - "name": "Draw.js"  
69 - },  
70 - {  
71 - "path": "../../../platform/src/data/",  
72 - "name": "Tree.js"  
73 - },  
74 - {  
75 - "path": "../../src/form/field/",  
76 - "name": "Field.js"  
77 - },  
78 - {  
79 - "path": "../../src/dd/",  
80 - "name": "DragTracker.js"  
81 - },  
82 - {  
83 - "path": "../../../platform/src/util/",  
84 - "name": "Offset.js"  
85 - },  
86 - {  
87 - "path": "../../../platform/src/",  
88 - "name": "ComponentQuery.js"  
89 - },  
90 - {  
91 - "path": "../../../platform/src/util/",  
92 - "name": "HashMap.js"  
93 - },  
94 - {  
95 - "path": "../../src/util/",  
96 - "name": "KeyNav.js"  
97 - },  
98 - {  
99 - "path": "../../src/util/",  
100 - "name": "Floating.js"  
101 - },  
102 - {  
103 - "path": "../../src/layout/container/boxOverflow/",  
104 - "name": "None.js"  
105 - },  
106 - {  
107 - "path": "../../src/layout/container/boxOverflow/",  
108 - "name": "Scroller.js"  
109 - },  
110 - {  
111 - "path": "../../../platform/src/",  
112 - "name": "AbstractManager.js"  
113 - },  
114 - {  
115 - "path": "../../../platform/src/data/",  
116 - "name": "Association.js"  
117 - },  
118 - {  
119 - "path": "../../../platform/src/data/",  
120 - "name": "SortTypes.js"  
121 - },  
122 - {  
123 - "path": "../../../platform/src/fx/",  
124 - "name": "CubicBezier.js"  
125 - },  
126 - {  
127 - "path": "../../../platform/src/fx/",  
128 - "name": "PropertyHandler.js"  
129 - },  
130 - {  
131 - "path": "../../src/util/",  
132 - "name": "Animate.js"  
133 - },  
134 - {  
135 - "path": "../../../platform/src/util/",  
136 - "name": "Filter.js"  
137 - },  
138 - {  
139 - "path": "../../src/",  
140 - "name": "ZIndexManager.js"  
141 - },  
142 - {  
143 - "path": "../../src/panel/",  
144 - "name": "Proxy.js"  
145 - },  
146 - {  
147 - "path": "../../../platform/src/",  
148 - "name": "PluginManager.js"  
149 - },  
150 - {  
151 - "path": "../../../platform/src/layout/",  
152 - "name": "Layout.js"  
153 - },  
154 - {  
155 - "path": "../../../platform/src/data/",  
156 - "name": "Connection.js"  
157 - },  
158 - {  
159 - "path": "../../../platform/src/util/",  
160 - "name": "Sorter.js"  
161 - },  
162 - {  
163 - "path": "../../../platform/src/fx/",  
164 - "name": "Queue.js"  
165 - },  
166 - {  
167 - "path": "../../src/dd/",  
168 - "name": "StatusProxy.js"  
169 - },  
170 - {  
171 - "path": "../../../platform/src/",  
172 - "name": "ElementLoader.js"  
173 - },  
174 - {  
175 - "path": "../../../platform/src/fx/target/",  
176 - "name": "Target.js"  
177 - },  
178 - {  
179 - "path": "../../../platform/src/state/",  
180 - "name": "Provider.js"  
181 - },  
182 - {  
183 - "path": "../../../platform/src/data/writer/",  
184 - "name": "Writer.js"  
185 - },  
186 - {  
187 - "path": "../../../platform/src/data/",  
188 - "name": "ResultSet.js"  
189 - },  
190 - {  
191 - "path": "",  
192 - "name": "VideoWindow.js"  
193 - },  
194 - {  
195 - "path": "",  
196 - "name": "BogusModule.js"  
197 - },  
198 - {  
199 - "path": "",  
200 - "name": "BogusMenuModule.js"  
201 - },  
202 - {  
203 - "path": "../../../platform/src/chart/",  
204 - "name": "Label.js"  
205 - },  
206 - {  
207 - "path": "../../../platform/src/chart/theme/",  
208 - "name": "Theme.js"  
209 - },  
210 - {  
211 - "path": "../../../platform/src/chart/theme/",  
212 - "name": "Base.js"  
213 - },  
214 - {  
215 - "path": "../../src/util/",  
216 - "name": "ComponentDragger.js"  
217 - },  
218 - {  
219 - "path": "../../../platform/src/util/",  
220 - "name": "Region.js"  
221 - },  
222 - {  
223 - "path": "../../../platform/src/",  
224 - "name": "XTemplate.js"  
225 - },  
226 - {  
227 - "path": "../../../platform/src/",  
228 - "name": "ModelManager.js"  
229 - },  
230 - {  
231 - "path": "../../src/form/",  
232 - "name": "Labelable.js"  
233 - },  
234 - {  
235 - "path": "../../../platform/src/",  
236 - "name": "ComponentManager.js"  
237 - },  
238 - {  
239 - "path": "../../src/",  
240 - "name": "FocusManager.js"  
241 - },  
242 - {  
243 - "path": "../../../platform/src/layout/component/",  
244 - "name": "Component.js"  
245 - },  
246 - {  
247 - "path": "../../src/layout/component/",  
248 - "name": "Button.js"  
249 - },  
250 - {  
251 - "path": "../../src/layout/component/",  
252 - "name": "ProgressBar.js"  
253 - },  
254 - {  
255 - "path": "../../src/layout/component/field/",  
256 - "name": "Field.js"  
257 - },  
258 - {  
259 - "path": "../../src/layout/component/field/",  
260 - "name": "Text.js"  
261 - },  
262 - {  
263 - "path": "../../src/layout/component/field/",  
264 - "name": "TextArea.js"  
265 - },  
266 - {  
267 - "path": "../../src/layout/component/field/",  
268 - "name": "HtmlEditor.js"  
269 - },  
270 - {  
271 - "path": "../../../platform/src/layout/container/",  
272 - "name": "AbstractContainer.js"  
273 - },  
274 - {  
275 - "path": "../../src/layout/container/",  
276 - "name": "Container.js"  
277 - },  
278 - {  
279 - "path": "../../src/layout/container/",  
280 - "name": "Anchor.js"  
281 - },  
282 - {  
283 - "path": "../../src/dd/",  
284 - "name": "DragDropManager.js"  
285 - },  
286 - {  
287 - "path": "../../../platform/src/data/",  
288 - "name": "Types.js"  
289 - },  
290 - {  
291 - "path": "../../../platform/src/data/",  
292 - "name": "Field.js"  
293 - },  
294 - {  
295 - "path": "../../../platform/src/data/",  
296 - "name": "NodeInterface.js"  
297 - },  
298 - {  
299 - "path": "../../../platform/src/",  
300 - "name": "Ajax.js"  
301 - },  
302 - {  
303 - "path": "../../../platform/src/util/",  
304 - "name": "AbstractMixedCollection.js"  
305 - },  
306 - {  
307 - "path": "../../../platform/src/util/",  
308 - "name": "Sortable.js"  
309 - },  
310 - {  
311 - "path": "../../../platform/src/util/",  
312 - "name": "MixedCollection.js"  
313 - },  
314 - {  
315 - "path": "../../src/menu/",  
316 - "name": "Manager.js"  
317 - },  
318 - {  
319 - "path": "../../../platform/src/data/",  
320 - "name": "Errors.js"  
321 - },  
322 - {  
323 - "path": "../../../platform/src/data/",  
324 - "name": "StoreManager.js"  
325 - },  
326 - {  
327 - "path": "../../../platform/src/draw/",  
328 - "name": "CompositeSprite.js"  
329 - },  
330 - {  
331 - "path": "../../../platform/src/chart/",  
332 - "name": "LegendItem.js"  
333 - },  
334 - {  
335 - "path": "../../../platform/src/chart/",  
336 - "name": "Legend.js"  
337 - },  
338 - {  
339 - "path": "../../../platform/src/data/",  
340 - "name": "AbstractStore.js"  
341 - },  
342 - {  
343 - "path": "../../../platform/src/draw/",  
344 - "name": "Surface.js"  
345 - },  
346 - {  
347 - "path": "../../../platform/src/fx/",  
348 - "name": "Easing.js"  
349 - },  
350 - {  
351 - "path": "../../../platform/src/util/",  
352 - "name": "Grouper.js"  
353 - },  
354 - {  
355 - "path": "../../../platform/src/layout/container/",  
356 - "name": "Auto.js"  
357 - },  
358 - {  
359 - "path": "../../../platform/src/layout/component/",  
360 - "name": "AbstractDock.js"  
361 - },  
362 - {  
363 - "path": "../../src/layout/component/",  
364 - "name": "Dock.js"  
365 - },  
366 - {  
367 - "path": "../../../platform/src/",  
368 - "name": "LoadMask.js"  
369 - },  
370 - {  
371 - "path": "../../../platform/src/",  
372 - "name": "ComponentLoader.js"  
373 - },  
374 - {  
375 - "path": "../../../platform/src/layout/component/",  
376 - "name": "Auto.js"  
377 - },  
378 - {  
379 - "path": "../../../platform/src/layout/component/",  
380 - "name": "Draw.js"  
381 - },  
382 - {  
383 - "path": "../../../platform/src/fx/target/",  
384 - "name": "Element.js"  
385 - },  
386 - {  
387 - "path": "../../../platform/src/fx/target/",  
388 - "name": "CompositeElement.js"  
389 - },  
390 - {  
391 - "path": "../../../platform/src/fx/target/",  
392 - "name": "Sprite.js"  
393 - },  
394 - {  
395 - "path": "../../../platform/src/fx/target/",  
396 - "name": "CompositeSprite.js"  
397 - },  
398 - {  
399 - "path": "../../../platform/src/fx/target/",  
400 - "name": "Component.js"  
401 - },  
402 - {  
403 - "path": "../../../platform/src/fx/",  
404 - "name": "Manager.js"  
405 - },  
406 - {  
407 - "path": "../../../platform/src/fx/",  
408 - "name": "Animator.js"  
409 - },  
410 - {  
411 - "path": "../../../platform/src/fx/",  
412 - "name": "Anim.js"  
413 - },  
414 - {  
415 - "path": "../../../platform/src/chart/",  
416 - "name": "Highlight.js"  
417 - },  
418 - {  
419 - "path": "../../src/layout/component/",  
420 - "name": "Tip.js"  
421 - },  
422 - {  
423 - "path": "../../src/layout/component/",  
424 - "name": "Tab.js"  
425 - },  
426 - {  
427 - "path": "../../../platform/src/selection/",  
428 - "name": "Model.js"  
429 - },  
430 - {  
431 - "path": "../../src/selection/",  
432 - "name": "RowModel.js"  
433 - },  
434 - {  
435 - "path": "../../src/selection/",  
436 - "name": "TreeModel.js"  
437 - },  
438 - {  
439 - "path": "../../../platform/src/state/",  
440 - "name": "Manager.js"  
441 - },  
442 - {  
443 - "path": "../../../platform/src/state/",  
444 - "name": "Stateful.js"  
445 - },  
446 - {  
447 - "path": "../../../platform/src/",  
448 - "name": "AbstractComponent.js"  
449 - },  
450 - {  
451 - "path": "../../src/",  
452 - "name": "Component.js"  
453 - },  
454 - {  
455 - "path": "../../src/button/",  
456 - "name": "Button.js"  
457 - },  
458 - {  
459 - "path": "../../src/",  
460 - "name": "ProgressBar.js"  
461 - },  
462 - {  
463 - "path": "../../../platform/src/chart/",  
464 - "name": "MaskLayer.js"  
465 - },  
466 - {  
467 - "path": "../../src/toolbar/",  
468 - "name": "Spacer.js"  
469 - },  
470 - {  
471 - "path": "../../src/toolbar/",  
472 - "name": "Fill.js"  
473 - },  
474 - {  
475 - "path": "../../src/form/field/",  
476 - "name": "Base.js"  
477 - },  
478 - {  
479 - "path": "../../src/form/field/",  
480 - "name": "Text.js"  
481 - },  
482 - {  
483 - "path": "../../src/form/field/",  
484 - "name": "TextArea.js"  
485 - },  
486 - {  
487 - "path": "../../../platform/src/draw/",  
488 - "name": "Component.js"  
489 - },  
490 - {  
491 - "path": "../../../platform/src/chart/",  
492 - "name": "Chart.js"  
493 - },  
494 - {  
495 - "path": "../../../platform/src/chart/",  
496 - "name": "TipSurface.js"  
497 - },  
498 - {  
499 - "path": "../../../platform/src/chart/axis/",  
500 - "name": "Abstract.js"  
501 - },  
502 - {  
503 - "path": "../../../platform/src/chart/axis/",  
504 - "name": "Axis.js"  
505 - },  
506 - {  
507 - "path": "../../../platform/src/chart/axis/",  
508 - "name": "Category.js"  
509 - },  
510 - {  
511 - "path": "../../../platform/src/chart/axis/",  
512 - "name": "Gauge.js"  
513 - },  
514 - {  
515 - "path": "../../../platform/src/chart/axis/",  
516 - "name": "Numeric.js"  
517 - },  
518 - {  
519 - "path": "../../../platform/src/chart/axis/",  
520 - "name": "Radial.js"  
521 - },  
522 - {  
523 - "path": "../../src/picker/",  
524 - "name": "Color.js"  
525 - },  
526 - {  
527 - "path": "../../src/toolbar/",  
528 - "name": "Item.js"  
529 - },  
530 - {  
531 - "path": "../../../platform/src/container/",  
532 - "name": "AbstractContainer.js"  
533 - },  
534 - {  
535 - "path": "../../src/container/",  
536 - "name": "Container.js"  
537 - },  
538 - {  
539 - "path": "../../src/container/",  
540 - "name": "Viewport.js"  
541 - },  
542 - {  
543 - "path": "../../src/panel/",  
544 - "name": "Header.js"  
545 - },  
546 - {  
547 - "path": "../../src/grid/header/",  
548 - "name": "Container.js"  
549 - },  
550 - {  
551 - "path": "../../src/grid/column/",  
552 - "name": "Column.js"  
553 - },  
554 - {  
555 - "path": "../../src/grid/",  
556 - "name": "RowNumberer.js"  
557 - },  
558 - {  
559 - "path": "../../src/tree/",  
560 - "name": "Column.js"  
561 - },  
562 - {  
563 - "path": "../../src/tab/",  
564 - "name": "Tab.js"  
565 - },  
566 - {  
567 - "path": "../../src/tab/",  
568 - "name": "Bar.js"  
569 - },  
570 - {  
571 - "path": "../../src/toolbar/",  
572 - "name": "Separator.js"  
573 - },  
574 - {  
575 - "path": "../../src/layout/container/boxOverflow/",  
576 - "name": "Menu.js"  
577 - },  
578 - {  
579 - "path": "../../src/layout/container/",  
580 - "name": "Box.js"  
581 - },  
582 - {  
583 - "path": "../../src/layout/container/",  
584 - "name": "HBox.js"  
585 - },  
586 - {  
587 - "path": "../../src/layout/container/",  
588 - "name": "VBox.js"  
589 - },  
590 - {  
591 - "path": "../../src/toolbar/",  
592 - "name": "Toolbar.js"  
593 - },  
594 - {  
595 - "path": "../../src/layout/container/",  
596 - "name": "Accordion.js"  
597 - },  
598 - {  
599 - "path": "../../../platform/src/panel/",  
600 - "name": "AbstractPanel.js"  
601 - },  
602 - {  
603 - "path": "../../../platform/src/data/writer/",  
604 - "name": "Json.js"  
605 - },  
606 - {  
607 - "path": "../../../platform/src/layout/container/",  
608 - "name": "AbstractFit.js"  
609 - },  
610 - {  
611 - "path": "../../src/layout/container/",  
612 - "name": "Fit.js"  
613 - },  
614 - {  
615 - "path": "../../../platform/src/layout/container/",  
616 - "name": "AbstractCard.js"  
617 - },  
618 - {  
619 - "path": "../../src/layout/container/",  
620 - "name": "Card.js"  
621 - },  
622 - {  
623 - "path": "../../../platform/src/data/reader/",  
624 - "name": "Reader.js"  
625 - },  
626 - {  
627 - "path": "../../../platform/src/data/reader/",  
628 - "name": "Json.js"  
629 - },  
630 - {  
631 - "path": "../../../platform/src/data/proxy/",  
632 - "name": "Proxy.js"  
633 - },  
634 - {  
635 - "path": "../../../platform/src/data/proxy/",  
636 - "name": "Server.js"  
637 - },  
638 - {  
639 - "path": "../../../platform/src/data/proxy/",  
640 - "name": "Ajax.js"  
641 - },  
642 - {  
643 - "path": "../../../platform/src/data/",  
644 - "name": "Model.js"  
645 - },  
646 - {  
647 - "path": "js/",  
648 - "name": "ShortcutModel.js"  
649 - },  
650 - {  
651 - "path": "../../../platform/src/data/",  
652 - "name": "Store.js"  
653 - },  
654 - {  
655 - "path": "../../../platform/src/data/",  
656 - "name": "ArrayStore.js"  
657 - },  
658 - {  
659 - "path": "../../../platform/src/data/",  
660 - "name": "JsonStore.js"  
661 - },  
662 - {  
663 - "path": "../../../platform/src/chart/axis/",  
664 - "name": "Time.js"  
665 - },  
666 - {  
667 - "path": "../../../platform/src/data/",  
668 - "name": "NodeStore.js"  
669 - },  
670 - {  
671 - "path": "../../../platform/src/data/",  
672 - "name": "TreeStore.js"  
673 - },  
674 - {  
675 - "path": "../../../platform/src/selection/",  
676 - "name": "DataViewModel.js"  
677 - },  
678 - {  
679 - "path": "../../../platform/src/view/",  
680 - "name": "AbstractView.js"  
681 - },  
682 - {  
683 - "path": "../../src/view/",  
684 - "name": "View.js"  
685 - },  
686 - {  
687 - "path": "../../src/view/",  
688 - "name": "Table.js"  
689 - },  
690 - {  
691 - "path": "../../src/grid/",  
692 - "name": "View.js"  
693 - },  
694 - {  
695 - "path": "../../src/tree/",  
696 - "name": "View.js"  
697 - },  
698 - {  
699 - "path": "../../src/dd/",  
700 - "name": "DragDrop.js"  
701 - },  
702 - {  
703 - "path": "../../src/dd/",  
704 - "name": "DD.js"  
705 - },  
706 - {  
707 - "path": "../../src/dd/",  
708 - "name": "DDProxy.js"  
709 - },  
710 - {  
711 - "path": "../../src/dd/",  
712 - "name": "DragSource.js"  
713 - },  
714 - {  
715 - "path": "../../src/panel/",  
716 - "name": "DD.js"  
717 - },  
718 - {  
719 - "path": "../../src/panel/",  
720 - "name": "Panel.js"  
721 - },  
722 - {  
723 - "path": "js/",  
724 - "name": "Desktop.js"  
725 - },  
726 - {  
727 - "path": "js/",  
728 - "name": "App.js"  
729 - },  
730 - {  
731 - "path": "../../src/window/",  
732 - "name": "Window.js"  
733 - },  
734 - {  
735 - "path": "../../src/window/",  
736 - "name": "MessageBox.js"  
737 - },  
738 - {  
739 - "path": "",  
740 - "name": "Settings.js"  
741 - },  
742 - {  
743 - "path": "../../src/tab/",  
744 - "name": "Panel.js"  
745 - },  
746 - {  
747 - "path": "",  
748 - "name": "TabWindow.js"  
749 - },  
750 - {  
751 - "path": "../../src/panel/",  
752 - "name": "Table.js"  
753 - },  
754 - {  
755 - "path": "../../src/grid/",  
756 - "name": "Panel.js"  
757 - },  
758 - {  
759 - "path": "",  
760 - "name": "GridWindow.js"  
761 - },  
762 - {  
763 - "path": "../../src/tree/",  
764 - "name": "Panel.js"  
765 - },  
766 - {  
767 - "path": "",  
768 - "name": "AccordionWindow.js"  
769 - },  
770 - {  
771 - "path": "../../src/tip/",  
772 - "name": "Tip.js"  
773 - },  
774 - {  
775 - "path": "../../src/tip/",  
776 - "name": "ToolTip.js"  
777 - },  
778 - {  
779 - "path": "../../../platform/src/chart/",  
780 - "name": "Tip.js"  
781 - },  
782 - {  
783 - "path": "../../../platform/src/chart/series/",  
784 - "name": "Series.js"  
785 - },  
786 - {  
787 - "path": "../../../platform/src/chart/series/",  
788 - "name": "Cartesian.js"  
789 - },  
790 - {  
791 - "path": "../../../platform/src/chart/series/",  
792 - "name": "Area.js"  
793 - },  
794 - {  
795 - "path": "../../../platform/src/chart/series/",  
796 - "name": "Bar.js"  
797 - },  
798 - {  
799 - "path": "../../../platform/src/chart/series/",  
800 - "name": "Column.js"  
801 - },  
802 - {  
803 - "path": "../../../platform/src/chart/series/",  
804 - "name": "Gauge.js"  
805 - },  
806 - {  
807 - "path": "../../../platform/src/chart/series/",  
808 - "name": "Line.js"  
809 - },  
810 - {  
811 - "path": "../../../platform/src/chart/series/",  
812 - "name": "Pie.js"  
813 - },  
814 - {  
815 - "path": "../../../platform/src/chart/series/",  
816 - "name": "Radar.js"  
817 - },  
818 - {  
819 - "path": "../../../platform/src/chart/series/",  
820 - "name": "Scatter.js"  
821 - },  
822 - {  
823 - "path": "",  
824 - "name": "SystemStatus.js"  
825 - },  
826 - {  
827 - "path": "../../src/tip/",  
828 - "name": "QuickTip.js"  
829 - },  
830 - {  
831 - "path": "../../src/tip/",  
832 - "name": "QuickTipManager.js"  
833 - },  
834 - {  
835 - "path": "../../src/form/field/",  
836 - "name": "HtmlEditor.js"  
837 - },  
838 - {  
839 - "path": "",  
840 - "name": "Notepad.js"  
841 - },  
842 - {  
843 - "path": "",  
844 - "name": "App.js"  
845 - },  
846 - {  
847 - "path": "../../../platform/src/util/",  
848 - "name": "Point.js"  
849 - },  
850 - {  
851 - "path": "../../src/",  
852 - "name": "Layer.js"  
853 - },  
854 - {  
855 - "path": "js/",  
856 - "name": "Video.js"  
857 - },  
858 - {  
859 - "path": "../../src/resizer/",  
860 - "name": "Resizer.js"  
861 - },  
862 - {  
863 - "path": "../../src/panel/",  
864 - "name": "Tool.js"  
865 - },  
866 - {  
867 - "path": "../../src/util/",  
868 - "name": "CSS.js"  
869 - },  
870 - {  
871 - "path": "../../src/grid/",  
872 - "name": "ColumnLayout.js"  
873 - },  
874 - {  
875 - "path": "../../src/grid/plugin/",  
876 - "name": "HeaderResizer.js"  
877 - },  
878 - {  
879 - "path": "../../../platform/src/data/",  
880 - "name": "Batch.js"  
881 - },  
882 - {  
883 - "path": "../../../platform/src/data/",  
884 - "name": "Request.js"  
885 - },  
886 - {  
887 - "path": "../../../platform/src/data/reader/",  
888 - "name": "Array.js"  
889 - },  
890 - {  
891 - "path": "../../src/view/",  
892 - "name": "TableChunker.js"  
893 - },  
894 - {  
895 - "path": "js/",  
896 - "name": "Wallpaper.js"  
897 - },  
898 - {  
899 - "path": "js/",  
900 - "name": "FitAllLayout.js"  
901 - },  
902 - {  
903 - "path": "",  
904 - "name": "WallpaperModel.js"  
905 - },  
906 - {  
907 - "path": "../../src/grid/",  
908 - "name": "Scroller.js"  
909 - },  
910 - {  
911 - "path": "../../src/menu/",  
912 - "name": "Item.js"  
913 - },  
914 - {  
915 - "path": "../../src/menu/",  
916 - "name": "KeyNav.js"  
917 - },  
918 - {  
919 - "path": "../../../platform/src/draw/",  
920 - "name": "Matrix.js"  
921 - },  
922 - {  
923 - "path": "../../../platform/src/data/proxy/",  
924 - "name": "Client.js"  
925 - },  
926 - {  
927 - "path": "../../src/resizer/",  
928 - "name": "Splitter.js"  
929 - },  
930 - {  
931 - "path": "../../src/toolbar/",  
932 - "name": "TextItem.js"  
933 - },  
934 - {  
935 - "path": "../../src/form/",  
936 - "name": "CheckboxManager.js"  
937 - },  
938 - {  
939 - "path": "../../src/grid/",  
940 - "name": "LockingView.js"  
941 - },  
942 - {  
943 - "path": "../../../platform/src/draw/",  
944 - "name": "SpriteDD.js"  
945 - },  
946 - {  
947 - "path": "../../src/dd/",  
948 - "name": "DragZone.js"  
949 - },  
950 - {  
951 - "path": "../../src/dd/",  
952 - "name": "Registry.js"  
953 - },  
954 - {  
955 - "path": "../../src/dd/",  
956 - "name": "DDTarget.js"  
957 - },  
958 - {  
959 - "path": "../../src/dd/",  
960 - "name": "ScrollManager.js"  
961 - },  
962 - {  
963 - "path": "../../src/menu/",  
964 - "name": "CheckItem.js"  
965 - },  
966 - {  
967 - "path": "../../src/menu/",  
968 - "name": "Separator.js"  
969 - },  
970 - {  
971 - "path": "../../src/menu/",  
972 - "name": "Menu.js"  
973 - },  
974 - {  
975 - "path": "../../../platform/src/data/proxy/",  
976 - "name": "Memory.js"  
977 - },  
978 - {  
979 - "path": "../../src/form/field/",  
980 - "name": "Checkbox.js"  
981 - },  
982 - {  
983 - "path": "../../src/layout/container/",  
984 - "name": "Border.js"  
985 - },  
986 - {  
987 - "path": "../../src/grid/",  
988 - "name": "Lockable.js"  
989 - },  
990 - {  
991 - "path": "../../../platform/src/draw/",  
992 - "name": "Sprite.js"  
993 - },  
994 - {  
995 - "path": "../../../platform/src/draw/engine/",  
996 - "name": "Svg.js"  
997 - },  
998 - {  
999 - "path": "../../../platform/src/draw/engine/",  
1000 - "name": "Vml.js"  
1001 - },  
1002 - {  
1003 - "path": "../../src/grid/header/",  
1004 - "name": "DragZone.js"  
1005 - },  
1006 - {  
1007 - "path": "js/",  
1008 - "name": "StartMenu.js"  
1009 - },  
1010 - {  
1011 - "path": "js/",  
1012 - "name": "TaskBar.js"  
1013 - },  
1014 - {  
1015 - "path": "../../src/dd/",  
1016 - "name": "DropTarget.js"  
1017 - },  
1018 - {  
1019 - "path": "../../src/dd/",  
1020 - "name": "DropZone.js"  
1021 - },  
1022 - {  
1023 - "path": "../../src/grid/header/",  
1024 - "name": "DropZone.js"  
1025 - },  
1026 - {  
1027 - "path": "../../src/grid/plugin/",  
1028 - "name": "HeaderReorderer.js"  
1029 - },  
1030 - {  
1031 - "path": "../../src/resizer/",  
1032 - "name": "ResizeTracker.js"  
1033 - },  
1034 - {  
1035 - "path": "../../src/resizer/",  
1036 - "name": "SplitterTracker.js"  
1037 - },  
1038 - {  
1039 - "path": "../../src/",  
1040 - "name": "ShadowPool.js"  
1041 - },  
1042 - {  
1043 - "path": "../../src/",  
1044 - "name": "Shadow.js"  
1045 - }  
1046 - ]  
1047 - }  
1048 - ],  
1049 - "resources": []  
1050 -}  
1051 \ No newline at end of file 0 \ No newline at end of file
js/lib/ux/grid/FiltersFeature.js 0 โ†’ 100644
@@ -0,0 +1,868 @@ @@ -0,0 +1,868 @@
  1 +/**
  2 + * FiltersFeature is a grid {@link Ext.grid.feature.Feature feature} that allows for a slightly more
  3 + * robust representation of filtering than what is provided by the default store.
  4 + *
  5 + * Filtering is adjusted by the user using the grid's column header menu (this menu can be
  6 + * disabled through configuration). Through this menu users can configure, enable, and
  7 + * disable filters for each column.
  8 + *
  9 + * #Features#
  10 + *
  11 + * ##Filtering implementations:##
  12 + *
  13 + * Default filtering for Strings, Numeric Ranges, Date Ranges, Lists (which can be backed by a
  14 + * {@link Ext.data.Store}), and Boolean. Additional custom filter types and menus are easily
  15 + * created by extending {@link Ext.ux.grid.filter.Filter}.
  16 + *
  17 + * ##Graphical Indicators:##
  18 + *
  19 + * Columns that are filtered have {@link #filterCls a configurable css class} applied to the column headers.
  20 + *
  21 + * ##Automatic Reconfiguration:##
  22 + *
  23 + * Filters automatically reconfigure when the grid 'reconfigure' event fires.
  24 + *
  25 + * ##Stateful:##
  26 + *
  27 + * Filter information will be persisted across page loads by specifying a `stateId`
  28 + * in the Grid configuration.
  29 + *
  30 + * The filter collection binds to the {@link Ext.grid.Panel#beforestaterestore beforestaterestore}
  31 + * and {@link Ext.grid.Panel#beforestatesave beforestatesave} events in order to be stateful.
  32 + *
  33 + * ##GridPanel Changes:##
  34 + *
  35 + * - A `filters` property is added to the GridPanel using this feature.
  36 + * - A `filterupdate` event is added to the GridPanel and is fired upon onStateChange completion.
  37 + *
  38 + * ##Server side code examples:##
  39 + *
  40 + * - [PHP](http://www.vinylfox.com/extjs/grid-filter-php-backend-code.php) - (Thanks VinylFox)
  41 + * - [Ruby on Rails](http://extjs.com/forum/showthread.php?p=77326#post77326) - (Thanks Zyclops)
  42 + * - [Ruby on Rails](http://extjs.com/forum/showthread.php?p=176596#post176596) - (Thanks Rotomaul)
  43 + *
  44 + * #Example usage:#
  45 + *
  46 + * var store = Ext.create('Ext.data.Store', {
  47 + * pageSize: 15
  48 + * ...
  49 + * });
  50 + *
  51 + * var filtersCfg = {
  52 + * ftype: 'filters',
  53 + * autoReload: false, //don't reload automatically
  54 + * local: true, //only filter locally
  55 + * // filters may be configured through the plugin,
  56 + * // or in the column definition within the headers configuration
  57 + * filters: [{
  58 + * type: 'numeric',
  59 + * dataIndex: 'id'
  60 + * }, {
  61 + * type: 'string',
  62 + * dataIndex: 'name'
  63 + * }, {
  64 + * type: 'numeric',
  65 + * dataIndex: 'price'
  66 + * }, {
  67 + * type: 'date',
  68 + * dataIndex: 'dateAdded'
  69 + * }, {
  70 + * type: 'list',
  71 + * dataIndex: 'size',
  72 + * options: ['extra small', 'small', 'medium', 'large', 'extra large'],
  73 + * phpMode: true
  74 + * }, {
  75 + * type: 'boolean',
  76 + * dataIndex: 'visible'
  77 + * }]
  78 + * };
  79 + *
  80 + * var grid = Ext.create('Ext.grid.Panel', {
  81 + * store: store,
  82 + * columns: ...,
  83 + * features: [filtersCfg],
  84 + * height: 400,
  85 + * width: 700,
  86 + * bbar: Ext.create('Ext.PagingToolbar', {
  87 + * store: store
  88 + * })
  89 + * });
  90 + *
  91 + * // a filters property is added to the GridPanel
  92 + * grid.filters
  93 + */
  94 +Ext.define('Ext.ux.grid.FiltersFeature', {
  95 + extend: 'Ext.grid.feature.Feature',
  96 + alias: 'feature.filters',
  97 + uses: [
  98 + 'Ext.ux.grid.menu.ListMenu',
  99 + 'Ext.ux.grid.menu.RangeMenu',
  100 + 'Ext.ux.grid.filter.BooleanFilter',
  101 + 'Ext.ux.grid.filter.DateFilter',
  102 + 'Ext.ux.grid.filter.DateTimeFilter',
  103 + 'Ext.ux.grid.filter.ListFilter',
  104 + 'Ext.ux.grid.filter.NumericFilter',
  105 + 'Ext.ux.grid.filter.StringFilter'
  106 + ],
  107 +
  108 + /**
  109 + * @cfg {Boolean} autoReload
  110 + * Defaults to true, reloading the datasource when a filter change happens.
  111 + * Set this to false to prevent the datastore from being reloaded if there
  112 + * are changes to the filters. See `{@link #updateBuffer}`.
  113 + */
  114 + autoReload : true,
  115 + /**
  116 + * @cfg {Boolean} encode
  117 + * Specify true for {@link #buildQuery} to use Ext.util.JSON.encode to
  118 + * encode the filter query parameter sent with a remote request.
  119 + * Defaults to false.
  120 + */
  121 + /**
  122 + * @cfg {Array} filters
  123 + * An Array of filters config objects. Refer to each filter type class for
  124 + * configuration details specific to each filter type. Filters for Strings,
  125 + * Numeric Ranges, Date Ranges, Lists, and Boolean are the standard filters
  126 + * available.
  127 + */
  128 + /**
  129 + * @cfg {String} filterCls
  130 + * The css class to be applied to column headers with active filters.
  131 + * Defaults to `'ux-filterd-column'`
  132 + */
  133 + filterCls : 'ux-filtered-column',
  134 + /**
  135 + * @cfg {Boolean} local
  136 + * <tt>true</tt> to use Ext.data.Store filter functions (local filtering)
  137 + * instead of the default (<tt>false</tt>) server side filtering.
  138 + */
  139 + local : false,
  140 + /**
  141 + * @cfg {String} menuFilterText
  142 + * defaults to `'Filters'`.
  143 + */
  144 + menuFilterText : 'Filters',
  145 + /**
  146 + * @cfg {String} paramPrefix
  147 + * The url parameter prefix for the filters.
  148 + * Defaults to `'filter'`.
  149 + */
  150 + paramPrefix : 'filter',
  151 + /**
  152 + * @cfg {Boolean} showMenu
  153 + * Defaults to true, including a filter submenu in the default header menu.
  154 + */
  155 + showMenu : true,
  156 + /**
  157 + * @cfg {String} stateId
  158 + * Name of the value to be used to store state information.
  159 + */
  160 + stateId : undefined,
  161 + /**
  162 + * @cfg {Number} updateBuffer
  163 + * Number of milliseconds to defer store updates since the last filter change.
  164 + */
  165 + updateBuffer : 500,
  166 +
  167 + // doesn't handle grid body events
  168 + hasFeatureEvent: false,
  169 +
  170 + /** @private */
  171 + constructor : function (config) {
  172 + var me = this;
  173 +
  174 + me.callParent(arguments);
  175 +
  176 + me.deferredUpdate = Ext.create('Ext.util.DelayedTask', me.reload, me);
  177 +
  178 + // Init filters
  179 + me.filters = me.createFiltersCollection();
  180 + me.filterConfigs = config.filters;
  181 + },
  182 +
  183 + init: function(grid) {
  184 + var me = this,
  185 + view = me.view,
  186 + headerCt = view.headerCt;
  187 +
  188 + me.bindStore(view.getStore(), true);
  189 +
  190 + // Listen for header menu being created
  191 + headerCt.on({
  192 + scope: me,
  193 + menucreate: me.onMenuCreate,
  194 + add: me.onAddRemoveColumn,
  195 + remove: me.onAddRemoveColumn
  196 + });
  197 +
  198 + view.on('refresh', me.onRefresh, me);
  199 + grid.on({
  200 + scope: me,
  201 + beforestaterestore: me.applyState,
  202 + beforestatesave: me.saveState,
  203 + beforedestroy: me.destroy
  204 + });
  205 +
  206 + // Add event and filters shortcut on grid panel
  207 + grid.filters = me;
  208 + grid.addEvents('filterupdate');
  209 + me.createFilters();
  210 + },
  211 +
  212 + createFiltersCollection: function () {
  213 + return Ext.create('Ext.util.MixedCollection', false, function (o) {
  214 + return o ? o.dataIndex : null;
  215 + });
  216 + },
  217 +
  218 + /**
  219 + * @private Create the Filter objects for the current configuration, destroying any existing ones first.
  220 + */
  221 + createFilters: function() {
  222 + var me = this,
  223 + hadFilters = me.filters.getCount(),
  224 + grid = me.getGridPanel(),
  225 + filters = me.createFiltersCollection(),
  226 + model = grid.store.model,
  227 + fields = model.prototype.fields,
  228 + field,
  229 + filter,
  230 + state;
  231 +
  232 + if (hadFilters) {
  233 + state = {};
  234 + me.saveState(null, state);
  235 + }
  236 +
  237 + function add (dataIndex, config, filterable) {
  238 + if (dataIndex && (filterable || config)) {
  239 + field = fields.get(dataIndex);
  240 + filter = {
  241 + dataIndex: dataIndex,
  242 + type: (field && field.type && field.type.type) || 'auto'
  243 + };
  244 +
  245 + if (Ext.isObject(config)) {
  246 + Ext.apply(filter, config);
  247 + }
  248 +
  249 + filters.replace(filter);
  250 + }
  251 + }
  252 +
  253 + // We start with filters from our config.
  254 + // Note that we will not recreate filters that are defined in the filters feature config block when
  255 + // reconfiguring the grid, but we must recreate any filters that are defined on the columns themselves.
  256 + if (!me.grid.reconfiguring) {
  257 + Ext.Array.each(me.filterConfigs, function (filterConfig) {
  258 + add(filterConfig.dataIndex, filterConfig);
  259 + });
  260 + }
  261 +
  262 + // Then we merge on filters from the columns in the grid. The columns' filters take precedence.
  263 + Ext.Array.each(grid.columnManager.getColumns(), function (column) {
  264 + if (column.filterable === false) {
  265 + filters.removeAtKey(column.dataIndex);
  266 + } else {
  267 + add(column.dataIndex, column.filter, column.filterable);
  268 + }
  269 + });
  270 +
  271 + me.removeAll();
  272 +
  273 + if (filters.items.length) {
  274 + me.initializeFilters(filters.items);
  275 + }
  276 +
  277 + if (hadFilters) {
  278 + me.applyState(null, state);
  279 + }
  280 + },
  281 +
  282 + /**
  283 + * @private
  284 + */
  285 + initializeFilters: function(filters) {
  286 + var me = this,
  287 + filtersLength = filters.length,
  288 + i, filter, FilterClass;
  289 +
  290 + for (i = 0; i < filtersLength; i++) {
  291 + filter = filters[i];
  292 + if (filter) {
  293 + FilterClass = me.getFilterClass(filter.type);
  294 + filter = filter.menu ? filter : new FilterClass(Ext.apply({
  295 + grid: me.grid
  296 + }, filter));
  297 + me.filters.add(filter);
  298 + Ext.util.Observable.capture(filter, me.onStateChange, me);
  299 + }
  300 + }
  301 + },
  302 +
  303 + onAddRemoveColumn: function () {
  304 + this.createFilters();
  305 + },
  306 +
  307 + /**
  308 + * @private Handle creation of the grid's header menu. Initializes the filters and listens
  309 + * for the menu being shown.
  310 + */
  311 + onMenuCreate: function(headerCt, menu) {
  312 + var me = this;
  313 +
  314 + // If the menu is ever destroyed, the filters need recreating because
  315 + // the filters' menu structures will be destroyed.
  316 + if (me.filtersNeedReCreating) {
  317 + me.createFilters();
  318 + me.filtersNeedReCreating = false;
  319 + }
  320 +
  321 + menu.on({
  322 + beforeshow: me.onMenuBeforeShow,
  323 + destroy: me.onMenuDestroy,
  324 + scope: me
  325 + });
  326 + },
  327 +
  328 + // The filters at first have to be created at init time so that state can be restored if the grid subsequently
  329 + // fires a beforestaterestore event.
  330 + // However after that, they may need recreating if the column menu is ever destroyed (due to column movement) because
  331 + // that tears down the whole filter item and submenu structure.
  332 + onMenuDestroy: function() {
  333 + this.filtersNeedReCreating = true;
  334 + },
  335 +
  336 + /**
  337 + * @private Handle showing of the grid's header menu. Sets up the filter item and menu
  338 + * appropriate for the target column.
  339 + */
  340 + onMenuBeforeShow: function(menu) {
  341 + var me = this,
  342 + menuItem, filter;
  343 +
  344 + if (me.showMenu) {
  345 + menuItem = me.menuItem;
  346 + if (!menuItem || menuItem.isDestroyed) {
  347 + me.createMenuItem(menu);
  348 + menuItem = me.menuItem;
  349 + }
  350 +
  351 + filter = me.getMenuFilter();
  352 +
  353 + if (filter) {
  354 + menuItem.setMenu(filter.menu, false);
  355 + menuItem.setChecked(filter.active);
  356 + // disable the menu if filter.disabled explicitly set to true
  357 + menuItem.setDisabled(filter.disabled === true);
  358 + }
  359 + menuItem.setVisible(!!filter);
  360 + me.sep.setVisible(!!filter);
  361 + }
  362 + },
  363 +
  364 + createMenuItem: function(menu) {
  365 + var me = this;
  366 + me.sep = menu.add('-');
  367 + me.menuItem = menu.add({
  368 + checked: false,
  369 + itemId: 'filters',
  370 + text: me.menuFilterText,
  371 + listeners: {
  372 + scope: me,
  373 + checkchange: me.onCheckChange,
  374 + beforecheckchange: me.onBeforeCheck
  375 + }
  376 + });
  377 + },
  378 +
  379 + getGridPanel: function() {
  380 + // This reference is injected in TableView.initFeatures
  381 + return this.grid;
  382 + },
  383 +
  384 + /**
  385 + * @private
  386 + * Handler for the grid's beforestaterestore event (fires before the state of the
  387 + * grid is restored).
  388 + * @param {Object} grid The grid object
  389 + * @param {Object} state The hash of state values returned from the StateProvider.
  390 + */
  391 + applyState : function (grid, state) {
  392 + var me = this,
  393 + key, filter;
  394 + me.applyingState = true;
  395 + me.clearFilters();
  396 + if (state.filters) {
  397 + for (key in state.filters) {
  398 + if (state.filters.hasOwnProperty(key)) {
  399 + filter = me.filters.get(key);
  400 + if (filter) {
  401 + filter.setValue(state.filters[key]);
  402 + filter.setActive(true);
  403 + }
  404 + }
  405 + }
  406 + }
  407 + me.deferredUpdate.cancel();
  408 +
  409 + delete me.applyingState;
  410 + delete state.filters;
  411 + },
  412 +
  413 + /**
  414 + * Saves the state of all active filters
  415 + * @param {Object} grid
  416 + * @param {Object} state
  417 + * @return {Boolean}
  418 + */
  419 + saveState : function (grid, state) {
  420 + var filters = {};
  421 + this.filters.each(function (filter) {
  422 + if (filter.active) {
  423 + filters[filter.dataIndex] = filter.getValue();
  424 + }
  425 + });
  426 +
  427 + return (state.filters = filters);
  428 + },
  429 +
  430 + /**
  431 + * @private
  432 + * Handler called by the grid 'beforedestroy' event
  433 + */
  434 + destroy : function () {
  435 + var me = this;
  436 +
  437 + me.deferredUpdate.cancel();
  438 + Ext.destroyMembers(me, 'menuItem', 'sep');
  439 + me.removeAll();
  440 + me.clearListeners();
  441 + },
  442 +
  443 + /**
  444 + * Remove all filters, permanently destroying them.
  445 + */
  446 + removeAll : function () {
  447 + if(this.filters){
  448 + Ext.destroy.apply(Ext, this.filters.items);
  449 + // remove all items from the collection
  450 + this.filters.clear();
  451 + }
  452 + },
  453 +
  454 + /**
  455 + * Changes the data store bound to this view and refreshes it.
  456 + * @param {Ext.data.Store} store The store to bind to this view
  457 + */
  458 + bindStore : function(store) {
  459 + var me = this;
  460 +
  461 + // Unbind from the old Store
  462 + if (me.store && me.storeListeners) {
  463 + me.store.un(me.storeListeners);
  464 + }
  465 +
  466 + // Set up correct listeners
  467 + if (store) {
  468 + me.storeListeners = {
  469 + scope: me
  470 + };
  471 + if (me.local) {
  472 + me.storeListeners.load = me.onLoad;
  473 + } else {
  474 + me.storeListeners['before' + (store.buffered ? 'prefetch' : 'load')] = me.onBeforeLoad;
  475 + }
  476 + store.on(me.storeListeners);
  477 + } else {
  478 + delete me.storeListeners;
  479 + }
  480 + me.store = store;
  481 + },
  482 +
  483 + getFilterByDataIndex: function (dataIndex) {
  484 + return Ext.Array.findBy(this.getFilterItems(), function (item) {
  485 + return item.dataIndex === dataIndex;
  486 + });
  487 + },
  488 +
  489 + /**
  490 + * @private
  491 + * Get the filter menu from the filters MixedCollection based on the clicked header
  492 + */
  493 + getMenuFilter : function () {
  494 + var header = this.view.headerCt.getMenu().activeHeader;
  495 + return header ? this.getFilterByDataIndex(header.dataIndex) : null;
  496 + },
  497 +
  498 + /** @private */
  499 + onCheckChange : function (item, value) {
  500 + this.getMenuFilter().setActive(value);
  501 + },
  502 +
  503 + /** @private */
  504 + onBeforeCheck : function (check, value) {
  505 + return !value || this.getMenuFilter().isActivatable();
  506 + },
  507 +
  508 + /**
  509 + * @private
  510 + * Handler for all events on filters.
  511 + * @param {String} event Event name
  512 + * @param {Object} filter Standard signature of the event before the event is fired
  513 + */
  514 + onStateChange : function (event, filter) {
  515 + if (event !== 'serialize') {
  516 + var me = this,
  517 + grid = me.getGridPanel();
  518 +
  519 + if (filter == me.getMenuFilter()) {
  520 + me.menuItem.setChecked(filter.active, false);
  521 + }
  522 +
  523 + if ((me.autoReload || me.local) && !me.applyingState) {
  524 + me.deferredUpdate.delay(me.updateBuffer);
  525 + }
  526 + me.updateColumnHeadings();
  527 +
  528 + if (!me.applyingState) {
  529 + grid.saveState();
  530 + }
  531 + grid.fireEvent('filterupdate', me, filter);
  532 + }
  533 + },
  534 +
  535 + /**
  536 + * @private
  537 + * Handler for store's beforeload event when configured for remote filtering
  538 + * @param {Object} store
  539 + * @param {Object} options
  540 + */
  541 + onBeforeLoad : function (store, options) {
  542 + var params;
  543 +
  544 + options.params = options.params || {};
  545 + this.cleanParams(options.params);
  546 + params = this.buildQuery(this.getFilterData());
  547 +
  548 + // Memory proxy
  549 + if (store.getProxy().isSynchronous && this.hasActiveFilter()) {
  550 + options.filters = [new Ext.util.Filter({
  551 + filterFn: this.getRecordFilter()
  552 + })];
  553 + }
  554 +
  555 + Ext.apply(options.params, params);
  556 + },
  557 +
  558 + /**
  559 + * @private
  560 + * Handler for store's load event when configured for local filtering
  561 + * @param {Object} store
  562 + */
  563 + onLoad : function (store) {
  564 + if (this.filters.length) {
  565 + store.filterBy(this.getRecordFilter());
  566 + }
  567 + },
  568 +
  569 + /**
  570 + * @private
  571 + * Handler called when the grid's view is refreshed
  572 + */
  573 + onRefresh : function () {
  574 + this.updateColumnHeadings();
  575 + },
  576 +
  577 + /**
  578 + * Update the styles for the header row based on the active filters
  579 + */
  580 + updateColumnHeadings: function (headerCt) {
  581 + var me = this,
  582 + headerCt = headerCt || me.view.headerCt;
  583 +
  584 + if (headerCt) {
  585 + headerCt.items.each(function (header) {
  586 + var filter;
  587 +
  588 + if (header.isGroupHeader) {
  589 + me.updateColumnHeadings(header);
  590 + }
  591 +
  592 + filter = me.getFilter(header.dataIndex);
  593 + header[filter && filter.active ? 'addCls' : 'removeCls'](me.filterCls);
  594 + });
  595 + }
  596 + },
  597 +
  598 + /** @private */
  599 + reload : function () {
  600 + var me = this,
  601 + store = me.view.getStore();
  602 +
  603 + if (me.local) {
  604 + store.clearFilter(true);
  605 + store.filterBy(me.getRecordFilter());
  606 + store.sort();
  607 + } else {
  608 + me.deferredUpdate.cancel();
  609 + if (store.buffered) {
  610 + store.data.clear();
  611 + }
  612 + store.loadPage(1);
  613 + }
  614 + },
  615 +
  616 + /**
  617 + * Method factory that generates a record validator for the filters active at the time
  618 + * of invokation.
  619 + * @private
  620 + */
  621 + getRecordFilter : function () {
  622 + var f = [], len, i,
  623 + lockingPartner = this.lockingPartner;
  624 +
  625 + this.filters.each(function (filter) {
  626 + if (filter.active) {
  627 + f.push(filter);
  628 + }
  629 + });
  630 +
  631 + // Be sure to check the active filters on a locking partner as well.
  632 + if (lockingPartner) {
  633 + lockingPartner.filters.each(function (filter) {
  634 + if (filter.active) {
  635 + f.push(filter);
  636 + }
  637 + });
  638 + }
  639 +
  640 + len = f.length;
  641 + return function (record) {
  642 + for (i = 0; i < len; i++) {
  643 + if (!f[i].validateRecord(record)) {
  644 + return false;
  645 + }
  646 + }
  647 + return true;
  648 + };
  649 + },
  650 +
  651 + hasActiveFilter: function(){
  652 + var result = false;
  653 + this.filters.each(function (filter) {
  654 + if (filter.active) {
  655 + result = true;
  656 + return false;
  657 + }
  658 + });
  659 + return result;
  660 + },
  661 +
  662 + /**
  663 + * Adds a filter to the collection and observes it for state change.
  664 + * @param {Object/Ext.ux.grid.filter.Filter} config A filter configuration or a filter object.
  665 + * @return {Ext.ux.grid.filter.Filter} The existing or newly created filter object.
  666 + */
  667 + addFilter : function (config) {
  668 + var me = this,
  669 + columns = me.getGridPanel().columnManager.getColumns(),
  670 + i, columnsLength, column, filtersLength, filter;
  671 +
  672 +
  673 + for (i = 0, columnsLength = columns.length; i < columnsLength; i++) {
  674 + column = columns[i];
  675 + if (column.dataIndex === config.dataIndex) {
  676 + column.filter = config;
  677 + }
  678 + }
  679 +
  680 + if (me.view.headerCt.menu) {
  681 + me.createFilters();
  682 + } else {
  683 + // Call getMenu() to ensure the menu is created, and so, also are the filters. We cannot call
  684 + // createFilters() withouth having a menu because it will cause in a recursion to applyState()
  685 + // that ends up to clear all the filter values. This is likely to happen when we reorder a column
  686 + // and then add a new filter before the menu is recreated.
  687 + me.view.headerCt.getMenu();
  688 + }
  689 +
  690 + for (i = 0, filtersLength = me.filters.items.length; i < filtersLength; i++) {
  691 + filter = me.filters.items[i];
  692 + if (filter.dataIndex === config.dataIndex) {
  693 + return filter;
  694 + }
  695 + }
  696 + },
  697 +
  698 + /**
  699 + * Adds filters to the collection.
  700 + * @param {Array} filters An Array of filter configuration objects.
  701 + */
  702 + addFilters : function (filters) {
  703 + if (filters) {
  704 + var me = this,
  705 + i, filtersLength;
  706 + for (i = 0, filtersLength = filters.length; i < filtersLength; i++) {
  707 + me.addFilter(filters[i]);
  708 + }
  709 + }
  710 + },
  711 +
  712 + /**
  713 + * Returns a filter for the given dataIndex, if one exists.
  714 + * @param {String} dataIndex The dataIndex of the desired filter object.
  715 + * @return {Ext.ux.grid.filter.Filter}
  716 + */
  717 + getFilter : function (dataIndex) {
  718 + return this.filters.get(dataIndex);
  719 + },
  720 +
  721 + /**
  722 + * Turns all filters off. This does not clear the configuration information
  723 + * (see {@link #removeAll}).
  724 + */
  725 + clearFilters : function () {
  726 + this.filters.each(function (filter) {
  727 + filter.setActive(false);
  728 + });
  729 + },
  730 +
  731 + getFilterItems: function () {
  732 + var me = this;
  733 +
  734 + // If there's a locked grid then we must get the filter items for each grid.
  735 + if (me.lockingPartner) {
  736 + return me.filters.items.concat(me.lockingPartner.filters.items);
  737 + }
  738 +
  739 + return me.filters.items;
  740 + },
  741 +
  742 + /**
  743 + * Returns an Array of the currently active filters.
  744 + * @return {Array} filters Array of the currently active filters.
  745 + */
  746 + getFilterData : function () {
  747 + var items = this.getFilterItems(),
  748 + filters = [],
  749 + n, nlen, item, d, i, len;
  750 +
  751 + for (n = 0, nlen = items.length; n < nlen; n++) {
  752 + item = items[n];
  753 + if (item.active) {
  754 + d = [].concat(item.serialize());
  755 + for (i = 0, len = d.length; i < len; i++) {
  756 + filters.push({
  757 + field: item.dataIndex,
  758 + data: d[i]
  759 + });
  760 + }
  761 + }
  762 + }
  763 + return filters;
  764 + },
  765 +
  766 + /**
  767 + * Function to take the active filters data and build it into a query.
  768 + * The format of the query depends on the {@link #encode} configuration:
  769 + *
  770 + * - `false` (Default) :
  771 + * Flatten into query string of the form (assuming <code>{@link #paramPrefix}='filters'</code>:
  772 + *
  773 + * filters[0][field]="someDataIndex"&
  774 + * filters[0][data][comparison]="someValue1"&
  775 + * filters[0][data][type]="someValue2"&
  776 + * filters[0][data][value]="someValue3"&
  777 + *
  778 + *
  779 + * - `true` :
  780 + * JSON encode the filter data
  781 + *
  782 + * {filters:[{"field":"someDataIndex","comparison":"someValue1","type":"someValue2","value":"someValue3"}]}
  783 + *
  784 + * Override this method to customize the format of the filter query for remote requests.
  785 + *
  786 + * @param {Array} filters A collection of objects representing active filters and their configuration.
  787 + * Each element will take the form of {field: dataIndex, data: filterConf}. dataIndex is not assured
  788 + * to be unique as any one filter may be a composite of more basic filters for the same dataIndex.
  789 + *
  790 + * @return {Object} Query keys and values
  791 + */
  792 + buildQuery : function (filters) {
  793 + var p = {}, i, f, root, dataPrefix, key, tmp,
  794 + len = filters.length;
  795 +
  796 + if (!this.encode){
  797 + for (i = 0; i < len; i++) {
  798 + f = filters[i];
  799 + root = [this.paramPrefix, '[', i, ']'].join('');
  800 + p[root + '[field]'] = f.field;
  801 +
  802 + dataPrefix = root + '[data]';
  803 + for (key in f.data) {
  804 + p[[dataPrefix, '[', key, ']'].join('')] = f.data[key];
  805 + }
  806 + }
  807 + } else {
  808 + tmp = [];
  809 + for (i = 0; i < len; i++) {
  810 + f = filters[i];
  811 + tmp.push(Ext.apply(
  812 + {},
  813 + {field: f.field},
  814 + f.data
  815 + ));
  816 + }
  817 + // only build if there is active filter
  818 + if (tmp.length > 0){
  819 + p[this.paramPrefix] = Ext.JSON.encode(tmp);
  820 + }
  821 + }
  822 + return p;
  823 + },
  824 +
  825 + /**
  826 + * Removes filter related query parameters from the provided object.
  827 + * @param {Object} p Query parameters that may contain filter related fields.
  828 + */
  829 + cleanParams : function (p) {
  830 + // if encoding just delete the property
  831 + if (this.encode) {
  832 + delete p[this.paramPrefix];
  833 + // otherwise scrub the object of filter data
  834 + } else {
  835 + var regex, key;
  836 + regex = new RegExp('^' + this.paramPrefix + '\[[0-9]+\]');
  837 + for (key in p) {
  838 + if (regex.test(key)) {
  839 + delete p[key];
  840 + }
  841 + }
  842 + }
  843 + },
  844 +
  845 + /**
  846 + * Function for locating filter classes, overwrite this with your favorite
  847 + * loader to provide dynamic filter loading.
  848 + * @param {String} type The type of filter to load ('Filter' is automatically
  849 + * appended to the passed type; eg, 'string' becomes 'StringFilter').
  850 + * @return {Function} The Ext.ux.grid.filter.Class
  851 + */
  852 + getFilterClass : function (type) {
  853 + // map the supported Ext.data.Field type values into a supported filter
  854 + switch(type) {
  855 + case 'auto':
  856 + type = 'string';
  857 + break;
  858 + case 'int':
  859 + case 'float':
  860 + type = 'numeric';
  861 + break;
  862 + case 'bool':
  863 + type = 'boolean';
  864 + break;
  865 + }
  866 + return Ext.ClassManager.getByAlias('gridfilter.' + type);
  867 + }
  868 +});
js/lib/ux/grid/HeaderToolTip.js deleted
@@ -1,43 +0,0 @@ @@ -1,43 +0,0 @@
1 -/**  
2 - * Project  : AMDA-NG  
3 - * Name : HeaderToolTip.js  
4 - * @class Ext.ux.grid.HeaderToolTip  
5 - * @author SENCHA  
6 - * @version $  
7 - ******************************************************************************  
8 - * FT Id : Date : Name - Description  
9 - ******************************************************************************  
10 - * :  
11 - */  
12 -Ext.define('Ext.ux.amdaGrid.HeaderToolTip', {  
13 -  
14 - alias: 'plugin.headertooltip',  
15 -  
16 - init : function(grid) {  
17 -  
18 - var headerCt = grid.headerCt;  
19 -  
20 - grid.headerCt.on("afterrender", function(g) {  
21 -  
22 - grid.tip = Ext.create('Ext.tip.ToolTip', {  
23 - target: headerCt.el,  
24 - delegate: ".x-column-header",  
25 - trackMouse: false,  
26 - renderTo: Ext.getBody(),  
27 - listeners: {  
28 - beforeshow: function(tip) {  
29 - var c = headerCt.down('gridcolumn[id=' + tip.triggerElement.id +']');  
30 - if (c && c.tooltip) {  
31 - tip.update(c.tooltip);  
32 - return true;  
33 - }  
34 - else {  
35 - tip.clearTimers();  
36 - return false;  
37 - }  
38 - }  
39 - }  
40 - });  
41 - });  
42 - }  
43 -});  
js/lib/ux/grid/TransformGrid.js 0 โ†’ 100644
@@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
  1 +/**
  2 + * A Grid which creates itself from an existing HTML table element.
  3 + */
  4 +Ext.define('Ext.ux.grid.TransformGrid', {
  5 + extend: 'Ext.grid.Panel',
  6 +
  7 + /**
  8 + * Creates the grid from HTML table element.
  9 + * @param {String/HTMLElement/Ext.Element} table The table element from which this grid will be created -
  10 + * The table MUST have some type of size defined for the grid to fill. The container will be
  11 + * automatically set to position relative if it isn't already.
  12 + * @param {Object} [config] A config object that sets properties on this grid and has two additional (optional)
  13 + * properties: fields and columns which allow for customizing data fields and columns for this grid.
  14 + */
  15 + constructor: function(table, config) {
  16 + config = Ext.apply({}, config);
  17 + table = this.table = Ext.get(table);
  18 +
  19 + var configFields = config.fields || [],
  20 + configColumns = config.columns || [],
  21 + fields = [],
  22 + cols = [],
  23 + headers = table.query("thead th"),
  24 + i = 0,
  25 + len = headers.length,
  26 + data = table.dom,
  27 + width,
  28 + height,
  29 + store,
  30 + col,
  31 + text,
  32 + name;
  33 +
  34 + for (; i < len; ++i) {
  35 + col = headers[i];
  36 +
  37 + text = col.innerHTML;
  38 + name = 'tcol-' + i;
  39 +
  40 + fields.push(Ext.applyIf(configFields[i] || {}, {
  41 + name: name,
  42 + mapping: 'td:nth(' + (i + 1) + ')/@innerHTML'
  43 + }));
  44 +
  45 + cols.push(Ext.applyIf(configColumns[i] || {}, {
  46 + text: text,
  47 + dataIndex: name,
  48 + width: col.offsetWidth,
  49 + tooltip: col.title,
  50 + sortable: true
  51 + }));
  52 + }
  53 +
  54 + if (config.width) {
  55 + width = config.width;
  56 + } else {
  57 + width = table.getWidth() + 1;
  58 + }
  59 +
  60 + if (config.height) {
  61 + height = config.height;
  62 + }
  63 +
  64 + Ext.applyIf(config, {
  65 + store: {
  66 + data: data,
  67 + fields: fields,
  68 + proxy: {
  69 + type: 'memory',
  70 + reader: {
  71 + record: 'tbody tr',
  72 + type: 'xml'
  73 + }
  74 + }
  75 + },
  76 + columns: cols,
  77 + width: width,
  78 + height: height
  79 + });
  80 + this.callParent([config]);
  81 +
  82 + if (config.remove !== false) {
  83 + // Don't use table.remove() as that destroys the row/cell data in the table in
  84 + // IE6-7 so it cannot be read by the data reader.
  85 + data.parentNode.removeChild(data);
  86 + }
  87 + },
  88 +
  89 + onDestroy: function() {
  90 + this.callParent();
  91 + this.table.remove();
  92 + delete this.table;
  93 + }
  94 +});
0 \ No newline at end of file 95 \ No newline at end of file
js/lib/ux/grid/css/GridFilters.css 0 โ†’ 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +/*
  2 + * GridFilters Styles
  3 + */
  4 +
  5 +.ux-filtered-column {
  6 + font-style: italic;
  7 + font-weight: bold;
  8 +}
  9 +
  10 +.ux-gridfilter-text-icon {
  11 + background-image: url(../images/find.png) !important;
  12 +}
js/lib/ux/grid/css/RangeMenu.css 0 โ†’ 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +/*
  2 + * RangeMenu Styles
  3 + */
  4 +
  5 +.ux-rangemenu-icon {
  6 + display: block;
  7 + height: 16px;
  8 + background: no-repeat 5px center;
  9 +}
  10 +
  11 +.ux-rangemenu-gt {
  12 + background-image: url(../images/greater_than.png) !important;
  13 +}
  14 +
  15 +.ux-rangemenu-lt {
  16 + background-image: url(../images/less_than.png) !important;
  17 +}
  18 +
  19 +.ux-rangemenu-eq {
  20 + background-image: url(../images/equals.png) !important;
  21 +}
js/lib/ux/grid/filter/BooleanFilter.js 0 โ†’ 100644
@@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
  1 +/**
  2 + * Boolean filters use unique radio group IDs (so you can have more than one!)
  3 + * <p><b><u>Example Usage:</u></b></p>
  4 + * <pre><code>
  5 +var filters = Ext.create('Ext.ux.grid.GridFilters', {
  6 + ...
  7 + filters: [{
  8 + // required configs
  9 + type: 'boolean',
  10 + dataIndex: 'visible'
  11 +
  12 + // optional configs
  13 + defaultValue: null, // leave unselected (false selected by default)
  14 + yesText: 'Yes', // default
  15 + noText: 'No' // default
  16 + }]
  17 +});
  18 + * </code></pre>
  19 + */
  20 +Ext.define('Ext.ux.grid.filter.BooleanFilter', {
  21 + extend: 'Ext.ux.grid.filter.Filter',
  22 + alias: 'gridfilter.boolean',
  23 +
  24 + /**
  25 + * @cfg {Boolean} defaultValue
  26 + * Set this to null if you do not want either option to be checked by default. Defaults to false.
  27 + */
  28 + defaultValue : false,
  29 + /**
  30 + * @cfg {String} yesText
  31 + * Defaults to 'Yes'.
  32 + */
  33 + yesText : 'Yes',
  34 + /**
  35 + * @cfg {String} noText
  36 + * Defaults to 'No'.
  37 + */
  38 + noText : 'No',
  39 +
  40 + /**
  41 + * @private
  42 + * Template method that is to initialize the filter and install required menu items.
  43 + */
  44 + init : function (config) {
  45 + var gId = Ext.id();
  46 + this.options = [
  47 + Ext.create('Ext.menu.CheckItem', {text: this.yesText, group: gId, checked: this.defaultValue === true}),
  48 + Ext.create('Ext.menu.CheckItem', {text: this.noText, group: gId, checked: this.defaultValue === false})];
  49 +
  50 + this.menu.add(this.options[0], this.options[1]);
  51 +
  52 + for(var i=0; i<this.options.length; i++){
  53 + this.options[i].on('click', this.fireUpdate, this);
  54 + this.options[i].on('checkchange', this.fireUpdate, this);
  55 + }
  56 + },
  57 +
  58 + /**
  59 + * @private
  60 + * Template method that is to get and return the value of the filter.
  61 + * @return {String} The value of this filter
  62 + */
  63 + getValue : function () {
  64 + return this.options[0].checked;
  65 + },
  66 +
  67 + /**
  68 + * @private
  69 + * Template method that is to set the value of the filter.
  70 + * @param {Object} value The value to set the filter
  71 + */
  72 + setValue : function (value) {
  73 + this.options[value ? 0 : 1].setChecked(true);
  74 + },
  75 +
  76 + /**
  77 + * @private
  78 + * Template method that is to get and return serialized filter data for
  79 + * transmission to the server.
  80 + * @return {Object/Array} An object or collection of objects containing
  81 + * key value pairs representing the current configuration of the filter.
  82 + */
  83 + getSerialArgs : function () {
  84 + var args = {type: 'boolean', value: this.getValue()};
  85 + return args;
  86 + },
  87 +
  88 + /**
  89 + * Template method that is to validate the provided Ext.data.Record
  90 + * against the filters configuration.
  91 + * @param {Ext.data.Record} record The record to validate
  92 + * @return {Boolean} true if the record is valid within the bounds
  93 + * of the filter, false otherwise.
  94 + */
  95 + validateRecord : function (record) {
  96 + return record.get(this.dataIndex) == this.getValue();
  97 + }
  98 +});
js/lib/ux/grid/filter/DateFilter.js 0 โ†’ 100644
@@ -0,0 +1,340 @@ @@ -0,0 +1,340 @@
  1 +/**
  2 + * Filter by a configurable Ext.picker.DatePicker menu
  3 + *
  4 + * Example Usage:
  5 + *
  6 + * var filters = Ext.create('Ext.ux.grid.GridFilters', {
  7 + * ...
  8 + * filters: [{
  9 + * // required configs
  10 + * type: 'date',
  11 + * dataIndex: 'dateAdded',
  12 + *
  13 + * // optional configs
  14 + * dateFormat: 'm/d/Y', // default
  15 + * beforeText: 'Before', // default
  16 + * afterText: 'After', // default
  17 + * onText: 'On', // default
  18 + * pickerOpts: {
  19 + * // any DatePicker configs
  20 + * },
  21 + *
  22 + * active: true // default is false
  23 + * }]
  24 + * });
  25 + */
  26 +Ext.define('Ext.ux.grid.filter.DateFilter', {
  27 + extend: 'Ext.ux.grid.filter.Filter',
  28 + alias: 'gridfilter.date',
  29 + uses: ['Ext.picker.Date', 'Ext.menu.Menu'],
  30 +
  31 + /**
  32 + * @cfg {String} afterText
  33 + * Defaults to 'After'.
  34 + */
  35 + afterText : 'After',
  36 + /**
  37 + * @cfg {String} beforeText
  38 + * Defaults to 'Before'.
  39 + */
  40 + beforeText : 'Before',
  41 + /**
  42 + * @cfg {Object} compareMap
  43 + * Map for assigning the comparison values used in serialization.
  44 + */
  45 + compareMap : {
  46 + before: 'lt',
  47 + after: 'gt',
  48 + on: 'eq'
  49 + },
  50 + /**
  51 + * @cfg {String} dateFormat
  52 + * The date format to return when using getValue.
  53 + * Defaults to 'm/d/Y'.
  54 + */
  55 + dateFormat : 'm/d/Y',
  56 +
  57 + /**
  58 + * @cfg {Date} maxDate
  59 + * Allowable date as passed to the Ext.DatePicker
  60 + * Defaults to undefined.
  61 + */
  62 + /**
  63 + * @cfg {Date} minDate
  64 + * Allowable date as passed to the Ext.DatePicker
  65 + * Defaults to undefined.
  66 + */
  67 + /**
  68 + * @cfg {Array} menuItems
  69 + * The items to be shown in this menu
  70 + * Defaults to:<pre>
  71 + * menuItems : ['before', 'after', '-', 'on'],
  72 + * </pre>
  73 + */
  74 + menuItems : ['before', 'after', '-', 'on'],
  75 +
  76 + /**
  77 + * @cfg {Object} menuItemCfgs
  78 + * Default configuration options for each menu item
  79 + */
  80 + menuItemCfgs : {
  81 + selectOnFocus: true,
  82 + width: 125
  83 + },
  84 +
  85 + /**
  86 + * @cfg {String} onText
  87 + * Defaults to 'On'.
  88 + */
  89 + onText : 'On',
  90 +
  91 + /**
  92 + * @cfg {Object} pickerOpts
  93 + * Configuration options for the date picker associated with each field.
  94 + */
  95 + pickerOpts : {},
  96 +
  97 + /**
  98 + * @private
  99 + * Template method that is to initialize the filter and install required menu items.
  100 + */
  101 + init : function (config) {
  102 + var me = this,
  103 + pickerCfg, i, len, item, cfg;
  104 +
  105 + pickerCfg = Ext.apply(me.pickerOpts, {
  106 + xtype: 'datepicker',
  107 + minDate: me.minDate,
  108 + maxDate: me.maxDate,
  109 + format: me.dateFormat,
  110 + border: 0,
  111 + listeners: {
  112 + scope: me,
  113 + select: me.onMenuSelect
  114 + }
  115 + });
  116 +
  117 + me.fields = {};
  118 + for (i = 0, len = me.menuItems.length; i < len; i++) {
  119 + item = me.menuItems[i];
  120 + if (item !== '-') {
  121 + cfg = {
  122 + itemId: 'range-' + item,
  123 + text: me[item + 'Text'],
  124 + menu: Ext.create('Ext.menu.Menu', {
  125 + layout: 'auto',
  126 + plain: true,
  127 + items: [
  128 + Ext.apply(pickerCfg, {
  129 + itemId: item
  130 + })
  131 + ]
  132 + }),
  133 + listeners: {
  134 + scope: me,
  135 + checkchange: me.onCheckChange
  136 + }
  137 + };
  138 + item = me.fields[item] = Ext.create('Ext.menu.CheckItem', cfg);
  139 + }
  140 + //me.add(item);
  141 + me.menu.add(item);
  142 + }
  143 + me.values = {};
  144 + },
  145 +
  146 + onCheckChange : function (item, checked) {
  147 + var me = this,
  148 + picker = item.menu.items.first(),
  149 + itemId = picker.itemId;
  150 +
  151 + me.setFieldValue(itemId, checked ? picker.getValue() : null);
  152 + me.setActive(me.isActivatable());
  153 + me.fireEvent('update', me);
  154 + },
  155 +
  156 + /**
  157 + * @private
  158 + * Handler method called when there is a keyup event on an input
  159 + * item of this menu.
  160 + */
  161 + onInputKeyUp : function (field, e) {
  162 + var k = e.getKey();
  163 + if (k == e.RETURN && field.isValid()) {
  164 + e.stopEvent();
  165 + this.menu.hide();
  166 + }
  167 + },
  168 +
  169 + /**
  170 + * Handler for when the DatePicker for a field fires the 'select' event
  171 + * @param {Ext.picker.Date} picker
  172 + * @param {Object} date
  173 + */
  174 + onMenuSelect : function (picker, date) {
  175 + var fields = this.fields,
  176 + field = this.fields[picker.itemId];
  177 +
  178 + field.setChecked(true);
  179 +
  180 + if (field == fields.on) {
  181 + fields.before.setChecked(false, true);
  182 + fields.after.setChecked(false, true);
  183 + } else {
  184 + fields.on.setChecked(false, true);
  185 + if (field == fields.after && this.getFieldValue('before') < date) {
  186 + fields.before.setChecked(false, true);
  187 + } else if (field == fields.before && this.getFieldValue('after') > date) {
  188 + fields.after.setChecked(false, true);
  189 + }
  190 + }
  191 +
  192 + // keep track of the picker value separately because the menu gets destroyed
  193 + // when columns order changes. We return this value from getValue() instead
  194 + // of picker.getValue()
  195 + this.setFieldValue(picker.itemId, date);
  196 +
  197 + this.fireEvent('update', this);
  198 +
  199 + picker.up('menu').hide();
  200 + },
  201 +
  202 + /**
  203 + * @private
  204 + * Template method that is to get and return the value of the filter.
  205 + * @return {String} The value of this filter
  206 + */
  207 + getValue : function () {
  208 + var key, result = {};
  209 + for (key in this.fields) {
  210 + if (this.fields[key].checked) {
  211 + result[key] = this.getFieldValue(key);
  212 + }
  213 + }
  214 + return result;
  215 + },
  216 +
  217 + /**
  218 + * @private
  219 + * Template method that is to set the value of the filter.
  220 + * @param {Object} value The value to set the filter
  221 + * @param {Boolean} preserve true to preserve the checked status
  222 + * of the other fields. Defaults to false, unchecking the
  223 + * other fields
  224 + */
  225 + setValue: function (value, preserve) {
  226 + var key, val;
  227 +
  228 + for (key in this.fields) {
  229 + val = value[key];
  230 +
  231 + if (val) {
  232 + this.getPicker(key).setValue(val);
  233 + // keep track of the picker value separately because the menu gets destroyed
  234 + // when columns order changes. We return this value from getValue() instead
  235 + // of picker.getValue()
  236 + this.setFieldValue(key, val);
  237 + this.fields[key].setChecked(true);
  238 + } else if (!preserve) {
  239 + this.fields[key].setChecked(false);
  240 + }
  241 + }
  242 + this.fireEvent('update', this);
  243 + },
  244 +
  245 + /**
  246 + * Template method that is to return <tt>true</tt> if the filter
  247 + * has enough configuration information to be activated.
  248 + * @return {Boolean}
  249 + */
  250 + isActivatable : function () {
  251 + var key;
  252 + for (key in this.fields) {
  253 + if (this.fields[key].checked) {
  254 + return true;
  255 + }
  256 + }
  257 + return false;
  258 + },
  259 +
  260 + /**
  261 + * @private
  262 + * Template method that is to get and return serialized filter data for
  263 + * transmission to the server.
  264 + * @return {Object/Array} An object or collection of objects containing
  265 + * key value pairs representing the current configuration of the filter.
  266 + */
  267 + getSerialArgs : function () {
  268 + var args = [];
  269 + for (var key in this.fields) {
  270 + if(this.fields[key].checked){
  271 + args.push({
  272 + type: 'date',
  273 + comparison: this.compareMap[key],
  274 + value: Ext.Date.format(this.getFieldValue(key), this.dateFormat)
  275 + });
  276 + }
  277 + }
  278 + return args;
  279 + },
  280 +
  281 + /**
  282 + * Get and return the date menu picker value
  283 + * @param {String} item The field identifier ('before', 'after', 'on')
  284 + * @return {Date} Gets the current selected value of the date field
  285 + */
  286 + getFieldValue : function(item){
  287 + return this.values[item];
  288 + },
  289 +
  290 + /**
  291 + * @private
  292 + */
  293 + setFieldValue: function (item, value) {
  294 + this.values[item] = value;
  295 + },
  296 +
  297 + /**
  298 + * Gets the menu picker associated with the passed field
  299 + * @param {String} item The field identifier ('before', 'after', 'on')
  300 + * @return {Object} The menu picker
  301 + */
  302 + getPicker : function(item){
  303 + return this.fields[item].menu.items.first();
  304 + },
  305 +
  306 + /**
  307 + * Template method that is to validate the provided Ext.data.Record
  308 + * against the filters configuration.
  309 + * @param {Ext.data.Record} record The record to validate
  310 + * @return {Boolean} true if the record is valid within the bounds
  311 + * of the filter, false otherwise.
  312 + */
  313 + validateRecord : function (record) {
  314 + var key,
  315 + pickerValue,
  316 + val = record.get(this.dataIndex),
  317 + clearTime = Ext.Date.clearTime;
  318 +
  319 + if(!Ext.isDate(val)){
  320 + return false;
  321 + }
  322 + val = clearTime(val, true).getTime();
  323 +
  324 + for (key in this.fields) {
  325 + if (this.fields[key].checked) {
  326 + pickerValue = clearTime(this.getFieldValue(key), true).getTime();
  327 + if (key == 'before' && pickerValue <= val) {
  328 + return false;
  329 + }
  330 + if (key == 'after' && pickerValue >= val) {
  331 + return false;
  332 + }
  333 + if (key == 'on' && pickerValue != val) {
  334 + return false;
  335 + }
  336 + }
  337 + }
  338 + return true;
  339 + }
  340 +});
js/lib/ux/grid/filter/DateTimeFilter.js 0 โ†’ 100644
@@ -0,0 +1,451 @@ @@ -0,0 +1,451 @@
  1 +/**
  2 + * Filter by a configurable Ext.picker.DatePicker menu
  3 + *
  4 + * This filter allows for the following configurations:
  5 + *
  6 + * - Any of the normal configs will be passed through to either component.
  7 + * - There can be a docked config.
  8 + * - The timepicker can be on the right or left (datepicker, too, of course).
  9 + * - Choose which component will initiate the filtering, i.e., the event can be
  10 + * configured to be bound to either the datepicker or the timepicker, or if
  11 + * there is a docked config it be automatically have the handler bound to it.
  12 + *
  13 + * Although not shown here, this class accepts all configuration options
  14 + * for {@link Ext.picker.Date} and {@link Ext.picker.Time}.
  15 + *
  16 + * In the case that a custom dockedItems config is passed in, the
  17 + * class will handle binding the default listener to it so the
  18 + * developer need not worry about having to do it.
  19 + *
  20 + * The default dockedItems position and the toolbar's
  21 + * button text can be passed a config for convenience, i.e.,:
  22 + *
  23 + * dock: {
  24 + * buttonText: 'Click to Filter',
  25 + * dock: 'left'
  26 + * }
  27 + *
  28 + * Or, pass in a full dockedItems config:
  29 + *
  30 + * dock: {
  31 + * dockedItems: {
  32 + * xtype: 'toolbar',
  33 + * dock: 'bottom',
  34 + * ...
  35 + * }
  36 + * }
  37 + *
  38 + * Or, give a value of `true` to accept dock defaults:
  39 + *
  40 + * dock: true
  41 + *
  42 + * But, it must be one or the other.
  43 + *
  44 + * Example Usage:
  45 + *
  46 + * var filters = Ext.create('Ext.ux.grid.FiltersFeature', {
  47 + * ...
  48 + * filters: [{
  49 + * // required configs
  50 + * type: 'datetime',
  51 + * dataIndex: 'date',
  52 + *
  53 + * // optional configs
  54 + * positionDatepickerFirst: false,
  55 + * //selectDateToFilter: false, // this is overridden b/c of the presence of the dock cfg object
  56 + *
  57 + * date: {
  58 + * format: 'm/d/Y',
  59 + * },
  60 + *
  61 + * time: {
  62 + * format: 'H:i:s A',
  63 + * increment: 1
  64 + * },
  65 + *
  66 + * dock: {
  67 + * buttonText: 'Click to Filter',
  68 + * dock: 'left'
  69 + *
  70 + * // allows for custom dockedItems cfg
  71 + * //dockedItems: {}
  72 + * }
  73 + * }]
  74 + * });
  75 + *
  76 + * In the above example, note that the filter is being passed a {@link #date} config object,
  77 + * a {@link #time} config object and a {@link #dock} config. These are all optional.
  78 + *
  79 + * As for positioning, the datepicker will be on the right, the timepicker on the left
  80 + * and the docked items will be docked on the left. In addition, since there's a {@link #dock}
  81 + * config, clicking the button in the dock will trigger the filtering.
  82 + */
  83 +Ext.define('Ext.ux.grid.filter.DateTimeFilter', {
  84 + extend: 'Ext.ux.grid.filter.DateFilter',
  85 + alias: 'gridfilter.datetime',
  86 +
  87 + /**
  88 + * @private
  89 + */
  90 + dateDefaults: {
  91 + xtype: 'datepicker',
  92 + format: 'm/d/Y'
  93 + },
  94 +
  95 + /**
  96 + * @private
  97 + */
  98 + timeDefaults: {
  99 + xtype: 'timepicker',
  100 + width: 100,
  101 + height: 200,
  102 + format: 'g:i A'
  103 + },
  104 +
  105 + /**
  106 + * @private
  107 + */
  108 + dockDefaults: {
  109 + dock: 'top',
  110 + buttonText: 'Filter'
  111 + },
  112 +
  113 + /**
  114 + * @cfg {Object} date
  115 + * A {@link Ext.picker.Date} can be configured here.
  116 + * Uses {@link #dateDefaults} by default.
  117 + */
  118 +
  119 + /**
  120 + * @cfg {Object} time
  121 + * A {@link Ext.picker.Time} can be configured here.
  122 + * Uses {@link #timeDefaults} by default.
  123 + */
  124 +
  125 + /**
  126 + * @cfg {Boolean/Object} dock
  127 + * A {@link Ext.panel.AbstractPanel#cfg-dockedItems} can be configured here.
  128 + * A `true` value will use the {@link #dockDefaults} default configuration.
  129 + * If present, the button in the docked items will initiate the filtering.
  130 + */
  131 +
  132 + /**
  133 + * @cfg {Boolean} [selectDateToFilter=true]
  134 + * By default, the datepicker has the default event listener bound to it.
  135 + * Setting to `false` will bind it to the timepicker.
  136 + *
  137 + * The config will be ignored if there is a `dock` config.
  138 + */
  139 + selectDateToFilter: true,
  140 +
  141 + /**
  142 + * @cfg {Boolean} [positionDatepickerFirst=true]
  143 + * Positions the datepicker within its container.
  144 + * A `true` value will place it on the left in the container.
  145 + * Set to `false` if the timepicker should be placed on the left.
  146 + * Defaults to `true`.
  147 + */
  148 + positionDatepickerFirst: true,
  149 +
  150 + reTime: /\s(am|pm)/i,
  151 +
  152 + /**
  153 + * Mix the value for the timepicker into the datepicker's date.
  154 + * @private
  155 + * @param {Ext.picker.Date} datepicker
  156 + * @param {Ext.picker.Time} timepicker
  157 + * @return Date object
  158 + */
  159 + addTimeSelection: function (datepicker, timepicker) {
  160 + var me = this,
  161 + date = datepicker.value,
  162 + selection = timepicker.getSelectionModel().getSelection(),
  163 + time, len, fn, val,
  164 + i = 0,
  165 + arr = [],
  166 + timeFns = ['setHours', 'setMinutes', 'setSeconds', 'setMilliseconds'];
  167 +
  168 +
  169 + if (selection.length) {
  170 + time = selection[0].get('disp');
  171 +
  172 + // Loop through all of the splits and add the time values.
  173 + arr = time.replace(me.reTime, '').split(':');
  174 +
  175 + for (len = arr.length; i < len; i++) {
  176 + fn = timeFns[i];
  177 + val = arr[i];
  178 +
  179 + if (val) {
  180 + date[fn](parseInt(val, 10));
  181 + }
  182 + }
  183 + }
  184 +
  185 + return date;
  186 + },
  187 +
  188 + /**
  189 + * @private
  190 + * Template method that is to initialize the filter and install required menu items.
  191 + */
  192 + init: function (config) {
  193 + var me = this,
  194 + dateCfg = Ext.applyIf(me.date || {}, me.dateDefaults),
  195 + timeCfg = Ext.applyIf(me.time || {}, me.timeDefaults),
  196 + dockCfg = me.dock, // should not default to empty object
  197 + defaultListeners = {
  198 + click: {
  199 + scope: me,
  200 + click: me.onMenuSelect
  201 + },
  202 + select: {
  203 + scope: me,
  204 + select: me.onMenuSelect
  205 + }
  206 + },
  207 + pickerCtnCfg, i, len, item, cfg,
  208 + items = [dateCfg, timeCfg],
  209 +
  210 + // we need to know the datepicker's position in the items array
  211 + // for when the itemId name is bound to it before adding to the menu
  212 + datepickerPosition = 0;
  213 +
  214 + if (!me.positionDatepickerFirst) {
  215 + items = items.reverse();
  216 + datepickerPosition = 1;
  217 + }
  218 +
  219 + pickerCtnCfg = Ext.apply(me.pickerOpts, {
  220 + xtype: !dockCfg ? 'container' : 'panel',
  221 + border: 0,
  222 + layout: 'hbox',
  223 + items: items
  224 + });
  225 +
  226 + // If there's no dock config then bind the default listener to the desired picker.
  227 + if (!dockCfg) {
  228 + if (me.selectDateToFilter) {
  229 + dateCfg.listeners = defaultListeners.select;
  230 + } else {
  231 + timeCfg.listeners = defaultListeners.select;
  232 + }
  233 + } else if (dockCfg) {
  234 + me.selectDateToFilter = null;
  235 +
  236 + if (dockCfg.dockedItems) {
  237 + pickerCtnCfg.dockedItems = dockCfg.dockedItems;
  238 + // TODO: allow config that will tell which item to bind the listener to
  239 + // right now, it's using the first item
  240 + pickerCtnCfg.dockedItems.items[dockCfg.bindToItem || 0].listeners = defaultListeners.click;
  241 + } else {
  242 + // dockCfg can be `true` if button text and dock position defaults are wanted
  243 + if (Ext.isBoolean(dockCfg)) {
  244 + dockCfg = {};
  245 + }
  246 + dockCfg = Ext.applyIf(dockCfg, me.dockDefaults);
  247 + pickerCtnCfg.dockedItems = {
  248 + xtype: 'toolbar',
  249 + dock: dockCfg.dock,
  250 + items: [{
  251 + xtype: 'button',
  252 + text: dockCfg.buttonText,
  253 + flex: 1,
  254 + listeners: defaultListeners.click
  255 + }]
  256 + };
  257 + }
  258 + }
  259 +
  260 + me.fields = {};
  261 + for (i = 0, len = me.menuItems.length; i < len; i++) {
  262 + item = me.menuItems[i];
  263 + if (item !== '-') {
  264 + pickerCtnCfg.items[datepickerPosition].itemId = item;
  265 +
  266 + cfg = {
  267 + itemId: item,
  268 + text: me[item + 'Text'],
  269 + menu: Ext.create('Ext.menu.Menu', {
  270 + layout: 'auto',
  271 + plain: true,
  272 + items: pickerCtnCfg
  273 + }),
  274 + listeners: {
  275 + scope: me,
  276 + checkchange: me.onCheckChange
  277 + }
  278 + };
  279 + item = me.fields[item] = Ext.create('Ext.menu.CheckItem', cfg);
  280 + }
  281 + me.menu.add(item);
  282 + }
  283 + me.values = {};
  284 + },
  285 +
  286 + /**
  287 + * @private
  288 + */
  289 + getCacheValues: function (item, checked) {
  290 + var menu = item.menu,
  291 + timepicker = menu.down('timepicker'),
  292 + datepicker = menu.down('datepicker'),
  293 + key = datepicker.itemId;
  294 +
  295 + return [key, checked ? this.addTimeSelection(datepicker, timepicker) : null];
  296 + },
  297 +
  298 + /**
  299 + * @private
  300 + */
  301 + onCheckChange: function (item, checked) {
  302 + var me = this;
  303 +
  304 + me.setFieldValue.apply(me, me.getCacheValues(item, checked));
  305 + me.setActive(me.isActivatable());
  306 + me.fireEvent('update', me);
  307 + },
  308 +
  309 + /**
  310 + * Handler for when the DatePicker for a field fires the 'select' event
  311 + * @param {Ext.picker.Date} picker
  312 + * @param {Object} date
  313 + */
  314 + onMenuSelect: function (picker, date) {
  315 + var me = this,
  316 + menu = me.menu,
  317 + fields = me.fields,
  318 + field;
  319 +
  320 + if (me.dock) {
  321 + // If there is a dock config then the button will trigger the menu select. In these cases, the picker
  322 + // function arg isn't actually a picker but the button that was clicked, so redefine the picker.
  323 + // Similarly, the date function argument will not be a Date type, so get it from the datepicker.
  324 + // The focusEl is going to be the check item.
  325 + picker = menu.getFocusEl().down('datepicker');
  326 + date = picker.value;
  327 + }
  328 +
  329 + field = me.fields[picker.itemId];
  330 + field.setChecked(true);
  331 +
  332 + if (field == fields.on) {
  333 + fields.before.setChecked(false, true);
  334 + fields.after.setChecked(false, true);
  335 + } else {
  336 + fields.on.setChecked(false, true);
  337 + if (field == fields.after && me.getFieldValue('before') < date) {
  338 + fields.before.setChecked(false, true);
  339 + } else if (field == fields.before && me.getFieldValue('after') > date) {
  340 + fields.after.setChecked(false, true);
  341 + }
  342 + }
  343 +
  344 + // Note that the date will not have the H:i:s info mixed into it. getCacheValues() will handle this.
  345 + me.setFieldValue.apply(me, me.getCacheValues(field, true));
  346 +
  347 + me.fireEvent('update', me);
  348 +
  349 + picker.up('menu').hide();
  350 + },
  351 +
  352 + /**
  353 + * @private
  354 + * Template method that is to get and return serialized filter data for
  355 + * transmission to the server.
  356 + * @return {Object/Array} An object or collection of objects containing
  357 + * key value pairs representing the current configuration of the filter.
  358 + */
  359 + getSerialArgs: function () {
  360 + var me = this,
  361 + key,
  362 + fields = me.fields,
  363 + args = [],
  364 + date = Ext.apply(me.dateDefaults, me.date || {}),
  365 + time = Ext.apply(me.timeDefaults, me.time || {});
  366 +
  367 + for (key in fields) {
  368 + if (fields[key].checked) {
  369 + args.push({
  370 + type: 'datetime',
  371 + comparison: me.compareMap[key],
  372 + value: Ext.Date.format(me.getFieldValue(key), date.format + ' ' + time.format)
  373 + });
  374 + }
  375 + }
  376 + return args;
  377 + },
  378 +
  379 + /**
  380 + * @private
  381 + * Template method that is to set the value of the filter.
  382 + * @param {Object} value The value to set the filter
  383 + * @param {Boolean} preserve true to preserve the checked status
  384 + * of the other fields. Defaults to false, unchecking the
  385 + * other fields
  386 + */
  387 + setValue: function (value, preserve) {
  388 + var me = this,
  389 + fields = me.fields,
  390 + key,
  391 + val,
  392 + datepicker;
  393 +
  394 + for (key in fields) {
  395 + val = value[key];
  396 + if (val) {
  397 + datepicker = me.menu.down('datepicker[itemId="' + key + '"]');
  398 + // Note that calling the Ext.picker.Date:setValue() calls Ext.Date.clearTime(),
  399 + // which we don't want, so just call update() instead and set the value on the component.
  400 + datepicker.update(val);
  401 + datepicker.value = val;
  402 + // keep track of the picker value separately because the menu gets destroyed
  403 + // when columns order changes. We return this value from getValue() instead
  404 + // of picker.getValue()
  405 + me.setFieldValue(key, val);
  406 +
  407 + fields[key].setChecked(true);
  408 + } else if (!preserve) {
  409 + fields[key].setChecked(false);
  410 + }
  411 + }
  412 + me.fireEvent('update', me);
  413 + },
  414 +
  415 + /**
  416 + * Template method that is to validate the provided Ext.data.Record
  417 + * against the filters configuration.
  418 + * @param {Ext.data.Record} record The record to validate
  419 + * @return {Boolean} true if the record is valid within the bounds
  420 + * of the filter, false otherwise.
  421 + */
  422 + validateRecord: function (record) {
  423 + // remove calls to Ext.Date.clearTime
  424 + var me = this,
  425 + key,
  426 + pickerValue,
  427 + val = record.get(me.dataIndex);
  428 +
  429 + if (!Ext.isDate(val)) {
  430 + return false;
  431 + }
  432 +
  433 + val = val.getTime();
  434 +
  435 + for (key in me.fields) {
  436 + if (me.fields[key].checked) {
  437 + pickerValue = me.getFieldValue(key).getTime();
  438 + if (key == 'before' && pickerValue <= val) {
  439 + return false;
  440 + }
  441 + if (key == 'after' && pickerValue >= val) {
  442 + return false;
  443 + }
  444 + if (key == 'on' && pickerValue != val) {
  445 + return false;
  446 + }
  447 + }
  448 + }
  449 + return true;
  450 + }
  451 +});
js/lib/ux/grid/filter/Filter.js 0 โ†’ 100644
@@ -0,0 +1,181 @@ @@ -0,0 +1,181 @@
  1 +/**
  2 + * Abstract base class for filter implementations.
  3 + */
  4 +Ext.define('Ext.ux.grid.filter.Filter', {
  5 + extend: 'Ext.util.Observable',
  6 +
  7 + /**
  8 + * @cfg {Boolean} active
  9 + * Indicates the initial status of the filter (defaults to false).
  10 + */
  11 + active : false,
  12 + /**
  13 + * @property {Boolean} active
  14 + * True if this filter is active. Use setActive() to alter after configuration.
  15 + */
  16 + /**
  17 + * @cfg {String} dataIndex
  18 + * The {@link Ext.data.Store} dataIndex of the field this filter represents.
  19 + * The dataIndex does not actually have to exist in the store.
  20 + */
  21 + dataIndex : null,
  22 + /**
  23 + * @property {Ext.menu.Menu} menu
  24 + * The filter configuration menu that will be installed into the filter submenu of a column menu.
  25 + */
  26 + menu : null,
  27 + /**
  28 + * @cfg {Number} updateBuffer
  29 + * Number of milliseconds to wait after user interaction to fire an update. Only supported
  30 + * by filters: 'list', 'numeric', and 'string'.
  31 + */
  32 + updateBuffer : 500,
  33 +
  34 + constructor : function (config) {
  35 + Ext.apply(this, config);
  36 +
  37 + this.addEvents(
  38 + /**
  39 + * @event activate
  40 + * Fires when an inactive filter becomes active
  41 + * @param {Ext.ux.grid.filter.Filter} this
  42 + */
  43 + 'activate',
  44 + /**
  45 + * @event deactivate
  46 + * Fires when an active filter becomes inactive
  47 + * @param {Ext.ux.grid.filter.Filter} this
  48 + */
  49 + 'deactivate',
  50 + /**
  51 + * @event serialize
  52 + * Fires after the serialization process. Use this to attach additional parameters to serialization
  53 + * data before it is encoded and sent to the server.
  54 + * @param {Array/Object} data A map or collection of maps representing the current filter configuration.
  55 + * @param {Ext.ux.grid.filter.Filter} filter The filter being serialized.
  56 + */
  57 + 'serialize',
  58 + /**
  59 + * @event update
  60 + * Fires when a filter configuration has changed
  61 + * @param {Ext.ux.grid.filter.Filter} this The filter object.
  62 + */
  63 + 'update'
  64 + );
  65 + Ext.ux.grid.filter.Filter.superclass.constructor.call(this);
  66 +
  67 + this.menu = this.createMenu(config);
  68 + this.init(config);
  69 + if(config && config.value){
  70 + this.setValue(config.value);
  71 + this.setActive(config.active !== false, true);
  72 + delete config.value;
  73 + }
  74 + },
  75 +
  76 + /**
  77 + * Destroys this filter by purging any event listeners, and removing any menus.
  78 + */
  79 + destroy : function(){
  80 + if (this.menu){
  81 + this.menu.destroy();
  82 + }
  83 + this.clearListeners();
  84 + },
  85 +
  86 + /**
  87 + * Template method to be implemented by all subclasses that is to
  88 + * initialize the filter and install required menu items.
  89 + * Defaults to Ext.emptyFn.
  90 + */
  91 + init : Ext.emptyFn,
  92 +
  93 + /**
  94 + * @private
  95 + * Creates the Menu for this filter.
  96 + * @param {Object} config Filter configuration
  97 + * @return {Ext.menu.Menu}
  98 + */
  99 + createMenu: function(config) {
  100 + config.plain = true;
  101 + return Ext.create('Ext.menu.Menu', config);
  102 + },
  103 +
  104 + /**
  105 + * Template method to be implemented by all subclasses that is to
  106 + * get and return the value of the filter.
  107 + * @return {Object} The 'serialized' form of this filter
  108 + * @template
  109 + */
  110 + getValue : Ext.emptyFn,
  111 +
  112 + /**
  113 + * Template method to be implemented by all subclasses that is to
  114 + * set the value of the filter and fire the 'update' event.
  115 + * @param {Object} data The value to set the filter
  116 + * @template
  117 + */
  118 + setValue : Ext.emptyFn,
  119 +
  120 + /**
  121 + * Template method to be implemented by all subclasses that is to
  122 + * return true if the filter has enough configuration information to be activated.
  123 + * Defaults to always returning true.
  124 + * @return {Boolean}
  125 + */
  126 + isActivatable : function(){
  127 + return true;
  128 + },
  129 +
  130 + /**
  131 + * Template method to be implemented by all subclasses that is to
  132 + * get and return serialized filter data for transmission to the server.
  133 + */
  134 + getSerialArgs : Ext.emptyFn,
  135 +
  136 + /**
  137 + * Template method to be implemented by all subclasses that is to
  138 + * validates the provided Ext.data.Record against the filters configuration.
  139 + * Defaults to always returning true.
  140 + * @param {Ext.data.Record} record The record to validate
  141 + * @return {Boolean} true if the record is valid within the bounds
  142 + * of the filter, false otherwise.
  143 + */
  144 + validateRecord : function(){
  145 + return true;
  146 + },
  147 +
  148 + /**
  149 + * Returns the serialized filter data for transmission to the server
  150 + * and fires the 'serialize' event.
  151 + * @return {Object/Array} An object or collection of objects containing
  152 + * key value pairs representing the current configuration of the filter.
  153 + */
  154 + serialize : function(){
  155 + var args = this.getSerialArgs();
  156 + this.fireEvent('serialize', args, this);
  157 + return args;
  158 + },
  159 +
  160 + /** @private */
  161 + fireUpdate : function(){
  162 + if (this.active) {
  163 + this.fireEvent('update', this);
  164 + }
  165 + this.setActive(this.isActivatable());
  166 + },
  167 +
  168 + /**
  169 + * Sets the status of the filter and fires the appropriate events.
  170 + * @param {Boolean} active The new filter state.
  171 + * @param {Boolean} suppressEvent True to prevent events from being fired.
  172 + */
  173 + setActive : function(active, suppressEvent){
  174 + if(this.active != active){
  175 + this.active = active;
  176 + if (suppressEvent !== true) {
  177 + this.fireEvent(active ? 'activate' : 'deactivate', this);
  178 + }
  179 + }
  180 + }
  181 +});
js/lib/ux/grid/filter/ListFilter.js 0 โ†’ 100644
@@ -0,0 +1,202 @@ @@ -0,0 +1,202 @@
  1 +/**
  2 + * List filters are able to be preloaded/backed by an Ext.data.Store to load
  3 + * their options the first time they are shown. ListFilter utilizes the
  4 + * {@link Ext.ux.grid.menu.ListMenu} component.
  5 + *
  6 + * List filters are also able to create their own list of values from all unique values of
  7 + * the specified {@link #dataIndex} field in the store at first time of filter invocation.
  8 + *
  9 + * Although not shown here, this class accepts all configuration options
  10 + * for {@link Ext.ux.grid.menu.ListMenu}.
  11 + *
  12 + * Example Usage:
  13 + *
  14 + * var filters = Ext.create('Ext.ux.grid.GridFilters', {
  15 + * ...
  16 + * filters: [{
  17 + * type: 'list',
  18 + * dataIndex: 'size',
  19 + * phpMode: true,
  20 + * // options will be used as data to implicitly creates an ArrayStore
  21 + * options: ['extra small', 'small', 'medium', 'large', 'extra large']
  22 + * }]
  23 + * });
  24 + *
  25 + */
  26 +Ext.define('Ext.ux.grid.filter.ListFilter', {
  27 + extend: 'Ext.ux.grid.filter.Filter',
  28 + alias: 'gridfilter.list',
  29 +
  30 + /**
  31 + * @cfg {Array} [options]
  32 + * `data` to be used to implicitly create a data store
  33 + * to back this list when the data source is **local**. If the
  34 + * data for the list is remote, use the {@link #store}
  35 + * config instead.
  36 + *
  37 + * If neither store nor {@link #options} is specified, then the choices list is automatically
  38 + * populated from all unique values of the specified {@link #dataIndex} field in the store at first
  39 + * time of filter invocation.
  40 + *
  41 + * Each item within the provided array may be in one of the
  42 + * following formats:
  43 + *
  44 + * - **Array** :
  45 + *
  46 + * options: [
  47 + * [11, 'extra small'],
  48 + * [18, 'small'],
  49 + * [22, 'medium'],
  50 + * [35, 'large'],
  51 + * [44, 'extra large']
  52 + * ]
  53 + *
  54 + * - **Object** :
  55 + *
  56 + * labelField: 'name', // override default of 'text'
  57 + * options: [
  58 + * {id: 11, name:'extra small'},
  59 + * {id: 18, name:'small'},
  60 + * {id: 22, name:'medium'},
  61 + * {id: 35, name:'large'},
  62 + * {id: 44, name:'extra large'}
  63 + * ]
  64 + *
  65 + * - **String** :
  66 + *
  67 + * options: ['extra small', 'small', 'medium', 'large', 'extra large']
  68 + *
  69 + */
  70 + /**
  71 + * @cfg {Boolean} phpMode
  72 + * Adjust the format of this filter. Defaults to false.
  73 + *
  74 + * When GridFilters `@cfg encode = false` (default):
  75 + *
  76 + * // phpMode == false (default):
  77 + * filter[0][data][type] list
  78 + * filter[0][data][value] value1
  79 + * filter[0][data][value] value2
  80 + * filter[0][field] prod
  81 + *
  82 + * // phpMode == true:
  83 + * filter[0][data][type] list
  84 + * filter[0][data][value] value1, value2
  85 + * filter[0][field] prod
  86 + *
  87 + * When GridFilters `@cfg encode = true`:
  88 + *
  89 + * // phpMode == false (default):
  90 + * filter : [{"type":"list","value":["small","medium"],"field":"size"}]
  91 + *
  92 + * // phpMode == true:
  93 + * filter : [{"type":"list","value":"small,medium","field":"size"}]
  94 + *
  95 + */
  96 + phpMode : false,
  97 + /**
  98 + * @cfg {Ext.data.Store} [store]
  99 + * The {@link Ext.data.Store} this list should use as its data source
  100 + * when the data source is **remote**. If the data for the list
  101 + * is local, use the {@link #options} config instead.
  102 + *
  103 + * If neither store nor {@link #options} is specified, then the choices list is automatically
  104 + * populated from all unique values of the specified {@link #dataIndex} field in the store at first
  105 + * time of filter invocation.
  106 + */
  107 +
  108 + /**
  109 + * @private
  110 + * Template method that is to initialize the filter.
  111 + * @param {Object} config
  112 + */
  113 + init : function (config) {
  114 + var me = this,
  115 + menu;
  116 +
  117 + me.dt = Ext.create('Ext.util.DelayedTask', me.fireUpdate, me);
  118 +
  119 + // If the List filter is auto-creating its store from the unique values in the grid store (i.e., no `options` or `store`
  120 + // configs), it will need to listen to grid store events to properly sync its options when the grid store changes.
  121 + if (!me.options && !me.store) {
  122 + menu = me.menu;
  123 + // Needed for when options are auto-generated from the grid store.
  124 + // See the comments in #onDataChanged.
  125 + me.autoGeneratedOptions = [];
  126 +
  127 + me.grid.store.on({
  128 + scope: me,
  129 + datachanged: menu.onDataChanged,
  130 + update: menu.onDataChanged
  131 + });
  132 + }
  133 + },
  134 +
  135 + /**
  136 + * @private
  137 + * Creates the Menu for this filter.
  138 + * @param {Object} config Filter configuration
  139 + * @return {Ext.menu.Menu}
  140 + */
  141 + createMenu: function(config) {
  142 + var menu = Ext.create('Ext.ux.grid.menu.ListMenu', config);
  143 + menu.on('checkchange', this.onCheckChange, this);
  144 + return menu;
  145 + },
  146 +
  147 + /**
  148 + * @private
  149 + * Template method that is to get and return the value of the filter.
  150 + * @return {String} The value of this filter
  151 + */
  152 + getValue : function () {
  153 + return this.menu.getSelected();
  154 + },
  155 + /**
  156 + * @private
  157 + * Template method that is to set the value of the filter.
  158 + * @param {Object} value The value to set the filter
  159 + */
  160 + setValue : function (value) {
  161 + this.menu.setSelected(value);
  162 + this.fireEvent('update', this);
  163 + },
  164 +
  165 + /**
  166 + * Template method that is to return true if the filter
  167 + * has enough configuration information to be activated.
  168 + * @return {Boolean}
  169 + */
  170 + isActivatable : function () {
  171 + return this.getValue().length > 0;
  172 + },
  173 +
  174 + /**
  175 + * @private
  176 + * Template method that is to get and return serialized filter data for
  177 + * transmission to the server.
  178 + * @return {Object/Array} An object or collection of objects containing
  179 + * key value pairs representing the current configuration of the filter.
  180 + */
  181 + getSerialArgs : function () {
  182 + return {type: 'list', value: this.phpMode ? this.getValue().join(',') : this.getValue()};
  183 + },
  184 +
  185 + /** @private */
  186 + onCheckChange : function(){
  187 + this.dt.delay(this.updateBuffer);
  188 + },
  189 +
  190 +
  191 + /**
  192 + * Template method that is to validate the provided Ext.data.Record
  193 + * against the filters configuration.
  194 + * @param {Ext.data.Record} record The record to validate
  195 + * @return {Boolean} true if the record is valid within the bounds
  196 + * of the filter, false otherwise.
  197 + */
  198 + validateRecord : function (record) {
  199 + var valuesArray = this.getValue();
  200 + return Ext.Array.indexOf(valuesArray, record.get(this.dataIndex)) > -1;
  201 + }
  202 +});
js/lib/ux/grid/filter/NumericFilter.js 0 โ†’ 100644
@@ -0,0 +1,112 @@ @@ -0,0 +1,112 @@
  1 +/**
  2 + * Filters using an Ext.ux.grid.menu.RangeMenu.
  3 + * <p><b><u>Example Usage:</u></b></p>
  4 + * <pre><code>
  5 +var filters = Ext.create('Ext.ux.grid.GridFilters', {
  6 + ...
  7 + filters: [{
  8 + type: 'numeric',
  9 + dataIndex: 'price'
  10 + }]
  11 +});
  12 + * </code></pre>
  13 + * <p>Any of the configuration options for {@link Ext.ux.grid.menu.RangeMenu} can also be specified as
  14 + * configurations to NumericFilter, and will be copied over to the internal menu instance automatically.</p>
  15 + */
  16 +Ext.define('Ext.ux.grid.filter.NumericFilter', {
  17 + extend: 'Ext.ux.grid.filter.Filter',
  18 + alias: 'gridfilter.numeric',
  19 + uses: ['Ext.form.field.Number'],
  20 +
  21 + /**
  22 + * @private
  23 + * Creates the Menu for this filter.
  24 + * @param {Object} config Filter configuration
  25 + * @return {Ext.menu.Menu}
  26 + */
  27 + createMenu: function(config) {
  28 + var me = this,
  29 + menu;
  30 + menu = Ext.create('Ext.ux.grid.menu.RangeMenu', config);
  31 + menu.on('update', me.fireUpdate, me);
  32 + return menu;
  33 + },
  34 +
  35 + /**
  36 + * @private
  37 + * Template method that is to get and return the value of the filter.
  38 + * @return {String} The value of this filter
  39 + */
  40 + getValue : function () {
  41 + return this.menu.getValue();
  42 + },
  43 +
  44 + /**
  45 + * @private
  46 + * Template method that is to set the value of the filter.
  47 + * @param {Object} value The value to set the filter
  48 + */
  49 + setValue : function (value) {
  50 + this.menu.setValue(value);
  51 + },
  52 +
  53 + /**
  54 + * Template method that is to return <tt>true</tt> if the filter
  55 + * has enough configuration information to be activated.
  56 + * @return {Boolean}
  57 + */
  58 + isActivatable : function () {
  59 + var values = this.getValue(),
  60 + key;
  61 + for (key in values) {
  62 + if (values[key] !== undefined) {
  63 + return true;
  64 + }
  65 + }
  66 + return false;
  67 + },
  68 +
  69 + /**
  70 + * @private
  71 + * Template method that is to get and return serialized filter data for
  72 + * transmission to the server.
  73 + * @return {Object/Array} An object or collection of objects containing
  74 + * key value pairs representing the current configuration of the filter.
  75 + */
  76 + getSerialArgs : function () {
  77 + var key,
  78 + args = [],
  79 + values = this.menu.getValue();
  80 + for (key in values) {
  81 + args.push({
  82 + type: 'numeric',
  83 + comparison: key,
  84 + value: values[key]
  85 + });
  86 + }
  87 + return args;
  88 + },
  89 +
  90 + /**
  91 + * Template method that is to validate the provided Ext.data.Record
  92 + * against the filters configuration.
  93 + * @param {Ext.data.Record} record The record to validate
  94 + * @return {Boolean} true if the record is valid within the bounds
  95 + * of the filter, false otherwise.
  96 + */
  97 + validateRecord : function (record) {
  98 + var val = record.get(this.dataIndex),
  99 + values = this.getValue(),
  100 + isNumber = Ext.isNumber;
  101 + if (isNumber(values.eq) && val != values.eq) {
  102 + return false;
  103 + }
  104 + if (isNumber(values.lt) && val >= values.lt) {
  105 + return false;
  106 + }
  107 + if (isNumber(values.gt) && val <= values.gt) {
  108 + return false;
  109 + }
  110 + return true;
  111 + }
  112 +});
js/lib/ux/grid/filter/StringFilter.js 0 โ†’ 100644
@@ -0,0 +1,134 @@ @@ -0,0 +1,134 @@
  1 +/**
  2 + * Filter by a configurable Ext.form.field.Text
  3 + * <p><b><u>Example Usage:</u></b></p>
  4 + * <pre><code>
  5 +var filters = Ext.create('Ext.ux.grid.GridFilters', {
  6 + ...
  7 + filters: [{
  8 + // required configs
  9 + type: 'string',
  10 + dataIndex: 'name',
  11 +
  12 + // optional configs
  13 + value: 'foo',
  14 + active: true, // default is false
  15 + iconCls: 'ux-gridfilter-text-icon' // default
  16 + // any Ext.form.field.Text configs accepted
  17 + }]
  18 +});
  19 + * </code></pre>
  20 + */
  21 +Ext.define('Ext.ux.grid.filter.StringFilter', {
  22 + extend: 'Ext.ux.grid.filter.Filter',
  23 + alias: 'gridfilter.string',
  24 +
  25 + /**
  26 + * @cfg {String} iconCls
  27 + * The iconCls to be applied to the menu item.
  28 + * Defaults to <tt>'ux-gridfilter-text-icon'</tt>.
  29 + */
  30 + iconCls : 'ux-gridfilter-text-icon',
  31 +
  32 + emptyText: 'Enter Filter Text...',
  33 + selectOnFocus: true,
  34 + width: 125,
  35 +
  36 + /**
  37 + * @private
  38 + * Template method that is to initialize the filter and install required menu items.
  39 + */
  40 + init : function (config) {
  41 + Ext.applyIf(config, {
  42 + enableKeyEvents: true,
  43 + labelCls: 'ux-rangemenu-icon ' + this.iconCls,
  44 + hideEmptyLabel: false,
  45 + labelSeparator: '',
  46 + labelWidth: 29,
  47 + listeners: {
  48 + scope: this,
  49 + keyup: this.onInputKeyUp,
  50 + el: {
  51 + click: function(e) {
  52 + e.stopPropagation();
  53 + }
  54 + }
  55 + }
  56 + });
  57 +
  58 + this.inputItem = Ext.create('Ext.form.field.Text', config);
  59 + this.menu.add(this.inputItem);
  60 + this.menu.showSeparator = false;
  61 + this.updateTask = Ext.create('Ext.util.DelayedTask', this.fireUpdate, this);
  62 + },
  63 +
  64 + /**
  65 + * @private
  66 + * Template method that is to get and return the value of the filter.
  67 + * @return {String} The value of this filter
  68 + */
  69 + getValue : function () {
  70 + return this.inputItem.getValue();
  71 + },
  72 +
  73 + /**
  74 + * @private
  75 + * Template method that is to set the value of the filter.
  76 + * @param {Object} value The value to set the filter
  77 + */
  78 + setValue : function (value) {
  79 + this.inputItem.setValue(value);
  80 + this.fireEvent('update', this);
  81 + },
  82 +
  83 + /**
  84 + * Template method that is to return <tt>true</tt> if the filter
  85 + * has enough configuration information to be activated.
  86 + * @return {Boolean}
  87 + */
  88 + isActivatable : function () {
  89 + return this.inputItem.getValue().length > 0;
  90 + },
  91 +
  92 + /**
  93 + * @private
  94 + * Template method that is to get and return serialized filter data for
  95 + * transmission to the server.
  96 + * @return {Object/Array} An object or collection of objects containing
  97 + * key value pairs representing the current configuration of the filter.
  98 + */
  99 + getSerialArgs : function () {
  100 + return {type: 'string', value: this.getValue()};
  101 + },
  102 +
  103 + /**
  104 + * Template method that is to validate the provided Ext.data.Record
  105 + * against the filters configuration.
  106 + * @param {Ext.data.Record} record The record to validate
  107 + * @return {Boolean} true if the record is valid within the bounds
  108 + * of the filter, false otherwise.
  109 + */
  110 + validateRecord : function (record) {
  111 + var val = record.get(this.dataIndex);
  112 +
  113 + if(typeof val != 'string') {
  114 + return (this.getValue().length === 0);
  115 + }
  116 +
  117 + return val.toLowerCase().indexOf(this.getValue().toLowerCase()) > -1;
  118 + },
  119 +
  120 + /**
  121 + * @private
  122 + * Handler method called when there is a keyup event on this.inputItem
  123 + */
  124 + onInputKeyUp : function (field, e) {
  125 + var k = e.getKey();
  126 + if (k == e.RETURN && field.isValid()) {
  127 + e.stopEvent();
  128 + this.menu.hide();
  129 + return;
  130 + }
  131 + // restart the timer
  132 + this.updateTask.delay(this.updateBuffer);
  133 + }
  134 +});
js/lib/ux/grid/images/equals.png 0 โ†’ 100644

217 Bytes

js/lib/ux/grid/images/find.png 0 โ†’ 100644

659 Bytes

js/lib/ux/grid/images/greater_than.png 0 โ†’ 100644

359 Bytes

js/lib/ux/grid/images/less_than.png 0 โ†’ 100644

354 Bytes

js/lib/ux/grid/menu/ListMenu.js 0 โ†’ 100644
@@ -0,0 +1,241 @@ @@ -0,0 +1,241 @@
  1 +/**
  2 + * This is a supporting class for {@link Ext.ux.grid.filter.ListFilter}.
  3 + * Although not listed as configuration options for this class, this class
  4 + * also accepts all configuration options from {@link Ext.ux.grid.filter.ListFilter}.
  5 + */
  6 +Ext.define('Ext.ux.grid.menu.ListMenu', {
  7 + extend: 'Ext.menu.Menu',
  8 +
  9 + /**
  10 + * @cfg {String} idField
  11 + * Defaults to 'id'.
  12 + */
  13 + idField : 'id',
  14 +
  15 + /**
  16 + * @cfg {String} labelField
  17 + * Defaults to 'text'.
  18 + */
  19 + labelField : 'text',
  20 + /**
  21 + * @cfg {String} paramPrefix
  22 + * Defaults to 'Loading...'.
  23 + */
  24 + loadingText : 'Loading...',
  25 + /**
  26 + * @cfg {Boolean} loadOnShow
  27 + * Defaults to true.
  28 + */
  29 + loadOnShow : true,
  30 + /**
  31 + * @cfg {Boolean} single
  32 + * Specify true to group all items in this list into a single-select
  33 + * radio button group. Defaults to false.
  34 + */
  35 + single : false,
  36 +
  37 + plain: true,
  38 +
  39 + constructor: function (cfg) {
  40 + var me = this,
  41 + gridStore;
  42 +
  43 + me.selected = [];
  44 + me.addEvents(
  45 + /**
  46 + * @event checkchange
  47 + * Fires when there is a change in checked items from this list
  48 + * @param {Object} item Ext.menu.CheckItem
  49 + * @param {Object} checked The checked value that was set
  50 + */
  51 + 'checkchange'
  52 + );
  53 +
  54 + me.callParent(arguments);
  55 +
  56 + gridStore = me.grid.store;
  57 +
  58 + if (me.store) {
  59 + me.add({
  60 + text: me.loadingText,
  61 + iconCls: 'loading-indicator'
  62 + });
  63 + me.store.on('load', me.onLoad, me);
  64 +
  65 + // A ListMenu which is completely unconfigured acquires its store from the unique values of its field in the store.
  66 + // If there are no records in the grid store, then we know it's async and we need to listen for its 'load' event.
  67 + } else if (gridStore.data.length) {
  68 + me.createMenuStore();
  69 + } else {
  70 + gridStore.on('load', me.createMenuStore, me, {single: true});
  71 + }
  72 + },
  73 +
  74 + destroy : function () {
  75 + var me = this,
  76 + store = me.store;
  77 +
  78 + if (store) {
  79 + if (me.autoStore) {
  80 + store.destroyStore();
  81 + } else {
  82 + store.un('unload', me.onLoad, me);
  83 + }
  84 + }
  85 +
  86 + if (me.autoGeneratedOptions) {
  87 + me.autoGeneratedOptions = null;
  88 +
  89 + me.grid.store.un({
  90 + scope: me,
  91 + datachanged: me.onDataChanged,
  92 + update: me.onDataChanged
  93 + });
  94 + }
  95 +
  96 + me.callParent();
  97 + },
  98 +
  99 + /**
  100 + * Lists will initially show a 'loading' item while the data is retrieved from the store.
  101 + * In some cases the loaded data will result in a list that goes off the screen to the
  102 + * right (as placement calculations were done with the loading item). This adapter will
  103 + * allow show to be called with no arguments to show with the previous arguments and
  104 + * thus recalculate the width and potentially hang the menu from the left.
  105 + */
  106 + show : function () {
  107 + var me = this;
  108 + if (me.loadOnShow && !me.loaded && !me.store.loading) {
  109 + me.store.load();
  110 + }
  111 + me.callParent();
  112 + },
  113 +
  114 + onDataChanged: function (store) {
  115 + // If the menu item options (and the options store) are being auto-generated from the grid store, then it
  116 + // needs to know when the grid store has changed its data so it can remain in sync.
  117 + //
  118 + // We need to gather the `autoGeneratedOptions` every time the menu items are created so we can compare values.
  119 + var autoGeneratedOptions = this.autoGeneratedOptions;
  120 +
  121 + // Note that autoGeneratedOptions won't be populated with values until the menu is shown and the Filter item's
  122 + // items are created.
  123 + if (autoGeneratedOptions) {
  124 + // Get all unique values, including nulls, either from .data or ._source (if filtered) and compare to the
  125 + // unique options gathered when the menu items are instanced.
  126 + if (!Ext.Array.equals(store.collect(this.idField, true, store.isFiltered()).sort(), autoGeneratedOptions.sort())) {
  127 + this.menu.createMenuStore(store);
  128 + }
  129 + }
  130 + },
  131 +
  132 + /** @private */
  133 + onLoad: function (store, records) {
  134 + var me = this,
  135 + gid, itemValue, i, len,
  136 + listeners = {
  137 + checkchange: me.checkChange,
  138 + scope: me
  139 + };
  140 +
  141 + Ext.suspendLayouts();
  142 + me.removeAll(true);
  143 + gid = me.single ? Ext.id() : null;
  144 + for (i = 0, len = records.length; i < len; i++) {
  145 + itemValue = records[i].get(me.idField);
  146 + me.add(Ext.create('Ext.menu.CheckItem', {
  147 + text: records[i].get(me.labelField),
  148 + group: gid,
  149 + checked: Ext.Array.contains(me.selected, itemValue),
  150 + hideOnClick: false,
  151 + value: itemValue,
  152 + listeners: listeners
  153 + }));
  154 + }
  155 +
  156 + me.loaded = true;
  157 + Ext.resumeLayouts(true);
  158 + me.fireEvent('load', me, records);
  159 + },
  160 +
  161 + createMenuStore: function () {
  162 + var me = this,
  163 + options = me.options || me.grid.store.collect(me.dataIndex, false, true),
  164 + i = 0,
  165 + len = options.length,
  166 + storeOptions = [],
  167 + idField = me.idField,
  168 + labelField = me.labelField,
  169 + value;
  170 +
  171 + for (; i < len; i++) {
  172 + value = options[i];
  173 +
  174 + switch (Ext.type(value)) {
  175 + case 'array':
  176 + storeOptions.push(value);
  177 + break;
  178 + case 'object':
  179 + storeOptions.push([value[idField], value[labelField]]);
  180 + break;
  181 + default:
  182 + if (value != null) {
  183 + storeOptions.push([value, value]);
  184 + }
  185 + }
  186 + }
  187 +
  188 + me.store = Ext.create('Ext.data.ArrayStore', {
  189 + fields: [idField, labelField],
  190 + data: storeOptions,
  191 + listeners: {
  192 + load: me.onLoad,
  193 + scope: me
  194 + }
  195 + });
  196 +
  197 + me.loaded = true;
  198 + me.autoStore = true;
  199 + },
  200 +
  201 + /**
  202 + * Get the selected items.
  203 + * @return {Array} selected
  204 + */
  205 + getSelected : function () {
  206 + return this.selected;
  207 + },
  208 +
  209 + /** @private */
  210 + setSelected : function (value) {
  211 + value = this.selected = [].concat(value);
  212 +
  213 + if (this.loaded) {
  214 + this.items.each(function(item){
  215 + item.setChecked(false, true);
  216 + for (var i = 0, len = value.length; i < len; i++) {
  217 + if (item.value == value[i]) {
  218 + item.setChecked(true, true);
  219 + }
  220 + }
  221 + });
  222 + }
  223 + },
  224 +
  225 + /**
  226 + * Handler for the 'checkchange' event from an check item in this menu
  227 + * @param {Object} item Ext.menu.CheckItem
  228 + * @param {Object} checked The checked value that was set
  229 + */
  230 + checkChange : function (item, checked) {
  231 + var value = [];
  232 + this.items.each(function(item){
  233 + if (item.checked) {
  234 + value.push(item.value);
  235 + }
  236 + });
  237 + this.selected = value;
  238 +
  239 + this.fireEvent('checkchange', item, checked);
  240 + }
  241 +});
js/lib/ux/grid/menu/RangeMenu.js 0 โ†’ 100644
@@ -0,0 +1,260 @@ @@ -0,0 +1,260 @@
  1 +/**
  2 + * Custom implementation of {@link Ext.menu.Menu} that has preconfigured items for entering numeric
  3 + * range comparison values: less-than, greater-than, and equal-to. This is used internally
  4 + * by {@link Ext.ux.grid.filter.NumericFilter} to create its menu.
  5 + */
  6 +Ext.define('Ext.ux.grid.menu.RangeMenu', {
  7 + extend: 'Ext.menu.Menu',
  8 +
  9 + /**
  10 + * @cfg {String} fieldCls
  11 + * The Class to use to construct each field item within this menu
  12 + * Defaults to:<pre>
  13 + * fieldCls : Ext.form.field.Number
  14 + * </pre>
  15 + */
  16 + fieldCls : 'Ext.form.field.Number',
  17 +
  18 + /**
  19 + * @cfg {Object} fieldCfg
  20 + * The default configuration options for any field item unless superseded
  21 + * by the <code>{@link #fields}</code> configuration.
  22 + * Defaults to:<pre>
  23 + * fieldCfg : {}
  24 + * </pre>
  25 + * Example usage:
  26 + * <pre><code>
  27 +fieldCfg : {
  28 + width: 150,
  29 +},
  30 + * </code></pre>
  31 + */
  32 +
  33 + /**
  34 + * @cfg {Object} fields
  35 + * The field items may be configured individually
  36 + * Defaults to <tt>undefined</tt>.
  37 + * Example usage:
  38 + * <pre><code>
  39 +fields : {
  40 + gt: { // override fieldCfg options
  41 + width: 200,
  42 + fieldCls: Ext.ux.form.CustomNumberField // to override default {@link #fieldCls}
  43 + }
  44 +},
  45 + * </code></pre>
  46 + */
  47 +
  48 + /**
  49 + * @cfg {Object} itemIconCls
  50 + * The itemIconCls to be applied to each comparator field item.
  51 + * Defaults to:<pre>
  52 +itemIconCls : {
  53 + gt : 'ux-rangemenu-gt',
  54 + lt : 'ux-rangemenu-lt',
  55 + eq : 'ux-rangemenu-eq'
  56 +}
  57 + * </pre>
  58 + */
  59 + itemIconCls : {
  60 + gt : 'ux-rangemenu-gt',
  61 + lt : 'ux-rangemenu-lt',
  62 + eq : 'ux-rangemenu-eq'
  63 + },
  64 +
  65 + /**
  66 + * @cfg {Object} fieldLabels
  67 + * Accessible label text for each comparator field item. Can be overridden by localization
  68 + * files. Defaults to:<pre>
  69 +fieldLabels : {
  70 + gt: 'Greater Than',
  71 + lt: 'Less Than',
  72 + eq: 'Equal To'
  73 +}</pre>
  74 + */
  75 + fieldLabels: {
  76 + gt: 'Greater Than',
  77 + lt: 'Less Than',
  78 + eq: 'Equal To'
  79 + },
  80 +
  81 + /**
  82 + * @cfg {Object} menuItemCfgs
  83 + * Default configuration options for each menu item
  84 + * Defaults to:<pre>
  85 +menuItemCfgs : {
  86 + emptyText: 'Enter Filter Text...',
  87 + selectOnFocus: true,
  88 + width: 125
  89 +}
  90 + * </pre>
  91 + */
  92 + menuItemCfgs : {
  93 + emptyText: 'Enter Number...',
  94 + selectOnFocus: false,
  95 + width: 155
  96 + },
  97 +
  98 + /**
  99 + * @cfg {Array} menuItems
  100 + * The items to be shown in this menu. Items are added to the menu
  101 + * according to their position within this array. Defaults to:<pre>
  102 + * menuItems : ['lt','gt','-','eq']
  103 + * </pre>
  104 + */
  105 + menuItems : ['lt', 'gt', '-', 'eq'],
  106 +
  107 + plain: true,
  108 +
  109 + constructor : function (config) {
  110 + var me = this,
  111 + fields, fieldCfg, i, len, item, cfg, Cls;
  112 +
  113 + me.callParent(arguments);
  114 +
  115 + fields = me.fields = me.fields || {};
  116 + fieldCfg = me.fieldCfg = me.fieldCfg || {};
  117 +
  118 + me.addEvents(
  119 + /**
  120 + * @event update
  121 + * Fires when a filter configuration has changed
  122 + * @param {Ext.ux.grid.filter.Filter} this The filter object.
  123 + */
  124 + 'update'
  125 + );
  126 +
  127 + me.updateTask = Ext.create('Ext.util.DelayedTask', me.fireUpdate, me);
  128 +
  129 + for (i = 0, len = me.menuItems.length; i < len; i++) {
  130 + item = me.menuItems[i];
  131 + if (item !== '-') {
  132 + // defaults
  133 + cfg = {
  134 + itemId: 'range-' + item,
  135 + enableKeyEvents: true,
  136 + hideEmptyLabel: false,
  137 + labelCls: 'ux-rangemenu-icon ' + me.itemIconCls[item],
  138 + labelSeparator: '',
  139 + labelWidth: 29,
  140 + listeners: {
  141 + scope: me,
  142 + change: me.onInputChange,
  143 + keyup: me.onInputKeyUp,
  144 + el: {
  145 + click: this.stopFn
  146 + }
  147 + },
  148 + activate: Ext.emptyFn,
  149 + deactivate: Ext.emptyFn
  150 + };
  151 + Ext.apply(
  152 + cfg,
  153 + // custom configs
  154 + Ext.applyIf(fields[item] || {}, fieldCfg[item]),
  155 + // configurable defaults
  156 + me.menuItemCfgs
  157 + );
  158 + Cls = cfg.fieldCls || me.fieldCls;
  159 + item = fields[item] = Ext.create(Cls, cfg);
  160 + }
  161 + me.add(item);
  162 + }
  163 + },
  164 +
  165 + stopFn: function(e) {
  166 + e.stopPropagation();
  167 + },
  168 +
  169 + /**
  170 + * @private
  171 + * called by this.updateTask
  172 + */
  173 + fireUpdate : function () {
  174 + this.fireEvent('update', this);
  175 + },
  176 +
  177 + /**
  178 + * Get and return the value of the filter.
  179 + * @return {String} The value of this filter
  180 + */
  181 + getValue : function () {
  182 + var result = {},
  183 + fields = this.fields,
  184 + key, field;
  185 +
  186 + for (key in fields) {
  187 + if (fields.hasOwnProperty(key)) {
  188 + field = fields[key];
  189 + if (field.isValid() && field.getValue() !== null) {
  190 + result[key] = field.getValue();
  191 + }
  192 + }
  193 + }
  194 + return result;
  195 + },
  196 +
  197 + /**
  198 + * Set the value of this menu and fires the 'update' event.
  199 + * @param {Object} data The data to assign to this menu
  200 + */
  201 + setValue : function (data) {
  202 + var me = this,
  203 + fields = me.fields,
  204 + key,
  205 + field;
  206 +
  207 + for (key in fields) {
  208 + if (fields.hasOwnProperty(key)) {
  209 + // Prevent field's change event from tiggering a Store filter. The final upate event will do that
  210 + field =fields[key];
  211 + field.suspendEvents();
  212 + field.setValue(key in data ? data[key] : '');
  213 + field.resumeEvents();
  214 + }
  215 + }
  216 +
  217 + // Trigger the filering of the Store
  218 + me.fireEvent('update', me);
  219 + },
  220 +
  221 + /**
  222 + * @private
  223 + * Handler method called when there is a keyup event on an input
  224 + * item of this menu.
  225 + */
  226 + onInputKeyUp: function(field, e) {
  227 + if (e.getKey() === e.RETURN && field.isValid()) {
  228 + e.stopEvent();
  229 + this.hide();
  230 + }
  231 + },
  232 +
  233 + /**
  234 + * @private
  235 + * Handler method called when the user changes the value of one of the input
  236 + * items in this menu.
  237 + */
  238 + onInputChange: function(field) {
  239 + var me = this,
  240 + fields = me.fields,
  241 + eq = fields.eq,
  242 + gt = fields.gt,
  243 + lt = fields.lt;
  244 +
  245 + if (field == eq) {
  246 + if (gt) {
  247 + gt.setValue(null);
  248 + }
  249 + if (lt) {
  250 + lt.setValue(null);
  251 + }
  252 + }
  253 + else {
  254 + eq.setValue(null);
  255 + }
  256 +
  257 + // restart the timer
  258 + this.updateTask.delay(this.updateBuffer);
  259 + }
  260 +});