/*!
 *
 *	JELLY JavaScript Library (c) 2008 Pete Boere --/ v.1+ /--
 *	last modified: 02/09/2008
 *
 */
var JELLY = function () {
	var treeWalker = function (el, mode, opts) {
			var repeat = opts.repeat || 1;
			switch (mode) {
				case 'sibling' : 
					do {
						do {
							el = el[opts.type + 'Sibling'];
						} while (el && el.nodeType !== 1); 
					 } while (el && --repeat);
				break;
				case 'child' : 
					switch (opts.type) { 
					case 'first':
						el = el.firstChild;
						while (el && el.nodeType !== 1) {
							el = el.nextSibling;
						}
						break;
					case 'last':
						el = el.lastChild;
						while (el && el.nodeType !== 1) {
							el = el.previousSibling;
						}  
						break;
					case 'all':
						el = el.firstChild;
						var children = [];
						while (el) {
							el = el.nextSibling;
							if (el && el.nodeType === 1) {
								children[children.length] = el;
							}
						}
						return children.length ? children : null;
					}
				break;
				case 'parent' : 
					do {
						el = el.parentNode;
					} while (el && --repeat);
			}
			return el;
		},
		toArray = function (obj) {
			var c = obj.length, result = [], i;
			for (i = 0; i < c; i++) {
				result[i] = obj[i];
			}
			return result;
		},
		isDefined = function (obj) {
			return typeof obj !== 'undefined';
		},
		isArray = function (obj) {
			return obj.constructor === Array;
		},
		isString = function (obj) {
			return typeof obj === 'string';
		},
		parseElement = function (obj) {
			return typeof obj === 'string' ? document.getElementById(obj) : obj;
		},
		document = window.document,
		htmlElement = document.documentElement;
		
	return {
		
	// ------>  Working with Elements  <-------

		addClass: function (el, cn) {
			el.className += el.className ? ' ' + cn : cn;
		},
		removeClass: function (el, cn) {
			var patt = new RegExp('(^|\\s|\\b)' + cn + '(\\s|$)', 'g');
			el.className = el.className.replace(patt, ' ').normalize();
		},
		hasClass: function (el, cn) {
			var patt = new RegExp('(^|\\s)' + cn + '(\\s|$)');
			return patt.test(el.className);
		},
		toggleClass: function (el, cn){
			if (JELLY.hasClass(el, cn)) {
				JELLY.removeClass(el, cn); 
			} else {
				JELLY.addClass(el, cn);
			}
		},
		createElement: function (type, args) {
			var el = document.createElement(type), i;
			for (i in args) {
				switch (i) {
					case 'setHTML' : el.innerHTML = args[i]; break;
					case 'setText' : el.appendChild(document.createTextNode(args[i])); break;
					case 'class' : el.className = args[i]; break;
					case 'style' : el.style.cssText = args[i]; break;
					default : el.setAttribute(i, args[i]);
				}
			}
			return el;
		},
		createBranch: function () {
			var args = toArray(arguments),
				refs = {},
				parseNode = function (arg) {
					if (isString(arg)) {
						return JELLY.createElement(arg);
					} else {
						return arg.cloneNode(true);
					}
				},
				indexRef = function (node) {
					var tagName = node.nodeName.toLowerCase();
					if (!refs[tagName]) {
						refs[tagName] = [node];
					} else {
						refs[tagName].push(node);
					}
				},
				currentContext = [parseNode(args.shift())];
			refs.root = currentContext[0];
			indexRef(currentContext[0]);
			args.each(function (item) {
				var store = [];
				for (var i = 0; i < currentContext.length; i++) { 
					if (!isArray(item)) {
						item = [item];
					}
					for (var j = 0; j < item.length; j++) { 
						(function () {
							var node = parseNode(item[j]);
							currentContext[i].appendChild(node);
							store.push(node);
							indexRef(node);
						})();
					}
				}
				currentContext = store;
			});
			return refs;
		},
		insertElement: function (el, datum, type) {
			type = type || 'bottom';
			switch (type) {
				case 'before' : return datum.parentNode.insertBefore(el, datum);
				case 'after' : return datum.parentNode.insertBefore(el, datum.nextSibling);
				case 'top' : return datum.insertBefore(el, datum.firstChild);
				default : return datum.appendChild(el);
			}
		},
		replaceElement: function (el, replacement) {
			return el.parentNode.replaceChild(replacement, el);
		},
		removeElement: function (el) {
			return el.parentNode.removeChild(el);
		},
		getFirst: function (el) {
			return treeWalker(el, 'child', {type: 'first'});
		},
		getLast: function (el) {
			return treeWalker(el, 'child', {type: 'last'});
		},
		getNext: function (el, repeat) {
			return treeWalker(el, 'sibling', {type: 'next', repeat: repeat});
		},
		getPrevious: function (el, repeat) {
			return treeWalker(el, 'sibling', {type: 'previous', repeat: repeat});
		},
		getChildren: function (el) {
			return treeWalker(el, 'child', {type: 'all'});
		},
		getChild: function (el, number) {
			number = number || 1;
			var collection = treeWalker(el, 'child', {type: 'all'});
			if (collection) {
				return collection[number - 1] || null;
			}
			return null;
		},
		getParent: function (el, repeat) {
			return treeWalker(el, 'parent', {repeat: repeat});
		},
		getXY: function (el) {
			var xy = [0, 0];
			do {
				xy[0] += el.offsetLeft;
				xy[1] += el.offsetTop;
			} while (el = el.offsetParent);
			return xy;
		},
		getX: function (el) {
			return JELLY.getXY(el)[0];
		},
		getY: function (el) {
			return JELLY.getXY(el)[1];
		},
		setXY: function (el, X, Y, unit) {
			unit = unit || 'px';
			el.style.left = X + unit;
			el.style.top = Y + unit;
		},
		getStyle: function (el, prop, parseInteger) {
			prop = JELLY.Str.toCamelCase(prop);
			var value;
			if (prop === 'opacity') { 
				if (el.__opacity === undefined) {
					el.__opacity = 1;
				}
				return el.__opacity;
			}
			if (el.style[prop]) {
				value = el.style[prop];
			} else if ('getComputedStyle' in window) {
				value = window.getComputedStyle(el, null)[prop];
			} else if ('currentStyle' in el) {
				value = el.currentStyle[prop];
			}
			return parseInteger === true ? parseInt(value, 10) : value;
		},
		setOpacity: function (el, val) {
			if (el.filters) {
				JELLY.setOpacity = function (el, val) {
					if (el.__opacity === undefined) {
						el.__opacity = 1;		
					}
					el.style.filter = val === 1 ? '' : 'alpha(opacity=' + val * 100 + ')';
					el.__opacity = val;
				};
			} else {
				JELLY.setOpacity = function (el, val) {
					if (el.__opacity === undefined) {
						el.__opacity = 1;		
					}
					el.style.opacity = el.__opacity = val;
				};
			}
			JELLY.setOpacity(el, val);
		},
		
		// ------>  Dom Query  <-------

		Dom: {
			getId: function (arg) {
				return document.getElementById(arg.replace(/(\w+)?#/, ''));
			},
			getTags: function (tag, node) {
				if (node === null) {
					return node;
				} 
				return toArray((node || document).getElementsByTagName(tag));
			},
			getClass: function () {
				if (isDefined(document.getElementsByClassName)) {
					return function (cname, node) {
						if (node === null) {
							return node;
						} 
						return toArray(
							(node || document).getElementsByClassName(cname.replace(/(\w+)?\./, '')));
					}; 
				}
				return function (cname, node) {
					if (node === null) {
						return node;
					} 
					var collection = toArray((node || document).getElementsByTagName('*')),
						result = [],
						patt = new RegExp('(^|\\s)' + cname.replace(/(\w+)?\./, '') + '(\\s|$)'),
						c = collection.length, i;
					for (i = 0; i < c; i++) {
						if (patt.test(collection[i].className)) {
							result[result.length] = collection[i];
						}
					}
					return result;
				};
			}(),
			filterByClass: function (cname, collection) {
				collection = collection || document.getElementsByTagName('*');
				var result = [],
					patt = new RegExp('(^|\\s)' + cname.replace(/(\w+)?\./, '') + '(\\s|$)'),
					c = collection.length, i;
				for (i = 0; i < c; i++) {
					if (patt.test(collection[i].className)) {
						result[result.length] = collection[i];
					}
				}
				return result;
			},
			filterByAttribute: function (collection, arg) {
				if (!isArray(collection)) {
					collection = toArray(collection);
				}
				arg = arg.normalize();
				var getAttr = function () {
						if (!isDefined(collection[0].hasAttibute) && JELLY.browser.ie) {
							return function (node, attribute) {
								switch (true) {
									case /for/i.test(attribute) :
										return node.attributes[attribute].nodeValue;
									case /class/i.test(attribute) :
										return node.attributes[attribute].nodeValue || null;
									case /href|src/i.test(attribute) :
										return node.getAttribute(attribute, 2);
									case /style/i.test(attribute) :
										return node.getAttribute(attribute).cssText.toLowerCase() || null;
								}
								return node.getAttribute(attribute);
							};
						}
						return function (node, attribute) {
							return node.getAttribute(attribute);
						};
					}(),   
					result = [],
					len = collection.length,
					i, node;
				if (/=/.test(arg)) {
					var parts = /([^\^\$\*]+)((?:\^|\$|\*)?=)([^$]*)/.exec(arg),
						patt,
						testNode;
					switch (parts[2]) {
						case '=' : patt = 
							patt = new RegExp('^' + parts[3] + '$'); 
							break;
						case '^=' : 
							patt = new RegExp('^' + parts[3]); 
							break;
						case '$=' : 
							patt = new RegExp(parts[3] + '$'); 
							break;
						case '*=' : 
							patt = new RegExp(parts[3]); 
							break;
					}
					testNode = function (node, testValue) {
						if (testValue !== null && patt.test(testValue)) {
							result[result.length] = node;
						}
					};
					for (i = 0; i < len; i++) {
						node = collection[i];
						testNode(node, getAttr(node, parts[1]));
					}
				} else {
					for (i = 0; i < len; i++) {
						node = collection[i];
						if (getAttr(node, arg) !== null) {
							result[result.length] = node;                    
						}
					}
				}
				return result;
			}
		},
		Q: function (arg) {
			var getId = JELLY.Dom.getId,
				getTags = JELLY.Dom.getTags,
				getClass = JELLY.Dom.getClass,
				filterByClass = JELLY.Dom.filterByClass,
				filterByAttribute = JELLY.Dom.filterByAttribute;
			if (!isString(arg)) {
				return toArray(arg);   
		   	}
		   	var parts = arg.normalize().split(' ');
		   	switch (true) {
		    	case /^(\w+)?#[^\s]+$/.test(arg) :
		        	return getId(arg);                                              
		   	    case /^\w+$/.test(arg) :
		    	    return getTags(arg);                                              
		      	case /^\w+\.[^\s]+$/.test(arg) :
		        	return filterByClass(arg, getTags(arg.split('.')[0]));  
		        case /^\.[^\s]+$/.test(arg) :
		            return getClass(arg);                          
		        case /^(\w+)?#[^\s]+\s\w+$/.test(arg) :
					return getTags(parts[1], getId(parts[0]));  
		        case /^(\w+)?#[^\s]+\s\.[^\s]+$/.test(arg) :
		            return getClass(parts[1], getId(parts[0]));
		        case /^(\w+)?#[^\s]+\s\w+\.[^\s]+$/.test(arg) :
		            return filterByClass(parts[1], getTags(parts[1].split('.')[0], getId(parts[0])));                  
		   	}
		   	var collection = [], 
				uniques = [], 
				attributeSelector, temp, p, i, c, j;
			for (i = 0; parts.length > i; i++) {
				p = parts[i];
				temp = [];
				if (/\[/.test(p)) {
					attributeSelector = p.split('[')[1].replace(']', '');
					p = p.split('[')[0];
				}
				switch (true) {
					case /^(\w+)?#[^\s]+$/.test(p) : 
						temp = [getId(p)];
						break;
					case /^\w+$/.test(p) : 
						if (i === 0) {
							temp = getTags(p);
						} else {
							for (j = 0; j < collection.length; j++) {
								c = getTags(p, collection[j]);
								if (c[0]) {
									temp = temp.concat(c);
								}
							}
						}
						break;
					case /^\.[^\s]+$/.test(p) : 
						if (i === 0) {
							temp = getClass(p);
						} else {
							for (j = 0; j < collection.length; j++) {
								c = getClass(p, collection[j]);
								if (c[0]) {
									temp = temp.concat(c);
								}
							}
						}
						break;
					case /^\w+\.[^\s]+$/.test(p) : 
						if (i === 0) {
							temp = filterByClass(p, getTags(p.split('.')[0]));
						} else {
							for (j = 0; j < collection.length; j++) {
								c = filterByClass(p, getTags(p.split('.')[0], collection[j]));
								if (c[0]) {
									temp = temp.concat(c);
								}
							}
						}
						break;
				}
				if (attributeSelector) {
					temp = filterByAttribute(temp, attributeSelector);
					attributeSelector = null;
				} 
				if (!temp[0]) {
					return null;
				}
				collection = temp;
			}
			while (c = collection.pop()) {
				if (!c.__jelly) {
					c.__jelly = true;
					uniques[uniques.length] = c;
				}
			}
			for (i = 0; uniques.length > i; i++) {
				uniques[i].__jelly = undefined;
			}
			return uniques.reverse();
		},

		// ------>  Events  <-------
		
		addEvent: function (obj, type, fn) {
			obj = parseElement(obj);
			var W3C = !!obj.addEventListener, 
				mouseEnter = /mouseenter/.test(type),
				mouseLeave = /mouseleave/.test(type),
				host = arguments.callee,
				wrapper, 
				ref;
			host.log = host.log || [];
			if (!W3C) {
				wrapper = function (e) {
					e = JELLY.fixEvent(e);
					fn.call(obj, e);
				};
			}
			if (mouseEnter || mouseLeave) {
				wrapper = function (e) {
					e = JELLY.fixEvent(e);
					if (!JELLY.mouseEnterLeave.call(obj, e)) {
						return;
					}
					fn.call(obj, e);
				};
				type = mouseEnter ? 'mouseover' : 'mouseout';
			}
			ref = [obj, type, wrapper || fn];
			host.log.push(ref);
			if (W3C) { 
				obj.addEventListener(type, wrapper || fn, false);
			} else {
				obj.attachEvent('on' + type, wrapper);
			}
			return ref;
		},
		removeEvent: function (arr) {
			if (arr[0].removeEventListener) {
				arr[0].removeEventListener(arr[1], arr[2], false);
			} else {
				arr[0].detachEvent('on' + arr[1], arr[2]);
			}
		},
		purgeEventLog: function () {
			if (JELLY.addEvent.log.length > 1) {
				var arr = JELLY.addEvent.log, i, c;
				for (i = 0; arr[i]; i++) {
					c = arr[i];					
					if (c[0] === window && c[1] === 'unload') {
						continue;
					}
					JELLY.removeEvent(c);
				}
			}
		}, 
		fixEvent: function (e) {
			e = e || window.event;
			if (window.event && !window.opera) {
				e.target = e.srcElement;
				e.relatedTarget = function () {
					switch (e.type) {
						case 'mouseover' : return e.fromElement;
						case 'mouseout' : return e.toElement;
					}		 
				}();
				e.stopPropagation = function () {e.cancelBubble = true;};
				e.preventDefault = function () {e.returnValue = false;};
				e.pageX = e.clientX + htmlElement.scrollLeft;
				e.pageY = e.clientY + htmlElement.scrollTop;
			}
			return e;
		},
		mouseEnterLeave: function (e) { 
			var related, i;
			if (e.relatedTarget) {
				related = e.relatedTarget;
				if (related.nodeType !== 1 || related === this) {
					return false;
				}
				var children = this.getElementsByTagName('*');
				for (i=0; children[i]; i++) {
					if (related === children[i]) {
						return false;
					}
				}
			}
			return true;
		},
		stopEvent: function (e) {
			e = JELLY.fixEvent(e);
			e.stopPropagation();
			e.preventDefault();
		},
				
		// ------>  Flash  <-------
		
		getFlashVersion: function () {
			var version = {major: null, build: null},
				description,
				versionString,
				aXflash;
			if (navigator.plugins && typeof navigator.plugins['Shockwave Flash'] === 'object') {
				description = navigator.plugins['Shockwave Flash'].description;
				if (description !== null) {
					versionString = description.replace(/^[^\d]+/, '');
					version.major = parseInt(versionString.replace(/^(.*)\..*$/, '$1'), 10);
					version.build = parseInt(versionString.replace(/^.*r(.*)$/, '$1'), 10);
				}
			} else if (JELLY.browser.ie) {
				try {
					aXflash = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
					description = aXflash.GetVariable('$version');
					if (description !== null) {
						versionString = description.replace(/^\S+\s+(.*)$/, '$1').split(',');
						version.major = parseInt(versionString[0], 10);
						version.build = parseInt(versionString[2], 10);
					}
				} catch(ex) {}
			}
			return version;
		},
		createFlashObject: function (opts) {
			var path = opts.path,
				attributes = opts.attributes || {},
				params = opts.params || {};
				vars = opts.vars || {},
				fallback = opts.fallback || 
					'<a href="http://www.adobe.com/go/getflashplayer">' + 
					'<img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif"' + 
					'alt="You need the latest Adobe Flash Player to view this content" /></a>',
				data = [],
				output = '<object';
			if (JELLY.browser.ie) {
				attributes.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
				params.movie = path;
			} else {
				attributes.data = path;
				attributes.type = 'application/x-shockwave-flash';
			}
			attributes.width = opts.width;
			attributes.height = opts.height;
			for (var i in attributes) {
				output += ' ' + i + '="' + attributes[i] + '"';
			}
			output += '>\n';
			for (var i in vars) {
				data.push(i + '=' + encodeURIComponent(vars[i]));
			}
			if (data.length > 0) {
				params.flashvars = data.join('&');
			} 
			for (var i in params) {
				output += '\t<param name="' + i + '" value="' + params[i] + '" />\n'; 
			}
			return output + fallback + '\n</object>';
		},
		
		// ------>  Utilities  <-------
		
		Str: {
			toCamelCase: function (str) {
				return str.replace(/-\D/gi, function (m) {
					return m.charAt(m.length - 1).toUpperCase();
				});
			}, 
			toCssCase: function (str) {
				return str.replace(/([A-Z])/g, '-$1').toLowerCase();
			}, 
			rgbToHex: function (str) {
				var rgb = str.match(/[\d]{1,3}/g), hex = [], i;
				for (i = 0; i < 3; i++) {
					var bit = (rgb[i]-0).toString(16);
					hex.push(bit.length === 1 ? '0'+bit : bit);
				}
				return '#' + hex.join('');
			},
			hexToRgb: function (str, array) {
				var hex = str.match(/^#([\w]{1,2})([\w]{1,2})([\w]{1,2})$/), rgb = [], i;
				for (i = 1; i < hex.length; i++) {
					if (hex[i].length === 1) {
						hex[i] += hex[i];
					}
					rgb.push(parseInt(hex[i], 16));
				}
				return array ? rgb : 'rgb(' + rgb.join(',') + ')';
			},
			parseColour: function (str, mode) {
				var rgbToHex = JELLY.Str.rgbToHex,
					hexToRgb = JELLY.Str.hexToRgb,
					hex = /^#/.test(str), 
					tempArray = [], temp;
				switch (mode) {
					case 'hex':
						if (hex) {
							return str;
						} else {
							return rgbToHex(str);
						}
					case 'rgb': 
						if (hex) {
							return hexToRgb(str);
						} else {
							return str;
						}
					case 'rgb-array': 
						if (hex) {
							return hexToRgb(str, true);
						} else {
							temp = str.replace(/rgb| |\(|\)/g, '').split(',');
							temp.each(function (item) {
									tempArray.push(parseInt(item, 10));
								});
							return tempArray;
						}
				}
			} 
		},
		getCookie: function (name) {
			var result = new RegExp(name + '=([^; ]+)').exec(document.cookie);
			if (!result) {
				return null;
			}
			return unescape(result[1]);
		},
		setCookie: function (name, value, expires, path, domain, secure) {
			if (expires) {
				expires = new Date(new Date().getTime()+((1000*60*60*24)*expires)).toGMTString();
			}
			document.cookie = name + '=' + escape(value) +
				(expires ? ';expires=' + expires : '') + 
				(path ? ';path=' + path : '') +
				(domain ? ';domain=' + domain : '') +	
				(secure ? ';secure' : '');
		},
		removeCookie: function (name, path, domain) {
			if (JELLY.getCookie(name)) {
				document.cookie = name + '=' +
					(path ? ';path=' + path : '') +
					(domain ? ';domain=' + domain : '') +	
					(';expires=' + new Date(0));
			}
		},
		getViewport: function () {
			if (isDefined(window.innerWidth)) {
				return function () {
					return [window.innerWidth, window.innerHeight];
				};
			} 
			if (isDefined(htmlElement) && isDefined(htmlElement.clientWidth) && htmlElement.clientWidth !== 0) { 
				return function () {
					return [htmlElement.clientWidth, htmlElement.clientHeight];
				};
			}
			return function () {
				return [document.body.clientWidth || 0, document.body.clientHeight || 0];
			};
		}(),
		getWindowScroll: function () {
			if (isDefined(window.pageYOffset)) {
				return function () {
					return [window.pageXOffset, window.pageYOffset];
				};
			} 
			return function () {
				if (isDefined(htmlElement.scrollTop) && 
					(htmlElement.scrollTop > 0 || htmlElement.scrollLeft > 0)) {
				return [htmlElement.scrollLeft, htmlElement.scrollTop];
				}
				return [document.body.scrollLeft, document.body.scrollTop];
			};
		}(),
		parseQueryString: function (el) {
			el = el || window;
			if (/\?/.test(el.href)) {
				var queries = el.href.split('?')[1].split('&'),
					 i = queries.length-1,
					 pairs = {},
					 parts;
				do {
					parts = queries[i].split('=');
					pairs[parts[0]] = parts[1];
				} while (i--);
				return pairs;
			}
			return null;
		},
		extend: function (obj1, obj2, overwrite) {
			for (var i in obj2) {
				if (isDefined(obj1[i]) && overwrite === false) {
					continue;
				}
				obj1[i] = obj2[i];
			}
			return obj1;
		},
		bind: function () {
            var args = toArray(arguments),
                obj = args.shift(),
                func = args.shift();
            return function () {
                return func.apply(obj, args);
            };
        },
        bindEventListener: function () {
            var args = toArray(arguments),
                obj = args.shift(),
                func = args.shift();
            return function (e) {
                return func.apply(obj, [e].concat(args));
            };
        },
		toArray: function (obj) {
			return toArray(obj);
		},
		isDefined: function (obj) {
			return isDefined(obj);
		},
		isArray: function (obj) {
			return isArray(obj);
		},
		isString: function (obj) {
			return isString(obj);
		},
		parseElement: function (obj) {
			return parseElement(obj);
		},
		browser: function () {
		  	var nav = window.navigator,
				ActiveX = 'ActiveXObject' in window,
				XHR = 'XMLHttpRequest' in window,
				SecurityPolicy = 'securityPolicy' in nav,
				TaintEnabled = 'taintEnabled' in nav,
				ua = nav.userAgent,
				Opera = /opera/i.test(ua),
				Firefox = /firefox/i.test(ua),
				Webkit = /webkit/i.test(ua);
			return {
				ie: ActiveX,
				ie6: ActiveX && !XHR,
				ie7: ActiveX && XHR,
				opera: Opera,
				firefox: Firefox || (SecurityPolicy && !ActiveX && !Opera),
				webkit: Webkit || (!TaintEnabled && !ActiveX && !Opera)
			};
		}(),
		trace: function () {
			if (window.console && window.console.log) {
				console.log(arguments);
			} 
		},
		local: "for(var i in JELLY){if(i!='local')eval('var '+i+'=JELLY.'+i)};"
	};
}(); 

JELLY.addEvent(window, 'unload', JELLY.purgeEventLog);

(function () {
	var browser = JELLY.browser,
		addClass = JELLY.addClass,
		htmlElement = document.documentElement,
		classname = 'other';
	for (var i in browser) {
		if (browser[i]) {
			classname = i;
		}
	}
	if (/^ie\d/.test(classname)) {
		classname += ' ie';
	} 
	addClass(htmlElement, classname);
	addClass(htmlElement, 'js');
	if (browser.ie6) {
		try {document.execCommand('BackgroundImageCache', false, true);} catch(ex) {};
	}
})();

JELLY.extend(Array.prototype, {
	forEach: function (fn, obj){
		for (var i = 0, j = this.length; i < j; i++) {
			fn.call(obj, this[i], i, this);
		}
	}
}, false);
Array.prototype.each = Array.prototype.forEach;

JELLY.extend(String.prototype, {
	trim: function () {
		return this.replace(/^\s+|\s+$/g, '');
	},
	normalize: function () {
		return this.replace(/\s{2,}/g, ' ').trim();
	}
});
/*!
 *
 * AJAX MODULE
 * 
 */
JELLY.Ajax = function (el, opts) {
	el = JELLY.parseElement(el);
	this.updateElement = el;
	JELLY.extend(this, opts || {});
	this.feedbackElement = this.feedbackElement || this.updateElement;
}
JELLY.Ajax.prototype = {
	onstart: function () {},
	onsuccess: function () {this.updateElement.innerHTML = this._xhr.responseText;},
	oncomplete: function () {},
	ontimeout: function () {alert('Server busy, please try again later');},
	timeout: 12000,
	nocache: false,
	feedbackClass: 'loading',
	feedback: function (bool) {
		this.inprogress = bool;
		if (bool) {
			JELLY.addClass(this.feedbackElement, this.feedbackClass);
		} else {
			JELLY.removeClass(this.feedbackElement, this.feedbackClass);
		}
	},
	request: function (file, opts) {
		var data;
		if (opts.formElement) {
			this._xhr.open('POST', file, true);
			this._xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			data = this.serializeFormData(opts.formElement);
		} else {
			this._xhr.open('GET', file, true);
		}
		if (this.nocache) {
			this._xhr.setRequestHeader('If-Modifed-Since', 'Sat, 1 Jan 2000 00:00:00 GMT');
		}
		this._xhr.send(data || null);
	},
	send: function (file, opts) {
		var me = this;
		if (me.inprogress) {
			return false;
		}
		me._xhr = me.getXHR();
		if (me._xhr) {
			me.feedback(true);
			me.onstart();
			me._xhr.onreadystatechange = function () {
				if (me.httpSuccess()) {
					clearTimeout(me.timer);
					me.onsuccess();
					me.feedback(false);
					me.oncomplete.call(me);
					me._xhr = null;
				} 
			};
			me.timer = setTimeout(function () {me.httpFail.call(me);}, me.timeout);
			me.request(file, opts || {});
			return true;
		}
		return false;
	},
	serializeFormData: function (form) {
		var append = function (field, value) {
				data[data.length] = 
					field.name + '=' + encodeURIComponent(value).replace(/%20/g, '+');
			},
			data = [], 
			radio_groups = [],
			field, unique, i, j;
		for (i = 0; i < form.elements.length; i++) {
			field = form.elements[i];
			switch (field.nodeName.toLowerCase()) {
				case 'input' : 
					switch (field.type.toLowerCase()) {
						case 'submit': 
							break;
						case 'checkbox': 
							if (field.checked) {
								append(field, field.value || 'on');
							}
							break;
						case 'radio':
							unique = true;
							for (j = 0; j < radio_groups.length; j++) {
								if (radio_groups[j] === field.name) {
									unique = false;
								}
							}
							if (unique) {
								radio_groups[radio_groups.length] = field.name;
							}
							break;
						default: append(field, field.value); 
					}
					break;
				default : append(field, field.value); 
			}
		}
		for (i = 0; i < radio_groups.length; i++) {
			var group = form[radio_groups[i]];
			for (j = 0; j < group.length; j++) {
				if (group[j].checked) {
					append(group[j], group[j].value || 'on');
					break;
				}
			}
		}
		return data.join('&');
	},
	getXHR: function () {
		var xhr = false;
		if ('XMLHttpRequest' in window) {
			xhr = new XMLHttpRequest();
		} else if ('ActiveXObject' in window) {
			try {
			xhr = new ActiveXObject('Msxml2.XMLHTTP');
			} catch (ex) {
				try {
				 xhr = new ActiveXObject('Microsoft.XMLHTTP');
				} catch (ex) {}
			}
		}
		return xhr;
	},
	httpSuccess: function () {
		return !!(
			this._xhr.readyState == 4 && (
				this._xhr.status == 200 || this._xhr.status == 304 || 
				(this._xhr.status == undefined && JELLY.browser.webkit))
		);
	},
	httpFail: function () {
		if (this._xhr !== null) {
			this._xhr.abort();
		}
		this.feedback(false);
		this.ontimeout();
	}
};
/*!
 *
 * ANIMATION MODULE  
 * Credits : Moofx, Transition equations by Robert Penner.
 *
 */
JELLY.transitions={linear:function(B,A,D,C){return D*B/C+A},quadIn:function(B,A,D,C){return D*(B/=C)*B+A},quadOut:function(B,A,D,C){return -D*(B/=C)*(B-2)+A},quadInOut:function(B,A,D,C){if((B/=C/2)<1){return D/2*B*B+A}return -D/2*((--B)*(B-2)-1)+A},cubicIn:function(B,A,D,C){return D*(B/=C)*B*B+A},cubicOut:function(B,A,D,C){return D*((B=B/C-1)*B*B+1)+A},cubicInOut:function(B,A,D,C){if((B/=C/2)<1){return D/2*B*B*B+A}return D/2*((B-=2)*B*B+2)+A},quartIn:function(B,A,D,C){return D*(B/=C)*B*B*B+A},quartOut:function(B,A,D,C){return -D*((B=B/C-1)*B*B*B-1)+A},quartInOut:function(B,A,D,C){if((B/=C/2)<1){return D/2*B*B*B*B+A}return -D/2*((B-=2)*B*B*B-2)+A},quintIn:function(B,A,D,C){return D*(B/=C)*B*B*B*B+A},quintOut:function(B,A,D,C){return D*((B=B/C-1)*B*B*B*B+1)+A},quintInOut:function(B,A,D,C){if((B/=C/2)<1){return D/2*B*B*B*B*B+A}return D/2*((B-=2)*B*B*B*B+2)+A},sineIn:function(B,A,D,C){return -D*Math.cos(B/C*(Math.PI/2))+D+A},sineOut:function(B,A,D,C){return D*Math.sin(B/C*(Math.PI/2))+A},sineInOut:function(B,A,D,C){return -D/2*(Math.cos(Math.PI*B/C)-1)+A},expoIn:function(B,A,D,C){return(B==0)?A:D*Math.pow(2,10*(B/C-1))+A},expoOut:function(B,A,D,C){return(B==C)?A+D:D*(-Math.pow(2,-10*B/C)+1)+A},expoInOut:function(B,A,D,C){if(B==0){return A}if(B==C){return A+D}if((B/=C/2)<1){return D/2*Math.pow(2,10*(B-1))+A}return D/2*(-Math.pow(2,-10*--B)+2)+A},circIn:function(B,A,D,C){return -D*(Math.sqrt(1-(B/=C)*B)-1)+A},circOut:function(B,A,D,C){return D*Math.sqrt(1-(B=B/C-1)*B)+A},circInOut:function(B,A,D,C){if((B/=C/2)<1){return -D/2*(Math.sqrt(1-B*B)-1)+A}return D/2*(Math.sqrt(1-(B-=2)*B)+1)+A},elasticIn:function(C,A,G,F,B,E){if(C==0){return A}if((C/=F)==1){return A+G}if(!E){E=F*0.3}if(!B){B=1}if(B<Math.abs(G)){B=G;var D=E/4}else{var D=E/(2*Math.PI)*Math.asin(G/B)}return -(B*Math.pow(2,10*(C-=1))*Math.sin((C*F-D)*(2*Math.PI)/E))+A},elasticOut:function(C,A,G,F,B,E){if(C==0){return A}if((C/=F)==1){return A+G}if(!E){E=F*0.3}if(!B){B=1}if(B<Math.abs(G)){B=G;var D=E/4}else{var D=E/(2*Math.PI)*Math.asin(G/B)}return B*Math.pow(2,-10*C)*Math.sin((C*F-D)*(2*Math.PI)/E)+G+A},elasticInOut:function(C,A,G,F,B,E){if(C==0){return A}if((C/=F/2)==2){return A+G}if(!E){E=F*(0.3*1.5)}if(!B){B=1}if(B<Math.abs(G)){B=G;var D=E/4}else{var D=E/(2*Math.PI)*Math.asin(G/B)}if(C<1){return -0.5*(B*Math.pow(2,10*(C-=1))*Math.sin((C*F-D)*(2*Math.PI)/E))+A}return B*Math.pow(2,-10*(C-=1))*Math.sin((C*F-D)*(2*Math.PI)/E)*0.5+G+A},backOffset:1.70158,backIn:function(B,A,E,D,C){if(!C){C=JELLY.transitions.backOffset}return E*(B/=D)*B*((C+1)*B-C)+A},backOut:function(B,A,E,D,C){if(!C){C=JELLY.transitions.backOffset}return E*((B=B/D-1)*B*((C+1)*B+C)+1)+A},backInOut:function(B,A,E,D,C){if(!C){C=JELLY.transitions.backOffset}if((B/=D/2)<1){return E/2*(B*B*(((C*=(1.525))+1)*B-C))+A}return E/2*((B-=2)*B*(((C*=(1.525))+1)*B+C)+2)+A},bounceIn:function(B,A,D,C){return D-JELLY.transitions.bounceOut(C-B,0,D,C)+A},bounceOut:function(B,A,D,C){if((B/=C)<(1/2.75)){return D*(7.5625*B*B)+A}else{if(B<(2/2.75)){return D*(7.5625*(B-=(1.5/2.75))*B+0.75)+A}else{if(B<(2.5/2.75)){return D*(7.5625*(B-=(2.25/2.75))*B+0.9375)+A}else{return D*(7.5625*(B-=(2.625/2.75))*B+0.984375)+A}}}},bounceInOut:function(B,A,D,C){if(B<C/2){return JELLY.transitions.bounceIn(B*2,0,D,C)*0.5+A}return JELLY.transitions.bounceOut(B*2-C,0,D,C)*0.5+D*0.5+A}};

JELLY.Tween = function (el, opts) {
	this.element = JELLY.parseElement(el);
	JELLY.extend(this, opts || {}); 
};
JELLY.Tween.prototype = {
	transition: JELLY.transitions.sineInOut,
	duration: 500,
	unit: 'px',
	timerSpeed: 20,
	setDuration: function (val) {
		this.duration = val;
		return this;
	},
	setTransition: function (val) {
		this.transition = JELLY.transitions[val];
		return this;
	},
	setOpacity: function (val) {
		JELLY.setOpacity(this.element, val);
		return this;
	},
	chain: function (fn) {
		this.chains = this.chains || [];
		this.chains.push(fn);
		return this;
	},
	stop: function () {
		clearInterval(this.timer);
		this.timer = null;
		return this;
	},
	start: function (props, values) {
		var me = this,
			isArray = JELLY.isArray,
			parseColour = JELLY.Str.parseColour,
			getStyle = JELLY.getStyle;
		if (me.timer) {
			clearInterval(me.timer);
		}
		if (isArray(props)) {
			me.props = props;
			me.values = values;
		} else {
			me.props = [props];
			me.values = [values];
		}
		var i = me.values.length - 1;
		do {
			me.props[i] = JELLY.Str.toCamelCase(me.props[i]);
			if (/color/i.test(me.props[i])) {
				if (!isArray(me.values[i])) {
					me.values[i] = [
						parseColour(getStyle(me.element, me.props[i]), 'rgb-array'), 
						parseColour(me.values[i], 'rgb-array')];
				} else {
					me.values[i] = [
						parseColour(me.values[i][0], 'rgb-array'), 
						parseColour(me.values[i][1], 'rgb-array')];
				}
				me.values[i].color = true;
			} else if (/backgroundPosition/.test(me.props[i])) {
				if (!isArray(me.values[i])) {
					me.values[i] = [[0, me.values[i]], [0, 0]];
				} else if (!isArray(me.values[i][0])) {
					me.values[i] = [[0, me.values[i][0]], [0, me.values[i][1]]];
				} 
				me.values[i].backgroundPosition = true;
			} else {
				if (!isArray(me.values[i])) {
					me.values[i] = [getStyle(me.element, me.props[i], true), me.values[i]]; 
				}
			}
		} while (i--)
		me.startTime = new Date().getTime();
		me.timer = setInterval(function () {me.step.call(me)}, me.timerSpeed);
		return me;
	},
	step: function () {
		var currentTime = new Date().getTime();
		if (currentTime < this.startTime + this.duration) {
			this.elapsedTime = currentTime - this.startTime;
		} else {
			this.stop();
			this.tidyUp();
			return this.callChain();
		}
		this.increase();
	},
	tidyUp: function () {
		var i = this.props.length - 1;
		do {
			if (this.values[i].color) {
				this.element.style[this.props[i]] = 'rgb(' + this.values[i][1].join(',') + ')';
			} else if (this.values[i].backgroundPosition) {
				this.element.style.backgroundPosition = 
					this.values[i][0][1] + this.unit + ' ' + 
					this.values[i][1][1] + this.unit;
			} else {
				this.setStyle(this.props[i], this.values[i][1]);
			}
		} while (i--)
	},
	increase: function () {
		var i = this.props.length - 1;
		do {
			if (this.values[i].color) {
				this.element.style[this.props[i]] = 'rgb(' + 
					Math.round(this.compute(this.values[i][0][0], this.values[i][1][0])) + ',' +
					Math.round(this.compute(this.values[i][0][1], this.values[i][1][1])) + ',' +
					Math.round(this.compute(this.values[i][0][2], this.values[i][1][2])) + ')'
			} else if (this.values[i].backgroundPosition) {
				this.element.style.backgroundPosition = 
					this.compute(this.values[i][0][0], this.values[i][0][1]) + this.unit + ' ' + 
					this.compute(this.values[i][1][0], this.values[i][1][1]) + this.unit;
			} else {
				this.setStyle(this.props[i], this.compute(this.values[i][0], this.values[i][1]));
			}
		} while (i--)
	},
	compute: function (from, to) {
		return this.transition(this.elapsedTime, from, (to - from), this.duration);
	},
	setStyle: function (p, val) {
		if (p === 'opacity') { 
			this.setOpacity(val)
		} else {
			this.element.style[p] = val + this.unit;
		}
	},
	setBackgroundPosition: function (val) {
		this.element.style.backgroundPosition = val[0] + this.unit + ' 0';
	},
	callChain: function () {
		var me = this;
		if (me.chains && me.chains.length) {
			setTimeout(function () {(me.chains.shift()).call(me)}, 10);
		}
	}
};
/*!
 *
 * SCROLL CLASS   
 *
 */
JELLY.Scroll = function (el, opts) {
	this.element = JELLY.parseElement(el);
	JELLY.extend(this, opts || {}); 
};
JELLY.Scroll.prototype = JELLY.extend(new JELLY.Tween(), {
	start: function (x, y) {
		var me = this, 
			element = me.element,
			isArray = JELLY.isArray;
		if (me.timer) {
			clearInterval(me.timer);
		}
		if (element === window) {
			var winpos = JELLY.getWindowScroll();
			if (!isArray(x)) {
				x = [winpos[0], x];				
			}
			if (!isArray(y)) {
				y = [winpos[1], y];
			}
			me.increase = function () {
				element.scrollTo(
					me.compute(me.values[0][0], me.values[0][1]), 
					me.compute(me.values[1][0], me.values[1][1]));
			};
		} else {
			if (!isArray(x)) {
				x = [element.scrollLeft, x];				
			}
			if (!isArray(y)) {
				y = [element.scrollTop, y];
			}
			me.increase = function () {
				element.scrollLeft = me.compute(me.values[0][0], me.values[0][1]); 
				element.scrollTop = me.compute(me.values[1][0], me.values[1][1]); 				
			};
		}
		me.values = [x, y];
		me.startTime = new Date().getTime();
		me.timer = setInterval(function () {me.step.call(me)}, me.timerSpeed);
		return me;
	},
	tidyUp: function () {
		if (this.element === window) {
			this.element.scrollTo(this.values[0][1], this.values[1][1]);
		} else {
			this.element.scrollLeft = this.values[0][1]; 
			this.element.scrollTop = this.values[1][1]; 
		}
	} 
});