tree.js 4.77 KB
Newer Older
Ad Schellevis's avatar
Ad Schellevis committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * 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 ?/, ' ');
		}
	}
}
20

Ad Schellevis's avatar
Ad Schellevis committed
21 22 23 24 25 26 27
/*
 * Initialise a tree node, converting all its LIs appropriately
 */
function initTree(el) {
	var i,j;
	var spanA, spanB, spanC;
	var startingPoint, stoppingPoint, childUL;
28

Ad Schellevis's avatar
Ad Schellevis committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
	// 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';
45 46


Ad Schellevis's avatar
Ad Schellevis committed
47 48 49 50 51 52 53 54 55 56 57 58 59
			// 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;
60
					break;
Ad Schellevis's avatar
Ad Schellevis committed
61 62
				}
			}
63

Ad Schellevis's avatar
Ad Schellevis committed
64 65 66 67
			// Move all the nodes up until that point into spanC
			for(j=startingPoint;j<stoppingPoint;j++) {
				spanC.appendChild(li.childNodes[startingPoint]);
			}
68

Ad Schellevis's avatar
Ad Schellevis committed
69 70 71
			// Insert the outermost extra span into the tree
			if(li.childNodes.length > startingPoint) li.insertBefore(spanA, li.childNodes[startingPoint]);
			else li.appendChild(spanA);
72

Ad Schellevis's avatar
Ad Schellevis committed
73 74 75 76 77 78 79 80 81
			// Process the children
			if(childUL != null) {
				if(initTree(childUL)) {
					addClass(li, 'children', 'closed');
					addClass(spanA, 'children', 'spanClosed');
				}
			}
		}
	}
82

Ad Schellevis's avatar
Ad Schellevis committed
83 84 85 86 87 88 89 90 91
	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;
	}
92

Ad Schellevis's avatar
Ad Schellevis committed
93
}
94

Ad Schellevis's avatar
Ad Schellevis committed
95 96 97 98 99 100 101

/*
 * +/- 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;
102

Ad Schellevis's avatar
Ad Schellevis committed
103
	while(el != null && (!el.tagName || el.tagName.toLowerCase() != "li")) el = el.parentNode;
104

Ad Schellevis's avatar
Ad Schellevis committed
105 106 107 108 109
	// Get UL within the LI
	var childSet = findChildWithTag(el, 'ul');
	var topSpan = findChildWithTag(el, 'span');

	if( force != null ){
110

Ad Schellevis's avatar
Ad Schellevis committed
111 112 113 114 115 116
		if( force == "open"){
			treeOpen( topSpan, el );
		}
		else if( force == "close" ){
			treeClose( topSpan, el );
		}
117

Ad Schellevis's avatar
Ad Schellevis committed
118
	}
119

Ad Schellevis's avatar
Ad Schellevis committed
120 121
	else if( childSet != null) {
		// Is open, close it
122
		if(!el.className.match(/(^| )closed($| )/)) {
Ad Schellevis's avatar
Ad Schellevis committed
123 124
			treeClose( topSpan, el );
		// Is closed, open it
125
		} else {
Ad Schellevis's avatar
Ad Schellevis committed
126 127 128 129 130 131 132 133 134 135
			treeOpen( topSpan, el );
		}
	}
}


function treeOpen( a, b ){
	removeClass(a,'spanClosed');
	removeClass(b,'closed');
}
136 137


Ad Schellevis's avatar
Ad Schellevis committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
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, '');
171
}
Ad Schellevis's avatar
Ad Schellevis committed
172 173 174

/*
 * Handlers for automated loading
175
 */
Ad Schellevis's avatar
Ad Schellevis committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
 _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);