/* MULTI-COLUMN LIST

*** NOTES ***
[2005-09-20 bsweeney]
Please refer to the documentation at http://www.project2061.org/includes/CodeLibrary/MultiColumnList/multiColumnList.manual.htm
*/

function mcl_bwcheck(){
	this.ver=navigator.appVersion;
	this.agent=navigator.userAgent;
	this.dom=document.getElementById?1:0;
	this.opera5=this.agent.indexOf("Opera 5")>-1;
	this.opera=this.agent.indexOf("Opera")>-1;
	this.safari=this.agent.indexOf("Safari")>-1;
	this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
	this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
	this.ie7=(this.ver.indexOf("MSIE 7")>-1 && this.dom)?1:0;
	this.ie8=(this.ver.indexOf("MSIE 8")>-1 && this.dom)?1:0;
	this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
	this.ie=this.ie4||this.ie5||this.ie6||this.ie7||this.ie8;
	this.mac=this.agent.indexOf("Mac")>-1;
	this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
	this.ns4=(document.layers && !this.dom)?1:0;
	this.ff2=(this.agent.indexOf("Firefox/2")>-1 && this.agent.indexOf("Gecko")>-1)?1:0;
	this.bw=(this.ie8 || this.ie7 || this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5);
	return this;
}

function mcl_addEvent(obj, evType, fn, useCapture){
	if (obj.addEventListener){
		obj.addEventListener(evType, fn, useCapture);
		return true;
	} else if (obj.attachEvent){
		var r = obj.attachEvent("on"+evType, fn);
		return r;
	}
}

function mcl_init (e) {
	var i, j, k
	var araListNodes = new Array();
	
	/* Add references to an array of list nodes that are to be styled for multiple columns. */
	var objCollection = document.getElementsByTagName('ul');
	for (i = 0; i < objCollection.length; i++) {
		objCollection[i].className.search(/\bmultiColumnList\b/ig) != -1 ? araListNodes.push(objCollection[i]) : null;
	}
	objCollection = document.getElementsByTagName('ol');
	for (i = 0; i < objCollection.length; i++) {
		objCollection[i].className.search(/\bmultiColumnList\b/ig) != -1 ? araListNodes.push(objCollection[i]) : null;
	}
	objCollection = null;
	
	/* For each list, parse the list items for layout */
	for (i = 0; i < araListNodes.length; i++) {
		if (araListNodes[i].className.indexOf(/\bmultiColumnList\b/ig)) {
			araListNodes[i].className += (araListNodes[i].className.length > 0 ? ' ' : '') + 'mclList';
			
			/* IE does not seem to handle list element positioning well. Bullets and numbering 
			are corrupted in the process. For now we'll reclassify the elements as block elements 
			if the script is run on IE (by adding the "iefix" class in addition to "mclList). */
			mcl_browser.ie || !mcl_showBullets ? araListNodes[i].className += ' nobullets' : null;
			
			/* Safari doesn't seem to take into account bullets when positioning and formatting list 
			items. This will add a class reference to the list element that will add a little padding 
			to the right of a list item in order to keep its content from overlapping the adjoining 
			column's bullets. */
			mcl_showBullets && mcl_browser.safari ? araListNodes[i].className += ' safarifix' : null;
			
			var intWidth = parseInt(araListNodes[i].offsetWidth);
			var intHeight = 0;
			var intColumnHeight = 0;
			var objCollection = araListNodes[i].getElementsByTagName('li');
			j = 0;
			
			/* Since IE doesn't handle bullets or numbering very well we're going to recreate that 
			information in the text of the list item. Currently the script doesn't take into account 
			list depth or the type attribute specifying the number. Also, the styling on the bullet to 
			provide appropriate positioning has to be done manually in the accompanying CSS file, though 
			we may be able to do this through script. This needs to happen early in the process in order 
			to take into account the height/width of the list items with the generated numbering applied. */
			if (mcl_browser.ie && araListNodes[i].nodeName == 'OL' && mcl_showBullets) {
				var orderedListStart = araListNodes[i].getAttribute('start') ? parseInt(araListNodes[i].getAttribute('start')) : 1;
				while (j < objCollection.length) {
					objCollection[j].innerHTML = '<span style="float: left; width: 1.5em; text-align: right; margin-right: .5em; margin-bottom: .5em;" class="mclOLBullet">' + (orderedListStart+j) + '.</span>' + objCollection[j].innerHTML;
					j++;
				}
				j = 0;
			}
			
			/* Safari (3.2.2) and Opera (9.63) on Windows is having problems displaying the bullets. The bullets 
			are there, but only show up if something is positioned on top of them. The code for creating the IE 
			numbering seems to provide the necessary cover. */
			if ((mcl_browser.opera || mcl_browser.safari) && araListNodes[i].nodeName == 'OL') {
				var orderedListStart = araListNodes[i].getAttribute('start') ? parseInt(araListNodes[i].getAttribute('start')) : 1;
				while (j < objCollection.length) {
					objCollection[j].innerHTML = '<span style="float: left; width: 2em; height: 1em; text-align: right; margin-right: .5em; margin-bottom: .5em;" class="mclOLBullet"></span>' + objCollection[j].innerHTML;
					j++;
				}
				j = 0;
			}
			
			/* Set the width of the list items then determine the total height of all list items. */
			while (j < objCollection.length) {
				objCollection[j].style.width = 100/mcl_columnCount + '%';
				intHeight += parseInt(objCollection[j].offsetHeight);
				j++;
			}
			var j = 0;
			
			/* Set up the first column based on the number of columns and the list height. */
			while (intColumnHeight < (intHeight/mcl_columnCount)) {
				intColumnHeight += parseInt(objCollection[j].offsetHeight);
				objCollection[j].style.marginLeft = '0px';
				j++;
			}
			
			/* Set up subsequent columns. */
			for (k=2; k <= mcl_columnCount; k++) {
				/* Sometime more columns are specified than can be filled (based on the height calculations).
				When this happens check to see if the current item has already been positioned. If so, we have 
				reached the end of the list and don't need to process the list items any further. */
				if (isNaN(parseInt(objCollection[j].style.marginTop))) {
					/* Set the top margin on the first list item of the column based on its current position and 
					the position of the first list item. */
					objCollection[j].style.marginTop = -1 * (parseInt(objCollection[j].offsetTop) - parseInt(objCollection[0].offsetTop)) + 'px';
					objCollection[j].mclColumnTop = true;
					var intCurrentHeight = 0;
					/* Set the width of each list item and determine the column height (which should be less then 
					or equal to the height of the first column). */
					while (intCurrentHeight <= (intColumnHeight) && j < objCollection.length) {
						intCurrentHeight += parseInt(objCollection[j].offsetHeight);
						objCollection[j].style.marginLeft = ((k-1)*(100/mcl_columnCount)) + '%';
						/* IE weirdness. For some reason IE does not always assign a margin when % is used. We'll 
						set the margin using pixels if the current offset is zero. */
						parseInt(objCollection[j].offsetLeft) == 0 ? objCollection[j].style.marginLeft = ((k-1)*(araListNodes[i].offsetWidth/mcl_columnCount)) + 'px' : null;
						/* IE weirdness. In quirks mode the width includes the margin so we need to take that 
						value into mind when determining the width of the list item. */
						(mcl_browser.ie6 || mcl_browser.ie7 || mcl_browser.ie8) && document.compatMode && document.compatMode == "BackCompat" ? objCollection[j].style.width = ((1/(mcl_columnCount+1-k))*100) + '%' : null;
						j++;
					}
					j-=1;
				}
			}
			/* Set the height of the last list item so that it fills in the empty space at the end of the 
			column its in. This will prevent following nodes from overlapping the list. */
			objCollection[j].style.height = (parseInt(objCollection[j].offsetHeight) + intColumnHeight - intCurrentHeight) + 'px';
		}
		
		/* Record the current height of the list. */
		araListNodes[i].mclHeight = parseInt(araListNodes[i].offsetHeight);
		/* Push the list reference onto the global array. */
		araMCLLists.push(araListNodes[i]);
	}
	mcl_intevalID = setInterval('mcl_correct()', 100);
}

function mcl_correct () {
	/* Since we have to specify the margin-top absolutely (in pixels) we need to constantly check to 
	make sure the height of the list hasn't changed through a font size change or list width change. This 
	function doesn't take into account the addition or removal of list items. We're assuming the list item 
	distribution is remaining static so we'll just correct the placement of each column. */
	var i, j, k
	var intColumnHeight = 0;
	var intCurrentHeight = 0;
	for (i = 0; i < araMCLLists.length; i++) {
		/* If the recorded height doesn't match the current height correct the list layout. */
		if (araMCLLists[i].mclHeight != parseInt(araMCLLists[i].offsetHeight)) {
			objListNodes = araMCLLists[i].getElementsByTagName('li');
			for (j = 0; j < objListNodes.length; j++) {
				/* If the current list item's mclColumnTop JS property is true it's the top of a column. 
				Reset the margin and calculate a new value. Also reset the column height calculation. */
				if (objListNodes[j].mclColumnTop == true) {
					objListNodes[j].style.marginTop = '0px';
					objListNodes[j].style.marginTop = -1 * (parseInt(objListNodes[j].offsetTop) - parseInt(objListNodes[0].offsetTop)) + 'px';
					intColumnHeight < intCurrentHeight ? intColumnHeight = intCurrentHeight : null;
					intCurrentHeight = 0;
				}
				/* Add the current list item's height to the column height. */
				intCurrentHeight += parseInt(objListNodes[j].offsetHeight);
				/* If a height is assigned to the list item we're assuming it's the last item in the list. 
				Recalculate the height necessary to fill in the empty space. */
				if (!isNaN(parseInt(objListNodes[j].style.height))) {
					objListNodes[j].style.height = (parseInt(objListNodes[j].offsetHeight) + intColumnHeight - intCurrentHeight) + 'px';
				}
			}
		}
		/* Record the current height of the list. */
		araMCLLists[i].mclHeight = parseInt(araMCLLists[i].offsetHeight);
	}
}

/* This function addresses a multi-column styling bug in FF2.
For details on the bug see http://www.eclecticgeek.com/bugs/cssMultiColumnOL/ */
function mcl_fixOL () {
	var dom_ol_nodes = document.getElementsByTagName('ol');
	for (i = 0; i < dom_ol_nodes.length; i++) {
		if (dom_ol_nodes[i].className.indexOf('multiColumnList')>-1) {
			dom_a_nodes = dom_ol_nodes[i].getElementsByTagName('a');
			for (j = 0; j < dom_a_nodes.length; j++) {
				mcl_addEvent(dom_a_nodes[j], 'mousedown', function (evt) { evt.preventDefault(); }, false);
			}
		}
	}
}

/* Global variable and event initialization */
var araMCLLists = new Array();
var mcl_browser = new mcl_bwcheck();
var mcl_intervalID = new Number();
/* Current versions of Mozilla support CSS3 columns, albeit in the "experimental" 
mode (i.e. prepended with -moz). Development versions of Safari support this feature 
as well in experimental form, but for now we'll continue to use the script since we don't 
have Safari versioning built in to the browser detection object. IE still has no support 
for this feature (of which I am aware). */
mcl_browser.ie || mcl_browser.safari || mcl_browser.opera ? mcl_addEvent(window, 'load', mcl_init, false) : null;

/* There is a bug in FF2 that causes the last column in an ordered list to renumber if it 
is styled with multiple columns. mcl_fixOL contains code that will prevent this from 
happening. */
mcl_browser.ff2 ? mcl_addEvent(window, 'load', mcl_fixOL, false) : null;


/* BEGIN USER-DEFINED VARIABLES */
var mcl_columnCount = 2;
var mcl_showBullets = true;
/* END USER-DEFINED VARIABLES */
