xtree.js
1
2 /*----------------------------------------------------------------------------\
3 | Cross Browser Tree Widget 1.17 |
4 |-----------------------------------------------------------------------------|
5 | Created by Emil A Eklund |
6 | (http://webfx.eae.net/contact.html#emil) |
7 | For WebFX (http://webfx.eae.net/) |
8 |-----------------------------------------------------------------------------|
9 | An object based tree widget, emulating the one found in microsoft windows, |
10 | with persistence using cookies. Works in IE 5+, Mozilla and konqueror 3. |
11 |-----------------------------------------------------------------------------|
12 | Copyright (c) 2000, 2001, 2002, 2003, 2006 Emil A Eklund |
13 |-----------------------------------------------------------------------------|
14 | Licensed under the Apache License, Version 2.0 (the "License"); you may not |
15 | use this file except in compliance with the License. You may obtain a copy |
16 | of the License at http://www.apache.org/licenses/LICENSE-2.0 |
17 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
18 | Unless required by applicable law or agreed to in writing, software |
19 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
20 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
21 | License for the specific language governing permissions and limitations |
22 | under the License. |
23 |-----------------------------------------------------------------------------|
24 | Dependencies: xtree.css (To set up the CSS of the tree classes) |
25 |-----------------------------------------------------------------------------|
26 | 2001-01-10 | Original Version Posted. |
27 | 2001-03-18 | Added getSelected and get/setBehavior that can make it behave |
28 | | more like windows explorer, check usage for more information. |
29 | 2001-09-23 | Version 1.1 - New features included keyboard navigation (ie) |
30 | | and the ability to add and remove nodes dynamically and some |
31 | | other small tweaks and fixes. |
32 | 2002-01-27 | Version 1.11 - Bug fixes and improved mozilla support. |
33 | 2002-06-11 | Version 1.12 - Fixed a bug that prevented the indentation line |
34 | | from updating correctly under some circumstances. This bug |
35 | | happened when removing the last item in a subtree and items in |
36 | | siblings to the remove subtree where not correctly updated. |
37 | 2002-06-13 | Fixed a few minor bugs cased by the 1.12 bug-fix. |
38 | 2002-08-20 | Added usePersistence flag to allow disable of cookies. |
39 | 2002-10-23 | (1.14) Fixed a plus icon issue |
40 | 2002-10-29 | (1.15) Last changes broke more than they fixed. This version |
41 | | is based on 1.13 and fixes the bugs 1.14 fixed withou breaking |
42 | | lots of other things. |
43 | 2003-02-15 | The selected node can now be made visible even when the tree |
44 | | control loses focus. It uses a new class declaration in the |
45 | | css file '.webfx-tree-item a.selected-inactive', by default it |
46 | | puts a light-gray rectangle around the selected node. |
47 | 2003-03-16 | Adding target support after lots of lobbying
|
48 | 2006-05-26 | Changed license to Apache Software License 2.0. |
49 |-----------------------------------------------------------------------------|
50 | Created 2000-12-11 | All changes are in the log above. | Updated 2006-05-26 |
51 \----------------------------------------------------------------------------*/
52
53 // tree config 對(duì)象,設(shè)定框架的基本信息。
54 var webFXTreeConfig = {
55 rootIcon : 'images/foldericon.png',
56 openRootIcon : 'images/openfoldericon.png',
57 folderIcon : 'images/foldericon.png',
58 openFolderIcon : 'images/openfoldericon.png',
59 fileIcon : 'images/file.png',
60 iIcon : 'images/I.png',
61 lIcon : 'images/L.png',
62 lMinusIcon : 'images/Lminus.png',
63 lPlusIcon : 'images/Lplus.png',
64 tIcon : 'images/T.png',
65 tMinusIcon : 'images/Tminus.png',
66 tPlusIcon : 'images/Tplus.png',
67 blankIcon : 'images/blank.png',
68 defaultText : 'Tree Item',
69 defaultAction : 'javascript:void(0);',
70 defaultBehavior : 'classic',
71 //默認(rèn)是否打開樹的根節(jié)點(diǎn)。
72 usePersistence : true
73 };
74
75 // tree handler 對(duì)象,設(shè)定常用的值和方法。
76 var webFXTreeHandler = {
77 idCounter : 0,
78 idPrefix : "webfx-tree-object-",
79 all : {},
80 behavior : null,
81 selected : null,
82 onSelect : null, /* should be part of tree, not handler */
83 getId : function() { return this.idPrefix + this.idCounter++; },
84 toggle : function (oItem) { this.all[oItem.id.replace('-plus','')].toggle(); },
85 select : function (oItem) { this.all[oItem.id.replace('-icon','')].select(); },
86 // 當(dāng)節(jié)點(diǎn)得到焦點(diǎn)時(shí),使id的元素執(zhí)行 focus(),節(jié)點(diǎn)方法。
87 focus : function (oItem) { this.all[oItem.id.replace('-anchor','')].focus(); },
88 blur : function (oItem) { this.all[oItem.id.replace('-anchor','')].blur(); },
89 keydown : function (oItem, e) { return this.all[oItem.id].keydown(e.keyCode); },
90 cookies : new WebFXCookie(),
91 insertHTMLBeforeEnd : function (oElement, sHTML) {
92 // 當(dāng)可以用 insertAdjacentHTML 方法時(shí)
93 if (oElement.insertAdjacentHTML != null) {
94 //sHTML插件 oElement 結(jié)束標(biāo)簽前
95 oElement.insertAdjacentHTML("BeforeEnd", sHTML)
96 return;
97 }
98 //當(dāng)不可以用 insertAdjacentHTML 方法時(shí)
99 var df; // DocumentFragment
100 var r = oElement.ownerDocument.createRange();
101 r.selectNodeContents(oElement);
102 r.collapse(false);
103 df = r.createContextualFragment(sHTML);
104 oElement.appendChild(df);
105 }
106 };
107
108
109 /*
110 * WebFXCookie class
111 * cookie 操作類
112 */
113 function WebFXCookie() {
114 if (document.cookie.length) { this.cookies = ' ' + document.cookie; }
115 }
116
117 //給 cookie 賦值,方法
118 WebFXCookie.prototype.setCookie = function (key, value) {
119 // 給 cookie 賦值方式,后面沒有指定失效日期,
120 // 則瀏覽器默認(rèn)是在關(guān)閉瀏覽器(也就是關(guān)閉所有窗口)之后過期。
121 //escape() 方法是把字符串按 URL 編碼方法來編碼的
122 document.cookie = key + "=" + escape(value);
123 }
124 /**
125 * @function 返回 cookie 中存儲(chǔ)著 key的值。
126 * 1.找到關(guān)鍵字在 cookies 中存儲(chǔ)的位置
127 * 2.判斷存儲(chǔ)位署 start 是否為-1,如果為-1表示不存在返回null
128 * 3.檢索start位置到';'的第一個(gè)位置,并賦予變量 end
129 * 4.如果 end =-1 ,表示不存在,則把 cookies 的長度賦予它
130 * 5.end -= start ,計(jì)算出關(guān)鍵字key 的長度.
131 * 6.使用 substr 方法 從起始索引號(hào)提取字符串中指定數(shù)目的字符。
132 * 7.提取'='后至字符串長度減去 '=' 的位置。
133 */
134 WebFXCookie.prototype.getCookie = function (key) {
135 if (this.cookies) {
136 var start = this.cookies.indexOf(' ' + key + '=');
137 if (start == -1) { return null; }
138 var end = this.cookies.indexOf(";", start);
139 if (end == -1) { end = this.cookies.length; }
140 end -= start;
141 var cookie = this.cookies.substr(start,end);
142 // unescqpe 方法,處理從cookie 接收過來的值,并按 URL編碼格式轉(zhuǎn)換成字符串。
143 return unescape(cookie.substr(cookie.indexOf('=') + 1, cookie.length - cookie.indexOf('=') + 1));
144 }
145 else { return null; }
146 }
147
148
149
150 /*
151 * WebFXTreeAbstractNode class
152 * tree 抽象節(jié)點(diǎn)類
153 */
154 function WebFXTreeAbstractNode(sText, sAction) {
155 this.childNodes = [];
156 this.id = webFXTreeHandler.getId();
157 this.text = sText || webFXTreeConfig.defaultText;
158 this.action = sAction || webFXTreeConfig.defaultAction;
159 this._last = false;
160 webFXTreeHandler.all[this.id] = this;
161 }
162
163 /*
164 * To speed thing up if you're adding multiple nodes at once (after load)
165 * use the bNoIdent parameter to prevent automatic re-indentation and call
166 * the obj.ident() method manually once all nodes has been added.
167 */
168 /**
169 * @function WebFXTreeAbstractNode 類的 add 方法 .
170 * @param node 節(jié)點(diǎn), bNoIdent
171 */
172 WebFXTreeAbstractNode.prototype.add = function (node, bNoIdent) {
173 //將this 對(duì)象賦予 node 節(jié)點(diǎn)的父節(jié)點(diǎn)。
174 node.parentNode = this;
175 //this 節(jié)點(diǎn)的添加一個(gè)節(jié)點(diǎn)是 node 節(jié)點(diǎn)。
176 this.childNodes[this.childNodes.length] = node;
177 // 變量 root 節(jié)點(diǎn),由 this 賦予
178 var root = this;
179 //為什么是2呢?因?yàn)?nbsp; 節(jié)點(diǎn)下標(biāo)從 0開始數(shù),并且要去掉它本身這個(gè)節(jié)點(diǎn)。
180 if (this.childNodes.length >= 2) {
181 this.childNodes[this.childNodes.length - 2]._last = false;
182 }
183 // root 變量成為根節(jié)點(diǎn)
184 while (root.parentNode) {
185 root = root.parentNode;
186 }
187 //如果樹已經(jīng)顯示
188 if (root.rendered) {
189 if (this.childNodes.length >= 2) {
190 //document.getElementById(this.childNodes[this.childNodes.length - 2].id + '-plus').src = ((this.childNodes[this.childNodes.length -2].folder)?((this.childNodes[this.childNodes.length -2].open)?webFXTreeConfig.tMinusIcon:webFXTreeConfig.tPlusIcon):webFXTreeConfig.tIcon);
191 //判斷應(yīng)該顯示的圖片。
192 if(this.childNodes[this.childNodes.length -2].folder){
193 var _icon ;
194 if(this.childNodes[this.childNodes.length -2].open){
195 _icon = webFXTreeConfig.tMinusIcon;
196 }else{
197 _icon = webFXTreeConfig.tPlusIcon;
198 }
199 document.getElementById(this.childNodes[this.childNodes.length - 2].id + '-plus').src = _icon;
200 }else{
201 document.getElementById(this.childNodes[this.childNodes.length - 2].id + '-plus').src = webFXTreeConfig.tIcon;
202 }
203
204 this.childNodes[this.childNodes.length - 2].plusIcon = webFXTreeConfig.tPlusIcon;
205 this.childNodes[this.childNodes.length - 2].minusIcon = webFXTreeConfig.tMinusIcon;
206 this.childNodes[this.childNodes.length - 2]._last = false;
207 }
208 this._last = true;
209 // 本節(jié)點(diǎn)賦予 foo對(duì)象
210 var foo = this;
211 while (foo.parentNode) {
212 for (var i = 0; i < foo.parentNode.childNodes.length; i++) {
213 if (foo.id == foo.parentNode.childNodes[i].id) {
214 break;
215 }
216 }
217 if (i == foo.parentNode.childNodes.length - 1) {
218 foo.parentNode._last = true;
219 }
220 else {
221 foo.parentNode._last = false;
222 }
223 foo = foo.parentNode;
224 }
225 webFXTreeHandler.insertHTMLBeforeEnd(document.getElementById(this.id + '-cont'), node.toString());
226
227 if ((!this.folder) && (!this.openIcon)) {
228 this.icon = webFXTreeConfig.folderIcon;
229 this.openIcon = webFXTreeConfig.openFolderIcon;
230 }
231 if (!this.folder) {
232 this.folder = true;
233 this.collapse(true);
234 }
235 if (!bNoIdent) {
236 this.indent();
237 }
238 }
239 return node;
240 }
241 /**
242 *@function 擴(kuò)展/折疊 方法
243 *1.判斷文件是否 折疊
244 *2.如果狀態(tài)是打開,執(zhí)行 collapse ()方法 ,將其折疊
245 *3.如果狀態(tài)是關(guān)閉,執(zhí)行 expand() 方法,將其打開
246 */
247 WebFXTreeAbstractNode.prototype.toggle = function() {
248 if (this.folder) {
249 if (this.open) {
250 this.collapse();
251 }
252 else {
253 this.expand();
254 }
255 }
256 }
257 /**
258 *@function 節(jié)點(diǎn)選擇方法
259 *當(dāng)選擇時(shí)此對(duì)象得到焦點(diǎn)
260 */
261 WebFXTreeAbstractNode.prototype.select = function() {
262 document.getElementById(this.id + '-anchor').focus();
263 }
264 /**
265 *@function 取消選擇方法
266 *1.節(jié)點(diǎn)的className 為空
267 *2.管理表示此節(jié)點(diǎn)未選擇
268 */
269 WebFXTreeAbstractNode.prototype.deSelect = function() {
270 document.getElementById(this.id + '-anchor').className = '';
271 webFXTreeHandler.selected = null;
272 }
273 /**
274 *@function 當(dāng)節(jié)點(diǎn)被選中時(shí),執(zhí)行此得到焦點(diǎn)方法
275 */
276 WebFXTreeAbstractNode.prototype.focus = function() {
277 // 如果選中為空,或者選中的不是本節(jié)點(diǎn),則執(zhí)行取消選擇方法。
278 if ((webFXTreeHandler.selected) && (webFXTreeHandler.selected != this)) {
279 webFXTreeHandler.selected.deSelect();
280 }
281 //選擇值賦予本節(jié)點(diǎn)。
282 webFXTreeHandler.selected = this;
283 //改變圖標(biāo)
284 if ((this.openIcon) && (webFXTreeHandler.behavior != 'classic')) {
285 document.getElementById(this.id + '-icon').src = this.openIcon;
286 }
287 document.getElementById(this.id + '-anchor').className = 'selected';
288 document.getElementById(this.id + '-anchor').focus();
289 // 如果 onSelect 存在,則執(zhí)行本節(jié)點(diǎn)的 onSelect 方法。猜想:一種擴(kuò)展方法嗎???
290 if (webFXTreeHandler.onSelect) {
291 webFXTreeHandler.onSelect(this);
292 }
293 }
294
295 WebFXTreeAbstractNode.prototype.blur = function() {
296 if ((this.openIcon) && (webFXTreeHandler.behavior != 'classic')) { document.getElementById(this.id + '-icon').src = this.icon; }
297 document.getElementById(this.id + '-anchor').className = 'selected-inactive';
298 }
299
300 /**
301 * @function 節(jié)點(diǎn)擴(kuò)張方法的據(jù)體實(shí)體。
302 */
303 WebFXTreeAbstractNode.prototype.doExpand = function() {
304 //改變節(jié)點(diǎn)的顯示圖標(biāo)。
305 if (webFXTreeHandler.behavior == 'classic') {
306 document.getElementById(this.id + '-icon').src = this.openIcon;
307 }
308 // 節(jié)點(diǎn)下的 div 層狀態(tài)為顯示
309 if (this.childNodes.length) {
310 document.getElementById(this.id + '-cont').style.display = 'block';
311 }
312 // 改變節(jié)點(diǎn)的狀態(tài)為:打開
313 this.open = true;
314 //設(shè)定 存儲(chǔ)在 cookie 上id為 '1'
315 if (webFXTreeConfig.usePersistence) {
316 webFXTreeHandler.cookies.setCookie(this.id.substr(18,this.id.length - 18), '1');
317 }
318 }
319
320 /**
321 *@function 節(jié)點(diǎn)收縮的據(jù)體操作
322 */
323 WebFXTreeAbstractNode.prototype.doCollapse = function() {
324 //改變節(jié)點(diǎn)的圖標(biāo)
325 if (webFXTreeHandler.behavior == 'classic') {
326 document.getElementById(this.id + '-icon').src = this.icon;
327 }
328 //改變 div 狀態(tài)為隱藏
329 if (this.childNodes.length) {
330 document.getElementById(this.id + '-cont').style.display = 'none';
331 }
332 //改變節(jié)點(diǎn)當(dāng)前狀態(tài)是:關(guān)閉
333 this.open = false;
334 //設(shè)定 存儲(chǔ)在 cookie 上的id為‘0’
335 if (webFXTreeConfig.usePersistence) {
336 webFXTreeHandler.cookies.setCookie(this.id.substr(18,this.id.length - 18), '0');
337 }
338 }
339 /**
340 *@function 打開所有的子節(jié)點(diǎn)
341 */
342 WebFXTreeAbstractNode.prototype.expandAll = function() {
343 this.expandChildren();
344 if ((this.folder) && (!this.open)) {
345 this.expand();
346 }
347 }
348 /**
349 *@function 打開本節(jié)點(diǎn)的子節(jié)點(diǎn)。跟 expandAll() 方法合用
350 */
351 WebFXTreeAbstractNode.prototype.expandChildren = function() {
352 for (var i = 0; i < this.childNodes.length; i++) {
353 this.childNodes[i].expandAll();
354 }
355 }
356 /**
357 *@function 關(guān)閉所有的節(jié)點(diǎn)。
358 */
359 WebFXTreeAbstractNode.prototype.collapseAll = function() {
360 this.collapseChildren();
361 if ((this.folder) && (this.open)) {
362 //有值,表示關(guān)閉時(shí),不獲得焦點(diǎn)
363 this.collapse(true);
364 }
365 }
366 /**
367 *@function 關(guān)閉本節(jié)點(diǎn)的子節(jié)點(diǎn),跟 collapseAll() 方法合用。
368 */
369 WebFXTreeAbstractNode.prototype.collapseChildren = function() {
370 for (var i = 0; i < this.childNodes.length; i++) {
371 this.childNodes[i].collapseAll();
372 }
373 }
374
375
376 WebFXTreeAbstractNode.prototype.indent = function(lvl, del, last, level, nodesLeft) {
377 /*
378 * Since we only want to modify items one level below ourself,
379 * and since the rightmost indentation position is occupied by
380 * the plus icon we set this to -2
381 */
382 if (lvl == null) {
383 lvl = -2;
384 }
385 var state = 0;
386 for (var i = this.childNodes.length - 1; i >= 0 ; i--) {
387 state = this.childNodes[i].indent(lvl + 1, del, last, level);
388 if (state) {
389 return;
390 }
391 }
392 if (del) {
393 if ((level >= this._level) && (document.getElementById(this.id + '-plus'))) {
394 if (this.folder) {
395 document.getElementById(this.id + '-plus').src = (this.open)?webFXTreeConfig.lMinusIcon:webFXTreeConfig.lPlusIcon;
396 this.plusIcon = webFXTreeConfig.lPlusIcon;
397 this.minusIcon = webFXTreeConfig.lMinusIcon;
398 }else if (nodesLeft) {
399 document.getElementById(this.id + '-plus').src = webFXTreeConfig.lIcon;
400 }
401 return 1;
402 }
403 }
404 var foo = document.getElementById(this.id + '-indent-' + lvl);
405 if (foo) {
406 if ((foo._last) || ((del) && (last))) {
407 foo.src = webFXTreeConfig.blankIcon;
408 } else {
409 foo.src = webFXTreeConfig.iIcon;
410 }
411 }
412 return 0;
413 }
414
415
416 /*
417 * WebFXTree class
418 */
419
420 function WebFXTree(sText, sAction, sBehavior, sIcon, sOpenIcon) {
421 //抽象節(jié)點(diǎn)操作,將 sText ,sAction 添加到樹節(jié)點(diǎn)中
422 this.base = WebFXTreeAbstractNode;
423 this.base(sText, sAction);
424
425 //OR 邏輯運(yùn)算符特性,如果 sIcon 字符串不為 空,那么在 Boolean 中表示 true,返回 sIcon,
426 //如果為空,則返回 webFXTreeConfig.rootIcon 字符串。
427 this.icon = sIcon || webFXTreeConfig.rootIcon;
428 this.openIcon = sOpenIcon || webFXTreeConfig.openRootIcon;
429
430 /* Defaults to open */
431 if (webFXTreeConfig.usePersistence) {
432 this.open = (webFXTreeHandler.cookies.getCookie(this.id.substr(18,this.id.length - 18)) == '0')?false:true;
433 } else {
434 this.open = true;
435 }
436 // 是否折疊
437 this.folder = true;
438 // 是否已經(jīng)瀏覽
439 this.rendered = false;
440 // 是否選擇
441 this.onSelect = null;
442 if (!webFXTreeHandler.behavior) {
443 webFXTreeHandler.behavior = sBehavior || webFXTreeConfig.defaultBehavior;
444 }
445 }
446 // 繼承: WebFXTree 繼承 WebFXTreeAbstractNode 所有的方法和屬性。
447 WebFXTree.prototype = new WebFXTreeAbstractNode;
448
449 WebFXTree.prototype.setBehavior = function (sBehavior) {
450 webFXTreeHandler.behavior = sBehavior;
451 };
452
453 WebFXTree.prototype.getBehavior = function (sBehavior) {
454 return webFXTreeHandler.behavior;
455 };
456
457 /**
458 *@function 樹得到焦點(diǎn)時(shí),返回得到焦點(diǎn)的節(jié)點(diǎn)信息
459 */
460 WebFXTree.prototype.getSelected = function() {
461 if (webFXTreeHandler.selected) {
462 return webFXTreeHandler.selected;
463 }
464 else {
465 return null;
466 }
467 }
468 /**
469 * @function 根節(jié)點(diǎn) remove 方法為空,不能刪除根節(jié)點(diǎn)吧。
470 */
471 WebFXTree.prototype.remove = function() { }
472
473 /**
474 * @function 擴(kuò)張方法,即打開根節(jié)點(diǎn)下的葉節(jié)點(diǎn)。
475 */
476 WebFXTree.prototype.expand = function() {
477 this.doExpand();
478 }
479 /**
480 *@function 收縮方法,即關(guān)閉根節(jié)點(diǎn)下的所有葉節(jié)點(diǎn)。
481 *對(duì)象得到焦點(diǎn)
482 */
483 WebFXTree.prototype.collapse = function(b) {
484 if (!b) {
485 this.focus();
486 }
487 this.doCollapse();
488 }
489 /*
490 WebFXTree.prototype.getFirst = function() {
491 return null;
492 }
493
494 WebFXTree.prototype.getLast = function() {
495 return null;
496 }
497
498 WebFXTree.prototype.getNextSibling = function() {
499 return null;
500 }
501
502 WebFXTree.prototype.getPreviousSibling = function() {
503 return null;
504 }
505 */
506 WebFXTree.prototype.keydown = function(key) {
507 if (key == 39) {
508 if (!this.open) { this.expand(); }
509 else if (this.childNodes.length) { this.childNodes[0].select(); }
510 return false;
511 }
512 if (key == 37) { this.collapse(); return false; }
513 if ((key == 40) && (this.open) && (this.childNodes.length)) { this.childNodes[0].select(); return false; }
514 return true;
515 }
516
517 WebFXTree.prototype.toString = function() {
518 var str = "<div id=\"" + this.id + "\" ondblclick=\"webFXTreeHandler.toggle(this);\" class=\"webfx-tree-item\" onkeydown=\"return webFXTreeHandler.keydown(this, event)\">" +
519 "<img id=\"" + this.id + "-icon\" class=\"webfx-tree-icon\" src=\"" + ((webFXTreeHandler.behavior == 'classic' && this.open)?this.openIcon:this.icon) + "\" onclick=\"webFXTreeHandler.select(this);\">" +
520 "<a href=\"" + this.action + "\" id=\"" + this.id + "-anchor\" onfocus=\"webFXTreeHandler.focus(this);\" onblur=\"webFXTreeHandler.blur(this);\"" +
521 (this.target ? " target=\"" + this.target + "\"" : "") +
522 ">" + this.text + "</a></div>" +
523 "<div id=\"" + this.id + "-cont\" class=\"webfx-tree-container\" style=\"display: " + ((this.open)?'block':'none') + ";\">";
524 var sb = [];
525 for (var i = 0; i < this.childNodes.length; i++) {
526 sb[i] = this.childNodes[i].toString(i, this.childNodes.length);
527 }
528 this.rendered = true;
529 // 返回字符串值,其中包含了連接到一起的數(shù)組的所有元素,元素由指定分隔符分隔開來.
530 return str + sb.join("") + "</div>";
531 };
532
533 /*
534 * WebFXTreeItem class
535 */
536
537 function WebFXTreeItem(sText, sAction, eParent, sIcon, sOpenIcon) {
538 this.base = WebFXTreeAbstractNode;
539 this.base(sText, sAction);
540 /* Defaults to close */
541 if (webFXTreeConfig.usePersistence) {
542 this.open = (webFXTreeHandler.cookies.getCookie(this.id.substr(18,this.id.length - 18)) == '1')?true:false;
543 } else {
544 this.open = false;
545 }
546 if (sIcon) {
547 this.icon = sIcon;
548 }
549 if (sOpenIcon) {
550 this.openIcon = sOpenIcon;
551 }
552 if (eParent) {
553 eParent.add(this);
554 }
555 }
556
557 // WebFXTreeItem 類繼承 WebFXTreeAbstractNode 抽象類所有的屬性和方法。
558 WebFXTreeItem.prototype = new WebFXTreeAbstractNode;
559
560 /**
561 *@function WebFXTreeItem 節(jié)點(diǎn)刪除方法
562 */
563 WebFXTreeItem.prototype.remove = function() {
564 var iconSrc = document.getElementById(this.id + '-plus').src;
565 // 父節(jié)點(diǎn)
566 var parentNode = this.parentNode;
567 // 上一個(gè)兄弟節(jié)點(diǎn)
568 var prevSibling = this.getPreviousSibling(true);
569 // 下一個(gè)兄弟節(jié)點(diǎn)
570 var nextSibling = this.getNextSibling(true);
571 // 父節(jié)點(diǎn)的折疊屬性
572 var folder = this.parentNode.folder;
573 // 判斷本節(jié)點(diǎn)是不是父節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)。
574 var last = ((nextSibling) && (nextSibling.parentNode) && (nextSibling.parentNode.id == parentNode.id))?false:true;
575 // 本節(jié)點(diǎn)的上一個(gè)兄弟節(jié)點(diǎn)得到焦點(diǎn)。
576 this.getPreviousSibling().focus();
577
578 this._remove();
579
580 if (parentNode.childNodes.length == 0) {
581 document.getElementById(parentNode.id + '-cont').style.display = 'none';
582 parentNode.doCollapse();
583 parentNode.folder = false;
584 parentNode.open = false;
585 }
586 if (!nextSibling || last) { parentNode.indent(null, true, last, this._level, parentNode.childNodes.length); }
587 if ((prevSibling == parentNode) && !(parentNode.childNodes.length)) {
588 prevSibling.folder = false;
589 prevSibling.open = false;
590 iconSrc = document.getElementById(prevSibling.id + '-plus').src;
591 iconSrc = iconSrc.replace('minus', '').replace('plus', '');
592 document.getElementById(prevSibling.id + '-plus').src = iconSrc;
593 document.getElementById(prevSibling.id + '-icon').src = webFXTreeConfig.fileIcon;
594 }
595 if (document.getElementById(prevSibling.id + '-plus')) {
596 if (parentNode == prevSibling.parentNode) {
597 iconSrc = iconSrc.replace('minus', '').replace('plus', '');
598 document.getElementById(prevSibling.id + '-plus').src = iconSrc;
599 } } }
600
601 /**
602 *@function 刪除方法的據(jù)體操作
603 */
604 WebFXTreeItem.prototype._remove = function() {
605 //遞歸刪除節(jié)點(diǎn)下的所有子節(jié)點(diǎn)
606 for (var i = this.childNodes.length - 1; i >= 0; i--) {
607 this.childNodes[i]._remove();
608 }
609 /*
610 * 循環(huán)查詢所有的子節(jié)點(diǎn),找到本節(jié)點(diǎn)在子節(jié)點(diǎn)的位置,將本節(jié)點(diǎn)后的節(jié)點(diǎn)分別向前占位,并使父節(jié)點(diǎn)的節(jié)點(diǎn)總量減一。
611 * 如果節(jié)點(diǎn)的位置 +1 等于父節(jié)點(diǎn)的子節(jié)點(diǎn)總數(shù),那么設(shè)置父節(jié)點(diǎn)的_last 為 true,即最后一個(gè)節(jié)點(diǎn)。
612 */
613 for (var i = 0; i < this.parentNode.childNodes.length; i++) {
614 if (this == this.parentNode.childNodes[i]) {
615 for (var j = i; j < this.parentNode.childNodes.length; j++) {
616 this.parentNode.childNodes[j] = this.parentNode.childNodes[j+1];
617 }
618 this.parentNode.childNodes.length -= 1;
619 if (i + 1 == this.parentNode.childNodes.length) {
620 this.parentNode._last = true;
621 }
622 break;
623 }
624 }
625 //本ID節(jié)點(diǎn)為NULL
626 webFXTreeHandler.all[this.id] = null;
627 //得到本節(jié)點(diǎn)元素,如果存在讓父元素刪除,
628 var tmp = document.getElementById(this.id);
629 if (tmp) {
630 tmp.parentNode.removeChild(tmp);
631 }
632 //得到本節(jié)點(diǎn)元素的 子空間,如果存在,刪除。
633 tmp = document.getElementById(this.id + '-cont');
634 if (tmp) {
635 tmp.parentNode.removeChild(tmp);
636 }
637 }
638
639 /**
640 *@function 擴(kuò)張方法
641 */
642 WebFXTreeItem.prototype.expand = function() {
643 this.doExpand();
644 document.getElementById(this.id + '-plus').src = this.minusIcon;
645 }
646 /**
647 *@function 收縮方法
648 */
649 WebFXTreeItem.prototype.collapse = function(b) {
650 if (!b) { this.focus(); }
651 this.doCollapse();
652 document.getElementById(this.id + '-plus').src = this.plusIcon;
653 }
654 /**
655 *@function 返回第一個(gè)節(jié)點(diǎn)
656 */
657 WebFXTreeItem.prototype.getFirst = function() {
658 return this.childNodes[0];
659 }
660
661 /**
662 *@function 返回上一個(gè)最后的子節(jié)點(diǎn)
663 */
664 WebFXTreeItem.prototype.getLast = function() {
665 //如果節(jié)點(diǎn)是打開,那么遞歸得到最后一個(gè)節(jié)點(diǎn);否則返回這個(gè)節(jié)點(diǎn)。
666 if (this.childNodes[this.childNodes.length - 1].open) {
667 return this.childNodes[this.childNodes.length - 1].getLast();
668 }else {
669 return this.childNodes[this.childNodes.length - 1];
670 }
671 }
672 /**
673 *@function 獲得本節(jié)點(diǎn)的下一個(gè)子節(jié)點(diǎn)。
674 */
675 WebFXTreeItem.prototype.getNextSibling = function() {
676 for (var i = 0; i < this.parentNode.childNodes.length; i++) {
677 if (this == this.parentNode.childNodes[i]) { break; }
678 }
679 if (++i == this.parentNode.childNodes.length) { return this.parentNode.getNextSibling(); }
680 else { return this.parentNode.childNodes[i]; }
681 }
682
683 /**
684 *@function 獲得本節(jié)點(diǎn)的前一個(gè)兄弟節(jié)點(diǎn)
685 */
686 WebFXTreeItem.prototype.getPreviousSibling = function(b) {
687 //判斷兄弟節(jié)點(diǎn)中,本節(jié)點(diǎn)所占的下標(biāo)。
688 for (var i = 0; i < this.parentNode.childNodes.length; i++) {
689 if (this == this.parentNode.childNodes[i]) {
690 break;
691 }
692 }
693 //如果下標(biāo) i 為零,即父節(jié)點(diǎn)只有一個(gè)子節(jié)點(diǎn),反回父節(jié)點(diǎn)。
694 if (i == 0) {
695 return this.parentNode;
696 } else {
697 if ((this.parentNode.childNodes[--i].open) || (b && this.parentNode.childNodes[i].folder)) {
698 return this.parentNode.childNodes[i].getLast();
699 } else {
700 return this.parentNode.childNodes[i];
701 }
702 }
703 }
704
705 WebFXTreeItem.prototype.keydown = function(key) {
706 if ((key == 39) && (this.folder)) {
707 if (!this.open) { this.expand(); }
708 else { this.getFirst().select(); }
709 return false;
710 }
711 else if (key == 37) {
712 if (this.open) { this.collapse(); }
713 else { this.parentNode.select(); }
714 return false;
715 }
716 else if (key == 40) {
717 if (this.open) { this.getFirst().select(); }
718 else {
719 var sib = this.getNextSibling();
720 if (sib) { sib.select(); }
721 }
722 return false;
723 }
724 else if (key == 38) { this.getPreviousSibling().select(); return false; }
725 return true;
726 }
727 /**
728 *@function 返回該對(duì)象的字符串表達(dá)
729 */
730 WebFXTreeItem.prototype.toString = function (nItem, nItemCount) {
731 var foo = this.parentNode;
732 var indent = '';
733 if (nItem + 1 == nItemCount) { this.parentNode._last = true; }
734 var i = 0;
735 while (foo.parentNode) {
736 foo = foo.parentNode;
737 indent = "<img id=\"" + this.id + "-indent-" + i + "\" src=\"" + ((foo._last)?webFXTreeConfig.blankIcon:webFXTreeConfig.iIcon) + "\">" + indent;
738 i++;
739 }
740 this._level = i;
741 if (this.childNodes.length) { this.folder = 1; }
742 else { this.open = false; }
743 if ((this.folder) || (webFXTreeHandler.behavior != 'classic')) {
744 if (!this.icon) { this.icon = webFXTreeConfig.folderIcon; }
745 if (!this.openIcon) { this.openIcon = webFXTreeConfig.openFolderIcon; }
746 }
747 else if (!this.icon) { this.icon = webFXTreeConfig.fileIcon; }
748 var label = this.text.replace(/</g, '<').replace(/>/g, '>');
749 var str = "<div id=\"" + this.id + "\" ondblclick=\"webFXTreeHandler.toggle(this);\" class=\"webfx-tree-item\" onkeydown=\"return webFXTreeHandler.keydown(this, event)\">" +
750 indent +
751 "<img id=\"" + this.id + "-plus\" src=\"" + ((this.folder)?((this.open)?((this.parentNode._last)?webFXTreeConfig.lMinusIcon:webFXTreeConfig.tMinusIcon):((this.parentNode._last)?webFXTreeConfig.lPlusIcon:webFXTreeConfig.tPlusIcon)):((this.parentNode._last)?webFXTreeConfig.lIcon:webFXTreeConfig.tIcon)) + "\" onclick=\"webFXTreeHandler.toggle(this);\">" +
752 "<img id=\"" + this.id + "-icon\" class=\"webfx-tree-icon\" src=\"" + ((webFXTreeHandler.behavior == 'classic' && this.open)?this.openIcon:this.icon) + "\" onclick=\"webFXTreeHandler.select(this);\">" +
753 "<a href=\"" + this.action + "\" id=\"" + this.id + "-anchor\" onfocus=\"webFXTreeHandler.focus(this);\" onblur=\"webFXTreeHandler.blur(this);\"" +
754 (this.target ? " target=\"" + this.target + "\"" : "") +
755 ">" + label + "</a></div>" +
756 "<div id=\"" + this.id + "-cont\" class=\"webfx-tree-container\" style=\"display: " + ((this.open)?'block':'none') + ";\">";
757 var sb = [];
758 for (var i = 0; i < this.childNodes.length; i++) {
759 sb[i] = this.childNodes[i].toString(i,this.childNodes.length);
760 }
761 this.plusIcon = ((this.parentNode._last)?webFXTreeConfig.lPlusIcon:webFXTreeConfig.tPlusIcon);
762 this.minusIcon = ((this.parentNode._last)?webFXTreeConfig.lMinusIcon:webFXTreeConfig.tMinusIcon);
763 return str + sb.join("") + "</div>";
764 }
765
766
767 面向?qū)ο蟮乃枷雭砭帉?nbsp;javascript 語言,運(yùn)用很多讓人容易忽略的js語言知識(shí),有著 DOM 方式,使整個(gè)框架看起來讓人驚嘆。
768 框架原來如此簡單!
769
下載框架
2 /*----------------------------------------------------------------------------\
3 | Cross Browser Tree Widget 1.17 |
4 |-----------------------------------------------------------------------------|
5 | Created by Emil A Eklund |
6 | (http://webfx.eae.net/contact.html#emil) |
7 | For WebFX (http://webfx.eae.net/) |
8 |-----------------------------------------------------------------------------|
9 | An object based tree widget, emulating the one found in microsoft windows, |
10 | with persistence using cookies. Works in IE 5+, Mozilla and konqueror 3. |
11 |-----------------------------------------------------------------------------|
12 | Copyright (c) 2000, 2001, 2002, 2003, 2006 Emil A Eklund |
13 |-----------------------------------------------------------------------------|
14 | Licensed under the Apache License, Version 2.0 (the "License"); you may not |
15 | use this file except in compliance with the License. You may obtain a copy |
16 | of the License at http://www.apache.org/licenses/LICENSE-2.0 |
17 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
18 | Unless required by applicable law or agreed to in writing, software |
19 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
20 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
21 | License for the specific language governing permissions and limitations |
22 | under the License. |
23 |-----------------------------------------------------------------------------|
24 | Dependencies: xtree.css (To set up the CSS of the tree classes) |
25 |-----------------------------------------------------------------------------|
26 | 2001-01-10 | Original Version Posted. |
27 | 2001-03-18 | Added getSelected and get/setBehavior that can make it behave |
28 | | more like windows explorer, check usage for more information. |
29 | 2001-09-23 | Version 1.1 - New features included keyboard navigation (ie) |
30 | | and the ability to add and remove nodes dynamically and some |
31 | | other small tweaks and fixes. |
32 | 2002-01-27 | Version 1.11 - Bug fixes and improved mozilla support. |
33 | 2002-06-11 | Version 1.12 - Fixed a bug that prevented the indentation line |
34 | | from updating correctly under some circumstances. This bug |
35 | | happened when removing the last item in a subtree and items in |
36 | | siblings to the remove subtree where not correctly updated. |
37 | 2002-06-13 | Fixed a few minor bugs cased by the 1.12 bug-fix. |
38 | 2002-08-20 | Added usePersistence flag to allow disable of cookies. |
39 | 2002-10-23 | (1.14) Fixed a plus icon issue |
40 | 2002-10-29 | (1.15) Last changes broke more than they fixed. This version |
41 | | is based on 1.13 and fixes the bugs 1.14 fixed withou breaking |
42 | | lots of other things. |
43 | 2003-02-15 | The selected node can now be made visible even when the tree |
44 | | control loses focus. It uses a new class declaration in the |
45 | | css file '.webfx-tree-item a.selected-inactive', by default it |
46 | | puts a light-gray rectangle around the selected node. |
47 | 2003-03-16 | Adding target support after lots of lobbying

48 | 2006-05-26 | Changed license to Apache Software License 2.0. |
49 |-----------------------------------------------------------------------------|
50 | Created 2000-12-11 | All changes are in the log above. | Updated 2006-05-26 |
51 \----------------------------------------------------------------------------*/
52
53 // tree config 對(duì)象,設(shè)定框架的基本信息。
54 var webFXTreeConfig = {
55 rootIcon : 'images/foldericon.png',
56 openRootIcon : 'images/openfoldericon.png',
57 folderIcon : 'images/foldericon.png',
58 openFolderIcon : 'images/openfoldericon.png',
59 fileIcon : 'images/file.png',
60 iIcon : 'images/I.png',
61 lIcon : 'images/L.png',
62 lMinusIcon : 'images/Lminus.png',
63 lPlusIcon : 'images/Lplus.png',
64 tIcon : 'images/T.png',
65 tMinusIcon : 'images/Tminus.png',
66 tPlusIcon : 'images/Tplus.png',
67 blankIcon : 'images/blank.png',
68 defaultText : 'Tree Item',
69 defaultAction : 'javascript:void(0);',
70 defaultBehavior : 'classic',
71 //默認(rèn)是否打開樹的根節(jié)點(diǎn)。
72 usePersistence : true
73 };
74
75 // tree handler 對(duì)象,設(shè)定常用的值和方法。
76 var webFXTreeHandler = {
77 idCounter : 0,
78 idPrefix : "webfx-tree-object-",
79 all : {},
80 behavior : null,
81 selected : null,
82 onSelect : null, /* should be part of tree, not handler */
83 getId : function() { return this.idPrefix + this.idCounter++; },
84 toggle : function (oItem) { this.all[oItem.id.replace('-plus','')].toggle(); },
85 select : function (oItem) { this.all[oItem.id.replace('-icon','')].select(); },
86 // 當(dāng)節(jié)點(diǎn)得到焦點(diǎn)時(shí),使id的元素執(zhí)行 focus(),節(jié)點(diǎn)方法。
87 focus : function (oItem) { this.all[oItem.id.replace('-anchor','')].focus(); },
88 blur : function (oItem) { this.all[oItem.id.replace('-anchor','')].blur(); },
89 keydown : function (oItem, e) { return this.all[oItem.id].keydown(e.keyCode); },
90 cookies : new WebFXCookie(),
91 insertHTMLBeforeEnd : function (oElement, sHTML) {
92 // 當(dāng)可以用 insertAdjacentHTML 方法時(shí)
93 if (oElement.insertAdjacentHTML != null) {
94 //sHTML插件 oElement 結(jié)束標(biāo)簽前
95 oElement.insertAdjacentHTML("BeforeEnd", sHTML)
96 return;
97 }
98 //當(dāng)不可以用 insertAdjacentHTML 方法時(shí)
99 var df; // DocumentFragment
100 var r = oElement.ownerDocument.createRange();
101 r.selectNodeContents(oElement);
102 r.collapse(false);
103 df = r.createContextualFragment(sHTML);
104 oElement.appendChild(df);
105 }
106 };
107
108
109 /*
110 * WebFXCookie class
111 * cookie 操作類
112 */
113 function WebFXCookie() {
114 if (document.cookie.length) { this.cookies = ' ' + document.cookie; }
115 }
116
117 //給 cookie 賦值,方法
118 WebFXCookie.prototype.setCookie = function (key, value) {
119 // 給 cookie 賦值方式,后面沒有指定失效日期,
120 // 則瀏覽器默認(rèn)是在關(guān)閉瀏覽器(也就是關(guān)閉所有窗口)之后過期。
121 //escape() 方法是把字符串按 URL 編碼方法來編碼的
122 document.cookie = key + "=" + escape(value);
123 }
124 /**
125 * @function 返回 cookie 中存儲(chǔ)著 key的值。
126 * 1.找到關(guān)鍵字在 cookies 中存儲(chǔ)的位置
127 * 2.判斷存儲(chǔ)位署 start 是否為-1,如果為-1表示不存在返回null
128 * 3.檢索start位置到';'的第一個(gè)位置,并賦予變量 end
129 * 4.如果 end =-1 ,表示不存在,則把 cookies 的長度賦予它
130 * 5.end -= start ,計(jì)算出關(guān)鍵字key 的長度.
131 * 6.使用 substr 方法 從起始索引號(hào)提取字符串中指定數(shù)目的字符。
132 * 7.提取'='后至字符串長度減去 '=' 的位置。
133 */
134 WebFXCookie.prototype.getCookie = function (key) {
135 if (this.cookies) {
136 var start = this.cookies.indexOf(' ' + key + '=');
137 if (start == -1) { return null; }
138 var end = this.cookies.indexOf(";", start);
139 if (end == -1) { end = this.cookies.length; }
140 end -= start;
141 var cookie = this.cookies.substr(start,end);
142 // unescqpe 方法,處理從cookie 接收過來的值,并按 URL編碼格式轉(zhuǎn)換成字符串。
143 return unescape(cookie.substr(cookie.indexOf('=') + 1, cookie.length - cookie.indexOf('=') + 1));
144 }
145 else { return null; }
146 }
147
148
149
150 /*
151 * WebFXTreeAbstractNode class
152 * tree 抽象節(jié)點(diǎn)類
153 */
154 function WebFXTreeAbstractNode(sText, sAction) {
155 this.childNodes = [];
156 this.id = webFXTreeHandler.getId();
157 this.text = sText || webFXTreeConfig.defaultText;
158 this.action = sAction || webFXTreeConfig.defaultAction;
159 this._last = false;
160 webFXTreeHandler.all[this.id] = this;
161 }
162
163 /*
164 * To speed thing up if you're adding multiple nodes at once (after load)
165 * use the bNoIdent parameter to prevent automatic re-indentation and call
166 * the obj.ident() method manually once all nodes has been added.
167 */
168 /**
169 * @function WebFXTreeAbstractNode 類的 add 方法 .
170 * @param node 節(jié)點(diǎn), bNoIdent
171 */
172 WebFXTreeAbstractNode.prototype.add = function (node, bNoIdent) {
173 //將this 對(duì)象賦予 node 節(jié)點(diǎn)的父節(jié)點(diǎn)。
174 node.parentNode = this;
175 //this 節(jié)點(diǎn)的添加一個(gè)節(jié)點(diǎn)是 node 節(jié)點(diǎn)。
176 this.childNodes[this.childNodes.length] = node;
177 // 變量 root 節(jié)點(diǎn),由 this 賦予
178 var root = this;
179 //為什么是2呢?因?yàn)?nbsp; 節(jié)點(diǎn)下標(biāo)從 0開始數(shù),并且要去掉它本身這個(gè)節(jié)點(diǎn)。
180 if (this.childNodes.length >= 2) {
181 this.childNodes[this.childNodes.length - 2]._last = false;
182 }
183 // root 變量成為根節(jié)點(diǎn)
184 while (root.parentNode) {
185 root = root.parentNode;
186 }
187 //如果樹已經(jīng)顯示
188 if (root.rendered) {
189 if (this.childNodes.length >= 2) {
190 //document.getElementById(this.childNodes[this.childNodes.length - 2].id + '-plus').src = ((this.childNodes[this.childNodes.length -2].folder)?((this.childNodes[this.childNodes.length -2].open)?webFXTreeConfig.tMinusIcon:webFXTreeConfig.tPlusIcon):webFXTreeConfig.tIcon);
191 //判斷應(yīng)該顯示的圖片。
192 if(this.childNodes[this.childNodes.length -2].folder){
193 var _icon ;
194 if(this.childNodes[this.childNodes.length -2].open){
195 _icon = webFXTreeConfig.tMinusIcon;
196 }else{
197 _icon = webFXTreeConfig.tPlusIcon;
198 }
199 document.getElementById(this.childNodes[this.childNodes.length - 2].id + '-plus').src = _icon;
200 }else{
201 document.getElementById(this.childNodes[this.childNodes.length - 2].id + '-plus').src = webFXTreeConfig.tIcon;
202 }
203
204 this.childNodes[this.childNodes.length - 2].plusIcon = webFXTreeConfig.tPlusIcon;
205 this.childNodes[this.childNodes.length - 2].minusIcon = webFXTreeConfig.tMinusIcon;
206 this.childNodes[this.childNodes.length - 2]._last = false;
207 }
208 this._last = true;
209 // 本節(jié)點(diǎn)賦予 foo對(duì)象
210 var foo = this;
211 while (foo.parentNode) {
212 for (var i = 0; i < foo.parentNode.childNodes.length; i++) {
213 if (foo.id == foo.parentNode.childNodes[i].id) {
214 break;
215 }
216 }
217 if (i == foo.parentNode.childNodes.length - 1) {
218 foo.parentNode._last = true;
219 }
220 else {
221 foo.parentNode._last = false;
222 }
223 foo = foo.parentNode;
224 }
225 webFXTreeHandler.insertHTMLBeforeEnd(document.getElementById(this.id + '-cont'), node.toString());
226
227 if ((!this.folder) && (!this.openIcon)) {
228 this.icon = webFXTreeConfig.folderIcon;
229 this.openIcon = webFXTreeConfig.openFolderIcon;
230 }
231 if (!this.folder) {
232 this.folder = true;
233 this.collapse(true);
234 }
235 if (!bNoIdent) {
236 this.indent();
237 }
238 }
239 return node;
240 }
241 /**
242 *@function 擴(kuò)展/折疊 方法
243 *1.判斷文件是否 折疊
244 *2.如果狀態(tài)是打開,執(zhí)行 collapse ()方法 ,將其折疊
245 *3.如果狀態(tài)是關(guān)閉,執(zhí)行 expand() 方法,將其打開
246 */
247 WebFXTreeAbstractNode.prototype.toggle = function() {
248 if (this.folder) {
249 if (this.open) {
250 this.collapse();
251 }
252 else {
253 this.expand();
254 }
255 }
256 }
257 /**
258 *@function 節(jié)點(diǎn)選擇方法
259 *當(dāng)選擇時(shí)此對(duì)象得到焦點(diǎn)
260 */
261 WebFXTreeAbstractNode.prototype.select = function() {
262 document.getElementById(this.id + '-anchor').focus();
263 }
264 /**
265 *@function 取消選擇方法
266 *1.節(jié)點(diǎn)的className 為空
267 *2.管理表示此節(jié)點(diǎn)未選擇
268 */
269 WebFXTreeAbstractNode.prototype.deSelect = function() {
270 document.getElementById(this.id + '-anchor').className = '';
271 webFXTreeHandler.selected = null;
272 }
273 /**
274 *@function 當(dāng)節(jié)點(diǎn)被選中時(shí),執(zhí)行此得到焦點(diǎn)方法
275 */
276 WebFXTreeAbstractNode.prototype.focus = function() {
277 // 如果選中為空,或者選中的不是本節(jié)點(diǎn),則執(zhí)行取消選擇方法。
278 if ((webFXTreeHandler.selected) && (webFXTreeHandler.selected != this)) {
279 webFXTreeHandler.selected.deSelect();
280 }
281 //選擇值賦予本節(jié)點(diǎn)。
282 webFXTreeHandler.selected = this;
283 //改變圖標(biāo)
284 if ((this.openIcon) && (webFXTreeHandler.behavior != 'classic')) {
285 document.getElementById(this.id + '-icon').src = this.openIcon;
286 }
287 document.getElementById(this.id + '-anchor').className = 'selected';
288 document.getElementById(this.id + '-anchor').focus();
289 // 如果 onSelect 存在,則執(zhí)行本節(jié)點(diǎn)的 onSelect 方法。猜想:一種擴(kuò)展方法嗎???
290 if (webFXTreeHandler.onSelect) {
291 webFXTreeHandler.onSelect(this);
292 }
293 }
294
295 WebFXTreeAbstractNode.prototype.blur = function() {
296 if ((this.openIcon) && (webFXTreeHandler.behavior != 'classic')) { document.getElementById(this.id + '-icon').src = this.icon; }
297 document.getElementById(this.id + '-anchor').className = 'selected-inactive';
298 }
299
300 /**
301 * @function 節(jié)點(diǎn)擴(kuò)張方法的據(jù)體實(shí)體。
302 */
303 WebFXTreeAbstractNode.prototype.doExpand = function() {
304 //改變節(jié)點(diǎn)的顯示圖標(biāo)。
305 if (webFXTreeHandler.behavior == 'classic') {
306 document.getElementById(this.id + '-icon').src = this.openIcon;
307 }
308 // 節(jié)點(diǎn)下的 div 層狀態(tài)為顯示
309 if (this.childNodes.length) {
310 document.getElementById(this.id + '-cont').style.display = 'block';
311 }
312 // 改變節(jié)點(diǎn)的狀態(tài)為:打開
313 this.open = true;
314 //設(shè)定 存儲(chǔ)在 cookie 上id為 '1'
315 if (webFXTreeConfig.usePersistence) {
316 webFXTreeHandler.cookies.setCookie(this.id.substr(18,this.id.length - 18), '1');
317 }
318 }
319
320 /**
321 *@function 節(jié)點(diǎn)收縮的據(jù)體操作
322 */
323 WebFXTreeAbstractNode.prototype.doCollapse = function() {
324 //改變節(jié)點(diǎn)的圖標(biāo)
325 if (webFXTreeHandler.behavior == 'classic') {
326 document.getElementById(this.id + '-icon').src = this.icon;
327 }
328 //改變 div 狀態(tài)為隱藏
329 if (this.childNodes.length) {
330 document.getElementById(this.id + '-cont').style.display = 'none';
331 }
332 //改變節(jié)點(diǎn)當(dāng)前狀態(tài)是:關(guān)閉
333 this.open = false;
334 //設(shè)定 存儲(chǔ)在 cookie 上的id為‘0’
335 if (webFXTreeConfig.usePersistence) {
336 webFXTreeHandler.cookies.setCookie(this.id.substr(18,this.id.length - 18), '0');
337 }
338 }
339 /**
340 *@function 打開所有的子節(jié)點(diǎn)
341 */
342 WebFXTreeAbstractNode.prototype.expandAll = function() {
343 this.expandChildren();
344 if ((this.folder) && (!this.open)) {
345 this.expand();
346 }
347 }
348 /**
349 *@function 打開本節(jié)點(diǎn)的子節(jié)點(diǎn)。跟 expandAll() 方法合用
350 */
351 WebFXTreeAbstractNode.prototype.expandChildren = function() {
352 for (var i = 0; i < this.childNodes.length; i++) {
353 this.childNodes[i].expandAll();
354 }
355 }
356 /**
357 *@function 關(guān)閉所有的節(jié)點(diǎn)。
358 */
359 WebFXTreeAbstractNode.prototype.collapseAll = function() {
360 this.collapseChildren();
361 if ((this.folder) && (this.open)) {
362 //有值,表示關(guān)閉時(shí),不獲得焦點(diǎn)
363 this.collapse(true);
364 }
365 }
366 /**
367 *@function 關(guān)閉本節(jié)點(diǎn)的子節(jié)點(diǎn),跟 collapseAll() 方法合用。
368 */
369 WebFXTreeAbstractNode.prototype.collapseChildren = function() {
370 for (var i = 0; i < this.childNodes.length; i++) {
371 this.childNodes[i].collapseAll();
372 }
373 }
374
375
376 WebFXTreeAbstractNode.prototype.indent = function(lvl, del, last, level, nodesLeft) {
377 /*
378 * Since we only want to modify items one level below ourself,
379 * and since the rightmost indentation position is occupied by
380 * the plus icon we set this to -2
381 */
382 if (lvl == null) {
383 lvl = -2;
384 }
385 var state = 0;
386 for (var i = this.childNodes.length - 1; i >= 0 ; i--) {
387 state = this.childNodes[i].indent(lvl + 1, del, last, level);
388 if (state) {
389 return;
390 }
391 }
392 if (del) {
393 if ((level >= this._level) && (document.getElementById(this.id + '-plus'))) {
394 if (this.folder) {
395 document.getElementById(this.id + '-plus').src = (this.open)?webFXTreeConfig.lMinusIcon:webFXTreeConfig.lPlusIcon;
396 this.plusIcon = webFXTreeConfig.lPlusIcon;
397 this.minusIcon = webFXTreeConfig.lMinusIcon;
398 }else if (nodesLeft) {
399 document.getElementById(this.id + '-plus').src = webFXTreeConfig.lIcon;
400 }
401 return 1;
402 }
403 }
404 var foo = document.getElementById(this.id + '-indent-' + lvl);
405 if (foo) {
406 if ((foo._last) || ((del) && (last))) {
407 foo.src = webFXTreeConfig.blankIcon;
408 } else {
409 foo.src = webFXTreeConfig.iIcon;
410 }
411 }
412 return 0;
413 }
414
415
416 /*
417 * WebFXTree class
418 */
419
420 function WebFXTree(sText, sAction, sBehavior, sIcon, sOpenIcon) {
421 //抽象節(jié)點(diǎn)操作,將 sText ,sAction 添加到樹節(jié)點(diǎn)中
422 this.base = WebFXTreeAbstractNode;
423 this.base(sText, sAction);
424
425 //OR 邏輯運(yùn)算符特性,如果 sIcon 字符串不為 空,那么在 Boolean 中表示 true,返回 sIcon,
426 //如果為空,則返回 webFXTreeConfig.rootIcon 字符串。
427 this.icon = sIcon || webFXTreeConfig.rootIcon;
428 this.openIcon = sOpenIcon || webFXTreeConfig.openRootIcon;
429
430 /* Defaults to open */
431 if (webFXTreeConfig.usePersistence) {
432 this.open = (webFXTreeHandler.cookies.getCookie(this.id.substr(18,this.id.length - 18)) == '0')?false:true;
433 } else {
434 this.open = true;
435 }
436 // 是否折疊
437 this.folder = true;
438 // 是否已經(jīng)瀏覽
439 this.rendered = false;
440 // 是否選擇
441 this.onSelect = null;
442 if (!webFXTreeHandler.behavior) {
443 webFXTreeHandler.behavior = sBehavior || webFXTreeConfig.defaultBehavior;
444 }
445 }
446 // 繼承: WebFXTree 繼承 WebFXTreeAbstractNode 所有的方法和屬性。
447 WebFXTree.prototype = new WebFXTreeAbstractNode;
448
449 WebFXTree.prototype.setBehavior = function (sBehavior) {
450 webFXTreeHandler.behavior = sBehavior;
451 };
452
453 WebFXTree.prototype.getBehavior = function (sBehavior) {
454 return webFXTreeHandler.behavior;
455 };
456
457 /**
458 *@function 樹得到焦點(diǎn)時(shí),返回得到焦點(diǎn)的節(jié)點(diǎn)信息
459 */
460 WebFXTree.prototype.getSelected = function() {
461 if (webFXTreeHandler.selected) {
462 return webFXTreeHandler.selected;
463 }
464 else {
465 return null;
466 }
467 }
468 /**
469 * @function 根節(jié)點(diǎn) remove 方法為空,不能刪除根節(jié)點(diǎn)吧。
470 */
471 WebFXTree.prototype.remove = function() { }
472
473 /**
474 * @function 擴(kuò)張方法,即打開根節(jié)點(diǎn)下的葉節(jié)點(diǎn)。
475 */
476 WebFXTree.prototype.expand = function() {
477 this.doExpand();
478 }
479 /**
480 *@function 收縮方法,即關(guān)閉根節(jié)點(diǎn)下的所有葉節(jié)點(diǎn)。
481 *對(duì)象得到焦點(diǎn)
482 */
483 WebFXTree.prototype.collapse = function(b) {
484 if (!b) {
485 this.focus();
486 }
487 this.doCollapse();
488 }
489 /*
490 WebFXTree.prototype.getFirst = function() {
491 return null;
492 }
493
494 WebFXTree.prototype.getLast = function() {
495 return null;
496 }
497
498 WebFXTree.prototype.getNextSibling = function() {
499 return null;
500 }
501
502 WebFXTree.prototype.getPreviousSibling = function() {
503 return null;
504 }
505 */
506 WebFXTree.prototype.keydown = function(key) {
507 if (key == 39) {
508 if (!this.open) { this.expand(); }
509 else if (this.childNodes.length) { this.childNodes[0].select(); }
510 return false;
511 }
512 if (key == 37) { this.collapse(); return false; }
513 if ((key == 40) && (this.open) && (this.childNodes.length)) { this.childNodes[0].select(); return false; }
514 return true;
515 }
516
517 WebFXTree.prototype.toString = function() {
518 var str = "<div id=\"" + this.id + "\" ondblclick=\"webFXTreeHandler.toggle(this);\" class=\"webfx-tree-item\" onkeydown=\"return webFXTreeHandler.keydown(this, event)\">" +
519 "<img id=\"" + this.id + "-icon\" class=\"webfx-tree-icon\" src=\"" + ((webFXTreeHandler.behavior == 'classic' && this.open)?this.openIcon:this.icon) + "\" onclick=\"webFXTreeHandler.select(this);\">" +
520 "<a href=\"" + this.action + "\" id=\"" + this.id + "-anchor\" onfocus=\"webFXTreeHandler.focus(this);\" onblur=\"webFXTreeHandler.blur(this);\"" +
521 (this.target ? " target=\"" + this.target + "\"" : "") +
522 ">" + this.text + "</a></div>" +
523 "<div id=\"" + this.id + "-cont\" class=\"webfx-tree-container\" style=\"display: " + ((this.open)?'block':'none') + ";\">";
524 var sb = [];
525 for (var i = 0; i < this.childNodes.length; i++) {
526 sb[i] = this.childNodes[i].toString(i, this.childNodes.length);
527 }
528 this.rendered = true;
529 // 返回字符串值,其中包含了連接到一起的數(shù)組的所有元素,元素由指定分隔符分隔開來.
530 return str + sb.join("") + "</div>";
531 };
532
533 /*
534 * WebFXTreeItem class
535 */
536
537 function WebFXTreeItem(sText, sAction, eParent, sIcon, sOpenIcon) {
538 this.base = WebFXTreeAbstractNode;
539 this.base(sText, sAction);
540 /* Defaults to close */
541 if (webFXTreeConfig.usePersistence) {
542 this.open = (webFXTreeHandler.cookies.getCookie(this.id.substr(18,this.id.length - 18)) == '1')?true:false;
543 } else {
544 this.open = false;
545 }
546 if (sIcon) {
547 this.icon = sIcon;
548 }
549 if (sOpenIcon) {
550 this.openIcon = sOpenIcon;
551 }
552 if (eParent) {
553 eParent.add(this);
554 }
555 }
556
557 // WebFXTreeItem 類繼承 WebFXTreeAbstractNode 抽象類所有的屬性和方法。
558 WebFXTreeItem.prototype = new WebFXTreeAbstractNode;
559
560 /**
561 *@function WebFXTreeItem 節(jié)點(diǎn)刪除方法
562 */
563 WebFXTreeItem.prototype.remove = function() {
564 var iconSrc = document.getElementById(this.id + '-plus').src;
565 // 父節(jié)點(diǎn)
566 var parentNode = this.parentNode;
567 // 上一個(gè)兄弟節(jié)點(diǎn)
568 var prevSibling = this.getPreviousSibling(true);
569 // 下一個(gè)兄弟節(jié)點(diǎn)
570 var nextSibling = this.getNextSibling(true);
571 // 父節(jié)點(diǎn)的折疊屬性
572 var folder = this.parentNode.folder;
573 // 判斷本節(jié)點(diǎn)是不是父節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)。
574 var last = ((nextSibling) && (nextSibling.parentNode) && (nextSibling.parentNode.id == parentNode.id))?false:true;
575 // 本節(jié)點(diǎn)的上一個(gè)兄弟節(jié)點(diǎn)得到焦點(diǎn)。
576 this.getPreviousSibling().focus();
577
578 this._remove();
579
580 if (parentNode.childNodes.length == 0) {
581 document.getElementById(parentNode.id + '-cont').style.display = 'none';
582 parentNode.doCollapse();
583 parentNode.folder = false;
584 parentNode.open = false;
585 }
586 if (!nextSibling || last) { parentNode.indent(null, true, last, this._level, parentNode.childNodes.length); }
587 if ((prevSibling == parentNode) && !(parentNode.childNodes.length)) {
588 prevSibling.folder = false;
589 prevSibling.open = false;
590 iconSrc = document.getElementById(prevSibling.id + '-plus').src;
591 iconSrc = iconSrc.replace('minus', '').replace('plus', '');
592 document.getElementById(prevSibling.id + '-plus').src = iconSrc;
593 document.getElementById(prevSibling.id + '-icon').src = webFXTreeConfig.fileIcon;
594 }
595 if (document.getElementById(prevSibling.id + '-plus')) {
596 if (parentNode == prevSibling.parentNode) {
597 iconSrc = iconSrc.replace('minus', '').replace('plus', '');
598 document.getElementById(prevSibling.id + '-plus').src = iconSrc;
599 } } }
600
601 /**
602 *@function 刪除方法的據(jù)體操作
603 */
604 WebFXTreeItem.prototype._remove = function() {
605 //遞歸刪除節(jié)點(diǎn)下的所有子節(jié)點(diǎn)
606 for (var i = this.childNodes.length - 1; i >= 0; i--) {
607 this.childNodes[i]._remove();
608 }
609 /*
610 * 循環(huán)查詢所有的子節(jié)點(diǎn),找到本節(jié)點(diǎn)在子節(jié)點(diǎn)的位置,將本節(jié)點(diǎn)后的節(jié)點(diǎn)分別向前占位,并使父節(jié)點(diǎn)的節(jié)點(diǎn)總量減一。
611 * 如果節(jié)點(diǎn)的位置 +1 等于父節(jié)點(diǎn)的子節(jié)點(diǎn)總數(shù),那么設(shè)置父節(jié)點(diǎn)的_last 為 true,即最后一個(gè)節(jié)點(diǎn)。
612 */
613 for (var i = 0; i < this.parentNode.childNodes.length; i++) {
614 if (this == this.parentNode.childNodes[i]) {
615 for (var j = i; j < this.parentNode.childNodes.length; j++) {
616 this.parentNode.childNodes[j] = this.parentNode.childNodes[j+1];
617 }
618 this.parentNode.childNodes.length -= 1;
619 if (i + 1 == this.parentNode.childNodes.length) {
620 this.parentNode._last = true;
621 }
622 break;
623 }
624 }
625 //本ID節(jié)點(diǎn)為NULL
626 webFXTreeHandler.all[this.id] = null;
627 //得到本節(jié)點(diǎn)元素,如果存在讓父元素刪除,
628 var tmp = document.getElementById(this.id);
629 if (tmp) {
630 tmp.parentNode.removeChild(tmp);
631 }
632 //得到本節(jié)點(diǎn)元素的 子空間,如果存在,刪除。
633 tmp = document.getElementById(this.id + '-cont');
634 if (tmp) {
635 tmp.parentNode.removeChild(tmp);
636 }
637 }
638
639 /**
640 *@function 擴(kuò)張方法
641 */
642 WebFXTreeItem.prototype.expand = function() {
643 this.doExpand();
644 document.getElementById(this.id + '-plus').src = this.minusIcon;
645 }
646 /**
647 *@function 收縮方法
648 */
649 WebFXTreeItem.prototype.collapse = function(b) {
650 if (!b) { this.focus(); }
651 this.doCollapse();
652 document.getElementById(this.id + '-plus').src = this.plusIcon;
653 }
654 /**
655 *@function 返回第一個(gè)節(jié)點(diǎn)
656 */
657 WebFXTreeItem.prototype.getFirst = function() {
658 return this.childNodes[0];
659 }
660
661 /**
662 *@function 返回上一個(gè)最后的子節(jié)點(diǎn)
663 */
664 WebFXTreeItem.prototype.getLast = function() {
665 //如果節(jié)點(diǎn)是打開,那么遞歸得到最后一個(gè)節(jié)點(diǎn);否則返回這個(gè)節(jié)點(diǎn)。
666 if (this.childNodes[this.childNodes.length - 1].open) {
667 return this.childNodes[this.childNodes.length - 1].getLast();
668 }else {
669 return this.childNodes[this.childNodes.length - 1];
670 }
671 }
672 /**
673 *@function 獲得本節(jié)點(diǎn)的下一個(gè)子節(jié)點(diǎn)。
674 */
675 WebFXTreeItem.prototype.getNextSibling = function() {
676 for (var i = 0; i < this.parentNode.childNodes.length; i++) {
677 if (this == this.parentNode.childNodes[i]) { break; }
678 }
679 if (++i == this.parentNode.childNodes.length) { return this.parentNode.getNextSibling(); }
680 else { return this.parentNode.childNodes[i]; }
681 }
682
683 /**
684 *@function 獲得本節(jié)點(diǎn)的前一個(gè)兄弟節(jié)點(diǎn)
685 */
686 WebFXTreeItem.prototype.getPreviousSibling = function(b) {
687 //判斷兄弟節(jié)點(diǎn)中,本節(jié)點(diǎn)所占的下標(biāo)。
688 for (var i = 0; i < this.parentNode.childNodes.length; i++) {
689 if (this == this.parentNode.childNodes[i]) {
690 break;
691 }
692 }
693 //如果下標(biāo) i 為零,即父節(jié)點(diǎn)只有一個(gè)子節(jié)點(diǎn),反回父節(jié)點(diǎn)。
694 if (i == 0) {
695 return this.parentNode;
696 } else {
697 if ((this.parentNode.childNodes[--i].open) || (b && this.parentNode.childNodes[i].folder)) {
698 return this.parentNode.childNodes[i].getLast();
699 } else {
700 return this.parentNode.childNodes[i];
701 }
702 }
703 }
704
705 WebFXTreeItem.prototype.keydown = function(key) {
706 if ((key == 39) && (this.folder)) {
707 if (!this.open) { this.expand(); }
708 else { this.getFirst().select(); }
709 return false;
710 }
711 else if (key == 37) {
712 if (this.open) { this.collapse(); }
713 else { this.parentNode.select(); }
714 return false;
715 }
716 else if (key == 40) {
717 if (this.open) { this.getFirst().select(); }
718 else {
719 var sib = this.getNextSibling();
720 if (sib) { sib.select(); }
721 }
722 return false;
723 }
724 else if (key == 38) { this.getPreviousSibling().select(); return false; }
725 return true;
726 }
727 /**
728 *@function 返回該對(duì)象的字符串表達(dá)
729 */
730 WebFXTreeItem.prototype.toString = function (nItem, nItemCount) {
731 var foo = this.parentNode;
732 var indent = '';
733 if (nItem + 1 == nItemCount) { this.parentNode._last = true; }
734 var i = 0;
735 while (foo.parentNode) {
736 foo = foo.parentNode;
737 indent = "<img id=\"" + this.id + "-indent-" + i + "\" src=\"" + ((foo._last)?webFXTreeConfig.blankIcon:webFXTreeConfig.iIcon) + "\">" + indent;
738 i++;
739 }
740 this._level = i;
741 if (this.childNodes.length) { this.folder = 1; }
742 else { this.open = false; }
743 if ((this.folder) || (webFXTreeHandler.behavior != 'classic')) {
744 if (!this.icon) { this.icon = webFXTreeConfig.folderIcon; }
745 if (!this.openIcon) { this.openIcon = webFXTreeConfig.openFolderIcon; }
746 }
747 else if (!this.icon) { this.icon = webFXTreeConfig.fileIcon; }
748 var label = this.text.replace(/</g, '<').replace(/>/g, '>');
749 var str = "<div id=\"" + this.id + "\" ondblclick=\"webFXTreeHandler.toggle(this);\" class=\"webfx-tree-item\" onkeydown=\"return webFXTreeHandler.keydown(this, event)\">" +
750 indent +
751 "<img id=\"" + this.id + "-plus\" src=\"" + ((this.folder)?((this.open)?((this.parentNode._last)?webFXTreeConfig.lMinusIcon:webFXTreeConfig.tMinusIcon):((this.parentNode._last)?webFXTreeConfig.lPlusIcon:webFXTreeConfig.tPlusIcon)):((this.parentNode._last)?webFXTreeConfig.lIcon:webFXTreeConfig.tIcon)) + "\" onclick=\"webFXTreeHandler.toggle(this);\">" +
752 "<img id=\"" + this.id + "-icon\" class=\"webfx-tree-icon\" src=\"" + ((webFXTreeHandler.behavior == 'classic' && this.open)?this.openIcon:this.icon) + "\" onclick=\"webFXTreeHandler.select(this);\">" +
753 "<a href=\"" + this.action + "\" id=\"" + this.id + "-anchor\" onfocus=\"webFXTreeHandler.focus(this);\" onblur=\"webFXTreeHandler.blur(this);\"" +
754 (this.target ? " target=\"" + this.target + "\"" : "") +
755 ">" + label + "</a></div>" +
756 "<div id=\"" + this.id + "-cont\" class=\"webfx-tree-container\" style=\"display: " + ((this.open)?'block':'none') + ";\">";
757 var sb = [];
758 for (var i = 0; i < this.childNodes.length; i++) {
759 sb[i] = this.childNodes[i].toString(i,this.childNodes.length);
760 }
761 this.plusIcon = ((this.parentNode._last)?webFXTreeConfig.lPlusIcon:webFXTreeConfig.tPlusIcon);
762 this.minusIcon = ((this.parentNode._last)?webFXTreeConfig.lMinusIcon:webFXTreeConfig.tMinusIcon);
763 return str + sb.join("") + "</div>";
764 }
765
766
767 面向?qū)ο蟮乃枷雭砭帉?nbsp;javascript 語言,運(yùn)用很多讓人容易忽略的js語言知識(shí),有著 DOM 方式,使整個(gè)框架看起來讓人驚嘆。
768 框架原來如此簡單!
769
下載框架
posted on 2009-07-09 09:44 星期五 閱讀(1219) 評(píng)論(0) 編輯 收藏 所屬分類: WEB FX