//------------------------------------------------------------------------------------------------
/*
	MicroMonster JS tools
	Версия 1.0
	Дата релиза: 31.08.2007
	Автор: JIyHaTuK (Зимаков Павел - pavel.zimakoff@gmail.com) <http://www.team-mosk.ru/>
	Описание: набор функций и классов для облегчения написания скриптов на JS
	Копирайты: создавался с оглядкой на Mootools 1.11 - copyright (c) 2007 Valerio Proietti, <http://mad4milk.net>
*/
//------------------------------------------------------------------------------------------------





//------------------------------------------------------------------------------------------------
/*
Структура: tool
Описание: содержит различные функции
Подробно: {struct} tool
Свойства:
	tool.opera - если брузер - Opera, то содержит его версию в строковом виде, иначе false
	tool.ie - если брузер - IE, то содержит его версию в строковом виде, иначе false
	tool.gecko - если брузер - на движке Gecko (Firefox), то содержит его версию в строковом виде, иначе false
Функции:
	empty() - пустая функция
	getUA(str) - поиск названий програм и их версий в строке User Agent
	bind(func, obj, args) - возвращает функцию, выполняя которую, будет вызываться функция func от имени объекта obj, с параметрами args. Если передаются событие, оно передаётся в func после всех аргументов
	merge(prop1, prop2[, ...]) - добавляет в prop1 все свойства из prop2, prop3, ... с последовательной заменой
	addProps(prop1, prop2[, ...]) - добавляет в prop1 все свойства из prop2, prop3, ... которых нет в prop1
	$native(prop1, [prop2, ...]) - добавляет метод .extend(param) для расширения переданных классов
	colorTest(val) - проверяет, является ли объект строковым заданием цвета
	parseColor(val) - преобразует строковое задание цвета в массив из 3 чисел
	getStyleProp(elem, prop) - возвращает текущее значение свойства стиля (которое вычислил браузер исходя из всех css и прочих)
	getElemPosition(element) - возвращает положение элемента относительно страницы
	eventExtend(func) - возвращает функцию для прикрепления к событиям, в которой будет преобразовываться объект event в кроссбраузерный вариант
	addEvent(elem, evt, func[, custom[, nobubble]]) - кросс-браузерная функция для закрепления событий, рекомендуется использовать её
	fireEvent(elem, evt[, args]) - вызов события
	delEvent(elem, evt, func[, custom[, nobubble]]) - кросс-браузерная функция для снятия обработчика с события, закреплённого с помощью addEvent, рекомендуется использовать её
	loadScript(src, func) - подключает *.js JavaScript
	loadCss(src) - подключает css файл
	preloadImgs(srcs) - выполняет предзагрузку изображения/изображений
*/
//------------------------------------------------------------------------------------------------
var tool={

/*
Функция: empty()
Описание: пустая функция, служит для задания начального значения событиям
Подробно: {void} tool.empty()
Аргументы: -
Возвращает: -
*/
	empty: function(){},

/*
Функция: getUA(str)
Описание: ищет название программы str в строке userAgent, возращает версию, если нашёл иначе false
Подробно: {string} tool.getUA({string} str)
Аргументы: str - строка для поиска
Возвращает: версию, если найдено название программы или false
*/
	getUA: function(str)
	{
		var ua=navigator.userAgent, res;
		if(res=ua.match(str+'([.\\d]*)')) return res[1];
		else return false;
	},

/*
Функция: bind(func, obj, args)
Описание: возвращает функцию, выполняя которую, будет вызываться функция func от имени объекта obj, с параметрами args. Если передаются событие, оно передаётся в func после всех аргументов
Подробно: {function} tool.bind({function} func, {object} obj, [{array} args])
Аргументы: func - выполняемая функция, obj - объект на который будет ссылаться this в функции func, args - аргументы
Возвращает: функцию -||-
*/
	bind: function(func, obj, args){ if(!args) args=[]; return function(e){ return func.apply(obj, args.concat(e)); }; },

/*
Функция: merge(prop1, prop2[, ...])
Описание: добавляет в prop1 все свойства из prop2, prop3, ... с последовательной заменой
Подробно: {void} tool.merge({object} prop1, {object} prop2, [{object} prop3[, ...]])
Аргументы: prop1 - объект, для добавления св-в; prop2, prop3, ... - объекты, откуда брать св-ва
Возвращает: -
*/
	merge: function()
	{
		for(var i=1; i<arguments.length; i++)
		{
			for(var prop in arguments[i]) arguments[0][prop] = arguments[i][prop];
		}
	},

/*
Функция: addProps(prop1, prop2[, ...])
Описание: добавляет в prop1 все свойства из prop2, prop3, ... которых нет в prop1
Подробно: {void} tool.addProps({object} prop1, {object} prop2, [{object} prop3[, ...]])
Аргументы: prop1 - объект, для добавления св-в; prop2, prop3, ... - объекты, откуда брать св-ва
Возвращает: -
*/	
	addProps: function()
	{
		for(var i=1; i<arguments.length; i++)
		{
			for(var prop in arguments[i]) if(!arguments[0][prop]) arguments[0][prop] = arguments[i][prop];
		}
	},

/*
Функция: $native(prop1, [prop2, ...])
Описание: добавляет метод .extend(param) для расширения переданных классов
Подробно: {void} $native({object} prop1, [{object} prop2, [{object} prop3, ...]])
Аргументы: prop1, prop2, ... - классы, которым будет добавлен метод
Возвращает: -
*/
	$native: function()
	{
		for(var i=0; i<arguments.length; i++)
		{
			arguments[i].extend = function(props)
			{
				for(var prop in props) if (!this.prototype[prop]) this.prototype[prop] = props[prop];
			};
		}
	},

/*
Функция: colorTest(val)
Описание: проверяет, является ли объект строковым заданием цвета
Подробно: {boolean} tool.colorTest({mixed} val)
Аргументы: val - тестируемый объект
Возвращает: возвращает true, если val - задан как '#rrggbb' или 'rgb(rrr, ggg, bbb)' и false, если иначе
*/
	colorTest: function(val)
	{
		if(typeof(val)!='string') return false;
		if(val.charAt(0)=='#' || val.substr(0,3)=='rgb') return true;
		return false;
	},

/*
Функция: parseColor(val)
Описание: преобразует строковое задание цвета в массив из 3 чисел
Подробно: {array[3]} tool.parseColor({string} val)
Аргументы: val - строковое задание цвета в формате '#rrggbb' или 'rgb(rrr, ggg, bbb)'
Возвращает: возвращает массив из 3 чисел, задающих цвет в формате rgb
*/
	parseColor: function(val)
	{
		if(val.substr(0,3)=='rgb')
		{
			res=val.match(/\d{1,3}/g);
			res=res.each(function(item){return parseInt(item,10);});
			return res;
		}
		if(val.charAt(0)=='#')
		{
			res=[];
			for(var i=0; i<3; i++) res[i]=parseInt(val.substr(i*2+1,2),16);
			return res;
		}
		return false;
	},

/*
Функция: getStyleProp(elem, prop)
Описание: возвращает текущее значение свойства стиля (которое вычислил браузер исходя из всех css и прочих)
Подробно: {string} tool.getStyleProp({HTMLElement} elem, {string} prop)
Аргументы: elem - элемент, у которого брать значение свойства стиля; prop - имя свойства
Возвращает: значение свойства
*/
	getStyleProp: function(elem, prop)
	{
		if(elem.style[prop]) return elem.style[prop];
		if (document.defaultView) return document.defaultView.getComputedStyle(elem, null)[prop];
			else if (elem.currentStyle) return elem.currentStyle[prop];
		return false;
	},

/*
Функция: getCssRule(selector)
Описание: ищет правило в подключённых css файлах
Подробно: {cssRule} tool.getCssRule({srting} selector)
Аргументы: selector - текст для поиска в селекторе
Возвращает: rule
*/	
	getCssRule: function(selector)
	{
		for(var i=0; i<document.styleSheets.length; i++)
		{
			var rules=document.styleSheets[i].cssRules || document.styleSheets[i].rules;
			for(var j=0; j<rules.length; j++)
				if(rules[j].selectorText.search(selector) > -1) return rules[j];
		}
		return false;
	},

/*
Функция: applyCssRule(elem, rule)
Описание: применяет правило CSS для элементов
Подробно: {void} tool.applyCssRule({HTMLElement/array} elem, {cssRule} rule)
Аргументы:
	elem - элемент или селектор элементов
	rule - CSS правило
Возвращает: -
*/	
	applyCssRule: function(elem, rule)
	{
		elem=$$(elem);
		for(var i=0; i<elem.length; i++)
			for(var prop in rule.style)
				if(typeof(rule.style[prop])=='string' && rule.style[prop].length>0 && prop!='cssText')
					elem[i].style[prop] = rule.style[prop];
	},

/*
Функция: getElemPosition(element)
Описание: возвращает положение элемента относительно страницы
Подробно: {struct} tool.getElemPosition({HTMLElement} element)
Аргументы: element - элемент, для которого будет проведён поиск координат
Возвращает: координаты в виде структуры {{number} x, {number} y}
*/	
	getElemPosition: function(element)
	{
		if(tool.gecko)
		{
			var elem=element, prev=false, xv=0, yv=0;
			while(elem && elem.tagName!='BODY')
			{
				xv+=elem.offsetLeft + ((elem==element) ? 0 : parseInt(tool.getStyleProp(elem, 'borderLeftWidth'),10));
				yv+=elem.offsetTop + ((elem==element) ? 0 : parseInt(tool.getStyleProp(elem, 'borderTopWidth'),10));
				prev=elem; elem=elem.offsetParent;
			}
			if(elem && prev && elem.tagName=='BODY')
			{
				var pos=tool.getStyleProp(prev,'position');
				if(['static','relative'].contains(pos))
				{
					xv+=parseInt(tool.getStyleProp(elem.ownerDocument.body,'borderLeftWidth'),10);
					yv+=parseInt(tool.getStyleProp(elem.ownerDocument.body,'borderTopWidth'),10);
				}
				if(pos=='fixed')
				{
					xv+=element.ownerDocument.html.scrollLeft;
					yv+=element.ownerDocument.html.scrollTop;
				}
			}
			elem=element.parentNode;
			while(elem && elem.tagName!='BODY')
			{
				xv-=elem.scrollLeft || 0;
				yv-=elem.scrollTop || 0;
				if(elem.offsetWidth!=elem.scrollWidth) xv+=parseInt(tool.getStyleProp(elem, 'borderLeftWidth'),10);
				if(elem.offsetHeight!=elem.scrollHeight) yv+=parseInt(tool.getStyleProp(elem, 'borderTopWidth'),10);
				elem=elem.parentNode;
			}
			return { x: xv, y: yv };
		}
		if(tool.opera)
		{
			var elem=element, xv=0, yv=0;
			while(elem && elem.tagName!='BODY')
			{
				xv+=elem.offsetLeft;
				yv+=elem.offsetTop;
				elem=elem.offsetParent;
			}
			if(!elem)
			{
				xv+=element.document.body.scrollLeft;
				yv+=element.document.body.scrollTop;
			}
			elem=element.parentNode;
			while(elem && elem.tagName!='BODY')
			{
				xv-=elem.scrollLeft || 0;
				yv-=elem.scrollTop || 0;
				elem=elem.parentNode;
			}
			return { x: xv, y: yv };
		}
		if(tool.ie)
		{
			var elem=element, xv=-elem.clientLeft, yv=-elem.clientTop;
			while(elem && elem.tagName!='HTML')
			{
				xv+=elem.offsetLeft + elem.clientLeft;
				yv+=elem.offsetTop + elem.clientTop;
				elem=elem.offsetParent;
			}
			var isStatic=(!elem)?'HTML':'BODY';
			elem=element.parentNode;
			while(elem && elem.tagName!=isStatic)
			{
				xv-=elem.scrollLeft || 0;
				yv-=elem.scrollTop || 0;
				elem=elem.parentNode;
			}
			return { x: xv, y: yv };
		}
	},
	
/*
Функция: eventExtend(func)
Описание: возвращает функцию для прикрепления к событиям, в которой будет преобразовываться объект event в кроссбраузерный вариант
Подробно: {function} tool.eventExtend({function} func)
Аргументы: func - функция обработчик, в неё будет передаваться универсальное событие
Возвращает: функция для закрепления на события
*/
	eventExtend: function(func, mouseEL)
	{
		return function(e)
		{
			if(typeof(tool)=='undefined'){ return; }
			if(mouseEL && tool.childLevel(e.currentTarget, e.relatedTarget)>=0) return;
			e = e || window.event;
			var evt={};
			evt.origEvent=e;
			evt.altKey=e.altKey; evt.ctrlKey=e.ctrlKey; evt.shiftKey=e.shiftKey; evt.metaKey=e.metaKey;
			evt.bubbles= e.bubbles || !e.cancelBubble;
			evt.button=(tool.ie)? ((e.button==1 || e.button==2) ? (e.button-1)*2 : false) : e.button;
			evt.keyCode=e.keyCode;
			evt.target=e.target || e.srcElement;
			evt.ownerDocument=(evt.target) ? (evt.target.ownerDocument || evt.target.document) : false;
			evt.currentTarget=e.currentTarget || false;
			evt.screenX=e.screenX; evt.screenY=e.screenY;
			if(tool.ie)
			{
				evt.clientX=e.clientX-2; evt.clientY=e.clientY-2;
				evt.pageX=evt.clientX+((evt.target) ? evt.ownerDocument.html.scrollLeft : 0);
				evt.pageY=evt.clientY+((evt.target) ? evt.ownerDocument.html.scrollTop : 0);
			}
			else
			{
				evt.clientX=e.clientX; evt.clientY=e.clientY;
				evt.pageX=e.pageX; evt.pageY=e.pageY;
			}
			evt.preventDefault=function()
			{
				if(this.origEvent.preventDefault) this.origEvent.preventDefault();
				else this.origEvent.returnValue=false;
			};
			evt.stopPropogation=function()
			{
				if(this.origEvent.stopPropogation) this.origEvent.stopPropogation();
				else this.origEvent.cancelBubble=true;
			};
			evt.stop=function(){ this.preventDefault(); this.stopPropogation(); };
			func.apply(this, [evt]);
		};
	},

/*
Функция: addEvent(elem, evt, func[, custom[, nobubble]])
Описание: кросс-браузерная функция для закрепления событий, рекомендуется использовать её
Подробно: {void} tool.addEvent({object} elem, {string} evt, {function} func, {boolean} custom, {boolean} nobubble)
Аргументы: elem - объект у которого возникает событие; evt - название события без on; func - привязываемая функция; custom - стандартное событие или расширенное; nobubble - всплывать событие (при закреплении стандартного события в W3C браузере)
Возвращает: -
*/
	addEvent: function(elem, evt, func, custom, nobubble)
	{
		if(!elem.$events) elem.$events = {};
		if(!elem.$events[evt]) elem.$events[evt] = {orig: []};
		var pos=elem.$events[evt].orig.push(func) - 1;
		if(!custom)
		{
			if(!elem.$events[evt].ext) elem.$events[evt].ext=[];
			elem.$events[evt].ext.push(tool.eventExtend(func, (!tool.ie && (evt=='mouseenter' || evt=='mouseleave'))));
			
			if(tool.ie) elem.attachEvent('on'+evt, elem.$events[evt].ext[pos]);
			else
			{
				var oEvt=evt;
				if(evt=='mouseenter') oEvt='mouseover'; if(evt=='mouseleave') oEvt='mouseout';
				elem.addEventListener(oEvt, elem.$events[evt].ext[pos], nobubble || false);
			}
		}
	},

/*
Функция: fireEvent(elem, evt[, args])
Описание: вызов события
Подробно: {void} tool.fireEvent({object} elem, {string} evt, {array} args)
Аргументы: elem - объект для которого генерируется событие; evt - название события без on; args - передаваемые аргументы функциям-обработчикам
Возвращает: -
*/	
	fireEvent: function(elem,evt, args)
	{
		if(!elem.$events || !elem.$events[evt]) return;
		for(var i=0; i<elem.$events[evt].orig.length; i++)
		{
			elem.$events[evt].orig[i].apply(elem, args || []);
		}
	},

/*
Функция: delEvent(elem, evt, func[, custom[, nobubble]])
Описание: кросс-браузерная функция для снятия обработчика с события, закреплённого с помощью addEvent, рекомендуется использовать её
Подробно: {void} tool.delEvent({object} elem, {string} evt, {function} func, {boolean} custom, {boolean} nobubble)
Аргументы: elem - объект у которого возникает событие; evt - название события без on; func - привязываемая функция; custom - стандартное событие или расширенное; nobubble - всплывать событие (при закреплении стандартного события в W3C браузере)
Возвращает: -
*/
	delEvent: function(elem, evt, func, custom, nobubble)
	{
		if(!custom)
		{
			var pos=elem.$events[evt].orig.indexOf(func);
			if(tool.ie) elem.detachEvent('on'+evt, elem.$events[evt].ext[pos]);
			else
			{
				var oEvt=evt;
				if(evt=='mouseenter') oEvt='mouseover'; if(evt=='mouseleave') oEvt='mouseout';
				elem.removeEventListener(oEvt, elem.$events[evt].ext[pos], nobubble || false);
			}
			elem.$events[evt].ext.splice(pos,1);
		}
		elem.$events[evt].orig.splice(pos,1);
	},

/*
Функция: loadScript(src, func);
Описание: подключает *.js JavaScript
Подробно: {void} tool.loadScript({string} src, {function(e)} func)
Аргументы: src - путь к скрипту, func - функция выполняемая после загрузки скрипта
Возвращает: -
*/
	loadScript: function(src,func)
	{
		var script=document.createElement('script');
		script.src=src;
		document.getElementsByTagName('head')[0].appendChild(script);
		tool.addEvent(script,'load', func);
		tool.addEvent(script,'readystatechange',function(e){ if(this.readyState='complete') func(e); });
		if(tool.opera) func();
	},

/*
Функция: loadCss(src)
Описание: подключает css файл
Подробно: {void} tool.loadCss({string} src)
Аргументы: src - путь к скрипту
Возвращает: -
*/
	loadCss: function(src)
	{
		var css=document.createElement('link');
		css.type='text/css'; css.rel='stylesheet'; css.href=src;
		document.getElementsByTagName('head')[0].appendChild(css);
	},

/*
Функция: preloadImgs(srcs)
Описание: выполняет предзагрузку изображения/изображений
Подробно: {array} tool.preloadImgs({mixed} srcs)
Аргументы: srcs - строка или массив строк путей к изображениям
Возвращает: массив изображений
*/
	preloadImgs: function(srcs)
	{
		var res=[];
		if(typeof(srcs)=='string') srcs=[srcs];
		for(var i=0; i<srcs.length; i++)
		{
			res[i]=new Image();
			res[i].src=srcs[i];
		}
		return res;
	},

/*
Функция: childLevel(parent, elem)
Описание: возвращает уровень вложенности элемента
Подробно: {number} tool.childLevel({HTMLElement} parent, {HTMLElement} elem)
Аргументы:
	parent - элемент в котором ищется ребёнок
	elem - ребёнок
Возвращает: уровень вложенности или -1
*/	
	childLevel: function(parent, elem)
	{
		var i=0;
		while(elem && elem!=parent){ i++; elem=elem.parentNode; }
		return elem ? i : -1;
	},

/*
Функция: getElementsByClass(elem, name)
Описание: выборка элементов по имени класса
Подробно: {array} tool.getElementsByClass({HTMLElement} elem, {string} class)
Аргументы:
	elem - элемент для поиска
	class - название класса
Возвращает: массив элементов
*/
	getElementsByClass: function(elem, name)
	{
		var sub,res=[];
		for(var i=0; i<elem.childNodes.length; i++)
		{
			if(elem.childNodes[i].className && elem.childNodes[i].className.indexOf(name)!=-1) res.push(elem.childNodes[i]);
			sub=tool.getElementsByClass(elem.childNodes[i],name);
			if(sub.length>0) res=res.concat(sub);
		}
		return res;
	},

/*
Функция: getElementsByName(elem, name)
Описание: выборка элементов по имени
Подробно: {array} tool.getElementsByName({HTMLElement} elem, {string} name)
Аргументы:
	elem - элемент для поиска
	name - имя
Возвращает: массив элементов
*/
	getElementsByName: function(elem, name)
	{
		var arr=elem.getElementsByTagName('*'), res=[];
		for(var i=0; i<arr.length; i++)
			if(arr[i].name && arr[i].name==name) res.push(arr[i]);
		return res;
	}
};

/*
Свойства: opera, ie, gecko
Описание: содержат версию (в строковом виде) используемого браузера, или false
Подробно: {mixed} tool.opera; {mixed} tool.ie; {mixed} tool.gecko
*/
tool.opera=tool.getUA('Opera/');
tool.ie=tool.getUA('MSIE ');
tool.gecko=tool.getUA('Gecko/');

// добавляем .extend к стандартным классам
tool.$native(Array, Boolean, Date, Function, Math, Number, RegExp, String);





//------------------------------------------------------------------------------------------------
/*
Функции глобальной области
	setTimeout(aFunc, aDelay, aArgs) - в ie нет третьего параметра в функции setTimeout и setInterval для передачи аргументов, здесь он добавляется
	setInterval(aFunc, aPeriod, aArgs) - в ie нет третьего параметра в функции setTimeout и setInterval для передачи аргументов, здесь он добавляется
	$(id) - "укороченная" версия document.getElementById
	$$(name) - возвращает массив элементов, которые имеют свойство class="name"
	Class([parent], props) - функция для создания привычного синтаксиса классов, конструктор передаётся как init
*/
//------------------------------------------------------------------------------------------------
/*
Функция:
	window.setTimeout(aFunc, aDelay, aArgs);
	window.setInterval(aFunc, aPeriod, aArgs);
Описание: в ie нет третьего параметра в функции setTimeout и setInterval для передачи аргументов, здесь он добавляется
Подробно:
	{void} window::setTimeout({function} aFunc, {Number} aDelay[, {mixed} aArg1[, {mixed} aArg2[, ...]]])
	{void} window::setInterval({function} aFunc, {Number} aPeriod[, {mixed} aArg1[, {mixed} aArg2[, ...]]])
Аргументы: aFunc - вызываемая функция; aDelay - задержка; aPeriod - период; aArgs - аргументы для передачи aFunc
Возвращает: -
Примечание: Для исправления! Каждой функции будут передаваться один и тот же массив aValArr, будут проблемы при передаче параметров 2 и более функциям разных аргументов.
*/
if(tool.ie)
{
	fNew=function(aFunc,aDelay)
	{
		var aValArr=[];
		for(i=2; i<arguments.length; i++) aValArr.push(arguments[i]);
		callback=function(){if(aFunc) return aFunc.apply(this, aValArr);};
		return fOrig(callback,aDelay);
	};
	fOrig=window.setTimeout;
	window.setTimeout=fNew;
	fOrig=window.setInterval;
	window.setInterval=fNew;
};

/*
Функция: $(id);
Описание: "укороченная" версия document.getElementById
Подробно: {HTMLElement} $({string} id)
Аргументы: id - свойство id элемента
Возвращает: найденый элемент
*/
var $=function(id){ return(document.getElementById(id)); };

/*
Функция: $$(name)
Описание: возвращает массив элементов, которые имеют свойство class="name"
Подробно: {array} $$({string} name)
Аргументы: name - свойство class элемента
Возвращает: найденые элементы
*/
var $$=function(name,elem)
{
	if(typeof(name)=='number')
	{
		if(name>=0) for(var i=0; i<name; i++) elem=elem.firstChild;
		else for(var i=0; i>name; i--) elem=elem.parentNode;
		return [elem];
	}
	if(typeof(name)=='function') return name(elem);
	if(typeof(name)=='object') return name[0] ? name : [name];
	var arr=name.split(' '), res=[elem || document], newRes=[];
	for(var i=0; i<arr.length; i++)
	{
		for(var j=0; j<res.length; j++)
		{
			if(arr[i].charAt(0)=='@')
				newRes=newRes.merge(newRes, tool.getElementsByName(res[j],arr[i].substr(1)));
			if(arr[i].charAt(0)=='.')
				newRes=newRes.merge(newRes, tool.getElementsByClass(res[j],arr[i].substr(1)));
			else
				if(arr[i].charAt(0)=='#' && $(arr[i].substr(1)))
					newRes=newRes.merge(newRes, [$(arr[i].substr(1))]);
				else newRes=newRes.merge(newRes, res[j].getElementsByTagName(arr[i]));
		}
		res=newRes; newRes=[];
	}
	return res;
	
};

document.html=document.getElementsByTagName('HTML')[0];
document.head=document.getElementsByTagName('HEAD')[0];

/*
Функция: Class([parent], props)
Описание: функция для создания привычного синтаксиса классов, конструктор передаётся как init
Подробно: {object} Class([{object} parent], {struct} props)
Аргументы: parent - наследовать от класса; props - св-ва, методы, события класса
Возвращает: прототип класса
Примечание: задавать начальные значения полям надо в конструкторе!!! Для вызова родительского конструктора пользуйтесь this.parent().
*/
var Class=function()
{
	var len=arguments.length-1;
	if(!arguments[len].init) arguments[len].init=tool.empty;
	if(len>0)
	{
		var p=arguments[0];
		var n=arguments[1].init;
		arguments[1].init=function()
		{
			this.parent=p;
			return n.apply(this, arguments);
		};
	}
	var res=arguments[len].init;
	res.prototype = arguments[len];
	res.prototype.addEvent=function(evt, func){ tool.addEvent(this, evt, func, true); };
	res.prototype.fireEvent=function(evt, args){ tool.fireEvent(this, evt, args); };
	res.prototype.delEvent=function(evt, func){ tool.delEvent(this, evt, func, true); };
	if(len>0) tool.addProps(res.prototype, arguments[0].prototype);
	return res;
};





//------------------------------------------------------------------------------------------------
/*
Класс: Array (Расширение)
Описание: расширяет стандартный класс, добавляя некоторые возможности
Свойства: -
Методы:
	each(fn) - возвращает массив с выполненным над ним функцией
	filter(fn) - возвращает фильтрованный функцией массив
	every(fn) - проверка удовлетворения условия ВСЕМИ элементами массива
	indexOf(item[,from]) - возвращает индекс элемента item, поиск с позиции from
	copy([start][,length]) - копирование массива с позиции start, длиной length
	contains(item[,from]) - возвращает true, если элемент есть в массиве, начиная с позиции from
	remove(items) - удаляет элементы из массива
	merge(arrs) - объеденяет массивы, удаляя повторные элементы
События: -
*/
//------------------------------------------------------------------------------------------------
Array.extend({
	push: function(val)
	{
		this[this.length]=val;
	},
/*
Метод: each(fn)
Описание: выполнение функции над каждым элементом
Подробно: {array} Array::each({function ({mixed} item, {number} index)} fn)
Аргументы: fn - функция обработки, ей передаётся два параметра fn(item,index), где item - значение, а index - его номер
Возращает: массив значений, составленный из результатов переданной функции
*/
	each: function(fn)
	{
		var result=[];
		for(var i=0; i<this.length; i++) result.push(fn(this[i],i));
		return result;
	},

/*
Метод: filter(fn)
Описание: фильтрация элементов массива согласно правилу, определённому функцией fn
Подробно: {array} Array::filter({function ({mixed} item, {number} index)} fn)
Аргументы: fn - функция фильтрации, ей передаётся два параметра fn(item,index), где item - значение, а index - его номер
Возращает: массив элементов, удовлетворяющих условию
*/
	filter: function(fn)
	{
		var result=[];
		for (var i=0; i<this.length; i++) if (fn(this[i],i)) result.push(this[i]);
		return result;
	},

/*
Метод: every(fn)
Описание: возвращает true, если все элементы удовлетворяют условию, заданному функцией fn
Подробно: {boolean} Array::every({function ({mixed} item, {number} index)} fn)
Аргументы: fn - функция условия, ей передаётся два параметра fn(item,index), где item - значение, а index - его номер
Возращает: true - все элементы удовлетворяют условию, иначе false
*/
	every: function(fn)
	{
		for (var i=0; i<this.length; i++) if (!fn(this[i],i)) return false;
		return true;
	},

/*
Метод: indexOf(item[,from])
Описание: возвращает номер индекса элемента
Подробно: {number} Array::indexOf({mixed} item[, {number} from])
Аргументы: item - искомый элемент; from - позиция, откуда начать поиск, при отрицательном значении, позиция от конца массива
Возращает: индекс элемента или -1, если он не найден
*/
	indexOf: function(item, from)
	{
		var len=this.length;
		for (var i=(from<0)?Math.max(0,len+from):from||0; i<len; i++)
			if (this[i]===item) return i;
		return -1;
	},

/*
Метод: copy([start][,length])
Описание: копирует массив, lenth элементов, начиная с позиции start
Подробно: {array} Array::copy({number} start[, {number} length])
Аргументы: start - стартовая позиция, при отрицательном значении, позиция от конца массива; length - кол-во копируемых элементов
Возращает: подмассив
*/
	copy: function(start, length)
	{
		start=start||0;
		if(start<0) start+=this.length;
		length=length||(this.length-start);
		var result=[];
		for(var i=0; i<length; i++) result.push(this[start++]);
		return result;
	},

/*
Метод: contains(item[,from])
Описание: возвращает true, если элемент присутствует в массиве
Подробно: {boolean} Array::contains({mixed} item[, {number} from])
Аргументы: item - элемент для поиска; from - стартовая позиция
Возращает: true - элемент найден в массиве, иначе false
*/
	contains: function(item, from){return this.indexOf(item, from)!=-1;},

/*
Метод: remove(items)
Описание: удаляет элементы из массива
Подробно: {array} Array::remove({mixed} item1[, {mixed} item2[, ...]])
Аргументы: items - элементы для удаления
Возращает: массив с удалёнными элементами
*/
	remove: function()
	{
		var result=[], args=[].merge(arguments);
		for (var i=0; i<this.length; i++) if (!args.contains(this[i])) result.push(this[i]);
		return result;
	},

/*
Метод: merge(arrs)
Описание: объеденяет массивы, удаляя повторные элементы
Подробно: {array} Array::merge({array} arr1[, {array} arr2[, ...]])
Аргументы: arrs - массивы для добавления
Возращает: объединенный массив
*/	
	merge: function()
	{
		var result=this;
		for (var i=0; i<arguments.length; i++)
			for (var j=0; j<arguments[i].length; j++) if(!result.contains(arguments[i][j])) result.push(arguments[i][j]);
		return result;
	}
});





//------------------------------------------------------------------------------------------------
/*
Класс: String (Расширение)
Описание: расширяет стандартный класс, добавляя некоторые возможности
Свойства: -
Методы:
	toInt() - конвертирует строку в целое число
	toFloat() - конвертирует строку в дробное число
	capitalize() - делает первую букву каждого слова заглавной
	trim() - удаляет все пробельные символы в начале и в конце строки
События: -
*/
//------------------------------------------------------------------------------------------------

String.extend({
/*
Метод: toInt()
Описание: конвертирует строку в целое число
Подробно: {number} String::toInt()
Аргументы: -
Возращает: полученное число
*/	
	toInt: function(){ return parseInt(this, 10); },

/*
Метод: toFloat()
Описание: конвертирует строку в дробное число
Подробно: {number} String::toFloat()
Аргументы: -
Возращает: полученное число
*/	
	toFloat: function(){ return parseFloat(this); },

/*
Метод: capitalize()
Описание: делает первую букву каждого слова заглавной
Подробно: {string} String::capitalize()
Аргументы: -
Возращает: строку с первыми заглавными буквами в каждом слове
*/	
	capitalize: function()
	{
		return this.replace(/(^|\s)./g, function(match){return match.toUpperCase();});
	},

/*
Метод: trim()
Описание: удаляет все пробельные символы в начале и в конце строки
Подробно: {string} String::trim()
Аргументы: -
Возращает: строку с удалёнными пробельными символами в начале и конце
*/
	trim: function(){ return this.replace(/^\s+|\s+$/g, '');},

/*
Метод: escapeRegExp()
Описание: экранирует все специальные символы, для последующей подстановки строки в регулярное выражение
Подробно: {string} String::escapeRegExp()
Аргументы: -
Возращает: строку с экранированными символами
*/
	escapeRegExp: function(){ return this.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); }
});





//------------------------------------------------------------------------------------------------
/*
Класс: Timer
Описание: класс для выполнения функций через промежутки времени
Свойства:
	delay - задержка перед началом срабатывания таймера
	period - период между срабатыванием таймера
	enabled - *только для чтения* состояние таймера: true - если таймер включён, false - выключен
	value - *только для чтения* время в мс, прошедшее с момента запуска таймера
	args - массив аргументов, для передачи исполняемой функции
Методы:
	init(options) - конструктор, которому можно передать начальные опции
	start() - запускает таймер
	stop() - останавливает таймер
	toggle() - переключает состояние таймера
События:
	onTimer - при срабатывании таймера, передаются параметры Timer::args
*/
//------------------------------------------------------------------------------------------------

Timer=new Class({
/*
Свойство: delay
Описание: задержка перед началом срабатывания таймера
Подробно: {number} Timer::delay
*/
	delay: false,
	
/*
Свойство: period
Описание: период между срабатыванием таймера
Подробно: {number} Timer::period
*/
	period: false,

/*
Свойство: enabled
Описание: *только для чтения* true - если таймер включён, false - выключен
Подробно: {boolean} Timer::enabled
*/
	enabled: false,

/*
Свойство: value
Описание:
	*только для чтения* время в мс, прошедшее с момента запуска таймера
	Примечание: время дискретное, увеличивается при срабатывании таймера
Подробно: {number} Timer::value
*/
	value: 0,

/*
Свойство: args
Описание: массив аргументов, для передачи исполняемой функции
Подробно: {array} Timer::args
*/
	args: [],

/*
Событие: onTimer
Описание: при срабатывании таймера выполняется привязанная к onTimer, которой передаются параметры Timer::args
Подробно: {function} Timer::onTimer
*/
	onTimer: function(obj)
	{
		obj.value=Math.round(obj.value+obj.period);
		obj.fireEvent('timer', obj.args);
	},

/*
Свойство: $pTimer
Описание: *внутреннее* указатель на таймер, для возможности остановки
Подробно: {?} Timer::$pTimer
*/
	$pTimer: false,

/*
Конструктор: init(options)
Описание: при создании таймера, можно передать конструктору параметры
Подробно: {void} Timer::init({struct} options)
Аргументы: options - параметры, заданные в виде структуры
*/	
	init: function(options)
	{
		if(options) tool.merge(this,options);
	},

/*
Метод: $periodF()
Описание: *внутреннее* функция выполняющаяся после выполнения задержки
Подробно: {void} Timer::$periodF()
Аргументы: -
Возращает: -
*/
	$periodF: function(obj)
	{	
		obj.value+=obj.delay;
		if(obj.period) obj.$pTimer=setInterval(obj.onTimer, obj.period, obj);
		else
		{
			obj.onTimer(obj);
			obj.enabled=false;
		}
	},

/*
Метод: start()
Описание: запускает таймер
Подробно: {void} Timer::start()
Аргументы: -
Возращает: -
*/
	start: function()
	{
		if(this.enabled) return;
		this.value=0; this.enabled=true;
		if(this.delay) this.$pTimer=setTimeout(this.$periodF,this.delay,this);
		else this.$periodF(this);
	},

/*
Метод: stop()
Описание: останавливает таймер
Подробно: {void} Timer::stop()
Аргументы: -
Возращает: -
Пример: см. в описании класса
*/
	stop: function()
	{
		if(!this.enabled) return;
		this.enabled=false;
		clearTimeout(this.$pTimer);
	},

/*
Метод: toggle()
Описание: переключает состояние таймера (запускает/останавливает)
Подробно: {void} Timer::toggle()
Аргументы: -
Возращает: -
Пример: см. в описании класса
*/
	toggle: function()
	{
		if(this.enabled) this.stop();
		else this.start();
	}
});





//------------------------------------------------------------------------------------------------
/*
Класс: TFx
Описание: класс для простого создания визуальных эффектов
Конструктор: init(object) - задаёт объект при инициализации
Свойства:
	object - объект к каторому будут применяться эффекты
	acts - *только для чтения* массив действий
	execAct - *только чтение* выполняемое действие
	duration - продолжительность эффекта в мс, задавать можно только когда эффект не выполняется
	fps - кадров в секунду - количество изменений свойства объекта в секунду, по умолчанию 50
	tmr - *только чтение* таймер для выполнения эффектов
Методы:
	setActions(actions) - задаёт набор действий для объекта
	start(duration, act) - выполняет эффект для объекта, если act - строка, то выполняется действия из заданного набора, иначе выполняется это действие
	stop() - останавливает эффект
События:
	onStart(object) - при старте эффекта, в функцию передаётся текущий объект object
	onComplete(object) - при завершении эффекта, в функцию передаётся текущий объект object
	onCancel(object) - при остановке ещё не завершённого эффекта, в функцию передаётся текущий объект object
*/
//------------------------------------------------------------------------------------------------

var TFx=new Class
({
/*
Свойство: object
Описание: объект к каторому будут применяться эффекты
Подробно: {object} TFx::object
*/
	object: false,

/*
Свойство: acts
Описание: *только для чтения* массив действий
Подробно: {object} TFx::acts
*/
	acts: false,

/*
Свойство: execAct
Описание: *только чтение* выполняемое действие
Подробно: {object} TFx::execAct
*/
	execAct: false,

/*
Свойство: duration
Описание: продолжительность эффекта в мс, задавать можно только когда эффект не выполняется
Подробно: {number} TFx::duration
*/
	duration: false,

/*
Свойство: fps
Описание: кадров в секунду - количество изменений свойства объекта в секунду, по умолчанию 50
Подробно: {number} TFx::fps
*/
	fps: false,

/*
Свойство: tmr
Описание: *только чтение* таймер для выполнения эффектов
Подробно: {TTimer} TFx::tmr
*/
	tmr: false,

/*
Конструктор: init(object)
Описание: задаёт объект при инициализации
Подробно: TFx::init({object} object)
Аргументы: object - объект, для которого будут выполняться эффекты
*/
	init: function(object)
	{
		this.object=object;
		this.acts={}; this.fps=30;
		this.tmr=new Timer({ args: [this] });
		this.tmr.addEvent('timer',	function(fx)
			{
				if(fx.tmr.value>fx.duration){ fx.stop(); fx.fireEvent('complete'); return; };
				var x=fx.tmr.value/fx.duration;
				for(prop in fx.execAct)
				{
					arr=fx.execAct[prop];
					if(arr.isColor) fx.object.style[prop]=TFx.colorMorph(arr.from,arr.to,arr.func(x,arr.param));
					else fx.object.style[prop]=arr.from+(arr.to-arr.from)*arr.func(x,arr.param)+arr.unit;
				}
			});
	},

/*
Метод: setAction(act)
Описание: *внутреннее* преобразует массив в действие
Подробно: {object} TFx::setAction({object} act)
Аргументы: act - действие заданное в виде массива ([start, end, funcparam, units])
Возращает: действие заданное в виде объекта (св-ва .start; .end; .func; .param)
*/
	setAction: function(act)
	{
		var res={}, arr;
		for(prop in act)
		{
			arr=act[prop];
			res[prop]={ start: arr[0], end: arr[1], unit: arr[3]?arr[3]:'px'};
			if(tool.colorTest(arr[1])) res[prop].isColor=true;
			// разбираем функцию
			if(typeof(arr[2])=='function') res[prop].func=arr[2];
			else
			{
				if(arr[2].length==2) { res[prop].param=arr[2][1]; res[prop].func=arr[2][0]; }
				else { res[prop].param=arr[2]; res[prop].func=TFx.style.combine; };
			}
		}
		return res;
	},

/*
Метод: setActions(actions)
Описание: задаёт набор действий для объекта
Подробно: {void} TFx::setActions({object} actions)
Аргументы: actions - набор действий
Возращает: -
*/
	setActions: function(actions) { for(act in actions) this.acts[act]=this.setAction(actions[act]); },

/*
Метод: start(duration, act)
Описание: выполняет эффект для объекта, если act - строка, то выполняется действия из заданного набора, иначе выполняется это действие
Подробно: {void} TFx::start({number} duration, {mixed} act)
Аргументы: duration - продолжительность эффекта, act - действие или его название
Возращает: -
*/
	start: function(duration, act)
	{
		if(this.tmr.enabled) return;
		this.duration=duration;
		if(typeof(act)=='string') this.execAct=this.acts[act];
		else this.execAct=this.setAction(act);
		var arr;
		for(prop in this.execAct)
		{	
			arr=this.execAct[prop];
			arr.from=arr.start || this.object.style[prop];
			if(arr.from=='') arr.from=tool.getStyleProp(this.object,prop);
			if(!arr.from) arr.from=(arr.isColor)?'#ffffff':'0';
			arr.to=arr.end;
			if(arr.isColor)
			{
				arr.from=tool.parseColor(arr.from);
				arr.to=tool.parseColor(arr.to);
			}
			else
			{
				arr.from=parseFloat(arr.from) || 0;
				arr.to=parseFloat(arr.to) || 0;
			}
		}
		this.tmr.period=1000/this.fps;
		this.tmr.start();
		this.fireEvent('start');
	},

/*
Метод: stop()
Описание: останавливает эффект
Подробно: {void} TFx::stop()
Аргументы: -
Возращает: -
*/
	stop: function()
	{
		var cancel=false; if(this.tmr.enabled) cancel=true;
		this.tmr.stop();
		if(cancel) this.fireEvent('cancel');
	}
});

/*
Функция: TFx.colorMorph(c1, c2, p)
Описание: возвращает промежуточный цвет между цветами с1 и с2, в близость к c2 - p (p=0..1)
Подробно: {array[3]} TFx.colorMorph({array[3]} c1, {array[3]} c2, {number} p)
Аргументы: c1 - цвет 1; с2 - цвет 2; p - насколько цвет близок к c2 (p=0..1)
Возвращает: цвет, между c1 и c2
*/
TFx.colorMorph=function(c1,c2,p)
{
	var res='#', s;
	for(var i=0; i<c1.length; i++)
	{
		s=Math.round(c1[i]+(c2[i]-c1[i])*p).toString(16);
		if(s.length<2) s="0"+s;
		res+=s;
	}
	return res;
};

/*
Структура: TFx.style
Описание: содержит функции координата-время, для задания стиля эффектов
Подробно: {object} TFx.style
Функции:
	combine - *внутренее* для сочетания двух эффектов - в начале и в конце, использовать нельзя в качестве задания стиля
	linear(x) - обычный линейный тип, пропорциональная зависимость от времени
	circ(x) - зависимость - окружность
	pow(x, params) - зависимость - степень, в params[0] передаётся степень, по умолчанию 2
	accel(x) - ускорение - несколько замедленный старт, а далее линейно
	back(x, params) - с уходом назад, в params[0] передаётся степень ухода назад, по умолчанию PI/2
	bounce(x) - эффект прыгающего мячика
	elastic(x, params) - эффект пружины, в params[0] передаётся степень жёсткосткости пружины, по умолчанию 0
*/
TFx.style=
{
	combine: function(x, params){ if(x<0.5) return params[0](x*2,params[1])/2; else return 1-params[2](2-x*2,params[3])/2; },
	linear: function(x){ return x; },
	circ: function(x){ return 1 - Math.sin(Math.acos(x)); },
	pow: function(x, params){ return Math.pow(x, params[0] || 2); },
	accel: function(x){ return 1 - Math.sin((1 - x) * Math.PI / 2); },
	back: function(x, params){ p = params[0] || 1.618; return Math.pow(x, 2)*((p + 1)*x-p); },
	bounce: function(x){
		var value;
		for (var a = 0, b = 1; 1; a += b, b /= 2){
			if (x >= (7 - 4 * a) / 11){
				value = - Math.pow((11 - 6 * a - 11 * x) / 4, 2) + b * b;
				break;
			}
		}
		return value;
	},
	elastic: function(x, params){ return Math.pow(2,10*--x)*Math.cos(20*x*Math.PI*(params[0] || 1)/3);}	
};






//------------------------------------------------------------------------------------------------
/*
Класс: TDrag
Описание: класс для создания перетаскивающихся и изменяющих размер объектов
Конструктор:
	init(object, ctrl, sz) - Описание: при создании объекта можно указать объект, "контролирующий" объект и перемещать его или менять размеры
Свойства:
	obj - объект к каторому будут применяться перетаскивание/изменение размеров
	ctrl - *только чтение* "контролирующий" объект, за который будет применяться перетаскивание/изменение размеров объекта
	pStart и mStart - значение свойства и позиция мышки в момент нажатия кнопки мыши
	pNow и mNow - текущие значения свойства и позиции мышки в момент нажатия кнопки мыши (при перетаскивании)
	min и max - ограничение на изменение свойства, в виде структуры {{number} x, {number} y}, значениями может быть число или функция f(x), где x - текущее св-во объекта
	mod - свойства в объекте style, которыми манипулируется объект, по умолчанию {x: 'left', y: 'top'} или {x: 'width', y: 'height'} взависимости от значения sz в конструкторе
	grid - размер сетки для привязки
Методы: -
События:
	onStart(obj) - событие вызывается при старте перетаскивания, передаётся объект, который перетаскивается/изменяется
	onComplete(obj) - событие вызывается при завершении перетаскивании, передаётся объект, который перетаскивается/изменяется
	onDrag(obj) - событие вызывается при каждом изменении координат, передаётся объект, который перетаскивается/изменяется
*/
//------------------------------------------------------------------------------------------------
var TDrag=new Class(
{
/*
Свойство: obj
Описание: объект к каторому будут применяться перетаскивание/изменение размеров
Подробно: {object} TDrag::obj
*/
	obj: false,

/*
Свойство: ctrl
Описание: "контролирующий" объект, за который будет применяться перетаскивание/изменение размеров объекта
Подробно: {object} TDrag::ctrl
*/
	ctrl: false,

/*
Свойства: pStart и mStart
Описание: значение свойства и позиция мышки в момент нажатия кнопки мыши
Подробно: {number} TDrag::pStart и {number} TDrag::mStart
*/
	pStart: false, mStart: false,

/*
Свойства: pNow и mNow
Описание: текущие значения свойства и позиции мышки в момент нажатия кнопки мыши (при перетаскивании)
Подробно: {number} TDrag::pNow и {number} TDrag::mNow
*/
	pNow: false, mNow: false,

/*
Свойства: min и max
Описание: ограничение на изменение свойства, в виде структуры {{number} x, {number} y}, значениями может быть число или функция f(x), где x - текущее св-во объекта
Подробно: {struct} TDrag::min и {struct} TDrag::max
*/
	min: false, max: false,

/*
Свойство: mod
Описание: свойства в объекте style, которыми манипулируется объект, по умолчанию {x: 'left', y: 'top'} или {x: 'width', y: 'height'} взависимости от значения sz в конструкторе
Подробно: {struct} TDrag::mod
*/
	mod: false,

/*
Свойства: bound
Описание: *внутреннее* содержит функции для привязки к событиям, дабы передавались параметры и от имени другого объекта
Подробно: {struct} TDrag::bound
*/
	bound: false,

/*
Свойства: grid
Описание: размер сетки для привязки
Подробно: {struct} TDrag::grid
*/
	grid: false,

/*
Конструктор: init(object, ctrl, sz)
Описание: при создании объекта можно указать объект, "контролирующий" объект и перемещать его или менять размеры
Подробно: TDrag::init({object} object, {object} ctrl, {boolean}  sz)
Аргументы: object - объект для перемещения/изменения размеров; ctrl - "контролирующий" объект; sz - если true, то значит необходимо менять размеры обекта
*/
	init: function(object, ctrl, sz)
	{
		this.obj=object;
		this.ctrl=ctrl || this.obj;
		if(sz==true) this.mod={x: 'width', y: 'height'}; else this.mod={x: 'left', y: 'top'};
		this.pStart={x: false, y: false}; this.pNow={x: false, y: false};
		this.mStart={x: false, y: false}; this.mNow={x: false, y: false};
		this.min={x: false, y: false}; this.max={x: false, y: false};
		this.bound=
		{
			start: tool.bind(this.start, this),
			move: tool.bind(this.move, this),
			stop: tool.bind(this.stop, this)
		};
		tool.addEvent(this.ctrl,'mousedown',this.bound.start);
	},

/*
Метод: start(e)
Описание: *** внутреннее *** вызывается в начале переноса, при событии mousedown
Подробно: TDrag::start({event} e)
Аргументы: e - событие мышки
*/
	start: function(e)
	{
		this.pStart.x=parseInt(this.obj.style[this.mod.x]) || ((this.mod.x=='width') ? this.obj.offsetWidth : 0);
		this.pStart.y=parseInt(this.obj.style[this.mod.y]) || ((this.mod.y=='height') ? this.obj.offsetHeight : 0);
		this.pNow.x=this.pStart.x; this.pNow.y=this.pStart.y;
		this.mStart.x=e.clientX; this.mStart.y=e.clientY;
		tool.addEvent(document,'mousemove',this.bound.move);
		tool.addEvent(document,'mouseup',this.bound.stop);
		this.fireEvent('start',[e]);
		e.stop();
	},

/*
Метод: move(e)
Описание: *** внутреннее *** вызывается при переносе объекта, событие mousemove
Подробно: TDrag::move({event} e)
Аргументы: e - событие мышки
*/
	move: function(e)
	{
		this.mNow.x=e.clientX; this.mNow.y=e.clientY;
		var x=this.pStart.x+(this.mNow.x-this.mStart.x); var y=this.pStart.y+(this.mNow.y-this.mStart.y);
		if(this.grid) { x=Math.round(x/this.grid)*this.grid; y=Math.round(y/this.grid)*this.grid; }
		var minX=(typeof(this.min.x)=='function') ? this.min.x.apply(this, [x]) : this.min.x;
		var minY=(typeof(this.min.y)=='function') ? this.min.y.apply(this, [y]) : this.min.y;
		var maxX=(typeof(this.max.x)=='function') ? this.max.x.apply(this, [x]) : this.max.x;
		var maxY=(typeof(this.max.y)=='function') ? this.max.y.apply(this, [y]) : this.max.y;
		x=Math.max(x, minX); if(maxX) x=Math.min(x, maxX);
		y=Math.max(y, minY); if(maxY) y=Math.min(y, maxY);
		this.obj.style[this.mod.x]=x+'px'; this.obj.style[this.mod.y]=y+'px';
		this.pNow.x=x; this.pNow.y=y;
		this.fireEvent('drag',[e]);
		e.stop();
	},


/*
Метод: stop(e)
Описание: *** внутреннее** вызывается окончании переноса, событие mouseup
Подробно: TDrag::stop({event} e)
Аргументы: e - событие мышки
*/
	stop: function(e)
	{
		tool.delEvent(document,'mousemove',this.bound.move);
		tool.delEvent(document,'mouseup',this.bound.stop);
		this.fireEvent('complete',[e]);
	}
	
});





//------------------------------------------------------------------------------------------------
/*
Класс: Ajax
Описание: класс для обмена информацией с сервером посредством AJAX (не перегружая всю страницу)
Конструктор:
	init([method[, async[, url]]]) - при создании объекта можно указать свойства method, async и url
Свойства:
	method - метод "общения" с сервером, get или post, по умолчанию post
	async - при true, выполнение будет асинхронное, т. е. работа скрипта не останавливается, а обрабатывать ответ надо в событии onSuccess; по умолчанию true
	requestHeader - структура, содержащая "шапки", которые будут отправляться серверу
	responseHeader - структура, содержащая "шапки", которые пришли от сервера при ответе
	url - url страницы сервера
	running - true - если в данный момент происходит запрос
	request - данные для передачи серверу, в виде структуры {'имя': 'значение'}
	response - ответ сервера, в виде структуры {{string} text, {XMLDocument} xml}
	status - код статуса ответа
Методы:
	send(url, request) - выполнение запроса
	cancel() - отмена запроса
События:
	onRequest(obj) - вызывается перед отправкой запроса, передаётся этот экземпляр
	onSuccess(obj) - при удаче (произошёл ответ сервера кодом 2xx), передаётся этот экземпляр
	onFailure(obj) - при неудаче (произошёл ответ сервера кодом отличным от 2xx), передаётся этот экземпляр
	onCancel(obj) - вызывается при отмене запроса (при остановке методом cancel во время передачи), передаётся этот экземпляр
*/
//------------------------------------------------------------------------------------------------
var Ajax=new Class(
{

/*
Свойство: method
Описание: метод "общения" с сервером, get или post, по умолчанию post
Подробно: {string} Ajax::method
*/
	method: 'post',

/*
Свойство: async
Описание: при true, выполнение будет асинхронное, т. е. работа скрипта не останавливается, а обрабатывать ответ надо в событии onSuccess; по умолчанию true
Подробно: {boolean} Ajax::async
*/
	async: true,

/*
Свойство: requestHeader
Описание: структура, содержащая "шапки", которые будут отправляться серверу
Подробно: {struct} Ajax::requestHeaders
*/
	requestHeaders: false,

/*
Свойство: responseHeader
Описание: структура, содержащая "шапки", которые пришли от сервера при ответе
Подробно: {struct} Ajax::responseHeaders
*/
	responseHeaders: false,

/*
Свойство: url
Описание: url страницы сервера
Подробно: {string} Ajax::url
*/
	url: false,

/*
Свойство: transport
Описание: *внутреннее* содержит объект XMLHttpRequest
Подробно: {XMLHttpRequest} Ajax::transport
*/
	transport: false,

/*
Свойство: running
Описание: true - если в данный момент происходит запрос
Подробно: {boolean} Ajax::running
*/
	running: false,

/*
Свойство: request
Описание: данные для передачи серверу, в виде структуры {'имя': 'значение'}
Подробно: {struct} Ajax::request
*/
	request: false,

/*
Свойство: response
Описание: ответ сервера, в виде структуры {{string} text, {XMLDocument} xml}
Подробно: {struct} Ajax::response
*/
	response: false,

/*
Свойство: status
Описание: код статуса ответа
Подробно: {number} Ajax::status
*/
	status: false,

/*
Конструктор: init([method[, async[, url]]])
Описание: при создании объекта можно указать свойства method, async и url
Подробно: Ajax::init([{string} method[, {boolean} async[, {string} url]]])
Аргументы: method - метод, async - асинхронно, url
*/
	init: function(method, async, url)
	{
		this.transport=(window.XMLHttpRequest) ? new XMLHttpRequest() : (tool.ie ? new ActiveXObject('Microsoft.XMLHTTP') : false);
		if(!this.transport) { alert('Ошибка в создании объекта XMLHttpRequest!'); return; }
		if(arguments.length>0) this.method=method;
		if(arguments.length>1) this.async=async;
		if(arguments.length>2) this.url=url;
	},

/*
Метод: send(url, request)
Описание: выполнение запроса
Подробно: Ajax::send({string} url, {struct} request)
Аргументы: url - url страницы на сервере, request - параметры, для передачи серверу
Возвращает: -
*/
	send: function(url, request)
	{
		this.url=url; this.request=request;
		this.running=true;
		var reqStr='';
		if(typeof(request)!='string')
		{
			for(var prop in this.request) reqStr+=encodeURIComponent(prop)+'='+encodeURIComponent(this.request[prop])+'&';
			reqStr=reqStr.substr(0,reqStr.length-1);
		}
		else reqStr=request;
		var url=this.url;
		if(this.method.toUpperCase()=='GET')
		{
			if(reqStr!='') url+=((url.indexOf('?')==-1)?'?':'&')+reqStr;
			reqStr=false;
		}
		this.transport.open(this.method.toUpperCase(), url, this.async);
		if(this.method.toUpperCase()=='POST') this.transport.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
		for(var prop in this.requestHeaders) this.transport.setRequestHeader(prop, this.requestHeaders[prop]);
		this.transport.onreadystatechange = tool.bind(this.onStateChange, this);
		this.fireEvent('request');
		this.transport.send(reqStr || null);
		if(!this.async) this.onStateChange();
	},

/*
Метод: onStateChange()
Описание: *внутреннее* выполнение разбора результата после прихода ответа
Подробно: Ajax::onStateChange()
Аргументы: -
Возвращает: -
*/
	onStateChange: function()
	{
		if (this.transport.readyState != 4 || !this.running) return;
		this.running = false;
		this.status = this.transport.status;
		var arr=this.transport.getAllResponseHeaders().split('\n');
		var prop; this.responseHeaders={};
		for(var i=0; i<arr.length; i++)
		{
			prop=arr[i].match(/([^:]+):\s*(.+)\s*/);
			if(prop) this.responseHeaders[prop[1]]=prop[2];
		}
		this.response={ text: this.transport.responseText, xml: this.transport.responseXML };
		this.transport.onreadystatechange = tool.empty;
		if(this.status>=200 && this.status<300) this.fireEvent('success');
		else this.fireEvent('failure');
	},

/*
Метод: cancel()
Описание: отмена запроса
Подробно: Ajax::cancel()
Аргументы: -
Возвращает: -
*/
	cancel: function(){
		if(!this.running) return;
		this.running = false;
		this.transport.abort();
		this.fireEvent('cancel');
	}
});





//------------------------------------------------------------------------------------------------
/*
Структура: Ajax
Описание: содержит функции парсинга Ajax-ответа
Функции:
	XSLT(xml, xsl)  - возвращает элемент div с содержимым, после выполнения XSLT-трансформации xml документа с помощью xsl таблицы стилей; удобно применять когда надо просто вывести данные в html (например, для вывода таблицы базы данных)
	JSON(data) - возвращает объект, который получается парсингом data как JavaScript; очень удобно применять, если ответные данные используются для каких то вычислений (например для анализа хода противника в Ajax-игре)
	XML(root, rules[, enterSub]) - парсинг XML (HTML), путём прохождения по его DOM-структуре и вызове функций назначенных на название тега, удобно применять совместно с функцией getNodeData. Вместе позволяют с лёгкостью парсит сложные случаи. За идею спасибо Zloboglazу. ;)
	getNodeData(node[, enterSub]) - функция для парсинга XML (HTML), обычно применяется совместно с Ajax.XML. Она обходит все дочерние элементы node и возвращает структуру, созданную из тегов имеющих текстовое значение. При указании enterSub выполняться будет рекурсивно.
*/
//------------------------------------------------------------------------------------------------

/*
Функция: Ajax.XSLT(xml, xsl)
Описание: возвращает элемент div с содержимым, после выполнения XSLT-трансформации xml документа с помощью xsl таблицы стилей; удобно применять когда надо просто вывести данные в html (например, для вывода таблицы базы данных)
Подробно: {HTMLDivElement} Ajax.XSLT({XMLDocument} xml, {XMLDocument} xsl)
Аргументы: xml - xml документ для трансформации; xsl - xsl таблица преобразования
Возвращает: элемент div с содержимым трансформации
*/
Ajax.XSLT=function(xml, xsl)
{
	var res=document.createElement('div');
	if(tool.ie) res.innerHTML=xml.transformNode(xsl);
	else
	{
		var xsltProcessor = new XSLTProcessor();
		xsltProcessor.importStylesheet(xsl);
		var trans=xsltProcessor.transformToDocument(xml).firstChild;
		for(var i=0; i<trans.childNodes.length; i++) res.appendChild(trans.childNodes[i].cloneNode(true));
	}
	return res;
};

/*
Функция: Ajax.JSON(data)
Описание: возвращает объект, который получается парсингом data как JavaScript
Подробно: {mixed} Ajax.JSON({string} data)
Аргументы: data - строка, содержащая данные
Возвращает: объект (структуру, массив, ...), рассматривая data как JavaScript
*/
Ajax.JSON=function(data) { return eval('('+data+')'); };

/*
Функция: Ajax.XML(root, rules[, enterSub])
Описание: парсинг XML (HTML), путём прохождения по его DOM-структуре и вызове функций назначенных на название тега, удобно применять совместно с функцией getNodeData. Вместе позволяют с лёгкостью парсит сложные случаи. За идею спасибо Zloboglazу. ;)
Подробно: {void} Ajax.XML({Element} root, {struct} rules, {boolean} enterSub)
Аргументы:
	root - элемент для анализа
	rules - набор правил, формируется следующим образом. название правила должно совпадать с именем тега. Если правило - функция, то она будет вызываться ПЕРЕД заходом в дочерние элементы. Если функция возвращает false, то заход в дочерние элементы не будет выполняться (если не указан enterSub), иначе будут анализироваться и доерние элементы. Если же указать правило, как структура {{function} before, {function} after}, то при выполнении сначала вызовется 'правило'.before, потом осуществится прохождение по дочерним элементам (в любом случае), затем вызовется 'правило'.after. Можно указать правило 'otherwise' которое будет выполняться всякий раз, когда попадётся тег, для которого не описано правил.
	enterSub - булевское значение, если указан true, то заход в дочерние элементы будет выполняться в любом случае, даже если правило возвращает false
Возвращает: -
*/
Ajax.XML=function(root, rules, enterSub)
{
	rules['otherwise']=rules['otherwise'] || tool.empty;
	var c = root.childNodes;
	var tag, f;
	for (var i = 0; i < c.length; i++)
	{
		if (c[i].nodeType != 1) continue;
		tag=c[i].tagName.toLowerCase();
		f=rules[tag] || rules['otherwise'];
		if(typeof(f)=='function') { if(f(c[i]) || enterSub) Ajax.XML(c[i], rules); }
		else { f.before(c[i]); Ajax.XML(c[i], rules); f.after(c[i]); }
	}
};

/*
Функция: Ajax.getNodeData(node[, enterSub])
Описание: функция для парсинга XML (HTML), обычно применяется совместно с Ajax.XML. Она обходит все дочерние элементы node и возвращает структуру, созданную из тегов имеющих текстовое значение. При указании enterSub выполняться будет рекурсивно.
Подробно: {struct} Ajax.getNodeData({Element} node, {boolean} enterSub)
Аргументы: node - элемент для анализа; enterSub - рекурсивное выполнение
Возвращает: структуру, содержащим данные из node
*/
Ajax.getNodeData=function(node, enterSub)
{
	var c=node.childNodes;
	var res={}, tag;
	for (var i = 0; i < c.length; i++)
	{
		if (c[i].nodeType != 1) continue;
		tag=c[i].tagName.toLowerCase();
		if(c[i].childNodes.length==1)
		{
			if(typeof(res[tag])!='undefined')
			{
				if(typeof(res[tag]=='string')) res[tag]=[res[tag]];
				res[tag].push(c[i].textContent || c[i].text);
			}
			else res[tag]=c[i].textContent || c[i].text;
		}
		else
			if(enterSub) res[tag]=Ajax.getNodeData(c[i],enterSub);
	}
	return res;
};





//------------------------------------------------------------------------------------------------
/*
Структура: Coookie
Описание: функции для работы с Cookie
Функции:
	
*/
//------------------------------------------------------------------------------------------------

Cookie={
/*
Функция: set(key, value[, options])
Описание: устанавливает cookie, ключ key в значение value. В options можно передать дополнительные параметры в виде структуры
Подробно: {void} Cookie.set({string} key, {string} value[, {struct} options])
Аргументы:
	key, value - ключ и значение, допускается, но не рекомендуется использовать национальные раскладки (т. к. они больше займут места);
	options - можно передать дополнительные параметры: expires - сколько секунд хранить cookie (если не указан, то будет храниться до закрытия браузера); path, domain, secure - путь, домен и безопасность кукиса
Возвращает: -
*/
	set: function(key, value, options)
	{
		var str=encodeURIComponent(key)+'='+encodeURIComponent(value);
		options=options || {};
		if(options.expires)
		{
			var date = new Date();
			date.setTime(date.getTime() + options.expires*1000);
			str+='; expires=' + date.toGMTString();
		}
		if(options.path) str+='; path=' + options.path;
		if(options.domain) str+='; domain=' + options.domain;
		if(options.secure) str += '; secure';
		document.cookie = str;
	},

/*
Функция: get(key)
Описание: возвращает значение key из Cookie
Подробно: {string} Cookie.get({string} key)
Аргументы: key - название ключа
Возвращает: значение ключа
*/
	get: function(key)
	{
		var value = document.cookie.match('(?:^|;)\\s*'+encodeURIComponent(key).escapeRegExp()+'=([^;]*)');
		return value ? decodeURIComponent(value[1].replace(/\+/g,'%20')) : false;
	},

/*
Функция: del(key)
Описание: удаляет cookie
Подробно: {void} Cookie.del({string} key)
Аргументы: key - название ключа
Возвращает: -
*/
	del: function(key){ Cookie.set(key,'',{expires: -1}); }
};

