/*!
 *
 *	JELLY JavaScript Library (c) 2008 Pete Boere --/ v.1+ /--
 *	last modified: 21/07/2008
 *
 */
var JELLY = function () {
	var treeWalker = function (elem, mode, config) {
			var repeat = config.repeat || 1;
			switch (mode) {
				case 'sibling' : 
					do {
						do {
							elem = elem[config.type + 'Sibling'];
						} while (elem && elem.nodeType !== 1); 
					 } while (elem && --repeat);
				break;
				case 'child' : 
					if (config.type === 'first') {
						elem = elem.firstChild;
						while (elem && elem.nodeType !== 1) {
							elem = elem.nextSibling;
						}
					} else if (config.type === 'last') {
						elem = elem.lastChild;
						while (elem && elem.nodeType !== 1) {
							elem = elem.previousSibling;
						}  
					}
				break;
				case 'parent' : 
					do {
						elem = elem.parentNode;
					} while (elem && --repeat);
			}
			return elem;
		},
		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';
		},
		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;
		},
		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'});
		},
		getParent: function (el, repeat) {
			return treeWalker(el, 'parent', {repeat: repeat});
		},
		getNext: function (el, repeat) {
			return treeWalker(el, 'sibling', {type: 'next', repeat: repeat});
		},
		getPrevious: function (el, repeat) {
			return treeWalker(el, 'sibling', {type: 'previous', 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) {
			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 (config) {
			var path = config.path,
				attributes = config.attributes || {},
				params = config.params || {};
				vars = config.vars || {},
				fallback = config.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 = config.width;
			attributes.height = config.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);
		},
		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 (func, obj){
		for (var i = 0, j = this.length; i < j; i++) {
			func.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();
	}
});
