var U={

	////////////////////////////////////////////////////////////////////
	// unsorted
	////////////////////////////////////////////////////////////////////
	bodyTag: function(){
		if(!this.bodyTagElement){
			this.bodyTagElement=document.getElementsByTagName('BODY')[0];
		}
		return this.bodyTagElement;
	},
	
	validateEmail: function(email){
		var result=email.search(/^[^@\s]+@[^@\s]+\.[^@\s]{2,}$/);
		result=(result==0)?true:false;
		return result;
	},
	
	homepage: function(){//
		return (location.pathname=='/' || location.pathname=='/index.html');
	},

	newin: function(link){
		if(link && link.href){
			var newin=window.open(link.href);
			return false;
		}
	},

	popup: function(url,w,h){
		var hash=(arguments[3] || {});
		var name=(hash.name || '_blank');
		var left=(U.getCookie('lastPopupLeft') || 75);
		left=parseInt(left)+ 25;
		if(window.screen && window.screen.width && left+parseInt(w)>window.screen.width)left=50;
		U.setCookie('lastPopupLeft',left);
		var top=(U.getCookie('lastPopupTop') || 75);
		top=parseInt(top)+ 25;
		if(window.screen && window.screen.height && top+parseInt(w)>window.screen.height)top=50;
		U.setCookie('lastPopupTop',top);
		var params='';
		params+='left='+ parseInt(left)+ ',';
		params+='top='+ parseInt(top)+ ',';
		params+='width='+ parseInt(w)+ ',';
		params+='height='+ parseInt(h)+ ',';
		params+='scrollbars='+ (hash.s || hash.scroll || hash.scrollbars || '0')+ ',';
		params+='resizable='+ (hash.r || hash.resize || hash.resizable || '0')+ ',';
		params+='menubar='+ (hash.m || hash.menu || hash.menubar || '0')+ ',';
		params+='titlebar='+ (hash.titlebar || '0')+ ',';
		params+='toolbar='+ (hash.toolbar || '0')+ ',';
		params+='location='+ (hash.location || '0')+ ',';
		params+='directories='+ (hash.directories || '0')+ ',';
		params+='hotkeys='+ (hash.hotkeys || '0')+ ',';
		params+='status='+ (hash.status || '0')+ ',';
		params+='dependent='+ (hash.dependent || '0')+ ',';
		params+='fullscreen='+ (hash.fullscreen || '0')+ ',';
		params+='channelmode='+ (hash.channelmode || '0');
		var win=window.open(url,name,params);
		try{
			win.focus();
			return win;
		}catch(e){}
	},

	tryUntil: function(function2run,testCondition){
		var testCondition=unescape(testCondition);
		var function2run=unescape(function2run);
		if(eval(testCondition)){
			try{
				eval(function2run);
			}catch(e){/*поскольку событие было отложено, возможно что к моменту реализации функция уже перестанет существовать*/}
		}else{
			var date=new Date();
			setTimeout('U.tryUntil("'+ escape(function2run)+ '","'+ escape(testCondition)+ '")',(arguments[2]|100));
		}
	},

	pda: function(){
		return window.screen.width<640;
	},

	props: function(hash){
		var result='';
		for(var i in hash){
			result+=''+ i+ ': '+ hash[i]+ '\n';
		}
		return result;
	},

	defvar: function(def,data){//возвращает дефолтное значение, если отсутствуют данные
		return (data)?data:def;
	},

	hashToStr: function(hash){//эта функция получает хэш и превращает его в строку
		//если тип параметра object
		if(typeof hash=='object'){
			//инициализируем результат
			var result='hash={};';
			//пробегаем по свойствам
			for(var i in hash){
				//собираем результат
				result+='hash.'+ i+ '="'+ hash[i]+ '";';
			}
		}
		//делаем escape
		result=escape(result);
		//возвращаем результат
		return result;
	},

	hashFromStr: function(str){//эта функция получает строку и выполняет unescape и eval; используется в паре с hashToStr()
		eval(unescape(str));
		return hash;
	},

	loadBigImage: function(hash){//загружает большое изображение и на время загрузки отображает анимированный гиф процесса загрузки
		//проверяем все ли данные переданы
		if(hash.bigImgBoxId && hash.bigImgSrc && hash.previewId && hash.loaderBgcolor && hash.loaderSrc){
			//проверяем существуют ли на странице переданные id
			if(U.gbi(hash.bigImgBoxId) && U.gbi(hash.previewId)){
				//проверяем загружена ли в браузер картинка анимированного лоадера
				//если нижеупомянутое действие еще не производилось
				if(!this.loadBigImage_loaderImage){
					//создаем объект Image для лоадера
					this.loadBigImage_loaderImage=new Image();
					//назначаем src
					this.loadBigImage_loaderImage.src=hash.loaderSrc;
				}
				//запускаем проверку загрузки лоадера
				this.loadBigImageTestLoader(U.hashToStr(hash))
			}
		}
	},

	loadBigImageTestLoader: function(hash){//эта функция нужна для ожидания загрузки картинки лоадера (вызывается из loadBigImage())
		//если загрузка лоадера закончена
		if(this.loadBigImage_loaderImage.complete){
			//вызываем loadBigImageEnd()
			this.loadBigImageEnd(hash);
		}else{
			//иначе еще раз вызываем loadBigImageTestLoader()
			setTimeout('U.loadBigImageTestLoader("'+ hash+ '")', 100);
		}
	},

	loadBigImageEnd: function(hash){//эта функция запускается после загрузки картинки лоадера и ждет загрузки большого изображения
		//распаковываем наш hash
		var hash=this.hashFromStr(hash);
		//если нижеописанное действие не производилось
		if(!this.loadBigImage_bigImage){
			//впервые создаем хэш с объектами Image, которые будут хранить все запрошенные загрузки
			this.loadBigImage_bigImage={};
		}
		//если нижеописанное действие не производилось
		if(!this.loadBigImage_bigImage[hash.previewId]){
			//создаем новый объект Image для текущей большой картинки (ассоциатором должен быть hash.previewId, поскольку hash.bigImgBoxId может быть общим для многих)
			this.loadBigImage_bigImage[hash.previewId]=new Image();
			//определяем ему src
			this.loadBigImage_bigImage[hash.previewId].src=hash.bigImgSrc;
		}
		//если загрузка большого изображения закончена
		if(this.loadBigImage_bigImage[hash.previewId].complete){
			//помещаем новое изображение в приготовленное место
			U.gbi(hash.bigImgBoxId).innerHTML='<img src="'+ this.loadBigImage_bigImage[hash.previewId].src+ '" width="'+ this.loadBigImage_bigImage[hash.previewId].width+ '" height="'+ this.loadBigImage_bigImage[hash.previewId].height+ '" alt="">';
			//если превьюшка была заменена на лоадер
			if(this.loadBigImage_bigImage[hash.previewId].loaderElement){
				//делаем на лоадер ссылку для упрощения кода
				var loader=this.loadBigImage_bigImage[hash.previewId].loaderElement;
				//заменяем лоадер на превьюшку
				loader.parentNode.replaceChild(this.loadBigImage_bigImage[hash.previewId].previewElement, loader);
				//удаляем объект лоадера, он больше не понадобится, поскольку изображение есть в кэше
				this.loadBigImage_bigImage[hash.previewId].loaderElement=null;
			}
			//если был передан код на исполнение, исполняем его
			if(hash.functionToRun){eval(hash.functionToRun);}
			//завершаем работу
			return;
		}else{
			//иначе 
			if(!preview){
				//делаем ссылку на превьюшку
				var preview=U.gbi(hash.previewId);
			}
			//если нижеописанное действие не производилось
			if(!this.loadBigImage_bigImage[hash.previewId].loaderElement){
				//создаем элемент картинки загрузчика
				this.loadBigImage_bigImage[hash.previewId].loaderElement=document.createElement('IMG');
				//делаем на этот элемент ссылку для упрощения кода
				var loader=this.loadBigImage_bigImage[hash.previewId].loaderElement;
				//устанавливаем загрузчику основные атрибуты
				loader.setAttribute('src',this.loadBigImage_loaderImage.src);
				loader.setAttribute('width',this.loadBigImage_loaderImage.width);
				loader.setAttribute('height',this.loadBigImage_loaderImage.height);
				//определяем его будущие позицию, родителя и отступы (которые будут сделаны с помощью бордеров с нужным цветом)
				var borderLeft=parseInt((preview.offsetWidth-loader.width)/2);
				var borderRight=preview.offsetWidth-loader.width-borderLeft;
				var borderTop=parseInt((preview.offsetHeight-loader.height)/2);
				var borderBottom=preview.offsetHeight-loader.height-borderTop;
				var top=preview.offsetTop;
				var left=preview.offsetLeft;
				//устанавливаем стили
				loader.style.position='relative';
				loader.style.zIndex=999;
				loader.style.borderColor=hash.loaderBgcolor;
				loader.style.borderTopWidth=borderTop+ 'px';
				loader.style.borderRightWidth=borderRight+ 'px';
				loader.style.borderBottomWidth=borderBottom+ 'px';
				loader.style.borderLeftWidth=borderLeft+ 'px';
				//дублируем старую певьюшку перед тем как удалить ее из кода страницы
				this.loadBigImage_bigImage[hash.previewId].previewElement=preview;
				//заменяем превьюшку на лоадер
				preview.parentNode.replaceChild(loader, preview);
			}
			//еще раз вызываем loadBigImageEnd()
			setTimeout('U.loadBigImageTestLoader("'+ this.hashToStr(hash)+ '")', 100);
		}
	},

	////////////////////////////////////////////////////////////////////
	// cookies
	////////////////////////////////////////////////////////////////////
	setCookie: function(cookieName,cookieContent,cookieExpireTime){
		if(cookieExpireTime>0){
			var expDate=new Date();
			expDate.setTime(expDate.getTime()+cookieExpireTime*1000*60*60);
			var expires=expDate.toGMTString();
			document.cookie=cookieName+"="+escape(cookieContent)+"; path="+escape('/')+"; expires="+expires;
		}else{
			document.cookie=cookieName+"="+escape(cookieContent)+"; path="+escape('/')+"";
		}
	},

	getCookie: function(cookieName){
		var ourCookie=document.cookie;
		if(!ourCookie || ourCookie=="")return "";
			ourCookie=ourCookie.split(";");
		var i=0;
		var Cookie;
		while(i<ourCookie.length){
			Cookie=ourCookie[i].split("=")[0];
			if(Cookie.charAt(0)==" ")
				Cookie=Cookie.substring(1);
			if(Cookie==cookieName){
				return unescape(ourCookie[i].split("=")[1]);
			}
			i++;
		}
		return ""
	},

	
	////////////////////////////////////////////////////////////////////
	// events
	////////////////////////////////////////////////////////////////////
	addEvent: function(node,event,func){
		if(!node)return;
		if(node.addEventListener){
			node.addEventListener(event,func,false);
		}else{
			if(node.attachEvent){
				event='on'+event;
				node.attachEvent(event,func);
			}
		}
	},

	stopEvent: function(event){
		event.cancelBubble = true; //отключить бег события выше для ИЕ
		event.returnValue = false; //отключить стандартное действие
		//то же самое для Мозиллы
		if(event.preventDefault) event.preventDefault(); 
		if(event.stopPropagation) event.stopPropagation();
	},

	clientX: function(){//определяем координату мыши по X
		return this.clientXY()[0];
	},

	clientY: function(){//определяем координату мыши по Y
		return this.clientXY()[1];
	},

	clientXY: function(evt){//возвращает координаты мыши
		if(evt){//автовызов, инициированный обработчиком событий
			U.clientXY_x=evt.clientX;
			U.clientXY_y=evt.clientY;
		}else{//вызвал программист
			if(typeof U.clientXY_x=='undefined'){
				U.clientXY_x=0;
				U.clientXY_y=0;
				this.addEvent(document,'mousemove',U.clientXY);
			}
			return [U.clientXY_x,U.clientXY_y];
		}
	},

	////////////////////////////////////////////////////////////////////
	// string
	////////////////////////////////////////////////////////////////////

	splitText: function(str,step){
		var step=step||1;
		var tmpArr=[];
		var result=[];
		var tmp, symb;
		for(var i=0; i<str.length; i++){
			symb=str.substr(i,1);
			if(symb=='&' && str.substr(i).indexOf(';')){
				tmp='&';
				do{
					i++;
					symb=str.substr(i,1);
					tmp+=symb;
				}while(symb && symb!=';');
				tmpArr.push(tmp);
			}else{
				if(symb=='<' && str.substr(i).indexOf('>')){
					tmp='<';
					do{
						i++;
						symb=str.substr(i,1);
						tmp+=symb;
					}while(symb && symb!='>');
					tmpArr.push(tmp);
				}else{
					tmpArr.push(symb);
				}
			}
		}
		if(step==1)return tmpArr;
		for(var i=0; i<tmpArr.length; i+=step){
			tmp='';
			for(var j=0; j<step; j++){
				tmp+=tmpArr[i+j]||'';
			}
			result.push(tmp);
		}
		return result;
	},

	////////////////////////////////////////////////////////////////////
	// DOM
	////////////////////////////////////////////////////////////////////

	gbi: function(id){
		return document.getElementById(id);
	},

	layerX: function(elem){//определяем левую координату слоя
		return this.layerXY(elem)[0];
	},

	layerY: function(elem){//определяем верхнюю координату слоя
		return this.layerXY(elem)[1];
	},

	layerXY: function(elem){//определяем обе координаты слоя
		var x=0;
		var y=0;
		while(elem){
			x+=elem.offsetLeft;
			y+=elem.offsetTop;
			elem=elem.offsetParent;
		}
		return [x,y];
	},

	////////////////////////////////////////////////////////////////////
	// array
	////////////////////////////////////////////////////////////////////
	array: {
		find: function(array,value){
			for(var i in array){
				if(array[i]==value){
					return i;
				}
			}
			return false;
		}
	},

	////////////////////////////////////////////////////////////////////
	// object
	////////////////////////////////////////////////////////////////////
	object: {
		af: function(obj,hash){//apply fields
			var fieldsArr=obj._af.split(',');
			var fieldName='';
			for(var i in fieldsArr){
				fieldName=fieldsArr[i];
				obj[fieldName]=(hash[fieldName]||null);
			}
		},

		join2str: function(obj){//join fields to a string
			var fieldsArr=obj._af.split(',');
			var fieldName='';
			var result='';
			for(var i in fieldsArr){
				fieldName=fieldsArr[i];
				if(obj[fieldName]!=null){
					result+=fieldName+ ':"'+ obj[fieldName]+ '",';
				}
			}
			if(result)result=result.substr(0,result.length-1);
			return result;
		}
	},

	////////////////////////////////////////////////////////////////////
	// animation
	////////////////////////////////////////////////////////////////////
	mouseScroll: function(hash){//осуществляет скроллирование слоя, контролируемое движением мыши
		/*
			hash имеет следующие поля
			id - id элемента для скроллирования
			repeat - способ прокрутки (xy, x, y, no), по умолчанию без прокрутки
			zone - ограничение зоны чувствительности мыши (xy, x, y, no), по умолчанию без ограничения
			methodX (methodY) - матем.метод (пока только sin, он же по умолчанию), описывающий движение слоя //kx (ky) - коэфициент ускорения (от 0 до ...) по оси X (Y), по умолчанию 0,  поэтому если не передан, движение не будет осуществляться по данной оси
		*/
		this._af='id,repeat,methodX,methodY,presets';
		U.object.af(this,hash);

		var layerid='layer'+ this.id;

		//находим слой для прокрутки 
		var layer=U.gbi(this.id);
		//находим родителя
		var parentLayer=layer.parentNode;

		//если это первый вызов
		if(this[layerid]!='object'){
			this[layerid]={};
			this._af='id,repeat,methodX,methodY,layer'+ this.id;
			//у родителя фиксируем ширину, высоту, position и overflow
			parentLayer.style.width=parentLayer.offsetWidth+ 'px';
			parentLayer.style.height=parentLayer.offsetHeight+ 'px';
			parentLayer.style.position='relative';
			parentLayer.style.overflow='hidden';
			//теперь у слоя для прокрутки фиксируем position
			layer.style.position='absolute';
			//запоминаем некоторые величины
			this[layerid].layerWidth=layer.offsetWidth;
			this[layerid].layerHeight=layer.offsetHeight;
			this[layerid].parentWidth=parentLayer.offsetWidth;
			this[layerid].parentHeight=parentLayer.offsetHeight;
			this[layerid].parentLeft=parentLayer.offsetLeft;
			this[layerid].parentTop=parentLayer.offsetTop;
		}
		//вычисляем смещение
			//определяем центр родителя
			var bodyWidth=document.getElementsByTagName('body')[0].offsetWidth;
			if(this[layerid].centerCoords && this[layerid].bodyWidth && this.bodyWidth==bodyWidth){
			}else{
				this[layerid].bodyWidth=bodyWidth;
				this[layerid].centerCoords=U.layerXY(parentLayer);
				this[layerid].centerCoords[0]+=parentLayer.offsetWidth/2;
				this[layerid].centerCoords[1]+=parentLayer.offsetHeight/2;
			}
			//определяем шаг
			var step=[0,0];
			if(this.methodX!=null)
				step[0]=-50*Math.sin((U.clientX()/this[layerid].centerCoords[0]-1)*Math.PI/2);
			if(this.methodY!=null)
				step[1]=-50*Math.sin((U.clientY()/this[layerid].centerCoords[1]-1)*Math.PI/2);
		//перерисовываем слой
		if(step[0]){
			var layerLeft=layer.offsetLeft + step[0];
			if(this.repeat!='x' && this.repeat!='xy'){
				if(layerLeft+this[layerid].layerWidth < this[layerid].parentWidth)
					layerLeft=this[layerid].parentWidth - this[layerid].layerWidth;
				if(layerLeft > 0)layerLeft=0;
			}
			layer.style.left=layerLeft+ 'px';
		}
		if(step[1])
			var layerTop=layer.offsetTop + step[1];
			if(this.repeat!='y' && this.repeat!='xy'){
				if(layerTop+this[layerid].layerHeight < this[layerid].parentHeight)
					layerTop=this[layerid].parentHeight - this[layerid].layerHeight;
				if(layerTop > 0)layerTop=0;
			}
			layer.style.top=(layer.offsetTop + step[1])+ 'px';
		//рекурсивно вызываем функцию
		setTimeout('U.mouseScroll({'+ U.object.join2str(this)+ '})',20);
	},

	typewriter: function(id,text,speed,endfunc){//эффект печатной машинки
		/*
			id - id элемента для применения эффекта
			text - сам текст, может содержать конечные теги, типа <br>
			speed - скорость вывода (необязательный параметр)
			endfunc - строка кода, который будет выполнен по окончании вывода текста
		*/

		//получаем текст в виде массива
		var step=U.defvar(1,parseInt(speed));
		var textArr=U.splitText(text,step);
		var arrStr=textArr.join('¬');
		var endfunc=(endfunc!='')?escape(endfunc):'';
		//создаем хэш для таймеров
		if(!U.typewriterTimer){U.typewriterTimer={};}
		//формируем функцию для рекурсивного вызова и запускаем ее
		this.typewriterRec(id,arrStr,endfunc);
	},

	typewriterRec: function(id,arrStr,endfunc,position){
		var textArr=arrStr.split('¬');
		var position=position||0;
		if(position==0){U.gbi(id).innerHTML='';}
		if(position<textArr.length && U.gbi(id)){
			//определяем очередную порцию текста
			var textPortion=textArr[position];
			//меняем пробелы на табуляцию, чтобы пробелы не проглатывались маками
			textPortion=textPortion.replace(/ /g, '&#9;');
			//в уже напечатанном тексте делаем обратную замену
			var textExisted=U.gbi(id).innerHTML
			textExisted=textExisted.replace(/&#9;/g, ' ');
			//добавляем очередную порцию текста
			U.gbi(id).innerHTML=textExisted+ textPortion;
			position++;
			U.typewriterTimer[id]=setTimeout('U.typewriterRec("'+ id+ '","'+ arrStr+ '","'+ endfunc+ '","'+ position+ '")',25);
		}else{
			var endfunc=unescape(endfunc);
			eval(endfunc);
		}
	}
}
