var Atira = {};

/**
 * @todo Maybe improve performace
 */
function $id() {
	var elements = new Array();

	for (var i = 0; i < arguments.length; i++) {
		var element = arguments[i];
		if (typeof element == 'string') {
			element = document.getElementById(element);
		}
		if (arguments.length == 1) {
			return element;			
		}
		elements.push(element);
	}

	return elements;
}
/**
 * Finds elements by class name
 */
function $class(className,parentElement) {
	var children = ($id(parentElement) || document.body).getElementsByTagName('*');
	var elements = [];
	for (var i=0;i<children.length;i++) {
		if (Atira.Element.hasClassName(children[i],className)) {
			elements[elements.length] = children[i];
		}
	}
	return elements;
}

/**
 * Implement push on array if not implemented
 */
if (!Array.prototype.push) {
	Array.prototype.push = function() {
		var startLength = this.length;
		for (var i = 0; i < arguments.length; i++) {
			this[startLength + i] = arguments[i];
		}
		return this.length;
	}
}





/////////////////////////////////////// Element /////////////////////////////////////

/**
 * @class
 * @constructor
 * @private
 */
Atira.Element = function() {}

/**
 * Removes a class from a given element
 * @static
 * @param {Element} element The element to alter
 * @param {String} className The class to remove
 */
Atira.Element.removeClassName = function(element, className) {
	element = $id(element);
	if (!element) return;		

	var newClassName = '';
	var a = element.className.split(' ');
	for (var i = 0; i < a.length; i++) {
		if (a[i] != className) {
			if (i > 0) {
				newClassName += ' ';				
			}
			newClassName += a[i];
		}
	}
	element.className = newClassName;
}

/**
 * Checks if an element has a given class
 * @static
 * @param {Element} element The element to inspect
 * @param {String} className The class to check
 * @return {boolean} True if element has class, false otherwise
 */
Atira.Element.hasClassName = function(element, className) {
	element = $id(element);
	if (!element) return;
	
	var a = element.className.split(' ');
	for (var i = 0; i < a.length; i++) {
		if (a[i] == className) {
			return true;
		}
	}
	return false;
}


/**
 * Adds a class to a given element
 * @static
 * @param {Element} element The element to alter
 * @param {String} className The class to add
 */
Atira.Element.addClassName = function(element, className) {
    element = $id(element);
	if (!element) return;
	
    Atira.Element.removeClassName(element, className);
    element.className += ' ' + className;
}

/**
 * Adds or removes a class from a given element
 * @static
 * @param {Element} element The element to alter
 * @param {String} className The class to add or remove
 */
Atira.Element.toggleClassName = function(element, className) {
    if (Atira.Element.hasClassName(element, className)) {
	    Atira.Element.removeClassName(element, className);
    } else {
	    Atira.Element.addClassName(element, className);
    }
}


/**
 * Finds an elements width as displayed by the browser
 * @param {Element} element The element to analyze
 * @return {int} The width in pixels of the element
 */
Atira.Element.getWidth = function(element) {
	element = $id(element);
	return element.offsetWidth;
}

/**
 * Finds an elements height as displayed by the browser
 * @param {Element} element The element to analyze
 * @return {int} The height in pixels of the element
 */
Atira.Element.getHeight = function(element) {
	element = $id(element);
	return element.offsetHeight;
}


/**
 * Finds an elements distance to the top of the document
 * @param {Element} element The element to analyze
 * @return {int} The width in pixels to the top of the document
 */
Atira.Element.getTop = function(element) {
	element = $id(element);
	var curtop = 0;
	if (element.offsetParent) {
		curtop = element.offsetTop
		while (element = element.offsetParent) {
			curtop += element.offsetTop
		}
	}
	return curtop;
}



/**
 * Finds an elements distance to the left of the document
 * @param {Element} element The element to analyze
 * @return {int} The width in pixels to the left of the document
 */
Atira.Element.getLeft = function(element) {
	element = $id(element);
	var curleft = 0;
	if (element.offsetParent) {
		curleft = element.offsetLeft
		while (element = element.offsetParent) {
			curleft += element.offsetLeft
		}
	}
	return curleft;
}


/**
 * Finds an elements distance to the top and left of the document Fx: {left:10,top:30}
 * @param {Element} element The element to analyze
 * @return {Object} The elements distance to the top and left of the document
 */
Atira.Element.getPosition = function(element) {
	element = $id(element);
	var curleft = curtop = 0;
	if (element.offsetParent) {
		curleft = element.offsetLeft
		curtop = element.offsetTop
		while (element = element.offsetParent) {
			curleft += element.offsetLeft
			curtop += element.offsetTop
		}
	}
	return {left:curleft,top:curtop};
}



////////////////////////////// Window //////////////////////////////

/**
 * @class
 * @constructor
 */
Atira.Window = function() {}

/**
 * Finds how far the window has scrolled from the top
 * @return {int} The number of pixels the window is scrolled from the top
 */
Atira.Window.getScrollTop = function() {
	var x,y;
	if (self.pageYOffset) // all except Explorer
	{
		y = self.pageYOffset;
	}
	else if (document.documentElement && document.documentElement.scrollTop)
		// Explorer 6 Strict
	{
		y = document.documentElement.scrollTop;
	}
	else if (document.body) // all other Explorers
	{
		y = document.body.scrollTop;
	}
	return y;
}

/**
 * Finds how far the window has scrolled from the left
 * @return {int} The number of pixels the window is scrolled from the left
 */
Atira.Window.getScrollLeft = function() {
	var x;
	if (self.pageYOffset) // all except Explorer
	{
		x = self.pageXOffset;
	}
	else if (document.documentElement && document.documentElement.scrollTop)
		// Explorer 6 Strict
	{
		x = document.documentElement.scrollLeft;
	}
	else if (document.body) // all other Explorers
	{
		x = document.body.scrollLeft;
	}
	return x;
}

/**
 * Finds the height of the windows visible view of the document
 * @return {int} The height of the windows view of the document in pixels
 */
Atira.Window.getInnerHeight = function() {
	var y;
	if (self.innerHeight) // all except Explorer
	{
		y = self.innerHeight;
	}
	else if (document.documentElement && document.documentElement.clientHeight)
		// Explorer 6 Strict Mode
	{
		y = document.documentElement.clientHeight;
	}
	else if (document.body) // other Explorers
	{
		y = document.body.clientHeight;
	}
	return y;
}

/**
 * Finds the width of the windows visible view of the document
 * @return {int} The width of the windows view of the document in pixels
 */
Atira.Window.getInnerWidth = function() {
	var x;
	if (self.innerHeight) // all except Explorer
	{
		x = self.innerWidth;
	}
	else if (document.documentElement && document.documentElement.clientHeight)
		// Explorer 6 Strict Mode
	{
		x = document.documentElement.clientWidth;
	}
	else if (document.body) // other Explorers
	{
		x = document.body.clientWidth;
	}
	return x;
}




////////////////////////////// Event ///////////////////////////////


/**
 * @class
 * @constructor
 */
Atira.Event = function() {}

/**
 * Stops an event from being handled by ancestors in the call hierarchy
 * @returns {void}
 * @param {Event} e An event
 */
Atira.Event.stop = function(e) {
	if (!e) e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
}

/**
 * Adds an event listener to an element
 * @returns {void}
 * @param {Element} element The element to attach the listener to
 * @param {String} event The name of the event. Fx: click or mouseover
 * @param {function} delegate The delegate function to receive the event
 * @param {boolean} bubble If the event should bubble (optional)
 */
Atira.Event.addListener = function(element,event,delegate,bubble) {
	bubble = bubble || false;
	if (element.addEventListener){
		element.addEventListener(event, delegate, bubble); 
	} else if (element.attachEvent){
		element.attachEvent('on'+event, delegate);
	}
}



/**
 * Removes an event listener from an element
 * @returns {void}
 * @param {Element} element The element to dettach the listener from
 * @param {String} event The name of the event. Fx: click or mouseover
 * @param {function} delegate The delegate to remove
 */
Atira.Event.removeListener = function(element,event,delegate) {
	if (element.removeEventListener){
		element.removeEventListener(event, delegate, false); 
	} else if (element.detachEvent){
		element.detachEvent('on'+event, delegate);
	}
}

/**
 * Registers a listener to be called when the page has finished loading
 * @returns {void}
 * @param {function} delegate The function to be called when the page has finished loading
 */
Atira.Event.addLoadListener = function(delegate) {
	if(typeof window.addEventListener != 'undefined')
	{
		//.. gecko, safari, konqueror and standard
		window.addEventListener('load', delegate, false);
	}
	else if(typeof document.addEventListener != 'undefined')
	{
		//.. opera 7
		document.addEventListener('load', delegate, false);
	}
	else if(typeof window.attachEvent != 'undefined')
	{
		//.. win/ie
		window.attachEvent('onload', delegate);
	}

	//** remove this condition to degrade older browsers
	else
	{
		//.. mac/ie5 and anything else that gets this far
	
		//if there's an existing onload function
		if(typeof window.onload == 'function')
		{
			//store it
			var existing = window.onload;
		
			//add new onload handler
			window.onload = function()
			{
				//call existing onload function
				existing();
			
				//call delegate onload function
				delegate();
			};
		}
		else
		{
			//setup onload function
			window.onload = delegate;
		}
	}
}

//////////////////////// Array /////////////////////////////////

/**
 * @class
 * @constructor
 */
Atira.Array = function() {};

Atira.Array.clearNulls = function(array) {
	alert(array.length);
	var clone = array.slice();
	for (var i=0;i<clone.length;i++) {
		
	}
}

///////////////////////// URL //////////////////////////////////

/**
 * @class
 * @constructor
 */
Atira.Location = function() {}

Atira.Location.set = function(url) {
	if (typeof(url)=='object') {
		url = Atira.Location.serialize(url);
	}
	window.location = url;
}

Atira.Location.changeParameter = function(key,value) {
	found = false;
	var parsed = Atira.Location.parse();
	for (var i=0;i<parsed.parameters.length;i++) {
		var parm = parsed.parameters[i];
		if (parm.key == key) {
			parm.value = value;
			found = true;
			break;
		}
	}
	if (!found) {
		parsed.parameters[parsed.parameters.length] = {key:key,value:value};
	}
	Atira.Location.set(parsed);
}

Atira.Location.addParameter = function(key,value) {
	found = false;
	var parsed = Atira.Location.parse();
	for (var i=0;i<parsed.parameters.length;i++) {
		var parm = parsed.parameters[i];
		if (parm.key == key && parm.value == value) {
			found = true;
			break;
		}
	}
	if (!found) {
		parsed.parameters[parsed.parameters.length] = {key:key,value:value};
	}
	Atira.Location.set(parsed);
}

Atira.Location.removeParameter = function(key,value) {
	found = false;
	var parsed = Atira.Location.parse();
	for (var i=0,j=0;i<parsed.parameters.length;i++,j++) {
		var parm = parsed.parameters[i];
		if (parm.key == key && (value==undefined || parm.value == value)) {
			parsed.parameters.splice(j,1);
			j--;
		}
	}
	Atira.Location.set(parsed);
}

Atira.Location.appendParameter = function(key,value) {
	
}

Atira.Location.hasParameter = function(key,value/*optional*/) {
	var exists = false;
	var parsed = Atira.Location.parse();
	for (var i=0;i<parsed.parameters.length;i++) {
		var parm = parsed.parameters[i];
		if (parm.key==key && (value==undefined || value==parm.value)) {
			exists = true;
		}
	}
	return exists;
}

Atira.Location.serialize = function(parsed) {
	var result = parsed.base;
	for (var i=0;i<parsed.parameters.length;i++) {
		result+=(i==0 ? '?' : '&');
		result+=parsed.parameters[i].key+'='+parsed.parameters[i].value;
	}
	return result;
}

/**
 * Parses the window location into a structure
 * Example: {base:'http://abc.com/xyz/test.html',parameters:[{key:'foo',value='bar'},{key:'name',value='john'}]}
 */
Atira.Location.parse = function() {
	var parsed = {base:'',parameters:[]};
	var loc = document.location;
	var paramsPart = loc.search.substr(1);
	var params = paramsPart.split('&');
	for (var i=0;i<params.length;i++) {
		var keyValue = params[i].split('=');
		if (keyValue[0].length>0) {
			var item = {key:'',value:''};
			item.key = keyValue[0];
			if (keyValue.length>1) item.value = keyValue[1];
			parsed.parameters[parsed.parameters.length] = item;
		}
	}
	var base = loc.toString().substr(0,loc.toString().length-paramsPart.length);
	if (base.substr(base.length-1)=='?') {
		base = base.substr(0,base.length-1);
	}
	parsed.base = base;
	return parsed;
}

///////////////////////// Browser //////////////////////////////////


/**
 * @class
 * @constructor
 */
Atira.Browser = function() {}

Atira.Browser.isIE = function() {
	var ua = navigator.userAgent;
	var opera = /opera [56789]|opera\/[56789]/i.test(ua);
	var ie = !opera && /MSIE/.test(ua);
	return ie;
}

/**
 * Tests if the browser is based on Apples WebKit
 * @return {bool} True if browser is WebKit based, false otherwise
 */
Atira.Browser.isAppleWebKit = function() {
	return /AppleWebKit/.test(navigator.userAgent);
}

/**
 * Tests if the browser is based on Apples WebKit
 * @return {bool} True if browser is WebKit based, false otherwise
 */
Atira.Browser.isGecko = function() {
	return /Gecko/.test(navigator.userAgent) && !Atira.Browser.isAppleWebKit();
}

/**
 * Tests if the browser is Opera
 * @return {bool} True if the browser is Opera, false otherwise
 */
Atira.Browser.isOpera = function() {
	/opera [56789]|opera\/[56789]/i.test(navigator.userAgent);
}

/**
 * Tests if the client is a Mac
 * @return {bool} True if the client is a Mac, false otherwise
 */
Atira.Browser.isMac = function() {
	return /mac/i.test(navigator.userAgent.toLowerCase());
}

/**
 * Tests if the browser uses the IE box model
 * @return {bool} True if browser uses IE box model, false otherwise
 */
Atira.Browser.isIEbox = function() {
	var ua = navigator.userAgent;
	var opera = /opera [56789]|opera\/[56789]/i.test(ua);
	var ie = !opera && /MSIE/.test(ua);
	return ie && (document.compatMode == null || document.compatMode != "CSS1Compat");
}