summaryrefslogtreecommitdiff
path: root/frontend/beta/js/YUI-extensions/data/Tree.js
Unidiff
Diffstat (limited to 'frontend/beta/js/YUI-extensions/data/Tree.js') (more/less context) (show whitespace changes)
-rw-r--r--frontend/beta/js/YUI-extensions/data/Tree.js412
1 files changed, 412 insertions, 0 deletions
diff --git a/frontend/beta/js/YUI-extensions/data/Tree.js b/frontend/beta/js/YUI-extensions/data/Tree.js
new file mode 100644
index 0000000..afa5b20
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/data/Tree.js
@@ -0,0 +1,412 @@
1YAHOO.namespace('ext.data');
2
3/**
4 * @class YAHOO.ext.data.Tree
5 * @extends YAHOO.ext.util.Observable
6 * The class represents a tree data structure and bubbles all the events for it's nodes. The nodes
7 * in the tree have most standard DOM functionality.
8 * @constructor
9 * @param {Node} root (optional) The root node
10 */
11YAHOO.ext.data.Tree = function(root){
12 this.nodeHash = {};
13 this.root = null;
14 if(root){
15 this.setRootNode(root);
16 }
17 this.events = {
18 'append' : true,
19 'remove' : true,
20 'move' : true,
21 'insert' : true,
22 'beforeappend' : true,
23 'beforeremove' : true,
24 'beforemove' : true,
25 'beforeinsert' : true
26 };
27};
28
29YAHOO.extendX(YAHOO.ext.data.Tree, YAHOO.ext.util.Observable, {
30 pathSeparator: '/',
31
32 getRootNode : function(){
33 return this.root;
34 },
35
36 setRootNode : function(node){
37 this.root = node;
38 node.ownerTree = this;
39 node.isRoot = true;
40 return node;
41 },
42
43 getNodeById : function(id){
44 return this.nodeHash[id];
45 },
46
47 registerNode : function(node){
48 this.nodeHash[node.id] = node;
49 },
50
51 unregisterNode : function(node){
52 delete this.nodeHash[node.id];
53 },
54
55 toString : function(){
56 return '[Tree'+(this.id?' '+this.id:'')+']';
57 }
58});
59
60/**
61 * @class YAHOO.ext.tree.Node
62 * @extends YAHOO.ext.util.Observable
63 * @cfg {String} text The text for this node
64 * @cfg {String} id The id for this node
65 * @constructor
66 * @param {Object} attributes The attributes/config for the node
67 */
68YAHOO.ext.data.Node = function(attributes){
69 this.attributes = attributes || {};
70 this.leaf = this.attributes.leaf;
71 this.id = this.attributes.id;
72 if(!this.id){
73 this.id = YAHOO.util.Dom.generateId(null, 'ynode-');
74 this.attributes.id = this.id;
75 }
76
77 this.childNodes = [];
78 if(!this.childNodes.indexOf){ // indexOf is a must
79 this.childNodes.indexOf = function(o){
80 for(var i = 0, len = this.length; i < len; i++){
81 if(this[i] == o) return i;
82 }
83 return -1;
84 };
85 }
86 this.parentNode = null;
87 this.firstChild = null;
88 this.lastChild = null;
89 this.previousSibling = null;
90 this.nextSibling = null;
91
92 this.events = {
93 'append' : true,
94 'remove' : true,
95 'move' : true,
96 'insert' : true,
97 'beforeappend' : true,
98 'beforeremove' : true,
99 'beforemove' : true,
100 'beforeinsert' : true
101 };
102};
103
104YAHOO.extendX(YAHOO.ext.data.Node, YAHOO.ext.util.Observable, {
105 fireEvent : function(evtName){
106 // first do standard event for this node
107 if(YAHOO.ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){
108 return false;
109 }
110 // then bubble it up to the tree if the event wasn't cancelled
111 if(this.ownerTree){
112 if(this.ownerTree.fireEvent.apply(this.ownerTree, arguments) === false){
113 return false;
114 }
115 }
116 return true;
117 },
118
119 isLeaf : function(){
120 return this.leaf === true;
121 },
122
123 setFirstChild : function(node){
124 this.firstChild = node;
125 },
126
127 setLastChild : function(node){
128 this.lastChild = node;
129 },
130
131 isLast : function(){
132 return (!this.parentNode ? true : this.parentNode.lastChild == this);
133 },
134
135 isFirst : function(){
136 return (!this.parentNode ? true : this.parentNode.firstChild == this);
137 },
138
139 hasChildNodes : function(){
140 return !this.isLeaf() && this.childNodes.length > 0;
141 },
142
143 appendChild : function(node){
144 var multi = false;
145 if(node instanceof Array){
146 multi = node;
147 }else if(arguments.length > 1){
148 multi = arguments;
149 }
150 // if passed an array or multiple args do them one by one
151 if(multi){
152 for(var i = 0, len = multi.length; i < len; i++) {
153 this.appendChild(multi[i]);
154 }
155 }else{
156 if(this.fireEvent('beforeappend', this.ownerTree, this, node) === false){
157 return false;
158 }
159 var index = this.childNodes.length;
160 var oldParent = node.parentNode;
161 // it's a move, make sure we move it cleanly
162 if(oldParent){
163 if(node.fireEvent('beforemove', node.getOwnerTree(), node, oldParent, this, index) === false){
164 return false;
165 }
166 oldParent.removeChild(node);
167 }
168 var index = this.childNodes.length;
169 if(index == 0){
170 this.setFirstChild(node);
171 }
172 this.childNodes.push(node);
173 node.parentNode = this;
174 var ps = this.childNodes[index-1];
175 if(ps){
176 node.previousSibling = ps;
177 ps.nextSibling = node;
178 }
179 this.setLastChild(node);
180 node.setOwnerTree(this.getOwnerTree());
181 this.fireEvent('append', this.ownerTree, this, node, index);
182 if(oldParent){
183 node.fireEvent('move', this.ownerTree, node, oldParent, this, index);
184 }
185 return node;
186 }
187 },
188
189 removeChild : function(node){
190 var index = this.childNodes.indexOf(node);
191 if(index == -1){
192 return false;
193 }
194 if(this.fireEvent('beforeremove', this.ownerTree, this, node) === false){
195 return false;
196 }
197
198 // remove it from childNodes collection
199 this.childNodes.splice(index, 1);
200
201 // update siblings
202 if(node.previousSibling){
203 node.previousSibling.nextSibling = node.nextSibling;
204 }
205 if(node.nextSibling){
206 node.nextSibling.previousSibling = node.previousSibling;
207 }
208
209 // update child refs
210 if(this.firstChild == node){
211 this.setFirstChild(node.nextSibling);
212 }
213 if(this.lastChild == node){
214 this.setLastChild(node.previousSibling);
215 }
216
217 node.setOwnerTree(null);
218 // clear any references from the node
219 node.parentNode = null;
220 node.previousSibling = null;
221 node.nextSibling = null;
222 this.fireEvent('remove', this.ownerTree, this, node);
223 return node;
224 },
225
226 insertBefore : function(node, refNode){
227 if(!refNode){ // like standard Dom, refNode can be null for append
228 return this.appendChild(node);
229 }
230 // nothing to do
231 if(node == refNode){
232 return false;
233 }
234
235 if(this.fireEvent('beforeinsert', this.ownerTree, this, node, refNode) === false){
236 return false;
237 }
238 var index = this.childNodes.indexOf(refNode);
239 var oldParent = node.parentNode;
240 var refIndex = index;
241
242 // when moving internally, indexes will change after remove
243 if(oldParent == this && this.childNodes.indexOf(node) < index){
244 refIndex--;
245 }
246
247 // it's a move, make sure we move it cleanly
248 if(oldParent){
249 if(node.fireEvent('beforemove', node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
250 return false;
251 }
252 oldParent.removeChild(node);
253 }
254 if(refIndex == 0){
255 this.setFirstChild(node);
256 }
257 this.childNodes.splice(refIndex, 0, node);
258 node.parentNode = this;
259 var ps = this.childNodes[refIndex-1];
260 if(ps){
261 node.previousSibling = ps;
262 ps.nextSibling = node;
263 }
264 node.nextSibling = refNode;
265 node.setOwnerTree(this.getOwnerTree());
266 this.fireEvent('insert', this.ownerTree, this, node, refNode);
267 if(oldParent){
268 node.fireEvent('move', this.ownerTree, node, oldParent, this, refIndex, refNode);
269 }
270 return node;
271 },
272
273 item : function(index){
274 return this.childNodes[index];
275 },
276
277 replaceChild : function(newChild, oldChild){
278 this.insertBefore(newChild, oldChild);
279 this.removeChild(oldChild);
280 return oldChild;
281 },
282
283 indexOf : function(child){
284 return this.childNodes.indexOf(child);
285 },
286
287 getOwnerTree : function(){
288 // if it doesn't have one, look for one
289 if(!this.ownerTree){
290 var p = this;
291 while(p){
292 if(p.ownerTree){
293 this.ownerTree = p.ownerTree;
294 break;
295 }
296 p = p.parentNode;
297 }
298 }
299 return this.ownerTree;
300 },
301
302 setOwnerTree : function(tree){
303 // if it's move, we need to update everyone
304 if(tree != this.ownerTree){
305 if(this.ownerTree){
306 this.ownerTree.unregisterNode(this);
307 }
308 this.ownerTree = tree;
309 var cs = this.childNodes;
310 for(var i = 0, len = cs.length; i < len; i++) {
311 cs[i].setOwnerTree(tree);
312 }
313 if(tree){
314 tree.registerNode(this);
315 }
316 }
317 },
318
319 getPath : function(attr){
320 attr = attr || 'id';
321 var p = this.parentNode;
322 var b = [this.attributes[attr]];
323 while(p){
324 b.unshift(p.attributes[attr]);
325 p = p.parentNode;
326 }
327 var sep = this.getOwnerTree().pathSeparator;
328 return sep + b.join(sep);
329 },
330
331 bubble : function(fn, scope, args){
332 var p = this;
333 while(p){
334 if(fn.call(scope || p, args || p) === false){
335 break;
336 }
337 p = p.parentNode;
338 }
339 },
340
341 cascade : function(fn, scope, args){
342 if(fn.call(scope || this, args || this) !== false){
343 var cs = this.childNodes;
344 for(var i = 0, len = cs.length; i < len; i++) {
345 cs[i].cascade(fn, scope, args);
346 }
347 }
348 },
349
350 eachChild : function(fn, scope, args){
351 var cs = this.childNodes;
352 for(var i = 0, len = cs.length; i < len; i++) {
353 if(fn.call(scope || this, args || cs[i]) === false){
354 break;
355 }
356 }
357 },
358
359 findChild : function(attribute, value){
360 var cs = this.childNodes;
361 for(var i = 0, len = cs.length; i < len; i++) {
362 if(cs[i].attributes[attribute] == value){
363 return cs[i];
364 }
365 }
366 return null;
367 },
368
369 /**
370 * Sorts this nodes children using the supplied sort function
371 * @param {Function} fn
372 * @param {Object} scope
373 */
374 sort : function(fn, scope){
375 var cs = this.childNodes;
376 var len = cs.length;
377 if(len > 0){
378 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
379 cs.sort(sortFn);
380 for(var i = 0; i < len; i++){
381 var n = cs[i];
382 n.previousSibling = cs[i-1];
383 n.nextSibling = cs[i+1];
384 if(i == 0){
385 this.setFirstChild(n);
386 }
387 if(i == len-1){
388 this.setLastChild(n);
389 }
390 }
391 }
392 },
393
394 contains : function(node){
395 return node.isAncestor(this);
396 },
397
398 isAncestor : function(node){
399 var p = this.parentNode;
400 while(p){
401 if(p == node){
402 return true;
403 }
404 p = p.parentNode;
405 }
406 return false;
407 },
408
409 toString : function(){
410 return '[Node'+(this.id?' '+this.id:'')+']';
411 }
412});