author | Giulio Cesare Solaroli <giulio.cesare@clipperz.com> | 2011-10-02 23:56:18 (UTC) |
---|---|---|
committer | Giulio Cesare Solaroli <giulio.cesare@clipperz.com> | 2011-10-02 23:56:18 (UTC) |
commit | ef68436ac04da078ffdcacd7e1f785473a303d45 (patch) (unidiff) | |
tree | c403752d66a2c4775f00affd4fa8431b29c5b68c /frontend/beta/js/YUI/treeview.js | |
parent | 597ecfbc0249d83e1b856cbd558340c01237a360 (diff) | |
download | clipperz-ef68436ac04da078ffdcacd7e1f785473a303d45.zip clipperz-ef68436ac04da078ffdcacd7e1f785473a303d45.tar.gz clipperz-ef68436ac04da078ffdcacd7e1f785473a303d45.tar.bz2 |
First version of the newly restructured repository
Diffstat (limited to 'frontend/beta/js/YUI/treeview.js') (more/less context) (ignore whitespace changes)
-rw-r--r-- | frontend/beta/js/YUI/treeview.js | 2182 |
1 files changed, 2182 insertions, 0 deletions
diff --git a/frontend/beta/js/YUI/treeview.js b/frontend/beta/js/YUI/treeview.js new file mode 100644 index 0000000..ea6b6ef --- a/dev/null +++ b/frontend/beta/js/YUI/treeview.js | |||
@@ -0,0 +1,2182 @@ | |||
1 | /* | ||
2 | Copyright (c) 2006, Yahoo! Inc. All rights reserved. | ||
3 | Code licensed under the BSD License: | ||
4 | http://developer.yahoo.net/yui/license.txt | ||
5 | version: 0.12.0 | ||
6 | */ | ||
7 | |||
8 | /** | ||
9 | * The treeview widget is a generic tree building tool. | ||
10 | * @module treeview | ||
11 | * @title TreeView Widget | ||
12 | * @requires yahoo | ||
13 | * @optional animation | ||
14 | * @namespace YAHOO.widget | ||
15 | */ | ||
16 | |||
17 | /** | ||
18 | * Contains the tree view state data and the root node. | ||
19 | * | ||
20 | * @class TreeView | ||
21 | * @constructor | ||
22 | * @param {string|HTMLElement} id The id of the element, or the element | ||
23 | * itself that the tree will be inserted into. | ||
24 | */ | ||
25 | YAHOO.widget.TreeView = function(id) { | ||
26 | if (id) { this.init(id); } | ||
27 | }; | ||
28 | |||
29 | YAHOO.widget.TreeView.prototype = { | ||
30 | |||
31 | /** | ||
32 | * The id of tree container element | ||
33 | * @property id | ||
34 | * @type String | ||
35 | */ | ||
36 | id: null, | ||
37 | |||
38 | /** | ||
39 | * The host element for this tree | ||
40 | * @property _el | ||
41 | * @private | ||
42 | */ | ||
43 | _el: null, | ||
44 | |||
45 | /** | ||
46 | * Flat collection of all nodes in this tree | ||
47 | * @property _nodes | ||
48 | * @type Node[] | ||
49 | * @private | ||
50 | */ | ||
51 | _nodes: null, | ||
52 | |||
53 | /** | ||
54 | * We lock the tree control while waiting for the dynamic loader to return | ||
55 | * @property locked | ||
56 | * @type boolean | ||
57 | */ | ||
58 | locked: false, | ||
59 | |||
60 | /** | ||
61 | * The animation to use for expanding children, if any | ||
62 | * @property _expandAnim | ||
63 | * @type string | ||
64 | * @private | ||
65 | */ | ||
66 | _expandAnim: null, | ||
67 | |||
68 | /** | ||
69 | * The animation to use for collapsing children, if any | ||
70 | * @property _collapseAnim | ||
71 | * @type string | ||
72 | * @private | ||
73 | */ | ||
74 | _collapseAnim: null, | ||
75 | |||
76 | /** | ||
77 | * The current number of animations that are executing | ||
78 | * @property _animCount | ||
79 | * @type int | ||
80 | * @private | ||
81 | */ | ||
82 | _animCount: 0, | ||
83 | |||
84 | /** | ||
85 | * The maximum number of animations to run at one time. | ||
86 | * @property maxAnim | ||
87 | * @type int | ||
88 | */ | ||
89 | maxAnim: 2, | ||
90 | |||
91 | /** | ||
92 | * Sets up the animation for expanding children | ||
93 | * @method setExpandAnim | ||
94 | * @param {string} type the type of animation (acceptable values defined | ||
95 | * in YAHOO.widget.TVAnim) | ||
96 | */ | ||
97 | setExpandAnim: function(type) { | ||
98 | if (YAHOO.widget.TVAnim.isValid(type)) { | ||
99 | this._expandAnim = type; | ||
100 | } | ||
101 | }, | ||
102 | |||
103 | /** | ||
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) | ||
108 | */ | ||
109 | setCollapseAnim: function(type) { | ||
110 | if (YAHOO.widget.TVAnim.isValid(type)) { | ||
111 | this._collapseAnim = type; | ||
112 | } | ||
113 | }, | ||
114 | |||
115 | /** | ||
116 | * Perform the expand animation if configured, or just show the | ||
117 | * element if not configured or too many animations are in progress | ||
118 | * @method animateExpand | ||
119 | * @param el {HTMLElement} the element to animate | ||
120 | * @param node {YAHOO.util.Node} the node that was expanded | ||
121 | * @return {boolean} true if animation could be invoked, false otherwise | ||
122 | */ | ||
123 | animateExpand: function(el, node) { | ||
124 | |||
125 | if (this._expandAnim && this._animCount < this.maxAnim) { | ||
126 | // this.locked = true; | ||
127 | var tree = this; | ||
128 | var a = YAHOO.widget.TVAnim.getAnim(this._expandAnim, el, | ||
129 | function() { tree.expandComplete(node); }); | ||
130 | if (a) { | ||
131 | ++this._animCount; | ||
132 | this.fireEvent("animStart", { | ||
133 | "node": node, | ||
134 | "type": "expand" | ||
135 | }); | ||
136 | a.animate(); | ||
137 | } | ||
138 | |||
139 | return true; | ||
140 | } | ||
141 | |||
142 | return false; | ||
143 | }, | ||
144 | |||
145 | /** | ||
146 | * Perform the collapse animation if configured, or just show the | ||
147 | * element if not configured or too many animations are in progress | ||
148 | * @method animateCollapse | ||
149 | * @param el {HTMLElement} the element to animate | ||
150 | * @param node {YAHOO.util.Node} the node that was expanded | ||
151 | * @return {boolean} true if animation could be invoked, false otherwise | ||
152 | */ | ||
153 | animateCollapse: function(el, node) { | ||
154 | |||
155 | if (this._collapseAnim && this._animCount < this.maxAnim) { | ||
156 | // this.locked = true; | ||
157 | var tree = this; | ||
158 | var a = YAHOO.widget.TVAnim.getAnim(this._collapseAnim, el, | ||
159 | function() { tree.collapseComplete(node); }); | ||
160 | if (a) { | ||
161 | ++this._animCount; | ||
162 | this.fireEvent("animStart", { | ||
163 | "node": node, | ||
164 | "type": "collapse" | ||
165 | }); | ||
166 | a.animate(); | ||
167 | } | ||
168 | |||
169 | return true; | ||
170 | } | ||
171 | |||
172 | return false; | ||
173 | }, | ||
174 | |||
175 | /** | ||
176 | * Function executed when the expand animation completes | ||
177 | * @method expandComplete | ||
178 | */ | ||
179 | expandComplete: function(node) { | ||
180 | --this._animCount; | ||
181 | this.fireEvent("animComplete", { | ||
182 | "node": node, | ||
183 | "type": "expand" | ||
184 | }); | ||
185 | // this.locked = false; | ||
186 | }, | ||
187 | |||
188 | /** | ||
189 | * Function executed when the collapse animation completes | ||
190 | * @method collapseComplete | ||
191 | */ | ||
192 | collapseComplete: function(node) { | ||
193 | --this._animCount; | ||
194 | this.fireEvent("animComplete", { | ||
195 | "node": node, | ||
196 | "type": "collapse" | ||
197 | }); | ||
198 | // this.locked = false; | ||
199 | }, | ||
200 | |||
201 | /** | ||
202 | * Initializes the tree | ||
203 | * @method init | ||
204 | * @parm {string|HTMLElement} id the id of the element that will hold the tree | ||
205 | * @private | ||
206 | */ | ||
207 | init: function(id) { | ||
208 | |||
209 | this.id = id; | ||
210 | |||
211 | if ("string" !== typeof id) { | ||
212 | this._el = id; | ||
213 | this.id = this.generateId(id); | ||
214 | } | ||
215 | |||
216 | /** | ||
217 | * When animation is enabled, this event fires when the animation | ||
218 | * starts | ||
219 | * @event animStart | ||
220 | * @type CustomEvent | ||
221 | * @param {YAHOO.widget.Node} node the node that is expanding/collapsing | ||
222 | * @parm {String} type the type of animation ("expand" or "collapse") | ||
223 | */ | ||
224 | this.createEvent("animStart", this); | ||
225 | |||
226 | /** | ||
227 | * When animation is enabled, this event fires when the animation | ||
228 | * completes | ||
229 | * @event animComplete | ||
230 | * @type CustomEvent | ||
231 | * @param {YAHOO.widget.Node} node the node that is expanding/collapsing | ||
232 | * @parm {String} type the type of animation ("expand" or "collapse") | ||
233 | */ | ||
234 | this.createEvent("animComplete", this); | ||
235 | |||
236 | /** | ||
237 | * Fires when a node is going to be expanded. Return false to stop | ||
238 | * the expand. | ||
239 | * @event collapse | ||
240 | * @type CustomEvent | ||
241 | * @param {YAHOO.widget.Node} node the node that is expanding/collapsing | ||
242 | */ | ||
243 | this.createEvent("collapse", this); | ||
244 | |||
245 | /** | ||
246 | * Fires when a node is going to be collapsed. Return false to stop | ||
247 | * the collapse. | ||
248 | * @event expand | ||
249 | * @type CustomEvent | ||
250 | * @param {YAHOO.widget.Node} node the node that is expanding/collapsing | ||
251 | */ | ||
252 | this.createEvent("expand", this); | ||
253 | |||
254 | this._nodes = []; | ||
255 | |||
256 | // store a global reference | ||
257 | YAHOO.widget.TreeView.trees[this.id] = this; | ||
258 | |||
259 | // Set up the root node | ||
260 | this.root = new YAHOO.widget.RootNode(this); | ||
261 | |||
262 | |||
263 | }, | ||
264 | |||
265 | /** | ||
266 | * Renders the tree boilerplate and visible nodes | ||
267 | * @method draw | ||
268 | */ | ||
269 | draw: function() { | ||
270 | var html = this.root.getHtml(); | ||
271 | this.getEl().innerHTML = html; | ||
272 | this.firstDraw = false; | ||
273 | }, | ||
274 | |||
275 | /** | ||
276 | * Returns the tree's host element | ||
277 | * @method getEl | ||
278 | * @return {HTMLElement} the host element | ||
279 | */ | ||
280 | getEl: function() { | ||
281 | if (! this._el) { | ||
282 | this._el = document.getElementById(this.id); | ||
283 | } | ||
284 | return this._el; | ||
285 | }, | ||
286 | |||
287 | /** | ||
288 | * Nodes register themselves with the tree instance when they are created. | ||
289 | * @method regNode | ||
290 | * @param node {Node} the node to register | ||
291 | * @private | ||
292 | */ | ||
293 | regNode: function(node) { | ||
294 | this._nodes[node.index] = node; | ||
295 | }, | ||
296 | |||
297 | /** | ||
298 | * Returns the root node of this tree | ||
299 | * @method getRoot | ||
300 | * @return {Node} the root node | ||
301 | */ | ||
302 | getRoot: function() { | ||
303 | return this.root; | ||
304 | }, | ||
305 | |||
306 | /** | ||
307 | * Configures this tree to dynamically load all child data | ||
308 | * @method setDynamicLoad | ||
309 | * @param {function} fnDataLoader the function that will be called to get the data | ||
310 | * @param iconMode {int} configures the icon that is displayed when a dynamic | ||
311 | * load node is expanded the first time without children. By default, the | ||
312 | * "collapse" icon will be used. If set to 1, the leaf node icon will be | ||
313 | * displayed. | ||
314 | */ | ||
315 | setDynamicLoad: function(fnDataLoader, iconMode) { | ||
316 | this.root.setDynamicLoad(fnDataLoader, iconMode); | ||
317 | }, | ||
318 | |||
319 | /** | ||
320 | * Expands all child nodes. Note: this conflicts with the "multiExpand" | ||
321 | * node property. If expand all is called in a tree with nodes that | ||
322 | * do not allow multiple siblings to be displayed, only the last sibling | ||
323 | * will be expanded. | ||
324 | * @method expandAll | ||
325 | */ | ||
326 | expandAll: function() { | ||
327 | if (!this.locked) { | ||
328 | this.root.expandAll(); | ||
329 | } | ||
330 | }, | ||
331 | |||
332 | /** | ||
333 | * Collapses all expanded child nodes in the entire tree. | ||
334 | * @method collapseAll | ||
335 | */ | ||
336 | collapseAll: function() { | ||
337 | if (!this.locked) { | ||
338 | this.root.collapseAll(); | ||
339 | } | ||
340 | }, | ||
341 | |||
342 | /** | ||
343 | * Returns a node in the tree that has the specified index (this index | ||
344 | * is created internally, so this function probably will only be used | ||
345 | * in html generated for a given node.) | ||
346 | * @method getNodeByIndex | ||
347 | * @param {int} nodeIndex the index of the node wanted | ||
348 | * @return {Node} the node with index=nodeIndex, null if no match | ||
349 | */ | ||
350 | getNodeByIndex: function(nodeIndex) { | ||
351 | var n = this._nodes[nodeIndex]; | ||
352 | return (n) ? n : null; | ||
353 | }, | ||
354 | |||
355 | /** | ||
356 | * Returns a node that has a matching property and value in the data | ||
357 | * object that was passed into its constructor. | ||
358 | * @method getNodeByProperty | ||
359 | * @param {object} property the property to search (usually a string) | ||
360 | * @param {object} value the value we want to find (usuall an int or string) | ||
361 | * @return {Node} the matching node, null if no match | ||
362 | */ | ||
363 | getNodeByProperty: function(property, value) { | ||
364 | for (var i in this._nodes) { | ||
365 | var n = this._nodes[i]; | ||
366 | if (n.data && value == n.data[property]) { | ||
367 | return n; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | return null; | ||
372 | }, | ||
373 | |||
374 | /** | ||
375 | * Returns a collection of nodes that have a matching property | ||
376 | * and value in the data object that was passed into its constructor. | ||
377 | * @method getNodesByProperty | ||
378 | * @param {object} property the property to search (usually a string) | ||
379 | * @param {object} value the value we want to find (usuall an int or string) | ||
380 | * @return {Array} the matching collection of nodes, null if no match | ||
381 | */ | ||
382 | getNodesByProperty: function(property, value) { | ||
383 | var values = []; | ||
384 | for (var i in this._nodes) { | ||
385 | var n = this._nodes[i]; | ||
386 | if (n.data && value == n.data[property]) { | ||
387 | values.push(n); | ||
388 | } | ||
389 | } | ||
390 | |||
391 | return (values.length) ? values : null; | ||
392 | }, | ||
393 | |||
394 | /** | ||
395 | * Removes the node and its children, and optionally refreshes the | ||
396 | * branch of the tree that was affected. | ||
397 | * @method removeNode | ||
398 | * @param {Node} The node to remove | ||
399 | * @param {boolean} autoRefresh automatically refreshes branch if true | ||
400 | * @return {boolean} False is there was a problem, true otherwise. | ||
401 | */ | ||
402 | removeNode: function(node, autoRefresh) { | ||
403 | |||
404 | // Don't delete the root node | ||
405 | if (node.isRoot()) { | ||
406 | return false; | ||
407 | } | ||
408 | |||
409 | // Get the branch that we may need to refresh | ||
410 | var p = node.parent; | ||
411 | if (p.parent) { | ||
412 | p = p.parent; | ||
413 | } | ||
414 | |||
415 | // Delete the node and its children | ||
416 | this._deleteNode(node); | ||
417 | |||
418 | // Refresh the parent of the parent | ||
419 | if (autoRefresh && p && p.childrenRendered) { | ||
420 | p.refresh(); | ||
421 | } | ||
422 | |||
423 | return true; | ||
424 | }, | ||
425 | |||
426 | /** | ||
427 | * Deletes this nodes child collection, recursively. Also collapses | ||
428 | * the node, and resets the dynamic load flag. The primary use for | ||
429 | * this method is to purge a node and allow it to fetch its data | ||
430 | * dynamically again. | ||
431 | * @method removeChildren | ||
432 | * @param {Node} node the node to purge | ||
433 | */ | ||
434 | removeChildren: function(node) { | ||
435 | while (node.children.length) { | ||
436 | this._deleteNode(node.children[0]); | ||
437 | } | ||
438 | |||
439 | node.childrenRendered = false; | ||
440 | node.dynamicLoadComplete = false; | ||
441 | if (node.expanded) { | ||
442 | node.collapse(); | ||
443 | } else { | ||
444 | node.updateIcon(); | ||
445 | } | ||
446 | }, | ||
447 | |||
448 | /** | ||
449 | * Deletes the node and recurses children | ||
450 | * @method _deleteNode | ||
451 | * @private | ||
452 | */ | ||
453 | _deleteNode: function(node) { | ||
454 | // Remove all the child nodes first | ||
455 | this.removeChildren(node); | ||
456 | |||
457 | // Remove the node from the tree | ||
458 | this.popNode(node); | ||
459 | }, | ||
460 | |||
461 | /** | ||
462 | * Removes the node from the tree, preserving the child collection | ||
463 | * to make it possible to insert the branch into another part of the | ||
464 | * tree, or another tree. | ||
465 | * @method popNode | ||
466 | * @param {Node} the node to remove | ||
467 | */ | ||
468 | popNode: function(node) { | ||
469 | var p = node.parent; | ||
470 | |||
471 | // Update the parent's collection of children | ||
472 | var a = []; | ||
473 | |||
474 | for (var i=0, len=p.children.length;i<len;++i) { | ||
475 | if (p.children[i] != node) { | ||
476 | a[a.length] = p.children[i]; | ||
477 | } | ||
478 | } | ||
479 | |||
480 | p.children = a; | ||
481 | |||
482 | // reset the childrenRendered flag for the parent | ||
483 | p.childrenRendered = false; | ||
484 | |||
485 | // Update the sibling relationship | ||
486 | if (node.previousSibling) { | ||
487 | node.previousSibling.nextSibling = node.nextSibling; | ||
488 | } | ||
489 | |||
490 | if (node.nextSibling) { | ||
491 | node.nextSibling.previousSibling = node.previousSibling; | ||
492 | } | ||
493 | |||
494 | node.parent = null; | ||
495 | node.previousSibling = null; | ||
496 | node.nextSibling = null; | ||
497 | node.tree = null; | ||
498 | |||
499 | // Update the tree's node collection | ||
500 | delete this._nodes[node.index]; | ||
501 | }, | ||
502 | |||
503 | /** | ||
504 | * TreeView instance toString | ||
505 | * @method toString | ||
506 | * @return {string} string representation of the tree | ||
507 | */ | ||
508 | toString: function() { | ||
509 | return "TreeView " + this.id; | ||
510 | }, | ||
511 | |||
512 | /** | ||
513 | * Generates an unique id for an element if it doesn't yet have one | ||
514 | * @method generateId | ||
515 | * @private | ||
516 | */ | ||
517 | generateId: function(el) { | ||
518 | var id = el.id; | ||
519 | |||
520 | if (!id) { | ||
521 | id = "yui-tv-auto-id-" + YAHOO.widget.TreeView.counter; | ||
522 | ++YAHOO.widget.TreeView.counter; | ||
523 | } | ||
524 | |||
525 | return id; | ||
526 | }, | ||
527 | |||
528 | /** | ||
529 | * Abstract method that is executed when a node is expanded | ||
530 | * @method onExpand | ||
531 | * @param node {Node} the node that was expanded | ||
532 | * @deprecated use treeobj.subscribe("expand") instead | ||
533 | */ | ||
534 | onExpand: function(node) { }, | ||
535 | |||
536 | /** | ||
537 | * Abstract method that is executed when a node is collapsed. | ||
538 | * @method onCollapse | ||
539 | * @param node {Node} the node that was collapsed. | ||
540 | * @deprecated use treeobj.subscribe("collapse") instead | ||
541 | */ | ||
542 | onCollapse: function(node) { } | ||
543 | |||
544 | }; | ||
545 | |||
546 | YAHOO.augment(YAHOO.widget.TreeView, YAHOO.util.EventProvider); | ||
547 | |||
548 | /** | ||
549 | * Count of all nodes in all trees | ||
550 | * @property YAHOO.widget.TreeView.nodeCount | ||
551 | * @type int | ||
552 | * @static | ||
553 | */ | ||
554 | YAHOO.widget.TreeView.nodeCount = 0; | ||
555 | |||
556 | /** | ||
557 | * Global cache of tree instances | ||
558 | * @property YAHOO.widget.TreeView.trees | ||
559 | * @type Array | ||
560 | * @static | ||
561 | * @private | ||
562 | */ | ||
563 | YAHOO.widget.TreeView.trees = []; | ||
564 | |||
565 | /** | ||
566 | * Counter for generating a new unique element id | ||
567 | * @property YAHOO.widget.TreeView.counter | ||
568 | * @static | ||
569 | * @private | ||
570 | */ | ||
571 | YAHOO.widget.TreeView.counter = 0; | ||
572 | |||
573 | /** | ||
574 | * Global method for getting a tree by its id. Used in the generated | ||
575 | * tree html. | ||
576 | * @method YAHOO.widget.TreeView.getTree | ||
577 | * @param treeId {String} the id of the tree instance | ||
578 | * @return {TreeView} the tree instance requested, null if not found. | ||
579 | * @static | ||
580 | */ | ||
581 | YAHOO.widget.TreeView.getTree = function(treeId) { | ||
582 | var t = YAHOO.widget.TreeView.trees[treeId]; | ||
583 | return (t) ? t : null; | ||
584 | }; | ||
585 | |||
586 | /** | ||
587 | * Global method for getting a node by its id. Used in the generated | ||
588 | * tree html. | ||
589 | * @method YAHOO.widget.TreeView.getNode | ||
590 | * @param treeId {String} the id of the tree instance | ||
591 | * @param nodeIndex {String} the index of the node to return | ||
592 | * @return {Node} the node instance requested, null if not found | ||
593 | * @static | ||
594 | */ | ||
595 | YAHOO.widget.TreeView.getNode = function(treeId, nodeIndex) { | ||
596 | var t = YAHOO.widget.TreeView.getTree(treeId); | ||
597 | return (t) ? t.getNodeByIndex(nodeIndex) : null; | ||
598 | }; | ||
599 | |||
600 | /** | ||
601 | * Add a DOM event | ||
602 | * @method YAHOO.widget.TreeView.addHandler | ||
603 | * @param el the elment to bind the handler to | ||
604 | * @param {string} sType the type of event handler | ||
605 | * @param {function} fn the callback to invoke | ||
606 | * @static | ||
607 | */ | ||
608 | YAHOO.widget.TreeView.addHandler = function (el, sType, fn) { | ||
609 | if (el.addEventListener) { | ||
610 | el.addEventListener(sType, fn, false); | ||
611 | } else if (el.attachEvent) { | ||
612 | el.attachEvent("on" + sType, fn); | ||
613 | } | ||
614 | }; | ||
615 | |||
616 | /** | ||
617 | * Remove a DOM event | ||
618 | * @method YAHOO.widget.TreeView.removeHandler | ||
619 | * @param el the elment to bind the handler to | ||
620 | * @param {string} sType the type of event handler | ||
621 | * @param {function} fn the callback to invoke | ||
622 | * @static | ||
623 | */ | ||
624 | |||
625 | YAHOO.widget.TreeView.removeHandler = function (el, sType, fn) { | ||
626 | if (el.removeEventListener) { | ||
627 | el.removeEventListener(sType, fn, false); | ||
628 | } else if (el.detachEvent) { | ||
629 | el.detachEvent("on" + sType, fn); | ||
630 | } | ||
631 | }; | ||
632 | |||
633 | /** | ||
634 | * Attempts to preload the images defined in the styles used to draw the tree by | ||
635 | * rendering off-screen elements that use the styles. | ||
636 | * @method YAHOO.widget.TreeView.preload | ||
637 | * @param {string} prefix the prefix to use to generate the names of the | ||
638 | * images to preload, default is ygtv | ||
639 | * @static | ||
640 | */ | ||
641 | YAHOO.widget.TreeView.preload = function(prefix) { | ||
642 | prefix = prefix || "ygtv"; | ||
643 | var styles = ["tn","tm","tmh","tp","tph","ln","lm","lmh","lp","lph","loading"]; | ||
644 | |||
645 | var sb = []; | ||
646 | |||
647 | for (var i = 0; i < styles.length; ++i) { | ||
648 | sb[sb.length] = '<span class="' + prefix + styles[i] + '"> </span>'; | ||
649 | } | ||
650 | |||
651 | var f = document.createElement("div"); | ||
652 | var s = f.style; | ||
653 | s.position = "absolute"; | ||
654 | s.top = "-1000px"; | ||
655 | s.left = "-1000px"; | ||
656 | f.innerHTML = sb.join(""); | ||
657 | |||
658 | document.body.appendChild(f); | ||
659 | |||
660 | YAHOO.widget.TreeView.removeHandler(window, | ||
661 | "load", YAHOO.widget.TreeView.preload); | ||
662 | |||
663 | }; | ||
664 | |||
665 | YAHOO.widget.TreeView.addHandler(window, | ||
666 | "load", YAHOO.widget.TreeView.preload); | ||
667 | |||
668 | /** | ||
669 | * The base class for all tree nodes. The node's presentation and behavior in | ||
670 | * response to mouse events is handled in Node subclasses. | ||
671 | * @namespace YAHOO.widget | ||
672 | * @class Node | ||
673 | * @param oData {object} a string or object containing the data that will | ||
674 | * be used to render this node | ||
675 | * @param oParent {Node} this node's parent node | ||
676 | * @param expanded {boolean} the initial expanded/collapsed state | ||
677 | * @constructor | ||
678 | */ | ||
679 | YAHOO.widget.Node = function(oData, oParent, expanded) { | ||
680 | if (oData) { this.init(oData, oParent, expanded); } | ||
681 | }; | ||
682 | |||
683 | YAHOO.widget.Node.prototype = { | ||
684 | |||
685 | /** | ||
686 | * The index for this instance obtained from global counter in YAHOO.widget.TreeView. | ||
687 | * @property index | ||
688 | * @type int | ||
689 | */ | ||
690 | index: 0, | ||
691 | |||
692 | /** | ||
693 | * This node's child node collection. | ||
694 | * @property children | ||
695 | * @type Node[] | ||
696 | */ | ||
697 | children: null, | ||
698 | |||
699 | /** | ||
700 | * Tree instance this node is part of | ||
701 | * @property tree | ||
702 | * @type TreeView | ||
703 | */ | ||
704 | tree: null, | ||
705 | |||
706 | /** | ||
707 | * The data linked to this node. This can be any object or primitive | ||
708 | * value, and the data can be used in getNodeHtml(). | ||
709 | * @property data | ||
710 | * @type object | ||
711 | */ | ||
712 | data: null, | ||
713 | |||
714 | /** | ||
715 | * Parent node | ||
716 | * @property parent | ||
717 | * @type Node | ||
718 | */ | ||
719 | parent: null, | ||
720 | |||
721 | /** | ||
722 | * The depth of this node. We start at -1 for the root node. | ||
723 | * @property depth | ||
724 | * @type int | ||
725 | */ | ||
726 | depth: -1, | ||
727 | |||
728 | /** | ||
729 | * The href for the node's label. If one is not specified, the href will | ||
730 | * be set so that it toggles the node. | ||
731 | * @property href | ||
732 | * @type string | ||
733 | */ | ||
734 | href: null, | ||
735 | |||
736 | /** | ||
737 | * The label href target, defaults to current window | ||
738 | * @property target | ||
739 | * @type string | ||
740 | */ | ||
741 | target: "_self", | ||
742 | |||
743 | /** | ||
744 | * The node's expanded/collapsed state | ||
745 | * @property expanded | ||
746 | * @type boolean | ||
747 | */ | ||
748 | expanded: false, | ||
749 | |||
750 | /** | ||
751 | * Can multiple children be expanded at once? | ||
752 | * @property multiExpand | ||
753 | * @type boolean | ||
754 | */ | ||
755 | multiExpand: true, | ||
756 | |||
757 | /** | ||
758 | * Should we render children for a collapsed node? It is possible that the | ||
759 | * implementer will want to render the hidden data... @todo verify that we | ||
760 | * need this, and implement it if we do. | ||
761 | * @property renderHidden | ||
762 | * @type boolean | ||
763 | */ | ||
764 | renderHidden: false, | ||
765 | |||
766 | /** | ||
767 | * This flag is set to true when the html is generated for this node's | ||
768 | * children, and set to false when new children are added. | ||
769 | * @property childrenRendered | ||
770 | * @type boolean | ||
771 | */ | ||
772 | childrenRendered: false, | ||
773 | |||
774 | /** | ||
775 | * Dynamically loaded nodes only fetch the data the first time they are | ||
776 | * expanded. This flag is set to true once the data has been fetched. | ||
777 | * @property dynamicLoadComplete | ||
778 | * @type boolean | ||
779 | */ | ||
780 | dynamicLoadComplete: false, | ||
781 | |||
782 | /** | ||
783 | * This node's previous sibling | ||
784 | * @property previousSibling | ||
785 | * @type Node | ||
786 | */ | ||
787 | previousSibling: null, | ||
788 | |||
789 | /** | ||
790 | * This node's next sibling | ||
791 | * @property nextSibling | ||
792 | * @type Node | ||
793 | */ | ||
794 | nextSibling: null, | ||
795 | |||
796 | /** | ||
797 | * We can set the node up to call an external method to get the child | ||
798 | * data dynamically. | ||
799 | * @property _dynLoad | ||
800 | * @type boolean | ||
801 | * @private | ||
802 | */ | ||
803 | _dynLoad: false, | ||
804 | |||
805 | /** | ||
806 | * Function to execute when we need to get this node's child data. | ||
807 | * @property dataLoader | ||
808 | * @type function | ||
809 | */ | ||
810 | dataLoader: null, | ||
811 | |||
812 | /** | ||
813 | * This is true for dynamically loading nodes while waiting for the | ||
814 | * callback to return. | ||
815 | * @property isLoading | ||
816 | * @type boolean | ||
817 | */ | ||
818 | isLoading: false, | ||
819 | |||
820 | /** | ||
821 | * The toggle/branch icon will not show if this is set to false. This | ||
822 | * could be useful if the implementer wants to have the child contain | ||
823 | * extra info about the parent, rather than an actual node. | ||
824 | * @property hasIcon | ||
825 | * @type boolean | ||
826 | */ | ||
827 | hasIcon: true, | ||
828 | |||
829 | /** | ||
830 | * Used to configure what happens when a dynamic load node is expanded | ||
831 | * and we discover that it does not have children. By default, it is | ||
832 | * treated as if it still could have children (plus/minus icon). Set | ||
833 | * iconMode to have it display like a leaf node instead. | ||
834 | * @property iconMode | ||
835 | * @type int | ||
836 | */ | ||
837 | iconMode: 0, | ||
838 | |||
839 | /** | ||
840 | * The node type | ||
841 | * @property _type | ||
842 | * @private | ||
843 | */ | ||
844 | _type: "Node", | ||
845 | |||
846 | /* | ||
847 | spacerPath: "http://us.i1.yimg.com/us.yimg.com/i/space.gif", | ||
848 | expandedText: "Expanded", | ||
849 | collapsedText: "Collapsed", | ||
850 | loadingText: "Loading", | ||
851 | */ | ||
852 | |||
853 | /** | ||
854 | * Initializes this node, gets some of the properties from the parent | ||
855 | * @method init | ||
856 | * @param oData {object} a string or object containing the data that will | ||
857 | * be used to render this node | ||
858 | * @param oParent {Node} this node's parent node | ||
859 | * @param expanded {boolean} the initial expanded/collapsed state | ||
860 | */ | ||
861 | init: function(oData, oParent, expanded) { | ||
862 | |||
863 | this.data = oData; | ||
864 | this.children = []; | ||
865 | this.index = YAHOO.widget.TreeView.nodeCount; | ||
866 | ++YAHOO.widget.TreeView.nodeCount; | ||
867 | this.expanded = expanded; | ||
868 | |||
869 | /** | ||
870 | * The parentChange event is fired when a parent element is applied | ||
871 | * to the node. This is useful if you need to apply tree-level | ||
872 | * properties to a tree that need to happen if a node is moved from | ||
873 | * one tre to another. | ||
874 | * | ||
875 | * @event parentChange | ||
876 | * @type CustomEvent | ||
877 | */ | ||
878 | this.createEvent("parentChange", this); | ||
879 | |||
880 | // oParent should never be null except when we create the root node. | ||
881 | if (oParent) { | ||
882 | oParent.appendChild(this); | ||
883 | } | ||
884 | }, | ||
885 | |||
886 | /** | ||
887 | * Certain properties for the node cannot be set until the parent | ||
888 | * is known. This is called after the node is inserted into a tree. | ||
889 | * the parent is also applied to this node's children in order to | ||
890 | * make it possible to move a branch from one tree to another. | ||
891 | * @method applyParent | ||
892 | * @param {Node} parentNode this node's parent node | ||
893 | * @return {boolean} true if the application was successful | ||
894 | */ | ||
895 | applyParent: function(parentNode) { | ||
896 | if (!parentNode) { | ||
897 | return false; | ||
898 | } | ||
899 | |||
900 | this.tree = parentNode.tree; | ||
901 | this.parent = parentNode; | ||
902 | this.depth = parentNode.depth + 1; | ||
903 | |||
904 | if (!this.href) { | ||
905 | this.href = "javascript:" + this.getToggleLink(); | ||
906 | } | ||
907 | |||
908 | if (! this.multiExpand) { | ||
909 | this.multiExpand = parentNode.multiExpand; | ||
910 | } | ||
911 | |||
912 | this.tree.regNode(this); | ||
913 | parentNode.childrenRendered = false; | ||
914 | |||
915 | // cascade update existing children | ||
916 | for (var i=0, len=this.children.length;i<len;++i) { | ||
917 | this.children[i].applyParent(this); | ||
918 | } | ||
919 | |||
920 | this.fireEvent("parentChange"); | ||
921 | |||
922 | return true; | ||
923 | }, | ||
924 | |||
925 | /** | ||
926 | * Appends a node to the child collection. | ||
927 | * @method appendChild | ||
928 | * @param childNode {Node} the new node | ||
929 | * @return {Node} the child node | ||
930 | * @private | ||
931 | */ | ||
932 | appendChild: function(childNode) { | ||
933 | if (this.hasChildren()) { | ||
934 | var sib = this.children[this.children.length - 1]; | ||
935 | sib.nextSibling = childNode; | ||
936 | childNode.previousSibling = sib; | ||
937 | } | ||
938 | this.children[this.children.length] = childNode; | ||
939 | childNode.applyParent(this); | ||
940 | |||
941 | return childNode; | ||
942 | }, | ||
943 | |||
944 | /** | ||
945 | * Appends this node to the supplied node's child collection | ||
946 | * @method appendTo | ||
947 | * @param parentNode {Node} the node to append to. | ||
948 | * @return {Node} The appended node | ||
949 | */ | ||
950 | appendTo: function(parentNode) { | ||
951 | return parentNode.appendChild(this); | ||
952 | }, | ||
953 | |||
954 | /** | ||
955 | * Inserts this node before this supplied node | ||
956 | * @method insertBefore | ||
957 | * @param node {Node} the node to insert this node before | ||
958 | * @return {Node} the inserted node | ||
959 | */ | ||
960 | insertBefore: function(node) { | ||
961 | var p = node.parent; | ||
962 | if (p) { | ||
963 | |||
964 | if (this.tree) { | ||
965 | this.tree.popNode(this); | ||
966 | } | ||
967 | |||
968 | var refIndex = node.isChildOf(p); | ||
969 | p.children.splice(refIndex, 0, this); | ||
970 | if (node.previousSibling) { | ||
971 | node.previousSibling.nextSibling = this; | ||
972 | } | ||
973 | this.previousSibling = node.previousSibling; | ||
974 | this.nextSibling = node; | ||
975 | node.previousSibling = this; | ||
976 | |||
977 | this.applyParent(p); | ||
978 | } | ||
979 | |||
980 | return this; | ||
981 | }, | ||
982 | |||
983 | /** | ||
984 | * Inserts this node after the supplied node | ||
985 | * @method insertAfter | ||
986 | * @param node {Node} the node to insert after | ||
987 | * @return {Node} the inserted node | ||
988 | */ | ||
989 | insertAfter: function(node) { | ||
990 | var p = node.parent; | ||
991 | if (p) { | ||
992 | |||
993 | if (this.tree) { | ||
994 | this.tree.popNode(this); | ||
995 | } | ||
996 | |||
997 | var refIndex = node.isChildOf(p); | ||
998 | |||
999 | if (!node.nextSibling) { | ||
1000 | return this.appendTo(p); | ||
1001 | } | ||
1002 | |||
1003 | p.children.splice(refIndex + 1, 0, this); | ||
1004 | |||
1005 | node.nextSibling.previousSibling = this; | ||
1006 | this.previousSibling = node; | ||
1007 | this.nextSibling = node.nextSibling; | ||
1008 | node.nextSibling = this; | ||
1009 | |||
1010 | this.applyParent(p); | ||
1011 | } | ||
1012 | |||
1013 | return this; | ||
1014 | }, | ||
1015 | |||
1016 | /** | ||
1017 | * Returns true if the Node is a child of supplied Node | ||
1018 | * @method isChildOf | ||
1019 | * @param parentNode {Node} the Node to check | ||
1020 | * @return {boolean} The node index if this Node is a child of | ||
1021 | * supplied Node, else -1. | ||
1022 | * @private | ||
1023 | */ | ||
1024 | isChildOf: function(parentNode) { | ||
1025 | if (parentNode && parentNode.children) { | ||
1026 | for (var i=0, len=parentNode.children.length; i<len ; ++i) { | ||
1027 | if (parentNode.children[i] === this) { | ||
1028 | return i; | ||
1029 | } | ||
1030 | } | ||
1031 | } | ||
1032 | |||
1033 | return -1; | ||
1034 | }, | ||
1035 | |||
1036 | /** | ||
1037 | * Returns a node array of this node's siblings, null if none. | ||
1038 | * @method getSiblings | ||
1039 | * @return Node[] | ||
1040 | */ | ||
1041 | getSiblings: function() { | ||
1042 | return this.parent.children; | ||
1043 | }, | ||
1044 | |||
1045 | /** | ||
1046 | * Shows this node's children | ||
1047 | * @method showChildren | ||
1048 | */ | ||
1049 | showChildren: function() { | ||
1050 | if (!this.tree.animateExpand(this.getChildrenEl(), this)) { | ||
1051 | if (this.hasChildren()) { | ||
1052 | this.getChildrenEl().style.display = ""; | ||
1053 | } | ||
1054 | } | ||
1055 | }, | ||
1056 | |||
1057 | /** | ||
1058 | * Hides this node's children | ||
1059 | * @method hideChildren | ||
1060 | */ | ||
1061 | hideChildren: function() { | ||
1062 | |||
1063 | if (!this.tree.animateCollapse(this.getChildrenEl(), this)) { | ||
1064 | this.getChildrenEl().style.display = "none"; | ||
1065 | } | ||
1066 | }, | ||
1067 | |||
1068 | /** | ||
1069 | * Returns the id for this node's container div | ||
1070 | * @method getElId | ||
1071 | * @return {string} the element id | ||
1072 | */ | ||
1073 | getElId: function() { | ||
1074 | return "ygtv" + this.index; | ||
1075 | }, | ||
1076 | |||
1077 | /** | ||
1078 | * Returns the id for this node's children div | ||
1079 | * @method getChildrenElId | ||
1080 | * @return {string} the element id for this node's children div | ||
1081 | */ | ||
1082 | getChildrenElId: function() { | ||
1083 | return "ygtvc" + this.index; | ||
1084 | }, | ||
1085 | |||
1086 | /** | ||
1087 | * Returns the id for this node's toggle element | ||
1088 | * @method getToggleElId | ||
1089 | * @return {string} the toggel element id | ||
1090 | */ | ||
1091 | getToggleElId: function() { | ||
1092 | return "ygtvt" + this.index; | ||
1093 | }, | ||
1094 | |||
1095 | /* | ||
1096 | * Returns the id for this node's spacer image. The spacer is positioned | ||
1097 | * over the toggle and provides feedback for screen readers. | ||
1098 | * @method getSpacerId | ||
1099 | * @return {string} the id for the spacer image | ||
1100 | */ | ||
1101 | /* | ||
1102 | getSpacerId: function() { | ||
1103 | return "ygtvspacer" + this.index; | ||
1104 | }, | ||
1105 | */ | ||
1106 | |||
1107 | /** | ||
1108 | * Returns this node's container html element | ||
1109 | * @method getEl | ||
1110 | * @return {HTMLElement} the container html element | ||
1111 | */ | ||
1112 | getEl: function() { | ||
1113 | return document.getElementById(this.getElId()); | ||
1114 | }, | ||
1115 | |||
1116 | /** | ||
1117 | * Returns the div that was generated for this node's children | ||
1118 | * @method getChildrenEl | ||
1119 | * @return {HTMLElement} this node's children div | ||
1120 | */ | ||
1121 | getChildrenEl: function() { | ||
1122 | return document.getElementById(this.getChildrenElId()); | ||
1123 | }, | ||
1124 | |||
1125 | /** | ||
1126 | * Returns the element that is being used for this node's toggle. | ||
1127 | * @method getToggleEl | ||
1128 | * @return {HTMLElement} this node's toggle html element | ||
1129 | */ | ||
1130 | getToggleEl: function() { | ||
1131 | return document.getElementById(this.getToggleElId()); | ||
1132 | }, | ||
1133 | |||
1134 | /* | ||
1135 | * Returns the element that is being used for this node's spacer. | ||
1136 | * @method getSpacer | ||
1137 | * @return {HTMLElement} this node's spacer html element | ||
1138 | */ | ||
1139 | /* | ||
1140 | getSpacer: function() { | ||
1141 | return document.getElementById( this.getSpacerId() ) || {}; | ||
1142 | }, | ||
1143 | */ | ||
1144 | |||
1145 | /* | ||
1146 | getStateText: function() { | ||
1147 | if (this.isLoading) { | ||
1148 | return this.loadingText; | ||
1149 | } else if (this.hasChildren(true)) { | ||
1150 | if (this.expanded) { | ||
1151 | return this.expandedText; | ||
1152 | } else { | ||
1153 | return this.collapsedText; | ||
1154 | } | ||
1155 | } else { | ||
1156 | return ""; | ||
1157 | } | ||
1158 | }, | ||
1159 | */ | ||
1160 | |||
1161 | /** | ||
1162 | * Generates the link that will invoke this node's toggle method | ||
1163 | * @method getToggleLink | ||
1164 | * @return {string} the javascript url for toggling this node | ||
1165 | */ | ||
1166 | getToggleLink: function() { | ||
1167 | return "YAHOO.widget.TreeView.getNode(\'" + this.tree.id + "\'," + | ||
1168 | this.index + ").toggle()"; | ||
1169 | }, | ||
1170 | |||
1171 | /** | ||
1172 | * Hides this nodes children (creating them if necessary), changes the | ||
1173 | * @method collapse | ||
1174 | * toggle style. | ||
1175 | */ | ||
1176 | collapse: function() { | ||
1177 | // Only collapse if currently expanded | ||
1178 | if (!this.expanded) { return; } | ||
1179 | |||
1180 | // fire the collapse event handler | ||
1181 | var ret = this.tree.onCollapse(this); | ||
1182 | |||
1183 | if (false === ret) { | ||
1184 | return; | ||
1185 | } | ||
1186 | |||
1187 | ret = this.tree.fireEvent("collapse", this); | ||
1188 | |||
1189 | if (false === ret) { | ||
1190 | return; | ||
1191 | } | ||
1192 | |||
1193 | if (!this.getEl()) { | ||
1194 | this.expanded = false; | ||
1195 | return; | ||
1196 | } | ||
1197 | |||
1198 | // hide the child div | ||
1199 | this.hideChildren(); | ||
1200 | this.expanded = false; | ||
1201 | |||
1202 | this.updateIcon(); | ||
1203 | |||
1204 | // this.getSpacer().title = this.getStateText(); | ||
1205 | |||
1206 | }, | ||
1207 | |||
1208 | /** | ||
1209 | * Shows this nodes children (creating them if necessary), changes the | ||
1210 | * toggle style, and collapses its siblings if multiExpand is not set. | ||
1211 | * @method expand | ||
1212 | */ | ||
1213 | expand: function() { | ||
1214 | // Only expand if currently collapsed. | ||
1215 | if (this.expanded) { return; } | ||
1216 | |||
1217 | // fire the expand event handler | ||
1218 | var ret = this.tree.onExpand(this); | ||
1219 | |||
1220 | if (false === ret) { | ||
1221 | return; | ||
1222 | } | ||
1223 | |||
1224 | ret = this.tree.fireEvent("expand", this); | ||
1225 | |||
1226 | if (false === ret) { | ||
1227 | return; | ||
1228 | } | ||
1229 | |||
1230 | if (!this.getEl()) { | ||
1231 | this.expanded = true; | ||
1232 | return; | ||
1233 | } | ||
1234 | |||
1235 | if (! this.childrenRendered) { | ||
1236 | this.getChildrenEl().innerHTML = this.renderChildren(); | ||
1237 | } else { | ||
1238 | } | ||
1239 | |||
1240 | this.expanded = true; | ||
1241 | |||
1242 | this.updateIcon(); | ||
1243 | |||
1244 | // this.getSpacer().title = this.getStateText(); | ||
1245 | |||
1246 | // We do an extra check for children here because the lazy | ||
1247 | // load feature can expose nodes that have no children. | ||
1248 | |||
1249 | // if (!this.hasChildren()) { | ||
1250 | if (this.isLoading) { | ||
1251 | this.expanded = false; | ||
1252 | return; | ||
1253 | } | ||
1254 | |||
1255 | if (! this.multiExpand) { | ||
1256 | var sibs = this.getSiblings(); | ||
1257 | for (var i=0; i<sibs.length; ++i) { | ||
1258 | if (sibs[i] != this && sibs[i].expanded) { | ||
1259 | sibs[i].collapse(); | ||
1260 | } | ||
1261 | } | ||
1262 | } | ||
1263 | |||
1264 | this.showChildren(); | ||
1265 | }, | ||
1266 | |||
1267 | updateIcon: function() { | ||
1268 | if (this.hasIcon) { | ||
1269 | var el = this.getToggleEl(); | ||
1270 | if (el) { | ||
1271 | el.className = this.getStyle(); | ||
1272 | } | ||
1273 | } | ||
1274 | }, | ||
1275 | |||
1276 | /** | ||
1277 | * Returns the css style name for the toggle | ||
1278 | * @method getStyle | ||
1279 | * @return {string} the css class for this node's toggle | ||
1280 | */ | ||
1281 | getStyle: function() { | ||
1282 | if (this.isLoading) { | ||
1283 | return "ygtvloading"; | ||
1284 | } else { | ||
1285 | // location top or bottom, middle nodes also get the top style | ||
1286 | var loc = (this.nextSibling) ? "t" : "l"; | ||
1287 | |||
1288 | // type p=plus(expand), m=minus(collapase), n=none(no children) | ||
1289 | var type = "n"; | ||
1290 | if (this.hasChildren(true) || (this.isDynamic() && !this.getIconMode())) { | ||
1291 | // if (this.hasChildren(true)) { | ||
1292 | type = (this.expanded) ? "m" : "p"; | ||
1293 | } | ||
1294 | |||
1295 | return "ygtv" + loc + type; | ||
1296 | } | ||
1297 | }, | ||
1298 | |||
1299 | /** | ||
1300 | * Returns the hover style for the icon | ||
1301 | * @return {string} the css class hover state | ||
1302 | * @method getHoverStyle | ||
1303 | */ | ||
1304 | getHoverStyle: function() { | ||
1305 | var s = this.getStyle(); | ||
1306 | if (this.hasChildren(true) && !this.isLoading) { | ||
1307 | s += "h"; | ||
1308 | } | ||
1309 | return s; | ||
1310 | }, | ||
1311 | |||
1312 | /** | ||
1313 | * Recursively expands all of this node's children. | ||
1314 | * @method expandAll | ||
1315 | */ | ||
1316 | expandAll: function() { | ||
1317 | for (var i=0;i<this.children.length;++i) { | ||
1318 | var c = this.children[i]; | ||
1319 | if (c.isDynamic()) { | ||
1320 | alert("Not supported (lazy load + expand all)"); | ||
1321 | break; | ||
1322 | } else if (! c.multiExpand) { | ||
1323 | alert("Not supported (no multi-expand + expand all)"); | ||
1324 | break; | ||
1325 | } else { | ||
1326 | c.expand(); | ||
1327 | c.expandAll(); | ||
1328 | } | ||
1329 | } | ||
1330 | }, | ||
1331 | |||
1332 | /** | ||
1333 | * Recursively collapses all of this node's children. | ||
1334 | * @method collapseAll | ||
1335 | */ | ||
1336 | collapseAll: function() { | ||
1337 | for (var i=0;i<this.children.length;++i) { | ||
1338 | this.children[i].collapse(); | ||
1339 | this.children[i].collapseAll(); | ||
1340 | } | ||
1341 | }, | ||
1342 | |||
1343 | /** | ||
1344 | * Configures this node for dynamically obtaining the child data | ||
1345 | * when the node is first expanded. Calling it without the callback | ||
1346 | * will turn off dynamic load for the node. | ||
1347 | * @method setDynamicLoad | ||
1348 | * @param fmDataLoader {function} the function that will be used to get the data. | ||
1349 | * @param iconMode {int} configures the icon that is displayed when a dynamic | ||
1350 | * load node is expanded the first time without children. By default, the | ||
1351 | * "collapse" icon will be used. If set to 1, the leaf node icon will be | ||
1352 | * displayed. | ||
1353 | */ | ||
1354 | setDynamicLoad: function(fnDataLoader, iconMode) { | ||
1355 | if (fnDataLoader) { | ||
1356 | this.dataLoader = fnDataLoader; | ||
1357 | this._dynLoad = true; | ||
1358 | } else { | ||
1359 | this.dataLoader = null; | ||
1360 | this._dynLoad = false; | ||
1361 | } | ||
1362 | |||
1363 | if (iconMode) { | ||
1364 | this.iconMode = iconMode; | ||
1365 | } | ||
1366 | }, | ||
1367 | |||
1368 | /** | ||
1369 | * Evaluates if this node is the root node of the tree | ||
1370 | * @method isRoot | ||
1371 | * @return {boolean} true if this is the root node | ||
1372 | */ | ||
1373 | isRoot: function() { | ||
1374 | return (this == this.tree.root); | ||
1375 | }, | ||
1376 | |||
1377 | /** | ||
1378 | * Evaluates if this node's children should be loaded dynamically. Looks for | ||
1379 | * the property both in this instance and the root node. If the tree is | ||
1380 | * defined to load all children dynamically, the data callback function is | ||
1381 | * defined in the root node | ||
1382 | * @method isDynamic | ||
1383 | * @return {boolean} true if this node's children are to be loaded dynamically | ||
1384 | */ | ||
1385 | isDynamic: function() { | ||
1386 | var lazy = (!this.isRoot() && (this._dynLoad || this.tree.root._dynLoad)); | ||
1387 | return lazy; | ||
1388 | }, | ||
1389 | |||
1390 | /** | ||
1391 | * Returns the current icon mode. This refers to the way childless dynamic | ||
1392 | * load nodes appear. | ||
1393 | * @method getIconMode | ||
1394 | * @return {int} 0 for collapse style, 1 for leaf node style | ||
1395 | */ | ||
1396 | getIconMode: function() { | ||
1397 | return (this.iconMode || this.tree.root.iconMode); | ||
1398 | }, | ||
1399 | |||
1400 | /** | ||
1401 | * Checks if this node has children. If this node is lazy-loading and the | ||
1402 | * children have not been rendered, we do not know whether or not there | ||
1403 | * are actual children. In most cases, we need to assume that there are | ||
1404 | * children (for instance, the toggle needs to show the expandable | ||
1405 | * presentation state). In other times we want to know if there are rendered | ||
1406 | * children. For the latter, "checkForLazyLoad" should be false. | ||
1407 | * @method hasChildren | ||
1408 | * @param checkForLazyLoad {boolean} should we check for unloaded children? | ||
1409 | * @return {boolean} true if this has children or if it might and we are | ||
1410 | * checking for this condition. | ||
1411 | */ | ||
1412 | hasChildren: function(checkForLazyLoad) { | ||
1413 | return ( this.children.length > 0 || | ||
1414 | (checkForLazyLoad && this.isDynamic() && !this.dynamicLoadComplete) ); | ||
1415 | }, | ||
1416 | |||
1417 | /** | ||
1418 | * Expands if node is collapsed, collapses otherwise. | ||
1419 | * @method toggle | ||
1420 | */ | ||
1421 | toggle: function() { | ||
1422 | if (!this.tree.locked && ( this.hasChildren(true) || this.isDynamic()) ) { | ||
1423 | if (this.expanded) { this.collapse(); } else { this.expand(); } | ||
1424 | } | ||
1425 | }, | ||
1426 | |||
1427 | /** | ||
1428 | * Returns the markup for this node and its children. | ||
1429 | * @method getHtml | ||
1430 | * @return {string} the markup for this node and its expanded children. | ||
1431 | */ | ||
1432 | getHtml: function() { | ||
1433 | |||
1434 | this.childrenRendered = false; | ||
1435 | |||
1436 | var sb = []; | ||
1437 | sb[sb.length] = '<div class="ygtvitem" id="' + this.getElId() + '">'; | ||
1438 | sb[sb.length] = this.getNodeHtml(); | ||
1439 | sb[sb.length] = this.getChildrenHtml(); | ||
1440 | sb[sb.length] = '</div>'; | ||
1441 | return sb.join(""); | ||
1442 | }, | ||
1443 | |||
1444 | /** | ||
1445 | * Called when first rendering the tree. We always build the div that will | ||
1446 | * contain this nodes children, but we don't render the children themselves | ||
1447 | * unless this node is expanded. | ||
1448 | * @method getChildrenHtml | ||
1449 | * @return {string} the children container div html and any expanded children | ||
1450 | * @private | ||
1451 | */ | ||
1452 | getChildrenHtml: function() { | ||
1453 | |||
1454 | var sb = []; | ||
1455 | sb[sb.length] = '<div class="ygtvchildren"'; | ||
1456 | sb[sb.length] = ' id="' + this.getChildrenElId() + '"'; | ||
1457 | if (!this.expanded) { | ||
1458 | sb[sb.length] = ' style="display:none;"'; | ||
1459 | } | ||
1460 | sb[sb.length] = '>'; | ||
1461 | |||
1462 | // Don't render the actual child node HTML unless this node is expanded. | ||
1463 | if ( (this.hasChildren(true) && this.expanded) || | ||
1464 | (this.renderHidden && !this.isDynamic()) ) { | ||
1465 | sb[sb.length] = this.renderChildren(); | ||
1466 | } | ||
1467 | |||
1468 | sb[sb.length] = '</div>'; | ||
1469 | |||
1470 | return sb.join(""); | ||
1471 | }, | ||
1472 | |||
1473 | /** | ||
1474 | * Generates the markup for the child nodes. This is not done until the node | ||
1475 | * is expanded. | ||
1476 | * @method renderChildren | ||
1477 | * @return {string} the html for this node's children | ||
1478 | * @private | ||
1479 | */ | ||
1480 | renderChildren: function() { | ||
1481 | |||
1482 | |||
1483 | var node = this; | ||
1484 | |||
1485 | if (this.isDynamic() && !this.dynamicLoadComplete) { | ||
1486 | this.isLoading = true; | ||
1487 | this.tree.locked = true; | ||
1488 | |||
1489 | if (this.dataLoader) { | ||
1490 | |||
1491 | setTimeout( | ||
1492 | function() { | ||
1493 | node.dataLoader(node, | ||
1494 | function() { | ||
1495 | node.loadComplete(); | ||
1496 | }); | ||
1497 | }, 10); | ||
1498 | |||
1499 | } else if (this.tree.root.dataLoader) { | ||
1500 | |||
1501 | setTimeout( | ||
1502 | function() { | ||
1503 | node.tree.root.dataLoader(node, | ||
1504 | function() { | ||
1505 | node.loadComplete(); | ||
1506 | }); | ||
1507 | }, 10); | ||
1508 | |||
1509 | } else { | ||
1510 | return "Error: data loader not found or not specified."; | ||
1511 | } | ||
1512 | |||
1513 | return ""; | ||
1514 | |||
1515 | } else { | ||
1516 | return this.completeRender(); | ||
1517 | } | ||
1518 | }, | ||
1519 | |||
1520 | /** | ||
1521 | * Called when we know we have all the child data. | ||
1522 | * @method completeRender | ||
1523 | * @return {string} children html | ||
1524 | */ | ||
1525 | completeRender: function() { | ||
1526 | var sb = []; | ||
1527 | |||
1528 | for (var i=0; i < this.children.length; ++i) { | ||
1529 | // this.children[i].childrenRendered = false; | ||
1530 | sb[sb.length] = this.children[i].getHtml(); | ||
1531 | } | ||
1532 | |||
1533 | this.childrenRendered = true; | ||
1534 | |||
1535 | return sb.join(""); | ||
1536 | }, | ||
1537 | |||
1538 | /** | ||
1539 | * Load complete is the callback function we pass to the data provider | ||
1540 | * in dynamic load situations. | ||
1541 | * @method loadComplete | ||
1542 | */ | ||
1543 | loadComplete: function() { | ||
1544 | this.getChildrenEl().innerHTML = this.completeRender(); | ||
1545 | this.dynamicLoadComplete = true; | ||
1546 | this.isLoading = false; | ||
1547 | this.expand(); | ||
1548 | this.tree.locked = false; | ||
1549 | }, | ||
1550 | |||
1551 | /** | ||
1552 | * Returns this node's ancestor at the specified depth. | ||
1553 | * @method getAncestor | ||
1554 | * @param {int} depth the depth of the ancestor. | ||
1555 | * @return {Node} the ancestor | ||
1556 | */ | ||
1557 | getAncestor: function(depth) { | ||
1558 | if (depth >= this.depth || depth < 0) { | ||
1559 | return null; | ||
1560 | } | ||
1561 | |||
1562 | var p = this.parent; | ||
1563 | |||
1564 | while (p.depth > depth) { | ||
1565 | p = p.parent; | ||
1566 | } | ||
1567 | |||
1568 | return p; | ||
1569 | }, | ||
1570 | |||
1571 | /** | ||
1572 | * Returns the css class for the spacer at the specified depth for | ||
1573 | * this node. If this node's ancestor at the specified depth | ||
1574 | * has a next sibling the presentation is different than if it | ||
1575 | * does not have a next sibling | ||
1576 | * @method getDepthStyle | ||
1577 | * @param {int} depth the depth of the ancestor. | ||
1578 | * @return {string} the css class for the spacer | ||
1579 | */ | ||
1580 | getDepthStyle: function(depth) { | ||
1581 | return (this.getAncestor(depth).nextSibling) ? | ||
1582 | "ygtvdepthcell" : "ygtvblankdepthcell"; | ||
1583 | }, | ||
1584 | |||
1585 | /** | ||
1586 | * Get the markup for the node. This is designed to be overrided so that we can | ||
1587 | * support different types of nodes. | ||
1588 | * @method getNodeHtml | ||
1589 | * @return {string} The HTML that will render this node. | ||
1590 | */ | ||
1591 | getNodeHtml: function() { | ||
1592 | return ""; | ||
1593 | }, | ||
1594 | |||
1595 | /** | ||
1596 | * Regenerates the html for this node and its children. To be used when the | ||
1597 | * node is expanded and new children have been added. | ||
1598 | * @method refresh | ||
1599 | */ | ||
1600 | refresh: function() { | ||
1601 | // this.loadComplete(); | ||
1602 | this.getChildrenEl().innerHTML = this.completeRender(); | ||
1603 | |||
1604 | if (this.hasIcon) { | ||
1605 | var el = this.getToggleEl(); | ||
1606 | if (el) { | ||
1607 | el.className = this.getStyle(); | ||
1608 | } | ||
1609 | } | ||
1610 | }, | ||
1611 | |||
1612 | /** | ||
1613 | * Node toString | ||
1614 | * @method toString | ||
1615 | * @return {string} string representation of the node | ||
1616 | */ | ||
1617 | toString: function() { | ||
1618 | return "Node (" + this.index + ")"; | ||
1619 | } | ||
1620 | |||
1621 | }; | ||
1622 | |||
1623 | YAHOO.augment(YAHOO.widget.Node, YAHOO.util.EventProvider); | ||
1624 | |||
1625 | /** | ||
1626 | * A custom YAHOO.widget.Node that handles the unique nature of | ||
1627 | * the virtual, presentationless root node. | ||
1628 | * @namespace YAHOO.widget | ||
1629 | * @class RootNode | ||
1630 | * @extends YAHOO.widget.Node | ||
1631 | * @param oTree {YAHOO.widget.TreeView} The tree instance this node belongs to | ||
1632 | * @constructor | ||
1633 | */ | ||
1634 | YAHOO.widget.RootNode = function(oTree) { | ||
1635 | // Initialize the node with null params. The root node is a | ||
1636 | // special case where the node has no presentation. So we have | ||
1637 | // to alter the standard properties a bit. | ||
1638 | this.init(null, null, true); | ||
1639 | |||
1640 | /* | ||
1641 | * For the root node, we get the tree reference from as a param | ||
1642 | * to the constructor instead of from the parent element. | ||
1643 | */ | ||
1644 | this.tree = oTree; | ||
1645 | }; | ||
1646 | |||
1647 | YAHOO.extend(YAHOO.widget.RootNode, YAHOO.widget.Node, { | ||
1648 | |||
1649 | // overrides YAHOO.widget.Node | ||
1650 | getNodeHtml: function() { | ||
1651 | return ""; | ||
1652 | }, | ||
1653 | |||
1654 | toString: function() { | ||
1655 | return "RootNode"; | ||
1656 | }, | ||
1657 | |||
1658 | loadComplete: function() { | ||
1659 | this.tree.draw(); | ||
1660 | } | ||
1661 | |||
1662 | }); | ||
1663 | /** | ||
1664 | * The default node presentation. The first parameter should be | ||
1665 | * either a string that will be used as the node's label, or an object | ||
1666 | * that has a string propery called label. By default, the clicking the | ||
1667 | * label will toggle the expanded/collapsed state of the node. By | ||
1668 | * changing the href property of the instance, this behavior can be | ||
1669 | * changed so that the label will go to the specified href. | ||
1670 | * @namespace YAHOO.widget | ||
1671 | * @class TextNode | ||
1672 | * @extends YAHOO.widget.Node | ||
1673 | * @constructor | ||
1674 | * @param oData {object} a string or object containing the data that will | ||
1675 | * be used to render this node | ||
1676 | * @param oParent {YAHOO.widget.Node} this node's parent node | ||
1677 | * @param expanded {boolean} the initial expanded/collapsed state | ||
1678 | */ | ||
1679 | YAHOO.widget.TextNode = function(oData, oParent, expanded) { | ||
1680 | |||
1681 | if (oData) { | ||
1682 | this.init(oData, oParent, expanded); | ||
1683 | this.setUpLabel(oData); | ||
1684 | } | ||
1685 | |||
1686 | }; | ||
1687 | |||
1688 | YAHOO.extend(YAHOO.widget.TextNode, YAHOO.widget.Node, { | ||
1689 | |||
1690 | /** | ||
1691 | * The CSS class for the label href. Defaults to ygtvlabel, but can be | ||
1692 | * overridden to provide a custom presentation for a specific node. | ||
1693 | * @property labelStyle | ||
1694 | * @type string | ||
1695 | */ | ||
1696 | labelStyle: "ygtvlabel", | ||
1697 | |||
1698 | /** | ||
1699 | * The derived element id of the label for this node | ||
1700 | * @property labelElId | ||
1701 | * @type string | ||
1702 | */ | ||
1703 | labelElId: null, | ||
1704 | |||
1705 | /** | ||
1706 | * The text for the label. It is assumed that the oData parameter will | ||
1707 | * either be a string that will be used as the label, or an object that | ||
1708 | * has a property called "label" that we will use. | ||
1709 | * @property label | ||
1710 | * @type string | ||
1711 | */ | ||
1712 | label: null, | ||
1713 | |||
1714 | textNodeParentChange: function() { | ||
1715 | |||
1716 | /** | ||
1717 | * Custom event that is fired when the text node label is clicked. The | ||
1718 | * custom event is defined on the tree instance, so there is a single | ||
1719 | * event that handles all nodes in the tree. The node clicked is | ||
1720 | * provided as an argument | ||
1721 | * | ||
1722 | * @event labelClick | ||
1723 | * @for YAHOO.widget.TreeView | ||
1724 | * @param {YAHOO.widget.Node} node the node clicked | ||
1725 | */ | ||
1726 | if (this.tree && !this.tree.hasEvent("labelClick")) { | ||
1727 | this.tree.createEvent("labelClick", this.tree); | ||
1728 | } | ||
1729 | |||
1730 | }, | ||
1731 | |||
1732 | /** | ||
1733 | * Sets up the node label | ||
1734 | * @method setUpLabel | ||
1735 | * @param oData string containing the label, or an object with a label property | ||
1736 | */ | ||
1737 | setUpLabel: function(oData) { | ||
1738 | |||
1739 | // set up the custom event on the tree | ||
1740 | this.textNodeParentChange(); | ||
1741 | this.subscribe("parentChange", this.textNodeParentChange); | ||
1742 | |||
1743 | if (typeof oData == "string") { | ||
1744 | oData = { label: oData }; | ||
1745 | } | ||
1746 | this.label = oData.label; | ||
1747 | |||
1748 | // update the link | ||
1749 | if (oData.href) { | ||
1750 | this.href = oData.href; | ||
1751 | } | ||
1752 | |||
1753 | // set the target | ||
1754 | if (oData.target) { | ||
1755 | this.target = oData.target; | ||
1756 | } | ||
1757 | |||
1758 | if (oData.style) { | ||
1759 | this.labelStyle = oData.style; | ||
1760 | } | ||
1761 | |||
1762 | this.labelElId = "ygtvlabelel" + this.index; | ||
1763 | }, | ||
1764 | |||
1765 | /** | ||
1766 | * Returns the label element | ||
1767 | * @for YAHOO.widget.TextNode | ||
1768 | * @method getLabelEl | ||
1769 | * @return {object} the element | ||
1770 | */ | ||
1771 | getLabelEl: function() { | ||
1772 | return document.getElementById(this.labelElId); | ||
1773 | }, | ||
1774 | |||
1775 | // overrides YAHOO.widget.Node | ||
1776 | getNodeHtml: function() { | ||
1777 | var sb = []; | ||
1778 | |||
1779 | sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">'; | ||
1780 | sb[sb.length] = '<tr>'; | ||
1781 | |||
1782 | for (var i=0;i<this.depth;++i) { | ||
1783 | // sb[sb.length] = '<td class="ygtvdepthcell"> </td>'; | ||
1784 | sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"> </td>'; | ||
1785 | } | ||
1786 | |||
1787 | var getNode = 'YAHOO.widget.TreeView.getNode(\'' + | ||
1788 | this.tree.id + '\',' + this.index + ')'; | ||
1789 | |||
1790 | sb[sb.length] = '<td'; | ||
1791 | // sb[sb.length] = ' onselectstart="return false"'; | ||
1792 | sb[sb.length] = ' id="' + this.getToggleElId() + '"'; | ||
1793 | sb[sb.length] = ' class="' + this.getStyle() + '"'; | ||
1794 | if (this.hasChildren(true)) { | ||
1795 | sb[sb.length] = ' onmouseover="this.className='; | ||
1796 | sb[sb.length] = getNode + '.getHoverStyle()"'; | ||
1797 | sb[sb.length] = ' onmouseout="this.className='; | ||
1798 | sb[sb.length] = getNode + '.getStyle()"'; | ||
1799 | } | ||
1800 | sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '">'; | ||
1801 | |||
1802 | /* | ||
1803 | sb[sb.length] = '<img id="' + this.getSpacerId() + '"'; | ||
1804 | sb[sb.length] = ' alt=""'; | ||
1805 | sb[sb.length] = ' tabindex=0'; | ||
1806 | sb[sb.length] = ' src="' + this.spacerPath + '"'; | ||
1807 | sb[sb.length] = ' title="' + this.getStateText() + '"'; | ||
1808 | sb[sb.length] = ' class="ygtvspacer"'; | ||
1809 | // sb[sb.length] = ' onkeypress="return ' + getNode + '".onKeyPress()"'; | ||
1810 | sb[sb.length] = ' />'; | ||
1811 | */ | ||
1812 | |||
1813 | sb[sb.length] = ' '; | ||
1814 | |||
1815 | sb[sb.length] = '</td>'; | ||
1816 | sb[sb.length] = '<td>'; | ||
1817 | sb[sb.length] = '<a'; | ||
1818 | sb[sb.length] = ' id="' + this.labelElId + '"'; | ||
1819 | sb[sb.length] = ' class="' + this.labelStyle + '"'; | ||
1820 | sb[sb.length] = ' href="' + this.href + '"'; | ||
1821 | sb[sb.length] = ' target="' + this.target + '"'; | ||
1822 | sb[sb.length] = ' onclick="return ' + getNode + '.onLabelClick(' + getNode +')"'; | ||
1823 | if (this.hasChildren(true)) { | ||
1824 | sb[sb.length] = ' onmouseover="document.getElementById(\''; | ||
1825 | sb[sb.length] = this.getToggleElId() + '\').className='; | ||
1826 | sb[sb.length] = getNode + '.getHoverStyle()"'; | ||
1827 | sb[sb.length] = ' onmouseout="document.getElementById(\''; | ||
1828 | sb[sb.length] = this.getToggleElId() + '\').className='; | ||
1829 | sb[sb.length] = getNode + '.getStyle()"'; | ||
1830 | } | ||
1831 | sb[sb.length] = ' >'; | ||
1832 | sb[sb.length] = this.label; | ||
1833 | sb[sb.length] = '</a>'; | ||
1834 | sb[sb.length] = '</td>'; | ||
1835 | sb[sb.length] = '</tr>'; | ||
1836 | sb[sb.length] = '</table>'; | ||
1837 | |||
1838 | return sb.join(""); | ||
1839 | }, | ||
1840 | |||
1841 | /** | ||
1842 | * Executed when the label is clicked. Fires the labelClick custom event. | ||
1843 | * @method onLabelClick | ||
1844 | * @param me {Node} this node | ||
1845 | * @scope the anchor tag clicked | ||
1846 | * @return false to cancel the anchor click | ||
1847 | */ | ||
1848 | onLabelClick: function(me) { | ||
1849 | return me.tree.fireEvent("labelClick", me); | ||
1850 | //return true; | ||
1851 | }, | ||
1852 | |||
1853 | toString: function() { | ||
1854 | return "TextNode (" + this.index + ") " + this.label; | ||
1855 | } | ||
1856 | |||
1857 | }); | ||
1858 | /** | ||
1859 | * A menu-specific implementation that differs from TextNode in that only | ||
1860 | * one sibling can be expanded at a time. | ||
1861 | * @namespace YAHOO.widget | ||
1862 | * @class MenuNode | ||
1863 | * @extends YAHOO.widget.TextNode | ||
1864 | * @param oData {object} a string or object containing the data that will | ||
1865 | * be used to render this node | ||
1866 | * @param oParent {YAHOO.widget.Node} this node's parent node | ||
1867 | * @param expanded {boolean} the initial expanded/collapsed state | ||
1868 | * @constructor | ||
1869 | */ | ||
1870 | YAHOO.widget.MenuNode = function(oData, oParent, expanded) { | ||
1871 | if (oData) { | ||
1872 | this.init(oData, oParent, expanded); | ||
1873 | this.setUpLabel(oData); | ||
1874 | } | ||
1875 | |||
1876 | /* | ||
1877 | * Menus usually allow only one branch to be open at a time. | ||
1878 | */ | ||
1879 | this.multiExpand = false; | ||
1880 | |||
1881 | |||
1882 | }; | ||
1883 | |||
1884 | YAHOO.extend(YAHOO.widget.MenuNode, YAHOO.widget.TextNode, { | ||
1885 | |||
1886 | toString: function() { | ||
1887 | return "MenuNode (" + this.index + ") " + this.label; | ||
1888 | } | ||
1889 | |||
1890 | }); | ||
1891 | /** | ||
1892 | * This implementation takes either a string or object for the | ||
1893 | * oData argument. If is it a string, we will use it for the display | ||
1894 | * of this node (and it can contain any html code). If the parameter | ||
1895 | * is an object, we look for a parameter called "html" that will be | ||
1896 | * used for this node's display. | ||
1897 | * @namespace YAHOO.widget | ||
1898 | * @class HTMLNode | ||
1899 | * @extends YAHOO.widget.Node | ||
1900 | * @constructor | ||
1901 | * @param oData {object} a string or object containing the data that will | ||
1902 | * be used to render this node | ||
1903 | * @param oParent {YAHOO.widget.Node} this node's parent node | ||
1904 | * @param expanded {boolean} the initial expanded/collapsed state | ||
1905 | * @param hasIcon {boolean} specifies whether or not leaf nodes should | ||
1906 | * have an icon | ||
1907 | */ | ||
1908 | YAHOO.widget.HTMLNode = function(oData, oParent, expanded, hasIcon) { | ||
1909 | if (oData) { | ||
1910 | this.init(oData, oParent, expanded); | ||
1911 | this.initContent(oData, hasIcon); | ||
1912 | } | ||
1913 | }; | ||
1914 | |||
1915 | YAHOO.extend(YAHOO.widget.HTMLNode, YAHOO.widget.Node, { | ||
1916 | |||
1917 | /** | ||
1918 | * The CSS class for the html content container. Defaults to ygtvhtml, but | ||
1919 | * can be overridden to provide a custom presentation for a specific node. | ||
1920 | * @property contentStyle | ||
1921 | * @type string | ||
1922 | */ | ||
1923 | contentStyle: "ygtvhtml", | ||
1924 | |||
1925 | /** | ||
1926 | * The generated id that will contain the data passed in by the implementer. | ||
1927 | * @property contentElId | ||
1928 | * @type string | ||
1929 | */ | ||
1930 | contentElId: null, | ||
1931 | |||
1932 | /** | ||
1933 | * The HTML content to use for this node's display | ||
1934 | * @property content | ||
1935 | * @type string | ||
1936 | */ | ||
1937 | content: null, | ||
1938 | |||
1939 | /** | ||
1940 | * Sets up the node label | ||
1941 | * @property initContent | ||
1942 | * @param {object} An html string or object containing an html property | ||
1943 | * @param {boolean} hasIcon determines if the node will be rendered with an | ||
1944 | * icon or not | ||
1945 | */ | ||
1946 | initContent: function(oData, hasIcon) { | ||
1947 | if (typeof oData == "string") { | ||
1948 | oData = { html: oData }; | ||
1949 | } | ||
1950 | |||
1951 | this.html = oData.html; | ||
1952 | this.contentElId = "ygtvcontentel" + this.index; | ||
1953 | this.hasIcon = hasIcon; | ||
1954 | |||
1955 | }, | ||
1956 | |||
1957 | /** | ||
1958 | * Returns the outer html element for this node's content | ||
1959 | * @method getContentEl | ||
1960 | * @return {HTMLElement} the element | ||
1961 | */ | ||
1962 | getContentEl: function() { | ||
1963 | return document.getElementById(this.contentElId); | ||
1964 | }, | ||
1965 | |||
1966 | // overrides YAHOO.widget.Node | ||
1967 | getNodeHtml: function() { | ||
1968 | var sb = []; | ||
1969 | |||
1970 | sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">'; | ||
1971 | sb[sb.length] = '<tr>'; | ||
1972 | |||
1973 | for (var i=0;i<this.depth;++i) { | ||
1974 | sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"> </td>'; | ||
1975 | } | ||
1976 | |||
1977 | if (this.hasIcon) { | ||
1978 | sb[sb.length] = '<td'; | ||
1979 | sb[sb.length] = ' id="' + this.getToggleElId() + '"'; | ||
1980 | sb[sb.length] = ' class="' + this.getStyle() + '"'; | ||
1981 | sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '"'; | ||
1982 | if (this.hasChildren(true)) { | ||
1983 | sb[sb.length] = ' onmouseover="this.className='; | ||
1984 | sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\''; | ||
1985 | sb[sb.length] = this.tree.id + '\',' + this.index + ').getHoverStyle()"'; | ||
1986 | sb[sb.length] = ' onmouseout="this.className='; | ||
1987 | sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\''; | ||
1988 | sb[sb.length] = this.tree.id + '\',' + this.index + ').getStyle()"'; | ||
1989 | } | ||
1990 | sb[sb.length] = '> </td>'; | ||
1991 | } | ||
1992 | |||
1993 | sb[sb.length] = '<td'; | ||
1994 | sb[sb.length] = ' id="' + this.contentElId + '"'; | ||
1995 | sb[sb.length] = ' class="' + this.contentStyle + '"'; | ||
1996 | sb[sb.length] = ' >'; | ||
1997 | sb[sb.length] = this.html; | ||
1998 | sb[sb.length] = '</td>'; | ||
1999 | sb[sb.length] = '</tr>'; | ||
2000 | sb[sb.length] = '</table>'; | ||
2001 | |||
2002 | return sb.join(""); | ||
2003 | }, | ||
2004 | |||
2005 | toString: function() { | ||
2006 | return "HTMLNode (" + this.index + ")"; | ||
2007 | } | ||
2008 | |||
2009 | }); | ||
2010 | /** | ||
2011 | * A static factory class for tree view expand/collapse animations | ||
2012 | * @class TVAnim | ||
2013 | * @static | ||
2014 | */ | ||
2015 | YAHOO.widget.TVAnim = function() { | ||
2016 | return { | ||
2017 | /** | ||
2018 | * Constant for the fade in animation | ||
2019 | * @property FADE_IN | ||
2020 | * @type string | ||
2021 | * @static | ||
2022 | */ | ||
2023 | FADE_IN: "TVFadeIn", | ||
2024 | |||
2025 | /** | ||
2026 | * Constant for the fade out animation | ||
2027 | * @property FADE_OUT | ||
2028 | * @type string | ||
2029 | * @static | ||
2030 | */ | ||
2031 | FADE_OUT: "TVFadeOut", | ||
2032 | |||
2033 | /** | ||
2034 | * Returns a ygAnim instance of the given type | ||
2035 | * @method getAnim | ||
2036 | * @param type {string} the type of animation | ||
2037 | * @param el {HTMLElement} the element to element (probably the children div) | ||
2038 | * @param callback {function} function to invoke when the animation is done. | ||
2039 | * @return {YAHOO.util.Animation} the animation instance | ||
2040 | * @static | ||
2041 | */ | ||
2042 | getAnim: function(type, el, callback) { | ||
2043 | if (YAHOO.widget[type]) { | ||
2044 | return new YAHOO.widget[type](el, callback); | ||
2045 | } else { | ||
2046 | return null; | ||
2047 | } | ||
2048 | }, | ||
2049 | |||
2050 | /** | ||
2051 | * Returns true if the specified animation class is available | ||
2052 | * @method isValid | ||
2053 | * @param type {string} the type of animation | ||
2054 | * @return {boolean} true if valid, false if not | ||
2055 | * @static | ||
2056 | */ | ||
2057 | isValid: function(type) { | ||
2058 | return (YAHOO.widget[type]); | ||
2059 | } | ||
2060 | }; | ||
2061 | } (); | ||
2062 | |||
2063 | /** | ||
2064 | * A 1/2 second fade-in animation. | ||
2065 | * @class TVFadeIn | ||
2066 | * @constructor | ||
2067 | * @param el {HTMLElement} the element to animate | ||
2068 | * @param callback {function} function to invoke when the animation is finished | ||
2069 | */ | ||
2070 | YAHOO.widget.TVFadeIn = function(el, callback) { | ||
2071 | /** | ||
2072 | * The element to animate | ||
2073 | * @property el | ||
2074 | * @type HTMLElement | ||
2075 | */ | ||
2076 | this.el = el; | ||
2077 | |||
2078 | /** | ||
2079 | * the callback to invoke when the animation is complete | ||
2080 | * @property callback | ||
2081 | * @type function | ||
2082 | */ | ||
2083 | this.callback = callback; | ||
2084 | |||
2085 | }; | ||
2086 | |||
2087 | YAHOO.widget.TVFadeIn.prototype = { | ||
2088 | /** | ||
2089 | * Performs the animation | ||
2090 | * @method animate | ||
2091 | */ | ||
2092 | animate: function() { | ||
2093 | var tvanim = this; | ||
2094 | |||
2095 | var s = this.el.style; | ||
2096 | s.opacity = 0.1; | ||
2097 | s.filter = "alpha(opacity=10)"; | ||
2098 | s.display = ""; | ||
2099 | |||
2100 | var dur = 0.4; | ||
2101 | var a = new YAHOO.util.Anim(this.el, {opacity: {from: 0.1, to: 1, unit:""}}, dur); | ||
2102 | a.onComplete.subscribe( function() { tvanim.onComplete(); } ); | ||
2103 | a.animate(); | ||
2104 | }, | ||
2105 | |||
2106 | /** | ||
2107 | * Clean up and invoke callback | ||
2108 | * @method onComplete | ||
2109 | */ | ||
2110 | onComplete: function() { | ||
2111 | this.callback(); | ||
2112 | }, | ||
2113 | |||
2114 | /** | ||
2115 | * toString | ||
2116 | * @method toString | ||
2117 | * @return {string} the string representation of the instance | ||
2118 | */ | ||
2119 | toString: function() { | ||
2120 | return "TVFadeIn"; | ||
2121 | } | ||
2122 | }; | ||
2123 | |||
2124 | /** | ||
2125 | * A 1/2 second fade out animation. | ||
2126 | * @class TVFadeOut | ||
2127 | * @constructor | ||
2128 | * @param el {HTMLElement} the element to animate | ||
2129 | * @param callback {Function} function to invoke when the animation is finished | ||
2130 | */ | ||
2131 | YAHOO.widget.TVFadeOut = function(el, callback) { | ||
2132 | /** | ||
2133 | * The element to animate | ||
2134 | * @property el | ||
2135 | * @type HTMLElement | ||
2136 | */ | ||
2137 | this.el = el; | ||
2138 | |||
2139 | /** | ||
2140 | * the callback to invoke when the animation is complete | ||
2141 | * @property callback | ||
2142 | * @type function | ||
2143 | */ | ||
2144 | this.callback = callback; | ||
2145 | |||
2146 | }; | ||
2147 | |||
2148 | YAHOO.widget.TVFadeOut.prototype = { | ||
2149 | /** | ||
2150 | * Performs the animation | ||
2151 | * @method animate | ||
2152 | */ | ||
2153 | animate: function() { | ||
2154 | var tvanim = this; | ||
2155 | var dur = 0.4; | ||
2156 | var a = new YAHOO.util.Anim(this.el, {opacity: {from: 1, to: 0.1, unit:""}}, dur); | ||
2157 | a.onComplete.subscribe( function() { tvanim.onComplete(); } ); | ||
2158 | a.animate(); | ||
2159 | }, | ||
2160 | |||
2161 | /** | ||
2162 | * Clean up and invoke callback | ||
2163 | * @method onComplete | ||
2164 | */ | ||
2165 | onComplete: function() { | ||
2166 | var s = this.el.style; | ||
2167 | s.display = "none"; | ||
2168 | // s.opacity = 1; | ||
2169 | s.filter = "alpha(opacity=100)"; | ||
2170 | this.callback(); | ||
2171 | }, | ||
2172 | |||
2173 | /** | ||
2174 | * toString | ||
2175 | * @method toString | ||
2176 | * @return {string} the string representation of the instance | ||
2177 | */ | ||
2178 | toString: function() { | ||
2179 | return "TVFadeOut"; | ||
2180 | } | ||
2181 | }; | ||
2182 | |||