/* atomic javascript
 *
 * -Ultra lightweight. ~5kb! Can be compressed further
 * -Totally extensible and modular, extend any part of the functionality.
 * -Doesn't attempt to do everything in one file. 
 * 
 *                                                               
 *  This file contains:
 *  -CSS selectors
 *  -OO tools for inheritance
 *  -Methods on elements, and groups of elements:
 *  	apply: Apply a function to an element or group of elements
 *  
 */
(function() {
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); };


//Cant use "extends" its reserved word in IE.
Function.prototype.inherit = function(parent) {
	var parentObject = new parent;
	for (var x in parentObject) this.prototype[x] = parentObject[x];

	this.___parent = null;
	this.prototype.__parent = function() {
		if (!this.___parent) {
			var self = this;
			this.___parent = new Object;    		 
			this.___parent.construct = function() {
				parent.apply(self, arguments); 	
			}
			for (var x in parentObject) {
				var item = parentObject[x];
		    		 if (typeof(item) == 'function') {
		    			 this.___parent[x] = function() {
		    			 item.apply(self, arguments);
		    		 }
		    	 }
			}
		}
		return this.___parent;
	}
	return this;
}


window.$e = function(tagName) {return $.extend($().createElement(tagName));}
window.$t = function (text) {return $().createTextNode(text);}

window.$ = function(selector, parent) {	
	if (typeof(selector) == 'object') return $.extend(selector);
	if (!selector || selector == '') return document;
	
	//deal with commas
	var parts = selector.split(',');
	var found = new Array();	
	if (!parent) parent = new atomElementGroup(new Array(document));
	if (parent.tagName) parent = new atomElementGroup(new Array(parent));
	
	for (var i = 0; i < parts.length; i++) {	
		//var trail = parts[i].match(/(([\s\.\#\>\{]|)[a-z0-9\_\-\=\~\^\"]+|\[.+"\])/g);
		var regExp = new RegExp('(([\\ ' + window.$.selectorChars.join('\\') + ']|)[a-z0-9\\_\\' + window.$.ruleChars.join('\\') +'\\"]+|\\[.+\\])', 'g');
		var trail =  parts[i].match(regExp);
		for (var j = 0; j < trail.length; j++) parent = $_(trail[j], parent);
		for (var j = 0; j < parent._elements.length; j++) found.push(parent._elements[j]);
	}
	
	for (var i = 0; i < found.length; i++) $.extend(found[i]);

	//if (found.length == 0) return null;
	if (found.length === 1) return found[0];
	else return new atomElementGroup(found);
}

window.$_ = function(selector, parent) {	
	for (var i = 0; i < $.cssRules.length; i++) {
		var rule = new $.cssRules[i](selector, parent);
		if (rule.check()) return new atomElementGroup(rule.getElements());
	}
}

window.$.selectorChars = new Array(' ', '.', '#', '>');
window.$.ruleChars = new Array('~', '^', '"', '=');



function cssRuleBase(rule, parent) {
	this.rule = rule;
	this.parent = parent;
}

var cssRuleBackwards = function(rule, parent) {
	this.__parent().construct(rule, parent);
	
	this.check = function() {
		return (this.rule.trim().charAt(0) == '[');
	}
	
	this.getElements = function() {
		
	}
	
}.inherit(cssRuleBase);

var cssRuleTagName = function(rule, parent) {
	this.rule = rule;
	this.parent = parent;
	
	this.check = function(rule, parent) {
		return this.rule.match(/^\s?[a-zA-Z]/i);
	}
	
	this.getElements = function() {
		if (this.parent.tagName) this.parent = new atomElementGroup(new Array(this.parent));
		var uid = Math.random();		
		var elements = new Array();
		for (var i = 0; i < this.parent._elements.length; i++) {
			var found = this.parent._elements[i].getElementsByTagName(this.rule.trim());
			for (var j = 0; j < found.length; j++) {
				if (!found[j].uid || found[j].uid !== uid) {
					found[j].uid = uid; //Tag the element so it's not picked up more than once when elements are nested.
					elements.push($.extend(found[j]));
				}
			}
		}		
		return elements;
	}
}.inherit(cssRuleBase);


var cssRuleCss3 = function(rule, parent) {
	this.__parent().construct(rule, parent);
	
	this.check = function() {
		return (this.rule.trim().charAt(0) == '[');
	}
	
	this.getElements = function() {
		var matches = /([a-zA-Z]+)(|[\~\|]*=?)\"?(.+)?\"?/.exec(this.rule.trim().substring(1));
		___DEBUG = matches;
		return this.findElements(matches[1], matches[3], matches[2]);
	}
	
	this.findElements = function(attribute, value, operator) {
		if (attribute == 'class') attribute = 'className';
		var foundElements = new Array();
		
		for (var i = 0; i < this.parent._elements.length; i++) {
			if (!this.parent._elements[i][attribute]) continue;

			if (operator == '') {
				if (this.parent._elements[i][attribute]) foundElements.push(this.parent._elements[i]);
			}
			else if (operator == '=') {
				if (this.parent._elements[i][attribute] == value) foundElements.push(this.parent._elements[i]);
			}
			else if (operator == '~=') {
				var parts = this.parent._elements[i][attribute].split(' ');
				for (var j = 0; j < parts.length; j++) {
					if (parts[j] == value) {
						foundElements.push(this.parent._elements[j]);
						continue;
					}
				}
			}
		}
		return foundElements;
	}
}.inherit(cssRuleBase);


var cssRuleStyle = function(rule, parent) {
	this.__parent().construct(rule, parent);
	
	this.check = function() {
		return (this.rule.trim().charAt(0) == '{');
	}
	
	this.getElements = function() {
		var matches = /([a-zA-Z]+)(|[[\~\|]*=)\"(.+)\"/.exec(this.rule.trim().substring(1));
		return this.findElements(matches[1], matches[3], matches[2]);
	}
	
	this.findElements = function(attribute, value, operator) {
		var found = new Array();
		for (var i = 0; i < this.parent._elements.length; i++) {
			if (this.parent._elements[i].computedStyle()[attribute] == value) {
				$.extend(this.parent._elements[i]);
				found.push(this.parent._elements[i]);
			}
		}		
		return found;
	}
}.inherit(cssRuleBase);


var cssRuleClassName = function(rule, parent) {
	this.rule = rule;
	this.parent = parent;
	
	this.check = function() {
		return (this.rule.trim().charAt(0) == '.');
	}
	
	this.getElements = function() {
		var foundElements = new Array();		
		for (i = 0; i < parent._elements.length; i++) {		
			var elements = parent._elements[i].all ? parent._elements[i].all : parent._elements[i].getElementsByTagName('*');
			
			className = this.rule.trim().substring(1).replace(/\-/g, '\\-');		
			var regExp = new RegExp('(^|\\s|-)' + className + '(\\s|-|$)');	
			for (var j = 0; j < elements.length; j++) {
				if (regExp.test(elements[j].className) || elements[j].className == className) {
					$.extend(elements[j]);
					foundElements.push(elements[j]);
				}
			}
		}
		return foundElements;
	}
}.inherit(cssRuleBase);


var cssRuleId = function(rule, parent) {
	this.__parent().construct(rule, parent);
	
	this.check = function() {
		return (this.rule.trim().charAt(0) == '#');
	}
	
	this.getElements = function() {
		return new Array($.extend($().getElementById(this.rule.trim().substring(1))));
	}
	
}.inherit(cssRuleBase);

$.cssRules = new Array(cssRuleId, cssRuleTagName, cssRuleClassName, cssRuleCss3, cssRuleStyle);

function atomElementGroup(elements) {
	this._elements = elements;

	this.count = function() {
		return this._elements.length;
	}
	
	this.item = function(index) {
		return this._elements[index];
	}
	
	this.$ = function(selector) {
		var elements = new Array();
		for (var i = 0; i < this._elements.length; i++) {
			var c = this._elements[i].$(selector);
			for (var j = 0; j < c.length; j++) elements.push(c[j]);
		}
		return elements;
	}
	
	var self = this;
	for (var name in $.elementPrototype) {
		var func = $.elementPrototype[name];
		if (!this[name]) {
			this[name] = function(f) {
				return function() {
					for (var i = 0; i < self._elements.length; i++)	f.apply(self._elements[i], arguments);
					}
			}(func);
		}
	}
	
}

var atom = {		
	init: function() {	
		//window.atom = this;
		for (x in this) $[x] = this[x];
		
		$.elementPrototype = new Object;
		
		$.ready = function(func) {
			if (!this.loadEvents) {
				this.loadEvents = new Array();
				this.loadEvents.push(func);
				
				var self = this;				
				var loadFunc = function() {
					for (var i = 0; i < self.loadEvents.length; i++) self.loadEvents[i]();
				}
				
				if($().addEventListener) $().addEventListener('DOMContentLoaded', loadFunc, false);
				else if (navigator.appName.toLowerCase().indexOf('explorer') > 0) {
					$().write('<script id="__onload" defer src="//:"><\/script>');
					$('#__onload').onreadystatechange = function() {
						if (this.readyState == 'complete') loadFunc();
					}
				}
			}
			else this.loadEvents.push(func);			
		}
				
		$.elementPrototype.$ = function(selector) {
			return $(selector, this);
		}
		
		//Finds first text node and sets the value.
		$.elementPrototype.setText = function(text) {
			this.firstChild.nodeValue = text;
		}
		
		$.elementPrototype.getText = function() {
			return this.firstChild.nodeValue;
		}
		
		$.elementPrototype.parent = function() {
			$.extend(this.parentNode);
			return this.parentNode;
		}
		
		$.elementPrototype.addClass = function(className) {
			if (this.className.indexOf(className) == -1) this.className = this.className + ' ' + className; 
		}
		
		$.elementPrototype.removeClass = function(className) {
			this.className = this.className.replace(className, '');
		}
		
		$.elementPrototype.item = function(index) {
			if (index > 0 ) return null
			else return this;
		}
		
		$.elementPrototype.getSibling = function(offset) {
			if (offset < 0) {
				offset = Math.abs(offset);
				dir = 'previous';
			}
			else if (offset = 0) {
				offset = 1;
				dir = 'next';
			}
			else dir = 'next';
			
			el = this;
			while (offset > 0) el = el[dir + 'Sibling'];
						
			return $.extend(el);		
		}
		
		$.elementPrototype.count = function() {
			return 1;
		}
			
		$.elementPrototype.empty = function() {
			while (this.hasChildNodes()) this.removeChild(this.firstChild);
		}
		
		$.elementPrototype.remove = function() {
			this.parentNode.removeChild(this);
		}
		
		$.elementPrototype.getChildElement = function(index) {
			var elementCount = 0;
			for (var i = 0; i < this.childNodes.length; i++) {
				if (this.childNodes[i].nodeType == 1) {
					if (index == elementCount) {
						$.extend(this.childNodes[i]);
						return this.childNodes[i];
					}
					elementCount++;
				}
			}
			return this.childNodes[index];
		}
		
		$.elementPrototype.apply = function(func) {
			this._tmp = func;
			this._tmp();
		}		
		
		$.elementPrototype.add = function(child) {
			if (child.tagName) this.appendChild(child);
			else this.appendChild(new $t(child));
		}
		
		$.elementPrototype.position = function() {
			var element = this;
			var selectedPosX = 0;
			var selectedPosY = 0;
			var width = element.offsetWidth;
			var height = element.offsetHeight;
			do {
				selectedPosX += element.offsetLeft;
			    selectedPosY += element.offsetTop;
			}
			while (element = element.offsetParent);

			
			return {left : selectedPosX ,top : selectedPosY, width: width, right: selectedPosX + width, bottom: selectedPosY + height, height: height}
		}
		
		$.elementPrototype.addEvent = function(type, func, stopBubble) {
			
			if (!this.events) this.events = new Array();
			if (!this.events[type]) this.events[type] = new Array();
			var self = this;
			
			var f = stopBubble ? function(e) { $.killEvent(e); return func.apply(self, arguments); } : function(e) { return func.apply(self, arguments); }
			
			if (this.attachEvent) this.attachEvent('on' + type, f );
			else if (this.addEventListener) this.addEventListener(type, f, stopBubble);

			return func;
		}
		
		$.elementPrototype.computedStyle = function() {
			if (this.currentStyle) return this.currentStyle;
			else return $().defaultView.getComputedStyle(this, null);
		}
	},
	
	include: function(name) {
		var script = new $e('script');
		script.setAttribute('type', 'text/javascript'),
		script.setAttribute('src', name);
		$('head').appendChild(script);
	},
	
	killEvent: function(eventObject) {
		if (window.event) {
			window.event.cancelBubble = true;
			window.event.returnValue = false;
		}

		if (eventObject) {
			if (eventObject.stopPropagation) eventObject.stopPropagation();
			if (eventObject.preventDefault) eventObject.preventDefault();
			if (navigator.appName.indexOf('Firefox') == 0) {
				if (eventObject.preventCapture) eventObject.preventCapture();
	    		if (eventObject.preventBubble) eventObject.preventBubble();
			}
			eventObject.cancelBubble = true;
			eventObject.returnValue = false;
		}
		return false;
	},
	
	extend: function(element) {
		if (!element) return;
		for (name in $.elementPrototype) element[name] = $.elementPrototype[name];
		
		element._elements = new Array();
		element._elements[0] = this;
		return element;
	}
}
atom.init();
})();


$.elementPrototype.load = function(args) {
	args.resultMode = Ajax2.RESULT_TEXT;
	var self = this;
	var onloaded = args.onloaded;
	if (args.callback) var cb = args.callback;
	args.callback = function(result) {
		self.empty();
		self.innerHTML = result;
		onloaded();
		cb();
	}
	new Ajax2(args);	
}