/**
 * @include  "d:/www/_common/js/js_common.js"
 */

/**
 * глобальный объект для работы с Ajax'ом
 * @type ajax_sender
 */
var oAjax = new ajax_sender();

/**
 * ссылка на оьъект XmlHttpRequest
 * (вдруг еще кому-то понадобится)
 * @type {Element}
 */
var xmlHttp = oAjax.createXmlHttpRequestObject();

/**
 * имя JS-объекта который приходит в ответе с сервера
 * @type String
 */
var pcResponseVarname = 'ajax_response_object';

/**
 * ответ сервера в виде JS-объекта
 * @type Object
 */
var ajax_response_object = null;

/**
 * если TRUE - то при обработке ответа сервера будут генерироваться ошибки
 * @type Boolean
 */
var plDebugRun = true;

/**
 * проверяет ответ сервера - пытается запустить обработчик
 * 
 * @return void
 */
function xmlHttp_handle_ServerResponse()
{
    if (xmlHttp.readyState == 4) 	//	продолжаем толькое если выполнен запрос
    {
        if (xmlHttp.status == 200)	//	продолжаем только если сервер ответил 200
        {
            oAjax.responseText = xmlHttp.responseText;
            oAjax.responseXML = xmlHttp.responseXML;

            xmlHttp_Make_ResponseObj();

            oAjax.anim_hide();

			if (plDebugRun) oAjax.responser();
			else
			{
	            try { oAjax.responser(); }	// пытаемся обработать ответ сервера
				catch (e) { alert(oAjax.msg_ResponseProcessError); return false; }
			}
        }
        else
		{
			oAjax.anim_hide();
			alert(oAjax.msg_GrantError + xmlHttp.status+ "\n" + xmlHttp.statusText);
			return false;
		}
    }
	return true;
}

/**
 * @class
 * класс для запросов к серверу и обработки ответов на них
 */
function ajax_sender()
{
	/**
	 * ссылка на объект XMLHttpRequest
	 * @type HTMLElement
	 */
    this.xmlHTTP = null;

	/**
	 * метод (POST или GET) которым отправляются данные
	 * по умолчанию POST
	 * @type String
	 */
    this.method = 'POST';

    /**
     * @var function
     * ссылка на пользовательскую функцию, которая будет обрабатывать ответ сервера
	 */
    this.responser = null;

	/**
	 * ответ сервера в виде текста
	 * @type String
	 */
    this.responseText = '';
    
	/**
	 * ответ сервера в виде XML (не используется)
	 * @type String
	 * @deprecated
	 */
    this.responseXML = '';

	/**
	 * @var Object
	 * ответ сервера в виде объекта (если таковой был)
	 */
	this.responseObj = {};

	/**
	 * @type Array
	 * @private 
	 */
    this.XmlHttpVersions = new Array("MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP");

	/**
	 * @type String
	 * @private
	 */
    this.msg_CantMakeXmlHTTP = 'Ошибка создания объекта XMLHttpRequest!\nЗапросы к серверу невозможны.';

	/**
	 * @type String
	 * @private
	 */
    this.msg_UseOpera = '\nПопробуйте включить в своем браузере JavaScript или смените свой браузер на более новый (например Opera 9 - www.opera.com)';

	/**
	 * @type String
	 * @private
	 */
    this.msg_CantConnect = 'Загрузить данные невозможно, т.к. отсутствует возможность связаться с сервером\n';

	/**
	 * @type String
	 * @private
	 */
    this.msg_GrantError = "Ошибка при доступе к серверу:\n";

	/**
	 * @type String
	 * @private
	 */
    this.msg_ResponseProcessError = 'Ошибка при обработке ответа сервера';

    /**
     * ID элемента-контейнера, в котором будет показываться анимация
     * @type String
     */
    this.anim_shell = 'upmeter_shell';

    /**
     * текст сообщения который будет демонстироваться с анимацией
     * @type String
     */
    this.anim_text = 'Идет загрузка. Подождите...';

    /**
     * ширина в Px элемента-контейнера, в котором будет показываться анимация
     * @type Number
     */
    this.anim_w = 240;

    /**
     * высота в Px элемента-контейнера, в котором будет показываться анимация
     * @type Number
     */
    this.anim_h = 120;
    
	/**
	 * 
	 * @type HTMLElement
	 */
    var oTarget = null;

	/**
	 * @public
	 * запускает запрос к серверу
	 * 
	 * @param {String} tcUrl - адрес скрипта, который будет отвечать нам
	 * @param {Function} tfFunction - ссылка на функцию-обработчик
	 * @param {String[]/String} taFields - или массив с ID полей формы, или ID формы (из которой будут отправлены все поля), или строка типа txtValue=1
	 * @param {Boolean} tlAnim - если TRUE - то показывать анимацию пока ждем ответа от сервера
	 * @return void
	 */
    this.start = function(tcUrl, tfFunction, taFields, tlAnim)
    {
        if (xmlHttp.readyState==4  ||  xmlHttp.readyState==0)	//	пробуем если сервер не занят
        {
            var lcPOSTFields = '';
            //  проверяем является ли строка (если это строка) спиской уже готовых параметров или ссылкой на объект
            if ((taFields.constructor == String)  &&  (!document.getElementById(taFields))) lcPOSTFields = taFields;
            else lcPOSTFields = this.makePost(taFields);

            try //	пробуем обратиться к серверу
            {
          		this.responser = tfFunction;
         		xmlHttp.open(this.method, tcUrl, true);
         		xmlHttp.onreadystatechange = xmlHttp_handle_ServerResponse;
          		xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
         		xmlHttp.send(lcPOSTFields);
                if (tlAnim) this.anim_show();
            }
            catch(e) { alert(this.msg_CantConnect +'\n'+ e.toString()); }
     	}
	}

	/**
	 * @public
	 * отправляет запрос к серверу
	 * после получения ответа - обновляет содержимое элемента toTarget
	 * 
	 * @param {String} tcUrl - адрес скрипта, который будет отвечать нам
	 * @param {HTMLElement} toTarget - ссылка на элемент, который будет обновляться или его ID
	 * @param {String[]/String} taFields - или массив с ID полей формы, или ID формы (из которой будут отправлены все поля), или строка типа txtValue=1
	 * @param {Boolean} tlAnim - если TRUE - то показывать анимацию пока ждем ответа от сервера
	 * @return void
	 */
	this.update = function(tcUrl, toTarget, taFields, tlAnim)
	{
        if (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)	//	пробуем если сервер не занят
        {
            var lcPOSTFields = '';
            //  проверяем является ли строка (если это строка) спиской уже готовых параметров или ссылкой на объект
            if ((taFields.constructor == String)  &&  (!id(taFields))) lcPOSTFields = taFields;
            else lcPOSTFields = this.makePost(taFields);

            try //	пробуем обратиться к серверу
            {
            	oTarget = (toTarget.constructor == String) ? id(toTarget) : toTarget;
          		this.responser = _ajaqsUpdate;
         		xmlHttp.open(this.method, tcUrl, true);
         		xmlHttp.onreadystatechange = xmlHttp_handle_ServerResponse;
          		xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
         		xmlHttp.send(lcPOSTFields);
                if (tlAnim) this.anim_show();
            }
            catch(e) { alert(this.msg_CantConnect +'\n'+ e.toString()); }
     	}
	}

	/**
	 * @private
	 * получив ответ - обновляет элемент oTarget
	 * 
	 * @return void
	 */
	function _ajaqsUpdate()
	{
		if (this.responseObj  &&  this.responseObj.result!='Ok')
		{
			alert(this.responseObj.error);
			return true;
		}

		if (this.responseText != '') oTarget.innerHTML = this.responseText;

		return true;
	}

	/**
	 * @public
	 * получает ответ от сервера через IFrame
	 * 
	 * @param {HTMLIFrameElement} toIFrame - ссылка на IFrame, в который принимали ответ
	 * @return void
	 */
	this.formComplete = function(toIFrame)
	{
		this.anim_hide();	//	убираем анимацию
		var loDoc = toIFrame.contentDocument;

		if (!loDoc && toIFrame.contentWindow) loDoc = toIFrame.contentWindow.document;
		if (!loDoc) loDoc = window.frames[toIFrame.id].document;
		if (!loDoc) return null;
		if (loDoc.location == "about:blank") return null;
	
		if (loDoc.XMLDocument) loDoc = loDoc.XMLDocument;

		this.responseText = loDoc.body.innerHTML;
//		try	{ console.log(this.responseText);} catch (e) { }

		try	{ eval(this.responseText); }
		catch (e) { ajax_response_object = null; }
		this.responseObj = ajax_response_object;

		if (this.responseObj)
	    {
			for (var i in this.responseObj) this.responseObj[i] = win2unicode(unescape(this.responseObj[i]));
	    }

//	    try	{ console.log(this.responseObj);} catch (e) { }
	    
	    return true;
	}

    /**
     * @public
     * кроссбраузерно создает объект XMLHttpRequest и возвращает ссылку на него
     *
     * @return {HTMLElement} - объект XMLHttpRequest
     */
    this.createXmlHttpRequestObject = function()
    {
    	var xmlHttp = null;

    	try { xmlHttp = new XMLHttpRequest(); }
    	catch(e)
    	{
    		for (var i = 0; i < this.XmlHttpVersions.length && !xmlHttp; i++)
    		{
    			try { xmlHttp = new ActiveXObject(this.XmlHttpVersions[i]); }
    			catch (e) {};	//	пустой обработчик исключения
    		}
    	}

    	if (!xmlHttp)
    	{
    		alert(this.msg_CantMakeXmlHTTP + this.msg_UseOpera);
    		this.xmlHTTP = false;
    	}
    	else this.xmlHTTP = xmlHttp;

    	return this.xmlHTTP;
    }

	/**
	 * @private
	 * формирует из полей формы строку запроса, которую будем отправлять на сервер
	 *
	 * @param {String[]/String} taFields - ID формы, или массив с ID элементов формы
	 * @return string - строка запроса готовая к отправке на сервер
	 */
    this.makePost = function(taFields)
    {
        var laFields = null;
        var laSend = new Array();
        var lcType = typeof (taFields);
		var lcVal  = '';

		if (taFields.constructor == Array)    //  получен массив с ID элементов формы
        {
            for (var i=0; i < taFields.length; i++)
            {
                var lcName = taFields[i];
				var loObj = id(lcName);
				if (!loObj)
				{
					alert('Элемент с ID ' + lcName + ' не существует');
					laSend[i] = lcName + '=';
				}
				else laSend[i] = _get_element(loObj);
            }
        }

        if (taFields.constructor == String)  //  получили строку
        {
            var loObj = id(taFields);
            if (loObj.tagName == 'FORM') //  это ID формы - значит будем отправлять всю форму
            {
                var loElements = loObj.elements;
                for (var i=0; i < loElements.length; i++)
                {
					laSend[i] = _get_element(loElements[i]);
                }
            }
            else    //  ID одного элемента формы
            {
                var loObj = id(taFields);
                laSend[0] = _get_element(loObj);
            }
        }

        return laSend.join('&');
    }

    /**
     * @private
     * возвращает пару строку вида txtValue=1 для элемента формы 
     * 
     * @param {HTMLElement} toObj - ссылка на элеиент формы
     * @return {String} - строка вида txtValue=1
     */
    function _get_element(toObj)
    {
    	var lcRet = '';
    	var lcName = toObj.id  ||  toObj.name;

		if (toObj.tagName=='INPUT'  &&  toObj.type=='checkbox') var lcVal = ((toObj.checked) ? "1" : "0");
        else var lcVal = _escape1251(toObj.value);

		return lcName+ '='+ lcVal;
    }

	/**
	 * @private
	 * правильно делает Escape для кириллицы
	 *
	 * @param {String} str - строчка, которую нужно "отискейпить"
	 * @return {String} - "отискейпеная" строка
	 */
    function _escape1251(str)
    {
    	if (!str  ||  str == '') return '';
        // Инициализируем таблицу перевода
        var trans = new Array();
        for (var i = 0x410; i <= 0x44F; i++)  trans[i] = i - 0x350; // А-Яа-я
        trans[0x401] = 0xA8;    // Ё
        trans[0x451] = 0xB8;    // ё

//	TGA - добавлено для поддержки украинского языка
        trans[0x490] = 0xA5;    // Ґ
        trans[0x491] = 0xB4;    // ґ

        trans[0x404] = 0xAA;    // Є
        trans[0x454] = 0xBA;    // є

        trans[0x406] = 0xB2;    // І
        trans[0x456] = 0xB3;    // і

        trans[0x407] = 0xAF;    // Ї
        trans[0x457] = 0xBF;    // ї

        var ret = [];
        // Составляем массив кодов символов, попутно переводим кириллицу
        for (var i = 0; i < str.length; i++)
        {
            var n = str.charCodeAt(i);
            if (typeof trans[n] != 'undefined') n = trans[n];
            if (n <= 0xFF) ret.push(n);
        }
        return escape(String.fromCharCode.apply(null, ret));
    }

	/**
	 * @public
	 * показывает анимацию загрузки
	 * 
	 * @return void
	 */
    this.anim_show = function()
    {
        var loUpmeter = id(this.anim_shell);

        if (!loUpmeter)
        {
            loUpmeter = dce('div');
            loUpmeter.id = this.anim_shell;

            var loUpText = dce('p');
            loUpText.innerHTML = this.anim_text;

            loUpmeter.appendChild(loUpText);
            dbac(loUpmeter);

            var loUpmeter = id(this.anim_shell);
        }

        // вертикальное позиционирование контейнера посредине окна
		try { var lnTop = calcPWindowY(this.anim_h); }
		catch (e) { var lnTop = 200; }
        // горизонтальное позиционирование контейнера посредине окна
		try { var lnLeft = calcPWindowX(this.anim_w); }
		catch (e) { var lnLeft = 200; }

        loUpmeter.style.left = lnLeft + 'px';
        loUpmeter.style.top  = lnTop + 'px';
        loUpmeter.style.width  = this.anim_w + 'px';
        loUpmeter.style.height  = this.anim_h  + 'px';
        loUpmeter.style.display = 'block';
        loUpmeter.style.zIndex = 300;
        loUpmeter.onclick = function() { hide(loUpmeter);};
    }

	/**
	 * @public
	 * скрывает анимацию загрузки
	 * 
	 * @return
	 */
    this.anim_hide = function()
    {
        hide(this.anim_shell);
    }

	/**
	 * выводит алерт c ответом сервера
	 * 
	 * @return void
	 */
    this.alert = function()
    {
    	if (this.responseObj) alert('responseObj \n' + getProps(this.responseObj));
    	else alert(this.responseText);
    }
}

/**
 * пытается преобразовать ответ сервера в JS-объект
 * 
 * @return {Object} - ответ сервера в виде JS-объекта
 */
function xmlHttp_Make_ResponseObj()
{
	if(oAjax.responseText.search(pcResponseVarname) > 0)
	{
//		console.log(oAjax.responseText);
	    if (plDebugRun)	//	отладочный вариант - если будет ошибка ее будет видно в консоли
	    {
	        eval(oAjax.responseText);

			oAjax.responseObj = (ajax_response_object) ? ajax_response_object : null;
	    }
	    else				//	боевой вариант - при ошибке будет исключение
	    {
			try
			{
				eval(oAjax.responseText);
				oAjax.responseObj = ajax_response_object;
			}
			catch (e) { ajax_response_object = null; }
	    }

	    if (oAjax.responseObj)
	    {
			for (var i in oAjax.responseObj) oAjax.responseObj[i] = win2unicode(unescape(oAjax.responseObj[i]));
	    }
	}
	else oAjax.responseObj = ajax_response_object = null;

    return oAjax.responseObj;
}

/**
 * обрабатывает ответ сервера после загрузки формы в IFrame
 * 
 * @param {HTMLElement} toIFrame - ссылка на IFrame в который загружалась форма
 * @return void
 */
function handle_FormResponse(toIFrame)
{
	oAjax.anim_hide();	//	убираем анимацию
	var loDoc = toIFrame.contentDocument;

	if (!loDoc && toIFrame.contentWindow) loDoc = toIFrame.contentWindow.document;
	if (!loDoc) loDoc = window.frames[toIFrame.id].document;
	if (!loDoc) return null;
	if (loDoc.location == "about:blank") return null;

	if (loDoc.XMLDocument) loDoc = loDoc.XMLDocument;
	oAjax.responseText = loDoc.body.innerHTML;

	xmlHttp_Make_ResponseObj();

	return true;
}


