]> code.citadel.org Git - citadel.git/blob - webcit/static/nanotree.js
* Began replacing the old, sux0r-prone tree view with NanoTree.
[citadel.git] / webcit / static / nanotree.js
1 /**
2 * Original Author of this file: Martin Mouritzen. (martin@nano.dk)
3 *
4 *
5 * (Lack of) Documentation:
6 *
7 *
8 * If a finishedLoading method exists, it will be called when the tree is loaded.
9 * (good to display a div, etc.).
10 *
11 *
12 * You have to set the variable rootNode (as a TreeNode).
13 *
14 * You have to set a container element, this is the element in which the tree will be.
15 *
16 *
17 * TODO: 
18 * Save cookies better (only 1 cookie for each tree). Else the page will totally cookieclutter.
19 *
20 ***********************************************************************
21 * Configuration variables.
22 ************************************************************************/
23
24 // Should the rootNode be displayed.
25 var showRootNode = true;
26
27 // Should the dashed lines between nodes be shown.
28 var showLines = true;
29
30 // Should the nodes be sorted? (You can either specify a number, then it will be sorted by that, else it will
31 // be sorted alphabetically (by name).
32 var sortNodes = true;
33
34 // This is IMPORTANT... use an unique id for each document you use the tree in. (else they'll get mixed up).
35 var documentID = window.location.href;
36
37 // being read from cookie.
38 var nodesOpen = new Array();
39
40 // RootNode of the tree.
41 var rootNode;
42
43 // Container to display the Tree in.
44 var container;
45
46 // Shows/Hides subnodes on startup
47 var showAllNodesOnStartup = false;
48
49 // Is the roots dragable?
50 var dragable = false;
51
52
53 /************************************************************************
54 * The following is just instancevariables.
55 ************************************************************************/
56 var href = '';
57
58 // rootNodeCallBack name (if null, it's not selectable).
59 var rootNodeCallBack = null;
60
61 // selectedNode
62 var selectedNode = null;
63
64 var states = '';
65 var statearray = new Array();
66
67 var treeNodeEdited = null;
68
69 var editaborted = false;
70
71 var floatDragElement = null;
72 var colouredElement = null;
73 var draggedNodeID = null;
74 var lastDraggedOnNodeID = null;
75
76
77 /**
78 * The TreeNode Object
79 * @param id unique id of this treenode
80 * @param name The title of this node
81 * @param icon The icon if this node (Can also be an array with 2 elements, the first one will represent the closed state, and the next one the open state)
82 * @param param A parameter, this can be pretty much anything. (eg. an array with information).
83 * @param orderNumber an orderNumber If one is given the nodes will be sorted by this (else they'll be sorted alphabetically (If sorting is on).
84 */
85 function TreeNode(id,name,icon,param,orderNumber) {
86         this.id = id;
87         this.childs = new Array();
88         this.name = (name == null ? 'unset name' : name);
89         this.icon = (icon == null ? '' : icon);
90         this.parent = null;
91         this.handler = null;
92         this.param = (param == null ? '' : param);
93         this.orderNumber = (orderNumber == null ? -1 : orderNumber);
94         
95         this.openeventlisteners = new Array();
96         this.editeventlisteners = new Array();
97         this.moveeventlisteners = new Array();
98         this.haschilds = false;
99         this.editable = false;
100         this.linestring = '';
101         
102         this.nextSibling = null;
103         this.prevSibling = null;
104         
105         this.childsHasBeenFetched = false;
106
107         this.getID = function() {
108                 return this.id;
109         }
110         this.setName = function(newname) {
111                 this.name = newname;
112         }
113         this.getName = function() {
114                 return this.name;
115         }
116         this.getParam = function() {
117                 return this.param;
118         }
119         this.setIcon = function(icon) {
120                 this.icon = icon;
121         }
122         this.getIcon = function() {
123                 if (typeof(this.icon) == 'object') {
124                         return this.icon[0];
125                 }
126                 return this.icon;
127         }
128         this.getOpenIcon = function() {
129                 if (typeof(this.icon) == 'object') {
130                         return this.icon[1];
131                 }
132                 return this.icon;
133         }
134         this.hasIcon = function () {
135                 return this.icon != '';
136         }
137         this.getOrderNumber = function() {
138                 return this.orderNumber;
139         }
140         this.addOpenEventListener = function(event) {
141                 this.openeventlisteners[this.openeventlisteners.length] = event;
142         }
143         this.gotOpenEventListeners = function() {
144                 return (this.openeventlisteners.length > 0);
145         }
146         this.addEditEventListener = function(event) {
147                 this.editeventlisteners[this.editeventlisteners.length] = event;
148         }
149         this.gotEditEventListeners = function() {
150                 return (this.editeventlisteners.length > 0);
151         }
152         this.addMoveEventListener = function(event) {
153                 this.moveeventlisteners[this.moveeventlisteners.length] = event;
154         }
155         this.gotMoveEventListeners = function() {
156                 return (this.moveeventlisteners.length > 0);
157         }
158         this.addChild = function(childNode) {
159                 var possiblePrevNode = this.childs[this.childs.length - 1]
160                 if (possiblePrevNode) {
161                         possiblePrevNode.nextSibling = childNode;
162                         childNode.prevSibling = possiblePrevNode;
163                         // alert(childNode.prevSibling);
164                 }
165
166                 this.childs[this.childs.length] = childNode;
167                 childNode.setParent(this);
168
169                 if (sortNodes) {
170                         function sortByOrder(a,b) {
171                                 var order1 = a.getOrderNumber();
172                                 var order2 = b.getOrderNumber();
173                                 if (order1 == -1 || order2 == -1) {
174                                         return a.getName().toLowerCase() > b.getName().toLowerCase() ? 1 : -1;
175                                 }
176                                 else {
177                                         if (order1 == order2) {
178                                                 // If they got the same order number, then we'll sort by their title.
179                                                 return a.getName().toLowerCase() > b.getName().toLowerCase() ? 1 : -1;
180                                         }
181                                         else {
182                                                 return order1 - order2;
183                                         }
184                                 }
185                         }
186                         this.childs.sort(sortByOrder);
187                 }
188         }
189         this.removeChild = function(childNode) {
190                 var found = false;
191                 for (var i=0;i<this.childs.length;i++) {
192                         if (found) {
193                                 this.childs[i] = this.childs[i + 1];
194                         }
195                         if (this.childs[i] == childNode) {
196                                 if (i == (this.childs.length - 1)) {
197                                         this.childs[i] = null;
198                                 }
199                                 else {
200                                         this.childs[i] = this.childs[i + 1];
201                                 }
202                                 found = true;
203                         }
204                 }
205                 if (found) {
206                         this.childs.length = this.childs.length-1;
207                 }
208         }
209         this.resetChilds = function() {
210                 this.childs = new Array();
211         }
212         this.setHasChilds = function(hasChilds) {
213                 this.haschilds = hasChilds;
214         }
215         this.hasChilds = function() {
216                 if (this.haschilds == true) {
217                         return true;
218                 }
219                 return (this.childs.length > 0);
220         }
221         this.getChildCount = function() {
222                 return this.childs.length;
223         }
224         this.getFirstChild = function() {
225                 if (this.hasChilds()) {
226                         return this.childs[0];
227                 }
228                 return null;
229         }
230         this.gotHandler = function() {
231                 return this.handler != null;
232         }
233         this.setHandler = function(handler) {
234                 this.handler = handler;
235         }
236         this.getHandler = function() {
237                 return this.handler;
238         }
239         this.setParent = function(parent) {
240                 this.parent = parent;
241         }
242         this.getParent = function() {
243                 return this.parent;
244         }
245         this.getLineString = function() {
246                 return this.linestring;
247         }
248         this.setLineString = function(string) {
249                 this.linestring = string;
250         }
251         this.isEditable = function() {
252                 return this.editable;
253         }
254         this.setEditable = function(editable) {
255                 this.editable = editable;
256         }
257         
258 }
259 function getTreeNode(nodeID) {
260         return findNodeWithID(rootNode,nodeID);
261 }
262 function findNodeWithID(node,nodeID) {
263         if (node.getID() == nodeID) {
264                 return node;
265         }
266         else {
267                 if (node.hasChilds()) {
268                         for(var i=0;i<node.getChildCount();i++) {
269                                 var value = findNodeWithID(node.childs[i],nodeID);
270                                 if (value != false) {
271                                         return value;
272                                 }
273                         }
274                 }
275                 return false;
276         }
277 }
278 function readStates() {
279         //setCookie('tree' + documentID,'');
280         states = getCookie('tree' + documentID);
281         if (states != null) {
282                 var array = states.split(';');
283                 for(var i=0;i<array.length;i++) {
284                         var singlestate = array[i].split('|');
285                         statearray[i] = new Array();
286                         statearray[i]["key"] = singlestate[0];
287                         statearray[i]["state"]  = singlestate[1];
288                 }
289         }
290 }
291 function getState(nodeID) {
292         for(var i=0;i<statearray.length;i++) {
293                 if (statearray[i]["key"] == nodeID) {
294                         state = statearray[i]["state"];
295                         if (state == null || state == '') {
296                                 state = 'closed';
297                         }
298                         return state;
299                 }
300         }
301         return "closed";
302 }
303 function writeStates(nodeID,newstate) {
304         //alert(nodeID);
305         var str = '';
306         var found = false;
307         for(var i=0;i<statearray.length;i++) {
308                 if (statearray[i]["key"] == nodeID) {
309                         statearray[i]["state"] = newstate;
310                         found = true;
311                 }
312                 if (statearray[i]["state"] != null) {
313                         str += statearray[i]["key"] + '|' + statearray[i]["state"] + ';';
314                 }
315         }
316         if (found == false) {
317                 statearray[statearray.length] = new Array();
318                 statearray[statearray.length - 1]["key"] = nodeID;
319                 statearray[statearray.length - 1]["state"] = newstate;
320                 if (newstate != null) {
321                         str += nodeID + '|' + newstate + ';';
322                 }
323         }
324         setCookie('tree' + documentID,str);
325 }
326 function showTree(path) {
327         readStates();
328         
329         href = path;
330         window.focus();
331         window.onblur = blurSelection;
332         window.onfocus = focusSelection;
333         var str = '';
334         str = '<div id="node' + rootNode.getID() + '" class="treetitle" style="display:' + (showRootNode == true ? 'block' : 'none') + '">';
335         str += '<nobr>';
336         if (rootNode.hasIcon()) {
337                 str += '<img src="' + rootNode.getIcon() + '" style="vertical-align:middle;">';
338         }
339         str += '<span style="vertical-align:middle;">&nbsp;' + rootNode.getName() + '</span>';
340         str += '</nobr></div>';
341         
342         if (rootNode.hasChilds()) {
343                 for(i=0;i<rootNode.childs.length;i++) {
344                         nodeContents = showNode(rootNode.childs[i],(i == (rootNode.getChildCount() -1)));
345                         str = str + nodeContents;
346                 }
347         }
348         container.innerHTML = str;
349         if (window.finishedLoading) {
350                 finishedLoading();
351         }
352 }
353 /**
354 * Shows the given node, and subnodes.
355 */
356 function showNode(treeNode,lastNode) {
357         linestring = treeNode.getLineString();
358         var state = getState(treeNode.getID());
359         var str;
360         str = '<div style="filter:alpha(opacity=100);" ondragenter="dragEnter(' + treeNode.getID() + ');" ondragleave="dragLeave();" ondragstart="startDrag(' + treeNode.getID() + ');" ondrag="dragMove();" ondragend="endDrag(' + treeNode.getID() + ')" id="node' + treeNode.getID() + '">';
361         str += '<nobr>';
362         for(var y=0;y<linestring.length;y++) {
363                 if (linestring.charAt(y) == 'I') {
364                         str += '<img src="' + href + 'static/' + (showLines ? 'line' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
365                 }
366                 else if (linestring.charAt(y) == 'B') {
367                         str += '<img src="' + href + 'static/white.gif" style="width:19px;height:20px;vertical-align:middle;">';
368                 }
369         }
370         if (treeNode.hasChilds()) {
371                 // If this is the first child of the rootNode, and showRootNode is false, we want to display a different icon.
372                 if (!showRootNode && (treeNode.getParent() == rootNode) && (treeNode.getParent().getFirstChild() == treeNode)) {
373                         if (!lastNode) {
374                                 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (state == 'open' ? (showLines ? 'minus_no_root' : 'minus_nolines') : (showLines ? 'plus_no_root' : 'plus_nolines')) + '.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
375                         }
376                         else {
377                                 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (state == 'open' ? 'minus_last' : 'plus_last') + '_no_root.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
378                         }
379                 }
380                 else {
381                         if (!lastNode) {
382                                 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (state == 'open' ? (showLines ? 'minus' : 'minus_nolines') : (showLines ? 'plus' : 'plus_nolines')) + '.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
383                         }
384                         else {
385                                 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (state == 'open' ? (showLines ? 'minus_last' : 'minus_nolines') : (showLines ? 'plus_last' : 'plus_nolines')) + '.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
386                         }
387                 }
388         }
389         else {
390                 // If this is the first child of the rootNode, and showRootNode is false, we want to display a different icon.
391                 if (!showRootNode && (treeNode.getParent() == rootNode) && (treeNode.getParent().getFirstChild() == treeNode)) {
392                         if (!lastNode) {
393                                 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (showLines ? 't_no_root' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
394                         }
395                         else {
396                                 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/white.gif" style="width:19px;height:20px;vertical-align:middle;">';
397                         }
398                 }
399                 else {
400                         if (!lastNode) {
401                                 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (showLines ? 't' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
402                         }
403                         else {
404                                 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (showLines ? 'lastnode' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
405                         }
406                 }
407         }
408         iconStartImage = treeNode.getIcon();
409         if (state != 'closed') {
410                 if (treeNode.hasChilds()) {
411                         iconStartImage = treeNode.getOpenIcon();
412                 }
413         }
414         
415         str += '<img id="iconimage' + treeNode.getID() + '" src="' + iconStartImage + '" style="vertical-align:middle;" OnClick="selectNode(' + treeNode.getID() + ')">';
416         str += '&nbsp;<span unselectable="ON" style="vertical-align:middle;" class="treetitle" ID="title' + treeNode.getID() + '" OnDblClick="handleNode(' + treeNode.getID() + ')" OnClick="selectNode(' + treeNode.getID() + ')">';
417         str += treeNode.getName();
418         str += '</span>';
419         str += '</nobr>';
420         str += '</div>';
421
422         if (treeNode.hasChilds()) {
423                 if (state == 'open') {
424                         str += '<div id="node' + treeNode.getID() + 'sub" style="display:block;">';
425                         fireOpenEvent(treeNode);
426                         // alert('openevent: ' + treeNode.getName());
427                 }
428                 else {
429                         str += '<div id="node' + treeNode.getID() + 'sub" style="display:' + (showAllNodesOnStartup == true ? 'block;' : 'none;') + ';">';
430                 }
431                 var subgroupstr = '';
432                 var newChar = '';
433
434                 if (!lastNode) {
435                         newChar = 'I';
436                 }
437                 else {
438                         newChar = 'B';
439                 }
440                 for(var z=0;z<treeNode.getChildCount();z++) {
441                         treeNode.childs[z].setLineString(linestring + newChar);
442                 }
443                 for(var z=0;z<treeNode.getChildCount();z++) {
444                         subgroupstr += showNode(treeNode.childs[z],(z == (treeNode.getChildCount() -1)));
445                 }
446                 str += subgroupstr;
447                 str += '</div>';
448         }
449         else {
450                 str += '<div id="node' + treeNode.getID() + 'sub" style="display:none;">';
451                 str += '</div>';
452         }
453         return str;
454 }
455 /*
456 function mouseMove() {
457         if (dragging) {
458                 alert('bob');
459         }
460 }
461 function mouseUp() {
462         if (dragging) {
463                 alert('dropped on something!');
464         }
465 }
466 */
467 function startDrag(nodeID) {
468         if (!dragable) {
469                 return;
470         }
471         draggedNodeID = nodeID;
472         
473         var srcObj = window.event.srcElement;
474         while(srcObj.tagName != 'DIV') {
475                 srcObj = srcObj.parentElement;
476         }
477         floatDragElement = document.createElement('DIV');
478
479         floatDragElement.innerHTML = srcObj.innerHTML;
480         floatDragElement.childNodes[0].removeChild(floatDragElement.childNodes[0].childNodes[0]);
481         
482         document.body.appendChild(floatDragElement);
483         floatDragElement.style.zIndex = 100;
484         floatDragElement.style.position = 'absolute';
485         floatDragElement.style.filter='progid:DXImageTransform.Microsoft.Alpha(1,opacity=60);';
486 }
487 function findSpanChild(element) {
488         if (element.tagName == 'SPAN') {
489                 return element;
490         }
491         else {
492                 if (element.childNodes) {
493                         for(var i=0;i<element.childNodes.length;i++) {
494                                 var value = findSpanChild(element.childNodes[i]);
495                                 if (value != false) {
496                                         return value;
497                                 }
498                         }
499                         return false;
500                 }
501         }
502 }
503 function dragEnter(nodeID) {
504         if (!dragable) {
505                 return;
506         }
507         lastDraggedOnNodeID = nodeID;
508         
509         if (colouredElement) {
510                 findSpanChild(colouredElement).className = 'treetitle';
511         }
512         colouredElement = window.event.srcElement;
513         while(colouredElement.tagName != 'DIV') {
514                 colouredElement = colouredElement.parentElement;
515                 if (colouredElement.tagName == 'BODY') {
516                         // Something gone seriously wrong.
517                         alert('Drag failure, reached <BODY>!');
518                         return;
519                 }
520         }       
521         findSpanChild(colouredElement).className = 'treetitleselectedfocused';
522 }
523 function dragLeave() {
524         if (!dragable) {
525                 return;
526         }
527 }
528 function endDrag(nodeID) {
529         if (!dragable) {
530                 return;
531         }
532         if (lastDraggedOnNodeID != null) {
533                 fireMoveEvent(getTreeNode(lastDraggedOnNodeID),draggedNodeID,lastDraggedOnNodeID);
534         }
535 }
536 function dragProceed() {
537         if (!dragable) {
538                 return;
539         }
540         var dragged = getTreeNode(draggedNodeID);
541         var newparent = getTreeNode(lastDraggedOnNodeID);
542
543         var oldparent = dragged.getParent();
544         
545         oldparent.removeChild(dragged);
546         newparent.addChild(dragged);
547         
548         refreshNode(oldparent);
549         refreshNode(newparent);
550         
551         _dragClean()
552 }
553 function dragCancel() {
554         if (!dragable) {
555                 return;
556         }
557         _dragClean()
558 }
559 /**
560 * Don't call this yourself.
561 */
562 function _dragClean() {
563         if (!dragable) {
564                 return;
565         }
566         if (colouredElement) {
567                 findSpanChild(colouredElement).className = 'treetitle';
568         }
569         
570         floatDragElement.parentElement.removeChild(floatDragElement);
571         floatDragElement = null;
572         colouredElement = null;
573         draggedNodeID = null;
574         lastDraggedOnNodeID = null;
575 }
576 function dragMove() {
577         if (!dragable) {
578                 return;
579         }
580         floatDragElement.style.top = window.event.clientY;
581         floatDragElement.style.left = window.event.clientX;
582 }
583 function editEnded() {
584         if (treeNodeEdited != null) {
585                 // treeNodeEdited.getID();
586                 var editTitle = document.getElementById('title' + treeNodeEdited.getID());
587                 var input = editTitle.childNodes[0];
588         
589                 var newValue = input.value;
590                 
591                 if (newValue == treeNodeEdited.getName()) {
592                         editTitle.innerHTML = newValue;
593                         treeNodeEdited = null;
594                         return;
595                 }
596         
597                 fireEditEvent(treeNodeEdited,newValue);
598                 
599                 if (!editaborted) {
600                         treeNodeEdited.setName(newValue);
601                         editTitle.innerHTML = newValue;
602                 }
603         
604                 treeNodeEdited = null;
605         }
606 }
607 function selectNode(nodeID) {
608         var treeNode = getTreeNode(nodeID);
609
610         if (selectedNode != null) {
611                 if (selectedNode == nodeID) {
612                         if (treeNode.isEditable()) {
613                                 if (treeNodeEdited == treeNode) {
614                                         return;
615                                 }
616                                 treeNodeEdited = treeNode;
617                                 var editTitle = document.getElementById('title' + treeNode.getID());
618                                 editTitle.className = 'editednode';
619                                 
620
621                                 editTitle.innerHTML = '<input type="text" onKeypress="if (event.keyCode == 13) { this.onblur = null; editEnded(); }" name="editednode" class="editednodeinput">';
622                                 var input = editTitle.childNodes[0];
623                                 input.value = treeNode.getName();
624                                 input.focus();
625                                 input.select();
626                                 input.onblur = editEnded;
627                         }
628                         return;
629                 }
630                 if (treeNodeEdited != null) {
631                         editEnded();
632                 }
633                 var oldNodeTitle = document.getElementById('title' + selectedNode);
634                 oldNodeTitle.className = 'treetitle';
635         }
636         selectedNode = nodeID;
637         var nodetitle = document.getElementById('title' + selectedNode);
638         nodetitle.className = 'treetitleselectedfocused';
639         
640         if (treeNode.gotHandler()) {
641                 eval(treeNode.getHandler() + '(getTreeNode(' + nodeID + '));');
642         }
643         else {
644                 standardClick(treeNode);
645         }
646 }
647 function refreshNode(treeNode) {
648         var submenu = document.getElementById('node' + treeNode.getID() + 'sub');
649         var str = '';
650         for(var i=0;i<treeNode.getChildCount();i++) {
651                 var parent = treeNode.getParent();
652                 if (!parent) {
653                         treeNode.childs[i].setLineString(treeNode.getLineString() + 'B');
654                 }
655                 else {
656                         if (parent.childs[parent.childs.length - 1] == treeNode) {
657                                 treeNode.childs[i].setLineString(treeNode.getLineString() + 'B');
658                         }
659                         else {
660                                 treeNode.childs[i].setLineString(treeNode.getLineString() + 'I');
661                         }
662                 }
663                 str += showNode(treeNode.childs[i],i == (treeNode.getChildCount() - 1));
664         }
665         var actionimage = document.getElementById('handler' + treeNode.getID());
666         if (treeNode.getChildCount() == 0) {
667                 // TreeNode haven't got any children, make sure the right image is displayed.
668                 if (actionimage.src.indexOf('last') == -1) {
669                         actionimage.src = href + 'static/' + (showLines ? 't' : 'white') + '.gif';
670                 }
671                 else {
672                         actionimage.src = href + 'static/' + (showLines ? 'lastnode' : 'white') + '.gif';
673                 }
674                 actionimage.onclick = null;
675                 
676                 // Close the submenu
677                 if (submenu) {
678                         submenu.style.display = 'none';
679                 }
680         }
681         else {
682                 // We have children, make sure to display the + and - icon.
683                 if (actionimage.src.indexOf('plus') != -1) {
684                         // The TreeNode have already got children, and displays them.
685                 }
686                 else if (actionimage.src.indexOf('minus') != -1) {
687                         // The TreeNode have already got children, and displays them.
688                 }
689                 else {
690                         if (actionimage.src.indexOf('last') == -1) {
691                                 actionimage.outerHTML = '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (showLines ? 'plus' : 'plus_nolines') + '.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
692                         }
693                         else {
694                                 actionimage.outerHTML = '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/plus_last.gif" style="width:19px;height:20px;vertical-align:middle;" OnClick="handleNode(' + treeNode.getID() + ');">';
695                         }
696                 }
697         }
698         submenu.innerHTML = str;
699 }
700 function handleNode(nodeID) {
701         var treeNode = getTreeNode(nodeID);     
702         if (!treeNode.hasChilds()) { // No reason to handle a node without childs.
703                 return;
704         }
705         
706         var submenu = document.getElementById('node' + nodeID + 'sub');
707         
708         var iconimageholder = document.getElementById('iconimage' + nodeID);
709         var actionimage = document.getElementById('handler' + nodeID);
710
711         // This will be used if showRootNode is set to false.
712         var firstChildOfRoot = false;
713         if (actionimage.src.indexOf('_no_root') != -1) {
714                 firstChildOfRoot = true;
715         }
716         
717         if (submenu.style.display == 'none') {
718                 writeStates(nodeID,'open');
719                 fireOpenEvent(treeNode);
720                 submenu.style.display = 'block';
721
722                 iconimageholder.src = treeNode.getOpenIcon();
723         
724                 if (actionimage.src.indexOf('last') == -1) {
725                         actionimage.src = href + 'static/' + ((firstChildOfRoot) ? 'minus_no_root' : (showLines ? 'minus' : 'minus_nolines')) + '.gif';
726                 }
727                 else {
728                         actionimage.src = href + 'static/' + ((firstChildOfRoot) ? 'minus_last_no_root' : (showLines ? 'minus_last' : 'minus_nolines')) + '.gif';
729                 }
730         }
731         else {
732                 writeStates(nodeID,'closed');
733                 submenu.style.display = 'none';
734                 
735                 iconimageholder.src = treeNode.getIcon();
736                 
737                 if (actionimage.src.indexOf('last') == -1) {
738                         actionimage.src = href + 'static/' + ((firstChildOfRoot) ? 'plus_no_root' : (showLines ? 'plus' : 'plus_nolines')) + '.gif';
739                 }
740                 else {
741                         actionimage.src = href + 'static/' + ((firstChildOfRoot) ? 'plus_last_no_root' : (showLines ? 'plus_last' : 'plus_nolines')) + '.gif';
742                 }
743         }
744 }
745 function fireOpenEvent(treeNode) {
746         if (treeNode.gotOpenEventListeners()) {
747                 for(var i=0;i<treeNode.openeventlisteners.length;i++) {
748                         eval(treeNode.openeventlisteners[i] + '(' + treeNode.getID() + ');');
749                 }
750         }
751 }
752 function fireEditEvent(treeNode,newVal) {
753         if (treeNode.gotEditEventListeners()) {
754                 for(var i=0;i<treeNode.editeventlisteners.length;i++) {
755                         eval(treeNode.editeventlisteners[i] + '(' + treeNode.getID() + ',\'' + escape(newVal) + '\');');
756                 }
757         }
758 }
759 function fireMoveEvent(treeNode,draggedNodeID,droppedOnNodeID) {
760         if (treeNode.gotMoveEventListeners()) {
761                 for(var i=0;i<treeNode.moveeventlisteners.length;i++) {
762                         eval(treeNode.moveeventlisteners[i] + '(' + draggedNodeID + ',' + droppedOnNodeID + ');');
763                 }
764         }
765 }
766 function blurSelection() {
767         if (selectedNode != null) {
768                 var oldNodeTitle = document.getElementById('title' + selectedNode);
769                 oldNodeTitle.className = 'treetitleselectedblured';
770         }
771 }
772 function focusSelection() {
773         if (selectedNode != null) {
774                 var oldNodeTitle = document.getElementById('title' + selectedNode);
775                 oldNodeTitle.className = 'treetitleselectedfocused';
776         }
777 }
778 function getCookieVal (offset) {  
779         var endstr = document.cookie.indexOf (";",offset);  
780         if (endstr == -1) {
781                 endstr = document.cookie.length;
782         }
783         return unescape(document.cookie.substring(offset,endstr));
784 }
785 function getCookie (name) {  
786         var arg = name + "=";
787         var alen = arg.length;
788         var clen = document.cookie.length;
789         var i = 0;
790         while (i < clen) {
791                 var j = i + alen;
792                 if (document.cookie.substring(i, j) == arg) {
793                         return getCookieVal(j);
794                 }
795                 i = document.cookie.indexOf(" ", i) + 1;
796                 if (i == 0) {
797                         break;
798                 }
799         }
800         return null;
801 }
802 function setCookie (name, value) {  
803         var argv = setCookie.arguments;  
804         var argc = setCookie.arguments.length;  
805         var expires = (argc > 2) ? argv[2] : null;  
806         var path = (argc > 3) ? argv[3] : null;  
807         var domain = (argc > 4) ? argv[4] : null;  
808         var secure = (argc > 5) ? argv[5] : false;  
809         document.cookie = name + "=" + escape (value) + ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) + ((path == null) ? "" : ("; path=" + path)) + ((domain == null) ? "" : ("; domain=" + domain)) + ((secure == true) ? "; secure" : "");
810 }
811 function expandNode() {
812         var state = getState(selectedNode);
813         if (state == 'open') {
814                 var currentTreeNode = getTreeNode(selectedNode);
815                 if (currentTreeNode.hasChilds()) {
816                         selectNode(currentTreeNode.childs[0].getID());
817                 }
818         }
819         else {
820                 handleNode(selectedNode);
821         }
822 }
823 function subtractNode() {
824         var state = getState(selectedNode);
825         if (state == 'closed') {
826                 var currentTreeNode = getTreeNode(selectedNode);
827                 var parent = currentTreeNode.getParent();
828                 if (parent != null && parent != rootNode) {
829                         selectNode(parent.getID());
830                 }
831         }
832         else {
833                 handleNode(selectedNode);
834         }
835 }
836 function selectPrevNode() {
837         var currentTreeNode = getTreeNode(selectedNode);
838         if (currentTreeNode.prevSibling != null) {
839
840                 var state = getState(currentTreeNode.prevSibling.getID());
841
842                 if (state == 'open' && currentTreeNode.prevSibling.hasChilds()) {
843                         // We have to find the last open child of the previoussiblings childs.
844                         var current = currentTreeNode.prevSibling.childs[currentTreeNode.prevSibling.childs.length - 1];
845                         var currentstate = 'open';
846                         while (current.hasChilds() && (getState(current.getID()) == 'open')) {
847                                 current = current.childs[current.childs.length - 1];
848                         }
849                         selectNode(current.getID());
850                 }
851                 else {
852                         selectNode(currentTreeNode.prevSibling.getID());
853                 }
854         }
855         else {
856                 if (currentTreeNode.getParent() != null && currentTreeNode.getParent() != rootNode) {
857                         selectNode(currentTreeNode.getParent().getID());
858                 }
859         }
860 }
861 function selectNextNode() {
862         var currentTreeNode = getTreeNode(selectedNode);
863
864         var state = getState(selectedNode);
865         if (state == 'open' && currentTreeNode.hasChilds()) {
866                 selectNode(currentTreeNode.childs[0].getID());
867         }       
868         else {
869                 if (currentTreeNode.nextSibling != null) {
870                         selectNode(currentTreeNode.nextSibling.getID());
871                 }
872                 else {
873                         // Continue up the tree until we either hit null, or a parent which have a child.
874                         var parent = currentTreeNode;
875                         while ((parent = parent.getParent()) != rootNode) {
876                                 if (parent.nextSibling != null) {
877                                         selectNode(parent.nextSibling.getID());
878                                         break;
879                                 }
880                         }
881                         /*
882                         if (currentTreeNode.getParent().nextSibling != null) {
883                                 selectNode(currentTreeNode.getParent().nextSibling.getID());
884                         }
885                         */
886                 }
887         }
888 }
889 function keyDown(event) {
890         if (window.event) {
891                 event = window.event;
892         }
893         if (event.keyCode == 38) { // Up
894                 selectPrevNode();
895                 return false;
896         }
897         else if (event.keyCode == 40) { // Down
898                 selectNextNode();
899                 return false;
900         }
901         else if (event.keyCode == 37) { // left
902                 subtractNode();
903                 return false;
904         }
905         else if (event.keyCode == 39) { // right
906                 expandNode();
907                 return false;
908         }
909 }
910 document.onkeydown = keyDown;