]> git.proxmox.com Git - sencha-touch.git/blame - src/src/util/Filter.js
import Sencha Touch 2.4.2 source
[sencha-touch.git] / src / src / util / Filter.js
CommitLineData
c4685c84
TL
1/**
2 * Represents a filter that can be applied to a {@link Ext.util.MixedCollection MixedCollection}. Can either simply
3 * filter on a property/value pair or pass in a filter function with custom logic. Filters are always used in the
4 * context of MixedCollections, though {@link Ext.data.Store Store}s frequently create them when filtering and searching
5 * on their records. Example usage:
6 *
7 * // Set up a fictional MixedCollection containing a few people to filter on
8 * var allNames = new Ext.util.MixedCollection();
9 * allNames.addAll([
10 * { id: 1, name: 'Ed', age: 25 },
11 * { id: 2, name: 'Jamie', age: 37 },
12 * { id: 3, name: 'Abe', age: 32 },
13 * { id: 4, name: 'Aaron', age: 26 },
14 * { id: 5, name: 'David', age: 32 }
15 * ]);
16 *
17 * var ageFilter = new Ext.util.Filter({
18 * property: 'age',
19 * value : 32
20 * });
21 *
22 * var longNameFilter = new Ext.util.Filter({
23 * filterFn: function(item) {
24 * return item.name.length > 4;
25 * }
26 * });
27 *
28 * // a new MixedCollection with the 3 names longer than 4 characters
29 * var longNames = allNames.filter(longNameFilter);
30 *
31 * // a new MixedCollection with the 2 people of age 32:
32 * var youngFolk = allNames.filter(ageFilter);
33 */
34Ext.define('Ext.util.Filter', {
35 isFilter: true,
36
37 config: {
38 /**
39 * @cfg {String} [property=null]
40 * The property to filter on. Required unless a `filter` is passed
41 */
42 property: null,
43
44 /**
45 * @cfg {RegExp/Mixed} [value=null]
46 * The value you want to match against. Can be a regular expression which will be used as matcher or any other
47 * value. Mixed can be an object or an array of objects.
48 */
49 value: null,
50
51 /**
52 * @cfg {Function} filterFn
53 * A custom filter function which is passed each item in the {@link Ext.util.MixedCollection} in turn. Should
54 * return true to accept each item or false to reject it
55 */
56 filterFn: Ext.emptyFn,
57
58 /**
59 * @cfg {Boolean} [anyMatch=false]
60 * True to allow any match - no regex start/end line anchors will be added.
61 */
62 anyMatch: false,
63
64 /**
65 * @cfg {Boolean} [exactMatch=false]
66 * True to force exact match (^ and $ characters added to the regex). Ignored if anyMatch is true.
67 */
68 exactMatch: false,
69
70 /**
71 * @cfg {Boolean} [caseSensitive=false]
72 * True to make the regex case sensitive (adds 'i' switch to regex).
73 */
74 caseSensitive: false,
75
76 /**
77 * @cfg {String} [root=null]
78 * Optional root property. This is mostly useful when filtering a Store, in which case we set the root to 'data'
79 * to make the filter pull the {@link #property} out of the data object of each item
80 */
81 root: null,
82
83 /**
84 * @cfg {String} id
85 * An optional id this filter can be keyed by in Collections. If no id is specified it will generate an id by
86 * first trying a combination of property-value, and if none if these were specified (like when having a
87 * filterFn) it will generate a random id.
88 */
89 id: undefined,
90
91 /**
92 * @cfg {Object} [scope=null]
93 * The scope in which to run the filterFn
94 */
95 scope: null
96 },
97
98 applyId: function(id) {
99 if (!id) {
100 if (this.getProperty()) {
101 id = this.getProperty() + '-' + String(this.getValue());
102 }
103 if (!id) {
104 id = Ext.id(null, 'ext-filter-');
105 }
106 }
107
108 return id;
109 },
110
111 /**
112 * Creates new Filter.
113 * @param {Object} config Config object
114 */
115 constructor: function(config) {
116 this.initConfig(config);
117 },
118
119 applyFilterFn: function(filterFn) {
120 if (filterFn === Ext.emptyFn) {
121 filterFn = this.getInitialConfig('filter');
122 if (filterFn) {
123 return filterFn;
124 }
125
126 var value = this.getValue();
127 if (!this.getProperty() && !value && value !== 0) {
128 // <debug>
129 Ext.Logger.error('A Filter requires either a property and value, or a filterFn to be set');
130 // </debug>
131 return Ext.emptyFn;
132 }
133 else {
134 return this.createFilterFn();
135 }
136 }
137 return filterFn;
138 },
139
140 /**
141 * @private
142 * Creates a filter function for the configured property/value/anyMatch/caseSensitive options for this Filter
143 */
144 createFilterFn: function() {
145 var me = this,
146 matcher = me.createValueMatcher();
147
148 return function(item) {
149 var root = me.getRoot(),
150 property = me.getProperty();
151
152 if (root) {
153 item = item[root];
154 }
155
156 return matcher.test(item[property]);
157 };
158 },
159
160 /**
161 * @private
162 * Returns a regular expression based on the given value and matching options
163 */
164 createValueMatcher: function() {
165 var me = this,
166 value = me.getValue(),
167 anyMatch = me.getAnyMatch(),
168 exactMatch = me.getExactMatch(),
169 caseSensitive = me.getCaseSensitive(),
170 escapeRe = Ext.String.escapeRegex;
171
172 if (value === null || value === undefined || !value.exec) { // not a regex
173 value = String(value);
174
175 if (anyMatch === true) {
176 value = escapeRe(value);
177 } else {
178 value = '^' + escapeRe(value);
179 if (exactMatch === true) {
180 value += '$';
181 }
182 }
183 value = new RegExp(value, caseSensitive ? '' : 'i');
184 }
185
186 return value;
187 }
188});