eventHandler = function (e)
{
	e = e ? e : window.event;
	if (this.obj)
	{
		if (this.obj.blockEvent)
		{
			e = e ? e : window.event;
			if (e.stopPropagation) e.stopPropagation();
			else e.cancelBubble = true;
			if (e.preventDefault) e.preventDefault();
			else e.returnValue = false;
		}
		if (e)
		{
			this.obj.event = e;
			this.handler();
		}
	}
}

timeoutHandler = function (elem)
{
	return function ()
	{
		elem.timeoutHandler();
	}
}

/************************************************************
Добавление новых функций для элемента
$ - поиск элемента по id
getByClassName - поиск по названию класса
getByClassNameAndTagName - поиск по названию класса и названию тэга
hideElem - скрытие элемента немедленный
showElem - показ элемента немедленный
toggleElem - скрытие показ в зависимости от текущего состояния
************************************************************/

// TODO вынести fade в отдельную структуру, хайд и шоу в отдельную, сделать мейкер EE
ExtendElem = function (hideClass, enableFade, minFadeOpacity, maxFadeOpacity, fadeShots)
{
	this.hiddenClassName = hideClass.split('|')[0];
	this.visibleClassName = hideClass.split('|')[1];

	this.enableFade = enableFade;
	this.minFadeOpacity = minFadeOpacity ? minFadeOpacity : 0;
	this.maxFadeOpacity = maxFadeOpacity ? maxFadeOpacity : 1;
	this.fadeShots = fadeShots ? fadeShots : 15;

	this.allElems = document.getElementsByTagName('*');

	this.$ = function (id)
	{
		return document.getElementById(id) || false;
	}

	this.setOpacityFunction = function ()
	{
		if (!window.opera && !navigator.product)
		{
			return function (value)
			{
				value *= 100;
				this.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity=' + value + ')';
				return true;
			}
		}
		else
		{
			return function (value)
			{
				this.style.opacity = value;
				return true;
			}
		}
	}

	this.getByClassName = function (className, parent)
	{
		var parent = parent ? parent : this.allElems;
		var result = new Array();
		for (var i = 0; i < parent.length; i++)
		{
			if (parent[i].className.search(className) > -1)
			{
				result.push(parent[i]);
			}
		}
		return result;
	}

	this.getByClassNameAndTagName = function (className, tagName, parent)
	{
		var parent = parent ? parent : this.allElems;
		var result = new Array();
		var parent = this.getByClassName(className, parent);
		for (var i = 0; i < parent.length; i++)
		{
			if (parent[i].tagName == tagName)
			{
				result.push(parent[i]);
			}
		}
		return result;
	}

	this.checkStatus = function ()
	{
		return this.className.search(this.ee.visibleClassName) > -1;
	}

	this.hideElem = function ()
	{
		this.className = this.className.replace(this.ee.visibleClassName, this.ee.hiddenClassName);
		return true;
	}

	this.showElem = function ()
	{
		this.className = this.className.replace(this.ee.hiddenClassName, this.ee.visibleClassName);
		if (this.focusElem)
		{
			this.focusElem.focus();
		}
		return true;
	}

	this.toggleElem = function ()
	{
		this.checkStatus() ? this.hide() : this.show();
		return true;
	}

	this.fadeToggleElem = function ()
	{
		this.checkStatus() ? this.fadeOut() : this.fadeIn();
		return true;
	}


	this.fadeIn = function ()
	{
		clearTimeout(this.fadeTimeout);
		this.timeoutHandler = this.fadeIn;
		this.show();
		if (!this.fadeOpacity)
		{
			this.fadeOpacity = this.ee.minFadeOpacity;
		}
		if (this.fadeOpacity >= this.ee.maxFadeOpacity)
		{
			this.fadeOpacity = this.ee.maxFadeOpacity
			this.setOpacity(this.fadeOpacity);
			// done!
		}
		else
		{
			this.fadeOpacity += (this.ee.maxFadeOpacity - this.ee.minFadeOpacity) / this.ee.fadeShots;
			this.setOpacity(this.fadeOpacity);
			this.fadeTimeout = setTimeout(timeoutHandler(this), 40);
		}
	}

	this.fadeOut = function ()
	{
		clearTimeout(this.fadeTimeout);
		this.timeoutHandler = this.fadeOut;
		if (!this.fadeOpacity)
		{
			this.fadeOpacity = this.ee.maxFadeOpacity;
		}
		if (this.fadeOpacity <= this.ee.minFadeOpacity)
		{
			this.fadeOpacity = this.ee.minFadeOpacity
			this.setOpacity(this.fadeOpacity);
			this.hide();
			// done!
		}
		else
		{
			this.fadeOpacity -= (this.ee.maxFadeOpacity - this.ee.minFadeOpacity) / this.ee.fadeShots;
			this.setOpacity(this.fadeOpacity);
			this.fadeTimeout = setTimeout(timeoutHandler(this), 40);
		}
	}

	this.add = function (elem)
	{
		elem.ee = this;

		if (this.enableFade)
		{
			elem.setOpacity = this.setOpacityFunction();
			elem.fadeIn = this.fadeIn;
			elem.fadeOut = this.fadeOut;
			elem.fadeToggle = this.fadeToggleElem;
		}

		elem.hide = this.hideElem;
		elem.show = this.showElem;
		elem.toggle = this.toggleElem;

		elem.checkStatus = this.checkStatus;
	}
}

/************************************************************
Выключатель с двумя и более активными элементами.
Включатель и выключатель раздельные.
Выключателей и включателей может быть какой угодно количество, активный элемент только один.

Парамметры:
blockClassName - имя класса открываемого блока (уникальное или берется первый найденный)
openClassName - имя класса всех открывашек
closeClassName - имя класса для все закрывашек
hideClass - названия классов для скрытия и показа блока в формате: скрыт|показан
openAsClose - открывашка при повторном клике закроет
closeAsOpen - закрывашка при повторном клике откроет
************************************************************/
BlockToggler = function (blockClassName, openClassName, closeClassName, hideClass, openAsClose, closeAsOpen, blockEvent)
{
	this.blockClassName = blockClassName;
	this.blockElem = new Array();

	this.openClassName = openClassName;
	this.openElems = new Array();

	this.closeClassName = closeClassName;
	this.closeElems = new Array();

	this.event = false;

	this.openAsClose = openAsClose;
	this.closeAsOpen = closeAsOpen;

	this.ee = new ExtendElem(hideClass, true);

	this.blockEvent = blockEvent;

	this.openBlock = function ()
	{
		this.obj.blockElem.show();
	}

	this.toggleBlock = function ()
	{
		this.obj.blockElem.fadeToggle();
	}

	this.closeBlock = function ()
	{
		this.obj.blockElem.hide();
	}

	this.prepare = function ()
	{
		if (this.blockClassName)
		{
			this.blockElem = this.ee.getByClassName(this.blockClassName, false);
		}
		if (this.openClassName)
		{
			this.openElems = this.ee.getByClassName(this.openClassName, false);
		}
		if (this.closeClassName)
		{
			this.closeElems = this.ee.getByClassName(this.closeClassName, false);
		}

		if (this.blockElem.length)
		{
			/* берем первый элемент */
			this.blockElem = this.blockElem[0];
			this.blockElem.obj = this;
			this.ee.add(this.blockElem);

			for (var i = 0; i < this.openElems.length; i++)
			{
				this.openElems[i].obj = this;
				if (this.openAsClose)
				{
					this.openElems[i].handler = this.toggleBlock;
				}
				else
				{
					this.openElems[i].handler = this.openBlock;
				}
				this.openElems[i].onclick = eventHandler;
			}
			for (var i = 0; i < this.closeElems.length; i++)
			{
				this.closeElems[i].obj = this;
				if (this.closeAsOpen)
				{
					this.closeElems[i].handler = this.toggleBlock;
				}
				else
				{
					this.closeElems[i].handler = this.closeBlock;
				}
				this.closeElems[i].onclick = eventHandler;
			}
		}
	}
	this.prepare();
}

/***************************************************************
example:
classNameRule = 'PrefixRoleId'
or:
classNameRule = 'RolePrefixId'
prefix - та часть которая всех объединяет
role - роль элемента, например toggler или block (переключатель или переключаемый блок)
id - та часть которая объединяет переключатель и блок
***************************************************************/
radioToggler = function (prefix, activeElemRole, passiveElemRole, classNameRule, hideClass, openAsClose, blockEvent, fade)
{
	this.ee = new ExtendElem(hideClass, fade);
	this.fade = fade;

	this.prefix = prefix;
	this.activeElemRole = activeElemRole;
	this.passiveElemRole = passiveElemRole;
	this.classNameRule = classNameRule;

	this.openAsClose = openAsClose;

	this.activeElems = new Array();
	this.passiveElems = new Array();

	this.blockEvent = blockEvent;

	this.makeClassName = function (active, id)
	{
		var role = active ? this.activeElemRole : this.passiveElemRole;
		return this.classNameRule.replace('Prefix', this.prefix).replace('Role', role).replace('Id', id);
	}

	this.getElemId = function (elem)
	{
		var classes = elem.className.split(' ');
		for (var i = 0; i < classes.length; i++)
		{
			if (classes[i].search(this.prefix) > -1)
			{
				var className = classes[i];
				if (classes[i].search(this.activeElemRole) > -1)
				{
					var role = this.activeElemRole;
					break;
				}
				else (classes[i].search(this.passiveElemRole) > -1)
				{
					var role = this.passiveElemRole;
					break;
				}
			}
		}
		return className.replace(this.prefix, '').replace(role, '');
	}

	// FIXME при быстром переключении туда-обратно срабатывает fadeOut вместо fadeIn и все элементы скрываются
	this.toggleElem = function (elem, className, fade)
	{
		// проверяем на сходство с текущим переключателем для активизации
		var show = 0;
		var classes = elem.className.split(' ');
		for (var i = 0; i < classes.length; i++)
		{
			if (className == classes[i])
			{
				show = 1;
			}
		}
		if (show)
		{
			// Смотрим активен ли сейчас текущий элемент
			if (elem.checkStatus())
			{
				// если элемент и так показан, то при наличие openAsClose его нужно скрыть
				if (this.openAsClose)
				{
					elem.hide();
				}
			}
			// не активен - показываем
			else
			{
				fade ? elem.fadeIn() : elem.show();
			}
		}
		// не совпал, прячем если активен
		else
		{
			fade ? elem.fadeOut() : elem.hide();
		}
	}

	this.toggleRadio = function ()
	{
		var id = this.obj.getElemId(this);
		var activeClassName = this.obj.makeClassName(true, id);
		var passiveClassName = this.obj.makeClassName(false, id);
		for (var i = 0; i < this.obj.activeElems.length; i++)
		{
			this.obj.toggleElem(this.obj.activeElems[i], activeClassName);
		}
		for (var i = 0; i < this.obj.passiveElems.length; i++)
		{
			this.obj.toggleElem(this.obj.passiveElems[i], passiveClassName, this.obj.fade);
		}
	}

	this.prepare = function ()
	{
		var activeClassName = this.makeClassName(true, '');
		var passiveClassName = this.makeClassName(false, '');

		this.activeElems = this.ee.getByClassName(activeClassName);
		this.passiveElems = this.ee.getByClassName(passiveClassName);

		for (var i = 0; i < this.activeElems.length; i++)
		{
			this.activeElems[i].obj = this;
			this.ee.add(this.activeElems[i]);

			this.activeElems[i].handler = this.toggleRadio;

			this.activeElems[i].onclick = eventHandler;
		}
		for (var i = 0; i < this.passiveElems.length; i++)
		{
			this.passiveElems[i].obj = this;
			this.ee.add(this.passiveElems[i]);
		}
	}
	this.prepare();
}
