/** * Represents a single sorter that can be used as part of the sorters configuration in Ext.mixin.Sortable. * * A common place for Sorters to be used are {@link Ext.data.Store Stores}. For example: * * @example miniphone * var store = Ext.create('Ext.data.Store', { * fields: ['firstName', 'lastName'], * sorters: 'lastName', * * data: [ * { firstName: 'Tommy', lastName: 'Maintz' }, * { firstName: 'Rob', lastName: 'Dougan' }, * { firstName: 'Ed', lastName: 'Spencer'}, * { firstName: 'Jamie', lastName: 'Avins' }, * { firstName: 'Nick', lastName: 'Poulden'} * ] * }); * * Ext.create('Ext.List', { * fullscreen: true, * itemTpl: '<div class="contact">{firstName} <strong>{lastName}</strong></div>', * store: store * }); * * In the next example, we specify a custom sorter function: * * @example miniphone * var store = Ext.create('Ext.data.Store', { * fields: ['person'], * sorters: [ * { * // Sort by first letter of last name, in descending order * sorterFn: function(record1, record2) { * var name1 = record1.data.person.name.split('-')[1].substr(0, 1), * name2 = record2.data.person.name.split('-')[1].substr(0, 1); * * return name1 > name2 ? 1 : (name1 == name2 ? 0 : -1); * }, * direction: 'DESC' * } * ], * data: [ * { person: { name: 'Tommy-Maintz' } }, * { person: { name: 'Rob-Dougan' } }, * { person: { name: 'Ed-Spencer' } }, * { person: { name: 'Nick-Poulden' } }, * { person: { name: 'Jamie-Avins' } } * ] * }); * * Ext.create('Ext.List', { * fullscreen: true, * itemTpl: '{person.name}', * store: store * }); */ Ext.define('Ext.util.Sorter', { isSorter: true, config: { /** * @cfg {String} property The property to sort by. Required unless `sorterFn` is provided */ property: null, /** * @cfg {Function} sorterFn A specific sorter function to execute. Can be passed instead of {@link #property}. * This function should compare the two passed arguments, returning -1, 0 or 1 depending on if item 1 should be * sorted before, at the same level, or after item 2. * * sorterFn: function(person1, person2) { * return (person1.age > person2.age) ? 1 : (person1.age == person2.age ? 0 : -1); * } */ sorterFn: null, /** * @cfg {String} root Optional root property. This is mostly useful when sorting a Store, in which case we set the * root to 'data' to make the filter pull the {@link #property} out of the data object of each item */ root: null, /** * @cfg {Function} transform A function that will be run on each value before * it is compared in the sorter. The function will receive a single argument, * the value. */ transform: null, /** * @cfg {String} direction The direction to sort by. */ direction: "ASC", /** * @cfg {Mixed} id An optional id this sorter can be keyed by in Collections. If * no id is specified it will use the property name used in this Sorter. If no * property is specified, e.g. when adding a custom sorter function we will generate * a random id. */ id: undefined }, constructor: function(config) { this.initConfig(config); }, // <debug> applySorterFn: function(sorterFn) { if (!sorterFn && !this.getProperty()) { Ext.Logger.error("A Sorter requires either a property or a sorterFn."); } return sorterFn; }, applyProperty: function(property) { if (!property && !this.getSorterFn()) { Ext.Logger.error("A Sorter requires either a property or a sorterFn."); } return property; }, // </debug> applyId: function(id) { if (!id) { id = this.getProperty(); if (!id) { id = Ext.id(null, 'ext-sorter-'); } } return id; }, /** * @private * Creates and returns a function which sorts an array by the given property and direction * @return {Function} A function which sorts by the property/direction combination provided */ createSortFunction: function(sorterFn) { var me = this, modifier = me.getDirection().toUpperCase() == "DESC" ? -1 : 1; //create a comparison function. Takes 2 objects, returns 1 if object 1 is greater, //-1 if object 2 is greater or 0 if they are equal return function(o1, o2) { return modifier * sorterFn.call(me, o1, o2); }; }, /** * @private * Basic default sorter function that just compares the defined property of each object */ defaultSortFn: function(item1, item2) { var me = this, transform = me._transform, root = me._root, value1, value2, property = me._property; if (root !== null) { item1 = item1[root]; item2 = item2[root]; } value1 = item1[property]; value2 = item2[property]; if (transform) { value1 = transform(value1); value2 = transform(value2); } return value1 > value2 ? 1 : (value1 < value2 ? -1 : 0); }, updateDirection: function() { this.updateSortFn(); }, updateSortFn: function() { this.sort = this.createSortFunction(this.getSorterFn() || this.defaultSortFn); }, /** * Toggles the direction of this Sorter. Note that when you call this function, * the Collection this Sorter is part of does not get refreshed automatically. */ toggle: function() { this.setDirection(Ext.String.toggle(this.getDirection(), "ASC", "DESC")); } });