src/cm/media/js/lib/yui/yui_3.10.3/build/tree-node/tree-node.js
changeset 525 89ef5ed3c48b
equal deleted inserted replaced
524:322d0feea350 525:89ef5ed3c48b
       
     1 /*
       
     2 YUI 3.10.3 (build 2fb5187)
       
     3 Copyright 2013 Yahoo! Inc. All rights reserved.
       
     4 Licensed under the BSD License.
       
     5 http://yuilibrary.com/license/
       
     6 */
       
     7 
       
     8 YUI.add('tree-node', function (Y, NAME) {
       
     9 
       
    10 /*jshint expr:true, onevar:false */
       
    11 
       
    12 /**
       
    13 Provides the `Tree.Node` class, which represents a tree node contained in a
       
    14 `Tree` data structure.
       
    15 
       
    16 @module tree
       
    17 @submodule tree-node
       
    18 **/
       
    19 
       
    20 /**
       
    21 Represents a tree node in a `Tree` data structure.
       
    22 
       
    23 @class Tree.Node
       
    24 @param {Tree} tree `Tree` instance with which this node should be associated.
       
    25 @param {Object} [config] Configuration hash for this node.
       
    26 
       
    27     @param {Boolean} [config.canHaveChildren=false] Whether or not this node can
       
    28         contain child nodes. Will be automatically set to `true` if not
       
    29         specified and `config.children` contains one or more children.
       
    30 
       
    31     @param {Tree.Node[]} [config.children] Array of `Tree.Node` instances
       
    32         for child nodes of this node.
       
    33 
       
    34     @param {Object} [config.data] Implementation-specific data related to this
       
    35         node. You may add arbitrary properties to this hash for your own use.
       
    36 
       
    37     @param {String} [config.id] Unique id for this node. This id must be unique
       
    38         among all tree nodes on the entire page, and will also be used as this
       
    39         node's DOM id when it's rendered by a TreeView. A unique id will be
       
    40         automatically generated unless you specify a custom value.
       
    41 
       
    42     @param {Object} [config.state] State hash for this node. You may add
       
    43         arbitrary state properties to this hash for your own use. See the
       
    44         docs for `Tree.Node`'s `state` property for details on state values used
       
    45         internally by `Tree.Node`.
       
    46 
       
    47 @constructor
       
    48 **/
       
    49 
       
    50 function TreeNode(tree, config) {
       
    51     config || (config = {});
       
    52 
       
    53     this.id   = this._yuid = config.id || this.id || Y.guid('treeNode-');
       
    54     this.tree = tree;
       
    55 
       
    56     this.children = config.children || [];
       
    57     this.data     = config.data || {};
       
    58     this.state    = config.state || {};
       
    59 
       
    60     if (config.canHaveChildren) {
       
    61         this.canHaveChildren = config.canHaveChildren;
       
    62     } else if (this.children.length) {
       
    63         this.canHaveChildren = true;
       
    64     }
       
    65 
       
    66     // Mix in arbitrary properties on the config object, but don't overwrite any
       
    67     // existing properties of this node.
       
    68     Y.mix(this, config);
       
    69 
       
    70     // If this node has children, loop through them and ensure their parent
       
    71     // references are all set to this node.
       
    72     for (var i = 0, len = this.children.length; i < len; i++) {
       
    73         this.children[i].parent = this;
       
    74     }
       
    75 }
       
    76 
       
    77 TreeNode.prototype = {
       
    78     // -- Public Properties ----------------------------------------------------
       
    79 
       
    80     /**
       
    81     Whether or not this node can contain child nodes.
       
    82 
       
    83     This value is falsy by default unless child nodes are added at instantiation
       
    84     time, in which case it will be automatically set to `true`. You can also
       
    85     manually set it to `true` to indicate that a node can have children even
       
    86     though it might not currently have any children.
       
    87 
       
    88     Note that regardless of the value of this property, appending, prepending,
       
    89     or inserting a node into this node will cause `canHaveChildren` to be set to
       
    90     true automatically.
       
    91 
       
    92     @property {Boolean} canHaveChildren
       
    93     **/
       
    94 
       
    95     /**
       
    96     Child nodes contained within this node.
       
    97 
       
    98     @property {Tree.Node[]} children
       
    99     @default []
       
   100     @readOnly
       
   101     **/
       
   102 
       
   103     /**
       
   104     Arbitrary serializable data related to this node.
       
   105 
       
   106     Use this property to store any data that should accompany this node when it
       
   107     is serialized to JSON.
       
   108 
       
   109     @property {Object} data
       
   110     @default {}
       
   111     **/
       
   112 
       
   113     /**
       
   114     Unique id for this node.
       
   115 
       
   116     @property {String} id
       
   117     @readOnly
       
   118     **/
       
   119 
       
   120     /**
       
   121     Parent node of this node, or `undefined` if this is an unattached node or
       
   122     the root node.
       
   123 
       
   124     @property {Tree.Node} parent
       
   125     @readOnly
       
   126     **/
       
   127 
       
   128     /**
       
   129     Current state of this node.
       
   130 
       
   131     Use this property to store state-specific info -- such as whether this node
       
   132     is "open", "selected", or any other arbitrary state -- that should accompany
       
   133     this node when it is serialized to JSON.
       
   134 
       
   135     @property {Object} state
       
   136     **/
       
   137 
       
   138     /**
       
   139     The Tree instance with which this node is associated.
       
   140 
       
   141     @property {Tree} tree
       
   142     @readOnly
       
   143     **/
       
   144 
       
   145     // -- Protected Properties -------------------------------------------------
       
   146 
       
   147     /**
       
   148     Mapping of child node ids to indices.
       
   149 
       
   150     @property {Object} _indexMap
       
   151     @protected
       
   152     **/
       
   153 
       
   154     /**
       
   155     Flag indicating whether the `_indexMap` is stale and needs to be rebuilt.
       
   156 
       
   157     @property {Boolean} _isIndexStale
       
   158     @default true
       
   159     @protected
       
   160     **/
       
   161     _isIndexStale: true,
       
   162 
       
   163     /**
       
   164     Simple way to type-check that this is an instance of Tree.Node.
       
   165 
       
   166     @property {Boolean} _isYUITreeNode
       
   167     @default true
       
   168     @protected
       
   169     **/
       
   170     _isYUITreeNode: true,
       
   171 
       
   172     /**
       
   173     Array of property names on this node that should be serialized to JSON when
       
   174     `toJSON()` is called.
       
   175 
       
   176     Note that the `children` property is a special case that is managed
       
   177     separately.
       
   178 
       
   179     @property {String[]} _serializable
       
   180     @protected
       
   181     **/
       
   182     _serializable: ['canHaveChildren', 'data', 'id', 'state'],
       
   183 
       
   184     // -- Public Methods -------------------------------------------------------
       
   185 
       
   186     /**
       
   187     Appends the given tree node or array of nodes to the end of this node's
       
   188     children.
       
   189 
       
   190     @method append
       
   191     @param {Object|Object[]|Tree.Node|Tree.Node[]} node Child node, node config
       
   192         object, array of child nodes, or array of node config objects to append
       
   193         to the given parent. Node config objects will automatically be converted
       
   194         into node instances.
       
   195     @param {Object} [options] Options.
       
   196         @param {Boolean} [options.silent=false] If `true`, the `add` event will
       
   197             be suppressed.
       
   198     @return {Tree.Node|Tree.Node[]} Node or array of nodes that were appended.
       
   199     **/
       
   200     append: function (node, options) {
       
   201         return this.tree.appendNode(this, node, options);
       
   202     },
       
   203 
       
   204     /**
       
   205     Removes all children from this node. The removed children will still be
       
   206     reusable unless the `destroy` option is truthy.
       
   207 
       
   208     @method empty
       
   209     @param {Object} [options] Options.
       
   210         @param {Boolean} [options.destroy=false] If `true`, the children will
       
   211             also be destroyed, which makes them available for garbage collection
       
   212             and means they can't be reused.
       
   213         @param {Boolean} [options.silent=false] If `true`, `remove` events will
       
   214             be suppressed.
       
   215         @param {String} [options.src] Source of the change, to be passed along
       
   216             to the event facade of the resulting event. This can be used to
       
   217             distinguish between changes triggered by a user and changes
       
   218             triggered programmatically, for example.
       
   219     @return {Tree.Node[]} Array of removed child nodes.
       
   220     **/
       
   221     empty: function (options) {
       
   222         return this.tree.emptyNode(this, options);
       
   223     },
       
   224 
       
   225     /**
       
   226     Performs a depth-first traversal of this node, passing it and each of its
       
   227     descendants to the specified _callback_, and returning the first node for
       
   228     which the callback returns a truthy value.
       
   229 
       
   230     Traversal will stop as soon as a truthy value is returned from the callback.
       
   231 
       
   232     See `Tree#traverseNode()` for more details on how depth-first traversal
       
   233     works.
       
   234 
       
   235     @method find
       
   236     @param {Object} [options] Options.
       
   237         @param {Number} [options.depth] Depth limit. If specified, descendants
       
   238             will only be traversed to this depth before backtracking and moving
       
   239             on.
       
   240     @param {Function} callback Callback function to call with the traversed
       
   241         node and each of its descendants. If this function returns a truthy
       
   242         value, traversal will be stopped and the current node will be returned.
       
   243 
       
   244         @param {Tree.Node} callback.node Node being traversed.
       
   245 
       
   246     @param {Object} [thisObj] `this` object to use when executing _callback_.
       
   247     @return {Tree.Node|null} Returns the first node for which the _callback_
       
   248         returns a truthy value, or `null` if the callback never returns a truthy
       
   249         value.
       
   250     **/
       
   251     find: function (options, callback, thisObj) {
       
   252         return this.tree.findNode(this, options, callback, thisObj);
       
   253     },
       
   254 
       
   255     /**
       
   256     Returns `true` if this node has one or more child nodes.
       
   257 
       
   258     @method hasChildren
       
   259     @return {Boolean} `true` if this node has one or more child nodes, `false`
       
   260         otherwise.
       
   261     **/
       
   262     hasChildren: function () {
       
   263         return !!this.children.length;
       
   264     },
       
   265 
       
   266     /**
       
   267     Returns the numerical index of this node within its parent node, or `-1` if
       
   268     this node doesn't have a parent node.
       
   269 
       
   270     @method index
       
   271     @return {Number} Index of this node within its parent node, or `-1` if this
       
   272         node doesn't have a parent node.
       
   273     **/
       
   274     index: function () {
       
   275         return this.parent ? this.parent.indexOf(this) : -1;
       
   276     },
       
   277 
       
   278     /**
       
   279     Returns the numerical index of the given child node, or `-1` if the node is
       
   280     not a child of this node.
       
   281 
       
   282     @method indexOf
       
   283     @param {Tree.Node} node Child node.
       
   284     @return {Number} Index of the child, or `-1` if the node is not a child of
       
   285         this node.
       
   286     **/
       
   287     indexOf: function (node) {
       
   288         var index;
       
   289 
       
   290         if (this._isIndexStale) {
       
   291             this._reindex();
       
   292         }
       
   293 
       
   294         index = this._indexMap[node.id];
       
   295 
       
   296         return typeof index === 'undefined' ? -1 : index;
       
   297     },
       
   298 
       
   299     /**
       
   300     Inserts a node or array of nodes at the specified index under this node, or
       
   301     appends them to this node if no index is specified.
       
   302 
       
   303     If a node being inserted is from another tree, it and all its children will
       
   304     be removed from that tree and moved to this one.
       
   305 
       
   306     @method insert
       
   307     @param {Object|Object[]|Tree.Node|Tree.Node[]} node Child node, node config
       
   308         object, array of child nodes, or array of node config objects to insert
       
   309         under the given parent. Node config objects will automatically be
       
   310         converted into node instances.
       
   311 
       
   312     @param {Object} [options] Options.
       
   313         @param {Number} [options.index] Index at which to insert the child node.
       
   314             If not specified, the node will be appended as the last child of the
       
   315             parent.
       
   316         @param {Boolean} [options.silent=false] If `true`, the `add` event will
       
   317             be suppressed.
       
   318         @param {String} [options.src='insert'] Source of the change, to be
       
   319             passed along to the event facade of the resulting event. This can be
       
   320             used to distinguish between changes triggered by a user and changes
       
   321             triggered programmatically, for example.
       
   322 
       
   323     @return {Tree.Node[]} Node or array of nodes that were inserted.
       
   324     **/
       
   325     insert: function (node, options) {
       
   326         return this.tree.insertNode(this, node, options);
       
   327     },
       
   328 
       
   329     /**
       
   330     Returns `true` if this node has been inserted into a tree, `false` if it is
       
   331     merely associated with a tree and has not yet been inserted.
       
   332 
       
   333     @method isInTree
       
   334     @return {Boolean} `true` if this node has been inserted into a tree, `false`
       
   335         otherwise.
       
   336     **/
       
   337     isInTree: function () {
       
   338         if (this.tree.rootNode === this) {
       
   339             return true;
       
   340         }
       
   341 
       
   342         return !!(this.parent && this.parent.isInTree());
       
   343     },
       
   344 
       
   345     /**
       
   346     Returns `true` if this node is the root of the tree.
       
   347 
       
   348     @method isRoot
       
   349     @return {Boolean} `true` if this node is the root of the tree, `false`
       
   350         otherwise.
       
   351     **/
       
   352     isRoot: function () {
       
   353         return this.tree.rootNode === this;
       
   354     },
       
   355 
       
   356     /**
       
   357     Returns this node's next sibling, or `undefined` if this node is the last
       
   358     child.
       
   359 
       
   360     @method next
       
   361     @return {Tree.Node} This node's next sibling, or `undefined` if this node is
       
   362         the last child.
       
   363     **/
       
   364     next: function () {
       
   365         if (this.parent) {
       
   366             return this.parent.children[this.index() + 1];
       
   367         }
       
   368     },
       
   369 
       
   370     /**
       
   371     Prepends a node or array of nodes at the beginning of this node's children.
       
   372 
       
   373     If a node being prepended is from another tree, it and all its children will
       
   374     be removed from that tree and moved to this one.
       
   375 
       
   376     @method prepend
       
   377     @param {Object|Object[]|Tree.Node|Tree.Node[]} node Child node, node config
       
   378         object, array of child nodes, or array of node config objects to prepend
       
   379         to this node. Node config objects will automatically be converted into
       
   380         node instances.
       
   381     @param {Object} [options] Options.
       
   382         @param {Boolean} [options.silent=false] If `true`, the `add` event will
       
   383             be suppressed.
       
   384     @return {Tree.Node|Tree.Node[]} Node or array of nodes that were prepended.
       
   385     **/
       
   386     prepend: function (node, options) {
       
   387         return this.tree.prependNode(this, node, options);
       
   388     },
       
   389 
       
   390     /**
       
   391     Returns this node's previous sibling, or `undefined` if this node is the
       
   392     first child
       
   393 
       
   394     @method previous
       
   395     @return {Tree.Node} This node's previous sibling, or `undefined` if this
       
   396         node is the first child.
       
   397     **/
       
   398     previous: function () {
       
   399         if (this.parent) {
       
   400             return this.parent.children[this.index() - 1];
       
   401         }
       
   402     },
       
   403 
       
   404     /**
       
   405     Removes this node from its parent node.
       
   406 
       
   407     @method remove
       
   408     @param {Object} [options] Options.
       
   409         @param {Boolean} [options.destroy=false] If `true`, this node and all
       
   410             its children will also be destroyed, which makes them available for
       
   411             garbage collection and means they can't be reused.
       
   412         @param {Boolean} [options.silent=false] If `true`, the `remove` event
       
   413             will be suppressed.
       
   414         @param {String} [options.src] Source of the change, to be passed along
       
   415             to the event facade of the resulting event. This can be used to
       
   416             distinguish between changes triggered by a user and changes
       
   417             triggered programmatically, for example.
       
   418     @chainable
       
   419     **/
       
   420     remove: function (options) {
       
   421         return this.tree.removeNode(this, options);
       
   422     },
       
   423 
       
   424     /**
       
   425     Returns the total number of nodes contained within this node, including all
       
   426     descendants of this node's children.
       
   427 
       
   428     @method size
       
   429     @return {Number} Total number of nodes contained within this node, including
       
   430         all descendants.
       
   431     **/
       
   432     size: function () {
       
   433         var children = this.children,
       
   434             len      = children.length,
       
   435             total    = len;
       
   436 
       
   437         for (var i = 0; i < len; i++) {
       
   438             total += children[i].size();
       
   439         }
       
   440 
       
   441         return total;
       
   442     },
       
   443 
       
   444     /**
       
   445     Serializes this node to an object suitable for use in JSON.
       
   446 
       
   447     @method toJSON
       
   448     @return {Object} Serialized node object.
       
   449     **/
       
   450     toJSON: function () {
       
   451         var obj   = {},
       
   452             state = this.state,
       
   453             i, key, len;
       
   454 
       
   455         // Do nothing if this node is marked as destroyed.
       
   456         if (state.destroyed) {
       
   457             return null;
       
   458         }
       
   459 
       
   460         // Serialize properties explicitly marked as serializable.
       
   461         for (i = 0, len = this._serializable.length; i < len; i++) {
       
   462             key = this._serializable[i];
       
   463 
       
   464             if (key in this) {
       
   465                 obj[key] = this[key];
       
   466             }
       
   467         }
       
   468 
       
   469         // Serialize child nodes.
       
   470         if (this.canHaveChildren) {
       
   471             obj.children = [];
       
   472 
       
   473             for (i = 0, len = this.children.length; i < len; i++) {
       
   474                 obj.children.push(this.children[i].toJSON());
       
   475             }
       
   476         }
       
   477 
       
   478         return obj;
       
   479     },
       
   480 
       
   481     /**
       
   482     Performs a depth-first traversal of this node, passing it and each of its
       
   483     descendants to the specified _callback_.
       
   484 
       
   485     If the callback function returns `Tree.STOP_TRAVERSAL`, traversal will be
       
   486     stopped immediately. Otherwise, it will continue until the deepest
       
   487     descendant of _node_ has been traversed, or until each branch has been
       
   488     traversed to the optional maximum depth limit.
       
   489 
       
   490     Since traversal is depth-first, that means nodes are traversed like this:
       
   491 
       
   492                 1
       
   493               / | \
       
   494              2  8  9
       
   495             / \     \
       
   496            3   7    10
       
   497          / | \      / \
       
   498         4  5  6    11 12
       
   499 
       
   500     @method traverse
       
   501     @param {Object} [options] Options.
       
   502         @param {Number} [options.depth] Depth limit. If specified, descendants
       
   503             will only be traversed to this depth before backtracking and moving
       
   504             on.
       
   505     @param {Function} callback Callback function to call with the traversed
       
   506         node and each of its descendants.
       
   507 
       
   508         @param {Tree.Node} callback.node Node being traversed.
       
   509 
       
   510     @param {Object} [thisObj] `this` object to use when executing _callback_.
       
   511     @return {Mixed} Returns `Tree.STOP_TRAVERSAL` if traversal was stopped;
       
   512         otherwise returns `undefined`.
       
   513     **/
       
   514     traverse: function (options, callback, thisObj) {
       
   515         return this.tree.traverseNode(this, options, callback, thisObj);
       
   516     },
       
   517 
       
   518     // -- Protected Methods ----------------------------------------------------
       
   519     _reindex: function () {
       
   520         var children = this.children,
       
   521             indexMap = {},
       
   522             i, len;
       
   523 
       
   524         for (i = 0, len = children.length; i < len; i++) {
       
   525             indexMap[children[i].id] = i;
       
   526         }
       
   527 
       
   528         this._indexMap     = indexMap;
       
   529         this._isIndexStale = false;
       
   530     }
       
   531 };
       
   532 
       
   533 Y.namespace('Tree').Node = TreeNode;
       
   534 
       
   535 
       
   536 }, '3.10.3');