2 Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 * The treeview widget is a generic tree building tool.
10 * @title TreeView Widget
11 * @requires yahoo, event
13 * @namespace YAHOO.widget
17 * Contains the tree view state data and the root node.
20 * @uses YAHOO.util.EventProvider
22 * @param {string|HTMLElement} id The id of the element, or the element
23 * itself that the tree will be inserted into.
25 YAHOO.widget.TreeView = function(id) {
26 if (id) { this.init(id); }
29 YAHOO.widget.TreeView.prototype = {
32 * The id of tree container element
39 * The host element for this tree
46 * Flat collection of all nodes in this tree. This is a sparse
47 * array, so the length property can't be relied upon for a
48 * node count for the tree.
56 * We lock the tree control while waiting for the dynamic loader to return
63 * The animation to use for expanding children, if any
64 * @property _expandAnim
71 * The animation to use for collapsing children, if any
72 * @property _collapseAnim
79 * The current number of animations that are executing
80 * @property _animCount
87 * The maximum number of animations to run at one time.
94 * Sets up the animation for expanding children
95 * @method setExpandAnim
96 * @param {string} type the type of animation (acceptable values defined
97 * in YAHOO.widget.TVAnim)
99 setExpandAnim: function(type) {
100 this._expandAnim = (YAHOO.widget.TVAnim.isValid(type)) ? type : null;
104 * Sets up the animation for collapsing children
105 * @method setCollapseAnim
106 * @param {string} the type of animation (acceptable values defined in
107 * YAHOO.widget.TVAnim)
109 setCollapseAnim: function(type) {
110 this._collapseAnim = (YAHOO.widget.TVAnim.isValid(type)) ? type : null;
114 * Perform the expand animation if configured, or just show the
115 * element if not configured or too many animations are in progress
116 * @method animateExpand
117 * @param el {HTMLElement} the element to animate
118 * @param node {YAHOO.util.Node} the node that was expanded
119 * @return {boolean} true if animation could be invoked, false otherwise
121 animateExpand: function(el, node) {
123 if (this._expandAnim && this._animCount < this.maxAnim) {
124 // this.locked = true;
126 var a = YAHOO.widget.TVAnim.getAnim(this._expandAnim, el,
127 function() { tree.expandComplete(node); });
130 this.fireEvent("animStart", {
144 * Perform the collapse animation if configured, or just show the
145 * element if not configured or too many animations are in progress
146 * @method animateCollapse
147 * @param el {HTMLElement} the element to animate
148 * @param node {YAHOO.util.Node} the node that was expanded
149 * @return {boolean} true if animation could be invoked, false otherwise
151 animateCollapse: function(el, node) {
153 if (this._collapseAnim && this._animCount < this.maxAnim) {
154 // this.locked = true;
156 var a = YAHOO.widget.TVAnim.getAnim(this._collapseAnim, el,
157 function() { tree.collapseComplete(node); });
160 this.fireEvent("animStart", {
174 * Function executed when the expand animation completes
175 * @method expandComplete
177 expandComplete: function(node) {
179 this.fireEvent("animComplete", {
183 // this.locked = false;
187 * Function executed when the collapse animation completes
188 * @method collapseComplete
190 collapseComplete: function(node) {
192 this.fireEvent("animComplete", {
196 // this.locked = false;
200 * Initializes the tree
202 * @parm {string|HTMLElement} id the id of the element that will hold the tree
209 if ("string" !== typeof id) {
211 this.id = this.generateId(id);
215 * When animation is enabled, this event fires when the animation
219 * @param {YAHOO.widget.Node} node the node that is expanding/collapsing
220 * @parm {String} type the type of animation ("expand" or "collapse")
222 this.createEvent("animStart", this);
225 * When animation is enabled, this event fires when the animation
227 * @event animComplete
229 * @param {YAHOO.widget.Node} node the node that is expanding/collapsing
230 * @parm {String} type the type of animation ("expand" or "collapse")
232 this.createEvent("animComplete", this);
235 * Fires when a node is going to be collapsed. Return false to stop
239 * @param {YAHOO.widget.Node} node the node that is collapsing
241 this.createEvent("collapse", this);
244 * Fires after a node is successfully collapsed. This event will not fire
245 * if the "collapse" event was cancelled.
246 * @event collapseComplete
248 * @param {YAHOO.widget.Node} node the node that was collapsed
250 this.createEvent("collapseComplete", this);
253 * Fires when a node is going to be expanded. Return false to stop
257 * @param {YAHOO.widget.Node} node the node that is expanding
259 this.createEvent("expand", this);
262 * Fires after a node is successfully expanded. This event will not fire
263 * if the "expand" event was cancelled.
264 * @event expandComplete
266 * @param {YAHOO.widget.Node} node the node that was expanded
268 this.createEvent("expandComplete", this);
272 // store a global reference
273 YAHOO.widget.TreeView.trees[this.id] = this;
275 // Set up the root node
276 this.root = new YAHOO.widget.RootNode(this);
278 var LW = YAHOO.widget.LogWriter;
282 // YAHOO.util.Event.onContentReady(this.id, this.handleAvailable, this, true);
283 // YAHOO.util.Event.on(this.id, "click", this.handleClick, this, true);
286 //handleAvailable: function() {
287 //var Event = YAHOO.util.Event;
292 * Renders the tree boilerplate and visible nodes
296 var html = this.root.getHtml();
297 this.getEl().innerHTML = html;
298 this.firstDraw = false;
302 * Returns the tree's host element
304 * @return {HTMLElement} the host element
308 this._el = document.getElementById(this.id);
314 * Nodes register themselves with the tree instance when they are created.
316 * @param node {Node} the node to register
319 regNode: function(node) {
320 this._nodes[node.index] = node;
324 * Returns the root node of this tree
326 * @return {Node} the root node
328 getRoot: function() {
333 * Configures this tree to dynamically load all child data
334 * @method setDynamicLoad
335 * @param {function} fnDataLoader the function that will be called to get the data
336 * @param iconMode {int} configures the icon that is displayed when a dynamic
337 * load node is expanded the first time without children. By default, the
338 * "collapse" icon will be used. If set to 1, the leaf node icon will be
341 setDynamicLoad: function(fnDataLoader, iconMode) {
342 this.root.setDynamicLoad(fnDataLoader, iconMode);
346 * Expands all child nodes. Note: this conflicts with the "multiExpand"
347 * node property. If expand all is called in a tree with nodes that
348 * do not allow multiple siblings to be displayed, only the last sibling
352 expandAll: function() {
354 this.root.expandAll();
359 * Collapses all expanded child nodes in the entire tree.
360 * @method collapseAll
362 collapseAll: function() {
364 this.root.collapseAll();
369 * Returns a node in the tree that has the specified index (this index
370 * is created internally, so this function probably will only be used
371 * in html generated for a given node.)
372 * @method getNodeByIndex
373 * @param {int} nodeIndex the index of the node wanted
374 * @return {Node} the node with index=nodeIndex, null if no match
376 getNodeByIndex: function(nodeIndex) {
377 var n = this._nodes[nodeIndex];
378 return (n) ? n : null;
382 * Returns a node that has a matching property and value in the data
383 * object that was passed into its constructor.
384 * @method getNodeByProperty
385 * @param {object} property the property to search (usually a string)
386 * @param {object} value the value we want to find (usuall an int or string)
387 * @return {Node} the matching node, null if no match
389 getNodeByProperty: function(property, value) {
390 for (var i in this._nodes) {
391 var n = this._nodes[i];
392 if (n.data && value == n.data[property]) {
401 * Returns a collection of nodes that have a matching property
402 * and value in the data object that was passed into its constructor.
403 * @method getNodesByProperty
404 * @param {object} property the property to search (usually a string)
405 * @param {object} value the value we want to find (usuall an int or string)
406 * @return {Array} the matching collection of nodes, null if no match
408 getNodesByProperty: function(property, value) {
410 for (var i in this._nodes) {
411 var n = this._nodes[i];
412 if (n.data && value == n.data[property]) {
417 return (values.length) ? values : null;
421 * Returns the treeview node reference for an anscestor element
422 * of the node, or null if it is not contained within any node
424 * @method getNodeByElement
425 * @param {HTMLElement} the element to test
426 * @return {YAHOO.widget.Node} a node reference or null
428 getNodeByElement: function(el) {
430 var p=el, m, re=/ygtv([^\d]*)(.*)/;
437 return this.getNodeByIndex(m[2]);
443 if (!p || !p.tagName) {
448 while (p.id !== this.id && p.tagName.toLowerCase() !== "body");
454 * Removes the node and its children, and optionally refreshes the
455 * branch of the tree that was affected.
457 * @param {Node} The node to remove
458 * @param {boolean} autoRefresh automatically refreshes branch if true
459 * @return {boolean} False is there was a problem, true otherwise.
461 removeNode: function(node, autoRefresh) {
463 // Don't delete the root node
468 // Get the branch that we may need to refresh
474 // Delete the node and its children
475 this._deleteNode(node);
477 // Refresh the parent of the parent
478 if (autoRefresh && p && p.childrenRendered) {
486 * wait until the animation is complete before deleting
487 * to avoid javascript errors
488 * @method _removeChildren_animComplete
489 * @param o the custom event payload
492 _removeChildren_animComplete: function(o) {
493 this.unsubscribe(this._removeChildren_animComplete);
494 this.removeChildren(o.node);
498 * Deletes this nodes child collection, recursively. Also collapses
499 * the node, and resets the dynamic load flag. The primary use for
500 * this method is to purge a node and allow it to fetch its data
502 * @method removeChildren
503 * @param {Node} node the node to purge
505 removeChildren: function(node) {
508 // wait until the animation is complete before deleting to
509 // avoid javascript errors
510 if (this._collapseAnim) {
511 this.subscribe("animComplete",
512 this._removeChildren_animComplete, this, true);
513 YAHOO.widget.Node.prototype.collapse.call(node);
520 while (node.children.length) {
521 this._deleteNode(node.children[0]);
525 YAHOO.widget.Node.prototype.expand.call(node);
528 node.childrenRendered = false;
529 node.dynamicLoadComplete = false;
535 * Deletes the node and recurses children
536 * @method _deleteNode
539 _deleteNode: function(node) {
540 // Remove all the child nodes first
541 this.removeChildren(node);
543 // Remove the node from the tree
548 * Removes the node from the tree, preserving the child collection
549 * to make it possible to insert the branch into another part of the
550 * tree, or another tree.
552 * @param {Node} the node to remove
554 popNode: function(node) {
557 // Update the parent's collection of children
560 for (var i=0, len=p.children.length;i<len;++i) {
561 if (p.children[i] != node) {
562 a[a.length] = p.children[i];
568 // reset the childrenRendered flag for the parent
569 p.childrenRendered = false;
571 // Update the sibling relationship
572 if (node.previousSibling) {
573 node.previousSibling.nextSibling = node.nextSibling;
576 if (node.nextSibling) {
577 node.nextSibling.previousSibling = node.previousSibling;
581 node.previousSibling = null;
582 node.nextSibling = null;
585 // Update the tree's node collection
586 delete this._nodes[node.index];
591 * TreeView instance toString
593 * @return {string} string representation of the tree
595 toString: function() {
596 return "TreeView " + this.id;
600 * Generates an unique id for an element if it doesn't yet have one
604 generateId: function(el) {
608 id = "yui-tv-auto-id-" + YAHOO.widget.TreeView.counter;
609 ++YAHOO.widget.TreeView.counter;
616 * Abstract method that is executed when a node is expanded
618 * @param node {Node} the node that was expanded
619 * @deprecated use treeobj.subscribe("expand") instead
621 onExpand: function(node) { },
624 * Abstract method that is executed when a node is collapsed.
626 * @param node {Node} the node that was collapsed.
627 * @deprecated use treeobj.subscribe("collapse") instead
629 onCollapse: function(node) { }
633 YAHOO.augment(YAHOO.widget.TreeView, YAHOO.util.EventProvider);
636 * Running count of all nodes created in all trees. This is
637 * used to provide unique identifies for all nodes. Deleting
638 * nodes does not change the nodeCount.
639 * @property YAHOO.widget.TreeView.nodeCount
643 YAHOO.widget.TreeView.nodeCount = 0;
646 * Global cache of tree instances
647 * @property YAHOO.widget.TreeView.trees
652 YAHOO.widget.TreeView.trees = [];
655 * Counter for generating a new unique element id
656 * @property YAHOO.widget.TreeView.counter
660 YAHOO.widget.TreeView.counter = 0;
663 * Global method for getting a tree by its id. Used in the generated
665 * @method YAHOO.widget.TreeView.getTree
666 * @param treeId {String} the id of the tree instance
667 * @return {TreeView} the tree instance requested, null if not found.
670 YAHOO.widget.TreeView.getTree = function(treeId) {
671 var t = YAHOO.widget.TreeView.trees[treeId];
672 return (t) ? t : null;
677 * Global method for getting a node by its id. Used in the generated
679 * @method YAHOO.widget.TreeView.getNode
680 * @param treeId {String} the id of the tree instance
681 * @param nodeIndex {String} the index of the node to return
682 * @return {Node} the node instance requested, null if not found
685 YAHOO.widget.TreeView.getNode = function(treeId, nodeIndex) {
686 var t = YAHOO.widget.TreeView.getTree(treeId);
687 return (t) ? t.getNodeByIndex(nodeIndex) : null;
692 * @method YAHOO.widget.TreeView.addHandler
693 * @param el the elment to bind the handler to
694 * @param {string} sType the type of event handler
695 * @param {function} fn the callback to invoke
698 YAHOO.widget.TreeView.addHandler = function (el, sType, fn) {
699 if (el.addEventListener) {
700 el.addEventListener(sType, fn, false);
701 } else if (el.attachEvent) {
702 el.attachEvent("on" + sType, fn);
708 * @method YAHOO.widget.TreeView.removeHandler
709 * @param el the elment to bind the handler to
710 * @param {string} sType the type of event handler
711 * @param {function} fn the callback to invoke
715 YAHOO.widget.TreeView.removeHandler = function (el, sType, fn) {
716 if (el.removeEventListener) {
717 el.removeEventListener(sType, fn, false);
718 } else if (el.detachEvent) {
719 el.detachEvent("on" + sType, fn);
724 * Attempts to preload the images defined in the styles used to draw the tree by
725 * rendering off-screen elements that use the styles.
726 * @method YAHOO.widget.TreeView.preload
727 * @param {string} prefix the prefix to use to generate the names of the
728 * images to preload, default is ygtv
731 YAHOO.widget.TreeView.preload = function(e, prefix) {
732 prefix = prefix || "ygtv";
735 var styles = ["tn","tm","tmh","tp","tph","ln","lm","lmh","lp","lph","loading"];
736 // var styles = ["tp"];
740 // save the first one for the outer container
741 for (var i=1; i < styles.length; i=i+1) {
742 sb[sb.length] = '<span class="' + prefix + styles[i] + '"> </span>';
745 var f = document.createElement("div");
747 s.className = prefix + styles[0];
748 s.position = "absolute";
753 f.innerHTML = sb.join("");
755 document.body.appendChild(f);
757 YAHOO.widget.TreeView.removeHandler(window,
758 "load", YAHOO.widget.TreeView.preload);
762 YAHOO.widget.TreeView.addHandler(window,
763 "load", YAHOO.widget.TreeView.preload);
766 * The base class for all tree nodes. The node's presentation and behavior in
767 * response to mouse events is handled in Node subclasses.
768 * @namespace YAHOO.widget
770 * @uses YAHOO.util.EventProvider
771 * @param oData {object} a string or object containing the data that will
772 * be used to render this node, and any custom attributes that should be
773 * stored with the node (which is available in noderef.data).
774 * @param oParent {Node} this node's parent node
775 * @param expanded {boolean} the initial expanded/collapsed state
778 YAHOO.widget.Node = function(oData, oParent, expanded) {
779 if (oData) { this.init(oData, oParent, expanded); }
782 YAHOO.widget.Node.prototype = {
785 * The index for this instance obtained from global counter in YAHOO.widget.TreeView.
792 * This node's child node collection.
799 * Tree instance this node is part of
806 * The data linked to this node. This can be any object or primitive
807 * value, and the data can be used in getNodeHtml().
821 * The depth of this node. We start at -1 for the root node.
828 * The href for the node's label. If one is not specified, the href will
829 * be set so that it toggles the node.
836 * The label href target, defaults to current window
843 * The node's expanded/collapsed state
850 * Can multiple children be expanded at once?
851 * @property multiExpand
857 * Should we render children for a collapsed node? It is possible that the
858 * implementer will want to render the hidden data... @todo verify that we
859 * need this, and implement it if we do.
860 * @property renderHidden
866 * This flag is set to true when the html is generated for this node's
867 * children, and set to false when new children are added.
868 * @property childrenRendered
871 childrenRendered: false,
874 * Dynamically loaded nodes only fetch the data the first time they are
875 * expanded. This flag is set to true once the data has been fetched.
876 * @property dynamicLoadComplete
879 dynamicLoadComplete: false,
882 * This node's previous sibling
883 * @property previousSibling
886 previousSibling: null,
889 * This node's next sibling
890 * @property nextSibling
896 * We can set the node up to call an external method to get the child
905 * Function to execute when we need to get this node's child data.
906 * @property dataLoader
912 * This is true for dynamically loading nodes while waiting for the
913 * callback to return.
914 * @property isLoading
920 * The toggle/branch icon will not show if this is set to false. This
921 * could be useful if the implementer wants to have the child contain
922 * extra info about the parent, rather than an actual node.
929 * Used to configure what happens when a dynamic load node is expanded
930 * and we discover that it does not have children. By default, it is
931 * treated as if it still could have children (plus/minus icon). Set
932 * iconMode to have it display like a leaf node instead.
939 * Specifies whether or not the content area of the node should be allowed
948 * If true, the node will alway be rendered as a leaf node. This can be
949 * used to override the presentation when dynamically loading the entire
950 * tree. Setting this to true also disables the dynamic load call for the
966 spacerPath: "http://us.i1.yimg.com/us.yimg.com/i/space.gif",
967 expandedText: "Expanded",
968 collapsedText: "Collapsed",
969 loadingText: "Loading",
973 * Initializes this node, gets some of the properties from the parent
975 * @param oData {object} a string or object containing the data that will
976 * be used to render this node
977 * @param oParent {Node} this node's parent node
978 * @param expanded {boolean} the initial expanded/collapsed state
980 init: function(oData, oParent, expanded) {
984 this.index = YAHOO.widget.TreeView.nodeCount;
985 ++YAHOO.widget.TreeView.nodeCount;
986 this.expanded = expanded;
989 * The parentChange event is fired when a parent element is applied
990 * to the node. This is useful if you need to apply tree-level
991 * properties to a tree that need to happen if a node is moved from
992 * one tree to another.
994 * @event parentChange
997 this.createEvent("parentChange", this);
999 // oParent should never be null except when we create the root node.
1001 oParent.appendChild(this);
1006 * Certain properties for the node cannot be set until the parent
1007 * is known. This is called after the node is inserted into a tree.
1008 * the parent is also applied to this node's children in order to
1009 * make it possible to move a branch from one tree to another.
1010 * @method applyParent
1011 * @param {Node} parentNode this node's parent node
1012 * @return {boolean} true if the application was successful
1014 applyParent: function(parentNode) {
1019 this.tree = parentNode.tree;
1020 this.parent = parentNode;
1021 this.depth = parentNode.depth + 1;
1024 this.href = "javascript:" + this.getToggleLink();
1027 // @todo why was this put here. This causes new nodes added at the
1028 // root level to lose the menu behavior.
1029 // if (! this.multiExpand) {
1030 // this.multiExpand = parentNode.multiExpand;
1033 this.tree.regNode(this);
1034 parentNode.childrenRendered = false;
1036 // cascade update existing children
1037 for (var i=0, len=this.children.length;i<len;++i) {
1038 this.children[i].applyParent(this);
1041 this.fireEvent("parentChange");
1047 * Appends a node to the child collection.
1048 * @method appendChild
1049 * @param childNode {Node} the new node
1050 * @return {Node} the child node
1053 appendChild: function(childNode) {
1054 if (this.hasChildren()) {
1055 var sib = this.children[this.children.length - 1];
1056 sib.nextSibling = childNode;
1057 childNode.previousSibling = sib;
1059 this.children[this.children.length] = childNode;
1060 childNode.applyParent(this);
1062 // part of the IE display issue workaround. If child nodes
1063 // are added after the initial render, and the node was
1064 // instantiated with expanded = true, we need to show the
1065 // children div now that the node has a child.
1066 if (this.childrenRendered && this.expanded) {
1067 this.getChildrenEl().style.display = "";
1074 * Appends this node to the supplied node's child collection
1076 * @param parentNode {Node} the node to append to.
1077 * @return {Node} The appended node
1079 appendTo: function(parentNode) {
1080 return parentNode.appendChild(this);
1084 * Inserts this node before this supplied node
1085 * @method insertBefore
1086 * @param node {Node} the node to insert this node before
1087 * @return {Node} the inserted node
1089 insertBefore: function(node) {
1090 var p = node.parent;
1094 this.tree.popNode(this);
1097 var refIndex = node.isChildOf(p);
1098 p.children.splice(refIndex, 0, this);
1099 if (node.previousSibling) {
1100 node.previousSibling.nextSibling = this;
1102 this.previousSibling = node.previousSibling;
1103 this.nextSibling = node;
1104 node.previousSibling = this;
1106 this.applyParent(p);
1113 * Inserts this node after the supplied node
1114 * @method insertAfter
1115 * @param node {Node} the node to insert after
1116 * @return {Node} the inserted node
1118 insertAfter: function(node) {
1119 var p = node.parent;
1123 this.tree.popNode(this);
1126 var refIndex = node.isChildOf(p);
1128 if (!node.nextSibling) {
1129 this.nextSibling = null;
1130 return this.appendTo(p);
1133 p.children.splice(refIndex + 1, 0, this);
1135 node.nextSibling.previousSibling = this;
1136 this.previousSibling = node;
1137 this.nextSibling = node.nextSibling;
1138 node.nextSibling = this;
1140 this.applyParent(p);
1147 * Returns true if the Node is a child of supplied Node
1149 * @param parentNode {Node} the Node to check
1150 * @return {boolean} The node index if this Node is a child of
1151 * supplied Node, else -1.
1154 isChildOf: function(parentNode) {
1155 if (parentNode && parentNode.children) {
1156 for (var i=0, len=parentNode.children.length; i<len ; ++i) {
1157 if (parentNode.children[i] === this) {
1167 * Returns a node array of this node's siblings, null if none.
1168 * @method getSiblings
1171 getSiblings: function() {
1172 return this.parent.children;
1176 * Shows this node's children
1177 * @method showChildren
1179 showChildren: function() {
1180 if (!this.tree.animateExpand(this.getChildrenEl(), this)) {
1181 if (this.hasChildren()) {
1182 this.getChildrenEl().style.display = "";
1188 * Hides this node's children
1189 * @method hideChildren
1191 hideChildren: function() {
1193 if (!this.tree.animateCollapse(this.getChildrenEl(), this)) {
1194 this.getChildrenEl().style.display = "none";
1199 * Returns the id for this node's container div
1201 * @return {string} the element id
1203 getElId: function() {
1204 return "ygtv" + this.index;
1208 * Returns the id for this node's children div
1209 * @method getChildrenElId
1210 * @return {string} the element id for this node's children div
1212 getChildrenElId: function() {
1213 return "ygtvc" + this.index;
1217 * Returns the id for this node's toggle element
1218 * @method getToggleElId
1219 * @return {string} the toggel element id
1221 getToggleElId: function() {
1222 return "ygtvt" + this.index;
1227 * Returns the id for this node's spacer image. The spacer is positioned
1228 * over the toggle and provides feedback for screen readers.
1229 * @method getSpacerId
1230 * @return {string} the id for the spacer image
1233 getSpacerId: function() {
1234 return "ygtvspacer" + this.index;
1239 * Returns this node's container html element
1241 * @return {HTMLElement} the container html element
1244 return document.getElementById(this.getElId());
1248 * Returns the div that was generated for this node's children
1249 * @method getChildrenEl
1250 * @return {HTMLElement} this node's children div
1252 getChildrenEl: function() {
1253 return document.getElementById(this.getChildrenElId());
1257 * Returns the element that is being used for this node's toggle.
1258 * @method getToggleEl
1259 * @return {HTMLElement} this node's toggle html element
1261 getToggleEl: function() {
1262 return document.getElementById(this.getToggleElId());
1266 * Returns the element that is being used for this node's spacer.
1268 * @return {HTMLElement} this node's spacer html element
1271 getSpacer: function() {
1272 return document.getElementById( this.getSpacerId() ) || {};
1277 getStateText: function() {
1278 if (this.isLoading) {
1279 return this.loadingText;
1280 } else if (this.hasChildren(true)) {
1281 if (this.expanded) {
1282 return this.expandedText;
1284 return this.collapsedText;
1293 * Generates the link that will invoke this node's toggle method
1294 * @method getToggleLink
1295 * @return {string} the javascript url for toggling this node
1297 getToggleLink: function() {
1298 return "YAHOO.widget.TreeView.getNode(\'" + this.tree.id + "\'," +
1299 this.index + ").toggle()";
1303 * Hides this nodes children (creating them if necessary), changes the
1307 collapse: function() {
1308 // Only collapse if currently expanded
1309 if (!this.expanded) { return; }
1311 // fire the collapse event handler
1312 var ret = this.tree.onCollapse(this);
1314 if (false === ret) {
1318 ret = this.tree.fireEvent("collapse", this);
1320 if (false === ret) {
1325 if (!this.getEl()) {
1326 this.expanded = false;
1328 // hide the child div
1329 this.hideChildren();
1330 this.expanded = false;
1335 // this.getSpacer().title = this.getStateText();
1337 ret = this.tree.fireEvent("collapseComplete", this);
1342 * Shows this nodes children (creating them if necessary), changes the
1343 * toggle style, and collapses its siblings if multiExpand is not set.
1346 expand: function(lazySource) {
1347 // Only expand if currently collapsed.
1348 if (this.expanded && !lazySource) {
1354 // When returning from the lazy load handler, expand is called again
1355 // in order to render the new children. The "expand" event already
1356 // fired before fething the new data, so we need to skip it now.
1358 // fire the expand event handler
1359 ret = this.tree.onExpand(this);
1361 if (false === ret) {
1365 ret = this.tree.fireEvent("expand", this);
1368 if (false === ret) {
1372 if (!this.getEl()) {
1373 this.expanded = true;
1377 if (!this.childrenRendered) {
1378 this.getChildrenEl().innerHTML = this.renderChildren();
1382 this.expanded = true;
1386 // this.getSpacer().title = this.getStateText();
1388 // We do an extra check for children here because the lazy
1389 // load feature can expose nodes that have no children.
1391 // if (!this.hasChildren()) {
1392 if (this.isLoading) {
1393 this.expanded = false;
1397 if (! this.multiExpand) {
1398 var sibs = this.getSiblings();
1399 for (var i=0; i<sibs.length; ++i) {
1400 if (sibs[i] != this && sibs[i].expanded) {
1406 this.showChildren();
1408 ret = this.tree.fireEvent("expandComplete", this);
1411 updateIcon: function() {
1413 var el = this.getToggleEl();
1415 el.className = this.getStyle();
1421 * Returns the css style name for the toggle
1423 * @return {string} the css class for this node's toggle
1425 getStyle: function() {
1426 if (this.isLoading) {
1427 return "ygtvloading";
1429 // location top or bottom, middle nodes also get the top style
1430 var loc = (this.nextSibling) ? "t" : "l";
1432 // type p=plus(expand), m=minus(collapase), n=none(no children)
1434 if (this.hasChildren(true) || (this.isDynamic() && !this.getIconMode())) {
1435 // if (this.hasChildren(true)) {
1436 type = (this.expanded) ? "m" : "p";
1439 return "ygtv" + loc + type;
1444 * Returns the hover style for the icon
1445 * @return {string} the css class hover state
1446 * @method getHoverStyle
1448 getHoverStyle: function() {
1449 var s = this.getStyle();
1450 if (this.hasChildren(true) && !this.isLoading) {
1457 * Recursively expands all of this node's children.
1460 expandAll: function() {
1461 for (var i=0;i<this.children.length;++i) {
1462 var c = this.children[i];
1463 if (c.isDynamic()) {
1464 alert("Not supported (lazy load + expand all)");
1466 } else if (! c.multiExpand) {
1467 alert("Not supported (no multi-expand + expand all)");
1477 * Recursively collapses all of this node's children.
1478 * @method collapseAll
1480 collapseAll: function() {
1481 for (var i=0;i<this.children.length;++i) {
1482 this.children[i].collapse();
1483 this.children[i].collapseAll();
1488 * Configures this node for dynamically obtaining the child data
1489 * when the node is first expanded. Calling it without the callback
1490 * will turn off dynamic load for the node.
1491 * @method setDynamicLoad
1492 * @param fmDataLoader {function} the function that will be used to get the data.
1493 * @param iconMode {int} configures the icon that is displayed when a dynamic
1494 * load node is expanded the first time without children. By default, the
1495 * "collapse" icon will be used. If set to 1, the leaf node icon will be
1498 setDynamicLoad: function(fnDataLoader, iconMode) {
1500 this.dataLoader = fnDataLoader;
1501 this._dynLoad = true;
1503 this.dataLoader = null;
1504 this._dynLoad = false;
1508 this.iconMode = iconMode;
1513 * Evaluates if this node is the root node of the tree
1515 * @return {boolean} true if this is the root node
1517 isRoot: function() {
1518 return (this == this.tree.root);
1522 * Evaluates if this node's children should be loaded dynamically. Looks for
1523 * the property both in this instance and the root node. If the tree is
1524 * defined to load all children dynamically, the data callback function is
1525 * defined in the root node
1527 * @return {boolean} true if this node's children are to be loaded dynamically
1529 isDynamic: function() {
1533 return (!this.isRoot() && (this._dynLoad || this.tree.root._dynLoad));
1539 * Returns the current icon mode. This refers to the way childless dynamic
1540 * load nodes appear (this comes into play only after the initial dynamic
1541 * load request produced no children).
1542 * @method getIconMode
1543 * @return {int} 0 for collapse style, 1 for leaf node style
1545 getIconMode: function() {
1546 return (this.iconMode || this.tree.root.iconMode);
1550 * Checks if this node has children. If this node is lazy-loading and the
1551 * children have not been rendered, we do not know whether or not there
1552 * are actual children. In most cases, we need to assume that there are
1553 * children (for instance, the toggle needs to show the expandable
1554 * presentation state). In other times we want to know if there are rendered
1555 * children. For the latter, "checkForLazyLoad" should be false.
1556 * @method hasChildren
1557 * @param checkForLazyLoad {boolean} should we check for unloaded children?
1558 * @return {boolean} true if this has children or if it might and we are
1559 * checking for this condition.
1561 hasChildren: function(checkForLazyLoad) {
1565 return ( this.children.length > 0 ||
1566 (checkForLazyLoad && this.isDynamic() && !this.dynamicLoadComplete) );
1571 * Expands if node is collapsed, collapses otherwise.
1574 toggle: function() {
1575 if (!this.tree.locked && ( this.hasChildren(true) || this.isDynamic()) ) {
1576 if (this.expanded) { this.collapse(); } else { this.expand(); }
1581 * Returns the markup for this node and its children.
1583 * @return {string} the markup for this node and its expanded children.
1585 getHtml: function() {
1587 this.childrenRendered = false;
1590 sb[sb.length] = '<div class="ygtvitem" id="' + this.getElId() + '">';
1591 sb[sb.length] = this.getNodeHtml();
1592 sb[sb.length] = this.getChildrenHtml();
1593 sb[sb.length] = '</div>';
1598 * Called when first rendering the tree. We always build the div that will
1599 * contain this nodes children, but we don't render the children themselves
1600 * unless this node is expanded.
1601 * @method getChildrenHtml
1602 * @return {string} the children container div html and any expanded children
1605 getChildrenHtml: function() {
1609 sb[sb.length] = '<div class="ygtvchildren"';
1610 sb[sb.length] = ' id="' + this.getChildrenElId() + '"';
1612 // This is a workaround for an IE rendering issue, the child div has layout
1613 // in IE, creating extra space if a leaf node is created with the expanded
1614 // property set to true.
1615 if (!this.expanded || !this.hasChildren()) {
1616 sb[sb.length] = ' style="display:none;"';
1618 sb[sb.length] = '>';
1621 // Don't render the actual child node HTML unless this node is expanded.
1622 if ( (this.hasChildren(true) && this.expanded) ||
1623 (this.renderHidden && !this.isDynamic()) ) {
1624 sb[sb.length] = this.renderChildren();
1627 sb[sb.length] = '</div>';
1633 * Generates the markup for the child nodes. This is not done until the node
1635 * @method renderChildren
1636 * @return {string} the html for this node's children
1639 renderChildren: function() {
1644 if (this.isDynamic() && !this.dynamicLoadComplete) {
1645 this.isLoading = true;
1646 this.tree.locked = true;
1648 if (this.dataLoader) {
1652 node.dataLoader(node,
1654 node.loadComplete();
1658 } else if (this.tree.root.dataLoader) {
1662 node.tree.root.dataLoader(node,
1664 node.loadComplete();
1669 return "Error: data loader not found or not specified.";
1675 return this.completeRender();
1680 * Called when we know we have all the child data.
1681 * @method completeRender
1682 * @return {string} children html
1684 completeRender: function() {
1687 for (var i=0; i < this.children.length; ++i) {
1688 // this.children[i].childrenRendered = false;
1689 sb[sb.length] = this.children[i].getHtml();
1692 this.childrenRendered = true;
1698 * Load complete is the callback function we pass to the data provider
1699 * in dynamic load situations.
1700 * @method loadComplete
1702 loadComplete: function() {
1703 this.getChildrenEl().innerHTML = this.completeRender();
1704 this.dynamicLoadComplete = true;
1705 this.isLoading = false;
1707 this.tree.locked = false;
1711 * Returns this node's ancestor at the specified depth.
1712 * @method getAncestor
1713 * @param {int} depth the depth of the ancestor.
1714 * @return {Node} the ancestor
1716 getAncestor: function(depth) {
1717 if (depth >= this.depth || depth < 0) {
1721 var p = this.parent;
1723 while (p.depth > depth) {
1731 * Returns the css class for the spacer at the specified depth for
1732 * this node. If this node's ancestor at the specified depth
1733 * has a next sibling the presentation is different than if it
1734 * does not have a next sibling
1735 * @method getDepthStyle
1736 * @param {int} depth the depth of the ancestor.
1737 * @return {string} the css class for the spacer
1739 getDepthStyle: function(depth) {
1740 return (this.getAncestor(depth).nextSibling) ?
1741 "ygtvdepthcell" : "ygtvblankdepthcell";
1745 * Get the markup for the node. This is designed to be overrided so that we can
1746 * support different types of nodes.
1747 * @method getNodeHtml
1748 * @return {string} The HTML that will render this node.
1750 getNodeHtml: function() {
1755 * Regenerates the html for this node and its children. To be used when the
1756 * node is expanded and new children have been added.
1759 refresh: function() {
1760 // this.loadComplete();
1761 this.getChildrenEl().innerHTML = this.completeRender();
1764 var el = this.getToggleEl();
1766 el.className = this.getStyle();
1774 * @return {string} string representation of the node
1776 toString: function() {
1777 return "Node (" + this.index + ")";
1782 YAHOO.augment(YAHOO.widget.Node, YAHOO.util.EventProvider);
1785 * The default node presentation. The first parameter should be
1786 * either a string that will be used as the node's label, or an object
1787 * that has a string propery called label. By default, the clicking the
1788 * label will toggle the expanded/collapsed state of the node. By
1789 * changing the href property of the instance, this behavior can be
1790 * changed so that the label will go to the specified href.
1791 * @namespace YAHOO.widget
1793 * @extends YAHOO.widget.Node
1795 * @param oData {object} a string or object containing the data that will
1796 * be used to render this node.
1800 * <dd>The text for the node's label</dd>
1802 * <dd>The title attribute for the label anchor</dd>
1804 * <dd>The title attribute for the label anchor</dd>
1806 * <dd>The href for the node's label. By default it is set to
1807 * expand/collapse the node.</dd>
1809 * <dd>The target attribute for the label anchor</dd>
1811 * <dd>A CSS class to apply to the label anchor</dd>
1813 * All other attributes are made available in noderef.data, which
1814 * can be used to store custom attributes. TreeView.getNode(s)ByProperty
1815 * can be used to retreive a node by one of the attributes.
1816 * @param oParent {YAHOO.widget.Node} this node's parent node
1817 * @param expanded {boolean} the initial expanded/collapsed state
1819 YAHOO.widget.TextNode = function(oData, oParent, expanded) {
1822 this.init(oData, oParent, expanded);
1823 this.setUpLabel(oData);
1828 YAHOO.extend(YAHOO.widget.TextNode, YAHOO.widget.Node, {
1831 * The CSS class for the label href. Defaults to ygtvlabel, but can be
1832 * overridden to provide a custom presentation for a specific node.
1833 * @property labelStyle
1836 labelStyle: "ygtvlabel",
1839 * The derived element id of the label for this node
1840 * @property labelElId
1846 * The text for the label. It is assumed that the oData parameter will
1847 * either be a string that will be used as the label, or an object that
1848 * has a property called "label" that we will use.
1854 textNodeParentChange: function() {
1857 * Custom event that is fired when the text node label is clicked. The
1858 * custom event is defined on the tree instance, so there is a single
1859 * event that handles all nodes in the tree. The node clicked is
1860 * provided as an argument
1863 * @for YAHOO.widget.TreeView
1864 * @param {YAHOO.widget.Node} node the node clicked
1866 if (this.tree && !this.tree.hasEvent("labelClick")) {
1867 this.tree.createEvent("labelClick", this.tree);
1873 * Sets up the node label
1874 * @method setUpLabel
1875 * @param oData string containing the label, or an object with a label property
1877 setUpLabel: function(oData) {
1879 // set up the custom event on the tree
1880 this.textNodeParentChange();
1881 this.subscribe("parentChange", this.textNodeParentChange);
1883 if (typeof oData == "string") {
1884 oData = { label: oData };
1886 this.label = oData.label;
1887 this.data.label = oData.label;
1891 this.href = encodeURI(oData.href);
1896 this.target = oData.target;
1900 this.labelStyle = oData.style;
1904 this.title = oData.title;
1907 this.labelElId = "ygtvlabelel" + this.index;
1911 * Returns the label element
1912 * @for YAHOO.widget.TextNode
1913 * @method getLabelEl
1914 * @return {object} the element
1916 getLabelEl: function() {
1917 return document.getElementById(this.labelElId);
1920 // overrides YAHOO.widget.Node
1921 getNodeHtml: function() {
1924 sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">';
1925 sb[sb.length] = '<tr>';
1927 for (var i=0;i<this.depth;++i) {
1928 //sb[sb.length] = '<td><div class="' + this.getDepthStyle(i) + '"> </div></td>';
1929 //sb[sb.length] = '<td><div class="' + this.getDepthStyle(i) + '"></div></td>';
1930 sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"><div class="ygtvspacer"></div></td>';
1933 var getNode = 'YAHOO.widget.TreeView.getNode(\'' +
1934 this.tree.id + '\',' + this.index + ')';
1936 sb[sb.length] = '<td';
1937 // sb[sb.length] = ' onselectstart="return false"';
1938 sb[sb.length] = ' id="' + this.getToggleElId() + '"';
1939 sb[sb.length] = ' class="' + this.getStyle() + '"';
1940 if (this.hasChildren(true)) {
1941 sb[sb.length] = ' onmouseover="this.className=';
1942 sb[sb.length] = getNode + '.getHoverStyle()"';
1943 sb[sb.length] = ' onmouseout="this.className=';
1944 sb[sb.length] = getNode + '.getStyle()"';
1946 sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '">';
1948 sb[sb.length] = '<div class="ygtvspacer">';
1951 sb[sb.length] = '<img id="' + this.getSpacerId() + '"';
1952 sb[sb.length] = ' alt=""';
1953 sb[sb.length] = ' tabindex=0';
1954 sb[sb.length] = ' src="' + this.spacerPath + '"';
1955 sb[sb.length] = ' title="' + this.getStateText() + '"';
1956 sb[sb.length] = ' class="ygtvspacer"';
1957 // sb[sb.length] = ' onkeypress="return ' + getNode + '".onKeyPress()"';
1958 sb[sb.length] = ' />';
1961 //sb[sb.length] = ' ';
1963 sb[sb.length] = '</div>';
1964 sb[sb.length] = '</td>';
1965 sb[sb.length] = '<td ';
1966 sb[sb.length] = (this.nowrap) ? ' nowrap="nowrap" ' : '';
1967 sb[sb.length] = ' >';
1968 sb[sb.length] = '<a';
1969 sb[sb.length] = ' id="' + this.labelElId + '"';
1971 sb[sb.length] = ' title="' + this.title + '"';
1973 sb[sb.length] = ' class="' + this.labelStyle + '"';
1974 sb[sb.length] = ' href="' + this.href + '"';
1975 sb[sb.length] = ' target="' + this.target + '"';
1976 sb[sb.length] = ' onclick="return ' + getNode + '.onLabelClick(' + getNode +')"';
1977 if (this.hasChildren(true)) {
1978 sb[sb.length] = ' onmouseover="document.getElementById(\'';
1979 sb[sb.length] = this.getToggleElId() + '\').className=';
1980 sb[sb.length] = getNode + '.getHoverStyle()"';
1981 sb[sb.length] = ' onmouseout="document.getElementById(\'';
1982 sb[sb.length] = this.getToggleElId() + '\').className=';
1983 sb[sb.length] = getNode + '.getStyle()"';
1985 sb[sb.length] = ' >';
1986 sb[sb.length] = this.label;
1987 sb[sb.length] = '</a>';
1988 sb[sb.length] = '</td>';
1989 sb[sb.length] = '</tr>';
1990 sb[sb.length] = '</table>';
1997 * Executed when the label is clicked. Fires the labelClick custom event.
1998 * @method onLabelClick
1999 * @param me {Node} this node
2000 * @scope the anchor tag clicked
2001 * @return false to cancel the anchor click
2003 onLabelClick: function(me) {
2004 return me.tree.fireEvent("labelClick", me);
2008 toString: function() {
2009 return "TextNode (" + this.index + ") " + this.label;
2014 * A custom YAHOO.widget.Node that handles the unique nature of
2015 * the virtual, presentationless root node.
2016 * @namespace YAHOO.widget
2018 * @extends YAHOO.widget.Node
2019 * @param oTree {YAHOO.widget.TreeView} The tree instance this node belongs to
2022 YAHOO.widget.RootNode = function(oTree) {
2023 // Initialize the node with null params. The root node is a
2024 // special case where the node has no presentation. So we have
2025 // to alter the standard properties a bit.
2026 this.init(null, null, true);
2029 * For the root node, we get the tree reference from as a param
2030 * to the constructor instead of from the parent element.
2035 YAHOO.extend(YAHOO.widget.RootNode, YAHOO.widget.Node, {
2037 // overrides YAHOO.widget.Node
2038 getNodeHtml: function() {
2042 toString: function() {
2046 loadComplete: function() {
2050 collapse: function() {},
2051 expand: function() {}
2055 * This implementation takes either a string or object for the
2056 * oData argument. If is it a string, we will use it for the display
2057 * of this node (and it can contain any html code). If the parameter
2058 * is an object, we look for a parameter called "html" that will be
2059 * used for this node's display.
2060 * @namespace YAHOO.widget
2062 * @extends YAHOO.widget.Node
2064 * @param oData {object} a string or object containing the data that will
2065 * be used to render this node.
2066 * Valid configuration properties:
2069 * <dd>The html content for the node</dd>
2071 * All other attributes are made available in noderef.data, which
2072 * can be used to store custom attributes. TreeView.getNode(s)ByProperty
2073 * can be used to retreive a node by one of the attributes.
2074 * @param oParent {YAHOO.widget.Node} this node's parent node
2075 * @param expanded {boolean} the initial expanded/collapsed state
2076 * @param hasIcon {boolean} specifies whether or not leaf nodes should
2077 * be rendered with or without a horizontal line line icon. If the icon
2078 * is not displayed, the content fills the space it would have occupied.
2079 * This option operates independently of the leaf node presentation logic
2080 * for dynamic nodes.
2082 YAHOO.widget.HTMLNode = function(oData, oParent, expanded, hasIcon) {
2084 this.init(oData, oParent, expanded);
2085 this.initContent(oData, hasIcon);
2089 YAHOO.extend(YAHOO.widget.HTMLNode, YAHOO.widget.Node, {
2092 * The CSS class for the html content container. Defaults to ygtvhtml, but
2093 * can be overridden to provide a custom presentation for a specific node.
2094 * @property contentStyle
2097 contentStyle: "ygtvhtml",
2100 * The generated id that will contain the data passed in by the implementer.
2101 * @property contentElId
2107 * The HTML content to use for this node's display
2114 * Sets up the node label
2115 * @property initContent
2116 * @param oData {object} An html string or object containing an html property
2117 * @param hasIcon {boolean} determines if the node will be rendered with an
2120 initContent: function(oData, hasIcon) {
2121 this.setHtml(oData);
2122 this.contentElId = "ygtvcontentel" + this.index;
2123 this.hasIcon = hasIcon;
2128 * Synchronizes the node.data, node.html, and the node's content
2130 * @param o {object} An html string or object containing an html property
2132 setHtml: function(o) {
2135 this.html = (typeof o === "string") ? o : o.html;
2137 var el = this.getContentEl();
2139 el.innerHTML = this.html;
2145 * Returns the outer html element for this node's content
2146 * @method getContentEl
2147 * @return {HTMLElement} the element
2149 getContentEl: function() {
2150 return document.getElementById(this.contentElId);
2153 // overrides YAHOO.widget.Node
2154 getNodeHtml: function() {
2157 sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">';
2158 sb[sb.length] = '<tr>';
2160 for (var i=0;i<this.depth;++i) {
2161 //sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"> </td>';
2162 sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"><div class="ygtvspacer"></div></td>';
2166 sb[sb.length] = '<td';
2167 sb[sb.length] = ' id="' + this.getToggleElId() + '"';
2168 sb[sb.length] = ' class="' + this.getStyle() + '"';
2169 sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '"';
2170 if (this.hasChildren(true)) {
2171 sb[sb.length] = ' onmouseover="this.className=';
2172 sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
2173 sb[sb.length] = this.tree.id + '\',' + this.index + ').getHoverStyle()"';
2174 sb[sb.length] = ' onmouseout="this.className=';
2175 sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
2176 sb[sb.length] = this.tree.id + '\',' + this.index + ').getStyle()"';
2178 //sb[sb.length] = '> </td>';
2179 sb[sb.length] = '><div class="ygtvspacer"></div></td>';
2182 sb[sb.length] = '<td';
2183 sb[sb.length] = ' id="' + this.contentElId + '"';
2184 sb[sb.length] = ' class="' + this.contentStyle + '"';
2185 sb[sb.length] = (this.nowrap) ? ' nowrap="nowrap" ' : '';
2186 sb[sb.length] = ' >';
2187 sb[sb.length] = this.html;
2188 sb[sb.length] = '</td>';
2189 sb[sb.length] = '</tr>';
2190 sb[sb.length] = '</table>';
2195 toString: function() {
2196 return "HTMLNode (" + this.index + ")";
2201 * A menu-specific implementation that differs from TextNode in that only
2202 * one sibling can be expanded at a time.
2203 * @namespace YAHOO.widget
2205 * @extends YAHOO.widget.TextNode
2206 * @param oData {object} a string or object containing the data that will
2207 * be used to render this node.
2211 * <dd>The text for the node's label</dd>
2213 * <dd>The title attribute for the label anchor</dd>
2215 * <dd>The title attribute for the label anchor</dd>
2217 * <dd>The href for the node's label. By default it is set to
2218 * expand/collapse the node.</dd>
2220 * <dd>The target attribute for the label anchor</dd>
2222 * <dd>A CSS class to apply to the label anchor</dd>
2224 * All other attributes are made available in noderef.data, which
2225 * can be used to store custom attributes. TreeView.getNode(s)ByProperty
2226 * can be used to retreive a node by one of the attributes.
2227 * @param oParent {YAHOO.widget.Node} this node's parent node
2228 * @param expanded {boolean} the initial expanded/collapsed state
2231 YAHOO.widget.MenuNode = function(oData, oParent, expanded) {
2233 this.init(oData, oParent, expanded);
2234 this.setUpLabel(oData);
2238 * Menus usually allow only one branch to be open at a time.
2240 this.multiExpand = false;
2245 YAHOO.extend(YAHOO.widget.MenuNode, YAHOO.widget.TextNode, {
2247 toString: function() {
2248 return "MenuNode (" + this.index + ") " + this.label;
2253 * A static factory class for tree view expand/collapse animations
2257 YAHOO.widget.TVAnim = function() {
2260 * Constant for the fade in animation
2265 FADE_IN: "TVFadeIn",
2268 * Constant for the fade out animation
2269 * @property FADE_OUT
2273 FADE_OUT: "TVFadeOut",
2276 * Returns a ygAnim instance of the given type
2278 * @param type {string} the type of animation
2279 * @param el {HTMLElement} the element to element (probably the children div)
2280 * @param callback {function} function to invoke when the animation is done.
2281 * @return {YAHOO.util.Animation} the animation instance
2284 getAnim: function(type, el, callback) {
2285 if (YAHOO.widget[type]) {
2286 return new YAHOO.widget[type](el, callback);
2293 * Returns true if the specified animation class is available
2295 * @param type {string} the type of animation
2296 * @return {boolean} true if valid, false if not
2299 isValid: function(type) {
2300 return (YAHOO.widget[type]);
2306 * A 1/2 second fade-in animation.
2309 * @param el {HTMLElement} the element to animate
2310 * @param callback {function} function to invoke when the animation is finished
2312 YAHOO.widget.TVFadeIn = function(el, callback) {
2314 * The element to animate
2321 * the callback to invoke when the animation is complete
2322 * @property callback
2325 this.callback = callback;
2329 YAHOO.widget.TVFadeIn.prototype = {
2331 * Performs the animation
2334 animate: function() {
2337 var s = this.el.style;
2339 s.filter = "alpha(opacity=10)";
2343 var a = new YAHOO.util.Anim(this.el, {opacity: {from: 0.1, to: 1, unit:""}}, dur);
2344 a.onComplete.subscribe( function() { tvanim.onComplete(); } );
2349 * Clean up and invoke callback
2350 * @method onComplete
2352 onComplete: function() {
2359 * @return {string} the string representation of the instance
2361 toString: function() {
2367 * A 1/2 second fade out animation.
2370 * @param el {HTMLElement} the element to animate
2371 * @param callback {Function} function to invoke when the animation is finished
2373 YAHOO.widget.TVFadeOut = function(el, callback) {
2375 * The element to animate
2382 * the callback to invoke when the animation is complete
2383 * @property callback
2386 this.callback = callback;
2390 YAHOO.widget.TVFadeOut.prototype = {
2392 * Performs the animation
2395 animate: function() {
2398 var a = new YAHOO.util.Anim(this.el, {opacity: {from: 1, to: 0.1, unit:""}}, dur);
2399 a.onComplete.subscribe( function() { tvanim.onComplete(); } );
2404 * Clean up and invoke callback
2405 * @method onComplete
2407 onComplete: function() {
2408 var s = this.el.style;
2411 s.filter = "alpha(opacity=100)";
2418 * @return {string} the string representation of the instance
2420 toString: function() {
2425 YAHOO.register("treeview", YAHOO.widget.TreeView, {version: "2.5.2", build: "1076"});