sort.js revision 2b9aa50d4077b548fa76a5d5330265ab397caad3
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithAdds support for sorting the table data by API methods `table.sort(...)` or
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith`table.toggleSort(...)` or by clicking on column headers in the rendered UI.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithSorting by the API is enabled automatically when this module is `use()`d. To
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithenable UI triggered sorting, set the DataTable's `sortable` attribute to
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithvar table = new Y.DataTable({
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith columns: [ 'id', 'username', 'name', 'birthdate' ],
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith data: [ ... ],
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith sortable: true
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithtable.render('#table');
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith</code></pre>
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithSetting `sortable` to `true` will enable UI sorting for all columns. To enable
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithUI sorting for certain columns only, set `sortable` to an array of column keys,
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithor just add `sortable: true` to the respective column configuration objects.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithThis uses the default setting of `sortable: auto` for the DataTable instance.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithvar table = new Y.DataTable({
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith { key: 'username', sortable: true },
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith { key: 'name', sortable: true },
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith { key: 'birthdate', sortable: true }
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith data: [ ... ]
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith // sortable: 'auto' is the default
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithvar table = new Y.DataTable({
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith columns: [ 'id', 'username', 'name', 'birthdate' ],
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith data: [ ... ],
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith sortable: [ 'username', 'name', 'birthdate' ]
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith</code></pre>
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithTo disable UI sorting for all columns, set `sortable` to `false`. This still
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithpermits sorting via the API methods.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithAs new records are inserted into the table's `data` ModelList, they will be inserted at the correct index to preserve the sort order.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithThe current sort order is stored in the `sortBy` attribute. Assigning this value at instantiation will automatically sort your data.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithSorting is done by a simple value comparison using < and > on the field
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithvalue. If you need custom sorting, add a sort function in the column's
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith`sortFn` property. Columns whose content is generated by formatters, but don't
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithrelate to a single `key`, require a `sortFn` to be sortable.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithfunction nameSort(a, b) {
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith var aa = a.get('lastName'),
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith bb = a.get('lastName');
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith if (aa === bb) {
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith aa = a.get('firstName');
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith bb = b.get('firstName');
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith return (aa > bb) ? 1 : (aa < bb) ? -1 : 0;
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smithvar table = new Y.DataTable({
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith columns: [ 'id', 'username', { key: name, sortFn: nameSort }, 'birthdate' ],
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith data: [ ... ],
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith sortable: [ 'username', 'name', 'birthdate' ]
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith</code></pre>
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke SmithSee the user guide for more details.
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith@module datatable-sort
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith@class DataTable.Sortable
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith@for DataTable
8fff801a4a38e13d6ff8181ff097fb01d058d617Luke Smith // Which columns in the UI should suggest and respond to sorting interaction
8fff801a4a38e13d6ff8181ff097fb01d058d617Luke Smith // pass an empty array if no UI columns should show sortable, but you want the
8fff801a4a38e13d6ff8181ff097fb01d058d617Luke Smith // table.sort(...) API
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Controls which column headers can trigger sorting by user clicks.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Acceptable values are:
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * "auto" - (default) looks for `sortable: true` in the column configurations
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `true` - all columns are enabled
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `false - no UI sortable is enabled
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * {String[]} - array of key names to give sortable headers
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @attribute sortable
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @type {String|String[]|Boolean}
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @default "auto"
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith The current sort configuration to maintain in the data.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Accepts column `key` strings or objects with a single property, the column
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith `key`, with a value of 1, -1, "asc", or "desc". E.g. `{ username: 'asc'
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith }`. String values are assumed to be ascending.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Example values would be:
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `"username"` - sort by the data's `username` field or the `key`
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith associated to a column with that `name`.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `{ username: "desc" }` - sort by `username` in descending order.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Alternately, use values "asc", 1 (same as "asc"), or -1 (same as "desc").
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `["lastName", "firstName"]` - ascending sort by `lastName`, but for
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith records with the same `lastName`, ascending subsort by `firstName`.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Array can have as many items as you want.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `[{ lastName: -1 }, "firstName"]` - descending sort by `lastName`,
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith ascending subsort by `firstName`. Mixed types are ok.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @attribute sortBy
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @type {String|String[]|Object|Object[]}
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Strings containing language for sorting tooltips.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @attribute strings
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @type {Object}
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @default (strings for current lang configured in the YUI instance config)
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Sort the data in the `data` ModelList and refresh the table with the new
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Acceptable values for `fields` are `key` strings or objects with a single
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith property, the column `key`, with a value of 1, -1, "asc", or "desc". E.g.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith `{ username: 'asc' }`. String values are assumed to be ascending.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Example values would be:
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `"username"` - sort by the data's `username` field or the `key`
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith associated to a column with that `name`.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `{ username: "desc" }` - sort by `username` in descending order.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Alternately, use values "asc", 1 (same as "asc"), or -1 (same as "desc").
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `["lastName", "firstName"]` - ascending sort by `lastName`, but for
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith records with the same `lastName`, ascending subsort by `firstName`.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Array can have as many items as you want.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith * `[{ lastName: -1 }, "firstName"]` - descending sort by `lastName`,
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith ascending subsort by `firstName`. Mixed types are ok.
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @method sort
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @param {String|String[]|Object|Object[]} fields The field(s) to sort by
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @param {Object} [payload] Extra `sort` event payload you want to send along
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @return {DataTable}
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith return this.fire('sort', Y.merge((payload || {}), {
6fe31db087e6c93b1df3a96b888b55344e039a23Luke Smith Template for the node that will wrap the header content for sortable
2b9aa50d4077b548fa76a5d5330265ab397caad3Luke Smith @property SORTABLE_HEADER_TEMPLATE
6fe31db087e6c93b1df3a96b888b55344e039a23Luke Smith @type {HTML}
2b9aa50d4077b548fa76a5d5330265ab397caad3Luke Smith @value '<div class="{className}" tabindex="0"><span class="{indicatorClass}"></span></div>'
2b9aa50d4077b548fa76a5d5330265ab397caad3Luke Smith SORTABLE_HEADER_TEMPLATE: '<div class="{className}" tabindex="0"><span class="{indicatorClass}"></span></div>',
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith Reverse the current sort direction of one or more fields currently being
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Pass the `key` of the column or columns you want the sort order reversed
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @method toggleSort
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @param {String|String[]} fields The field(s) to reverse sort order for
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @param {Object} [payload] Extra `sort` event payload you want to send along
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith @return {DataTable}
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // To avoid updating column configs or sortBy directly
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith for (i = 0, len = current.length; i < len; ++i) {
8fff801a4a38e13d6ff8181ff097fb01d058d617Luke Smith for (i = 0, len = columns.length; i < len; ++i) {
59c94cbcd6e39e484a57bf3665a8ab98beafc57eLuke Smith return this.fire('sort', Y.merge((payload || {}), {
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith //--------------------------------------------------------------------------
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // Protected properties and methods
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith //--------------------------------------------------------------------------
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith Sorts the `data` ModelList based on the new `sortBy` configuration.
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith @method _afterSortByChange
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith @param {EventFacade} e The `sortByChange` event
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith _afterSortByChange: function (e) {
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith // Can't use a setter because it's a chicken and egg problem. The
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith // columns need to be set up to translate, but columns are initialized
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith // from Core's initializer. So construction-time assignment would
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith // Don't sort unless sortBy has been set
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Applies the sorting logic to the new ModelList if the `newVal` is a new
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith @method _afterSortDataChange
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @param {EventFacade} e the `dataChange` event
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // object values always trigger a change event, but we only want to
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // call _initSortFn if the value passed to the `data` attribute was a
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // new ModelList, not a set of new data as an array, or even the same
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // ModelList.
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith if (e.prevVal !== e.newVal || e.newVal.hasOwnProperty('_compare')) {
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith Checks if any of the fields in the modified record are fields that are
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith currently being sorted by, and if so, resorts the `data` ModelList.
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith @method _afterSortRecordChange
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith @param {EventFacade} e The Model's `change` event
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith for (i = 0, len = this._sortBy.length; i < len; ++i) {
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Subscribes to state changes that warrant updating the UI, and adds the
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith click handler for triggering the sort operation from the UI.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _bindSortUI
8fff801a4a38e13d6ff8181ff097fb01d058d617Luke Smith _bindSortUI: function () {
8fff801a4a38e13d6ff8181ff097fb01d058d617Luke Smith this.after(['sortableChange', 'sortByChange', 'columnsChange'],
7517febf3436a32039d125a66566b1c7be2f6b26Luke Smith this._sortHandle = this.delegate(['click','keypress'],
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Sets the `sortBy` attribute from the `sort` event's `e.sortBy` value.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _defSortFn
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @param {EventFacade} e The `sort` event
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith _defSortFn: function (e) {
8fff801a4a38e13d6ff8181ff097fb01d058d617Luke Smith this.set.apply(this, ['sortBy', e.sortBy].concat(e.details));
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Removes the click subscription from the header for sorting.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method destructor
8fff801a4a38e13d6ff8181ff097fb01d058d617Luke Smith destructor: function () {
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Getter for the `sortBy` attribute.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Supports the special subattribute "sortBy.state" to get a normalized JSON
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith version of the current sort state. Otherwise, returns the last assigned
55e462437cab83f4f545bfd864f3296cd2a0e806Luke Smith For example:
55e462437cab83f4f545bfd864f3296cd2a0e806Luke Smith <pre><code>var table = new Y.DataTable({
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith columns: [ ... ],
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith data: [ ... ],
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith sortBy: 'username'
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith table.get('sortBy'); // 'username'
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith table.get('sortBy.state'); // { key: 'username', dir: 1 }
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith table.sort(['lastName', { firstName: "desc" }]);
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith table.get('sortBy'); // ['lastName', { firstName: "desc" }]
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith table.get('sortBy.state'); // [{ key: "lastName", dir: 1 }, { key: "firstName", dir: -1 }]
55e462437cab83f4f545bfd864f3296cd2a0e806Luke Smith </code></pre>
55e462437cab83f4f545bfd864f3296cd2a0e806Luke Smith @method _getSortBy
55e462437cab83f4f545bfd864f3296cd2a0e806Luke Smith @param {String|String[]|Object|Object[]} val The current sortBy value
55e462437cab83f4f545bfd864f3296cd2a0e806Luke Smith @param {String} detail String passed to `get(HERE)`. to parse subattributes
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // "sortBy." is 7 characters. Used to catch
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // TODO: table.get('sortBy.asObject')? table.get('sortBy.json')?
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith for (i = 0, len = this._sortBy.length; i < len; ++i) {
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // TODO: Always return an array?
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith return { state: (state.length === 1) ? state[0] : state };
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Sets up the initial sort state and instance properties. Publishes events
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith and subscribes to attribute change events to maintain internal state.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method initializer
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith initializer: function () {
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith var boundParseSortable = Y.bind('_parseSortable', this);
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith dataChange : Y.bind('_afterSortDataChange', this),
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith sortByChange : Y.bind('_afterSortByChange', this),
771b101c1b151cb6c431aceafc4c95cdd8d2a4e1Luke Smith "*:change" : Y.bind('_afterSortRecordChange', this)
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Creates a `_compare` function for the `data` ModelList to allow custom
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith sorting by multiple fields.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _initSortFn
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith _initSortFn: function () {
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // TODO: This should be a ModelList extension.
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // FIXME: Modifying a component of the host seems a little smelly
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // FIXME: Declaring inline override to leverage closure vs
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // compiling a new function for each column/sortable change or
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // binding the _compare implementation to this, resulting in an
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // extra function hop during sorting. Lesser of three evils?
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith for (i = 0, len = self._sortBy.length; !cmp && i < len; ++i) {
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // FIXME? Requires columns without sortFns to have key
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // TODO: is this necessary? Should it be elsewhere?
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // Leave the _compare method in place to avoid having to set it
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // up again. Mistake?
6fe31db087e6c93b1df3a96b888b55344e039a23Luke Smith Add the sort related strings to the `strings` map.
6fe31db087e6c93b1df3a96b888b55344e039a23Luke Smith @method _initSortStrings
6fe31db087e6c93b1df3a96b888b55344e039a23Luke Smith // Not a valueFn because other class extensions will want to add to it
6fe31db087e6c93b1df3a96b888b55344e039a23Luke Smith this.set('strings', Y.mix((this.get('strings') || {}),
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Fires the `sort` event in response to user clicks on sortable column
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _onUITriggerSort
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @param {DOMEventFacade} e The `click` event
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith _onUITriggerSort: function (e) {
8a6a44314c3b8b5125191793d3f3e0baa8d31b7cLuke Smith var id = e.currentTarget.getAttribute('data-yui3-col-id'),
7517febf3436a32039d125a66566b1c7be2f6b26Luke Smith // In case a headerTemplate injected a link
7517febf3436a32039d125a66566b1c7be2f6b26Luke Smith // TODO: Is this overreaching?
7517febf3436a32039d125a66566b1c7be2f6b26Luke Smith if (id === sortBy[i] || Math.abs(sortBy[i][id] === 1)) {
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Normalizes the possible input values for the `sortable` attribute, storing
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith the results in the `_sortable` property.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _parseSortable
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith _parseSortable: function () {
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith for (i = 0, len = sortable.length; i < len; ++i) {
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // isArray is called because arrays are objects, but will rely
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // on getColumn to nullify them for the subsequent if (col)
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith } else if (sortable) {
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Initial application of the sortable UI.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _renderSortable
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Parses the current `sortBy` attribute into a normalized structure for the
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith `data` ModelList's `_compare` method. Also updates the column
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith configurations' `sortDir` properties.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _setSortBy
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith _setSortBy: function () {
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // Purge current sort state from column configs
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith for (i = 0, len = columns.length; i < len; ++i) {
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // TODO: be more thorough
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith column.className = column.className.replace(sortedClass, '');
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // Have to use a for-in loop to process sort({ foo: -1 })
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // Allow sorting of any model field and any column
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // FIXME: this isn't limited to model attributes, but there's no
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // convenient way to get a list of the attributes for a Model
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // subclass *including* the attributes of its superclasses.
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith column = this.getColumn(name) || { _id: name, key: name };
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Array of column configuration objects of those columns that need UI setup
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith for user interaction.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @property _sortable
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @type {Object[]}
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith //_sortable: null,
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Array of column configuration objects for those columns that are currently
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith being used to sort the data. Fake column objects are used for fields that
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith are not rendered as columns.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @property _sortBy
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @type {Object[]}
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith //_sortBy: null,
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Replacement `comparator` for the `data` ModelList that defers sorting logic
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith to the `_compare` method. The deferral is accomplished by returning `this`.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _sortComparator
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @param {Model} item The record being evaluated for sort position
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @return {Model} The record
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith // Defer sorting to ModelList's _compare
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Applies the appropriate classes to the `boundingBox` and column headers to
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith indicate sort state and sortability.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Also currently wraps the header content of sortable columns in a `<div>`
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith liner to give a CSS anchor for sort indicators.
55e462437cab83f4f545bfd864f3296cd2a0e806Luke Smith @method _uiSetSortable
4eafc0c9b36f7e8efc8808ab27dbed7ff8dc87abLuke Smith _uiSetSortable: function () {
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith sortableClass = this.getClassName('sortable', 'column'),
7a40ce4f121af8aa648db1d17f5b09be0b55df4bLuke Smith indicatorClass= this.getClassName('sort', 'indicator'),
7517febf3436a32039d125a66566b1c7be2f6b26Luke Smith for (i = 0, len = columns.length; i < len; ++i) {
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith // TODO: this.head.render() + decorate cells?
7517febf3436a32039d125a66566b1c7be2f6b26Luke Smith this._theadNode.all('.' + sortableClass).each(function (node) {
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith for (i = 0, len = columns.length; i < len; ++i) {
6fe31db087e6c93b1df3a96b888b55344e039a23Luke Smith (col.sortDir === 1) ? 'reverseSortBy' : 'sortBy'), {
7517febf3436a32039d125a66566b1c7be2f6b26Luke Smith // To combat VoiceOver from reading the sort title as the
7517febf3436a32039d125a66566b1c7be2f6b26Luke Smith // column header
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Allows values `true`, `false`, "auto", or arrays of column names through.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _validateSortable
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @param {Any} val The input value to `set("sortable", VAL)`
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @return {Boolean}
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith return val === 'auto' || isBoolean(val) || isArray(val);
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith Allows strings, arrays of strings, objects, or arrays of objects.
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @method _validateSortBy
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @param {String|String[]|Object|Object[]} val The new `sortBy` value
f5443cec5378e1eba162c289f05b690b699c9c7bLuke Smith @return {Boolean}
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith return val === null ||
32624ce45f113c04dfa5469b3cc77df42c4c46f4Luke Smith (isArray(val) && (isString(val[0]) || isObject(val, true)));