/*!

Jelly JavaScript, Copyright (c) 2008-2009 Pete Boere.

MIT style license: http://www.opensource.org/licenses/mit-license.php
project page: http://code.google.com/p/jelly-javascript/
this build compiled: 2009-09-23 

*/
;(function () {

/**

Base

@description 
	Initialization of Jelly namespace
	Base set of shortcuts and utility functions

*/

var J = window.JELLY = { __JELLY__: 1.12 },

	// shortcuts
	//
	win = window,
	doc = win.document,
	docRoot = doc.documentElement,
	docHead = doc.getElementsByTagName('head')[0],
	standardEventModel = 'addEventListener' in doc,
	querySelectorAll = 'querySelectorAll' in doc,
	functionLit = function () {},
	
	// browser detection
	//
	browser = function () {
		var nav = win.navigator,
			ua = nav.userAgent,
			activex = 'ActiveXObject' in win,
			xhr = 'XMLHttpRequest' in win,
			securityPolicy = 'securityPolicy' in nav,
			taintEnabled = 'taintEnabled' in nav,
			opera = /opera/i.test(ua),
			firefox = /firefox/i.test(ua),				
			webkit = /webkit/i.test(ua),
			ie = activex ? ( querySelectorAll ? 8 : ( xhr ? 7 : 6 ) ) : 0;
		return {
			ie: ie,
			ie6: ie === 6,
			ie7: ie === 7,
			ie8: ie === 8,
			opera: opera,
			firefox: firefox || ( securityPolicy && !activex && !opera ),
			webkit: webkit || ( !taintEnabled && !activex && !opera ),
			safariMobile: /safari/i.test( ua ) && /mobile/i.test( ua ),
			chrome: webkit && /chrome/i.test( ua )
		};
	}(),
	msie = browser.ie,
	
	// 'is' functions
	//
	objToString = {}.toString,
	
	isDefined = function ( obj ) { 
		return typeof obj !== 'undefined'; 
	},
	
	isUndefined = function ( obj ) { 
		return typeof obj === 'undefined'; 
	},
	
	isNull = function ( obj ) { 
		return obj === null; 
	},
	
	isBoolean = function ( obj ) { 
		return typeof obj === 'boolean'; 
	},
	
	isString = function ( obj ) { 
		return typeof obj === 'string'; 
	},
	
	isNumber = function ( obj ) { 
		return typeof obj === 'number'; 
	},
	
	isInteger = function ( obj ) { 
		return isNumber( obj ) ? !( obj % 1 ) : false; 
	}, 
	
	isFloat = function ( obj ) { 
		return isNumber( obj ) ? !!( obj % 1 ) : false; 
	}, 
	
	isNumeric = function ( obj ) { 
		return isString( obj ) || isNumber( obj ) ? /^\s*\d+\.?\d*?\s*$/.test( ( obj+'' ) ) : false; 
	},
	
	isObject = function ( obj ) { 
		return obj+'' === '[object Object]';
	},
	
	isObjectLike = function ( obj ) { 
		return !!obj && !isObject( obj ) && ( typeof obj === 'object' || isFunction( obj ) );
	},
	
	isFunction = function ( obj ) { 
		// Opera can't handle a wrapped 'return typeof === "function"'
		return objToString.call( obj ) === '[object Function]'; 
	},
	
	isElement = function () {
		if ( !msie ) {
			return function ( obj ) {
				return /^\[object HTML[A-Za-z]*Element\]$/.test( objToString.call( obj ) );
			};
		} 
		return function ( obj ) {
			return isObjectLike( obj ) && !!obj.nodeName && obj.nodeType === 1; 
		};

	}(),
	
	isNodeList = function () { 
		if ( !msie ) {
			return function ( obj ) {
				return /^\[object (HTMLCollection|NodeList)\]$/.test( objToString.call( obj ) );
			};
		} 
		return function ( obj ) {
			return isArrayLike( obj ) && !!obj.item; 
		};
	}(),
	
	isArray = function ( obj ) { 
		return objToString.call( obj ) === '[object Array]'; 
	},	
	
	isArrayLike = function ( obj ) { 
		return isObjectLike( obj ) && !isObject( obj ) && 
			!isArray( obj ) && !isFunction( obj ) && isInteger( obj.length ); 
	},	
	
	inArray = function ( obj, arr ) { 
		return arr.indexOf( obj ) !== -1; 
	},
	
	toArray = function ( obj ) {
		var result = [], n = obj.length, i = 0;
		for ( i; i < n; i++ ) { 
			result[i] = obj[i]; 
		}
		return result;
	},
	
	// return false for empty strings, arrays or objects
	//
	empty = function ( arg ) {
		if ( isString( arg ) ) {
			return /^\s*$/.test( arg );
		}
		else if ( isArray( arg ) ) {
			return !arg.length;
		}
		else if ( isObject( arg ) ) {
			return !Object.keys( arg ).length;
		}
		return !arg;
	},
	
	// extend objects with the option to not overwrite defined members
	//
	extend = function ( a, b, overwrite ) {
		for ( var mem in b ) {
			if ( isUndefined( a[mem] ) || isDefined( a[mem] ) && overwrite !== false ) {
				a[mem] = b[mem];
			}
		}
		return a;
	},
	
	// defered function invocation wrapper
	//
	defer = function () {
		var args = toArray( arguments ),
			func = args.shift(),
			scope = args.shift() || {};
		return setTimeout( function () { func.apply( scope, args ); }, 0 );
	},
	
	// console api wrappers
	//
	createLogger = function ( method ) {
		var console = win.console;
		if ( console && console[method] ) {  	
			return function () {
				console[method].apply( console, toArray( arguments ) ); 
			};
		}
		return functionLit;
	},
	log = createLogger('log'),
	logWarn = createLogger('warn'),
	logError = createLogger('error');
		
extend( J, {
	win: win,
	doc: doc,
	docRoot: docRoot, 
	docHead: docHead,
	functionLit: functionLit,
	browser: browser,
	isDefined: isDefined,
	isUndefined: isUndefined,
	isNull: isNull,
	isBoolean: isBoolean,
	isString: isString,
	isNumber: isNumber,
	isInteger: isInteger,
	isFloat: isFloat,
	isNumeric: isNumeric,
	isObject: isObject,	
	isFunction: isFunction,
	isElement: isElement,
	isNodeList: isNodeList,
	isArray: isArray,
	toArray: toArray,
	empty: empty,
	extend: extend,	
	defer: defer,
	log: log,
	logWarn: logWarn,
	logError: logError	
});

/**

Native extensions

@description 
	Patching native support for standard object methods
	Implementing ecmascript 5 features where possible	

*/

// Array methods
//
extend( Array.prototype, {
	
	forEach: function ( fn, obj ) {
		for ( var i = 0, n = this.length; i < n; i++ ) { 
			fn.call( obj, this[i], i, this ); 
		}
	},
	
	indexOf: function (obj, from) {
		from = isDefined(from) ? 
			( from < 0 ? Math.max( 0, this.length + from ) : from ) : 0;
		for ( var i = from, n = this.length; i < n; i++ ) { 
			if ( this[i] === obj ) { 
				return i; 
			} 
		}
		return -1;
	},
	
	filter: function (fn, obj) {
		for ( var i = 0, n = this.length, arr = []; i < n; i++ ) { 
			if ( fn.call( obj, this[i], i, this ) ) { 
				arr.push( this[i] ); 
			} 
		}
		return arr;
	},
	
	map: function (fn, obj) {
		for ( var i = 0, n = this.length, arr = []; i < n; i++ ) { 
			arr.push( fn.call( obj, this[i], i, this ) ); 
		}
		return arr;
	},
	
	some: function (fn, obj) {
		for ( var i = 0, n = this.length; i < n; i++ ) { 
			if ( fn.call( obj, this[i], i, this ) ) { 
				return true; 
			} 
		}
		return false;
	},
	
	every: function (fn, obj) {
		for ( var i = 0, n = this.length; i < n; i++ ) { 
			if ( !fn.call( obj, this[i], i, this ) ) { 
				return false; 
			} 
		}
		return true;
	}
	
}, false);

// common alias for convenience
Array.prototype.each = Array.prototype.forEach;


// String methods
//
extend( String.prototype, {
	
	trim: function () {
		return this.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' );
	}
	
}, false);


// Function methods
//
extend( Function.prototype, {
	
	bind: function () {
		if ( arguments.length < 2 && !isDefined( arguments[0] ) ) { 
			return this; 
		}
		var args = toArray( arguments ),
			scope = args.shift(),
			fn = this; 
		return function () {
			for ( var i = 0, arr = toArray( args ); arguments.length > i; i++ ) { 
				arr.push( arguments[i] ); 
			}
			return fn.apply( scope, arr );
		};
	}
	
}, false);


// HTMLElement methods
//
if ( win.HTMLElement && HTMLElement.prototype ) {
	
	extend( HTMLElement.prototype, {
	
		contains: function ( el ) {
			return !!( this.compareDocumentPosition( el ) & 16 );
		}
	
	}, false);
	
}

// ecmascript 5
//
extend( Object, {
	
	keys: function ( obj ) {
		var res = [], key;
		for ( key in obj ) {
			if ( obj.hasOwnProperty( key ) ) {
				res.push( key );
			}
		}
		return res;
	}
	
}, false);

/**

Monitor

@description 
	A means to finding bugs when no decent console API is available

*/

(function () {

var self = J.Monitor = {

	lines: 0,
	logLimit: 150,
	
	container: function () {
		var elem = doc.createElement( 'div' );
		elem.style.cssText = [
				'font: 11px consolas, "courier new", monospace',
				'position: ' + ( !browser.ie ? 'fixed' : 'absolute' ),
				'overflow: auto',
				'width: 300px',
				'height: 150px',
				'background: #000',
				'border: 1px solid #fff',
				'border-width: 0 0 1px 1px',
				'top: 0',
				'right: 0',
				'color: #fff' 
			].join(';');
		return elem;
	}(),
	
	enable: function () {
		if ( !self.loadHandler ) {
			self.loadHandler = addDomReady ( function () {
				insertElement( self.container );
			});
		} 
		return self;
	},
	
	log: function () {
		if ( self.cancel ) { return; }
		var printLine = function ( msg ) {
				self.container.innerHTML += 
					'<span style="' + [
					'display: block',
					'border-bottom: 1px solid #333',
					'padding: 5px 8px 2px',
					].join(';') + 
					'">' + 
					msg + 
					'</span>';
			},
			styler = function ( obj ) {
				var format = { tag: 'b', color: '#fff', value: obj+'' },
					tmpl = '<%{tag} style="color:%{color};">%{value}</%{tag}>';
				if ( isNumber( obj ) ) {
					format.color = 'orange';
				}
				else if ( isObject( obj ) ) {
					format.color = 'lime';
					format.value ='Object';
				}
				else if ( isElement( obj ) ) {
					format.color = 'hotpink';
					format.value = obj.nodeName + ' ' + obj;
				} 
				else if ( isNodeList( obj ) ) {
					format.color = 'tomato';
					format.value = 'NodeList ' + obj;
				}
				else if ( isFunction( obj ) ) {
					format.color = 'yellow';
				}
				return bindData( tmpl, format );	
			},
			args = toArray( arguments ).map( function ( arg ) {
				if ( isArray( arg ) ) {
					return '[' + arg.map( styler ).join(', ') + ']';
				}
				return styler( arg );
			});
				
		if ( self.lines++ < self.logLimit ) {
			printLine( args.length < 2 ? args[0] : args.join(' ') ); 
		}
		else {
			self.cancel = true;
			printLine( 'Log limit reached' );
		}
	}
};

})();

/**

Profiler

@description 
	A simple profiling utility

*/

(function () {

var self = J.Profiler = {
	
	container: function () {
		var elem = doc.createElement( 'div' );
		elem.style.cssText = [
				'position: ' + ( !browser.ie ? 'fixed' : 'absolute' ),
				'font: bold 17px arial, sans-serif',
				'max-height: 200px',
				'text-align: center',
				'overflow: auto',
				'background: #000',
				'border: 1px solid #fff',
				'border-width: 0 0 1px 1px',
				'top: 0',
				'left: 0',
				'color: #fff' 
			].join(';');
		return elem;
	}(),
	
	startTime: 0,
	
	start: function ( label ) {
		self.label = label ? ('<span style="font-size:12px;font-weight:normal;color:#ccc;display:block;padding:0 0 3px;">' + label + '</span>') : '';
		self.active = true;
		self.startTime = (+new Date);
	},
	
	stop: function () {
		var result = (+new Date) - self.startTime; 
		if ( !self.active ) {
			result = 'n/a';
		}
		self.active = false;
		return self.reveal( result );
	},
	
	reveal: function ( result ) {
		if ( !self.container.parentNode ) {
			try {
				doc.body.appendChild( self.container );
			} 
			catch ( ex ) {
				if ( !self.loadHandler ) {
					self.loadHandler = addDomReady( function () {
						insertElement( self.container );
					});
				}
			}
		}
		self.container.innerHTML += 
			'<span style="padding:10px 20px;display:block;border-bottom:1px solid #333;">' + 
			self.label + 
			result + 
			'</span>';
		return self;
	},
	
	clear: function () {
		if ( self.container.parentNode ) {
			self.container.innerHTML = '';
		}
		return self;
	}
	
};

})();

/**

Class

@description 
	Utilities for defining classes on the Jelly namespace

*/

var	defineClass = function ( name, opts ) {
		var Class = opts.__init || function () {},
			Static = opts.__static || {},
			Extends = opts.__extends,
			Prototype = Class.prototype; 
		
		extend( Prototype, defineClassAbstract );
		
		( isArray( Extends ) ? Extends : 
			( Extends ? [ Extends ] : [] ) ).each( function ( obj ) {
			extend( Prototype, obj.prototype ); 
			Class.__parent = obj;
		});
		
		extend( Class, Static );
		
		['__init', '__static', '__extends'].each( function ( mem ) {
			delete opts[mem];
		});
					
		extend( Prototype, opts );
		
		Prototype.constructor = Class;
		
		Class.__name = name;
		
		J[name] = Class;
		
		return Class;
	},
	
	// base methods which are automatically implemented
	//
	defineClassAbstract = {
		
		fire: function () {
			var args = toArray( arguments ),
				event = 'on' + capitalize( args.shift() ),
				func = this[event];
			
			// If no argument is specified we just pass in the object as default
			if ( empty( args ) ) {
				args.push( this );
			}
			return func ? func.apply( this, args ) : false;
		},
		
		isInstanceOf: function () {
			return this.constuctor.__name;
		},
		
		set: function ( a, b ) {
			var self = this;
			if ( isObject( a ) ) {
				return extend( self, a );;
			} 
			self[a] = b;
			return self;
		}
	};

J.defineClass = defineClass;

/**

Strings

@description 
	Utility functions for working with strings

*/

var	contains = function ( haystack, needle, caseInsensitive ) {
		if ( caseInsensitive ) {
			haystack = haystack.toLowerCase();
			needle = needle.toLowerCase();			
		}
		return haystack.indexOf( needle ) !== -1;
	},
	
	normalize = function ( str ) {
		return str.replace( /\s{2,}/g, ' ' ).trim();
	},
	
	capitalize = function ( str, firstWord ) {
		return str.replace( firstWord ? /^\s*[a-z]/ : /(^|\s+)[a-z]/g, function ( m ) {
			return m.toUpperCase();
		});
	},
	
	camelize = function ( str ) {
		return str.replace( /-([a-z])/g, function ( m, m1 ) {
			return m1.toUpperCase();
		});
	}, 

	rgbToHex = function ( str ) {
		var rgb = str.match( /[\d]{1,3}/g ), 
			hex = [], 
			i = 0;
		for ( i; 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 = 1;
		for ( i; 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 hex = /^#/.test( str ), 
			tempArray = [], 
			temp;
		switch (mode) {
			case 'hex':	
				return hex ? str : rgbToHex( str );
			case 'rgb': 
				return hex ? hexToRgb( str ) : 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;
				}
		}
	},
	
	stripTags = function ( str, allow ) {
		if ( !allow ) { 
			return str.replace( /<[^>]*>/g, '' ); 
		} 
		allow = allow.replace( /\s+/g, '' ).split(',').map( function (s) {
			return s +' |'+ s +'>|/'+ s +'>';   
		}).join('|');
		return str.replace( new RegExp( '<(?!'+ allow +')[^>]+>', 'g' ), '' );
	},
	
	bindData = function ( str, data ) {
		var m;
		while ( m = /%\{\s*([^\}\s]+)\s*\}/.exec( str ) ) {
			str = str.replace( m[0], data[m[1]] || '??' );
		}
		return str;
	},
	
	evalScripts = function ( str ) {
		var wrapper = createElement( 'div', { html: str } ), 
			res = [];
		toArray( getElements( wrapper, 'script' ) ).each( function (el) {
			res.push( win['eval']( el.innerHTML ) );
		});
		return res;
	};
	
extend( J, { 
	contains: contains,
	normalize: normalize,
	capitalize: capitalize,
	camelize: camelize,
	parseColour: parseColour,
	stripTags: stripTags,
	bindData: bindData,
	evalScripts: evalScripts
});

/**

Elements

@description 
	Utility functions for working with elements and manipulating the DOM

*/

var addClass = function ( el, cn ) {
		el = getElement(el);
		if ( hasClass( el, cn ) ) { return; }
		el.className += el.className ? ' ' + cn : cn;
	}, 
	
	removeClass = function ( el, cn ) {
		el = getElement(el);
		if ( !el.className ) { return; } 
		var patt = new RegExp( '(^|\\s)' + cn + '(\\s|$)' );
		el.className = normalize( el.className.replace( patt, ' ' ) );
	},
	
	hasClass = function ( el, cn ) {
		return (' ' + ( getElement(el) ).className + ' ').indexOf( cn ) !== -1;
	},
	
	toggleClass = function ( el, cn ) {
		el = getElement(el);
		if ( hasClass( el, cn ) ) { 
			removeClass( el, cn ); 
		} 
		else { 
			addClass( el, cn ); 
		}
	},
	
	getElement = function ( obj ) { 
		return typeof obj === 'string' ? doc.getElementById( obj ) : obj; 
	},
	
	getElements = function ( a, b ) { 
		return ( b ? getElement( a ) : doc ).getElementsByTagName( b || a ); 
	},

	createElement = function ( arg, attrs ) {
		var el;
		if ( !/[#:\.]/.test(arg) ) {
			el = doc.createElement(arg), key;
			for ( key in attrs ) {
				switch (key) {
					case 'html': 
						el.innerHTML = attrs[key]; 
						break;
					case 'text': 
						el.appendChild( doc.createTextNode( attrs[key] ) ); 
						break;
					case 'class': 
						el.className = attrs[key]; 
						break;
					case 'style': 
						el.style.cssText = attrs[key]; 
						break;
					default: 
						el.setAttribute( key, attrs[key] );
				}
			}
		} 
		else {
			var arg = arg.trim(),
				stringKey = '__JELLY_CE__',
				stringTokens = [], 
				m;
			while ( m = /('|")([^\1]*?)\1/.exec( arg ) ) {
				arg = arg.replace( m[0], stringKey );
				stringTokens.push( m[2] );
			}
			arg = arg.replace( /\s*(:|,)\s*/g, '$1' );
			var parts = arg.split(' '),
				first = parts.shift(),
				leadId = first.indexOf('#') !== -1,
				leadClass = first.indexOf('.') !== -1,
				type = 'div',
				attributes = {},
				branchMapData = null,
				tmp;
			if ( leadId || leadClass ) {
				tmp = leadId ? first.split('#') : first.split('.'); 
				type = tmp.shift() || type;
				attributes[leadId ? 'id':'class'] = tmp.join(' ');
			} 
			else {
				type = first;
			}
			if ( parts[0] ) {
				parts[0].split(',').each(function (tkn) {
					tkn = tkn.split(':');
					var value = tkn[1] === stringKey ? stringTokens.shift() : tkn[1];
					if (tkn[0] === '@') {
						branchMapData = value;
					} 
					else {
						attributes[tkn[0]] = value;
					}
				});
			} 
			el = createElement( type.toLowerCase(), attributes );
		}
		return attrs === true ? { elem: el, ref: branchMapData } : el;
	},

	createBranch = function () {
		var args = toArray( arguments ),
			res = {},
			context,
			parseToken = function ( arg ) {
				if ( arg && isObject( arg ) ) {
					if ( isElement( arg.root ) ) {
						for ( var key in arg ) {
							if ( isArray( arg[key] ) ) {
								var nodeName = arg[key][0].nodeName.toLowerCase();
								res[nodeName] = res[nodeName] || [];
								arg[key].each( function (el) { 
									res[nodeName].push(el); 
								});
							} 
							else if ( key !== 'root' ) { res[key] = arg[key]; }
						} 
						return arg.root;
					} 
				}
				else if ( isElement(arg) ) { 
					return arg; 
				} 
				else if ( !isString(arg) ) { 
					return; 
				} 
				var obj = createElement(arg, true),
					elem = obj.elem,
					type = elem.nodeName.toLowerCase();
				res[type] = res[type] || [];
				res[type].push(elem);
				if ( obj.ref ) { res[obj.ref] = elem; }
				return elem;
			};
		res.root = context = parseToken( args.shift() );
		args.each(function (feed) {
			if ( !isArray(feed) ) { 
				context = context.appendChild( parseToken( feed ) ); 
			} 
			else { 
				feed.each( function (o) { 
					context.appendChild( parseToken(o) ) 
				}); 
			}
		});
		return res;
	},
	
	wrapElement = function ( el, wrapper ) {
		el = getElement(el);
		var pnt = el.parentNode, next = el.nextSibling;
		wrapper.appendChild(el);
		return next ? pnt.insertBefore( wrapper, next ) : pnt.appendChild( wrapper );	
	},
	
	withElement = function ( el, callback, scope ) {
		el = getElement(el);
		if (el) { return callback.call(scope || el, el); }
		return el;
	},
	
	replaceElement = function ( el, replacement ) {
		el = getElement(el);
		return el.parentNode.replaceChild( replacement, el );
	},
	
	removeElement = function (el) {
		el = getElement(el);
		return el.parentNode.removeChild(el);
	},
	
	removeChildren = function ( parent ) {
		var children = getChildren( getElement( parent ) );
		children.each( removeElement );
		return children;
	}, 
	
	insertElement = function ( el, datum ) {
		el = getElement(el);
		return ( getElement(datum) || doc.body ).appendChild(el);
	},
	
	insertTop = function ( el, datum ) {
		if ( !( el = getElement(el) ) || !( datum = getElement(datum) ) ) { return false; }
		if ( datum.firstChild ) { 
			return datum.insertBefore( el, datum.firstChild ); 
		}
		else { 
			return datum.appendChild(el); 
		}
	},
	
	insertBefore = function ( el, datum ) {
		datum = getElement(datum);
		return datum.parentNode.insertBefore( getElement(el), datum );
	},
	
	insertAfter = function ( el, datum ) {
		if ( !(el = getElement(el)) || !(datum = getElement(datum)) ) { return false; }
		var next = J.getNext(datum);
		if ( next ) { 
			return datum.parentNode.insertBefore(el, next); 
		} 
		else { 
			return datum.parentNode.appendChild(el); 
		}
	},
	
	getFirst = function (el) {
		el = el.firstChild;
		while ( el && el.nodeType !== 1 ) {
			el = el.nextSibling;
		}
		return el;
	},
	
	getLast = function (el) {
		el = el.lastChild;
		while ( el && el.nodeType !== 1 ) {
			el = el.previousSibling;
		}
		return el;
	},
	
	getNext = function (el) {
		el = el.nextSibling;
		while ( el && el.nodeType !== 1 ) {
			el = el.nextSibling;
		}
		return el;
	},
	
	getPrevious = function (el) {
		el = el.previousSibling;
		while ( el && el.nodeType !== 1 ) {
			el = el.previousSibling;
		}
		return el;
	},
	
	getChildren = function (el) {
		var elements = [], el = el.firstChild;
		while (el) {
			if ( el.nodeType === 1 ) {
				elements[elements.length] = el;
			}
			el = el.nextSibling;
		}
		return elements;
	},
	
	getXY = function (el) {
		el = getElement(el);
		var xy = [0, 0];
		if ( !el ) {
			return xy;
		} 
		if ( 'getBoundingClientRect' in el ) {
			var bounds = el.getBoundingClientRect(),
				winScroll = getWindowScroll(),
				left = bounds.left,
				top = bounds.top;
			xy = [ left + winScroll[0], top + winScroll[1] ];
		} 
		else {
			xy = [ el.offsetLeft, el.offsetTop ];
			while ( el = el.offsetParent ) {
				xy[0] += el.offsetLeft;
				xy[0] += parseInt( getStyle( el, 'border-left-width' ) ) || 0;
				xy[1] += el.offsetTop;
				xy[1] += parseInt( getStyle( el, 'border-top-width' ) ) || 0;
			}
		}
		return xy;
	},

	setXY = function ( el, X, Y, unit ) {
		el = getElement(el);
		unit = unit || 'px';
		el.style.left = X + unit;
		el.style.top = Y + unit;
	},
	
	getX = function (el) {
		return getXY(el)[0];
	},
	
	setX = function ( el, X, unit ) {
		( getElement(el) ).style.left = X + ( unit || 'px' );
	},
	
	getY = function (el) {
		return getXY(el)[1];
	},
	
	setY = function ( el, Y, unit ) {
		( getElement(el) ).style.top = Y + ( unit || 'px' );
	},
	
	getAttribute = function () {
		if ( !isDefined( docRoot.hasAttribute ) && msie ) {
			return function ( node, attr ) {
				switch ( attr ) {
					case 'class': 
						return node.className || null;
					case 'href': 
					case 'src': 
						return node.getAttribute( attr, 2 ) || null;						
					case 'style': 
						return node.getAttribute( attr ).cssText.toLowerCase() || null;
					case 'for': 
						return node.attributes[attr].nodeValue || null;
				}
				return node.getAttribute( attr ) || null;
			};
		}
		return function ( node, attr ) { 
			return node.getAttribute( attr ); 
		};
	}(),
	
	getStyle = function ( el, prop ) {
		var val, prop = camelize( prop );
		if ( prop === 'opacity' ) { 
			if ( !isDefined( el.__opacity ) ) { 
				el.__opacity = 1; 
			}
			return el.__opacity;
		}
		if ( el.style[prop] ) { 
			return el.style[prop]; 
		} 
		else if ( 'getComputedStyle' in win ) { 
			return win.getComputedStyle( el, null )[prop]; 
		} 
		else if ( 'currentStyle' in el ) { 
			return el.currentStyle[prop]; 
		}
	},
	
	setStyle = function ( el, a, b ) {
		var set = function ( prop, value ) {
				if ( prop === 'float' ) {
					prop = 'cssFloat';
				}
				if ( prop === 'opacity' ) {
					setOpacity( el, value );	
				}
				else {
					el.style[camelize( prop )] = value;
				}
			},
			prop;
		if ( isObject( a ) ) {	
			for ( prop in a ) {
				set( prop, a[prop] );
			}
		}
		else if ( b ) {
			set( a, b );			
		}
	},
	
	setOpacity = function () {
		if ( 'filters' in docRoot ) {
			return function ( el, val ) {
				if ( el.__opacity === undefined ) {
					el.__opacity = 1;
				}
				el.style.filter = val === 1 ? '' : 'alpha(opacity=' + (val * 100) + ')';
				el.__opacity = val;
			};
		} 
		return function ( el, val ) {
			if ( el.__opacity === undefined ) {
				el.__opacity = 1;
			}
			el.style.opacity = el.__opacity = val;
		};
	}(),
	
	storeData = function ( el, name, value ) {
		var cache = elementData, elementKey = cache.ns;
		if ( !( el = getElement(el) ) ) { return; }
		if ( !( elementKey in el ) ) { 
			el[elementKey] = elementUid(); 
			cache[el[elementKey]] = {};
		}
		cache[el[elementKey]][name] = value;
	},
	
	retrieveData = function ( el, name ) {
		var cache = elementData, elementKey = cache.ns;
		if ( !( el = getElement(el) ) ) { return; }
		if ( elementKey in el && el[elementKey] in cache ) {
			return cache[el[elementKey]][name];
		}
		return null;
	},
	
	removeData = function ( el, name ) {
		var cache = elementData, elementKey = cache.ns;
		if ( !( el = getElement(el) ) ) { return; }
		if ( elementKey in el && el[elementKey] in cache ) {  
			delete cache[el[elementKey]][name];
		}
	},
	
	elementData = { ns:'jelly_' + (+new Date) },
	
	elementUid = function () { 
		var uid = 0;
		return function () { return ++uid; }
	}();

extend( J, {
	addClass: addClass,
	removeClass: removeClass,
	hasClass: hasClass,
	toggleClass: toggleClass,
	getElements: getElements,	
	getElement: getElement,
	createElement: createElement,
	createBranch: createBranch,
	wrapElement: wrapElement,
	withElement: withElement,
	replaceElement: replaceElement,
	removeElement: removeElement,
	insertElement: insertElement,
	insertTop: insertTop,
	insertBefore: insertBefore,
	insertAfter: insertAfter,
	getFirst: getFirst,
	getLast: getLast,
	getNext: getNext,
	getPrevious: getPrevious,
	getChildren: getChildren,
	getXY: getXY,
	setXY: setXY,
	getX: getX,
	setX: setX,
	getY: getY,
	setY: setY,
	getAttribute: getAttribute,
	getStyle: getStyle,
	setStyle: setStyle,
	setOpacity: setOpacity,
	storeData: storeData,
	retrieveData: retrieveData,
	removeData: removeData
});

/**

Events

@description 
	Event utilities

*/

var addEvent = function ( obj, type, fn ) {
		obj = getElement(obj);
		var mouseEnter = type === 'mouseenter',
			mouseLeave = type === 'mouseleave',
			wrapper, 
			handle;
			
		if ( obj === doc && type === 'domready' ) {
			return addDomReady( fn );
		} 
		if ( !standardEventModel ) {
			wrapper = function ( e ) {
				fn.call( obj, fixEvent( e ) );
			};
		}
		if ( mouseEnter || mouseLeave ) {
			wrapper = function ( e ) {
				e = fixEvent( e );
				if ( !mouseEnterLeave.call( obj, e ) ) { return; }
				fn.call( obj, e );
			};
			type = mouseEnter ? 'mouseover' : 'mouseout';
		}
		handle = [ obj, type, wrapper || fn ];
		eventLog.push( handle );
		if ( standardEventModel ) { 
			obj.addEventListener( type, wrapper || fn, false ); 
		} 
		else { 
			obj.attachEvent( 'on' + type, wrapper ); 
		}
		return handle;
	},
	
	removeEvent = function ( handle ) {
		if ( handle ) { 
			if ( !isArray( handle ) ) {
				return removeDomReady( handle );
			} 
			if ( standardEventModel ) {
				handle[0].removeEventListener( handle[1], handle[2], false ); 
			} 
			else {
				handle[0].detachEvent( 'on' + handle[1], handle[2] ); 
			}
		}
	},
	
	eventLog = [], 
	
	purgeEventLog = function () {
		for ( var i = 0, handle; eventLog[i]; i++ ) {
			handle = eventLog[i];					
			if ( handle[0] !== win && handle[1] !== 'unload' ) {
				removeEvent( handle );
			}
		}
	}, 
	
	fixEvent = function () {
		if ( standardEventModel ) {
			return function (e) { return e; };
		}
		return function (e) {
			e = win.event;
			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 + docRoot.scrollLeft;
			e.pageY = e.clientY + docRoot.scrollTop;
			return e;
		};		
	}(),
	
	mouseEnterLeave = function (e) { 
		var related, i;
		if ( e.relatedTarget ) {
			try {
				related = e.relatedTarget;
				if ( related.nodeType !== 1 || related === this ) { 
					return false; 
				}
				var children = this.getElementsByTagName('*'), n = children.length, i = 0;
				for ( i; n > i; i++ ) {
					if ( related === children[i] ) { 
						return false; 
					}
				}
			}
			catch ( ex ) {}
		}
		return true;
	},
	
	stopEvent = function (e) {
		e = fixEvent(e);
		e.stopPropagation();
		e.preventDefault();
		return e;
	};
	
extend( J, { 
	addEvent: addEvent,
	removeEvent: removeEvent,
	stopEvent: stopEvent,
	fixEvent: fixEvent
});

// IE garbage collection to prevent memory leaks
//
if ( browser.ie && browser.ie < 8 ) { 
	addEvent( win, 'unload', purgeEventLog );
}

/**

Dom ready

@description 
	Cross browser interface for the DOMContentLoaded event 

*/

(function () {
  
	var self = J.DomReady = {
	
			ready: false,
			
			handlers: {},
			
			add: function ( callback, ref ) {
				var ref = ref || ++uid;
				self.handlers[ref] = callback;
				return ref;
			},
			
			remove: function ( ref ) {
				delete self.handlers[ ref ];
			},
			
			fire: function () {
				if ( self.ready ) { return; }
				self.ready = true;
				clearTimeout( pollTimer );
				for ( var handler in self.handlers ) {
					try { 
						self.handlers[ handler ](); 
					}
					catch (ex) { 
						logError(ex); 
					}
				} 
			}
		},
		
		uid = 0,
		pollTimer,
	
		checkReadyState = function () {
			if ( doc.readyState === 'complete' ) {
				doc.detachEvent( 'onreadystatechange', checkReadyState );
				self.fire();
			}
		}, 
		
		scrollPoller = function () {
			try { 
				docRoot.doScroll( 'left' ); 
			}
			catch (e) {
				pollTimer = setTimeout( scrollPoller, 10 );
				return;
			}
			self.fire();
		};
	
	//	The easy way and the convoluted way 
	//
	if ( standardEventModel ) {
		addEvent( doc, 'DOMContentLoaded', self.fire );
	} 
	else {
		doc.attachEvent( 'onreadystatechange', checkReadyState );
		if ( win === top ) {
			pollTimer = setTimeout( scrollPoller, 0 );
		}
	}

	// Fallback catch all
	//
	addEvent( win, 'load', self.fire );
	
})();

var DomReady = J.DomReady,
	addDomReady = function ( callback, ref ) {
		return DomReady.add( callback, ref || null );
	},
	removeDomReady = function ( ref ) {
		DomReady.remove( ref );
	};

extend( J, {
	addDomReady: addDomReady,
	removeDomReady: removeDomReady
});

/**

Cookies

@description 
	Utility functions for working with cookies

*/

extend(J, { 
	
	getCookie: function ( name ) {
		var result = new RegExp( name + '=([^; ]+)' ).exec( doc.cookie );
		return result ? unescape( result[1] ) : null;
	},
	
	setCookie: function ( name, value, expires, path, domain, secure ) {
		if ( expires ) {
			var expireTime = (+new Date) + ( ( 1000*60*60*24 ) * expires );
			expires = new Date( expireTime ).toUTCString();
		}
		doc.cookie = name + '=' + escape( value ) +
			( expires ? ';expires=' + expires : '' ) + 
			( path ? ';path=' + path : '' ) +
			( domain ? ';domain=' + domain : '' ) +	
			( secure ? ';secure' : '' );
	},
	
	removeCookie: function ( name, path, domain ) {
		if ( J.getCookie( name ) ) {
			doc.cookie = name + '=' +
				( path ? ';path=' + path : '' ) +
				( domain ? ';domain=' + domain : '' ) +	
				( ';expires=' + new Date(0) );
		}
	}
	
});

/**

Flash

@description 
	Utility functions for working with flash objects

*/

extend( J, {
	
	getFlashVersion: function () {
		var ver = { major: 0, build: 0 },
			plugins = navigator.plugins,
			desc,
			versionString;
		if ( plugins && isObject( plugins['Shockwave Flash'] ) ) {
			desc = plugins['Shockwave Flash'].description;
			if ( desc !== null ) {
				versionString = desc.replace( /^[^\d]+/, '' );
				version.major = parseInt( versionString.replace( /^(.*)\..*$/, '$1' ), 10 );
				version.build = parseInt( versionString.replace( /^.*r(.*)$/, '$1' ), 10 );
			}
		} 
		else if ( msie ) {
			try {
				var axflash = new ActiveXObject( 'ShockwaveFlash.ShockwaveFlash' );
				desc = axflash.GetVariable( '$version' );
				if ( desc !== null ) {
					versionString = desc.replace( /^\S+\s+(.*)$/, '$1' ).split(',');
					version.major = parseInt( versionString[0], 10 );
					version.build = parseInt( versionString[2], 10 );
				}
			} catch (ex) {}
		}
		return version;
	},
	
	createFlashObject: function ( path, width, height, fallback, params, vars, attributes ) {
		var params = params || {};
			vars = vars || {},
			attrs = attributes || {},
			fallback = fallback || 'You need <a href="http://www.adobe.com/go/getflashplayer">Adobe Flash Player</a> installed to view this content</a>',
			data = [],
			key,
			output = '<object';
		if ( msie ) {
			attrs.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
			params.movie = path;
		} 
		else {
			attrs.data = path;
			attrs.type = 'application/x-shockwave-flash';
		}
		attrs.width = width;
		attrs.height = height;
		for ( key in attrs ) { 
			output += ' ' + i + '="' + attr[i] + '"'; 
		}
		output += '>\n';
		for ( key in vars ) { 
			data.push( key + '=' + encodeURIComponent( vars[key] ) ); 
		}
		if ( data.length > 0 ) { 
			params.flashvars = data.join('&'); 
		} 
		for ( key in params ) { 
			output += '\t<param name="' + key + '" value="' + params[key] + '" />\n'; 
		}
		return output + fallback + '\n</object>';
	},
	
	embedFlashObject: function ( el, path, width, height, params, vars, attributes ) {
		el = getElement(el);
		el.innerHTML = J.createFlashObject( 
			path, width, height, el.innerHTML, params || {}, vars || {}, attributes || {} );
	}
	
});

/**

Misc

@description 
	Various utilities

*/

var getViewport = function () {
		if ( isDefined( docRoot.clientWidth ) && docRoot.clientWidth !== 0 ) { 
			return function () {
				return [ docRoot.clientWidth, docRoot.clientHeight ];
			};
		}
		return function () {
			return [ doc.body.clientWidth || 0, doc.body.clientHeight || 0 ];
		};
	}(),

	getWindowScroll = function () {
		if ( isDefined( win.pageYOffset ) ) {
			return function () {
				return [ win.pageXOffset, win.pageYOffset ];
			};
		} 
		return function () {
			if ( isDefined( docRoot.scrollTop ) && 
				( docRoot.scrollTop > 0 || docRoot.scrollLeft > 0 ) ) {
				return [ docRoot.scrollLeft, docRoot.scrollTop ];
			}
			return [ doc.body.scrollLeft, doc.body.scrollTop ];
		};
	}(),
	
	parseQuery = function ( el ) {
		el = el || win.location;
		var data = {};
		if ( /\?/.test( el.href ) ) {
			var queries = el.href.split('?')[1].split('&'),
				i = queries.length-1,
				parts;
			do {
				parts = queries[i].split('=');
				data[parts[0]] = decodeURIComponent( parts[1].replace(/\+/g, '%20') );
			} while (i--);
		}
		return data;
	},
	
	buildQuery = function () {
		var append = function ( name, value ) {
				if ( !name ) { return; } 
				if ( callbackFilter ) { value = callbackFilter.call(value, value); } 
				data.push( name + '=' + encodeURIComponent(value).replace(/%20/g, '+') );
			}, 
			parseElement = function ( el ) {
				if ( !isElement(el) || !/^(input|textarea|select)$/i.test(el.nodeName) ) {
					return; 
				}
				var type = el.type.toLowerCase(),
					name = el.name, 
					value = el.value;
				switch ( type ) {
					case 'checkbox': 
						if ( el.checked ) { append( name, value || 'on' ); }
						break;
					case 'radio': 
						if ( el.checked ) { append( name, value ); }
						break;
					default: 
						append( name, value );
				}
			}
			args = toArray( arguments ),
			callbackFilter = isFunction( args[args.length-1] ) ? args.pop() : null;
			data = [];
			
		args.each(function ( arg ) {
			if ( isObject(arg) && isInteger(arg.length) ) {
				( isArray(arg) ? arg : toArray(arg) ).each( parseElement );
			}
			else if ( isObject(arg, true) ) {
				for ( var key in arg ) { append( key, arg[key] ); }
			}
			else if ( isString(arg) || isElement(arg) ) {
				var el = getElement(arg);
				if (el) {
					parseElement(el);
					J.Q( el, 'textarea, input, select' ).each( parseElement );
				}
				else {
					data.push( arg );	
				}
			}
		});
		return data.join('&');
	},
	
	unpack = function () {
		var stack = ['var J=JELLY'], mem, i = 1;
		for ( mem in J ) { 
			stack[ i++ ] = mem + '=J.' + mem;
		}
		return stack.join(',') + ';';
	};

extend( J, {
	getViewport: getViewport,
	getWindowScroll: getWindowScroll,
	parseQuery: parseQuery,
	buildQuery: buildQuery,
	unpack: unpack
});

/**

Page init

@description  
	Page load tasks

*/

// fix background image flicker on ie6

if ( browser.ie6 ) { 
	try { 
		doc.execCommand( 'BackgroundImageCache', false, true );
	} catch (ex) {}; 
}

// adding informational css classes to the root element for convenience

var classname = ['unknown'], key;
for ( key in browser ) {
	if ( browser[key] ) {
		if ( classname[0] === 'unknown' ) { 
			classname = [key]; 
		} 
		else {
			classname.push( key );
		}
	}
}
addClass( docRoot, 'js ' + classname.join(' ') );

/**

Selector Engine

@description 
	A fast cross-browser interface for querying the DOM with CSS selectors

*/
(function () {
	
	var	pushToStack = function ( value ) {
			stack[stack.length] = value;
		},
		
		unMark = function ( group ) {
			for ( var i = 0, n = group.length; i < n; i++ ) {
				group[i][uniqueKey] = null;
			}
		},
		
		filterUnique = function ( group ) {
			var el, 
				n = group.length, 
				uniques = [];
			while (n) {
				el = group[--n];
				if ( !el[uniqueKey] ) {
					el[uniqueKey] = true;
					uniques[uniques.length] = el;
				}
			}
			n = uniques.length;
			while (n) {
				uniques[--n][uniqueKey] = null;
			}
			return uniques.reverse();
		},
		
		/*====================================
			selector parsing
		=====================================*/
		
		parseTokenComponent = function ( part, fetchOrFilter ) {
			var obj = { 
					mode: fetchOrFilter ? _fetch_ : _filter_, 
					not: false,
					type: _id_
				};
			if ( /^(\w+)?#\w/.test( part ) ) {
				obj.val = part.split('#');
			} 
			else if ( /^\w+|\*$/.test( part ) ) {
				obj.type = _tag_; 
				obj.val = part;
			} 
			else if ( /^\.\w/.test( part ) ) { 
				obj.type = _class_;	
				obj.val = part.replace( /^\./, '' );
			} 
			else if ( /^\[/.test( part ) ) { 
				obj.type = _attr_;	
				obj.val = part.replace( /\[|\]/g, '' );	
			} 
			else if ( /^\+|>|~/.test( part ) ) { 
				obj.type = _combi_; 
				obj.val = part;			
			} 
			else if ( /:not\(/.test( part ) ) {
				obj = parseTokenComponent( part.replace( /\:not\(|\)$/g, '' ) );
				obj.not = true;
			} 
			else if ( /^:/.test( part ) ) { 
				var _tmp = part.replace( /^:|\)$/g, '' ).split( '(' );
				obj.type = _pseudo_; 
				obj.kind = _tmp[0];
				obj.val = _tmp[1];
			} 
			return obj;
		},

		parseSelector = function ( selector ) {
			var result = [],
				// Seperate out the combinators + > ~, then split
				parts = normalize( selector.replace( /(>|~(?!=)|\+(?!\d))/g, ' $1 ' ) ).split( ' ' ),
				universal = { mode: _fetch_, type: _tag_, val: '*' },
				getByClass = 'getElementsByClassName' in doc,
				sibling = false;

			for ( var i = 0, tmp; i < parts.length; i++ ) { 
				tmp = parts[i].
						replace( /([^\(\#\.\[])(:)/g, '$1 $2' ).
						replace( /([^\(])(\[|\.)/g, '$1 $2' ).
						replace( /\:not\(\s*/g, ':not(' ).trim().split(' ');	
				for ( var j = 0, obj; j < tmp.length; j++ ) {
					obj = parseTokenComponent( tmp[j], !j );
					if ( sibling ) {
						obj.mode = _filter_;
					} 
					else if ( j === 0 && 
						( obj.type === _pseudo_ || obj.type === _attr_ || 
							( obj.type === _class_ && !getByClass ) || 
						obj.not ) ) {
						result.push( universal );
						obj.mode = _filter_;
					}
					if ( contains( tmp[j], uniqueKey ) ) {
						obj[obj.type === _attr_ ? 'spValue' : 'val'] = strings.shift();
					}
					result.push( obj );
					sibling = /^(~|\+)$/.test( obj.val );
				}
			}
			result.postFilter = !( parts.length === 1 || parts.length === 3 && /^[\+~]$/.test( parts[1] ) );
			return result;
		},	
		
		/*====================================
			ids
		=====================================*/
		
		mergeId = function ( tkn ) {
			var tag = tkn.val[0], 
				id = tkn.val[1];
				
			if ( tkn.mode === _filter_ ) { 
				for ( var i = 0, n = collection.length; i < n; i++) {
					if ( tag ) {
						if ( ( collection[i].tagName.toLowerCase() === tag && 
								collection[i].id === id ) !== tkn.not) {
							pushToStack( collection[i] );
						}
					} 
					else if ( ( collection[i].id === id ) !== tkn.not ) {
						pushToStack( collection[i] );
					}
					if ( !tkn.not && stack[0] ) {
						return;
					}
				}
			} 
			else {
				if ( !tag ) {
					stack[0] = getElement( id );
				} 
				else {
					var elem = getElement( id );
					if ( elem && elem.tagName.toLowerCase() === tag ) {
						stack[0] = elem;	
					}
				}
				if ( !firstRun && stack[0] ) {
					var flag = false;
					for ( var i = 0, n = collection.length; i < n; i++ ) {
						if ( stack[0].contains( collection[i] ) ) {
							flag = true;
							break;
						}
					}
					if ( !flag ) {
						stack[0] = null;
					}
				} 
			}
		}, 
		
		/*====================================
			tags
		=====================================*/
		
		mergeTags = function ( tkn ) {
			var extra = msie && tkn.val === '*';	
			if ( firstRun ) {
				for ( var i = 0, tags = getElements( tkn.val ), n = tags.length; i < n; i++) {
					if ( extra ) { 
						if ( tags[i].nodeType === 1 ) {
							pushToStack( tags[i] );
						}
					}
					else {
						pushToStack( tags[i] );
					}
				}
			} 
			else if ( tkn.not || tkn.mode === _filter_ ) {
				for ( var i = 0, test = tkn.val.toUpperCase(), n = collection.length; i < n; i++ ) {
					if ( ( collection[i].nodeName.toUpperCase() === test ) !== tkn.not ) {
						pushToStack( collection[i] );
					}
				}
			} 
			else {
				for ( var i = 0, n = collection.length; i < n; i++ ) {
					var tags = getElements( collection[i], tkn.val ), 
						n2 = tags.length, 
						j = 0;
					for ( j; j < n2; j++ ) {
						if ( extra ) {
							if ( tags[j].nodeType === 1 ) {
								pushToStack( tags[j] );
							}
						}
						else {
							pushToStack( tags[j] );
						}
					}
				}
			}
		},
		
		/*====================================
			class
		=====================================*/
		
		mergeClass = function ( tkn ) {
			var val = tkn.val, 
				not = tkn.not, 
				n = collection.length, 
				i = 0;
			if ( tkn.mode === _fetch_ ) {
				if ( firstRun ) {
					stack = toArray( doc.getElementsByClassName( val ) );
				} 
				else {
					for ( i; i < n; i++ ) {
						var tags = collection[i].getElementsByClassName( val ), 
							n2 = tags.length, 
							j = 0;
						for ( j; j < n2; j++ ) {
							pushToStack( tags[j] );
						}
					}
				}
			} 
			else {
				var patt = new RegExp( '(^|\\s)' + val + '(\\s|$)' ), 
					classname;
				for ( i; i < n; i++ ) {
					classname = collection[i].className;
					if ( !classname ) {
						if ( not ) {
							pushToStack( collection[i] );
						}
						continue;
					} 
					if ( patt.test( classname ) !== not ) {
						pushToStack( collection[i] );
					} 
				}
			}
		},		
		
		/*====================================
			attributes
		=====================================*/
	
		attributeTests = {
			'=': function ( attr, val ) { return attr === val; }, 
			'^=': function ( attr, val ) { return attr.indexOf( val ) === 0; }, 
			'$=': function ( attr, val ) { return attr.substr( attr.length - val.length ) === val; }, 
			'*=': function ( attr, val ) { return attr.indexOf( val ) !== -1; }, 
			'|=': function ( attr, val ) { return attr.indexOf( val ) === 0; }, 
			'~=': function ( attr, val ) { return (' ' + attr + ' ').indexOf(' ' + val + ' ') !== -1; } 
		},
		
		mergeAttribute = function ( tkn ) {
			var n = collection.length, 
				i = 0;
			if ( contains( tkn.val, '=' ) ) {
				var parts = /([\w-]+)([^=]?=)(.+)/.exec( tkn.val ), 
					attr, 
					val = isDefined( tkn.spValue ) ? tkn.spValue : parts[3];
				for ( i; i < n; i++ ) {
					attr = getAttribute( collection[i], parts[1] );
					if ( ( attr !== null && attributeTests[parts[2]]( attr, val ) ) !== tkn.not ) {
						pushToStack( collection[i] );
					}
				}
			} 
			else {
				for ( i; i < n; i++ ) {
					if ( ( getAttribute( collection[i], tkn.val ) !== null ) !== tkn.not ) {
						pushToStack( collection[i] );
					}
				}
			}
		},
		
		/*====================================
			pseudo classes
		=====================================*/
	
		mergePseudo = function ( tkn ) {
			var kind = tkn.kind,
				not = tkn.not;
			if ( /^(nth-|first-of|last-of)/.test( kind ) ) {
				stack = pseudoTests[kind]( collection, tkn ); 
			} 
			else if ( kind === 'root' && !not ) {
				stack[0] = rootElement;
			} 
			else if ( kind === 'target' && !not ) {
				var hash = win.location.href.split('#')[1] || null;
				stack[0] = getElement( hash ) || getElements( hash )[0];
			} 
			else {
				for ( var i = 0, n = collection.length; i < n; i++ ) {
					if ( pseudoTests[kind]( collection[i], tkn ) !== not ) {
						pushToStack( collection[i] );
					}
				}
			}
		},
		
		parseNthExpr = function ( expr ) {
			var obj = { mode: 'all' };
			obj.direction = expr.indexOf('-') === 0 ? 'neg' : 'pos';
			
			if ( expr === 'n' ) { 
				return obj;
			} 
			else if ( /^\d+$/.test( expr ) ) {
				obj.mode = 'child';
				obj.val = parseInt( expr, 10 );
				return obj;
			} 
			obj.mode = 'an+b';
			
			if ( /^(even|2n|2n\+2)$/.test( expr ) ) {
				obj.oddEven = 0;
			} 
			else if ( /^(odd|2n\+1)$/.test( expr ) ) {
				obj.oddEven = 1;
			}
			var pts = expr.split('n');
			obj.start = pts[1] ? parseInt( pts[1], 10 ) : 1;
			obj.jump = pts[0] && pts[0] !== '-'	? parseInt( pts[0].replace( /^\-/, '' ), 10 ) : 1;		
			return obj;
		},
		
		nthChildFilter = function ( collection, expr, ofType, last, not ) {
			expr = parseNthExpr( expr );
			if ( expr.mode === 'all' ) { return collection; }				
			var	result = [], 
				parentCache = [], 
				nodeName = collection[0].nodeName,
				testType = ofType ? 
					function (el) { return el.nodeType === 1 && el.nodeName === nodeName; } : 
					function (el) { return el.nodeType === 1; },		
				append = function ( cond ) { 
					if ( cond ) { 
						result.push( collection[i] ); 
					}
				};
			for ( var i = 0, n = collection.length, pnt, uid; i < n; i++ ) {
				pnt = collection[i].parentNode, 
				uid = 1;
				if ( !pnt[uniqueKey] ) {
					var el = pnt[ !last ? 'firstChild' : 'lastChild' ],
						direction = !last ? 'nextSibling' : 'previousSibling'; 
					for ( el; el; el = el[direction] ) {
						if ( testType( el ) ) {
							el.nodeIndex = uid++;
						}
					}
					pnt[uniqueKey] = 1;
					parentCache.push( pnt );
				}
				if ( expr.mode === 'child' ) { 
					append( ( ( collection[i].nodeIndex === expr.val ) !== not ) );
				} 
				else if ( isDefined( expr.oddEven ) ) { 
					append( ( collection[i].nodeIndex % 2 === expr.oddEven ) !== not );
				} 
				else {
					if ( expr.direction === 'pos' ) {
						if ( collection[i].nodeIndex < expr.start ) {
							if ( not ) {
								append( true );
							} 
						} 
						else { 
							append( ( ( collection[i].nodeIndex - expr.start ) % expr.jump === 0 ) !== not ); 
						}
					} 
					else {
						if ( collection[i].nodeIndex > expr.start ) {
							if ( not ) { 
								append( true ); 
							} 
						} 
						else { 
							append( ( ( expr.start - collection[i].nodeIndex ) % expr.jump === 0 ) !== not ); 
						}
					}
				}
			}
			unMark( parentCache );
			return expr.direction === 'neg' ? result.reverse() : result;
		},
		
		pseudoTests = {
			'nth-child': function ( tags, tkn ) {
				return nthChildFilter( tags, tkn.val, false, false, tkn.not );
			},
			'nth-of-type': function ( tags, tkn ) {
				return nthChildFilter( tags, tkn.val, true, false, tkn.not );
			},
			'nth-last-child': function ( tags, tkn ) {
				return nthChildFilter( tags, tkn.val, false, true, tkn.not );
			},
			'nth-last-of-type': function ( tags, tkn ) {
				return nthChildFilter( tags, tkn.val, true, true, tkn.not );
			},
			'first-of-type': function ( tags, tkn ) {
				return nthChildFilter( tags, '1', true, false, tkn.not );
			},
			'last-of-type': function ( tags, tkn ) {
				return nthChildFilter( tags, '1', true, true, tkn.not );
			},
			'only-child': function (el) {
				return !getNext(el) && !getPrevious(el);
			},
			'only-of-type': function (el) {
				var tags = getElements( el.parentNode, el.nodeName );
				if ( tags.length === 1 && tags[0].parentNode === el.parentNode ) {
					return true;
				} 
				else {
					for ( var bool = true, n = tags.length, i = 0, c = 0; i < n; i++ ) {
						if ( el.parentNode === tags[i].parentNode ) {
							c++; 
							if ( c > 1 ) {
								return false;
							}
						}
					}
					return true;
				}
			},
			'first-child': function (el) { return !getPrevious(el); },
			'last-child': function (el) { return !getNext(el); }, 
			'checked': function (el) { return el.checked; },
			'enabled': function (el) { return !el.disabled; },
			'disabled': function (el) { return el.disabled; },
			'empty': function (el) { return !el.firstChild;	},
			'lang': function ( el, tkn ) { return el.getAttribute('lang') === tkn.val; },
			'root': function (el) { return el === rootElement; },
			'target': function (el) {
				var hash = win.location.href.split('#')[1] || null;
				return el.id === hash || el.name === hash;
			}
		},
		
		/*====================================
			combinators
		=====================================*/

		mergeDirectSibling = function () {
			for ( var i = 0, n = collection.length, next; i < n; i++ ) {
				if ( next = getNext( collection[i] ) ) {
					pushToStack( next );
				}
			}
		},
		
		mergeAdjacentSibling = function () {
			var store = [], 
				sibs = [], 
				collectionLength = collection.length, 
				i = 0,
				next,
				parental; 
			for ( i; i < collectionLength; i++ ) {
				parental = collection[i].parentNode;
				parental[uniqueKey] = true;
				store.push({
					parent: parental, 
					child: collection[i]
				});
			}	
			for ( i = 0; i < store.length; i++ ) {
				if ( store[i].parent[uniqueKey] ) {
					store[i].parent[uniqueKey] = null;
					sibs.push( store[i].child );
				}
			}
			for ( i = 0; i < sibs.length; i++ ) {
				next = sibs[i].nextSibling;
				while ( next ) {
					if ( next.nodeType === 1 ) {
						pushToStack( next );
					}
					next = next.nextSibling;
				}
			}
		},
		
		filterChildren = function () {
			var result = [], 
				collectionLength = collection.length, 
				stackLength = stack.length, 
				parentElem,
				i = 0,
				j; 
			for ( i; i < stackLength; i++ ) {
				parentElem = stack[i].parentNode; 
				for ( j = 0; j < collectionLength; j++ ) {  
					if ( collection[j] === parentElem ) {
						result.push( stack[i] );
						break;
					}
				}
			}
			stack = result;
		},
		
		firstRun = 1,
		strings = [],
		uniqueKey = '__JELLY_Q__',
		stack = [],
		collection = [],
		
		_filter_ = 1,
		_fetch_ = 2,
		
		_id_ = 3,
		_tag_ = 4,
		_class_ = 5,
		_attr_ = 6,
		_pseudo_ = 7,
		_combi_ = 8,
		
		/*====================================
			execute
		=====================================*/
		
		execute = function ( a, b ) {
		
			var contextMode = !!b,
				selector = contextMode ? b : a;
			
			collection = contextMode ? [ getElement( a ) ] : [];

			if ( firstRun ) {
				var m;
				while ( m = /('|")([^\1]*?)\1/.exec( selector ) ) {
					strings.push( m[2] );
					selector = selector.split( m[0] );
					selector = [ selector[0], uniqueKey, selector[1] ].join('');   
				}
			}
			
			// Split and recurse for comma chained selectors
			if ( contains( selector, ',' ) ) {
				var combo = [],	
					parts = selector.split(','), 
					part;
				firstRun = 0;
				while ( part = parts.shift() ) {
					combo = combo.concat( contextMode ? execute( a, part ) : execute( part ) );
				}
				firstRun = 1;
				return filterUnique( combo );
			}
			
			firstRun = !b;
			
			var tokens = parseSelector( selector ),
				children = null; 
				
			// log(tokens)
		
			for ( var i = 0, n = tokens.length, token; i < n; i++ ) {
			
				stack = []; 
				token = tokens[i];
				switch ( token.type ) {
					case _id_: 
						mergeId( token ); 
						break;
					case _tag_: 
						mergeTags( token ); 
						break;
					case _class_: 
						mergeClass( token ); 
						break;
					case _attr_: 
						mergeAttribute( token ); 
						break;
					case _pseudo_: 
						mergePseudo( token ); 
						break
					case _combi_: 
						if ( token.val === '+' ) {
							mergeDirectSibling( token );
						} 
						else if ( token.val === '~' ) {
							mergeAdjacentSibling( token );
						}
				}
				if ( children ) { 
					filterChildren(); 
				}
				if ( token.val === '>' ) {
					children = true;
					continue;
				}
				if ( !stack[0] ) {
					return addSugar( [] );
				}
				children = null;
				firstRun = 0;
				collection = stack;
			}
			if ( tokens.postFilter ) { 
				return addSugar( filterUnique( collection ) ); 
			}
			return addSugar( collection );
		},
		
		nativeSelectorEngine = function ( a, b ) {
			try { 
				return addSugar( toArray( b ? ( getElement( a ) ).querySelectorAll( b ) : doc.querySelectorAll( a ) ) );
			} catch ( ex ) { 
				logWarn( ex ); 
			}
		},
		
		/*====================================
			sugar methods
		=====================================*/
		
		addSugar = function ( collection ) {
			for ( var meth in sugarMethods ) {
				collection[meth] = sugarMethods[meth];
			}
			return collection;
		},
		
		sugarMethods = {};
		
// Build our sugar methods object
[ 'addClass', 'removeClass', 'setStyle', 'addEvent' ].each( function ( meth ) {
	sugarMethods[meth] = function () {
		var args = toArray( arguments ),
			n = this.length,
			i = 0;
		for ( i; i < n; i++ ) {
			J[meth].apply( {}, [this[i]].concat( args ) );
		}  
		return this;
	} 
});
	
/*====================================
	external api
=====================================*/

J.Q = function () {
	if ( querySelectorAll ) {
		if ( !browser.ie ) { 
			return nativeSelectorEngine; 
		} 
		return function ( a, b ) {
			if ( /\:(nth|las|onl|not|tar|roo|emp|ena|dis|che)/.test( b || a ) ) { 
				return execute( a, b ); 
			}
			return nativeSelectorEngine( a, b );
		}
	} 
	return execute;
}();
	
})();

var Q = J.Q;

/**

Request

@description
	XHR wrapper class

*/

(function () {

var Class = defineClass( 'Request', {
		
		__init: function ( obj ) {
			extend( this, obj );
		},
		
		__static: {
			timeout: 15000	
		},
		
		noCache: true,
		async: true,
		cleanUp: true,
		feedback: { start: functionLit, stop: functionLit },
		requestHeaders: {},
		
		send: function ( method, request, callback ) {
			var self = this,
			file = request,
			data = null,
			method = method.toUpperCase(),
			xhr = self.xhr ? self.xhr : self.getXHR();
			if ( self.inProgress || !xhr ) {
				return false;
			}
			if ( method === 'POST' ) {
				var tmp = request.split('?');
				file = tmp[0];
				data = tmp[1];
				self.requestHeaders['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
				self.requestHeaders['Content-length'] = data.length;
			}
			if ( method === 'GET' && self.noCache ) {
				self.requestHeaders['If-Modifed-Since'] = 'Sat, 1 Jan 2000 00:00:00 GMT';
			}
			
			xhr.open( method, file, self.async );
			
			xhr.onreadystatechange = function () {
				if ( xhr.readyState === 4 ) {
					self.fire( 'complete', xhr );
					clearTimeout(self.timer);
					self.feedback.stop();
					var status = xhr.status,
						statusOk = ( status >= 200 && status < 300 ) || status === 304 ||
						( status === undefined && browser.webkit );
					if ( statusOk ) {
						self.fire( 'success', xhr );
						if ( callback ) {
							callback.call( self, xhr );
						}
					}
					else {
						self.fire( 'fail', xhr );
					}
					if ( self.cleanUp ) {
						self.xhr = null;
					}
					self.inProgress = false;
				}
			};
						
			for ( var key in self.requestHeaders ) {
				xhr.setRequestHeader( key, self.requestHeaders[key] );
			}
			xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' );
			
			self.feedback.start();
			self.timer = setTimeout( function () {
				xhr.abort();
				self.fire( 'timeout', xhr );               
				self.inProgress = false;
			}, self.timeout || Class.timeout );
			
			self.inProgress = true;
			xhr.send( data );
			self.fire( 'request', xhr );
			return true;
		},
		
		post: function ( file, data, callback ) {
			return this.send( 'post', file + '?' + (data || 'empty'), callback );
		},
		
		get: function ( request, callback ) {
			return this.send( 'get', request, callback );
		},
		
		getXHR: function () {
			if ('XMLHttpRequest' in win) {
				return function () {
					return new XMLHttpRequest();
				};
			}
			return function () {
				var xhr = false;
				try { xhr = new ActiveXObject('Msxml2.XMLHTTP'); } catch (ex) {
					try { xhr = new ActiveXObject('Microsoft.XMLHTTP'); } catch (ex) {} 
				}
				return xhr;
			};
		}()
		
	});
	
})();

/**

Easings

@description 
	Easing equations by Robert Penner 

*/

J.easings={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=J.easings.backOffset}return E*(B/=D)*B*((C+1)*B-C)+A},backOut:function(B,A,E,D,C){if(!C){C=J.easings.backOffset}return E*((B=B/D-1)*B*((C+1)*B+C)+1)+A},backInOut:function(B,A,E,D,C){if(!C){C=J.easings.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-J.easings.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 J.easings.bounceIn(B*2,0,D,C)*0.5+A}return J.easings.bounceOut(B*2-C,0,D,C)*0.5+D*0.5+A}};

/**

Tween

@description   
	Class for creating element tweens

*/

(function () {

var Class = defineClass( 'Tween', {
		
		__init: function ( el, opts ) {
			this.el = getElement(el);
			extend( this, opts || {} ); 
		},
		
		__static: {
			
			uid: 0,
			tweens: {},
			timerSpeed: 20,
			
			subscribe: function ( inst ) {
				Class.tweens[ inst.tweenId ] = function () {
						inst.step.call(inst);
					};
				if ( !Class.timerHandle ) {
					Class.startTimer();
				}
			},
			
			unSubscribe: function ( inst ) {
				delete Class.tweens[ inst.tweenId ];
				clearTimeout( Class.timeoutHandle );
				Class.timeoutHandle = setTimeout( function () {
						if ( empty( Class.tweens ) ) {
							Class.stopTimer();
						}
					}, 250);
			},
			
			startTimer: function () {
                var handler = function () {
						for ( var key in Class.tweens ) {
							Class.tweens[key]();
						} 
					};
				//log('Timer started')
				Class.timerHandle = setInterval( handler, Class.timerSpeed );
			},
			
			stopTimer: function () {
				if ( Class.timerHandle ) {
					//log( 'Timer stopped ')
					clearInterval( Class.timerHandle );
				}
				Class.timerHandle = null;
			}
	
		},
		
		easing: J.easings.sineInOut,
		duration: 500,
		unit: 'px',
		
		setEasing: function (val) {
			this.easing = J.easings[val];
			return this;
		},
        
		setDuration: function (val) {
			this.duration = val;
			return this;
		},
        
		setOpacity: function ( val ) {
			setOpacity( this.el, val );
			return this;
		},
		
		setElement: function ( el ) {
			this.el = getElement( el );
			return this;
		},
        
		sequence: function () {
			this.sequenceStack = toArray( arguments );
			this.callSequence();
			return this;
		}, 
		
		callSequence: function () {
			var self = this,
                next = isArray( self.sequenceStack ) ? self.sequenceStack.shift() : null;
			if ( next ) { 
                if ( isFunction(next) ) {
                    next.call( self, self );
                }
                else {
                    self.start( next );
                }
			}
			else {
				self.fire( 'sequenceComplete' );
			}
		},
		
		stop: function () {
			Class.unSubscribe( this );
			return this;
		},
		
		start: function ( obj ) {
			var self = this,
				args = toArray( arguments ),
				elem = self.el;
				
			if ( isDefined( args[1] ) ) {
				obj = {};
				obj[args[0]] = args[1];
			} 
			self.stop();
			self.stack = [];
			
			if ( isArray( elem ) || isNodeList( elem ) ) {
				elem = elem[0];
			}
			
			if ( 'duration' in obj ) {
				self.setDuration( obj.duration );	
				delete obj.duration;
			}
			if ( 'easing' in obj ) {
				self.setEasing( obj.easing);	
				delete obj.easing;
			}
			
			var prop, key, value;
			
			for ( prop in obj ) {
				key = camelize( prop ),
				value = obj[prop];
				if ( prop.indexOf('color') !== -1 ) {
					if ( isArray( value ) ) {
						value = [	
                            parseColour( value[0], 'rgb-array' ), 
                            parseColour( value[1], 'rgb-array' )];
					}
					else {
						var style = getStyle( elem, key );
						if ( isNaN( style ) && !isString( style ) ) { 
							return logWarn( 'getStyle for "%s" returns NaN', key );
						} 
						value = [ 
							parseColour( style, 'rgb-array' ), 
							parseColour( value, 'rgb-array' )];
					}
					value.color = true;
				}
				else if ( prop === 'background-position' ) {
					if ( isArray( value[0] ) ) {
						value = [value[0][0], value[0][1]], [value[1][0], value[1][1]];
					}
					else {
                        var startX = 0,
							startY = 0,
							current = getStyle( elem, key ),
							m = /(\d+)[\w%]{1,2}\s+(\d+)[\w%]{1,2}/.exec( current );
						if ( current && m ) {
							startX = parseInt( m[1], 10 );
							startY = parseInt( m[2], 10 );
						}
						value = [[startX, value[0]], [startY, value[1]]];
					}
                    value.bgp = true;
				}
				else {
					if ( !isArray( value ) ) {
						var style = parseInt( getStyle( elem, key ), 10 );
						if ( isNaN( style ) && !isString( style ) ) { 
							return logWarn( 'getStyle for "%s" returns NaN', key );
						} 
						value = [style, value]; 
                    }
					else {
						value = value;
					}
					if ( prop === 'opacity' ) {
						value.opac = true;
					}
				}
                self.stack.push({
                    prop: key, 
                    from: value[0], 
                    to: value[1], 
                    color: value.color, 
                    bgp: value.bgp,
                    opac: value.opac
                });
			}
			self.startTime = +(new Date);
			self.tweenId = ++Class.uid;
            Class.subscribe( self );
            self.fire( 'start' );
			return self;
		},
		
		step: function () {
			var self = this, 
				currentTime = +(new Date);
            if ( currentTime < self.startTime + self.duration ) {
				self.elapsedTime = currentTime - self.startTime;
			} 
            else {
				self.stop();
                self.tidyUp();
				setTimeout(	function () { 
                    self.fire( 'complete' );
					self.callSequence(); 
				}, 0 );
                return;
			}
            self.increase();
		},
		
		tidyUp: function () {
			var self = this,
                item,
				style = self.el.style,
                stackCounter = self.stack.length - 1;
            do {
            	item = self.stack[stackCounter];
                if ( item.opac ) { 
                    self.setOpacity( item.to );                 
                }
                else if ( item.color ) { 
                    style[item.prop] = 'rgb(' + item.to.join(',') + ')'; 
				} 
				else if ( item.bgp ) {
					style.backgroundPosition = item.to[0] + self.unit + ' ' + item.to[1] + self.unit;
				} 
				else {
                    style[item.prop] = item.to + self.unit; 
                }
            } while ( stackCounter-- )
		},
		
		increase: function () {
			var self = this, 
                item,
                round = Math.round,
				style = self.el.style,
                stackCounter = self.stack.length - 1,
				roundPx = ( browser.ie || browser.opera || browser.webkit ) && self.unit === 'px';
            do {
                item = self.stack[stackCounter];
                if ( item.opac ) {
                    self.setOpacity( self.compute( item.from, item.to ) );  
                } 
                else if ( item.color ) {
					style[item.prop] = 'rgb(' + 
						round( self.compute( item.from[0], item.to[0] ) ) + ',' +
						round( self.compute( item.from[1], item.to[1] ) ) + ',' +
						round( self.compute( item.from[2], item.to[2] ) ) + ')';
				}
				else if ( item.bgp ) {
                    style.backgroundPosition = 
						self.compute( item.from[0], item.to[0] ) + self.unit + ' ' + 
						self.compute( item.from[1], item.to[1] ) + self.unit;
				}
				else { 
                    var computed = self.compute( item.from, item.to );
                    style[item.prop] = ( roundPx ? round( computed ) : computed ) + self.unit;
				}				
            } while ( stackCounter-- )

		},
		
		compute: function ( from, to ) {
			return this.easing( this.elapsedTime, from, ( to - from ), this.duration );
		}

	});

})(); 

var Tween = J.Tween;

/**

Load

@description   
	Singleton for managing the loading of assets: scripts, stylesheets and images
	
@api
		
@examples
	
*/

(function () {

var self = J.Load = {

	cache: {},
	
	js: function ( path, callback, opts ) {
		if ( self.cache[path] ) {
			return self.cache[path];
		}
		var attrs = extend( { type: 'text/javascript' }, opts || {} ),
			el = createElement( 'script', attrs ),
			onload = function () {
				self.cache[path] = el;
				if ( callback ) {
					defer( callback, el, el );
				}
			}, 
			onerror = function ( ex ) {
				logError( ex, ': "' + path + '"');
			};
		try {			
			if ( el.readyState ) {  
				el.onreadystatechange = function () {
					if ( /^(loaded|complete)$/.test( el.readyState ) ) {
						el.onreadystatechange = null;
						onload();
					}
				};
			} 
			else {  
				el.onload = onload;
				el.onerror = onerror;
			}
			el.src = path;
			insertElement( el, docHead );
			return el;
		} 
		catch ( ex ) {
			logError( ex );
		}
	},
	
	// No callbacks or error reporting is possible when loading css
	css: function ( path, opts ) {
		if ( self.cache[path] ) {
			return self.cache[path];
		}
		var attrs = extend( { type: 'text/css', media: 'screen', rel: 'stylesheet' }, opts || {} ),
			link = createElement( 'link', attrs );
		link.href = path;
		self.cache[path] = link;
		insertElement( link, docHead );
		return link;
	},
	
	img: function ( path, callback, opts ) {
		callback = callback || functionLit;
		var cached = self.cache[path];
		if ( cached ) {
			callback.call( cached, cached );
			return cached;
		}
		var img = createElement( 'img', opts || {} ),
			onload = function () {
				self.cache[path] = img;
				callback.call( img, img );
			},
			onerror = function (e) {
				logWarn( capitalize( e.type ) + ' loading image: "' + path + '"' );
			};
		img.onload = onload;
		img.onerror = img.onabort = onerror;
		img.src = path;
		return img;
	},
	
	purge: function ( path ) {
		var obj = self.cache[path];
		if ( !obj ) {
			return;
		}
		if ( docRoot.contains( obj ) ) {
			removeElement( obj );
		}
		obj.onload = obj.onerror = obj.onabort = null;
		delete self.cache[path];
	}
	
};
	
})();

var Load = J.Load;

/**

Poll

@description   
	Class for creating polling objects that can manage multiple subscribed callbacks

@api
	>> Instance mode
	var obj = new Poll( (Integer) milliseconds )
	** (Mixed) handle **  obj.subscribe( (Function) callback [, (String) handle] )
	** void **  obj.unSubscribe( (Mixed) handle )
	
	>> Static mode
	** (Mixed) handle **  Poll.subscribe( (Integer|String) time/term , (Function) callback [, (String) handle] )
	** void **  Poll.unSubscribe( (Integer|String) time/term , (Mixed) handle )
	
@examples
	// Create a new polling object
	var poller = new Poll( 200 );
	
	var handle = poller.subscribe( function () { ... } );
	poller.unSubscribe( handle );
	
	poller.subscribe( function () { ... }, 'myref' );
	poller.unSubscribe( 'myref' );
	
	
	// Subscribe to a global polling object
	var handle = Poll.subscribe( 100, function () { ... } );
	Poll.unSubscribe( 100, handle );
	
	var handle = Poll.subscribe( 'fast', function () { ... } );
	Poll.unSubscribe( 'fast', handle );

*/

(function () {

var Class = defineClass( 'Poll', {
		
	__static: {
		
		pollTime: 300,
		handlers: {},
		
		subscribe: function ( key, fn, ref ) {
			var keyStr = key+'', speed;
			if ( !( keyStr in Class.handlers ) ) {
				speed = isString( key ) ? Class.keywords[key] : key;
				Class.handlers[keyStr] = new Poll( speed );
			}
			return Class.handlers[keyStr].subscribe( fn, ref || null );
		},
		
		unSubscribe: function ( key, handlerId ) {
			Class.handlers[key].unsubscribe( handlerId );
		},

		keywords: {
			'vslow': 1000,
			'slow': 500,
			'fast': 100,
			'vfast': 50
		}
	},
	
	__init: function ( pollTime ) {
		extend( this, {
			handlers: {},
			pollTime: pollTime || Class.pollTime
		});
	},
	
	setPollTime: function ( ms ) {
		this.pollTime = ms;
		return this;
	},
	
	start: function () {
		var self = this;
		clearTimeout( self.timerHandle );
		clearTimeout( self.firstPoll );
		self.firstPoll = setTimeout( function () { 
			(function poll () {
				for ( var key in self.handlers ) {
					try {
						self.handlers[key]();
					}
					catch (ex) {
						logError(ex);
					}
				} 
				self.timerHandle = setTimeout( poll, self.pollTime );
			})()
		}, self.pollTime );
	}, 
	
	stop: function () {
		var self = this;
		clearTimeout( self.timerHandle );
		self.timerHandle = null;
		return self;
	},
	
	clear: function () {
		var self = this;
		self.stop();
		self.handlers = null;
		return self;
	},
	
	subscribe: function ( fn, ref ) {
		var self = this, handlerId;
		self.uid = self.uid || 0;
		handlerId = ref || ++self.uid;
		if ( self.handlers[handlerId] ) {
			return false;
		}
		self.handlers[handlerId] = fn;
		if ( !self.timerHandle ) {
			self.start();
		}
		return handlerId;
	},
	
	unSubscribe: function ( handlerId ) {
		var self = this;
		delete self.handlers[handlerId];
		if ( empty( self.handlers ) ) {
			self.stop();
		}
	}
});
	
})();

var Poll = J.Poll;

/**

Overlay

@description   
	Interface for managing screen overlays
	
@api
	new Overlay([ options ])
	
@examples
	new Overlay({ 
			style: {
				opacity: .2,
				background: '#fff'
			},
			
			// Enable tweening
			tween: true,
			
			// Optional show/hide event handlers
			onShow: function ( self ) {
				self.tween.start({
						opacity: .2,
						duration: 140
					});	
			},
			onHide: function ( self ) {
				self.tween.setOpacity( 0 );
			}
		});
		
*/

(function () {

if ( typeof __JELLY__ === 'undefined' ) { window['eval']( JELLY.unpack() ); }

var Class = defineClass( 'Overlay', {

		__init: function ( opts ) {
			opts = opts || {};
			var self = this,
				screen = self.screen = Class.screen.cloneNode( false );
			extend( self, opts );
			if ( opts.style ) {
				setStyle( screen, opts.style );
				delete self.style;
			} 
			if ( opts.tween ) {
				self.tween = new Tween( screen, ( isObject( opts.tween ) ? opts.tween : {} ) );
			} 
		},
		
		show: function () {
			var self = this;
			if ( !self.inserted ) {
				insert( self );
				defer( self.fire, self, 'show' );
			}
		},
		
		hide: function () {
			var self = this;
			if ( self.inserted ) {
				remove( self );
				defer( self.fire, self, 'hide' );
			}
		}
	}),
	
	// Hidden methods 
	
	insert = function ( self ) {
		if ( browser.ie6 ) {
			Class.shim.style.height =
			self.screen.style.height = 
				Math.max( docRoot.scrollHeight, getViewport()[1] ) + 'px';
			insertElement( Class.shim );
		}
		insertElement( self.screen );
		self.inserted = true;
	},
	
	remove = function ( self ) {
		if ( browser.ie6 ) {
			removeElement( Class.shim );
		}
		removeElement( self.screen );
		self.inserted = false;
	},

	// Automatically create and bind the default elements to the constructor
	
	shim = Class.shim = createElement( 'iframe#jy-overlay-shim frameBorder:0,scrolling:no' ),
	screen = Class.screen = createElement( '#jy-overlay' ),
	css = {
			position: browser.ie6 ? 'absolute' : 'fixed',
			top: 0,
			left: 0,
			width: '100%',
			height: '100%',
			background: '#fff',
			opacity: .5 
		};

// Set up default overlay styles

setStyle( screen, css );

css.opacity = 
css.background = 0;
setStyle( shim, css );
	
})();

/**

DragDrop

@description 

@api
	var dragger = new DragDrop( element [, options] );

@examples
	
	var dragger = new DragDrop( element, {} )
	
	dragger.onDrag = function () { ... }
	
	dragger.onDrop = function () { ... }
	

*/

(function () {

if ( typeof __JELLY__ === 'undefined' ) { window['eval']( JELLY.unpack() ); }

var Class = defineClass( 'DragDrop', {
		
		__init: function ( el, opts ) {
			opts = opts || {};
			var self = this,
				defaults = {
					el: getElement( el ),
					axis: 'xy',
					offset: { xMin: 0, xMax: 0, yMin: 0, yMax: 0 }					
				};
	
			extend( self, extend( defaults, opts ) );
			
			if ( opts.droppables ) {
				self.droppables = isString( opts.droppables ) ? Q( opts.droppables ) : opts.droppables;
			}
			self.container = getElement( opts.container );
			
			self.handle = getElement( opts.handle ) || self.el;
			self.bindEvts( self );
		},
		

		bindEvts: function ( self ) {
			
			var el = self.el,
				handle = self.handle,
				
				containerElement = self.container,
				droppableElements = self.droppables,
				
				data = {},
										
				mousedown = function ( e ) {
					
					e.preventDefault();
					setWindowBounds( self );
					
					var pageX = e.pageX,
						pageY = e.pageY,
						coords = getXY( el );
						
					switch ( self.axis ) {
						case 'x':
							self.moveX = 1;
							break;
						case 'y':
							self.moveY = 1;
							break;
						default:
							self.moveY = 
							self.moveX = 1;
					}
					
					var	element = {
							T: coords[1],
							R: coords[0] + el.offsetWidth,
							B: coords[1] + el.offsetHeight,
							L: coords[0],
							W: el.offsetWidth,
							H: el.offsetHeight,
							startX: parseInt( getStyle( el, 'left' ), 10 ) || 0,
							startY: parseInt( getStyle( el, 'top' ), 10 ) || 0
						},
						mouse = {
							startX: pageX,
							startY: pageY,
							bounds: windowBounds 
						};
						
					data = {
						handleX: pageX - coords[0],
						handleY: pageY - coords[1],
						element: element,
						mouse: mouse
					};
			
					if ( containerElement ) {
						var isWindow = containerElement === win,
							coords = isWindow ? windowBounds : getXY( containerElement ),
							containerTop = isWindow ? coords.T : coords[1],
							containerLeft = isWindow ? coords.L : coords[0],
							containerWidth = isWindow ? coords.R : containerElement.offsetWidth,
							containerHeight = isWindow ? coords.B : containerElement.offsetHeight,
							offset = self.offset,
						
							container = {
								T: containerTop,
								R: containerLeft + containerWidth,
								B: containerTop + containerHeight,		
								L: containerLeft,
								W: containerWidth,
								H: containerHeight
							},
							mouseBounds = {
								T: containerTop + offset.yMin,
								R: containerLeft + containerWidth - element.W + offset.xMax,
								B: containerTop + containerHeight - element.H + offset.yMax,
								L: containerLeft + offset.xMin
							},
							elementBounds = {
								T: element.startY + ( container.T - element.T ) + offset.yMin,
								R: element.startX + ( container.R - element.R ) + offset.xMax,
								B: element.startY + ( container.B - element.B ) + offset.yMax,
								L: element.startX + ( container.L - element.L ) + offset.xMin
							};
							data.container = container;
							data.mouse.bounds = mouseBounds;
							data.element.bounds = elementBounds;
					} 
					
					if ( droppableElements ) {
						data.dropZones = droppableElements.map( function ( el ) {
							var coords = getXY( el );
							return {
									el: el,
									T: coords[1],
									R: coords[0] + el.offsetWidth,
									B: coords[1] + el.offsetHeight,
									L: coords[0]
								};
						});
					}
					
					self.onDrag = self.onDrag || functionLit;
					
					if ( !docMouseMove ) { 
						docMouseMove = addEvent( doc, 'mousemove', mousemove );
					}
					if ( !docMouseUp ) { 
						docMouseUp = addEvent( doc, 'mouseup', mouseup );
					}
					self.fire( 'start', e, data );
				},
				
				mousemove = function ( e ) {
					
					e.preventDefault();
					
					if ( self.onDrag.call( self, e, data ) === false ) {
						return;
					}
					
					var	mouse = data.mouse,
						handle = data.handle,
						element = data.element,
						container = data.container,
						pageX = e.pageX,
						pageY = e.pageY,
						currentMouseLeft = pageX - data.handleX,
						currentMouseTop = pageY - data.handleY;

					if ( containerElement ) {
						if ( self.moveX ) { 
							if ( currentMouseLeft >= mouse.bounds.R ) {
								moveX( self, element.bounds.R );
							}
							else if ( currentMouseLeft <= mouse.bounds.L ) {
								moveX( self, element.bounds.L );
							}
							else {
								moveX( self, element.startX + ( pageX - mouse.startX ) );
							}
						}
						if ( self.moveY ) { 
							if ( currentMouseTop >= mouse.bounds.B ) {
								moveY( self, element.bounds.B );
							}
							else if ( currentMouseTop <= mouse.bounds.T ) {
								moveY( self, element.bounds.T );
							}
							else {
								moveY( self, element.startY + ( pageY - mouse.startY ) );
							}
						}
					}
					else {					
						if ( self.moveX ) { 
							moveX( self, element.startX + ( pageX - mouse.startX ) );
						}
						if ( self.moveY ) { 
							moveY( self, element.startY + ( pageY - mouse.startY ) );
						}
					}
					
					if ( droppableElements ) {
						var currentDropZoneEntered = null,
							dropZones = data.dropZones,
							n = dropZones.length,
							i = 0;
							
						for ( i; i < n; i++ ) {
							var droppable = dropZones[i],
								inZone = 
									pageX > droppable.L && 
									pageX < droppable.R && 
									pageY > droppable.T && 
									pageY < droppable.B;
							
							if ( inZone ) {
								currentDropZoneEntered = true;
								if ( droppable !== self.dropZone ) {
									self.fire( 'enter', e, droppable.el );
									if ( self.dropZone ) {
										self.fire( 'leave', e, self.dropZone.el );
									} 
									self.dropZone = droppable;
								}
								break;
							} 
						}
						if ( self.dropZone && !currentDropZoneEntered ) {
							self.fire( 'leave', e, self.dropZone.el );
							self.dropZone = null;
						}
					}

				},
				
				mouseup = function ( e ) {
					stopEvent( e );
					cancelDrag( self );
					self.fire( 'drop', e, data );
				},
				
				moveX = function ( self, value ) {
					self.el.style.left = value + 'px';
				},
				
				moveY = function ( self, value ) {
					self.el.style.top = value + 'px';
				};
			
			if ( !self.mouseDown ) {
				self.mouseDown = addEvent( handle, 'mousedown', mousedown );
			}
		},
		
		releaseEvts: function ( self ) {
			releaseDocEvts();
			removeEvent( self.mouseDown );
			return self;
		}
	
	}),
	
	releaseDocEvts = function () {
		removeEvent( docMouseMove );
		removeEvent( docMouseUp );
		docMouseMove = 
		docMouseUp = null;	
	},
	
	cancelDrag = function ( self ) {
		releaseDocEvts();
		self.fire( 'cancel' );
	},
	
	// Document event storage 
	docMouseMove,
	docMouseUp,
	
	windowBounds, 
	setWindowBounds = function ( self ) {
		var viewport = getViewport(),
			winScroll = getWindowScroll();
		windowBounds = {
			T: 0, 
			R: viewport[0], 
			B: viewport[1] + winScroll[1], 
			L: 0
		};
	};
	
})();
})(); // End outer closure