// get element id in dom d.
function d_e(d, id) { return d.getElementById(id) }

// get element id in document
function e(id) { return d_e(document, id) }

// get elements with ids enumerated (id0, id1, id2) in dom d.
function d_e_enum(d, id)
{
	var i = 0;
	var elements = Array();
	while(element = d_e(d, id + i)) elements[i++] = element;
	return elements;
}

// get elements with ids enumerated in document.
function e_enum(id) { return d_e_enum(document, id) }

// get tags of name n in document d.
function d_tags(d,n) { return d.getElementsByTagName(n) }

// get tags of name n in document.
function tags(n) { return d_tags(document, n) }

// get attribute a from element e
function att(e, a) { return e.getAttribute(a) }

// call do_f when on_f is called.
function listen(on_f, do_f) {
	if(on_f) {
		var old_f = on_f;
		on_f = function() { old_f(); do_f(); }
	}
	else on_f = do_f;
}

// add style class c to element el.
function add(el,c){ 
	if(el.className.indexOf(c) == -1) el.className += el.className ? " "+c : c;
}

// remove style class c from element el.
function rem(el,c){
	var all = el.className.split(" ");
	var newc = Array();
	for(i in all) if(all[i] != c) newc[newc.length] = all[i];
	el.className = newc.join(" ");
}

// toggle class c on element el.
function tgl(el,c) {
	if(el.className.indexOf(c) == -1) add(el,c);
	else rem(el,c);
}

// convenience function for a toggling link.
function tgl_link(link, el) {
	tgl(e(el), 'disHide');
	tgl(link, 'open');
	link.blur();
	return false;
}

// show element with given id via display.
function s(id){ rem(e(id),'disHide'); }

// hide element with given id via display.
function h(id){ add(e(id),'disHide'); }

// simpler syntax for getting the selected value from a select field.
function selectValue(el){
	return el.options[el.selectedIndex].value;
}

// even simpler syntax.
function onSelectValue(id, callback){
	e(id).onchange = function() { callback( selectValue( e(id) ) ); }
}

// call function cb with the absolute position of el as parameters.
function getAbsPos(el, cb){
	var x = 0;
	var y = 0;
	if(el.offsetParent) {
		while(el.offsetParent){
			x += el.offsetLeft;
			y += el.offsetTop;
			el = el.offsetParent;
		}
	}
	else {
		x = el.x;
		y = el.y;
	}
	cb(x, y);
}

// position an element absolutely.
function posAbs(el, x, y){
	var relx = el.offsetLeft;
	var rely = el.offsetTop;
	var cb = function(absx, absy){
		el.style.left = (x - absx + relx) + "px";
		el.style.top = (y - absy + rely) + "px";
	}	
	getAbsPos(el, cb);
}

// conform element heights.
function conformY(els, extra) {
	var h=0;
	var w=els[0].offsetWidth;
	for(var i=0; i < els.length; i++) { 
		els[i].style.width = w + "px";
		h = Math.max(els[i].offsetHeight, h);
	}
	for(i=0; i < els.length; i++) {
		els[i].style.height = forceInt(h + extra) + 'px';
	}
	return h;
}

// a safari hack to invalidate a node.
function invalidate(el) {
	if(navigator.userAgent.indexOf("Safari") != -1) {
		var p = el.parentNode;
		var n = el.nextSibling;
		p.removeChild(el);
		p.insertBefore(el, n);
	}
}

// sets toggling help text for an element.
function helpText(el, text) {
	el.onfocus = function() { if(el.value == text) el.value = ""; }
	el.onblur = function() { if(el.value == "") el.value = text; }
}

// pass it the id of an input, and it will find it's form's index in the forms array.
function getFormIndexForElementId(id) {
	for(i in document.forms) if(document.forms[i] == document.getElementById(id).form) return i;
}

// Cross browser friendly XMLHttpRequest creation.
function newXMLHttpRequest() {
	return window.XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject("Microsoft.XMLHTTP");
}

// monitor a text field for changes as they happen. Pass it the input element and a callback that takes the input's value.
function monitorInput(el, callback) {
	var val = el.value;
	var monitor = function() {
		if(val != el.value) {
			val = el.value;
			callback(val);
		}
	}
	el.onfocus = function() {
		var interval = setInterval(monitor, 100);
		el.onblur = function() {
			monitor();
			clearInterval(interval);
		}
	}
}

// Dead simple ajax wrapper for getting text back from an url.
function fetcher() {
	this.ajax = newXMLHttpRequest();
	this.in_progress = false;
	this.use_cache = true;
	this.cache = new Array();
	
	var i = this;
	
	this.load = function(url) {
		if(i.use_cache && i.cache[url]) {
			i.ondata(i.cache[url]);
		}
		else {
			i.cancel();
			i.in_progress = true;
			i.ajax.open('post', url);
			i.ajax.onreadystatechange = function() {
				if(i.ajax.readyState == 4) {
					i.in_progress = false;
					if(i.ajax.status == 200) {
						i.cache[url] = i.ajax.responseText;
						i.ondata(i.ajax.responseText);
					}
					else {
						i.onfailure(i.ajax.statusText);
					}
				}
			}
			i.ajax.send('');
		}
	}
	
	this.cancel = function() {
		if(i.in_progress) {
			i.ajax.abort();
		}
	}
	
	// callbacks
	this.ondata = function(data) {}
	this.onfailure = function(err) {}
}

// Convenient alphabet.
function alphabet() {
	return Array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
}

// Destroy an array element at an index.
function array_unset(arr, ind) {
	var new_arr = new Array();
	var i;
	for(i in arr) {
		if(i != ind) {
			new_arr[i] = arr[i];
		}
	}
	return new_arr;
}

// Get all the selected values from a multi select.
function get_selected(el) {
	var sel = new Array();
	for(var i = 0; i < el.options.length; i++) {
		sel[i] = el.options[i].selected;
	}
	return sel;
}

// Forces a value to be an int no matter what.
function forceInt(val) {
	return isNaN(parseInt(val)) ? 0 : parseInt(val);
}

// Checks if node c is a child of node p.
function is_descendent(c, p) {
	while(c = c.parentNode) {
		if(c == p) {
			return true;
		}
	}
	return false;
}

