/***** Модуль поддержки перемещающихся панелей. ********************************
Проверено в браузерах: IE 6, IE 8, Opera 9.64, FireFox 3.5.8, Safari 4.0.5.
Документация располагается в отдельном файле.
Автор: Нагаев Дмитрий (callidus@yandex.ru, ICQ: 228-460-108).
*******************************************************************************/

var movPanels = new Array();    // Все области с панелями.
var MOV_PANELS_BRACKET_POS = 5; // Положение края кронштейна в пикселях.
var MOV_PANELS_INTERVAL = 50;   // Интервал анимации в миллисекундах.

// Коэффициент упругости от 0 до 1 включительно.
// 0 - панели не упруги, 1 - панели максимально упруги.
var movPanelsPE = 1.0;
var movPanelsBE = 1.0; // упругость кронштейнов

var movPanelsA = -100.0;   // Ускорение (для учёта сил сопротивления).
var movPanelsMaxV = 300.0; // Максимальная скорость.
var movPanelsMinV = 0.5;   // Минимальная скорость.
var movPanelsTimer = null; // хэндл таймера

var movPanelsCaptured = null;
var movPanelsPosX = 0;

/** Инициализация конкретного DIV.MovPanels.
 * На входе ссылка на DIV-элемент для инициализации.
 * Возвращает true если успешно и false если нет.
 *
 * Для работы понадобятся DIV.Left, DIV.Right, DIV.Panel,
 * A.Catcher, SPAN.HookLeft, SPAN.HookRight.
 **/
function movPanelsInitDIV(div)
{
	if ((typeof(div) == 'undefined') || (typeof(div.firstChild) == 'undefined') || (typeof(div.firstChild.firstChild) == 'undefined')) return false;

	panels = new Object();

	container = div.firstChild.firstChild;
	if ((container.nodeType != 1) || (container.nodeName != 'DIV') || (container.className != 'Container') || (container.children.length < 3)) return false;

	elem = container.firstChild;
	container = undefined;
	leftElem = null;
	rightElem = null;
	centerElem = null;
	while (elem != undefined)
	{
		if ((elem.nodeType == 1) && (elem.nodeName == 'DIV'))
		{
			switch (elem.className) {
			case 'Left': leftElem = elem; break;
			case 'Right': rightElem = elem; break;
			case 'Center': centerElem = elem; break;
			};
		};
		elem = elem.nextSibling;
	};
	if ((leftElem == null) || (rightElem == null) || (centerElem == null)) return false;

	panels.leftElem = leftElem;
	panels.rightElem = rightElem;

	panels.panels = new Array();
	elem = centerElem.firstChild;
	while (elem != undefined)
	{
		if ((elem.nodeType == 1) && (elem.nodeName == 'DIV') && (elem.className == 'Panel') && (elem.children.length > 0))
		{
			elem2 = elem.firstChild;
			while (elem2 != undefined)
			{
				if ((elem2.nodeType == 1) && (elem2.nodeName == 'A') && (elem2.className == 'Catcher'))
				{
					// нужно найти крючки
					spans = elem2.getElementsByTagName('SPAN');
					hook_left = null;
					hook_right = null;
					for (si = 0; si < spans.length; si++) switch (spans[si].className) {
					case 'HookLeft': hook_left = spans[si]; break;
					case 'HookRight': hook_right = spans[si]; break;
					};
					spans = undefined;
					if ((hook_left != null) && (hook_right != null))
					{
						ni = panels.panels.length;
						panels.panels[ni] = new Object();
						panels.panels[ni].elem = elem;
						panels.panels[ni].catcher = elem2;
						panels.panels[ni].panels = panels;
						panels.panels[ni].leftHookElem = hook_left;
						panels.panels[ni].rightHookElem = hook_right;
						panels.panels[ni].index = ni;
						panels.panels[ni].v = 0.0;
						break;
					};
					hook_left = undefined;
					hook_right = undefined;
				};
				elem2 = elem2.nextSibling;
			};
		};
		elem = elem.nextSibling;
	};
	if (panels.panels.length == 0)
	{
		panels = undefined;
		return false;
	};

	// ### 2 ### Связывание и настройка элементов. ###
	for (j = 0; j < panels.panels.length; j++)
	{
		panels.panels[j].catcher.panel = panels.panels[j];
		panels.panels[j].catcher.onmousedown = function(e) { return movPanels_MouseDown(e); };
		panels.panels[j].catcher.onselectstart = function(e) { return false; };
		panels.panels[j].catcher.style.cursor = 'w-resize';
	};

	// ### * ### Выход ###
	movPanelsApplyBounds(panels);
	panels.index = movPanels.length;
	movPanels[panels.index] = panels;
	now = new Date();
	panels.prevFrame = now.getTime();
	now = undefined;
	panels = undefined;

	return true;
};

/** Инициализация всех DIV.MovPanels. **/
function movPanelsInitAll()
{
	divs = document.getElementsByTagName('DIV');
	for (i = 0; i < divs.length; i++) if (divs[i].className == 'MovPanels') movPanelsInitDIV(divs[i]);
	if (movPanels.length == 0) return false;

	body = document.getElementsByTagName('BODY')[0];
	body.onmousemove = function(e) { return movPanels_MouseMove(e); };
	if (+"\v1")
	{ // не IE
		window.onmouseup = function(e) { return movPanels_MouseUp(e); };
	} else {
		body.onmouseup = function(e) { return movPanels_MouseUp(e); };
	};
	movPanelsTimer = setInterval('movPanelsAnimateAll();', MOV_PANELS_INTERVAL);

	return true;
};

function movPanels_GetMouseX(e)
{
	if (e.pageX) return e.pageX;
	if (e.clientX) return e.clientX
			+ (document.documentElement.scrollLeft || document.body.scrollLeft)
			- document.documentElement.clientLeft;
	return 0;
};

/** Обработчик onMouseDown для панели. **/
function movPanels_MouseDown(e)
{
	elem = (e == undefined ? event.srcElement : e.target);
	if (!e) e = event;
	while (elem != null)
	{
		if ((elem.nodeType == 1) && (elem.nodeName == 'A') && (elem.className == 'Catcher')) break;
		elem = elem.parentNode;
	};
	if ((elem == null) || (elem.panel == undefined)) return false;

	movPanelsCaptured = elem.panel;
	movPanelsPosX = movPanels_GetMouseX(e);
	try { elem.setCapture(); } catch(ex) { ; };
	return false;
};

/** Обработчик onMouseMove для <BODY>. **/
function movPanels_MouseMove(e)
{
	if (movPanelsCaptured == null) return;
	if (!e) e = event;
	x = movPanels_GetMouseX(e);
	dx = x - movPanelsPosX;
	movPanelsCaptured.v += dx;
	if (movPanelsCaptured.v > 0)
	{
		if (movPanelsCaptured.v > movPanelsMaxV) movPanelsCaptured.v = movPanelsMaxV;
	} else {
		if (movPanelsCaptured.v < -movPanelsMaxV) movPanelsCaptured.v = -movPanelsMaxV;
	};
	movPanelsPosX = x;
};

/** Обработчик onMouseUp для <BODY>. **/
function movPanels_MouseUp(e)
{
	if (movPanelsCaptured == null) return;
	elem = (e == undefined ? event.srcElement : e.target);
	try { movPanelsCaptured.catcher.releaseCapture(); } catch(ex) { ; };
	movPanelsCaptured = null;
};

/** Обработчик onMouseUp для <BODY>. **/
function movPanels_OnResize()
{
	for (p = 0; p < movPanels.length; p++) movPanelsApplyBounds(movPanels[p]);
};

/** "Принимает" новые габариты системы.
 * Заодно поправляет положения панелей если они ушли за допустимые пределы.
 * Вызывать в самом начале, после ресайза окна и при смене габаритов панелей.
 **/
function movPanelsApplyBounds(panels)
{
	// Создаём се ссылки чтобы короче код был.
	pans = panels.panels;
	last = pans[pans.length-1];

	// min_x и max_x считаем по кронштейнам и крюкам крайних панелей.
	panels.min_x = panels.leftElem.offsetLeft
		+ panels.leftElem.offsetWidth - MOV_PANELS_BRACKET_POS
		- pans[0].leftHookElem.offsetLeft;

	panels.max_x = panels.rightElem.offsetLeft + MOV_PANELS_BRACKET_POS
		- last.rightHookElem.offsetLeft - last.rightHookElem.offsetWidth;

	// Обновляем длины панелей.
	total_w = 0;
	for (pi = 0; pi < pans.length; pi++)
	{
		pans[pi].x = pans[pi].elem.offsetLeft;
		pans[pi].w = pans[pi].elem.offsetWidth;
		total_w += pans[pi].w;
	};
	panels.blocked = (total_w - last.w >= panels.max_x - panels.min_x);

	mx = panels.max_x + last.w;
	for (pi = pans.length-1; pi >= 0; pi--)
	{
		mx -= pans[pi].w;
		pans[pi].max_x = mx;
	};
	mx = undefined;

	//// Терь нужно посмотреть не заходит ли какая панель за пределы.
	//// Сначала с кронштейнами разбираемся, а потом смотрим нет ли нахлёстов.

	// С заблокированными отдельный разговор - сбросить скоростя и выстроить в ряд.
	if (panels.blocked)
	{
		x = panels.min_x;
		for (pi = 0; pi < pans.length; pi++)
		{
			pans[pi].x = x;
			pans[pi].elem.style.left = Math.floor(pans[pi].x)+'px';
			pans[pi].v = 0.0;
			x = pans[pi].x + pans[pi].w
		};
		return;
	};

	if (pans[0].x < panels.min_x)
	{
		pans[0].x = panels.min_x;
		pans[0].elem.style.left = Math.floor(pans[0].x)+'px';
	};

	for (pi = 1; pi < pans.length; pi++)
	{
		ppi = pi-1;
		if (pans[pi].x < pans[ppi].x + pans[ppi].w)
		{
			pans[pi].x = pans[ppi].x + pans[ppi].w;
			pans[pi].elem.style.left = Math.floor(pans[pi].x)+'px';
		};
	};

	if (last.x > panels.max_x)
	{
		last.x = panels.max_x;
		last.elem.style.left = Math.floor(last.x)+'px';
		for (pi = pans.length-1; pi > 0; pi--)
		{
			ppi = pi-1;
			if (pans[ppi].x > pans[pi].x - pans[ppi].w)
			{
				pans[ppi].x = pans[pi].x - pans[ppi].w;
				pans[ppi].elem.style.left = Math.floor(pans[ppi].x)+'px';
			};
		};
	};
};

/** Производит анимацию набора панелей. **/
function movPanelsAnim(panels)
{
	now = new Date();
	t = (now.getTime() - panels.prevFrame) / 1000;
	panels.prevFrame = now.getTime();
	now = undefined;

	pans = panels.panels;
	accel = movPanelsA;

	while (t > 0.0)
	{
		// Необходимо найти ближайшее событие.
		// Для этого ищем тех что движутся и смотрим столкновения.
		leftTc = null;   // Время столкновения с левым краем.
		rightTc = null;  // Время столкновения с правым краем.
		centerTc = null; // Время самого раннего столновения панелей.

		 // Столкновение с левым кронштейном.
		pi = 0;
		if (pans[pi].v < 0.0)
		{
			// дискриминант
			d = (pans[pi].v*pans[pi].v + 2 * accel * (pans[pi].x - panels.min_x));
			if (d > 0)
			{
				tc = (pans[pi].v+Math.sqrt(d))/accel;
				if ((tc >= 0.0) && (tc <= t)) leftTc = tc;
			};
		};

		// Столкновение с правым кронштейном.
		pi = pans.length-1;
		if (pans[pi].v > 0.0)
		{
			// дискриминант
			d = (pans[pi].v*pans[pi].v - 2 * accel * (pans[pi].x - panels.max_x));
			if (d > 0)
			{
				tc = (-pans[pi].v+Math.sqrt(d))/accel;
				if ((tc >= 0.0) && (tc <= t)) rightTc = tc;
			};
		};

		// Столкновение между панелями.
		// Первую можно пропустить, так как она может столкнуться только
		// с кронштейном. Поэтому просчёт начинаем со второй.
		for (pi = 1; pi < pans.length; pi++)
		{
			ppi = pi-1;
			if (pans[ppi].v <= pans[pi].v) continue;

			dv = pans[ppi].v - pans[pi].v;
			da = (pans[ppi].v > 0 ? accel : -accel) - (pans[pi].v > 0 ? accel : -accel);
			dx = pans[ppi].x + pans[ppi].w - pans[pi].x;
			if (da == 0.0)
			{
				tc = -dx / dv;
			} else {
				d = dv*dv - 2*da*dx;
				if (d <= 0) continue;
				tc = (-dv+Math.sqrt(d))/da;
			};

			if ((tc == null) || (tc > t)) continue;
			// Смотрим не является ли это событие ранним.
			if ((centerTc == null) || (tc < centerTc))
			{
				centerTc = tc;
				centerI = ppi;
			};
		};

		// Определение самого раннего события.
		minK = 0;
		if (leftTc != null)
		{
			minTc = leftTc;
			minK = 1;
		} else {
			minTc = null;
		};
		if ((rightTc != null) && ((minTc == null) || (rightTc < minTc)))
		{
			minTc = rightTc;
			minK = 2;
		};
		if ((centerTc != null) && ((minTc == null) || (centerTc < minTc)))
		{
			minTc = centerTc;
			minK = 3;
		};

		// Нужно "перемотать" систему на время minTc.
		// Сначала нужно обработать столкновение,
		// а потом сдвинуть остальные панели.
		if (minTc == null) minTc = t;

		// Пометим те, которые нужно перетащить.
		// С тех что ударяются снимем эти метки и сдвинем потом те,
		// у которых осталось nra = false.
		for (pi = 0; pi < pans.length; pi++) pans[pi].nra = false;

		// Обработка ударов. Про них написано в отдельной документации.
		switch (minK) {
		case 1: // левый удар
			pi = 0;
			pans[pi].v = pans[pi].v - accel*minTc;
			if (pans[pi].v > -movPanelsMinV)
			{
				pans[pi].v = 0.0;
			} else {
				pans[pi].v = -pans[pi].v * movPanelsBE;
			};
			pans[pi].x = panels.min_x;
			pans[pi].elem.style.left = Math.floor(pans[pi].x)+'px';
			pans[pi].nra = false; // эту панель сдвигать уже не надо
			break;
		case 2: // правый удар
			pi = pans.length-1;
			pans[pi].v = pans[pi].v + accel*minTc;
			if (pans[pi].v < movPanelsMinV)
			{
				pans[pi].v = 0.0;
			} else {
				pans[pi].v = -pans[pi].v * movPanelsBE;
			};
			pans[pi].x = panels.max_x;
			pans[pi].elem.style.left = Math.floor(pans[pi].x)+'px';
			pans[pi].nra = false; // эту панель сдвигать уже не надо
			break;
		case 3: // столкновение панелей
			pi = centerI;
			ppi = pi + 1;

			// Выставляет сначала левую, а потом сразу за ней правую.
			// Кроме этого нужно пересчитать скоростя.
			// Сначала получаем высчитываем какие будут скорости
			// перед самим столкновением с учётом ускорения,
			// а потом нужно узнать скорости сразу после столкновения
			// уже с учётом коэффициента упругости.

			// Считаем скорости и позиции перед ударом.
			v = pans[pi].v;
			if (v > 0.0)
			{
				pans[pi].v = pans[pi].v + accel*minTc;
				if (pans[pi].v < movPanelsMinV)
				{
					pans[pi].x = pans[pi].x - (v*v)/(2*accel);
					pans[pi].v = 0.0;
				} else {
					pans[pi].x = pans[pi].x + v*minTc + (accel*minTc*minTc)/2;
				};
				if (pans[pi].x > pans[pi].max_x) pans[pi].x = pans[pi].max_x;
			} else {
				pans[pi].v = pans[pi].v - accel*minTc;
				if (pans[pi].v > -movPanelsMinV)
				{
					pans[pi].x = pans[pi].x + (v*v)/(2*accel);
					pans[pi].v = 0.0;
				} else {
					pans[pi].x = pans[pi].x + v*minTc - (accel*minTc*minTc)/2;
				};
			};
			// Выставляем на новое положение.
			pans[pi].elem.style.left = Math.floor(pans[pi].x)+'px';
			pans[ppi].x = pans[pi].x + pans[pi].w;
			pans[ppi].elem.style.left = Math.floor(pans[ppi].x)+'px';

			// Расчитываем скорости после удара.
			mv = (pans[pi].v + pans[ppi].v) / 2;
			v = (pans[ppi].v - mv) * movPanelsPE + mv;
			pans[ppi].v = (pans[pi].v - mv) * movPanelsPE + mv;
			pans[pi].v = v;

			// Эти панели уже сдвигать не надо.
			pans[pi].nra = true;
			pans[ppi].nra = true;
			break;
		};

		// Двиагаем остальные панели.
		for (pi = 0; pi < pans.length; pi++)
		{
			if (pans[pi].nra || (pans[pi].v == 0.0)) continue;
			// Заодно нам нужно будет пересчитать скорости.
			v = pans[pi].v;
			if (v > 0.0)
			{
				pans[pi].v = pans[pi].v + accel * minTc;
				if (pans[pi].v < movPanelsMinV)
				{
					pans[pi].v = 0.0;
					pans[pi].x = pans[pi].x - (v*v) / (accel*2);
				} else {
					pans[pi].x = pans[pi].x + v * minTc + (accel * minTc * minTc) / 2;
				};
			} else {
				pans[pi].v = pans[pi].v - accel * minTc;
				if (pans[pi].v > -movPanelsMinV)
				{
					pans[pi].v = 0.0;
					pans[pi].x = pans[pi].x + (v*v) / (accel*2);
				} else {
					pans[pi].x = pans[pi].x + v * minTc - (accel * minTc * minTc) / 2;
				};
			};
			pans[pi].elem.style.left = Math.floor(pans[pi].x)+'px';
		};

		t = t - minTc;
	};
};

/** Производит анимацию всех наборов панелей. **/
function movPanelsAnimateAll()
{
	for (ai = 0; ai < movPanels.length; ai++) movPanelsAnim(movPanels[ai]);
};