[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/datatable-sort/ -> datatable-sort.js (source)

   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 &lt; and &gt; 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});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1