[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/tree-sortable/ -> tree-sortable.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('tree-sortable', function (Y, NAME) {
   9  
  10  /*jshint expr:true, onevar:false */
  11  
  12  /**
  13  Extension for `Tree` that makes nodes sortable.
  14  
  15  @module tree
  16  @submodule tree-sortable
  17  @main tree-sortable
  18  **/
  19  
  20  /**
  21  Extension for `Tree` that makes nodes sortable.
  22  
  23  @class Tree.Sortable
  24  @constructor
  25  @param {Object} [config] Configuration options.
  26  @param {Function} [config.sortComparator] Default comparator function to use
  27      when sorting a node's children if the node itself doesn't have a custom
  28      comparator function. If not specified, insertion order will be used by
  29      default.
  30  @param {Boolean} [config.sortReverse=false] If `true`, node children will be
  31      sorted in reverse (descending) order by default. Otherwise they'll be sorted
  32      in ascending order.
  33  @extensionfor Tree
  34  **/
  35  
  36  /**
  37  Fired after a node's children are re-sorted.
  38  
  39  @event sort
  40  @param {Tree.Node} node Node whose children were sorted.
  41  @param {Boolean} reverse `true` if the children were sorted in reverse
  42      (descending) order, `false` otherwise.
  43  @param {String} src Source of the event.
  44  **/
  45  var EVT_SORT = 'sort';
  46  
  47  function Sortable() {}
  48  
  49  Sortable.prototype = {
  50      // -- Public Properties ----------------------------------------------------
  51  
  52      /**
  53      If `true`, node children will be sorted in reverse (descending) order by
  54      default. Otherwise they'll be sorted in ascending order.
  55  
  56      @property {Boolean} sortReverse
  57      @default false
  58      **/
  59      sortReverse: false,
  60  
  61      // -- Lifecycle ------------------------------------------------------------
  62      initializer: function (config) {
  63          this.nodeExtensions = this.nodeExtensions.concat(Y.Tree.Node.Sortable);
  64  
  65          if (config) {
  66              if (config.sortComparator) {
  67                  this.sortComparator = config.sortComparator;
  68              }
  69  
  70              if ('sortReverse' in config) {
  71                  this.sortReverse = config.sortReverse;
  72              }
  73          }
  74      },
  75  
  76      // -- Public Methods -------------------------------------------------------
  77  
  78      /**
  79      Sorts the children of every node in this tree.
  80  
  81      A `sort` event will be fired for each node whose children are sorted, which
  82      can get very noisy. If this is a large tree, you may want to set the
  83      `silent` option to `true` to suppress these events.
  84  
  85      @method sort
  86      @param {Object} [options] Options.
  87          @param {Boolean} [options.silent] If `true`, no `sort` events will be
  88              fired.
  89          @param {Function} [options.sortComparator] Custom comparator function to
  90              use. If specified, this will become the new comparator function for
  91              each node, overwriting any previous comparator function that was set
  92              for the node.
  93          @param {Boolean} [options.sortReverse] If `true`, children will be
  94              sorted in reverse (descending) order. Otherwise they'll be sorted in
  95              ascending order. This will become each node's new sort order,
  96              overwriting any previous sort order that was set for the node.
  97          @param {String} [options.src] Source of the sort operation. Will be
  98              passed along to the `sort` event facade.
  99      @chainable
 100      **/
 101      sort: function (options) {
 102          return this.sortNode(this.rootNode, Y.merge(options, {deep: true}));
 103      },
 104  
 105      /**
 106      Default comparator function to use when sorting a node's children if the
 107      node itself doesn't have a custom comparator function.
 108  
 109      If not specified, insertion order will be used by default.
 110  
 111      @method sortComparator
 112      @param {Tree.Node} node Node being sorted.
 113      @return {Number|String} Value by which the node should be sorted relative to
 114          its siblings.
 115      **/
 116      sortComparator: function (node) {
 117          return node.index();
 118      },
 119  
 120      /**
 121      Sorts the children of the specified node.
 122  
 123      By default, only the node's direct children are sorted. To sort all nodes in
 124      the hierarchy (children, children's children, etc.), set the `deep` option
 125      to `true`. If this is a very deep hierarchy, you may also want to set
 126      `silent` to true to avoid generating a flood of `sort` events.
 127  
 128      @method sortNode
 129      @param {Tree.Node} node Node whose children should be sorted.
 130      @param {Object} [options] Options.
 131          @param {Boolean} [options.deep=false] If `true`, all of this node's
 132              children (and their children, and so on) will be traversed and
 133              re-sorted as well.
 134          @param {Boolean} [options.silent] If `true`, no `sort` event will be
 135              fired.
 136          @param {Function} [options.sortComparator] Custom comparator function to
 137              use. If specified, this will become the node's new comparator
 138              function, overwriting any previous comparator function that was set
 139              for the node.
 140          @param {Boolean} [options.sortReverse] If `true`, children will be
 141              sorted in reverse (descending) order. Otherwise they'll be sorted in
 142              ascending order. This will become the node's new sort order,
 143              overwriting any previous sort order that was set for the node.
 144          @param {String} [options.src] Source of the sort operation. Will be
 145              passed along to the `sort` event facade.
 146      @chainable
 147      **/
 148      sortNode: function (node, options) {
 149          // Nothing to do if the node has no children.
 150          if (!node.children.length) {
 151              return this;
 152          }
 153  
 154          options || (options = {});
 155  
 156          if (options.deep) {
 157              // Unset the `deep` option so we don't cause an infinite loop.
 158              options = Y.merge(options, {deep: false});
 159  
 160              var self = this;
 161  
 162              // Traverse and sort all nodes (including this one).
 163              this.traverseNode(node, function (nodeToSort) {
 164                  self.sortNode(nodeToSort, options);
 165              });
 166  
 167              return this;
 168          }
 169  
 170          var comparator = this._getSortComparator(node, options),
 171              reverse;
 172  
 173          if ('sortReverse' in options) {
 174              reverse = node.sortReverse = options.sortReverse;
 175          } else if ('sortReverse' in node) {
 176              reverse = node.sortReverse;
 177          } else {
 178              reverse = this.sortReverse;
 179          }
 180  
 181          node.children.sort(Y.rbind(this._sort, this, comparator, reverse));
 182          node._isIndexStale = true;
 183  
 184          if (!options.silent) {
 185              this.fire(EVT_SORT, {
 186                  node   : node,
 187                  reverse: !!reverse,
 188                  src    : options.src
 189              });
 190          }
 191  
 192          return this;
 193      },
 194  
 195      // -- Protected Methods ----------------------------------------------------
 196  
 197      /**
 198      Compares value _a_ to value _b_ for sorting purposes.
 199  
 200      Values are assumed to be the result of calling a sortComparator function.
 201  
 202      @method _compare
 203      @param {Mixed} a First value to compare.
 204      @param {Mixed} b Second value to compare.
 205      @return {Number} `-1` if _a_ should come before _b_, `0` if they're
 206          equivalent, `1` if _a_ should come after _b_.
 207      @protected
 208      **/
 209      _compare: function (a, b) {
 210          return a < b ? -1 : (a > b ? 1 : 0);
 211      },
 212  
 213      /**
 214      Compares value _a_ to value _b_ for sorting purposes, but sorts them in
 215      reverse (descending) order.
 216  
 217      Values are assumed to be the result of calling a sortComparator function.
 218  
 219      @method _compareReverse
 220      @param {Mixed} a First value to compare.
 221      @param {Mixed} b Second value to compare.
 222      @return {Number} `-1` if _a_ should come before _b_, `0` if they're
 223          equivalent, `1` if _a_ should come after _b_.
 224      @protected
 225      **/
 226      _compareReverse: function (a, b) {
 227          return b < a ? -1 : (b > a ? 1 : 0);
 228      },
 229  
 230      /**
 231      Overrides `Tree#_getDefaultNodeIndex()` to provide insertion-time sorting
 232      for nodes inserted without an explicit index.
 233  
 234      @method _getDefaultNodeIndex
 235      @param {Tree.Node} parent Parent node.
 236      @param {Tree.Node} node Node being inserted.
 237      @param {Object} [options] Options passed to `insertNode()`.
 238      @return {Number} Index at which _node_ should be inserted into _parent_'s
 239          `children` array.
 240      @protected
 241      **/
 242      _getDefaultNodeIndex: function (parent, node) {
 243          /*jshint bitwise:false */
 244  
 245          var children   = parent.children,
 246              comparator = this._getSortComparator(parent),
 247              max        = children.length,
 248              min        = 0,
 249              reverse    = 'sortReverse' in parent ? parent.sortReverse : this.sortReverse;
 250  
 251          if (!max) {
 252              return max;
 253          }
 254  
 255          // Special case: if the sortComparator is the default sortComparator,
 256          // cheat and just return the first or last index of the children array.
 257          //
 258          // This is necessary because the default sortComparator relies on
 259          // the node's index, which is always -1 for uninserted nodes.
 260          if (comparator._unboundComparator === Sortable.prototype.sortComparator) {
 261              return reverse ? 0 : max;
 262          }
 263  
 264          var compare = reverse ? this._compareReverse : this._compare,
 265              needle  = comparator(node);
 266  
 267          // Perform an iterative binary search to determine the correct position
 268          // for the node based on the return value of the comparator function.
 269          var middle;
 270  
 271          while (min < max) {
 272              middle = (min + max) >> 1; // Divide by two and discard remainder.
 273  
 274              if (compare(comparator(children[middle]), needle) < 0) {
 275                  min = middle + 1;
 276              } else {
 277                  max = middle;
 278              }
 279          }
 280  
 281          return min;
 282      },
 283  
 284      /**
 285      Returns a sort comparator function derived from the given _node_ and
 286      _options_, and bound to the correct `thisObj` based on where it was found.
 287  
 288      @method _getSortComparator
 289      @param {Tree.Node} node Node on which to look for a `sortComparator`
 290          function.
 291      @param {Object} [options] Options object on which to look for a
 292          `sortComparator` function.
 293      @return {Function} Properly bound sort comparator function.
 294      @protected
 295      **/
 296      _getSortComparator: function (node, options) {
 297          var boundComparator,
 298              comparator,
 299              thisObj;
 300  
 301          if (options && options.sortComparator) {
 302              comparator = node.sortComparator = options.sortComparator;
 303          } else if (node.sortComparator) {
 304              comparator = node.sortComparator;
 305              thisObj    = node;
 306          } else {
 307              comparator = this.sortComparator;
 308              thisObj    = this;
 309          }
 310  
 311          boundComparator = function () {
 312              return comparator.apply(thisObj, arguments);
 313          };
 314  
 315          boundComparator._unboundComparator = comparator;
 316  
 317          return boundComparator;
 318      },
 319  
 320      /**
 321      Array sort function used by `sortNode()` to re-sort a node's children.
 322  
 323      @method _sort
 324      @param {Tree.Node} a First node to compare.
 325      @param {Tree.Node} b Second node to compare.
 326      @param {Function} comparator Comparator function.
 327      @param {Boolean} [reverse=false] If `true`, this will be a reverse
 328          (descending) comparison.
 329      @return {Number} `-1` if _a_ is less than _b_, `0` if equal, `1` if greater.
 330      @protected
 331      **/
 332      _sort: function (a, b, comparator, reverse) {
 333          return this[reverse ? '_compareReverse' : '_compare'](
 334              comparator(a), comparator(b));
 335      }
 336  };
 337  
 338  Y.Tree.Sortable = Sortable;
 339  /**
 340  @module tree
 341  @submodule tree-sortable
 342  **/
 343  
 344  /**
 345  `Tree.Node` extension that adds methods useful for nodes in trees that use the
 346  `Tree.Sortable` extension.
 347  
 348  @class Tree.Node.Sortable
 349  @constructor
 350  @extensionfor Tree.Node
 351  **/
 352  
 353  function NodeSortable() {}
 354  
 355  NodeSortable.prototype = {
 356      /**
 357      Sorts this node's children.
 358  
 359      @method sort
 360      @param {Object} [options] Options.
 361          @param {Boolean} [options.silent] If `true`, no `sort` event will be
 362              fired.
 363          @param {Function} [options.sortComparator] Custom comparator function to
 364              use. If specified, this will become the node's new comparator
 365              function, overwriting any previous comparator function that was set
 366              for the node.
 367          @param {Boolean} [options.sortReverse] If `true`, children will be
 368              sorted in reverse (descending) order. Otherwise they'll be sorted in
 369              ascending order. This will become the node's new sort order,
 370              overwriting any previous sort order that was set for the node.
 371          @param {String} [options.src] Source of the sort operation. Will be
 372              passed along to the `sort` event facade.
 373      @chainable
 374      **/
 375      sort: function (options) {
 376          this.tree.sortNode(this, options);
 377          return this;
 378      }
 379  };
 380  
 381  Y.Tree.Node.Sortable = NodeSortable;
 382  
 383  
 384  }, '3.17.2', {"requires": ["tree"]});


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