// excerpts from the Orange Shadow Technologies JS Libraries
// license granted by Jason Ward of Orange Shadow Technologies.

/** takes a dom object and a boolean to show or hide the element
	currently handles the special case of table row that causes problems
	todo: handle (or find out how to get) default display for an object and set to that
 */
function ostShowHide(obj, show) {
		if (show) {
			if (!!obj.nodeName && obj.nodeName.toUpperCase() == "TR") {
				try {
					obj.style.display="table-row";
				} catch(e) {
					obj.style.display = "block";
				}
			} else {
				obj.style.display = "block";  /* can't we just set it "" ??? */
			}
		} else {
			obj.style.display = "none";
		}
}		
function insertChildCallback(node, parent, ib) {
	parent.insertBefore(node, ib.insertBefore);
	ib.insertBefore = node;
	return false;
}
function moveChildren(oldParent, newParent, before) {
	var ibf = new Object({insertBefore: before}); 
	ostEtaretiDescendents(oldParent, 1, insertChildCallback, newParent, ibf);
}
/* convenience wrappers */
function ostGetChildCount(node, filter /* [, filterargs... ] */) {
	var newargs = [node, 1].concat(Array.prototype.slice.call(arguments, 1));	/* probably not the most effecient way of doing this */
	return ostGetDescendentCount.apply(this, newargs);
}
/* returns an array (actual array) of children - only useful if you filter */
function ostGetChildren(node, filter /* [, filterargs...] */) {
	var newargs = [node, 1].concat(Array.prototype.slice.call(arguments, 1));	/* probably not the most effecient way of doing this */
	return ostGetDescendents.apply(this, newargs);
}

/* the meat of getDescendents */

function __getDescendentCountCallback(node, gcobj) {
	gcobj.filterargs[0] = node;
	if (gcobj.filter.apply(this, gcobj.filterargs)) {
		++gcobj.count;
	}
	return false /* don't stop the iterator */
}
function ostGetDescendentCount(parent, depth, filter /*, filterargs...*/) {
	var filterargs = Array.prototype.slice.call(arguments, 3);
	filterargs.unshift(null);
	var gcobj = { count: 0, filter: filter || __ostReturnTrue, filterargs: filterargs };
	ostIterateDescendents(parent, depth, __getDescendentCountCallback, gcobj);
	return gcobj.count;
}

/* andfilter - does a logical and of the passed filter functions
	short circuits */ 

/* each argument should be an array with the function followed by the arguments to the function */
/* this isn't thread safe... but then again neither is js */
function ostAndFilter() {
	var method = this;
	var filters = [];
	for (var a=0,l=arguments.length; a < l; ++a) {
		var filtersettings = arguments[a]; /* should be an array!!!! */
		var newfilter = { filter: filtersettings[0], filterargs: filtersettings.slice(1) };
		newfilter.filterargs.unshift(null);	/* make room for the node */
		filters.push(newfilter);
	}
	return function() {
		var i = 0; l = filters.length;
		var node = arguments[0];	/* called with the node as the first argument */
		while (i < l) {
			var curfilter = filters[i];
			curfilter.filterargs[0] = node;
			if (curfilter.filter.apply(method, curfilter.filterargs)) {
				++i;
			} else {
				break; /* choose your favorite method of breaking a loop... */
			}
		}
		return i == l; 
	}
}
function ostOrFilter() {
	var method = this;
	var filters = [];
	for (var a=0,l=arguments.length; a < l; ++a) {
		var filtersettings = arguments[a]; /* should be an array!!!! */
		var newfilter = { filter: filtersettings[0], filterargs: filtersettings.slice(1) };
		newfilter.filterargs.unshift(null);	/* make room for the node */
		filters.push(newfilter);
	}
	return function() {
		var i = 0; l = filters.length;
		var node = arguments[0];	/* called with the node as the first argument */
		while (i < l) {
			var curfilter = filters[i];
			curfilter.filterargs[0] = node;
			if (curfilter.filter.apply(method, curfilter.filterargs)) {
				break;
			} else {
				++i;
			}
		}
		return i < l; 
	}
}
function ostNotFilter() {
/* todo: implement if you need it !!! */
}
/* function to test the id of a node against a regexp */
function ostNodeIdFilter(node, idPattern) {
	return (!!node.id && idPattern.test(node.id));
}
/* compares the node.nodeName to the namespec insensitively */
function ostNodeNameFilter(node, tagName) {
	return !!node.nodeName && node.nodeName.toUpperCase() == tagName.toUpperCase();
}
function __getDescendentsCallback(node, gdobj) {
	gdobj.filterargs[0] = node;
	if (gdobj.filter.apply(this, gdobj.filterargs)) {
		gdobj.results.push(node);
	}
	return false /* don't stop the iterator */
}
/* dummy function that returns true
 * combine with similar functions in this file
 *  */
function __ostReturnTrue() { return true; }
function __ostReturnFalse() { return false; }

/* returns an array containing the descendents matched by filterfunc */
function ostGetDescendents(parent, depth, filter /*, filterargs */) {
	var filterargs = Array.prototype.slice.call(arguments, 3);
	filterargs.unshift(null);
	 
	var gdObj = {
		results: new Array(),
		filter: filter || __ostReturnTrue,
		filterargs: filterargs
	 };
	ostIterateDescendents(parent, depth, __getDescendentsCallback, gdObj);
	return gdObj.results;	
}
/* the business end of iteratedescendents */
function __doIterateDescendents(parent, curdepth, maxdepth, func, args) {
	var retval = false;
	var children = parent.childNodes;
	if (!!children && !!children.length) {
		for (var n=0, l=children.length; n<l; ++n) {
			if (!!children[n]) { /* it is possible for children to have a null */
				args[0] = children[n];
				retval = func.apply(null, args);
				if (!retval && maxdepth > curdepth) __doIterateDescendents(args[0], curdepth+1, maxdepth, func, args); 
			}
		}
	}
	return retval;
}

/* todo: could probably benefit from a iterate function the takes a filter function and a function function */


/* general function to iterate the descendents of a node and perform an operation on them */
/* parent is the parent node (with .childNodes) */
/* any args past func will be passed to the callback function */
/* func is called in the global context (this==window) so be sure to bind if you need it
	to act on an object
	if func returns true (or something that evaluates as true, this stops)
	 */
	 
function ostIterateDescendents(parent, depth, func /*, args... */) {
	depth = depth || ost_MAXDEPTHR;
	/* ourargs is an array with arguments to send to callback function
		the first slot is reserved for the node we're looking at
	 */
	var ourargs = Array.prototype.slice.call(arguments, 3 /* ostIterateDescendents.length */);
	ourargs.unshift(null);
	return __doIterateDescendents(parent, 1, depth, func, ourargs);
}

/* goes through the childnodes backwards (at each level, DOES NOT start at bottom */
function __doEtaretiDescendents(parent, curdepth, maxdepth, func, args) {
	var retval = false;
	var children = parent.childNodes;
	if (!!children && !!children.length) {
		for (var n=children.length-1; n>-1 && !retval; --n) {
			args[0] = children[n];
			if (!!args[0]) {
				retval = func.apply(null, args);
				if (!retval && maxdepth > curdepth) {
					retval = __doEtaretiDescendents(args[0], curdepth+1, maxdepth, func, args);
				} 
			}
		}
	}
	return retval;
}

/* general function to iterate backwards the descendents of a node and perform an operation on them */
/* parent is the parent node (with .childNodes) */
/* any args past func will be passed to the callback function */
function ostEtaretiDescendents(parent, depth, func /*, args... */) {
	if (!depth) depth = ost_MAXDEPTHR;
	/* ourargs is an array with arguments to send to callback function
		the first slot is reserved for the node we're looking at
	 */
	var ourargs = Array.prototype.slice.call(arguments, 3 /* ostIterateDescendents.length */);
	ourargs.unshift(null);
	return __doEtaretiDescendents(parent, 1, depth, func, ourargs);
}

/* why isn't this using etaretidescendents ? */
function ostGetLastChild(parent, conditionfunc /* args */) {
	var lastChild = null;
	var conditionfuncargs = Array.prototype.slice.call(arguments, 2);
	conditionfuncargs.unshift(null);
	
	var childList = parent.childNodes;
	if (!!childList && childList.length) {
		for (var n=childList.length-1;lastChild == null && n>-1;--n) {	/* just going through backwards */
			conditionfuncargs[0] = childList[n];
			if (conditionfunc.apply(null, conditionfuncargs)) {
				lastChild = childList[n];
			}
		}
	}
	return lastChild;
}
/* convenience wrapper */
function ostGetLastChildById(p, idPattern) {
	return ostGetLastChild(p, ostNodeIdFilter, idPattern);
}

function ostGetFirstChild(parent, conditionfunc /* args */) {
	var firstChild = null;
	var conditionfuncargs = Array.prototype.slice.call(arguments, 2);
	conditionfuncargs.unshift(null);
	
	var childList = parent.childNodes;
	if (!!childList && childList.length) {
		for (var n=0,l=childList.length;n<l && firstChild == null;++n) {
			conditionfuncargs[0] = childList[n];
			if (conditionfunc.apply(null, conditionfuncargs)) {
				firstChild = childList[n];
			}
		}
	}
	return firstChild;
}
/* 
	for the most part, assume the correct thing is being passed in
*/
function ostSetRadioGroupValue(gorb, value) {
	/* normalize our group var in case we were passed a member of the group */
	var group = !!gorb.length ? gorb : gorb.form[gorb.name];
	if (!!!group) {
		return;	/* false */
	}
	
	if (!!group.length) {  /* assumes we'll never have length of zero */
		for(var i = 0; i < group.length; ++i) {
			var radioBtn = group[i];
			if (!!radioBtn) {
				radioBtn.checked = (radioBtn.value == value);	/* may need some special handling for 3rd state */
			}
		}
	} else {
		/* assume the group is a single radio object */
		var radioBtn = group;
		radioBtn.checked = (radioBtn.value == value);
	}
}
function ostGetRadioGroupValue(group) {
	group = !!group.name ? group.form[group.name] : group; // in case we're called with a radio button
	var groupLength = group.length;
	var groupVal;	/* could very well be undefined */
	if(ostIsDefined(groupLength)) {
		/* we have a collection */
		for(var i = 0; i < groupLength; ++i) {
			var radiobtn = group[i];
			if (!!radiobtn && radiobtn.checked == true) {
				groupVal = radiobtn.value;
				break;
			}
		}		
	} else {
		/* we have a single radio button (only one with this name) */
		if (group.checked) groupVal = group.value;
	}
	return groupVal;
}

function ostIsDefined(v) {
	return typeof(v) != "undefined";
}

function ostIsString(v) {
	return typeof(v) == "string";
}
function $(elementOrId) {
	if (ostIsString(elementOrId)) {
		return document.getElementById(elementOrId);
	} else {
		return elementOrId;
	}
}
	function ostAJAXRequest(uri, onsuccess, onerror) {
		this.uri = uri;
		this.request = this.createRequest();
		this.request.onreadystatechange = __ostAJAXOnReadyStateChange.bind(this);
		this.readyState = this.request.readyState;
		this.onsuccess = onsuccess;
		this.onerror = onerror;
		this.params = new Array();
	}
	ostAJAXRequest.prototype.createRequest = function() {
		var xmlhttprequest;
		if (typeof XMLHttpRequest != "undefined") {
			xmlhttprequest = new XMLHttpRequest();
		} else if (window.ActiveXObject) {
			var axvers = [ "MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0",
						   "MSXML2.XMLHttp", "Microsoft.XMLHttp" ];
			for (var i=0; i < axvers.length; ++i) {
				try {
					xmlhttprequest = new ActiveXObject(axvers[i]);
				} catch (e) {}
			}
		}
		if (!xmlhttprequest) {
			throw new Error("Could not create XMLHttp");
		}
		return xmlhttprequest;
	}
	ostAJAXRequest.prototype.addParameter = function(name, value) {
		this.params.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));		
	}
	ostAJAXRequest.prototype.getPostBody = function() {
		return this.params.join("&");
	}
	ostAJAXRequest.prototype.getQueryString = function() {
		return this.params.join("&");
	}
	ostAJAXRequest.prototype.post = function(user, password) {
		this.request.open("post", this.uri, !!this.onsuccess, user, password);
		this.request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		this.request.send(this.getPostBody());
	}
	ostAJAXRequest.prototype.addQueryString = function() {
		var qs = this.getQueryString();
		if (!!qs && qs != "") {
			return [ this.uri, (this.uri.indexOf("?") == -1) ? "?" : "&", qs].join("");
		}
		return this.uri;
	}
	ostAJAXRequest.prototype.get = function(user, password) {
		var uriqs = this.addQueryString(this.uri);
		this.request.open("get", uriqs, !!this.onsuccess, user, password);
		this.request.send(null);
	}
	ostAJAXRequest.prototype.getUri = function () {
		return this.uri;
	}
	ostAJAXRequest.prototype.setUri = function (uri) {
		this.uri = uri;
	}
	ostAJAXRequest.prototype.abort = function() {
		this.request.abort();
	}
	ostAJAXRequest.prototype.setRequestHeader = function (header, value) {
		this.request.setRequestHeader(header, value);
	}
	ostAJAXRequest.prototype.getResponseHeader = function (header) {
		return this.request.getResponseHeader(header);
	}
	ostAJAXRequest.prototype.getAllResponseHeaders = function () {
		return this.request.getAllResponseHeaders();
	}
	ostAJAXRequest.prototype.getResponseText = function () {
		return this.request.responseText;
	}
	ostAJAXRequest.prototype.getResponseXML = function () {
		return this.request.responseXML;
	}
	ostAJAXRequest.prototype.getStatus = function () {
		return this.request.status;
	}
	ostAJAXRequest.prototype.getStatusText = function () {
		return this.request.statusText;
	}
	ostAJAXRequest.prototype.wasSuccess = function () {
		return (this.getStatus() == 200);
	}
	ostAJAXRequest.prototype.getReadyState = function () {
		return this.request.readyState;
	}
	function __ostAJAXOnReadyStateChange() {
		this.readyState = this.request.readyState;
		if (this.request.readyState == 4) {
			if (this.wasSuccess()) {
				if (!!this.onsuccess) this.onsuccess(this);
			} else {
				if (!!this.onerror) this.onerror(this);
			}
		}
	}
	Function.prototype.bind = function(obj) { 
	  var __method = this;
	  return function() { 
	    return __method.apply(obj, arguments); 
	   }; 
	}