/*
  Column layout
  Version: v0.5

  Copyright (C) 2008 by Systemantics, Bureau for Informatics

  Lutz Issler
  Mauerstr. 10-12
  52064 Aachen
  GERMANY

  Web:    www.systemantics.net
  Email:  mail@systemantics.net

  This script is NOT open source. If you want to use this script,
  please contact Systemantics in order to obtain a licence.
*/
var COLUMN_LAYOUT = {
	// Fields
	buttonClassName : "",
	buttonBackText : "",
	buttonNextText : "",
	numCols : 1000,
	numColsById : new Object(),
	fixedCols : false,
	balanceSinglePage : false,
	cloneEls : new Object(),
	// Sets the button properties
	setPageProperties : function(buttonClassName, buttonNextText, buttonBackText) {
		this.buttonClassName = buttonClassName;
		this.buttonBackText = buttonBackText;
		this.buttonNextText = buttonNextText;
	},
	// Sets the number of columns
	setCols : function(numCols) {
		this.numCols = numCols;
	},
	// Sets whether or not there should be exactly >numCols< columns
	setFixedCols : function(fixedCols) {
		this.fixedCols = fixedCols;
	},
	// Sets whether or not to balance the length of the columns
	// if there is only a single page
	setBalanceMode : function(balanceMode) {
		this.balanceSinglePage = balanceMode;
	},
	// Internal: Create a button
	createButton : function(text, handler) {
		button = document.createElement("A");
		button.className = COLUMN_LAYOUT.buttonClassName;
		button.href="#";
		button.onclick = handler;
		button.appendChild(document.createTextNode(text));
		return button;
	},
	// Internal: Layout an element
	_layoutElement : function(el, colClassName, fixedCols) {
		// Some semi-global variables
		var colHeight;
		var colWidth;
		var col;
		var currentColEl;
		var cols = new Array();
		var colNum = 0;
		var colSet = 0;

		// Save numCols property for this element
		this.numColsById[el.id] = this.numCols;

		// Remove child nodes
		while (el.hasChildNodes()) {
			el.removeChild(el.firstChild);
		}

		// Macro function (with side effects)
		function _newColumn() {
			colNum++;
			col = document.createElement("DIV");
			col.id = el.id+"__col"+(colSet*COLUMN_LAYOUT.numCols+colNum);
			col.className = colClassName;
			el.appendChild(col);
			currentColEl = col;
			colWidth = col.clientWidth;
			cols.push(col);
		}

		// Initialize the column height
		if (fixedCols) {
			// Chose a height that results in exactly >numCols< columns
			var lineHeight = parseInt(DHTMLAPI.getComputedStyle(el, "line-height"));
			col = document.createElement("DIV");
			col.className = colClassName;
			for (var i=0; i<this.cloneEls[el.id].childNodes.length; i++) {
				col.appendChild(this.cloneEls[el.id].childNodes[i].cloneNode(true));
			}
			el.appendChild(col);
			colHeight = Math.ceil(DHTMLAPI.getElementHeight(col)/this.numCols);
			if (colHeight%lineHeight>0) {
				colHeight += lineHeight;
			}
			el.removeChild(col);
		} else {
			// Take the height from the element to be layouted
			colHeight = DHTMLAPI.getElementHeight(el);
		}

		// Start with first child of the initial node
		var currentEl = this.cloneEls[el.id].firstChild;
		var subnodes = new Array();
		var href = null;
		var lastNodeType = 0;
		_newColumn();
		while (currentEl) {
			if (currentEl.nodeType==1) {
				// An element node
				// Create a similar node and append it to the current column
				var newEl = currentEl.cloneNode(false);
				currentColEl.appendChild(newEl);
				if (currentEl.firstChild) {
					subnodes.push(currentEl.cloneNode(false));
					currentColEl = newEl;
					currentEl = currentEl.firstChild;
				} else {
					// Advance to next sibling on this or a parent level
					while (currentEl && !currentEl.nextSibling) {
						currentEl = currentEl.parentNode;
						currentColEl = currentColEl.parentNode;
						var node = subnodes.pop();
						// Hack: delete the previously saved HREF
						if (node=="A") {
							href = null;
						}
					}
					if (currentEl) {
						currentEl = currentEl.nextSibling;
					}
				}
				lastNodeType = 1;
			} else if (currentEl.nodeType==3) {
				// A text node
				var newEl = document.createTextNode("");
				currentColEl.appendChild(newEl);
				// Append word by word
				var words = currentEl.data.split(/ /);
				for (var i=0; i<words.length; i++) {
					if (lastNodeType==3) {
						newEl.appendData(" ");
					}
					newEl.appendData(words[i]);
					if (col.offsetHeight>colHeight) {
						// This column is full

						if (!fixedCols && colNum==COLUMN_LAYOUT.numCols) {
							// Add the "next" button
							col.appendChild(COLUMN_LAYOUT.createButton(COLUMN_LAYOUT.buttonNextText, function(e) {
								if (!e) var e = window.event;
								var ids = (e.target ? e.target : e.srcElement).parentNode.getAttribute("id").split("__");
								var col = parseInt(ids[1].substr(3));
								for (var i=1; i<=col; i++) {
									DHTMLAPI.setDisplayMode(ids[0]+"__col"+i, "none");
								}
								for (var i=col+1; i<=col+COLUMN_LAYOUT.numColsById[ids[0]]; i++) {
									DHTMLAPI.setDisplayMode(ids[0]+"__col"+i, "block");
								}
							}));
							do {
								// Remove words until the "next" button
								// fits into the current column
								newEl.data = newEl.data.substr(0, newEl.data.length-words[i].length-1);
								i--;
							} while (i>=0 && col.offsetHeight>colHeight);
							i++;

							colNum = 0;
							colSet++;
						} else {
							// Remove only the last word
							newEl.data = newEl.data.substr(0, newEl.data.length-words[i].length-1);
						}

						// Start a new column
						_newColumn();

						// Add the same subnode nesting to the new column
						// as there was in the old column
						for (var j=0; j<subnodes.length; j++) {
							newEl = subnodes[j].cloneNode(false);
							currentColEl.appendChild(newEl);
							currentColEl = newEl;
						}

						// Add a text node at the bottom level
						// in order to continue the column
						newEl = document.createTextNode(words[i]);
						currentColEl.appendChild(newEl);
					}
					lastNodeType = 3;
				}
				// Advance to next sibling on this or a parent level
				while (currentEl && !currentEl.nextSibling) {
					currentEl = currentEl.parentNode;
					currentColEl = currentColEl.parentNode;
					var node = subnodes.pop();
					// Hack: delete the previously saved HREF
					if (node=="A") {
						href = null;
					}
					lastNodeType = 0;
				}
				if (currentEl) {
					currentEl = currentEl.nextSibling;
				}
			} else {
				// Any other node (comments, for instance)
				// Advance to next sibling on this or a parent level
				lastNodeType = currentEl.nodeType;
				while (currentEl && !currentEl.nextSibling) {
					currentEl = currentEl.parentNode;
					currentColEl = currentColEl.parentNode;
					var node = subnodes.pop();
					// Hack: delete the previously saved HREF
					if (node=="A") {
						href = null;
					}
				}
				if (currentEl) {
					currentEl = currentEl.nextSibling;
				}
			}
		}
		if (colSet>0 && colNum<=COLUMN_LAYOUT.numCols) {
			col.appendChild(COLUMN_LAYOUT.createButton(
				COLUMN_LAYOUT.buttonBackText,
				function(e) {
					if (!e) var e = window.event;
					var ids = (e.target ? e.target : e.srcElement).parentNode.getAttribute("id").split("__");
					var col = parseInt(ids[1].substr(3));
					for (var i=col-COLUMN_LAYOUT.numColsById[ids[0]]+1; i<=col; i++) {
						DHTMLAPI.setDisplayMode(ids[0]+"__col"+i, "none");
					}
					for (var i=1; i<=COLUMN_LAYOUT.numColsById[ids[0]]; i++) {
						DHTMLAPI.setDisplayMode(ids[0]+"__col"+i, "block");
					}
				}
			));
		}
		return cols;
	},
	// Layouts the specified element
	layoutElement : function(el, colClassName) {
		if (el==null) {
			return;
		}

		// Clone original node for later traversal
		if (!this.cloneEls[el.id]) {
			this.cloneEls[el.id] = el.cloneNode(true);
		}

		// Layout the columns
		var cols = this._layoutElement(el, colClassName, this.fixedCols);

		// Check whether we have to re-run the algorithm
		// to balance the column lengths
		if (this.balanceSinglePage && cols.length<=this.numCols) {
			this._layoutElement(el, colClassName, true);
		}

		// Hide all columns with index > numCols
		for (var i=this.numCols; i<cols.length; i++) {
			cols[i].style.display = "none";
		}
	},
	// Layouts all DIV elements which belong to the specified CSS class
	layoutAll : function(className, colClassName) {
		var divs = document.getElementsByTagName("DIV");
		var search = new RegExp(".*\\b"+className+"\\b.*");
		for (var d=0; d<divs.length; d++) {
			if (search.test(divs[d].className)) {
				this.layoutElement(divs[d], colClassName);
			}
		}
	}
}

