dokuwiki-indexmenu-plugin – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /*--------------------------------------------------------|
2 | dTree 2.05 | www.destroydrop.com/javascript/tree/ |
3 |--------------------------------------------------------|
4 | Copyright (c) 2002-2003 Geir Landro |
5 | |
6 | This script can be used freely as long as all |
7 | copyright messages are intact. |
8 | |
9 | Updated: 17.04.2003 |
10 |--------------------------------------------------------|
11 | Modified for Dokuwiki by |
12 | Samuele Tognini <samuele@samuele.netsons.org> |
13 | under GPL 2 license |
14 | (http://www.gnu.org/licenses/gpl.html) |
15 | Updated: 29.08.2009 |
16 |--------------------------------------------------------|
17 | Modified for Dokuwiki by |
18 | Rene Hadler <rene.hadler@iteas.at> |
19 | under GPL 2 license |
20 | (http://www.gnu.org/licenses/gpl.html) |
21 | Updated: 07.08.2012 |
22 |--------------------------------------------------------|
23 | jQuery update - 27 02 2012 |
24 | Gerrit Uitslag <klapinklapin@gmail.com |
25 |--------------------------------------------------------|
26 | indexmenu | https://www.dokuwiki.org/plugin:indexmenu |
27 |-------------------------------------------------------*/
28  
29 /**
30 * dTreeNode object
31 *
32 * @param {string} dokuid page id of node
33 * @param {number} id node id
34 * @param {number} pid parent node id
35 * @param {string} name Page Title
36 * @param {number|string} hns page id of headpage of namespace
37 * @param {number} isdir is directory?
38 * @param {number} ajax load subnodes by ajax
39 * @constructor
40 */
41 function dTreeNode(dokuid, id, pid, name, hns, isdir, ajax) {
42 this.dokuid = dokuid; // page id of node
43 this.id = id; // id number of node
44 this.pid = pid; // id number of parent node
45 this.name = name; // ns/page title
46 this.hns = hns; // headpage of namespace or zero
47 this.isdir = isdir; // is directory
48 this.ajax = ajax; // load its nodes by ajax
49 this._io = 0; // is node open
50 this._is = false; // is selected
51 this._ls = false; // is last sibling
52 this._hc = ajax; // has children
53 this._ai = 0; // id number of first child....
54 this._p = false; // parent dTreeNode
55 this._lv = 0; // level
56 this._ok = false; // all children are loaded
57 this._cp = false; // current page
58 this.icon = ''; // icon of closed node
59 this.iconOpen = ''; // icon of opened node
60 }
61  
62 /**
63 * Tree object
64 *
65 * @param {string} objName id of the indexmenu, has form 'indexmenu_<identifier>'
66 * @param {string} theme name of theme dir
67 * @constructor
68 */
69 function dTree(objName, theme) {
70 var objExt = IndexmenuUtils.determineExtension(theme);
71 this.config = {
72 urlbase: DOKU_BASE + 'doku.php?id=', // base of dokuwiki (set in page)
73 plugbase: DOKU_BASE + 'lib/plugins/indexmenu', // base of plugin folder
74 useCookies: true, // use cookies (set in page) e.g. disabled for context option
75 scroll: true, // enable scrolling of tree in too small columns (set in page)
76 toc: true, // enable ToC popups in tree (set in page)
77 scorespace: false, // replace underscores with space
78 maxjs: 1, // number set by maxjs option (set in page)
79 jsajax: '', // &max=#&sort=(t|d)&msort=(indexmenu_n|<metakey>)&rsort=1&nsort=1&hsort=1&nopg=1&skipns=+=/.../&skipfile=+=/.../(set in page)
80 sepchar: ':', // value ':', ';' or '/' (set in page)
81 theme: theme // dir name of theme folder
82 };
83 var objImg = this.config.plugbase + '/images/' + theme + '/';
84 this.icon = {
85 root: objImg + 'base.' + objExt,
86 folder: objImg + 'folder.' + objExt,
87 folderH: objImg + 'folderh.' + objExt,
88 folderOpen: objImg + 'folderopen.' + objExt,
89 folderHOpen: objImg + 'folderhopen.' + objExt,
90 node: objImg + 'page.' + objExt,
91 empty: objImg + 'empty.' + objExt,
92 line: objImg + 'line.' + objExt,
93 join: objImg + 'join.' + objExt,
94 joinBottom: objImg + 'joinbottom.' + objExt,
95 plus: objImg + 'plus.' + objExt,
96 plusBottom: objImg + 'plusbottom.' + objExt,
97 minus: objImg + 'minus.' + objExt,
98 minusBottom: objImg + 'minusbottom.' + objExt,
99 nlPlus: objImg + 'nolines_plus.' + objExt,
100 nlMinus: objImg + 'nolines_minus.' + objExt
101 };
102 this.obj = objName; // id of this indexmenu
103 this.aNodes = []; // array of nodes
104 this.aIndent = []; // array stores the indents of the tree (contains values 0 or 1)
105 this.root = new dTreeNode(false, -1);
106 this.selectedNode = null; // node id
107 this.selectedFound = false; // set to true when found
108 this.completed = false; // succesfull written js tree to the page
109 this.scrllTmr = 0; // store timer for horizontal scrolling the page
110 this.pageid = JSINFO.id || ''; // current page
111 this.fajax = false; // if retrieve next level of opened nodes
112 }
113 /**
114 * CSS classes:
115 *
116 * a.nodeFdUrl Namespace with url link (headpage)
117 * a.node Namespace without url link
118 * a.nodeUrl Page
119 * a.nodeSel Last visited page
120 * a.navSel Current page
121 */
122  
123  
124 /**
125 * Adds a new node to the node array
126 *
127 * @param {string} dokuid page id of node
128 * @param {number} id node id
129 * @param {number} pid parent node id
130 * @param {string} name Page Title
131 * @param {number|string} hns page id of headpage of namespace
132 * @param {number} isdir is directory?
133 * @param {number} ajax load subnodes by ajax
134 */
135 dTree.prototype.add = function (dokuid, id, pid, name, hns, isdir, ajax) {
136 this.aNodes[this.aNodes.length] = new dTreeNode(dokuid, id, pid, name, hns, isdir, ajax);
137 };
138  
139 /**
140 * Open all nodes, if no node status was stored in cookie
141 */
142 dTree.prototype.openAll = function () {
143 if (!this.getCookie('co' + this.obj)) {
144 this.oAll(true);
145 }
146 };
147  
148 /**
149 * Outputs the tree to the page. Called by document.write after adding the nodes to the tree.
150 *
151 * @returns {string} html of whole tree
152 */
153 dTree.prototype.toString = function () {
154 var str = '';
155 this.pageid = this.pageid.replace(/:/g,this.config.sepchar);
156 if (this.config.scroll) {
157 str += '<div id="cdtree_' + this.obj + '" class="dtree" style="position:relative;overflow:hidden;width:100%;">';
158 }
159 str += '<div id="dtree_' + this.obj + '" class="dtree ' + this.config.theme + '" style="overflow:';
160 if (this.config.scroll) {
161 str += 'visible;position:relative;width:100%"';
162 } else {
163 str += 'hidden;"';
164 }
165 str += '>';
166 if (jQuery('#dtree_' + this.obj)[0]) {
167 str += '<div class="error">Indexmenu id conflict</div>';
168 }
169 if (this.config.toc) {
170 str += '<div id="t' + this.obj + '" class="indexmenu_tocbullet ' + this.config.theme + '" style="display:none;" title="Table of contents"></div>';
171 str += '<div id="toc_' + this.obj + '" style="display:none;"></div>';
172 }
173 if (this.config.useCookies) {
174 this.selectedNode = this.getSelected();
175 }
176 str += this.addNode(this.root) + '</div>';
177 if (this.config.scroll) {
178 str += '<div id="z' + this.obj + '" class="indexmenu_rarrow"></div>';
179 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>';
180 str += '</div>';
181 }
182 this.completed = true;
183 //hide the fallback nojs indexmenu
184 jQuery('#nojs_' + this.obj).css("display", "none"); //using .hide(); let's crash opera
185 return str;
186 };
187  
188 /**
189 * Creates the tree structure
190 *
191 * @param {dTreeNode} pNode
192 * @returns {string} html of node (inclusive children)
193 */
194 dTree.prototype.addNode = function (pNode) {
195 var str = '', cn, n = pNode._ai, l = pNode._lv + 1;
196 for (n; n < this.aNodes.length; n++) {
197 if (this.aNodes[n].pid == pNode.id) {
198 cn = this.aNodes[n];
199 cn._p = pNode;
200 cn._ai = n;
201 cn._lv = l;
202 this.setCS(cn);
203 if (cn._hc && !cn._io && this.config.useCookies) {
204 cn._io = this.isOpen(cn.id);
205 }
206 if (this.pageid == (!cn.hns && cn.dokuid || cn.hns)) {
207 cn._cp = true;
208 } else if (cn.id == this.selectedNode && !this.selectedFound) {
209 cn._is = true;
210 this.selectedNode = n;
211 this.selectedFound = true;
212 }
213 if (!cn._hc && cn.isdir && !cn.ajax && !cn.hns) {
214 if (cn._ls) {
215 str += this.noderr(cn, n);
216 }
217 } else {
218 str += this.node(cn, n);
219 }
220 if (cn._ls) {
221 break;
222 }
223 }
224 }
225 return str;
226 };
227  
228 /**
229 * Create empty node
230 *
231 * @param {dTreeNode} node
232 * @param {int} nodeId
233 * @returns {string} html of empty node
234 */
235 dTree.prototype.noderr = function (node, nodeId) {
236 var str = '<div class="dTreeNode">' + this.indent(node, nodeId);
237 str += '<div class="emptynode" title="Empty"></div></div>';
238 return str;
239 };
240  
241 /**
242 * Creates the node icon, url and text
243 *
244 * @param {dTreeNode} node
245 * @param {int} nodeId
246 * @returns {string} html of node (inclusive children)
247 */
248 dTree.prototype.node = function (node, nodeId) {
249 var h = 1, jsfnc, str;
250 jsfnc = 'onmouseover="' + this.obj + '.show_feat(\'' + nodeId + '\');" onmousedown="return IndexmenuContextmenu.checkcontextm(\'' + nodeId + '\',' + this.obj + ',event);" oncontextmenu="return IndexmenuContextmenu.stopevt(event)"';
251 if (node._lv > this.config.maxjs) {
252 h = 0;
253 } else {
254 node._ok = true;
255 }
256 str = '<div class="dTreeNode">' + this.indent(node, nodeId);
257 node.icon = (this.root.id == node.pid) ? this.icon.root : ((node.hns) ? this.icon.folderH : ((node._hc) ? this.icon.folder : this.icon.node));
258 node.iconOpen = (node._hc) ? ((node.hns) ? this.icon.folderHOpen : this.icon.folderOpen) : this.icon.node;
259 if (this.root.id == node.pid) {
260 node.icon = this.icon.root;
261 node.iconOpen = this.icon.root;
262 }
263 str += '<img id="i' + this.obj + nodeId + '" src="' + ((node._io) ? node.iconOpen : node.icon) + '" alt="" />';
264 if (!node._hc || node.hns) {
265 str += '<a id="s' + this.obj + nodeId + '" class="' + ((node._cp) ? 'navSel' : ((node._is) ? 'nodeSel' : (node._hc) ? 'nodeFdUrl' : 'nodeUrl'));
266 str += '" href="' + this.config.urlbase;
267 (node.hns) ? str += node.hns : str += node.dokuid;
268 str += '"' + ' title="' + node.name + '"' + jsfnc;
269 str += ' onclick="javascript: ' + this.obj + '.s(' + nodeId + ');"';
270 if(this.config.scorespace)
271 str += '>' + node.name.replace(/_/g, " ") + '</a>';
272 else
273 str += '>' + node.name + '</a>';
274 }
275 else if (node.pid != this.root.id) {
276 if(this.config.scorespace)
277 str += '<a id="s' + this.obj + nodeId + '" href="javascript: ' + this.obj + '.o(' + nodeId + '); " class="node"' + jsfnc + '>' + node.name.replace(/_/g, " ") + '</a>';
278 else
279 str += '<a id="s' + this.obj + nodeId + '" href="javascript: ' + this.obj + '.o(' + nodeId + '); " class="node"' + jsfnc + '>' + node.name + '</a>';
280 } else {
281 if(this.config.scorespace)
282 str += node.name.replace(/_/g, " ");
283 else
284 str += node.name;
285 }
286 str += '</div>';
287 if (node._hc) {
288 str += '<div id="d' + this.obj + nodeId + '" class="clip" style="display:' + ((this.root.id == node.pid || node._io) ? 'block' : 'none') + ';">';
289 if (h) {
290 str += this.addNode(node);
291 }
292 str += '</div>';
293 }
294 this.aIndent.pop();
295 return str;
296 };
297  
298 /**
299 * Adds the empty and line icons which indent the node
300 *
301 * @param {dTreeNode} node
302 * @param {int} nodeId
303 * @returns {string} html of indent icons
304 */
305 dTree.prototype.indent = function (node, nodeId) {
306 var n, str = '';
307 if (this.root.id != node.pid) {
308 for (n = 0; n < this.aIndent.length; n++) {
309 str += '<img src="' + ( (this.aIndent[n] == 1) ? this.icon.line : this.icon.empty ) + '" alt="" />';
310 }
311 if (node._ls) {
312 this.aIndent.push(0);
313 } else {
314 this.aIndent.push(1);
315 }
316 if (node._hc) {
317 str += '<a href="javascript: ' + this.obj + '.o(' + nodeId + ');"><img id="j' + this.obj + nodeId + '" src="';
318 str += ( (node._io) ? ((node._ls) ? this.icon.minusBottom : this.icon.minus) : ((node._ls) ? this.icon.plusBottom : this.icon.plus ) );
319 str += '" alt="" /></a>';
320 } else {
321 str += '<img src="' + ((node._ls) ? this.icon.joinBottom : this.icon.join) + '" alt="" />';
322 }
323 }
324 return str;
325 };
326  
327 /**
328 * Checks if a node has any children and if it is the last sibling
329 *
330 * @param {dTreeNode} node
331 */
332 dTree.prototype.setCS = function (node) {
333 var lastId, n;
334 for (n = 0; n < this.aNodes.length; n++) {
335 if (this.aNodes[n].pid == node.id) {
336 node._hc = true;
337 }
338 if (this.aNodes[n].pid == node.pid) {
339 lastId = this.aNodes[n].id;
340 }
341 }
342 if (lastId == node.id) {
343 node._ls = true;
344 }
345 };
346  
347 /**
348 * Returns the selected node as stored in cookie
349 *
350 * @returns {int} node id
351 */
352 dTree.prototype.getSelected = function () {
353 var sn = this.getCookie('cs' + this.obj);
354 return (sn) ? parseInt(sn, 10) : null;
355 };
356  
357 /**
358 * Highlights the selected node
359 *
360 * @param {int} id node id
361 */
362 dTree.prototype.s = function (id) {
363 var eOld, eNew, cn = this.aNodes[id];
364 if (this.selectedNode != id) {
365 eNew = jQuery("#s" + this.obj + id)[0];
366 if (!eNew) {
367 return;
368 }
369 if (this.selectedNode || this.selectedNode === 0) {
370 eOld = jQuery("#s" + this.obj + this.selectedNode)[0];
371 eOld.className = "node";
372 }
373 eNew.className = "nodeSel";
374 this.selectedNode = id;
375 if (this.config.useCookies) {
376 this.setCookie('cs' + this.obj, cn.id);
377 }
378 }
379 };
380  
381 /**
382 * Toggle Open or close
383 *
384 * @param {int} id node id
385 */
386 dTree.prototype.o = function (id) {
387 var cn = this.aNodes[id];
388 this.nodeStatus(!cn._io, id, cn._ls);
389 cn._io = !cn._io;
390 if (this.config.useCookies) {
391 this.updateCookie();
392 }
393 // scroll
394 this.divdisplay('z', 0);
395 this.resizescroll("block");
396 };
397  
398 /**
399 * Open or close all nodes
400 *
401 * @param {boolean} status if true open
402 */
403 dTree.prototype.oAll = function (status) {
404 for (var n = 0; n < this.aNodes.length; n++) {
405 if (this.aNodes[n]._hc && this.aNodes[n].pid != this.root.id) {
406 this.nodeStatus(status, n, this.aNodes[n]._ls);
407 this.aNodes[n]._io = status;
408 }
409 }
410 if (this.config.useCookies) {
411 this.updateCookie();
412 }
413 };
414  
415 /**
416 * Opens the tree to a specific node
417 *
418 * @param {number} nId node id
419 * @param {boolean} bSelect
420 * @param {boolean} bFirst
421 */
422 dTree.prototype.openTo = function (nId, bSelect, bFirst) {
423 var n, cn;
424 if (!bFirst) {
425 for (n = 0; n < this.aNodes.length; n++) {
426 if (this.aNodes[n].id == nId) {
427 nId = n;
428 break;
429 }
430 }
431 }
432 this.fill(this.aNodes[nId].pid);
433 cn = this.aNodes[nId];
434 if (cn.pid == this.root.id || !cn._p) {
435 return;
436 }
437 cn._io = 1;
438 if (this.completed && cn._hc) {
439 this.nodeStatus(true, cn._ai, cn._ls);
440 }
441 if (cn._is) {
442 (this.completed) ? this.s(cn._ai) : this._sn = cn._ai;
443 }
444 this.openTo(cn._p._ai, false, true);
445 };
446  
447 /**
448 * Open the given nodes, if no node status is already stored
449 *
450 * @param {Array|string} nodes array of nodes to open or empty string to open all nodes
451 */
452 dTree.prototype.getOpenTo = function (nodes) {
453 if (nodes === '') {
454 this.openAll();
455 } else if (!this.config.useCookies || !this.getCookie('co' + this.obj)) {
456 for (var n = 0; n < nodes.length; n++) {
457 this.openTo(nodes[n], false, true);
458 }
459 }
460 };
461  
462 /**
463 * Change the status of a node(open or closed)
464 *
465 * @param {boolean} status true if open
466 * @param {int} id node id
467 * @param {boolean} bottom true if bottom node
468 */
469 dTree.prototype.nodeStatus = function (status, id, bottom) {
470 if (status && !this.fill(id)) {
471 return;
472 }
473 var eJoin, eIcon;
474 eJoin = jQuery('#j' + this.obj + id)[0];
475 eIcon = jQuery('#i' + this.obj + id)[0];
476 eIcon.src = (status) ? this.aNodes[id].iconOpen : this.aNodes[id].icon;
477 eJoin.src = ((status) ? ((bottom) ? this.icon.minusBottom : this.icon.minus) : ((bottom) ? this.icon.plusBottom : this.icon.plus));
478 jQuery('#d' + this.obj + id)[0].style.display = (status) ? 'block' : 'none';
479 };
480  
481 /**
482 * [Cookie] Clears a cookie
483 */
484 dTree.prototype.clearCookie = function () {
485 var now, yday;
486 now = new Date();
487 yday = new Date(now.getTime() - 1000 * 60 * 60 * 24);
488 this.setCookie('co' + this.obj, 'cookieValue', yday);
489 this.setCookie('cs' + this.obj, 'cookieValue', yday);
490 };
491  
492 /**
493 * [Cookie] Sets value in a cookie
494 *
495 * @param {string} cookieName
496 * @param {string} cookieValue
497 * @param {Date} expires
498 * @param {string} path
499 * @param {string} domain
500 * @param {boolean} secure
501 */
502 dTree.prototype.setCookie = function (cookieName, cookieValue, expires, path, domain, secure) {
503 document.cookie =
504 encodeURIComponent(cookieName) + '=' + encodeURIComponent(cookieValue) +
505 (expires ? '; expires=' + expires.toUTCString() : '') +
506 ';path=/' + (domain ? '; domain=' + domain : '') +
507 (secure ? '; secure' : '');
508 };
509  
510 /**
511 * [Cookie] Gets a value from a cookie
512 *
513 * @param cookieName
514 * @returns {string}
515 */
516 dTree.prototype.getCookie = function (cookieName) {
517 var cookieValue = '', pN, posValue, endPos;
518 pN = document.cookie.indexOf(encodeURIComponent(cookieName) + '=');
519 if (pN != -1) {
520 posValue = pN + (encodeURIComponent(cookieName) + '=').length;
521 endPos = document.cookie.indexOf(';', posValue);
522 if (endPos != -1) {
523 cookieValue = decodeURIComponent(document.cookie.substring(posValue, endPos));
524 }
525 else {
526 cookieValue = decodeURIComponent(document.cookie.substring(posValue));
527 }
528 }
529 return (cookieValue);
530 };
531  
532 /**
533 * [Cookie] Stores ids of open nodes as a string in cookie
534 */
535 dTree.prototype.updateCookie = function () {
536 var str = '', n;
537 for (n = 0; n < this.aNodes.length; n++) {
538 if (this.aNodes[n]._io && this.aNodes[n].pid != this.root.id) {
539 if (str) {
540 str += '.';
541 }
542 str += this.aNodes[n].id;
543 }
544 }
545 this.setCookie('co' + this.obj, str);
546 };
547  
548 /**
549 * [Cookie] Checks if a node id is in the cookie
550 *
551 * @param {int} id node id
552 * @return {Boolean} if open true
553 */
554 dTree.prototype.isOpen = function (id) {
555 var n, aOpen = this.getCookie('co' + this.obj).split('.');
556 for (n = 0; n < aOpen.length; n++) {
557 if (aOpen[n] == id) {
558 return true;
559 }
560 }
561 return false;
562 };
563  
564 /**
565 * Open the node of the current namespace
566 *
567 * @param {int} max
568 */
569 dTree.prototype.openCurNS = function (max) {
570 var r, cn, match, t, i, n, cnsa, cna;
571 var cns = this.pageid;
572 r = new RegExp("\\b" + this.config.sepchar + "\\b", "g");
573 match = cns.match(r) || -1;
574 if (max > 0 && match.length >= max) {
575 t = cns.split(this.config.sepchar);
576 n = (this.aNodes[0].dokuid == '') ? 0 : this.aNodes[0].dokuid.split(this.config.sepchar).length;
577 t.splice(max + n, t.length);
578 cnsa = t.join(this.config.sepchar);
579 }
580 for (i = 0; i < this.aNodes.length; i++) {
581 cn = this.aNodes[i];
582 if (cns == cn.dokuid || cns == cn.hns) {
583 this.openTo(cn.id, false, true);
584 this.fajax = false;
585 if (cn.pid >= 0) {
586 jQuery(this.scroll("l", 4, cn.pid, 1));
587 }
588 break;
589 }
590 if (cnsa == cn.dokuid || cnsa == cn.hns) {
591 cna = cn;
592 this.fajax = true;
593 }
594 }
595 if (cna) {
596 this.openTo(cna.id, false, true);
597 }
598 };
599  
600 /**
601 * Load children when not available
602 *
603 * @param {int} id node id
604 * @returns {boolean}
605 */
606 dTree.prototype.fill = function (id) {
607 if (id == -1 || this.aNodes[id]._ok) {
608 return true;
609 }
610 var n = id, $eLoad, a, rd, ln, eDiv;
611 if (this.aNodes[n].ajax) {
612 //temporary load indicator
613 $eLoad = jQuery('#l' + this.obj);
614 if (!$eLoad.length) {
615 $eLoad = IndexmenuUtils.createPicker('l' + this.obj);
616 }
617 jQuery('#s' + this.obj + n).parent().append($eLoad);
618 $eLoad
619 .html('Loading ...')
620 .css({width: 'auto'})
621 .show();
622  
623 //retrieves children
624 this.getAjax(n);
625 return true;
626 }
627 rd = [];
628 while (!this.aNodes[n]._ok) {
629 rd[rd.length] = n;
630 n = this.aNodes[n].pid;
631 }
632 for (ln = rd.length - 1; ln >= 0; ln--) {
633 id = rd[ln];
634 a = this.aNodes[id];
635 eDiv = jQuery('#d' + this.obj + id)[0];
636 if (!eDiv) {
637 return false;
638 }
639 this.aIndent = [];
640 n = a;
641 while (n.pid >= 0) {
642 if (n._ls) {
643 this.aIndent.unshift(0);
644 } else {
645 this.aIndent.unshift(1);
646 }
647 n = n._p;
648 }
649 eDiv.innerHTML = this.addNode(a);
650 a._ok = true;
651 }
652 return true;
653 };
654  
655 /**
656 * Open the nodes stored in cookie
657 */
658 dTree.prototype.openCookies = function () {
659 var n, cn, aOpen = this.getCookie('co' + this.obj).split('.');
660 for (n = 0; n < aOpen.length; n++) {
661 if (aOpen[n] === "") {
662 break;
663 }
664 cn = this.aNodes[aOpen[n]];
665 if (!cn._ok) {
666 this.nodeStatus(true, aOpen[n], cn._ls);
667 cn._io = 1;
668 }
669 }
670 };
671  
672 /**
673 * Scrolls the index
674 *
675 * @param {string} where to move to
676 * @param {int} s start
677 * @param {int} n parent node id
678 * @param {int} i
679 */
680 dTree.prototype.scroll = function (where, s, n, i) {
681 if (!this.config.scroll) {
682 return false;
683 }
684 var w, dtree, dtreel, nodeId;
685 dtree = jQuery('#dtree_' + this.obj)[0];
686 dtreel = parseInt(dtree.offsetLeft, 0);
687 if (where == "r") {
688 jQuery('#left_' + this.obj)[0].style.border = "thin inset";
689 this.scrollRight(dtreel, s);
690 } else {
691 nodeId = jQuery('#s' + this.obj + n)[0];
692 if (nodeId == null) {
693 return false;
694 }
695 w = parseInt(dtree.parentNode.offsetWidth - nodeId.offsetWidth - nodeId.offsetLeft, 0);
696 if (this.config.toc) {
697 w = w - 11;
698 }
699 if (dtreel <= w) {
700 return;
701 }
702 this.resizescroll("none");
703 this.stopscroll();
704 this.scrollLeft(dtreel, s, w - 3, i);
705 }
706 };
707  
708 /**
709 * Scroll index to the left
710 *
711 * @param {int} lft current position
712 * @param {int} s start
713 * @param {int} w width
714 * @param {int} i
715 */
716 dTree.prototype.scrollLeft = function (lft, s, w, i) {
717 if (lft < w - i - 10) {
718 this.divdisplay('z', 0);
719 this.scrllTmr = 0;
720 return;
721 }
722 var self = this;
723 jQuery('#dtree_' + self.obj)[0].style.left = lft + "px";
724 this.scrllTmr = setTimeout(function () {
725 self.scrollLeft(lft - s, s + i, w, i);
726 }, 20);
727 };
728  
729 /**
730 * Scroll Index back to the right
731 *
732 * @param {int} lft current position
733 * @param {int} s start
734 */
735 dTree.prototype.scrollRight = function (lft, s) {
736 if (lft >= s) {
737 this.divdisplay('left_', 0);
738 this.stopscroll();
739 return;
740 }
741 var self = this;
742 jQuery('#dtree_' + self.obj)[0].style.left = lft + "px";
743 if (lft > -15) {
744 s = 1;
745 }
746 this.scrllTmr = setTimeout(function () {
747 self.scrollRight(lft + s, s + 1);
748 }, 20);
749 };
750  
751 /**
752 * Stop scroll movement
753 */
754 dTree.prototype.stopscroll = function () {
755 jQuery('#left_' + this.obj)[0].style.border = "none";
756 clearTimeout(this.scrllTmr);
757 this.scrllTmr = 0;
758 };
759  
760 /**
761 * Show features and add event handlers for ToC and scroll
762 *
763 * @param {int} n node id
764 */
765 dTree.prototype.show_feat = function (n) {
766 var w, div, id, dtree, dtreel, self, node = jQuery('#s' + this.obj + n)[0];
767 self = this;
768 if (this.config.toc && node.className != "node") {
769 div = jQuery('#t' + this.obj)[0];
770 id = (this.aNodes[n].hns) ? this.aNodes[n].hns : this.aNodes[n].dokuid;
771 div.onmousedown = function () {
772 IndexmenuContextmenu.createTocMenu('call=indexmenu&req=toc&id=' + decodeURIComponent(id), 'picker_' + self.obj, 't' + self.obj);
773 };
774 node.parentNode.appendChild(div);
775 if (div.style.display == "none") {
776 div.style.display = "inline";
777 }
778 }
779 if (this.config.scroll) {
780 div = jQuery('#z' + this.obj)[0];
781 div.onmouseover = function () {
782 div.style.border = "none";
783 self.scroll("l", 1, n, 0);
784 };
785 div.onmousedown = function () {
786 div.style.border = "thin inset";
787 self.scroll("l", 4, n, 1);
788 };
789 div.onmouseout = function () {
790 div.style.border = "none";
791 self.stopscroll();
792 };
793 div.onmouseup = div.onmouseover;
794 dtree = jQuery('#dtree_' + this.obj)[0];
795 dtreel = parseInt(dtree.offsetLeft, 0);
796 w = parseInt(dtree.parentNode.offsetWidth - node.offsetWidth - node.offsetLeft + 1, 0);
797 if (dtreel > w) {
798 div.style.display = "none";
799 div.style.top = node.offsetTop + "px";
800 div.style.left = parseInt(node.offsetLeft + node.offsetWidth + w - 12, 0) + "px";
801 div.style.display = "block";
802 }
803 }
804 };
805  
806 /**
807 * Show and resize the scroll-back button relatively to size of tree
808 *
809 * @param {string} status 'block' or 'none'
810 */
811 dTree.prototype.resizescroll = function (status) {
812 var dtree, w, h, left = jQuery('#left_' + this.obj)[0];
813 if (!left) {
814 return;
815 }
816 if (left.style.display == status) {
817 dtree = jQuery('#dtree_' + this.obj)[0];
818 w = parseInt(dtree.offsetHeight / 3, 0);
819 h = parseInt(w / 50, 0) * 50;
820 if (h < 50) {
821 h = 50;
822 }
823 left.style.height = h + "px";
824 left.style.top = w + "px";
825 if (status == "none") {
826 left.style.display = "block";
827 }
828 }
829 };
830  
831 /**
832 * Toggle Open or close
833 *
834 * @param {int} n node id
835 */
836 dTree.prototype.getAjax = function (n) {
837 var node, req, curns, selft = this;
838 node = selft.aNodes[n];
839  
840 req = 'call=indexmenu&req=index&idx=' + node.dokuid + decodeURIComponent(this.config.jsajax);
841 curns = this.pageid.substring(0, this.pageid.lastIndexOf(this.config.sepchar));
842  
843 if (this.fajax) {
844 req += '&nss=' + curns + '&max=1';
845 }
846  
847 var onCompletion = function (data) {
848 var i, ajxnodes, ajxnode, plus;
849 plus = selft.aNodes.length - 1;
850 eval(data);
851 if (!ajxnodes instanceof Array || ajxnodes.length < 1) {
852 ajxnodes = [
853 ['', 1, 0, '', 0, 1, 0]
854 ];
855 }
856 node.ajax = false;
857 for (i = 0; i < ajxnodes.length; i++) {
858 ajxnode = ajxnodes[i];
859 ajxnode[2] = (ajxnode[2] == 0) ? node.id : ajxnode[2] + plus;
860 ajxnode[1] += plus;
861 selft.add(ajxnode[0], ajxnode[1], ajxnode[2], ajxnode[3], ajxnode[4], ajxnode[5], ajxnode[6]);
862 }
863 if (selft.fajax) {
864 selft.fajax = false;
865 selft.openCurNS(0);
866 } else {
867 selft.openTo(node.id, false, true);
868 }
869 jQuery('#l' + selft.obj).hide();
870 };
871  
872 jQuery.post(
873 DOKU_BASE + 'lib/exe/ajax.php',
874 'call=indexmenu&'+req,
875 onCompletion,
876 'html'
877 );
878 };
879  
880 /**
881 * Load custom css for theme
882 */
883 dTree.prototype.loadCss = function () {
884 var oLink = document.createElement("link");
885 oLink.href = this.config.plugbase + '/images/' + this.config.theme + '/style.css';
886 oLink.rel = "stylesheet";
887 oLink.type = "text/css";
888 document.getElementsByTagName('head')[0].appendChild(oLink);
889 };
890  
891 /**
892 * Show the contextmenu
893 *
894 * @param {int} n node id
895 * @param {Event} e event
896 * @returns {boolean}
897 */
898 dTree.prototype.contextmenu = function (n, e) {
899 var type, node, cdtree, rmenu;
900 cdtree = jQuery("#cdtree_" + this.obj)[0];
901 rmenu = jQuery('#r' + this.obj)[0];
902 if (!rmenu) {
903 return true;
904 }
905 IndexmenuContextmenu.mouseposition(rmenu, e);
906 var cmenu = window.indexmenu_contextmenu;
907 node = this.aNodes[n];
908 if(this.config.scorespace)
909 rmenu.innerHTML = '<div class="indexmenu_rmenuhead" title="' + node.name + '">' + node.name.replace(/_/g, " ") + "</div>";
910 else
911 rmenu.innerHTML = '<div class="indexmenu_rmenuhead" title="' + node.name + '">' + node.name + "</div>";
912 rmenu.appendChild(document.createElement('ul'));
913 type = (node.isdir || node._hc) ? 'ns' : 'pg';
914 IndexmenuContextmenu.arrconcat(cmenu['all'][type], this, n);
915 if (node.hns) {
916 IndexmenuContextmenu.arrconcat(cmenu[type], this, n);
917 type = 'pg';
918 IndexmenuContextmenu.arrconcat(cmenu['all'][type], this, n);
919 }
920 IndexmenuContextmenu.arrconcat(cmenu[type], this, n);
921 rmenu.style.display = 'inline';
922 return false;
923 };
924  
925 /**
926 * Show/hide object with given id of current indexmenu
927 *
928 * @param {string} obj
929 * @param {boolean} visible true: visible, false hide.
930 */
931 dTree.prototype.divdisplay = function (obj, visible) {
932 var o = jQuery('#' + obj + this.obj)[0];
933 if (!o) {
934 return;
935 }
936 (visible) ? o.style.display = 'inline' : o.style.display = 'none';
937 };
938  
939 /**
940 * Initialise the dTree index
941 *
942 * @param {int} hasstyle has an additional css style sheet
943 * @param {int} nocookies use no cookies
944 * @param {string} opennodes string of initial opened nodes
945 * @param {int} nav is navbar option set
946 * @param {int} max max level of available nodes (deeper levels are included with js)
947 * @param {int} nomenu show no menu
948 */
949 dTree.prototype.init = function (hasstyle, nocookies, opennodes, nav, max, nomenu) {
950 if (hasstyle) {
951 this.loadCss();
952 }
953 if (!nocookies) {
954 this.openCookies();
955 }
956 //open given nodes
957 if (opennodes) {
958 this.getOpenTo(opennodes.split(" "));
959 }
960 if (nav) {
961 this.openCurNS(max);
962 }
963 //create contextmenu
964 if (!nomenu) {
965 var self = this;
966 IndexmenuUtils.createPicker('r' + this.obj, 'indexmenu_rmenu ' + this.config.theme);
967 jQuery('#r' + this.obj)[0].oncontextmenu = IndexmenuContextmenu.stopevt;
968 jQuery(document).click(function() {
969 self.divdisplay('r', 0);
970 });
971 }
972 };