Completed
Push — master ( 2177e8...5b06fe )
by Felipe
12s
created

js/xloadtree/xtree2.js   F

Complexity

Total Complexity 412
Complexity/F 2.71

Size

Lines of Code 1559
Function Count 152

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
nc 0
dl 0
loc 1559
rs 2.4
c 1
b 0
f 0
wmc 412
mnd 4
bc 314
fnc 152
bpm 2.0657
cpm 2.7105
noi 17

How to fix   Complexity   

Complexity

Complex classes like js/xloadtree/xtree2.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*----------------------------------------------------------------------------\
2
|                            XTree 2 PRE RELEASE                              |
3
|                                                                             |
4
| This is a pre release and redistribution is discouraged.                    |
5
| Watch http://webfx.eae.net for the final version                            |
6
|                                                                             |
7
|-----------------------------------------------------------------------------|
8
|                   Created by Erik Arvidsson & Emil A Eklund                 |
9
|                  (http://webfx.eae.net/contact.html#erik)                   |
10
|                  (http://webfx.eae.net/contact.html#emil)                   |
11
|                      For WebFX (http://webfx.eae.net/)                      |
12
|-----------------------------------------------------------------------------|
13
| A tree menu system for IE 5.5+, Mozilla 1.4+, Opera 7, KHTML                |
14
|-----------------------------------------------------------------------------|
15
|         Copyright (c) 1999 - 2005 Erik Arvidsson & Emil A Eklund            |
16
|-----------------------------------------------------------------------------|
17
| This software is provided "as is", without warranty of any kind, express or |
18
| implied, including  but not limited  to the warranties of  merchantability, |
19
| fitness for a particular purpose and noninfringement. In no event shall the |
20
| authors or  copyright  holders be  liable for any claim,  damages or  other |
21
| liability, whether  in an  action of  contract, tort  or otherwise, arising |
22
| from,  out of  or in  connection with  the software or  the  use  or  other |
23
| dealings in the software.                                                   |
24
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
25
| This  software is  available under the  three different licenses  mentioned |
26
| below.  To use this software you must chose, and qualify, for one of those. |
27
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
28
| The WebFX Non-Commercial License          http://webfx.eae.net/license.html |
29
| Permits  anyone the right to use the  software in a  non-commercial context |
30
| free of charge.                                                             |
31
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
32
| The WebFX Commercial license           http://webfx.eae.net/commercial.html |
33
| Permits the  license holder the right to use  the software in a  commercial |
34
| context. Such license must be specifically obtained, however it's valid for |
35
| any number of  implementations of the licensed software.                    |
36
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
37
| GPL - The GNU General Public License    http://www.gnu.org/licenses/gpl.txt |
38
| Permits anyone the right to use and modify the software without limitations |
39
| as long as proper  credits are given  and the original  and modified source |
40
| code are included. Requires  that the final product, software derivate from |
41
| the original  source or any  software  utilizing a GPL  component, such  as |
42
| this, is also licensed under the GPL license.                               |
43
|-----------------------------------------------------------------------------|
44
| 2004-02-21 | Pre release distributed to a few selected tester               |
45
| 2005-06-06 | Added single tab index to improve keyboard navigation          |
46
|-----------------------------------------------------------------------------|
47
| Dependencies: xtree2.css Used to define the look and feel                   |
48
|-----------------------------------------------------------------------------|
49
| Created 2003-??-?? | All changes are in the log above. | Updated 2004-06-06 |
50
\----------------------------------------------------------------------------*/
51
52
53
//
54
// WebFXTreePersisitance
55
function WebFXTreePersistence() {}
56
var _p = WebFXTreePersistence.prototype;
57
_p.getExpanded = function (oNode) {
58
	return false;
59
};
60
_p.setExpanded = function (oNode, bOpen) {};
61
62
63
// Cookie handling
64
function WebFXCookie() {}
65
66
_p = WebFXCookie.prototype;
67
68
_p.setCookie = function (sName, sValue, nDays) {
69
	var expires = "";
70
	if (typeof nDays == "number") {
71
		var d = new Date();
72
		d.setTime(d.getTime() + nDays * 24 * 60 * 60 * 1000);
73
		expires = "; expires=" + d.toGMTString();
74
	}
75
76
	document.cookie = sName + "=" + escape(sValue) + expires + "; path=/";
77
};
78
79
_p.getCookie = function (sName) {
80
	var re = new RegExp("(\;|^)[^;]*(" + sName + ")\=([^;]*)(;|$)");
81
	var res = re.exec(document.cookie);
82
	return res != null ? unescape(res[3]) : null;
83
};
84
85
_p.removeCookie = function (name) {
86
	this.setCookie(name, "", -1);
87
};
88
89
90
//
91
// persistence using cookies
92
//
93
// This is uses one cookie with the ids of the expanded nodes separated using '+'
94
//
95
function WebFXTreeCookiePersistence() {
96
	this._openedMap = {};
97
	this._cookies = new WebFXCookie;
98
	var s = this._cookies.getCookie(this.cookieName);
99
	if (s) {
100
		var a = s.split("+");
101
		for (var i = a.length - 1; i >= 0; i--)
102
			this._openedMap[a[i]] = true;
103
	}
104
}
105
106
_p = WebFXTreeCookiePersistence.prototype = new WebFXTreePersistence;
107
108
_p.cookieName = "webfx-tree-cookie-persistence"
109
110
_p.getExpanded = function (oNode) {
111
	return oNode.id in this._openedMap;
112
};
113
114
_p.setExpanded = function (oNode, bOpen) {
115
	var old = this.getExpanded(oNode);
116
	if (old != bOpen) {
117
		if (bOpen) {
118
			this._openedMap[oNode.id] = true;
119
		} else {
120
			delete this._openedMap[oNode.id];
121
		}
122
123
		var res = [];
124
		var i = 0;
125
		for (var id in this._openedMap)
126
			res[i++] = id;
127
		this._cookies.setCookie(this.cookieName, res.join("+"));
128
	}
129
};
130
131
132
// this object provides a few useful methods when working with arrays
133
var arrayHelper = {
134
	indexOf: function (a, o) {
135
		for (var i = 0; i < a.length; i++) {
136
			if (a[i] == o) {
137
				return i;
138
			}
139
		}
140
		return -1;
141
	},
142
143
	insertBefore: function (a, o, o2) {
144
		var i = this.indexOf(a, o2);
145
		if (i == -1) {
146
			a.push(o);
147
		} else {
148
			a.splice(i, 0, o);
149
		}
150
	},
151
152
	remove: function (a, o) {
153
		var i = this.indexOf(a, o);
154
		if (i != -1) {
155
			a.splice(i, 1);
156
		}
157
	}
158
};
159
160
///////////////////////////////////////////////////////////////////////////////
161
// WebFX Tree Config object                                                  //
162
///////////////////////////////////////////////////////////////////////////////
163
var webFXTreeConfig = {
164
	rootIcon: "images/folder.png",
165
	openRootIcon: "images/openfolder.png",
166
	folderIcon: "images/folder.png",
167
	openFolderIcon: "images/openfolder.png",
168
	fileIcon: "images/file.png",
169
	iIcon: "images/I.png",
170
	lIcon: "images/L.png",
171
	lMinusIcon: "images/Lminus.png",
172
	lPlusIcon: "images/Lplus.png",
173
	tIcon: "images/T.png",
174
	tMinusIcon: "images/Tminus.png",
175
	tPlusIcon: "images/Tplus.png",
176
	plusIcon: "images/plus.png",
177
	minusIcon: "images/minus.png",
178
	blankIcon: "images/blank.png",
179
	defaultText: "Tree Item",
180
	defaultAction: null,
181
	defaultBehavior: "classic",
182
	usePersistence: true
183
};
184
185
///////////////////////////////////////////////////////////////////////////////
186
// WebFX Tree Handler object                                                 //
187
///////////////////////////////////////////////////////////////////////////////
188
189
var webFXTreeHandler = {
190
	ie: /msie/i.test(navigator.userAgent),
191
	opera: /opera/i.test(navigator.userAgent),
192
	idCounter: 0,
193
	idPrefix: "wfxt-",
194
	getUniqueId: function () {
195
		return this.idPrefix + this.idCounter++;
196
	},
197
	all: {},
198
	getNodeById: function (sId) {
199
		return all[sId];
200
	},
201
	addNode: function (oNode) {
202
		this.all[oNode.id] = oNode;
203
	},
204
	removeNode: function (oNode) {
205
		delete this.all[oNode.id];
206
	},
207
208
	handleEvent: function (e) {
209
		var el = e.target || e.srcElement;
210
		while (el != null && !this.all[el.id]) {
211
			el = el.parentNode;
212
		}
213
214
		if (el == null) {
215
			return false;
216
		}
217
		var node = this.all[el.id];
218
		if (typeof node["_on" + e.type] == "function") {
219
			return node["_on" + e.type](e);
220
		}
221
		return false;
222
	},
223
224
	dispose: function () {
225
		if (this.disposed) return;
226
		for (var id in this.all) {
227
			this.all[id].dispose();
228
		}
229
		this.disposed = true;
230
	},
231
232
	htmlToText: function (s) {
233
		return String(s).replace(/\s+|<([^>])+>|&amp;|&lt;|&gt;|&quot;|&nbsp;/gi, this._htmlToText);
234
	},
235
236
	_htmlToText: function (s) {
237
		switch (s) {
238
		case "&amp;":
239
			return "&";
240
		case "&lt;":
241
			return "<";
242
		case "&gt;":
243
			return ">";
244
		case "&quot;":
245
			return "\"";
246
		case "&nbsp;":
247
			return String.fromCharCode(160);
248
		default:
249
			if (/\s+/.test(s)) {
250
				return " ";
251
			}
252
			if (/^<BR/gi.test(s)) {
253
				return "\n";
254
			}
255
			return "";
256
		}
257
	},
258
259
	textToHtml: function (s) {
260
		return String(s).replace(/&|<|>|\n|\"|\u00A0/g, this._textToHtml);
261
	},
262
263
	_textToHtml: function (s) {
264
		switch (s) {
265
		case "&":
266
			return "&amp;";
267
		case "<":
268
			return "&lt;";
269
		case ">":
270
			return "&gt;";
271
		case "\n":
272
			return "<BR>";
273
		case "\"":
274
			return "&quot;"; // so we can use this in attributes
275
		default:
276
			return "&nbsp;";
277
		}
278
	},
279
280
	persistenceManager: new WebFXTreeCookiePersistence()
281
};
282
283
284
///////////////////////////////////////////////////////////////////////////////
285
// WebFXTreeAbstractNode
286
///////////////////////////////////////////////////////////////////////////////
287
288
function WebFXTreeAbstractNode(sText, oAction, oIconAction) {
289
	this.childNodes = [];
290
	if (sText) this.text = sText;
291
	if (oAction) this.action = oAction;
292
	if (oIconAction) this.iconAction = oIconAction;
293
294
	this.id = webFXTreeHandler.getUniqueId();
295
	if (webFXTreeConfig.usePersistence) {
296
		this.open = webFXTreeHandler.persistenceManager.getExpanded(this);
297
	}
298
	webFXTreeHandler.addNode(this);
299
}
300
301
302
_p = WebFXTreeAbstractNode.prototype;
303
_p._selected = false;
304
_p.indentWidth = 15;
305
_p.open = false;
306
_p.text = webFXTreeConfig.defaultText;
307
_p.action = null;
308
_p.iconAcion = null;
309
_p.target = null;
310
_p.toolTip = null;
311
_p._focused = false;
312
313
/* begin tree model */
314
315
_p.add = function (oChild, oBefore) {
316
	var oldLast;
317
	var emptyBefore = this.childNodes.length == 0;
318
	var p = oChild.parentNode;
319
320
	if (oBefore == null) { // append
321
		if (p != null)
322
			p.remove(oChild);
323
		oldLast = this.getLastChild();
324
		this.childNodes.push(oChild);
325
	} else { // insertBefore
326
		if (oBefore.parentNode != this) {
327
			throw new Error("Can only add nodes before siblings");
328
		}
329
		if (p != null) {
330
			p.remove(oChild);
331
		}
332
333
		arrayHelper.insertBefore(this.childNodes, oChild, oBefore);
334
	}
335
336
	if (oBefore) {
337
		if (oBefore == this.firstChild) {
338
			this.firstChild = oChild;
339
		}
340
		oChild.previousSibling = oBefore.previousSibling;
341
		oBefore.previousSibling = oChild;
342
		oChild.nextSibling = oBefore;
343
	} else {
344
		if (!this.firstChild) {
345
			this.firstChild = oChild;
346
		}
347
		if (this.lastChild) {
348
			this.lastChild.nextSibling = oChild;
349
		}
350
		oChild.previousSibling = this.lastChild;
351
		this.lastChild = oChild;
352
	}
353
354
	oChild.parentNode = this;
355
	var t = this.getTree();
356
	if (t) {
357
		oChild.tree = t;
358
	}
359
	var d = this.getDepth();
360
	if (d != null) {
361
		oChild.depth = d + 1;
362
	}
363
364
	if (this.getCreated() && !t.getSuspendRedraw()) {
365
		var el = this.getChildrenElement();
366
		var newEl = oChild.create();
367
		var refEl = oBefore ? oBefore.getElement() : null;
368
		el.insertBefore(newEl, refEl);
369
370
		if (oldLast) {
371
			oldLast.updateExpandIcon();
372
		}
373
		if (emptyBefore) {
374
			this.setExpanded(this.getExpanded());
375
			// if we are using classic expand will not update icon
376
			if (t && t.getBehavior() != "classic")
377
				this.updateIcon();
378
		}
379
	}
380
381
	return oChild;
382
};
383
384
385
_p.remove = function (oChild) {
386
	// backwards compatible. If no argument remove the node
387
	if (arguments.length == 0) {
388
		if (this.parentNode) {
389
			return this.parentNode.remove(this);
390
		}
391
		return null;
392
	}
393
394
	// if we remove selected or tree with the selected we should select this
395
	var t = this.getTree();
396
	var si = t ? t.getSelected() : null;
397
	if (si == oChild || oChild.contains(si)) {
398
		if (si.getFocused()) {
399
			this.select();
400
			window.setTimeout("WebFXTreeAbstractNode._onTimeoutFocus(\"" + this.id + "\")", 10);
401
		} else {
402
			this.select();
403
		}
404
	}
405
406
	if (oChild.parentNode != this) {
407
		throw new Error("Can only remove children");
408
	}
409
	arrayHelper.remove(this.childNodes, oChild);
410
411
	if (this.lastChild == oChild) {
412
		this.lastChild = oChild.previousSibling;
413
	}
414
	if (this.firstChild == oChild) {
415
		this.firstChild = oChild.nextSibling;
416
	}
417
	if (oChild.previousSibling) {
418
		oChild.previousSibling.nextSibling = oChild.nextSibling;
419
	}
420
	if (oChild.nextSibling) {
421
		oChild.nextSibling.previousSibling = oChild.previousSibling;
422
	}
423
424
	var wasLast = oChild.isLastSibling();
425
426
	oChild.parentNode = null;
427
	oChild.tree = null;
428
	oChild.depth = null;
429
430
	if (t && this.getCreated() && !t.getSuspendRedraw()) {
431
		var el = this.getChildrenElement();
432
		var childEl = oChild.getElement();
433
		el.removeChild(childEl);
434
		if (wasLast) {
435
			var newLast = this.getLastChild();
436
			if (newLast) {
437
				newLast.updateExpandIcon();
438
			}
439
		}
440
		if (!this.hasChildren()) {
441
			el.style.display = "none";
442
			this.updateExpandIcon();
443
			this.updateIcon();
444
		}
445
	}
446
447
	return oChild;
448
};
449
450
WebFXTreeAbstractNode._onTimeoutFocus = function (sId) {
451
	var jsNode = webFXTreeHandler.all[sId];
452
	jsNode.focus();
453
};
454
455
_p.getId = function () {
456
	return this.id;
457
};
458
459
_p.getTree = function () {
460
	throw new Error("getTree called on Abstract Node");
461
};
462
463
_p.getDepth = function () {
464
	throw new Error("getDepth called on Abstract Node");
465
};
466
467
_p.getCreated = function () {
468
	var t = this.getTree();
469
	return t && t.rendered;
470
};
471
472
_p.getParent = function () {
473
	return this.parentNode;
474
};
475
476
_p.contains = function (oDescendant) {
477
	if (oDescendant == null) return false;
478
	if (oDescendant == this) return true;
479
	var p = oDescendant.parentNode;
480
	return this.contains(p);
481
};
482
483
_p.getChildren = _p.getChildNodes = function () {
484
	return this.childNodes;
485
};
486
487
_p.getFirstChild = function () {
488
	return this.childNodes[0];
489
};
490
491
_p.getLastChild = function () {
492
	return this.childNodes[this.childNodes.length - 1];
493
};
494
495
_p.getPreviousSibling = function () {
496
	return this.previousSibling;
497
	//var p = this.parentNode;
498
	//if (p == null) return null;
499
	//var cs = p.childNodes;
500
	//return cs[arrayHelper.indexOf(cs, this) - 1]
501
};
502
503
_p.getNextSibling = function () {
504
	return this.nextSibling;
505
	//var p = this.parentNode;
506
	//if (p == null) return null;
507
	//var cs = p.childNodes;
508
	//return cs[arrayHelper.indexOf(cs, this) + 1]
509
};
510
511
_p.hasChildren = function () {
512
	return this.childNodes.length > 0;
513
};
514
515
_p.isLastSibling = function () {
516
	return this.nextSibling == null;
517
	//return this.parentNode && this == this.parentNode.getLastChild();
518
};
519
520
_p.findChildByText = function (s, n) {
521
	if (!n) {
522
		n = 0;
523
	}
524
	var isRe = s instanceof RegExp;
525
	for (var i = 0; i < this.childNodes.length; i++) {
526
		if (isRe && s.test(this.childNodes[i].getText()) ||
527
			this.childNodes[i].getText() == s) {
528
			if (n == 0) {
529
				return this.childNodes[i];
530
			}
531
			n--;
532
		}
533
	}
534
	return null;
535
};
536
537
_p.findNodeByText = function (s, n) {
538
	if (!n) {
539
		n = 0;
540
	}
541
	var isRe = s instanceof RegExp;
542
	if (isRe && s.test(this.getText()) || this.getText() == s) {
543
		if (n == 0) {
544
			return this.childNodes[i];
545
		}
546
		n--;
547
	}
548
549
	var res;
550
	for (var i = 0; i < this.childNodes.length; i++) {
551
		res = this.childNodes[i].findNodeByText(s, n);
552
		if (res) {
553
			return res;
554
		}
555
	}
556
	return null;
557
};
558
559
/* end tree model */
560
561
_p.setId = function (sId) {
562
	var el = this.getElement();
563
	webFXTreeHandler.removeNode(this);
564
	this.id = sId;
565
	if (el) {
566
		el.id = sId;
567
	}
568
	webFXTreeHandler.addNode(this);
569
};
570
571
_p.isSelected = function () {
572
	return this._selected;
573
};
574
575
_p.select = function () {
576
	this._setSelected(true);
577
};
578
579
_p.deselect = function () {
580
	this._setSelected(false);
581
};
582
583
_p._setSelected = function (b) {
584
	var t = this.getTree();
585
	if (!t) return;
586
	if (this._selected != b) {
587
		this._selected = b;
588
589
		var wasFocused = false; // used to keep focus state
590
		var si = t.getSelected();
591
		if (b && si != null && si != this) {
592
			var oldFireChange = t._fireChange;
593
			wasFocused = si._focused;
594
			t._fireChange = false;
595
			si._setSelected(false);
596
			t._fireChange = oldFireChange;
597
		}
598
599
		var el = this.getRowElement();
600
		if (el) {
601
			el.className = this.getRowClassName();
602
		}
603
		if (b) {
604
			this._setTabIndex(t.tabIndex);
605
			t._selectedItem = this;
606
			t._fireOnChange();
607
			t.setSelected(this);
608
			if (wasFocused) {
609
				this.focus();
610
			}
611
		} else {
612
			this._setTabIndex(-1);
613
		}
614
615
		if (t.getBehavior() != "classic") {
616
			this.updateIcon();
617
		}
618
	}
619
};
620
621
622
_p.getExpanded = function () {
623
	return this.open;
624
};
625
626
_p.setExpanded = function (b) {
627
	var ce;
628
	this.open = b;
629
	var t = this.getTree();
630
	if (this.hasChildren()) {
631
		var si = t ? t.getSelected() : null;
632
		if (!b && this.contains(si)) {
633
			this.select();
634
		}
635
636
		var el = this.getElement();
637
		if (el) {
638
			ce = this.getChildrenElement();
639
			if (ce) {
640
				ce.style.display = b ? "block" : "none";
641
			}
642
			var eie = this.getExpandIconElement();
643
			if (eie) {
644
				eie.src = this.getExpandIconSrc();
645
			}
646
		}
647
648
		if (webFXTreeConfig.usePersistence) {
649
			webFXTreeHandler.persistenceManager.setExpanded(this, b);
650
		}
651
	} else {
652
		ce = this.getChildrenElement();
653
		if (ce)
654
			ce.style.display = "none";
655
	}
656
	if (t && t.getBehavior() == "classic") {
657
		this.updateIcon();
658
	}
659
};
660
661
_p.toggle = function () {
662
	this.setExpanded(!this.getExpanded());
663
};
664
665
_p.expand = function () {
666
	this.setExpanded(true);
667
};
668
669
_p.collapse = function () {
670
	this.setExpanded(false);
671
};
672
673
_p.collapseChildren = function () {
674
	var cs = this.childNodes;
675
	for (var i = 0; i < cs.length; i++) {
676
		cs[i].collapseAll();
677
	}
678
};
679
680
_p.collapseAll = function () {
681
	this.collapseChildren();
682
	this.collapse();
683
};
684
685
_p.expandChildren = function () {
686
	var cs = this.childNodes;
687
	for (var i = 0; i < cs.length; i++) {
688
		cs[i].expandAll();
689
	}
690
};
691
692
_p.expandAll = function () {
693
	this.expandChildren();
694
	this.expand();
695
};
696
697
_p.reveal = function () {
698
	var p = this.getParent();
699
	if (p) {
700
		p.setExpanded(true);
701
		p.reveal();
702
	}
703
};
704
705
_p.openPath = function (sPath, bSelect, bFocus) {
706
	if (sPath == "") {
707
		if (bSelect) {
708
			this.select();
709
		}
710
		if (bFocus) {
711
			window.setTimeout("WebFXTreeAbstractNode._onTimeoutFocus(\"" + this.id + "\")", 10);
712
		}
713
		return;
714
	}
715
716
	var parts = sPath.split("/");
717
	var remainingPath = parts.slice(1).join("/");
718
	var t = this.getTree();
719
	if (sPath.charAt(0) == "/") {
720
		if (t) {
721
			t.openPath(remainingPath, bSelect, bFocus);
722
		} else {
723
			throw "Invalid path";
724
		}
725
	} else {
726
		// open
727
		this.setExpanded(true);
728
		parts = sPath.split("/");
729
		var ti = this.findChildByText(parts[0]);
730
		if (!ti) {
731
			throw "Could not find child node with text \"" + parts[0] + "\"";
732
		}
733
		ti.openPath(remainingPath, bSelect, bFocus);
734
	}
735
};
736
737
_p.focus = function () {
738
	var el = this.getLabelElement();
739
	if (el) {
740
		el.focus();
741
	}
742
};
743
744
_p.getFocused = function () {
745
	return this._focused;
746
};
747
748
_p._setTabIndex = function (i) {
749
	var a = this.getLabelElement();
750
	if (a) {
751
		a.setAttribute("tabindex", i);
752
	}
753
};
754
755
756
// HTML generation
757
758
_p.toHtml = function () {
759
	var sb = [];
760
	var cs = this.childNodes;
761
	var l = cs.length;
762
	for (var y = 0; y < l; y++) {
763
		sb[y] = cs[y].toHtml();
764
	}
765
766
	var t = this.getTree();
767
	var hideLines = !t.getShowLines() || t == this.parentNode && !t.getShowRootLines();
768
769
	var childrenHtml = "<div class=\"webfx-tree-children" +
770
		(hideLines ? "-nolines" : "") + "\" style=\"" +
771
		this.getLineStyle() +
772
		(this.getExpanded() && this.hasChildren() ? "" : "display:none;") +
773
		"\">" +
774
		sb.join("") +
775
		"</div>";
776
777
	return "<div class=\"webfx-tree-item\" id=\"" +
778
		this.id + "\"" + this.getEventHandlersHtml() + ">" +
779
		this.getRowHtml() +
780
		childrenHtml +
781
		"</div>";
782
};
783
784
_p.getRowHtml = function () {
785
	var t = this.getTree();
786
	return "<div class=\"" + this.getRowClassName() + "\" style=\"padding-left:" +
787
		Math.max(0, (this.getDepth() - 1) * this.indentWidth) + "px\">" +
788
		this.getExpandIconHtml() +
789
		//"<span class=\"webfx-tree-icon-and-label\">" +
790
		this.getIconHtml() +
791
		this.getLabelHtml() +
792
		//"</span>" +
793
		"</div>";
794
};
795
796
_p.getRowClassName = function () {
797
	return "webfx-tree-row" + (this.isSelected() ? " selected" : "") +
798
		(this.action ? "" : " no-action");
799
};
800
801
_p.getLabelHtml = function () {
802
	var toolTip = this.getToolTip();
803
	var target = this.getTarget();
804
	var link = this._getHref();
805
806
	if (link == '#') {
807
		return "<span class=\"webfx-tree-item-label\" tabindex=\"-1\"" +
808
			(toolTip ? " title=\"" + webFXTreeHandler.textToHtml(toolTip) + "\"" : "") +
809
			" onfocus=\"webFXTreeHandler.handleEvent(event)\"" +
810
			" onblur=\"webFXTreeHandler.handleEvent(event)\">" +
811
			this.getHtml() + "</span>";
812
	}
813
814
	return "<a href=\"" + webFXTreeHandler.textToHtml(link) +
815
		"\" class=\"webfx-tree-item-label\" tabindex=\"-1\"" +
816
		(toolTip ? " title=\"" + webFXTreeHandler.textToHtml(toolTip) + "\"" : "") +
817
		(target ? " target=\"" + target + "\"" : "") +
818
		" onfocus=\"webFXTreeHandler.handleEvent(event)\"" +
819
		" onblur=\"webFXTreeHandler.handleEvent(event)\">" +
820
		this.getHtml() + "</a>";
821
};
822
823
_p._getHref = function () {
824
	if (typeof this.action == "string")
825
		return this.action;
826
	else
827
		return "#";
828
};
829
830
_p._getIconHref = function () {
831
	if (typeof this.iconAction == "string")
832
		return this.iconAction;
833
	else
834
		return this._getHref();
835
}
836
837
_p.getEventHandlersHtml = function () {
838
	return "";
839
};
840
841
_p.getIconHtml = function () {
842
	// here we are not using textToHtml since the file names rarerly contains
843
	// HTML...
844
	var link = this._getIconHref();
845
	if (link == '#')
846
		return "<img class=\"webfx-tree-icon\" src=\"" + this.getIconSrc() + "\">&nbsp;";
847
848
	return "<a href=\"" + webFXTreeHandler.textToHtml(link) +
849
		"\"><img class=\"webfx-tree-icon\" src=\"" + this.getIconSrc() + "\"></a>";
850
};
851
852
_p.getIconSrc = function () {
853
	throw new Error("getIconSrc called on Abstract Node");
854
};
855
856
_p.getExpandIconHtml = function () {
857
	// here we are not using textToHtml since the file names rarerly contains
858
	// HTML...
859
	return "<img class=\"webfx-tree-expand-icon\" src=\"" +
860
		this.getExpandIconSrc() + "\">";
861
};
862
863
864
_p.getExpandIconSrc = function () {
865
	var src;
866
	var t = this.getTree();
867
	var hideLines = !t.getShowLines() || t == this.parentNode && !t.getShowRootLines();
868
869
	if (this.hasChildren()) {
870
		var bits = 0;
871
		/*
872
			Bitmap used to determine which icon to use
873
			1  Plus
874
			2  Minus
875
			4  T Line
876
			8  L Line
877
		*/
878
879
		if (t && t.getShowExpandIcons()) {
880
			if (this.getExpanded()) {
881
				bits = 2;
882
			} else {
883
				bits = 1;
884
			}
885
		}
886
887
		if (t && !hideLines) {
888
			if (this.isLastSibling()) {
889
				bits += 4;
890
			} else {
891
				bits += 8;
892
			}
893
		}
894
895
		switch (bits) {
896
		case 1:
897
			return webFXTreeConfig.plusIcon;
898
		case 2:
899
			return webFXTreeConfig.minusIcon;
900
		case 4:
901
			return webFXTreeConfig.lIcon;
902
		case 5:
903
			return webFXTreeConfig.lPlusIcon;
904
		case 6:
905
			return webFXTreeConfig.lMinusIcon;
906
		case 8:
907
			return webFXTreeConfig.tIcon;
908
		case 9:
909
			return webFXTreeConfig.tPlusIcon;
910
		case 10:
911
			return webFXTreeConfig.tMinusIcon;
912
		default: // 0
913
			return webFXTreeConfig.blankIcon;
914
		}
915
	} else {
916
		if (t && hideLines) {
917
			return webFXTreeConfig.blankIcon;
918
		} else if (this.isLastSibling()) {
919
			return webFXTreeConfig.lIcon;
920
		} else {
921
			return webFXTreeConfig.tIcon;
922
		}
923
	}
924
};
925
926
_p.getLineStyle = function () {
927
	return "background-position:" + this.getLineStyle2() + ";";
928
};
929
930
_p.getLineStyle2 = function () {
931
	return (this.isLastSibling() ? "-100" : (this.getDepth() - 1) * this.indentWidth) + "px 0";
932
};
933
934
// End HTML generation
935
936
// DOM
937
// this returns the div for the tree node
938
_p.getElement = function () {
939
	return document.getElementById(this.id);
940
};
941
942
// the row is the div that is used to draw the node without the children
943
_p.getRowElement = function () {
944
	var el = this.getElement();
945
	if (!el) return null;
946
	return el.firstChild;
947
};
948
949
// plus/minus image
950
_p.getExpandIconElement = function () {
951
	var el = this.getRowElement();
952
	if (!el) return null;
953
	return el.firstChild;
954
};
955
956
_p.getIconElement = function () {
957
	var el = this.getRowElement();
958
	if (!el) return null;
959
	return el.childNodes[1];
960
};
961
962
// anchor element
963
_p.getLabelElement = function () {
964
	var el = this.getRowElement();
965
	if (!el) return null;
966
	return el.lastChild;
967
};
968
969
// the div containing the children
970
_p.getChildrenElement = function () {
971
	var el = this.getElement();
972
	if (!el) return null;
973
	return el.lastChild;
974
};
975
976
977
// IE uses about:blank if not attached to document and this can cause Win2k3
978
// to fail
979
if (webFXTreeHandler.ie) {
980
	_p.create = function () {
981
		var dummy = document.createElement("div");
982
		dummy.style.display = "none";
983
		document.body.appendChild(dummy);
984
		dummy.innerHTML = this.toHtml();
985
		var res = dummy.removeChild(dummy.firstChild);
986
		document.body.removeChild(dummy);
987
		return res;
988
	};
989
} else {
990
	_p.create = function () {
991
		var dummy = document.createElement("div");
992
		dummy.innerHTML = this.toHtml();
993
		return dummy.removeChild(dummy.firstChild);
994
	};
995
}
996
997
// Getters and setters for some common fields
998
999
_p.setIcon = function (s) {
1000
	this.icon = s;
1001
	if (this.getCreated()) {
1002
		this.updateIcon();
1003
	}
1004
};
1005
1006
_p.getIcon = function () {
1007
	return this.icon;
1008
};
1009
1010
_p.setOpenIcon = function (s) {
1011
	this.openIcon = s;
1012
	if (this.getCreated()) {
1013
		this.updateIcon();
1014
	}
1015
};
1016
1017
_p.getOpenIcon = function () {
1018
	return this.openIcon;
1019
};
1020
1021
_p.setText = function (s) {
1022
	this.setHtml(webFXTreeHandler.textToHtml(s));
1023
};
1024
1025
_p.getText = function () {
1026
	return webFXTreeHandler.htmlToText(this.getHtml());
1027
};
1028
1029
_p.setHtml = function (s) {
1030
	this.text = s;
1031
	var el = this.getLabelElement();
1032
	if (el) {
1033
		el.innerHTML = s;
1034
	}
1035
};
1036
1037
_p.getHtml = function () {
1038
	return this.text;
1039
};
1040
1041
_p.setTarget = function (s) {
1042
	this.target = s;
1043
};
1044
1045
_p.getTarget = function () {
1046
	return this.target;
1047
};
1048
1049
_p.setToolTip = function (s) {
1050
	this.toolTip = s;
1051
	var el = this.getLabelElement();
1052
	if (el) {
1053
		el.title = s;
1054
	}
1055
};
1056
1057
_p.getToolTip = function () {
1058
	return this.toolTip;
1059
};
1060
1061
_p.setAction = function (oAction) {
1062
	this.action = oAction;
1063
	var el = this.getLabelElement();
1064
	if (el) {
1065
		el.href = this._getHref();
1066
	}
1067
	el = this.getRowElement();
1068
	if (el) {
1069
		el.className = this.getRowClassName();
1070
	}
1071
};
1072
1073
_p.getAction = function () {
1074
	return this.action;
1075
};
1076
1077
_p.setIconAction = function (oAction) {
1078
	this.iconAction = oAction;
1079
	var el = this.getIconElement();
1080
	if (el) {
1081
		el.href = this._getIconHref();
1082
	}
1083
}
1084
1085
_p.getIconAction = function () {
1086
	if (this.iconAction)
1087
		return this.iconAction;
1088
	return _p.getAction();
1089
};
1090
1091
// update methods
1092
1093
_p.update = function () {
1094
	var t = this.getTree();
1095
	if (t.suspendRedraw) return;
1096
	var el = this.getElement();
1097
	if (!el || !el.parentNode) return;
1098
	var newEl = this.create();
1099
	el.parentNode.replaceChild(newEl, el);
1100
	this._setTabIndex(this.tabIndex); // in case root had the tab index
1101
	var si = t.getSelected();
1102
	if (si && si.getFocused()) {
1103
		si.focus();
1104
	}
1105
};
1106
1107
_p.updateExpandIcon = function () {
1108
	var t = this.getTree();
1109
	if (t.suspendRedraw) return;
1110
	var img = this.getExpandIconElement();
1111
	img.src = this.getExpandIconSrc();
1112
	var cel = this.getChildrenElement();
1113
	cel.style.backgroundPosition = this.getLineStyle2();
1114
};
1115
1116
_p.updateIcon = function () {
1117
	var t = this.getTree();
1118
	if (t.suspendRedraw) return;
1119
	var img = this.getIconElement();
1120
	img.src = this.getIconSrc();
1121
};
1122
1123
// End DOM
1124
1125
_p._callSuspended = function (f) {
1126
	var t = this.getTree();
1127
	var sr = t.getSuspendRedraw();
1128
	t.setSuspendRedraw(true);
1129
	f.call(this);
1130
	t.setSuspendRedraw(sr);
1131
};
1132
1133
// Event handlers
1134
1135
_p._onmousedown = function (e) {
1136
	var el = e.target || e.srcElement;
1137
	// expand icon
1138
	if (/webfx-tree-expand-icon/.test(el.className) && this.hasChildren()) {
1139
		this.toggle();
1140
		if (webFXTreeHandler.ie) {
1141
			window.setTimeout("WebFXTreeAbstractNode._onTimeoutFocus(\"" + this.id + "\")", 10);
1142
		}
1143
		return false;
1144
	}
1145
1146
	this.select();
1147
	if ( /*!/webfx-tree-item-label/.test(el.className) && */ !webFXTreeHandler.opera) { // opera cancels the click if focus is called
1148
1149
		// in case we are not clicking on the label
1150
		if (webFXTreeHandler.ie) {
1151
			window.setTimeout("WebFXTreeAbstractNode._onTimeoutFocus(\"" + this.id + "\")", 10);
1152
		} else {
1153
			this.focus();
1154
		}
1155
	}
1156
	var rowEl = this.getRowElement();
1157
	if (rowEl) {
1158
		rowEl.className = this.getRowClassName();
1159
	}
1160
1161
	return false;
1162
};
1163
1164
_p._onclick = function (e) {
1165
	var el = e.target || e.srcElement;
1166
	// expand icon
1167
	if (/webfx-tree-expand-icon/.test(el.className) && this.hasChildren()) {
1168
		return false;
1169
	}
1170
1171
	var doAction = null;
1172
	if (/webfx-tree-icon/.test(el.className) && this.iconAction) {
1173
		doAction = this.iconAction;
1174
	} else {
1175
		doAction = this.action;
1176
	}
1177
1178
	if (typeof doAction == "function") {
1179
		doAction();
1180
	} else if (doAction != null) {
1181
		window.open(doAction, this.target || "_self");
1182
	}
1183
	return false;
1184
};
1185
1186
1187
_p._ondblclick = function (e) {
1188
	var el = e.target || e.srcElement;
1189
	// expand icon
1190
	if (/webfx-tree-expand-icon/.test(el.className) && this.hasChildren()) {
1191
		return;
1192
	}
1193
1194
	this.toggle();
1195
};
1196
1197
_p._onfocus = function (e) {
1198
	this.select();
1199
	this._focused = true;
1200
};
1201
1202
_p._onblur = function (e) {
1203
	this._focused = false;
1204
};
1205
1206
_p._onkeydown = function (e) {
1207
	var n;
1208
	var rv = true;
1209
	switch (e.keyCode) {
1210
	case 39: // RIGHT
1211
		if (e.altKey) {
1212
			rv = true;
1213
			break;
1214
		}
1215
		if (this.hasChildren()) {
1216
			if (!this.getExpanded()) {
1217
				this.setExpanded(true);
1218
			} else {
1219
				this.getFirstChild().focus();
1220
			}
1221
		}
1222
		rv = false;
1223
		break;
1224
	case 37: // LEFT
1225
		if (e.altKey) {
1226
			rv = true;
1227
			break;
1228
		}
1229
		if (this.hasChildren() && this.getExpanded()) {
1230
			this.setExpanded(false);
1231
		} else {
1232
			var p = this.getParent();
1233
			var t = this.getTree();
1234
			// don't go to root if hidden
1235
			if (p && (t.showRootNode || p != t)) {
1236
				p.focus();
1237
			}
1238
		}
1239
		rv = false;
1240
		break;
1241
1242
	case 40: // DOWN
1243
		n = this.getNextShownNode();
1244
		if (n) {
1245
			n.focus();
1246
		}
1247
		rv = false;
1248
		break;
1249
	case 38: // UP
1250
		n = this.getPreviousShownNode()
1251
		if (n) {
1252
			n.focus();
1253
		}
1254
		rv = false;
1255
		break;
1256
	}
1257
1258
	if (!rv && e.preventDefault) {
1259
		e.preventDefault();
1260
	}
1261
	e.returnValue = rv;
1262
	return rv;
1263
};
1264
1265
_p._onkeypress = function (e) {
1266
	if (!e.altKey && e.keyCode >= 37 && e.keyCode <= 40) {
1267
		if (e.preventDefault) {
1268
			e.preventDefault();
1269
		}
1270
		e.returnValue = false;
1271
		return false;
1272
	}
1273
};
1274
1275
// End event handlers
1276
1277
_p.dispose = function () {
1278
	if (this.disposed) return;
1279
	for (var i = this.childNodes.length - 1; i >= 0; i--) {
1280
		this.childNodes[i].dispose();
1281
	}
1282
	this.tree = null;
1283
	this.parentNode = null;
1284
	this.childNodes = null;
1285
	this.disposed = true;
1286
};
1287
1288
// Some methods that are usable when navigating the tree using the arrows
1289
_p.getLastShownDescendant = function () {
1290
	if (!this.getExpanded() || !this.hasChildren()) {
1291
		return this;
1292
	}
1293
	// we know there is at least 1 child
1294
	return this.getLastChild().getLastShownDescendant();
1295
};
1296
1297
_p.getNextShownNode = function () {
1298
	if (this.hasChildren() && this.getExpanded()) {
1299
		return this.getFirstChild();
1300
	} else {
1301
		var p = this;
1302
		var next;
1303
		while (p != null) {
1304
			next = p.getNextSibling();
1305
			if (next != null) {
1306
				return next;
1307
			}
1308
			p = p.getParent();
1309
		}
1310
		return null;
1311
	}
1312
};
1313
1314
_p.getPreviousShownNode = function () {
1315
	var ps = this.getPreviousSibling();
1316
	if (ps != null) {
1317
		return ps.getLastShownDescendant();
1318
	}
1319
	var p = this.getParent();
1320
	var t = this.getTree();
1321
	if (!t.showRootNode && p == t) {
1322
		return null;
1323
	}
1324
	return p;
1325
};
1326
1327
1328
///////////////////////////////////////////////////////////////////////////////
1329
// WebFXTree
1330
///////////////////////////////////////////////////////////////////////////////
1331
1332
function WebFXTree(sText, oAction, sBehavior, sIcon, oIconAction, sOpenIcon) {
1333
	WebFXTreeAbstractNode.call(this, sText, oAction, oIconAction);
1334
	if (sIcon) this.icon = sIcon;
1335
	if (sOpenIcon) this.openIcon = sOpenIcon;
1336
	if (sBehavior) this.behavior = sBehavior;
1337
}
1338
1339
_p = WebFXTree.prototype = new WebFXTreeAbstractNode;
1340
_p.indentWidth = 19;
1341
_p.open = true;
1342
_p._selectedItem = null;
1343
_p._fireChange = true;
1344
_p.rendered = false;
1345
_p.suspendRedraw = false;
1346
_p.showLines = true;
1347
_p.showExpandIcons = true;
1348
_p.showRootNode = true;
1349
_p.showRootLines = true;
1350
1351
_p.getTree = function () {
1352
	return this;
1353
};
1354
1355
_p.getDepth = function () {
1356
	return 0;
1357
};
1358
1359
_p.getCreated = function () {
1360
	return this.rendered;
1361
};
1362
1363
1364
/* end tree model */
1365
1366
_p.getExpanded = function () {
1367
	return !this.showRootNode || WebFXTreeAbstractNode.prototype.getExpanded.call(this);
1368
};
1369
1370
_p.setExpanded = function (b) {
1371
	if (!this.showRootNode) {
1372
		this.open = b;
1373
	} else {
1374
		WebFXTreeAbstractNode.prototype.setExpanded.call(this, b);
1375
	}
1376
};
1377
1378
_p.getExpandIconHtml = function () {
1379
	return "";
1380
};
1381
1382
// we don't have an expand icon here
1383
_p.getIconElement = function () {
1384
	var el = this.getRowElement();
1385
	if (!el) return null;
1386
	return el.firstChild;
1387
};
1388
1389
// no expand icon for root element
1390
_p.getExpandIconElement = function (oDoc) {
1391
	return null;
1392
};
1393
1394
_p.updateExpandIcon = function () {
1395
	// no expand icon
1396
};
1397
1398
_p.getRowClassName = function () {
1399
	return WebFXTreeAbstractNode.prototype.getRowClassName.call(this) +
1400
		(this.showRootNode ? "" : " webfx-tree-hide-root");
1401
};
1402
1403
1404
// if classic then the openIcon is used for expanded, otherwise openIcon is used
1405
// for selected
1406
1407
_p.getIconSrc = function () {
1408
	var behavior = this.getTree() ? this.getTree().getBehavior() : webFXTreeConfig.defaultBehavior;
1409
	var open = behavior == "classic" && this.getExpanded() ||
1410
		behavior != "classic" && this.isSelected();
1411
	if (open && this.openIcon) {
1412
		return this.openIcon;
1413
	}
1414
	if (!open && this.icon) {
1415
		return this.icon;
1416
	}
1417
	// fall back on default icons
1418
	return open ? webFXTreeConfig.openRootIcon : webFXTreeConfig.rootIcon;
1419
};
1420
1421
_p.getEventHandlersHtml = function () {
1422
	return " onclick=\"return webFXTreeHandler.handleEvent(event)\" " +
1423
		"onmousedown=\"return webFXTreeHandler.handleEvent(event)\" " +
1424
		"ondblclick=\"return webFXTreeHandler.handleEvent(event)\" " +
1425
		"onkeydown=\"return webFXTreeHandler.handleEvent(event)\" " +
1426
		"onkeypress=\"return webFXTreeHandler.handleEvent(event)\"";
1427
};
1428
1429
_p.setSelected = function (o) {
1430
	if (this._selectedItem != o && o) {
1431
		o._setSelected(true);
1432
	}
1433
};
1434
1435
_p._fireOnChange = function () {
1436
	if (this._fireChange && typeof this.onchange == "function") {
1437
		this.onchange();
1438
	}
1439
};
1440
1441
_p.getSelected = function () {
1442
	return this._selectedItem;
1443
};
1444
1445
_p.tabIndex = "";
1446
1447
_p.setTabIndex = function (i) {
1448
	var n = this._selectedItem || (this.showRootNode ? this : this.firstChild);
1449
	this.tabIndex = i;
1450
	if (n) {
1451
		n._setTabIndex(i);
1452
	}
1453
};
1454
1455
_p.getTabIndex = function () {
1456
	return this.tabIndex;
1457
};
1458
1459
_p.setBehavior = function (s) {
1460
	this.behavior = s;
1461
};
1462
1463
_p.getBehavior = function () {
1464
	return this.behavior || webFXTreeConfig.defaultBehavior;
1465
};
1466
1467
_p.setShowLines = function (b) {
1468
	if (this.showLines != b) {
1469
		this.showLines = b;
1470
		if (this.rendered) {
1471
			this.update();
1472
		}
1473
	}
1474
};
1475
1476
_p.getShowLines = function () {
1477
	return this.showLines;
1478
};
1479
1480
_p.setShowRootLines = function (b) {
1481
	if (this.showRootLines != b) {
1482
		this.showRootLines = b;
1483
		if (this.rendered) {
1484
			this.update();
1485
		}
1486
	}
1487
};
1488
1489
_p.getShowRootLines = function () {
1490
	return this.showRootLines;
1491
};
1492
1493
_p.setShowExpandIcons = function (b) {
1494
	if (this.showExpandIcons != b) {
1495
		this.showExpandIcons = b;
1496
		if (this.rendered) {
1497
			this.getTree().update();
1498
		}
1499
	}
1500
};
1501
1502
_p.getShowExpandIcons = function () {
1503
	return this.showExpandIcons;
1504
};
1505
1506
_p.setShowRootNode = function (b) {
1507
	if (this.showRootNode != b) {
1508
		this.showRootNode = b;
1509
		if (this.rendered) {
1510
			this.getTree().update();
1511
		}
1512
	}
1513
};
1514
1515
_p.getShowRoootNode = function () {
1516
	return this.showRootNode;
1517
};
1518
1519
_p.onchange = function () {};
1520
1521
_p.create = function () {
1522
	var el = WebFXTreeAbstractNode.prototype.create.call(this);
1523
	this.setTabIndex(this.tabIndex);
1524
	this.rendered = true;
1525
	return el;
1526
};
1527
1528
_p.write = function () {
1529
	document.write(this.toHtml());
1530
	this.setTabIndex(this.tabIndex);
1531
	this.rendered = true;
1532
};
1533
1534
_p.setSuspendRedraw = function (b) {
1535
	this.suspendRedraw = b;
1536
};
1537
1538
_p.getSuspendRedraw = function () {
1539
	return this.suspendRedraw;
1540
};
1541
1542
1543
///////////////////////////////////////////////////////////////////////////////
1544
// WebFXTreeItem
1545
///////////////////////////////////////////////////////////////////////////////
1546
1547
function WebFXTreeItem(sText, oAction, eParent, sIcon, oIconAction, sOpenIcon) {
1548
	WebFXTreeAbstractNode.call(this, sText, oAction, oIconAction);
1549
	if (sIcon) this.icon = sIcon;
1550
	if (sOpenIcon) this.openIcon = sOpenIcon;
1551
	if (eParent) eParent.add(this);
1552
}
1553
1554
_p = WebFXTreeItem.prototype = new WebFXTreeAbstractNode;
1555
_p.tree = null;
1556
1557
/* tree model */
1558
1559
_p.getDepth = function () {
1560
	if (this.depth != null) {
1561
		return this.depth;
1562
	}
1563
	if (this.parentNode) {
1564
		var pd = this.parentNode.getDepth();
1565
		return this.depth = (pd != null ? pd + 1 : null);
1566
	}
1567
	return null;
1568
};
1569
1570
_p.getTree = function () {
1571
	if (this.tree) {
1572
		return this.tree;
1573
	}
1574
	if (this.parentNode) {
1575
		return this.tree = this.parentNode.getTree();
1576
	}
1577
	return null;
1578
};
1579
1580
_p.getCreated = function () {
1581
	var t = this.getTree();
1582
	return t && t.getCreated();
1583
};
1584
1585
// if classic then the openIcon is used for expanded, otherwise openIcon is used
1586
// for selected
1587
_p.getIconSrc = function () {
1588
	var behavior = this.getTree() ? this.getTree().getBehavior() : webFXTreeConfig.defaultBehavior;
1589
	var open = behavior == "classic" && this.getExpanded() ||
1590
		behavior != "classic" && this.isSelected();
1591
	if (open && this.openIcon) {
1592
		return this.openIcon;
1593
	}
1594
	if (!open && this.icon) {
1595
		return this.icon;
1596
	}
1597
1598
	// fall back on default icons
1599
	if (this.hasChildren()) {
1600
		return open ? webFXTreeConfig.openFolderIcon : webFXTreeConfig.folderIcon;
1601
	}
1602
	return webFXTreeConfig.fileIcon;
1603
};
1604
1605
/* end tree model */
1606
1607
1608
if (window.attachEvent) {
1609
	window.attachEvent("onunload", function () {
1610
		for (var id in webFXTreeHandler.all)
1611
			webFXTreeHandler.all[id].dispose();
1612
	});
1613
}