2
YUI 3.13.0 (build 508226d)
3
Copyright 2013 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
8
YUI.add('tree-node', function (Y, NAME) {
10
/*jshint expr:true, onevar:false */
13
Provides the `Tree.Node` class, which represents a tree node contained in a
14
`Tree` data structure.
21
Represents a tree node in a `Tree` data structure.
24
@param {Tree} tree `Tree` instance with which this node should be associated.
25
@param {Object} [config] Configuration hash for this node.
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.
31
@param {Tree.Node[]} [config.children] Array of `Tree.Node` instances
32
for child nodes of this node.
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.
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.
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`.
50
function TreeNode(tree, config) {
51
config || (config = {});
53
this.id = this._yuid = config.id || this.id || Y.guid('treeNode-');
56
this.children = config.children || [];
57
this.data = config.data || {};
58
this.state = config.state || {};
60
if (config.canHaveChildren) {
61
this.canHaveChildren = config.canHaveChildren;
62
} else if (this.children.length) {
63
this.canHaveChildren = true;
66
// Mix in arbitrary properties on the config object, but don't overwrite any
67
// existing properties of this node.
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;
77
TreeNode.prototype = {
78
// -- Public Properties ----------------------------------------------------
81
Whether or not this node can contain child nodes.
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.
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
92
@property {Boolean} canHaveChildren
96
Child nodes contained within this node.
98
@property {Tree.Node[]} children
104
Arbitrary serializable data related to this node.
106
Use this property to store any data that should accompany this node when it
107
is serialized to JSON.
109
@property {Object} data
114
Unique id for this node.
116
@property {String} id
121
Parent node of this node, or `undefined` if this is an unattached node or
124
@property {Tree.Node} parent
129
Current state of this node.
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.
135
@property {Object} state
139
The Tree instance with which this node is associated.
141
@property {Tree} tree
145
// -- Protected Properties -------------------------------------------------
148
Mapping of child node ids to indices.
150
@property {Object} _indexMap
155
Flag indicating whether the `_indexMap` is stale and needs to be rebuilt.
157
@property {Boolean} _isIndexStale
164
Simple way to type-check that this is an instance of Tree.Node.
166
@property {Boolean} _isYUITreeNode
170
_isYUITreeNode: true,
173
Array of property names on this node that should be serialized to JSON when
174
`toJSON()` is called.
176
Note that the `children` property is a special case that is managed
179
@property {String[]} _serializable
182
_serializable: ['canHaveChildren', 'data', 'id', 'state'],
184
// -- Public Methods -------------------------------------------------------
187
Appends the given tree node or array of nodes to the end of this node's
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
195
@param {Object} [options] Options.
196
@param {Boolean} [options.silent=false] If `true`, the `add` event will
198
@return {Tree.Node|Tree.Node[]} Node or array of nodes that were appended.
200
append: function (node, options) {
201
return this.tree.appendNode(this, node, options);
205
Returns this node's depth.
207
The root node of a tree always has a depth of 0. A child of the root has a
208
depth of 1, a child of that child will have a depth of 2, and so on.
211
@return {Number} This node's depth.
220
parent = this.parent;
224
parent = parent.parent;
231
Removes all children from this node. The removed children will still be
232
reusable unless the `destroy` option is truthy.
235
@param {Object} [options] Options.
236
@param {Boolean} [options.destroy=false] If `true`, the children will
237
also be destroyed, which makes them available for garbage collection
238
and means they can't be reused.
239
@param {Boolean} [options.silent=false] If `true`, `remove` events will
241
@param {String} [options.src] Source of the change, to be passed along
242
to the event facade of the resulting event. This can be used to
243
distinguish between changes triggered by a user and changes
244
triggered programmatically, for example.
245
@return {Tree.Node[]} Array of removed child nodes.
247
empty: function (options) {
248
return this.tree.emptyNode(this, options);
252
Performs a depth-first traversal of this node, passing it and each of its
253
descendants to the specified _callback_, and returning the first node for
254
which the callback returns a truthy value.
256
Traversal will stop as soon as a truthy value is returned from the callback.
258
See `Tree#traverseNode()` for more details on how depth-first traversal
262
@param {Object} [options] Options.
263
@param {Number} [options.depth] Depth limit. If specified, descendants
264
will only be traversed to this depth before backtracking and moving
266
@param {Function} callback Callback function to call with the traversed
267
node and each of its descendants. If this function returns a truthy
268
value, traversal will be stopped and the current node will be returned.
270
@param {Tree.Node} callback.node Node being traversed.
272
@param {Object} [thisObj] `this` object to use when executing _callback_.
273
@return {Tree.Node|null} Returns the first node for which the _callback_
274
returns a truthy value, or `null` if the callback never returns a truthy
277
find: function (options, callback, thisObj) {
278
return this.tree.findNode(this, options, callback, thisObj);
282
Returns `true` if this node has one or more child nodes.
285
@return {Boolean} `true` if this node has one or more child nodes, `false`
288
hasChildren: function () {
289
return !!this.children.length;
293
Returns the numerical index of this node within its parent node, or `-1` if
294
this node doesn't have a parent node.
297
@return {Number} Index of this node within its parent node, or `-1` if this
298
node doesn't have a parent node.
301
return this.parent ? this.parent.indexOf(this) : -1;
305
Returns the numerical index of the given child node, or `-1` if the node is
306
not a child of this node.
309
@param {Tree.Node} node Child node.
310
@return {Number} Index of the child, or `-1` if the node is not a child of
313
indexOf: function (node) {
316
if (this._isIndexStale) {
320
index = this._indexMap[node.id];
322
return typeof index === 'undefined' ? -1 : index;
326
Inserts a node or array of nodes at the specified index under this node, or
327
appends them to this node if no index is specified.
329
If a node being inserted is from another tree, it and all its children will
330
be removed from that tree and moved to this one.
333
@param {Object|Object[]|Tree.Node|Tree.Node[]} node Child node, node config
334
object, array of child nodes, or array of node config objects to insert
335
under the given parent. Node config objects will automatically be
336
converted into node instances.
338
@param {Object} [options] Options.
339
@param {Number} [options.index] Index at which to insert the child node.
340
If not specified, the node will be appended as the last child of the
342
@param {Boolean} [options.silent=false] If `true`, the `add` event will
344
@param {String} [options.src='insert'] Source of the change, to be
345
passed along to the event facade of the resulting event. This can be
346
used to distinguish between changes triggered by a user and changes
347
triggered programmatically, for example.
349
@return {Tree.Node[]} Node or array of nodes that were inserted.
351
insert: function (node, options) {
352
return this.tree.insertNode(this, node, options);
356
Returns `true` if this node has been inserted into a tree, `false` if it is
357
merely associated with a tree and has not yet been inserted.
360
@return {Boolean} `true` if this node has been inserted into a tree, `false`
363
isInTree: function () {
364
if (this.tree && this.tree.rootNode === this) {
368
return !!(this.parent && this.parent.isInTree());
372
Returns `true` if this node is the root of the tree.
375
@return {Boolean} `true` if this node is the root of the tree, `false`
378
isRoot: function () {
379
return !!(this.tree && this.tree.rootNode === this);
383
Returns this node's next sibling, or `undefined` if this node is the last
387
@return {Tree.Node} This node's next sibling, or `undefined` if this node is
392
return this.parent.children[this.index() + 1];
397
Prepends a node or array of nodes at the beginning of this node's children.
399
If a node being prepended is from another tree, it and all its children will
400
be removed from that tree and moved to this one.
403
@param {Object|Object[]|Tree.Node|Tree.Node[]} node Child node, node config
404
object, array of child nodes, or array of node config objects to prepend
405
to this node. Node config objects will automatically be converted into
407
@param {Object} [options] Options.
408
@param {Boolean} [options.silent=false] If `true`, the `add` event will
410
@return {Tree.Node|Tree.Node[]} Node or array of nodes that were prepended.
412
prepend: function (node, options) {
413
return this.tree.prependNode(this, node, options);
417
Returns this node's previous sibling, or `undefined` if this node is the
421
@return {Tree.Node} This node's previous sibling, or `undefined` if this
422
node is the first child.
424
previous: function () {
426
return this.parent.children[this.index() - 1];
431
Removes this node from its parent node.
434
@param {Object} [options] Options.
435
@param {Boolean} [options.destroy=false] If `true`, this node and all
436
its children will also be destroyed, which makes them available for
437
garbage collection and means they can't be reused.
438
@param {Boolean} [options.silent=false] If `true`, the `remove` event
440
@param {String} [options.src] Source of the change, to be passed along
441
to the event facade of the resulting event. This can be used to
442
distinguish between changes triggered by a user and changes
443
triggered programmatically, for example.
446
remove: function (options) {
447
return this.tree.removeNode(this, options);
451
Returns the total number of nodes contained within this node, including all
452
descendants of this node's children.
455
@return {Number} Total number of nodes contained within this node, including
459
var children = this.children,
460
len = children.length,
463
for (var i = 0; i < len; i++) {
464
total += children[i].size();
471
Serializes this node to an object suitable for use in JSON.
474
@return {Object} Serialized node object.
476
toJSON: function () {
481
// Do nothing if this node is marked as destroyed.
482
if (state.destroyed) {
486
// Serialize properties explicitly marked as serializable.
487
for (i = 0, len = this._serializable.length; i < len; i++) {
488
key = this._serializable[i];
491
obj[key] = this[key];
495
// Serialize child nodes.
496
if (this.canHaveChildren) {
499
for (i = 0, len = this.children.length; i < len; i++) {
500
obj.children.push(this.children[i].toJSON());
508
Performs a depth-first traversal of this node, passing it and each of its
509
descendants to the specified _callback_.
511
If the callback function returns `Tree.STOP_TRAVERSAL`, traversal will be
512
stopped immediately. Otherwise, it will continue until the deepest
513
descendant of _node_ has been traversed, or until each branch has been
514
traversed to the optional maximum depth limit.
516
Since traversal is depth-first, that means nodes are traversed like this:
527
@param {Object} [options] Options.
528
@param {Number} [options.depth] Depth limit. If specified, descendants
529
will only be traversed to this depth before backtracking and moving
531
@param {Function} callback Callback function to call with the traversed
532
node and each of its descendants.
534
@param {Tree.Node} callback.node Node being traversed.
536
@param {Object} [thisObj] `this` object to use when executing _callback_.
537
@return {Mixed} Returns `Tree.STOP_TRAVERSAL` if traversal was stopped;
538
otherwise returns `undefined`.
540
traverse: function (options, callback, thisObj) {
541
return this.tree.traverseNode(this, options, callback, thisObj);
544
// -- Protected Methods ----------------------------------------------------
545
_reindex: function () {
546
var children = this.children,
550
for (i = 0, len = children.length; i < len; i++) {
551
indexMap[children[i].id] = i;
554
this._indexMap = indexMap;
555
this._isIndexStale = false;
559
Y.namespace('Tree').Node = TreeNode;