Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / util / Sorter.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.Sorter
17  * @extends Object
18
19 Represents a single sorter that can be applied to a Store. The sorter is used
20 to compare two values against each other for the purpose of ordering them. Ordering
21 is achieved by specifying either:
22 - {@link #property A sorting property}
23 - {@link #sorterFn A sorting function} 
24
25 As a contrived example, we can specify a custom sorter that sorts by rank:
26
27     Ext.define('Person', {
28         extend: 'Ext.data.Model',
29         fields: ['name', 'rank']
30     });
31
32     Ext.create('Ext.data.Store', {
33         model: 'Person',
34         proxy: 'memory',
35         sorters: [{
36             sorterFn: function(o1, o2){
37                 var getRank = function(o){
38                     var name = o.get('rank');
39                     if (name === 'first') {
40                         return 1;
41                     } else if (name === 'second') {
42                         return 2;
43                     } else {
44                         return 3;
45                     }
46                 },
47                 rank1 = getRank(o1),
48                 rank2 = getRank(o2);
49                 
50                 if (rank1 === rank2) {
51                     return 0;
52                 }
53                 
54                 return rank1 < rank2 ? -1 : 1;
55             }
56         }],
57         data: [{
58             name: 'Person1',
59             rank: 'second'
60         }, {
61             name: 'Person2',
62             rank: 'third'
63         }, {
64             name: 'Person3',
65             rank: 'first'
66         }] 
67     });
68
69  * @markdown
70  */
71 Ext.define('Ext.util.Sorter', {
72
73     /**
74      * @cfg {String} property The property to sort by. Required unless {@link #sorterFn} is provided.
75      * The property is extracted from the object directly and compared for sorting using the built in
76      * comparison operators.
77      */
78     
79     /**
80      * @cfg {Function} sorterFn A specific sorter function to execute. Can be passed instead of {@link #property}.
81      * This sorter function allows for any kind of custom/complex comparisons.
82      * The sorterFn receives two arguments, the objects being compared. The function should return:
83      * <ul>
84      * <li>-1 if o1 is "less than" o2</li>
85      * <li>0 if o1 is "equal" to o2</li>
86      * <li>1 if o1 is "greater than" o2</li>
87      * </ul>
88      */
89     
90     /**
91      * @cfg {String} root Optional root property. This is mostly useful when sorting a Store, in which case we set the
92      * root to 'data' to make the filter pull the {@link #property} out of the data object of each item
93      */
94     
95     /**
96      * @cfg {Function} transform A function that will be run on each value before
97      * it is compared in the sorter. The function will receive a single argument,
98      * the value.
99      */
100     
101     /**
102      * @cfg {String} direction The direction to sort by. Defaults to ASC
103      */
104     direction: "ASC",
105     
106     constructor: function(config) {
107         var me = this;
108         
109         Ext.apply(me, config);
110         
111         //<debug>
112         if (me.property === undefined && me.sorterFn === undefined) {
113             Ext.Error.raise("A Sorter requires either a property or a sorter function");
114         }
115         //</debug>
116         
117         me.updateSortFunction();
118     },
119     
120     /**
121      * @private
122      * Creates and returns a function which sorts an array by the given property and direction
123      * @return {Function} A function which sorts by the property/direction combination provided
124      */
125     createSortFunction: function(sorterFn) {
126         var me        = this,
127             property  = me.property,
128             direction = me.direction || "ASC",
129             modifier  = direction.toUpperCase() == "DESC" ? -1 : 1;
130         
131         //create a comparison function. Takes 2 objects, returns 1 if object 1 is greater,
132         //-1 if object 2 is greater or 0 if they are equal
133         return function(o1, o2) {
134             return modifier * sorterFn.call(me, o1, o2);
135         };
136     },
137     
138     /**
139      * @private
140      * Basic default sorter function that just compares the defined property of each object
141      */
142     defaultSorterFn: function(o1, o2) {
143         var me = this,
144             transform = me.transform,
145             v1 = me.getRoot(o1)[me.property],
146             v2 = me.getRoot(o2)[me.property];
147             
148         if (transform) {
149             v1 = transform(v1);
150             v2 = transform(v2);
151         }
152
153         return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
154     },
155     
156     /**
157      * @private
158      * Returns the root property of the given item, based on the configured {@link #root} property
159      * @param {Object} item The item
160      * @return {Object} The root property of the object
161      */
162     getRoot: function(item) {
163         return this.root === undefined ? item : item[this.root];
164     },
165     
166     /**
167      * Set the sorting direction for this sorter.
168      * @param {String} direction The direction to sort in. Should be either 'ASC' or 'DESC'.
169      */
170     setDirection: function(direction) {
171         var me = this;
172         me.direction = direction;
173         me.updateSortFunction();
174     },
175     
176     /**
177      * Toggles the sorting direction for this sorter.
178      */
179     toggle: function() {
180         var me = this;
181         me.direction = Ext.String.toggle(me.direction, "ASC", "DESC");
182         me.updateSortFunction();
183     },
184     
185     /**
186      * Update the sort function for this sorter.
187      * @param {Function} fn (Optional) A new sorter function for this sorter. If not specified it will use the
188      * default sorting function.
189      */
190     updateSortFunction: function(fn) {
191         var me = this;
192         fn = fn || me.sorterFn || me.defaultSorterFn;
193         me.sort = me.createSortFunction(fn);
194     }
195 });