/**
 * General library
 *
 * @version 1.1
 */


/**
 * Preload images
 */
var preloadedImages = {};

function $()
{
	return window.document.getElementById(arguments[0]);
}

function preloadImage(label, url)
{
	if (!(label in preloadedImages))
	{
		preloadedImages[label] = new Image();
		preloadedImages[label].src = url;
	}
}

function getPreloadImage(label)
{
	if (label in preloadedImages)
	{
		return preloadedImages[label].src;
	}
	else if (label != 'empty')
	{
		preloadImage('empty', 'images/icons/empty.gif');
		return getPreloadImage('empty');
	}
	else
	{
		return 'images/icons/empty.gif';
	}
}

function imgHoverOn()
{
	if (this.__hoverStateImg)
		this.src = getPreloadImage(this.__hoverStateImg);
}

function imgHoverOff()
{
	if (this.__normalStateImg)
		this.src = getPreloadImage(this.__normalStateImg);
}

function imageHoverSwap(img, normalStateImg, hoverStateImg)
{
	img.src = getPreloadImage(normalStateImg);

	if (!img.__hoverStateImg)
	{
		addEvent(img, 'mouseover', imgHoverOn);
		addEvent(img, 'mouseout', imgHoverOff);
	}

	img.__hoverStateImg = hoverStateImg;
	img.__normalStateImg = normalStateImg;
}

/**
 * Browser detection
 */
var is = {
	opera : !!window.opera,
	ie: /*@cc_on!@*/false,
	khtml: navigator.userAgent.indexOf('KHTML') > -1,
	gecko: navigator.userAgent.indexOf('KHTML') == -1 && navigator.userAgent.indexOf('Gecko') > -1,
	mac: navigator.userAgent.indexOf('Macintosh') > -1
}

/**
 * Object extention
 */
Object.extend = function(dest, source, allowOverwrite)
{
	for (var prop in source)
	{
		if (source.hasOwnProperty(prop) && (allowOverwrite || !dest.hasOwnProperty(prop)))
		{
			dest[prop] = source[prop];
		}
	}
	
	return dest;
}

/**
 * String extentions
 */
Object.extend(String.prototype,
{
	trim: function()
	{
		return this.replace(/^\s+|\s+$/, '');
	}
});

/**
 * Array extentions
 */
Object.extend(Array.prototype,
{
	indexOf: function(searchElement, fromIndex)
	{
		var l = this.length;
		var x = 0;
		
		if (fromIndex)
		{
			x = fromIndex;
			if (x < 0)
			{
				x += l;
				if (x < 0) x = 0;
			}
		}

		while (x < l)
		{
			if (this[x] === searchElement)
			{
				return x;
			}
			
			x++;
		}

		return -1;
	}
});

/**
 * Function extentions
 */
Object.extend(Function.prototype,
{
	bind: function()
	{
		var handler = this, args = [].slice.call(arguments, 0), object = args.shift();
		
		return function()
		{
			return handler.apply(object, args.concat([].slice.call(arguments, 0)));
		}
	}
});

/**
 * Dom load stuff
 */
var DomLoaded =
{
	onload: [],
	loaded: function()
	{
		if (DomLoaded.done) return;
		DomLoaded.done = true;

		if (DomLoaded.timer)
			clearInterval(DomLoaded.timer);

		if (DomLoaded.onload.length)
			DomLoaded.execute();
	},
	load: function(fireThis)
	{
		DomLoaded.onload.push(fireThis);

		if (DomLoaded.onload.length == 1)
		{
			if (document.addEventListener)
				document.addEventListener('DOMContentLoaded', DomLoaded.loaded, false);
			else
			{
				DomLoaded.timer = setInterval(function()
				{
					if (document.getElementById('domLoaded'))
						DomLoaded.loaded();
				}, 10);
			}

			// generic fallback
			addEvent(window, 'load', DomLoaded.loaded);
		}
	},
	execute: function()
	{
		var func = DomLoaded.onload.shift();

		if (typeof func == 'function')
			func();

		if (DomLoaded.onload.length)
			setTimeout(DomLoaded.execute, 10);
	}
}

/**
 * Event handling
 */
var addEvent = function()
{
	if (document.addEventListener)
	{
		return function(element, type, handler)
		{
			element.addEventListener(type, handler, false);
		}
	}
	else
	{
		function handleEvent(event)
		{
			event = event || fixEvent(window.event);

			var handlers = this.events[event.type], returnValue;
			for (var x in handlers)
			{
				if (handlers.hasOwnProperty(x) && handlers[x].call(this, event) === false)
					returnValue = false;
			}

			return returnValue;
		}
		function fixEvent(event)
		{
			event.preventDefault = preventDefault;
			event.stopPropagation = stopPropagation;

			return event;
		}
		function preventDefault()
		{
			this.returnValue = false;
		}
		function stopPropagation()
		{
			this.cancelBubble = true;
		}
		function removeAllEvents()
		{
			while ((cachedEvent = eventCache.pop()))
			{
				removeEvent(cachedEvent.element, cachedEvent.type, cachedEvent.handler);
			}
		}
		
		var guid = 1;
		var eventCache = [];
		
		return function(element, type, handler)
		{
			if (!handler.$$guid) handler.$$guid = guid++;
			if (!element.events) element.events = {};
			if (!element.events[type])
			{
				element.events[type] = {};
				if (element['on' + type]) element.events[type][0] = element['on' + type];
				element['on' + type] = handleEvent;
			}

			element.events[type][handler.$$guid] = handler;

			if (type != 'unload')
			{
				if (!eventCache.length) addEvent(window, 'unload', removeAllEvents);

				eventCache.push(
				{
					element: element,
					type: type,
					handler: handler
				});
			}
		}
	}
}();

var removeEvent = function()
{
	if (document.removeEventListener)
	{
		return function(element, type, handler)
		{
			element.removeEventListener(type, handler, false);
		}
	}
	else
	{
		return function(element, type, handler)
		{
			if (element.events && element.events[type] && handler.$$guid)
			{
				delete element.events[type][handler.$$guid];
			}
		}
	}
}();

function getURLParams(url)
{
	var parms = {};
	var i = url.indexOf('?');
	if (i > -1)
	{
		var get = url.substr(i+1);
		var p1 = get.split('&'), p2;
		i = p1.length;
		
		while (i--)
		{
			p2 = p1[i].split('=');
			parms[p2[0]] = p2[1];
		}
	}

	return parms;
}

function getURLAnchor(url)
{
	var parts = url.split('#');
	
	if (parts.length > 1)
	{
		return parts[parts.length - 1];
	}
	else
	{
		return null;
	}
}

function appendURLParams(url, params)
{
	var queryString = createQueryString(params);

	if (queryString)
	{
		queryString = (url.indexOf('?') == -1 ? '?' : '&') + queryString;
	}

	return queryString;
}

function appendDataParms(data, params)
{
	var queryString = createQueryString(params);

	if (queryString)
	{
		queryString = (data == '' ? '' : '&') + queryString;
	}

	return queryString;
}

function createQueryString(params)
{
	var query = [];

	for (var parm in params)
	{
		if (params.hasOwnProperty(parm))
		{
			query.push(encodeURIComponent(parm) + '=' + encodeURIComponent(params[parm]));
		}
	}

	return query.join('&');
}


/**
 * Returns all the elements in form along with the values as a query string.
 *
 * form can be both a form or an array with elements.
 */
function getQueryString(form)
{
	var elements = form.length === undefined ? form.elements : form;
	var element = null;
	
	var elementName, values, parts = [];
	var x, y, k, o;
	for (x = 0; x < elements.length; x++)
	{
		element = elements[x];
		
		if (element.name && !element.disabled)
		{
			values = [];
			
			switch (element.tagName.toLowerCase())
			{
				case 'input':
					k = element.type.toLowerCase();
					if ((k == 'checkbox' || k == 'radio') && !element.checked)
					{
						break;
					}
				case 'textarea':
					values.push(element.value);
					break;
				case 'select':
					k = element.options;
					o = [];
					
					if (element.multiple)
					{
						for (y = 0; y < k.length; y++)
						{
							if (k[y].selected) o.push(k[y]);
						}
					}
					else
					{
						o.push(k[element.selectedIndex]);
					}
						
					for (y = 0; y < o.length; y++)
					{
						k = o[y].value;
						if (!k && !('value' in k))
						{
							k = o.text;
						}
						
						values.push(k);

					}
					break;
			}
			
			if (k = values.length)
			{
				elementName = encodeURIComponent(element.name);
				for (y = 0; y < k; y++)
				{
					parts.push(elementName + '=' + encodeURIComponent(values[y]));
				}
			}
		}
	}
	
	return parts.join('&');
}

function Set()
{
	var set = {}, i = arguments.length;
	while (i--)
		set[arguments[i]] = 1;

	return set;
}

/**
 * HTML builder
 */
function HTMLBuilder(newInstance)
{
	if (this == window)
		return new HTMLBuilder();

	if (HTMLBuilder.instance && !newInstance)
		return HTMLBuilder.instance;

	this.idList = {};
	this.eventHandlers = Set(
		'onblur', 'onchange', 'onclick', 'ondblclick', 'onfocus', 'onkeydown',
		'onkeypress', 'onkeyup', 'onload', 'onmousedown', 'onmousemove', 'onmouseout',
		'onkouseover', 'onmouseup', 'onreset', 'onselect', 'onsubmit', 'onunload'
	);

	if (!newInstance)
		HTMLBuilder.instance = this;

	return this;
}
Object.extend(HTMLBuilder.prototype,
{
	build: function(object)
	{
		this.idList = {};

		return this.buildRecursive(object);
	},
	buildRecursive: function(object)
	{
		var element = null, attribute, i;

		if (object.o)
		{
			element = object.o;
		}
		else if (object.n)
		{
			if (object.n == '#text')
			{
				element = document.createTextNode(object.v || '');
			}
			else
			{
				if (object.a && 'name' in object.a)
				{
					try
					{
						element = document.createElement('<' + object.n + ' name=' + object.a['name'] + '>');
						delete object.a['name'];
					}
					catch(e) {}
				}

				if (element || (element = document.createElement(object.n)))
				{
					if (object.a)
					{
						for (attribute in object.a)
						{
							if (object.a[attribute] !== null && object.a.hasOwnProperty(attribute))
							{
								if (	attribute in this.eventHandlers &&
									typeof object.a[attribute] != 'function'
								)
								{
									element[attribute] = new Function(object.a[attribute]);
								}
								else
								{
									if (attribute == 'id')
										this.idList[object.a[attribute]] = element;

									if (attribute == 'style')
										element.style.cssText = object.a['style'];
									else
										element[attribute] = object.a[attribute];
								}
							}
						}
					}

					if (object.c && object.c.length)
					{
						if (object.n == 'table' &&
							!(object.c[0].n in Set('tbody', 'theader', 'tfooter'))
						)
						{
							element.appendChild(
								this.build(
									{
										n: 'tbody',
										c: object.c
									}
								)
							);
						}
						else
						{
							var childNode;
							for (i = 0; i < object.c.length; i++)
							{
								if (object.c[i] && (childNode = this.buildRecursive(object.c[i])))
									element.appendChild(childNode)
							}
						}
					}
				}
			}
		}

		return element;
	},
	getIdList: function()
	{
		return Object.extend({}, this.idList);
	},
	getElementById: function(id)
	{
		return this.idList[id] || null;
	}
});

function getElementsByClassName(className, nodeName, parentElement, callback)
{
	if (!nodeName)
		nodeName = '*';

	if (!parentElement)
		parentElement = window.document;

	var results = [], multiple = className.indexOf('|') > -1, s, i = 0, element;

	if (parentElement.getElementsByClassName && nodeName == '*' && !multiple)
	{
		results = parentElement.getElementsByClassName(className);

		if (callback)
			Array.forEach(results, callback);
	}
	else if (document.evaluate && !multiple)
	{
		s = document.evaluate(
			".//" + nodeName + "[contains(concat(' ', @class, ' '), ' " + className + " ')]",
			parentElement,
			null,
			XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
			null
		);

		while ((element = s.snapshotItem(i++)))
		{
			results.push(element);
			if (callback)
				callback(element);
		}
	}
	else
	{
		s = parentElement.getElementsByTagName(nodeName);
		var re = new RegExp('(^|\\s)' + className + '(\\s|$)'), elementClassName;

		while ((element = s[i++]))
		{
			elementClassName = element.className;
			if (elementClassName.length && (elementClassName == className || re.test(elementClassName)))
			{
				results.push(element);
				if (callback)
				{
					callback(element);
				}
			}
		}
	}

	return results;
}

function addClass(element, className)
{
	if (element.className.length)
	{
		var classes = getClassList(element);
		if (classes.indexOf(className) == -1)
		{
			classes.push(className);
			setClassList(element, classes);
		}
	}
	else
		element.className = className;
}

function removeClass(element, className)
{
	var classes = getClassList(element), index;
	
	if ((index = classes.indexOf(className)) > -1)
	{
		delete classes[index];
		
		setClassList(element, classes);
	}
}

function replaceClass(element, oldclass, newclass)
{
	var classes = getClassList(element), index;
	
	if ((index = classes.indexOf(oldclass)) > -1 && classes.indexOf(newclass) == -1)
	{
		classes[index] = newclass;
		setClassList(element, classes);
	}
}

function hasClass(element, className)
{
	var elementClassName = element.className;
	
	return elementClassName.length && (elementClassName == className || getClassRegExp(className).test(elementClassName));
}

function getClassList(element)
{
	return element.className.length ? element.className.split(/\s+/) : [];
}

function isIgnorable(nod)
{
	return (nod.nodeType == 8 || (nod.nodeType == 3 && !/[^\t\n\r ]/.test(nod.data)));
}

function setClassList(element, classes)
{
	element.className = classes.join(' ');
}

function firstChild(parent, nodeName)
{
	if (parent && parent.nodeType == 1)
	{
		var res = parent.firstChild;
		if (nodeName)
		{
			nodeName = nodeName.toLowerCase();
			do
			{
				if (res.nodeName.toLowerCase() == nodeName)
				{
					return res;
				}
			}
			while ((res = res.nextSibling));
		}
		else
		{
			do
			{
				if (!isIgnorable(res))
				{
					return res;
				}
			}
			while ((res = res.nextSibling));
		}
	}

	return null;
}

function lastChild(parent, nodeName)
{
	if (parent && parent.nodeType == 1)
	{
		var res = parent.lastChild;
		if (nodeName)
		{
			nodeName = nodeName.toLowerCase();
			do
			{
				if (res.nodeName.toLowerCase() == nodeName)
				{
					return res;
				}
			}
			while ((res = res.previousSibling));
		}
		else
		{
			do
			{
				if (!isIgnorable(res))
				{
					return res;
				}				
			}
			while ((res = res.previousSibling));
		}
	}

	return null;
}

function nodeAfter(sib, nodeName)
{
	if (sib)
	{
		if (nodeName)
		{
			nodeName = nodeName.toLowerCase();
			while ((sib = sib.nextSibling))
			{
				if (sib.nodeName.toLowerCase() == nodeName)
				{
					return sib;
				}
			}
		}
		else
		{
			while ((sib = sib.nextSibling))
			{
				if (!is_ignorable(sib))
				{
					return sib;
				}
			}
		}
	}

	return null;
}

function getOffsetTop(el, offsetParent)
{
	var offsetTop = 0;
	if (!offsetParent) offsetParent = null;
	
	do
	{
		offsetTop += el.offsetTop
	} while ((el = el.offsetParent) && el != offsetParent);

	return offsetTop;
}

function getOffsetLeft(el, offsetParent)
{
	var offsetLeft = 0;
	if (!offsetParent) offsetParent = null;
	
	do
	{
		offsetLeft += el.offsetLeft
	} while ((el = el.offsetParent) && el != offsetParent);

	return offsetLeft;
}

function setRelativePosition(element, offsetElement, offsetTop, offsetLeft, minTop, minLeft)
{
	var offsetParent = null;
	try { offsetParent = element.offsetParent; } catch(e) {}

	var top = getOffsetTop(offsetElement, offsetParent) + offsetTop;
	if (minTop !== undefined && top + (offsetParent && getOffsetTop(offsetParent)) < minTop) top = minTop;

	var left = getOffsetLeft(offsetElement, offsetParent) + offsetLeft;
	if (minLeft !== undefined && left + (offsetParent && getOffsetLeft(offsetParent)) < minLeft) left = minLeft;

	element.style.top = top + 'px';
	element.style.left = left + 'px';
}

function cancelEvent(e)
{
	if (e.preventDefault)
	{
		e.preventDefault();
	}
	else if (e.cancelBubble != undefined)
	{
		if (e.keyCode) e.keyCode = 0;
		e.returnValue = 0;
		e.cancelBubble = true;
	}

	return false;
}

var regExpCache = {};
function getClassRegExp(className)
{
	if (!(className in regExpCache))
		regExpCache[className] = new RegExp('(^|\\s)' + className + '(\\s|$)');

	return regExpCache[className];
}