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 = new Array();
	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'
	);

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)/javascript').test(contentTypeHeader.toLowerCase()))
			return ContentType.JS;
		if(new RegExp('text/xml').test(contentTypeHeader.toLowerCase()))
			return ContentType.XML;
	}
}

var BrowserType = new Enum(
		'OTHER',
		'MSIE',
		'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 = /Gecko\//i;
	if(re.test(navigator.userAgent))
		return BrowserType.GECKO;
 
	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.DispatchEvent = function(customEvent)
{
	var eventListeners = this.eventListeners[customEvent.type];
	if(eventListeners)
		for(var i = 0; i < eventListeners.length; i++)
		{
			try
			{
				eventListeners[i].handleEvent(customEvent);
			}
			catch(e)
			{
				trace(this.constructor.getFunctionName() +' listener error: '+ e.message);
				trace(e.stack);
			}
		}
}*/

/*Function.Templates.InitEvent = function(eventType)
{
	return new CustomEvent(eventType);
}*/

Function.Templates.AddEventListener = function(eventType, eventListener)
{
	if(!this.eventListeners[eventType])
		this.eventListeners[eventType] = new Array();

	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.trim = function()
{
	return this.replace(/^\W+/, '').replace(/\W+$/g, '');
}

String.prototype.toLocalized = function(_locale)
{
	var locale = typeof(_locale) == 'string' ? _locale : window.getLocale();

	if(!locale)
		trace('WARNING: locale not defined');
	else
	{
		var resource = LocaleResource.GetResource(locale);

		if(resource)
		{
			message = resource.getMessage(this);
	
			if(message)
				return message;
			else
				trace('WARNING: value not found for "'+ this +'" in resource for locale "'+ locale +'"');
		}
		else
			trace('WARNING: resource not found for locale "'+ locale +'"');
	}

	return this;
}

String.prototype.__format = function(parameters)
{
	var c = '\u200B',
		message = this.valueOf().replace('%%', c);

	for(var i = 0, varIdx; i < parameters.length; i++)
	{
		varIdx = message.indexOf('%');
		message = message.substring(0, varIdx) + parameters[i] + message.substring(varIdx +1);
	}

	return message.replace(c, '%%');
}

String.prototype.toLocalizedWithParameters = function(parameters, locale)
{
	return this.toLocalized(locale).__format(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)
	{
		trace(e.message);
		trace(e.stack);
	}

	try
	{
		if(!locale && self.opener)
			locale = self.opener.getLocale();
	}
	catch(e)
	{
		trace(e.message);
		trace(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];
						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()
					{
						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 = 'cz.acolyte.gui.hidden',
	DISABLE_CLASSNAME = 'cz.acolyte.gui.disabled',
	HILITE_CLASSNAME = 'cz.acolyte.gui.hilited',
	
	/*
	 * MSIE likes to ignore style.cssFloat
	 */
	FLOAT_LEFT_CLASSNAME = 'cz.acolyte.floatLeft',
	FLOAT_RIGHT_CLASSNAME = 'cz.acolyte.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);
				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);
				break;
		}
	else
	{
		var message = 'unable_to_add_css_rules';
		alert(message);
		throw new Error(message);
	}

