/** 
* @projectDescription 	A quicker way to write JavaScript
*
* @author	Wouter Smit
* @version	2.1
*/

var qjs = {

	// Determines whether qjs is done loading
	initialized: false,
	ready: false,
	onloadFired: false,

	// GET parameters
	GET: [],

	/**
		* Add 'prototype' functions to elements
		* @return {null}
	*/
	init: function() {
		if ( !qjs.ready ) {
			qjs.ready = true;
			qjs._getParameters();
			var $all = qjs._tag("*");
			for ( var i=0; i < $all.length; i++ ) {
				qjs._addHandlers($all[i]);
			}
			if(qjs.onInit)qjs.onInit();
			if(qjs.oninit)qjs.oninit();
			if(qjs.onloadFired == false ) {
				if ( qjs.onLoad ) {
					qjs.onLoad();
					qjs.onloadFired = true;
				}
				if ( qjs.onload ) {
					qjs.onload();
					qjs.onloadFired = true;
				}
			}
		}
	},

	/**
		* Retrieves all GET parameters ?a=1&b=2
	*/
	_getParameters: function() {
		var $loc = window.location.href;
		var $parts = $loc.split("?").join("&").split("&");
		for ( var i = 0; i < $parts.length; i++ ) {
			if ( $parts[i].indexOf("=") == -1 ) {
				qjs.GET[$parts[i].split("=")[0]] = "";
			} else {
				qjs.GET[$parts[i].split("=")[0]] = $parts[i].split("=")[1];
			}
		}
	},

	/**
		* Adds special qjs proto functions to an element
		* @param {Mixed} $element element
	*/
	_addHandlers: function($element) {
		if ( $element ) {
			$element.create = qjs._create;
			$element.append = qjs._append;
			$element.css = qjs._css;
			$element.html = qjs._html;
			$element.previous = qjs._previous;
			$element.next = qjs._next;
			$element.show = qjs._show;
			$element.hide = qjs._hide;
			$element.toggle = qjs._toggle;
			$element.fade = qjs._fade;
			$element.alpha = qjs._setAlpha;
			$element.tween = qjs._tween;
			$element.setBounds = qjs._setBounds;
			$element.hasHandlers = true;
		}
	},

	/**
		* Creates an element, adding all qjs proto functions
		* @param {Mixed} $element element
		* @param {Mixed} $type tagname
		* @return {Mixed} created element
	*/
	_create: function($type) {
		// If called from element.create
		if (arguments.length == 0) {
			$type = this;
		}
		var $createdElement = document.createElement($type.toUpperCase());
		qjs._addHandlers($createdElement);
		return $createdElement;
	},

	/**
		* Appends element a to element b (appendChild), adding all qjs proto functions
		* @param {Mixed} $element element
		* @return {Mixed} appended element
	*/
	_append: function($elementA, $elementB) {
		// If called from element.append
		if (arguments.length == 1) {
			$elementB = $elementA;
			$elementA = this;
		}

		// Create the element when element b is a string
		if ( typeof($elementB) == "string" ) {
			var $temp = $elementB;
			$elementB = qjs._create($temp);
		}

		$elementA.appendChild($elementB);
		if ( !$elementB.hadHandlers ) qjs._addHandlers($elementB);
		return $elementB;
	},

	/**
		* Sets a string of css code to an element
		* @param {Mixed} $element element
		* @param {String} $cssCode CSS code
	*/
	_css: function($element, $cssCode) {
		// If called from element.css
		if (arguments.length == 1) {
			$cssCode = $element;
			$element = this;
		}
		// Create an array of all properties so set
		if ( $element ) {
			if ( !$element.hasHandlers ) qjs._addHandlers($element);
			var $cssArray = $cssCode.split(";");
			for ( var i=0; i < $cssArray.length; i++ ) {
				var $prop = $cssArray[i].substr(0,$cssArray[i].indexOf(":"));
				var $val = $cssArray[i].substr($cssArray[i].indexOf(":")+1);
				while ( $prop.indexOf("-") != -1 ) {
					var $propPart = $prop.substr($prop.indexOf("-")+1,1);
					$prop = $prop.split("-"+$propPart).join($propPart.toUpperCase());
				}
				if ( $prop != "" ) $element["style"][$prop] = $val;
			}
			return $element;
		}
	},

	/**
		* Set the innerHTML of an element
		* @param {Mixed} $element element
		* @param {String} $htmlCode HTML code
		* @return {String} innerHTML
	*/
	_html: function($element, $htmlCode) {
		// If called from element.html
		if (arguments.length == 1) {
			$htmlCode = $element;
			$element = this;
		}
		if ( $element ) {
			if ( !$element.hasHandlers ) qjs._addHandlers($element);
			if ( $htmlCode ) $element.innerHTML = $htmlCode;

			// Update handlers for any elements that are created in the innerHTML
			var $innerElements = qjs._tag("*", $element);
			for ( var i = 0; i < $innerElements.length; i++ ) {
				if ( !$innerElements[i].hasHandlers ) qjs._addHandlers($innerElements[i]);
			}

			return $element.innerHTML;
		}
	},

	/**
		* Get all elements with given tagNames
		* @param {String} $tagName tagName
		* @param {Mixed} [$element] element (default=document)
		* @return {Array}
	*/
	_tag: function($tagName, $element) {
		if(!$element)$element = document;
		if ( $element ) {
			return $element.getElementsByTagName($tagName);
		} else {
			return [];
		}
	},

	/**
		* Get element with given id
		* @param {String} $id id of the element
		* @return {Mixed}
	*/
	_id: function($id) {
		if ( $id == "*" ) {
			var $out = [];
			var $all = qjs._tag("*");
			for ( var i=0; i < $all.length; i++ ) {
				if ($all[i].id ) {
					if ( !$all[i].hasHandlers ) qjs._addHandlers($all[i]);
					$out.push($all[i]);
				}
			}
			return $out;
		} else {
			return document.getElementById($id);
		}
	},

	/**
		* Get all elements with given name
		* @param {String} $name name
		* @return {Array}
	*/
	_name: function($name) {
		var $all = qjs._tag("*");
		var i = 0;
		if ( $name == "*" ) {
			var $out = [];
			for ( i=0; i < $all.length; i++ ) {
				if ($all[i].name ) {
					if ( !$all[i].hasHandlers ) qjs._addHandlers($all[i]);
					$out.push($all[i]);
				}
			}
			return $out;
		} else {
			for ( i=0; i < $all.length; i++ ) {
				if ($all[i].name ) {
					if ($all[i].name == $name) {
						if ( !$all[i].hasHandlers ) qjs._addHandlers($all[i]);
						return $all[i];
					}
				}
			}
		}
	},

	/**
		* Get all elements with given class name
		* @param {String} $className class name
		* @return {Array}
	*/
	_class: function($className) {
		var $out = [];
		var $all = qjs._tag("*");
		for ( var i=0; i < $all.length; i++ ) {
			if ($all[i].className ) {
				var $classParts = $all[i].className.split(" ");
				for ( var j=0; j < $classParts.length; j++ ) {
					if ( $classParts[j] == $className ) {
						if ( !$all[i].hasHandlers ) qjs._addHandlers($all[i]);
						$out.push($all[i]);
					}
				}
			}
		}
		return $out;
	},

	/**
		* previousSibling with textNode filter and optional tagName filter
		* @param {String} $element element
		* @param {Mixed} [$tag] tagName (optional)
	*/
	_previous: function($element, $tag) {
		// If called from element.previous
		if ( this.tagName ) {
			$tag = $element;
			$element = this;
		}
		if ( !$element ) return;
		if ( !$element.previousSibling ) return;
		var $el = $element.previousSibling;
		if ( $tag ) {
			$tag = $tag.toUpperCase();
			while( $el.nodeType!=1 || $el.tagName != $tag ) {
				$el = $el.previousSibling;
			}
		} else {
			while( $el.nodeType!=1 ) {
				$el = $el.previousSibling;
			}
		}
		if ( !$el.hasHandlers ) qjs._addHandlers($el);
		return $el;
	},

	/**
		* nextSibling with textNode filter and optional tagName filter
		* @param {String} $element element
		* @param {Mixed} [$tag] tagName (optional)
	*/
	_next: function($element, $tag) {
		// If called from element.next
		if ( this.tagName ) {
			$tag = $element;
			$element = this;
		}
		if ( !$element ) return;
		if ( !$element.nextSibling ) return;
		var $el = $element.nextSibling;
		if ( $tag ) {
			$tag = $tag.toUpperCase();
			while( $el.nodeType!=1 || $el.tagName != $tag ) {
				$el = $el.nextSibling;
			}
		} else {
			while( $el.nodeType!=1 ) {
				$el = $el.nextSibling;
			}
		}
		if ( !$el.hasHandlers ) qjs._addHandlers($el);
		return $el;
	},

	/**
		* returns a runtime CSS style property
		* @param {String} $element element
		* @param {String} $prop property
		* @return {Mixed} [$propval] property value
	*/
	_getStyle: function($element, $prop) {
		var $propval = undefined;
		if ( !$element ) return null;
		if ( $element.currentStyle ) {
			$propval = $element.currentStyle[$prop];
		} else if (window.getComputedStyle) {
			$propval = document.defaultView.getComputedStyle($element,null).getPropertyValue($prop);
		}
		return $propval;
	},

	/**
		* shows an element using style.display (block)
		* @param {String} $element element
	*/
	_show: function($element) {
		// If called from element.show
		if ( this.tagName ) {
			$element = this;
		}
		if ($element) {
			if ( !$element.hasHandlers ) qjs._addHandlers($element);
			$element.style.display = ($element.oldDisplay) ? ($element.oldDisplay != "none") ? $element.oldDisplay : "block" : "block";
		}
		return $element;
	},

	/**
		* hides an element using style.display (none)
		* @param {String} $element element
	*/
	_hide: function($element) {
		// If called from element.hide
		if ( this.tagName ) {
			$element = this;
		}

		if ($element) {
			if ( !$element.hasHandlers ) qjs._addHandlers($element);
			$element.oldDisplay = qjs._getStyle($element,"display");
			$element.style.display = "none";
		}
		return $element;
	},

	/**
		* toggles style.display (none or block)
		* @param {String} $element element
	*/
	_toggle: function($element) {
		// If called from element.toggle
		if ( this.tagName ) {
			$element = this;
		}
		if ($element) {
			if ( !$element.hasHandlers ) qjs._addHandlers($element);
			if ( qjs._getStyle($element,"display") != "none" || qjs._getStyle($element,"display") == "" ) {
				$element.hide();
			} else {
				$element.show();
			}
		}
		return $element;
	},

	/**
		* fade in/out the given element, based on display :block, :'' or :none
		* @param {String} $element element
		* @param {Int} [$duration] duration=1 (optional)
		* @param {String} [$direction] direction (in/out) (optional)
		* @param {Function} [$callback] callback function (optional)
	*/
	_fade: function($element, $duration, $direction, $callback) {
		// If called from element.fade
		if ( this.tagName ) {
			$callback = $direction;
			$direction = $duration;
			$duration = $element;
			$element = this;
		}
		if ( !$element ) return;

		if ( !$element.hasHandlers ) qjs._addHandlers($element);
		if ( !$duration ) $duration = 1;
		if ( !$element.currentAlpha ) $element.currentAlpha = (qjs._getStyle($element,"display") == "block" || qjs._getStyle($element,"display") == "") ? 100 : 0;
		if ( !$element.fadeDirection ) $element.fadeDirection = ($element.currentAlpha==100) ? "out" : "in";
		var $targetDirection = ($direction) ? $direction : $element.fadeDirection;
		if ( $targetDirection == "in" ) {
			$element.currentAlpha += 100 / ($duration * 24);
			if ( $element.currentAlpha < 100 ) {
				qjs._timeout(qjs._fade, (1 / 24) * 1000, $element, $duration, $direction, $callback);
			} else {
				$element.currentAlpha = 100;
				$element.fadeDirection = "out";
				if ( $callback ) $callback();
			}
			if ( $element.currentAlpha > 0 ) $element.show();
		} else {
			$element.currentAlpha -= 100 / ($duration * 24);
			if ( $element.currentAlpha > 0 ) {
				qjs._timeout(qjs._fade, (1 / 24) * 1000 ,$element, $duration, $direction, $callback);
			} else {
				$element.currentAlpha = 0;
				$element.fadeDirection = "in";
				$element.hide();
				if ( $callback ) $callback();
			}
		}
		qjs._setAlpha($element,$element.currentAlpha);
		return $element;
	},

	/**
		* apply alpha opacity to given element
		* @param {String} $element element
		* @param {Int} $alpha alpha
	*/
	_setAlpha: function($element, $alpha) {
		// If called from element.alpha
		if ( this.tagName ) {
			$alpha = $element;
			$element = this;
		}
		if ( $element ) {
			if ( !$element.hasHandlers ) qjs._addHandlers($element);
			$element.style.filter = ($element.currentAlpha==100) ? "" : "alpha(opacity=" + Math.round($alpha) + ")";
			$element.style.MozOpacity = $element.style.opacity = $alpha / 100;
		}
		return $element;
	},

	/**
		* tween bounds of the given element
		* @param {String} $element element
		* @param {Object} [$bounds] bounds
		* @param {Int} duration=1 (optional)
		* @param {Function} [$callback] callback function (optional)
	*/
	_tween: function($element, $bounds, $duration, $callback) {
		// If called from element.tween
		if ( this.tagName ) {
			$callback = $duration;
			$duration = $bounds;
			$bounds = $element;
			$element = this;
		}
		if ( !$element ) return;

		if ( !$element.hasHandlers ) qjs._addHandlers($element);
		if ( !$duration ) $duration = 1;

		if ( !$element.bounds ) $element.bounds = {x:$element.offsetLeft, y:$element.offsetTop, width:$element.offsetWidth, height:$element.offsetHeight};
		if ( !$bounds.x ) $bounds.x = $element.bounds.x;
		if ( !$bounds.y ) $bounds.y = $element.bounds.y;
		if ( !$bounds.width ) $bounds.width = $element.bounds.width;
		if ( !$bounds.height ) $bounds.height = $element.bounds.height;

		if ( $element.bounds.x < $bounds.x ) {
			$element.bounds.x = Math.ceil($element.offsetLeft + (($bounds.x - $element.bounds.x) / ($duration * 2)));
		} else if ( $element.bounds.x > $bounds.x ) {
			$element.bounds.x = Math.ceil($element.offsetLeft - (($element.bounds.x - $bounds.x) / ($duration * 2)));
		}

		if ( $element.bounds.y < $bounds.y ) {
			$element.bounds.y = Math.ceil($element.offsetTop + (($bounds.y - $element.bounds.y) / ($duration * 2)));
		} else if ( $element.bounds.y > $bounds.y ) {
			$element.bounds.y = Math.ceil($element.offsetTop - (($element.bounds.y - $bounds.y) / ($duration * 2)));
		}

		if ( $element.bounds.width < $bounds.width ) {
			$element.bounds.width = Math.ceil($element.offsetWidth + (($bounds.width - $element.bounds.width) / ($duration * 2)));
		} else if ( $element.bounds.width > $bounds.width ) {
			$element.bounds.width = Math.ceil($element.offsetWidth - (($element.bounds.width - $bounds.width) / ($duration * 2)));
		}

		if ( $element.bounds.height < $bounds.height ) {
			$element.bounds.height = Math.ceil($element.offsetHeight + (($bounds.height - $element.bounds.height) / ($duration * 2)));
		} else if ( $element.bounds.height > $bounds.height ) {
			$element.bounds.height = Math.ceil($element.offsetHeight - (($element.bounds.height - $bounds.height) / ($duration * 2)));
		}

		if ( $element.bounds.x != $bounds.x || $element.bounds.y != $bounds.y || $element.bounds.width != $bounds.width || $element.bounds.height != $bounds.height ) {
			qjs._setBounds($element,$element.bounds);
			qjs._timeout(qjs._tween, (1 / 24) * 1000, $element, $bounds, $duration, $callback);
		} else {
			$element.bounds = {x:$element.offsetLeft, y:$element.offsetTop, width:$element.offsetWidth, height:$element.offsetHeight};
			if ( $callback ) $callback();
		}
		return $element;
	},

	/**
		* apply x,y,w,h dimension (bounds) to given element
		* @param {String} $element element
		* @param {Obj} $bounds bounds
	*/
	_setBounds: function($element, $bounds) {
		// If called from element.setBounds
		if ( this.tagName ) {
			$bounds = $element;
			$element = this;
		}
		if ( $element ) {
			if ( !$element.hasHandlers ) qjs._addHandlers($element);
			$element.css("left:" + $bounds.x + "px");
			$element.css("top:" + $bounds.y + "px");
			$element.css("width:" + $bounds.width + "px");
			$element.css("height:" + $bounds.height + "px");
		}
		return $element;
	},

	/**
		* Add an eventListener or attachEvent, depending on browser
		* @param {Mixed} $element element
		* @param {String} $eventName event name
		* @param {Function} $eventFunction function
	*/
	_listener: function($element, $eventName, $eventFunction) {
		if ($element) {
			if ( $element.addEventListener ) {
				$element.addEventListener($eventName, $eventFunction, false);
			} else if ($element.attachEvent) {
				$element.attachEvent("on" + $eventName, $eventFunction);
			}
		}
	},

	/**
		* setTimeout with parameter (argument) support
		* @param {Mixed} $function function
		* @param {String} $delay delay
		* @param {Array} $restParam restParam
	*/
	_timeout: function($function, $delay) {
		var $rest = arguments;
		var $apply = [];
		for ( var i=2; i < $rest.length; i++ ) {
			$apply.push($rest[i]);
		}
		var $func = function(){$function.apply(this,$apply)};
		setTimeout($func, $delay);
	},

	/**
		* setInterval with parameter (argument) support
		* @param {Mixed} $function function
		* @param {String} $interval interval
		* @param {Array} $restParam restParam
	*/
	_interval: function($function, $interval) {
		var $rest = arguments;
		var $apply = [];
		for ( var i=2; i < $rest.length; i++ ) {
			$apply.push($rest[i]);
		}
		var $func = function(){$function.apply(this,$apply)};
		setInterval($func, $interval);
	},

	/**
		* foreach array loop
		* @param {Array} $arr

	*/
	_each: function($arr, $function) {
		if ( $function ) {
			for ( var i = 0; i < $arr.length; i++ ) $function($arr[i]);
		}

		var $rest = arguments;
		var $apply = [];
		for ( var i = 1; i < $rest.length; i++ ) {
			$apply.push($rest[i]);
		}

		$arr.create = function(){return qjs._callFromEach($arr, qjs._create, arguments)}
		$arr.append = function(){return qjs._callFromEach($arr, qjs._append, arguments)}
		$arr.css = function(){return qjs._callFromEach($arr, qjs._css, arguments)}
		$arr.html = function(){return qjs._callFromEach($arr, qjs._html, arguments)}
		$arr.previous = function(){return qjs._callFromEach($arr, qjs._previous, arguments)}
		$arr.next = function(){qjs._callFromEach($arr, qjs._next, arguments)}
		$arr.show = function(){return qjs._callFromEach($arr, qjs._show, arguments)}
		$arr.hide = function(){return qjs._callFromEach($arr, qjs._hide, arguments)}
		$arr.toggle = function(){return qjs._callFromEach($arr, qjs._toggle, arguments)}
		$arr.fade = function(){return qjs._callFromEach($arr, qjs._fade, arguments)}
		$arr.alpha = function(){return qjs._callFromEach($arr, qjs._alpha, arguments)}

		return $arr;
	},

	/**
		* proto function called from array loop
		* @param {Mixed} $function function

	*/
	_callFromEach: function($array, $function, $args) {
		for ( var i = 0; i < $array.length; i++ ) {
			var $rest = $args;
			var $apply = [];
			$apply[0] = $array[i];
			for ( var j = 0; j < $rest.length; j++ ) {
				$apply.push($rest[j]);
			}
			$function.apply(this,$apply);
		}
		return $array;
	},

	/**
		* Add infinite window.onload handlers
		* @param {Function} $function function
	*/
	_addOnload: function($function) {
		if (!qjs.initialized) {
			qjs.initialized = true;
			if ( document.all) {
				qjs._listener(window, "load", $function);
				qjs._listener(window, "load", qjs.init);
				return null;
			} else {
				qjs._listener(window, "load", qjs.init);
			}
		}
		qjs._listener(window, "load", $function);
		return null;
	}

}
qjs._addOnload(qjs.init);
