/*
 * Constants
 */

$undef = 'undefined';
$func = 'function';
$max_tries = 8;

/*
 * Variables
 */

$loaded_files = [];
$send_queue = [];

/*
 * Handlers
 */

$begin_request = null;
$end_request = null;

/*
 * Base functions
 */

function $error(msg)
{
	alert('[!] Error occurred: ' + msg);
}

function $in_process()
{
	return ($sendQueue.length != 0);
}

function $send(url, data, callback_func, try_num)
{
	if (typeof(callback_func)==$func && $send_queue.length != 0)
	{
		$send_queue.push({url:url, data:data, callback_func:callback_func});
		return;
	}

	var req = null;
	if (typeof(try_num) == $undef) try_num = 1;

	if (window.XMLHttpRequest)
	{
		req = new XMLHttpRequest();
	}
	else
	if (window.ActiveXObject)
	{
		var msxmls = ['Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'];

		for (var i = 0; i < msxmls.length; i++)
		{
			try
			{
				req = new ActiveXObject(msxmls[i]);
				break;
			}
			catch (ex) {}
		}
	}

	if (req===null || req===false)
	{
		$error('Unable find XMLHttpRequest or it ActiveX alalog.');
		return null;
	}

	if (typeof(callback_func) == $func)
	{
		req.onreadystatechange = function()
		{
			if (req.readyState == 4)
			{
				if (req.status==200 || req.status==304) callback_func(req.responseText);
				else callback_func(null);

				if ($send_queue.length != 0)
				{
					var msg = $send_queue.shift();
					$send(msg.url, msg.data, msg.callback_func);
				}
				else {
					if ($end_request != null) $end_request();
				}
			}
		};

		if ($begin_request != null) $begin_request();
	}

	req.open((data === false ? 'GET' : 'POST'), url, (typeof(callback_func)==$func ? true : false));

	if (data === false)
	{
		try {
			req.send(null);
		} catch (e) {}
	}
	else
	{
		try
		{
			req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			req.setRequestHeader('Content-length', data.length);
			req.setRequestHeader('Connection', 'close');
			req.send(data);
		}
		catch (e) {}
	}

	if (typeof(callback_func) == $func) return null;

	try
	{
		status = req.status;

		if (req.status!=200 && req.status!=304) {
			if (try_num < $max_tries) $send(url, data, callback_func, try_num+1);
			else $error('Error sending request to "' + url + '": ' + req.status + '\n' + req.responseText);
		} else {
			return req.responseText;
		}
	}
	catch (e) { $error('Internal error while sending request to "' + url + '".'); }

	return null;
}

function $get(id)
{
	return document.getElementById(id);
}

function $style(id)
{
	var element = $get(id);
	return (element ? element.style : null);
}

function $show(id)
{
	var styles = $style(id);
	if (styles) styles.display = '';
}

function $hide(id)
{
	var styles = $style(id);
	if (styles) styles.display = 'none';
}

function $create(tag_name, attrs, styles)
{
	var element = document.createElement(tag_name.toUpperCase());

	if (element)
	{
		if (typeof(attrs)!=$undef && attrs!=null) {
			for (var k in attrs) element[k] = attrs[k];
		}

		if (typeof(styles)!=$undef && styles!=null) {
			for (var k in styles) element.style[k] = styles[k];
		}
	}

	return element;
}

function $include(name)
{
	if (typeof($loaded_files['z' + name]) != $undef) return;

	var script_text = $send('js/' + name + '.js', false);

	if (script_text == null)
	{
		$error('Module "' + name + '" not found');
		return;
	}

	if (script_text != '')
	{
		var ex;
		var script_element = $create('script', { type:'text/javascript', text:script_text });

		if (typeof(script_element.src) != $undef)
		{
			try {
				delete script_element.src;
			} catch (ex) {}
		}

		try
		{
			document.getElementsByTagName('HEAD')[0].appendChild(script_element);
			document.getElementsByTagName('HEAD')[0].removeChild(script_element);
			$loaded_files['z' + name] = true;
		}
		catch (ex)
		{
			$error('Can\' include module "' + name + '"');
		}
	}
}

function $extend(th, base)
{
	/*
	 * REMARK: some_object.prototype sucks, multiple inheritance - rules
	 */
	var obj = new base();
	for (var k in obj) th[k] = obj[k];
}

function $rm_childs(el)
{
	while (el.firstChild) el.removeChild(el.firstChild);
}

function $pos(el)
{
	var res = { top:0, left: 0 };

	while (el)
	{
		res.top += el.offsetTop;
		res.left += el.offsetLeft;
		el = el.offsetParent;
	}

	return res;
}

function $add_handler(element, event_name, event_handler)
{
	if (element.addEventListener) element.addEventListener(event_name, event_handler, false);
	else
	if (element.attachEvent) element.attachEvent('on' + event_name, event_handler);
}

function $rm_handler(element, event_name, event_handler)
{
	if (element.removeEventListener) element.removeEventListener(event_name, event_handler, false);
	else
	if (element.detachEvent) element.detachEvent('on' + event_name, event_handler);
}

function $new(obj_class)
{
	var obj = new obj_class();

	if (typeof(obj.init) != $undef)
	{
		var args = [];
		for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);

		obj.init.apply(obj, args);
	}

	return obj;
}

function $html(str)
{
	return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

function $current_style(el, prop)
{
	if (el.currentStyle)
	{
		var spl = prop.split('-');

		prop = spl[0];
		for (var i = 1; i < spl.length; i++) prop += spl[i].substring(0, 1).toUpperCase() + spl[i].substring(1);

		return el.currentStyle[prop];
	}
	else
	if (window.getComputedStyle) return document.defaultView.getComputedStyle(el, null).getPropertyValue(prop);

	return '';
}

$info = function() {
	return {
		window: function() {
			return {
				width: function() {
					return (window.innerWidth ? window.innerWidth : document.body.offsetWidth);
				},
				height: function() {
					return (window.innerHeight ? window.innerHeight : document.body.offsetHeight + 24);
				}
			};
		}(),
		page: function() {
			return {
				height: function() {
					return ((window.innerHeight && window.scrollMaxY) ? (window.innerHeight + window.scrollMaxY) : ((document.body.scrollHeight > document.body.offsetHeight) ? document.body.scrollHeight : document.body.offsetHeight));
				}
			}
		}
	};
}();

/*
 * Base class
 */

function BaseClass()
{
	this.delegate = function(func)
	{
		var _func = func;
		var _this = this;

		var _args = [];
		for (var i = 1; i < arguments.length; i++) _args.push(arguments[i]);

		return function(_event)
		{
			if (typeof(event) == 'undefined') event = _event;
			return _func.apply(_this, _args);
		};
	}

	this.delegate_ne = function(func)
	{
		var _func = func;
		var _this = this;

		var _args = [];
		for (var i = 1; i < arguments.length; i++) _args.push(arguments[i]);

		return function()
		{
			var args = [];
			for (var i = 0; i < _args.length; i++) args.push(_args[i]);
			for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
			return _func.apply(_this, args);
		};
	}

	this.new_uid = function()
	{
		if (typeof(window['__global_guid_counter__']) == 'undefined') window['__global_guid_counter__'] = 0;
		window['__global_guid_counter__']++;
		return String(window['__global_guid_counter__']) + (new Date()).valueOf();
	}
}

function Exception()
{
	$extend(this, BaseClass);

	this.message = '';

	this.init = function(message)
	{
		if (typeof(message) != $undef) this.message = message;
	}
}
