
// DOM Object class (basic class for any DOM element that should be treated like an object

var DOMObject = Class.create(
{
	//CONSTRUCTOR
	// default_props	(object) : object containing all default properties of element
	// instance_props	(object) :	object containing all mutable properties of element
	// overwrite_props	(bool)	 :	boolean, determining whether to overwrite props with default
	// addition_props	(object) :	any additions to the props. Usefull only for Inheritance
	
	initialize: function(parent, default_props, instance_props, overwrite_props, addition_props)
	{
		//save parent
		this.parent = parent || $(document.body);
		//set default_props to global variable
		this.default_props = default_props || {};		
		//Define structure array to store all internal DOM elements
		this.structure = new Hash();
		//Call Build function to insert element
		this.build();
		//set props to the values defined by user
		this.set(instance_props || {}, overwrite_props || false, addition_props || {});
	},
	
	//DESCRIPTION: set function alters values of the dom objects properties and calls the flush function
	// props		(object)	: properties defined by user
	// overwrite	(bool)		: if true then overwire all properties to default, then apply user changes
	//	additions	(object)	: any additions (used for inheritance)
	
	set:function(props, overwrite, additions)
	{		
		//start with default props if overwite is enabled or if props are null (first run)
		if((this.props == null) || (overwrite))
		{
			//clone default props
			this.props = Object.clone(this.default_props);						
			//extend with any additions
			this.props = Object.extend(this.props, additions || {});
		}
		else //use currently existing props, but cast from hash back to object
		{
			this.props = this.props.toObject()
		}
				
		//extend props with user props and cash global props to hash
		this.props = Object.extend(this.props, props || {});
		this.props = $H(this.props);
		
		//call flush function
		this.flush();
	},
	
	//DESCRIPTION: this function allows an easy way to access and set elements in the structure hash
	S:function(key, value)
	{
		if(value == null)
		{
			return this.structure.get(key)
		}
		else
		{
			this.structure.set(key, value);
		}
	},
	
	//DESCRIPTION: this function allows an easy way to access and set elements in the props hash
	P:function(key, value)
	{
		if(value == null)
		{
			return this.props.get(key)
		}
		else
		{
			this.props.set(key, value);
		}
	},
	
	//DESCRIPTION: show and hide act on the root div, effectively showing it or hiding it
	hide:function(){this.structure.get('root').hide()},
	show:function(){this.structure.get('root').show()},
	
	//DESCRIPTION: this function is used to get the root of the object, so it can be inserted into other place
	getRoot:function(){return this.structure.get('root')},
	//DESCRIPTION: this function should update any elements in the structure hash using any properties from the properties hash
	flush:function(){},
	//DESCRIPTION: this function should create any dom elements used by this class and save them in the structure hash
	build:function()
	{
		var root = new Element('div');
		this.parent.insert({bottom : root});
		this.structure.set('root', root);
	},
	//DESCRIPTION: this function terminates the existance of the DOM Object
	destroy:function(complete)
	{
		this.S('root').remove();
		if(complete)
		{
			delete this;			
		}	
	}
	
});


// Container Class Should be used whever you need an Object that contains multiple DOM elements

var DOMContainer = Class.create(DOMObject,
{
	//CONSTRUCTOR
	//	This constructor builds a basic Container class for a DOMObject
	//	To initialize pass {element : klass} where element is the class of the "contained" object
	
	initialize:function($super, parent, instance_props, overwrite, addition_props)
	{
		default_props = {element : Class.create(), header : null, footer : null};
		$super(parent, default_props, instance_props || {}, overwrite, addition_props);
		this.S('elements', new Hash());
	},

	//CONSTRUCTOR
	//	This class builds a basic container class that can be used to hold many DOM objects
	//	It generates a root with three divs (HEADER) (CONTAINER) (FOOTER)
	
	build:function($super)
	{
		$super();
		var root = this.S('root');
		
		var header = $E({parent : root, style : {display : 'none', position : 'relative'}});
		var container = $E({parent : root});
		var footer = $E({parent : root, style : {display : 'none', position : 'relative'}});
		
		this.S('header', header);
		this.S('container', container);
		this.S('footer', footer);
	},
	
	flush:function()
	{
		if(this.P('lightContainer') == true)
		{
			var header = this.S('header');
			if(header != null)
			{
				header.remove();
				this.S('container').remove();
				this.S('footer').remove();				
			}
			this.S('container', this.S('root'));			
		}
		else
		{
			var h = this.P('header');
			var f = this.P('footer');
			
			if(h != null)
			{
				this.S('header').show();
			}
			
			if(f != null)
			{
				this.S('footer').show();
			}
		}
	},
	
	//DESCRIPTION
	//	This class appends an element to the end of the container.
	//INPUT
	//	see setElm() but omit 'position' parameter
	
	addElm:function(props, overwrite, additions)
	{
		var elements = this.S('elements')
		var position = elements.keys().length;
		this.setElm(position, props, overwrite);
	},
	
	//Description
	//	This function should be used to insert new elements into the DOM structure
	//Input
	//	position	(int)		: position of box
	//	props		(object)	: object containing initialization for the specified Box Object
	//	overwrite	(bool)		: boolean that determines whether box is overwritten with defauly values
	
	setElm:function(position, props, overwrite, additions)
	{
		var container = this.S('container');
		var elements = this.S('elements');
		var count = elements.keys().length;
		
		var elmKlass = this.P('element');
		
		//generate needed elements
		for(var i = count; i <= position; i ++)
		{			
			elements.set(i, new elmKlass(container));
		}
		
		elements.get(position).set(props, overwrite, additions);
	},
	
	//Description
	//	This function removes all boxes from DOM and internal datastructure after a given specified index.
	//Input
	//	index - index of last valid link
	//Returns
	//	NULL
	
	truncate:function(index)
	{
		var elements = this.S('elements');
		var count = elements.keys().length;
		
		if((index < count) && (index >= 0))
		{
			for(var i = index; i < count; i ++)
			{
				elements.get(i).getRoot().remove();
				elements.unset(i);
			}
		} 
	}
	
});

//DESCRIPTION
//	The Switch class should be used whenever you need an object that contains multiple object, but should only display one at a time. ex; a light box.

var DOMSwitch = Class.create(DOMObject,
{
	//CONSTRUCTOR
	//	This constructor builds a basic Container class for a DOMObject
	//	To initialize add elements via add method, and display them using {visible : key}
	
	initialize:function($super, parent, instance_props, overwrite, addition_props)
	{
		default_props = {visible : null, fill : true, appear : true};
		$super(parent, default_props, instance_props || {}, overwrite, addition_props);
		this.S('elements', new Hash());
	},
	
	build:function($super)
	{
		$super();
		var root = this.S('root');
		var temp = $E({parent : root, style : {display : 'none'}});
		this.S('temp', temp);
	},
	
	add:function(key, root, options)
	{
		//store element pointer
		options = options || {};
		this.S('elements').set(key, {root : root, onView : (options.onView || Prototype.emptyFunction), onHide : options.onHide || Prototype.emptyFunction});
		
		//hide and store into temp
		if(Object.isElement(root))
		{
			this.S('elements').get(key).root.hide();
		}
		this.S('temp').insert(this.S('elements').get(key).root);
	
	},
	
	flush:function()
	{
		//move old element to temp
		if((this.curRoot != null) && (this.P('visible') != this.curKey))
		{
			this.S('elements').get(this.curKey).onHide();
			this.curRoot.hide();
			this.S('temp').insert(this.curRoot);	
		}
		
		//insert new elements
		if((this.P('visible') != this.curKey) && (this.P('visible') != null))
		{
			this.curKey = this.P('visible');
			this.curRoot = this.S('elements').get(this.curKey).root;
			this.S('root').insert(this.curRoot || "");
			
			if(this.P('appear'))
			{
				this.curRoot.appear();
			}
			else
			{
				this.curRoot.show();
			}
			
			this.S('elements').get(this.curKey).onView();
			
		}
		
		//fill root
		if(this.P('fill'))
		{
			this.S('root').setStyle({width : '100%', height: '100%'});
		}
		else
		{
			this.S('root').setStyle({width : 'auto', height: 'auto'});
		}
	}

});


//DESCRIPTION
//	The Switch class should be used whenever you need an you need only 1 child div visible in a parent, but do not want to manipulate the DOM

var DOMLightSwitch = Class.create(DOMObject,
{
	//CONSTRUCTOR
	//	This constructor builds a basic Container class for a DOMObject
	//	To initialize add elements via add method, and display them using {visible : key}
	
	initialize:function($super, parent, instance_props, overwrite, addition_props)
	{
		default_props = {visible : null, fill : true, appear : true, allBlock : false};
		$super(parent, default_props, instance_props || {}, overwrite, addition_props);
		this.S('elements', new Hash());
	},
	
	build:function($super)
	{
		//leave blank so no divs will be created
	},
	
	add:function(key, root, options)
	{
		//store element pointer
		options = options || {};
		this.S('elements').set(key, {root : root, onView : (options.onView || Prototype.emptyFunction), onHide : options.onHide || Prototype.emptyFunction});
		
		//hide and store into temp
		if(Object.isElement(root))
		{
			this.S('elements').get(key).root.hide();
		}
	
	},
	
	flush:function()
	{
		//move old element to temp
		if((this.curRoot != null) && (this.P('visible') != this.curKey))
		{
			this.curRoot.hide();
			this.S('elements').get(this.curKey).onHide();	
		}
		
		//insert new elements
		if((this.P('visible') != this.curKey) && (this.P('visible') != null))
		{
			this.curKey = this.P('visible');
			this.curRoot = this.S('elements').get(this.curKey).root;
			
			if(this.P('appear'))
			{
				this.curRoot.appear();
			}
			else
			{
				(this.P('allBlock')) ? this.curRoot.style.display = 'block' : this.curRoot.show();
			}
			
			this.S('elements').get(this.curKey).onView();			
		}
	}

});

//Tool class : (basic class for any tool to go into)
//NOTE: A tool is a div that contains content should be though of as a separate HTML page

var DOMPage = Class.create(DOMObject,
{
	//CONSTRUCTOR
	// root		(DOM)	: any DOM element for tool to be inserted into
	
	initialize:function($super, parent, instance_props)
	{
		default_props = {position : 'absolute', top : '0px', left : '0px', width : '100%', height : '100%', background : '#FFFFFF', border : '0px none'};
		$super(parent, default_props, instance_props || {});	
	},
	
	flush:function()
	{
		var root = this.structure.get('root');
		root.setStyle(this.props.toObject());
	},
	
	insert:function(key, element)
	{
		var parent = this.structure.get(key);
		if(parent != null)
		{
			parent.insert(element);
		}
	},
	
	get:function(key)
	{
		return this.structure.get(key);
	}
});

//THIs class allows for easier management of JSON data

var DataObject = Class.create({

	initialize:function(instanceFields, props)
	{
		this.props = {onSave : Prototype.emptyFunction, onLoad : Prototype.emptyFunction, autoSave : true};
			Object.extend(this.props, props || {});
			this.props = $H(this.props);
		this.fieldStructure = Object.toJSON(instanceFields);
		this.clear();
	},
	
	P:function(key, value)
	{
		if(value == null)
		{
			return this.props.get(key)
		}
		else
		{
			this.props.set(key, value);
		}
	},
	
	F:function(key, value)
	{
		if(value == null)
		{
			return this.fields.get(key)
		}
		else
		{
			this.fields.set(key, value);
		}
	},
	
	clear:function()
	{
		this.fields = $H(this.fieldStructure.evalJSON());
	},
	
	toJSON:function()
	{
		return Object.toJSON(this.fields.toObject());
	},
	
	evalJSON:function(data)
	{
		if(Object.isString(data))
		{
			data = data.evalJSON();
			this.fields = $H(data);
			this.P('onLoad')();
		}
		else
		{
			this.clear();
		}
	},
	
	save:function()
	{
		this.P('onSave')(this.toJSON());
	},
	
	autoSave:function()
	{
		if(this.P('autoSave'))
		{
			this.save();
		}
	}
});

var dblClickAgent = Class.create(
{
	initialize:function(instance_props)
	{
		var default_props = {onDoubleClick : Prototype.emptyFunction, onClick : Prototype.emptyFunction, root : new Element('div'), areturns : false}
		this.props = Object.extend(default_props, instance_props || {});
		this.bindEvents();
	},
	
	bindEvents:function()
	{
		var root = this.props.root;
		if(root.tagName == "A")
		{
			root.onclick = this.preClick.bind(this);//bind directly for links
		}
		else
		{
			Event.observe(root, "click", this.preClick.bind(this));
		}
		Event.observe(root, "dblclick", this.doubleClick.bind(this));
	},
	
	preClick:function()
	{
		this.doubleClick = false;
		setTimeout(this.singleClick.bind(this), 150);
		return this.props.areturns;
	},	
	
	singleClick:function()
	{
		if(!this.doubleClick)
		{
			this.props.onClick();
		}
	},
	
	doubleClick:function()
	{
		this.doubleClick = true;
		this.props.onDoubleClick();
	}
});

var delayedMouseoverAgent = Class.create(
{
	initialize:function(instance_props)
	{
		var default_props = {delay : 500, onMouseover : Prototype.emptyFunction, root : new Element('div')}
		this.props = Object.extend(default_props, instance_props || {});
		this.bindEvents();
	},
	
	bindEvents:function()
	{
		var root = this.props.root;
		Event.observe(root, "mouseover", this.preMouse.bind(this));
		Event.observe(root, "mouseout", this.mouseOut.bind(this));
	},
	
	preMouse:function()
	{
		this.mouseover = true;
		setTimeout(this.mouseOver.bind(this), this.props.delay);
	},
	
	mouseOver:function()
	{
		if(this.mouseover)
		{
			this.props.onMouseover();
		}
	},
	
	mouseOut:function()
	{
		this.mouseover = false;
	}
});

var delayedKeyDownAgent = Class.create(
{
	initialize:function(instance_props)
	{
		var default_props = {delay : 300, event : 'keyup', action : Prototype.emptyFunction, root : new Element('div')}
		this.props = Object.extend(default_props, instance_props || {});
		this.count = 0;
                this.bindEvents();
	},
	
	bindEvents:function()
	{
		var root = this.props.root;
		Event.observe(root, this.props.event, this.event.bind(this, null, null));
	},
	
	event:function(lastTime, count, event)
	{		
		var time = new Date().getTime();
		lastTime = lastTime || time;
		
        var delta = time - lastTime;
		if(delta >= this.props.delay)
		{
			if(this.count == count)
	        {
	            this.props.action(event);
	        }
		}
		else
		{
			this.count ++;
            setTimeout(this.event.bind(this, lastTime, (this.count + 0)), this.props.delay + 30, event);
		}

	}
});

var delayedMouseoutAgent = Class.create(
{
	initialize:function(instance_props)
	{
		var default_props = {delay : 500, onMouseout : Prototype.emptyFunction, root : new Element('div')}
		this.props = Object.extend(default_props, instance_props || {});
		this.bindEvents();
	},
	
	bindEvents:function()
	{
		var root = this.props.root;
		Event.observe(root, "mouseout", this.preMouse.bind(this));
		Event.observe(root, "mouseover", this.mouseOver.bind(this));
	},
	
	preMouse:function()
	{
		this.mouseover = true;
		setTimeout(this.mouseOut.bind(this), this.props.delay);
	},
	
	mouseOut:function()
	{
		if(this.mouseover)
		{
			this.props.onMouseout();
		}
	},
	
	mouseOver:function()
	{
		this.mouseover = false;
	}
});


var documentMouseMoveObserver = 
{
	initalize:function()
	{
		if(!this.active)
		{
			this.active = true;
			Event.observe(document, "mousemove", this.listen.bind(this));
		}
	},
	
	start:function()
	{
		this.initalize();
		this.moved = false;
	},
	
	listen:function()
	{
		if(this.moved == false){this.moved = true}
	},
	
	state:function()
	{
		return this.moved;
	}
}

var DraggableClickListener = Class.create(
{
	initialize:function(root, instance_props)
	{
		var default_props = {onClick : Prototype.emptyFunction}
		Object.extend(default_props, instance_props || {});
		this.root = root;
		this.p = default_props;	
		this.bindEvents();
	},
	
	bindEvents:function()
	{
		Event.observe(this.root, "mousedown", this.mouseDownListener.bind(this));
		Event.observe(this.root, "mouseup", this.mouseUpListener.bind(this));
	},
	
	mouseDownListener:function()
	{
		documentMouseMoveObserver.start();
	},
	
	mouseUpListener:function(event)
	{
		if((event.isLeftClick() || event.metaKey || event.ctrlKey) && (documentMouseMoveObserver.state() == false))
		{
			this.p.onClick(event);
			return true;
		}
		return false;
	}
});

var html5Input = Class.create(DOMObject,
{
	initialize:function($super, parent, styles, e, noWrap)
	{
		default_props = {};
		
		this.events = Object.extend({onFocus : Prototype.emptyFunction, onBlur : Prototype.emptyFunction}, e || {})
		this.styles = styles;
		this.dcl = this.documentClickListener.bindAsEventListener(this);
		this.noWrap = noWrap;
		$super(parent, default_props, {});
		
		//set init conditions
		this.focused = false;
		this.S('root').setStyle({background : 'transparent', border : '1px solid transparent'});
		
	},
	
	build:function($super)
	{
		$super();
		var root = this.S('root');
			var input = $E({parent : root, style : {minHeight : this.styles.lineHeight, wordWrap : 'break-word', outline : '0px none'}});
			input.addClassName('HTML5INPUT');
			input.onclick = this.focus.bind(this);
			var bind = this;
			input.onkeydown = function(e){if(e.keyCode==13){e.preventDefault(); bind.S('input').blur(); bind.blur()}}	
		this.S('input', input);
		
		if(this.noWrap)
		{
			input.setStyle({whiteSpace : 'noWrap'});
		}
	},
	
	flush:function()
	{
		this.S('root').setStyle(this.styles);
	},
	
	update:function(text)
	{
		this.S('input').update(text);
	},
	
	value:function()
	{
		return this.S('input').innerHTML;
	},
	
	focus:function()
	{
		if(this.focused == false)
		{
			this.focused = true;
			this.S('root').setStyle({background : '#FFFFFF', border : '1px solid #DDDDDD'});
			Event.observe(document, "click", this.dcl);
			this.S('input').setAttribute('contenteditable', 'true');
			this.S('input').select();
			this.events.onFocus();		
		}
	},
	
	blur:function()
	{
		this.S('root').setStyle({background : 'transparent', border : '1px solid transparent'});
		Event.observe(document, "click", this.dcl);	
		Event.stopObserving(document, "click", this.dcl);
		this.S('input').setAttribute('contenteditable', 'false');
		this.events.onBlur();
		this.focused = false;		
	},
	
	documentClickListener:function(event)
	{
		var se = event.element();//event.originalTarget || event.srcElement;
		if(se != this.S('input'))
		{
			this.blur();
		}
	}
});


//This Function was developed to make Element creation easy and fast

function $E(user_pars)
{
	//Define Defaults
	pars = {type : 'div', attr : {}, inner : null, style : {},  styleE : {}, parent : $(document.body), insert : 'bottom'}
	//Extend parameters
	Object.extend(pars, user_pars);
	//create DOM element
	var E = new Element(pars.type, pars.attr);
	//update content
	if(pars.inner != null){E.update(pars.inner)}
	//extend and styles
	var style = Object.clone(pars.style);
	Object.extend(style, pars.styleE);	
	E.setStyle(style);
	//specify insert type and insert
	var I = new Object;
	I[pars.insert] = E;
	pars.parent.insert(I);
	//return element
	return E;
}

//MULTI REQUEST CLASS:
//	This class allows you to send multiple ajax requests at the same time

var MultiRequestAgent = Class.create(
{
	//CONSTRUCTOR
	//	props	(Object)	: url - url for all data requests, onLoad : function to call when all data has been recieved after a request
	
	initialize:function(props)
	{
		this.props = 
		{
			//agent props
			url : 'http://www.gotproject.com/php/tools/data.php', 
			postVar : 'queue',
			idField : 'request', 
			//callback props
			onLoad : Prototype.emptyFunction, 
			//inner props
			requests : [],
			requestType : 'get'
		}
		
		Object.extend(this.props, props || {});
		this.props = $H(this.props);
	},
	
	//DESCRIPTION
	//	This function sets the onLoad function for the class. This function will be called when a request has loaded
	
	setOnLoad:function(fun)
	{
		if(Object.isFunction(fun))
		{
			this.props.set('onLoad', fun);
		}
	},
	
	setOnFail:function(fun)
	{
		if(Object.isFunction(fun))
		{
			this.props.set('onFail', fun);
		}	
	},
	
	//DESCRIPTION:
	//	This function can be used to add a request to the queue
	//INPUTS:
	//	key		(string)	: string identifying the request
	//	object	(object)	: object containing parameters and values
	
	add:function(key, object)
	{
		var field = this.props.get('idField');
		var requests = this.props.get('requests');
		var index = requests.length;
		
		if(this.props.get('requestType') == 'get')
		{
			object['ANTICACHE'] = Math.floor(Math.random()*100000);
		}
		
		object[field] = key;
		requests.push(object);
		return index;						
	},
	
	//DESCRIPTION:
	//	This function clears the request queue
	
	clear:function()
	{
		this.props.set('requests', new Array());	
	},
	
	//DESCRIPTION
	// call this function to launch the request queue directly
	
	launch:function()
	{
		var requests = this.props.get('requests');
		if(requests.length != 0)
		{
			this.ajaxRequest(this.pack(this.props.get('requests')));
			this.clear();
		}
		else
		{
			this.props.get('onFail');//launch blank response to clear dispatch launching variable
		}
	},

	pack:function(object)
	{
		REQUEST = new Object();
		REQUEST[this.props.get('postVar')] = Object.toJSON(object);
		return $H(REQUEST).toQueryString();	
	},
	
	ajaxRequest:function(parameters, method)
	{
		if(parameters.length > 2000){method = 'post'}
		new Ajax.Request(this.props.get('url'), 
		{
			method: method || this.props.get('requestType'),
	  		parameters : parameters,
	  		onComplete : this.responseEvent.bind(this)
		});	
	},
	
	responseEvent:function(response)
	{
		if(response.status == 200)
		{			
			try
			{
				this.response = response.responseText.evalJSON();
				this.props.get('onLoad')();
			}
			catch(err)
			{
				alert(err);
				alert("Oh no! An error occurred.\nPress ok to reload this page.");
				location.reload(true);				
			}		
		}
		else
		{
			this.props.get('onLoad')();//call to clear disatch
		}		
	},
	
	//DESCRIPTION
	//	call this function to get the result of the query
	
	get:function(index)
	{
		index = index || 0;
		return this.response[index];
	}
});

DataDispatch = Class.create(
{
	initialize:function(instance_props)
	{
		var default_props = 
		{
			//program props
			server : new MultiRequestAgent(), 
			delay : 50,
			queue : [],
			requests : new Hash(),
			//internal vars
			launchLock : false,
			dataLock : false
		};
		
		this.props = Object.extend(default_props, instance_props || {});
		this.props.server.setOnLoad(this.dispatch.bind(this));	
		this.props.server.setOnFail(this.failCatch.bind(this));
	},
	
	//Description:
	//	This is the only "public" function that should be called by external classes.
	
	add:function(type, object, fun)
	{
		fun = fun || Prototype.emptyFunction;
		object = object || {};
		
		if(Object.isString(type) && Object.isFunction(fun))
		{		
			if(!this.props.dataLock)
			{
				var queue = this.props.queue;
				queue.push({type : type, object : object, fun : fun});
				setTimeout(this.preLaunch.bind(this, queue.length + 0), this.props.delay);
			}
			else
			{
				setTimeout(this.add.bind(this, key, object, fun), 50);
			}
		}
	},
	
	preLaunch:function(rcount)
	{
		if(this.props.queue.length == rcount)
		{
			if(!this.props.launchLock)
			{
				this.props.launchLock = true;
				//build request object
				this.props.dataLock = true;
				this.buildRequestData();
				this.props.queue = [];
				this.props.dataLock = false;
				this.props.server.launch();			
			}
			else
			{
				//take care of possibility of a launch occuring while anouther is running
				setTimeout(this.preLaunch.bind(this, rcount), 200);
			}
		}
	},
	
	buildRequestData:function()
	{
		//clear requests
		this.requests = new Array();
		var queue = this.props.queue;
		var server = this.props.server;
			server.clear();
		
		for(var i = 0; i < queue.length; i ++)
		{
			var props = queue[i];
			var id = server.add(props.type, props.object);
			this.requests[id] = props.fun;			
		}
	},	
	
	dispatch:function()
	{
		var requests = this.requests;
		var len = requests.length;
		for(var i = 0; i < len; i++)
		{
			var data = this.props.server.get(i);
			var fun = requests[i];
			if(Object.isFunction(fun))
			{
				fun(data);
			}
		}
		
		//clear lock
		this.props.launchLock = false;
	},
	
	failCatch:function()
	{
		this.props.launchLock = false;
	}
	
});

var EasyMessageBox = Class.create(DOMObject, 
{
	initialize:function($super, parent, instance_props)
	{
		var default_props = {message : "", background : '#FFFFFF', border : '#000000', color : '#333333', rclass : 'easymessageround', duration : 2000, minWidth : '100px', start : false};
		$super(parent, default_props, instance_props);
	},
	
	build:function($super)
	{
		$super();
		var root = this.S('root');
			root.setStyle({margin : '0px auto'});
			var child = $E({parent : root, style : {verticalAlign : 'middle'}});
			var message = $E({parent : child, style : {margin : '0px', padding : '3px 15px', textAlign : 'center'}});
			message.hide();
		
		if(Prototype.Browser.IE)
		{
			root.setStyle({position: 'absolute', left: '50%'});
			child.setStyle({position: 'relative', left: '-50%'});
		}
		else
		{
			root.setStyle({display : 'table'});
		}
		
		this.S('message', message);		
	},
	
	flush:function()
	{
		this.S('message').update(this.P('message'));
		this.S('message').setStyle({background : this.P('background'), border : '1px solid ' + this.P('border'),  borderTop : '0px none', color : this.P('color'), minWidth : this.P('minWidth')});
		this.S('message').setAttribute('class', this.P('rclass'));
		
		if(this.P('start'))
		{
			this.show();
		}
	},
	
	show:function()
	{
		Effect.Appear(this.S('message'), {duration: 1.0});
		if(Object.isNumber(this.P('duration')))
		{
			setTimeout(this.fade.bind(this), this.P('duration'));
		}
	},
	
	fade:function()
	{
		Effect.Fade(this.S('message'), {duration: 1.0, afterFinish : this.destroy.bind(this, false)});
	}

});

var EasyMessage = Class.create(DOMContainer,
{
	//CONSTRUCTOR
	
	initialize:function($super, parent, instance_props)
	{
		var default_props = {zIndex : '20', element : EasyMessageBox};
		$super(parent, default_props, instance_props);
		this.throbbers = Array();
	},
	
	build:function($super)
	{
		$super();
		var root = this.S('root');
			root.setStyle({position : 'relative'});
		var container = this.S('container');
		container.setStyle({position : 'fixed', top : '0px', left : '0px', width : '100%'});
	},
	
	flush:function()
	{
		this.S('root').setStyle({zIndex : this.P('zIndex')});
	},
	
	out:function(message, instance_props)
	{
		instance_props = instance_props || {};
		instance_props.message = message;
		var default_props = {type : 0, duration : 2000, start : true, message : ""};
		Object.extend(default_props, instance_props);
		
		if(default_props.type == 0)
		{
			Object.extend(default_props, {background : '#FFF09F', border : '#FFE65F'});
		}
		else
		if(default_props.type == 1)
		{
			Object.extend(default_props, {background : '#FF7F7F', border : '#FF4F4F'});
		}
		else
		if(default_props.type == 2)
		{
			Object.extend(default_props, {message : instance_props.message + "...", background : '#FFF09F', border : '#FFE65F', duration : false, start : true});
			var elements = this.S('elements')
			var position = elements.keys().length;
			this.throbbers.push(position);
		}		
		
		this.addElm(default_props);		
	},
	
	clearThrobber:function()
	{
		var t = this.throbbers;
		var len = t.length;
		
		for(var i = 0; i < len; i ++)
		{
			this.setElm(t.pop(), {duration : 1000});
		}
	}
});

function startEasyMessage()
{
	this.Message = new EasyMessage();
}
Event.observe(window, "load", this.startEasyMessage);

//css style functions
/*Element.prototype.round = function(props)
{
	if(Prototype.Browser.IE){return}
	
	if(Object.isNumber(props.r))
	{
		this.style.setProperty("-moz-border-radius", props.r + "px", "");
		this.style.setProperty("-webkit-border-radius", props.r + "px", "");
	}
	else
	{
		var p = {tr : 0, tl : 0, br : 0, bl : 0}
		Object.extend(p, props);	
		
		this.style.setProperty("-moz-border-radius-topright", p.tr + "px", "");
		this.style.setProperty("-moz-border-radius-topleft", p.tl + "px", "");
		this.style.setProperty("-moz-border-radius-bottomright", p.br + "px", "");
		this.style.setProperty("-moz-border-radius-bottomleft", p.bl + "px", "");		
		
		this.style.setProperty("-webkit-border-top-right-radius", p.tr + "px", "");
		this.style.setProperty("-webkit-border-top-left-radius", p.tl + "px", "");
		this.style.setProperty("-webkit-border-bottom-right-radius", p.br + "px", "");
		this.style.setProperty("-webkit-border-bottom-left-radius", p.bl + "px", "");
	}
}

Element.prototype.shadow = function(props)
{
	if(Prototype.Browser.IE){return}
	
	var p = {x : 0, y : 0, s : '3px', c : 'rgba(0,0,0,.5)'}
	Object.extend(p, props);
	
	this.style.setProperty("-moz-box-shadow", p.x + 'px ' + p.y + 'px ' + p.s + ' ' + p.c, "");
	this.style.setProperty("-webkit-box-shadow", p.x + 'px ' + p.y + 'px ' + p.s + ' ' + p.c, "");	
}

Element.prototype.unshadow = function()
{
	if(Prototype.Browser.IE){return}
	
	this.style.setProperty("-moz-box-shadow", "", "");
	this.style.setProperty("-webkit-box-shadow", "", "");	
}*/

var elementStyleMethods = 
{
	round:function(elm, props)
	{
		if(Prototype.Browser.IE){return}
		
		if(Object.isNumber(props.r))
		{
			elm.style.setProperty("-moz-border-radius", props.r + "px", "");
			elm.style.setProperty("-webkit-border-radius", props.r + "px", "");
			elm.style.setProperty("border-radius", props.r + "px", "");
		}
		else
		{
			var p = {tr : 0, tl : 0, br : 0, bl : 0}
			Object.extend(p, props);	
			
			elm.style.setProperty("-moz-border-radius-topright", p.tr + "px", "");
			elm.style.setProperty("-moz-border-radius-topleft", p.tl + "px", "");
			elm.style.setProperty("-moz-border-radius-bottomright", p.br + "px", "");
			elm.style.setProperty("-moz-border-radius-bottomleft", p.bl + "px", "");		
			
			elm.style.setProperty("-webkit-border-top-right-radius", p.tr + "px", "");
			elm.style.setProperty("-webkit-border-top-left-radius", p.tl + "px", "");
			elm.style.setProperty("-webkit-border-bottom-right-radius", p.br + "px", "");
			elm.style.setProperty("-webkit-border-bottom-left-radius", p.bl + "px", "");
			
			elm.style.setProperty("border-top-right-radius", p.tr + "px", "");
			elm.style.setProperty("border-top-left-radius", p.tl + "px", "");
			elm.style.setProperty("border-bottom-right-radius", p.br + "px", "");
			elm.style.setProperty("border-bottom-left-radius", p.bl + "px", "");
		}
	},

	shadow:function(elm, props)
	{
		if(Prototype.Browser.IE){return}
		
		var p = {x : 0, y : 0, s : '3px', c : 'rgba(0,0,0,.5)'}
		Object.extend(p, props);
		
		elm.style.setProperty("-moz-box-shadow", p.x + 'px ' + p.y + 'px ' + p.s + ' ' + p.c, "");
		elm.style.setProperty("-webkit-box-shadow", p.x + 'px ' + p.y + 'px ' + p.s + ' ' + p.c, "");
		elm.style.setProperty("box-shadow", p.x + 'px ' + p.y + 'px ' + p.s + ' ' + p.c, "");	
	},

	unshadow:function(elm)
	{
		if(Prototype.Browser.IE){return}
		
		elm.style.setProperty("-moz-box-shadow", "", "");
		elm.style.setProperty("-webkit-box-shadow", "", "");
		elm.style.setProperty("box-shadow", "", "");	
	}

}
Element.addMethods(elementStyleMethods);

var Resizeable = Class.create(
{
	initialize:function(root, props)
	{
		//set root
		this.structure = new Hash();
		this.structure.set('root', root);
		
		//set props
		this.props = {rootHeight : 200, rootWidth : 200, top : 0, left : 0, handleHeight : 20, handleWidth : 20, background : 'transparent', vOffset : 0, onStart : Prototype.emptyFunction, onEnd : Prototype.emptyFunction, handleZIndex : 100}
			Object.extend(this.props, props || {});
			this.props = $H(this.props);		
		this.buildHandle();
		this.startObserving();
	},
	
	buildHandle:function()
	{
		var root = this.structure.get('root');
			root.setStyle({position : 'absolute', top : this.props.get('top') + 'px', left : this.props.get('left') + 'px', width : this.props.get('rootWidth') + 'px', height : this.props.get('rootHeight') + 'px'});
		var handle = new Element('div');
			handle.setStyle({position : 'absolute', bottom : (0 - this.props.get('vOffset')) + 'px', right : '0px', height : this.props.get('handleHeight') + 'px', width : this.props.get('handleWidth') + 'px', background : this.props.get('background'), cursor : 'se-resize', zIndex : this.props.get('handleZIndex')})
			$E({parent : handle}) // add empty div to from being able to be dragged away by user
		this.structure.set('handle', handle);
		root.insert({bottom : handle});
	},
	
	startObserving:function()
	{
		var handle = this.structure.get('handle');		
		this.moveListener = this.moveListener.bindAsEventListener(this);		
		Event.observe(handle, 'mousedown', this.liquify.bind(this));
		Event.observe(handle, 'mouseup', this.solidify.bind(this));	
	},
	
		liquify:function(event)
		{
			var rd = this.structure.get('root').getDimensions();
			this.props.set('position', {x : event.pointerX(), y : event.pointerY(), rx : rd.width, ry : rd.height});
			Event.observe(window,'mousemove', this.moveListener);
			this.props.get('onStart')();
		},
		
		solidify:function()
		{
			Event.stopObserving(window,'mousemove', this.moveListener);
			this.props.get('onEnd')();
		},
	
	moveListener:function(event)
	{
		var initMousePos = this.props.get('position');
		var width = initMousePos.rx + (event.pointerX() - initMousePos.x);
		var height = initMousePos.ry + (event.pointerY() - initMousePos.y);
		
		var root = this.structure.get('root');
		root.setStyle({width : width + 'px', height : height + 'px'});
	}
	
});

var zIndexManager = Class.create(
{
    initialize:function(props)
    {
        this.p = {index : 10}
        Object.extend(this.p, props)
        this.p.items = new Array();        
    },
    
    add:function(root)
    {
        var l = this.raise.bind(this, root);
        Event.observe(root, "click", l);
        this.p.items.push({r : root, i : this.p.max, l : l});  
    },
    
    remove:function(root)
    {
		var items = this.p.items;
		var l = items.length;
		while(l--)
		{
		    if(items[l].r == root)
		    {
				var i = items[l];
				Event.stopObserving(i.r, "click", i.l);
				items[l] = null;
				this.p.items = items.compact();
				return true;
		    }
		}
		return false;
    },
    
    raise:function(root)
    {
        if(root != this.lastClick)
        {
            root.style.zIndex = this.p.index;
            this.p.index ++;
            this.lastClick = root;
        }
    }
});
var ZINDEXMANAGER = new zIndexManager();
