1 /**
  2  * The Nornix.TreeMenu class creates an explorer-like interface from nested lists.
  3  * The menu can use either server-side facilities to create classes for
  4  * the styling with CSS to hook on to.
  5  *
  6  * Only set inAllowWhitespace to true when necessary, as it slows down the script.
  7  *
  8  * Usage instructions at http://treemenu.nornix.com/info/usage
  9  *
 10  * @author      Anders Nawroth <http://www.anders.nawroth.com/>
 11  * @license     LGPL http://treemenu.nornix.com/license
 12  * @copyright   2006--2008
 13  * @version     @version@
 14  * @constructor
 15  * @param       {String} inMenuId id of the element surrounding the menu
 16  * @param       {boolean} inAllowWhitespace if true, whitespace is allowed between menu elements in the HTML code
 17  */
 18 Nornix.TreeMenu = function (inMenuId, inAllowWhitespace)
 19 {
 20 	/**
 21 	 * Variable to work around ECMAScript problems with the "this" keyword inside event handlers.
 22 	 * @type Object
 23 	 */
 24 	var thiz = this;
 25 	/**
 26 	 * Id of current menu wrapper.
 27 	 * @type String
 28 	 */
 29 	var menuId = inMenuId ? inMenuId : "menu";
 30 	/**
 31 	 * Allow whitespace in menu (true) or not (false).
 32 	 * @type boolean
 33 	 */
 34 	var allowWhitespace = (inAllowWhitespace === false) ? false : true;
 35 	/**
 36 	 * Name of cookie, to avoid namespace-clashes between multiple menus.
 37 	 * @type String
 38 	 */
 39 	var cookieName = "tree" + menuId;
 40 	/**
 41 	 * Cookie with saved node status information.
 42 	 * @type String
 43 	 */
 44 	var oldTree = Nornix.cookies.read(cookieName);	
 45 	/**
 46 	 * Clean tree state on save when set to true.
 47 	 * @type String
 48 	 */
 49 	var cleanMemory = false;
 50 	/**
 51 	 * Empty location for links, to compare with.
 52 	 * @type String
 53 	 */
 54 	var emptyHref = window.location+"#";
 55 	/**
 56 	 * Unordered list to clone from.
 57 	 * @type HTMLULElement
 58 	 */
 59 	var folderUl = document.createElement("ul");
 60 	/**
 61 	 * List item to clone from.
 62 	 * @type HTMLLIElement
 63 	 */
 64 	var folderLi = document.createElement("li");
 65 	/**
 66 	 * Span to clone and use for folder open/close icons.
 67 	 * @type HTMLSpanElement
 68 	 */
 69 	var folderSpan = document.createElement("span");
 70 	/**
 71 	 * Anchor element to clone from.
 72 	 * @type HTMLAnchorElement
 73 	 */
 74 	var commonAnchor = document.createElement("a");
 75 	/**
 76 	 * Anchor element to clone from for action "buttons".
 77 	 * @type HTMLAnchorElement
 78 	 */
 79 	var actionAnchor = commonAnchor.cloneNode(false);
 80 	/**
 81 	 * Object to keep menu buttons.
 82 	 * @type HTMLAnchorElement[]
 83 	 */
 84 	var menuButtons = [];
 85 	/**
 86 	 * Object to store navigation items.
 87 	 * @property {HTMLAnchorElement} first
 88 	 * @property {HTMLAnchorElement} previous
 89 	 * @property {HTMLAnchorElement} next
 90 	 * @property {HTMLAnchorElement} last
 91 	 */
 92 	var navigation = {};
 93 	/**
 94 	 * Object to keep menu button actions.
 95 	 * @type Function[]
 96 	 */
 97 	var menuButtonActions = [];
 98 	/**
 99 	 * The current marked item in the menu.
100 	 * This has to be remebered for dynamic usage of the menu.
101 	 * @type HTMLAnchorElement
102 	 */
103 	var currentItem = null;
104 	/**
105 	 * The href attribute that was removed from the current marked item in the menu.
106 	 * This has to be remebered for dynamic usage of the menu.
107 	 * @type String
108 	 */
109 	var currentItemHref = null;
110 	/**
111 	 * The selected item in the menu.
112 	 * This has to be remebered for dynamic usage of the menu.
113 	 * @type HTMLAnchorElement
114 	 */
115 	var selectedItem = null;
116 	/**
117 	 * RegExp to find the class "open" in strings.
118 	 * @type RegExp
119 	 * @final
120 	 */
121 	var openPattern = /(^| )open( |$)/;
122 	/**
123 	 * Open all folders in the menu.
124 	 * @function
125 	 */
126 	this.openAll = openAll;
127 	/**
128 	 * Close all folders in the menu.
129 	 * @function
130 	 */
131 	this.closeAll = closeAll;
132 	/**
133 	 * Open tree to the specified node.
134 	 * The node can be given as a DOM node or as a string.
135 	 * The string will be interpreted as a href value
136 	 * or node text value.
137 	 * When used in event handlers, the return value can be useful.
138 	 * @function
139 	 * @param {HTMLAnchorElement|HTMLLIElement|string} node node to open to, either A och LI element or string
140 	 * @param {boolean} [setFocus] set to true to move focus to node
141 	 * @param {boolean} [makeCurrent] make node the current node
142 	 * @param {boolean} [doClick] set to true to "click" the link 
143 	 * @param {Event} [e] event object
144 	 * @return {boolean} true if the event action should continue.
145 	 */
146 	this.openToNode = openToNode;
147 
148 	/**
149 	 * Start the tree menu.
150 	 * Initialization will be called as soon as the menu object is available in the DOM.
151 	 * Images will be preloaded while the DOM is built.
152 	 * @function
153 	 */
154 	this.start = function()
155 	{
156 		if (!document.getElementById || !document.createElement) return;
157 		Nornix.events.delayedInit(menuId, init);
158 		actionAnchor.href = "javascript:;";
159 		// no preload for IE < 7
160 		if (thiz.config.preloadImages && !Nornix.util.isIeLt7)
161 		{
162 			Nornix.dom.imagePreload(thiz.config.preloadImages, thiz.config.imagePath, thiz.config.imageExtension);
163 		}
164 	};
165 	
166 	/**
167 	 * Prevent the tree state from being saved when leaving the page.
168 	 * @function
169 	 */
170 	this.preventStatusSave = function()
171 	{
172 		cleanMemory = true;
173 	};
174 	
175 	/**
176 	 * Register a menu button an its action.
177 	 * @param {HTMLAnchorElement} menu button element
178 	 * @param {Function} function to call when button is pressed
179 	 */
180 	this.registerMenuButton = function(a, func)
181 	{
182 		menuButtons[menuButtons.length] = a;
183 		menuButtonActions[menuButtonActions.length] = func;
184 	}
185 	
186 	/**
187 	 * Create a new menu (document) node.
188 	 * If child nodes are added the node will automatically
189 	 * become a folder node, once it's inside of the menu.
190 	 * To add more attributes or whatever, use the returned LI
191 	 * element or the A element at LI.firstChild.
192 	 * @function
193 	 * @param {string} text link text for node
194 	 * @param {string} [href] link address to use for A element (or null)
195 	 * @param {string} [title] text to show on hovering the A element
196 	 * @param {HTMLLIElement} the LI element that was created
197 	 */
198 	this.createNode = function(text, href, title)
199 	{
200 		var li = folderLi.cloneNode(false);
201 		li.className = "document";
202 		var a = commonAnchor.cloneNode(false);
203 		if (href)
204 		{
205 			a.href = href;
206 		}
207 		if (title)
208 		{
209 			a.title = title;
210 		}
211 		a.appendChild(document.createTextNode(text));
212 		li.appendChild(a);
213 		return li;
214 	};
215 	
216 	/**
217 	 * Add child node to a menu item.
218 	 * @function
219 	 * @param {HTMLAnchorElement|string} node menu item
220 	 * @param {HTMLLIElement|string} newNode child node to add
221 	 * @param {boolean} [showNode] make new node visible
222 	 * @param {boolean} [setFocus] set focus to the child node
223 	 * @param {boolean} [move] set to true when moving a node
224 	 */
225 	this.appendChild = function(node, newNode, showNode, setFocus, move)
226 	{
227 		insertNode(function(node, newNode, showNode)
228 		{
229 			var ul;
230 			Nornix.css.add(newNode, "last");
231 			if (node.nextSibling && Nornix.dom.eqNodeName(node.nextSibling, "ul"))
232 			{
233 				ul = node.nextSibling;
234 				Nornix.css.add(newNode, "last");
235 				makeFolder(node.parentNode, node, showNode);
236 			}
237 			else
238 			{
239 				ul = folderUl.cloneNode(false);
240 				var p = node.parentNode;
241 				p.appendChild(ul);
242 				makeFolder(p, node, showNode);
243 				thiz.menuFolders[thiz.menuFolders.length] = p; // not at correct position
244 			}
245 			ul.appendChild(newNode);
246 			if (newNode.previousSibling)
247 			{
248 				Nornix.css.remove(newNode.previousSibling, "last");
249 			}
250 		}, node, newNode, showNode, setFocus, move);
251 	};
252 	
253 	/**
254 	 * Add menu item before another menu item.
255 	 * @function
256 	 * @param {HTMLAnchorElement|string} node menu item
257 	 * @param {HTMLLIElement|string} newNode child node to add
258 	 * @param {boolean} [showNode] make new node visible
259 	 * @param {boolean} [setFocus] set focus to the child node
260 	 * @param {boolean} [move] set to true when moving a node
261 	 */
262 	this.insertBefore = function(node, newNode, showNode, setFocus, move)
263 	{
264 		insertNode(function(node, newNode)
265 		{
266 			node.parentNode.parentNode.insertBefore(newNode, node.parentNode);
267 		}, node, newNode, showNode, setFocus, move);
268 	};
269 
270 	/**
271 	 * Add menu item after another menu item.
272 	 * @function
273 	 * @param {HTMLAnchorElement|string} node menu item
274 	 * @param {HTMLLIElement|string} newNode child node to add
275 	 * @param {boolean} [showNode] make new node visible
276 	 * @param {boolean} [setFocus] set focus to the child node
277 	 * @param {boolean} [move] set to true when moving a node
278 	 */
279 	this.insertAfter = function(node, newNode, showNode, setFocus, move)
280 	{
281 		insertNode(function(node, newNode)
282 		{
283 			if (node.parentNode.nextSibling)
284 			{
285 				node.parentNode.parentNode.insertBefore(newNode, node.parentNode.nextSibling);
286 			}
287 			else
288 			{
289 				node.parentNode.parentNode.appendChild(newNode);
290 			}
291 		}, node, newNode, showNode, setFocus, move);
292 	};
293 	
294 	/**
295 	 * Remove item from menu.
296 	 * @function
297 	 * @param {HTMLAnchorElement|string} node menu item
298 	 */
299 	this.remove = function(node)
300 	{
301 		node = resolveNode(node);
302 		if (!node || !node.parentNode || !node.parentNode.parentNode) return;
303 		// check if current node! TODO?
304 		cleanUpForReMove(node.parentNode);
305 		node.parentNode.parentNode.removeChild(node.parentNode);
306 		refreshItemLists();
307 	};
308 
309 	/**
310 	 * Go to and click first link in menu.
311 	 * @function
312 	 */
313 	this.goToFirstNode = function() {goToNavigationItem("first")};
314 
315 	/**
316 	 * Go to and click last link in menu.
317 	 * @function
318 	 */
319 	this.goToLastNode = function() {goToNavigationItem("last")};
320 
321 	/**
322 	 * Go to and click next link in menu.
323 	 * @function
324 	 */
325 	this.goToNextNode = function() {goToNavigationItem("next")};
326 
327 	/**
328 	 * Go to and click previous link in menu.
329 	 * @function
330 	 */
331 	this.goToPrevNode = function() {goToNavigationItem("previous")};
332 
333 	/**
334 	 * Initialize the menu.
335 	 * @param {HTMLElement} menu the menu object
336 	 */
337 	function init (menu)
338 	{
339 		thiz.menu = menu;
340 		thiz.menuElements = Nornix.dom.live2copy(thiz.menu.getElementsByTagName("li"));
341 
342 		// set classes, if not set in the HTML, and prepare folders
343 		setClasses();
344 
345 		// re-render menu now if IE
346 		ieFix();
347 
348 		// add extra icons
349 		if (thiz.config.openCloseAll)
350 		{
351 			createOpenCloseAllIcons();
352 		}
353 		if (thiz.config.navigationIcons)
354 		{
355 			createNavigationIcons();
356 		}
357 
358 		// set up event handling now
359 		EventHandlers();
360 		
361 		// initialize navigation, if enabled
362 		if (thiz.config.navigationIcons)
363 		{
364 			initNavigation();
365 		}
366 	}
367 
368 	/**
369 	 * Initialize the event handlers for click and keyboard events.
370 	 * @constructor
371 	 */
372 	function EventHandlers()
373 	{
374 		init();
375 
376 		/**
377 		 * Initialize event handlers.
378 		 */
379 		function init ()
380 		{
381 			// add click handler to menu link.
382 			if (thiz.config.menuLinkElement)
383 			{
384 				Nornix.events.add(document.getElementById(thiz.config.menuLinkElement), 'click', menuJump);
385 				if (Nornix.util.isIe)
386 				{
387 					Nornix.events.add(document.getElementById(thiz.config.menuLinkElement), 'focus', menuJumpIe);
388 				}
389 			}
390 
391 			// if whitespace was allowed, remove it and change the setting to reflect the new state
392 			if (allowWhitespace)
393 			{
394 				removeWhitespace(thiz.menu);
395 				allowWhitespace = false;
396 			}
397 
398 			// add event handlers to menu
399 			Nornix.events.add(thiz.menu, 'click', checkClickDynamic, true);
400 			Nornix.events.add(thiz.menu, 'keydown', checkKeyDynamic, true);
401 
402 			// add unload handler to save cookie
403 			Nornix.events.add(window, "unload", save);
404 		}
405 
406 		/**
407 		 * Move focus to the root element of the menu.
408 		 * Used as an event handler.
409 		 * @member EventHandlers
410 		 * @param {Event} e event object
411 		 * @return {boolean} always returns false
412 		 */
413 		function menuJump (e)
414 		{
415 			focusNode(thiz.menu);
416 			Nornix.events.cancel(e);
417 			return false;
418 		}
419 
420 		/**
421 		 * Move focus from the menu link to the menu itself in Internet Exlorer.
422 		 * Used as an event handler.
423 		 * @param {Event} e event object
424 		 */
425 		function menuJumpIe (e)
426 		{
427 			if (e.altKey)
428 			{
429 				focusNode(thiz.menu);
430 			}
431 		}
432 
433 		/**
434 		 * Handle all click events.
435 		 * @param {Event} e event object
436 		 * @return {boolean} false when handling the event
437 		 */
438 		function checkClickDynamic (e)
439 		{
440 			var t = e.target;
441 			if (Nornix.css.contains(t, "menuAction"))
442 			{
443 				var i = menuButtons.length;
444 				while (i--)
445 				{
446 					if (t === menuButtons[i])
447 					{
448 						menuButtonActions[i]();
449 						break;
450 					}
451 				}
452 				Nornix.events.cancel(e);
453 				return false;
454 			}
455 			var p = t.parentNode;
456 			if (!p) return true; // we can't handle this
457 			if (Nornix.dom.eqNodeName(t, "span"))
458 			{
459 				toggle(p);
460 				return;
461 			}
462 			return performClick(p, t, e);
463 		}
464 	
465 		/**
466 		 * Handle all keydown events.
467 		 * Looks for lots of different key strokes.
468 		 * @param {Event} e event object
469 		 * @return {boolean} true if the event action should continue.
470 		 */
471 		function checkKeyDynamic(e)
472 		{
473 			var isRoot, isFolder, isDocument, isCloser, isOpener, isDocOrFolder, isCloserOpener,
474 				o = e.target, p = o.parentNode, node;
475 			switch (true)
476 			{
477 				case Nornix.css.contains(o, "root"): isRoot = true; break;
478 				case Nornix.css.contains(o, "closeTree"): isCloser = true; isCloserOpener = true; break;
479 				case Nornix.css.contains(o, "openTree"): isOpener = true; isCloserOpener = true; break;
480 				case Nornix.css.contains(p, "folder"): isFolder = true; isDocOrFolder = true; break;
481 				case Nornix.css.contains(p, "document"): isDocument = true; isDocOrFolder = true; break;
482 				default: return true; // not found;
483 			}
484 
485 			var keyCode = e.keyCode !== null ? e.keyCode : e.which;
486 			if (keyCode === 56) // ( 8
487 			{
488 				openAll();
489 			}
490 			else if (keyCode === 57) // 9 )
491 			{
492 				closeAll(o);
493 				if (!o.offsetParent)
494 				{
495 					Nornix.dom.findChildOfType(
496 						thiz.menu, "a", function (a)
497 						{
498 							focusAnchor(a);
499 						}
500 					);
501 				}
502 			}
503 			else if (isFolder && (keyCode === 32 || (keyCode === 13 && isHrefEmpty(o))))
504 			{
505 				toggle(p);
506 			}
507 			else if (isRoot && keyCode === 40) // arrow down
508 			{
509 				Nornix.dom.findChildOfType(p, "ul", function (ul) {
510 					focusNode(ul.firstChild);
511 				});
512 			}
513 			else if (isRoot && keyCode === 39 && o.nextSibling) // arrow right
514 			{
515 				focusAnchor(o.nextSibling);
516 			}
517 			else if (isDocOrFolder && (keyCode === 40)) // arrow down
518 			{
519 				if (isDocument || !isOpen(p))
520 				{
521 					if (node = p.nextSibling) focusNode(node);
522 					else if (isFolder && (node = o.nextSibling.firstChild))
523 					{
524 						// open folder when moving down from it
525 						toggle(p);
526 						focusNode(node);
527 					}
528 					else
529 					{
530 						// find next available node
531 						node = p;
532 						while (node && node !== thiz.menu)
533 						{
534 							node = node.parentNode.parentNode;
535 							if (node.nextSibling)
536 							{
537 								focusNode(node.nextSibling);
538 								break;
539 							}
540 						}
541 					}
542 				}
543 				else
544 				{
545 					if (node = p.nextSibling) focusNode(node);
546 					else if (node = o.nextSibling.firstChild) focusNode(node);
547 				}
548 			}
549 			else if (isDocOrFolder && (keyCode === 38)) // arrow up
550 			{
551 				if (node = p.previousSibling) focusNode(node);
552 				else if (node = p.parentNode.parentNode) focusNode(node);
553 			}
554 			else if (isDocOrFolder && (keyCode === 39)) // arrow right
555 			{
556 				if (isDocument && (node = p.nextSibling)) focusNode(node);
557 				else if (o.nextSibling && (node = o.nextSibling.firstChild))
558 				{
559 					if (isOpen(p)) focusNode(node);
560 					else
561 					{
562 						toggle(p);
563 						focusNode(node);
564 					}
565 				}
566 			}
567 			else if (isDocOrFolder && (keyCode === 37)) // arrow left
568 			{
569 				if (node = p.parentNode.parentNode) focusNode(node);
570 				if (isFolder && isOpen(p)) toggle(p);
571 			}
572 			else if (isDocument && (keyCode === 32)) {} // SPACE
573 			else if (keyCode === 27 && thiz.config.contentElement) // ESC
574 			{
575 				window.location.hash = thiz.config.contentElement;
576 			}
577 			else if (isCloserOpener && (keyCode === 37 || keyCode === 38)) // arrow left or up
578 			{
579 				focusAnchor(o.previousSibling);
580 			}
581 			else if (isCloser && (keyCode === 39 || keyCode === 40)) // arrow right or down
582 			{
583 				focusAnchor(o.nextSibling);
584 			}
585 			else if (isOpener && (keyCode === 39 || keyCode === 40)) // arrow right or down
586 			{
587 				focusNode(o.nextSibling.firstChild);
588 			}
589 			else
590 			{
591 				return true;
592 			}
593 			Nornix.events.cancel(e);
594 			return false;
595 		}
596 	}
597 
598 	/**
599 	 * Cleanup node before (re)moval of it
600 	 * @param {HTMLLIElement} li list item to clean up
601 	 */
602 	function cleanUpForReMove(li)
603 	{
604 		if (Nornix.css.contains(li, "last"))
605 		{
606 			if (li.previousSibling)
607 			{
608 				Nornix.css.add(li.previousSibling, "last");
609 			}
610 			else
611 			{
612 				makeDocument(li.parentNode.parentNode);
613 			}
614 			Nornix.css.remove(li, "last");
615 		}
616 	}
617 	
618 	/**
619 	 * Refresh list of items, folders and the current item.
620 	 */
621 	function refreshItemLists()
622 	{
623 		thiz.menuElements = Nornix.dom.live2copy(thiz.menu.getElementsByTagName("li"));
624 		var li, i = 0, folders = [], currentFound = false, selectedFound = false,
625 		 current = (currentItem && currentItem.parentNode) ? currentItem.parentNode : null;
626 		while (li = thiz.menuElements[i++])
627 		{
628 			if (isFolder(li))
629 			{
630 				folders[folders.length] = li;
631 			}
632 			if (li === current)
633 			{
634 				currentFound = true;
635 			}
636 			if (li === selectedItem)
637 			{
638 				selectedFound = true;
639 			}
640 		}
641 		thiz.menuFolders = folders;
642 		if (!currentFound)
643 		{
644 			currentItem = currentItemHref = null;
645 		}
646 		if (!selectedFound)
647 		{
648 			selectedItem = null;
649 		}
650 		getMenuNodes(true);
651 	}
652 	
653 	/**
654 	 * Insert node in menu.
655 	 * @param {Function} insertFunc insert implementation (node, newNode)
656 	 * @param {HTMLAnchorElement|string} node menu item
657 	 * @param {HTMLLIElement|string} newNode child node to add
658 	 * @param {boolean} [showNode] make new node visible
659 	 * @param {boolean} [setFocus] set focus to the child node
660 	 * @param {boolean} [move] set to true when moving a node
661 	 */
662 	function insertNode(insertFunc, node, newNode, showNode, setFocus, move)
663 	{
664 		node = resolveNode(node);
665 		if (!node) return;
666 		if (typeof(newNode) === "string")
667 		{
668 			newNode = resolveNode(newNode);
669 			if (!newNode || !newNode.parentNode) return;
670 			newNode = newNode.parentNode;
671 			move = true;
672 		}
673 		if (!node.parentNode || !node.parentNode.parentNode) return;
674 		if (move)
675 		{
676 			cleanUpForReMove(newNode);
677 		}
678 		insertFunc(node, newNode, showNode);
679 		if (showNode)
680 		{
681 			openToNode(newNode.firstChild, setFocus);
682 		}
683 		ieFix();
684 		refreshItemLists();
685 	}
686 	
687 	/**
688 	 * Perform link click.
689 	 * @param {HTMLLIElement} li HTML li element
690 	 * @param {HTMLAnchorElement} a anchor element to click
691 	 * @param {Event} [e] event object
692 	 * @return {boolean} false when handling the event
693 	 */
694 	function performClick(li, a, e)
695 	{
696 		// change current marker?
697 		if (!isHrefEmpty(a))
698 		// :TODO: isHrefEmpty should be replaceable by some user function in this case!
699 		{
700 			changeCurrentItem(a, false, false);
701 		}
702 		// run click handlers and toggle folders if empty href
703 		if (isFolder(li))
704 		{
705 			// p is a folder
706 			if (thiz.hooks.dynamicFolderLinks && !thiz.hooks.dynamicFolderLinks(a))
707 			{
708 				if (thiz.config.navigationIcons)
709 				{
710 					initNavigation();
711 				}
712 				if (e)
713 				{
714 					Nornix.events.cancel(e);
715 				}
716 				return false;
717 			}
718 			if (isHrefEmpty(a))
719 			{
720 				toggle(li);
721 				if (e)
722 				{
723 					Nornix.events.cancel(e);
724 				}
725 				return false;
726 			}
727 		}
728 		else
729 		{
730 			// p is not a folder, then it iss a document
731 			if (thiz.hooks.dynamicDocumentLinks && !thiz.hooks.dynamicDocumentLinks(a))
732 			{
733 				if (thiz.config.navigationIcons)
734 				{
735 					initNavigation();
736 				}
737 				if (e)
738 				{
739 					Nornix.events.cancel(e);
740 				}
741 				return false;
742 			}
743 		}
744 		return true; // not found
745 	}
746 
747 	/**
748 	 * If node is of type string, resolve it to the corresponding A element.
749 	 * @param {HTMLAnchorElement|string}
750 	 * @return {HTMLAnchorElement} the node
751 	 */
752 	function resolveNode(node)
753 	{
754 		if (typeof(node) === "string")
755 		{
756 			return searchNode(node);
757 		}
758 		return node;
759 	}
760 	
761 	/**
762 	 * Get node from text.
763 	 * Will match te first node that contains the value of
764 	 * the text parameter. Make sure it has enough uniqueness.
765 	 * If no found in the href attributes, it will search the
766 	 * link texts too, if not configured otherwise.
767 	 * @param {string} text text to search for
768 	 * @return {HTMLAnchorELement} node or null
769 	 */
770 	function searchNode(text)
771 	{
772 		var a, collection = getMenuNodes();
773 		switch (thiz.config.searchNodeMode)
774 		{
775 			case 0:
776 				return searchInText(collection, text) || searchInHref(collection, text);
777 			case 1:
778 				return searchInHref(collection, text) || searchInText(collection, text);
779 			case 2:
780 				return searchInText(collection, text);
781 			case 3:
782 				return searchInHref(collection, text);
783 		}
784 		return null;
785 	}
786 	
787 	
788 	/**
789 	 * Get node from text, compare to text contents.
790 	 * @param {string} text text to search for
791 	 * @return {HTMLAnchorELement} node or null
792 	 */
793 	function searchInText(collection, text)
794 	{
795 		var i = 0, a;
796 		while (a = collection[i++])
797 		{
798 			if ((currentItem && a === currentItem && Nornix.dom.getTextContent(currentItem).indexOf(text) !== -1)
799 			|| (Nornix.dom.getTextContent(a).indexOf(text) !== -1))
800 			{
801 				return a;
802 			}
803 		}
804 		return null;
805 	}
806 	
807 	/**
808 	 * Get node from text, compare to href attributet contents.
809 	 * @param {string} text text to search for
810 	 * @return {HTMLAnchorELement} node or null
811 	 */
812 	function searchInHref(collection, text)
813 	{
814 		var i = 0, a;
815 		while (a = collection[i++])
816 		{
817 			if ((currentItemHref && a === currentItem && currentItemHref.indexOf(text) !== -1)
818 			 || (a.href.indexOf(text) !== -1))
819 			{
820 				return a;
821 			}
822 		}
823 		return null;
824 	}
825 	
826 	/**
827 	 * Get all Anchor elements from the menu as a static copy.
828 	 * @param {boolean} reset clean the list if it already exists
829 	 * @return {HTMLAnchorElement[]}
830 	 */
831 	function getMenuNodes(reset)
832 	{
833 		if (!thiz.menuNodes || reset)
834 		{
835 			thiz.menuNodes = Nornix.dom.live2copy(thiz.menu.getElementsByTagName('a'));
836 		}
837 		return thiz.menuNodes;
838 	}
839 	
840 	/**
841 	 * Open tree to the specified node.
842 	 * See the public interace of the function.
843 	 */
844 	function openToNode(node, setFocus, makeCurrent, doClick, e)
845 	{
846 		var li, a;
847 		node = resolveNode(node);
848 		if (!node) return true;
849 		if (Nornix.dom.eqNodeName(node, "a"))
850 		{
851 			a = node;
852 			li = a.parentNode;
853 		}
854 		else
855 		{
856 			li = node;
857 			// - find an <a> element that is a child of the menu element
858 			Nornix.dom.findChildOfType(
859 				li, "a", function (theA)
860 				{
861 					a = theA;
862 				}
863 			);
864 		}
865 		if (!a) return true; // :TODO: error handling
866 		if (makeCurrent && !doClick) setEmptyHrefAsCurrent(a);
867 		if (isFolder(li)) // only open if li is folder
868 		{
869 			Nornix.css.add(li, "folder"); // why this??
870 			makeOpen(li);
871 		}
872 		// trace upwards in the tree to open the "path" to the current page
873 		node = li.parentNode.parentNode;
874 		while (node && node != thiz.menu)
875 		{
876 			makeOpen(node);
877 			node = node.parentNode.parentNode;
878 		}
879 		ieFix();
880 		if (setFocus)
881 		{
882 			focusAnchor(a);
883 		}
884 		if (doClick)
885 		{
886 			if (a.click)
887 			{
888 				a.click();
889 				return false;
890 			}
891 			if (a.onclick && !a.onclick()) return false;
892 			if (!performClick(li, a, e)) return false;
893 			// fake a link click
894 			if (a.target)
895 			{
896 				if (window.top.frames && window.top.frames[a.target])
897 				{
898 					window.top.frames[a.target].location.href = a.href;
899 					return false;
900 				}
901 				// :TODO: handle special cases _blank _top _self _parent
902 				return true;
903 			}
904 			window.location.href = a.href;
905 			return false;
906 		}
907 		return true;
908 	}
909 
910 	/**
911 	 * Set classes to display tree menu.
912 	 * Dynamically set classes on menu items, when not set in the HTML code.
913 	 * Uses the classes folder/document/open/closed/last.
914 	 * Make folders from the current element/page and "upwards" open.
915 	 * Prepare tree from stored state in cookie.
916 	 * Adds span elements inside all li elements.
917 	 * Adds event handlers on menu items.
918 	 * Creates the thiz.menuFolders array of all menu folders.
919 	 * @requires Nornix.cookies Uses the Nornix.cookies.read() function.
920 	 */
921 	function setClasses ()
922 	{
923 		var folders = [];
924 		var i, li, a, itemIsFolder, chr;
925 		var menuElements = thiz.menuElements;
926 		// setup
927 		var span;
928 		// setup list of folder elements
929 		var iFolder = 0;
930 
931 		if (thiz.config.dynamicClasses)
932 		{
933 			var page = window.location.href;
934 			// set up root node
935 			// - find an <a> element that is a child of the menu element
936 			Nornix.dom.findChildOfType(
937 				thiz.menu, "a", function (a)
938 				{
939 					Nornix.css.add(a, "root");
940 					if (a.href && a.href === page)
941 					{
942 						setEmptyHrefAsCurrent(a);
943 					}
944 				}
945 			);
946 
947 			// loop list items
948 			i = 0;
949 			while (li = menuElements[i++])
950 			{
951 				a = li.firstChild;
952 				itemIsFolder = isFolder(li);
953 				if (itemIsFolder)
954 				{
955 					folders[folders.length] = li;
956 					chr = oldTree.charAt(iFolder++);
957 					makeFolder(li, a, (chr && chr === "-"));
958 				}
959 				else
960 				{
961 					Nornix.css.add(li, "document");
962 				}
963 				if (allowWhitespace)
964 				{
965 					Nornix.dom.findChildOfType(
966 						li.parentNode, "li",
967 						function (last)
968 						{
969 							if (li === last)
970 							{
971 								Nornix.css.add(li, "last");
972 							}
973 						},
974 						true // search backwards
975 					);
976 				}
977 				else
978 				{
979 					if (li === li.parentNode.lastChild)
980 					{
981 						Nornix.css.add(li, "last");
982 					}
983 				}
984 				if (a && a.href && a.href === page)
985 				{
986 					openToNode(a, thiz.config.focusCurrentItem, true);
987 				}
988 			}
989 		}
990 		else
991 		{
992 			i = 0;
993 			while (li = menuElements[i++])
994 			{
995 				a = li.firstChild;
996 				itemIsFolder = isFolder(li);
997 				// current item?
998 				if (isHrefEmpty(a))
999 				{
1000 					setEmptyHrefAsCurrent(a);
1001 				}
1002 				if (itemIsFolder)
1003 				{
1004 					folders[folders.length] = li;
1005 					// open/close folders with the space bar or enter key or mouse click
1006 					chr = oldTree.charAt(iFolder++);
1007 					makeFolder(li, a, (chr && chr === "-"));
1008 				}
1009 			}
1010 			// is root node also current?
1011 			i = 0;
1012 			while (a = thiz.menu.childNodes[i++])
1013 			{
1014 				if (Nornix.dom.eqNodeName(a, "a"))
1015 				{
1016 					setEmptyHrefAsCurrent(a);
1017 					break;
1018 				}
1019 			}
1020 		}
1021 		thiz.menuFolders = folders;
1022 	}
1023 
1024 	/**
1025 	 * Make LI element a folder.
1026 	 * @param {HTMLLIElement} li menu item to make a folder
1027 	 * @param {HTMLAnchorElement} a anchor element in menu item 
1028 	 * @param {boolean} isOpen true to set folder to an open state
1029 	 */
1030 	function makeFolder(li, a, isOpen)
1031 	{
1032 		span = folderSpan.cloneNode(false);
1033 		li.insertBefore(span, a);
1034 		Nornix.css.swap(li, "document", "folder");
1035 		if (isOpen)
1036 		{
1037 			makeOpen(li);
1038 		}
1039 		else
1040 		{
1041 			makeClosed(li);
1042 		}
1043 	}
1044 	
1045 	/**
1046 	 * Make LI element a document (from being a folder).
1047 	 * @param {HTMLLIElement} li menu item to make a folder
1048 	 */
1049 	function makeDocument(li)
1050 	{
1051 		li.removeChild(li.firstChild); // remove span on folder
1052 		if (li.firstChild.nextSibling) // make sure to remove UL if it exists
1053 		{
1054 			li.removeChild(li.firstChild.nextSibling);
1055 		}
1056 		Nornix.css.swap(li, "folder", "document");
1057 		Nornix.css.remove(li, "open");
1058 		Nornix.css.remove(li, "closed");
1059 	}
1060 
1061 	/**
1062 	 * Check if Anchor element has no href attribute, in that case, set it to "javascript:;".
1063 	 * @param {HTMLAnchorElement} a anchor element to test/change
1064 	 */
1065 	function setEmptyHrefAsCurrent (a)
1066 	{
1067 		if (thiz.config.dynamicClasses)
1068 		{
1069 			changeCurrentItem(a, true, false);
1070 		}
1071 		if (!a.href)
1072 		{
1073 			changeCurrentItem(a, false, true);
1074 		}
1075 	}
1076 
1077 	/**
1078 	 * Move "current" status to another document node.
1079 	 * Also remove status on old current node.
1080 	 * @param {HTMLAnchorElement} a anchor element to change
1081 	 * @param {boolean} [remove] set to true to remove href attribute
1082 	 * @param {boolean} [setDummy] set to true to make a dummy link (javascript:;)
1083 	 */
1084 	function changeCurrentItem (a, remove, setDummy)
1085 	{
1086 		// remove current status on old current item
1087 		if (currentItem)
1088 		{
1089 			if (currentItemHref)
1090 			{
1091 				currentItem.href = currentItemHref; // restore href
1092 			}
1093 			Nornix.css.remove(currentItem, "current");
1094 			if (thiz.config.markCurrentItem)
1095 			{
1096 				currentItem.removeChild(currentItem.firstChild);
1097 			}
1098 		}
1099 		currentItemHref = a.href;
1100 		currentItem = a;
1101 		Nornix.css.add(a, "current");
1102 		if (thiz.config.markCurrentItem)
1103 		{
1104 			a.insertBefore(folderSpan.cloneNode(false), a.firstChild);
1105 		}
1106 		if (remove)
1107 		{
1108 			a.removeAttribute("href");
1109 		} else if (setDummy)
1110 		{
1111 			a.href = "javascript:;";
1112 		}
1113 	}
1114 
1115 	/**
1116 	 * Method that saves the current tree state in a cookie.
1117 	 * @requires Nornix Uses the Nornix.cookies.create() function.
1118 	 */
1119 	function save()
1120 	{
1121 		var s = "";
1122 		if (!cleanMemory)
1123 		{
1124 			var i = 0, li, menuFolders = thiz.menuFolders;
1125 			while (li = menuFolders[i++])
1126 			{
1127 				if (isOpen(li))
1128 				{
1129 					s += "-";
1130 				}
1131 				else
1132 				{
1133 					s += "+";
1134 				}
1135 			}
1136 		}
1137 		Nornix.cookies.create(cookieName, s);
1138 	}
1139 
1140 	/**
1141 	 * Check if a link is emtpy or "fake-empty".
1142 	 * "#" or "javascript:;" are regarded as "fake-empty".
1143 	 * @param {HTMLAnchorElement} node HTML a element
1144 	 * @return {boolean} true if the link is empty or "fake-empty"
1145 	 */
1146 	function isHrefEmpty(node)
1147 	{
1148 		if (node.href && (node.href == emptyHref || node.href === "javascript:;"))
1149 		{
1150 			return true;
1151 		}
1152 		return !node.href;
1153 	}
1154 
1155 	/**
1156 	 * IE bugfix, forces IE to "re-render" the menu
1157 	 * and sets focus on elements that have fired an event
1158 	 */
1159 	function ieFix() {}
1160 	if (Nornix.util.isIe && Nornix.util.isIeLt7)
1161 	{
1162 		ieFix = function(li)
1163 		{
1164  			thiz.menu.style.position = "absolute";
1165  			thiz.menu.style.position = "relative";
1166 			try
1167 			{
1168 				window.event.srcElement.focus();
1169 			}
1170 			catch (err) {}
1171 		};
1172 	}
1173 	else
1174 	{
1175 		ieFix = function(){};
1176 	}
1177 
1178 	/**
1179 	 * Remove whitespace from node and children.
1180 	 * One level of unrolling the recursive call seems
1181 	 * to be the optimal choice in IE6 (which is slow in this case).
1182 	 * @param {HTMLElement} n HTML element
1183 	 */
1184 	function removeWhitespace(n)
1185 	{
1186 		var i = 0, c;
1187 		while (c = n.childNodes[i])
1188 		{
1189 			switch (c.nodeType)
1190 			{
1191 				case 1:
1192 					var j = 0, c2;
1193 					while (c2 = c.childNodes[j])
1194 					{
1195 						switch (c2.nodeType)
1196 						{
1197 							case 1: // element node
1198 								removeWhitespace(c2);
1199 								break;
1200 							case 3: // text node
1201 								if (!/\S/.test(c2.nodeValue))
1202 								{
1203 									c.removeChild(c2);
1204 									continue;
1205 								}
1206 								break;
1207 							case 8: // comments and empty textnodes
1208 								c.removeChild(c2);
1209 								continue;
1210 						}
1211 						j++;  // don't move!
1212 					}
1213 					break;
1214 				case 3:
1215 					if (!/\S/.test(c.nodeValue))
1216 					{
1217 						n.removeChild(c);
1218 						continue;
1219 					}
1220 					break;
1221 				case 8:
1222 					n.removeChild(c);
1223 					continue;
1224 			}
1225 			i++; // don't move this, the deleting of nodes depends on the ++ not being run!
1226 		}
1227 	}
1228 	
1229 	/*
1230 	 * Get node li element from a or span child element.
1231 	 * Traces parent nodes until a li element is found, or the
1232 	 * menu element is found and null returned.
1233 	 * @param {HTMLElement, String} el HTML element or id of element
1234 	 * @return {HTMLLIElement, undefined} node corresponding to element
1235 	function getTargetNode (el)
1236 	{
1237 		if (typeof(el) === 'string')
1238 		{
1239 			el = document.getElementById(el);
1240 		}
1241 		while (el && el !== thiz.menu)
1242 		{
1243 			if (Nornix.dom.eqNodeName(el, 'li'))
1244 			{
1245 				return el;
1246 			}
1247 			el = el.parentNode;
1248 		}
1249 		return null;
1250 	}
1251 	 */
1252 
1253 	/**
1254 	 * Check if a li element is representng a folder in the menu.
1255 	 * Works different when  allowing or not allowing whitespace in the menu.
1256 	 * @param {HTMLLIElement} li HTML li element
1257 	 * @return {boolean} true if the li element is a folder in the menu
1258 	 */
1259 	function isFolder (li)
1260 	{
1261 		if (allowWhitespace)
1262 		{
1263 			return Nornix.dom.findChildOfType(li, "ul", function (x) {return true;});
1264 		}
1265 		else
1266 		{
1267 			return li.childNodes.length > 1;
1268 		}
1269 	}
1270 
1271 	/**
1272 	 * Toggle a folder in the menu.
1273 	 * @param {HTMLLIElement} node HTML li element
1274 	 */
1275 	function toggle(node)
1276 	{
1277 		if (!isOpen(node))
1278 		{
1279 			makeOpen(node);
1280 		}
1281 		else
1282 		{
1283 			makeClosed(node);
1284 		}
1285 		ieFix();
1286 	}
1287 
1288 	/**
1289 	 * Make a folder in the menu open.
1290 	 * @param {HTMLLIElement} li HTML li element
1291 	 */
1292 	function makeOpen(li)
1293 	{
1294 		Nornix.css.swap(li, "closed", "open");
1295 		li.firstChild.title = thiz.texts.closeFolderTitle;
1296 	}
1297 
1298 	/**
1299 	 * Make a folder in the menu closed.
1300 	 * @param {HTMLLIElement} li HTML li element
1301 	 */
1302 	function makeClosed(li)
1303 	{
1304 		Nornix.css.swap(li, "open", "closed");
1305 		li.firstChild.title = thiz.texts.openFolderTitle;
1306 	}
1307 
1308 	/**
1309 	 * Close all folders.
1310 	 */
1311 	function closeAll()
1312 	{
1313 		var i = 0, menuFolders = thiz.menuFolders;
1314 		while (li = menuFolders[i++])
1315 		{
1316 			makeClosed(li);
1317 		}
1318 		ieFix();
1319 	}
1320 	
1321 	/**
1322 	 * Open all folders.
1323 	 */
1324 	function openAll()
1325 	{
1326 		var i = 0, menuFolders = thiz.menuFolders;
1327 		while (li = menuFolders[i++])
1328 		{
1329 			makeOpen(li);
1330 		}
1331 		ieFix();
1332 	}
1333 
1334 	/**
1335 	 * Move focus to the a element in this LI or DIV element.
1336 	 * @param {HTMLLIElement} node HTML li element (or other element)
1337 	 * @return {boolean} true if the move was successful.
1338 	 */
1339 	function focusNode (node)
1340 	{
1341 		if (!node || !node.firstChild) return false;
1342 		var n = node.firstChild;
1343 		if (focusAnchor(n)) return true;
1344 		if (!n.nextSibling || node === thiz.menu) return false;
1345 		n = n.nextSibling;
1346 		return focusAnchor(n);
1347 	}
1348 
1349 	/**
1350 	 * Move focus to the a element sent.
1351 	 * @param {HTMLAnchorElement} a HTML A element
1352 	 * @return {boolean} true if the move of focus was successful.
1353 	 */
1354 	function focusAnchor (a)
1355 	{
1356 		if (Nornix.dom.eqNodeName(a, "a"))
1357 		{
1358 			a.focus();
1359 			selectedItem = a;
1360 			return true;
1361 		}
1362 		return false;
1363 	}
1364 	
1365 	/**
1366 	 * Get the current best guess on the selected menu item.
1367 	 * @return {HTMLAnchorElement} the selected item
1368 	 */
1369 	function getSelectedItem()
1370 	{
1371 		return selectedItem ? selectedItem : currentItem;
1372 	}
1373 	
1374 	/**
1375 	 * Initialize the navigation object.
1376 	 * Calculate first, previous, next and last menu items.
1377 	 */
1378 	function initNavigation()
1379 	{
1380 		var node = selectedItem ? selectedItem : currentItem,
1381 		 nodes = getMenuNodes(), i = 0, a;
1382 		if (!node || Nornix.css.contains(node, "menuAction"))
1383 		{
1384 			node = navigation.first = searchRelatedNode(nodes, 0, 1);
1385 		}
1386 		if (!node) return null;
1387 		while (a = nodes[i++])
1388 		{
1389 			if (a === node)
1390 			{
1391 				i--;
1392 				break;
1393 			}
1394 		}
1395 		if (i >= nodes.length) return;
1396 		if (!navigation.first)
1397 		{
1398 			navigation.first = searchRelatedNode(nodes, 0, 1);
1399 		}
1400 		navigation.last = searchRelatedNode(nodes, nodes.length - 1, -1);
1401 		navigation.next = searchRelatedNode(nodes, i + 1, 1);
1402 		if (!navigation.next)
1403 		{
1404 			navigation.next = navigation.first;
1405 		}
1406 		if (i < 1)
1407 		{
1408 			navigation.previous = navigation.last;
1409 		}
1410 		else
1411 		{
1412 			navigation.previous = searchRelatedNode(nodes, i - 1, -1);
1413 		}
1414 	}
1415 
1416 	/**
1417 	 * Open page from navigation item.
1418 	 * @param {string} one of "first", "previous", "next", "last"
1419 	 */
1420 	function goToNavigationItem(name)
1421 	{
1422 		if (!navigation[name]) initNavigation();
1423 		if (!navigation[name]) return;
1424 		var node = navigation[name];
1425 		if (!node) return;
1426 		openToNode(node, true, true, true);
1427 	}
1428 
1429 	/**
1430 	 * Find the next real link in any direction.
1431 	 * @param {HTMLAnchorELement[]} nodes nodes to search in
1432 	 * @param {integer} i node position to start from (index in the nodes collection)
1433 	 * @param {integer} direction +1 is forward, -1 is backward
1434 	 * @return {HTMLAnchorElement|null} next "real" node in the selected direction
1435 	 */
1436 	function searchRelatedNode(nodes, i, direction)
1437 	{
1438 		var a;
1439 		while ((a = nodes[i]) && (a !== currentItem || currentItemHref === "javascript:;") && isHrefEmpty(a)) {i += direction}
1440 		if (i < nodes.length && i >= 0)
1441 		{
1442 			return nodes[i];
1443 		}
1444 		return null;
1445 	}
1446 
1447 	/**
1448 	 * Check if folder is open.
1449 	 * @param {HTMLLIElement} node HTML li element
1450 	 * @return {boolean} true if the folder is open
1451 	 */
1452 	function isOpen(li)
1453 	{
1454 		return li.className.search(openPattern) !== -1;
1455 	}
1456 
1457 	/**
1458 	 * Create an Anchor element for use as "action button".
1459 	 * @param {string} className class name of anchor
1460 	 * @param {string} title title of anchor
1461 	 * @return {HTMLAnchorElement} anchor "button"
1462 	 */
1463 	function createMenuActionAnchor(className, title, func)
1464 	{
1465 		var a = actionAnchor.cloneNode(false); // new <a> tag
1466 		a.className = className + " menuAction";
1467 		a.title = title;
1468 		thiz.registerMenuButton(a, func);
1469 		return a;
1470 	}
1471 	
1472 	/**
1473 	 * Add icons as HTML anchor elements for opening and closing all icons.
1474 	 */
1475 	function createOpenCloseAllIcons()
1476 	{
1477 		thiz.menu.insertBefore(createMenuActionAnchor("closeTree", thiz.texts.closeTreeTitle, closeAll),
1478 		 thiz.menu.firstChild.nextSibling);
1479 		thiz.menu.insertBefore(createMenuActionAnchor("openTree", thiz.texts.openTreeTitle, openAll),
1480 		 thiz.menu.firstChild.nextSibling.nextSibling);
1481 	}
1482 	
1483 	/**
1484 	 * Add icons as HTML anchor elements for navigating.
1485 	 */
1486 	function createNavigationIcons()
1487 	{
1488 /*		var relLink = document.createElement("link");
1489 		var link = 
1490 		document.documentElement.firstChild.appendChild(link.cloneNode(false));*/
1491 		thiz.menu.appendChild(createMenuActionAnchor("treeNavigation goFirstTree", thiz.texts.goFirstTitle, thiz.goToFirstNode));
1492 		thiz.menu.appendChild(createMenuActionAnchor("treeNavigation goPrevTree", thiz.texts.goPrevTitle, thiz.goToPrevNode));
1493 		thiz.menu.appendChild(createMenuActionAnchor("treeNavigation goNextTree", thiz.texts.goNextTitle, thiz.goToNextNode));
1494 		thiz.menu.appendChild(createMenuActionAnchor("treeNavigation goLastTree", thiz.texts.goLastTitle, thiz.goToLastNode));
1495 	}
1496 }
1497 
1498 /**
1499  * Configuration of the menu.
1500  * 
1501  * To change all instances, use: Nornix.TreeMenu.prototype.config.parameter = value;
1502  * To change a particular instance, use: treemenu.config.parameter = value;
1503  * 
1504  * @memberOf Nornix.TreeMenu
1505  * @namespace Nornix.TreeMenu.config
1506  * @param {boolean} dynamicClasses Turn dynamic class population on/off.
1507  * @param {boolean} openCloseAll Add icons for open/close all folders.
1508  * @param {boolean} navigationIcons Add icons for navigation.
1509  * @param {boolean} markCurrentItem Add an empty span to mark the current element.
1510  * @param {boolean} focusCurrentItem Move focus to the current menu item.
1511  * @param {string|boolean} contentElement ID of the page content containing element. Set to false to disable.
1512  * @param {string|boolean} menuLinkElement ID of a link to the menu. Set to false to disable.
1513  * @param {string[]} preloadImages Names of images to preload. Be careful with the order of the images!
1514  * @param {string} imagePath Path to the images used in the menu. Used for preloading.
1515  * @param {string} imageExtension Extension for image files. A dot is not automatically prepended!
1516  * @param {integer} searchNodeMode How to resolve nodes when searching for them:
1517  * 0: try text search first, then try href attributes
1518  * 1: try href attributes first, then try text
1519  * 2: try only text search
1520  * 3: try only href attribute search
1521  */
1522 Nornix.TreeMenu.prototype.config =
1523 {
1524 	"dynamicClasses" : true,
1525 	"openCloseAll" : true,
1526 	"navigationIcons" : true,
1527 	"markCurrentItem" : false,
1528 	"focusCurrentItem" : false,
1529 	"contentElement" : false,
1530 	"menuLinkElement" : false,
1531 	"preloadImages" :
1532 		[
1533 			"treemenu-sprites",
1534 			"treemenu-line",
1535 			"treemenu-corner"
1536 /*			"home-icon",
1537 			"close-icon",
1538 			"open-icon",
1539 			"plus-node",
1540 			"minus-node",
1541 			"folder-closed-icon",
1542 			"doc-node-icon",
1543 			"folder-open-icon",
1544 			"treemenu-line",
1545 			"treemenu-current",
1546 			"first",
1547 			"previous",
1548 			"next",
1549 			"last"*/
1550 		],
1551 	"imagePath" : "style/nornix-",
1552 	"imageExtension" : ".png",
1553 	"searchNodeMode" : 0
1554 };
1555 
1556 /**
1557  * Extension points to add your own hooks inside the tree menu.
1558  * 
1559  * The functions used will receive the clicked HTMLAnchorElement as
1560  * a parameter. To prevent any further event handling, let the function
1561  * return false, otherwise let it return true.
1562  * 
1563  * To change all instances, use: Nornix.TreeMenu.prototype.hooks.parameter = value;
1564  * To change a particular instance, use: treemenu.hooks.parameter = value;
1565  * 
1566  * @memberOf Nornix.TreeMenu
1567  * @namespace Nornix.TreeMenu.hooks
1568  * @param {Function|boolean} dynamicDocumentLinks Function to handle document node clicks (or set to false).
1569  * @param {Function|boolean} dynamicFolderLinks Function to handle folder node clicks (or set to false).
1570  */
1571 Nornix.TreeMenu.prototype.hooks =
1572 {
1573 	"dynamicDocumentLinks" : false,
1574 	"dynamicFolderLinks" : false
1575 };
1576 
1577 /**
1578  * Texts used by the tree menu.
1579  * 
1580  * To change all instances, use: Nornix.TreeMenu.prototype.texts.parameter = value;
1581  * To change a particular instance, use: treemenu.texts.parameter = value;
1582  * 
1583  * To change all texts use: Nornix.TreeMenu.prototype.texts = {...}; or
1584  * treemenu.prototype.texts = {...}; (see source code!)
1585  * 
1586  * @memberOf Nornix.TreeMenu
1587  * @namespace Nornix.TreeMenu.texts
1588  * @param {string} closeTreeTitle Text for "close all" title attribute
1589  * @param {string} openTreeTitle Text for "open all" title attribute.
1590  * @param {string} closeFolderTitle Text for "close folder" title attribute.
1591  * @param {string} openFolderTitle Text for "open folder" title attribute.
1592  * @param {string} goFirstTitle Text for "go to first" title attribute.
1593  * @param {string} goLastTitle Text for "go to last" title attribute.
1594  * @param {string} goNextTitle Text for "go to next" title attribute.
1595  * @param {string} goPrevTitle Text for "go to previous" title attribute.
1596  */
1597 Nornix.TreeMenu.prototype.texts =
1598 {
1599 	"closeTreeTitle" : "close all folders",
1600 	"openTreeTitle" : "open all folders",
1601 	"closeFolderTitle" : "close folder",
1602 	"openFolderTitle" : "open folder",
1603 	"goFirstTitle" : "first page",
1604 	"goLastTitle" : "last page",
1605 	"goNextTitle" : "next page",
1606 	"goPrevTitle" : "previous page"
1607 };
1608 
1609 
1610 // start the script
1611 var treemenu = new Nornix.TreeMenu();
1612 treemenu.start();
1613