/* dom.js   author: brandon curtis

this is a collection of functions for working with elements. if you are building an app that uses gecko or
certain builds of webkit, these functions will be added to all HTML elements and you can use them all at any
time. IE and the stock tiger safari don't support that though, so to have access to these methods, you have
to call extend_element, with your element as the argument.
*/

function get(id) {
	// get- this is simply a way to save 20 keystrokes
	return document.getElementById(id);
}

(function() {
	var methods = {
		getElementsByClassName : function(class_name) {
			var elements = this.getElementsByTagName('*');
			var matches = [];
			for (var i in elements) {
				if (elements.hasOwnProperty(i) && elements[i].has_class(class_name)) matches.push(elements[i]);
			}
			return matches;
		},
		boot_children : function() {
			// this removes all child nodes from an element. element_obj.innerHTML = ''; works equally well,
			// but i prefer this approach.
			while (this.childNodes.length) this.removeChild(this.firstChild);
		},

		// these return the top and left values of an element relative to the entire document.
		top  : function() {
			// this gets the absolute top of an element
			// i saw the concept for this on quirksmode, but i had to modify it to account for scrolling

			var top = 0;

			// to be accurate, we have to include scroll offsets for elements with overflow,
			// but if we add the scroll offset of the target element, that would be a problem
			var target = this;
			var elem   = this;

			do {
				// add the offsetTop of the current element
				// subtract scroll offset, unless we're still on the target element
				top += elem.offsetTop - ((target == elem) ? 0 : elem.scrollTop);
	
				// work our way up to the root
				elem = elem.offsetParent;
			} while (elem);

			return top;
		},
		left : function() {
			// left - same thing as top, but for the x coord
			var left   = 0;
			var target = this;
			var elem   = this;
			do {
				left += elem.offsetLeft - ((target == elem) ? 0 : elem.scrollLeft);
				elem = elem.offsetParent;
			} while (elem);
			return left;
		},

		/*
		i use multiple class names all over the place and these are easy ways to add/remove/toggle class names
		while leaving the any other class names already assigned to the element as they were. all 4 of these
		can operate on more than one class name at once - just put spaces between each class name in the
		argument string, e.g. element.toggle_class('foo bar');
		*/
		has_class    : function(names) {
			// has_class - checks to see if a class name is there. returns true if so.
			// if multiple names are given, they all have to be there to return true.

			if (typeof names != 'string') return false;
			names = names.split(' ');
			var exists = true;
			for (var i = 0; i < names.length; i++) {
				if (!this.className.match(new RegExp('\\b' + names[i] + '\\b'))) exists = false;
			}
			return exists;
		},
		add_class    : function(names) {
			// add_class - add a class name (unless it's already there).
			// returns true if all supplied names were added (false if any were already there).

			if (typeof names != 'string') return false;
			names = names.split(' ');
			var added = true;
			for (var i = 0; i < names.length; i++) {
				if (this.className.match(new RegExp('\\b' + names[i] + '\\b'))) added = false;
				else {
					if (this.className.length) this.className += ' ' + names[i];
					else this.className = names[i];
				}
			}
			return added;
		},
		remove_class : function(names) {
			// remove_class - remove a class name.
			// returns true if all supplied names were removed (false if any weren't there)

			if (typeof names != 'string') return false;
			names = names.split(' ');
			var all_removed = true;
	
			// get a list of current class names
			var old_names = this.className.split(' ');
	
			// make an array for the new list of class names
			var new_names = [];
	
			// do some comparisons
			var found;
			for (var i = 0; i < old_names.length; i++) {
				found = false;
				for (var j = 0; j < names.length; j++) {

					// if the current name arg is found in the current className string, do nothing
					if (old_names[i] == names[j]) found = true;
			
					// otherwise, add it to the new class name array
					else new_names.push(old_names[i]);
				}
				if (!found) all_removed = false;
			}
	
			// assign new new class names back to the element
			this.className = new_names.join(' ');
	
			return all_removed;
		},
		toggle_class : function(names) {
			// toggle_class - toggle given class names. doesn't return anything.

			if (typeof names != 'string') return;
			names = names.split(' ');
			for (var i = 0; i < names.length; i++) {
				// try to add it. if it was already there, then remove it
				if (!this.add_class(names[i])) this.remove_class(names[i]);
			}
		},
		flip_class   : function(names, flip) {
			// flip class? i don't know what the hell to call this.
			// if 'flip' is true, class names are added, else they are removed. doesn't return anything

			if (typeof names != 'string') return;
			if (flip) this.add_class(names);
			else this.remove_class(names);
		}
	};
	
	// lame browsers need to call this. it applies these methods to the element you pass in.
	window.extend_element = function(element) {
		for (var i in methods) {
			if (! methods.hasOwnProperty(i) || element[i]) continue;
			
			element[i] = methods[i];
		}
	}

	// add the methods to HTMLElement, if it exists. then you don't have to bother calling extend_element manually,
	// if you don't care about supporting anything besides gecko or webkit nightlies.
	if (window.HTMLElement) extend_element(HTMLElement.prototype);
})();