diff -r 322d0feea350 -r 89ef5ed3c48b src/cm/media/js/lib/yui/yui_3.10.3/build/tree/tree-debug.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/media/js/lib/yui/yui_3.10.3/build/tree/tree-debug.js Tue Jul 16 14:29:46 2013 +0200 @@ -0,0 +1,888 @@ +/* +YUI 3.10.3 (build 2fb5187) +Copyright 2013 Yahoo! Inc. All rights reserved. +Licensed under the BSD License. +http://yuilibrary.com/license/ +*/ + +YUI.add('tree', function (Y, NAME) { + +/*jshint expr:true, onevar:false */ + +/** +Provides a generic tree data structure and related functionality. + +A tree has a root node, which may contain any number of child nodes, which may +themselves contain child nodes, ad infinitum. + +Child nodes are lightweight function instances which delegate to the tree for +all significant functionality, so trees remain performant and memory-efficient +even with thousands and thousands of nodes. + +@module tree +@main tree +**/ + +/** +The `Tree` class represents a generic tree data structure. A tree has a root +node, which may contain any number of child nodes, which may themselves contain +child nodes, ad infinitum. + +This class doesn't expose any UI, but is intended to be used as a data structure +or base class for other components. For example, the SmugMug TreeView gallery +module extends Tree and provides a TreeView UI. + +@class Tree +@param {Object} [config] Config options. + @param {Object[]|Tree.Node[]} [config.nodes] Array of tree node config + objects or `Tree.Node` instances to add to this tree at initialization + time. + @param {Object|Tree.Node} [config.rootNode] Node to use as the root node of + this tree. +@constructor +@extends Base +**/ + +var Lang = Y.Lang, + + /** + Fired when a node is added to this Tree. The `src` property will indicate + how the node was added ("append", "insert", "prepend", etc.). + + @event add + @param {Number} index Index at which the node will be added. + @param {Tree.Node} node Node being added. + @param {Tree.Node} parent Parent node to which the node will be added. + @param {String} src Source of the event ("append", "insert", "prepend", + etc.). + @preventable _defAddFn + **/ + EVT_ADD = 'add', + + /** + Fired when this Tree is cleared. + + @event clear + @param {Tree.Node} rootNode New root node of this tree (the old root node is + always destroyed when a tree is cleared). + @param {String} src Source of the event. + @preventable _defClearFn + **/ + EVT_CLEAR = 'clear', + + /** + Fired when a node is removed from this Tree. + + @event remove + @param {Boolean} destroy Whether or not the node will be destroyed after + being removed from this tree. + @param {Tree.Node} node Node being removed. + @param {Tree.Node} parent Parent node from which the node will be removed. + @param {String} src Source of the event. + @preventable _defRemoveFn + **/ + EVT_REMOVE = 'remove'; + +var Tree = Y.Base.create('tree', Y.Base, [], { + // -- Public Properties ---------------------------------------------------- + + /** + Reference to the `children` array of this Tree's `rootNode`. + + This is a convenience property to allow you to type `tree.children` instead + of `tree.rootNode.children`. + + @property {Tree.Node[]} children + @readOnly + **/ + + /** + The `Tree.Node` class or subclass that should be used for nodes created by + this tree. + + You may specify an actual class reference or a string that resolves to a + class reference at runtime. + + @property {String|Tree.Node} nodeClass + @default Y.Tree.Node + **/ + nodeClass: Y.Tree.Node, + + /** + Optional array containing one or more extension classes that should be mixed + into the `nodeClass` when this Tree is instantiated. The resulting composed + node class will be unique to this Tree instance and will not affect any + other instances, nor will it modify the defined `nodeClass` itself. + + This provides a late-binding extension mechanism for nodes that doesn't + require them to extend `Y.Base`, which would incur a significant performance + hit. + + @property {Array} nodeExtensions + @default [] + **/ + nodeExtensions: [], + + /** + Root node of this Tree. + + @property {Tree.Node} rootNode + @readOnly + **/ + + // -- Protected Properties ------------------------------------------------- + + /** + Simple way to type-check that this is a Tree instance. + + @property {Boolean} _isYUITree + @default true + @protected + **/ + _isYUITree: true, + + /** + Composed node class based on `nodeClass` that mixes in any extensions + specified in `nodeExtensions`. If there are no extensions, this will just be + a reference to `nodeClass`. + + @property {Tree.Node} _nodeClass + @protected + **/ + + /** + Mapping of node ids to node instances for nodes in this tree. + + @property {Object} _nodeMap + @protected + **/ + + /** + Default config object for the root node. + + @property {Object} _rootNodeConfig + @protected + **/ + _rootNodeConfig: {canHaveChildren: true}, + + // -- Lifecycle ------------------------------------------------------------ + initializer: function (config) { + config || (config = {}); + + if (config.nodeClass) { + this.nodeClass = config.nodeClass; + } + + if (config.nodeExtensions) { + this.nodeExtensions = this.nodeExtensions.concat(config.nodeExtensions); + } + + /** + Hash of published custom events. + + @property {Object} _published + @default {} + @protected + **/ + this._published || (this._published = {}); + this._nodeMap = {}; + + // Allow all extensions to initialize, then finish up. + this.onceAfter('initializedChange', function () { + this._composeNodeClass(); + + this.clear(config.rootNode, {silent: true}); + + if (config.nodes) { + this.insertNode(this.rootNode, config.nodes, {silent: true}); + } + }); + }, + + destructor: function () { + this.destroyNode(this.rootNode, {silent: true}); + + this.children = null; + this.rootNode = null; + this._nodeClass = null; + this._nodeMap = null; + this._published = null; + }, + + // -- Public Methods ------------------------------------------------------- + + /** + Appends a node or array of nodes as the last child of the specified parent + node. + + If a node being appended is from another tree, it and all its children will + be removed from that tree and moved to this one. + + @method appendNode + @param {Tree.Node} parent Parent node. + @param {Object|Object[]|Tree.Node|Tree.Node[]} node Child node, node config + object, array of child nodes, or array of node config objects to append + to the given parent. Node config objects will automatically be converted + into node instances. + @param {Object} [options] Options. + @param {Boolean} [options.silent=false] If `true`, the `add` event will + be suppressed. + @return {Tree.Node|Tree.Node[]} Node or array of nodes that were + appended. + **/ + appendNode: function (parent, node, options) { + return this.insertNode(parent, node, Y.merge(options, { + index: parent.children.length, + src : 'append' + })); + }, + + /** + Clears this tree by destroying the root node and all its children. If a + `rootNode` argument is provided, that node will become the root node of this + tree; otherwise, a new root node will be created. + + @method clear + @param {Object|Tree.Node} [rootNode] If specified, this node will be used as + the new root node. + @param {Object} [options] Options. + @param {Boolean} [options.silent=false] If `true`, the `clear` event + will be suppressed. + @param {String} [options.src] Source of the change, to be passed along + to the event facade of the resulting event. This can be used to + distinguish between changes triggered by a user and changes + triggered programmatically, for example. + @chainable + **/ + clear: function (rootNode, options) { + return this._fireTreeEvent(EVT_CLEAR, { + rootNode: this.createNode(rootNode || this._rootNodeConfig), + src : options && options.src + }, { + defaultFn: this._defClearFn, + silent : options && options.silent + }); + }, + + /** + Creates and returns a new `Tree.Node` instance associated with (but not + yet appended to) this tree. + + @method createNode + @param {Object|Tree.Node} [config] Node configuration. If a `Tree.Node` + instance is specified instead of a config object, that node will be + adopted into this tree (if it doesn't already belong to this tree) and + removed from any other tree to which it belongs. + @return {Tree.Node} New node. + **/ + createNode: function (config) { + /*jshint boss:true */ + config || (config = {}); + + // If `config` is already a node, just ensure it's in the node map and + // return it. + if (config._isYUITreeNode) { + this._adoptNode(config); + return config; + } + + // First, create nodes for any children of this node. + if (config.children) { + var children = []; + + for (var i = 0, len = config.children.length; i < len; i++) { + children.push(this.createNode(config.children[i])); + } + + config = Y.merge(config, {children: children}); + } + + var node = new this._nodeClass(this, config); + + return this._nodeMap[node.id] = node; + }, + + /** + Removes and destroys a node and all its child nodes. Once destroyed, a node + is eligible for garbage collection and cannot be reused or re-added to the + tree. + + @method destroyNode + @param {Tree.Node} node Node to destroy. + @param {Object} [options] Options. + @param {Boolean} [options.silent=false] If `true`, `remove` events will + be suppressed. + @param {String} [options.src] Source of the change, to be passed along + to the event facade of the resulting events. This can be used to + distinguish between changes triggered by a user and changes + triggered programmatically, for example. + @chainable + **/ + destroyNode: function (node, options) { + var child, i, len; + + options || (options = {}); + + for (i = 0, len = node.children.length; i < len; i++) { + child = node.children[i]; + + // Manually remove the child from its parent; this makes destroying + // all children of the parent much faster since there's no splicing + // involved. + child.parent = null; + + // Destroy the child. + this.destroyNode(child, options); + } + + if (node.parent) { + this.removeNode(node, options); + } + + node.children = null; + node.data = null; + node.state = {destroyed: true}; + node.tree = null; + node._htmlNode = null; + node._indexMap = null; + + delete this._nodeMap[node.id]; + + return this; + }, + + /** + Removes all children from the specified node. The removed children will + still be reusable unless the `destroy` option is truthy. + + @method emptyNode + @param {Tree.Node} node Node to empty. + @param {Object} [options] Options. + @param {Boolean} [options.destroy=false] If `true`, the children will + also be destroyed, which makes them available for garbage collection + and means they can't be reused. + @param {Boolean} [options.silent=false] If `true`, `remove` events will + be suppressed. + @param {String} [options.src] Source of the change, to be passed along + to the event facade of the resulting events. This can be used to + distinguish between changes triggered by a user and changes + triggered programmatically, for example. + @return {Tree.Node[]} Array of removed child nodes. + **/ + emptyNode: function (node, options) { + var removed = []; + + while (node.children.length) { + removed.push(this.removeNode(node.children[0], options)); + } + + return removed; + }, + + /** + Performs a depth-first traversal of _node_, passing it and each of its + descendants to the specified _callback_, and returning the first node for + which the callback returns a truthy value. + + Traversal will stop as soon as a truthy value is returned from the callback. + + See `traverseNode()` for more details on how depth-first traversal works. + + @method findNode + @param {Tree.Node} node Node to traverse. + @param {Object} [options] Options. + @param {Number} [options.depth] Depth limit. If specified, descendants + will only be traversed to this depth before backtracking and moving + on. + @param {Function} callback Callback function to call with the traversed + node and each of its descendants. If this function returns a truthy + value, traversal will be stopped and the current node will be returned. + + @param {Tree.Node} callback.node Node being traversed. + + @param {Object} [thisObj] `this` object to use when executing _callback_. + @return {Tree.Node|null} Returns the first node for which the _callback_ + returns a truthy value, or `null` if the callback never returns a truthy + value. + **/ + findNode: function (node, options, callback, thisObj) { + var match = null; + + // Allow callback as second argument. + if (typeof options === 'function') { + thisObj = callback; + callback = options; + options = {}; + } + + this.traverseNode(node, options, function (descendant) { + if (callback.call(thisObj, descendant)) { + match = descendant; + return Tree.STOP_TRAVERSAL; + } + }); + + return match; + }, + + /** + Returns the tree node with the specified id, or `undefined` if the node + doesn't exist in this tree. + + @method getNodeById + @param {String} id Node id. + @return {Tree.Node} Node, or `undefined` if not found. + **/ + getNodeById: function (id) { + return this._nodeMap[id]; + }, + + /** + Inserts a node or array of nodes at the specified index under the given + parent node, or appends them to the parent if no index is specified. + + If a node being inserted is from another tree, it and all its children will + be removed from that tree and moved to this one. + + @method insertNode + @param {Tree.Node} parent Parent node. + @param {Object|Object[]|Tree.Node|Tree.Node[]} node Child node, node config + object, array of child nodes, or array of node config objects to insert + under the given parent. Node config objects will automatically be + converted into node instances. + + @param {Object} [options] Options. + @param {Number} [options.index] Index at which to insert the child node. + If not specified, the node will be appended as the last child of the + parent. + @param {Boolean} [options.silent=false] If `true`, the `add` event will + be suppressed. + @param {String} [options.src='insert'] Source of the change, to be + passed along to the event facade of the resulting event. This can be + used to distinguish between changes triggered by a user and changes + triggered programmatically, for example. + + @return {Tree.Node[]} Node or array of nodes that were inserted. + **/ + insertNode: function (parent, node, options) { + options || (options = {}); + parent || (parent = this.rootNode); + + // If `node` is an array, recurse to insert each node it contains. + // + // Note: If you're getting an exception here because `node` is null when + // you've passed in a reference to some other node's `children` array, + // that's happening because nodes must be removed from their current + // parent before being added to the new one, and the `children` array is + // being modified while the nodes are inserted. + // + // Solution: pass a copy of the other node's `children` array instead of + // the original. Doing the copy operation here would have a negative + // impact on performance, so you're on your own since this is such a + // rare edge case. + if ('length' in node && Lang.isArray(node)) { + var inserted = []; + + for (var i = 0, len = node.length; i < len; i++) { + inserted.push(this.insertNode(parent, node[i], options)); + + if ('index' in options) { + options.index += 1; + } + } + + return inserted; + } + + node = this.createNode(node); + + var index = options.index; + + if (typeof index === 'undefined') { + index = this._getDefaultNodeIndex(parent, node, options); + } + + this._fireTreeEvent(EVT_ADD, { + index : index, + node : node, + parent: parent, + src : options.src || 'insert' + }, { + defaultFn: this._defAddFn, + silent : options.silent + }); + + return node; + }, + + /** + Prepends a node or array of nodes at the beginning of the specified parent + node. + + If a node being prepended is from another tree, it and all its children will + be removed from that tree and moved to this one. + + @method prependNode + @param {Tree.Node} parent Parent node. + @param {Object|Object[]|Tree.Node|Tree.Node[]} node Child node, + node config object, array of child nodes, or array of node config + objects to prepend to the given parent. Node config objects will + automatically be converted into node instances. + @param {Object} [options] Options. + @param {Boolean} [options.silent=false] If `true`, the `add` event will + be suppressed. + @return {Tree.Node|Tree.Node[]} Node or array of nodes that were + prepended. + **/ + prependNode: function (parent, node, options) { + return this.insertNode(parent, node, Y.merge(options, { + index: 0, + src : 'prepend' + })); + }, + + /** + Removes the specified node from its parent node. The removed node will still + be reusable unless the `destroy` option is truthy. + + @method removeNode + @param {Tree.Node} node Node to remove. + @param {Object} [options] Options. + @param {Boolean} [options.destroy=false] If `true`, the node and all its + children will also be destroyed, which makes them available for + garbage collection and means they can't be reused. + @param {Boolean} [options.silent=false] If `true`, the `remove` event + will be suppressed. + @param {String} [options.src] Source of the change, to be passed along + to the event facade of the resulting event. This can be used to + distinguish between changes triggered by a user and changes + triggered programmatically, for example. + @return {Tree.Node} Node that was removed. + **/ + removeNode: function (node, options) { + options || (options = {}); + + this._fireTreeEvent(EVT_REMOVE, { + destroy: !!options.destroy, + node : node, + parent : node.parent, + src : options.src || 'remove' + }, { + defaultFn: this._defRemoveFn, + silent : options.silent + }); + + return node; + }, + + /** + Returns the total number of nodes in this tree, at all levels. + + Use `rootNode.children.length` to get only the number of top-level nodes. + + @method size + @return {Number} Total number of nodes in this tree. + **/ + size: function () { + return this.rootNode.size() + 1; + }, + + /** + Serializes this tree to an object suitable for use in JSON. + + @method toJSON + @return {Object} Serialized tree object. + **/ + toJSON: function () { + return this.rootNode.toJSON(); + }, + + /** + Performs a depth-first traversal of _node_, passing it and each of its + descendants to the specified _callback_. + + If the callback function returns `Tree.STOP_TRAVERSAL`, traversal will be + stopped immediately. Otherwise, it will continue until the deepest + descendant of _node_ has been traversed, or until each branch has been + traversed to the optional maximum depth limit. + + Since traversal is depth-first, that means nodes are traversed like this: + + 1 + / | \ + 2 8 9 + / \ \ + 3 7 10 + / | \ / \ + 4 5 6 11 12 + + @method traverseNode + @param {Tree.Node} node Node to traverse. + @param {Object} [options] Options. + @param {Number} [options.depth] Depth limit. If specified, descendants + will only be traversed to this depth before backtracking and moving + on. + @param {Function} callback Callback function to call with the traversed + node and each of its descendants. + + @param {Tree.Node} callback.node Node being traversed. + + @param {Object} [thisObj] `this` object to use when executing _callback_. + @return {Mixed} Returns `Tree.STOP_TRAVERSAL` if traversal was stopped; + otherwise returns `undefined`. + **/ + traverseNode: function (node, options, callback, thisObj) { + // Allow callback as second argument. + if (typeof options === 'function') { + thisObj = callback; + callback = options; + options = {}; + } + + options || (options = {}); + + var stop = Tree.STOP_TRAVERSAL, + unlimited = typeof options.depth === 'undefined'; + + if (callback.call(thisObj, node) === stop) { + return stop; + } + + var children = node.children; + + if (unlimited || options.depth > 0) { + var childOptions = unlimited ? options : {depth: options.depth - 1}; + + for (var i = 0, len = children.length; i < len; i++) { + if (this.traverseNode(children[i], childOptions, callback, thisObj) === stop) { + return stop; + } + } + } + }, + + // -- Protected Methods ---------------------------------------------------- + + /** + Moves the specified node and all its children from another tree to this + tree. + + @method _adoptNode + @param {Tree.Node} node Node to adopt. + @param {Object} [options] Options to pass along to `removeNode()`. + @protected + **/ + _adoptNode: function (node, options) { + var oldTree = node.tree; + + if (oldTree === this) { + return; + } + + for (var i = 0, len = node.children.length; i < len; i++) { + this._adoptNode(node.children[i], {silent: true}); + } + + oldTree.removeNode(node, options); + delete oldTree._nodeMap[node.id]; + + // If this node isn't an instance of this tree's composed _nodeClass, + // then we need to recreate it to avoid potentially breaking things in + // really weird ways. + if (!(node instanceof this._nodeClass) + || oldTree._nodeClass !== this._nodeClass) { + + node = this.createNode(node.toJSON()); + } + + node.tree = this; + this._nodeMap[node.id] = node; + }, + + /** + Composes a custom late-bound tree node class (if necessary) based on the + classes specified in this Tree's `nodeClass` and `nodeExtensions` + properties. + + The composed class is stored in this Tree's `_nodeClass` property. If + composition wasn't necessary, then `_nodeClass` will just be a reference to + `nodeClass`. + + @method _composeNodeClass + @protected + **/ + _composeNodeClass: function () { + var nodeClass = this.nodeClass, + nodeExtensions = this.nodeExtensions, + composedClass; + + if (typeof nodeClass === 'string') { + // Look for a namespaced node class on `Y`. + nodeClass = Y.Object.getValue(Y, nodeClass.split('.')); + + if (nodeClass) { + this.nodeClass = nodeClass; + } else { + Y.error('Tree: Node class not found: ' + nodeClass); + return; + } + } + + if (!nodeExtensions.length) { + this._nodeClass = nodeClass; + return; + } + + // Compose a new class by mixing extensions into nodeClass. + composedClass = function () { + var extensions = composedClass._nodeExtensions; + + nodeClass.apply(this, arguments); + + for (var i = 0, len = extensions.length; i < len; i++) { + extensions[i].apply(this, arguments); + } + }; + + Y.extend(composedClass, nodeClass); + + for (var i = 0, len = nodeExtensions.length; i < len; i++) { + Y.mix(composedClass.prototype, nodeExtensions[i].prototype, true); + } + + composedClass._nodeExtensions = nodeExtensions; + this._nodeClass = composedClass; + }, + + /** + Utility method for lazily publishing and firing events. + + @method _fireTreeEvent + @param {String} name Event name to fire. + @param {Object} facade Event facade. + @param {Object} [options] Options. + @param {Function} [options.defaultFn] Default handler for this event. + @param {Boolean} [options.silent=false] Whether the default handler + should be executed directly without actually firing the event. + @chainable + @protected + **/ + _fireTreeEvent: function (name, facade, options) { + if (options && options.silent) { + if (options.defaultFn) { + options.defaultFn.call(this, facade); + } + } else { + if (options && options.defaultFn && !this._published[name]) { + this._published[name] = this.publish(name, { + defaultFn: options.defaultFn + }); + } + + this.fire(name, facade); + } + + return this; + }, + + /** + Returns the default insertion index that should be used when _node_ is + inserted as a child of _parent_ without an explicit index. + + The primary purpose of this method is to serve as a hook point for + extensions and plugins that need to customize insertion order. + + @method _getDefaultNodeIndex + @param {Tree.Node} parent Parent node. + @param {Tree.Node} node Node being inserted. + @param {Object} [options] Options passed to `insertNode()`. + @return {Number} Index at which _node_ should be inserted into _parent_'s + `children` array. + @protected + **/ + _getDefaultNodeIndex: function (parent/*, node, options*/) { + return parent.children.length; + }, + + /** + Removes the specified node from its parent node if it has one. + + @method _removeNodeFromParent + @param {Tree.Node} node Node to remove. + @protected + **/ + _removeNodeFromParent: function (node) { + var parent = node.parent, + index; + + if (parent) { + index = parent.indexOf(node); + + if (index > -1) { + parent.children.splice(index, 1); + parent._isIndexStale = true; + node.parent = null; + } + } + }, + + // -- Default Event Handlers ----------------------------------------------- + _defAddFn: function (e) { + var node = e.node, + parent = e.parent; + + // Remove the node from its existing parent if it has one. + if (node.parent) { + this._removeNodeFromParent(node); + } + + // Add the node to its new parent at the desired index. + node.parent = parent; + parent.children.splice(e.index, 0, node); + + parent.canHaveChildren = true; + parent._isIndexStale = true; + }, + + _defClearFn: function (e) { + var newRootNode = e.rootNode; + + if (this.rootNode) { + this.destroyNode(this.rootNode, {silent: true}); + } + + this._nodeMap = {}; + this._nodeMap[newRootNode.id] = newRootNode; + + this.rootNode = newRootNode; + this.children = newRootNode.children; + }, + + _defRemoveFn: function (e) { + var node = e.node; + + if (e.destroy) { + this.destroyNode(node, {silent: true}); + } else if (e.parent) { + this._removeNodeFromParent(node); + } else if (this.rootNode === node) { + // Guess we'll need a new root node! + this.rootNode = this.createNode(this._rootNodeConfig); + this.children = this.rootNode.children; + } + } +}, { + /** + Return this value from a `Tree#traverseNode()` or `Tree.Node#traverse()` + callback to immediately stop traversal. + + @property STOP_TRAVERSAL + @static + **/ + STOP_TRAVERSAL: {} +}); + +Y.Tree = Y.mix(Tree, Y.Tree); + + +}, '3.10.3', {"requires": ["base-build", "tree-node"]});