/* * Content-separated javascript tree widget * Copyright (C) 2005 SilverStripe Limited * Feel free to use this on your websites, but please leave this message in the fies * http://www.silverstripe.com/blog */ /* * Initialise all trees identified by <ul class="tree"> */ function autoInit_trees() { var candidates = document.getElementsByTagName('ul'); for(var i=0;i<candidates.length;i++) { if(candidates[i].className && candidates[i].className.indexOf('tree') != -1) { initTree(candidates[i]); candidates[i].className = candidates[i].className.replace(/ ?unformatted ?/, ' '); } } } /* * Initialise a tree node, converting all its LIs appropriately */ function initTree(el) { var i,j; var spanA, spanB, spanC; var startingPoint, stoppingPoint, childUL; // Find all LIs to process for(i=0;i<el.childNodes.length;i++) { if(el.childNodes[i].tagName && el.childNodes[i].tagName.toLowerCase() == 'li') { var li = el.childNodes[i]; // Create our extra spans spanA = document.createElement('span'); spanB = document.createElement('span'); spanC = document.createElement('span'); spanA.appendChild(spanB); spanB.appendChild(spanC); spanA.className = 'a ' + li.className.replace('closed','spanClosed'); spanA.onMouseOver = function() {}; spanB.className = 'b'; spanB.onclick = treeToggle; spanC.className = 'c'; // Find the UL within the LI, if it exists stoppingPoint = li.childNodes.length; startingPoint = 0; childUL = null; for(j=0;j<li.childNodes.length;j++) { if(li.childNodes[j].tagName && li.childNodes[j].tagName.toLowerCase() == 'div') { startingPoint = j + 1; continue; } if(li.childNodes[j].tagName && li.childNodes[j].tagName.toLowerCase() == 'ul') { childUL = li.childNodes[j]; stoppingPoint = j; break; } } // Move all the nodes up until that point into spanC for(j=startingPoint;j<stoppingPoint;j++) { spanC.appendChild(li.childNodes[startingPoint]); } // Insert the outermost extra span into the tree if(li.childNodes.length > startingPoint) li.insertBefore(spanA, li.childNodes[startingPoint]); else li.appendChild(spanA); // Process the children if(childUL != null) { if(initTree(childUL)) { addClass(li, 'children', 'closed'); addClass(spanA, 'children', 'spanClosed'); } } } } if(li) { // li and spanA will still be set to the last item addClass(li, 'last', 'closed'); addClass(spanA, 'last', 'spanClosed'); return true; } else { return false; } } /* * +/- toggle the tree, where el is the <span class="b"> node * force, will force it to "open" or "close" */ function treeToggle(el, force) { el = this; while(el != null && (!el.tagName || el.tagName.toLowerCase() != "li")) el = el.parentNode; // Get UL within the LI var childSet = findChildWithTag(el, 'ul'); var topSpan = findChildWithTag(el, 'span'); if( force != null ){ if( force == "open"){ treeOpen( topSpan, el ); } else if( force == "close" ){ treeClose( topSpan, el ); } } else if( childSet != null) { // Is open, close it if(!el.className.match(/(^| )closed($| )/)) { treeClose( topSpan, el ); // Is closed, open it } else { treeOpen( topSpan, el ); } } } function treeOpen( a, b ){ removeClass(a,'spanClosed'); removeClass(b,'closed'); } function treeClose( a, b ){ addClass(a,'spanClosed'); addClass(b,'closed'); } /* * Find the a child of el of type tag */ function findChildWithTag(el, tag) { for(var i=0;i<el.childNodes.length;i++) { if(el.childNodes[i].tagName != null && el.childNodes[i].tagName.toLowerCase() == tag) return el.childNodes[i]; } return null; } /* * Functions to add and remove class names * Mac IE hates unnecessary spaces */ function addClass(el, cls, forceBefore) { if(forceBefore != null && el.className.match(new RegExp('(^| )' + forceBefore))) { el.className = el.className.replace(new RegExp("( |^)" + forceBefore), '$1' + cls + ' ' + forceBefore); } else if(!el.className.match(new RegExp('(^| )' + cls + '($| )'))) { el.className += ' ' + cls; el.className = el.className.replace(/(^ +)|( +$)/g, ''); } } function removeClass(el, cls) { var old = el.className; var newCls = ' ' + el.className + ' '; newCls = newCls.replace(new RegExp(' (' + cls + ' +)+','g'), ' '); el.className = newCls.replace(/(^ +)|( +$)/g, ''); } /* * Handlers for automated loading */ _LOADERS = Array(); function callAllLoaders() { var i, loaderFunc; for(i=0;i<_LOADERS.length;i++) { loaderFunc = _LOADERS[i]; if(loaderFunc != callAllLoaders) loaderFunc(); } } function appendLoader(loaderFunc) { if(window.onload && window.onload != callAllLoaders) _LOADERS[_LOADERS.length] = window.onload; window.onload = callAllLoaders; _LOADERS[_LOADERS.length] = loaderFunc; } appendLoader(autoInit_trees);