/*
Dependencies: none
Functions list:
	addEvent(elem, evType, fn)
	removeEvent(elem, evType, fn)
	addClass(o, c)
	removeClass(o, c)
	getCookie(name)
	setCookie(name, value, props)
	deleteCookie(name)
	byId(node)
	
*/



// возвращает cookie если есть или undefined
function getCookie(name){
	var matches = document.cookie.match(new RegExp(
		"(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
	))
	return matches ? decodeURIComponent(matches[1]) : undefined
}
 
// уcтанавливает cookie
function setCookie(name, value, props) {
	props = props || {}
	var exp = props.expires
	if (typeof exp == "number" && exp) {
		var d = new Date()
		d.setTime(d.getTime() + exp*1000)
		exp = props.expires = d
	}
	if(exp && exp.toUTCString) { props.expires = exp.toUTCString() }
	
	value = encodeURIComponent(value)
	var updatedCookie = name + "=" + value
	for(var propName in props){
		updatedCookie += "; " + propName
		var propValue = props[propName]
		if(propValue !== true){ updatedCookie += "=" + propValue }
	}
	document.cookie = updatedCookie
}
 
// удаляет cookie
function deleteCookie(name){
	setCookie(name, null, { expires: -1 })
}

function getFnName(fn){
	return fn.toString().match(/function ([^(]*)\(/)[1];
}

function isOldIE(){
	if(navigator.appName!='Microsoft Internet Explorer')return false;
	return (navigator.appVersion.indexOf('MSIE 7.')!=-1 || navigator.appVersion.indexOf('MSIE 6.')!=-1)?true:false;
}

function isIE(){
	if(navigator.appName!='Microsoft Internet Explorer')return false;
	return true;
}

function isWebkit(){
	if(navigator.appVersion.indexOf('AppleWebKit')!=-1)return true;
	return false;
}


/* getBrowser (added 16.01.2011)
Возвращает объект:
{
	name: 'firefox' / 'ie' / 'opera' / 'chrome' / 'safari'
	version: [3,6,13]
}
*/
function getBrowser(){
	var ret={};
	var agent=window.navigator.userAgent;
	if(/Firefox\/([0-9.]+)/.test(agent)){
		ret.name='firefox';
		ret.version=/Firefox\/([0-9.]+)/.exec(agent)[1].split('.');
	}else if(/MSIE ([0-9.]+)/.test(agent)){
		ret.name='ie';
		ret.version=/MSIE ([0-9.]+)/.exec(agent)[1].split('.');
	}else if(/Chrome\/([0-9.]+)/.test(agent)){
		ret.name='chrome';
		ret.version=/Chrome\/([0-9.]+)/.exec(agent)[1].split('.');
	}else if(agent.indexOf('Opera')!=-1 && /Version\/([0-9.]+)/.test(agent)){
		ret.name='opera';
		ret.version=/Version\/([0-9.]+)/.exec(agent)[1].split('.');
	}else if(agent.indexOf('Safari')!=-1 && /Version\/([0-9.]+)/.test(agent)){
		ret.name='safari';
		ret.version=/Version\/([0-9.]+)/.exec(agent)[1].split('.');
	}else return false;
	for(var key in ret.version)ret.version[key]=parseInt(ret.version[key]);
	return ret;
}

/**	создает объектно-ориентированный класс с наследованием.

	obj = newClass(menu,{
		constructor:function(){
			this.constructor.prototype.constructor.call(this);
		}
	});

эквивалентно ПХП-шному:

	class obj extends menu{
		function __construct(){
			parent::__construct();
		}
	}

*/

function newClass(parent,prop){
	// Dynamically create class constructor.
	var _ret = function(){
		// Stupid JS need exactly one "operator new" calling for parent
		// constructor just after class definition.
		if(_ret.preparing) return delete(_ret.preparing);

		var self=this;
		/*var self=null;
		var _ololo=function(){
			self=this;
		}
		_ololo();*/

		// Call custom constructor.
		if(_ret.constr){
			var self=this;
			this.constructor=_ret; // we need it!
			_ret.constr.apply(this,arguments);
		}
	}

	_ret.prototype = {}; // no prototype by default
	if(parent){
		parent.preparing=true;
		_ret.prototype=new parent;
		_ret.prototype.constructor=parent;
		_ret.constr=parent; // BY DEFAULT - parent constructor
	}
	if(prop){
		var cname = "constructor";
		for (var k in prop){
			if(k!=cname)
				_ret.prototype[k] = prop[k];
				//_ret[k] = prop[k];
		}
		if(prop[cname]&&prop[cname]!=Object)
			_ret.constr=prop[cname];
	}

	return _ret;
}

// создает дебажный див, в который выводить отладочную инфу
function __debug_div(){
	var debugDiv=byId('__debug_div');
	if(!debugDiv){
		debugDiv=document.createElement('pre');
		debugDiv.id='__debug_div';
		debugDiv.style.cssText='position:fixed;bottom:0;left:0;background:silver;color:green;'+
			'font-family:monospace;font-size:11px;padding:1px 3px;border:1px inset silver;margin:0;'+
			'max-height:500px;overflow-x:visible;overflow-y:scroll;';
		addEvent(window,'domready',function(){
			//window.onLoad
			document.body.appendChild(debugDiv);
		})
		document.body.appendChild(debugDiv);
	}
	return debugDiv;
}

// (added 16.01.2011) расписывает переменную и выводит ее на подобии print_r в пхп.
function _show(variable){
	var debugDiv=__debug_div();
	debugDiv.innerHTML=print_r(variable,true);
}

// (added 16.01.2011) выводит в нижний плавающий див значения
function _values(vars){
	var debugDiv=__debug_div();
	debugDiv.innerHTML=implode('; ',vars);
}

var _GET={};
var _COOKIES={};
/* __parseVars (beta) (added 16.01.2011)
возвращает массив из созданных переменных из адресной строки и куков. Получаются 2 массива (Как в пхп):
_GET
_COOKIES
Функция вызывается автоматически при загрузке скрипта.
*/
function __parseVars(){
	var allcookies = document.cookie;
	cookiearray  = allcookies.split(';');
	for(var i=0; i<cookiearray.length; i++){
		name = cookiearray[i].split('=')[0];
		value = cookiearray[i].split('=')[1];
		_COOKIES[trim(name)]=decodeURI(value);
	}
}
__parseVars();
//__show(_COOKIES);

function kbdReverseLayout(el){
	new_el = el;// выходящий элемент
	A = new Array();
	A["й"]="q";A["ц"]="w";A["у"]="e";A["к"]="r";A["е"]="t";A["н"]="y";A["г"]="u";A["ш"]="i";A["щ"]="o";A["з"]="p";A["х"]="[";A["ъ"]="]";
	A["ф"]="a";A["ы"]="s";A["в"]="d";A["а"]="f";A["п"]="g";A["р"]="h";A["о"]="j";A["л"]="k";A["д"]="l";A["ж"]=";";A["э"]="'";
	A["я"]="z";A["ч"]="x";A["с"]="c";A["м"]="v";A["и"]="b";A["т"]="n";A["ь"]="m";A["б"]=",";A["ю"]=".";A["."]="/";
	A["Й"]="Q";A["Ц"]="W";A["У"]="E";A["К"]="R";A["Е"]="T";A["Н"]="Y";A["Г"]="U";A["Ш"]="I";A["Щ"]="O";A["З"]="P";A["Х"]="[";A["Ъ"]="]";
	A["Ф"]="A";A["Ы"]="S";A["В"]="D";A["А"]="F";A["П"]="G";A["Р"]="H";A["О"]="J";A["Л"]="K";A["Д"]="L";A["Ж"]=";";A["Э"]="'";
	A["Я"]="Z";A["Ч"]="X";A["С"]="C";A["М"]="V";A["И"]="B";A["Т"]="N";A["Ь"]="M";A["Б"]=",";A["Ю"]=".";A["."]="/";
	new_el.value = el.value.replace(/([\u0410-\u0451])/g,
		function (str,p1,offset,s) {
			if (A[str] != 'undefined'){return A[str];}
		}
	);
}

function validateEmail(input){
	var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
	var address = typeof(input)=='object'?input.value:input;
	return reg.test(address);
/*	if(reg.test(address)==false){
		//input.style.outline='2px dotted red';
		if(stylize)input.style.background='#ffc0c0';
		return false;
	}else{
		//input.style.outline='none';
		if(stylize)input.style.background='';
		return true;
	}*/
}

// ========================= DOM functions =========================

// events

// устарело!
function addEvent(elem, evType, fn) {
	if (elem.addEventListener){
		elem.addEventListener(evType,fn,false)
		return fn
	}
	iefn = function() { fn.call(elem) }
	elem.attachEvent('on' + evType, iefn);
	return iefn;
}
 
// устарело!
function removeEvent(elem, evType, fn) {
	if (elem.addEventListener){
		elem.removeEventListener(evType, fn, false)
		return
	}
	elem.detachEvent('on' + evType, fn);
}

Element.prototype.addEvent=function(evType,fn){
	if(evType=='mousewheel')if(getBrowser()['name']=='firefox')evType='DOMMouseScroll';
	if(this.addEventListener){
		this.addEventListener(evType, fn, false);
		return fn;
	}
	iefn = function() { fn.call(this) }
	this.attachEvent('on' + evType, iefn);
	return iefn;
}
//*/

/* ------------- experiment! ------------- /
Element.prototype.addEvent=function(evType,fn){
	//var _self=this;
	if(evType=='mousewheel')if(getBrowser()['name']=='firefox')evType='DOMMouseScroll';
	if(this.addEventListener){
		this.addEventListener(evType,function(event){
			fn.call(self,event);
		},false);
		return fn;
	}
	iefn=function(){fn.call(this)}
	this.attachEvent('on'+evType,iefn);
	return iefn;
}
 ------------- /experiment! ------------- */

Element.prototype.removeEvent=function(evType, fn) {
	if(evType=='mousewheel')if(getBrowser()['name']=='firefox')evType='DOMMouseScroll';
	if (this.addEventListener){
		this.removeEventListener(evType, fn, false)
		return
	}
	this.detachEvent('on' + evType, fn);
}


// возвращает 1, если колесико мыши прокручено вниз, или -1 если вверх
Event.prototype.wheelDirect=function(){
	if(getBrowser().name=='chrome')return this.wheelDelta<0?1:-1;
	return this.detail<0?-1:1;
}

// возвращает координаты клика мыши, относительно элемента, на котором установлен обработчик события
Event.prototype.clickCoords=function(){
	return {
		x:this.pageX-this.currentTarget.getCoords().x,
		y:this.pageY-this.currentTarget.getCoords().y
	}
}



function addClass(o, c){
	var re = new RegExp("(^|\\s)" + c + "(\\s|$)", "g")
	if (re.test(o.className)) return
	o.className = (o.className + " " + c).replace(/\s+/g, " ").replace(/(^ | $)/g, "")
}

function removeClass(o, c){
	var re = new RegExp("(^|\\s)" + c + "(\\s|$)", "g")
	o.className = o.className.replace(re, "$1").replace(/\s+/g, " ").replace(/(^ | $)/g, "")
}

// возвращает элемент по его ИДу
function byId(node){
	return typeof node == 'string' ? document.getElementById(node) : node
}

/* node (added 16.01.2011)
Возвращает сгенеренный элемент дом-дерева. Синтаксис использования:
var tagUL = node('ul');
var menuItem = node({
	tagName:'li',
	className:'',
	style:'display:none',
	childNodes:[
		{
			tagName:'a',
			className:stylePrefix+'_container',
			onClick:function(event){alert('lol!');},
			onMouseover:[
				function(event){alert('hui!');},
				function(event){alert('pizda!');}
			],
			childNodes:{
				tagName:'span',
				className:stylePrefix+'',
				innerHTML:params.caption
			}
		},{
			tagName:'div',
			className:stylePrefix+'_childnodes',
			innerHTML:'huina'
		}
	]
});
alert(menuItem.childNodes[0].childNodes[0].innerHTML); // params.caption
*/
function node(data){
	if(typeof(data)=='string')return document.createTextNode(data);
	if(typeof(data)=='object'){
		var ret=document.createElement(data.tagName);
		for(var key in data)switch(key){
		case 'childNodes':
			//alert(typeof(data[key])+' '+key+' '+data[key]);
			switch(typeof(data[key])){
			case 'array':
			case 'object':
				for(var k2 in data[key])ret.appendChild(arguments.callee(data[key][k2]));
				break;
			//case 'object':ret.appendChild(data[key]);break;
			default:ret.innerHTML=data[key];
			}
			break;
		case 'style':ret.style.cssText=data[key];break;
		case 'attributes':
			for(var k_attr in data[key])
				ret.setAttribute(k_attr,data[key][k_attr]);
			break;
		case 'tagName':break;
		default:
			if(key.substr(0,2)=='on'){// обработчики событий
				switch(typeof(data[key])){
				case 'array':
					for(var k2 in data[key])if(typeof(data[key][k2])=='function')
						addEvent(ret,/on([a-zA-Z]+)/.exec(key)[1].toLowerCase(),data[key][k2]);
					break;
				case 'function':
					addEvent(ret,/on([a-zA-Z]+)/.exec(key)[1].toLowerCase(),data[key]);
					break;
				}
			}else ret[key]=data[key];
		}
		return ret;
	}
	return false;
}

// возвращает тру, если whom вложен во who (на любом уровне вложенности)
function isParent(who,whom){// кто, кому
	while(1){
		if(who.parentNode==whom)return true;
		if(who.parentNode==document)return false;
		who=who.parentNode;
	}
}

/** возвращает координаты элемента относительно верхнего левого угла документа
Устарело
*/
function getCoords(element){
	var left = element.offsetLeft;
	var top = element.offsetTop;
	for (var parent = element.offsetParent; parent; parent = parent.offsetParent){
		left += parent.offsetLeft;
		top += parent.offsetTop;
	}
	return {x:left,y:top};
}

Element.prototype.getCoords=function(){
	var left = this.offsetLeft;
	var top = this.offsetTop;
	for (var parent = this.offsetParent; parent; parent = parent.offsetParent){
		left += parent.offsetLeft;
		top += parent.offsetTop;
	}
	return {x:left,y:top};
}

/** пробегает по элементу и вложенным элементам, применяя функцию handler. Если element не указан,
по умолчанию применяется body
	ret - это значение, которое вернула функция handler, и будет передаваться 3-м аргументом в хэндлеры вложенных элементов.
*/
function nodeWalk(handler,element,ret){
	element=!element?document.body:element;
	var r=handler(element,ret);
	for(var _i=0;_i<element.childNodes.length;_i++)
		if(element.childNodes[_i].nodeType==1)
			arguments.callee(handler,element.childNodes[_i],r);
}

/** Проверяет тег на соответствие критериям exp. Возвращает тру или фалс.
Примеры:
	element.match('a.next')
				- вернет тру, если element является ссылкой, и класс next
	element.match({tagName:'a',target:'_blank'})
				- вернет тру, если element является ссылкой, и содержит аттрибут target="_blank"
*/

Element.prototype.match=function(exp){
	var errs=0;
	var classes=this.className.split(' ');
	switch(typeof exp){
	case 'string':
		var matches=/^([a-z0-9_]+)?([\t ]*)(\.([a-z0-9_-]+))?$/i.exec(exp);
		if(matches==null)return false;
		//alert(matches);
		if(matches[1])if(matches[1].toLowerCase()!=this.tagName.toLowerCase())errs++;
//		alert(errs);
//		alert(matches);
		if(matches[4])if(!php.in_array(matches[4],classes))errs++;
//		alert(errs);
		break;
	case 'object':
		// search in: 1) dom properties; 2) attributes; 3) tagName&className
		for(var k in exp)switch(k){
			case 'class':case 'className':
				if(!php.in_array(exp[k],classes))errs++;
				break;
			case 'tag':case 'tagName':
				if(exp[k].toLowerCase()!=this.tagName.toLowerCase())errs++;
				break;
			default:
				if(this.getAttribute(k)!=exp[k] && this[k]!=exp[k])errs++;
		}
		break;
	}
	return errs==0;
}

/** Следующая группа функций извлекает элементы, по соответствию критериям params.
Как сформировать params, см выше функцию match

функция				возвращает					тег(и), который(е)
------------------------------------------------------------------------------------------------
selectChild			элемент						является childNode в элементе
selectChilds		массив элементов			является childNode в элементе
selectNode			элемент						вложен на любом уровне в элементе
selectNodes			массив элементов			вложен на любом уровне в элементе

*/

document.selectChild=function(params){
	for(var i=0;i<document.body.childNodes.length;i++)
		if(document.body.childNodes[i]==1)
			if(document.body.childNodes[i].match(params))
				return document.body.childNodes[i];
}

document.selectChilds=function(params){
	var ret=[];
	for(var i=0;i<document.body.childNodes.length;i++)
		if(document.body.childNodes[i]==1)
			if(document.body.childNodes[i].match(params))
				ret[ret.length]=document.body.childNodes[i];
	return ret;
}

document.selectNode=function(params){
	var ret=null;
	nodeWalk(function(element){
		if(!element.match)return false;
		if(element.match(params))ret=element;
	});
	return ret;
}

document.selectNodes=function(params){
	var ret=[];
	nodeWalk(function(element){
		if(element.match==undefined)return false;
		if(element.match(params))ret[ret.length]=element;
	});
	return ret;
}

Element.prototype.selectChild=function(params){
	//alert(this.childNodes);
	for(var i=0;i<this.childNodes.length;i++)
		if(this.childNodes[i].nodeType==1)
			if(this.childNodes[i].match(params))
				return this.childNodes[i];
}

Element.prototype.selectChilds=function(params){
	var ret=[];
	for(var i=0;i<this.childNodes.length;i++)
		if(this.childNodes[i].nodeType==1)
			if(this.childNodes[i].match(params))
				ret[ret.length]=this.childNodes[i];
	return ret;
}

Element.prototype.selectNode=function(params){
	var ret=null;
	nodeWalk(function(element){
		if(element.match(params))ret=element;
	},this);
	return ret;
}

Element.prototype.selectNodes=function(params){
	var ret=[];
	nodeWalk(function(element){
		if(element.match(params))ret[ret.length]=element;
	},this);
	return ret;
}

/** Применяет к выделенному тексту (либо месту курсора) обработчик handler. Текст по прежнему остается
выделенным, однако обработанным.
Устаревшая функция! Вместо нее использовать this.processSelected(fn)
*/
function processSelected(textarea,handler){
	/*
	textarea.value
	textarea.selectionEnd
	textarea.selectionStart
	textarea.textLength
	*/
	var start=textarea.selectionStart;
	var end=textarea.selectionEnd;
	var oldText=textarea.value.substring(start,end);
	var newText=handler(oldText);
	var scrollTop=textarea.scrollTop;
	textarea.value=
		textarea.value.substring(0,start)+
		newText.text+
		textarea.value.substring(end);
	textarea.setSelectionRange(start+newText.start,start+newText.end);
	textarea.focus();
	textarea.scrollTop=scrollTop;
}

/* Устаревшая функция! Вместо нее использовать this.autoExpand */
function textareaAutoExpand(event){
	var dopusk=10;
	var elem=event.currentTarget;
	if(elem.__oldHeight==undefined)elem.__oldHeight=elem.scrollHeight;
	if(elem.scrollHeight>elem.__oldHeight){
		if(elem.value=='')elem.style.height=0;
		else elem.style.height=(elem.scrollHeight+dopusk >= window.innerHeight-50)? window.innerHeight-50 : elem.scrollHeight+dopusk;
	}else{
		if(elem.value=='')elem.style.height=0;
	}
	elem.__oldHeight=elem.scrollHeight;
}

// this.processSelected(fn)
document.createElement('textarea').constructor.prototype.processSelected=
	function(handler){return processSelected(this,handler)}

document.createElement('textarea').constructor.prototype.autoExpand=
	function(){
		var dopusk=10;
		if(this.__oldHeight==undefined)this.__oldHeight=this.scrollHeight;
		if(this.scrollHeight>this.__oldHeight){
			if(this.value=='')this.style.height=0;
			else this.style.height=(this.scrollHeight+dopusk >= window.innerHeight-50)? window.innerHeight-50 : this.scrollHeight+dopusk;
		}else{
			if(this.value=='')this.style.height=0;
		}
		this.__oldHeight=this.scrollHeight;
	}

// ========================= functions like in PHP =========================

function explode(delimiter,str){
	return str.split(delimiter);
}

function implode(glue,pieces){
	return ((pieces instanceof Array )?pieces.join(glue):pieces);
}

function htmlspecialchars(s){
	var div = document.createElement('div');
	var text = document.createTextNode(s);
	div.appendChild(text);
	return div.innerHTML;
}

function trim(str,charlist){
	charlist=!charlist?' \s\xA0':charlist.replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g,'\$1');
	var re=new RegExp('^[' + charlist + ']+|[' + charlist + ']+$','g');
	return str.replace(re,'');
}

function print_r(array,return_val){
	var level=return_val;
	var arr=array;

    var print_red_text = "";
    if(!level) level = 0;
    var level_padding = "";
    for(var j=0; j<level+1; j++) level_padding += "    ";
    if(typeof(arr) == 'object') {
        for(var item in arr) {
            var value = arr[item];
            if(typeof(value) == 'object') {
                print_red_text += level_padding + "'" + item + "' :\n";
                print_red_text += print_r(value,level+1);
			}else print_red_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
        }
    }else  print_red_text = "===>"+arr+"<===("+typeof(arr)+")";
    return print_red_text;
}

function in_array(needle, haystack, strict) {   // Checks if a value exists in an array
	var found = false, key, strict = !!strict;
	for (key in haystack) {
		if ((strict && haystack[key] === needle) || (!strict && haystack[key] == needle)) {
			found = true;
			break;
		}
	}
	return found;
}

function array_unique( array ) {    // Removes duplicate values from an array
	var p, i, j;
	for(i = array.length; i;){
	    for(p = --i; p > 0;){
	        if(array[i] === array[--p]){
	            for(j = p; --p && array[i] === array[p];);
	            i -= array.splice(p + 1, j - p).length;
	        }
	    }
	}
	return true;
}

var php={
	explode:explode,
	implode:implode,
	htmlspecialchars:htmlspecialchars,
	trim:trim,
	print_r:print_r,
	in_array:in_array,
	array_unique:array_unique
};

// ========================= AJAX =========================


function ajax(params){
	var self=this;
	var ready=function(){
		// self - ссылка на класс, this.readyState - состояние аякса, this.status - код ответа http
		//alert('ready! '+this.readyState+' httpStatus: '+this.status+' self.url = '+self.url);
		if(this.readyState==4)alert(this.responseText);
	}
	
	var getXmlHttp=function(){
		var xmlhttp;
		try{xmlhttp=new ActiveXObject("Msxml2.XMLHTTP");}catch(e){
			try{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}catch(E){xmlhttp=false;}
		}
		if(!xmlhttp && typeof XMLHttpRequest!='undefined')xmlhttp = new XMLHttpRequest();
		return xmlhttp;
	}
	
	var urlEncode=function(arr){
		if(typeof(arr)=='object'){
			var encodedLine=[];
			for(var key in arr)encodedLine[encodedLine.length]=key+'='+encodeURIComponent(arr[key]);
			encodedLine=implode('&',encodedLine);
			return encodedLine;
		}
		return '';
	}
	
	this.url=params.url==null?window.location.pathname:params.url;
	this.handler=params.handler==null?null:params.handler;

	this.get=params.get==null?null:params.get;
	this.post=params.post==null?null:params.post;
	this.files=params.files==null?null:params.files;
	this.eval=params.eval==true?true:false;

	if(this.files!=null){
		var DateObj=new Date;
		var iframeId='_AjaxUploadIframe_'+DateObj.getTime();

		if(isOldIE())var ajaxIframe=document.createElement('<iframe name="'+iframeId+'"></iframe>');
		else{
			var ajaxIframe=document.createElement('iframe');
			ajaxIframe.name=iframeId;
		}
		ajaxIframe.id=iframeId;
		ajaxIframe.style.display='none';
		ajaxIframe.src='about:blank';

		var ajaxForm=document.createElement('form');
		ajaxForm.encoding='multipart/form-data';
		ajaxForm.action=this.url.indexOf('?')==-1?(this.url+'?'+urlEncode(this.get)):(this.url+'&'+urlEncode(this.get));
		ajaxForm.method='post';
		ajaxForm.target=iframeId;
		ajaxForm.style.display='none';

		if(this.post!=null)for(var key in this.post){
			var input=document.createElement('textarea');
			input.name=key;
			input.value=htmlspecialchars(this.post[key]);
			ajaxForm.appendChild(input);
		}
		for(var key in this.files){
			if(isIE() || isWebkit()){// chrome, safari, IE don't cloning nodes with values
				var input=this.files[key].cloneNode(false);
				this.files[key].parentNode.insertBefore(input,this.files[key]);
				this.files[key].name=key;
				ajaxForm.appendChild(this.files[key]);
			}else{// firefox, opera
				var input=this.files[key].cloneNode(false);
				input.name=key;
				ajaxForm.appendChild(input);
			}
		}
		document.body.appendChild(ajaxForm);
		document.body.appendChild(ajaxIframe);

		var ev=addEvent(ajaxIframe,'load',function(event){
			var resp=this.contentWindow.document.body.innerText===undefined?
				this.contentDocument.body.textContent:this.contentWindow.document.body.innerText;
			document.body.removeChild(ajaxForm);
			if(!isIE()){
				removeEvent(this,'load',arguments.callee);
				ajaxIframe.contentWindow.location.href='about:blank';
			}
			document.body.removeChild(ajaxIframe);
			if(self.eval)resp=eval('('+resp+')');
			self.handler(resp);
		});
		ajaxForm.submit();
	}else{
		var xmlHttp=getXmlHttp();
		if(isOldIE()){
			if(this.get==null){this.get=[];this.get['_ie_cache_hash']=Math.random();}
				else this.get['_ie_cache_hash']=Math.random();
		}
		xmlHttp.onreadystatechange=function(){
			if(xmlHttp.readyState==4){
				resp=self.eval?eval('('+xmlHttp.responseText+')'):xmlHttp.responseText;
				self.handler(resp);
			}
		};
		xmlHttp.open(
			this.post==null?'GET':'POST',
			(this.url.indexOf('?')==-1?(this.url+'?'+urlEncode(this.get)):(this.url+'&'+urlEncode(this.get))),
			true
		);
		xmlHttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
		xmlHttp.send(this.post==null?null:urlEncode(this.post));
	}
}

// возвращает сгенеренную json-строку из яваскрипт-объекта
function array2json(arr){
	var parts = [];
	var is_list = (Object.prototype.toString.apply(arr) === '[object Array]');
	
	for(var key in arr) {
		var value = arr[key];
		if(typeof value == "object") { //Custom handling for arrays
			if(is_list) parts.push(array2json(value)); /* :RECURSION: */
			else parts[key] = array2json(value); /* :RECURSION: */
		} else {
			var str = "";
			if(!is_list) str = '"' + key + '":';
			
			//Custom handling for multiple data types
			if(typeof value == "number") str += value; //Numbers
			else if(value === false) str += 'false'; //The booleans
			else if(value === true) str += 'true';
			else str += '"' + value + '"'; //All other things
			// :TODO: Is there any more datatype we should be in the lookout for? (Functions?)
			
			parts.push(str);
		}
	}
	var json = parts.join(",");
	if(is_list) return '[' + json + ']';//Return numerical JSON
	return '{' + json + '}';//Return associative JSON
}

// ========================= user =========================

function user(arr){
	var self=this;
	if(typeof(arr)=='object')for(var k in arr)this[k]=arr[k];
	
	// есть ли права админа
	this.isadmin=function(){
		return self.status==3;
	}

	// есть ли права модера
	this.ismoder=function(){
		return self.status==3 || self.status==2;
	}
}







