[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('datatable-sort', function (Y, NAME) { 9 10 /** 11 Adds support for sorting the table data by API methods `table.sort(...)` or 12 `table.toggleSort(...)` or by clicking on column headers in the rendered UI. 13 14 @module datatable 15 @submodule datatable-sort 16 @since 3.5.0 17 **/ 18 var YLang = Y.Lang, 19 isBoolean = YLang.isBoolean, 20 isString = YLang.isString, 21 isArray = YLang.isArray, 22 isObject = YLang.isObject, 23 24 toArray = Y.Array, 25 sub = YLang.sub, 26 27 dirMap = { 28 asc : 1, 29 desc: -1, 30 "1" : 1, 31 "-1": -1 32 }; 33 34 35 /** 36 _API docs for this extension are included in the DataTable class._ 37 38 This DataTable class extension adds support for sorting the table data by API 39 methods `table.sort(...)` or `table.toggleSort(...)` or by clicking on column 40 headers in the rendered UI. 41 42 Sorting by the API is enabled automatically when this module is `use()`d. To 43 enable UI triggered sorting, set the DataTable's `sortable` attribute to 44 `true`. 45 46 <pre><code> 47 var table = new Y.DataTable({ 48 columns: [ 'id', 'username', 'name', 'birthdate' ], 49 data: [ ... ], 50 sortable: true 51 }); 52 53 table.render('#table'); 54 </code></pre> 55 56 Setting `sortable` to `true` will enable UI sorting for all columns. To enable 57 UI sorting for certain columns only, set `sortable` to an array of column keys, 58 or just add `sortable: true` to the respective column configuration objects. 59 This uses the default setting of `sortable: auto` for the DataTable instance. 60 61 <pre><code> 62 var table = new Y.DataTable({ 63 columns: [ 64 'id', 65 { key: 'username', sortable: true }, 66 { key: 'name', sortable: true }, 67 { key: 'birthdate', sortable: true } 68 ], 69 data: [ ... ] 70 // sortable: 'auto' is the default 71 }); 72 73 // OR 74 var table = new Y.DataTable({ 75 columns: [ 'id', 'username', 'name', 'birthdate' ], 76 data: [ ... ], 77 sortable: [ 'username', 'name', 'birthdate' ] 78 }); 79 </code></pre> 80 81 To disable UI sorting for all columns, set `sortable` to `false`. This still 82 permits sorting via the API methods. 83 84 As new records are inserted into the table's `data` ModelList, they will be inserted at the correct index to preserve the sort order. 85 86 The current sort order is stored in the `sortBy` attribute. Assigning this value at instantiation will automatically sort your data. 87 88 Sorting is done by a simple value comparison using < and > on the field 89 value. If you need custom sorting, add a sort function in the column's 90 `sortFn` property. Columns whose content is generated by formatters, but don't 91 relate to a single `key`, require a `sortFn` to be sortable. 92 93 <pre><code> 94 function nameSort(a, b, desc) { 95 var aa = a.get('lastName') + a.get('firstName'), 96 bb = a.get('lastName') + b.get('firstName'), 97 order = (aa > bb) ? 1 : -(aa < bb); 98 99 return desc ? -order : order; 100 } 101 102 var table = new Y.DataTable({ 103 columns: [ 'id', 'username', { key: name, sortFn: nameSort }, 'birthdate' ], 104 data: [ ... ], 105 sortable: [ 'username', 'name', 'birthdate' ] 106 }); 107 </code></pre> 108 109 See the user guide for more details. 110 111 @class DataTable.Sortable 112 @for DataTable 113 @since 3.5.0 114 **/ 115 function Sortable() {} 116 117 Sortable.ATTRS = { 118 // Which columns in the UI should suggest and respond to sorting interaction 119 // pass an empty array if no UI columns should show sortable, but you want the 120 // table.sort(...) API 121 /** 122 Controls which column headers can trigger sorting by user clicks. 123 124 Acceptable values are: 125 126 * "auto" - (default) looks for `sortable: true` in the column configurations 127 * `true` - all columns are enabled 128 * `false - no UI sortable is enabled 129 * {String[]} - array of key names to give sortable headers 130 131 @attribute sortable 132 @type {String|String[]|Boolean} 133 @default "auto" 134 @since 3.5.0 135 **/ 136 sortable: { 137 value: 'auto', 138 validator: '_validateSortable' 139 }, 140 141 /** 142 The current sort configuration to maintain in the data. 143 144 Accepts column `key` strings or objects with a single property, the column 145 `key`, with a value of 1, -1, "asc", or "desc". E.g. `{ username: 'asc' 146 }`. String values are assumed to be ascending. 147 148 Example values would be: 149 150 * `"username"` - sort by the data's `username` field or the `key` 151 associated to a column with that `name`. 152 * `{ username: "desc" }` - sort by `username` in descending order. 153 Alternately, use values "asc", 1 (same as "asc"), or -1 (same as "desc"). 154 * `["lastName", "firstName"]` - ascending sort by `lastName`, but for 155 records with the same `lastName`, ascending subsort by `firstName`. 156 Array can have as many items as you want. 157 * `[{ lastName: -1 }, "firstName"]` - descending sort by `lastName`, 158 ascending subsort by `firstName`. Mixed types are ok. 159 160 @attribute sortBy 161 @type {String|String[]|Object|Object[]} 162 @since 3.5.0 163 **/ 164 sortBy: { 165 validator: '_validateSortBy', 166 getter: '_getSortBy' 167 }, 168 169 /** 170 Strings containing language for sorting tooltips. 171 172 @attribute strings 173 @type {Object} 174 @default (strings for current lang configured in the YUI instance config) 175 @since 3.5.0 176 **/ 177 strings: {} 178 }; 179 180 Y.mix(Sortable.prototype, { 181 182 /** 183 Sort the data in the `data` ModelList and refresh the table with the new 184 order. 185 186 Acceptable values for `fields` are `key` strings or objects with a single 187 property, the column `key`, with a value of 1, -1, "asc", or "desc". E.g. 188 `{ username: 'asc' }`. String values are assumed to be ascending. 189 190 Example values would be: 191 192 * `"username"` - sort by the data's `username` field or the `key` 193 associated to a column with that `name`. 194 * `{ username: "desc" }` - sort by `username` in descending order. 195 Alternately, use values "asc", 1 (same as "asc"), or -1 (same as "desc"). 196 * `["lastName", "firstName"]` - ascending sort by `lastName`, but for 197 records with the same `lastName`, ascending subsort by `firstName`. 198 Array can have as many items as you want. 199 * `[{ lastName: -1 }, "firstName"]` - descending sort by `lastName`, 200 ascending subsort by `firstName`. Mixed types are ok. 201 202 @method sort 203 @param {String|String[]|Object|Object[]} fields The field(s) to sort by 204 @param {Object} [payload] Extra `sort` event payload you want to send along 205 @return {DataTable} 206 @chainable 207 @since 3.5.0 208 **/ 209 sort: function (fields, payload) { 210 /** 211 Notifies of an impending sort, either from clicking on a column 212 header, or from a call to the `sort` or `toggleSort` method. 213 214 The requested sort is available in the `sortBy` property of the event. 215 216 The default behavior of this event sets the table's `sortBy` attribute. 217 218 @event sort 219 @param {String|String[]|Object|Object[]} sortBy The requested sort 220 @preventable _defSortFn 221 **/ 222 return this.fire('sort', Y.merge((payload || {}), { 223 sortBy: fields || this.get('sortBy') 224 })); 225 }, 226 227 /** 228 Template for the node that will wrap the header content for sortable 229 columns. 230 231 @property SORTABLE_HEADER_TEMPLATE 232 @type {String} 233 @value '<div class="{className}" tabindex="0"><span class="{indicatorClass}"></span></div>' 234 @since 3.5.0 235 **/ 236 SORTABLE_HEADER_TEMPLATE: '<div class="{className}" tabindex="0" unselectable="on"><span class="{indicatorClass}"></span></div>', 237 238 /** 239 Reverse the current sort direction of one or more fields currently being 240 sorted by. 241 242 Pass the `key` of the column or columns you want the sort order reversed 243 for. 244 245 @method toggleSort 246 @param {String|String[]} fields The field(s) to reverse sort order for 247 @param {Object} [payload] Extra `sort` event payload you want to send along 248 @return {DataTable} 249 @chainable 250 @since 3.5.0 251 **/ 252 toggleSort: function (columns, payload) { 253 var current = this._sortBy, 254 sortBy = [], 255 i, len, j, col, index; 256 257 // To avoid updating column configs or sortBy directly 258 for (i = 0, len = current.length; i < len; ++i) { 259 col = {}; 260 col[current[i]._id] = current[i].sortDir; 261 sortBy.push(col); 262 } 263 264 if (columns) { 265 columns = toArray(columns); 266 267 for (i = 0, len = columns.length; i < len; ++i) { 268 col = columns[i]; 269 index = -1; 270 271 for (j = sortBy.length - 1; i >= 0; --i) { 272 if (sortBy[j][col]) { 273 sortBy[j][col] *= -1; 274 break; 275 } 276 } 277 } 278 } else { 279 for (i = 0, len = sortBy.length; i < len; ++i) { 280 for (col in sortBy[i]) { 281 if (sortBy[i].hasOwnProperty(col)) { 282 sortBy[i][col] *= -1; 283 break; 284 } 285 } 286 } 287 } 288 289 return this.fire('sort', Y.merge((payload || {}), { 290 sortBy: sortBy 291 })); 292 }, 293 294 //-------------------------------------------------------------------------- 295 // Protected properties and methods 296 //-------------------------------------------------------------------------- 297 /** 298 Sorts the `data` ModelList based on the new `sortBy` configuration. 299 300 @method _afterSortByChange 301 @param {EventFacade} e The `sortByChange` event 302 @protected 303 @since 3.5.0 304 **/ 305 _afterSortByChange: function () { 306 // Can't use a setter because it's a chicken and egg problem. The 307 // columns need to be set up to translate, but columns are initialized 308 // from Core's initializer. So construction-time assignment would 309 // fail. 310 this._setSortBy(); 311 312 // Don't sort unless sortBy has been set 313 if (this._sortBy.length) { 314 if (!this.data.comparator) { 315 this.data.comparator = this._sortComparator; 316 } 317 318 this.data.sort(); 319 } 320 }, 321 322 /** 323 Applies the sorting logic to the new ModelList if the `newVal` is a new 324 ModelList. 325 326 @method _afterSortDataChange 327 @param {EventFacade} e the `dataChange` event 328 @protected 329 @since 3.5.0 330 **/ 331 _afterSortDataChange: function (e) { 332 // object values always trigger a change event, but we only want to 333 // call _initSortFn if the value passed to the `data` attribute was a 334 // new ModelList, not a set of new data as an array, or even the same 335 // ModelList. 336 if (e.prevVal !== e.newVal || e.newVal.hasOwnProperty('_compare')) { 337 this._initSortFn(); 338 } 339 }, 340 341 /** 342 Checks if any of the fields in the modified record are fields that are 343 currently being sorted by, and if so, resorts the `data` ModelList. 344 345 @method _afterSortRecordChange 346 @param {EventFacade} e The Model's `change` event 347 @protected 348 @since 3.5.0 349 **/ 350 _afterSortRecordChange: function (e) { 351 var i, len; 352 353 for (i = 0, len = this._sortBy.length; i < len; ++i) { 354 if (e.changed[this._sortBy[i].key]) { 355 this.data.sort(); 356 break; 357 } 358 } 359 }, 360 361 /** 362 Subscribes to state changes that warrant updating the UI, and adds the 363 click handler for triggering the sort operation from the UI. 364 365 @method _bindSortUI 366 @protected 367 @since 3.5.0 368 **/ 369 _bindSortUI: function () { 370 var handles = this._eventHandles; 371 372 if (!handles.sortAttrs) { 373 handles.sortAttrs = this.after( 374 ['sortableChange', 'sortByChange', 'columnsChange'], 375 Y.bind('_uiSetSortable', this)); 376 } 377 378 if (!handles.sortUITrigger && this._theadNode) { 379 handles.sortUITrigger = this.delegate(['click','keydown'], 380 Y.rbind('_onUITriggerSort', this), 381 '.' + this.getClassName('sortable', 'column')); 382 } 383 }, 384 385 /** 386 Sets the `sortBy` attribute from the `sort` event's `e.sortBy` value. 387 388 @method _defSortFn 389 @param {EventFacade} e The `sort` event 390 @protected 391 @since 3.5.0 392 **/ 393 _defSortFn: function (e) { 394 this.set.apply(this, ['sortBy', e.sortBy].concat(e.details)); 395 }, 396 397 /** 398 Getter for the `sortBy` attribute. 399 400 Supports the special subattribute "sortBy.state" to get a normalized JSON 401 version of the current sort state. Otherwise, returns the last assigned 402 value. 403 404 For example: 405 406 <pre><code>var table = new Y.DataTable({ 407 columns: [ ... ], 408 data: [ ... ], 409 sortBy: 'username' 410 }); 411 412 table.get('sortBy'); // 'username' 413 table.get('sortBy.state'); // { key: 'username', dir: 1 } 414 415 table.sort(['lastName', { firstName: "desc" }]); 416 table.get('sortBy'); // ['lastName', { firstName: "desc" }] 417 table.get('sortBy.state'); // [{ key: "lastName", dir: 1 }, { key: "firstName", dir: -1 }] 418 </code></pre> 419 420 @method _getSortBy 421 @param {String|String[]|Object|Object[]} val The current sortBy value 422 @param {String} detail String passed to `get(HERE)`. to parse subattributes 423 @protected 424 @since 3.5.0 425 **/ 426 _getSortBy: function (val, detail) { 427 var state, i, len, col; 428 429 // "sortBy." is 7 characters. Used to catch 430 detail = detail.slice(7); 431 432 // TODO: table.get('sortBy.asObject')? table.get('sortBy.json')? 433 if (detail === 'state') { 434 state = []; 435 436 for (i = 0, len = this._sortBy.length; i < len; ++i) { 437 col = this._sortBy[i]; 438 state.push({ 439 column: col._id, 440 dir: col.sortDir 441 }); 442 } 443 444 // TODO: Always return an array? 445 return { state: (state.length === 1) ? state[0] : state }; 446 } else { 447 return val; 448 } 449 }, 450 451 /** 452 Sets up the initial sort state and instance properties. Publishes events 453 and subscribes to attribute change events to maintain internal state. 454 455 @method initializer 456 @protected 457 @since 3.5.0 458 **/ 459 initializer: function () { 460 var boundParseSortable = Y.bind('_parseSortable', this); 461 462 this._parseSortable(); 463 464 this._setSortBy(); 465 466 this._initSortFn(); 467 468 this._initSortStrings(); 469 470 this.after({ 471 'table:renderHeader': Y.bind('_renderSortable', this), 472 dataChange : Y.bind('_afterSortDataChange', this), 473 sortByChange : Y.bind('_afterSortByChange', this), 474 sortableChange : boundParseSortable, 475 columnsChange : boundParseSortable 476 }); 477 this.data.after(this.data.model.NAME + ":change", 478 Y.bind('_afterSortRecordChange', this)); 479 480 // TODO: this event needs magic, allowing async remote sorting 481 this.publish('sort', { 482 defaultFn: Y.bind('_defSortFn', this) 483 }); 484 }, 485 486 /** 487 Creates a `_compare` function for the `data` ModelList to allow custom 488 sorting by multiple fields. 489 490 @method _initSortFn 491 @protected 492 @since 3.5.0 493 **/ 494 _initSortFn: function () { 495 var self = this; 496 497 // TODO: This should be a ModelList extension. 498 // FIXME: Modifying a component of the host seems a little smelly 499 // FIXME: Declaring inline override to leverage closure vs 500 // compiling a new function for each column/sortable change or 501 // binding the _compare implementation to this, resulting in an 502 // extra function hop during sorting. Lesser of three evils? 503 this.data._compare = function (a, b) { 504 var cmp = 0, 505 i, len, col, dir, cs, aa, bb; 506 507 for (i = 0, len = self._sortBy.length; !cmp && i < len; ++i) { 508 col = self._sortBy[i]; 509 dir = col.sortDir, 510 cs = col.caseSensitive; 511 512 if (col.sortFn) { 513 cmp = col.sortFn(a, b, (dir === -1)); 514 } else { 515 // FIXME? Requires columns without sortFns to have key 516 aa = a.get(col.key) || ''; 517 bb = b.get(col.key) || ''; 518 if (!cs && typeof(aa) === "string" && typeof(bb) === "string"){// Not case sensitive 519 aa = aa.toLowerCase(); 520 bb = bb.toLowerCase(); 521 } 522 cmp = (aa > bb) ? dir : ((aa < bb) ? -dir : 0); 523 } 524 } 525 526 return cmp; 527 }; 528 529 if (this._sortBy.length) { 530 this.data.comparator = this._sortComparator; 531 532 // TODO: is this necessary? Should it be elsewhere? 533 this.data.sort(); 534 } else { 535 // Leave the _compare method in place to avoid having to set it 536 // up again. Mistake? 537 delete this.data.comparator; 538 } 539 }, 540 541 /** 542 Add the sort related strings to the `strings` map. 543 544 @method _initSortStrings 545 @protected 546 @since 3.5.0 547 **/ 548 _initSortStrings: function () { 549 // Not a valueFn because other class extensions will want to add to it 550 this.set('strings', Y.mix((this.get('strings') || {}), 551 Y.Intl.get('datatable-sort'))); 552 }, 553 554 /** 555 Fires the `sort` event in response to user clicks on sortable column 556 headers. 557 558 @method _onUITriggerSort 559 @param {DOMEventFacade} e The `click` event 560 @protected 561 @since 3.5.0 562 **/ 563 _onUITriggerSort: function (e) { 564 var id = e.currentTarget.getAttribute('data-yui3-col-id'), 565 column = id && this.getColumn(id), 566 sortBy, i, len; 567 568 if (e.type === 'keydown' && e.keyCode !== 32) { 569 return; 570 } 571 572 // In case a headerTemplate injected a link 573 // TODO: Is this overreaching? 574 e.preventDefault(); 575 576 if (column) { 577 if (e.shiftKey) { 578 sortBy = this.get('sortBy') || []; 579 580 for (i = 0, len = sortBy.length; i < len; ++i) { 581 if (id === sortBy[i] || Math.abs(sortBy[i][id]) === 1) { 582 if (!isObject(sortBy[i])) { 583 sortBy[i] = {}; 584 } 585 586 sortBy[i][id] = -(column.sortDir||0) || 1; 587 break; 588 } 589 } 590 591 if (i >= len) { 592 sortBy.push(column._id); 593 } 594 } else { 595 sortBy = [{}]; 596 597 sortBy[0][id] = -(column.sortDir||0) || 1; 598 } 599 600 this.fire('sort', { 601 originEvent: e, 602 sortBy: sortBy 603 }); 604 } 605 }, 606 607 /** 608 Normalizes the possible input values for the `sortable` attribute, storing 609 the results in the `_sortable` property. 610 611 @method _parseSortable 612 @protected 613 @since 3.5.0 614 **/ 615 _parseSortable: function () { 616 var sortable = this.get('sortable'), 617 columns = [], 618 i, len, col; 619 620 if (isArray(sortable)) { 621 for (i = 0, len = sortable.length; i < len; ++i) { 622 col = sortable[i]; 623 624 // isArray is called because arrays are objects, but will rely 625 // on getColumn to nullify them for the subsequent if (col) 626 if (!isObject(col, true) || isArray(col)) { 627 col = this.getColumn(col); 628 } 629 630 if (col) { 631 columns.push(col); 632 } 633 } 634 } else if (sortable) { 635 columns = this._displayColumns.slice(); 636 637 if (sortable === 'auto') { 638 for (i = columns.length - 1; i >= 0; --i) { 639 if (!columns[i].sortable) { 640 columns.splice(i, 1); 641 } 642 } 643 } 644 } 645 646 this._sortable = columns; 647 }, 648 649 /** 650 Initial application of the sortable UI. 651 652 @method _renderSortable 653 @protected 654 @since 3.5.0 655 **/ 656 _renderSortable: function () { 657 this._uiSetSortable(); 658 659 this._bindSortUI(); 660 }, 661 662 /** 663 Parses the current `sortBy` attribute into a normalized structure for the 664 `data` ModelList's `_compare` method. Also updates the column 665 configurations' `sortDir` properties. 666 667 @method _setSortBy 668 @protected 669 @since 3.5.0 670 **/ 671 _setSortBy: function () { 672 var columns = this._displayColumns, 673 sortBy = this.get('sortBy') || [], 674 sortedClass = ' ' + this.getClassName('sorted'), 675 i, len, name, dir, field, column; 676 677 this._sortBy = []; 678 679 // Purge current sort state from column configs 680 for (i = 0, len = columns.length; i < len; ++i) { 681 column = columns[i]; 682 683 delete column.sortDir; 684 685 if (column.className) { 686 // TODO: be more thorough 687 column.className = column.className.replace(sortedClass, ''); 688 } 689 } 690 691 sortBy = toArray(sortBy); 692 693 for (i = 0, len = sortBy.length; i < len; ++i) { 694 name = sortBy[i]; 695 dir = 1; 696 697 if (isObject(name)) { 698 field = name; 699 // Have to use a for-in loop to process sort({ foo: -1 }) 700 for (name in field) { 701 if (field.hasOwnProperty(name)) { 702 dir = dirMap[field[name]]; 703 break; 704 } 705 } 706 } 707 708 if (name) { 709 // Allow sorting of any model field and any column 710 // FIXME: this isn't limited to model attributes, but there's no 711 // convenient way to get a list of the attributes for a Model 712 // subclass *including* the attributes of its superclasses. 713 column = this.getColumn(name) || { _id: name, key: name }; 714 715 if (column) { 716 column.sortDir = dir; 717 718 if (!column.className) { 719 column.className = ''; 720 } 721 722 column.className += sortedClass; 723 724 this._sortBy.push(column); 725 } 726 } 727 } 728 }, 729 730 /** 731 Array of column configuration objects of those columns that need UI setup 732 for user interaction. 733 734 @property _sortable 735 @type {Object[]} 736 @protected 737 @since 3.5.0 738 **/ 739 //_sortable: null, 740 741 /** 742 Array of column configuration objects for those columns that are currently 743 being used to sort the data. Fake column objects are used for fields that 744 are not rendered as columns. 745 746 @property _sortBy 747 @type {Object[]} 748 @protected 749 @since 3.5.0 750 **/ 751 //_sortBy: null, 752 753 /** 754 Replacement `comparator` for the `data` ModelList that defers sorting logic 755 to the `_compare` method. The deferral is accomplished by returning `this`. 756 757 @method _sortComparator 758 @param {Model} item The record being evaluated for sort position 759 @return {Model} The record 760 @protected 761 @since 3.5.0 762 **/ 763 _sortComparator: function (item) { 764 // Defer sorting to ModelList's _compare 765 return item; 766 }, 767 768 /** 769 Applies the appropriate classes to the `boundingBox` and column headers to 770 indicate sort state and sortability. 771 772 Also currently wraps the header content of sortable columns in a `<div>` 773 liner to give a CSS anchor for sort indicators. 774 775 @method _uiSetSortable 776 @protected 777 @since 3.5.0 778 **/ 779 _uiSetSortable: function () { 780 var columns = this._sortable || [], 781 sortableClass = this.getClassName('sortable', 'column'), 782 ascClass = this.getClassName('sorted'), 783 descClass = this.getClassName('sorted', 'desc'), 784 linerClass = this.getClassName('sort', 'liner'), 785 indicatorClass= this.getClassName('sort', 'indicator'), 786 sortableCols = {}, 787 i, len, col, node, liner, title, desc; 788 789 this.get('boundingBox').toggleClass( 790 this.getClassName('sortable'), 791 columns.length); 792 793 for (i = 0, len = columns.length; i < len; ++i) { 794 sortableCols[columns[i].id] = columns[i]; 795 } 796 797 // TODO: this.head.render() + decorate cells? 798 this._theadNode.all('.' + sortableClass).each(function (node) { 799 var col = sortableCols[node.get('id')], 800 liner = node.one('.' + linerClass), 801 indicator; 802 803 if (col) { 804 if (!col.sortDir) { 805 node.removeClass(ascClass) 806 .removeClass(descClass); 807 } 808 } else { 809 node.removeClass(sortableClass) 810 .removeClass(ascClass) 811 .removeClass(descClass); 812 813 if (liner) { 814 liner.replace(liner.get('childNodes').toFrag()); 815 } 816 817 indicator = node.one('.' + indicatorClass); 818 819 if (indicator) { 820 indicator.remove().destroy(true); 821 } 822 } 823 }); 824 825 for (i = 0, len = columns.length; i < len; ++i) { 826 col = columns[i]; 827 node = this._theadNode.one('#' + col.id); 828 desc = col.sortDir === -1; 829 830 if (node) { 831 liner = node.one('.' + linerClass); 832 833 node.addClass(sortableClass); 834 835 if (col.sortDir) { 836 node.addClass(ascClass); 837 838 node.toggleClass(descClass, desc); 839 840 node.setAttribute('aria-sort', desc ? 841 'descending' : 'ascending'); 842 } 843 844 if (!liner) { 845 liner = Y.Node.create(Y.Lang.sub( 846 this.SORTABLE_HEADER_TEMPLATE, { 847 className: linerClass, 848 indicatorClass: indicatorClass 849 })); 850 851 liner.prepend(node.get('childNodes').toFrag()); 852 853 node.append(liner); 854 } 855 856 title = sub(this.getString( 857 (col.sortDir === 1) ? 'reverseSortBy' : 'sortBy'), // get string 858 { 859 title: col.title || '', 860 key: col.key || '', 861 abbr: col.abbr || '', 862 label: col.label || '', 863 column: col.abbr || col.label || 864 col.key || ('column ' + i) 865 } 866 ); 867 868 node.setAttribute('title', title); 869 // To combat VoiceOver from reading the sort title as the 870 // column header 871 node.setAttribute('aria-labelledby', col.id); 872 } 873 } 874 }, 875 876 /** 877 Allows values `true`, `false`, "auto", or arrays of column names through. 878 879 @method _validateSortable 880 @param {Any} val The input value to `set("sortable", VAL)` 881 @return {Boolean} 882 @protected 883 @since 3.5.0 884 **/ 885 _validateSortable: function (val) { 886 return val === 'auto' || isBoolean(val) || isArray(val); 887 }, 888 889 /** 890 Allows strings, arrays of strings, objects, or arrays of objects. 891 892 @method _validateSortBy 893 @param {String|String[]|Object|Object[]} val The new `sortBy` value 894 @return {Boolean} 895 @protected 896 @since 3.5.0 897 **/ 898 _validateSortBy: function (val) { 899 return val === null || 900 isString(val) || 901 isObject(val, true) || 902 (isArray(val) && (isString(val[0]) || isObject(val, true))); 903 } 904 905 }, true); 906 907 Y.DataTable.Sortable = Sortable; 908 /** 909 Used when the instance's `sortable` attribute is set to 910 "auto" (the default) to determine which columns will support 911 user sorting by clicking on the header. 912 913 If the instance's `key` attribute is not set, this 914 configuration is ignored. 915 916 { key: 'lastLogin', sortable: true } 917 918 @property sortable 919 @type Boolean 920 @for DataTable.Column 921 */ 922 /** 923 When the instance's `caseSensitive` attribute is set to 924 `true` the sort order is case sensitive (relevant to string columns only). 925 926 Case sensitive sort is marginally more efficient and should be considered 927 for large data sets when case insensitive sort is not required. 928 929 { key: 'lastLogin', sortable: true, caseSensitive: true } 930 931 @property caseSensitive 932 @type Boolean 933 @for DataTable.Column 934 */ 935 /** 936 Allows a column to be sorted using a custom algorithm. The 937 function receives three parameters, the first two being the 938 two record Models to compare, and the third being a boolean 939 `true` if the sort order should be descending. 940 941 The function should return `1` to sort `a` above `b`, `-1` 942 to sort `a` below `b`, and `0` if they are equal. Keep in 943 mind that the order should be reversed when `desc` is 944 `true`. 945 946 The `desc` parameter is provided to allow `sortFn`s to 947 always sort certain values above or below others, such as 948 always sorting `null`s on top. 949 950 { 951 label: 'Name', 952 sortFn: function (a, b, desc) { 953 var an = a.get('lname') + b.get('fname'), 954 bn = a.get('lname') + b.get('fname'), 955 order = (an > bn) ? 1 : -(an < bn); 956 957 return desc ? -order : order; 958 }, 959 formatter: function (o) { 960 return o.data.lname + ', ' + o.data.fname; 961 } 962 } 963 964 @property sortFn 965 @type Function 966 @for DataTable.Column 967 */ 968 /** 969 (__read-only__) If a column is sorted, this 970 will be set to `1` for ascending order or `-1` for 971 descending. This configuration is public for inspection, 972 but can't be used during DataTable instantiation to set the 973 sort direction of the column. Use the table's 974 [sortBy](DataTable.html#attr_sortBy) 975 attribute for that. 976 977 @property sortDir 978 @type {Number} 979 @readOnly 980 @for DataTable.Column 981 */ 982 983 Y.Base.mix(Y.DataTable, [Sortable]); 984 985 986 }, '3.17.2', {"requires": ["datatable-base"], "lang": ["en", "fr", "es", "hu"], "skinnable": true});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |