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