Openovate | Open Source Innovations
eve.full.js

Jump to

Source Code

Source Code

var eve = (function(settings) {
	/*	Class Definition
	--------------------------------*/
	var c = function() {
		/*	Constants
		--------------------------------*/
		this.CLASSNAME	= name;
		this.BROWSER	= null;
		
		/*	Public Properties
		--------------------------------*/
		this.settings = settings || {};//global settings
		
		/*	Private Properties
		--------------------------------*/
		var _uid = 0;	//uid counter
		var _data = {};	//data registry
		
		/*	Registry Methods
		--------------------------------*/
		this.getSpace = function() {
			//get the arguments and transform it to an array
			var args = this.args(arguments);
			
			//what object are we looking into?
			//if the first argument is not an object
			//use data registry
			
			//we do not want to use the type function 
			//in this case because we don't care what 
			//type of object it is
			var data = typeof args[0] == 'object' ? args.shift() : _data;
			
			if(args.length > 0)
			{		
				var result;
				//foreach argument
				this.parse(args, function(key, value, length) {
					//this is the last item
					if((key+1) == length)
					{
						//assign it to result
						//exit loop
						result = data[value];
						return false;
					}
					//the name does not exist
					if(!data[value])
					{
						//assign result to null
						//exit loop
						result = null;
						return false;
					}
					
					//walk the object
					data = data[value];
				});
				return result;
			}
			
			//if no arguments
			//return the whole object
			return data;
		};
		
		this.setSpace = function() {
			//get the arguments and transform it to an array
			var args = this.args(arguments);
			
			//what object are we looking into?
			//if the first argument is not an object
			//use data registry
			
			//we do not want to use the type function 
			//in this case because we don't care what 
			//type of object it is
			var data = typeof args[0] == 'object' ? args.shift() : _data;
			
			if(args.length > 1)
			{
				var last, index; 
				//foreach argument
				this.parse(args, this.bind(function(key, value, length) {
					//this is the last item
					if((key+1) == length)
					{	
						//set it to the value
						//exit loop
						last[index] = value;
						return false;
					}
					//the name does not exist
					if(!data[value])
					{
						//lets create the name
						data[value] = {};
						if(value == '[]')
						{// {test2:  [{test3:1209}]}
							last[index] = [];
							last[index].push({});
							value = 0;
							data = last[index];
						}
						
					}
					
					//walk the object
					last = data;
					index = value;
					data = data[value];
				}, this));
			}
			
			//allow chainability
			return this;
		};
		
		this.freeSpace = function() {
			//get the arguments and transform it to an array
			var args = this.args(arguments);
			
			//what object are we looking into?
			//if the first argument is not an object
			//use data registry
			
			//we do not want to use the type function 
			//in this case because we don't care what 
			//type of object it is
			var data = typeof args[0] == 'object' ? args.shift() : _data;
			
			if(args.length > 0)
			{
				//foreach argument
				this.parse(args, function(key, value, length) {
					//this is the last item
					if((key+1) == length)
					{
						//delete it
						//exit loop
						delete data[value];
						return false;
					}
					
					//walk the object
					data = data[value];
				});
			}
			
			//allow chainability
			return this;
		};
		
		this.isSpace = function() {
			//get the arguments and transform it to an array
			var args = this.args(arguments);
			
			//what object are we looking into?
			//if the first argument is not an object
			//use data registry
			
			//we do not want to use the type function 
			//in this case because we don't care what 
			//type of object it is
			var data = typeof args[0] == 'object' ? args.shift() : _data;
			
			if(args.length > 0)
			{
				//foreach argument
				return this.parse(args, function(key, value, length) {
					//the name does not exist
					if(!data[value])
					{
						//exit loop
						//false will be returned
						return false;
					}
					
					//walk the object
					data = data[value];
				}, {bool: true});
				
			}
			
			//if no arguments 
			//then return false
			return false;
		};
		
		/*	Misc Utility Methods
		--------------------------------*/
		this.args = function(args) {
			return Array.prototype.slice.call(args);
		};
		
		this.type = function( obj, has ) {
			has = typeof has == 'string' ? [has] : has;
			has = has instanceof Array ? has : [];
			
			var value = (function(obj) {
				var test, type = typeof obj;
			
				//is it a function?
				if( type == 'function' )
				{
					test = obj.toString();
					//is it a regex?
					if( ( /^\/.*\/$/ ).test(test))
					{
						return 'regexp';
					}
					
					if( ( /^\[object.*\]$/i ).test(test))
					{
						type = 'object';
					}
				}
				
				if(type != 'object')
				{
					return type;
				}
				
				//typical cases
				switch(obj)
				{
					case null:
						return 'null';
					case window:
						return 'window';
					case window.event:	
						return 'event';
					case document:
						return 'document';
				}
				
				//is it an eve class?
				if(obj.CLASSNAME)
				{
					return obj.CLASSNAME;
				}
				
				if( window.event && (event.type == obj.type))
				{
					return 'event';
				}
				
				if( obj.nodeType !== null )
				{	
					switch(obj.nodeType)
					{
						case 1:
							return 'element';
						case 3:
							return 'textnode';
					}
				}
				
				if( obj.constructor !== null )
				{
					switch( obj.constructor )
					{																	
						case Array:
							type = 'array';
							break;
						case Date:
							return 'date';
						case RegExp:
							return 'regexp';
						case Object:
							type = 'object';
							break;
						case ReferenceError:
							return 'error';
						default:
							test = obj.constructor.toString().match( /\s*function (.*)\(/ );
							if( test !== null )
							{
								return test[1];
							}
					}
				}
				
				if( obj.toString !== null )
				{
					test = obj.toString().match( /^\[object (.*)\]$/i );
					if(test !== null)	
					{
						switch(test[1].toLowerCase())
						{
							case 'window':
								return 'window';
							case 'event':
								return 'event';
							case 'math':
								return 'math';
							case 'error':	
								return 'error';
							case 'nodelist':
							case 'htmlcollection':
							case 'elementarray':
								return 'domcollection';
						}
					}
				}
				
				if(obj.callee != null)
				{
					return 'arguments';
				}
				
				return type;
			})(obj);
			
			if(has.length > 0)
			{
				return this.has(has, value);
			}
			return value;
		};
		
		this.uid = function() {
			return _uid ++;
		};
		
		/*	Constructor
		--------------------------------*/
		this.BROWSER = (function(version) {
			//Go from most to least used
			//IE all versions
			var test = /MSIE\x20[0-9]*/g.exec(navigator.userAgent);
			if(test)
			{
				test = test[0].replace('MS', '').split(' ');
				return version ? test : test[0];
			}
			
			//FF all versions
			test = /Firefox\/[0-9]*/g.exec(navigator.userAgent);
			if(test)
			{
				test = test[0].split('/');
				return version ? test : test[0];
			}
			
			//Chrome all versions
			test = /Chrome\/[0-9]*/g.exec(navigator.userAgent);
			if(test)
			{
				test = test[0].split('/');
				return version ? test : test[0];
			}
			
			//Safari all versions
			test = /[0-9]*\.[0-9]*\x20Safari/g.exec(navigator.userAgent);
			if(test)
			{
				test = test[0].split(' ').reverse();
				test[1] = /^[A-Z0-9]*/g.exec(test[1])[0];
				return version ? test : test[0];
			}
			
			//Opera all versions
			test = /^Opera\/[0-9]*/g.exec(navigator.userAgent);
			if(test)
			{
				test = test[0].split('/');
				return version ? test : test[0];
			}
			
			return version ? [navigator.userAgent, false] : navigator.userAgent;
		})(true);
	},
	
	//link new class to outer 
	//and inner global scope
	$ = new c();
	
	/*	Static Class Methods
	--------------------------------*/
	c.prototype = (function() {
		var p = c.prototype; //ofuscation
		
		p.def = function() {
			var name, args = $.args(arguments),
				namespace = this.type(args[0], 'object') ? args.shift() : p;
			
			while(this.type(args[0], 'string'))
			{
				name = args.shift();
				if(typeof namespace[name] == 'undefined')
				{
					namespace[name] = {};
				}
				if(typeof args[0] == 'string')
				{
					namespace = namespace[name];
				}
			}
			
			var parent = this.type(args[1], 'function') ? args[1].prototype : args[1] || {};
			
			//build the class
			var c = function() {};
			
			//build the module
			namespace[name] = function() {
				//	Constructor
				//--------------------------------//
				var args = $.args(arguments);
			
				//check for loader
				if(typeof c.prototype.__load == 'function')
				{
					return c.prototype.__load(c, args);
				}
				
				var instance = new c();
					
				//check for constructor
				if(typeof instance.__construct == 'function')
				{
					instance.__construct.apply(instance, args);
				}
				
				return instance;
			};
			
			//link module prototype and class prototype
			c.prototype = namespace[name].prototype;
			
			//add parent then the definition
			this.ext(c.prototype, parent, {__parent: parent}, args[0] || {});
			
			return this;
		};
		
		p.ext = function() {
			var i, j, args = $.args(arguments), dest = args.shift();	
			dest = typeof dest == 'function' ? dest.prototype : dest;
			
			for(i in args)
			{
				for(j in args[i])
				{
					dest[j] = args[i][j];
				}
			}
			
			return dest;
		};
		
		return p;
	})();
	
	/*	Static Object Methods
	--------------------------------*/
	c.prototype = (function() {
		var p = c.prototype; //ofuscation
		
		p.clone = function(obj) {
			var dest = {};
			if(obj instanceof Array)
			{
				dest = [];
				this.parse(obj, this.bind(function(key, value) {
					dest.push(value);
				}, this));
			}
			else if(typeof obj == 'object')
			{
				this.parse(obj, this.bind(function(key, value) {
					dest[key] = value;
				}, this));
			}
			else
			{
				dest = obj;
			}
			return dest;
		};
		
		p.destroy = function(obj) {
			switch(this.type(obj))
			{
				case 'object':
					//we do not want to use this.each in this case 
					//because we want to keep the original object reference
					for(var key in obj)
					{
						switch(this.type(obj[key]))
						{
							case 'array':
							case 'object':
								this.destroy(obj[key]);
								break;
						}
						delete obj[key];
					}
					break;
				case 'array':
					//we do not want to use this.each in this case 
					//because we want to keep the original object reference
					for(var i = 0, length = obj.length; i < length; i++)
					{
						switch(this.type(obj))
						{
							case 'array':
							case 'object':
								this.destroy(obj[i]);
								break;
						}
						
						delete obj[i];
					}
					break;
			}
			return null;
		};
		
		p.parse = p.each = function(obj, callback, options) {
			options = options || {};
			options.start 	= options.start || 0;
			options.range 	= options.range || 0;
			
			var key, i = 0, length = options.range ? options.start + options.range : this.size(obj);

			if(obj instanceof Array)
			{
				//do the simple array, string parser
				for(i = options.start; i < length; i++)
				{
					//if the callback returns false
					if(callback(i, obj[i], length) === false)
					{
						//exit the loop
						//return false
						return false;
					}
				}
			}
			else if(typeof obj == 'object')
			{
				//do the object parser
				for(key in obj)
				{
					if(i >= length)
					{
						break;
					}
					
					if(i < options.start)
					{
						i++;
						continue;
					}
					
					//if the callback returns false
					if(callback(key, obj[key]) === false)
					{
						//exit the loop
						//return false
						return false;
					}
					
					i++;
				}
			}
			
			return true;
		};
		
		p.has = function(obj, value) {
			switch(this.type(obj))
			{
				case 'object':
				case 'array':
					return !(this.parse(obj, function(k, v) {
						if(v == value)
						{
							return false;
						}
					}));
				case 'number':
					obj = obj.toString();
				case 'string':
					return obj.search(value) != -1;
				default:
					return obj == value;
			}
		};
		
		p.combine = function() {
			var	args = this.args(arguments), 
				dest = args.shift();
			switch(typeof dest)
			{
				case 'object':
					//foreach arguments
					this.parse(args, this.bind(function(i, arg) {
						//foreach obj
						this.parse(arg, function(key, value){
							if(dest instanceof Array)
							{
								dest.push(value);
							}
							else
							{
								dest[key] = value;
							}
						});
					}, this));
					break;
				case 'string':
					dest = dest.concat.apply(dest, args);
					break;
			}
			
			return dest;
		};
		
		p.rparse = p.reach = function(obj, callback, options) {
			switch(this.type(obj))
			{
				case 'string':
					//reverse order a string
					obj = this.split("").reverse().join("");
					break;
				default:
					var list = [], dest = {};
					//reverse store the keys
					this.parse(obj, function(key, value) {
						list.unshift(key);
					});
					
					//parse through the keys
					this.parse(list, function(key, value) {
						dest[value] = obj[value];
					});
					obj = dest;
					break;
			}
			
			return this.parse(obj, callback, options);
		};
		
		p.replace = function(obj, look, change) {
			switch(this.type(obj))
			{
				case 'array':
				case 'object':
					this.parse(obj, function(k,v){
						if(v == look)
						{
							obj[k] = change;
						}
					});
					break;
				case 'string':
					obj = obj.replace(new RegExp(look, 'g'), change);
					break;
				case 'number':
					obj = parseInt(obj.toString().replace(new RegExp(look, 'g'), change));
					break;
			}
			return obj;
		};
		
		p.size = function(obj) {
			switch(typeof obj)
			{
				case 'object':
					var i, length = 0;
					for(i in obj)
					{
						length ++;
					}
					return length;
				case 'number':
					obj = obj.toString();
				case 'array':
				case 'string':
					return obj.length;
			}
			
			return 0;
		};
		
		p.toJson = function(obj, options) {
			var json = [];
			switch(this.type(obj))
			{
				case 'object':
					this.parse(obj, this.bind(function(k, v) {
						var subJson = [k,v];
						this.parse(subJson, this.bind(function(k, v) {
							switch(this.type(v))
							{
								case 'string':
									subJson[k] = '"'+v+'"';
									break;
								case 'object':
									subJson[k] = this.toJson(v, options);
									break;
								case 'array':
									subJson[k] = this.toJson(v, options);
									break;
								case 'function':
									subJson[k] = v.toString();
									if(!options.func)
									{
										return null;
									}
									break;
								case 'undefined':
									subJson[k] = 'undefined';
									if(!options.undef)
									{
										return null;
									}
									break;
							}
						}, this));
						json.push(subJson[0]+':'+subJson[1]);
					}, this));
					return '{'+json.join(',')+'}';
				case 'array':
					this.parse(obj, this.bind(function(k, v) {
						switch(this.type(v))
						{
							case 'string':
								v = '"'+v+'"';
								break;
							case 'object':
								v = this.toJson(v, options);
								break;
							case 'array':
								v = this.toJson(v, options);
								break;
							case 'function':
								v = v.toString();
								if(!options.func)
								{
									return null;
								}
								break;
							case 'undefined':
								v = 'undefined';
								if(!options.undef)
								{
									return null;
								}
								break;
						}
						json.push(v);
					}, this));
					return '['+json.join(',')+']';
				case 'string':
					return '"'+obj+'"';
				case 'number':
				case 'bool':
					return obj;
				default:
					return null;
			}
		};
		
		p.toParam = function(obj, root) {
			var query = [];
			this.parse(obj, this.bind(function(key, value) {
				var subroot = root ? root+'['+escape(key)+']' : escape(key);
				key = root ? subroot : key;
				
				switch(this.type(value))
				{
					case 'string':
					case 'number':
						query.push(key+'='+escape(value));
						break;
					case 'array':
						query.push(this.toParam(value, subroot));
						break;
					case 'object':
						query.push(this.toParam(value, subroot));
						break;
				}
			},  this));
			return query.join('&');
		};
		
		return p;
	})();
	
	/*	Static Function Methods
	--------------------------------*/
	c.prototype = (function() {
		var p = c.prototype; //ofuscation
		
		p.bind = function() {
			//lets get the args for this.
			var args = this.args(arguments);
			//this is the function that bind attached to
			var __method = args.shift(), __scope = args.shift();
			
			if(typeof __method != 'function')
			{
				return null;
			}
			
			//now lets run this function
			return function() {
				//lets concat the arguments from the old one and the new one
				for(var i = 0, length = arguments.length, args2 = []; i < length; i++)
				{
					args2.push(arguments[i]);
				}
				return __method.apply(__scope, args2.concat(args));
			};
		};
		
		return p;
	})();
	
	/*	Static String Methods
	--------------------------------*/
	c.prototype = (function() {
		var p = c.prototype; //ofuscation
		
		p.camelize = function(string) {
			string = string.replace(/\-(\w)/g, function(all, letter) { 
				return letter.toUpperCase(); 
			}); 
			
			return string.replace(string.charAt(0), string.charAt(0).toLowerCase());
		};
		
		p.dasherize = function(string) {
			return string.replace(/[A-Z]/g, function(all, letter) { 
				if(letter === 0)
				{
					return all.toLowerCase();
				}
				return '-'+all.toLowerCase(); 
			}); 
		};
		
		p.entities = function(string) {
			return string.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g, '&quot;');
		};
		
		p.escape = function(string) {
			return escape(string);
		};
		
		p.search = function(string, substri) {
			return string.match(new RegExp(substri,'g'));
		};
		
		p.isJson = function(string) {
			return (/^[\],:{}\s]*$/).test(string.
				replace(/\\["\\\/bfnrtu]/g,'@').
				replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').
				replace(/(?:^|:|,)(?:\s*\[)+/g,''));
		};
		
		p.strip = function(string) {
			return string.replace(/<\/?[^>]+>/gi, '');
		};
		
		p.trim = function(string) {
			return string.replace(/^\s+|\s+$/g ,'');
		};
		
		p.template = function(string, obj) {
			this.parse(obj, this.bind(function(k, v) {
				string = this.replace(string, '#'+k, v);
			}, this));
			return string;
		};
		
		p.toElement = function(string, i) {
			string = this.createElement('div', {}, string).children();
			return typeof i == 'number' ? string.get(i) : string;
		};
		
		p.truncate = function(string, length, truncation) {
			if(string.length > length)
			{
				return string.slice(0, length) + truncation;
			}
			
			return string;
		};
		
		p.unentities = function(string) {
			return string.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&quot;/g,'"');
		};
		
		p.unescape = function(string) {
			return unescape(string);
		};
		
		p.unserialize = function(string, object) {
			if(this.isJson(string))
			{
				eval('string = '+this._native);
				return string;
			}
			
			object = object === false ? false : true;
			var unserial = {};
			this.parse(string.split('&'), this.bind(function(k, v) {
				var t = v.split('=');
				if(object)
				{
					if(t[0].indexOf('[') != -1)
					{
						var args = this.replace(this.replace(t[0], ']', '').split('['), '', '[]');
						args.unshift(unserial);
						args.push(t[1]);
						this.set.apply(this, args)	;
						return true;
				  	}
				}
				unserial[t[0]] = t[1];
				return true;
			}, this));
								   
			return unserial;
		};
		
		p.validAlphaNum = function(str) {
			return (/^([a-zA-Z0-9_-]+)$/).test(str);
		};
		
		p.validUrl = function(str) {
			return (/^(([\w]+:)?\/\/)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,4}(:[\d]+)?(\/([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)*(\?(&?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?$/).test(str);
		};
		
		p.validEmail = function(str) {
			return (/^((([a-z]|[0-9]|!|#|$|%|&|'|\*|\+|\-|\/|=|\?|\^|_|`|\{|\||\}|~)+(\.([a-z]|[0-9]|!|#|$|%|&|'|\*|\+|\-|\/|=|\?|\^|_|`|\{|\||\}|~)+)*)@((((([a-z]|[0-9])([a-z]|[0-9]|\-){0,61}([a-z]|[0-9])\.))*([a-z]|[0-9])([a-z]|[0-9]|\-){0,61}([a-z]|[0-9])\.)[\w]{2,4}|(((([0-9]){1,3}\.){3}([0-9]){1,3}))|(\[((([0-9]){1,3}\.){3}([0-9]){1,3})\])))$/).test(str);
		};
		
		return p;
	})();
			
	/*	Static Number Methods
	--------------------------------*/
	c.prototype = (function() {
		var p = c.prototype; //ofuscation
		
		p.square = function(num) {
			return num * num;
		};
		
		p.hypotenuse = function(side1, side2, round) {
			//pythagorean theorem
			var r =  Math.sqrt(this.square(side1) + this.square(side2));
			if(round)
			{
				r = Math.round(r);
			}
			return r;
		};
		
		//cos A = (a² - b² - c²)/2bc
		p.trigSSS = function(oppositeSide, side2, side3, round) {
			var r = 180/Math.PI * Math.acos((this.square(side2) + this.square(side3) - this.square(oppositeSide)) / (2 * side2 * side3));
			if(round)
			{
				r = Math.round(r);
			}
			return r;
		};
		//TODO: QA and fix
		p.trigSAS = function(side1, oppositeAngle, side2, round) {
			var r = Math.sqrt(this.square(side1) + this.square(side2) - 2 * side1 * side2 * Math.cos(oppositeAngle));
			if(round)
			{
				r = Math.round(r);
			}
			return r;
		};
		//TODO: QA and fix
		p.trigSSA = function(oppositeSide, sideMatch, angleMatch, round) {
			var r = 180/Math.PI * Math.asin((Math.sin(angleMatch) * oppositeSide) / sideMatch);
			if(round)
			{
				r = Math.round(r);
			}
			return r;
		};
		
		return p;
	})();
			
	/*	Static Element Methods
	--------------------------------*/
	c.prototype = (function() {
		var p = c.prototype; //ofuscation
		
		p.createElement = function(tag, attr, value, parent) {
			return this.Element(document.createElement(tag), attr, value, parent);
		};
		
		p.documentSize = function() {
			var windowSize = this.windowSize();
			var scrollHeight = (document.compatMode != 'CSS1Compat') ? document.body.scrollHeight : document.documentElement.scrollHeight;
			var scrollWidth = (document.compatMode != 'CSS1Compat') ? document.body.scrollWidth : document.documentElement.scrollWidth;
			return {width: Math.max(scrollWidth, windowSize.width), height: Math.max(scrollHeight,windowSize.height)};
		};

		p.select = function(selector, context, index) {
			if(typeof context == 'number')
			{
				index = context;
				context = document.body;
			}
			
			context = context || document.body;
			
			return this.Element(context).select(selector, index);
		};
		
		p.windowCenter = function() {
			var viewport = this.windowSize();
			return {left: Math.round(viewport.width / 2), top: Math.round(viewport.height / 2)};
		};
		
		p.windowOffset = function() {
			if(typeof(window.pageYOffset)=='number') 
			{
				return {left: window.pageXOffset, top: window.pageYOffset};
			}
			return {left: document.documentElement.scrollLeft, top: document.documentElement.scrollTop};
		};
		
		p.windowSize = function() {
			// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
			if (typeof window.innerWidth != 'undefined')
			{
				return {width: window.innerWidth, height: window.innerHeight};
			}
			 
			// IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
			if (typeof document.documentElement != 'undefined' && 
				typeof document.documentElement.clientWidth != 'undefined' && 
				document.documentElement.clientWidth !== 0)
			{
				return {width: document.documentElement.clientWidth, height: document.documentElement.clientHeight};
			}
			 
			 // older versions of IE
			return {width: document.getElementsByTagName('body')[0].clientWidth, height: document.getElementsByTagName('body')[0].clientHeight};
		};
		
		return p;
	})();
	
	/*	Static Event Methods
	--------------------------------*/
	c.prototype = (function() {
		var p = c.prototype; //ofuscation
		
		p.keyPressed = function(e) {
			if (e.keyCode) 
			{
				if(e.keyCode == 13)
				{
					return 'enter';
				}
				return String.fromCharCode(e.keyCode);
			}
			if (e.which) 
			{
				if(e.which == 13)
				{
					return 'enter';
				}
				return String.fromCharCode(e.which);
			}
			return null;
		};
		
		p.mousePosition = function(e) {
			if (e.pageX || e.pageY) 	
			{
				return {left: e.pageX, top: e.pageY};
			}
			return e.clientX || e.clientY ? {left: e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft, 
				top: e.clientY + document.body.scrollTop + document.documentElement.scrollTop} : {left:0, top:0};
		};
		
		p.rightClicked = function(e) {
			if(e.which) 
			{
				return e.which == 3;
			}
			
			return e.button ? e.button == 2 : false;
		};
		
		p.stop = function(e) {
			if(e.preventDefault)
			{
				e.preventDefault(); 
			}
			if(e.stopPropagation)
			{
				e.stopPropagation();
			}
			if(e.returnValue)
			{
				e.returnValue = false;
			}
			if(e.cancelBubble)
			{
				e.cancelBubble = true;
			}
			
			return this;
		};
		
		p.target = function(e) {
			var t = e.target? e.target : e.srcElement;
			return t.nodeType == 3 ? t.parentNode: t;
		};
		
		return p;
	})();
	
	/*	Static Request Methods
	--------------------------------*/
	c.prototype = (function() {
		var p = c.prototype; //ofuscation
		var _includes = {};
		
		p.include = function() {
			var args = this.args(arguments),
			url = args.shift();
			
			//if the url is already included
			if(_includes[url])
			{
				if(this.type(args[0], 'string'))
				{
					return this.include.apply(this, args);
				}
				args[0](_includes[url]);
				return this;
			}
			else
			{
				var id = this.uid();
				
				//lets include this
				if(url.indexOf('.css') != -1)
				{
					_includes[url] = this.createElement('link', {id: 'css_link'+id, type:'text/css', rel:'stylesheet', href:url});
					
					if(this.type(args[0], 'string'))
					{
						return this.include.apply(this, args);
					}
					args[0](_includes[url]);
				}
				else
				{
					
					_includes[url] = this.createElement('script', {id: 'js_script'+id, type:'text/javascript', src:url});
					
					_includes[url].on('readystatechange', this.bind(function (e) {
						if (_includes[url].el.readyState == 'complete' || _includes[url].el.readyState == 'loaded' )
						{
							if(this.type(args[0], 'string'))
							{
								return this.include.apply(this, args);
							}
							args[0](_includes[url]);
						}
					}, this));
					
					_includes[url].on('load', this.bind(function (e) {
						if(this.type(args[0], 'string'))
						{
							return this.include.apply(this, args);
						}
						args[0](_includes[url]);
					}, this));
				}
				this.Element(document).select('head', 0).append(_includes[url]);
			}
			
			return this;
		};
		
		p.proxy = function(url, options) {
			//set defaults for options
			options 			= options || {};
			options.data 		= options.data || '';
			options.form		= options.form || false;
			options.method 		= options.method || 'get';
			options.success 	= options.success || function(callback){};
			options.failiure 	= options.failiure || function(callback){};
			options.timeout		= options.timeout || 0;	
			
			var id = this.uid();
			
			//the constructor
			
			//In IE, using the DOM to create an iframe
			//does not properly register in windows.frames
			//To make it work in IE we simply create a wrapper
			//and set the innerHTML to a string version of the 
			//iframe, select that iframe and append it
			
			//create a wrapper for the frame
			//set the innerHTML of that wrapper to a string version of the iframe
			//Opera needs the SRC to be a real HTML page.
			var frame = this.toElement('<iframe id="frame_proxy'+id+'" name="frame_proxy'+id+'" src="//:" style="display:none;"></iframe>', 0);
			//appending will happen later on
			
			//we build a form to facilitate 
			//POST and GET methods to the server
			
			var form = true, input, key;
			
			//if we do not have a form element already
			if(options.form === false)
			{
				//this flag is an indicator of whether a form
				//was passed in the options
				//this will be used when determining 
				//whether to remove a form or not
				form = false;
				//build a form using native JS
				options.form = this.createElement('form', {method: options.method, action: url});
				//now add the params as hidden fields in the form
				var data = this.unserialize(options.data);
				for(key in data)
				{
					input = this.createElement('input', {type: 'hidden', name: key, value: data[key]});
					options.form.append(input);
				}
			}
			else
			{
				options.form = $.Element(options.form);
			}
			
			//Either way lets set the target of the form to the iframe
			options.form.attr('target', 'frame_proxy'+id);
			
			//set a timeout
			if(options.timeout)
			{
				var timeout = setTimeout(function() {
					//remove the frame
					setTimeout(function() {
						frame.remove();
					}, 1);
					options.failiure(new Error('Server took to long to respond.'));
				}, options.timeout);
			}
			
			//start listening to the iframe
			//frame.onload will run when the page is loaded even if you 
			//trigger a new page to it when it's part of the document
			//frame.onload is supported by FF2, FF3, Safari, IE6, IE7, Opera, Chrome
			frame.on('load', function(e) {
				//clear the timeout
				clearTimeout(timeout);
				
				//get the contents of the iframe
				//and run callback
				try
				{
					options.success(frame.attr('contentWindow').document.body.innerHTML);
				}
				catch(err)
				{
					options.failiure(new Error('Remote calls not supported.'));
				}
				
				//if there was not a form 
				//passed into options.form
				if(!form)
				{
					//remove the form from body
					options.form.remove();
				}
				
				//frame.parentNode.removeChild(frame);
				//causes FF2+3 to not complete loading
				//I do not have a work around for this
				//remove the iframe from body
				setTimeout(function() {
					frame.remove();
				}, 1);
			});
			
			if(frame.parent().el)
			{
				frame.remove();
			}
			
			//we need to append the form as well for IE or form.submit() wont work
			//append frame and form to the body in that order
			this.Element(document.body).append(frame);
			
			if(!form)
			{
				this.Element(document.body).append(options.form);
			}
			
			//run form submit
			options.form.el.submit();
			//form.submit will trigger frame.onload() 
			//because we set the target of the form to the iframe
		};
		
		return p;
	})();
	
	/*	Base Class
	--------------------------------*/
	$.def('Base', new function() {
		/*	Constants
		--------------------------------*/
		this.CLASSNAME = 'Base';
		
		/*	Public Methods
		--------------------------------*/
		this.on = function(type, callback, ref) {
			var uid = $.uid();
				
			//is this event set?
			if(!$.isSpace(this, '__events', type))
			{
				$.setSpace(this, '__events', type, {});
			}
			
			//using bind there is no way to track by unique functions
			//because bind will return the same overrided function
			$.setSpace(this, '__events', type, uid, callback);
			//lets make a function that will read the callbacks 
			//ironically using bind to make this happen
			if(!$.isSpace(this, '__triggers', type))
			{ 
				$.setSpace(this, '__triggers', type, $.bind(function(){ 
					var args = $.args(arguments), i, result = true;
					
					//for each callback
					//I did not want to use $.each 
					//because returning false would end the loop
					//I wanted to parse through all the events
					//then if one of them were false return false
					//returning false used in events are used for triggers
					var events = $.getSpace(this, '__events', type);
					for(i in events)
					{
						if($.type(events[i]) == 'function' && events[i].apply(this, args) === false)
						{
							result = false;
						}
					}
					return result;
				}, this));
				
			}
			
			var off = $.bind(this.unbind, this, type, uid);
			return { id: uid, type: type, unbind: off, off: off };
		};
		
		this.trigger = function(type) {
			var args = $.args(arguments); 
			type = args.shift();
			
			if($.isSpace(this, '__triggers', type))
			{
				if($.getSpace(this, '__triggers', type).apply(this, args) === false)
				{
					return false;
				}
			}
			return this;
		};
		
		this.off = function(evt) {
			var args = $.args(arguments), callbacks = {}, type = $.type(evt);
			//if there are two arguments and the first is a string and the second is a function
			switch(true)
			{
				case args.length == 2 && $.type(args[0]) == 'string':
					var newCallBacks = {};
					callbacks = $.getSpace(this, '__events', evt);
					//for each event callback
					$.parse(callbacks, function(key, callback) {
						//if this callback does not equals 
						//what the user wanted to unlisten to
						if($.type(args[1]) == 'function' && callback != args[1])
						{
							newCallBacks[key] = callback;
						}
						else if(key != args[1])
						{
							newCallBacks[key] = callback;
						}
						
					});
					$.setSpace(this, '__events', evt, newCallBacks);
					break;
				case type == 'string':
					$.setSpace(this, '__events', evt, callbacks);
					break;
				case type == 'number':
					$.parse($.getSpace(this, '__events'), function(type, callbacks) {
						callbacks = {};
						//for each event callback
						$.parse(callbacks, function(key, callback) {
							if(key != evt)
							{
								callbacks[key] = callback;
							}
						});
						
						$.setSpace(this, '__events', type, callbacks);
					});
					break;
			}
			
			return this;
		};
	});
	
	/*	Element Class
	--------------------------------*/
	$.def('Element', new function() {
		/* Constants
		-------------------------------------*/
		this.CLASSNAME = 'Element';
		
		/* Loader
		--------------------------------*/
		this.__load = function(instance, args) {
			var el = null;
			
			switch($.type(args[0]))
			{
				case 'string':
					args[0] = document.getElementById(args[0]);
					if(!args[0])
					{
						return null;
					}
				case 'element':
				case 'window':
				case 'document':
					el = new instance();
					el.__construct(args[0]);
					break;
				case this.CLASSNAME:
					el = args[0];
					break;	
				default:
					return null;
			}
			
			if(args[1])
			{
				el.attr(args[1]);
			}
			if(args[2])
			{
				el.html(args[2]);
			}
			if(args[3])
			{
				$.Element(args[3]).append(el);
			}
			
			//return instance
			return el;
		};
		
		/* Constructor
		--------------------------------*/
		this.__construct = function(el) {
			this.el = this.register(el);
		};
		
		/*	Registry Methods
		--------------------------------*/
		this.getSpace = function() {
			var args = $.args(arguments);
			args.unshift(cache[this.uid()]);
			return $.getSpace.apply($, args);
		};
		
		this.setSpace = function() {
			var args = $.args(arguments);
			args.unshift(cache[this.uid()]);
			$.setSpace.apply($, args);
			return this;
		};
		
		this.freeSpace = function() {
			var args = $.args(arguments);
			args.unshift(cache[this.uid()]);
			$.freeSpace.apply($, args);
			return this;
		};
		
		this.isSpace = function() {
			var args = $.args(arguments);
			args.unshift(cache[this.uid()]);
			return $.isSpace.apply($, args);
		};
		
		/* Set/Get Combination Methods
		--------------------------------*/
		this.attr = function() {
			var args = $.args(arguments);
			if(args.length == 2)
			{
				var obj = {};
				obj[args[0]] = args[1];
				args[0] = obj;
			}
			
			if($.type(args[0], 'object'))
			{
				$.parse(args[0], $.bind(function _attr_parse(key, value){
					switch(key)
					{
						case 'class':
						case 'className':
							this.el.className = value;
							break;
						case 'html':
						case 'innerHTML':
							this.el.innerHTML = value;
							break;
						default:
							this.el[key] = value;
							break;
					}
				}, this));
				return this;
			}
			
			switch(args[0])
			{
				case 'class':
				case 'className':
					return this.el.className;
				case 'tag':
				case 'tagName':
					return this.el.tagName;
				case 'html':
				case 'innerHTML':
					return this.el.innerHTML;
				default:
					return this.el[args[0]];
			}
		};
		
		this.css = function() {
			var args = $.args(arguments);
			if(args.length == 2)
			{
				var obj = {};
				obj[args[0]] = args[1];
				args[0] = obj;
			}
			
			if($.type(args[0], 'object'))
			{
				$.parse(args[0], $.bind(function(key, value){
					try
					{
						this.el.style[key] = value;
					} catch(e) {}
				}, this));
				return this;
			}
			
			var r = null;
			
			if (this.el.currentStyle)
			{
				//this needs to be Camelized
				r = this.el.currentStyle[$.camelize(args[0])];
			}
			else if (window.getComputedStyle)
			{
				//this needs to be dasherized
				r = document.defaultView.getComputedStyle(this.el,null).getPropertyValue($.dasherize(args[0]));
			}
			
			return !!(r) ? r : this.el.style[args[0]];
		};
		
		this.tag = function() {
			var tag = this.attr('tag') || '';
			return tag.toLowerCase();
		};
		
		this.html = function() {
			if(arguments.length == 1)
			{
				return this.attr('html', arguments[0]);
			}
			return this.attr('html');
		};
		
		this.opacity = function(value) {
			if(!$.type(value, ['number']))
			{
				if($.BROWSER[0] == 'IE')
				{
					return $.replace($.replace(this.css('filter'),  'alpha(opacity=', ''), ')', '');
				}
				return this.css('opacity');
			}
			
			this.css('opacity', value/10);
			this.css('filter', 'alpha(opacity=' + value*10 + ')');
			
			return this;
		};

		/*	Class Methods
		--------------------------------*/
		this.addClass = function(name) {
			if(!this.hasClass(name))
			{
				this.el.className += ' ' + name;
			}
			return this;
		};
		
		this.removeClass = function(name) {
			if(this.hasClass(name))
			{
				this.el.className = this.el.className.replace(' ' + name, '');
				this.el.className = this.el.className.replace(name + ' ', '');
				if(this.el.className == name)
				{
					this.el.className = '';
				}
			}
			return this;
		};
		
		this.hasClass = function hasClass(name) {
			return $.has(this.el.className.split(' '), name);
		};
		
		/*	Dimension Methods
		--------------------------------*/
		this.trueCoordinates = function() {
			var position 	= this.truePosition(), 
				size 		= this.trueSize();
			
			return [{left: position.left, top: position.top}, 
					{top:position.top+size.height, left:position.left+size.width}];
		};
		
		this.truePosition = function() {
			var size 		= this.trueSize(),
				windowSize	= $.windowSize(),
				curleft 	= 0, 
				curtop 		= 0, 
				el 			= this.el;
			
			do {
				curleft += el.offsetLeft;
				curtop += el.offsetTop;
			} while (el = el.offsetParent);
			
			return {
				left: curleft, top: curtop,
				right: windowSize.width-(curleft+size.width),
				bottom: windowSize.height-(curtop+size.height)};
		};
		
		this.trueSize = function() {
			var display = this.css('display'), originalWidth, originalHeight;
			if (display != 'none' && display !== null) // Safari bug
			{
				originalHeight = this.css('offsetHeight');
				originalWidth = this.css('offsetWidth');
				if(originalWidth && originalHeight)
				{
					return {width: originalWidth, height: originalHeight};
				}
				
				return {width: this.attr('clientWidth'), height: this.attr('clientHeight')};
			}
			
			// All *Width *Height properties give 0 on elements with display none,
			// so enable the element temporarily
			var originalVisibility = this.css('visibility');
			var originalPosition = this.css('position');
			var originalDisplay = this.css('display');
			
			this.css('visibility', 'visible');
			this.css('position', 'absolute');
			this.css('display', 'block');
			
			originalHeight = this.attr('clientHeight');
			originalWidth = this.attr('clientWidth');
			
			this.css('visibility', originalVisibility);
			this.css('position', originalPosition);
			this.css('display', originalDisplay);
			
			return  {width: originalWidth, height: originalHeight};
		};
		
		/*	Node Action Methods
		--------------------------------*/
		this.clone = function(children, events) {
			children = $.type(children, 'boolean') ? children : true;
			events = $.type(events, 'boolean') ? events : true;
			
			//effectively clean up this node
			var container = document.createElement('div');
			container.appendChild(this.el.cloneNode(true));
			
			var clone = $.toElement(container.innerHTML, 0);
			
			//no parents
			clone.el.parentNode.removeChild(clone.el);
			//no children
			clone.children().parse(function(k, el) {
				el.remove();
			});
			
			//add back cloned registry items
			cache[clone.uid()] = $.clone(this.getSpace());
			
			if(children)
			{
				//do the same for children
				this.children().parse(function _clone_children_recursive(k, el) {
					el = el.clone(children);
					clone.append(el);
				});
			}
			
			//remove event callbacks
			clone.freeSpace('__events');
			
			if(events)
			{
				//properly add back the events
				$.parse(this.getSpace('__events'), function(type, callbacks) {
					$.parse(callbacks, function(id, callback) {
						clone.on(type, callback);
					});
				});	
			}
			
			return clone;
		};
		
		this.remove = function() {
			if(this.el.parentNode)
			{
				this.el.parentNode.removeChild(this.el);
			}
			return this;
		};
		
		this.replace = function(el) {
			el = $.Element(el);
			if(this.el.parentNode)
			{
				this.el.parentNode.replaceChild(el.el, this.el);
			}
			return node;
		};
		
		this.prepend = function(el) {
			el = $.Element(el);
			if(this.el.childNodes.length > 0)
			{
				this.el.insertBefore(el.el, this.el.childNodes[0]);
				return this;
			}
			return this.append(el);
		};
		
		this.append = function(el) {
			el = $.Element(el);
			this.el.appendChild(el.el);
			return this
		};
		
		this.before = function(el) {
			el = $.Element(el);
			this.el.parentNode.insertBefore(el.el, this.el);
			return this;
		};
		
		this.after = function(el) {
			var next = this.next();
			if(next)
			{
				el = $.Element(el);
				this.el.parentNode.insertBefore(el.el, next.el);
			}
			else
			{
				$.Element(this.el.parentNode).append(el);
			}
			return this;
		};
		
		/* Traversal Methods
		-------------------------------------*/
		this.previous = function() {
			var prev = this.el;
			while(prev = prev.previousSibling)
			{
				if(prev.nodeType != 3)
				{
					return $.Element(prev);
				}
			}
			return null;
		};

		this.next = function() {
			var next = this.el;
			while(next = next.nextSibling)
			{
				if(next.nodeType != 3)
				{
					return $.Element(next);
				}
			}
			return null;
		};
		
		this.parent = function() {
			if(this.el.parentNode)
			{
				return $.Element(this.el.parentNode);
			}
			return false;
		};
		
		this.select = function(selectors, index) {
			selectors = selectors.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
			if(selectors.length == 0)
			{
				return $.Collection([this]);
			}
			
			selectors = selectors.split(' ');
			
			var selector = selectors.shift(), results = {}, select = [];
			
			//div#id.class[type=field1].class[type=field1],div#id.class[type=field1].class[type=field1]
			selector = $.replace(selector, ']', ''); 
			selector = $.replace('!'+selector, ',', ',!');
			//!div#id.class[type=field1.class[type=field1,!div#id.class[type=field1.class[type=field1
			var subselects = selector.split(',');
			//!div#id.class[type=field1.class[type=field1
			$.parse(subselects, $.bind(function(k, v) {
				var t = v.match(/!([a-zA-Z0-9])+/g);
				var i = v.match(/#([a-zA-Z0-9_-])+/g);
				var c = v.match(/\.([a-zA-Z0-9_-])+/g);
				var a = v.match(/\[([a-zA-Z0-9_-])+=([a-zA-Z0-9])+/g);
											
				t = t && t.length > 0 ? t[0].replace('!', '') : '*';
				i = i && i.length > 0 ? i[0].replace('#', '') : null;
				
				var r = i ? [document.getElementById(i)] : _collection(this.el.getElementsByTagName(t));

				$.parse(r, function(k, el) {
					var el = $.Element(el);
					//test for id
					var match1 = !i || el.attr('id') == i;
					
					//test for tag
					var match2 = t == '*' || el.tag() == t.toLowerCase();
					//test for classes
					var match3 = $.parse(c, function(k, c) {
						if(typeof c == 'string')
						{
							c = c.replace('.', '');
							return el.hasClass(c);
						}
					});
					//test for attributes
					var match4 = $.parse(a, function(k, a) {
						a = a.replace('[', '').split('=');
													 
						return el.tag(a[0]) == a[1] || el.tag(a[0]) === true;
					});
					
					if(match1 && match2 && match3 && match4)
					{
						el.select(selectors.join(' ')).parse(function(k, el) {
							results[el.el[expando]] = el;
						});
					}
				});
			}, this));
			
			//transform into an array
			$.parse(results, function(k, node) {
				select.push(node);
			});
				
			return $.type(index, 'number') ? select[index] : $.Collection(select);
		};
		
		this.parents = function(selector, index) {
			selector = selector || '*';
			selector = selector.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
			
			var select = [];
			
			//div#id.class[type=field1].class[type=field1],div#id.class[type=field1].class[type=field1]
			selector = $.replace(selector, ']', ''); 
			selector = $.replace('!'+selector, ',', ',!');
			//!div#id.class[type=field1.class[type=field1,!div#id.class[type=field1.class[type=field1
			var subselects = selector.split(',');
			//!div#id.class[type=field1.class[type=field1
			$.parse(subselects, $.bind(function(k, v) {
				var t = v.match(/!([a-zA-Z0-9])+/g);
				var i = v.match(/#([a-zA-Z0-9_-])+/g);
				var c = v.match(/\.([a-zA-Z0-9_-])+/g);
				var a = v.match(/\[([a-zA-Z0-9_-])+=([a-zA-Z0-9])+/g);
													
				t = t && t.length > 0 ? t[0].replace('!', '') : '*';
				i = i && i.length > 0 ? i[0].replace('#', '') : null;
				
				var element = $.Element(this.el.parentNode);
					
				while(element.tag() != 'html')
				{
					//test for tag
					var match1 = t == '*' || element.tag() == t;
					
					//test for id
					var match2 = !i || element.attr('id') == i;
					
					//test for classes
					var match3 = $.parse(c, function(k, c) {
						if(typeof c == 'string')
						{
							c = c.replace('.', '');
							return el.hasClass(c);
						}
					});
					
					//test for attributes
					var match4 = $.parse(a, function(k, a) {
						a = a.replace('[', '').split('=');
						return element.attr(a[0]) == a[1] || element.attr(a[0]) === true;
					});
					
					if(match1 && match2 && match3 && match4)
					{
						select.push(element);
					}
					
					element = element.parent();
				}
			}, this));
				
			return $.type(index, 'number') ? select[index] : $.Collection(select);
		};
		
		this.children = function(selector, index) {
			selector = selector || '*';
			selector = selector.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
			
			var select = [];
			//div#id.class[type=field1].class[type=field1],div#id.class[type=field1].class[type=field1]
			selector = $.replace(selector, ']', ''); 
			selector = $.replace('!'+selector, ',', ',!');
			//!div#id.class[type=field1.class[type=field1,!div#id.class[type=field1.class[type=field1
			var subselects = selector.split(',');
			//!div#id.class[type=field1.class[type=field1
			$.parse(subselects, $.bind(function(k, v) {
				var t = v.match(/!([a-zA-Z0-9])+/g);
				var i = v.match(/#([a-zA-Z0-9_-])+/g);
				var c = v.match(/\.([a-zA-Z0-9_-])+/g);
				var a = v.match(/\[([a-zA-Z0-9_-])+=([a-zA-Z0-9])+/g);
													
				t = t && t.length > 0 ? t[0].replace('!', '') : '*';
				i = i && i.length > 0 ? i[0].replace('#', '') : null;
				
				var r = _collection(this.el.childNodes);
				$.parse(r, function(k, el) {
					var element = $.Element(el);
					
					//test for tag
					var match1 = t == '*' || element.tag() == t;
					
					//test for id
					var match2 = !i || element.attr('id') == i;
					
					//test for classes
					var match3 = $.parse(c, function(k, c) {
						if(typeof c == 'string')
						{
							c = c.replace('.', '');
							return el.hasClass(c);
						}
					});
					
					//test for attributes
					var match4 = $.parse(a, function(k, a) {
						a = a.replace('[', '').split('=');
						return element.attr(a[0]) == a[1] || element.attr(a[0]) === true;
					});
						
					if(match1 && match2 && match3 && match4)
					{
						select.push(element);
					}
				});
			}, this));
				
			return $.type(index, 'number') ? select[index] : $.Collection(select);
		};
		
		/* Event Methods
		-------------------------------------*/
		this.on = function(type, callback) {
			var uid = $.uid();
			
			//is this event set?
			if(!this.isSpace('__events', type))
			{
				this.setSpace('__fires', type, $.bind(this.fire, this, type));
				if( this.el.attachEvent ) 
				{
					this.el.attachEvent('on'+type, this.getSpace('__fires', type));
				}
				else if(this.el.addEventListener)
				{
					this.el.addEventListener( type, this.getSpace('__fires', type), false );
				}
				else
				{
					this.el['on'+type] = this.getSpace('__fires', type);
				}
				
				var tag = this.tag();
				
				//Special Case for onload 
				//it will only case for the first append
				//this will enable onload capabilities for elements
				if(type == 'load' && tag && tag != 'body' &&
				   tag.indexOf('frame') == -1 &&  tag != 'img' && 
				   tag != 'link' && tag != 'script') {
					//find the parent
					var parent = this.el;
					while(parent.parentNode !== null && parent.parentNode.tagName && parent.parentNode.tagName != 'BODY')
					{
						parent = parent.parentNode;
					}
					if(parent.parentNode == null || !parent.parentNode.tagName)
					{
						var interval = setInterval(function() {
							if(parent.parentNode !== null && parent.parentNode.tagName)
							{
								while(parent.parentNode !== null && parent.parentNode.tagName && parent.parentNode.tagName != 'BODY')
								{
									parent = parent.parentNode;
								}
								
								if(parent.parentNode !== null && parent.parentNode.tagName && parent.parentNode.tagName == 'BODY')
								{
									clearInterval(interval);
									node.trigger('load', el);
								}
							}
						}, 10);
						setTimeout(function() {
							clearInterval(interval);
						}, 5000);
					}
				}
			}
			
			//using bind there is no way to track by unique functions
			//because bind will return the same overrided function
			this.setSpace('__events', type, uid, callback);
			return { id: uid, type: type, off: $.bind(this.off, this, type, uid) };
		};
		
		this.trigger = function(type) {
			var args = $.args(arguments);
			if(document.dispatchEvent)
			{
				var e = false, events = {
					MouseEvents: 	['click', 'mousedown', 'mousemove', 
									 'mouseout', 'mouseover', 'mouseup'],
					HTMLEvents: 	['abort', 'blur', 'change', 'error', 
									 'focus', 'load', 'reset', 'resize', 
									 'scroll', 'select', 'submit', 'unload'],
					UIEvents: 		['keydown', 'keypress', 'keyup']
				};
				
				$.parse(events, function(key, types) {
					if($.has(types, type))
					{
						e = document.createEvent(key);
						return false;
					}
				});
				
				if(e)
				{
					e.initEvent(type, true, true); 
					this.el.dispatchEvent(e);
					return this;
				}
			}
			else if(document.fireEvent)
			{
				var fired = true;
				try {
					fired = this.el.fireEvent('on'+type)
				} catch(e) {
					fired = false;
				}
				if(fired)
				{
					return this;
				}
			}
			
			args.unshift($.event || {});
			args[0].target = args[0].currentTarget = this.el;
			
			return this.fire.apply(this, args);
		};
		
		this.fire = function() {
			var args = $.args(arguments), e = args.shift(), type = args.shift();
			lastEvent = e;
			args.unshift(e);
			
			//for each callback
			//I did not want to use $.each 
			//because returning false would end the loop
			//I wanted to parse through all the events
			//then if one of them were false return false
			//returning false used in events are used for triggers
			if(this.isSpace('__events', type))
			{
				var callbacks = this.getSpace('__events', type);
				
				for(i in callbacks)
				{
					if($.type(callbacks[i]) == 'function')
					{
						callbacks[i].apply(callbacks[i], args);
					}
				}
			}
			
			return this;
		};
		
		this.off = function(evt) {
			var args = $.args(arguments), callbacks = {};
			//if there are two arguments and the first is a string and the second is a function
			if(args.length == 2 && $.type(args[0], 'string'))
			{
				var newCallBacks = {};
				var callbacks = this.getSpace('__events', evt);
				//for each event callback
				$.parse(callbacks, function(key, callback) {
					//if this callback does not equals 
					//what the user wanted to unlisten to
					if($.type(args[1]) == 'function' && value != args[1])
					{
						newCallBacks[key] = callback;
					}
					else if(key != args[1])
					{
						newCallBacks[key] = callback;
					}
				});
				this.setSpace('__events', evt, newCallBacks);
			}
			
			//if a type was just given
			else if($.type(evt) == 'string')
			{
				this.setSpace('__events', evt, callbacks);
			}
			
			//if a type was just given
			else if($.type(evt) == 'number')
			{
				$.parse(this.getSpace('__events'), function(type, callbacks) {
					callbacks = {};
					//for each event callback
					$.parse(callbacks, function _off_callbacks(key, callback) {
						if(key != evt)
						{
							callbacks[key] = callback;
						}
					});
					
					this.setSpace('__events', type, callbacks);
				});
			}
			
			if(this.isSpace('__events', evt) && $.size(this.getSpace('__events', evt)) == 0)
			{
				if (this.el.removeEventListener)
				{
					this.el.removeEventListener(evt, this.getSpace('__fires', evt), false );
				}
				else if (this.el.detachEvent)
				{
					this.el.detachEvent("on"+evt, this.getSpace('__fires', evt));
				}

				this.freeSpace('__fires', evt)
				this.freeSpace('__events', evt);
			}
			return this;
		};
		
		/* Misc Methods
		-------------------------------------*/
		this.childOf = function(el) {
			el = $.Element(el);
			var parent = this.parent();
			while(parent)
			{
				if(parent.uid() == el.uid()) {
					return true;
				}
				parent = parent.parent();
			}
			return false;
		};
		
		this.parentOf = function(el) {
			return $.Element(el).childOf(this.el);
		}
		
		this.tween = function(styles, duration) {
			var args = $.args(arguments);
			
			if(!$.type(args[0], 'object'))
			{
				return this;
			}
			
			styles = args.shift();
			duration = typeof args[0] == 'number' ? args.shift() : 30;
			callback = typeof args[0] == 'function' ? args.shift() : false;
			
			$.parse(styles, $.bind(function(key, value) {
				this.css(key, value[0]);
			}, this));

			
			var step = 1, interval = setInterval($.bind(function(uid, styles, duration, callback, args) {
				if($.BROWSER[0] != 'Firefox')
				{
					var args2 = $.args(arguments);
					styles 		= args2[0];
					duration 	= args2[1];
					callback 	= args2[2];
					args 		= args2[3];
				}
				$.parse(styles, $.bind(function(key, value) {
					
					//find the distance between start and end
					var m, increment = (parseFloat(value[1]) - parseFloat(value[0])) / duration;
					
					switch(true)
					{
						case value[0].indexOf('px') != -1:
							m = 'px';
							break;
						case value[0].indexOf('em') != -1:
							m = 'em';
							break;
						default:
							m = '';
							break;
					}
					
					//case for opacity expected 1-10
					if(key == 'opacity')
					{
						this.opacity(parseFloat(value[0]) + (increment * step));
					}
					else
					{
						this.css(key, (parseFloat(value[0]) + (increment * step))+m );
					}
				}, this));
				
				if(step >= duration)
				{
					clearInterval(interval);
					if(typeof callback == 'function')
					{
						callback(this, styles, duration);
					}
					if(args && args.length > 0)
					{
						this.tween.apply(this, args);
					}
				}
				step ++;
			}, this, styles, duration, callback, args), 1);
			
			return this;
		};
		
		this.serialize = function(string) {
			string = string === false ? false : true;
			
			var serial = {};
			
			this.select('input').parse(function(key, el) {
				el = $.Element(el);
				if(el.attr('type') == 'radio')
				{
					if(el.attr('checked'))
					{
						serial[el.attr('name')] = el.attr('value');
					}
				}
				else if(el.attr('type') == 'checkbox')
				{
					if(el.attr('checked'))
					{
						serial[el.attr('name')] = el.attr('value') || 1;
					}
					else
					{
						serial[el.attr('name')] = 0;
					}
				}
				else
				{
					serial[el.attr('name')] = el.attr('value');
				}
			});
			
			this.select('textarea').parse(function(key, el) {
				el = $.Element(el);
				serial[el.attr('name')] = el.html();
			});
			
			this.select('select').parse(function(key, el) {
				el = $.Element(el);
				el.select('option').parse(function(k, v) {
					v = $.Element(v);
					if(v.attr('selected'))
					{
						serial[el.attr('name')] = v.attr('value');
					}
				});
			});
			
			if(string)
			{
				var newSerial = [];
				$.parse(serial, function(key, value){
					newSerial.push(key+'='+value);
				});
				return newSerial.join('&');
			}
			return serial;
		};
		
		this.register = function(el) {
			//make an id and cache
			//for this element
			var id = el[expando];
			
			if(!$.type(id, 'number')) 
			{
				cache.push({});
				el[expando] = cache.length - 1;
			}
			
			return el;
		};
		
		this.purge = function() {
			delete this.el[expando];
			return this;
		};
		
		this.uid = function() {
			return this.el[expando];
		};
		
		/* Private Properties
		-------------------------------------*/
		var cache 	= [], 
		expando = $.CLASSNAME + +new Date(),
		lastEvent = $.event;
		/* Private Methods
		-------------------------------------*/
		_collection = function(list) {
			var dest = [];
			for(var i = 0, length = list.length; i < length; i++)
			{
				if(list[i].nodeType == 1)
				{
					dest.push(list[i]);
				}
			}
			return dest;
		};
	});
	//set a global event object now that
	//the element class is definied
	//we need this for custom events
	//http://fbloggs.wordpress.com/2006/12/27/how-to-create-a-global-event-object-for-both-ie-and-firefox/
	var setGlobalEvent = $.Element(document).on('click', function(e) {
		e  = e || window.event;
		$.event = {};
		for(var k in e)
		{
			$.event[k] = e[k];
		}
		setGlobalEvent.off();
	});

	$.Element(document).trigger('click');
	
	/*	Collection Class
	--------------------------------*/
	$.def('Collection', new function() {
		/* Constants
		-------------------------------------*/
		this.CLASSNAME = 'Collection';
		
		/* Loader
		--------------------------------*/
		this.__load = function(instance, args) {
			var collection = [];
			switch($.type(args[0]))
			{
				case 'string':
					args[0] = document.getElementById(args[0]);
				case 'element':
				case 'window':
				case 'document':
					collection = [$.Element(args[0])];
				case 'domcollection':
				case 'array':
					collection = new instance();
					collection.__construct(args[0]);
					break;
				case this.CLASSNAME:
					collection = args[0];
					break;	
				default:
					return null;
			}
			if(args[1])
			{
				collection.attr(args[1]);
			}
			if(args[2])
			{
				collection.html(args[2]);
			}
			
			//return instance
			return collection;
		};
		
		/* Constructor
		--------------------------------*/
		this.__construct = function(collection) {
			this.collection = [];
			for(var i = 0, length = collection.length; i < length; i++)
			{
				this.collection.push($.Element(collection[i]));
			}
			
			this.unique();
		};
		
		/*	Parser Methods
		--------------------------------*/
		this.parse = this.each = function(callback, options) {
			$.parse(this.collection, callback, options);
			return this;
		};
		
		this.get = function(num) {
			if(typeof num == 'number')
			{
				return this.collection[num];
			}
			return this.collection;
		};
		
		this.map = function(method, args) {
			args = $.args(args);
			
			this.parse(function(k, el) {
				el[method].apply(el, args);
			});
			
			return this;
		};
		
		this.combine = function() {
			var	args = $.args(arguments);
				
			//foreach arguments
			$.parse(args, $.bind(function(i, arg) {
				arg = $.Collection(arg);
				arg.parse(function(key, el) {
					this.collection.push(el);
				});
			}, this));
			
			this.unique();
			
			return this;
		};
		
		this.pop = function(returnElement) {
			returnElement = returnElement === false ? false : true;
			if(returnElement)
			{
				return this.collection.pop();
			}
			this.collection.pop();
			return this;
		};
		
		this.push = function(el) {
			el = $.Element(el);
			if(el)
			{
				this.collection.push(el);
			}
			return this;
		};
		
		this.rparse = this.reach = function(obj, callback, options) {
			$.rparse(this.collection, callback, options);
			return this;
		};
		
		this.shift = function(returnElement) {
			returnElement = returnElement === false ? false : true;
			if(returnElement)
			{
				return this.collection.shift();
			}
			this.collection.shift();
			return this;
		};
		
		this.size = function() {
			return this.collection.length;
		};
		
		this.unique = function() {
			var unique = {};
			
			this.parse(function(k, el) {
				if($.type(el, ['Element', 'element']))
				{
					el = $.Element(el);
					unique[el.uid()] = el;
				}
			});
			
			this.collection = [];
			
			$.parse(unique, $.bind(function(k, el) {
				this.collection.push(el);
			}, this));
			
			return this;
		};
		
		this.unshift = function(el) {
			el = $.Element(el);
			if(el)
			{
				this.collection.unshift(el);
			}
			return this;
		};
		
		/*	Other Methods
		--------------------------------*/
		this.html = function() {
			if(arguments.length == 1)
			{
				return this.map('html', arguments);
			}
			
			//return them something
			if(this.collection.length > 0)
			{
				return this.collection[0].html.apply(this.collection[0], args);
			}
			
			return null;
		};
		
		this.remove = function() {
			this.map('remove', arguments);
			//this is the end of the road
			return null;
		};
		
		/*	Pure Setter Methods
		--------------------------------*/
		$.parse(['set', 'unset', 'isset', 'addClass', 
			'removeClass', 'on', 'trigger', 'fire', 'off'], 
			$.bind(function(key, method) {
			this[method] = function() {
				return this.map(method, arguments);
			};
		
		}, this));
		
		$.parse(['attr', 'css'], $.bind(function(key, method) {
			this[method] = function() {
				if(arguments.length == 1 && typeof arguments[0] == 'string')
				{
					//return them something
					if(this.collection.length > 0)
					{
						return this.collection[0][method].apply(this.collection[0], args);
					}
					
					return null;
				}
				return this.map(method, arguments);
			};
		}, this));
		
		/*	Return Element Methods
		--------------------------------*/
		$.parse(['clone', 'previous', 'next', 'parent'], 
			$.bind(function(key, method) {
			this[method] = function() {
				var args = $.args(arguments);
				
				this.parse($.bind(function(k, el) {
					this.collection[key] = el[method].apply(el, args);
				}, this));
				
				return this;
			};
		}, this));
		
		/*	Return Collection Methods
		--------------------------------*/
		$.parse(['select', 'parents', 'children'], 
			$.bind(function(key, method) {
			this[method] = function(selector, index) {
				
				//produce a new collection
				var collection = $.Collection([]);
				this.parse(function(k, el) {
					collection.combine(el[method].call(el, selector, index));
				});
				
				return collection;
			};
		}, this));
	});
	
	/*	Component - AJAX Class
	--------------------------------*/
	$.def('Ajax', new function() {
		$.settings.timeout = 10000;					   
		var transport = $.BROWSER[0] == 'IE' ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest()
			   
		/* Constants
		-------------------------------------*/
		this.CLASSNAME = 'Ajax';
		this.STATE = {READY: 'ready', REQUEST: 'request'};
		
		/* Constructor
		--------------------------------*/
		this.__construct = function(url, params) {
			this.url 		= url;
			this.params 	= params || null;
			this.STATE		= this.STATE.READY;
			this.timeout 	= [];
			this.callbacks 	= [];
		};
		
		/* Methods
		--------------------------------*/
		this.queue = function(callback) {
			this.callbacks.push(callback);
			return this;
		};
		
		this.abort = function() {
			transport.abort();
			
			//clear timeout
			while(timeout = this.timeout.pop())
			{
				clearTimeout(timeout);
			}
			
			this.state = this.STATE.READY;
			return this;
		};
		
		this.send = function(options) {
			//set default options
			options = $.combine({
				method: 'POST',		poll: false,
				timeout: $.settings.timeout
			}, options || {});
			
			//see if this is already being requested
			if(this.state != this.STATE.READY)
			{
				return;
			}
			
			//set the state
			this.state = this.STATE.REQUEST;
			
			//set a timeout
			this.timeout.push(setTimeout($.bind(function(timeout) {
				//abort it
				this.abort();
			}, this, true), options.timeout));
			
			//try sending this out
			try {
				if(options.method.toUpperCase() == 'POST')
				{
					//open connection
					transport.open(options.method, this.url, true);
					
					//Send the proper header information along with the request
					transport.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
					transport.setRequestHeader("Content-length", $.type(this.params, 'string') ? this.params.length : 0);
					transport.setRequestHeader("Connection", "close");
					
					//give a chance for user to override
					$.parse(options.headers, $.bind(function(key, value) {
						if(value instanceof Array)
						{
							transport.setRequestHeader(value[0], value[1]);
						}
					}, this));
					
					//set the response callback
					transport.onreadystatechange = $.bind(this.receive, this, options);
					
					//send it out
					transport.send(this.params);
				}
				else
				{
					//open connection
					transport.open(options.method, this.url+'?'+this.params, true);
					
					//set the response callback
					transport.onreadystatechange = $.bind(this.receive, this, options);
					
					//send it out
					transport.send(null);
				}
			} catch(e){}
			
			return this;
		};
		
		this.receive = function(options) {
			try
			{
				if (transport.readyState == 4 || transport.readyState == 'complete') 
				{
					var timeout,
						status 		= transport.status,
						response 	= transport.responseText;
					
					//clear timeout
					while(timeout = this.timeout.pop())
					{
						clearTimeout(timeout);
					}
					
					//change state
					this.state = this.STATE.RESPONSE;
					
					//handle errors
					if(!response)
					{
						response = new Error('No Response');
					}
					else if(status != 200)
					{
						response = new Error('Response Error');
					}
					else
					{
						this.lastResponse = response;
					}
					
					//run the response handler
					$.parse(this.callbacks, $.bind(function(key, value){
						value(response, this);
					}, this));
					
					//change state
					this.state = this.STATE.READY;
					
					//test for polling
					if(options.poll)
					{
						//call polling
						this.send(options);
					}
				}
			} catch(e) {}
		};
	}, $.Base);	
	
	/*	Interaction - Draggable Class
	--------------------------------*/
	$.def('Draggable', new function() {
		this.CLASSNAME = 'Draggable';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (target, options) {
			//track all binds
			this.events = {};
			
			//flag for dragging
			this.dragging = false;
			
			//set the target
			this.target = this.proxy = $.Element(target);
			
			//check if target has a draggable obj
			if(this.target.isSpace('draggable'))
			{
				//lets destroy it
				this.target.getSpace('draggable').__destruct();
			}
			
			//track original target style when the mousemove event is on
			this.originalTargetStyle = {
				display: 	this.target.css('display') || '',
				visibility: this.target.css('visibility') || '',
				position: 	this.target.css('position') || '',
				left: 		this.target.css('left') || '',
				top: 		this.target.css('top') || '',
				marginLeft: this.target.css('left') || '',
				marginTop: 	this.target.css('top') || ''};
			
			//set the options
			this.options = options || {};
			switch($.type(this.options.handle))
			{
				case 'element':
					this.handle = $.Element(this.options.handle);
					break;
				case 'Element':
					this.handle = this.options.handle;
					break;
				case 'array':
					this.options.handle[1] = !!(this.options.handle[1]) ? this.options.handle[1] : document.body;
					this.handle = $.select(this.options.handle[0], this.options.handle[1]);
					break;
				default:
					this.handle = this.target;
					break;
			}
			
			this.options.snapDetection = this.options.snapDetection || 15;
			
			//fix the snap option
			if(this.options.snap)
			{
				//if string then it's a selector
				//otherwise it can only be an element or Element
				switch($.type(this.options.snap)) 
				{
					case 'array':
						$.parse(this.options.snap, function(k, v) {
							this.options.snap[k] = $.Element(v);
						});
						break;
					case 'string':
						this.options.snap = $.select(this.options.snap).get();
						$.parse(this.options.snap, function(k, v) {
							this.options.snap[k] = $.Element(v);
						});
						break;
					case 'element':
						this.options.snap = [$.Element(this.options.snap)];
						break;
					case 'Element':
						this.options.snap = [this.options.snap];
						break;
					default:
						this.options.snap = false;
						break;
				}
			}
			
			//start listening to the handler
			$.setSpace(this.events, 'handle', 'mousedown', this.handle.on('mousedown', $.bind(this.start, this)));
			
			//mark on the element that there is a drag obj associated
			this.target.setSpace('draggable', this);
		};
		
		/* Methods
		-------------------------------------*/
		this.start = function(e) {
			this.dragging 	= true;
			
			//get the mouse position and target position
			var mousePosition 	= $.mousePosition(e),
				coordinates = this.target.trueCoordinates();
			
			this.init = {
				mouse: mousePosition,
				position: {
					left: mousePosition.left - parseInt(coordinates[0].left), 
					top: mousePosition.top - parseInt(coordinates[0].top)},
				relative: {
					left: mousePosition.left - (parseInt(this.target.css('left')) || 0), 
					top: mousePosition.top - (parseInt(this.target.css('top'))||0)},
				margin: {
					left: mousePosition.left - (parseInt(this.target.css('marginLeft')) || 0), 
					top: mousePosition.top - (parseInt(this.target.css('marginTop')) || 0)}};
			
			//listen to the window and body mouse up
			$.setSpace(this.events, 'window', 'mouseup', $.Element(window).on('mouseup', $.bind(this.stop, this)));
			$.setSpace(this.events, 'body', 'mouseup', 	$.Element(document.body).on('mouseup', $.bind(this.stop, this)));
			
			if($.BROWSER[0] == 'IE')
			{
				//start observing the mouse movement 
				//also keep track of this event so later we can stop listening;
				$.setSpace(this.events, 'document', 'mousemove', $.Element(document).on('mousemove', $.bind(this.drag, this)));
				
			}
			else
			{
				//start observing the mouse movement 
				//also keep track of this event so later we can stop listening;
				$.setSpace(this.events, 'window', 'mousemove', $.Element(window).on('mousemove', $.bind(this.drag, this)));
			}
			
			//trigger
			if(this.trigger('start', this, e) === false)
			{
				return false;
			}
			
			//stop default action
			$.stop(e);
			
			if(this.options.proxy === true)
			{
				//lets make a clone of the target node called a proxy
				this.proxy = this.getProxy(this.target);
			}
			else if(typeof this.options.proxy == 'function')
			{
				this.proxy = this.options.proxy(this.target);
			}
			else
			{
				this.proxy = this.target;
			}
			
			if(this.options.type == 'margin')
			{
				this.proxy.css('marginLeft', this.target.css('marginLeft') || 0);
				this.proxy.css('marginTop', this.target.css('marginTop') || 0);
			}
			else
			{
				this.proxy.css('position', this.target.css('position')|| 'relative');
				this.proxy.css('left', this.target.css('left') || 0);
				this.proxy.css('top', this.target.css('top') || 0);
			}
			
			//lets switch the proxy with the real element so the user wont know the difference
			this.target.css('display', 'none');
			this.proxy.css('display', 'block');
			this.proxy.css('visibility', this.originalTargetStyle.visibility);
		};

		this.drag = function(e) {
			//stop default action
			$.stop(e);
			//some math 
			var mousePosition = $.mousePosition(e),
				size = this.proxy.trueSize(),
				relative = {
					left: mousePosition.left - this.init.relative.left,
					top: mousePosition.top - this.init.relative.top},
				position = {
					left: mousePosition.left - this.init.position.left,
					top: mousePosition.top - this.init.position.top},
				margin = {
					left: mousePosition.left - this.init.margin.left,
					top: mousePosition.top - this.init.margin.top},
				coords = [
					{left: position.left,
					top: position.top},
					{left: position.left + size.width,
					top: position.top + size.height}];
				
			//if the mouse leaves the view port lets stop dragging
			if(mousePosition.left < 2 || mousePosition.top < 2)
			{
				this.stop(e);
				return;
			}
			
			if(this.options.snap)
			{
				_snapCalc.call(this, coords, this.options.type == 'margin' ? margin : relative);
			}
			
			//if there is a grid lets do some epic math
			if(typeof this.options.grid == 'number')
			{
				_gridCalc.call(this, this.options.type == 'margin' ? margin : relative);
			}
			
			//if there are container and the target passes it then lets not drag
			if(this.options.container)
			{
				var containerCoords = _getCoords.call(this, this.options.container);
				
				if(coords[0].left < containerCoords[0].left)
				{
					relative.left += containerCoords[0].left - coords[0].left;
					margin.left += containerCoords[0].left - coords[0].left;
				}
				else if(coords[1].left > containerCoords[1].left)
				{
					relative.left -= coords[1].left - containerCoords[1].left;
					margin.left += containerCoords[0].left - coords[0].left;
				}
				
				if(coords[0].top < containerCoords[0].top)
				{
					relative.top += containerCoords[0].top - coords[0].top;
					margin.left += containerCoords[0].left - coords[0].left;
				}
				if(coords[1].top > containerCoords[1].top)
				{
					relative.top -= coords[1].top - containerCoords[1].top;
					margin.left += containerCoords[0].left - coords[0].left;
				}
			}
			
			//trigger
			if(this.trigger('drag', this, e, this.options.type == 'margin' ? margin : relative, position) === false)
			{
				return false;
			}
			
			//if there's a direction option and it's not set to vertical
			if(!this.options.direction || this.options.direction != 'vertical')
			{
				if(this.options.type == 'margin')
				{
					this.proxy.css('marginLeft', margin.left+'px');
				}
				else
				{
					this.proxy.css('left', relative.left+'px');
				}
			}
			
			//if there's a direction option and it's not set to horizontal
			if(!this.options.direction || this.options.direction != 'horizontal')
			{
				if(this.options.type == 'margin')
				{
					this.proxy.css('marginTop', margin.top+'px');
				}
				else
				{
					this.proxy.css('top', relative.top+'px');
				}
			}
		};

		this.stop = function(e) {
			if(!this.dragging)
			{
				return;
			}
			
			this.dragging = false;
			
			//unlisten
			this.events.window.mouseup.off();
			this.events.body.mouseup.off();
			if($.BROWSER[0] == 'IE')
			{
				//start observing the mouse movement 
				//also keep track of this event so later we can stop listening;
				 this.events.document.mousemove.off();
			}
			else
			{
				this.events.window.mousemove.off();
			}
			
			if(this.trigger('stop', this, e) === false)
			{
				return false;
			}
			$.stop(e);
			//lets bring the real node back and destroy the proxy
			if(this.options.type == 'margin')
			{
				this.target.css('marginLeft', this.proxy.css('marginLeft'));
				this.target.css('marginTop', this.proxy.css('marginTop'));
			}
			else
			{
				this.target.css('position', 'relative');
				this.target.css('top', this.proxy.css('top'));
				this.target.css('left', this.proxy.css('left'));
			}
			
			if(this.options.proxy === true || typeof this.options.proxy == 'function')
			{
				//lets make a clone of the target node called a proxy
				this.removeProxy(this.proxy);
			}
			
			this.target.css('visibility', this.originalTargetStyle.visibility);
			this.target.css('display', this.originalTargetStyle.display);
		};

		this.getProxy = function(target) {
			var proxy = target.clone();
			target.after(proxy);
			return proxy;
		};
		
		this.removeProxy = function(proxy) {
			proxy.remove();
			return this;
		};
		
		/* Private Methods
		-------------------------------------*/
		var _getCoords = function (coords) {
			if($.type(coords, 'array') && 
			   typeof coords[0].left == 'number' &&
			   typeof coords[0].top == 'number' &&
			   typeof coords[1].left == 'number' &&
			   typeof coords[1].top == 'number')
			{
				return coords;
			}
			else if($.type(coords, ['element', 'Element']))
			{
				return $.Element(coords).trueCoordinates();
			}
			coords = $.documentSize();
			return [{left: 0, top: 0}, {left:coords.width, top: coords.height}];
		};

		var _gridCalc = function(position) {
			//if the remainder is less than half the grid size
			if(position.left % this.options.grid < Math.round(this.options.grid / 2))
			{
				//lets round down
				position.left -=  position.left % this.options.grid;
			}
			else
			{
				//lets round up
				position.left += this.options.grid - (position.left % this.options.grid);
			}
			
			//if the remainder is less than half the grid size
			if(position.top % this.options.grid < Math.round(this.options.grid / 2))
			{
				//lets round down
				position.top -= position.top % this.options.grid;
			}
			else
			{
				//lets round up
				position.top += this.options.grid - (position.top % this.options.grid);
			}
		};

		var _snapCalc = function(coords, position) {
			$.parse(this.options.snap, $.bind(function(k, el) {
				var snapCoords = el.trueCoordinates(),
					detection = [{
						left: {
							min: snapCoords[0].left - this.options.snapDetection,
							max: snapCoords[0].left + this.options.snapDetection},
						top: {
							min: snapCoords[0].top - this.options.snapDetection,
							max: snapCoords[0].top + this.options.snapDetection}}, {
						left: {
							min: snapCoords[1].left - this.options.snapDetection,
							max: snapCoords[1].left + this.options.snapDetection},
						top: {
							min: snapCoords[1].top - this.options.snapDetection,
							max: snapCoords[1].top + this.options.snapDetection}
					}];
				
				var snappedHoriz = !$.parse([0,1], function(k, v) {
					if(detection[0].left.min < coords[v].left && coords[v].left < detection[0].left.max)
					{
						//find the distance between the left and the snap coords left
						position.left += snapCoords[0].left - coords[v].left;
						
						//no need to iterate any more
						return false;
					}
					
					if(detection[1].left.min < coords[v].left && coords[v].left < detection[1].left.max)
					{
						//find the distance between the left and the snap coords left
						position.left += snapCoords[1].left - coords[v].left;
						
						//no need to iterate any more
						return false;
					}
				});
				
				var snappedVert = !$.parse([0,1], function(k, v) {
					if(detection[0].top.min < coords[v].top && coords[v].top < detection[0].top.max)
					{
						//find the distance between the left and the snap coords left
						position.top += snapCoords[0].top - coords[v].top;
						//no need to iterate any more
						return false;
					}
					
					if(detection[1].top.min < coords[v].top && coords[v].top < detection[1].top.max)
					{
						//find the distance between the left and the snap coords left
						position.top += snapCoords[1].top - coords[v].top;
						//no need to iterate any more
						return false;
					}
				});
				
				//if we snapped
				if(snappedHoriz || snappedVert)
				{
					//no need to iterate any more
					return false;
				}
			}, this));
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			//unlisten
			this.events.window.mouseup.off();
			this.events.body.mouseup.off();
			if($.BROWSER[0] == 'IE')
			{
				//start observing the mouse movement 
				//also keep track of this event so later we can stop listening;
				 this.events.document.mousemove.off();
			}
			else
			{
				this.events.window.mousemove.off();
			}
			$.destroy(this);
			
			//unmark on the element that there is a drag obj associated
			this.target.freeSpace('draggable');
		};
	}, $.Base);
	
	/*	Interaction - Droppable Class
	--------------------------------*/
	$.def('Droppable', new function() {
		this.CLASSNAME = 'Droppable';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function(target, items, options) {
			//track all the events
			this.events = {};
			
			this.options = options || {};
			
			//set target
			this.target = $.Element(target);
			
			if(this.target.isSpace('droppable'))
			{
				this.target.getSpace('droppable').__destruct();
			}
			
			if(!(items instanceof Array))
			{
				items = [items];
			}
			
			$.parse(items, $.bind(function(key, el){
				el = $.Element(el);
				//set drag triggers
				if(el.isSpace('draggable'))
				{
					$.setSpace(this.events, 'elements', key, 'stop', el.getSpace('draggable').on('stop', $.bind(this.drop, this)));
				}
			}, this));
			
			this.target.setSpace('droppable', this);
		};
		
		/* Methods
		-------------------------------------*/
		this.drop = function(dragObj, e) {
			if(this.trigger('drop', dragObj, this, e) === false)
			{
				return false;
			}
			
			//if the mouse was over the target while dragging
			//lets append it to the target
			var coords = this.target.trueCoordinates(),
				mousePosition = $.mousePosition(e);
				
			if(coords[0].left < mousePosition.left && coords[1].left > mousePosition.left &&
				coords[0].top < mousePosition.top && coords[1].top > mousePosition.top)
			{
				//lets bring the real node back and destroy the proxy
				if(this.options.type == 'margin')
				{
					dragObj.target.css('marginTop', dragObj.originalTargetStyle.marginTop);
					dragObj.target.css('marginLeft', dragObj.originalTargetStyle.marginLeft);
				}
				else
				{
					dragObj.target.css('position', dragObj.originalTargetStyle.position);
					dragObj.target.css('top', dragObj.originalTargetStyle.top);
					dragObj.target.css('left', dragObj.originalTargetStyle.left);
				}
				
				dragObj.target.css('visibility', dragObj.originalTargetStyle.visibility);
				
				if(dragObj.options.proxy === true || typeof dragObj.options.proxy == 'function')
				{
					//lets make a clone of the target node called a proxy
					dragObj.removeProxy(dragObj.proxy);
				}
			
				this.target.append(dragObj.target);
			
				return false;
			}
			
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function droppable_destroy() {
			$.parse(this.events.elements, $.bind(function(key, evt) {
				this.events.elements[key].stop.off();
			}, this));
			
			//unmark on the element that there is a drop obj associated
			this.target.freeSpace('droppable');
			
			$.destroy(this);
			
		};
	}, $.Base);
	
	/*	Interaction - Resizable Class
	--------------------------------*/
	$.def('Resizable', new function() {
		this.CLASSNAME = 'Resizable';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (target, handles, options) {
			//variable list
			//track all binds
			this.events = {};
			
			//flag for dragging
			this.resizing = false;
			
			//set the target
			this.target = $.Element(target);
			
			//check if target has a draggable obj
			if(this.target.isSpace('resizable'))
			{
				//lets destroy it
				this.target.getSpace('resizable').__destruct();
			}
			
			//track original target style when the mousemove event is on
			this.originalTargetStyle = {
				display: 	this.target.css('display'),
				visible: 	this.target.css('visible'),
				height:		this.target.css('height'),
				width: 		this.target.css('width'),
				marginLeft: this.target.css('marginLeft'),
				marginTop: 	this.target.css('marginTop'),
				position:	this.target.css('position'),
				top:		this.target.css('top'),
				left:		this.target.css('left'),
				visibility:	this.target.css('visibility')};
				
			var size = this.target.trueSize();
			
			this.targetSizeOffset = {
				width: size.width - parseInt(this.originalTargetStyle.width) || 0, 
				height: size.height - parseInt(this.originalTargetStyle.height) || 0};
			
			//set the handles
			this.handles = handles || {};
			//listen to handles
			$.parse(this.handles, $.bind(function(key, el) {
				if(!el) 
				{
					return;
				}
				this.handles[key] = $.Element(el);
				$.setSpace(this.events, 'handle', key, 'mousedown', this.handles[key].on('mousedown', $.bind(this.start, this, key)));
			}, this));
			
			//set the options
			this.options = options || {};
			//set option defaults
			this.options.snapDetection = this.options.snapDetection || 15;
			
			//fix the snap option
			if(this.options.snap)
			{
				//if string then it's a selector
				//otherwise it can only be an element or Element
				switch($.type(this.options.snap)) 
				{
					case 'array':
						$.parse(this.options.snap, $.bind(function(k, v) {
							this.options.snap[k] = $.Element(v);
						}, this));
						break;
					case 'string':
						this.options.snap = $.select(this.options.snap).get();
						$.parse(this.options.snap, $.bind(function(k, v) {
							this.options.snap[k] = $.Element(v);
						}, this));
						break;
					case 'element':
						this.options.snap = [$.Element(this.options.snap)];
						break;
					case 'Element':
						this.options.snap = [this.options.snap];
						break;
					default:
						this.options.snap = false;
						break;
				}
			}
			
			//mark on the element that there is a drag obj associated
			this.target.setSpace('resizable', this);
		};
		
		/* Methods
		-------------------------------------*/
		this.start = function(e, direction) {
			if(this.resizing)
			{
				return false;
			}
			
			this.resizing 	= true;
			
			this.direction = direction;
			//get the mouse position and target position
			var mousePosition 	= $.mousePosition(e), 
				targetMargin 	= {left: parseInt(this.target.css('marginLeft')) || 0, top: parseInt(this.target.css('marginTop')) || 0},
				targetPosition 	= {left: parseInt(this.target.css('left')) || 0, top: parseInt(this.target.css('top')) || 0},
				coordinates 	= this.target.trueCoordinates();
				
			this.init = {
				position: mousePosition,
				coords: coordinates,
				offset: {
					left: mousePosition.left - parseInt(coordinates[0].left), 
					top: mousePosition.top - parseInt(coordinates[0].top)},
				relative: {
					left: targetPosition.left,
					top: targetPosition.top},
				size: {
					width: 	parseInt(this.target.css('width')) || coordinates[1].left - coordinates[0].left, 
					height: parseInt(this.target.css('height')) || coordinates[1].top - coordinates[0].top},
				margin: {
					left: targetMargin.left,
					top: targetMargin.top}
			};
			
			//trigger
			if(this.trigger('start', this, e, mousePosition, targetMargin, coordinates) === false)
			{
				return false;
			}
			
			//stop event
			$.stop(e);
			
			//listen to the window and body mouse up
			$.setSpace(this.events, 'window', 'mouseup', $.Element(window).on('mouseup', $.bind(this.stop, this)));
			$.setSpace(this.events, 'body', 'mouseup', 	$.Element(document.body).on('mouseup', $.bind(this.stop, this)));
			
			//start observing the mouse movement 
			//also keep track of this event so later we can stop listening;
			if($.BROWSER[0] == 'IE')
			{
				//start observing the mouse movement 
				//also keep track of this event so later we can stop listening;
				$.setSpace(this.events, 'document', 'mousemove', $.Element(document).on('mousemove', $.bind(this.resize, this)));
			}
			else
			{
				$.setSpace(this.events, 'window', 'mousemove', $.Element(window).on('mousemove', $.bind(this.resize, this)));
			}
			
			if(this.options.proxy === true)
			{
				//lets make a clone of the target node called a proxy
				this.proxy = this.getProxy(this.target);
			}
			else if(typeof this.options.proxy == 'function')
			{
				this.proxy = this.options.proxy(this.target);
			}
			else
			{
				this.proxy = this.target;
			}
			
			//setup the proxy to be draggable
			if(this.options.type == 'margin')
			{
				this.proxy.css('marginLeft', targetMargin.left);
				this.proxy.css('marginTop', targetMargin.top);
			}
			else
			{
				this.proxy.css('position', this.target.css('position') || 'relative');
				this.proxy.css('left', targetPosition.left+'px');
				this.proxy.css('top', targetPosition.top+'px');
			}
			
			//lets switch the proxy with the real element so the user wont know the difference
			this.target.css('display', 'none');
			this.proxy.css('display', 'block');
			this.proxy.css('visibility', this.originalTargetStyle.visibility);
		};
		
		this.resize = function(e) {
			//stop event
			$.stop(e);
			
			//set variables
			var mousePosition 	= $.mousePosition(e),
				proxyCoords 		= this.proxy.trueCoordinates(),
				size			= {
					width: 		parseInt(this.proxy.css('width')) || 0,
					height:		parseInt(this.proxy.css('height')) || 0},
				margin 			= {
					left: 		parseInt(this.proxy.css('marginLeft')) || 0,
					top: 		parseInt(this.proxy.css('marginTop')) || 0},
				relative 		= {
					left: 		parseInt(this.proxy.css('left')) || 0,
					top: 		parseInt(this.proxy.css('top')) || 0},
				diff			= {
					top:		mousePosition.top - this.init.position.top,
					left:		mousePosition.left - this.init.position.left},
				position 		= {
					left: 		mousePosition.left - this.init.offset.left,
					top: 		mousePosition.top - this.init.offset.top},
				coords 			= $.clone(this.init.coords);
			
			//if the mouse leaves the view port lets stop dragging
			if(mousePosition.left < 2 || mousePosition.top < 2)
			{
				this.stop(e);
				return;
			}
			
			//get the standard resize calculations
			switch(true)
			{
				case this.direction.indexOf('n') != -1:
					margin.top = this.init.margin.top + diff.top;
					relative.top = this.init.relative.top + diff.top;
					size.height = this.init.size.height - diff.top;
					break;
				case this.direction.indexOf('s') != -1:
					size.height = this.init.size.height + diff.top;
					break;
			}
			
			switch(true)
			{
				case this.direction.indexOf('w') != -1: 
					margin.left = this.init.margin.left + diff.left;
					relative.left = this.init.relative.left + diff.left;
					size.width = this.init.size.width - diff.left;
					break;
				case this.direction.indexOf('e') != -1: 
					size.width = this.init.size.width + diff.left;
					break;
			}
			
			//validate width and height
			size.width = size.width < 0 ? 0 : size.width;
			size.height = size.height < 0 ? 0 : size.height;
			
			//GRID OPTION CALCULATIONS
			//if there is a grid lets do some epic math
			if(typeof this.options.grid == 'number')
			{
				_gridCalc.call(this, size, this.options.type == 'margin' ? margin : relative);
			}
			
			//container and snap require for the 
			//coordinates to be calculated
			//we want to do this after the grid calculations
			//for accuracy
			_coordCalc.call(this, size, coords, diff);
			
			//SNAP OPTION CALCULATIONS
			//if snap lets do some epic math
			if(this.options.snap)
			{
				_snapCalc.call(this, size, coords, this.options.type == 'margin' ? margin : relative);
			}
			
			//CONTAINER OPTION CALCULATIONS
			//if there are container and the target passes it then lets not drag
			if(this.options.container)
			{
				var containerCoords = _getCoords.call(this, this.options.container);
				
				var containerCoords = _getCoords.call(this, this.options.container);
				
				if(coords[0].left < containerCoords[0].left)
				{
					relative.left += containerCoords[0].left - coords[0].left;
					margin.left += containerCoords[0].left - coords[0].left;
				}
				else if(coords[1].left > containerCoords[1].left)
				{
					relative.left -= coords[1].left - containerCoords[1].left;
					margin.left += containerCoords[0].left - coords[0].left;
				}
				
				if(coords[0].top < containerCoords[0].top)
				{
					relative.top += containerCoords[0].top - coords[0].top;
					margin.left += containerCoords[0].left - coords[0].left;
				}
				if(coords[1].top > containerCoords[1].top)
				{
					relative.top -= coords[1].top - containerCoords[1].top;
					margin.left += containerCoords[0].left - coords[0].left;
				}
			}
			
			//MIN AND MAX OPTION CALCULATIONS
			if(typeof this.options.minWidth == 'number' && size.width < this.options.minWidth)
			{
				return false;
			}
			
			if(typeof this.options.maxWidth == 'number' && size.width > this.options.maxWidth)
			{
				return false;
			}
			
			if(typeof this.options.minHeight == 'number' && size.height < this.options.minHeight)
			{
				return false;
			}
			
			if(typeof this.options.maxHeight == 'number' && size.height > this.options.maxHeight)
			{
				return false;
			}
			
			//trigger the resize
			if(this.trigger('resize', this, e, coords, this.options.type == 'margin' ? margin : relative, size, diff) === false)
			{
				return false;
			}
			
			//now add the final calcs to the proxy
			this.proxy.css('width', size.width+'px');
			this.proxy.css('height', size.height+'px');
			
			if(this.options.type == 'margin')
			{
				this.proxy.css('marginLeft', margin.left+'px');
				this.proxy.css('marginTop', margin.top+'px');
			}
			else
			{
				this.proxy.css('left', relative.left+'px');
				this.proxy.css('top', relative.top+'px');
			}
		};
		
		this.stop = function(e) {
			if(!this.resizing)
			{
				return;
			}
			
			this.resizing = false;
			
			//unlisten
			this.events.window.mouseup.off();
			this.events.body.mouseup
			if($.BROWSER[0] == 'IE')
			{
				//start observing the mouse movement 
				//also keep track of this event so later we can stop listening;
				this.events.document.mousemove.off();
			}
			else
			{
				this.events.window.mousemove.off();
			}
			
			if(this.trigger('stop', this, e) === false)
			{
				return false;
			}
			
			//lets bring the real node back and destroy the proxy
			if(this.options.type == 'margin')
			{
				this.target.css('marginLeft', this.proxy.css('marginLeft'));
				this.target.css('marginTop', this.proxy.css('marginTop'));
			}
			else
			{
				this.target.css('left', this.proxy.css('left'));
				this.target.css('top', this.proxy.css('top'));
			}
			
			this.target.css('width', this.proxy.css('width'));
			this.target.css('height', this.proxy.css('height'));

			if(this.options.proxy === true || typeof this.options.proxy == 'function')
			{
				//lets make a clone of the target node called a proxy
				this.removeProxy(this.proxy);
			}
			
			this.target.css('visibility', this.originalTargetStyle.visibility);
			this.target.css('display', this.originalTargetStyle.display);
		};
		
		this.getProxy = function(target) {
			var proxy = target.clone();
			target.after(proxy);
			return proxy;
		};
		
		this.removeProxy = function(proxy) {
			proxy.remove();
			return this;
		};
		
		/* Private Methods
		-------------------------------------*/
		var _getCoords = function (coords) {
			if($.type(coords, 'array') && 
			   typeof coords[0].left == 'number' &&
			   typeof coords[0].top == 'number' &&
			   typeof coords[1].left == 'number' &&
			   typeof coords[1].top == 'number')
			{
				return coords;
			}
			else if($.type(coords, ['element', 'Element']))
			{
				return $.Element(coords).trueCoordinates();
			}
			coords = $.documentSize();
			return [{left: 0, top: 0}, {left:coords.width, top: coords.height}];
		};
		
		var _gridCalc = function(size, margin) {
			var grid = [];
			switch(true)
			{
				case this.direction.indexOf('n') != -1:
					grid.push([size, 'height']);
					grid.push([margin, 'top']);
					break;
				case this.direction.indexOf('s') != -1:
					grid.push([size, 'height']);
					break;
			}
			
			switch(true)
			{
				case this.direction.indexOf('w') != -1: 
					grid.push([size, 'width']);
					grid.push([margin, 'left']);
					break;
				case this.direction.indexOf('e') != -1: 
					grid.push([size, 'width']);
					break;
			}
			
			$.parse(grid, $.bind(function(k, v) {
				//if the remainder is less than half the grid size
				if(v[0][v[1]] % this.options.grid < $.round(this.options.grid / 2))
				{
					//lets round down
					v[0][v[1]] -=  v[0][v[1]] % this.options.grid;
				}
				else
				{
					//lets round up
					v[0][v[1]] += this.options.grid - (v[0][v[1]] % this.options.grid);
				}
			}, this));
		};
		
		var _coordCalc = function(size, coords, diff) {
			switch(true)
			{
				case this.direction.indexOf('n') != -1:
					coords[0].top += diff.top;
					break;
				case this.direction.indexOf('s') != -1:
					coords[1].top = this.init.coords[0].top + size.height;
					coords[1].top += this.targetSizeOffset.height;
					break;
			}
			
			switch(true)
			{
				case this.direction.indexOf('w') != -1: 
					coords[0].left += diff.left;
					break;
				case this.direction.indexOf('e') != -1: 
					coords[1].left = this.init.coords[0].left + size.width;
					coords[1].left += this.targetSizeOffset.width;
					break;
			}
		};
		
		var _snapCalc = function(size, coords, margin) {
			$.parse(this.options.snap, $.bind(function(k, el) {
				var snapCoords = el.trueCoordinates(),
					detection = [{
						left: {
							min: snapCoords[0].left - this.options.snapDetection,
							max: snapCoords[0].left + this.options.snapDetection},
						top: {
							min: snapCoords[0].top - this.options.snapDetection,
							max: snapCoords[0].top + this.options.snapDetection}}, {
						left: {
							min: snapCoords[1].left - this.options.snapDetection,
							max: snapCoords[1].left + this.options.snapDetection},
						top: {
							min: snapCoords[1].top - this.options.snapDetection,
							max: snapCoords[1].top + this.options.snapDetection}}];
				
				var snappedHoriz = !$.parse([0,1], $.bind(function(k, v) {
					if(detection[0].left.min < coords[v].left && coords[v].left < detection[0].left.max)
					{
						//find the distance between the left and the snap coords left
						switch(true)
						{
							case this.direction.indexOf('w') != -1: 
								margin.left += snapCoords[0].left - coords[v].left;
								size.width -= snapCoords[0].left - coords[v].left;
								break;
							case this.direction.indexOf('e') != -1: 
								size.width += snapCoords[0].left - coords[v].left;
								break;
								
						}
					}
					
					else if(detection[1].left.min < coords[v].left && coords[v].left < detection[1].left.max)
					{
						//find the distance between the left and the snap coords left
						switch(true)
						{
							case this.direction.indexOf('w') != -1: 
								margin.left += snapCoords[1].left - coords[v].left;
								size.width -= snapCoords[1].left - coords[v].left;
								break;
							case this.direction.indexOf('e') != -1: 
								size.width += snapCoords[1].left - coords[v].left;
								break;
						}
					}
				}, this));
				
				var snappedVert = !$.parse([0,1], $.bind(function(k, v) {
					if(detection[0].top.min < coords[v].top && coords[v].top < detection[0].top.max)
					{	
						//find the distance between the left and the snap coords left
						switch(true)
						{
							case this.direction.indexOf('n') != -1: 
								margin.top += snapCoords[0].top - coords[v].top;
								size.height -= snapCoords[0].top - coords[v].top;
								break;
							case this.direction.indexOf('s') != -1: 
								size.height += snapCoords[0].top - coords[v].top;
								break;
						}
					}
					
					else if(detection[1].top.min < coords[v].top && coords[v].top < detection[1].top.max)
					{	
						//find the distance between the left and the snap coords left
						switch(true)
						{
							case this.direction.indexOf('n') != -1: 
								margin.top += snapCoords[1].top - coords[v].top;
								size.height -= snapCoords[1].top - coords[v].top;
								break;
							case this.direction.indexOf('s') != -1: 
								size.height += snapCoords[1].top - coords[v].top;
								break;
						}
					}
				}, this));
				
				//if we snapped
				if(snappedHoriz || snappedVert)
				{
					//no need to iterate any more
					return false;
				}
			}, this));
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			//unlisten
			try
			{
				this.events.window.mouseup.off();
				this.events.body.mouseup.off();
				if($.BROWSER[0] == 'IE')
				{
					//start observing the mouse movement 
					//also keep track of this event so later we can stop listening;
					this.events.document.mousemove.off();
				}
				else
				{
					this.events.window.mousemove.off();
				}
			} catch(e) {}
			$.parse(this.events.handle, $.bind(function(key, evt) {
				this.events.handle[key].mousedown.off();
			}, this));
			
			//unmark on the element that there is a resize obj associated
			this.target.freeSpace('resizable');
			
			$.destroy(this);
		};
	}, $.Base);
	
	/*	Interaction - Sortable Class
	--------------------------------*/
	$.def('Sortable', new function() {
		this.CLASSNAME = 'Sortable';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function(target, options) {
			
			this.events = {};
			this.target = $.Element(target);
			
			if(this.target.isSpace('sortable'))
			{
				this.target.getSpace('sortable').__destruct();
			}
			
			this.draggables = [];
			this.elements = [];
			this.options = $.combine({direction: 'all', tree: false}, options || {});
			switch($.type(this.options.items))
			{
				case 'string':
					//assume is selector
					this.options.items = $.select(this.options.items, this.target).get();
					break;
				case 'Element':
				case 'element':
					this.options.items = [$.Element(this.options.items)];
					break;
				case 'array':
					this.options.items = this.options.items;
					break;
				default: 
					this.options.items = [];
			}
			
			$.parse(this.options.items, $.bind(function(key, el){
				this.add(el);
			}, this));
			
			this.target.setSpace('sortable', this);
		};
		
		/* Methods
		-------------------------------------*/
		this.add = function(el) {
			el = $.Element(el);
			this.elements.push(el);
			var key = this.elements.length - 1;
			//make draggable
			if(!el.isSpace('draggable'))
			{
				$.Draggable(el, {direction: this.options.direction});
				this.draggables.push(el);
			}
			$.setSpace(this.events, 'elements', key, 'start', el.getSpace('draggable').on('start', $.bind(this.start, this)));
			$.setSpace(this.events, 'elements', key, 'start', el.getSpace('draggable').on('drag', $.bind(this.arrange, this)));
			$.setSpace(this.events, 'elements', key, 'start', el.getSpace('draggable').on('stop', $.bind(this.stop, this)));
		};
		
		this.start = function(dragObj, e) {
			//trigger
			if(this.trigger('start', dragObj, this, e) === false)
			{
				return false;
			}
			$.stop(e);
			
			if(typeof dragObj.options.proxy == 'function')
			{
				dragObj.proxy = dragObj.options.proxy(dragObj.target);
			}
			else
			{
				//lets make a clone of the target node called a proxy
				dragObj.proxy = dragObj.getProxy(dragObj.target);
			}
			
			if(dragObj.options.type == 'margin')
			{
				dragObj.proxy.css('marginLeft', dragObj.target.css('marginLeft') || 0);
				dragObj.proxy.css('marginTop', dragObj.target.css('marginTop') || 0);
				
				var proxyCoords 	= dragObj.proxy.trueCoordinates();
				targetCoords 	= dragObj.target.trueCoordinates();
				
				dragObj.proxy.css('marginLeft', (targetCoords[0].left - proxyCoords[0].left)+'px');
				dragObj.proxy.css('marginTop', (targetCoords[0].top - proxyCoords[0].top)+'px');
			}
			else
			{
				dragObj.proxy.css('position', 'absolute');
				dragObj.proxy.css('left', (parseInt(dragObj.target.css('left')) || 0)+'px' || 0);
				dragObj.proxy.css('top', (parseInt(dragObj.target.css('top')) || 0)+'px' || 0);
				var proxyCoords 	= dragObj.proxy.trueCoordinates();
				targetCoords 	= dragObj.target.trueCoordinates();
				
				dragObj.proxy.css('left', (targetCoords[0].left - proxyCoords[0].left)+'px');
				dragObj.proxy.css('top', (targetCoords[0].top - proxyCoords[0].top)+'px');
			}
			
			//lets switch the proxy with the real element so the user wont know the difference
			dragObj.target.css('visibility', 'hidden');
			dragObj.proxy.css('display', 'block');
			dragObj.proxy.css('visibility', dragObj.originalTargetStyle.visibility);
			
			//get the mouse position and target position
			var mousePosition 	= $.mousePosition(e),
				coordinates = dragObj.proxy.trueCoordinates();
				
			//we need to redo the init
			dragObj.init = {
				mouse: mousePosition,
				position: {
					left: mousePosition.left - parseInt(coordinates[0].left), 
					top: mousePosition.top - parseInt(coordinates[0].top)},
				relative: {
					left: mousePosition.left - (parseInt(dragObj.proxy.css('left')) || 0), 
					top: mousePosition.top - (parseInt(dragObj.proxy.css('top'))||0)},
				margin: {
					left: mousePosition.left - (parseInt(dragObj.proxy.css('marginLeft')) || 0), 
					top: mousePosition.top - (parseInt(dragObj.proxy.css('marginTop')) || 0)}};
			return false;
		};
		
		this.arrange = function(dragObj, e, position) {
			
			if(this.trigger('sort', dragObj, this, e) === false)
			{
				return false;
			}
			
			//if the mouse was over the target while dragging
			//lets append it to the target
			var dragObjCoords = dragObj.proxy.trueCoordinates(), dragTargetCoords = dragObj.target.trueCoordinates(), mousePosition = $.mousePosition(e);
			
			//check the current element
			if(dragTargetCoords[0].left < mousePosition.left && dragTargetCoords[1].left > mousePosition.left &&
				dragTargetCoords[0].top < mousePosition.top && dragTargetCoords[1].top > mousePosition.top)
			{
				return;
			}
			
			$.rparse(this.elements, function(key, el) {
				el = $.Element(el);
				var coords = el.trueCoordinates();
				if(coords[0].left < mousePosition.left && coords[1].left > mousePosition.left &&
					coords[0].top < mousePosition.top && coords[1].top > mousePosition.top)
				{
					//if the current drag is a child of this element
					//and the current drag has a previous
					if(dragObj.target.childOf(el) && dragObj.target.previous())
					{
						return true;
					}
					
					//given: coordinates of element in question and mouse coords
					//find: x on the hypontenuse
					//see: "law of cosine" and "law of sine"
					
						//get all the sides
					var sideA = coords[1].left - coords[0].left,
						sideB = coords[1].top - coords[0].top,
						sideC = $.hypotenuse(sideA, sideB),
						//get all the angles
						angleA = $.trigSSS(sideA, sideB, sideC),
						angleB = $.trigSSS(sideB, sideA, sideC),
						//find the difference between top el coord and mouse top
						newSideB = mousePosition.top - coords[0].top,
						//a = b*sin[A]/sin[B] = 4*.90631/.42262 = 8.5780
						//c = b*sin[C]/sin[B] = 4*1/.42262 = 9.4648
						newSideA = newSideB * Math.sin(angleB)  / Math.sin(angleA),
						x = coords[1].left - newSideA;
						$.createElement('div', {}, null, document.body).css({position:'absolute', background:'red', height:'2px', width:'2px'}).css('top', (coords[1].top - newSideB)+'px').css('left', x+'px');
						console.log(angleA+' '+angleB);
					//if the mouse is before the x threshold
					if(mousePosition.left < x)
					{
						//if there is a next and the next is the current element
						if(dragObj.target.next() && dragObj.target.next().uid() == el.uid())
						{
							//we can end it here
							return false;
						}
						
						el.before(dragObj.target);
						return false;
					}
					
					el.after(dragObj.target);
					return false;
				}
			});
		};
		
		this.stop = function(dragObj, e) {
			if(this.trigger('stop', dragObj, this, e) === false)
			{
				return false;
			}
			$.stop(e);
			//if the mouse was over the target while dragging
			//lets append it to the target
			var coords = this.target.trueCoordinates(), mousePosition = $.mousePosition(e);
			
			dragObj.target.css('visibility', dragObj.originalTargetStyle.visibility);
			dragObj.target.css('display', dragObj.originalTargetStyle.display);
			dragObj.target.css('position', '');
			dragObj.target.css('top', '');
			dragObj.target.css('left', '');
			
			dragObj.proxy.remove();

			//unlisten
			dragObj.events.window.mouseup.off();
			dragObj.events.body.mouseup.off();
			if($.BROWSER[0] == 'IE')
			{
				//start observing the mouse movement 
				//also keep track of this event so later we can stop listening;
				 dragObj.events.document.mousemove.off();
			}
			else
			{
				dragObj.events.window.mousemove.off();
			}
			
			//lets undo what startDrag has initialized
			return false;
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			//unlisten to all drop event
			$.parse(this.events.items, $.bind(function(key, evt) {
				this.events.items[key].start.off();
				this.events.items[key].drag.off();
				this.events.items[key].stop.off();
			}, this));
			
			//if a draggable was created lets destruct it 
			$.parse(this.draggables, function(k, el) {
				el.getSpace('draggable').__destruct();
			});
			
			//remove the sortable object from the target
			this.target.freeSpace('sortable');
			
			//destroy this class instance
			$.destroy(this);
		};
	}, $.Base);
	
	/*	Accordian - Marquee Class
	--------------------------------*/
	$.def('Accordian', new function() {
		this.CLASSNAME = 'Accordian';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (target, options) {
			this.target 	= $.Element(target);
			
			this.events = {};
			this.items = [];
			this.active = {};
			this.options = $.combine({
				direction: 'vertical',
				showAll: true,
				hideAll: true
			}, options || {});
			
			switch($.type(this.options.items))
			{
				case 'array':
					this.options.items = this.options.items;
					break;
				default: 
					this.options.items = [];
					break;
			}
			
			$.parse(this.options.items, $.bind(function(key, args) {
				this.add.apply(this, args);
			}, this));
		};
		
		/* Setter Methods
		-------------------------------------*/
		this.add = function(trigger, target, options) {
			trigger = $.Element(trigger);
			target = $.Element(target);
			//set default options
			options = $.combine({ 
				bindActive: 'click',
				show: false,
				height: false
			}, options);
			if(!options.height)
			{
				options.height = target.trueSize().height;
				target.css('overflow', 'hidden');
			}
			//add it to the store
			this.items.push([trigger, target, options]);
			
			var id = this.items.length - 1;
			
			if(options.show === false)
			{
				this.hide(id);
			}
			else
			{
				this.show(id);
			}
			
			//trigger before any actual changes to the dom occurs
			//we do this to allow the end developer to customize the outcome
			if(this.trigger('add', this, id) === false)
			{
				return false;
			}
			
			//store this bind method
			$.setSpace(this.events, 'items', id, options.bindActive, trigger.on(options.bindActive, $.bind(_eventToggle, this, id)));
			
			return this;
		};
		
		this.show = function(id) {
			var i = this.items[id];
			
			//trigger before any actual changes to the dom occurs
			//we do this to allow the end developer to customize the outcome
			if(this.trigger('show', this, id) === false)
			{
				return false;
			}
			
			if(this.active[id] === true)
			{
				i[1].css('display', 'none');
				this.active[id] = false;
				return this;
			}
			
			this.active[id] = true;
			i[1].css('display', 'block');
			
			if(i[2].height)
			{
				i[1].tween({height: [0+'px', parseInt(i[2].height)+'px']});
			}
			
		};
		
		this.hide = function(id) {
			var i = this.items[id];
			
			//trigger before any actual changes to the dom occurs
			//we do this to allow the end developer to customize the outcome
			if(this.trigger('hide', this, id) === false)
			{
				return false;
			}
			
			if(this.active[id] == false)
			{
				i[1].css('display', 'none');
				return this;
			}
			
			this.active[id] = false;
			if(i[2].height)
			{
				i[1].tween({height: [parseInt(i[2].height)+'px', 0+'px']}, function() {
					i[1].css('display', 'none');
				});
			}
			else
			{
				i[1].css('display', 'none');
			}
		};
		
		this.toggle = function(id) {
			var i = this.items[id];
			
			//trigger before any actual changes to the dom occurs
			//we do this to allow the end developer to customize the outcome
			if(this.trigger('toggle', this, id) === false)
			{
				return false;
			}
			
			if(!this.options.showAll)
			{
				$.parse(this.active, $.bind(function(key, active) {
					if(active && key != id)
					{
						this.hide(key);
					}
				}, this)); 
				
				if(this.options.showOne && this.active[id])
				{
					return this;
				}
			}
			
			if(!this.options.hideAll)
			{
				var oneIsActive = $.parse(this.active, $.bind(function(key, active) {
					if(active)
					{
						return false;
					}
				}, this)); 
				
				if(!oneIsActive && this.active[id])
				{
					return this;
				}
			}
			
			if(i[1].css('display') == 'none')
			{
				this.show(id);
			}
			else
			{
				this.hide(id);
				
			}
			return this;
		};
		
		/* Private Methods
		-------------------------------------*/
		var _eventToggle = function(e, id) {
			$.stop(e);
			this.toggle(id);
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			//for each tab, off
			if($.isSpace(this.events, 'items'))
			{
				$.parse($.getSpace(this.events, 'items'), $.bind(function(id, evt) {
					evt[this.tabs[id][2].bindActive].off();
				}, this));
			}
			$.destroy(this);
		};
	}, $.Base);
	
	/*	Widget - Box Class
	--------------------------------*/
	$.def('Box', new function() {
		this.CLASSNAME = 'Box';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (target, options) {
			this.dragObj	= null;
			this.resizeObj 	= null;
			this.events 		= {};
			
			if($.type(target, 'string') && !$.validAlphaNum(target))
			{
				this.target = $.toElement(target);
			}
			else
			{
				this.target = $.Element(target);
			}
			
			this.options = $.combine({
				head: null,			body: null,
				foot: null,			close: null,
				draggable: null,	resizable: null,
				append: $.Element(document.body)
			}, options);
			
			//handle dragging
			if(this.options.draggable)
			{
				if($.type(this.options.draggable, 'object'))
				{
					this.dragObj = $.Draggable(this.target, this.options.draggable);
				}
				else
				{
					this.dragObj = $.Draggable(this.target);
				}
				$.setSpace(this.events, 'drag', 'start', this.dragObj.on('start', $.bind(this.dragStart, this)));
				$.setSpace(this.events, 'drag', 'drag', this.dragObj.on('drag', $.bind(this.drag, this)));
				$.setSpace(this.events, 'drag', 'stop', this.dragObj.on('stop', $.bind(this.dragStop, this)));
			}
			
			//handle resizable
			if($.type(this.options.resizable, 'object') && $.type(this.options.resizable.handles, 'object'))
			{
				if($.type(this.options.resizable.options, 'object'))
				{
					this.resizeObj = $.Resizable(
						this.target, 
						this.options.resizable.handles,
						this.options.resizable.options);
				}
				else
				{
					this.resizeObj = $.Resizable(
						this.target, 
						this.options.resizable.handles);
				}
				
				$.setSpace(this.events, 'resize', 'start', this.resizeObj.on('start', $.bind(this.resizeStart, this)));
				$.setSpace(this.events, 'resize', 'resize', this.resizeObj.on('resize', $.bind(this.resize, this)));
				$.setSpace(this.events, 'resize', 'stop', this.resizeObj.on('stop', $.bind(this.resizeStop, this)));
			}
			
			//handle close node
			if(this.options.close)
			{
				$.setSpace(this.events, 'close', 'click', $.Element(this.options.close).on('click', $.bind(this.close, this)));
			}
		};
		
		/* Setter Methods
		-------------------------------------*/
		this.setHead = function(html) {
			if(this.options.head)
			{
				switch($.type(html))
				{
					case 'string':
						$.Element(this.options.head).html(html);
						break;
					case 'Element':
					case 'element':
						$.Element(this.options.head).html('');
						$.Element(this.options.head).append(html);
						break;
				}
				
			}
			return this;
		};
		
		this.setBody = function(html) {
			if(this.options.body)
			{
				switch($.type(html))
				{
					case 'string':
						$.Element(this.options.body).html(html);
						break;
					case 'Element':
					case 'element':
						$.Element(this.options.body).html('');
						$.Element(this.options.body).append(html);
						break;
				}
				
			}
			return this;
		};
		
		this.setFoot = function(html) {
			if(this.options.foot)
			{
				switch($.type(html))
				{
					case 'string':
						$.Element(this.options.foot).html(html);
						break;
					case 'Element':
					case 'element':
						$.Element(this.options.foot).html('');
						$.Element(this.options.foot).append(html);
						break;
				}
				
			}
			return this;
		};
		
		/* Event Methods
		-------------------------------------*/
		this.dragStart = function(scope, e, margin ) {
			return this.trigger('dragStart', scope, e, margin);
		};
		
		this.drag = function(scope, e, margin) {
			return this.trigger('drag', scope, e, margin);
		};
		
		this.dragStop = function(scope, e) {
			return this.trigger('dragStop', scope, e);
		};
		
		this.resizeStart = function(scope, e, mousePosition, targetMargin, targetPosition) {
			return this.trigger('resizeStart', scope, e, mousePosition, targetMargin, targetPosition);
		};
		
		this.resize = function(scope, e, coords, margin, size) {
			return this.trigger('resize', scope, e, coords, margin, size);
		};
		
		this.resizeStop = function(scope, e) {
			return this.trigger('resizeStop', scope, e);
		};
		
		this.open = function(trigger) {
			trigger = !(trigger === false);
			if(trigger && this.trigger('open', this) === false)
			{
				return false;
			}
			
			this.options.append.append(this.target);
			this.target.css('display', 'block');
			return this;
		};
		
		this.close = function(trigger) {
			trigger = !(trigger === false);
			if(trigger && this.trigger('close', this) === false)
			{
				return false;
			}
			
			this.target.remove();

			return this;
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			if(this.dragObj)
			{
				this.events.drag.start.off();
				this.events.drag.drag.off();
				this.events.drag.stop.off();
				this.dragObj.__destruct();
			}
			
			if(this.resizeObj)
			{
				this.events.resize.start.off();
				this.events.resize.resize.off();
				this.events.resize.stop.off();
				this.resizeObj.__destruct();
			}
			
			if(this.options.close)
			{
				$.Element(this.options.close).unbind('click', this.events.close.click);
			}
			
			$.destroy(this);
		};
	}, $.Base);
	
	/*	Widget - Dialog Class
	--------------------------------*/
	$.def('Dialog', new function() {
		this.CLASSNAME = 'Dialog';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (target, options) {
			
			this.__parent.__construct.call(this, target, options);
			
			this.options = $.combine({
				modal: true,	fixed: true,
				zIndex: 9998
			}, this.options || {});
			
			
			//set the target zIndex
			if(this.options.zIndex)
			{
				this.target.css('zIndex', this.options.zIndex)
			}
			
			if(this.options.modal)
			{
				//build a modal
				this.options.modal = _modal.call(this, this.options.modal, this.options.zIndex);
				
				//listen to click
				if(this.options.closeOnModalClick)
				{
					$.setSpace(this.events, 'modal', 'click', $.Element(this.options.modal).on('click', $.bind(this.close, this)));
				}
			}
			
			//set fixed
			if(this.options.fixed)
			{
				//center the target
				_center.call(this, this.target);
				//listen to resize
				$.setSpace(this.events, 'window', 'scroll', 	$.Element(window).on('scroll', $.bind(_scroll, this)));
				$.setSpace(this.events, 'resize', 'resizeStop', 	this.on('resizeStop', $.bind(_resizeStop, this)));
			}
			
			if($.BROWSER[0] == 'IE' && $.BROWSER[1] == 6)
			{
				this.IEfix = $.createElement('iframe');
			}
			
			$.setSpace(this.events, 'window', 'resize', $.Element(window).on('resize', $.bind(_fix, this)));
		};
		
		/* Public Methods
		-------------------------------------*/
		this.open = function() {
			//call parent open no trigger
			this.__parent.open.call(this, false);
			
			if(this.options.modal)
			{
				
				//stetch the modal over the document
				_cover.call(this, this.options.modal);
				
				if(this.IEfix)
				{
					
					this.IEfix.css({
						position:	'absolute',
						top:		this.options.modal.css('top'),		left:		this.options.modal.css('left'),
						width:		this.options.modal.css('width'),	height:		this.options.modal.css('height'),
						opacity:	'0',								filter:		'alpha(opacity=0)'
					});
					
					//insert the modal before the target
					this.target.before(this.IEfix);
				}
				
				//insert the modal before the target
				this.target.before(this.options.modal);
			}
			_center.call(this, this.target);
			
			if(this.__parent.open.call(this) === false)
			{
				return false;
			}
			
			return this;
		};
		
		this.close = function() {
			if(this.__parent.close.call(this) === false)
			{
				return false;
			}
			
			if(this.options.modal)
			{
				this.options.modal.remove();
				if(this.IEfix)
				{
					this.options.IEfix.remove();
				}
			}
			
			
			return this;
		};
		
		/* Private Methods
		-------------------------------------*/
		var _center = function(node) {
			var windowCenter = $.windowCenter(),
				windowOffset = $.windowOffset(),
				size = node.trueSize();
			
			node.css({
				position: 'relative',
				margin: 0,
				top: (windowCenter.top + windowOffset.top - (size.height / 2))+'px',
				left: (windowCenter.left + windowOffset.left - (size.width / 2))+'px'
			});

			return this;
		};
		
		var _cover = function(node) {
			node.css('display', 'none');
			//get the screen size
			var documentSize = $.documentSize();
			node.css({
				position:	'absolute',
				top:		0,								left:		0,
				width:		documentSize.width+'px',		height:		documentSize.height+'px'
			});
			node.css('display', '');
			return this;
		};
		
		var _fix = function(e) {
			if(this.options.fixed)
			{
				_center.call(this, this.target);
			}
			if($.type(this.options.modal, ['element', 'Element']))
			{
				_cover.call(this, this.options.modal);
			}
			
			return this;
		};
		
		var _modal = function(modal, zIndex) {
			switch($.type(modal))
			{
				case 'boolean':
					if(modal === true)
					{
						modal = $.createElement('div', {id: 'model'}).css({
							zIndex:		zIndex,		background:	'#000',							
							opacity:	'.4',		filter:		'alpha(opacity=40)'
						});
					}
					break;
				case 'string':
				case 'element':
				case 'Element':
					modal = $.Element(modal);
					break;
				default:
					return null;
			}
			
			modal = !!(modal) ? modal : $.createElement('div', {id: 'model'}).css({
				zIndex:		zIndex,		background:	'#000',							
				opacity:	'.4',		filter:		'alpha(opacity=40)'
			});
			
			return modal;
		};
		
		var _resizeStop = function(resize) {
			//lets bring the real node back and destroy the proxy
			resize.target.css('width', resize.proxy.css('width'));
			resize.target.css('height', resize.proxy.css('height'));
			resize.proxy.remove();
			resize.target.css('display', resize.originalTargetStyle.display);
			
			_center.call(this, this.target);
			
			return false;
		};
		
		var _scroll = function(e) {
			var original = this.target.css('display');
			this.target.css('display', 'none');
			_center.call(this, this.target);
			this.target.css('display', original);
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			if(this.options.modal)
			{
				this.events.modal.click.off();
			}
			
			this.events.window.resize.off();
			
			this.__parent.__destruct.call(this);
		};
	}, $.Box.prototype);
	
	/*	Widget - Marquee Class
	--------------------------------*/
	$.def('Marquee', new function() {
		this.CLASSNAME = 'Menu';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (target, container, options) {
			this.target 	= $.Element(target);
			this.container 	= $.Element(container);
			
			this.events = {};
			
			this.options = $.combine({
				draggable: false,
				direction: 'all',
				handles: {}
			}, options || {});
			
			if(this.options.draggable === true)
			{
				this.options.draggable = $.Draggable(this.target, {direction: this.options.direction}).on('drag', $.bind(_eventDrag, this));
			}
			
			//set the handles
			this.options.handles = this.options.handles || {};
			//listen to handles
			$.parse(this.options.handles, $.bind(function(key, el) {
				if(!el) 
				{
					return;
				}
				this.options.handles[key] = $.Element(el);
				$.setSpace(this.events, 'handle', key, 'click', this.options.handles[key].on('click', $.bind(this.scroll, this, key)));
			}, this));
		};
		
		/* Setter Methods
		-------------------------------------*/
		this.scroll = function(e, direction) {
			var position = {
					left: parseInt(this.target.css('left')) || 0, 
					top: parseInt(this.target.css('top')) || 0},
				targetSize = this.target.trueSize(),
				containerSize = this.container.trueSize(),
				origPosition = $.clone(position);
			//get the standard resize calculations
			if(targetSize.height > containerSize.height)
			{
				switch(true)
				{
					case direction.indexOf('n') != -1:
						position.top += containerSize.height;
						break;
					case direction.indexOf('s') != -1:
						position.top -= containerSize.height;
						break;
				}
				
				if(position.top > 0)
				{
					position.top = 0;
				}
				
				var maxTop = -1 * (targetSize.height - containerSize.height);
				
				if(position.top < maxTop)
				{
					position.top = maxTop;
				}
			}
			if(targetSize.width > containerSize.width)
			{
				switch(true)
				{
					case direction.indexOf('w') != -1: 
						position.left += containerSize.width;
						break;
					case direction.indexOf('e') != -1: 
						position.left -= containerSize.width;
						break;
				}
				
				//validate max scroll and min scroll
				if(position.left > 0)
				{
					position.left = 0;
				}
				
				var maxLeft = -1 * (targetSize.width - containerSize.width);
				
				if(position.left < maxLeft)
				{
					position.left = maxLeft;
				}
			}
			
			if(this.trigger('scroll', this, position, direction) === false)
			{
				return false;
			}
			
			this.target.tween({
				left: [origPosition.left+'px', position.left+'px'],
				top: [origPosition.top+'px', position.top+'px']
			}, 100);
		};
		
		/* Private Methods
		-------------------------------------*/
		var _eventDrag = function(dragObj, e, position) {
			_fixPosition.call(this, position);
		};
		
		var _fixPosition = function(position) {
			//validate max scroll and min scroll
			if(position.left > 0)
			{
				position.left = 0;
			}
			
			if(position.top > 0)
			{
				position.top = 0;
			}
			
			var targetSize = this.target.trueSize();
			var containerSize = this.container.trueSize();
			var maxTop = -1 * (targetSize.height - containerSize.height);
			var maxLeft = -1 * (targetSize.width - containerSize.width);
			
			if(position.left < maxLeft)
			{
				position.left = maxLeft;
			}
			
			if(position.top < maxTop)
			{
				position.top = maxTop;
			}
			
			return position;
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			$.destroy(this);
		};
	}, $.Base);
	
	/*	Widget - Menu Class
	--------------------------------*/
	$.def('Menu', new function() {
		this.CLASSNAME = 'Menu';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (target, options) {
			this.events = {};
			
			this.target = $.Element(target);
			this.items = [];
			//set up the options
			this.options = $.combine({hoverClass: 'hover'}, options || {});
			
			switch($.type(this.options.items))
			{
				case 'string':
					//assume is selector
					this.options.items = $.select(this.options.items, this.target).get();
					break;
				case 'Element':
				case 'element':
					this.options.items = [$.Element(this.options.items)];
					break;
				case 'array':
					this.options.items = this.options.items;
					break;
				default: 
					this.options.items = [];
			}
			
			$.parse(this.options.items, $.bind(function(key, el){
				this.add(el);
			}, this));
		};
		
		/* Setter Methods
		-------------------------------------*/
		this.add = function(el) {
			el = $.Element(el);	
			this.items.push(el);
			
			id = this.items.length -1;
			
			//trigger before any actual changes to the dom occurs
			//we do this to allow the end developer to customize the outcome
			if(this.trigger('add', this, id) === false)
			{
				return false;
			}
			
			//start listening to events
			$.setSpace(this.events, 'items', id, 'mouseover', el.on('mouseover', $.bind(this.show, this, el)));
			$.setSpace(this.events, 'items', id, 'mouseout', el.on('mouseout', $.bind(this.hide, this, el)));
			$.setSpace(this.events, 'items', id, 'click', el.on('click', $.bind(this.run, this, el)));
		};
		
		this.show = function(e, el) {
			//trigger
			if(this.trigger('show', this, e, el) === false)
			{
				return false;
			}
			
			$.stop(e);
			el.addClass(this.options.hoverClass);
			var parents = el.parents();
			//pop out the body
			parents.pop();
			
			parents.parse($.bind(function(i, el) {
				//get the id
				var uid = el.uid();
				$.parse(this.items, $.bind(function(j, el2) {
					if(el2.uid() == uid)
					{
						el.addClass(this.options.hoverClass);
						return false;
					}
				}, this));
			}, this))
			
			return this;
		};
		
		this.hide = function(e, el) {
			//trigger
			if(this.trigger('hide', this, e, el) === false)
			{
				return false;
			}
			
			$.stop(e);
			el.removeClass(this.options.hoverClass);
			var parents = el.parents();
			//pop out the body
			parents.pop();
			
			parents.parse($.bind(function(i, el) {
				//get the id
				var uid = el.uid();
				$.parse(this.items, $.bind(function(j, el2) {
					if(el2.uid() == uid)
					{
						el.removeClass(this.options.hoverClass);
						return false;
					}
				}, this));
			}, this))
			
			return this;
		};
		
		this.run = function(e, el) { 
			$.stop(e);
			//trigger
			if(this.trigger('run', this, el) === false)
			{
				return false;
			}
			
			this.hide(e, el);
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			//unlisten to all drop event
			$.parse(this.events.items, $.bind(function(key, evt) {
				this.events.items[key].mouseover.off();
				this.events.items[key].mouseout.off();
				this.events.items[key].click.off();
			}, this));
			$.destroy(this);
		};
	}, $.Base);
	
	/*	Widget - Tabs Class
	--------------------------------*/
	$.def('Tabs', new function() {
		this.CLASSNAME = 'Tabs';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (options) {
			//this is our event bind manager
			//we need this to successfully unbind events
			this.events = {};
			
			//this will act like a store for tabs
			this.tabs = [];
			
			//whenever a tab is removed it will make this tab active
			this.defaultTab = 0;
			
			//this will be used to track the active tab incase we need 
			//to make it inactive
			this.activeTab = null;
			
			//set default options
			this.options = $.combine({ 
				close: false, //the element on click will close
				sortable: false
			}, options);
			this.options.close = $.Element(this.options.close);
			
			switch($.type(this.options.items))
			{
				case 'array':
					this.options.items = this.options.items;
					break;
				default: 
					this.options.items = [];
					break;
			}
			
			$.parse(this.options.items, $.bind(function(key, args){
				this.add.apply(this, args);
			}, this));
			
			//if close tab, onclick close the active tab
			if($.type(this.options.close, 'Element'))
			{
				$.setSpace(this.events, 'close', 'click', this.options.close.on('click', $.bind(_eventRemove, this)));
			}
			
			if(this.options.sortable instanceof Array)
			{
				this.sortable = $.Sortable.apply($.Sortable, this.options.sortable);
				$.setSpace(this.events, 'sortable', 'stop', this.sortable.on('stop', $.bind(_eventStop, this)));
				$.setSpace(this.events, 'sortable', 'start', this.sortable.on('start', $.bind(_eventStart, this)));
			}
		};
		
		/* Setter Methods
		-------------------------------------*/
		this.add = function(tab, target, options) {
			tab = $.Element(tab);
			target = $.Element(target);
			//set default options
			options = $.combine({ 
				defaultTab: true, //whether to set this as active
				bindActive: 'click',
				activeClass: null,
				active: true,
				append: false
			}, options);
			
			//add it to the store
			this.tabs.push([tab, target, options]);
			
			var id = this.tabs.length - 1;
			
			//set the defaultTab if option.defaultTab is true
			if(options.defaultTab)
			{
				this.defaultTab = id;
			}
			
			//trigger before any actual changes to the dom occurs
			//we do this to allow the end developer to customize the outcome
			if(this.trigger('add', this, id) === false)
			{
				return false;
			}
			
			//store this bind method
			$.setSpace(this.events, 'tab', id, options.bindActive, tab.on(options.bindActive, $.bind(_eventActive, this, id)));
			//add to sortable
			if(this.sortable)
			{
				tab.setSpace('TabSortableId', id);
				this.sortable.add(tab);
			}
			if(options.active)
			{
				this.active(id);
			}
			return this;
		};
		
		this.remove = function(id) {
			//get the tab
			var tab = this.tabs[id];
			
			//trigger before any actual changes to the dom occurs
			//we do this to allow the end developer to customize the outcome
			if(this.trigger('remove', this, id) === false)
			{
				return false;
			}
			
			//get the bound callback and unbind this
			$.getSpace(this.events, 'tab', id, tab[2].bindActive).off();
			
			//remove
			tab[0].remove();
			tab[1].remove();
			
			if(this.tabs.length > 0)
			{
				if(this.defaultTab == this.activeTab)
				{
					this.defaultTab = 0;
				}
				this.active(this.defaultTab);
			}
			return this;
		};
		
		this.active = function(id) {
			//get the tab
			var tab = this.tabs[id];
			
			//trigger before any actual changes to the dom occurs
			//we do this to allow the end developer to customize the outcome
			if(this.trigger('active', this, id) === false)
			{
				return false;
			}
			
			//set current active tab as inactive
			if(typeof this.activeTab == 'number')
			{					
				this.inactive(this.activeTab);
			}
			
			//set this to active
			if(tab[2].activeClass)
			{
				tab[0].addClass(tab[2].activeClass);
			}
			
			tab[1].css('display', 'block');
			
			//if they want to append this somewhere
			if($.type(tab[2].append, ['Element', 'element']))
			{
				$.Element(tab[2].append).append(tab[1]);
			}
			
			//Flag this active flag now
			this.activeTab = id;
			
			return this;
		};
		
		this.inactive = function(id) {
			//get the tab
			var tab = this.tabs[id];
			
			//trigger before any actual changes to the dom occurs
			//we do this to allow the end developer to customize the outcome
			if(this.trigger('inactive', this, id) === false)
			{
				return false;
			}
			
			//set tab as inactive
			if(tab[2].activeClass)
			{
				tab[0].removeClass(tab[2].activeClass);
			}
			tab[1].css('display', 'none');
			
			return this;
		};
		
		/* Private Methods
		-------------------------------------*/
		var _eventRemove = function(e) {
			$.stop(e);
			if(typeof this.activeTab == 'number')
			{
				this.remove(this.activeTab);
			}
		};
		
		var _eventActive = function(e, id) {
			$.stop(e);
			this.active(id);
		};
		
		var _eventStart = function(dragObj, sortObj, e) {
			this.mouseDown = false;
			setTimeout($.bind(function() {
				this.mouseDown = true;
			}, this), 350);
		};
		
		var _eventStop = function(dragObj, sortObj, e) {
			var id = $.Element(dragObj.target).getSpace('TabSortableId');
			if(typeof id == 'number')
			{
				if(!this.mouseDown)
				{
					this.active(id);
				}
			}
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			$.parse(this.tabs, $.bind(function(id, tab) {
				tab.freeSpace('TabSortableId');
			}, this));
			
			//if close tab, off
			if($.isSpace(this.events, 'close', 'click'))
			{
				$.getSpace(this.events, 'close', 'click').off();
			}
			
			//for each tab, off
			if($.isSpace(this.events, 'tab'))
			{
				$.parse($.getSpace(this.events, 'tab'), $.bind(function(id, evt) {
					evt[this.tabs[id][2].bindActive].off();
				}, this));
			}
			
			if($.isSpace(this.events, 'sortable', 'stop'))
			{
				this.events.sortable.stop.off();
			}
			$.destroy(this);
		};
	}, $.Base);
	
	/*	Widget - Tooltip Class
	--------------------------------*/
	$.def('Tooltip', new function() {
		this.CLASSNAME = 'Tooltip';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (target, options) {
			options = $.combine({
				body: 	target //set a body
			}, options || {});
			
			this.__parent.__construct.call(this, target, options);
			
			this.events 	= {};
			this.tooltips 	= [];
			
			this.options = $.combine({
				showDelay: 		0,//timeout before showing tooltip
				hideDelay:		0,//timeout before hiding tooltip
				autohide: 		false,//flag to hide tooltip after hideDelay has been reached
				followMouse: 	false//tooltip follows mouse
			}, this.options || {});
			
			if($.BROWSER[0] == 'IE' && $.BROWSER[1] == 6)
			{
				this.IEfix = $.createElement('iframe');
			}
			
			this.target.css('position', 'relative');
		};
		
		/* Public Methods
		-------------------------------------*/
		this.add = function(trigger, target, options) {
			options = $.combine({
				bindShow: 	'mouseover',//event to listen to on the target to show tooltip
				bindHide: 	'mouseout',//event to listen to on the target to hide tooltip
				position:	'mouse',//initial position; 'top','bottom','left','right','mouse'
				zIndex: 	'auto',
				offsetLeft: 0,
				offsetTop: 	0
			}, options || {});
			
			//push it into the stack
			this.tooltips.push([$.Element(trigger), $.Element(target), options, false]);
			//so this way we can get an ID
			var id = this.tooltips.length-1;
			
			//listen to the trigger
			$.setSpace(this.events, 'tooltip', id, options.bindShow, $.Element(trigger).on(options.bindShow, $.bind(this.open, this, id)));
			$.setSpace(this.events, 'tooltip', id, options.bindHide, $.Element(trigger).on(options.bindHide, $.bind(this.close, this, id)));
			$.setSpace(this.events, 'target', id, options.bindHide, $.Element(this.target).on(options.bindHide, $.bind(this.close, this, id)));
			
			//return the id incase they want to remove it later
			return id;
		};
		
		this.remove = function(id) {
			$.getSpace(this.events, 'tooltip', id, this.tooltips[id][2].bindShow).off();
			$.getSpace(this.events, 'tooltip', id, this.tooltips[id][2].bindHide).off();
			$.getSpace(this.events, 'target', id, this.tooltips[id][2].bindHide).off();
		};
		
		this.open = function(e, id) {
			if(this.tooltips[id][3])
			{
				return;
			}
			$.stop(e);
			
			var mousePosition = $.mousePosition(e);
			
			this.tooltips[id][3] = true;
			
			setTimeout($.bind(function() {
				if(this.tooltips[id][3])
				{
					//force to close
					this.__parent.close.call(this, false);
					//set the position
					switch(this.tooltips[id][2].position)
					{
						case 'top':
							this.openTop(id);
							break;
						case 'left':
							this.openLeft(id);
							break;
						case 'bottom':
							this.openBottom(id);
							break;
						case 'right':
							this.openRight(id);
							break;
						default:
							this.openMouse(mousePosition, id);
							break;
					}
				}
				
				if(this.options.autohide)
				{
					setTimeout($.bind(function() {
						this.close(e, id);
					}, this), this.options.autohide);
				}
				
				//call parent open
				if(this.trigger('open', this) === false)
				{
					return false;
				}
			}, this, e), this.options.showDelay || 0);
		};
		
		this.openTop = function(id) {
			//set the body
			this.setBody(this.tooltips[id][1].clone());
			
			//call parent open no triggers
			this.__parent.open.call(this, false);
			
			var triggerCoords = this.tooltips[id][0].trueCoordinates(), 	//get the coords of the trigger
				targetCoords = this.target.trueCoordinates(), 			//get the coords of the box
				offset = {//find the difference between the top trigger and the bottom target
					top: targetCoords[1].top-triggerCoords[0].top, 
					left: targetCoords[0].left-triggerCoords[0].left},
				margin = {//adjust the margintop on the target to the offset found
					top: (parseInt(this.target.css('marginTop')) || 0) - offset.top + this.tooltips[id][2].offsetTop, 
					left: (parseInt(this.target.css('marginLeft')) || 0) - offset.left + this.tooltips[id][2].offsetLeft};
					
			//set the css
			this.target.css({
				marginTop: margin.top+'px',
				marginLeft: margin.left+'px',
				zIndex: this.tooltips[id][2].zIndex || 'auto'
			});
			
			return this;
		};
		
		this.openLeft = function(id) {
			//set the body
			this.setBody(this.tooltips[id][1].clone());
			
			//call parent open no triggers
			this.__parent.open.call(this, false);
			
			var triggerCoords = this.tooltips[id][0].trueCoordinates(), 	//get the coords of the trigger
				targetCoords = this.target.trueCoordinates(), 			//get the coords of the box
				offset = {//find the difference between the top trigger and the bottom target
					top: targetCoords[0].top-triggerCoords[0].top, 
					left: targetCoords[1].left-triggerCoords[0].left},
				margin = {//adjust the margintop on the target to the offset found
					top: (parseInt(this.target.css('marginTop')) || 0) - offset.top + this.tooltips[id][2].offsetTop, 
					left: (parseInt(this.target.css('marginLeft')) || 0) - offset.left + this.tooltips[id][2].offsetLeft};

			//set the css
			this.target.css({
				marginTop: margin.top+'px',
				marginLeft: margin.left+'px',
				zIndex: this.tooltips[id][2].zIndex || 'auto'
			});
			
			return this;
		};
		
		this.openBottom = function(id) {
			//set the body
			this.setBody(this.tooltips[id][1].clone());
			
			//call parent open no triggers
			this.__parent.open.call(this, false);
			
			var triggerCoords = this.tooltips[id][0].trueCoordinates(), 	//get the coords of the trigger
				targetCoords = this.target.trueCoordinates(), 			//get the coords of the box
				offset = {//find the difference between the top trigger and the bottom target
					top: targetCoords[0].top-triggerCoords[1].top, 
					left: targetCoords[0].left-triggerCoords[0].left},
				margin = {//adjust the margintop on the target to the offset found
					top: (parseInt(this.target.css('marginTop')) || 0) - offset.top + this.tooltips[id][2].offsetTop, 
					left: (parseInt(this.target.css('marginLeft')) || 0) - offset.left + this.tooltips[id][2].offsetLeft};
			
			//set the css
			this.target.css({
				marginTop: margin.top+'px',
				marginLeft: margin.left+'px',
				zIndex: this.tooltips[id][2].zIndex || 'auto'
			});
			
			return this;
		};
		
		this.openRight = function(id) {
			//set the body
			this.setBody(this.tooltips[id][1].clone());
			
			//call parent open no triggers
			this.__parent.open.call(this, false);
			
			var triggerCoords = this.tooltips[id][0].trueCoordinates(), 	//get the coords of the trigger
				targetCoords = this.target.trueCoordinates(), 			//get the coords of the box
				offset = {//find the difference between the top trigger and the bottom target
					top: targetCoords[0].top-triggerCoords[0].top, 
					left: targetCoords[0].left-triggerCoords[1].left},
				margin = {//adjust the margintop on the target to the offset found
					top: (parseInt(this.target.css('marginTop')) || 0) - offset.top + this.tooltips[id][2].offsetTop, 
					left: (parseInt(this.target.css('marginLeft')) || 0) - offset.left + this.tooltips[id][2].offsetLeft};
			
			//set the css
			this.target.css({
				marginTop: margin.top+'px',
				marginLeft: margin.left+'px',
				zIndex: this.tooltips[id][2].zIndex || 'auto'
			});
			
			return this;
		};
		
		this.openMouse = function(mousePosition, id) {
			//set the body
			this.setBody(this.tooltips[id][1].clone());
			
			//call parent open no triggers
			this.__parent.open.call(this, false);
			
			var offset = {}, margin, 
				targetCoords = this.target.trueCoordinates(), //get the coords of the box
				windowSize = $.windowSize(),			//get the size of the window
				targetSize = this.target.trueSize();			//get the size of the box
				
			switch(true)
			{
				case mousePosition.left+targetSize.width < windowSize.width:
					offset.left = targetCoords[0].left-mousePosition.left;
					break;
				default:
					offset.left = targetCoords[1].left-mousePosition.left-3;
					break;
			}
			
			switch(true)
			{
				case mousePosition.top+targetSize.height < windowSize.height:
					offset.top = targetCoords[0].top-mousePosition.top;
					break;
				default:
					offset.top = targetCoords[1].top-mousePosition.top-3;
					break;
			}
			
			margin = {//adjust the margintop on the target to the offset found
				top: (parseInt(this.target.css('marginTop')) || 0) - offset.top + this.tooltips[id][2].offsetTop, 
				left: (parseInt(this.target.css('marginLeft')) || 0) - offset.left + this.tooltips[id][2].offsetLeft};
			
			//set the css
			this.target.css({
				marginTop: margin.top+'px',
				marginLeft: margin.left+'px',
				zIndex: this.tooltips[id][2].zIndex || 'auto'
			});
			
			return this;
		};
		
		this.close = function(e, id) {
			if(!this.tooltips[id][3])
			{
				return;
			}
			$.stop(e);
			var targetCoords = this.target.trueCoordinates(), //get the coords of the box
				mousePosition = $.mousePosition(e); 		//get the mouse position
				
			if(!(targetCoords[0].left < mousePosition.left && 
				mousePosition.left < targetCoords[1].left && 
				targetCoords[0].top < mousePosition.top && 
				mousePosition.top < targetCoords[1].top))
			{
				this.tooltips[id][3] = false;
				setTimeout($.bind(function() {
					if(!this.tooltips[id][3])
					{
						//call parent close
						if(this.__parent.close.call(this) === false)
						{
							return false;
						}
					}
				}, this), this.options.hideDelay || 0);
			}
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			if(this.options.modal)
			{
				this.events.modal.click.off()
			}
			
			this.events.window.resize.off()
			
			this.__parent.__destruct.call(this);
		};
		
	}, $.Box.prototype);
	
	/*	Field - Slider Class
	--------------------------------*/
	$.def('Slider',  new function() {
		this.CLASSNAME 	= 'Slider';
		this.HORIZONTAL = 'horizontal';
		this.VERTICAL 	= 'vertical';
		this.LEFT		= 'left';
		this.TOP		= 'top';
		
		/* Constructor
		-------------------------------------*/
		this.__construct = function (options) {
			this.options = $.combine({
				values: [0,100],
				range: true,
				floating: false,
				direction: this.HORIZONTAL
			}, options || {});
			
			this.options.hValues = $.type(this.options.hValues, 'array') ? this.options.hValues : this.options.values;
			this.options.vValues = $.type(this.options.vValues, 'array') ? this.options.vValues : this.options.values;
			this.options.hRange = $.type(this.options.hRange, 'boolean') ? this.options.hRange : this.options.range;
			this.options.vRange = $.type(this.options.vRange, 'boolean') ? this.options.vRange : this.options.range;
			
			this.handle = $.toElement('<div class="handle"></div>', 0);
			this.field = $.toElement('<div class="slider"></div>', 0).append(this.handle);
			
			//make handle draggable
			this.drag = $.Draggable(this.handle, {
				container: this.field, 
				direction: this.options.direction
			});
			
			this.drag.on('start', $.bind(this.eventStart, this));
			this.drag.on('drag', $.bind(this.eventSlide, this));
			this.drag.on('stop', $.bind(this.eventStop, this));
			this.field.on('mousedown', $.bind(this.eventMousedown, this));
		};
		
		/* Public Methods
		-------------------------------------*/
		this.getPosition = function(percent, direction) {
			direction = direction == this.HORIZONTAL ? this.HORIZONTAL : this.VERTICAL;
			var property = direction == this.HORIZONTAL ? this.LEFT: this.TOP;
			
			
			//this is some tricky math
			
			//find out what percentage from the start of the container to the middle of the handle is
			//first get the coords of the handle
			var handleCoords = this.handle.trueCoordinates(),
			
			//radius = the size of the handle / 2
			radius = (handleCoords[1][property] - handleCoords[0][property]) / 2;
			
			//get the coords of the container
			containerCoords = this.field.trueCoordinates();
			
			//and trim the coords - the radius
			containerCoords[0][property] += radius;
			containerCoords[1][property] -= radius;
			//now get the size of the new container cooords
			containerSize = containerCoords[1][property] - containerCoords[0][property];
			
			return containerSize * percent * .01;
		};
		
		this.getPercent = function(position, size, direction) {
			direction = direction == this.HORIZONTAL ? this.HORIZONTAL : this.VERTICAL;
			var percent, property = direction == this.HORIZONTAL ? this.LEFT: this.TOP;
			//this is some tricky math
			
			//radius = the size of the handle / 2
			radius = size / 2;
			
			//get the coords of the container
			containerCoords = this.field.trueCoordinates();
			
			//and trim the coords - the radius
			containerCoords[0][property] += radius;
			containerCoords[1][property] -= radius;
			
			//take the radius and add that to the first coordinate 
			var tick = radius + position;
			
			//now get the size of the new container cooords
			containerSize = containerCoords[1][property] - containerCoords[0][property],
			
			//get the size of the tick
			tickSize = tick - containerCoords[0][property];
			
			//lastly calculate the percentages tickSize / containerSize
			percent = tickSize / containerSize;
			if(percent < 0)
			{
				percent = 0;
			}
			
			if(percent > 1)
			{
				percent = 1;
			}
			
			return percent;
		};
		
		this.getValue = function(percent, direction) {
			direction = direction == this.HORIZONTAL ? this.HORIZONTAL : this.VERTICAL;
			
			var value 	= null,
			values 		= direction == this.HORIZONTAL ? this.options.hValues : this.options.vValues,
			range 		= direction == this.HORIZONTAL ? this.options.hRange : this.options.vRange,
			property	= direction == this.HORIZONTAL ? this.LEFT: this.TOP,
			property2	= direction == this.HORIZONTAL ? 'width': 'height',
			coords		= this.handle.trueCoordinates(),
			position	= coords[0],
			size		= this.handle.trueSize();
			
			percent = percent || this.getPercent(position[property], size[property2], direction);
			if(values.length == 2 && $.type(values[0], 'number') && 
				$.type(values[1], 'number') && range)
			{
				//take the difference between the min and max
				range = values[1] - values[0];
				value = (percent * range) + values[0];
				if(!this.options.floating)
				{
					value = Math.round(value);
				}
			}
			else
			{
				//build some ticks
				$.parse(values, $.bind(function(tick, val) {
					//if the percent is > tick / value.length
					if(percent <= ((tick+1) / values.length))
					{
						value = val;
						return false;
					}
				}, this));
			}
			
			return value;
		};
		
		this.setValue = function(value, direction) {
			direction = direction == this.HORIZONTAL ? this.HORIZONTAL : this.VERTICAL;
			
			this.handle.css('position', 'relative');
			
			//trigger
			if(this.trigger('change', this, value, direction) === false)
			{
				return false
			}
			
			var percent, position,
			values 		= direction == this.HORIZONTAL ? this.options.hValues : this.options.vValues,
			property 	= direction == this.HORIZONTAL ? this.LEFT: this.TOP,
			range 		= direction == this.HORIZONTAL ? this.options.hRange : this.options.vRange;
			
			if(values.length > 2 || !range)
			{
				//find value
				$.parse(values, $.bind(function(tick, val) {
					if(val == value)
					{
						percent = (tick + .5) / values.length * 100;
						position = this.getPosition(percent, direction);
						this.handle.css(property, position+'px');
						return false;
					}
				}, this));
			}
			else
			{
				//some math here
				var diff = value - values[0];
				range = values[1] - values[0];
				diff = diff < 0 ? 0 : diff;
				percent = diff / range * 100;
				position = this.getPosition(percent, direction);
				this.handle.css(property, position+'px');
			}
			return this;
		};
		
		this.eventMousedown = function(e) {
			if(this.dragging)
			{
				this.dragging = false;
				return true;
			}
			
			//get the mouse position
			var mousePosition = $.mousePosition(e),
			//get the size of the handle 
			size = this.handle.trueSize(),
			//center the handle on the mouse click
			position = {
				left: mousePosition.left - (size.width / 2), 
				top: mousePosition.top - (size.height / 2), 
			},
			
			directions 	= [this.HORIZONTAL, this.VERTICAL],
			triggerValues = [];
			
			if($.has([this.HORIZONTAL, this.VERTICAL], this.options.direction))
			{
				directions = [this.options.direction];
			}
			
			$.parse(directions, $.bind(function(k, direction) {
				var property	= direction == this.HORIZONTAL ? this.LEFT: this.TOP,
					property2	= direction == this.HORIZONTAL ? 'width': 'height',
					percent		= this.getPercent(position[property], size[property2], direction),
					values 	= direction == this.HORIZONTAL ? this.options.hValues : this.options.vValues,
					range 	= direction == this.HORIZONTAL ? this.options.hRange : this.options.vRange,
					value 		= this.getValue(percent, direction);
				triggerValues.push(value);
				if($.type(value, 'undefined'))
				{
					return true;
				}	
				
				if(values.length > 2 || !range)
				{
					this.setValue(value, direction);
				}
				else
				{
					//set the raw value
					this.handle.css(property, this.getPosition(percent * 100, direction) + 'px');
				}
				
			}, this));
			
			var value;
			switch(this.options.direction)
			{
				case this.VERTICAL:
					value = triggerValues[0];
					break;
				case this.HORIZONTAL:
					value = triggerValues[0];
					break;
				default:
					value = {horizontal: triggerValues[0], vertical: triggerValues[1]};
					break;
			}
			
			this.drag.start(e);
		};
		
		this.eventStart = function() {
			this.dragging = true;
		};
		
		this.eventSlide = function(dragObj, e, relative, position) {
			var size = this.handle.trueSize(),
			triggerValues = [],
			directions = [this.HORIZONTAL, this.VERTICAL];
			
			if($.has([this.HORIZONTAL, this.VERTICAL], this.options.direction))
			{
				directions = [this.options.direction];
			}
			
			$.parse(directions, $.bind(function(k, direction) {
					var property	= direction == this.HORIZONTAL ? this.LEFT: this.TOP,
						property2	= direction == this.HORIZONTAL ? 'width': 'height',
						percent		= this.getPercent(position[property], size[property2], direction),
						value 		= this.getValue(percent, direction),
						values 		= direction == this.HORIZONTAL ? this.options.hValues : this.options.vValues,
						range 		= direction == this.HORIZONTAL ? this.options.hRange : this.options.vRange;
				
				triggerValues.push(value);	
				if($.type(value) == 'undefined')
				{
					return false;
				}
				
				if(values.length > 2 || !range)
				{
					$.parse(values, $.bind(function(tick, val) {
						if(val == value)
						{
							var percent = ((tick + .5) / values.length) * 100;
							relative[property] = this.getPosition(percent, direction);
							return false;
						}
					}, this));
				}
			}, this));
			
			var value;
			switch(this.options.direction)
			{
				case this.VERTICAL:
					value = triggerValues[0];
					break;
				case this.HORIZONTAL:
					value = triggerValues[0];
					break;
				default:
					value = {horizontal: triggerValues[0], vertical: triggerValues[1]};
					break;
			}
			
			return this.trigger('slide', this, value);
		};
		
		this.eventStop = function() {
			return this.trigger('change', this, this.getValue());
		};
		
		/* Destructor
		-------------------------------------*/
		this.__destruct = function() {
			$.destroy(this);
		};
	}, $.Base);
	
	return $;
})({path: 'http://eve.openovate.com/themes/admin/scripts/'});
Contact Us | openovate.com. All rights reserved.