if(!console)
	var console = {
			log: function(message)
			{
			}
		};

Function.CreateAbstractMethod = function(sgntr)
{
	var errMsgTmp = 'abstract method in '+ this.constructor.getFunctionName() +' not implemented',
		functionBody = 'throw new Error("'+ errMsgTmp +'")';
	return new Function(functionBody);
}

Function.__Hierarchy = {};

Function.prototype.Extend = function(baseClass)
{
	var constructor = this.prototype.constructor;

	// odstranit z root, zapsat do root baseClass {

	if(!Function.__Hierarchy[baseClass.name])
		Function.__Hierarchy[baseClass.name] = {};

	Function.__Hierarchy[baseClass.name][constructor.getFunctionName()] = constructor;

	// }

	this.__copyPrototypeFields(baseClass);

	this.prototype.constructor = constructor;
	this.prototype.parent = baseClass;
}

Function.prototype.Implement = function(baseClass)
{
	this.__copyPrototypeFields(baseClass);
}

Function.GetHierarchy = function()
{
	var rootClasses = Function.__GetRootClasses(),
		names = {};

	for(var className in rootClasses)
	{
		names[className] = {};

		if(rootClasses[className])
			names[className] = rootClasses[className].GetHierarchy();
	}

	return names;
}

Function.__GetRootClasses = function()
{
	var rootClasses = {},
		isRoot_tmp = null;

	for(var baseClassName in Function.__Hierarchy)
	{
		isRoot_tmp = true;

		for(var baseClassName2 in Function.__Hierarchy)
		{
			for(var className in Function.__Hierarchy[baseClassName2])
				if(className == baseClassName)
				{
					isRoot_tmp = false;
					break;
				}

			if(!isRoot_tmp)
				break;
		}

		if(isRoot_tmp)
			rootClasses[baseClassName] = window[baseClassName];
	}

	return rootClasses;
}

Function.prototype.GetHierarchy = function()
{
	var names = {};

	for(var className in Function.__Hierarchy[this.name])
	{
		names[className] = {};

		if(Function.__Hierarchy[this.name][className])
			names[className] = Function.__Hierarchy[this.name][className].GetHierarchy();
	}

	return names;
}

Function.prototype.Super = function()
{
	return this.prototype.parent || Function;
}

Function.prototype.__copyPrototypeFields = function(source)
{
	for(var field in source.prototype)
		if(!this.prototype[field]) // nechceme prepsat jiz existujici fields
			this.prototype[field] = source.prototype[field];
}

Function.InstanceOf = function(constructorFunction)
{
	return constructorFunction == Function;
}

Function.prototype.InstanceOf = function(constructorFunction)
{
	if(this == constructorFunction)
		return true;

	var parentFunction = this.Super();
	return parentFunction.InstanceOf(constructorFunction);
}

//--

Boolean.Parse = function(str)
{
	var tmp = str.toLowerCase();

	if(tmp == 'true' || tmp == 't' || tmp == '1')
		return true;

	if(tmp == 'false' || tmp == 'f' || tmp == '0')
		return false;

	return Boolean(str).valueOf();
}

//--

function Enum()
{
	this.__init.apply(this, arguments);
}

// static {

Enum.Extend = function(base)
{
	var e = new Enum();
	e.__pushValues(base.values);
	for(var i = 1; i < arguments.length; i++)
	{
		e[arguments[i]] = e.values.length;
		e.values.push(arguments[i]);
	}
	return e;
}

// }

// private {

Enum.prototype.__init = function()
{
	this.values = [];
	this.__pushValues(arguments);
}

Enum.prototype.__pushValues = function(values)
{
	for(var i = 0; i < values.length; i++)
	{
		this[values[i]] = i;
		this.values.push(values[i]);
	}
}

// }

//--

var ContentType = new Enum(
		'TEXT',
		'JS',
		'XML',
		'HTML'
	);

function identifyContent(xmlHttpRequest)
{
	var contentTypeHeader = xmlHttpRequest.getResponseHeader('content-type');
	if(contentTypeHeader)
	{
		if(new RegExp('text/plain').test(contentTypeHeader.toLowerCase()))
			return ContentType.TEXT;
		if(new RegExp('(text|application)/(x-javascript|javascript|json)').test(contentTypeHeader.toLowerCase()))
			return ContentType.JS;
		if(new RegExp('text/xml').test(contentTypeHeader.toLowerCase()))
			return ContentType.XML;
		if(new RegExp('text/html').test(contentTypeHeader.toLowerCase()))
			return ContentType.HTML;
	}
}

var BrowserType = new Enum(
		'OTHER',
		'MSIE',
		'WEBKIT',
		'GECKO'
	);

function __identifyBrowserVersion(browserType)
{
	var version = 0;

	switch(browserType)
	{
		case BrowserType.MSIE:
			var results = new RegExp('.*MSIE\\s?([0-9]).*').exec(navigator.appVersion);
			if(results)
				version = parseInt(results[1]);
			break;
	}

	return version;
}

function __identifyBrowserType()
{
	var re;

	re = /MSIE/i;
	if(re.test(navigator.userAgent))
		return BrowserType.MSIE;

	re = /Safari/i;
	if(re.test(navigator.userAgent))
		return BrowserType.WEBKIT;

	re = /SeaMonkey\//i;
	if(re.test(navigator.userAgent))
		return BrowserType.SEAMONKEY;

	re = /Firefox\//i;
	if(re.test(navigator.userAgent))
		return BrowserType.FIREFOX;

	return BrowserType.OTHER;
}

var BROWSER_TYPE = __identifyBrowserType(),
	BROWSER_VERSION = __identifyBrowserVersion(BROWSER_TYPE);

Function.prototype.__getFunctionName_MS = function()
{
	return new RegExp('function\\s+(\\w+)\\b', 'ig').exec(this.toString())[1];
}

Function.prototype.__getFunctionName_W3C = function()
{
	return this.name;
}

switch(BROWSER_TYPE)
{
	case BrowserType.MSIE:
		Function.prototype.getFunctionName = Function.prototype.__getFunctionName_MS;
		break;

	default:
		Function.prototype.getFunctionName = Function.prototype.__getFunctionName_W3C;
		break;
}

if(BROWSER_TYPE == BrowserType.MSIE)
	if(!Array.prototype.indexOf)
		Array.prototype.indexOf = function(element)
		{
			for(var i = 0; i < this.length; i++)
				if(this[i] == element)
					return i;

			return -1;
		}

//--

Function.Templates = {};

Function.Templates.AddEventListener = function(eventType, eventListener)
{
	if(typeof(eventListener.handleEvent) != 'function')
		throw new Error('not_a_listener');

	if(!this.eventListeners[eventType])
		this.eventListeners[eventType] = [];

	if(this.eventListeners[eventType].indexOf(eventListener) < 0)
		this.eventListeners[eventType].push(eventListener);
}

Function.Templates.RemoveEventListener = function(eventType, eventListener)
{
	var listeners = this.eventListeners[eventType];
	if(listeners)
	{
		var idx = listeners.indexOf(eventListener);
		if(idx > - 1)
			listeners.splice(idx, 1);
	}
}

Function.Templates.Log = function(message, level)
{
	var loggerName = this.constructor.getFunctionName();
	Logger.GetLogger(loggerName).log(loggerName +': '+ message, level);
}

//--

String.prototype.startsWith = function(prefix)
{
	return this.valueOf().substr(0, prefix.length) == prefix;
}

String.prototype.endsWith = function(suffix)
{
	return this.valueOf().substr(this.length - suffix.length) == suffix;
}

String.prototype.trim = function()
{
	return this.replace(/^\s+/, '').replace(/\s+$/g, '');
}

String.prototype.toLocalized = function(_locale)
{
	var locale = typeof(_locale) == 'string' ? _locale : window.getLocale();

	if(!locale)
		console.log('WARNING: locale not defined');
	else
	{
		var resource = LocaleResource.GetResource(locale);

		if(resource)
		{
			message = resource.getMessage(this);

			if(message)
				return message;
			else
				console.log('WARNING: value not found for "'+ this +'" in resource for locale "'+ locale +'"');
		}
		else
			console.log('WARNING: resource not found for locale "'+ locale +'"');
	}

	return this;
}

String.prototype.format = function()
{
	var c = '\u200B',
		message = this.valueOf().replace('%%', c);

	for(var i = 0, varIdx; i < arguments.length; i++)
	{
		varIdx = message.indexOf('%');
		message = message.substring(0, varIdx) + arguments[i] + message.substring(varIdx +1);
	}

	message = message.replace(c, '%');

	return message;
}

String.prototype.toLocalizedWithParameters = function(parameters, locale)
{
	var localized = this.toLocalized(locale);
	return localized.format.apply(localized, parameters);
}

//--

function getLocale()
{
	return typeof(locale) != 'undefined' ? locale : Utils.GetCookie('locale');
/*	var locale = self.locale;

	try
	{
		if(!locale && self.parent && self.parent != self)
			locale = self.parent.getLocale();
	}
	catch(e)
	{
		console.log(e.message);
		console.log(e.stack);
	}

	try
	{
		if(!locale && self.opener)
			locale = self.opener.getLocale();
	}
	catch(e)
	{
		console.log(e.message);
		console.log(e.stack);
	}

	self.locale = locale;

	return locale;*/
}

if(BROWSER_TYPE == BrowserType.MSIE)
	window.__msEventListeners = {};

function __findHandler_MS(targetId, eventType, listenerId)
{
	if(window.__msEventListeners[targetId])
		if(window.__msEventListeners[targetId][eventType])
			if(window.__msEventListeners[targetId][eventType][listenerId])
				return window.__msEventListeners[targetId][eventType][listenerId];
	return null;
}

function __attachHandler_W3C(target, eventType, listener)
{
	target.addEventListener(eventType, listener, false);
}

function __attachHandler_MS(target, eventType, listener)
{
	eventType = eventType.toLowerCase();
	if(eventType.indexOf('on') != 0)
		eventType = 'on'+ eventType;

	var listenerId = window.document.uniqueID,
		targetId;

	switch(target.nodeType)
	{
		case 1: // Node.ELEMENT_NODE
			targetId = target.uniqueID;
			break;

		default: // window, ... ?
			targetId = 0;
			break;
	}

	if(!window.__msEventListeners[targetId])
		window.__msEventListeners[targetId] = {};
	if(!window.__msEventListeners[targetId][eventType])
		window.__msEventListeners[targetId][eventType] = {};

	var alreadyAttached = false,
		listeners = window.__msEventListeners[targetId][eventType];
	for(var id in listeners)
		if(listeners[id] == listener)
		{
			alreadyAttached = true;
			break;
		}
	if(!alreadyAttached)
	{
		window.__msEventListeners[targetId][eventType][listenerId] = listener;
		target.attachEvent(eventType, new Function('event', 'var listener = window.__findHandler_MS("'+ targetId +'", "'+ eventType +'", "'+ listenerId +'"); if(listener) { listener.handleEvent(event); }'));
	}
}

function __detachHandler_W3C(target, eventType, listener)
{
	target.removeEventListener(eventType, listener, false);
}

function __detachHandler_MS(target, eventType, listener)
{
	eventType = eventType.toLowerCase();
	if(eventType.indexOf('on') != 0)
		eventType = 'on'+ eventType;

	var targetId,
		listenerId = null;

	switch(target.nodeType)
	{
		case 1: // Node.ELEMENT_NODE
			targetId = target.uniqueID;
			break;

		default:
			targetId = 0;
			break;
	}

	if(window.__msEventListeners[targetId])
		if(window.__msEventListeners[targetId][eventType])
		{
			var listeners = window.__msEventListeners[targetId][eventType];
			for(var id in listeners)
				if(listeners[id] == listener)
				{
					delete listeners[id];
					listenerId = id;
					break;
				}
		}

	return listenerId;
}

switch(BROWSER_TYPE)
{
	case BrowserType.MSIE:
		attachHandler = __attachHandler_MS;
		detachHandler = __detachHandler_MS;
		break;

	default:
		attachHandler = __attachHandler_W3C;
		detachHandler = __detachHandler_W3C;
		break;
}

if(BROWSER_TYPE == BrowserType.MSIE)
{
	window.__executeElementEventHandler_MS = function(eventType, listenerIdx, elementId, event)
	{
		var listener = event.srcElement.ownerDocument.getElementById(elementId).__msEventListeners[eventType][listenerIdx];
		listener.handleEvent(event);
	}
}

if(BROWSER_TYPE == BrowserType.MSIE)
{
	window.__msTimeoutHandlers = {};
	window.__msTimeoutHandlers_map = {}; // timeoutId -> timeoutHandlerId

	window.__setTimeout = window.setTimeout;
	window.setTimeout = function(func, delay)
	{
		var args = [];
		for(var i = 2; i < arguments.length; i++)
			args.push(arguments[i]);

		var timeoutHandlerId = window.document.uniqueID,
			timeoutId = window.__setTimeout('window.__msTimeoutHandlers["'+ timeoutHandlerId +'"].go()', delay)
			timeoutHandler = {
					timeoutId: timeoutId,
					timeoutHandlerId: timeoutHandlerId,
					func: func,
					args: args,
					go: function()
					{
						delete window.__msTimeoutHandlers[timeoutHandlerId];
						delete window.__msTimeoutHandlers_map[timeoutId];
						if(typeof(this.func) == 'string')
							eval(this.func);
						else
							this.func.apply(self, this.args);
					}
				};
		window.__msTimeoutHandlers[timeoutHandlerId] = timeoutHandler;
		window.__msTimeoutHandlers_map[timeoutId] = timeoutHandlerId;
		return timeoutId;
	}

	window.__clearTimeout = window.clearTimeout;
	window.clearTimeout = function(timeoutId)
	{
		window.__clearTimeout(timeoutId);
		var timeoutHandlerId = window.__msTimeoutHandlers_map[timeoutId];
		delete window.__msTimeoutHandlers[timeoutHandlerId];
		delete window.__msTimeoutHandlers_map[timeoutId];
	}

	window.__msIntervalHandlers = {};
	window.__msIntervalHandlers_map = {}; // intervalId -> intervalHandlerId

	window.__setInterval = window.setInterval;
	window.setInterval = function(func, delay)
	{
		var args = [];
		for(var i = 2; i < arguments.length; i++)
			args.push(arguments[i]);

		var intervalHandlerId = window.document.uniqueID,
			intervalHandler = {
					func: func,
					args: args,
					go: function()
					{
						if(typeof(this.func) == 'string')
							eval(this.func);
						else
							this.func.apply(self, this.args);
					}
				};
		window.__msIntervalHandlers[intervalHandlerId] = intervalHandler;
		var intervalId = window.__setInterval('window.__msIntervalHandlers["'+ intervalHandlerId +'"].go()', delay);
		window.__msIntervalHandlers_map[intervalId] = intervalHandlerId;
		return intervalId;
	}

	window.__clearInterval = window.clearInterval;
	window.clearInterval = function(intervalId)
	{
		window.__clearInterval(intervalId);
		var intervalHandlerId = window.__msIntervalHandlers_map[intervalId];
		delete window.__msIntervalHandlers[intervalHandlerId];
		delete window.__msIntervalHandlers_map[intervalId];
	}
}

var Horizontal = new Enum(
		'LEFT',
		'CENTER',
		'RIGHT'
	),
	Vertical = new Enum(
		'TOP',
		'MIDDLE',
		'BOTTOM'
	);

attachHandler(window.document, 'mousemove', {
		handleEvent: function(event)
		{
			window.lastMouseX = event.clientX;
			window.lastMouseY = event.clientY;
		}
	});

var DISPLAY_NONE_CLASSNAME = 'net.jzaruba.gui.hidden',
	DISABLE_CLASSNAME = 'net.jzaruba.gui.disabled',
	HILITE_CLASSNAME = 'net.jzaruba.gui.hilited',
	ACTIVE_CLASSNAME = 'net.jzaruba.gui.active',
	DRAG_GIZMO_CLASSNAME = 'net.jzaruba.gui.dragGizmo',
	FLOATING_UI_CLASSNAME = 'net.jzaruba.gui.floatingUi',

	/*
	 * MSIE likes to ignore style.cssFloat, therefoe we rather 
	 * set float by this class
	 */
	FLOAT_LEFT_CLASSNAME = 'net.jzaruba.floatLeft',
	FLOAT_RIGHT_CLASSNAME = 'net.jzaruba.floatRight';

if(document.styleSheets.length > 0)
	switch(BROWSER_TYPE)
	{
		case BrowserType.MSIE:
			document.styleSheets[0].addRule('.'+ DISPLAY_NONE_CLASSNAME.replace(/\./g, '\\.'), 'display: none !important;', document.styleSheets[0].rules.length);
			document.styleSheets[0].addRule('.'+ FLOAT_LEFT_CLASSNAME.replace(/\./g, '\\.'), 'float: left;', document.styleSheets[0].rules.length);
			document.styleSheets[0].addRule('.'+ FLOAT_RIGHT_CLASSNAME.replace(/\./g, '\\.'), 'float: right;', document.styleSheets[0].rules.length);
			document.styleSheets[0].addRule('.'+ FLOATING_UI_CLASSNAME.replace(/\./g, '\\.'), 'z-index: 10;', document.styleSheets[0].rules.length);
			document.styleSheets[0].addRule('.'+ DRAG_GIZMO_CLASSNAME.replace(/\./g, '\\.'), 'z-index: 11; border: 1px dotted gray;', document.styleSheets[0].rules.length);
			break;

		default:
			document.styleSheets[0].insertRule('.'+ DISPLAY_NONE_CLASSNAME.replace(/\./g, '\\.') +' {display: none !important;}', document.styleSheets[0].cssRules.length);
			document.styleSheets[0].insertRule('.'+ FLOAT_LEFT_CLASSNAME.replace(/\./g, '\\.') +' {float: left;}', document.styleSheets[0].cssRules.length);
			document.styleSheets[0].insertRule('.'+ FLOAT_RIGHT_CLASSNAME.replace(/\./g, '\\.') +' {float: right;}', document.styleSheets[0].cssRules.length);
			document.styleSheets[0].insertRule('.'+ FLOATING_UI_CLASSNAME.replace(/\./g, '\\.') +' {z-index: 10;}', document.styleSheets[0].cssRules.length);
			document.styleSheets[0].insertRule('.'+ DRAG_GIZMO_CLASSNAME.replace(/\./g, '\\.') +' {z-index: 11; border: 1px dotted gray;}', document.styleSheets[0].cssRules.length);
			break;
	}
else
	throw new Error('unable_to_add_css_rules');

attachHandler(window, 'load', {
		handleEvent: function(event)
		{
			if(typeof(DEFAULT_SCRIPT_TO_IMPORT) != 'undefined')
				/*
				 * user has specified additional resource(s) to load and evaluate
				 */
				if(typeof(DEFAULT_SCRIPT_TO_IMPORT) == 'string')
					Utils.ImportScript(self, DEFAULT_SCRIPT_TO_IMPORT);
				else
					if(DEFAULT_SCRIPT_TO_IMPORT.length)
						Utils.ImportScriptsInSequence(self, DEFAULT_SCRIPT_TO_IMPORT);

			if(typeof(defaultLoadHandlers) != 'undefined')
				for(var i = 0, obj; obj = defaultLoadHandlers[i++];)
					{
						if(typeof(obj) == 'function')
							obj.apply(this);
						else
							obj.handleEvent(event);
					}

			detachHandler(window, 'load', this);
		}
	});
function Utils()
{
}

Utils.__EscapedChars = {
		34: '&quot;',
		39: '&#39;',
		60: '&lt;',
		62: '&gt;'
	};
Utils.Escape = function(string)
{
	for(var i = string.length, p; i > 0; i--)
	{
		p = i -1;

		var charCode = string.charCodeAt(p);
		if(Utils.__EscapedChars[charCode])
			string = string.substr(0, p).concat(Utils.__EscapedChars[charCode]).concat(string.substr(p +1));
	}

	return string;
}

/**
 * Přidá elementu CSS-třídu daného jména.
 *
 * @param {String} className ...
 */
Utils.AddCSSClass = function(htmlElement, className)
{
	if(Utils.TestCSSClass(htmlElement, className))
		return false;

	var classNames = htmlElement.className ? htmlElement.className.split(new RegExp('\\s+')) : [];
	classNames.push(className);
	htmlElement.className = classNames.join(' ');

	return true;
}

/**
 * Odstraní z elementu CSS-třídu daného jména.
 *
 * @param {String} className ...
 */
Utils.RemoveCSSClass = function(htmlElement, className)
{
	if(!Utils.TestCSSClass(htmlElement, className))
		return false;

	var tmpClassName = htmlElement.className.replace(new RegExp('\\b' + className + '\\b', 'g'), ''),
		classNames = tmpClassName.split(new RegExp('\\s+'));
	htmlElement.className = classNames.length > 1 ? classNames.join(' ') : classNames[0];

	return true;
}

/**
 * Otestuje, je-li element označen danou CSS-třídou.
 *
 * @param {String} className ...
 */
Utils.TestCSSClass = function(htmlElement, className)
{
//	return new RegExp('\\b' + className + '\\b', 'g').test(htmlElement.className);
	return new RegExp('(?:\\s|^)\\b' + className + '\\b(?:\\s|$)', 'g').test(htmlElement.className);
}

Utils.GetCookie = function(name)
{
	var re = new RegExp();
	re.compile(name + '=([^;]*);?', 'i');

	var reResults = [];

	if(!re.test(document.cookie))
		return null;

	reResults = re.exec(document.cookie);

	return reResults[1];
}

Utils.SetCookie = function(name, value, _expires, _domain, _path, _secure)
{
	var c = name + '=' + escape(value) +
		(typeof(_expires)!='undefined' ? '; expires=' + _expires.toGMTString() : '') +
		(typeof(_domain)!='undefined' ? '; domain=' + _domain : '') +
		(typeof(_path)!='undefined' ? '; path=' + _path : '') +
		(typeof(_secure)!='undefined' ? '; secure' : '');

	document.cookie = c;
}

Utils.LocalizeDate = function(date, _locale)
{
	var locale = typeof(_locale) == 'string' ? _locale : window.getLocale(),
		pattern = 'dateFormatPattern'.toLocalized(l);

	var formatter = {
			// "strftime" http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
			date: date,
			locale: locale,
			'char': '%',
			a: function() // is replaced by the locale's abbreviated weekday name.
			{
				return Utils.Day.values[(this.date.getDay() + 6) % 7].toLowerCase().toLocalized(this.locale).substr(0, 2);
			},
			e: function() // is replaced by the day of the month as a decimal number [1,31]; a single digit is preceded by a space.
			{
				return this.date.getDate();
			},
			m: function() // is replaced by the month as a decimal number [01,12].
			{
				return this.date.getMonth() +1;
			},
			B: function() // is replaced by the locale's full month name.
			{
				return Utils.Month.values[this.date.getMonth()].toLowerCase().toLocalized(this.locale)
			}
		};

	var string = pattern.replace(formatter['char'] + formatter['char'], formatter['char']),
		member;
	for(var mark in formatter)
	{
		member = formatter[mark];
		if(typeof(member) == 'function')
			string = string.replace(formatter['char'] + mark, member.apply(formatter));
	}
	return string;
}

Utils.Day = new Enum(
		'MONDAY',
		'TUESDAY',
		'WEDNESDAY',
		'THURSDAY',
		'FRIDAY',
		'SATURDAY',
		'SUNDAY'
	);

Utils.Month = new Enum(
		'JANUARY',
		'FEBRUARY',
		'MARCH',
		'APRIL',
		'MAY',
		'JUNE',
		'JULY',
		'AUGUST',
		'SEPTEMBER',
		'OCTOBER',
		'NOVEMBER',
		'DECEMBER'
	);

Utils.GetComputedStyleProperty_W3C = function(element, propertyName)
{
	return element.ownerDocument.defaultView.getComputedStyle(element, null).getPropertyValue(propertyName);
}

Utils.GetComputedStyleProperty_MS = function(element, propertyName)
{
	return element.currentStyle[propertyName];
}

switch(BROWSER_TYPE)
{
	case BrowserType.MSIE:
		Utils.GetComputedStyleProperty = Utils.GetComputedStyleProperty_MS;
		break;

	default:
		Utils.GetComputedStyleProperty = Utils.GetComputedStyleProperty_W3C;
		break;
}

/**
 * Ze style-property vyjadřující rozměr/délku extrahuje číselnou část.
 * Příklad: ze '320px' vrátí 320;
 *
 * @param propertyValue ...
 * @param unit předpokládané jednotky
 * @param unitIsDefault jednotky nemusí být ve vstupu definovány (jsou default)
 *
 * @return nalezenou číselnou hodnotu
 */
Utils.ExtractDimension = function(propertyValue, _unit, _unitIsDefault)
{
	var re = /^(-?0[0-9]+\.?[0-9]*)([^0-9]*)/i,
		results = re.exec(propertyValue);

	if(results &&
		(
			// units not defined ...
			typeof(_unit) == 'undefined' ||
			(
				// or may be considered a default and therefore ommited...
				(!results[2] && _unitIsDefault) ||
				// or they just do match
				(_unit == results[2])
			)
		)
	)
		return results[1] *1;
	else
		return null;
}

Utils.CreateElement = function(document, tagName, _attributes)
{
	var element = document.createElement(tagName);

	if(typeof(_attributes) != 'undefined')
		for(var idx in _attributes)
			element.setAttribute(idx, _attributes[idx]);

	return element;
}

/**
 * ...
 *
 * @param input ...
 * @param length ...
 * @param ch ...
 */
Utils.PadRight = function(string, length, ch)
{
	if(typeof(string) != 'string')
		throw new Error('not_string');

	var l = string.length;

	for(var i = 0; i < length - l; i++)
		string = string.concat(ch);

	return string;
}

/**
 * ...
 *
 * @param input ...
 * @param length ...
 * @param ch ...
 */
Utils.PadLeft = function(string, length, ch)
{
	if(typeof(string) != 'string')
		throw new Error('not_string');

	var l = string.length;

	for(var i = 0; i < length - l; i++)
		string = ch.concat(string);

	return string;
}

Utils.__CreateXMLHttpRequest_W3C = function(_listener)
{
	var xmlHttpRequest = new XMLHttpRequest();
	if(typeof(_listener) != 'undefined')
	{
		xmlHttpRequest.addEventListener('load', _listener, false);
		xmlHttpRequest.addEventListener('error', _listener, false);
	}

	return xmlHttpRequest;
}

Utils.__CreateXMLHttpRequest_MSIE6 = function(_listener)
{
	var xmlHttpRequest = new ActiveXObject('MSXML2.XMLHTTP.3.0');
	if(typeof(_listener) != 'undefined')
	{
		if(!window.__msEventListeners)
			window.__msEventListeners = {};
		if(!window.__msEventListeners['readystatechange'])
			window.__msEventListeners['readystatechange'] = {};
		var id = window.document.uniqueID;
		window.__msEventListeners['readystatechange'][id] = {request: xmlHttpRequest, listener: _listener};

		xmlHttpRequest.onreadystatechange = new Function('var id = "'+ id +'", request = window.__msEventListeners["readystatechange"][id].request, listener = window.__msEventListeners["readystatechange"][id].listener; if(request.readyState == 4) { listener.handleEvent({target: request}); delete window.__msEventListeners["readystatechange"][id]; }');
	}

	return xmlHttpRequest;
}

Utils.__CreateXMLHttpRequest_MSIE7 = function(_listener)
{
	var xmlHttpRequest = new XMLHttpRequest();
	if(typeof(_listener) != 'undefined')
	{
		xmlHttpRequest.listener = _listener;
		xmlHttpRequest.onreadystatechange = function()
		{
			if(this.readyState == 4)
				this.listener.handleEvent({target: this});
		}
	}

	return xmlHttpRequest;
}

switch(BROWSER_TYPE)
{
	case BrowserType.MSIE:
		Utils.__CreateXMLHttpRequest = typeof(XMLHttpRequest) == 'undefined' ? Utils.__CreateXMLHttpRequest_MSIE6 : Utils.__CreateXMLHttpRequest_MSIE7;
		break;

	default:
		Utils.__CreateXMLHttpRequest = Utils.__CreateXMLHttpRequest_W3C;
		break;
}

Utils.CreateXMLHttpRequest = function()
{
	console.log('!Utils.CreateXMLHttpRequest'); // DEPRECATED (melo by se volat odeslani requestu prislusne metody)
	return Utils.__CreateXMLHttpRequest.apply(this, arguments);
}

/**
 * @param {Object} _listener jakakoli hodnota (i null) zpusobi, ze request bude asynchronni/neblokujici;
 */
Utils.SendHttpHEADRequestWithHeaders = function(url, headers, _listener)
{
	var listener = typeof(_listener) != 'undefined' ? _listener : null,
		async = listener ? true : false,
		xmlHttpRequest = async ? Utils.__CreateXMLHttpRequest(listener) : Utils.__CreateXMLHttpRequest();

	xmlHttpRequest.open('HEAD', url, async);

	for(var fieldName in headers)
		xmlHttpRequest.setRequestHeader(fieldName, headers[fieldName]);

	xmlHttpRequest.send(null);

	return xmlHttpRequest;
}

Utils.SendHttpHEADRequest = function(url, _listener)
{
	return Utils.SendHttpHEADRequestWithHeaders(url, {}, _listener);
}

/**
 * @param {Object} _listener jakakoli hodnota (i null) zpusobi, ze request bude asynchronni/neblokujici;
 */
Utils.SendHttpPOSTRequestWithHeaders = function(url, data, headers, _responseHandler)
{
	var message = Utils.BuildURIDataMessage(data),
		responseHandler = typeof(_responseHandler) != 'undefined' ? _responseHandler : null,
		async = responseHandler ? true : false,
		xmlHttpRequest;

	if(responseHandler)
		xmlHttpRequest = Utils.__CreateXMLHttpRequest({
				responseHandler: responseHandler,
				handleEvent: function(event)
				{
					var request = event.target;
					if(request.readyState == 4)
						this.responseHandler.handleResponse(request);
				}
			});
	else
		xmlHttpRequest = Utils.__CreateXMLHttpRequest();

	xmlHttpRequest.open('POST', url, async);

	Utils.__SetRequestHeaders(xmlHttpRequest, headers, {
			'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
			'cache-control': 'no-cache',
			'content-length': message.length
		});

	xmlHttpRequest.send(message);

	return xmlHttpRequest;
}

Utils.SendHttpPOSTRequest = function(url, data, _responseHandler)
{
	return Utils.SendHttpPOSTRequestWithHeaders.call(this, url, data, {}, _responseHandler);
}

Utils.__SetRequestHeaders = function(request, headers, _defaultHeaders)
{
	var headersMerged;

	if(typeof(_defaultHeaders) != 'undefined')
	{
		headersMerged = {};

		// copy all default headers, setting their field-names to lower-case
		for(var fieldName in _defaultHeaders)
			headersMerged[fieldName.toLowerCase()] = _defaultHeaders[fieldName];

		for(var fieldName in headers)
		{
			// remove default header for this field
			delete headersMerged[fieldName.toLowerCase()];
			headersMerged[fieldName] = headers[fieldName];
		}
	}
	else
		headersMerged = headers;

	for(var fieldName in headersMerged)
		request.setRequestHeader(fieldName, headersMerged[fieldName]);

	return headersMerged;
}

/**
 * Pokud je pritomen parametr responseHandler, je request vykonan asynchronne.
 * @param {Object} _responseHandler tomuto handleru bude po ukonceni predan request v parametru
 */
Utils.MakeRequestWithHeaders = function(url, headers, _responseHandler)
{
	var responseHandler = typeof(_responseHandler) != 'undefined' ? _responseHandler : null,
		async = responseHandler ? true : false,
		xmlHttpRequest;

	if(responseHandler)
		xmlHttpRequest = Utils.__CreateXMLHttpRequest({
				responseHandler: responseHandler,
				handleEvent: function(event)
				{
					var request = event.target;
					if(request.readyState == 4)
						this.responseHandler.handleResponse(request);
				}
			});
	else
		xmlHttpRequest = Utils.__CreateXMLHttpRequest();

	xmlHttpRequest.open('GET', url, async);

	Utils.__SetRequestHeaders(xmlHttpRequest, headers, {
			'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
			'cache-control': 'no-cache'
		});

	xmlHttpRequest.send(null);

	return xmlHttpRequest;
}

Utils.MakeRequest = function(url, _responseHandler)
{
	return Utils.MakeRequestWithHeaders.call(this, url, {}, _responseHandler);
}

/**
 * ...
 *
 * @param {Element} replacement
 * @param {Element} placeholder
 * @param {Array} attributesToCopy
 */
Utils.ReplaceElements = function(replacement, placeholder, _attributesToCopy)
{
	if(typeof(_attributesToCopy) != 'undefined' && _attributesToCopy.length > 0)
		for(var i = 0, attName; i < _attributesToCopy.length; i++)
		{
			attName = _attributesToCopy[i];
			replacement.setAttribute(attName, placeholder.getAttribute(attName));
		}

	placeholder.parentNode.replaceChild(replacement, placeholder);
}

Utils.GetGap = function(refElement, toElement)
{
	var gap = {
			top: 0,
			right: 0,
			bottom: 0,
			left: 0
		},
		node = refElement;
	while(node != toElement)
	{
		if(!node.offsetParent)
			throw new Error('element_unreachable');

		gap.top += node.offsetTop;
		gap.right += node.offsetParent.clientWidth - node.offsetWidth - node.offsetLeft;
		gap.bottom += node.offsetParent.clientHeight - node.offsetHeight - node.offsetTop;
		gap.left += node.offsetLeft;

		node = node.offsetParent;
	}

	return gap;
}

Utils.GetAbsoluteOffset = function(fromHtmlElement, _toHtmlElement)
{
	var toHtmlElement = typeof(_toHtmlElement) != 'undefined' ? _toHtmlElement : fromHtmlElement.ownerDocument.documentElement,
		tmp = fromHtmlElement,
		offset = {top: 0, left: 0};
/*
 * mozny problem:
 * offsetParent neukazuje vzdy na primo-nadrazeny element (treba TR neni offsetParentem pro TD);
 * pokud by tedy toHtmlElementem bylo TR, nastal by mess
 */
	// posbirat offset od vsech offsetParents
	var offsetLeft = 0,
		offsetTop = 0;
	while(tmp && tmp != toHtmlElement)
	{
		offsetLeft += tmp.offsetLeft;
		offsetTop += tmp.offsetTop;

		tmp = (tmp.parentNode && tmp.offsetParent) ? tmp.offsetParent : null;
	}
	offset.left += offsetLeft;
	offset.top += offsetTop;

	tmp = fromHtmlElement.parentNode;

	if(BROWSER_TYPE != BrowserType.WEBKIT)
	{
			// posbirat scroll od vsech offsetParents
			var scrollLeft = 0,
				scrollTop = 0;
			while(tmp && tmp != toHtmlElement && tmp.nodeType == 1 && tmp.style.position != 'relative')
			{
				scrollLeft += tmp.scrollLeft;
				scrollTop += tmp.scrollTop;

				tmp = tmp.parentNode;
			}
			offset.left -= scrollLeft;
			offset.top -= scrollTop;
	}

	return offset;
}

Utils.GetQueryParameter = function(url, name)
{
	var value = null,
		queryStartIdx = url.indexOf('?') + 1;
	if(queryStartIdx > 0)
	{
		var query = url.substr(queryStartIdx),
			parameters_GET = Utils.ParseQueryParameters(query);

		value = parameters_GET[name];
	}

	return value;
}

Utils.RemoveQueryParameter = function(url, name)
{
	return Utils.SetQueryParameter(url, name, null);
}

Utils.SetQueryParameter = function(url, name, value)
{
	var queryStartIdx = url.indexOf('?') + 1;
	if(queryStartIdx > 0)
	{
		/*
		 * query string present
		 */

		var query = url.substr(queryStartIdx),
			parameters_GET = Utils.ParseQueryParameters(query);

		if(value == null)
			delete parameters_GET[name];
		else
			parameters_GET[name] = value;

		url = url.substr(0, queryStartIdx) + Utils.BuildURIDataMessage(parameters_GET);
	}
	else
		/*
		 * query string not present
		 */
		if(value != null)
			url += '?'+ name +'='+ value;

	return url;
}

/**
 * Implicitní poevod JS-arrays do AS pro Flash vypouští vlastnost length.
 * Proto pole poevádími na objekty s vlastností length.
 *
 * @param possiblyArray ...
 * @return ...
 */
Utils.FixForFlash = function(possiblyArray)
{
	if(typeof(possiblyArray) == 'object')
		if(typeof(possiblyArray.length) == 'number')
		{
			var result = {length: possiblyArray.length};
			for(var i = 0; i < possiblyArray.length; i++)
				result[i] = Utils.FixForFlash(possiblyArray[i]);
			return result;
		}
		else
		{
			var result = {};
			for(var idx in possiblyArray)
				result[idx] = Utils.FixForFlash(possiblyArray[idx]);
			return result;
		}

	return possiblyArray;
}

/**
 * @param {String} dateString '2009-08-30 04:00:00'
 */
Utils.ParseDate = function(dateString)
{
	var re = /([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/,
		result = re.exec(dateString);
	return new Date(result[1], result[2] -1, result[3], result[4], result[5], result[6], 0);
}

Utils.ParseQueryParameters = function(query)
{
	var urlParameters = {},
		uriComponents = query.split('&'),
		uriComponent,
		keyValePair;
	for(var i = 0; i < uriComponents.length; i++)
	{
		uriComponent = uriComponents[i];
		keyValePair = uriComponent.split('=');
		urlParameters[decodeURIComponent(keyValePair[0])] = decodeURIComponent(keyValePair[1]);
	}

	return urlParameters;
}

// {

function __listEntry(key, _value)
{
	var component;

	if(typeof(_value) == 'object')
		if(_value !== null)
		{
			var components = [];
			if(typeof(_value.length) == 'number')
				/*
				 * array
				 */
				if(_value.length > 0)
					for(var i = 0; i < _value.length; i++)
						components.push(__listEntry(key +'['+ i +']', _value[i]));
				else
					/*
					 * empty array
					 */
					components.push(key +'=');
			else
				/*
				 * object
				 */
				for(var idx in _value)
					components.push(__listEntry(key +'.'+ idx, _value[idx]));
			component = components.join('&');
		}
		else
			component = encodeURIComponent(key) +'=';
	else
		if(typeof(_value) == 'undefined')
			component = encodeURIComponent(key) +'=';
		else
			/*
			 * other types
			 */
			component = encodeURIComponent(key) +'='+ encodeURIComponent(_value);

	return component;
}

/**
 * @param {Object} data ...
 */
Utils.BuildURIDataMessage = function(data)
{
	var components = [];

	for(var idx in data)
		components.push(__listEntry(idx, data[idx]));

	return components.join('&');
}

// }

Utils.__ImportScript_Element = function(targetWindow, scriptSrcAttribute, _callbackObject)
{
	var scriptElement = targetWindow.document.createElement('script');
	scriptElement.setAttribute('src', scriptSrcAttribute);
	scriptElement.setAttribute('type', 'text/javascript');
	scriptElement.setAttribute('defer', 'defer');
	scriptElement.addEventListener('load', {
			callbackObject: typeof(_callbackObject) != 'undefined' ? _callbackObject : null,
			handleEvent: function(event)
			{
				if(this.callbackObject)
					this.callbackObject.onLoad(event);
			}
		}, false);
	scriptElement.addEventListener('error', {
			callbackObject: typeof(_callbackObject) != 'undefined' ? _callbackObject : null,
			handleEvent: function(event)
			{
				if(this.callbackObject)
					this.callbackObject.onError(event);
			}
		}, false);
	targetWindow.document.getElementsByTagName('head')[0].appendChild(scriptElement);
}

Utils.ImportScript = function(targetWindow, scriptSrcAttribute, _callbackObject)
{
	if(true)
		return Utils.__ImportScript_Element.apply(this, arguments);

	var callbackObject = typeof(_callbackObject) != 'undefined' ? _callbackObject : null;
	if(callbackObject)
	{
		var responseHandler = {
					targetWindow: targetWindow,
					callbackObject: callbackObject,
					handleResponse: function(xmlHttpRequest)
					{
						if(xmlHttpRequest.readyState == 4)
							switch(xmlHttpRequest.status)
							{
								case 200: // OK
									if(identifyContent(xmlHttpRequest) == ContentType.JS)
										try
										{
											this.targetWindow.eval(xmlHttpRequest.responseText);
										}
										catch(e)
										{
											console.log(e.message);
										}

									if(this.callbackObject)
										this.callbackObject.onLoad(xmlHttpRequest);
									break;

								default:
									if(this.callbackObject)
										this.callbackObject.onError(xmlHttpRequest);
									break;
							}
					}
				},
			xmlHttpRequest = Utils.MakeRequest(scriptSrcAttribute, responseHandler);
	}
	else
	{
		var xmlHttpRequest = Utils.MakeRequest(scriptSrcAttribute);
		if(identifyContent(xmlHttpRequest) == ContentType.JS)
			targetWindow.eval(xmlHttpRequest.responseText);
	}
}

Utils.ImportScriptsInSequence = function(targetWindow, srcAttributes, _callbackObject)
{
	var obj = {
			targetWindow: targetWindow,
			callbackObject: (typeof(_callbackObject) != 'undefined') ? _callbackObject : null,
			srcAttributes: srcAttributes,
			idx: 0,
			__loadNext: function()
			{
				var srcAttribute = this.srcAttributes[this.idx++];
				Utils.ImportScript(this.targetWindow, srcAttribute, this);
			},
			start: function()
			{
				this.__loadNext();
			},
			onLoad: function(event)
			{
				if(this.idx < this.srcAttributes.length)
					this.__loadNext();
				else
					/*
					 * no scripts to load
					 */
					if(this.callbackObject)
						this.callbackObject.onLoad(event);
			},
			onError: function(event)
			{
				if(this.callbackObject)
					this.callbackObject.onError(event);
			}
		};
	obj.start();
}

Utils.ImportStyle = function(targetWindow, styleHrefAttribute)
{
	var linkEleemnt = targetWindow.document.createElement('link');
	linkEleemnt.setAttribute('type', 'text/css');
	linkEleemnt.setAttribute('rel', 'stylesheet');
	linkEleemnt.setAttribute('media', 'all');
	linkEleemnt.setAttribute('href', styleHrefAttribute);
	/*
	 * seems that load/error handlers don't work the way they do for scripts
	 */
	targetWindow.document.getElementsByTagName('head')[0].appendChild(linkEleemnt);
}

/**
 *
 * @param {Element|Array} element_or_more
 */
Utils.CalculateAbsRect = function()
{
	var lefts = [],
		tops = [],
		widths = [],
		heights = [];

	for(var i = 0, totalOffset; i < arguments.length; i++)
	{
		totalOffset = Utils.GetAbsoluteOffset(arguments[i]);
		tops.push(totalOffset.top);
		lefts.push(totalOffset.left);
		widths.push(arguments[i].offsetWidth);
		heights.push(arguments[i].offsetHeight);
	}

	return {
			left: Math.min.apply(self, lefts),
			top: Math.min.apply(self, tops),
			width: Math.max.apply(self, widths),
			height: Math.max.apply(self, heights)
		};
}

Utils.CalculateIntersection = function(rect1, rect2)
{
	var left = Math.max(rect1.left, rect2.left),
		top = Math.max(rect1.top, rect2.top),
		width = Math.min(rect1.left + rect1.width, rect2.left + rect2.width) - left,
		height = Math.min(rect1.top + rect1.height, rect2.top + rect2.height) - top;

	return {left: left, top: top, width: width, height: height};
}

Utils.CreateRectangle = function(ownerDocument, left, top, width, height)
{
	var rectangle = ownerDocument.createElement('div');
	rectangle.style.position = 'absolute';
	rectangle.style.left = left +'px';
	rectangle.style.top = top +'px';
	rectangle.style.width = width +'px';
	rectangle.style.height = height +'px';
	return rectangle;
}

/**
 * Vrati "adresu" daneho nodu.
 * Adresou je mineno pole odkazu na vetev, jehoz prvni polozkou je documentElement,
 * posledni polozkou je samotny node.
 *
 * @param node objekt, jehoz adresu chceme znat
 * @return adresa nodu
 */
Utils.GetNodePath = function(node)
{
	var path = [node];
	var parent_node = node.parentNode;
	while(parent_node && parent_node.nodeType != Node.DOCUMENT_NODE)
	{
		path.unshift(parent_node);
		parent_node = parent_node.parentNode;
	}
	return path;
}

Utils.CreateDefaultNSResolver = function(node)
{
	switch(node.nodeType)
	{
		case Node.DOCUMENT_NODE:
			return new Function('return "'+ node.documentElement.namespaceURI +'"');

		case Node.ELEMENT_NODE:
			return new Function('return "'+ node.ownerDocument.documentElement.namespaceURI +'"');
	}
}

Utils.__DiacriticsMap = {
		a: '\\u00E1',
		c: '\\u010D',
		d: '\\u010F',
		e: '\\u00E9\\u011B',
		i: '\\u00ED',
		n: '\\u0148',
		o: '\\u00F3',
		r: '\\u0159',
		s: '\\u0161',
		t: '\\u0165',
		u: '\\u00FA\\u016F',
		y: '\\u00FD',
		z: '\\u017E',
		A: '\\u00C1',
		C: '\\u010C',
		D: '\\u010E',
		E: '\\u00C9\\u011A',
		I: '\\u00CD',
		N: '\\u0147',
		O: '\\u00D3',
		R: '\\u0158',
		S: '\\u0160',
		T: '\\u0164',
		U: '\\u00DA\\u016E',
		Y: '\\u00DD',
		Z: '\\u017D'
	};
Utils.RemoveDiacritics = function(str)
{
	for(var letter in Utils.__DiacriticsMap)
		str = str.replace(new RegExp('['+ Utils.__DiacriticsMap[letter] +']', 'g'), letter);
	return str;
}

/**
 * ...
 *
 * Jednosměrné hledání vztahu mezi nody funguje tak, že hledání vazby je provedeno
 * pouze ve směru od node2 stromem vzhůru; pokud mezi předky node2 není node1 nalezen,
 * hledání dále neprobíhá, a je vyvolána vyjímka. (Zjednodušeně se dá řící, že předpokládáme,
 * že node1 je rodičem, node2 potomkem.)
 *
 * @param {Node} node1 ...
 * @param {Node} node2 ...
 * @param owm {boolean} (optional) jednosměrné hledání relationship
 * @return počet kroků/nodů od node2 k node1, null neexistuje-li vztah
 */
Utils.GetRelationship = function(node1, node2, _oneWayMode)
{
	var oneWayMode = (typeof(_oneWayMode) == 'boolean') ? _oneWayMode : false;

	if(node1 == node2)
		return 0;

	var tmp,
		relationship;

	tmp = node2.parentNode;
	relationship = 0;
	while(tmp)
	{
		relationship++;
		if(tmp == node1)
			return relationship;

		tmp = tmp.parentNode;
	}

	tmp = node1.parentNode;
	relationship = 0;
	while(tmp && !oneWayMode)
	{
		relationship--;
		if(tmp == node2)
			return relationship;

		tmp = tmp.parentNode;
	}

	return null;
}

Utils.IsElementWithinDOM = function(domRoot, element)
{
	return Utils.GetRelationship(domRoot, element, true) != null;
}

Utils.IsPointAboveNode = function(x, y, node)
{
	var totalNodeOffset = Utils.GetAbsoluteOffset(node),
		isAbove = Utils.IsPointInRectangle(x, y, totalNodeOffset.left, totalNodeOffset.top, totalNodeOffset.left + node.offsetWidth +1, totalNodeOffset.top + node.offsetHeight +1)

	return isAbove;
}

Utils.IsPointInRectangle = function(x, y, x1, y1, x2, y2)
{
	var isActuallyOut =
		x < x1 ||
		x >= x2 ||
		y < y1 ||
		y >= y2;

	return !isActuallyOut;
}

/**
 * Vyhleda prvni vhodny input a preda mu focus
 *
 * @return input, ktery focus prijal
 */
Utils.FocusFormInput = function(form)
{
	for(var i = 0, element; i < form.elements.length; i++)
	{
		element = form.elements[i];

		try
		{
			switch(element.tagName)
			{
				case 'input':
					switch(element.type)
					{
						case 'hidden':
							continue;
					}
				case 'select':
				case 'textarea':
					element.focus();
					return element;
			}
		}
		catch(e)
		{
			// element z nejakeho duvodu neprijal focus
		}
	}

	return null;
}

/**
 * @return {Date} ...
 */
Utils.GetWeekStart = function(refDate, imperial)
{
	var offset = Utils.GetFirstDayInMonth(refDate, imperial),
		dayInMonth = Math.floor((refDate.getDate() + offset -1) / 7) * 7 - offset +1,
		date = new Date();
	date.setFullYear(refDate.getFullYear(), refDate.getMonth(), dayInMonth);
	date.setHours(0, 0, 0, 0);
	return date;
}

/**
 * @param imperial ...
 * @return {int} pořadové číslo (v týdnu) prvního dne v měsíci daného data
 */
Utils.GetFirstDayInMonth = function(refDate, imperial)
{
	var date = new Date();
	date.setFullYear(refDate.getFullYear(), refDate.getMonth(), 1);
	date.setHours(0, 0, 0, 0);
	return (typeof(imperial) == 'boolean' && imperial) ? date.getDay() : (date.getDay() + 6) % 7;
}

/**
 * @return {int} poeet dní v misíci
 */
Utils.GetMonthLength = function(refDate)
{
	return new Date(refDate.getFullYear(), refDate.getMonth() + 1, 0).getDate();
}

Utils.GetRootNode = function(node)
{
	var rootNode = node;
	while(rootNode.parentNode && (BROWSER_TYPE != BrowserType.MSIE || rootNode.parentNode.nodeType != 11))
		rootNode = rootNode.parentNode;
	return rootNode;
}

Utils.__FindFirstPositionedOffsetAncestor = function(element, _lastToTest)
{
	var lastToTest = typeof(_lastToTest) != 'undefined' ? _lastToTest : element.ownerDocument.documentElement,
		tmp = element.offsetParent;
	if(tmp)
		do
		{
			if(!tmp.offsetParent || Utils.GetComputedStyleProperty(tmp, 'position') != 'static')
				break;
			tmp = tmp.offsetParent;
		}
		while(tmp && tmp != lastToTest)

	return tmp;
}

/*Utils.__GetElementPositioning_MS = function(element)
{
	return element.currentStyle.position;
}

Utils.__GetElementPositioning_W3C = function(element)
{
	return element.currentStyle.position;
}

switch(BROWSER_TYPE)
{
	case BrowserType.MSIE:
		Utils.__GetElementPositioning = Utils.__GetElementPositioning_MS;
		break;

	default:
		Utils.__GetElementPositioning = Utils.__GetElementPositioning_W3C;
		break;
}*/

// alignment {

/*
 * left je absolutni, proto musime zjistit offset k nejblizsimu pozicovanemu elementu,
 * a offset od nej k rootu od absolutniho offsetu odecist
 */
Utils.AlignElementToX = function(element, left, hAlign)
{
	element.style.position = 'absolute';
//	element.style.zIndex = 2;

	// find nearest positioned element {

	var nearestPositionedAncestor = Utils.__FindFirstPositionedOffsetAncestor(element);
	if(!nearestPositionedAncestor)
		return;

	left -= Utils.GetAbsoluteOffset(nearestPositionedAncestor).left;
	if(BROWSER_TYPE == BrowserType.MSIE)
		left -= nearestPositionedAncestor.clientLeft;

	// }

	switch(hAlign)
	{
		case Horizontal.RIGHT:
			var oldLeft = element.style.left;
			element.style.left = left +'px';
			return oldLeft != element.style.left;

		case Horizontal.CENTER:
			var oldLeft = element.style.left;
			element.style.left = left - element.offsetWidth / 2 +'px';
			// odecist ??? document.body.scrollLeft
			return oldLeft != element.style.left;

		case Horizontal.LEFT:
			var oldRight = element.style.right;
			element.style.right = nearestPositionedAncestor.offsetWidth/*clientWidth*/ - left +'px';
			// odecist ??? document.body.scrollLeft
			return oldRight != element.style.right;
	}
}

Utils.AlignElementToY = function(element, top, vAlign)
{
	element.style.position = 'absolute';
//	element.style.zIndex = 2;

	// find nearest positioned element {

	var nearestPositionedAncestor = Utils.__FindFirstPositionedOffsetAncestor(element);
	if(!nearestPositionedAncestor)
		return;

	top -= Utils.GetAbsoluteOffset(nearestPositionedAncestor).top;
	if(BROWSER_TYPE == BrowserType.MSIE)
		top -= nearestPositionedAncestor.clientTop;

	// }

	switch(vAlign)
	{
		case Vertical.BOTTOM:
			var oldTop = element.style.top;
			element.style.top = top +'px';
			return oldTop != element.style.top;

		case Vertical.MIDDLE:
			var oldTop = element.style.top;
			element.style.top = top - element.offsetHeight / 2 +'px';
			 // odecist ??? document.body.scrollTop
			return oldTop != element.style.top;

		case Vertical.TOP:
			var oldBottom = element.style.bottom;
			element.style.bottom = nearestPositionedAncestor.offsetHeight/*clientHeight*/ - top +'px';
			 // odecist ??? document.body.scrollTop
			return oldBottom != element.style.bottom;
	}
}

/**
 * @param {HTMLElement} element
 * @param {Horizontal} hAlign
 * @param {HTMLElement} refElement
 * @param {Horizontal} refEdge
 * @param {int} _offset
 */
Utils.SetElementHorizontalAlignment = function(element, hAlign, refElement, refEdge, _offset)
{
	var refPoint = Utils.GetAbsoluteOffset(refElement).left;
	switch(refEdge)
	{
		case Horizontal.CENTER:
			refPoint += refElement.offsetWidth / 2;
			break;

		case Horizontal.RIGHT:
			refPoint += refElement.offsetWidth;
			break;
	}

	return Utils.AlignElementToX(element, refPoint + (isNaN(_offset) ? 0 : _offset), hAlign);
}

/**
 * @param {HTMLElement} element
 * @param {Vertical} vAlign
 * @param {HTMLElement} refElement
 * @param {Vertical} refEdge
 * @param {int} _offset
 */
Utils.SetElementVerticalAlignment = function(element, vAlign, refElement, refEdge, _offset)
{
	var refPoint = Utils.GetAbsoluteOffset(refElement).top;
	switch(refEdge)
	{
		case Vertical.MIDDLE:
			refPoint += refElement.offsetHeight / 2;
			break;

		case Vertical.BOTTOM:
			refPoint += refElement.offsetHeight;
			break;
	}

	return Utils.AlignElementToY(element, refPoint + (isNaN(_offset) ? 0 : _offset), vAlign);
}

Utils.GetHash = function(string)
{
	var re = new RegExp('^.*#([^#]+)$');
	if(re.test(string))
		return re.exec(string)[1];
	else
		return null;
}

Utils.DispatchSimpleEvent_W3C = function(eventType, target)
{
	var event = target.ownerDocument.createEvent('HTMLEvents');
	event.initEvent(
			eventType, // type
			true, // canbubble
			false // cancellable
		);
	target.dispatchEvent(event);
}

Utils.DispatchSimpleEvent_MS = function(eventType, target)
{
	eventType = eventType.toLowerCase();
	if(eventType.indexOf('on') != 0)
		eventType = 'on'+ eventType;

	target.fireEvent(eventType);
}

Utils.ResolveHref = function(href, _document)
{
	var doc = typeof(_document) == 'undefined' ? document : doc;
		anchorElement = doc.createElement('a');
	anchorElement.setAttribute('href', href);
	return anchorElement.href;
}

switch(BROWSER_TYPE)
{
	case BrowserType.MSIE:
		Utils.DispatchSimpleEvent = Utils.DispatchSimpleEvent_MS;
		break;

	default:
		Utils.DispatchSimpleEvent = Utils.DispatchSimpleEvent_W3C;
		break;
}

// }

/*function trace(message)
{
	if(window.top != self)
		try
		{
			window.top.trace(message);
			return;
		}
		catch(e)
		{
			// window top nezna trace
		}

	try
	{
		if(typeof(logConsole) == 'object' && logConsole)
			logConsole.trace(message);
		else
			window.dump(Utils.RemoveDiacritics(message.toString()) +'\n');
	}
	catch(e)
	{
		window.status = 'trace_failed';
	}
}*/
function CustomEvent()
{
	this.__init.apply(this, arguments);
}

self.CustomEvent = CustomEvent;

// private {

CustomEvent.prototype.__init = function(type)
{
	this.type = type;
	this.target = null;
	this.currentTarget = null;
}

CustomEvent.prototype.__runHandlers = function(eventDispatcher)
{
	var eventListeners = eventDispatcher.eventListeners[this.type];
	if(eventListeners)
		for(var i = 0; i < eventListeners.length; i++)
			try
			{
				eventListeners[i].handleEvent(this);
			}
			catch(e)
			{
				console.log(eventDispatcher.constructor.getFunctionName() +' listener error: '+ e.message);
				if(e.stack)
					console.log(e.stack);
			}
}

CustomEvent.prototype.__dispatch = function(eventDispatcher)
{
	this.currentTarget = eventDispatcher;

	this.__runHandlers(eventDispatcher);
}

// }

// public {

CustomEvent.prototype.dispatch = function(eventDispatcher)
{
	this.target = eventDispatcher;

	this.__dispatch(eventDispatcher);
}

// }

//--

function CustomGUIEvent()
{
	this.__init.apply(this, arguments);
}

self.CustomGUIEvent = CustomGUIEvent;

CustomGUIEvent.Extend(CustomEvent);

// private {

CustomGUIEvent.prototype.__init = function(type, originalDOMEvent)
{
	CustomGUIEvent.Super().prototype.__init.apply(this, arguments);

	this.originalDOMEvent = originalDOMEvent;

	this.bubblePath = [];

	this.bubbles = true;
	this.cancelBubble = false;
}

CustomGUIEvent.prototype.__dispatch = function(guiElement)
{
	this.currentTarget = guiElement;

	this.bubblePath.push(guiElement);

	this.__runHandlers(guiElement);

	if(this.bubbles && !this.cancelBubble)
	{
		var parentGUIElement = guiElement.getParent();
		if(parentGUIElement)
			this.__dispatch(parentGUIElement);
	}
}

// }

// public {

CustomGUIEvent.prototype.isLeavingSubtree = function(guiElement)
{
	var event = this.originalDOMEvent;
	if(!event)
		throw new Error('original_dom_event_not_available');

	var newTarget = BROWSER_TYPE == BrowserType.MSIE ? event.toElement : event.relatedTarget;
	if(!newTarget)
	{
		return; // cursor might have hovered over a context-menu
		throw new Error('event_target_not_available');
	}

	return !Utils.IsElementWithinDOM(guiElement.getMainElement(), newTarget);
}

CustomGUIEvent.prototype.isEnteringSubtree = function(guiElement)
{
	var event = this.originalDOMEvent;
	if(!event)
		throw new Error('original_dom_event_not_available');

	var oldTarget = BROWSER_TYPE == BrowserType.MSIE ? event.fromElement : event.relatedTarget;
	if(!oldTarget)
	{
		return; // cursor might have hovered over a context-menu
		throw new Error('event_target_not_available');
	}

	return !Utils.IsElementWithinDOM(guiElement.getMainElement(), oldTarget);
}

// }
function ArrayList()
{
	this.__init.apply(this, arguments);
}

ArrayList.EventType = new Enum('CHANGE');

ArrayList.Operation = new Enum(
		'REMOVE',
		'ADD'
	);

// private {

ArrayList.prototype.__init = function()
{
	this.eventListeners = [];

	this.items = [];
	this.source = null;

	this.sourceListener = {
			receiver: this,
			handleEvent: function(customEvent)
			{
				/*
				 * ... zprava pro receiver, ze request zdroje byl dokoncen
				 */
			}
		};
}

ArrayList.prototype.__addItem = function(item, targetIdx)
{
	if(this.items.indexOf(item) < 0)
		if(typeof(targetIdx) == 'undefined')
			this.items.push(item);
		else
		{
			targetIdx = (targetIdx < 0) ? (this.items.length - targetIdx) : targetIdx;
			this.items.splice(targetIdx, 0, item);
		}
	else
		throw new Error('item_already_exists');
}

// }

// public {

ArrayList.prototype.addEventListener = Function.Templates.AddEventListener;

ArrayList.prototype.addItem = function(item, targetIdx)
{
	this.__addItem(item, targetIdx);

	var customEvent = new CustomEvent(ArrayList.EventType.CHANGE);
	customEvent.operation = ArrayList.Operation.ADD; 
	customEvent.newItem = item;
	customEvent.dispatch(this);
}

ArrayList.prototype.clear = function()
{
	this.setItems([]);
}

ArrayList.prototype.removeEventListener = Function.Templates.RemoveEventListener;

ArrayList.prototype.indexOf = function(item)
{
	return this.getItems().indexOf(item);
}

ArrayList.prototype.getItem = function(idx)
{
	return this.items[idx];
}

ArrayList.prototype.getItems = function()
{
	return [].concat(this.items);
}

ArrayList.prototype.getLength = function()
{
	return this.items.length;
}

ArrayList.prototype.removeItem = function(item)
{
	var idx = this.items.indexOf(item);

	if(idx < 0)
		throw new Error('item_not_found');

	this.items.splice(idx, 1);

	var customEvent = new CustomEvent(ArrayList.EventType.CHANGE);
	customEvent.operation = ArrayList.Operation.REMOVE; 
	customEvent.oldItem = item;
	customEvent.dispatch(this) 
}

ArrayList.prototype.setItems = function(items)
{
	this.items = items;

	new CustomEvent(ArrayList.EventType.CHANGE).dispatch(this);
}

// }
function FormWrapper()
{
	this.__init.apply(this, arguments);
}

self.FormWrapper = FormWrapper;

// private {

FormWrapper.prototype.__init = function(formElement)
{
	this.formElement = formElement;
	this.inputFormats = {};
	this.ruleGroup = new FormRuleGroup();
	this.enabled = false;

	attachHandler(this.formElement, 'submit', {
			receiver: this,
			handleEvent: function(event)
			{
				this.receiver.__handleSubmit(event);
			}
		});

	// register elements {

	var valueChangeListener = {
			receiver: this, 
			handleEvent: function(event)
			{
				this.receiver.__update();
			}
		};
	for(var i = 0, element, elements = this.formElement.elements; i < elements.length; i++)
	{
		element = elements[i];
		attachHandler(element, 'keyup', valueChangeListener);
		attachHandler(element, 'change', valueChangeListener);
		attachHandler(element, 'focus', valueChangeListener);
		attachHandler(element, 'blur', valueChangeListener);
	}

	// }
}

FormWrapper.prototype.__handleSubmit = function(event)
{
	switch(BROWSER_TYPE)
	{
		case BrowserType.MSIE:
			event.returnValue = false;
			break;

		default:
			event.preventDefault();
			break;
	}

	this.__update();

	if(this.enabled)
		this.__send();
	else
		alert('invalid_data'.toLocalized());
}

FormWrapper.prototype.__send = function()
{
	var parameters = this.__collectValues();
	parameters['FormWrapperRequestId'] = [new Date().getTime()];

	Utils.SendHttpPOSTRequest(this.formElement.action, parameters, {
		receiver: this,
		handleResponse: function(request)
		{
			if(request.status == 200)
			{
				if(identifyContent(request) == ContentType.TEXT && request.responseText)
					alert(request.responseText);
				else
					alert('thank_you'.toLocalized());

				this.receiver.formElement.reset();
			}
			else
				if(identifyContent(request) == ContentType.TEXT && request.responseText)
					alert(request.responseText);
				else
					alert('request_failed'.toLocalized() +' ('+ request.statusText +')');
		}
	});
}

FormWrapper.prototype.__collectValues = function()
{
	var values = {};
	for(var i = 0, element, elements = this.formElement.elements; i < elements.length; i++)
	{
		element = elements[i];

		// send this element's value or not? {

		if(element.disabled || !element.name)
			continue;

		switch(element.type)
		{
			case 'radio':
			case 'checkbox':
				if(!element.checked)
					continue;

			default:
				// treat SELECTs specially
				if(element.tagName.toUpperCase() == 'SELECT')
				{
					var option;
					for(var idx in element.options)
					{
						option = element.options[idx];
						if(option.selected)
						{
							if(!values[element.name])
								values[element.name] = [];
							values[element.name].push(option.value);
						}
					}
				}
				else
				{
					if(!values[element.name])
						values[element.name] = [];
					values[element.name].push(element.value);
				}

				break;
		}

		// }
	}

	return values;
}

FormWrapper.prototype.__registerInputElements = function()
{
	for(var i = 0, element, elements = this.formElement.elements; i < elements.length; i++)
	{
		element = elements[i];
	}
}

FormWrapper.prototype.__getInputByName = function(inputName)
{
	return this.formElement.elements[inputName];
}

FormWrapper.prototype.__testGroup = function(ruleGroup)
{
	for(var i = 0, groupIsOK, groups = ruleGroup.groups; i < groups.length; i++)
	{
		groupIsOK = this.__testGroup(groups[i]);
		if(ruleGroup.mode ^ groupIsOK)
			return groupIsOK;
	}

	var inputIsNotOK;
	for(var inputName in ruleGroup.rules)
	{
		inputIsNotOK = (this.__getInputByName(inputName).value ? true : false) ^ ruleGroup.rules[inputName];
		if(ruleGroup.mode ^ !inputIsNotOK)
			return !inputIsNotOK;
	}

	return ruleGroup.mode;
}

FormWrapper.prototype.__checkInput = function(inputName)
{
	var input = this.__getInputByName(inputName),
		inputValue = input.value,
		inputFormat = this.inputFormats[inputName];

	if(inputValue && inputFormat && !new RegExp(inputFormat).test(inputValue))
		return false;

	return true;
}

FormWrapper.prototype.__testFormats = function()
{
	for(var inputName in this.inputFormats)
		if(!this.__checkInput(inputName))
			return false;

	return true;
}

FormWrapper.prototype.__update = function()
{
	this.enabled = this.__validate();

	for(var i = 0, element, elements = this.formElement.elements; i < elements.length; i++)
	{
		element = elements[i];
		if(element.type == 'submit')
		{
			if(this.enabled)
				Utils.RemoveCSSClass(element, 'disabled');
			else
				Utils.AddCSSClass(element, 'disabled');
		}
	}
}

FormWrapper.prototype.__validate = function()
{
	if(this.__testGroup(this.ruleGroup))
		if(this.__testFormats())
			return true;

	return false;
}

// }

// public {

FormWrapper.prototype.setInputFormat = function(inputName, format)
{
	this.inputFormats[inputName] = format;
}

FormWrapper.prototype.setRuleGroup = function(ruleGroup)
{
	this.ruleGroup = ruleGroup;
}

FormWrapper.prototype.addRule = function(inputName, rule)
{
	this.ruleGroup.addRule(inputName, rule);
}

// }

//--

function FormRuleGroup()
{
	this.__init.apply(this, arguments);
}

self.FormRuleGroup = FormRuleGroup;

// private {

/**
 * @param {boolean} mode 'AND' operator?
 */
FormRuleGroup.prototype.__init = function(mode)
{
	this.mode = typeof(mode) == 'boolean' ? mode : true;
	this.groups = [];
	this.rules = {};
}

// }

// public {

FormRuleGroup.prototype.addGroup = function(ruleGroup)
{
	this.groups.push(ruleGroup);
}

/**
 * @param {String} inputName
 * @param {boolean} rule inputName required? (or prohibited)
 */
FormRuleGroup.prototype.addRule = function(inputName, _rule)
{
	var rule = typeof(_rule) == 'boolean' ? _rule : true;
	this.rules[inputName] = rule;
}

// }
function LocaleResource(locale)
{
	if(LocaleResource.Resources[locale])
		throw new Error('duplicate_resource');

	LocaleResource.Resources[locale] = this;

	this.locale = locale;
	this.messages = [];
}

// static {

LocaleResource.Resources = {};

LocaleResource.GetResource = function(locale)
{
	return LocaleResource.Resources[locale];
}

LocaleResource.GetAvailableResources = function()
{
	return LocaleResource.Resources;
}

// }

// public {

LocaleResource.prototype.getMessage = function(key)
{
	return this.messages[key];
}

LocaleResource.prototype.putMessage = function(key, message)
{
	this.messages[key] = message;
}

LocaleResource.prototype.putMessages = function(messages)
{
	this.messages = messages;
}

LocaleResource.prototype.getLocaleName = function()
{
	return this.locale;
}

// }
/**
 * Rozsiruje funkcnost existujiciho elementu.
 */
function GUIElement()
{
	this.__init.apply(this, arguments);
}

self.GUIElement = GUIElement;

// private static {

GUIElement.__CreateElement = function(ownerDocument, tagName, _attributes)
{
	var element = (ownerDocument.documentElement.namespaceURI) ? ownerDocument.createElementNS(ownerDocument.documentElement.namespaceURI, tagName) : ownerDocument.createElement(tagName);

	if(typeof(_attributes) != 'undefined')
		for(var idx in _attributes)
			element.setAttribute(idx, _attributes[idx]);

	return element;
}

// }

// public static {

/*
 * muted when in modal mode following events are muted when not coming from currently modal guiElement
 */
GUIElement.__EventTypesMuted = ['mousedown', 'mouseup', 'click', 'keydown', 'keyup', 'keypress'];

GUIElement.GRAPHICS_PATH = 'styles/graphics/';

GUIElement.EventType = new Enum(
		'CLICK',
		'BBOX_CHANGE',
		'VISIBILITY_CHANGE',
		'MOUSEOUT',
		'MOUSEOVER'
	);

/**
 * @param {HTMLElement} htmlElement from this element we start when looking for our GUIElement instance
 * @param {Function} _classRequired if we want a particular function/class we can specify it here
 * @return GUIElement instance associated with this element
 */
GUIElement.GetByHTMLElement = function(htmlElement, _classRequired)
{
	var classRequired = typeof(_classRequired) == 'undefined' ? this : _classRequired;

	while(htmlElement)
	{
		if(htmlElement.guiElement)
			/*
			 * this htmlElement is a mainElement of GUIElement
			 */
			if(htmlElement.guiElement.constructor.InstanceOf(classRequired))
				/*
				 * this GUIElement instance's type matches the required one
				 */
				return htmlElement.guiElement;

		htmlElement = htmlElement.parentNode;
	}

	return null;
}

// }

// private {

GUIElement.prototype.__mainElementClassName = null;

GUIElement.prototype.__mainElementTagName = 'div';

GUIElement.prototype.__init = function(ownerDocument)
{
	this.innerElement = this.__prepareMainElement(ownerDocument)
	this.mainElement = Utils.GetRootNode(this.innerElement);

	this.mainElement.guiElement = this;

	this.isModal = false;

	this.mouseCheckId = null;

	this.eventListeners = [];

	this.horizontalAlign = null; // urcuje polohu zarovnavaneho elementu vuci referencnimu bodu
	this.horizontalRefElement = null;
	this.horizontalRefEdge = null; // urcuje referencni od na RefElement
	this.horizontalOffset = 0;

	this.verticalAlign = null;
	this.verticalRefElement = null;
	this.verticalRefEdge = null;
	this.verticalOffset = 0;

	this.lastBBoxAbsRect = null;
	this.shadow = null;

	this.__attachListeners();

	this.modalHandler = {
			receiver: this,
			scopeElement: this.mainElement,
			handleEvent: function(event)
			{
				if(this.receiver.isModal && this.receiver.isVisible())
					if(Utils.GetRelationship(this.scopeElement, event.target, true) == null)
					{
						this.receiver.blink(250);
						this.receiver.__muteEvent(event);
					}
			}
		};

	if(!this.constructor.instances)
		this.constructor.instances = [];
	this.constructor.instances.push(this);
}

GUIElement.prototype.__prepareMainElement = function(ownerDocument)
{
	var innerElement = GUIElement.__CreateElement(ownerDocument, this.__mainElementTagName);

	if(this.__mainElementClassName)
		Utils.AddCSSClass(innerElement, this.__mainElementClassName);

	return innerElement;
}

GUIElement.prototype.__attachListeners = function()
{
	var mouseEventListener = {
			receiver: this,
			handleEvent: function(event)
			{
				this.receiver.__handleMouseEvent(event);
			}
		};

	attachHandler(this.mainElement, 'click', mouseEventListener);
	attachHandler(this.mainElement, 'mouseout', mouseEventListener);
	attachHandler(this.mainElement, 'mouseover', mouseEventListener);
}

GUIElement.prototype.__log = Function.Templates.Log;

GUIElement.prototype.__getUniqueId = function()
{
	return this.constructor.getFunctionName() + this.constructor.instances.indexOf(this);
}

GUIElement.prototype.__handleMouseEvent = function(event)
{
	switch(event.type)
	{
		case 'click':
			if(event.button == 0)
				new CustomGUIEvent(GUIElement.EventType.CLICK, event).dispatch(this);
			break;

		case 'mouseout':
			new CustomGUIEvent(GUIElement.EventType.MOUSEOUT, event).dispatch(this);
			break;

		case 'mouseover':
			new CustomGUIEvent(GUIElement.EventType.MOUSEOVER, event).dispatch(this);
			break;
	}

	switch(BROWSER_TYPE)
	{
		case BrowserType.MSIE:
			event.cancelBubble = true;
			break;

		default:
			event.stopPropagation();
			break;
	}
}

GUIElement.prototype.__createTextNode = function(nodeValue)
{
	return this.getOwnerDocument().createTextNode(nodeValue);
}

GUIElement.prototype.__createButton = function(label, _handler, _attributes)
{
	var buttonElement = this.__createElement('button', _attributes);
	buttonElement.appendChild(this.__createTextNode(label));
	if(typeof(_handler) != 'undefined')
		buttonElement.addEventListener('click', _handler, false);

	return buttonElement;
}

GUIElement.prototype.__createLabeledDiv = function(label, _attributes)
{
	var divElement = this.__createElement('div', _attributes);
	divElement.appendChild(this.__createElement('legend')).appendChild(this.__createTextNode(label));

	return divElement;
}

GUIElement.prototype.__createCheckbox = function(label, _clickHandler)
{
	var element = this.__createElement('span'),
		checkboxElement = element.appendChild(this.__createElement('input', {type: 'checkbox'}));

	element.appendChild(this.__createTextNode(label));

	if(typeof(_clickHandler) != 'undefined')
		checkboxElement.addEventListener('click', _clickHandler, false);

	return checkboxElement;
}

GUIElement.prototype.__createElement = function(tagName, _attributes)
{
	return GUIElement.__CreateElement(this.getOwnerDocument(), tagName, _attributes);
}

GUIElement.prototype.__testCSSClass = function(className)
{
	return Utils.RemoveCSSClass(this.mainElement, className);
}

GUIElement.prototype.__updateAlignment = function()
{
	var mainElement = this.getMainElement();
	Utils.SetElementHorizontalAlignment(mainElement, this.horizontalAlign, this.horizontalRefElement, this.horizontalRefEdge, this.horizontalOffset);
	Utils.SetElementVerticalAlignment(mainElement, this.verticalAlign, this.verticalRefElement, this.verticalRefEdge, this.verticalOffset);
}

GUIElement.prototype.__setHorizontalAlignment = function(align, refElement, refEdge, offset)
{
	this.horizontalAlign = align;
	this.horizontalRefElement = refElement;
	this.horizontalRefEdge = refEdge;
	this.horizontalOffset = offset;
}

GUIElement.prototype.__setVerticalAlignment = function(align, refElement, refEdge, offset)
{
	this.verticalAlign = align;
	this.verticalRefElement = refElement;
	this.verticalRefEdge = refEdge;
	this.verticalOffset = offset;
}

GUIElement.prototype.__addShadow = function()
{
	this.shadow = new ElementShadow(this.getOwnerDocument(), this, this.mainElement);
}

GUIElement.prototype.__checkBBoxDimensions = function()
{
	if(this.lastBBoxAbsRect)
	{
		var bBoxAbsRect = this.getBBoxAbsRect(),
			changed = true;

		if(this.lastBBoxAbsRect.top == bBoxAbsRect.top)
			if(this.lastBBoxAbsRect.left == bBoxAbsRect.left)
				if(this.lastBBoxAbsRect.width == bBoxAbsRect.width)
					if(this.lastBBoxAbsRect.height == bBoxAbsRect.height)
						changed = false;
		if(changed)
		{
			this.lastBBoxAbsRect = bBoxAbsRect;

			new CustomEvent(GUIElement.EventType.BBOX_CHANGE).dispatch(this);

			var parentGUIElement = this.getParent();
			if(parentGUIElement)
				this.__checkBBoxDimensions();
		}
	}
	else
		this.lastBBoxAbsRect = this.getBBoxAbsRect();
}

GUIElement.prototype.__muteEvent = function(event)
{
	if(BROWSER_TYPE == BrowserType.MSIE)
	{
		event.returnValue = false;
		event.cancelBubble = true;
	}
	else
	{
		event.stopPropagation();
		event.preventDefault();
	}
}

// }

// public {

GUIElement.prototype.blink = function(duration)
{
	this.addCSSClass(HILITE_CLASSNAME);
	window.setTimeout(new Function('guiElement', 'guiElement.removeCSSClass(HILITE_CLASSNAME)'), duration, this);
}

/**
 * @see Utils#SetElementHorizontalAlignment
 * @see Utils#SetElementVerticalAlignment
 */
GUIElement.prototype.setAlignment = function(hAlign, hRefElement, hRefEdge, vAlign, vRefElement, vRefEdge, hOffset, vOffset)
{
	this.__setHorizontalAlignment(hAlign, hRefElement, hRefEdge, hOffset);
	this.__setVerticalAlignment(vAlign, vRefElement, vRefEdge, vOffset);

	this.__updateAlignment();
	this.__checkBBoxDimensions();
}

GUIElement.prototype.setModal = function(_isModal)
{
	this.isModal = (typeof(_isModal) == 'boolean') ? _isModal : true;

	for(var i = 0, eventType; eventType = GUIElement.__EventTypesMuted[i]; i++)
		if(this.isModal)
			this.getOwnerDocument().addEventListener(eventType, this.modalHandler, true);
		else
			this.getOwnerDocument().removeEventListener(eventType, this.modalHandler, true);
}

GUIElement.prototype.addEventListener = Function.Templates.AddEventListener;

GUIElement.prototype.removeEventListener = Function.Templates.RemoveEventListener;

GUIElement.prototype.getBBoxAbsRect = function()
{
	return Utils.CalculateAbsRect(this.getMainElement());
}

GUIElement.prototype.getMainElement = function()
{
	return this.mainElement;
}

GUIElement.prototype.getInnerElement = function()
{
	return this.innerElement;
}

GUIElement.prototype.getOwnerDocument = function()
{
	return this.getMainElement().ownerDocument;
}

GUIElement.prototype.hide = function()
{
	if(this.addCSSClass(DISPLAY_NONE_CLASSNAME))
		new CustomEvent(GUIElement.EventType.VISIBILITY_CHANGE).dispatch(this);

/*	this.__updateAlignment();
	this.__checkBBoxDimensions();*/
}

GUIElement.prototype.show = function()
{
	if(this.removeCSSClass(DISPLAY_NONE_CLASSNAME))
		new CustomEvent(GUIElement.EventType.VISIBILITY_CHANGE).dispatch(this);

	if(this.horizontalAlign || this.verticalAlign)
	{
		this.__updateAlignment();
		this.__checkBBoxDimensions();
	}
}

GUIElement.prototype.addShadow = function()
{
	if(this.shadow)
		return;

	this.__addShadow();
}

GUIElement.prototype.setHilited = function(hilited)
{
	if(hilited)
		this.addCSSClass(HILITE_CLASSNAME);
	else
		this.removeCSSClass(HILITE_CLASSNAME);
}

GUIElement.prototype.markActive = function(_active)
{
	if(typeof(_active) == 'boolean' && !_active)
		this.removeCSSClass(ACTIVE_CLASSNAME);
	else
		this.addCSSClass(ACTIVE_CLASSNAME);
}

GUIElement.prototype.addCSSClass = function(className)
{
	return Utils.AddCSSClass(this.mainElement, className);
}

GUIElement.prototype.removeCSSClass = function(className)
{
	return Utils.RemoveCSSClass(this.mainElement, className);
}

GUIElement.prototype.getParent = function(_classRequired)
{
	return GUIElement.GetByHTMLElement(this.mainElement.parentNode, _classRequired);
}

/**
 * @return {boolean} is this element's main element actually visible in the client area?
 */
GUIElement.prototype.isVisible = function()
{
	return this.mainElement.clientWidth > 0 && this.mainElement.clientHeight > 0;
}

/**
 * @return {boolean} is this element hidden by DISPLAY_NONE_CLASSNAME?
 */
GUIElement.prototype.isHidden = function()
{
	return Utils.TestCSSClass(this.getMainElement(), DISPLAY_NONE_CLASSNAME);
}

// }
function Console()
{
	this.__init.apply(this, arguments);
}

self.Console = Console;

Console.Extend(GUIElement);

Console.Mode = new Enum('APPEND', 'REWRITE');

// private {

Console.prototype.__mainElementClassName = 'net.jzaruba.gui.Console';

Console.prototype.__init = function()
{
	Console.Super().prototype.__init.apply(this, arguments);

	this.mode = Console.Mode.APPEND;
	this.lastRewritableLine = null;
}

Console.prototype.__createLine = function(message)
{
	var line = this.__createElement('div');
	line.appendChild(this.__createTextNode(message));
	return line;
}

// }

// public {

Console.prototype.setMode = function(mode)
{
	this.mode = mode;
}

Console.prototype.trace = function(message)
{
	var line = this.__createLine(message);
	if(this.mode == Console.Mode.APPEND)
	{
		this.mainElement.appendChild(line);

		this.lastRewritableLine = null;
	}
	else
	{
		if(this.lastRewritableLine)
			this.mainElement.replaceChild(line, this.lastRewritableLine);
		else
			this.mainElement.appendChild(line);

		this.lastRewritableLine = line;
	}
}

// }
function Cloak()
{
	this.__init.apply(this, arguments);
}

Cloak.Extend(GUIElement);

// private {

Cloak.prototype.__mainElementClassName = 'net.jzaruba.gui.Cloak';

Cloak.prototype.__init = function()
{
	Cloak.Super().prototype.__init.apply(this, arguments);
}

Cloak.prototype.fillDocumentElement = function()
{
	this.mainElement.style.position = 'absolute';
	this.mainElement.style.top = '0px';
	this.mainElement.style.left = '0px';
	this.mainElement.style.width = window.document.documentElement.clientWidth +'px';
	this.mainElement.style.height = window.document.documentElement.clientHeight +'px';
}

// }

// public {

Cloak.prototype.show = function()
{
	Cloak.Super().prototype.show.apply(this, arguments);

	switch(BROWSER_TYPE)
	{
		case BrowserType.MSIE:
			if(BROWSER_VERSION == 6)
				this.fillDocumentElement();
			break;
	}
}

// }
function ImagePreloader()
{
	this.__init.apply(this, arguments);
}

self.ImagePreloader = ImagePreloader;

ImagePreloader.Extend(GUIElement);

ImagePreloader.EventType = Enum.Extend(GUIElement.EventType, 
	'ITEM_LOADED',
	'ALL_ITEMS_LOADED');

// private {

ImagePreloader.prototype.__mainElementTagName = 'img';

ImagePreloader.prototype.__init = function()
{
	ImagePreloader.Super().prototype.__init.apply(this, arguments);

	this.sources = null;
	this.idx = -1;

	attachHandler(this.mainElement, 'load', {
			receiver: this, 
			handleEvent: function(event)
			{
				this.receiver.__handleLoad();
			}
		});
}

ImagePreloader.prototype.__handleLoad = function()
{
	if(this.idx < 0)
		return;

	var customEvent = new CustomEvent(ImagePreloader.EventType.ITEM_LOADED);
	customEvent.item = this.mainElement.src;
	customEvent.dispatch(this);

	if(this.idx == this.sources.length -1)
		new CustomEvent(ImagePreloader.EventType.ALL_ITEMS_LOADED).dispatch(this);
	else
	{
		this.idx++;
		window.setTimeout(new Function('imagePreloader', 'imagePreloader.__load()'), 0, this);
	}
}

ImagePreloader.prototype.__load = function()
{
	this.mainElement.src = this.sources[this.idx];
}

ImagePreloader.prototype.__start = function()
{
	this.idx = 0;
	this.__load();
}

// }

// public {

ImagePreloader.prototype.loadMany = function(sources)
{
	this.sources = sources;

	this.__start();
}

ImagePreloader.prototype.load = function(source)
{
	this.loadMany([source])
}

ImagePreloader.prototype.stop = function()
{
	this.idx = -1;
	this.sources = null;
	this.mainElement.src = '';
}
// }
/*
 * V MSIE imgLoader po kazdem pouziti rusime/znovuvytvarime; 
 * ve FF jej pouze skryvame/odkryvame.
 * 
 * todo: obrazky by asi nemel prednacitat Viewer (mozna spis samotna gallery)
 * Viewer ovsem musi vedet, je-li dalsi/predchozi obrazek k dispozici
 */

function Viewer()
{
	this.__init.apply(this, arguments);
}

self.Viewer = Viewer;

Viewer.Extend(GUIElement);

Viewer.EventType = Enum.Extend(GUIElement.EventType, 
	'IMAGE_SET',
	'REQUEST_PREVIOUS',
	'REQUEST_NEXT');

// private {

Viewer.prototype.__mainElementTagName = 'table';

Viewer.prototype.__mainElementClassName = 'net.jzaruba.gui.imagegallery.Viewer';

Viewer.prototype.__init = function()
{
	Viewer.Super().prototype.__init.apply(this, arguments);

	this.mainElement.cellSpacing = 0;

	this.imageItem = null;
	this.srcResolved = null;

	this.sequence = null;

	this.idx = null;
	this.total = null;

	this.loaderReadyListener = {
		receiver: this,
		handleEvent: function(event)
		{
			this.receiver.__onLoaderReady(event);
		}
	};
	/*
	 * v MSIE je hlidani pripravenosti image docela osidne; 
	 * zatim se mi nejvic osvedcilo nechat image style.width i .height '0px' a pockat na readyState 'complete', 
	 * pote zmenit rozmery na auto (style.width i .height nastavit na ''), nasledujici event 'resize' uz (zatim vzdycky!) 
	 * hlasi spravne rozmery obrazku
	 */
	this.readyStateListener = {
		viewer: this,
		handleEvent: function(event)
		{
			if(event.srcElement.readyState == 'complete')
			{
				event.srcElement.style.width = '';
				event.srcElement.style.height = '';

				setTimeout(new Function('viewer', 'viewer.__onLoaderReady()'), 0, this.viewer);
			}

		}
	};

	var tHead = this.__createElement('thead'),
		tBody = this.__createElement('tbody'),
		tFoot = this.__createElement('tfoot');
	this.mainElement.appendChild(tHead);
	this.mainElement.appendChild(tBody);
	this.mainElement.appendChild(tFoot);

	var tr0 = this.__createElement('tr'),
		tr1 = this.__createElement('tr'),
		tr2 = this.__createElement('tr');
	tHead.appendChild(tr0);
	tBody.appendChild(tr1);
	tFoot.appendChild(tr2);

	var tdN = this.__createElement('th'),
		tdCenter = this.__createElement('td'),
		tdS = this.__createElement('td');
	tr0.appendChild(tdN);
	tr1.appendChild(tdCenter);
	tr2.appendChild(tdS);

	/*
	 * Jako stageElement nepouzivame bunku, protoze pokud bychom nastavovali 
	 * sirku natvrdo primo bunce, ovlivnovali bychom tim i sirku cele tabulky.
	 * Takhle si tabulka vybere sirku sama podle obsahu, a obrazek ji muze jedine rozsirit; 
	 * nikoli zuzit a tim zpusobit nasilne zaloemni textu v jinych bunkach.
	 */
	this.stageElement = tdCenter.appendChild(this.__createElement('div'));
	Utils.AddCSSClass(this.stageElement, 'stage');
	this.stageElement.style.backgroundRepeat = 'no-repeat';
	this.stageElement.style.backgroundPosition = 'center center';
	this.stageElement.style.display = 'inline-block';
	this.stageElement.style.padding = '0px';
	this.stageElement.style.verticalAlign = 'bottom';
	this.captionTextNode = this.__createTextNode('');

	this.descriptionTextNode = this.__createTextNode('');
	this.commentTextNode = this.__createTextNode('');

	this.previousButton = this.__createButton('\u25c4', 'previousButton');
	Utils.AddCSSClass(this.previousButton, FLOAT_LEFT_CLASSNAME);
	attachHandler(this.previousButton, 'click', {
		receiver: this,
		handleEvent: function(event)
		{
			this.receiver.__requestPrevious();
		}
	});

	this.nextButton = this.__createButton('\u25ba', 'nextButton');
	Utils.AddCSSClass(this.nextButton, FLOAT_RIGHT_CLASSNAME);
	attachHandler(this.nextButton, 'click', {
		receiver: this,
		handleEvent: function(event)
		{
			this.receiver.__requestNext();
		}
	});

	this.closeButton = this.__createButton('x', 'closeButton');
	attachHandler(this.closeButton, 'click', {
		receiver: this,
		handleEvent: function(event)
		{
			this.receiver.__close();
		}
	});

	tdN.appendChild(this.closeButton);
	tdN.appendChild(this.captionTextNode);

	this.stageElement.appendChild(this.previousButton);
	this.stageElement.appendChild(this.nextButton);

	this.descriptionElement = this.__createElement('div');
	this.commentElement = this.__createElement('div');
	Utils.AddCSSClass(this.descriptionElement, this.__mainElementClassName +'.description');
	Utils.AddCSSClass(this.commentElement, this.__mainElementClassName +'.comment');

	tdS.appendChild(this.descriptionElement);
	tdS.appendChild(this.commentElement);

	this.descriptionElement.appendChild(this.descriptionTextNode);
	this.commentElement.appendChild(this.commentTextNode);

	// controls {

	this.previousButtonEnabled;
	this.nextButtonEnabled;

	this.controlsPrimaryState;

	this.__updateControlsEnabled();

	// }
}

Viewer.prototype.__createButton = function(caption, buttonClassName)
{
	var button = this.__createElement('a'),
		textLabel = this.__createElement('span');

	Utils.AddCSSClass(button, buttonClassName);
	button.appendChild(textLabel);
	Utils.AddCSSClass(textLabel, 'alternateText');
	textLabel.appendChild(this.__createTextNode(caption));
	if(BROWSER_TYPE == BrowserType.MSIE)
	{
		var hoverListener = {
			handleEvent: function(event)
			{
				switch(event.type)
				{
					case 'mouseover':
						Utils.AddCSSClass(event.srcElement, 'hover');
						break;

					case 'mouseout':
						Utils.RemoveCSSClass(event.srcElement, 'hover');
						break;
				}
			}
		};
		attachHandler(button, 'mouseover', hoverListener);
		attachHandler(button, 'mouseout', hoverListener);
	}
	return button;
}

Viewer.prototype.__updateControlsVisibility = function()
{
	if(this.controlsPrimaryState && this.previousButtonEnabled)
		Utils.RemoveCSSClass(this.previousButton, DISPLAY_NONE_CLASSNAME);
	else
		Utils.AddCSSClass(this.previousButton, DISPLAY_NONE_CLASSNAME);

	if(this.controlsPrimaryState && this.nextButtonEnabled)
		Utils.RemoveCSSClass(this.nextButton, DISPLAY_NONE_CLASSNAME);
	else
		Utils.AddCSSClass(this.nextButton, DISPLAY_NONE_CLASSNAME);
}

Viewer.prototype.__onLoaderReady = function()
{
	if(this.srcResolved == this.imgLoaderElement.src)
		this.__setupStage(this.imgLoaderElement.src, this.imgLoaderElement.clientWidth, this.imgLoaderElement.clientHeight);
}

Viewer.prototype.__createImageLoaderElement = function()
{
	var imgElement = this.__createElement('img');
	Utils.AddCSSClass(imgElement, 'net.jzaruba.gui.imagegallery.Viewer.imageLoader');
	switch(BROWSER_TYPE)
	{
		case BrowserType.MSIE:
			imgElement.style.width = '0px';
			imgElement.style.height = '0px';
			attachHandler(imgElement, 'readystatechange', this.readyStateListener);
			break;

		default:
			attachHandler(imgElement, 'load', this.loaderReadyListener);
			break;
	}
	return imgElement;
}

Viewer.prototype.__close = Viewer.prototype.hide;

Viewer.prototype.__setControlsPrimaryState = function(controlsPrimaryState)
{
	this.controlsPrimaryState = controlsPrimaryState;

	this.__updateControlsVisibility();
}

Viewer.prototype.__updateControlsEnabled = function()
{
	this.previousButtonEnabled = this.idx > 0;
	this.nextButtonEnabled = typeof(this.total) == 'number' && this.idx +1 < this.total;

	this.__updateControlsVisibility();
}

Viewer.prototype.__lockStage = function(backgroundImage, width, height)
{
	this.stageElement.style.backgroundImage = 'url("'+ backgroundImage +'")';
	this.stageElement.style.width = width +'px';
	this.stageElement.style.height = height +'px';

/*	this.__updateAlignment();
	this.__checkBBoxDimensions();*/
}

Viewer.prototype.__unlockStage = function()
{
	this.stageElement.style.backgroundImage = '';
	this.stageElement.style.width = '';
	this.stageElement.style.height = '';

/*	this.__updateAlignment();
	this.__checkBBoxDimensions();*/
}

Viewer.prototype.__prepareImageLoader_MS = function()
{
	this.__dismissImageLoader();

	this.imgLoaderElement = this.stageElement.appendChild(this.__createImageLoaderElement());
}

Viewer.prototype.__prepareImageLoader_W3C = function()
{
	if(!this.imgLoaderElement)
		this.imgLoaderElement = this.stageElement.appendChild(this.__createImageLoaderElement());
	else
		this.imgLoaderElement.style.display = '';
}

switch(BROWSER_TYPE)
{
	case BrowserType.MSIE:
		Viewer.prototype.__prepareImageLoader = Viewer.prototype.__prepareImageLoader_MS;
		break;

	default:
		Viewer.prototype.__prepareImageLoader = Viewer.prototype.__prepareImageLoader_W3C;
		break;
}

Viewer.prototype.__dismissImageLoader_MS = function()
{
	if(this.imgLoaderElement && this.imgLoaderElement.parentNode)
	{
		this.imgLoaderElement.parentNode.removeChild(this.imgLoaderElement);
		detachHandler(this.imgLoaderElement, 'readystatechange', this.readyStateListener);
		this.imgLoaderElement = null;
	}
}

Viewer.prototype.__dismissImageLoader_W3C = function()
{
	this.imgLoaderElement.style.display = 'none';
}

switch(BROWSER_TYPE)
{
	case BrowserType.MSIE:
		Viewer.prototype.__dismissImageLoader = Viewer.prototype.__dismissImageLoader_MS;
		break;

	default:
		Viewer.prototype.__dismissImageLoader = Viewer.prototype.__dismissImageLoader_W3C;
		break;
}

Viewer.prototype.__resetStage = function()
{
	this.__unlockStage();
	this.__setControlsPrimaryState(false);
	this.__prepareImageLoader();
}

Viewer.prototype.__setupStage = function(backgroundImage, width, height)
{
	this.__lockStage(backgroundImage, width, height);
	this.__dismissImageLoader();
	this.__setControlsPrimaryState(true);

/*	this.__updateAlignment();
	this.__checkBBoxDimensions();*/
}

Viewer.prototype.__updateCaption = function()
{
	var title = this.imageItem.title,
		string = title ? title +' ' : null,

		idx = this.idx,
		total = this.total;

	if(typeof(idx) == 'number')
		if(typeof(total) == 'number')
			string = '%(%/%)'.format(string, idx +1, total);
		else
			string = '%(%)'.format(string, idx +1);

	this.captionTextNode.nodeValue = string;
}

Viewer.prototype.__setComment = function(string)
{
	this.commentElement.style.display = string ? 'block' : 'none';
	this.commentTextNode.nodeValue = string;
}

Viewer.prototype.__setDescription = function(string)
{
	this.descriptionElement.style.display = string ? 'block' : 'none';
	this.descriptionTextNode.nodeValue = string;
}

Viewer.prototype.__requestPrevious = function()
{
	if(this.sequence)
		this.setIndex(this.sequence.indexOf(this.imageItem) -1);
	else
		new CustomEvent(Viewer.EventType.REQUEST_PREVIOUS).dispatch(this);
}

Viewer.prototype.__requestNext = function()
{
	if(this.sequence)
		this.setIndex(this.sequence.indexOf(this.imageItem) +1);
	else
		new CustomEvent(Viewer.EventType.REQUEST_NEXT).dispatch(this);
}

// }

// public {

Viewer.prototype.setIndex = function(idx)
{
	if(idx < 0 || idx + 1 > this.sequence.length)
		return;

	var imageItem = this.sequence[idx];
	this.setImage(imageItem);
	this.setPosition(idx, this.sequence.length);
}

Viewer.prototype.setImage = function(imageItem)
{
	this.__resetStage();

	this.imageItem = imageItem;

	this.imgLoaderElement.src = this.imageItem.src;
	this.srcResolved = this.imgLoaderElement.src;

	this.__updateCaption();

	this.__setDescription(this.imageItem.description);
	this.__setComment(this.imageItem['comment']);

	new CustomEvent(Viewer.EventType.IMAGE_SET).dispatch(this);
}

Viewer.prototype.setPosition = function(idx, total)
{
	this.idx = idx;
	this.total = total;

	this.__updateCaption();
	this.__updateControlsEnabled();
}

Viewer.prototype.getCurrentImageItem = function()
{
	throw new Error('hm, tak to asi fakt potrebujeme');
	return this.imageItem;
}

Viewer.prototype.setSequence = function(sequence)
{
	this.sequence = sequence;
}

Viewer.prototype.showFirst = function()
{
	this.setIndex(0);
}

Viewer.prototype.showLast = function()
{
	this.setIndex(this.sequence.length -1);
}

Viewer.prototype.showPrevious = function()
{
	this.__requestPrevious();
}

Viewer.prototype.showNext = function()
{
	this.__requestNext();
}

// }
/**
 * Vycte z DOM-struktury tabSet.
 */

function TabSheet()
{
	this.__init.apply(this, arguments);
}

self.TabSheet = TabSheet;

TabSheet.DEFAULT_LINK_TAGNAME = 'A';
TabSheet.DEFAULT_TAB_TAGNAME = 'DIV';

// private {

TabSheet.prototype.__init = function(contextElement, linkClassName, tabClassName)
{
	this.contextElement = contextElement;

	this.links = {};
	this.tabs = {};

	this.linkListener = {
			receiver: this,
			handleEvent: function(event)
			{
				var target = BROWSER_TYPE == BrowserType.MSIE ? event.srcElement : event.target,
					targetId = Utils.GetHash(target.href);
				if(targetId)
					this.receiver.__setActiveTab(targetId);

/*				switch(BROWSER_TYPE)
				{
					case BrowserType.MSIE:
						event.returnValue = false;
						break;

					default:
						event.preventDefault();
						break;
				}*/
			}
		};

	this.currentId = null;

	this.__lookupLinks(linkClassName);
	this.__lookupTabs(tabClassName);

	for(var id in this.tabs)
	{
		this.__setActiveTab(id);
		break;
	}
}

TabSheet.prototype.__lookupLinks = function(linkClassName)
{
	/*
	 * find all activation links within tabSheet
	 */
	for(var i = 0, targetId, node, nodes = this.contextElement.getElementsByTagName(TabSheet.DEFAULT_LINK_TAGNAME); i < nodes.length; i++)
	{
		node = nodes[i];
		if(node.nodeType == 1 && Utils.TestCSSClass(node, linkClassName))
		{
			targetId = Utils.GetHash(node.href);
			if(targetId)
			{
				/*
				 * this link has url fragment
				 */
				attachHandler(node, 'click', this.linkListener);
				this.links[targetId] = node;
			}
		}
	}
}

TabSheet.prototype.__lookupTabs = function(tabClassName)
{
	for(var i = 0, node, nodes = this.contextElement.getElementsByTagName(TabSheet.DEFAULT_TAB_TAGNAME); i < nodes.length; i++)
	{
		node = nodes[i];
		if(node.nodeType == 1 && Utils.TestCSSClass(node, tabClassName) && node.id)
		{
			this.tabs[node.id] = node;
			/*
			 * whenever this element gets activated via url fragment (#) activate its tab
			 */
			attachHandler(node, 'activate', {
					receiver: this,
					handleEvent: function(event)
					{
						this.receiver.__handleActivateEvent(event);
					}
				});
		}
	}
}

/**
 * observed element has been activated by url fragment
 */
TabSheet.prototype.__handleActivateEvent = function(event)
{
	var target = BROWSER_TYPE == BrowserType.MSIE ? event.srcElement : event.target;
	for(var id in this.tabs)
		if(this.tabs[id] == target)
		{
			this.__setActiveTab(id);
			break;
		}
}

TabSheet.prototype.__setActiveTab = function(id)
{
	if(this.currentId == id)
		return;

	if(!this.tabs[id])
		throw new Error('tab_not_found');

	for(var linkId in this.links)
		if(linkId == id)
			Utils.AddCSSClass(this.links[linkId], 'active');
		else
			Utils.RemoveCSSClass(this.links[linkId], 'active');

	for(var tabId in this.tabs)
		if(tabId == id)
			Utils.RemoveCSSClass(this.tabs[tabId], DISPLAY_NONE_CLASSNAME);
		else
			Utils.AddCSSClass(this.tabs[tabId], DISPLAY_NONE_CLASSNAME);

	this.currentId = id;
}

// }

// public {

TabSheet.prototype.setActiveTab = TabSheet.prototype.__setActiveTab;

// }
/**
 * Vycte z DOM-struktury obrazkovou galerii. 
 * Na galerii je připojen Viever.
 */
function Gallery()
{
	this.__init.apply(this, arguments);
}

self.Gallery = Gallery;

Gallery.EventType = new Enum('IMAGE_PICK');

// static {

Gallery.InitViewer = function(_doc)
{
	var doc = typeof(_doc) == 'undefined' ? document : _doc;

	// cloak {

	var cloak = new Cloak(doc);
	cloak.hide();
	doc.body.appendChild(cloak.getMainElement());

	// }

	// centrovaci tabulka {

	var centerTable = doc.createElement('table'),
		centerTableCell = centerTable.appendChild(doc.createElement('tBody')).appendChild(doc.createElement('tr')).appendChild(doc.createElement('td'));

	centerTable.cellSpacing = 0;

	Utils.AddCSSClass(centerTable, 'centerTable');
	Utils.AddCSSClass(centerTable, DISPLAY_NONE_CLASSNAME);
	Utils.AddCSSClass(centerTableCell, 'centerTableCell');

	doc.body.appendChild(centerTable);

	// }

	var viewer = new Viewer(doc);
	viewer.hide();
	centerTableCell.appendChild(viewer.getMainElement());

	var viewerKeyListener = {
			viewer: viewer,
			handleEvent: function(event)
			{
				var cancelBubble = true;

				switch(event.keyCode)
				{
					case 27: // esc
						this.viewer.hide();
						break;

					case 35: // end
						this.viewer.showLast();
						break;

					case 36: // home
						this.viewer.showFirst();
						break;

					case 37: // left arrow
						this.viewer.showPrevious();
						break;

					case 39: // right arrow
						this.viewer.showNext();
						break;

					default:
						cancelBubble = false;
						break;
				}

				if(cancelBubble)
					switch(BROWSER_TYPE)
					{
						case BrowserType.MSIE:
							event.cancelBubble = true;
							event.returnValue = false;
							break;

						default:
							event.stopPropagation();
							event.preventDefault();
							break;
					}
			}
		};

	viewer.addEventListener(Viewer.EventType.VISIBILITY_CHANGE, {
		document: doc,
		cloak: cloak,
		centerTable: centerTable,
		viewerKeyListener: viewerKeyListener,
		handleEvent: function(customEvent)
		{
			if(customEvent.target.isHidden())
			{
				customEvent.target.setImage({});

				Utils.AddCSSClass(this.centerTable, DISPLAY_NONE_CLASSNAME);
				this.cloak.hide();
				detachHandler(this.document, 'keydown', this.viewerKeyListener);
			}
			else
			{
				Utils.RemoveCSSClass(this.centerTable, DISPLAY_NONE_CLASSNAME);
				this.cloak.show();
				attachHandler(this.document, 'keydown', this.viewerKeyListener);
			}
		}
	});

	return viewer;
}

// }

// private {

Gallery.prototype.__init = function(contextElement, _thumbnailTagName, _thumbnailClassName)
{
	this.mainElement = contextElement;

	var thumbnailTagName = typeof(_thumbnailTagName) == 'undefined' ? 'A' : _thumbnailTagName,
		thumbnailClassName = typeof(_thumbnailClassName) == 'undefined' ? null : _thumbnailClassName;

	this.eventListeners = [];

	Utils.AddCSSClass(this.mainElement, 'net.jzaruba.dom.Gallery');

	this.pageIdx = 0;
	this.pageLength = 0;

	this.model = new ArrayList();
	this.currentImage = null;

	// posbirani nahledu {

	var thumbnailElements = this.__findThumbnailElements(contextElement, thumbnailTagName, thumbnailClassName);
	for(var i = 0; i < thumbnailElements.length; i++)
		this.__processThumbnail(thumbnailElements[i]);

	// }
}

Gallery.prototype.__processThumbnail = function(sourceElement)
{
	var fullsizeSource = sourceElement.href,
		title = null,
		longDesc = null;

	for(var i = 0, imageElement, imageElements = sourceElement.getElementsByTagName('img'); imageElement = imageElements[i]; i++)
	{
		title = imageElement.title;
		longDesc = imageElement.getAttribute('longdesc');
		break;
	}

	if(!title)
		title = sourceElement.title;

	var imageDescriptor = {
				src: fullsizeSource,
				title: title,
				'comment': longDesc
			};

	attachHandler(sourceElement, 'click', {
			receiver: this,
			imageDescriptor: imageDescriptor,
			handleEvent: function(event)
			{
				switch(BROWSER_TYPE)
				{
					case BrowserType.MSIE:
						event.returnValue = false;
						break;

					default:
						event.preventDefault();
						break;
				}

				this.receiver.__showImage(this.imageDescriptor);
			}
		});

	this.model.addItem(imageDescriptor);
}

Gallery.prototype.__isPageAvailable = function(idx)
{
	return idx > -1 && (this.pageLength && idx < Math.ceil(this.mainElement.tBodies[0].rows.length / this.pageLength));
}

Gallery.prototype.__showImage = function(imageDescriptor)
{
	this.currentImage = imageDescriptor;

	var customEvent = new CustomEvent(Gallery.EventType.IMAGE_PICK)
	customEvent.imageDescriptor = imageDescriptor;
	customEvent.dispatch(this);
}

Gallery.prototype.__findThumbnailElements = function(contextElement, thumbnailTagName, thumbnailClassName)
{
	var thumbnailElements = [];
	for(var i = 0, element, elements = contextElement.getElementsByTagName(thumbnailTagName); i < elements.length; i++)
	{
		element = elements[i];
		if(!thumbnailClassName || Utils.TestCSSClass(element, thumbnailClassName))
			thumbnailElements.push(element);
	}
	return thumbnailElements;
}

Gallery.prototype.__update = function()
{
	this.__updateDisplayRange();
}

Gallery.prototype.__updateDisplayRange = function()
{
	for(var i = 0, row, rows = this.mainElement.tBodies[0].rows; i < rows.length; i++)
	{
		row = rows[i];
		if(this.pageLength > 0 && (i < this.pageIdx * this.pageLength || i +1 > this.pageIdx * this.pageLength + this.pageLength))
			Utils.AddCSSClass(row, DISPLAY_NONE_CLASSNAME);
		else
			Utils.RemoveCSSClass(row, DISPLAY_NONE_CLASSNAME);
	}
}

// }

// public {

Gallery.prototype.getModel = function()
{
	return this.model;
}

Gallery.prototype.addEventListener = Function.Templates.AddEventListener;

Gallery.prototype.moveByPage = function(step)
{
	this.setPageIndex(this.pageIdx + step);
}

Gallery.prototype.setPageIndex = function(pageIdx)
{
	this.pageIdx = pageIdx;

	this.__update();
}

Gallery.prototype.setPageLength = function(pageLength)
{
	this.pageLength = pageLength;

	this.__update();
}

// }
function Button1()
{
	this.__init.apply(this, arguments);
}

self.Button1 = Button1;

// private {

Button1.prototype.__init = function(element)
{
	this.element = element;
	attachHandler(this.element, 'mouseover', {
			receiver: this, 
			handleEvent: function(event)
			{
				this.receiver.__expand();
			}
		});
	attachHandler(this.element, 'mouseout', {
			receiver: this, 
			handleEvent: function(event)
			{
				this.receiver.__collapse();
			}
		});

	Utils.AddCSSClass(this.element, 'clickable');

	// find lower part {

	this.lower = null;
	for(var i = 0, node, nodes = this.element.childNodes; i < nodes.length; i++)
	{
		node = nodes[i];
		if(node.nodeType == 1 && Utils.TestCSSClass(node, 'lower'))
		{
			this.lower = node;
			break;
		}
	}

	// }

	// find upper part {

	this.upper = null;
	for(var i = 0, node, nodes = this.element.childNodes; i < nodes.length; i++)
	{
		node = nodes[i];
		if(node.nodeType == 1 && Utils.TestCSSClass(node, 'upper'))
		{
			this.upper = node;
			break;
		}
	}

	// }

	this.board = this.__createBoard(this.element.title);
	Utils.AddCSSClass(this.board, DISPLAY_NONE_CLASSNAME);
	this.element.appendChild(this.board);
	var width = this.upper.clientWidth;

	this.board.style.width = width +'px';
	this.board.style.position = 'absolute';
	this.element.title = '';

	this.boardAligned = false;
}

Button1.prototype.__createBoard = function(text)
{
	var doc = this.element.ownerDocument,
		board = doc.createElement('div'),
		textContainer = doc.createElement('div');
	textContainer.appendChild(doc.createTextNode(text))
	board.appendChild(textContainer);
	Utils.AddCSSClass(textContainer, 'text');
	Utils.AddCSSClass(board, 'board');
	return board;
}


Button1.prototype.__expand = function(_forceBoardRealignment)
{
	Utils.AddCSSClass(this.element, 'hover');
	Utils.RemoveCSSClass(this.board, DISPLAY_NONE_CLASSNAME);
	
	var forceBoardRealignment = typeof(_forceBoardRealignment) == 'boolean' && _forceBoardRealignment;

	if(forceBoardRealignment || (!this.boardAligned || BROWSER_TYPE != BrowserType.MSIE))
	{
		Utils.SetElementHorizontalAlignment(this.board, Horizontal.RIGHT, this.upper, Horizontal.LEFT);
		Utils.SetElementVerticalAlignment(this.board, Vertical.BOTTOM, this.upper, Vertical.BOTTOM);

		this.boardAligned = true;
	}

	Utils.SetElementHorizontalAlignment(this.lower, Horizontal.RIGHT, this.upper, Horizontal.LEFT);
	Utils.SetElementVerticalAlignment(this.lower, Vertical.BOTTOM, this.board, Vertical.BOTTOM);
}

Button1.prototype.__collapse = function()
{
	Utils.RemoveCSSClass(this.element, 'hover');
	Utils.AddCSSClass(this.board, DISPLAY_NONE_CLASSNAME);

	this.lower.style.position = '';
}

// }

// public {

Button1.prototype.setActive = function(active)
{
	if(active)
		Utils.AddCSSClass(this.element, 'active');
	else
		Utils.RemoveCSSClass(this.element, 'active');
}

// }
function Button2()
{
	this.__init.apply(this, arguments);
}

self.Button2 = Button2;

// private static {

Button2.__FindImage = function(element)
{
	for(var i = 0, node, nodes = element.childNodes; i < nodes.length; i++)
	{
		node = nodes[i];
		if(node.nodeType == 1 && Utils.TestCSSClass('image'))
			return node;
	}
}

// }

// private {

Button2.prototype.__init = function(element)
{
	this.element = element;

	this.image = Button2.__FindImage(this.element);

	attachHandler(this.element, 'mouseover', {
			receiver: this,
			handleEvent: function(event)
			{
				this.receiver.__setHilited(true);
			}
		});
	attachHandler(this.element, 'mouseout', {
			receiver: this,
			handleEvent: function(event)
			{
				this.receiver.__setHilited(false);
			}
		});
	
	Utils.AddCSSClass(this.element, 'clickable');
}

Button2.prototype.__setHilited = function(hilited)
{
	if(hilited)
		Utils.AddCSSClass(this.element, 'hover');
	else
		Utils.RemoveCSSClass(this.element, 'hover');
}

// }

// public {

Button2.prototype.setActive = function(active)
{
	if(active)
		Utils.AddCSSClass(this.element, 'active');
	else
		Utils.RemoveCSSClass(this.element, 'active');
}

// }
var locale = 'cs_CZ',
	logConsole = null,
	bigButtons = {};

var resource = new LocaleResource('cs_CZ');
resource.putMessages(
	{
		data_sent: 'Odesláno',
		request_failed: 'Došlo k chybě při odesílání',
		invalid_data: 'Neplatné nebo nekompletní údaje'
	});
			

function init()
{
	setupButtons();

	attachHandler(document.documentElement, 'click', {
			handleEvent: function(event)
			{
				var target = BROWSER_TYPE == BrowserType.MSIE ? event.srcElement : event.target,
					doc = target.ownerDocument;
				if(target.nodeType == 1 && target.tagName == 'A')
				{
					var docHref = doc.location.href,
						targetHref = target.href;
					if(targetHref)
					{
						var docHash = doc.location.hash,
							docHrefWithoutHash = docHref.substr(-docHref.length, docHref.length - docHash.length),
							shareDocPath = targetHref.substr(0, docHrefWithoutHash.length) == docHrefWithoutHash;

						if(shareDocPath)
						{
							/*
							 * this link points to a place within current document
							 */

							var targetId = Utils.GetHash(targetHref),
								equalWithoutHash = targetHref == docHrefWithoutHash +'#'+ targetId;
							if(equalWithoutHash)
							{
								var targetElement = doc.getElementById(targetId);
		
/*								switch(BROWSER_TYPE)
								{
									case BrowserType.MSIE:
										event.returnValue = false;
										break;
				
									default:
										event.preventDefault();
										break;
								}
								console.log('preventDefault in scripts/js/freebar/main.js');*/
		
								Utils.DispatchSimpleEvent('activate', targetElement)
							} 
						}
					}
				}
			}
		});
}

function initViewer()
{
	// cloak {

	var cloak = new Cloak(document);
	cloak.hide();
	document.body.appendChild(cloak.getMainElement());

	// }

	// centrovaci tabulka {

	var centerTable = document.createElement('table'),
		centerTableCell = centerTable.appendChild(document.createElement('tBody')).appendChild(document.createElement('tr')).appendChild(document.createElement('td'));

	centerTable.cellSpacing = 0;

	Utils.AddCSSClass(centerTable, 'centerTable');
	Utils.AddCSSClass(centerTable, DISPLAY_NONE_CLASSNAME);
	Utils.AddCSSClass(centerTableCell, 'centerTableCell');

	document.body.appendChild(centerTable);

	// }

	var viewer = new Viewer(document);
	viewer.hide();
	centerTableCell.appendChild(viewer.getMainElement());

	var viewerKeyListener = {
			viewer: viewer,
			handleEvent: function(event)
			{
				var cancelBubble = true;

				switch(event.keyCode)
				{
					case 27: // esc
						this.viewer.hide();
						break;

					case 35: // end
						this.viewer.showLast();
						break;

					case 36: // home
						this.viewer.showFirst();
						break;

					case 37: // left arrow
						this.viewer.showPrevious();
						break;
	
					case 39: // right arrow
						this.viewer.showNext();
						break;

					default:
						cancelBubble = false;
						break;
				}

				if(cancelBubble)
					switch(BROWSER_TYPE)
					{
						case BrowserType.MSIE:
							event.cancelBubble = true;
							event.returnValue = false;
							break;

						default:
							event.stopPropagation();
							event.preventDefault();
							break;
					}
			}
		};

	viewer.addEventListener(Viewer.EventType.VISIBILITY_CHANGE, {
		document: document,
		cloak: cloak,
		centerTable: centerTable,
		viewerKeyListener: viewerKeyListener,
		handleEvent: function(customEvent)
		{
			if(customEvent.target.isHidden())
			{
				customEvent.target.setImage({});

				Utils.AddCSSClass(this.centerTable, DISPLAY_NONE_CLASSNAME);
				this.cloak.hide();
				detachHandler(this.document, 'keydown', this.viewerKeyListener);
			}
			else
			{
				Utils.RemoveCSSClass(this.centerTable, DISPLAY_NONE_CLASSNAME);
				this.cloak.show();
				attachHandler(this.document, 'keydown', this.viewerKeyListener);
			}
		}
	});

	return viewer;
}

function setupButtons()
{
	for(var i = 0, bigButton, node, nodes = document.getElementById('buttonRow').childNodes; i < nodes.length; i++)
	{
		node = nodes[i];
		if(node.nodeType == 1 && Utils.TestCSSClass(node, 'button1'))
		{
			bigButton = new Button1(node);
			bigButton.setActive(node.id == 'button_'+ PAGE);
			bigButtons[node.id] = bigButton;
		}
	}

	//--

	for(var i = 0, node, nodes = document.getElementById('twinButtonsLeft').childNodes; i < nodes.length; i++)
	{
		node = nodes[i];
		if(node.nodeType == 1 && Utils.TestCSSClass(node, 'button2'))
			new Button2(node).setActive(node.id == 'button_'+ PAGE);
	}
	for(var i = 0, node, nodes = document.getElementById('twinButtonsRight').childNodes; i < nodes.length; i++)
	{
		node = nodes[i];
		if(node.nodeType == 1 && Utils.TestCSSClass(node, 'button2'))
			new Button2(node).setActive(node.id == 'button_'+ PAGE);
	}
}

attachHandler(window, 'load', {
		handleEvent: function(event)
		{
			if(BROWSER_TYPE == BrowserType.MSIE)
			{
/*				logConsole = new Console(document);
				document.body.appendChild(logConsole.getMainElement());*/
			}

			init();

			for(var i = 0; i < loadListeners.length; i++)
				loadListeners[i].handleEvent(event);
		}
	});

