Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / util / Filter.js
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  * @class Ext.util.Filter
17  * @extends Object
18  * <p>Represents a filter that can be applied to a {@link Ext.util.MixedCollection MixedCollection}. Can either simply
19  * filter on a property/value pair or pass in a filter function with custom logic. Filters are always used in the context
20  * of MixedCollections, though {@link Ext.data.Store Store}s frequently create them when filtering and searching on their
21  * records. Example usage:</p>
22 <pre><code>
23 //set up a fictional MixedCollection containing a few people to filter on
24 var allNames = new Ext.util.MixedCollection();
25 allNames.addAll([
26     {id: 1, name: 'Ed',    age: 25},
27     {id: 2, name: 'Jamie', age: 37},
28     {id: 3, name: 'Abe',   age: 32},
29     {id: 4, name: 'Aaron', age: 26},
30     {id: 5, name: 'David', age: 32}
31 ]);
32
33 var ageFilter = new Ext.util.Filter({
34     property: 'age',
35     value   : 32
36 });
37
38 var longNameFilter = new Ext.util.Filter({
39     filterFn: function(item) {
40         return item.name.length > 4;
41     }
42 });
43
44 //a new MixedCollection with the 3 names longer than 4 characters
45 var longNames = allNames.filter(longNameFilter);
46
47 //a new MixedCollection with the 2 people of age 24:
48 var youngFolk = allNames.filter(ageFilter);
49 </code></pre>
50  */
51 Ext.define('Ext.util.Filter', {
52
53     /* Begin Definitions */
54
55     /* End Definitions */
56     /**
57      * @cfg {String} property The property to filter on. Required unless a {@link #filterFn} is passed
58      */
59     
60     /**
61      * @cfg {Function} filterFn A custom filter function which is passed each item in the {@link Ext.util.MixedCollection} 
62      * in turn. Should return true to accept each item or false to reject it
63      */
64     
65     /**
66      * @cfg {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
67      */
68     anyMatch: false,
69     
70     /**
71      * @cfg {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false.
72      * Ignored if anyMatch is true.
73      */
74     exactMatch: false,
75     
76     /**
77      * @cfg {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
78      */
79     caseSensitive: false,
80     
81     /**
82      * @cfg {String} root Optional root property. This is mostly useful when filtering a Store, in which case we set the
83      * root to 'data' to make the filter pull the {@link #property} out of the data object of each item
84      */
85
86     /**
87      * Creates new Filter.
88      * @param {Object} config (optional) Config object
89      */
90     constructor: function(config) {
91         Ext.apply(this, config);
92         
93         //we're aliasing filter to filterFn mostly for API cleanliness reasons, despite the fact it dirties the code here.
94         //Ext.util.Sorter takes a sorterFn property but allows .sort to be called - we do the same here
95         this.filter = this.filter || this.filterFn;
96         
97         if (this.filter == undefined) {
98             if (this.property == undefined || this.value == undefined) {
99                 // Commented this out temporarily because it stops us using string ids in models. TODO: Remove this once
100                 // Model has been updated to allow string ids
101                 
102                 // Ext.Error.raise("A Filter requires either a property or a filterFn to be set");
103             } else {
104                 this.filter = this.createFilterFn();
105             }
106             
107             this.filterFn = this.filter;
108         }
109     },
110     
111     /**
112      * @private
113      * Creates a filter function for the configured property/value/anyMatch/caseSensitive options for this Filter
114      */
115     createFilterFn: function() {
116         var me       = this,
117             matcher  = me.createValueMatcher(),
118             property = me.property;
119         
120         return function(item) {
121             return matcher.test(me.getRoot.call(me, item)[property]);
122         };
123     },
124     
125     /**
126      * @private
127      * Returns the root property of the given item, based on the configured {@link #root} property
128      * @param {Object} item The item
129      * @return {Object} The root property of the object
130      */
131     getRoot: function(item) {
132         return this.root == undefined ? item : item[this.root];
133     },
134     
135     /**
136      * @private
137      * Returns a regular expression based on the given value and matching options
138      */
139     createValueMatcher : function() {
140         var me            = this,
141             value         = me.value,
142             anyMatch      = me.anyMatch,
143             exactMatch    = me.exactMatch,
144             caseSensitive = me.caseSensitive,
145             escapeRe      = Ext.String.escapeRegex;
146         
147         if (!value.exec) { // not a regex
148             value = String(value);
149
150             if (anyMatch === true) {
151                 value = escapeRe(value);
152             } else {
153                 value = '^' + escapeRe(value);
154                 if (exactMatch === true) {
155                     value += '$';
156                 }
157             }
158             value = new RegExp(value, caseSensitive ? '' : 'i');
159          }
160          
161          return value;
162     }
163 });