2 * Original Author of this file: Martin Mouritzen. (martin@nano.dk)
5 * (Lack of) Documentation:
8 * If a finishedLoading method exists, it will be called when the tree is loaded.
9 * (good to display a div, etc.).
12 * You have to set the variable rootNode (as a TreeNode).
14 * You have to set a container element, this is the element in which the tree will be.
18 * Save cookies better (only 1 cookie for each tree). Else the page will totally cookieclutter.
20 ***********************************************************************
21 * Configuration variables.
22 ************************************************************************/
24 // Should the rootNode be displayed.
25 var showRootNode = true;
27 // Should the dashed lines between nodes be shown.
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).
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;
37 // being read from cookie.
38 var nodesOpen = new Array();
40 // RootNode of the tree.
43 // Container to display the Tree in.
46 // Shows/Hides subnodes on startup
47 var showAllNodesOnStartup = false;
49 // Is the roots dragable?
53 /************************************************************************
54 * The following is just instancevariables.
55 ************************************************************************/
58 // rootNodeCallBack name (if null, it's not selectable).
59 var rootNodeCallBack = null;
62 var selectedNode = null;
65 var statearray = new Array();
67 var treeNodeEdited = null;
69 var editaborted = false;
71 var floatDragElement = null;
72 var colouredElement = null;
73 var draggedNodeID = null;
74 var lastDraggedOnNodeID = null;
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).
85 function TreeNode(id,name,icon,param,orderNumber) {
87 this.childs = new Array();
88 this.name = (name == null ? 'unset name' : name);
89 this.icon = (icon == null ? '' : icon);
92 this.param = (param == null ? '' : param);
93 this.orderNumber = (orderNumber == null ? -1 : orderNumber);
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 = '';
102 this.nextSibling = null;
103 this.prevSibling = null;
105 this.childsHasBeenFetched = false;
107 this.getID = function() {
110 this.setName = function(newname) {
113 this.getName = function() {
116 this.getParam = function() {
119 this.setIcon = function(icon) {
122 this.getIcon = function() {
123 if (typeof(this.icon) == 'object') {
128 this.getOpenIcon = function() {
129 if (typeof(this.icon) == 'object') {
134 this.hasIcon = function () {
135 return this.icon != '';
137 this.getOrderNumber = function() {
138 return this.orderNumber;
140 this.addOpenEventListener = function(event) {
141 this.openeventlisteners[this.openeventlisteners.length] = event;
143 this.gotOpenEventListeners = function() {
144 return (this.openeventlisteners.length > 0);
146 this.addEditEventListener = function(event) {
147 this.editeventlisteners[this.editeventlisteners.length] = event;
149 this.gotEditEventListeners = function() {
150 return (this.editeventlisteners.length > 0);
152 this.addMoveEventListener = function(event) {
153 this.moveeventlisteners[this.moveeventlisteners.length] = event;
155 this.gotMoveEventListeners = function() {
156 return (this.moveeventlisteners.length > 0);
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);
166 this.childs[this.childs.length] = childNode;
167 childNode.setParent(this);
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;
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;
182 return order1 - order2;
186 this.childs.sort(sortByOrder);
189 this.removeChild = function(childNode) {
191 for (var i=0;i<this.childs.length;i++) {
193 this.childs[i] = this.childs[i + 1];
195 if (this.childs[i] == childNode) {
196 if (i == (this.childs.length - 1)) {
197 this.childs[i] = null;
200 this.childs[i] = this.childs[i + 1];
206 this.childs.length = this.childs.length-1;
209 this.resetChilds = function() {
210 this.childs = new Array();
212 this.setHasChilds = function(hasChilds) {
213 this.haschilds = hasChilds;
215 this.hasChilds = function() {
216 if (this.haschilds == true) {
219 return (this.childs.length > 0);
221 this.getChildCount = function() {
222 return this.childs.length;
224 this.getFirstChild = function() {
225 if (this.hasChilds()) {
226 return this.childs[0];
230 this.gotHandler = function() {
231 return this.handler != null;
233 this.setHandler = function(handler) {
234 this.handler = handler;
236 this.getHandler = function() {
239 this.setParent = function(parent) {
240 this.parent = parent;
242 this.getParent = function() {
245 this.getLineString = function() {
246 return this.linestring;
248 this.setLineString = function(string) {
249 this.linestring = string;
251 this.isEditable = function() {
252 return this.editable;
254 this.setEditable = function(editable) {
255 this.editable = editable;
259 function getTreeNode(nodeID) {
260 return findNodeWithID(rootNode,nodeID);
262 function findNodeWithID(node,nodeID) {
263 if (node.getID() == nodeID) {
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) {
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];
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 == '') {
303 function writeStates(nodeID,newstate) {
307 for(var i=0;i<statearray.length;i++) {
308 if (statearray[i]["key"] == nodeID) {
309 statearray[i]["state"] = newstate;
312 if (statearray[i]["state"] != null) {
313 str += statearray[i]["key"] + '|' + statearray[i]["state"] + ';';
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 + ';';
324 setCookie('tree' + documentID,str);
326 function showTree(path) {
331 window.onblur = blurSelection;
332 window.onfocus = focusSelection;
334 str = '<div id="node' + rootNode.getID() + '" class="treetitle" style="display:' + (showRootNode == true ? 'block' : 'none') + '">';
336 if (rootNode.hasIcon()) {
337 str += '<img src="' + rootNode.getIcon() + '" style="vertical-align:middle;">';
339 str += '<span style="vertical-align:middle;"> ' + rootNode.getName() + '</span>';
340 str += '</nobr></div>';
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;
348 container.innerHTML = str;
349 if (window.finishedLoading) {
354 * Shows the given node, and subnodes.
356 function showNode(treeNode,lastNode) {
357 linestring = treeNode.getLineString();
358 var state = getState(treeNode.getID());
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() + '">';
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;">';
366 else if (linestring.charAt(y) == 'B') {
367 str += '<img src="' + href + 'static/white.gif" style="width:19px;height:20px;vertical-align:middle;">';
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)) {
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() + ');">';
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() + ');">';
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() + ');">';
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() + ');">';
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)) {
393 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (showLines ? 't_no_root' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
396 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/white.gif" style="width:19px;height:20px;vertical-align:middle;">';
401 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (showLines ? 't' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
404 str += '<img id="handler' + treeNode.getID() + '" src="' + href + 'static/' + (showLines ? 'lastnode' : 'white') + '.gif" style="width:19px;height:20px;vertical-align:middle;">';
408 iconStartImage = treeNode.getIcon();
409 if (state != 'closed') {
410 if (treeNode.hasChilds()) {
411 iconStartImage = treeNode.getOpenIcon();
415 str += '<img id="iconimage' + treeNode.getID() + '" src="' + iconStartImage + '" style="vertical-align:middle;" OnClick="selectNode(' + treeNode.getID() + ')">';
416 str += ' <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();
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());
429 str += '<div id="node' + treeNode.getID() + 'sub" style="display:' + (showAllNodesOnStartup == true ? 'block;' : 'none;') + ';">';
431 var subgroupstr = '';
440 for(var z=0;z<treeNode.getChildCount();z++) {
441 treeNode.childs[z].setLineString(linestring + newChar);
443 for(var z=0;z<treeNode.getChildCount();z++) {
444 subgroupstr += showNode(treeNode.childs[z],(z == (treeNode.getChildCount() -1)));
450 str += '<div id="node' + treeNode.getID() + 'sub" style="display:none;">';
456 function mouseMove() {
463 alert('dropped on something!');
467 function startDrag(nodeID) {
471 draggedNodeID = nodeID;
473 var srcObj = window.event.srcElement;
474 while(srcObj.tagName != 'DIV') {
475 srcObj = srcObj.parentElement;
477 floatDragElement = document.createElement('DIV');
479 floatDragElement.innerHTML = srcObj.innerHTML;
480 floatDragElement.childNodes[0].removeChild(floatDragElement.childNodes[0].childNodes[0]);
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);';
487 function findSpanChild(element) {
488 if (element.tagName == 'SPAN') {
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) {
503 function dragEnter(nodeID) {
507 lastDraggedOnNodeID = nodeID;
509 if (colouredElement) {
510 findSpanChild(colouredElement).className = 'treetitle';
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>!');
521 findSpanChild(colouredElement).className = 'treetitleselectedfocused';
523 function dragLeave() {
528 function endDrag(nodeID) {
532 if (lastDraggedOnNodeID != null) {
533 fireMoveEvent(getTreeNode(lastDraggedOnNodeID),draggedNodeID,lastDraggedOnNodeID);
536 function dragProceed() {
540 var dragged = getTreeNode(draggedNodeID);
541 var newparent = getTreeNode(lastDraggedOnNodeID);
543 var oldparent = dragged.getParent();
545 oldparent.removeChild(dragged);
546 newparent.addChild(dragged);
548 refreshNode(oldparent);
549 refreshNode(newparent);
553 function dragCancel() {
560 * Don't call this yourself.
562 function _dragClean() {
566 if (colouredElement) {
567 findSpanChild(colouredElement).className = 'treetitle';
570 floatDragElement.parentElement.removeChild(floatDragElement);
571 floatDragElement = null;
572 colouredElement = null;
573 draggedNodeID = null;
574 lastDraggedOnNodeID = null;
576 function dragMove() {
580 floatDragElement.style.top = window.event.clientY;
581 floatDragElement.style.left = window.event.clientX;
583 function editEnded() {
584 if (treeNodeEdited != null) {
585 // treeNodeEdited.getID();
586 var editTitle = document.getElementById('title' + treeNodeEdited.getID());
587 var input = editTitle.childNodes[0];
589 var newValue = input.value;
591 if (newValue == treeNodeEdited.getName()) {
592 editTitle.innerHTML = newValue;
593 treeNodeEdited = null;
597 fireEditEvent(treeNodeEdited,newValue);
600 treeNodeEdited.setName(newValue);
601 editTitle.innerHTML = newValue;
604 treeNodeEdited = null;
607 function selectNode(nodeID) {
608 var treeNode = getTreeNode(nodeID);
610 if (selectedNode != null) {
611 if (selectedNode == nodeID) {
612 if (treeNode.isEditable()) {
613 if (treeNodeEdited == treeNode) {
616 treeNodeEdited = treeNode;
617 var editTitle = document.getElementById('title' + treeNode.getID());
618 editTitle.className = 'editednode';
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();
626 input.onblur = editEnded;
630 if (treeNodeEdited != null) {
633 var oldNodeTitle = document.getElementById('title' + selectedNode);
634 oldNodeTitle.className = 'treetitle';
636 selectedNode = nodeID;
637 var nodetitle = document.getElementById('title' + selectedNode);
638 nodetitle.className = 'treetitleselectedfocused';
640 if (treeNode.gotHandler()) {
641 eval(treeNode.getHandler() + '(getTreeNode(' + nodeID + '));');
644 standardClick(treeNode);
647 function refreshNode(treeNode) {
648 var submenu = document.getElementById('node' + treeNode.getID() + 'sub');
650 for(var i=0;i<treeNode.getChildCount();i++) {
651 var parent = treeNode.getParent();
653 treeNode.childs[i].setLineString(treeNode.getLineString() + 'B');
656 if (parent.childs[parent.childs.length - 1] == treeNode) {
657 treeNode.childs[i].setLineString(treeNode.getLineString() + 'B');
660 treeNode.childs[i].setLineString(treeNode.getLineString() + 'I');
663 str += showNode(treeNode.childs[i],i == (treeNode.getChildCount() - 1));
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';
672 actionimage.src = href + 'static/' + (showLines ? 'lastnode' : 'white') + '.gif';
674 actionimage.onclick = null;
678 submenu.style.display = 'none';
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.
686 else if (actionimage.src.indexOf('minus') != -1) {
687 // The TreeNode have already got children, and displays them.
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() + ');">';
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() + ');">';
698 submenu.innerHTML = str;
700 function handleNode(nodeID) {
701 var treeNode = getTreeNode(nodeID);
702 if (!treeNode.hasChilds()) { // No reason to handle a node without childs.
706 var submenu = document.getElementById('node' + nodeID + 'sub');
708 var iconimageholder = document.getElementById('iconimage' + nodeID);
709 var actionimage = document.getElementById('handler' + nodeID);
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;
717 if (submenu.style.display == 'none') {
718 writeStates(nodeID,'open');
719 fireOpenEvent(treeNode);
720 submenu.style.display = 'block';
722 iconimageholder.src = treeNode.getOpenIcon();
724 if (actionimage.src.indexOf('last') == -1) {
725 actionimage.src = href + 'static/' + ((firstChildOfRoot) ? 'minus_no_root' : (showLines ? 'minus' : 'minus_nolines')) + '.gif';
728 actionimage.src = href + 'static/' + ((firstChildOfRoot) ? 'minus_last_no_root' : (showLines ? 'minus_last' : 'minus_nolines')) + '.gif';
732 writeStates(nodeID,'closed');
733 submenu.style.display = 'none';
735 iconimageholder.src = treeNode.getIcon();
737 if (actionimage.src.indexOf('last') == -1) {
738 actionimage.src = href + 'static/' + ((firstChildOfRoot) ? 'plus_no_root' : (showLines ? 'plus' : 'plus_nolines')) + '.gif';
741 actionimage.src = href + 'static/' + ((firstChildOfRoot) ? 'plus_last_no_root' : (showLines ? 'plus_last' : 'plus_nolines')) + '.gif';
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() + ');');
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) + '\');');
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 + ');');
766 function blurSelection() {
767 if (selectedNode != null) {
768 var oldNodeTitle = document.getElementById('title' + selectedNode);
769 oldNodeTitle.className = 'treetitleselectedblured';
772 function focusSelection() {
773 if (selectedNode != null) {
774 var oldNodeTitle = document.getElementById('title' + selectedNode);
775 oldNodeTitle.className = 'treetitleselectedfocused';
778 function getCookieVal (offset) {
779 var endstr = document.cookie.indexOf (";",offset);
781 endstr = document.cookie.length;
783 return unescape(document.cookie.substring(offset,endstr));
785 function getCookie (name) {
786 var arg = name + "=";
787 var alen = arg.length;
788 var clen = document.cookie.length;
792 if (document.cookie.substring(i, j) == arg) {
793 return getCookieVal(j);
795 i = document.cookie.indexOf(" ", i) + 1;
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" : "");
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());
820 handleNode(selectedNode);
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());
833 handleNode(selectedNode);
836 function selectPrevNode() {
837 var currentTreeNode = getTreeNode(selectedNode);
838 if (currentTreeNode.prevSibling != null) {
840 var state = getState(currentTreeNode.prevSibling.getID());
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];
849 selectNode(current.getID());
852 selectNode(currentTreeNode.prevSibling.getID());
856 if (currentTreeNode.getParent() != null && currentTreeNode.getParent() != rootNode) {
857 selectNode(currentTreeNode.getParent().getID());
861 function selectNextNode() {
862 var currentTreeNode = getTreeNode(selectedNode);
864 var state = getState(selectedNode);
865 if (state == 'open' && currentTreeNode.hasChilds()) {
866 selectNode(currentTreeNode.childs[0].getID());
869 if (currentTreeNode.nextSibling != null) {
870 selectNode(currentTreeNode.nextSibling.getID());
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());
882 if (currentTreeNode.getParent().nextSibling != null) {
883 selectNode(currentTreeNode.getParent().nextSibling.getID());
889 function keyDown(event) {
891 event = window.event;
893 if (event.keyCode == 38) { // Up
897 else if (event.keyCode == 40) { // Down
901 else if (event.keyCode == 37) { // left
905 else if (event.keyCode == 39) { // right
910 document.onkeydown = keyDown;