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