var $extend = function(extendThis, extendWith, overwrite)
{
	if (typeof(extendWith) != 'object') return;
	for (var n in extendWith)
	{
		if (!extendThis[n] || overwrite === true) extendThis[n] = extendWith[n];
	}
}

var $get = function(elmName, container) {
	container = container || document;
	return container.getElementById(elmName);
}

$extend(String.prototype, {
	trim: function()
	{
		return this.replace(/^ /, '').replace(/ $/, '');
	}
});

$extend(Array.prototype, {
	indexOf: function(find)
	{
		for (var n = 0, j = this.length; n < j; n++)
		{
			if (this[n] == find) return n;
		}
		return -1;
	},
	pushArray: function(arr)
	{
		for (var n = 0, j = arr.length; n < j; n++)
		{
			this.push(arr[n]);
		}
	},
	foreach: function(fn)
	{
		for (var n = 0, j = this.length; n < j; n++)
		{
			fn.call(this[n]);
		}
	}
});

var framework = {
	browser: {
		ie: !!document.all,
		opera: !!window.opera,
		gecko: navigator.userAgent.indexOf('Gecko') != -1 && navigator.userAgent.indexOf('KHTML') == -1,
		safari: navigator.userAgent.indexOf('AppleWebKit') != -1
	},
	core: {
		CheckOptions: function(options, required, appendIfUnset)
		{
			if (!options)
			{
				framework.CatchError('.*: No options defined');
				return false;
			}
			var n = 0, j = 0;
			for (n = 0, j = required.length; n < j; n++)
			{
				if (!options[required[n]])
				{
					framework.CatchError('.*: options.'+required[n]+' are not defined');
					return false;
				}
			}
			if (appendIfUnset && typeof(appendIfUnset) == 'object')
			{
				for (n in appendIfUnset)
				{
					if (!options[n])
					{
						options[n] = appendIfUnset[n];
					}
				}
			}
			return true;
		}
	},
	dom: {
		ChildOf: function(container, element, deep)
		{
			var r;
			if (container && container.childNodes)
			{
				for (var n = 0, j = container.childNodes.length; n < j; n++)
				{
					if (container.childNodes[n] == element) return true;
					if (deep === true)
					{
						r = this.ChildOf(container.childNodes[n], element, true);
						if (r === true) return true;
					}
				}
			}
			return false;
		}
	},
	event: {
		documentReady: false,
		readyBound: false,
		readyListeners: [],
		safariTimer: null,
		Add: function(element, event, listener)
		{
			if (!element)
			{
				framework.CatchError('.event.Add: element has no properties');
				return false;
			}
			if (element.addEventListener)
			{
				element.addEventListener(event, listener, false);
			}
			else if (element.attachEvent)
			{
				element.attachEvent('on'+event, listener);
			}
			else
			{
				element['on'+event] = listener;
			}
		},
		Remove: function(element, event, listener)
		{
			if (!element)
			{
				framework.CatchError('.event.Remove: element has no properties');
				return false;
			}
			if (element.removeEventListener)
			{
				element.removeEventListener(event, listener, false);
			}
			else if (element.detachEvent)
			{
				element.detachEvent('on'+event, listener);
			}
			else
			{
				delete element['on'+event];
			}
		},
		Ready: function(listener)
		{
			if (framework.event.documentReady)
			{
				listener.apply(document);
				return;
			}

			if (typeof(listener) != 'function')
			{
				framework.event.documentReady = true;
				for (var n = 0, j = framework.event.readyListeners.length; n < j; n++)
				{
					framework.event.readyListeners[n]();
				}
				if (framework.browser.gecko || framework.browser.opera)
				{
					framework.event.Remove(document, 'DOMContentLoaded', framework.event.Ready);
				}
				return;
			}

			this.readyListeners.push(listener);
			if (!this.readyBound)
			{
				this.ReadyBind();
			}
		},
		ReadyBind: function()
		{
			if (framework.browser.gecko || framework.browser.opera)
			{
				framework.event.Add(document, 'DOMContentLoaded', framework.event.Ready);
			}
			else if (framework.browser.ie)
			{
				document.write("<scr" + "ipt id=__ie_init defer=true " + 
					"src=//:><\/script>");

				var script = document.getElementById("__ie_init");
				if (script)
					script.onreadystatechange = function() {
						if (this.readyState != 'complete') return;
						framework.event.Ready();
					};
			
				script = null;
			}
			else if (framework.browser.safari)
			{
				var safariTimer = this.safariTimer;
				safariTimer = setInterval(function(){
					if (document.readyState == 'loaded' || document.readyState == 'complete')
					{
						clearInterval(safariTimer);
						safariTimer = null;

						framework.event.Ready();
					}
				}, 10); 
			}
			else
			{
				framework.event.Add(window, 'load', framework.event.Ready);
			}
		}
	},
	selector: {
		Parse: function(selector)
		{
			var matches = framework.selector.Simple(selector);
			if (matches !== false) return framework.selector.CheckAttribute(matches, selector);

			// The advanced selectors:
			var selectors 	= [];
			var matches 	= [];

			if (selector.indexOf(',') != -1)	selectors 	 = selector.split(',');
			else								selectors[0] = selector;

			var match;
			while(selector = selectors.shift())
			{
				selector = selector.trim();
				match = framework.selector.Simple(selector);
				if (match)
				{
					for (var n = 0, j = match.length; n < j; n++)
					{
						matches.push(match[n]);
					}
				}
				else if (selector.indexOf(' ') != -1)
				{
					var container = document;
					var hierachy = selector.split(' ');
					var child;
					var tmpmatch;
					while(childSelector = hierachy.shift())
					{
						if (match)
						{
							matches = [];
							for (var n = 0, m = match, j = m.length; n < j; n++)
							{
								tmpmatch = framework.selector.CheckAttribute(framework.selector.Simple(childSelector, m[n]), childSelector);
								if (tmpmatch)
								{
									matches.pushArray(tmpmatch);
								}
							}
							match = matches;
						}
						else
						{
							match = framework.selector.Simple(childSelector);
						}
					}
				}
			}

			return matches;
		},
		CheckAttribute: function(elements, selector)
		{
			if (framework.test.attributeSelector.test(selector))
			{
				var attribute 			= framework.test.attributeSelector.exec(selector)[2];
				if (attribute == 'class') attribute = 'className';
				var attributeSelector 	= framework.test.attributeSelector.exec(selector)[4];
				var attributeValue		= framework.test.attributeSelector.exec(selector)[5];
				var returnElements 		= [];
				for (var n = 0, j = elements.length; n < j; n++)
				{
					if (elements[n] && elements[n][attribute])
					{
						var val = elements[n][attribute];
						switch (attributeSelector)
						{
							case '=':
								if (val == attributeValue)
								{
									returnElements.push(elements[n]);
								}
								break;
							case '|=':
								if (val.indexOf(attributeValue) == 0)
								{
									returnElements.push(elements[n]);
								}
								break;
							case '~=':
								if (val.split(' ').indexOf(attributeValue) != -1)
								{
									returnElements.push(elements[n]);
								}
								break;
							case '*=':
								if (val.indexOf(attributeValue) != -1)
								{
									returnElements.push(elements[n]);
								}
								break;
							case '^=':
								if (val.indexOf(attributeValue) == 0)
								{
									returnElements.push(elements[n]);
								}
								break;
							case '$=':
								if (val.length-attributeValue.length == val.indexOf(attributeValue) && val.indexOf(attributeValue) != -1)
								{
									returnElements.push(elements[n]);
								}
								break;
							default:
								returnElements.push(elements[n]);
								break;
						}
					}
				}
				return returnElements;
			}
			return elements;
		},
		// Simple selector (ID, className or tagName)
		Simple: function(selector, container)
		{
			container = container || document;
			var matches = [];

			if (framework.test.id.test(selector) && container === document)
			{
				return [$get(framework.test.id.exec(selector)[1], container)];
			}
			else if (framework.test.id.test(selector))
			{
				var match = container.getElementsByTagName('*');
				for (var n = 0, j = match.length; n < j; n++)
				{
					if (match[n].id == framework.test.id.exec(selector)[1])
					{
						matches.push(match[n]);
					}
				}
				return matches;
			}
			else if (framework.test.tagName.test(selector))
			{
				var m = container.getElementsByTagName(selector);
				for (var n = 0, j = m.length; n < j; n++)
				{
					matches.push(m[n]);
				}
				return matches;
			}
			else if (framework.test.className.test(selector))
			{
				return document.getElementsByClassName(framework.test.className.exec(selector)[1], container);
			}
			else if (selector == '*')
			{
				return container.getElementsByTagName('*');
			}
			else if (framework.test.attributeSelector.test(selector))
			{
				return framework.selector.Simple(framework.test.attributeSelector.exec(selector)[1]);
			}

			return false;
		}
	},
	test: {
		id: /^#([-a-z_0-9]+)$/i,
		tagName: /^([a-z]+)$/i,
		className: /^\.([-a-z_0-9]+)$/i,
		attributeSelector: /^([^ ]*?)\[(.*?)(([~\|\^\$\*]?=)"(.*?)")?\]$/i
	},
	errors: [],
	CatchError: function(error)
	{
		framework.errors.push(error);
	},
	Debug: function()
	{
		if (console && console.log)
			console.log(framework.errors);
		else
			alert(framework.errors);
	},
	Eval: function(script)
	{
		if (framework.browser.safari)
		{
			window.setTimeout(function(){
				window.eval(script);
			}, 0);
			return true;
		}
		window.eval(script);
		return true;
	},
	Extend: function(obj)
	{
		for(var n in obj)
			this[n] = obj[n];
	},
	FileExists: function(url)
	{
		var success = false;
		framework.ajax.Get(url, {
			async: false,
			onSuccess: function()
			{
				success = true;
			},
			onFailure: function()
			{
				success = false;
			}
		});
		return success;
	},
	GetElements: function(selector)
	{
		return framework.selector.Parse(selector);
	},
	GetOffset: function(obj)
	{
		if (typeof obj == 'string')
			obj = $get(obj);

		// Initial object
		var o = {top: 0, left: 0, width: obj.offsetWidth, height: obj.offsetHeight};

		while(obj.offsetParent)
		{
			o.top  += obj.offsetTop;
			o.left += obj.offsetLeft;
			obj = obj.offsetParent;
		}
		return o;
	},
	GetStyle: function(obj, style)
	{
		if (typeof obj == 'string')
			obj = $get(obj);

		// Credits to
		//   Peter-Paul Koch @ quirksmode.org
		// for this functionality

		if (obj.currentStyle)
			var val = obj.currentStyle[style];
		else if (window.getComputedStyle)
			var val = document.defaultView.getComputedStyle(obj, null).getPropertyValue(style);

		return val;
	},
	LoadModule: function(module)
	{
		framework.ajax.Get('framework.'+module+'.js', {
			async: false,
			responseType: 'script'
		});
	},
	NodeName: function(elm)
	{
		return elm.nodeName.toLowerCase();
	}
};