// $Id: lib.js,v 1.1 2004/09/30 20:18:36 rcarver Exp $

// ===========================================================================
// ===========================================================================

if (typeof Function.prototype.apply == "undefined") {
	// Function.apply does not exist in ie5mac, ie5win
	Function.prototype.apply = function(target, args) {
		target.__apply_tmp__ = this;
		if (args && args.length) {
			var call = "args[0]";
			for (var i = 1; i < args.length; i++) {
				call += ", args[" + i + "]";
			}
			eval("target.__apply_tmp__(" + call + ");");
		} else {
			target.__apply_tmp__();
		}
		delete(target.__apply_tmp__);
	}
}


if (typeof Array.prototype.indexOf == "undefined") {
	Array.prototype.indexOf = function(value) {
		var len = this.length;
		for (var i = 0; i < len; i++) {
			if (this[i] == value) return i;
		}
		return -1;
	}
}
/*
if (typeof Array.prototype.concat == "undefined") {
	Array.prototype.concat = function(array) {
		var a = [];
		var len = this.length;
		for (var i = 0; i < len; i++) a[i] = this[i];
		var len = array.length;
		for (var i = 0; i < len; i++) a[i] = array[i];
		return a;
	}
}
*/
Array.fromCollection = function(collection) {
	var a = [];
	var len = collection.length;
	for (var i = 0; i < len; i++) a[i] = collection[i];
	return a;
}


// ===========================================================================
// ===========================================================================


/**
 * Broadcaster is a simple publish/subscribe system modelled after Flash's ASBroadcaster
 **/
Broadcaster = new Object();
Broadcaster.initialize = function(object) {
	object.addListener = this._addListener;
	object.removeListener = this._removeListener;
	object.broadcastMessage = this._broadcastMessage;
	object._broadcasterListeners = [];
}
Broadcaster._broadcastMessage = function(event, args) {
	var list = this._broadcasterListeners;
	for (var i = 0; i < list.length; i++) {
		var o = list[i];
		if (o && typeof o[event] != "undefined") {
			o[event](args);
		}
	}
}	
Broadcaster._addListener = function(object) {
	var list = this._broadcasterListeners;
	if (list.indexOf(object) == -1) {
		list[list.length] = object;
	}
}
Broadcaster._removeListener = function(event, object) {
	var list = this._broadcasterListeners;
	var index = list.indexOf(object);
	if (index != -1) {
		list[index] = null;
	}
}


// ===========================================================================
// ===========================================================================


/**
 * SimpleEvent is a way to run multiple callbacks from a single object event,
 * such as onclick, or onload
 **/
SimpleEvent = new Object();

SimpleEvent.addEvent = function(object, event, func) {
	if (object[event] != null) {
		var oldEvent = object[event];
		object[event] = function() {
			oldEvent();
			func();
		}
	} else {
		object[event] = function() {
			func();
		}
	}
}

SimpleEvent.addOnLoadEvent = function(action) {
	SimpleEvent.addEvent(window, "onload", action);
}
SimpleEvent.addBeforeLoadEvent = function(action) {
	SimpleEvent.addEvent(window, "beforeload", action);
}
SimpleEvent.addBeforeLoadEvent(function(){});	// dummy placeholder event


// ===========================================================================
// ===========================================================================


/**
 * NodeBuilder is a utility for creating common bits of markup
 **/
NodeBuilder = new Object();
NodeBuilder.createAnchor = function(txt, title, href) {
	var a = document.createElement("a");
	if (txt) {
		a.appendChild(document.createTextNode(txt));	
	}
	if (title || txt) {
		a.title = (title) ? title : txt;
	}
	a.href = (href) ? href : "#";
	if (!href) {
		a.onfocus = function() { this.blur() };		
	}
	return a;
}


// ===========================================================================
// ===========================================================================


CSSUtil = new Object();
CSSUtil.iex = document.all;
CSSUtil.showTableRow = function(element) {
	element.style.display = (this.iex) ? "block" : "table-row";
}
CSSUtil.showTableRowGroup = function(element) {
	element.style.display = (this.iex) ? "block" : "table-row-group";
}
CSSUtil.hideElement = function(element) {
	element.style.display = "none";
}
CSSUtil.hasClassName = function(element, className) {
	return (element.className.indexOf(className) != -1);
}
CSSUtil.hasClassName = function(element, className) {
	return element.className.indexOf(className) != -1;
}	
CSSUtil.addClassName = function(element, className) {
	element.className += " " + className;
}
CSSUtil.removeClassName = function(element, className) {
	element.className = element.className.replace(new RegExp("\\s*(" + className + ")", "g"), "");
}					


// ===========================================================================
// ===========================================================================


/**
 * NodeUtil is a utility for digging through the DOM in a uniform and cross-browser way.
 * Original functions by Travis Beckham/http://www.squidfingers.com
 **/
NodeUtil = new Object();

if (!window.Node) {
	window.Node = {ELEMENT_NODE:1, TEXT_NODE:3};
}

NodeUtil.getChildren = function(node, filter){
	var result = new Array();
	var children = node.childNodes;
	for(var i = 0; i < children.length; i++){
		if(this.checkNode(children[i], filter)) result[result.length] = children[i];
	}
	return result;
}
NodeUtil.getElementChildren = function(node){
	return this.getChildren(node, "ELEMENT_NODE");
}
NodeUtil.getFirstChild = function(node, filter){
	var child;
	var children = node.childNodes;
	for(var i = 0; i < children.length; i++){
		child = children[i];
		if(this.checkNode(child, filter)) return child;
	}
	return null;
}
NodeUtil.getFirstTextChild = function(node){
	return this.getFirstChild(node, "TEXT_NODE");
}
NodeUtil.getFirstElementChild = function(node){
	return this.getFirstChild(node, "ELEMENT_NODE");
}
/**
 * Returns the next sibling based on filter node type
 **/
NodeUtil.getNextSibling = function(node, filter){
	for(var sibling = node.nextSibling; sibling != null; sibling = sibling.nextSibling){
		if(this.checkNode(sibling, filter)) return sibling;
	}
	return null;
}
/**
 * Return the next element sibling
 **/
NodeUtil.getNextSiblingElement = function(node){
	return this.getNextSibling(node, "ELEMENT_NODE");
}

NodeUtil.checkNode = function(node, filter) {
	return (filter == null || node.nodeType == Node[filter] || node.nodeName.toUpperCase() == filter.toUpperCase());
}

/**
 * Returns: Array of elements that match the arguments. 
 * If no elements are found, an empty array is returned.
 **/
NodeUtil.getElementsByAttr = function(node, aName, aValue, tName){
	var i, elms, attr, rslt = [];
	if (!node) node = document;
	if (!tName) tName = "*";	

	elms = NodeUtil.getElementsByTagName(node, tName);

	for(i = 0; i < elms.length; i++){
		// Note: in IE5/Mac, element.getAttribute("class") returns null 
		// even when the class attribute contains a value. 
		// However, element.className will return the correct value.
		attr = aName == "class" ? elms[i].className : elms[i].getAttribute(aName);
		if(attr){
			if(aValue && attr != aValue){
				continue;
			}
			rslt[rslt.length] = elms[i];
		}
	}
	return rslt;
}

NodeUtil.getElementById = function(node, id) {
	return node.getElementById(id);
}

NodeUtil.getElementsByTagName = function(node, tagName) {
	return node.getElementsByTagName(tagName);
}

// ===========================================================================
// ===========================================================================

/**
 * DOMUtil is a utility for simplfying certain DOM related operations
 **/
DOMUtil = new Object();

/**
 * Return all radios in a family
 * @param radioOrName A radio input to find its other family members or the name of the family to find
 * @param ignoreRadio If radioOrName is a radio, a value of true will ignore the input
 **/
DOMUtil.findRadioFamily = function(radioOrName, ignoreRadio) {
	return this._findInputFamily("radio", radioOrName, ignoreRadio);
}

/**
 * Return all checkboxes in a family
 * @param checkboxOrName A checkbox input to find its other family members or the name of the family to find
 * @param ignoreCheckbox If checkboxOrName is a checkbox, a value of true will ignore the input
 **/
DOMUtil.findCheckboxFamily = function(checkboxOrName, ignoreCheckbox) {
	return this._findInputFamily("checkbox", checkboxOrName, ignoreCheckbox);
}

DOMUtil._findInputFamily = function(inputType, inputOrName, ignoreInput) {
	var name = (typeof inputOrName == "string") ? inputOrName : inputOrName.name;
	var inputs = document.getElementsByTagName("input");
	var output = [];
	for (var i = 0; i < inputs.length; i++) {
		if (inputs[i].type == inputType && inputs[i].name == name) {
			if (!(typeof inputOrName == "object" && ignoreInput && inputs[i] == inputOrName)) {
				output[output.length] = inputs[i];
			}
		}
	}
	return output;
}
