function $(id)
{
	return DOM.get(id);
};

function uniqid()
{
	var currDate = new Date();
	var uniqId = String(currDate.getUTCMilliseconds().toString() + Math.random());
	
	var uniqIdChars = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
	
	var uniqIdStr = "";
	
	for(var charId = 0; charId < uniqId.length; charId++)
	{
		var currChar = uniqId.substring(charId, charId + 1);
		
		if (currChar != '.')
		{
			uniqIdStr += uniqIdChars[currChar];
		}
	}
	
	return uniqIdStr;
};

function log(content)
{
	if (window.console) console.log(content);
};

[].indexOf || (Array.prototype.indexOf = function(v){
       for(var i = this.length; i-- && this[i] !== v;);
       return i;
});

if (!document.getElementsByClassName)
{
	document.getElementsByClassName = function(className, parentElement)
	{
		if (!!document.evaluate)
		{
			var expression = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
			var results = [];
		    var query = document.evaluate(expression, parentElement || document,
		      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
		    for (var i = 0, length = query.snapshotLength; i < length; i++)
		      results.push(query.snapshotItem(i));
		    return results;
		}
		else
		{
			var children = (parentElement || document.body).getElementsByTagName('*');
			var elements = [], child;
			for (var i = 0, length = children.length; i < length; i++) {
				child = children[i];
				if (DOM.hasClassName(child, className))
				elements.push(child);
			}
			return elements;
		}
	};
}

Array.prototype.clone = function()
{
	var clone = [];
	for (var i = 0; i < this.length; i++)
	{
		if (typeof this[i] == 'object')
		{
			if ('join' in this[i] && 'splice' in this[i])
			{
				clone[i] = this[i].clone();
			}
			else clone[i] = Object.clone(this[i]);;
		}
		else clone[i] = this[i];
	}
	return clone;
};

Function.prototype.bind = function()
{
	if (arguments.length < 2 && typeof arguments[0] == 'undefined') return this;
    var __method = this, args = $A(arguments), object = args.shift();
    return function()
	{
      return __method.apply(object, args.concat($A(arguments)));
    }
};

Function.prototype.bindAsEventListener = function()
{
	var __method = this, args = $A(arguments), object = args.shift();
	return function(event)
	{
		return __method.apply(object, [event || window.event].concat(args));
	}
};
	
$A = Array.from = function(iterable)
{
	if (!iterable) return [];
	if (iterable.toArray)
	{
		return iterable.toArray();
	}
	else
	{
		var results = [];
		for (var i = 0, length = iterable.length; i < length; i++)
		{
			results.push(iterable[i]);
		}
		return results;
	}
};

Function.prototype.delay = function(delay)
{
	return setTimeout(this, delay);
};

Object.extend = function (src, ext)
{
	for (var p in ext)
	{
		src[p] = (ext[p] && typeof ext[p].clone == 'function' ? ext[p].clone() : ext[p]);
	}
	return src;
};

Object.clone = function (src)
{
	return Object.extend({}, src);
};

String.prototype.strip = function()
{
	return this.replace(/^\s+/, '').replace(/\s+$/, '');
};

String.prototype.camelize = function()
{
	var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
};

String.prototype.fill = function(data)
{
	var s = this, data = data || {};
	for (p in data)
	{
		s = s.replace(new RegExp('\\{' + p + '\\}', 'g'), data[p]);
	}
	return s;
};

RegExp.escape = function(str)
{
	return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

var DOM = {
	get : function(id)
	{
		return document.getElementById(id);
	},
	
	showHide : function(element)
	{
		if (element.style.display == '')
		{
			DOM.hide(element);
			return false;
		}
		else
		{
			DOM.show(element);
			return true;
		}
	},
	
	openClose : function(element, img)
	{
		if (DOM.showHide(element))
		{
			img.src = img.src.replace(/Open/, 'Close');
		}
		else
		{
			img.src = img.src.replace(/Close/, 'Open');
		}
	},
	
	show : function(element, display)
	{
		element.style.display = display ? display : '';
	},
	
	hide : function(element)
	{
		element.style.display = 'none';
	},
	
	height : function(element)
	{
		return element.offsetHeight;
	},
	
	width : function(element)
	{
		return element.offsetWidth;
	},
	
	disable : function(element)
	{
		element.setAttribute('disabled', 'disabled');
	},
	
	disabled : function(element)
	{
		return (element.getAttribute('disabled') == 'disabled');
	},
	
	enable : function(element)
	{
		element.removeAttribute('disabled');
	},
	
	center : function(element)
	{
		element.style.top = (Browser.posTop() + Browser.pageHeight() / 2 - element.offsetHeight / 2) + 'px';
		element.style.left = (Browser.posLeft() + Browser.pageWidth() / 2 - element.offsetWidth / 2) + 'px';
	},
	
	centerdiv : function(element, width, height)
	{
		element.style.top = (Browser.posTop() + (Browser.pageHeight() - height) / 2) + 'px';
		element.style.left = (Browser.posLeft() + (Browser.pageWidth() - width) / 2) + 'px';
	},

	
	setStyle : function(element, styles)
	{
		styles = styles || {};

		for (var s in styles)
		{
			if (s == 'opacity') {
				DOM.setOpacity(element, styles[s]);
			}
			else if (s == 'float')
			{
				element.style.cssFloat = element.style.styleFloat = styles[s];
			}
			else
			{
				element.style[s] = styles[s];
			}
		}
	},
	
	setOpacity : function(element, opacity)
	{
		var style = element.style, opacity = opacity <= 0 ? 0.0001 : opacity;
		style.opacity = opacity;
		style.MozOpacity = opacity;
		style.KhtmlOpacity = opacity;
		style.filter = 'alpha(opacity=' + (opacity * 100) +')';
	},
	
	getOpacity : function(element)
	{
		return parseFloat(element.style.opacity ? element.style.opacity : 1.0);
	},
	
	appear : function(element, delay, done)
	{
		DOM.show(element);
		return DOM.fade(element, {
			from: 0,
			to: 1,
			delay: delay,
			before: DOM.show,
			done: done
		});
	},
	
	disappear : function(element, delay, done)
	{
		return DOM.fade(element, {
			from: element.style.opacity || 1,
			to: 0,
			delay: delay,
			done: function(){DOM.hide(element);if (typeof done == 'function') done(element)}
		});
	},
	
	fade : function(element, options)
	{
		var animation = {
			
			element : element,
			
			options : Object.extend({
				from: 0,
				to: 1,
				step: 0.1,
				delay: 50
			}, options || {}),
			
			start : function()
			{
				this.step = this.options.from < this.options.to ? this.options.step : this.options.step * -1;
				this.timeout = setTimeout(this.update.bind(this), this.options.delay);
				DOM.setOpacity(this.element, this.options.from);
				return this;
			},
			
			update : function ()
			{
				var opacity = DOM.getOpacity(this.element) + this.step;
				DOM.setOpacity(this.element, opacity);
				if ((this.step > 0 && opacity <= this.options.to) || (this.step < 0 && opacity >= this.options.to))
				{
					this.timeout = setTimeout(this.update.bind(this), this.options.delay);
				}
				else this.stop();
				return this;
			},
			
			stop : function ()
			{
				clearTimeout(this.timeout);
				if (typeof this.options.done == 'function') this.options.done(this.element);
				return this;
			}
		};

		return animation.start();
	},
	
	setPosition : function(element, x, y)
	{
		DOM.setStyle(element, {
			left : x+'px',
			top : y+'px'
		});
	},
	
	setDimensions : function(element, width, height)
	{
		DOM.setStyle(element, {
			width : width+'px',
			height : height+'px'
		});
	},
	
	create : function(tag, attributes, innerHTML)
	{
		var elm = document.createElement(tag), event;
		attributes = attributes || {};
		for (var a in attributes)
		{
			if (a == 'style')
			{
				if (typeof attributes[a] == 'string')
				{
					var styles = {};
					if (attributes[a].charAt(attributes[a].length - 1) == ';')
						attributes[a] = attributes[a].slice(0, attributes[a].length - 1);
					var styleAttributes = attributes[a].split(';'), att;
					for (var i = 0; i < styleAttributes.length; i++)
					{
						att = styleAttributes[i].split(':');
						styles[att[0].strip().camelize()] = att[1].strip();
					}
					DOM.setStyle(elm, styles);
				}
				else DOM.setStyle(elm, attributes[a]);
			}
			else if (a == 'class')
			{
				elm.className = attributes[a];
			}
			else if (event = a.match(/^on(.*)$/))
			{
				var funct;
				if (typeof attributes[a] == 'function')
				{
					funct = attributes[a];
				}
				else if (typeof attributes[a] == 'string')
				{
					funct = function(){eval(attributes[a])};
				}
				if (funct) DOM.addEvent(elm, event[1].toLowerCase(), funct.bindAsEventListener(elm));
			}
			else elm.setAttribute(a, attributes[a]);
		}
		
		if (innerHTML)
		{
			elm.innerHTML = innerHTML;
		}
		
		return elm;
	},
	
	next : function(element, time)
	{
		if (typeof time == 'undefined') time = 1;
		if (!element.parentNode || element == element.parentNode.lastChild) return null;
		if (element.nextSibling.nodeType != 1) return DOM.next(element.nextSibling, time);
		if (time == 1)
			return element.nextSibling;
		else
			return DOM.next(element.nextSibling, time - 1);
	},
	
	previous : function(element, time)
	{
		if (typeof time == 'undefined') time = 1;
		if (!element.parentNode || element == element.parentNode.firstChild) return null;
		if (element.previousSibling.nodeType != 1) return DOM.previous(element.previousSibling, time);
		if (time == 1)
			return element.previousSibling;
		else
			return DOM.previous(element.previousSibling, time - 1);
	},
	
	swap : function(element, swap)
	{
		if (element.parentNode != swap.parentNode) return false;
		
		if (element.swapNode) 
			element.swapNode(swap);
		else
		{
			var n = element.nextSibling, p = swap.parentNode;
     		swap.parentNode.replaceChild(element, swap);
       		p.insertBefore(swap, n);
		}
		
		return element;
	},
	
	cloneNode : function(element, deep)
	{
		if (navigator.userAgent.indexOf('MSIE') != -1)
		{
			if (typeof(element.tagName) == 'undefined')
			{
				// whitespaces?
				return null;
			}
			
			var Clone = DOM.create(element.tagName);
			
			for(var attrId = 0; attrId < element.attributes.length; attrId++)
			{
				if (element.attributes[attrId].nodeValue)
				{
					Clone.setAttribute(element.attributes[attrId].nodeName, element.attributes[attrId].nodeValue);
				}
			}
			
			if (!deep)
			{
				return Clone;
			}
			
			if (['COL', 'COLGROUP', 'FRAMESET', 'HEAD', 'HTML', 'STYLE', 'TABLE', 'TBODY', 'TFOOT', 'THEAD', 'TITLE', 'TR'].indexOf(element.tagName) == -1)
			{
				Clone.innerHTML = element.innerHTML;
			}
			else
			{
				for(var childId = 0; childId < element.childNodes.length; childId++)
				{
					var deepCloned = DOM.cloneNode(element.childNodes[childId], true);
					
					if (deepCloned != null)
					{
						Clone.appendChild(deepCloned);
					}
				}
			}
			
			return Clone;
		}
		else
		{
			return element.cloneNode(deep);
		}
	},
	
	children : function(element)
	{
		var children = [];
		for (var i = 0; i < element.childNodes.length; i++)
		{
			if (element.childNodes[i].nodeType == 1) children.push(element.childNodes[i]);
		}
		return children;
	},
	
	remove : function(element)
	{
		element.parentNode.removeChild(element);
	},
	
	empty : function(element)
	{
		if ('scrollTop' in element) try{element.scrollTop = 0}catch(e){};

		while (element.firstChild)
		{
			element.removeChild(element.firstChild);
		}
	},
	
	getClassName : function(element)
	{
		var elementClassName = element.className;
		
		if (elementClassName == '')
		{
			elementClassName = element.getAttribute('class');
			if (elementClassName === null) return '';
		}

		if (typeof(elementClassName) != 'String')
		{
			elementClassName = elementClassName.toString();
		}
		
		return elementClassName;
	},
	
	hasClassName : function(element, className)
	{
		var elementClassName = DOM.getClassName(element);
		return (elementClassName.length > 0 && (elementClassName == className ||
			new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
	},
	
	addClassName : function(element, className)
	{
		if (DOM.hasClassName(element, className)) return;
		element.className += (' ' + className);
	},
	
	removeClassName : function(element, className)
	{
		if (arguments[0])
		{
			for (var i = 1; i < arguments.length; i++)
			{
				arguments[0].className = arguments[0].className.replace(
	      			new RegExp("(^|\\s+)" + arguments[i] + "(\\s+|$)"), ' ').replace(/^\s+/, '').replace(/\s+$/, '');
			}
		}
	},
	
	up : function(element, tag_name, time)
	{
		if (typeof time == 'undefined') time = 1;
		tag_name = tag_name.toUpperCase();
		while (element && tag_name)
		{
			if (element.tagName == tag_name && --time == 0) return element;
			element = element.parentNode;
		}
		return null;
	},
	
	addEvent : function(element, type, callback, runNow)
	{
		if (element.addEventListener)
			element.addEventListener(type, callback, false);
		else if (element.attachEvent)
		{
			element['e' + type + callback] = callback;
			element[type + callback] = function() {element['e' + type + callback](window.event)};
			element.attachEvent('on' + type, element[type + callback]);
		}
		
		if (runNow)
		{
			callback.bind(element).apply();
		}
	},
	
	removeEvent : function(element, type, callback)
	{
		if (element.removeEventListener)
			element.removeEventListener(type, callback, false);
		else if (element.detachEvent && element[type + callback])
		{
			element.detachEvent('on' + type, element[type + callback]);
			element[type + callback] = null;
			element['e' + type + callback] = null;
		}
	},
	
	stopEvent : function(event)
	{
		event.cancelBubble = true;
		event.returnValue = false;
	
		if (event.stopPropagation)
		{
			event.stopPropagation();
			event.preventDefault();
		}
	},
	
	eventTarget : function(event)
	{
		if (event.target) return event.target;
		if (event.srcElement) return event.srcElement;
	},
	
	eventKeyCode : function (event)
	{
		if (event.keyCode) return event.keyCode;
		if (event.which) return event.which;
	},
	
	populate : function(element, url, template, parameters, id_column, label_column)
	{
		if (typeof(url) == "string")
		{
			var response, req = new AJAX.Request(url, {
				method: "POST",
				json: true,
				asynchronous: false,
				parameters: parameters || {}
			});
			
			response = req.response.responseJSON.options;
		}
		else
		{
			response = url;
		}
		
		if (response)
		{
			if (typeof template == 'string')
			{
				var generated = '';
				for (var i = 0; i < response.length; i++)
				{
					generated += template.fill(response[i]);
				}
				element.innerHTML = '';
			}
			else
			{
				if (typeof(response.length) != "undefined")
				{
					for (var i = 0; i < response.length; i++)
					{
						if (typeof(response[i]) == "string")
						{
							element.appendChild(template({label: response[i], value: i}));
						}
						else
						{
							element.appendChild(template(response[i], id_column, label_column));
						}
					}
				}
				else
				{
					for(var rEntry in response)
					{
						if (typeof(response[rEntry]) == "string")
						{
							element.appendChild(template({label: response[rEntry], value: rEntry}));
						}
						else
						{
							element.appendChild(template(response[rEntry], id_column, label_column));
						}
					}
				}
			}
		}
	},
	
	cumulativeOffset: function(element)
	{
		var top = 0, left = 0;
		do
		{
			top  += element.offsetTop  || 0;
			left += element.offsetLeft || 0;
			element = element.offsetParent;
		}
		while(element);
		return {top : top, left : left};
	},
	
	formElements : function(element)
	{
		var elements = [];
		if (document.evaluate)
		{
			var result = document.evaluate('descendant::input|descendant::textarea|descendant::select', element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
			for (var i = 0; i < result.snapshotLength; i++)
			{
				elements.push(result.snapshotItem(i));
			}
		}
		else
		{
			var collections = [
				element.getElementsByTagName('input'),
				element.getElementsByTagName('select'),
				element.getElementsByTagName('textarea')
			];
			for (var i = 0; i < collections.length; i++)
			{
				for (var h = 0; h < collections[i].length; h++)
				{
					elements.push(collections[i][h]);
				}
			}
		}
		return elements;
	},
	
	nextFormElement : function(element, time)
	{
		var form = element.form;
		if (typeof time == 'undefined') time = 1;
		for (var i = 0; i < form.elements.length; i++)
		{
			if (form.elements[i] == element) return i + time < form.elements.length ? form.elements[i + time] : null;
		}
	},
	
	previousFormElement : function(element, time)
	{
		var form = element.form;
		time = typeof time == 'undefined' ? -1 : -time;
		for (var i = 0; i < form.elements.length; i++)
		{
			if (form.elements[i] == element) return i + time >= 0 ? form.elements[i + time] : null;
		}
	},
	
	currentStyle : function(element, property)
	{
		if (element.currentStyle)
		{
			return element.currentStyle[property.camelize()];
		}
		else
		{
			return element.ownerDocument.defaultView.getComputedStyle(element, null).getPropertyValue(property);
		}
	}
};

function Dispatcher()
{
	this.Callbacks = [];
	
	this.Dispatch = function(context)
	{
		var funcArgs = $A(arguments);
		funcArgs.shift();
		
		for(var callBackId = 0; callBackId < this.Callbacks.length; callBackId++)
		{
			this.Callbacks[callBackId].apply(context, funcArgs);
		}
	};
	
	this.Add = function(Callback)
	{
		this.Callbacks.push(Callback);
		return this;
	};

	this.Empty = function()
	{
		this.Callbacks = [];
		return this;
	};
};