///////////////////////////////////////////////////////////////////////////////
//
//	TUNA FRAMEWORK
//
//	Файл tuna.js
//
//	Author Kononenko Sergey <kononenheg@gmail.com>
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//	Область имен.
//////////////////////////////////////////////////////////////////////////////

var tuna;
if (!tuna) tuna = {};

///////////////////////////////////////////////////////////////////////////////
//
//	Статические методы и переменные области имен tuna.
//
///////////////////////////////////////////////////////////////////////////////

/**
 * Версия библиотеки.
 *
 * @type String
 * @static
 */
tuna.VERSION = '2.0.1';

/**
 * Адрес PHP сценария компоновки JavaScript файлов.
 *
 * @type String
 * @static
 */
tuna.PHP = 'tuna.php';

/**
 * Корневая директория импорта JavaScript классов для PHP сценария компоновки.
 *
 * @type String
 * @static
 */
tuna.PHP_ROOT = 'js';

/**
 * Корневая директория импорта JavaScript классов для цепной последовательной
 * загрузки.
 *
 * @type String
 * @static
 */
tuna.JS_ROOT = 'js';

/**
 * Функция импорта скомпонованного кода.
 * 
 * Загружает JavaScript файлы требуемых классов, а также файлы
 * всех классов связанных с требуемыми. Загрузка уже скомпонованного файла.
 *
 * @param {Array} classes Набор полных имен классов.
 */
tuna.includeMerged = function(classes)
{
	//Запрашиваем сервис компоновки скрипта.
	tuna.__loadAndEval
		(tuna.PHP + "?c[]=" + classes.join("&c[]=") + "&r=" + tuna.PHP_ROOT);
}

/**
 * Функция загрузки кода одного класса
 *
 * Загружает и выполняет код одного JavaScript класса.
 * Лучше использовать версию includeMerged, так как в данной верчии возможны
 * зацикливания.
 *
 * Но (!) важно использовать этот для обоззначения зависимостей.
 *
 * TODO: Убрать зацикливания.
 *
 * @deprecated
 * @param {String} className Полное имя требуемого класса.
 */
tuna.include = function (className)
{
	var nameHash = className.split(".");

	//Проверяем на объевленность класса.
	var nameScope = window;
	for (var key in nameHash)
	{
		nameScope = nameScope[nameHash[key]];

		//Если какой либо области имен нет - возвращаем.
		if (!nameScope) break;
	}

	//Если класса нет - загружаем его.
	if (!nameScope)
		tuna.__loadAndEval(tuna.JS_ROOT + "/" + nameHash.join("/") + ".js");
}

/**
 * Функция загрузки и выполнения кода.
 *
 * @param {String} url Адрес JavaScript сценария.
 */
tuna.__loadAndEval = function(url)
{
	//TODO: Реализовать проверкку возможностей браузера в самом начале.
       
	//Переменная запроса
	var request = (window.ActiveXObject == null) ?
		new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

	try
	{
		//Синхронно запрашиваем скрипт
		request.open("GET", url, false);
		request.send(null);
	}
	catch (error)
	{
		alert("Ошибка отсылки запроса (" + url + "): " + 702);
	}

	//Если ответ не успешен - ошибка с возвратом.
	if (request.status != 200 && request.status != 304)
	{
		alert("Ошибка " + request.status +
			" HTTP запроса JavaScript сценария: " + url, 401);
		return;
	}

	try
	{
		tuna.__eval(request.responseText)
	}
	catch (error)
	{
		alert("Ошибка выполнения загруженного скрипта: " +
			request.responseText, 704);
	}

	//Очищаем переменную запроса
	request = null;
}

/**
 * Функция выполнения строки JavaScript кода в глобальной области имен.
 *
 * @param {String} code Строка кода.
 */
tuna.__eval = function(code)
{
	return (window.execScript != null) ?
		 window.execScript(code) : window.eval(code);
}

/**
 * Добавление слушателя события DOM модели.
 *
 * @param {Element} element Элемент слушатель события которого необходимо
 * установить.
 * @param {String} type Тип события.
 * @param {Function} handler Обработчик события.
 * @param {String} lambdaIndex Индекс лямбда функции, при повторной записи на
 * один индекс возможны утечки памяти.
 */
tuna.addEventListener = function(element, type, handler, lambdaIndex)
{
	if (element.addEventListener)
		element.addEventListener(type, handler, false);
	else if (element.attachEvent)
		element.attachEvent("on" + type, handler);

	//Костыль для замыканий
	if (lambdaIndex != null)
	{
		if (element.__listeners == null) element.__listeners = {};
		element.__listeners[lambdaIndex] = handler;
	}
}

/**
 * Удаление слушателя события DOM модели.
 *
 * @param {Element} element Элемент слушатель события которого необходимо
 * удалить.
 * @param {String} type Тип события.
 * @param {Function} handlerOrLambdaIndex Обработчик события либо индекс
 * лямбда функции.
 */
tuna.removeEventListener = function(element, type, handlerOrLambdaIndex)
{
	var handler;

	if (typeof handlerOrLambdaIndex == 'string')
	{
		if (element.__listeners == null)
			alert("Лямбда функция не зарегистрирована: " + 710);

		handler = element.__listeners[handlerOrLambdaIndex];
		element.__listeners[handlerOrLambdaIndex] = null;
	}
	else handler = handlerOrLambdaIndex;

	if (element.removeEventListener)
		element.removeEventListener(type, handler, false);
	else if (element.detachEvent)
		element.detachEvent("on" + type, handler);

	handler = null;
}

/**
 * Определение индекса узла в родительском.
 *
 * @param {Element} node Узел индекс которрого требуется найти.
 * @return {Number} Индекс узла (-1 если родительского узла нет).
 */
tuna.getChildIndex = function(node)
{
	var result = -1;

	if (node.parentNode)
	{
		var child = node.parentNode.firstChild;

		while (child)
		{
			result++;

			if (child == node)
				break;

			child = child.nextSibling;
		}

		child = null;
	}

	return result;
}

/**
 * Возвращение дочернего узла по идентификатору
 *
 * @param {Element} parent Узел, дочерний элемент которого требуется.
 * @param {Number} index Индекс узла.
 * @return {Element} Наденный узел.
 */
tuna.getChildAt = function(parent, index)
{
	if (parent.childNodes.length - 1 < index)
		return null;
	
	return parent.childNodes[index];
}