/**
 * Webstores.js - Webstores javascript library
 * 
 * @author  Webstores <info at webstores dot nl>
 *           Copyright (c) Webstores internet totaalbureau <http://www.webstores.nl/>
 */
var WS = {
	version: 1.4,
	
	browser: {
		IE: /msie/i.test(navigator.userAgent),
		IE6: /msie 6/i.test(navigator.userAgent),
		IE7: /msie 7/i.test(navigator.userAgent),
		Gecko: /gecko/i.test(navigator.userAgent),
		Webkit: /webkit/i.test(navigator.userAgent)
	},
	
	/**
	 * Returns an element
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @return The reference to the element
	 */
	$: function(el) {
		return (typeof el == 'string') ?
			document.getElementById(el) : el;
	},
	
	/**
	 * Logs to console if present
	 * 
	 * @param {Mixed} obj Anything you want to log to console
	 */
	log: function(obj) {
		if(console)
			console.log(obj);
	},
	
	/**
	 * Hides an element
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @return {Object} The hidden element
	 */
	hide: function(el) {
		el = $(el);
		el.style.display = 'none';
		return el;
	},
	
	/**
	 * Shows a hidden element
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @return {Object} The visible element
	 */
	show: function(el) {
		el = $(el);
		el.style.display = '';
		return el;
	},
	
	/**
	 * Toggles an element's display property
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 */
	toggle: function(el) {
		el = $(el);
		el.style.display == '' ?
			this.hide(el) : this.show(el);
	},
	
	/**
	 * Add a class name to an element
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @param {String} cls The class name to add
	 */
	addClass: function(el, cls) {
		el = $(el);
		if(!this.hasClass(el, cls))
			el.className += (' ' + cls);
	},
	
	/**
	 * Remove a class name from an element
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @param {String} cls The class name to remove
	 */
	removeClass: function(el, cls) {
		el = $(el);
		if(this.hasClass(el, cls)) {
			var regex = new RegExp('(\\s|^)' + cls + '(\\s|$)');
			el.className = el.className.replace(regex, ' ');
		}
	},
	
	/**
	 * Toggle between a class name
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @param {String} cls The class name to toggle
	 */
	toggleClass: function(el, cls) {
		if(this.hasClass(el, cls))
			this.removeClass(el, cls);
		else
			this.addClass(el, cls);
	},
	
	/**
	 * Check if an element has a class name
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @param {String} cls The class name to check
	 * @param {Boolean} greedy If true, matches any characters in className
	 * @return {Boolean} True if the element has the className, false otherwise
	 */
	hasClass: function(el, cls, greedy) {
		el = $(el);
		if(greedy) {
			var regex = new RegExp(cls);
			return regex.test(el.className);
		}
		else {
			return el.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
		}
	},
	
	/**
	 * Gets the opacity of an element
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @return {Number} The element's opacity
	 */
	getOpacity: function(el) {
		el = $(el);
		if(WS.browser.IE) {
			return el.style.filter ? (parseFloat(el.style.filter.replace('alpha(opacity=', '')) / 100) : 1.0;
		}
		return el.style.opacity || 1.0;
	},
	
	/**
	 * Sets the opacity of an element
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @param {Number} op The opacity value (0-10)
	 */
	setOpacity: function(el, op) {
		el = $(el);
		if(WS.browser.IE) {
			var ieOp = (op * 100);
			if(ieOp < 100)
				el.style.filter = 'alpha(opacity=' + ieOp + ')';
			else
				el.style.filter = '';
		}
		else {
			el.style.opacity = op;
		}
	},
	
	/**
	 * Set the text of an element
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @param {String} text The element's new text
	 */
	setText: function(el, text) {
		el = $(el);
		el.innerHTML = text;
	}
}

if(typeof $ != 'function') {
	$ = WS.$;
}


/**
 * WS.DOM - Webstores DOM traversing
 * 
 * @author  Webstores <info at webstores dot nl>
 *           Copyright (c) Webstores internet totaalbureau <http://www.webstores.nl/>
 */
WS.DOM = {
	/**
	 * Dustin Diaz's implementation of getElementsByClass
	 * 
	 * @param {String} searchClass Class name as a string
	 * @param {Object} node (optional) Supply a node (default: document)
	 * @param {Object} tag (optional) Limit the results by adding a tagName (default: *)
	 * @return {Array} An array containing the elements found
	 * 
	 * @see http://www.dustindiaz.com/getelementsbyclass/
	 */
	getElementsByClass: function(searchClass, node, tag) {
		var classElements = new Array();
		if(node == null)
			node = document;
		if(tag == null)
			tag = '*';
		var els = node.getElementsByTagName(tag);
		var elsLen = els.length;
		var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
		for(i = 0, j = 0; i < elsLen; i++) {
			if(pattern.test(els[i].className)) {
				classElements[j] = els[i];
				j++;
			}
		}
		return classElements;
	},
	
	/**
	 * Returns a child node at an arbitary depth
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @param {Number} depth The depth of the child node to retrieve
	 * @return {Object} The child node of arbitary depth
	 */
	getChild: function(el, depth) {
		var child = $(el).childNodes[depth-1];
		while(child.nodeType != 1)
			child = child.nextSibling;
		return child;
	},
	
	/**
	 * Returns a sibling node
	 * 
	 * @param {Mixed} el The ID of or reference to an element
	 * @return {Object} The element's sibling node
	 */
	next: function(el) {
		var next = el.nextSibling;
		while(next.nodeType != 1)
			next = next.nextSibling;
		return next;
	},
	
	/**
	 * Inserts a node after the reference node
	 * 
	 * @param {Object} parent The node's parent node
	 * @param {Object} node The node to insert
	 * @param {Object} referenceNode The node to insert after
	 */
	insertAfter: function(parent, node, referenceNode) {
		parent.insertBefore(node, referenceNode.nextSibling);
	}
}


/**
 * WS.Util - Webstores utility
 * 
 * @author  Webstores <info at webstores dot nl>
 *           Copyright (c) Webstores internet totaalbureau <http://www.webstores.nl/>
 */
WS.Util = {
	/**
	 * Hides and reveals default input value
	 * 
	 * @param {String} id The ID of the search input
	 * @param {String} fCls The input focus class
	 */
	toggleValue: function(id, fCls) {
		var el = document.getElementById(id);
		var v = el.value;
		
		el.onfocus = function() {
			if(this.value == v)
				this.value = '';
			this.className = fCls;
		}
		el.onblur = function() {
			if(this.value == '')
				this.value = v;
			this.className = '';
		}
	},
	
	/**
	 * Creates a toggler/container pair
	 * 
	 * @param {String} id The ID of the elements
	 */
	createToggle: function(id) {
		var t = $(id + '-toggle');
		var c = $(id + '-container');
		WS.hide(c);
		
		addEvent(t, 'click', function(e) {
			WS.Event.stopEvent(e);
			WS.toggle(c);
		});
	}
}


/**
 * WS.Event - Webstores event handling
 * 
 * @author  Webstores <info at webstores dot nl>
 *           Copyright (c) Webstores internet totaalbureau <http://www.webstores.nl/>
 */
WS.Event = {
	/**
	 * Cross browser function to ADD an event listener to an element
	 * 
	 * @param {Mixed} el The ID of or reference to an element to add the event to
	 * @param {String} type The event type, eg. 'click' or 'focus'
	 * @param {Function} listener The function to execute when the event fires
	 * @param {Boolean} useCapture (optional) Whether to use event capturing, or event bubbling (default).
	 */
	addEvent: function(el, type, listener, useCapture) {
		var el = $(el);
		var self = this;
		useCapture = useCapture || false;
		
		if(WS.browser.Gecko) {
			el.addEventListener(type, listener, useCapture);
		}
		else if(WS.browser.IE) {
			var r = el.attachEvent('on' + type, function() {
				listener.call(el, window.event);
			});
			return r;
		}
	},
	
	/**
	 * Cross browser function to REMOVE an event listener from an element
	 * 
	 * @param {Mixed} el The ID of or reference to an element to remove the event from
	 * @param {String} type The event type being removed, eg. 'click' or 'focus'
	 * @param {Function} listener The EventListener function to be removed
	 * @param {Boolean} useCapture (optional)
	 */
	removeEvent: function(el, type, listener, useCapture) {
		useCapture = useCapture || false;
		
		if(WS.browser.Gecko) {
			el.removeEventListener(type, listener, useCapture);
		}
		else if(WS.browser.IE) {
			var r = el.detachEvent('on' + type, listener);
		}
	},
	
	/**
	 * Cross browser function to stop the default event behavior
	 * 
	 * @param {Object} e The event object to stop
	 */
	stopEvent: function(e) {
		e = e || window.event;
		
		if(WS.browser.Gecko)
			e.preventDefault();
		else
			e.returnValue = false;
	}
}


/**
 * WS.Fx - Webstores effects
 * 
 * @author  Webstores <info at webstores dot nl>
 *           Copyright (c) Webstores internet totaalbureau <http://www.webstores.nl/>
 */
WS.Fx = {
	duration: 50,
		
	/*FadeOut: function(el) {
		el = $(el);
		var oldOpacity = el.getInlineOpacity();
		var options = Object.extend({
			from: el.getOpacity() || 1.0,
			to: 0.0,
			afterFinishInternal: function(effect) {
				if (effect.options.to!=0) return;
				effect.el.hide().setStyle({opacity: oldOpacity});
			}
		}, arguments[1] || { });
		return new Effect.Opacity(el, options);
	},
	
	FadeIn: function(el) {
		el = $(el);
		var options = Object.extend({
			from: (el.getStyle('display') == 'none' ? 0.0 : el.getOpacity() || 0.0),
			to: 1.0,
			afterFinishInternal: function(effect) {
				effect.el.forceRerendering();
			},
			beforeSetup: function(effect) {
				effect.el.setOpacity(effect.options.from).show();
			}
		}, arguments[1] || { });
		return new Effect.Opacity(el, options);
	}*/
	
	Fade: function(el, opacity, effectFn) {
		el = $(el);
		effectFn = effectFn || WS.Fx.Transitions.Linear;
		
		console.log(WS.getOpacity(el));
		//if(opacity !== 0)
		//	opacity = opacity || 10;
		setOpacity(el, effectFn.call(opacity, WS.getOpacity(el), 0, this.duration));
		var self = this;
		
		if(opacity > 0) {
			setTimeout(function() {
				console.log(WS.getOpacity(el));
				self.Fade(el);
			}, 1000 / this.duration);
		}
	}
}


/**
 * WS.Transitions - Webstores effect transitions
 * 
 * @author  Webstores <info at webstores dot nl>
 *           Copyright (c) Webstores internet totaalbureau <http://www.webstores.nl/>
 */
WS.Fx.Transitions = {
	/**
	 * Linear transition
	 * 
	 * @param {Number} t Time
	 * @param {Number} b Begin
	 * @param {Number} c Change
	 * @param {Number} d Duration
	 */
	Linear: function(t, b, c, d) {
		return c * t / d + b;
	},
	
	/**
	 * Quint out transition
	 * 
	 * @param {Number} t Time
	 * @param {Number} b Begin
	 * @param {Number} c Change
	 * @param {Number} d Duration
	 */
	QuintOut: function(t, b, c, d) {
		return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
	}
}


/**
 * Javascript extensions
 * ------------------------------------------------------------
 */

/**
 * Binds a function call to a specific scope
 */
Function.prototype.bind = function() {
	var handler = this, args = Array.slice(arguments, 0), obj = args.shift();
	
	return function() {
		return handler.apply(obj, args.concat(Array.slice(arguments, 0)));
	}
}

/**
 * Internet Explorer does not have Array.indexOf implemented
 * 
 * @param {Mixed} obj The object to find
 * @return {Number} The index of obj or -1
 */
if(!Array.indexOf) {
	Array.prototype.indexOf = function(obj) {
		for(var i = 0; i < this.length; i++) {
			if(this[i] == obj) {
				return i;
			}
		}
		return -1;
	}
}

/**
 * Returns the first element of the array
 * 
 * @return The first element of the array
 */
Array.prototype.first = function() {
	return this[0];
}

/**
 * Returns the last element of the array
 * 
 * @return The last element of the array
 */
Array.prototype.last = function() {
	return this[this.length - 1];
}
