1 /*----------------------------------------------------|
2 | dTree 2.05 | www.destroydrop.com/javascript/tree/ |
3 |---------------------------------------------------|
4 | Copyright (c) 2002-2003 Geir Landro |
6 | This script can be used freely as long as all |
7 | copyright messages are intact. |
9 | Updated: 17.04.2003 |
10 |---------------------------------------------------|
11 | Modified for Dokuwiki by |
12 | Samuele Tognini <samuele@netsons.org> |
13 | under GPL 2 license |
14 | (http://www.gnu.org/licenses/gpl.html) |
15 | Updated: 29.08.2009 |
16 |---------------------------------------------------|
17 | indexmenu | wiki.splitbrain.org/plugin:indexmenu |
18 |--------------------------------------------------*/
21 function Node(dokuid, id, pid, name, hns, isdir, ajax) {
40 function dTree(objName,theme) {
41 var objExt = indexmenu_findExt(theme);
43 urlbase:DOKU_BASE+'doku.php?id=',
44 plugbase:DOKU_BASE+'lib/plugins/indexmenu',
53 var objImg=this.config.plugbase+'/images/'+theme+'/';
55 root: objImg + 'base.'+objExt,
56 folder: objImg + 'folder.'+objExt,
57 folderH: objImg + 'folderh.'+objExt,
58 folderOpen: objImg + 'folderopen.'+objExt,
59 folderHOpen: objImg + 'folderhopen.'+objExt,
60 node: objImg + 'page.'+objExt,
61 empty: objImg + 'empty.'+objExt,
62 line: objImg + 'line.'+objExt,
63 join: objImg + 'join.'+objExt,
64 joinBottom: objImg + 'joinbottom.'+objExt,
65 plus: objImg + 'plus.'+objExt,
66 plusBottom: objImg + 'plusbottom.'+objExt,
67 minus: objImg + 'minus.'+objExt,
68 minusBottom: objImg + 'minusbottom.'+objExt,
69 nlPlus: objImg + 'nolines_plus.'+objExt,
70 nlMinus: objImg + 'nolines_minus.'+objExt
75 this.root = new Node(false,-1);
76 this.selectedNode = null;
77 this.selectedFound = false;
78 this.completed = false;
80 this.pageid=window.indexmenu_ID||'';
84 // Adds a new node to the node array
85 dTree.prototype.add = function(dokuid, id, pid, name, hns, isdir, ajax) {
86 this.aNodes[this.aNodes.length] = new Node(dokuid, id, pid, name, hns, isdir, ajax);
89 // Open/close all nodes
90 dTree.prototype.openAll = function() {
91 if (!this.getCookie('co' + this.obj)) {
96 // Outputs the tree to the page
97 dTree.prototype.toString = function() {
99 if (this.config.scroll) {str += '<div id="cdtree_'+this.obj+'" class="dtree" style="position:relative;overflow:hidden;width:100%;">';}
100 str += '<div id="dtree_'+this.obj+'" class="dtree '+this.config.theme+'" style="overflow:';
101 if (this.config.scroll) { str += 'visible;position:relative;width:100%"';} else {str += 'hidden;"';}
103 if ($('dtree_'+this.obj)) {str += '<div class="error">Indexmenu id conflict</div>';}
104 if (this.config.toc) {
105 str += '<div id="t' + this.obj + '" class="indexmenu_tocbullet '+this.config.theme+'" style="display:none;" title="Table of contents"></div>';
106 str += '<div id="toc_' + this.obj + '" style="display:none;"></div>';
108 if (this.config.useCookies) { this.selectedNode = this.getSelected(); }
109 str += this.addNode(this.root)+'</div>';
110 if (this.config.scroll) {
111 str += '<div id="z' + this.obj + '" class="indexmenu_rarrow"></div>';
112 str += '<div id="left_'+this.obj+'" class="indexmenu_larrow" style="display:none;" title="Click to scroll back" onmousedown="javascript:'+this.obj+'.scroll(\'r\',1);" onmouseup="javascript:'+this.obj+'.stopscroll();"></div>';
115 this.completed = true;
116 this.divdisplay('nojs_',0);
120 // Creates the tree structure
121 dTree.prototype.addNode = function(pNode) {
122 var str = '',cn,n=pNode._ai,l=pNode._lv+1;
123 for (n; n<this.aNodes.length; n++) {
124 if (this.aNodes[n].pid == pNode.id) {
130 if (cn._hc && !cn._io && this.config.useCookies) {cn._io = this.isOpen(cn.id);}
131 if (this.pageid == (!cn.hns && cn.dokuid || cn.hns)) {
133 } else if (cn.id == this.selectedNode && !this.selectedFound) {
135 this.selectedNode = n;
136 this.selectedFound = true;
138 if (!cn._hc && cn.isdir && !cn.ajax && !cn.hns) {
139 if (cn._ls) {str += this.noderr(cn, n);}
141 str += this.node(cn, n);
149 dTree.prototype.noderr = function(node, nodeId) {
150 var str = '<div class="dTreeNode">' + this.indent(node, nodeId);
151 str += '<div class="emptynode" title="Empty"></div></div>';
155 // Creates the node icon, url and text
156 dTree.prototype.node = function(node, nodeId) {
158 jsfnc='onmouseover="'+this.obj+'.show_feat(\''+nodeId+'\');" onmousedown="return indexmenu_checkcontextm(\''+nodeId+'\','+this.obj+',event);" oncontextmenu="return indexmenu_stopevt(event)"';
159 if (node._lv > this.config.maxjs) {h=0;} else {node._ok=true;}
160 str = '<div class="dTreeNode">' + this.indent(node, nodeId);
161 node.icon = (this.root.id == node.pid) ? this.icon.root : ((node.hns) ? this.icon.folderH : ((node._hc) ? this.icon.folder : this.icon.node));
162 node.iconOpen = (node._hc) ? ((node.hns) ? this.icon.folderHOpen : this.icon.folderOpen) : this.icon.node;
163 if (this.root.id == node.pid) {
164 node.icon = this.icon.root;
165 node.iconOpen = this.icon.root;
167 str += '<img id="i' + this.obj + nodeId + '" src="' + ((node._io) ? node.iconOpen : node.icon) + '" alt="" />';
168 if (!node._hc || node.hns) {
169 str += '<a id="s' + this.obj + nodeId + '" class="' + ((node._cp) ? 'navSel' : ((node._is) ? 'nodeSel' : (node._hc) ? 'nodeFdUrl' : 'nodeUrl')) ;
170 str += '" href="' + this.config.urlbase;
171 (node.hns) ? str +=node.hns : str += node.dokuid;
172 str += '"' + ' title="' + node.name + '"' +jsfnc;
173 str += ' onclick="javascript: ' + this.obj + '.s(' + nodeId + ');"';
174 str += '>'+node.name+'</a>';
176 else if (node.pid != this.root.id) {
177 str += '<a id="s' + this.obj + nodeId + '" href="javascript: ' + this.obj + '.o(' + nodeId + '); " class="node"' + jsfnc + '>'+node.name+'</a>';
183 str += '<div id="d' + this.obj + nodeId + '" class="clip" style="display:' + ((this.root.id == node.pid || node._io) ? 'block' : 'none') + ';">';
184 if (h) {str += this.addNode(node);}
191 // Adds the empty and line icons
192 dTree.prototype.indent = function(node, nodeId) {
194 if (this.root.id != node.pid) {
195 for (n=0; n<this.aIndent.length; n++) {
196 str += '<img src="' + ( (this.aIndent[n] == 1) ? this.icon.line : this.icon.empty ) + '" alt="" />';
199 this.aIndent.push(0);
201 this.aIndent.push(1);
204 str += '<a href="javascript: ' + this.obj + '.o(' + nodeId+');"><img id="j' + this.obj + nodeId + '" src="';
205 str += ( (node._io) ? ((node._ls) ? this.icon.minusBottom : this.icon.minus) : ((node._ls) ? this.icon.plusBottom : this.icon.plus ) );
206 str += '" alt="" /></a>';
207 } else {str += '<img src="' + ((node._ls) ? this.icon.joinBottom : this.icon.join) + '" alt="" />';}
212 // Checks if a node has any children and if it is the last sibling
213 dTree.prototype.setCS = function(node) {
215 for (n=0; n<this.aNodes.length; n++) {
216 if (this.aNodes[n].pid == node.id) {node._hc = true;}
217 if (this.aNodes[n].pid == node.pid) {lastId = this.aNodes[n].id;}
219 if (lastId==node.id) {node._ls = true;}
222 // Returns the selected node
223 dTree.prototype.getSelected = function() {
224 var sn = this.getCookie('cs' + this.obj);
225 return (sn) ? sn : null;
228 // Highlights the selected node
229 dTree.prototype.s = function(id) {
230 var eOld,eNew,cn = this.aNodes[id];
231 if (this.selectedNode != id) {
232 eNew = $("s" + this.obj + id);
233 if (!eNew ) {return;}
234 if (this.selectedNode || this.selectedNode===0) {
235 eOld = $("s" + this.obj + this.selectedNode);
236 eOld.className = "node";
238 eNew.className = "nodeSel";
239 this.selectedNode = id;
240 if (this.config.useCookies) {this.setCookie('cs' + this.obj, cn.id);}
244 // Toggle Open or close
245 dTree.prototype.o = function(id) {
246 var cn = this.aNodes[id];
247 this.nodeStatus(!cn._io, id, cn._ls);
249 if (this.config.useCookies) {this.updateCookie();}
250 this.divdisplay('z',0);
251 this.resizescroll("block");
254 // Open or close all nodes
255 dTree.prototype.oAll = function(status) {
256 for (var n=0; n<this.aNodes.length; n++) {
257 if (this.aNodes[n]._hc && this.aNodes[n].pid != this.root.id) {
258 this.nodeStatus(status, n, this.aNodes[n]._ls);
259 this.aNodes[n]._io = status;
262 if (this.config.useCookies) {this.updateCookie();}
265 // Opens the tree to a specific node
266 dTree.prototype.openTo = function(nId, bSelect, bFirst) {
269 for (n=0; n<this.aNodes.length; n++) {
270 if (this.aNodes[n].id == nId) {
276 this.fill(this.aNodes[nId].pid);
278 if (cn.pid==this.root.id || !cn._p) {return;}
280 if (this.completed && cn._hc) {this.nodeStatus(true, cn._ai, cn._ls);}
282 (this.completed) ? this.s(cn._ai) : this._sn=cn._ai;
284 this.openTo(cn._p._ai, false, true);
287 dTree.prototype.getOpenTo = function(nodes) {
290 } else if (!this.config.useCookies ||!this.getCookie('co' + this.obj)) {
291 for (var n=0; n<nodes.length; n++) {
292 this.openTo(nodes[n],false,true);
297 // Change the status of a node(open or closed)
298 dTree.prototype.nodeStatus = function(status, id, bottom) {
299 if (status && !this.fill(id)) {return;}
301 eJoin= $('j' + this.obj + id);
302 eIcon= $('i' + this.obj + id);
303 eIcon.src = (status) ? this.aNodes[id].iconOpen : this.aNodes[id].icon;
304 eJoin.src = ((status)?((bottom)?this.icon.minusBottom:this.icon.minus):((bottom)?this.icon.plusBottom:this.icon.plus));
305 $('d' + this.obj + id).style.display = (status) ? 'block': 'none';
308 // [Cookie] Clears a cookie
309 dTree.prototype.clearCookie = function() {
312 yday = new Date(now.getTime() - 1000 * 60 * 60 * 24);
313 this.setCookie('co'+this.obj, 'cookieValue', yday);
314 this.setCookie('cs'+this.obj, 'cookieValue', yday);
317 // [Cookie] Sets value in a cookie
318 dTree.prototype.setCookie = function(cookieName, cookieValue, expires, path, domain, secure) {
320 escape(cookieName) + '=' + escape(cookieValue) +
321 (expires ? '; expires=' + expires.toGMTString() : '') +
323 (domain ? '; domain=' + domain : '') +
324 (secure ? '; secure' : '');
327 // [Cookie] Gets a value from a cookie
328 dTree.prototype.getCookie = function(cookieName) {
329 var cookieValue = '',pN,posValue,endPos;
330 pN = document.cookie.indexOf(escape(cookieName) + '=');
332 posValue = pN + (escape(cookieName) + '=').length;
333 endPos = document.cookie.indexOf(';', posValue);
334 if (endPos != -1) {cookieValue = unescape(document.cookie.substring(posValue, endPos));}
335 else {cookieValue = unescape(document.cookie.substring(posValue));}
337 return (cookieValue);
340 // [Cookie] Returns ids of open nodes as a string
341 dTree.prototype.updateCookie = function() {
343 for (n=0; n<this.aNodes.length; n++) {
344 if (this.aNodes[n]._io && this.aNodes[n].pid != this.root.id) {
345 if (str) {str += '.';}
346 str += this.aNodes[n].id;
349 this.setCookie('co' + this.obj, str);
352 // [Cookie] Checks if a node id is in a cookie
353 dTree.prototype.isOpen = function(id) {
354 var n,aOpen = this.getCookie('co' + this.obj).split('.');
355 for (n=0; n<aOpen.length; n++){
356 if (aOpen[n] == id) {return true;}
361 dTree.prototype.openCurNS = function (max){
362 var r,cn,match,t,i,n,cnsa,cna,cns=this.pageid;
363 r=new RegExp ("\\b"+this.config.sepchar+"\\b","g");
364 match=cns.match(r)||-1;
365 if (max>0 && match.length >= max) {
366 t = cns.split(this.config.sepchar);
367 n = (this.aNodes[0].dokuid == '') ? 0 :this.aNodes[0].dokuid.split(this.config.sepchar).length;
368 t.splice(max + n,t.length);
369 cnsa=t.join(this.config.sepchar);
371 for (i=0; i<this.aNodes.length; i++){
373 if (cns == cn.dokuid || cns == cn.hns) {
374 this.openTo(cn.id,false,true);
377 addInitEvent(this.scroll("l",4,cn.pid,1));
381 if (cnsa == cn.dokuid || cnsa == cn.hns) {
386 if (cna) {this.openTo(cna.id,false,true);}
389 dTree.prototype.fill = function(id) {
390 if (id == -1 || this.aNodes[id]._ok ) {return true;}
391 var n=id,eLoad,node,a,rd,ln,eDiv;
392 if (this.aNodes[n].ajax) {
393 eLoad=$('l' + this.obj);
394 node=$('s'+this.obj+n);
395 if (!eLoad) {eLoad=indexmenu_createPicker('l' + this.obj);}
396 eLoad.innerHTML='Loading ...';
397 $('s'+this.obj+n).parentNode.appendChild(eLoad);
398 eLoad.style.width='auto';
399 eLoad.style.display='inline';
404 while (!this.aNodes[n]._ok) {
406 n=this.aNodes[n].pid;
408 for (ln=rd.length-1; ln>=0; ln--) {
411 eDiv=$('d' + this.obj + id);
412 if (!eDiv) {return false;}
417 this.aIndent.unshift(0);
419 this.aIndent.unshift(1);
423 eDiv.innerHTML=this.addNode(a);
429 dTree.prototype.openCookies = function() {
430 var n,cn,aOpen = this.getCookie('co' + this.obj).split('.');
431 for (n=0; n<aOpen.length; n++){
432 if (aOpen[n] === "") {break;}
433 cn = this.aNodes[aOpen[n]];
434 if (!cn._ok) {this.nodeStatus(true, aOpen[n], cn._ls);cn._io = 1;}
438 dTree.prototype.scroll = function (where,s,n,i){
439 if (!this.config.scroll) {return false;}
440 var w,dtree,dtreel,nodeId;
441 dtree=$('dtree_'+this.obj);
442 dtreel=parseInt(dtree.offsetLeft,0);
444 $('left_'+this.obj).style.border="thin inset";
445 this.scrollRight(dtreel,s);
447 nodeId=$('s'+this.obj+n);
448 w = parseInt(dtree.parentNode.offsetWidth - nodeId.offsetWidth - nodeId.offsetLeft,0);
449 if (this.config.toc) {w=w-11;}
450 if (dtreel <= w) {return;}
451 this.resizescroll("none");
453 this.scrollLeft(dtreel,s,w-3,i);
457 dTree.prototype.scrollLeft = function (lft,s,w,i){
458 if(lft < w - i -10) {
459 this.divdisplay('z',0);
464 $('dtree_'+self.obj).style.left = lft + "px";
465 this.scrllTmr=setTimeout(function (){self.scrollLeft(lft - s,s+i,w,i);},20);
469 dTree.prototype.scrollRight = function (lft,s){
471 this.divdisplay('left_',0);
476 $('dtree_'+self.obj).style.left = lft + "px";
478 this.scrllTmr=setTimeout(function (){self.scrollRight(lft+s,s+1);},20);
481 dTree.prototype.stopscroll = function (){
482 $('left_'+this.obj).style.border="none";
483 clearTimeout(this.scrllTmr);
487 dTree.prototype.show_feat = function (n){
488 var w,div,id,dtree,dtreel,self,node=$('s'+this.obj+n);
490 if (this.config.toc && node.className != "node") {
492 id =(this.aNodes[n].hns) ? this.aNodes[n].hns : this.aNodes[n].dokuid;
493 div.onmousedown=function (){indexmenu_createTocMenu('req=toc&id='+decodeURIComponent(id),'picker_'+self.obj,'t'+self.obj);};
494 node.parentNode.appendChild(div);
495 if (div.style.display=="none") {div.style.display="inline";}
497 if (this.config.scroll) {
499 div.onmouseover=function(){div.style.border="none";self.scroll("l",1,n,0);};
500 div.onmousedown=function(){div.style.border="thin inset";self.scroll("l",4,n,1);};
501 div.onmouseout=function(){div.style.border="none";self.stopscroll();};
502 div.onmouseup=div.onmouseover;
503 dtree=$('dtree_'+this.obj);
504 dtreel=parseInt(dtree.offsetLeft,0);
505 w = parseInt(dtree.parentNode.offsetWidth - node.offsetWidth - node.offsetLeft + 1,0);
507 div.style.display="none";
508 div.style.top = node.offsetTop+"px";
509 div.style.left = parseInt(node.offsetLeft + node.offsetWidth + w - 12,0)+"px";
510 div.style.display="block";
515 dTree.prototype.resizescroll = function (status){
516 var dtree,w,h,left=$('left_'+this.obj);
518 if (left.style.display==status) {
519 dtree=$('dtree_'+this.obj);
520 w=parseInt(dtree.offsetHeight/3,0);
521 h= parseInt(w/50,0)*50;
523 left.style.height=h+"px";
524 left.style.top = w+"px";
525 if (status=="none") {left.style.display="block";}
529 // Toggle Open or close
530 dTree.prototype.getAjax = function(n) {
531 var node,req,curns,selft=this;
532 node=selft.aNodes[n];
533 // We use SACK to do the AJAX requests
534 var Ajax = new sack(DOKU_BASE+'lib/plugins/indexmenu/ajax.php');
535 req='req=index&idx='+node.dokuid+decodeURIComponent(this.config.jsajax);
536 curns=this.pageid.substring(0,this.pageid.lastIndexOf(this.config.sepchar));
537 Ajax.encodeURIString=false;
538 Ajax.onCompletion = function(){
539 var i,ajxnodes,ajxnode,plus;
540 plus=selft.aNodes.length -1;
542 if (!isArray(ajxnodes) || ajxnodes.length < 1) {
543 ajxnodes=[['', 1, 0, '', 0, 1, 0]];
546 for(i=0;i<ajxnodes.length;i++){
548 ajxnode[2]=(ajxnode[2]==0) ? node.id : ajxnode[2] + plus;
550 selft.add(ajxnode[0],ajxnode[1],ajxnode[2],ajxnode[3],ajxnode[4],ajxnode[5],ajxnode[6]);
556 selft.openTo(node.id,false,true);
558 $('l'+selft.obj).style.display='none';
561 req +='&nss='+curns+'&max=1';
563 Ajax.encodeURIString = false;
564 Ajax.runAJAX(encodeURI(req));
568 dTree.prototype.loadCss = function() {
569 var oLink = document.createElement("link");
570 oLink.href = this.config.plugbase+'/images/'+this.config.theme+'/style.css';
571 oLink.rel = "stylesheet";
572 oLink.type = "text/css";
573 document.getElementsByTagName('head')[0].appendChild(oLink);
577 dTree.prototype.contextmenu = function(n,e) {
578 var li,id,html,type,node,self,cmenu,cdtree,rmenu,X=0,Y=0,i;
579 cdtree= $("cdtree_" + this.obj);
580 rmenu=$('r' + this.obj);
581 if(!rmenu) { return true; }
582 indexmenu_mouseposition(rmenu,e);
583 cmenu=window.indexmenu_contextmenu[0];
584 node =this.aNodes[n];
586 rmenu.innerHTML='<div class="indexmenu_rmenuhead" title="'+node.name+'">'+node.name+"</div>";
587 for (i=0; i<cmenu.length; i++,i++,i++,i++) {
588 if (((!node._hc || node.hns) && !cmenu[i+2])||(node._hc && !node.hns && !cmenu[i+3])) {continue;}
592 id =(node.hns) ? node.hns : node.dokuid;
593 html='<a title="'+cmenu[i]+'" href="'+eval(cmenu[i+1])+'">'+html+'</a>';
596 rmenu.appendChild(document.createElement('ul'));
598 li=document.createElement(type);
600 rmenu.lastChild.appendChild(li);
602 rmenu.style.display='inline';
606 dTree.prototype.divdisplay = function(obj,v) {
607 var o=$(obj+this.obj);
608 if (!o) {return false;}
609 (v) ? o.style.display='inline': o.style.display='none' ;
612 dTree.prototype.init = function(s,c,n,nav,max) {
613 if (s) {this.loadCss();}
614 if (!c) {this.openCookies();}
615 if (n) {this.getOpenTo(n.split(" "));}
616 if (nav) {this.openCurNS(max);}
617 if (window.indexmenu_contextmenu) {
619 indexmenu_createPicker('r'+ this.obj,'indexmenu_rmenu '+this.config.theme);
620 $('r'+ this.obj).oncontextmenu=indexmenu_stopevt;
621 addEvent(document, 'click', function() {self.divdisplay('r',0);});
625 // If Push and pop is not implemented by the browser
626 if (!Array.prototype.push) {
627 Array.prototype.push = function array_push() {
628 for(var i=0;i<arguments.length;i++){
629 this[this.length]=arguments[i];
634 if (!Array.prototype.pop) {
635 Array.prototype.pop = function array_pop() {
636 var lstEl = this[this.length-1];
637 this.length = Math.max(this.length-1,0);