/** 
 * @fileoverview
 *
 * <h3>AJAX & AHAH library for multithreaded loading of a web pages and other data </h3> 
 *
 * @name Fullajax
 *
 * @version LINKER SiRusAjaX - SRAX v1.0 rc3 build 21 <br><br>
 *
 * <a href="http://www.fullajax.ru">http://www.fullajax.ru</a> 
 *
 * <br><br>  
 *  
 * AJAX и AHAH библиотека для мультипоточной загрузки веб странииц и других данных <br><br>
 * 
 * Функциональные фозможности: <br>
 * 1) мультипоточность <br>
 * 2) полная поддержка GET и POST запросов <br>
 * 3) раздельная история для каждого потока загрузки веб страниц <br>
 * 4) парсинг и выполнение скриптов, линков и стилей подгружаемых в контенте веб страниц <br>
 * 5) аплоад (загрузка на сервер) файлов без перезагрузки <br>
 * 6) препроцессор ответа запроса данных <br>
 * 7) история для кнопок "Назад" и "Вперед" <br>
 * 8) прямые сылки на AJAX запросы веб страниц <br>
 * 9) авто-фильтр якорей - автоматическое преобразование ссылок в АЯКС <br>
 * 10) триггер контента  <br>
 * 11) обработчик TITLE <br>
 * 12) ловушка для document.write для каждого потока <br>
 * 13) ускоритель загрузки скриптов - паралельная их загрузка с последовательным применением <br>
 * 14) список поддерживаемых счетчиков : Google Analytics, Rating@Mail.ru <br>
 * 15) поддержка событий onload & onunload тега <body> для каждого потока <br>
 * 16) исправлен баг в Опере при использовании setTimeout & setInterval во время Назад/Вперед AJAX навигации по истории<br>
 * 17) две модели запроса HTML сраниц - один-к-одному и один-ко-многим
 * 18) возможность включения и отключения коррекции относительных (релятивных) путей (href и src)
 * 19) захват и отработка события window.onload
 * 20) кросс-браузерное проигрывание звуковых файлов
 * 
 * <br><br> 
 * 
 * Библиотека протестирована на Firefox 2.0+, Internet Explorer 6.0+, Opera 9.0+, Safari 3.0+ <br> 
 *  
 * <br><br> 
 *  
 *  
 * @author Руслан Синицкий <a href="mailto:si-rus@list.ru">si-rus@list.ru</a>. Авторские права защищены (c) 2007-2008. Используйте данную библиотеку в соответствии с лицензией. <br>
 *  
 * <br><br> 
 *
 * Если вы хотите что-либо добавить или отредактировать в данной библиотеке, предварительно свяжитесь с автором <a href="mailto:si-rus@list.ru">si-rus@list.ru</a>!
 *  
 *   
 * <br><br><br>
 * 
 * Advantages:<br>
 * 1) multithread <br>
 * 2) full support GET and POST request <br> 
 * 3) separate history for each thread (for loading of a web pages) <br>
 * 4) parsing and execution of script, link and style tags from loaded content <br>
 * 5) upload file without reload <br>
 * 6) preprocessor data response <br>
 * 7) history for "Back" & "Forward" buttons <br>
 * 8) direct link for AJAX request of HTML <br>
 * 9) auto-filter of anchors - automatic trasformation to AJAX <br>
 * 10) content trigger <br>
 * 11) TITLE processing <br>
 * 12) trap for document.write for every thread <br>
 * 13) accelerate download of scripts - parallel download with serial apply <br>
 * 14) list of support counters : Google Analytics, Rating@Mail.ru <br>
 * 15) support event onload & onunload of tag <body> for every thread <br>
 * 16) Opera bug fixed where setTimeout & setInterval in use Back/Forward AJAX history<br>
 * 17) two models request of HTML pages - one-to-one & one-to-many
 * 18) on & off option for correction relative path (href & src)
 * 19) capture & execute window.onload event
 * 20) cross-browser playing of sound files
 *  
 * <br><br>
 * 
 * Library tested with Firefox 2.0+, Internet Explorer 6.0+, Opera 9.0+, Safari 3.0+ <br>
 *  
 * <br><br> 
 *  
 * @author Ruslan Sinitskiy <a href="mailto:si-rus@list.ru">si-rus@list.ru</a>. Copyright (c) 2007-2008. All rights reserved. Use is library to license terms. <br>
 *  
 * <br><br> 
 *
 * IF YOU WISH TO ADD TO OR EDIT THIS LIBRARY CONTACT <a href="mailto:si-rus@list.ru">si-rus@list.ru</a> FIRST! <br>
 *  
 * 
 *
 */

if (!window.SRAX || (window.SRAX.TYPE != 'full')){

/**
* Функция логирования
* @param {Any} any значение
*/
function log(){
    if (window.console && window.console.log) console.log(arguments);
}

function info(){
    if (window.console && window.console.info) console.info(arguments);
}

function error(){
    if (window.console && window.console.error) console.error(arguments);
}

function warn(){
    if (window.console && window.console.warn) console.warn(arguments);
}

/**
* Список XMLHTTP ActiveXObject движков
*/
var IE_ENGINE = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'];

/**
* Функция поиска элемента по его id
* @param {String} idElem id элемента
* @return {Element} найденный элемент
*/
function id(idElem){
    return document.getElementById(idElem);
}

/**
* Функция для програмной навигации по истории Назад 
* @param {String} id id родительского элемента
*/
function back(id) {
  if (!id) id = SRAX.Default.mainLayer;
  SRAX.Html.thread[id].go(-1);
}

/**
* Функция для програмной навигации  по истории Вперед
* @param {String} id id родительского элемента
*/
function forward(id) {
  if (!id) id = SRAX.Default.mainLayer;
  SRAX.Html.thread[id].go(1);
}

/**
* Функция для програмной навигации по истории на заданное количество шагов 
* @param {String} val количесвто шагов Вперед(+)/Назад(-)
* @param {String} id id элемента
*/
function go(val, id) {
  if (!id) id = SRAX.Default.mainLayer;
  SRAX.Html.thread[id].go(val);
}


/**
* Функция обрезания крайних пробелов и переводов строки
* @return {String} строка
*/
String.prototype.trim = function(){ return this.replace(/\s*((\S+\s*)*)/, "$1").replace(/((\s*\S+)*)\s*/, "$1");}

/**
* Функция замены всех найденных заменяемых значений
* @param {String} s1 искомое заменяемое значение
* @param {String} s2 новое значение
* @return {String} строка
*/
String.prototype.replaceAll = function(s1, s2) {return this.split(s1).join(s2)}

/**
* Функция проверки заканчиваеться ли строка на указанное значение
* @param {String} value указанное значение
* @param {Boolean} caseSensitive если == true не чуствительна к регистру
* @return {Boolean} результат проверки
*/
String.prototype.endWith=function(value, caseSensitive){
    if (caseSensitive) {
        if (this.toLowerCase().substring(this.length-value.length,this.length)==value.toLowerCase()) return true;
    } else {
        if (this.substring(this.length-value.length,this.length)==value) return true;
    }
    return false;
}
      
/**
* Функция проверки начинаеться ли строка с указанного значения
* @param {String} value указанное значение
* @param {Boolean} caseSensitive если == true не чуствительна к регистру
* @return {Boolean} результат проверки
*/
String.prototype.startWith=function(str, caseSensitive){
    if (caseSensitive) {
        if (this.toLowerCase().substring(0,str.length)==str.toLowerCase()) return true;
    } else { 
        if (this.substring(0,str.length)==str) return true;
    }
    return false;
}

/**
* Функция поиска индекса элемента в указанном массиве
* @param {Array} arr массив
* @param {String/Element/Function/Object/Any} el элемент
* @param {Integer} start индекс начала поиска
* @return {Integer} индекс элемента (= -1 если не найден)
*/
function arrayIndexOf(arr, el, start){
    var ind = -1;
    for(var i = (start || 0); i < arr.length; i++){
        if(arr[i] == el) {
            ind = i;
            break;
        }
    }
    return ind;
}

/**
* Функция удаления элемента в указанном массиве
* @param {Array} arr массив
* @param {String/Element/Function/Object/Any} el элемент
* @return {Array} новый массив
*/
function arrayRemoveOf(arr, el){
  for(i = 0; i < arr.length; i++) if (el == arr[i]) arr.splice(i, 1);
  return arr;
}


/**
* Функция для прерывания запроса HTML
* @param {String} id запроса
*/ 
function abort(id){
    if (SRAX.Html.thread[id]) SRAX.Html.thread[id].abort();
}

/**
* Функция для запроса HTML
* @param {String} url URL адрес запроса
* @param {Object} options объект конфигурации <br> пример: {callback:myfunction, id:'myid', method:'post', form:'id-from'} <br><br>
* 
* Возможные параметры options: <br>
* id - id родительского элемента <br>
* method - метода запроса данных post или get (по умолчанию) <br>
* form - id формы, сама форма, id элемента или сам элемент, с которого необходимо собрать параметры <br>
* params - строка параметров, которые необходимо включиють в запрос (name=val1&name=val2) <br>
* callback - функция обратного вызова <br>
* callbackOps - опции, которые передаються в функцию обратного вызова <br>
* nohistory (noHistory)- флаг использования контейнера истории, по умолчанию false - т.е. история включена <br>
* cut - id блока вырезаемого блока - используеться для вырезания на сервере из общего контента блока с указанным id - данный параметр передается в хеадере запроса AJAX_CUT_BLOCK <br>
* rc - использовать (true) или не использовать (false - по умолчанию) коррекцию относительных ссылок <br>
* overwrite - флаг перезаписи заменяемых событий true или false (по умолчанию - функции на событиях onclick и onsubmit - не перезаписываются, а сохраняются)<br>
* destroy - флаг авто удаления процесса после окончания запроса <br>
* url - URL адрес запроса (при использовании синтаксиса hax(options)) <br>
* html - HTML текст, емуляция запроса-ответа, при наличии данного параметра запрос HTML c сервер не осуществляется <br>
* anticache - флаг антикеширования true или false (по умолчанию) <br>
* startpage - признак первой страницы истории true или false (по умолчанию) <br>
* async - флаг выполнения асинхронного запроса true (по умолчанию) или false <br>
* historycache - флаг использования кеша истории true или false (по умолчанию) <br>
* seal - флаг так называемой изоляции true или false (по умолчанию) - используется для решения конфликтов стилей
* 
* @return {HTMLThread} объект процесса запроса HTML
*/
function hax(url, options){
    if (!options) options = {};
    if (typeof url == 'string') options.url = url; else options = url;    
    if (options.nohistory == null) options.nohistory = options.noHistory;
    var thread = SRAX.Html.thread[options.id] ? SRAX.Html.thread[options.id] : new SRAX.HTMLThread(options.id);    
    thread.setOptions(options, true);
    if (SRAX.Html.ASYNCHRONOUS){
        thread.request();
    } else {
        SRAX.Html.storage.push(thread.id);
        if (SRAX.Html.storage.length == 1) thread.request();
    }
    return thread;
}

/**
* Функция запроса HTML методом GET
* @param {String} url URL адрес запроса
* @param {String/Object} id_or_options
*                        если == Object -> объект конфигурации (пример: {'callback':myfunction, 'id':'myid', 'method':'post', 'form':'id-from'}) <br>
*                        если == String -> id родительского элемента, в который вставляется результат запроса HTML (если null - тогда body)
* @param {String/Element} form id формы или сама форма
* @param {Function} callback callback функция обратного вызова
* @param {Object} callbackOps объект опции передаються в callback функцию
* @return {Object} HTMLThread объект процесса запроса HTML
*/
function get(url, id_or_options, form, callback, callbackOps){
    if (typeof id_or_options == 'object') return hax(url, id_or_options);
    var options = {};
    options.id = id_or_options;
    options.form = form;
    options.callback = callback;
    options.callbackOps = callbackOps;
    return hax(url, options);
}

/**
* Функция запроса HTML методом POST
* @param {String} url URL адрес запроса
* @param {String/Object} id_or_options 
*                        если == Object -> объект конфигурации (пример: {'callback':myfunction, 'id':'myid', 'method':'post', 'form':'id-from'}) <br> 
*                        если == String -> id родительского элемента, в который вставляется результат запроса HTML (если null - тогда body)
* @param {String/Element} form id формы или сама форма
* @param {Function} callback callback функция обратного вызова
* @param {Object} callbackOps объект опции передаються в callback функцию
* @return {Object} HTMLThread объект процесса запроса HTML
*/
function post(url, id_or_options, form, callback, callbackOps){
    if (typeof id_or_options == 'object') {
        id_or_options.method = 'post';
        return hax(url, id_or_options);
    }
    var options = {};
    options.id = id_or_options;
    options.form = form;
    options.callback = callback;
    options.callbackOps = callbackOps;
    options.method = 'post';
    return hax(url, options);
}


/**
* Функция для запроса данных
* @param {String} url URL адрес запроса
* @param {Object} options объект конфигурации <br> пример: {callback:myfunction, id:'myid', method:'post', params:'name1=value1&name2=value2'} <br><br>
* 
* Возможные параметры options: <br>
* id - id потока <br>
* method - метода запроса данных post или get (по умолчанию) <br>
* form - id формы, сама форма, id элемента или сам элемент, с которого необходимо собрать параметры <br>
* params - строка параметров, которые необходимо включиють в запрос (name=val1&name=val2) <br>
* callback - функция обратного вызова <br>
* callbackOps - опции, которые передаються в функцию обратного вызова <br>
* destroy - флаг авто удаления процесса после окончания запроса true или false (по умолчанию) <br>
* url - URL адрес запроса (при использовании синтаксиса dax(options)) <br>
* anticache - флаг антикеширования true или false (по умолчанию) <br>
* async - флаг выполнения асинхронного запроса true (по умолчанию) или false <br>
* 
* @return {Object} DATAThread объект процесса запроса данных
*/
function dax(url, options){
    if (!options) options = {};
    if (typeof url == 'string') options.url = url; else options = url;
    if (!options.id) options.id = 'undefined';
    var thread = SRAX.Data.thread[options.id] ? SRAX.Data.thread[options.id] : new SRAX.DATAThread(options.id);
    thread.setOptions(options, true);
    thread.request();
    return thread;
}

/**
* Функция для прерывания запроса данных
* @param {String} id id запроса
*/
function abortData(id){
    if (SRAX.Data.thread[id]) SRAX.Data.thread[id].abort();
    SRAX.showDaxLoading(false, id);
}

/**
* Функция запроса данных методом GET
* @param {String} url URL адрес запроса
* @param {Function} callback callback функция обратного вызова
* @param {String} idThread id запроса
* @param {Object} callbackOps объект опции передаються в callback функцию
* @param {Boolean} destroy флаг авто удаления процесса после окончания запроса 
* @return {Object} DATAThread объект процесса запроса данных
*/
function getData(url, callback, idThread, callbackOps, anticache, destroy){
    var options = {};
    options.callback = callback;
    options.id = idThread;
    options.callbackOps = callbackOps;
    options.anticache = anticache;
    options.destroy = destroy;
    return dax(url, options);
}

/**
* Функция запроса данных методом POST
* @param {String} url URL адрес запроса
* @param {String} body параметры запроса (пример: 'name1=value1&name2=value2') 
* @param {Function} callback callback функция обратного вызова
* @param {String} idThread id запроса
* @param {Object} callbackOps объект опции передаються в callback функцию
* @param {Boolean} destroy флаг авто удаления процесса после окончания запроса 
* @return {Object} DATAThread объект процесса запроса данных
*/
function postData(url, params, callback, idThread, callbackOps, anticache, destroy){
    var options = {};
    options.params = params;
    options.callback = callback;
    options.id = idThread;
    options.callbackOps = callbackOps;
    options.anticache = anticache;
    options.destroy = destroy;
    options.method = 'post';
    return dax(url, options);
}


/**
* Главный объект-библиотека 
*/
var SRAX = {
    
    /**
    * Идентификатор данной библиотеки, для решения проблем совместного использования разных частей SRAX библиотеки
    */
    TYPE : 'full',

    /**
    * Параметры по умолчанию 
    */
    Default : {
        /**
        * Флаг дебагинга AJAX запросов
        * @type Boolean 
        */
        DEBUG_AJAX : false,

        /**
        * Флаг дебагинга загрузки скриптов &lt;script>
        * @type Boolean 
        */
        DEBUG_SCRIPT : false,

        /**
        * Флаг дебагинга загрузки линков &lt;link>
        * @type Boolean 
        */
        DEBUG_LINK : false,

        /**
        * Флаг дебагинга загрузки стилей &lt;style>
        * @type Boolean 
        */
        DEBUG_STYLE : false,

        /**
        * Кодировка запросов (по умолчанию = 'UTF-8')
        * @type String 
        */
        CHARSET : 'UTF-8',

        /**
        * Флаг использования авто-аякс фильтра
        * @type Boolean 
        */
        USE_FILTER_WRAP : true,

        /**
        * Флаг отключения истории браузеров
        * @type Boolean 
        */
        NO_HISTORY : false,

        /**
        * Флаг использования кеша истории HTML
        * @type Boolean 
        */
        USE_HISTORY_CACHE : true,

        /**
        * Длина кеша истории HTML (по умолчанию = 100)
        * @type Boolean 
        */
        LENGTH_HISTORY_CACHE : 100,

        /**
        * Флаг повторной переинициализации линков &lt;link>
        * @type Boolean 
        */
        LINK_REPEAT : false,

        /**
        * Флаг использования кеша скриптов &lt;script>
        * @type Boolean 
        */
        USE_SCRIPT_CACHE : true,

        /**
        * Флаг повторной переинициализации скриптов &lt;script> с атрибутом src
        * @type Boolean 
        */
        SCRIPT_SRC_REPEAT_APPLY : true,

        /**
        * Флаг отключения загрузки скриптов с помощью AJAX
        * @type Boolean 
        */
        SCRIPT_NOAX : false,

        /**
        * Флаг коррекции относительных ссылок для href и src
        * @type Boolean 
        */
        RELATIVE_CORRECTION : false,

        /**
        * Флаг перезаписи заменяемых событий 
        * @type Boolean 
        */
        OVERWRITE : false,

        /**
        * id элемента-лоадера по умолчанию - сигнализатора загрузки HTML 
        */
        loader : 'loading',

        /**
        * id элемента-лоадера по умолчанию - сигнализатора загрузки данных 
        */
        loader2 : 'loading2',

        /**
        * суфикс элемента-лоадера для каждого потока 
        */
        loaderSufix : '_loading',

        /**
        * id родительского элемента для загрузки HTML по умолчанию
        */
        mainLayer : 'linkerLayer',

        /**
        * метки блока для модели запроса #2
        */
        model2Marker : {
            ax : '<!-- :ax:',
            begin : ':begin: //-->',
            end : ':end: //-->'
        },

        /**
        * Флаг авто удаления DATAThread процесса после окончания запроса
        * @type Boolean 
        */
        DAX_AUTO_DESTROY : false,

        /**
        * Флаг авто удаления HTMLThread процесса после окончания запроса
        * @type Boolean 
        */
        HAX_AUTO_DESTROY : false,

        /**
        * Флаг антикеш для DATAThread 
        * @type Boolean 
        */
        DAX_ANTICACHE : false,

        /**
        * Флаг антикеш для HTMLThread 
        * @type Boolean 
        */
        HAX_ANTICACHE : false

        
    },

    /**
    * Список скриптов, которые не должны кешироваться 
    * @type Array 
    */
    LIST_NO_CACHE_SCRIPTS : [],

    /**
    * Cписок скриптов, которые не должны загружаться
    * @type Array 
    */
    LIST_NO_LOAD_SCRIPTS : [],

    /**
    * Cписок линков, которые не должны загружаться
    * @type Array
    */
    LIST_NO_LOAD_LINKS : [],

    /**
    * Метод инициализации основных контейнеров и прочего
    */
    init : function(){
        var agent = navigator.userAgent.toLowerCase();
        this.browser = {
            safari: agent.indexOf("safari") != -1,
            opera: agent.indexOf("opera") != -1,
            msie: (agent.indexOf("msie") != -1) && (agent.indexOf("opera") == -1),
            mozilla: (agent.indexOf("mozilla") != -1) && ((agent.indexOf("webkit") == -1) && (agent.indexOf("compatible") == -1))
        }
        
        this.addEventsListener(this.DATAThread);
        this.addEventsListener(this.HTMLThread);
        this.addEventsListener(this.History);
        
        this.addContainerListener(this.Html);
        this.addContainerListener(this.Data);

        this.LoadUnloadContainer = {};

        this.scriptsCache = [];
        this.scriptsCache[0] = [];
        this.scriptsCache[1] = [];
 
        this.scriptsCacheTemp = [];
        this.scriptsCacheTemp[0] = [];
        this.scriptsCacheTemp[1] = [];

        this.linksCache = [];
        this.History.prefixListener.ax = this.go2Hax;
        this.readyHndlr = [];
        this.onReady(function(){
            setInterval(SRAX.History.check, 200);
            SRAX.initCPLNLS();
            SRAX.initCPLNLL();
            if (SRAX.browser.opera){
                var img = document.createElement('img');
                img.setAttribute('style','position:absolute;left:-1px;top:-1px;opacity:0;width:0px;height:0px');
                img.setAttribute('alt','');
                img.setAttribute('src','javascript:location.href="javascript:void(0)"');
                document.body.appendChild(img);
            }
        });

    },

    /**
    * Инициализация события-триггера готовоности документа
    */
    initOnReady : function(){
        if (SRAX.isReadyInited) return;
        SRAX.isReadyInited = true;
        //событие запускается после полного построения DOM, но раньше чем событие window.onload 
	if (SRAX.browser.mozilla || SRAX.browser.opera) {
            SRAX.addEvent(document, 'DOMContentLoaded', SRAX.ready);
        } else 
        if (SRAX.browser.msie) {
            document.write('<s'+'cript id="ie-srax-loader" defer="defer" src="/'+'/:"></s'+'cript>');
            var defer = document.getElementById("ie-srax-loader");
            defer.onreadystatechange = function(){
                if(this.readyState == "complete") {
                    this.parentNode.removeChild(this);
                    SRAX.ready();
                }
            };
            defer = null;
	} else 
        if (SRAX.browser.safari){
		SRAX.safariTimer = setInterval(function(){
			if (document.readyState == "loaded" || 
				document.readyState == "complete") {
				clearInterval(SRAX.safariTimer);
				SRAX.safariTimer = null;
				SRAX.ready();
			}
		}, 10); 
         }
         SRAX.addEvent(window, 'load', SRAX.ready);
    },
    /**
    * Регистрация Функций на событии onReady 
    * @param {Function} handler функция, которая должна выполниться
    */
    onReady : function(handler){
        if (SRAX.isReady) {
            handler();
        } else {
            SRAX.readyHndlr.push(handler);        
            SRAX.initOnReady();
        }
    },

    /**
    * Метод для выполнения зарегистрированных функций на событии onReady 
    */
    ready : function(){
        if (SRAX.isReady) return;
        SRAX.isReady = true;
        for (var i = 0; i < SRAX.readyHndlr.length; i++){
            try{
                SRAX.readyHndlr[i]();
            } catch(ex){
                error(ex);
            }
        }
        SRAX.readyHndlr = null;
    },

    /**
    * Функция прикрепления события к обьекту 
    * пример: SRAX.addEvent(window, 'load', function() {alert('onload')})
    * @param {Object} obj объект к которому прикрепляеться событие
    * @param {String} name имя события (без префикса on)
    * @param {Function} handler функция, которая должна выполниться
    */
    addEvent : function(obj, name, handler) {
	if (obj.attachEvent) obj.attachEvent('on' + name, handler);
	else obj.addEventListener(name, handler, false);
    },

    /**
    * Функция получения объекта
    * @param {String/Object} obj id объекта или сам объект
    * @return {Object} объект
    */
    get : function(obj){
        if (typeof obj == 'string') obj = id(obj);
        return obj;
    },

    /**
    * Контейнер для body onload & unload
    */
    LoadUnloadContainer : null,

    /**
    * Временный кеш скриптов - для проверки состояния загрузки
    */
    scriptsCacheTemp : null,

    /**
    * Кеш скриптов
    */
    scriptsCache : null,

    /**
    * Кеш линков
    */
    linksCache : null,
    
    /**
    * List No Load Scripts - LNLS <br>
    * Функция очистки LIST_NO_LOAD_SCRIPTS <br>
    * обнуление (очистка) текущего списка
    */
    clearLNLS: function(){
        SRAX.LIST_NO_LOAD_SCRIPTS = [];
    },

    /**
    * Current Page List No Load Scripts - CPLNLS <br>
    * Инициализация LIST_NO_LOAD_SCRIPTS <br>
    * все скрипты из <head> текущей страницы попадают в список скриптов, которые повторно не загружаются
    * @param {Boolean} clear параметр предварительного обнуления (очистки) текущего списка
    */
    initCPLNLS: function(clear){
        if (clear) SRAX.clearLNLS();
        var head = document.getElementsByTagName('head')[0];
        var scripts = head.getElementsByTagName('script');
        for (var i = 0; i < scripts.length; i++){
            if (scripts[i].src == null || scripts[i].src == '') continue;
            SRAX.LIST_NO_LOAD_SCRIPTS[SRAX.LIST_NO_LOAD_SCRIPTS.length] = scripts[i].src;
        }        
    },

    /**
    * List No Load Links - LNLL <br>
    * Функция очистки LIST_NO_LOAD_LINKS <br>
    * обнуление (очистка) текущего списка
    */
    clearLNLL: function(){
        SRAX.LIST_NO_LOAD_LINKS = [];
    },

    /**
    * Current Page List No Load links - CPLNLL <br>
    * Инициализация LIST_NO_LOAD_LINKS <br>
    * все линки из <head> текущей страницы попадают в список линков, которые повторно не загружаются
    * @param {Boolean} clear параметр предварительного обнуления (очистки) текущего списка
    */
    initCPLNLL: function(clear){
        if (clear) SRAX.clearLNLL();
        var head = document.getElementsByTagName('head')[0];
        var links = head.getElementsByTagName('link');
        for (var i = 0; i < links.length; i++){
            if (links[i].href == null || links[i].href == '') continue;
            SRAX.LIST_NO_LOAD_LINKS[SRAX.LIST_NO_LOAD_LINKS.length] = links[i].href;
        }
    },

    /**
    * Обьект значение : эквивалент, которые надо заменить в ссылке (используется для сокращения ссылок)
    */
    linkEqual : {
        // хак для Оперы - Опера не воспринимает в location.hash все что после ? в ссылке с # 
        // к примеру http://cold.udelau.ru/index.php#:ax:center:/ajax.php?block=passport&module=showfolders
        // без хака location.hash будет равен #:ax:center:/ajax.php - т.е. история не будет срабатывать
         '?':'[~q~]'
    },

    /**
    * Функция для прямой замены определенных (linkEqual) значений в ссылке на их эквивалентные значения <br> 
    * @param {String} url URL ссылка
    * @param {Boolean} reverse прямая (false) или обратная (true) замена 
    * @return {String} результат замены
    */
    replaceLinkEqual : function(url, reverse){
        for (var i in SRAX.linkEqual){
            if (reverse) {
                url = url.replaceAll(SRAX.linkEqual[i],i);
            } else {
                url = url.replaceAll(i, SRAX.linkEqual[i]);
            }
        }
        return url;
    },

    /**
    * Объект-контейнер - при использовании модели запроса #2 - соответствие ИД блоков ответа сервера - ИД блокам клиента <br><br>
    * Пример:<br>
    * SRAX.Model2Blocks['id-all-layer'] = {'block-m-left':'left','block-content':'all'}; <br>
    */
    Model2Blocks : {},

    /**
    * количесвто живых процессов запроса данных
    */
    countDRInProcess : 0,

    /**
    * Функция препроцессорной обработки данных, 
    * если переопределена вызывается перед вызовом callback функции,
    * одна для всех запросов данных
    * @param {Object} ops входящие параметры (ops.xmlhttp - объект XmlHttpRequest, thread - процесс владелец)
    * @return {Boolean} результат обработки
    */
    DaxPreprocessor : function(ops){
    },

    /**
    * Функция препроцессорной обработки HTML, 
    * если переопределена вызывается перед вызовом callback функции,
    * одна для всех запросов HTML
    * @param {Object} ops входящие параметры (ops.xmlhttp - объект XmlHttpRequest, thread - процесс владелец)
    * @return {Boolean} результат обработки
    */
    HtmlPreprocessor : function (ops){
    },

    /**
    * Объект процесса запроса данных
    * @param {Object} idThread id запроса
    */
    DATAThread : function(idThread) {
        var xmlhttp;
        var _this = this;
        this.inprocess = false;
        this.id = idThread;
        var ops = this.options = {};

        SRAX.Data.thread[idThread] = this;
        SRAX.Data.register(this);

        this.repeat = function(params){
            ops.params = params;
            _this.request();
        }

        this.setOptions = function(options, overwrite){
            if (overwrite) {
                ops = options; 
                if (ops.async == null) ops.async = true;
            } else {
                for (var i in options) ops[i] = options[i];
            }
            this.options = ops;
        }

        this.getOptions = function(){
            return ops;
        }

        function init() {
            if (window.XMLHttpRequest) {
                return new XMLHttpRequest();
            } else 
            if (window.ActiveXObject){
                try {
                  var http = null;
                  for (var i = 0; i < IE_ENGINE.length; i++){
                    try {
                        http = new ActiveXObject(IE_ENGINE[i]);
                        if (http != null) break;
                    } catch (e){}
                  }
                  return http;
                } catch (e) {}
            }
        }
    
        function processRequest(obj) {
          if (!obj || !obj.readyState) obj = xmlhttp;
          try{
            if (obj.readyState == 4) {
              _this.inprocess = false;
              SRAX.showDaxLoading(_this.inprocess, idThread);
              var status = obj.isAbort ? -1 : obj.status;

              var success = (status >= 200 && status < 300) || status == 304 || (status == 0 && location.protocol == 'file:');

              _this.fireEvent('response',
                   {response:obj,
                   url:ops.url,
                   id:idThread,
                   status:status,
                   success:success, 
                   callbackOps:ops.callbackOps}
              )
              if (status != -1 && SRAX.DaxPreprocessor({xmlhttp:obj, thread:_this}) !== false && ops.callback) {
                   ops.callback(obj, idThread, success, ops.callbackOps);
                   if (SRAX.Default.DEBUG_AJAX) log('callback id:' + idThread);                   
              }

              if ((ops.destroy != null) ? ops.destroy : SRAX.Default.DAX_AUTO_DESTROY){
                   _this.destroy();
              }
            }
          } catch (ex){
              log(ex);
              _this.fireEvent('exception',
                   {response:obj,
                   url:ops.url,
                   id:idThread,
                   exception:ex,
                   options:ops}
              )
              _this.inprocess = false;
              SRAX.showDaxLoading(_this.inprocess, idThread);
              if ((ops.destroy != null) ? ops.destroy : SRAX.Default.DAX_AUTO_DESTROY){
                   _this.destroy();
              }
          }
        }
        
        this.isProcess = function (){
            return _this.inprocess;
        }
        
        this.request = function(){
            var method = (ops.method && ops.method.toLowerCase() == 'post') ? 'post':'get';
            try{
                var options = {
                    url:ops.url,
                    id:idThread,
                    form:ops.form,
                    params:ops.params,
                    method:method
                }

                if (_this.fireEvent('beforerequest', options) !== false){
                    var body = SRAX.createQuery(ops.form);
                    if (ops.params) {
                        if (body != '' && !ops.params.startWith('&')) body += '&';
                        body += ops.params; 
                    }
                    if (method != 'post' && body != '') {
                        if (ops.url.indexOf('?') == -1){
                            ops.url += '?' + body
                        } else {
                            ops.url += ((ops.url.endWith('?') || ops.url.endWith('&')) ? '' : '&') + body
                        }
                    }
                    if (_this.inprocess) _this.abort();
                    _this.inprocess = true;
                    if (!xmlhttp) xmlhttp = init();
                    xmlhttp.open(method.toUpperCase(), ops.url, ops.async);
                    xmlhttp.onreadystatechange = processRequest;
                    xmlhttp.setRequestHeader('AJAX_ENGINE', 'Fullajax');
                    if (ops.anticache != null ? ops.anticache : SRAX.Default.DAX_ANTICACHE) xmlhttp.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT');
                    xmlhttp.setRequestHeader('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest');
                    if (method == 'post') xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; Charset=' + SRAX.Default.CHARSET);            
                    xmlhttp.send((method == 'post') ? body : null);

                    SRAX.showDaxLoading(_this.inprocess, idThread);
                    if (SRAX.Default.DEBUG_AJAX) log(method + ' ' + ops.url + ' params:' + body + ' id:' + idThread);
                    _this.fireEvent('afterrequest', options);
                }
            } catch (ex){
                _this.abort();
                throw ex;
            }
        }

        this.abort = function(){
            _this.inprocess = false;
            if (!xmlhttp) return;
            xmlhttp.isAbort = true;
            try{
                xmlhttp.abort();
            } catch (ex){}
            xmlhttp = null;
        }

        this.destroy = function(){
            SRAX.Data.thread[idThread] = null;
            delete SRAX.Data.thread[idThread];
        }
    },

    /**
    * Функция отображения/скрытия объекта лоадер-сигнализатор запроса данных (картника с играющей загрузкой)
    * @param {Boolean} show показать/скрыть
    * @param {String} obj id процесса запроса данных 
    */
    showDaxLoading : function(show, obj){
        obj = SRAX.getDaxImg(obj);
        if (obj && obj.style){
          if (show) {
              SRAX.countDRInProcess++;
              if (obj.style.visibility != null && obj.style.visibility != '') obj.style.visibility = 'visible'; 
              else obj.style.display = 'block';
          } else {
              if (SRAX.countDRInProcess > 0) SRAX.countDRInProcess--; 
              if (SRAX.countDRInProcess == 0) {
                  if (obj.style.visibility != null && obj.style.visibility != '') obj.style.visibility = 'hidden'; 
                  else obj.style.display = 'none';
              }
          }
        } 
    },

    /**
    * Функция доступа к объекту лоадер-сигнализатор запроса данных (картника с играющей загрузкой)
    * @param {String} obj id запроса
    * @return {Object} объект лоадер-сигнализатор запроса данных 
    */
    getDaxImg : function(obj){
        if (typeof obj == 'string') obj = SRAX.get(obj + SRAX.Default.loaderSufix);
        if (!obj) {
            obj = SRAX.get(SRAX.Default.loader2);
            if (!obj) {
                obj = SRAX.get(SRAX.Default.loader);
            }
        }
        return obj;  
    },

    /**
    * Функция отображения/скрытия объекта лоадер-сигнализатор запроса HTML (картника с играющей загрузкой)
    * @param {Boolean} show показать/скрыть
    * @param {String} obj id процесса запроса HTML 
    */
    showHaxLoading : function(show, obj){
        obj = SRAX.getHaxImg(obj)
        if (obj && obj.style){
          if (show) {
              if (obj.style.visibility != null && obj.style.visibility != '') obj.style.visibility = 'visible'; 
              else obj.style.display = 'block';
        } else {
            for (var i in SRAX.Html.thread) {
                if (SRAX.Html.thread[i] && SRAX.Html.thread[i].isProcess()) break;
                if (obj.style.visibility != null && obj.style.visibility != '') obj.style.visibility = 'hidden'; 
                else obj.style.display = 'none';
            }
          }
        } 
    },
 
    /**
    * Функция доступа к объекту лоадер-сигнализатор запроса HTML (картника с играющей загрузкой)
    * @param {String} obj id родительского элемента
    * @return {Object} объект лоадер-сигнализатор запроса HTML 
    */
    getHaxImg : function(obj){
        if (typeof obj == 'string') obj = SRAX.get(obj + SRAX.Default.loaderSufix);
        if (!obj) {
            obj = SRAX.get(SRAX.Default.loader);
            if (!obj) {
                obj = SRAX.get(SRAX.Default.loader2);
            }
        }
        return obj;  
    },


    /**
    * Функция для кодирования симовлов
    * @param {String} text текст
    * @return {String} закодированный текст
    */
    encode : function(text){
        return encodeURIComponent(text);
    },

    /**
    * Функция для декодирования симовлов
    * @param {String} text закодированный текст
    * @return {String} декодированный текст
    */
    decode : function(text){
        return decodeURIComponent(text);
    },

    /**
    * Функция авто-сборки параметров
    * @param {String/Element} obj id формы или сама форма 
    * @return {String} строка завернутых параметров (пример: 'name1=value1&name2=value2')
    */
    createQuery : function(obj, ops) {
        if (!ops) ops = {};
        obj = SRAX.get(obj);
        if (!obj) return '';
        var names = [];
        var vals = [];

        var inputs = obj.getElementsByTagName("input");       
        for(var i = 0; i < inputs.length; i++ ) {
          var type = inputs[i].type.toLowerCase();
          var name = inputs[i].name;
          if (name == null || name == '') name = inputs[i].id;
          if (!name) continue;
          var value = SRAX.encode(inputs[i].value);
          if (type == "text" || type == "password" || type == "hidden") {
              names.push(SRAX.encode(name));
            vals.push(value);
          } else
          if (type == "checkbox" || type == "radio") {
            if (inputs[i].checked) {
              names.push(SRAX.encode(name));
              if (value == null || value == '') 
                vals.push(inputs[i].checked);
              else 
                vals.push(value);
            }
          } else 
          if (type == 'select-multiple') {
              for (var j = 0; j < inputs[i].options.length; j++){
                if (inputs[i].options[j].selected) {
                    names.push(SRAX.encode(name));
                    vals.push(SRAX.encode(inputs[i].options[j].value));
                }
              }
          }
        }

        var selects = obj.getElementsByTagName("select");       
        for(var i = 0; i < selects.length; i++ ) {
            var type = selects[i].type.toLowerCase();
            var name = selects[i].name;
            if (name == null || name == '') name = selects[i].id;
            if (selects[i].selectedIndex == -1) continue;
            names.push(SRAX.encode(name));
            vals.push(SRAX.encode(selects[i].options[selects[i].selectedIndex].value));
        }   

        var textareas = obj.getElementsByTagName("textarea");       
        for(var i = 0; i < textareas.length; i++) {
            var name = textareas[i].name;
            if (name == null || name == '') name = textareas[i].id;
            names.push(SRAX.encode(name));
            vals.push(SRAX.encode(textareas[i].value));
        }
        var query = [];
        for (var i = 0; i < names.length; i++){ 
            if (ops.skipEmpty && vals[i] == '') continue;
            query.push(names[i] + '=' + vals[i]);
        }
        return query.join('&');
    },

    /**
    * Функция парсер строки
    * @param {String} str строка
    * @param {String} sprt разделитель
    * @return {Array} массив строк
    */
    stringTokenizer : function (str, sprt){
        var res = [];
        var ind = str.indexOf(sprt);
        while (ind != -1){
            res[res.length] = str.substring(0, ind);
            str = str.substring(ind + sprt.length);
            ind = str.indexOf(sprt);
        }
        if (str.length != 0) res[res.length] = str;
        return res;
    },

    /**
    * Функция выделения значения необходимого параметра из строки параметров
    * @param {String} params строка параметров разделенная пробелами (к примеру &lt;link href="/path/style.css" type="text/css">)
    * @param {Object} obj объект, которому присвоить распарсенные параметры
    */
    applyParams : function(params, obj){
        var arr = SRAX.stringTokenizer(params, ' ');
        for (var i = 0; i < arr.length; i++){
            var ind1 = arr[i].indexOf("=");
            if (ind1 != -1){
                var ind = SRAX.indexMarksOfAttribute(arr[i],ind1+1);
                var name = arr[i].substring(0, ind1).trim(); 
                var val = arr[i].substring(ind[0] + 1, ind[1]).trim(); 
                obj[name] = val;
            }
        }
        return obj;
    },
    
    /**
    * Функция выделения маркеров (' или ") значения атрибута
    * @param {String} str строка
    * @return {Array} arr массив из 2-х значений (первое - стартовая позиция, второе - конечная позиция)
    */
    indexMarksOfAttribute : function(str, start){
        if (start == null) start = 0;
        var ind1 = str.indexOf("'", start);
        var ind2 = str.indexOf('"', start);
        if (ind2 != -1){
            if (ind2 < ind1 || ind1 == -1) {
                ind1 = ind2; 
                ind2 = str.indexOf('"', ind1 + 1);    
            } else {
                ind2 = str.indexOf("'", ind1 + 1);
            }
        } else {
            if (ind1 == -1){
                ind1 = str.indexOf('=');
                ind1++;
                while (str.substring(ind1).startWith(' ')) ind1++;
                str = str.replaceAll('>','');
                ind2 = str.length-1;
                while (str.substring(ind2,1).endWith(' ')) ind2--;
                ind1--;
                ind2++;
            } else {
                ind2 = str.indexOf("'", ind1 + 1);
            }
        }
        return [ind1, ind2];
    },
    
    /**
    * Функция выделения значения необходимого параметра из строки параметров
    * @param {String} params строка завернутых параметров (пример: 'name1=value1&name2=value2')
    * @param {String} name имя необходимого параметра
    * @return {String} значение необходимого параметра
    */
    getParam : function(params, name){
        var ind1 = params.toLowerCase().indexOf(' ' + name);
        var val;
        if (ind1 != -1){
            var ind = SRAX.indexMarksOfAttribute(params,ind1 + name.length + 1);
            val = params.substring(ind[0] + 1, ind[1]); 
        }
        return val;
    },

    /**
    * Функция для преобразования зарегистрированных значений наборов символов
    * @param {String} str текст для преобразовния 
    * @return {String} результат преобразовния 
    * 
    */
    entitiesConvertor : function(str){
        if (str == null) return str;
        if (!SRAX.tempDiv) SRAX.tempDiv = document.createElement('div');
        SRAX.tempDiv.innerHTML = str;
        return SRAX.tempDiv[this.browser.msie ? 'innerText' : 'textContent'];
    },

    /**
    * Функция создания объекта &lt;script>
    * @param {String} text текст-тело скрипта
    * @return {Object} объект &lt;script>
    */
    makeScript : function(text){
        if (text.indexOf('SRAX.init()') != -1) text = '<script type="text/javascript"></'+'script>';
        var script = document.createElement('script');
        var ind1 = text.toLowerCase().indexOf('<script');
        var ind2 = text.indexOf('>', ind1 + 1);
        var ind3 = text.toLowerCase().lastIndexOf('</'+'script>');

        if(ind1 != -1 && ind2 != -1){
            var params = text.substring(ind1, ind2 + 1);
            SRAX.applyParams(params, script);
        }
        if (script.src) script.src = SRAX.entitiesConvertor(script.src);
        if (ind3 != -1) text = text.substring(ind2 + 1, ind3); else text = '';

        if (text.length > 0)
            if (SRAX.browser.msie) {
                script.text = text; 
            } else {
                var body = document.createTextNode(text);
                script.appendChild(body); 
            }

        if (script.id == null) script.id = script.src;                
        return script;
    },
    
    /**
    * Функция выделения и применения объекта &lt;style>
    * @param {String} text текст-тело стиля
    */
    appendStyle : function(text, idLayer, seal){
        text = text.toLowerCase();
        var ind1 = text.indexOf('<style');
        var ind2 = text.indexOf('>', ind1 + 1);
        var ind3 = text.indexOf('</style>', ind2 + 1);
        var params = text.substring(ind1, ind2+1);
        var obj = SRAX.applyParams(params, {});
        if (obj['ax:skip'] == 'true' || obj['ax:skip'] == '1') return;        

        text = text.substring(ind2 + 1, ind3);

        ind1 = text.indexOf('@import ');
        while (ind1 != -1){
            ind2 = text.indexOf('(', ind1 + 1);
            ind3 = text.indexOf(')', ind2 + 1);
            var href = text.substring(ind2 + 1, ind3);
            href = '<link rel="stylesheet" type="text/css" href="' + href + '"/>';
            SRAX.appendLink(href, idLayer, seal);
            text = text.indexOf(0,ind1)+text.indexOf(ind3+1);
            ind1 = text.indexOf('@import ');
        }
        if (seal) text = SRAX.sealStyle(text, idLayer);
        if (text.length > 0){
            var style = document.createElement('style');
            style.type = 'text/css';
            if (style.styleSheet) {
                style.styleSheet.cssText = text;
            } else {
                if (SRAX.browser.mozilla || SRAX.browser.opera){
                    style.innerHTML = text;
                } else {
                    var cssText = document.createTextNode(text);
                    style.appendChild(cssText);
                }                        
            }
            var head = document.getElementsByTagName('head')[0];
            head.appendChild(style);  
            if (SRAX.Default.DEBUG_STYLE) log('Style ' + text);
        }
    },

    /**
    * Функция изоляции css<br>
    * используется для решения конфликтов между подгружаемыми стилями
    * 
    * @param {String} text текст-тело стиля
    * @return {String} обработаный текст-тело стиля
    */
    sealStyle : function(text, idLayer){
        var ind1 = 0;
        var ind2 = text.indexOf('{');
        var mark = '#'+idLayer+' ';
        var res = '';
        while (ind2 > -1){
            res += mark + text.substring(ind1+1, ind2).trim().replaceAll(',',','+mark);
            ind1 = text.indexOf('}', ind2);
            if (ind1 > -1) res += text.substring(ind2, ind1+1);
            ind2 = ind1 == -1 ? -1 : text.indexOf('{', ind1);
        }
        return res;
    },

    /**
    * Функция выделения и применения объекта &lt;link>
    * @param {String} text текст-тело линка
    */
    appendLink : function(text, idLayer, seal){
        text = text.toLowerCase();
        var ind1 = text.indexOf('<link');
        var ind2 = text.indexOf('>', ind1 + 1);
        if(ind1 != -1 && ind2 != -1){
            var params = text.substring(ind1, ind2 + 1);
            var link = document.createElement('link');
            SRAX.applyParams(params, link);
            if (link.href) link.href = SRAX.entitiesConvertor(link.href);

            if (link['ax:skip'] == 'true' || link['ax:skip'] == '1') return;

            var href = seal ? idLayer + ':'+link.href : link.href;
            if (SRAX.indexOfCacheSrc(SRAX.linksCache, href) != -1) {
                if (!SRAX.Default.LINK_REPEAT || link['ax:repeat'] == 'false' || link['ax:repeat'] == '0'){
                        return; 
                }
            } else {
                SRAX.linksCache[SRAX.linksCache.length] = href;
            }

            if (SRAX.indexOfCacheSrc(SRAX.LIST_NO_LOAD_LINKS, href) != -1) return;
            
            if (seal && link.rel == 'stylesheet') {
                try {
                    getData(link.href, function(resp, id, status, idLayer) {
                        var text = status ? resp.responseText : '';  
                        SRAX.appendStyle('<style>'+text+'</style>', idLayer, true);
                    }, link.href, idLayer);
                    return;
                } catch(ex){
                    log('error seal ' + link.href)
                }
            }

            if (document.createStyleSheet) {
                document.createStyleSheet(link.href);
            } else {
                var head = document.getElementsByTagName('head')[0];
                head.appendChild(link);  
            }

            if (SRAX.Default.DEBUG_LINK) log('append LINK ' + link.href);
                        
        }
    },

    /**
    * Функция определения принаджлежности к HTML коментариям,<br>
    * используется для определения того что кусок нижеследующий за параметром text закоментирован или нет
    * 
    * @param {String} text текст HTML
    * @return {Boolean} результат проверки принадлежности (true - да, false - нет)
    */
    isHTMLComment : function(text){  
        var ind1 = text.lastIndexOf('<!--');
        var ind2 = text.indexOf('-->', ind1 + 1);
        if (ind1 != -1 && ind2 == -1) {
            return true;
        } else {
            return false;
        }
    },

    /**
    * Функция определения принаджлежности к HTML части (т.е. что не пренадлежит не скрипту и не стилю),<br>
    * используется для определения к чему относится нижеследующее за параметром text
    * 
    * @param {String} text текст HTML
    * @return {Boolean} результат проверки принадлежности (true - да, false - нет)
    */
    isHTML : function(text){        
        text = text.toLowerCase();
        function isNoEntry(type){
            var res = true;
            var ind1 = text.lastIndexOf('<'+type);
            var ind2 = text.indexOf('</'+type+'>', ind1 + 1);
            var ind3 = text.indexOf('>', ind1 + 1);
            var ind4 = text.indexOf('/>', ind1 + 1);
            if (ind1 != -1 && ind3 != -1) {
                if (ind2 == -1 && ind4 != ind3+1){
                    res = false;               
                }
            }
            return res;
        }

        return isNoEntry('script') && isNoEntry('style');
    },
    
    /**
    * Функция коррекции относительных ссылок для href и src<br><br>
    * 
    * @param {String} text текст HTML
    * @param {String} url URL адрес HTML
    * @param {String} type тип (href или src)
    * @return {String} текст HTML
    */
    relativeCorrection : function(text, url, type){
        var ind1 = url.lastIndexOf('/');
        if (ind1 != -1) {
            url = url.substring(0, ind1+1);
        }
        ind1 = text.toLowerCase().indexOf(type);
        while (ind1 != -1){
            var ind = SRAX.indexMarksOfAttribute(text, ind1 + 1);
            if (SRAX.isHTML(text.substring(0, ind1 + 1)) && ind[0] != -1 && ind[1] != -1){
                var val = text.substring(ind[0] + 1, ind[1]); 
                if (!val.startWith('/') && !val.startWith('#') && SRAX.parseUri(val).protocol == ''){
                        text = text.substring(0, ind[0] + 1) + url + text.substring(ind[0] + 1); 
                  }
            }
            ind1 = text.toLowerCase().indexOf(type, ind1 + 1);
        }
        return text;
    },
    
    /**
    * Функция проверки содержит ли кеш-массив указанный линк
    * 
    * @param {Array} arr кеш-массив
    * @param {String} src src или href линк
    * @return {Integer} результат проверки (-1 = не содержит)
    */
    indexOfCacheSrc : function(arr, src){
        var ind = arrayIndexOf(arr,src);
        if (ind == -1){
            if (src.startWith(location.protocol)) {
                src = src.replace(location.protocol + '//' + location.host,''); 
            } else {
                src = location.protocol + '//' + location.host + src;
            }
            ind = arrayIndexOf(arr,src);
        }
        return ind;
    },
    
    /**
    * Обобщенная функция парсинга и применения объектов &lt;link>,&lt;style>,&lt;title>,&lt;script><br><br>
    * 
    * Список параметров: <br>
    * text - текст HTML <br>
    * idLayer - id родительского элемента<br>
    * url - URL адрес скрипта
    * isNew - новые данные (true) или рекурсия (false) 
    * owner - объект-владелец (в основном скрипт) данного процеса (введен для отслеживания окончания document.write)
    * rc - использовать (true) или не использовать (false - по умолчанию) коррекцию относительных ссылок <br>
    * 
    * @param {Object} options опции парсинга HTML
    */
    parsingText : function(options){
        if (options == null) options = {};
        var text = options.text;
        var idLayer = options.id;
        var url = options.url;
        var isNew = options.isNew; 
        var owner = options.owner;
        if ((options.rc != null) ? options.rc : SRAX.Default.RELATIVE_CORRECTION) {
            text = SRAX.relativeCorrection(text, url, 'src'); 
            text = SRAX.relativeCorrection(text, url, 'href'); 
            text = SRAX.relativeCorrection(text, url, 'action'); 
        }
        text = SRAX.parsingLinkAndStyle(text, idLayer, options.seal);
        
        text = SRAX.parsingFrameset(text);

        var ind01 = text.toLowerCase().indexOf('<head>');
        var start = '';
        if (ind01 != -1)  {
            start += text.substring(0, ind01);
            text = text.substring(ind01);
        } else {
            start = text;
            text = '';
        }
        var ind02 = text.toLowerCase().indexOf('</head>');
        var end = '';
        if (ind02 != -1) {
            end += text.substring(ind02+7);
            text = text.substring(0,ind02+7);
        }

        
        text = SRAX.parsingTitle(text, idLayer);

        text = start + text + end;
        text = SRAX.parsingLoadUnload(text, idLayer);
        var obj = SRAX.parsingScript(text, idLayer, owner && owner['ax:noax']);

        new SRAX.writeHtml(idLayer, obj.scriptsHead, obj.scriptBody, obj.html, url, isNew, owner);
    },
    
    /**
    * Функция парсинга событий body -> onload & onunload
    * @param {String} text текст HTML
    * @param {String} idLayer id родительского элемента
    * @return {String} текст HTML
    */    
    parsingLoadUnload : function(text, idLayer){
        var onload;
        var onunload;
        var ind1 = text.toLowerCase().indexOf('<body');
        if (ind1 != -1){
            var ind2 = text.indexOf('>', ind1+1);
            if (ind2 != -1){
                var body = text.substring(ind1, ind2 + 1);
                onload = SRAX.getParam(body, 'onload'); 
                onunload = SRAX.getParam(body, 'onunload');                 
            }
        }

        if (SRAX.LoadUnloadContainer[idLayer] == null) SRAX.LoadUnloadContainer[idLayer] = {};

        SRAX.LoadUnloadContainer[idLayer].onload = onload;
        SRAX.LoadUnloadContainer[idLayer].onunload = SRAX.LoadUnloadContainer[idLayer].nextonunload;
        SRAX.LoadUnloadContainer[idLayer].nextonunload = onunload;

        return text;
    },

    /**
    * Функция парсинга &lt;title>
    * @param {String} text текст HTML
    * @param {idLayer} id родительского элемента
    * @return {String} текст HTML
    */
    parsingTitle : function(text, idLayer){
        var tmp = text.toLowerCase();
        var ind1 = tmp.indexOf('<title>');
        var ind2 = tmp.indexOf('</title>', ind1 + 1);
        var once = false;
        while (ind1 != -1 && ind2 != -1) {
            if (!SRAX.isHTMLComment(text.substring(0, ind1)) && !once) {
                once |= SRAX.titleChange(text.substring(ind1 + 7, ind2), idLayer);
            }
            text = text.substring(0,ind1) + text.substring(ind2+8);
            tmp = text;
            ind1 = tmp.indexOf('<title>', ind1+1);
            ind2 = tmp.indexOf('</title>', ind1 + 1);
        }
        return text;
    },

    /**
    * Функция изменения &lt;title>
    * @param {String} text текст HTML
    * @param {idLayer} id родительского элемента
    * @return {Boolean} результат изменения
    */
    titleChange : function(title, idLayer){
        var oldTitle = document.title;
        if (SRAX.Html.fireEvent(idLayer, 'beforetitlechange', {oldTitle:oldTitle, newTitle:title}) !== false){
            document.title = title;
            SRAX.Html.fireEvent(idLayer, 'titlechange', {oldTitle:oldTitle, newTitle:title});
            return true;
        }
        return false;
    },

    /**
    * Функция парсинга &lt;frameset>
    * @param {String} text текст HTML
    * @return {String} текст HTML
    */
    parsingFrameset : function(text){
        var ind1 = text.toLowerCase().indexOf('<frameset');
        if (ind1 > -1){
            var ind2 = text.toLowerCase().indexOf('>', ind1);
            var ind3 = text.toLowerCase().indexOf('</frameset>');
            if (ind2 > -1 && ind3 > -1){
                var tmp = text.substring(ind1,ind3+11);
                var gid = SRAX.genId();
                tmp = "<iframe style='height:100%;width:100%;border:0' id='"+gid+"'></iframe><script>var obj = id('"+gid+"');var doc = obj[obj.contentWindow ? 'contentWindow' : 'contentDocument'].document;doc.open();doc.write('"+tmp.replaceAll('\n','').replaceAll('\r','').trim()+"');doc.close()</script>";
                text = text.substring(0,ind1)+tmp+text.substring(ind3+11);
            }
        }
        return text;
    },

    /**
    * Функция последовательного парсинга &lt;link> и &lt;style>
    * @param {String} text текст HTML
    * @return {String} текст HTML
    */
    parsingLinkAndStyle : function(text, idLayer, seal){
        var l1 = text.toLowerCase().indexOf('<link');
        var s1 = text.toLowerCase().indexOf('<style');
        var html = '';
        var ind1 = -1;
        var ind2 = -1;
        
        if ((l1 < s1 && l1 != -1) || s1 == -1){
            ind1 = l1;
            ind2 = text.indexOf('>', ind1 + 1);            
        } else {
            ind1 = s1;
            ind2 = text.toLowerCase().indexOf('</style>', ind1 + 1);    
        }

        while(ind1 != -1 && ind2 != -1){
            if (ind1 > 0) html += text.substring(0, ind1);

            if ((l1 < s1 && l1 != -1) || s1 == -1) {
                if (!SRAX.isHTMLComment(text.substring(0, ind1))) SRAX.appendLink(text.substring(ind1, ind2 + 1), idLayer, seal);
                text = text.substring(ind2 + 1);
            } else {
                if (!SRAX.isHTMLComment(text.substring(0, ind1))) SRAX.appendStyle(text.substring(ind1, ind2 + 8), idLayer, seal);
                text = text.substring(ind2 + 8);
            }
            l1 = text.toLowerCase().indexOf('<link');
            s1 = text.toLowerCase().indexOf('<style');

            
            if ((l1 < s1 && l1 != -1) || s1 == -1){
                ind1 = l1;
                ind2 = text.indexOf('>', ind1 + 1);
            } else {
                ind1 = s1;
                ind2 = text.toLowerCase().indexOf('</style>', ind1 + 1);    
            }

        }

        if (text.length > 0) html += text;
        return html; 
        
    },

    /**
    * Функция парсинга &lt;script>
    * @param {String} text текст HTML
    * @param {String} idLayer id родительского элемента
    * @return {String} текст HTML
    */
    parsingScript : function(text, idLayer, noax){        
        var ind1 = text.toLowerCase().indexOf('<script');
        var ind2 = text.toLowerCase().indexOf('</'+'script>', ind1 + 1);
        var n = 9;
        var ind3 = text.indexOf('>', ind1 + 1);
        var ind4 = text.indexOf('/>', ind1 + 1);
        if (ind3 != -1 && ind4 !=- 1 && ind3 == ind4 + 1) {
            ind2 = ind4; 
            n = 2;
        }

        var scriptsHead = [];
        var html = [];
        var scriptBody = [];
        var placeScript = 0;
        while(ind1 != -1 && ind2 != -1){    
            if (ind1 > 0) html[html.length] = text.substring(0, ind1);
            var script = SRAX.makeScript(text.substring(ind1, ind2 + n));                
            if (noax) script['ax:noax'] = 1;
            text = text.substring(ind2 + n);
            ind1 = text.toLowerCase().indexOf('<script');
            ind2 = text.toLowerCase().indexOf('</'+'script>', ind1 + 1);
            n = 9;
            ind3 = text.indexOf('>', ind1 + 1);
            ind4 = text.indexOf('/>', ind1 + 1);
            if (ind3 != -1 && ind4 !=- 1 && ind3 == ind4 + 1) {
                ind2 = ind4; 
                n = 2;
            }
            

            if (html.length == 0 || !SRAX.isHTMLComment(html.join(''))){
                if (text.toLowerCase().indexOf('<body') == -1) {
                    if (html.length == 0 || html[html.length - 1].indexOf('_place_of_script_') == -1) {
                        html[html.length] = '<span id="'+idLayer+'_place_of_script_'+placeScript+'" style="display:none"><!--place of script # ' + placeScript + '//--></span>';
                        placeScript++;
                    }
                    script.place = idLayer+'_place_of_script_'+(placeScript-1);
                    var old_place = id(script.place);
                    if (old_place) old_place.id += 'old'; 
                }

                if (script['ax:skip'] == 'true' || script['ax:skip'] == '1') continue;
                if (script.src != null && script.src != '') {
                    if (script.src.indexOf('linker.js') != -1 || 
                    SRAX.indexOfCacheSrc(SRAX.LIST_NO_LOAD_SCRIPTS, script.src) != -1) continue;
                    var ind = SRAX.indexOfCacheSrc(SRAX.scriptsCache[0],script.src);
                    if (ind != -1) {
                        if ((script['ax:repeat'] == null || (script['ax:repeat'] != 'false' && script['ax:repeat'] != '0')) && SRAX.Default.SCRIPT_SRC_REPEAT_APPLY){
                            SRAX.scriptsCache[1][ind].place = script.place;
                            script = SRAX.cloneScript(SRAX.scriptsCache[1][ind]);                    
                        } else {
                            script = SRAX.makeScript('<script type="text/javascript">//no repeat '+script.src+'</'+'script>');
                        }
                    } else {
                        try{
                            if (SRAX.Data.thread[script.src] != null && SRAX.Data.thread[script.src].isProcess()) {
                                script = SRAX.Data.thread[script.src].options.callbackOps;
                            } else {
                                if (SRAX.Default.SCRIPT_NOAX || script['ax:noax']) script.xss = true; else new SRAX.startLoadScript(script, idLayer);
                            }
                        } catch (ex){
                            log(ex);
                        }
                    }
                }

                if (text.toLowerCase().indexOf('<body') != -1) {
                    scriptsHead[scriptsHead.length] = script; 
                } else {
                    scriptBody[scriptBody.length] = script;
                }
            }
        }

        if (text.length > 0) html[html.length] = text;

        return {
            'scriptsHead':scriptsHead,
            'scriptBody':scriptBody,
            'html':html
        }
    },


    /**
    * Функция завершения загрузки скрипта &lt;script>
    * @param {Object} resp объект ответ
    * @param {String} id id процесса загрузки скрипта
    */
    finishLoadScript : function(resp, id, status, oldScript) {
        var text = status ? resp.responseText : '';  
        var script = SRAX.makeScript('<script type="text/javascript">'+text+'</'+'script>');
        script.place = oldScript.place;
        script.id = id;
        var ind = SRAX.indexOfCacheSrc(SRAX.scriptsCacheTemp[0],id);
        if (ind == -1) ind = SRAX.scriptsCacheTemp[0].length;
        SRAX.scriptsCacheTemp[0][ind] = id;
        SRAX.scriptsCacheTemp[1][ind] = script;


        if (SRAX.Default.USE_SCRIPT_CACHE && SRAX.indexOfCacheSrc(SRAX.LIST_NO_CACHE_SCRIPTS,id) == -1) {
            ind = SRAX.indexOfCacheSrc(SRAX.scriptsCache[0],id);
            if (ind == -1) ind = SRAX.scriptsCache[0].length;
            SRAX.scriptsCache[0][ind] = id;
            SRAX.scriptsCache[1][ind] = SRAX.cloneScript(script);
        }


    },

    /**
    * Функция начала загрузки скрипта &lt;script>
    * @param {String} url URL адрес скрипта 
    */
    startLoadScript : function(script, idLayer) {
        try{
            getData(script.src, SRAX.finishLoadScript, script.src, script);
        } catch (ex){
            script.id = script.xss = script.src;
            //log(ex);
        }
    },
    

    /**
    * Функция клонирования скриптов
    * @param {Object} old объект скрипт
    * @return {Object} script объект скрипт
    */
    cloneScript : function(old, options){
        if (options == null) options = {};
        var script = document.createElement('script');        
        var params = ['src','type','language','defer','text','id','place'];
        for (var i = 0; i < params.length; i++){
            try{
                var val = old[params[i]];
                if (options[params[i]] != null) val = options[params[i]];
                if (val != null && val != '') script[params[i]] = val;        
            } catch (ex){}
        }
        return script;
    },


    /**
    * Функция рендеринга загруженной HTML страницы и применения скриптов
    * @param {String} idLayer id родительского элемента
    * @param {Array} scriptsHead скрипты блока <head>
    * @param {Array} scriptsBody скрипты блока <body>
    * @param {Array} html текст HTML
    * @param {String} url URL адрес запроса
    * @param {Boolean} isNew новые данные (true)  или рекурсия (false)
    * @param {Object} owner объект-владелец данного процеса
    */
    writeHtml : function (idLayer, scriptsHead, scriptBody, html, url, isNew, owner){

        var i = 0;
        SRAX.removeScripts(scriptsHead);
        SRAX.removeScripts(scriptBody);

        SRAX.Html.fireEvent(idLayer,'unload');
        if (isNew) SRAX.onUnloadBody(idLayer, url);        

        this.load = function() {

            if (i == scriptsHead.length) {
                new SRAX.loadBody(scriptBody, html, idLayer, url, isNew, owner);
                return;
            }
            
            if (scriptsHead[i].src != null && scriptsHead[i].src != '') {
                var ind = SRAX.indexOfCacheSrc(SRAX.scriptsCacheTemp[0],scriptsHead[i].src);
                if (ind != -1) {
                    scriptsHead[i] = SRAX.cloneScript(SRAX.scriptsCacheTemp[1][ind]);
                }
            }


            if ((scriptsHead[i].src == null || scriptsHead[i].src == '') && !(i >= 1 ? scriptsHead[i-1].inprocess : false)) {
                new SRAX.appendScript(scriptsHead[i], idLayer, url);
                SRAX.docWriteTraper.apply(idLayer);
                i++;
            } else {
                if (scriptsHead[i].src && !SRAX.xssLoading){
                     if (scriptsHead[i].loaded){
                        SRAX.docWriteTraper.apply(idLayer)
                        i++;
                     } else {
                        if (scriptsHead[i].xss) {
                            scriptsHead[i].xss = false;
                            new SRAX.appendScript(scriptsHead[i], idLayer, url);
                        }
                     }
                }
            }

            var _this = this;
            this.recall = function() {_this.load()};
            setTimeout(this.recall, 10);
        }

        this.load();

    },

    /**
    * Функция загрузки тела страницы
    * @param {Array} scripts скрипты блока <body>
    * @param {Array} html текст HTML
    * @param {idLayer} id родительского элемента
    * @param {String} url URL адрес скрипта 
    * @param {Boolean} isNew новые данные (true) или рекурсия (false)
    * @param {Object}  owner объект-владелец данного процеса
    */
    loadBody : function(scripts, html, idLayer, url, isNew, owner){
        var i = 0;
        var bodyId = (idLayer == null) ? 'document.body' : idLayer;

        if (SRAX.Model2Blocks[idLayer]){
            SRAX.paintHtml2(html.join(''), idLayer, url, isNew);
        } else {
            SRAX.paintHtml(html.join(''), idLayer, url, isNew);
        }
        
        if (isNew) SRAX.Effect.use(idLayer);
        
        this.checkload = function() {
            if (i >= scripts.length) {
                SRAX.docWriteTraper.apply(idLayer)
                if (!SRAX.xssLoading && !(i >= 1 ? (scripts[i-1].inprocess || scripts[i-1].countproc) : false)) {
                    if (isNew) SRAX.onLoadBody(idLayer, url);
                    SRAX.Html.fireEvent(idLayer,'load');
                    if (SRAX.Default.USE_FILTER_WRAP) {
                        var model2 = SRAX.Model2Blocks[idLayer];
                        if (model2){
                            for (var n in model2){
                                var layer = SRAX.get(model2[n]);
                                if (layer) SRAX.Filter.wrap(layer, url);
                            }
                        } else {
                            SRAX.Filter.wrap(idLayer, url);
                        }
                    }
                    if (owner) {
                        owner.inprocess = false;
                        if (owner.countproc) owner.countproc--;
                    }
                    SRAX.ContentTrigger.use(idLayer, url);
                    if (!SRAX.Html.ASYNCHRONOUS && SRAX.Html.storage[0] == idLayer){
                        SRAX.Html.storage.splice(0,1);
                        if (SRAX.Html.storage.length > 0) {
                            SRAX.Html.thread[idLayer]
                            SRAX.Html.thread[idLayer].request();
                        }
                    }
                    SRAX.showHaxLoading(0, idLayer);
                    return;
                }
            } else {
                if (scripts[i].src != null && scripts[i].src != '') {
                    var ind = SRAX.indexOfCacheSrc(SRAX.scriptsCacheTemp[0],scripts[i].src);
                    if (ind != -1) {
                        scripts[i] = SRAX.cloneScript(SRAX.scriptsCacheTemp[1][ind]);
                    }
                }

                if ((scripts[i].src == null || scripts[i].src == '') && !(i >= 1 ? scripts[i-1].inprocess : false)) {
                    new SRAX.appendScript(scripts[i], idLayer, url);
                    SRAX.docWriteTraper.apply(idLayer)
                    i++;
                } else {
                    if (scripts[i].src && !SRAX.xssLoading){
                         if (scripts[i].loaded){
                            SRAX.docWriteTraper.apply(idLayer)
                            i++;
                         } else {
                            if (scripts[i].xss) {
                                scripts[i].xss = false;
                                new SRAX.appendScript(scripts[i], idLayer, url);
                            }
                         }
                    }
                }
            }
            var _this = this;
            this.recall = function() {_this.checkload()};
            setTimeout(this.recall, 10);
        }
        this.checkload();

    },

    /**
    * Функция для эмуляции события onload
    * необходимо доработать - отслеживать окончание загрузки ресурсов, которые подключенные через document.write
    * @param {String} idLayer id родительского элемента
    * @param {String} url URL адрес скрипта 
    */
    onLoadBody : function(idLayer, url){
        if (SRAX.LoadUnloadContainer[idLayer].onload) {
            SRAX.parsingText({id:idLayer, url:url, text:'<script id="ax:script:temp" type="text/javascript">'+SRAX.LoadUnloadContainer[idLayer].onload+'</'+'script>', isNew:false});
        }
        if (SRAX.isCOL){
            if (window.onload) window.onload(); 
            window.onload = null; 
        }
    },
    
    
    /**
    * Функция для захвата события window.onload, определенного с помощью javascript <br>
    * желательно вызывать данную функцию перед закрывающим тегом </body>, т.е. в самую последную очередь
    */
    captureOnLoad : function(){
        window.prevonload = window.onload; 
        window.onload = function(){    
            if (window.prevonload) window.prevonload();
            window.prevonload = null;
            window.onload = null;    
        }

        window.addEventListener = function(event, handler){
          window['on'+event] = handler;           
        }
        window.attachEvent = function(event, handler){
          window[event] = handler;           
        }
        SRAX.isCOL = true;
    },

    /**
    * Функция для эмуляции события onload
    * необходимо доработать - отслеживать окончание загрузки ресурсов, которые подключенные через document.write
    * @param {String} idLayer id родительского элемента
    * @param {String} url URL адрес скрипта 
    */
    onUnloadBody : function(idLayer, url){
        if (SRAX.LoadUnloadContainer[idLayer].onunload) {
            eval(SRAX.LoadUnloadContainer[idLayer].onunload);
        }
    },

    /**
    * Функция для рендеринга HTML в заданном элементе - при запросе каждого блока по отдельности 
    * @param {String} html текст HTML
    * @param {String} idLayer id родительского элемента
    * @param {Boolean} isNew новые данные (true) или рекурсия (false) 
    */
    paintHtml : function(html, idLayer, url, isNew){
        var options = {
            html : html, 
            url : url
        }
        if (isNew) {
            if (SRAX.Html.fireEvent(idLayer,'beforepaint', options) !== false){
                SRAX.PaintHtmlEvent.use(idLayer);
                SRAX.writeTo(html, idLayer);
                SRAX.Html.fireEvent(idLayer,'afterpaint', options);
                SRAX.PaintHtmlEvent.use(idLayer, true);
            }
        } else {
            if (SRAX.Html.fireEvent(idLayer,'beforepaintadd', options) !== false){
                SRAX.addTo(html, idLayer);
                SRAX.Html.fireEvent(idLayer,'afterpaintadd', options);
            }
        }
    },

    /**
    * Функция для рендеринга HTML в заданном элементе - при запросе всех блоков в одном потоке (модель запроса #2 - специально разработанно для Joomla)
    * @param {String} html текст HTML
    * @param {String} idLayer id родительского элемента
    * @param {Boolean} isNew новые данные (true) или рекурсия (false) 
    */
    paintHtml2 : function(html, idLayer, url, isNew){
        var blocks = SRAX.Model2Blocks[idLayer];
        var ind1 = html.indexOf(SRAX.Default.model2Marker.ax);
        var ind2 = html.indexOf(SRAX.Default.model2Marker.begin, ind1+1);
        var ind3 = html.indexOf(SRAX.Default.model2Marker.ax, ind2+1);
        var ind4 = html.indexOf(SRAX.Default.model2Marker.end, ind3+1);
        while (ind1 != -1 && ind2 != -1 && ind3 != -1 && ind4 != -1){
            var id = html.substring(ind1 + SRAX.Default.model2Marker.ax.length, ind2);
            var text = html.substring(ind2 + SRAX.Default.model2Marker.begin.length, ind3);
            if (blocks[id]) {
                var options = {
                    html : text, 
                    url : url,
                    id:id,
                    block:blocks[id]
                }
                if (isNew){
                    if (SRAX.Html.fireEvent(idLayer,'beforepaint', options) !== false){
                        SRAX.PaintHtmlEvent.use(blocks[id]);
                        SRAX.writeTo(text, blocks[id]);
                        SRAX.Html.fireEvent(idLayer,'afterpaint');
                        SRAX.PaintHtmlEvent.use(blocks[id], true);
                    }
                } else {
                    if (SRAX.Html.fireEvent(idLayer,'beforepaintadd', options) !== false){
                        SRAX.addTo(text, blocks[id]);
                        SRAX.Html.fireEvent(idLayer,'afterpaintadd');
                    }
                }
            }
            ind1 = html.indexOf(SRAX.Default.model2Marker.ax, ind4+1);
            ind2 = html.indexOf(SRAX.Default.model2Marker.begin, ind1+1);
            ind3 = html.indexOf(SRAX.Default.model2Marker.ax, ind2+1);
            ind4 = html.indexOf(SRAX.Default.model2Marker.end, ind3+1);
        }

    },

    /**
    * Обьект-ловушка для обработки document.write и document.writeln
    */
    docWriteTraper : new function(){
        var scripts = {};
        var urls = {};
        var texts = {};
        
        this.add = function(text, id, url, script){
            if (script.inprocessTO) clearTimeout(script.inprocessTO);
            script.inprocess = true;
            scripts[id] = script;
            urls[id] = url;
            if (!texts[id]) texts[id] = '';
            texts[id] += text;
            this.checkMutiLine(id);
        }

        //проверка на возможность мультилинейного использования write для внедрения в документов тегов
        //первая временная реализация - нужна доработка
        this.checkMutiLine = function(id){
            var text = texts[id];
            var ind1 = text.indexOf('<');
            while (ind1 != -1){
                var n = 1;
                var s = text.charAt(ind1+n).trim();
                while(s != '' && s != '>'){
                    if (s == '/' && text.charAt(ind1+n+1) == '>') {
                        this.apply(id);
                        return;
                    }
                    s = text.charAt(ind1+(++n)).trim();
                }
                var tag = text.substring(ind1+1,ind1+n);
                var ind2 = text.indexOf('</'+tag+'>', ind1);
                if (ind2 > -1) {
                    this.apply(id);
                    break;
                } else {
                    var ind3 = text.indexOf('>', ind1+1+tag.length);
                    if (ind3 != -1 && (tag == 'img' || tag == 'input' || tag == 'br' || tag == 'hr')){
                        this.apply(id);
                        return;
                    }
                    ind1 = text.indexOf('<', ind1+1);
                }
            }
        }

        this.apply = function(id){
            if (!texts[id]) return;
            var text = texts[id];
            delete texts[id];
            if (!scripts[id].countproc) scripts[id].countproc = 1; else scripts[id].countproc++;
            SRAX.parsingText({text:text, id:scripts[id].place, url:urls[id], isNew:false, owner:scripts[id]});
            var layer = SRAX.get(scripts[id].place);
            if (layer) {
                var parent = layer.parentNode;
                while (layer.childNodes.length > 0){
                    parent.insertBefore(layer.firstChild, layer);
                }
            }
        }

        this.applyAll = function(){
            for (var i in texts){
                if (texts[i]) SRAX.docWriteTraper.apply(i);
            }
        }
    },

    /**
    * Функция применения скрипта путем добавления его в элемент head
    * @param {Object/String} script объект скрипт / входной параметр типа String, тогда обозначает URL адрес скрипта
    * @param {String/Function} idLayer id родительского элемента / если входной параметр script типа String, тогда idLayer обозначает callback функцию, которая выполнится после загрузки скрипта
    * @param {String/Boolean} url URL адрес скрипта / если входной параметр script типа String, тогда url обозначает флаг отключения использования AJAX загрузки скрипта
    */
    appendScript : function(script, idLayer, url) {
        if (typeof script == 'string'){
            var scripts = document.getElementsByTagName('script');
            var regexp = eval('/(?:SRAX.appendScript)((\\n|\\r|.)*?)(?:'+script.replaceAll('/','\\/')+')/ig');
            var place = null; 
            for (var i = 0; i < scripts.length; i++){
                if (regexp.exec(scripts[i].innerHTML)){
                    place = scripts[i];
                    break;
                }
            }
            var span = document.createElement('span');
            span.callback = idLayer ? idLayer : function(){};
            span.id = SRAX.genId();
            span.style.display = 'none';
            span['ax:script:mark'] = 1;
            if (place) place.parentNode.insertBefore(span, place); else document.body.appendChild(span);
            SRAX.parsingText({id:span.id, url:script, text:'<body onload="id(\''+span.id+'\').callback()"><script type="text/javascript" src="'+script+'"'+(url?' ax:noax="1"':'')+'></script></body>', isNew:true});
            return;
        }
        /**
        * Ловушка для обработки document.write и document.writeln
        * @param {text} текст-тело скрипта
        * @return {text} текст-тело скрипта
        */
        SRAX.docWriteTraper.apply(idLayer);
        document.write = function(text){
            SRAX.docWriteTraper.add(text, idLayer, url, script);
        }

        document.writeln = function(text){
            document.write(text+'\n');
        }

        if (SRAX.Default.DEBUG_SCRIPT) {
            var ids = script.id;
            if (!ids || ids == '') ids = script.innerHTML.trim().substring(0,100) + '\n...';
            log('append script -> ' + ids);
        }

        if (script.src) {
            script.inprocess = true; 
            SRAX.xssLoading = true;            
            script.onload = script.onreadystatechange = function(){
                if (this.loaded) return;
                this.loaded = true;
                this.onload = this.onreadystatechange = null;
                var _this = this;
                SRAX.xssLoading = false;
                this.inprocessTO = setTimeout(function(){
                    _this.inprocess = false;                    
                }, 100);
            }
        }
        
        var head = document.getElementsByTagName('head')[0];
        head.appendChild(script); 
    },

    /**
    * Функция немедленного исполнения скрипта через eval
    * @param {String} text текст скрипта
    */
    evalScript : function(text) {
        try{
            if (SRAX.browser.safari){
                window._evalCode = text;
                new SRAX.appendScript(SRAX.makeScript('<script type="text/javascript">eval(window._evalCode)</script>'));
            } else
            if (window.execScript) window.execScript(text); else window.eval(text);
        } catch (ex){
            error(ex);
            return false;
        }
        return true;
    },
    
    /**
    * Функция удаления скриптов
    * @param {Array} scripts объекты скрипты
    */
    removeScripts : function(scripts) {
        var head = document.getElementsByTagName('head')[0];
        var includedScripts = head.getElementsByTagName('script');
        if (includedScripts == null) return;
        for (var i = 0; i <= scripts.length; i++){
            if (i != scripts.length && typeof scripts[i] == 'string') continue;
            var id = 'ax:script:temp';
            if (i != scripts.length){
                id = scripts[i].id;
            }
            if (id != null && id != '') {
                for (var j = 0; j < includedScripts.length; j++){
                    if (includedScripts[j].id == id) {
                        if (SRAX.Default.DEBUG_SCRIPT) log('remove script ' + includedScripts[j].id);
                        head.removeChild(includedScripts[j]);                    
                        break;
                    }
                }
            } else {
                for (var j = 0; j < includedScripts.length; j++){   
                    if (includedScripts[j].innerHTML == scripts[i].innerHTML) {
                        if (SRAX.Default.DEBUG_SCRIPT) log('remove script ' + scripts[i].innerHTML);
                        head.removeChild(includedScripts[j]);                    
                        break;
                    }
                }                
            }
        }
    },
   
    /**
    * Объект процесса запроса HTML
    * @param {String} idLayer id родительского элемента, в который вставляется результат запроса HTML (если null - тогда в document.body)
    */
    HTMLThread : function(idLayer){
        if (idLayer == null) idLayer = SRAX.Default.mainLayer;
        var xmlhttp;

        var _this = this;

        this.inprocess = false;
        this.id = idLayer;
        var ops = this.options = {};

        SRAX.Html.thread[idLayer] = this;
        SRAX.Html.register(this);

        this.repeat = function(form, nohistory, params){
            ops.form = form;
            ops.nohistory = nohistory;
            ops.params = params;
            _this.request();
        }

        this.setOptions = function(options, overwrite){
            if (overwrite) {
                ops = options; 
                if (ops.async == null) ops.async = true;
            } else {
                for (var i in options) ops[i] = options[i];
            }
            this.options = ops;
        }

        this.getOptions = function(){
            return ops;
        }
        
        this.isProcess = function (){
            return _this.inprocess;
        }       
        
        this.request = function(){
            var method = (ops.method && ops.method.toLowerCase() == 'post') ? 'post':'get';
            try{
                var options = {
                    html:ops.html,
                    url:ops.url,
                    id:idLayer,
                    form:ops.form,
                    nohistory:ops.nohistory,
                    params:ops.params,
                    method:method
                }
                if (_this.fireEvent('beforerequest', options) !== false){
                    var action = function() {
                        var body = SRAX.createQuery(ops.form);
                        if (ops.params) {
                            if (body != '' && !ops.params.startWith('&')) body += '&';
                            body += ops.params; 
                        }
                        if (method != 'post' && body != '') {
                            if (ops.url.indexOf('?') == -1){
                                ops.url += '?' + body
                            } else {
                                ops.url += ((ops.url.endWith('?') || ops.url.endWith('&')) ? '' : '&') + body
                            }
                        }
                        if (_this.inprocess) _this.abort();
                        _this.inprocess = true;
                        var ind = location.href.indexOf('#');
                        var href = (ind == -1) ? location.href : location.href.substring(0, ind);
                        var useAnticache = href.endWith(ops.url) || (ops.anticache != null ? ops.anticache : SRAX.Default.HAX_ANTICACHE);
                        ind = HTMLHistory.getIndex(ops.url);
                        if (!ops.html && !useAnticache && ind != -1){
                            ops.html = HTMLHistory.storage[ind][1];
                        }
                        if (ops.html){
                            processRequest({readyState:4,status:200,responseText:ops.html})
                            ops.html = null;
                        } else {
                            if (!xmlhttp) xmlhttp = init();
                            xmlhttp.open(method.toUpperCase(), ops.url, ops.async);
                            xmlhttp.onreadystatechange = processRequest;
                            if (ops.cut) xmlhttp.setRequestHeader('AJAX_CUT_BLOCK', ops.cut);
                            if (useAnticache) xmlhttp.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT');
                            xmlhttp.setRequestHeader('AJAX_ENGINE', 'Fullajax');
                            xmlhttp.setRequestHeader('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest');
                            if (method == 'post') xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; Charset=' + SRAX.Default.CHARSET);            
                            xmlhttp.send((method == 'post') ? body : null);
                        }
                        SRAX.showHaxLoading(_this.inprocess, idLayer);
                        if (SRAX.Default.DEBUG_AJAX) log(method + ' ' + ops.url + ' params:' + body + ' id:' + idLayer);
                    };
                    if (!SRAX.Effect.use(idLayer, true, action)) action();
                    _this.fireEvent('afterrequest', options)
                }
            } catch (ex){
                _this.abort();
                throw ex;
            }
        }
  
        this.abort = function(){
            _this.inprocess = false;
            if (!xmlhttp) return;
            xmlhttp.isAbort = true;
            try{
                xmlhttp.abort();
            } catch (ex){}
            xmlhttp = null;
        }
        
        this.destroy = function(){
            SRAX.Html.thread[idLayer] = null;
            delete SRAX.Html.thread[idLayer];
        }

        function init() {
            if (window.ActiveXObject) {
                try {
                    var http = null;
                    for (var i = 0; i < IE_ENGINE.length; i++){
                        try {
                            http = new ActiveXObject(IE_ENGINE[i]);
                            if (http != null) break;
                        } catch (e){}
                    }
                    return http;
                } catch (e) {}
            } else
            if (window.XMLHttpRequest) {
                return new XMLHttpRequest();
            }
        }

        function processRequest(obj) {   
          if (!obj || !obj.readyState) obj = xmlhttp;
            try{
                if (obj.readyState == 4) {
                    var status = obj.isAbort ? -1 : obj.status;
                    var success = (status >= 200 && status < 300) || status == 304 || (status == 0 && location.protocol == 'file:');

                    _this.fireEvent('response',
                         {response:obj,
                         url:ops.url,
                         id:idLayer,
                         status:status,
                         success:success, 
                         callbackOps:ops.callbackOps}
                    )

                    if (status != -1 && SRAX.HtmlPreprocessor({xmlhttp:obj, thread:_this}) !== false) {
                        if (ops.callback) {
                            ops.callback(obj, idLayer, success, ops.callbackOps);
                            if (SRAX.Default.DEBUG_AJAX) log('callback id:' + idLayer);
                        }
                        _this.inprocess = false;
                        if (success) {
                            var text = obj.responseText;
                            if (text) {
                                HTMLHistory.add(ops.url, text);
                                _this.inprocess = true;
                                SRAX.parsingText({owner:_this, text:text, id:idLayer, url:ops.url, isNew:true, rc:ops.rc, seal:ops.seal})
                            } else {
                                warn('empty response: ' + idLayer + ' => ' + ops.url);
                                SRAX.Effect.use(idLayer);
                            }
                            if (SRAX.Default.DEBUG_AJAX) log('response ok:' + ops.url);
                        } else {
                            SRAX.showMessage(ops.url, obj.status, obj.statusText);
                            SRAX.Effect.use(idLayer);
                        }
                    }

                    SRAX.showHaxLoading(_this.inprocess, idLayer);
                    if ((ops.destroy != null) ? ops.destroy : SRAX.Default.HAX_AUTO_DESTROY){
                         _this.destroy();
                    }
                } 
            } catch (ex){
                log(ex);
                _this.fireEvent('exception',
                     {response:obj,
                     url:ops.url,
                     id:idLayer,
                     exception:ex,
                     options:ops}
                )
                SRAX.Effect.use(idLayer);
                _this.inprocess = false;
                SRAX.showHaxLoading(_this.inprocess, idLayer);
                if ((ops.destroy != null) ? ops.destroy : SRAX.Default.HAX_AUTO_DESTROY){
                     _this.destroy();
                }
            }
        }
        
        var HTMLHistory = this.history = {
            storage : [],        
            
            startPageHtml : null,

            startPageUrl : null,

            current : 0,

            currentUrl : function(){
                if (this.storage.length == 0 || this.current <= 0) return null;
                return this.storage[HTMLHistory.current][0]
            },
            
            add : function (loc, data) {
                this.current++;
                var host = location.host;
                if (loc.href) loc = loc.href;
                var ind = loc.indexOf(host);
                if (ind != -1) loc = loc.substring(ind + host.length);
                    
                loc = SRAX.replaceLinkEqual(loc);
                if (ops.startpage){
                    ops.startpage = false;
                    HTMLHistory.startPageHtml = data;
                    HTMLHistory.startPageUrl = loc;
                    SRAX.History.setCurrent(location.hash);
                }
                var useHist = !(ops.nohistory != null ? ops.nohistory : SRAX.Default.NO_HISTORY);
                if (useHist) {
                    if (HTMLHistory.startPageHtml == null) {
                        var html = ['<head><title>'+document.title+'</title></head>']; 
                        var model2 = SRAX.Model2Blocks[idLayer];
                        if (model2){
                            for (var i in model2){
                                var layer = SRAX.get(model2[i]);
                                if (layer) html.push(SRAX.Default.model2Marker.ax + i + SRAX.Default.model2Marker.begin + layer.innerHTML + SRAX.Default.model2Marker.ax + i + SRAX.Default.model2Marker.end);                                
                            }
                        } else {
                            var layer = SRAX.get(idLayer);
                            if (layer == null) layer = document.body;
                            html.push(layer.innerHTML);
                        }
                        HTMLHistory.startPageHtml = html.join('');
                        HTMLHistory.startPageUrl = location.href;
                    }
                    SRAX.History.add(idLayer, loc);
                }


                if (this.current > SRAX.Default.LENGTH_HISTORY_CACHE){
                    this.current--;
                    this.storage.splice(0,1);
                }

                this.storage.length = this.current; 
                this.storage[this.storage.length] = [SRAX.replaceLinkEqual(loc, true), data];
            }, 

            get : function (val) {
                return this.storage[val];
            },

            getIndex : function(loc){
                for (var i = 0; i < this.storage.length; i++){                
                    if (this.storage[i] != null && loc == this.storage[i][0]) {
                        return i;
                    }
                }
                return -1;
            }

        }
  
        this.go2History = function(loc){
            if (HTMLHistory.currentUrl() != loc) {
                var uhc = ops.historycache != null ? ops.historycache : SRAX.Default.USE_HISTORY_CACHE;
                if (!uhc || !this.go2UrlHistory(loc)) {
                    loc = SRAX.replaceLinkEqual(loc, true);
                    var options = {
                        url: loc,
                        nohistory:true
                    }
                    this.setOptions(options);
                    this.request();
                }
            }
        }
        
        this.go2UrlHistory = function(loc) {   
            var ind = HTMLHistory.getIndex(loc);
            if (ind != -1) {
                this.go(ind - HTMLHistory.current);
                SRAX.History.setCurrent(location.hash);
                return true;
            }
        }

        this.go = function(val) {   
            var curr = HTMLHistory.current + val; 
            if (curr < 0) curr = 0; else if (curr > HTMLHistory.storage.length - 1) curr = HTMLHistory.storage.length - 1;
            if (curr == 0) return HTMLHistory.go2StartPage();
            HTMLHistory.current = curr;
            var text = HTMLHistory.storage[curr][1];
            if (text) SRAX.parsingText({owner:_this, text:text, id:idLayer, url:HTMLHistory.storage[curr][0], isNew:true, rc:ops.rc, seal:ops.seal});
        },

        this.go2StartPage = function(){
            if (HTMLHistory.startPageHtml) SRAX.parsingText({owner:_this, text:HTMLHistory.startPageHtml, id:idLayer, url:HTMLHistory.startPageUrl, isNew:true, rc:ops.rc, seal:ops.seal});
            HTMLHistory.current = 0;
        }

        this.getSrartPageUrl = function(){
            return HTMLHistory.startPageUrl;
        }
    },

    /**
    * Функция проверки наличия прямой ссылки
    * @return {Boolean} результат проверки
    */
    directLink: function(){
        var ind = location.href.indexOf('#');        
        if (ind != -1){
            location.href = location.href.substring(0, ind) + SRAX.replaceLinkEqual(location.href.substring(ind))
        }

        SRAX.History.setCurrent(location.hash);
        return SRAX.go2Hax(true, location.hash);
    },

    /**
    * Функция перехода по аякс ссылке
    * @return {Object} объект аякс ссылки
    */
    go2Hax : function(startPage, href){
        var prevAx = SRAX.parseAx(SRAX.History.previous);
        if (!href) href = SRAX.History.current;
        var curAx = SRAX.parseAx(href);
        var i = 0;
        var options = {
            oldHash:SRAX.History.previous,
            newHash:SRAX.History.current
        }
        for (var id in curAx){
            i++;
            if (prevAx[id] == curAx[id]) {
                prevAx[id] = null;
                continue;
            }
            prevAx[id] = null;
            options.id = id;
            options.url = curAx[id];
            if (SRAX.Html.fireEvent(id, 'beforehistorychange', options) === false) continue;
            if (SRAX.Html.thread[id] == null) {
                var url = SRAX.replaceLinkEqual(curAx[id], true);
                var obj = SRAX.parseUri(url);
                var options = SRAX.Filter.getOptions(obj.path, obj.query);
                if (options == null) options = {};
                hax(url, {id:id, nohistory:startPage, startPage:startPage, rc:options.rc});
            } else {
                var action = function(){
                    SRAX.Html.thread[id].go2History(curAx[id]);
                }
                if (!SRAX.Effect.use(id, true, action)) action();
            }
        }
        
        for (var id in prevAx){
            if (prevAx[id] && SRAX.Html.thread[id]) {
                options.id = id;
                options.url = SRAX.Html.thread[id].getSrartPageUrl();
                options.startpage = true;
                if (SRAX.Html.fireEvent(id, 'beforehistorychange', options) === false) continue;
                var action = SRAX.Html.thread[id].go2StartPage;
                if (!SRAX.Effect.use(id, true, action)) action();
            }
        }
        curAx.size = i;
        return curAx;
    },

    /**
    * Функция формирования ax ссылок
    * @param {String} hash строка-якорь адреса 
    * @param {String} id индентификатор ссылки
    * @param {String} url URL ссылки
    * @param {String} prefix префикс ссылки
    * @return {String} результирующий hash
    */
    makeAx : function(hash, id, url, prefix){
        if (!prefix) prefix = 'ax'
        var axid = ':'+prefix+':' + id + ':';
        var ind2 = hash.indexOf(axid);
        if (ind2 != -1) {
            var oldUrl = hash.substring(ind2);
            var ind3 = oldUrl.indexOf(':',ind2+axid.length+1);
            if (ind3 != -1) oldUrl = oldUrl.substring(0,ind3);
            hash = hash.replace(oldUrl,axid + url);
        } else {
            hash += axid + url;
        }
        return hash;
    },

    /**
    * Функция парсинга ax ссылок
    * @param {String} href адрес    
    * @param {String} prefix префикс ссылки
    * @return {Object} объект слой-ссылка
    */
    parseAx : function (href, prefix){
        if (!prefix) prefix = 'ax'
        var locAx = {};
        if (href == null) return locAx;
        href = SRAX.replaceLinkEqual(href, true);
        var ind1 = href.indexOf(':'+prefix+':');
        while (ind1 != -1){
            var idLayer;
            var ind2 = href.indexOf(':', ind1+prefix.length+2);
            if (ind2 != -1) idLayer = href.substring(ind1 + prefix.length+2, ind2); else ind2 = ind1;
            ind1 = href.indexOf(':'+prefix+':',ind2 + 1);
            var loc = href.substring(ind2+1);
            var ind3 = loc.indexOf(':');
            if (ind3 != -1) loc = loc.substring(0,ind3);
            if (loc != null && loc != '' && idLayer != null) {
                locAx[idLayer] = loc;
            }
        }
        return locAx;
    },


    /**
    * Объект - менеджер истории
    */
    History : {
        /**
        * Предыдущий hash адрес страницы 
        */
        previous:null,

        /**
        * Текущий hash адрес страницы 
        */
        current:null,

        /**
        * Метод для установки текущего hash адреса 
        * @param {hash} текущий hash адрес страницы 
        */
        setCurrent : function(hash){
            SRAX.History.previous = SRAX.History.current;
            SRAX.History.current = hash;
        },

        prefixListener : {
        },

        check : function(){   

            if (SRAX.browser.msie) {
                if (SRAX.History.frame != null) {
                    var inner = SRAX.replaceLinkEqual(SRAX.History.frame.contentWindow.document.body.innerText);
                    if (inner != SRAX.History.current){
                        //location.href = inner;
                        location.hash = inner;
                    }
                }
            }

            var hash = SRAX.replaceLinkEqual(location.hash);

            if (SRAX.History.current != null && hash != SRAX.History.current){
                SRAX.History.setCurrent(hash);
                for (var i in SRAX.History.prefixListener){
                    SRAX.History.prefixListener[i]();
                }
            }

        }, 

        add : function(id, loc, prefix){
            var hash = SRAX.replaceLinkEqual(location.hash, true);                
            hash = SRAX.makeAx(hash, id, loc, prefix);
            var rhash = SRAX.replaceLinkEqual(hash);
            var res = SRAX.History.fireEvent('beforeadd', {
                hash:hash,
                rhash:rhash,
                id:id,
                loc:loc,
                prefix:prefix
            })
            if (res === false) return; else
            if (typeof res == 'string') rhash = SRAX.replaceLinkEqual(res);

            location.hash = rhash;
            if (SRAX.browser.msie || SRAX.browser.safari){
                var frame = SRAX.History.frame;
                if (frame == null) {
                    //Отключенн хак истории для Safari, потому как в версии Safari 3.0.4 история работает аналогично Firefox
                    /*
                    if (SRAX.browser.safari){
                        frame = document.createElement('form');
                        frame.method = 'get';
                        document.body.insertBefore(frame,document.body.firstChild);                        

                        var action = '';
                        if (SRAX.History.previous) action = SRAX.History.previous;

                        frame.action = action;
                        frame.submit();
                    } else 
                    */
                    if (SRAX.browser.msie) {                  
                        frame = document.createElement('iframe');
                        frame.style.display = 'none';
                        document.body.appendChild(frame);

                        var content = frame.contentWindow ? frame.contentWindow : frame.contentDocument;
                        var doc = content.document;
                        doc.open();
                        var innerHTML = '';
                        if (SRAX.History.previous) innerHTML = SRAX.History.previous;

                        if (!innerHTML.startWith('#')) innerHTML = '#' + innerHTML;
                        doc.write(innerHTML);
                        doc.close();
                        doc.body.innerHTML = innerHTML;
                    }
                    SRAX.History.frame = frame;
                }         
                /*
                if (SRAX.browser.safari && false){
                    frame.action = location.hash;
                    frame.submit();
                } else 
                */
                if (SRAX.browser.msie) {
                    var content = frame.contentWindow ? frame.contentWindow : frame.contentDocument;
                    var doc = content.document;
                    doc.open();
                    doc.write(location.hash);
                    doc.close();
                    doc.body.innerHTML = location.hash;
                }
            }
            SRAX.History.setCurrent(location.hash);
        }

    },
 
    
    /**
    * Объект квази-AJAX аплоадер файлов<br><br>
    *
    * пример использования <br>
    * &lt;form action="/upload.jsp" method="post" enctype="multipart/form-data" onsubmit="new SRAX.Uploader(this, startCallback, finishCallback)"> <br>
    * &nbsp;&nbsp;&nbsp;&nbsp;  &lt;input type="file" name="form[file]" /> <br>
    * &lt;/form>
    *
    * @param {String/Element} form id формы или сама форма 
    * @param {Function} beforeStart выполняемая функция до начала загрузки
    * @return {Function} afterFinish выполняемая функция после окончания загрузки
    * @param {Boolean} manual флаг старта загрузки вручную (form.submit())
    */
    Uploader : function(form, beforeStart, afterFinish, manual){
        var container; 
        var iframe = null;
        this.init = function() {
            form = SRAX.get(form);
            var id = SRAX.genId();
            form.setAttribute('target', id);
            container = document.createElement('div');
            container.innerHTML = '<iframe style="display:none" src="about:blank" onload="this._onload()" id="'+id+'" name="'+id+'"></iframe>';
            this.iframe = iframe = container.firstChild;
            this.iframe.uploader = this;

            this.setAfterFinish = setAfterFinish = function(afterFinish){
                iframe._onload = function(){
                    var content = this.contentWindow ? this.contentWindow : this.contentDocument;
                    var body = content.document.body;
                    var text = body[SRAX.browser.msie ? 'innerText' : 'textContent'];
                    afterFinish(text);
                }
            }

            if (afterFinish) {
                var set = function(){
                    setAfterFinish(afterFinish);
                    if (manual) form.submit()
                }
                if (manual) iframe._onload = set; else set(); 
            } else iframe._onload = function(){};
            form.appendChild(container);
            form.setAttribute('target', id);
            if (beforeStart) beforeStart();
        }
        
        this.init();

        this.getIframe = function(){
            return iframe;
        }

        this.destroy = function(){
            form.removeChild(container);
        }
        
    },

    /**
    * Объект триггер контекста<br><br>
    *
    * пример: <br>
    * SRAX.ContentTrigger.add({id:'header', handler:myFunction, options:{'opt1':'val1'}});
    *
    */
    ContentTrigger : {
        triggers : {},
        
        add : function(options){
            if (options == null) options = {};
            if (options.id == null) options.id = 'document.body';

            var arr = SRAX.ContentTrigger.triggers[options.id];
            if (arr == null) arr = [];
            arr[arr.length] = options;
            SRAX.ContentTrigger.triggers[options.id] = arr;
        },

        get : function(id){
            if (id == null) id = 'document.body';
            for (var el in SRAX.ContentTrigger.triggers){
                if (el == id || el == '*') return SRAX.ContentTrigger.triggers[el];
            }        
        }, 

        use : function(id, url){
            var trigger = SRAX.ContentTrigger.get(id);
            if (trigger) {
                for (var i = 0; i < trigger.length; i++){
                    if (trigger[i] && trigger[i].handler) trigger[i].handler(url, trigger[i].options);
                }
            }
        }
        
    },
    
    /**
    * Объект эффект - для добавления эффекта изменения контента <br><br>
    *
    * пример: <br>
    * SRAX.Effect.add({id:'center', <br>
    * &nbsp;&nbsp;  start:function(id, options){ <br>
    * &nbsp;&nbsp;&nbsp;&nbsp;      Ext.get(id).fadeOut({ endOpacity: .25, duration: 2}); <br>
    * &nbsp;&nbsp;  }, <br>
    * &nbsp;&nbsp;  end:function(id, options){ <br>
    * &nbsp;&nbsp;&nbsp;&nbsp;      Ext.get(id).fadeIn({ endOpacity: .75, duration: 2}); <br>
    * &nbsp;&nbsp;  } <br>
    * }); <br><br>
    *
    * Список параметров: <br>
    * id - id блока <br>
    * start - функция эффекта при начале запроса контента  <br>
    * end - функция начала после окончания запроса контента  <br>
    *
    */

    Effect : {
        effects : {},
        
        add : function(options){
            if (options == null) options = {};
            if (options.id == null) options.id = 'document.body';

            var arr = SRAX.Effect.effects[options.id];
            if (arr == null) arr = [];
            arr[arr.length] = options;
            SRAX.Effect.effects[options.id] = arr;
        },

        get : function(id){
            if (id == null) id = 'document.body';
            for (var el in SRAX.Effect.effects){
                if (el == id || el == '*') return SRAX.Effect.effects[el];
            }        
        }, 

        use : function(id, start, callback){
            var effect = SRAX.Effect.get(id);
            var exist = false;
            if (effect) {
                exist = true;
                for (var i = 0; i < effect.length; i++){
                    var func = (i == effect.length - 1) ? callback : null;
                    if (start) {                
                        if (effect[i] && effect[i].start) effect[i].start(id, func);
                    } else {
                        if (effect[i] && effect[i].end) effect[i].end(id, func);
                    }
                }
            }
            return exist;
        }

    },

    /**
    * Менеджер событий прорисовки HTML контента <br><br>
    *
    * пример: <br>
    * SRAX.PaintHtmlEvent.add({id:'menu', handler:function(options){alert('Menu Update')}, after:true}); <br><br>
    *
    * Список параметров: <br>
    * id - id блока <br>
    * handler - функция, вызываемая при данном событии <br>
    * after - false (по умолчанию) - вызывется перед изменением или false - вызывется после изменения контента <br>
    *
    */
    PaintHtmlEvent : {
        events : {},

        add : function(options){
            if (options == null) options = {};
            if (options.id == null) options.id = 'document.body';

            var arr = SRAX.PaintHtmlEvent.events[options.id];
            if (arr == null) arr = [];
            arr[arr.length] = options;
            SRAX.PaintHtmlEvent.events[options.id] = arr;
        },

        get : function(id){
            if (id == null) id = 'document.body';
            for (var el in SRAX.PaintHtmlEvent.events){
                if (el == id || el == '*') return SRAX.PaintHtmlEvent.events[el];
            }        
        }, 

        use : function(id, after){
            var events = SRAX.PaintHtmlEvent.get(id);
            if (events) {
                for (var i = 0; i < events.length; i++){
                    if (events[i] && events[i].handler) {
                        if ((!after && !events[i].after) || (after && events[i].after)) events[i].handler(events[i].options);
                    }
                }
            }
        }

    },

    /**
    * Объект фильтр ссылок - для "авто-заворачивания" в AJAX <br><br>
    *
    * пример: <br>
    * SRAX.Filter.add({'id':'header','url':'header'});  <br><br>
    *
    * Список параметров: <br>
    * id - id блока  <br>
    * url - ссылка <br>
    * urlType - 'contain' (по умолчанию) или 'start' или 'end' - соответсвенно содержит, начинаеться или заканчиваеться  <br>
    * query - строка запроса  <br>
    * queryType - 'contain' (по умолчанию) или 'start' или 'end' - соответсвенно содержит, начинаеться или заканчиваеться  <br>
    * joinLogic - логика обьединения url и query - 'or' (по умолчанию) или 'and'  <br>
    * urlChanger - функция изменения обволакиваемой ссылки urlChanger: function(url){return url.replace('index.php', 'mypage.php')}  <br>
    * target - true заворачивать ссылки с атрибутом target (_self,  _parent, _top, _blank) или false - не заворачивать (по умолчанию) <br>
    * type - если = 'data', тогда для запроса используется dax, иначе используется hax <br>
    * handler - функция обработки клика, если не указана, тогда используеться функция в соответсвии с type <br>
    *
    */
    Filter : {
        shema : {},
        
        add : function(options){
            if (options == null) options = {};
            if (options.id == null) options.id = 'document.body';

            var arr = SRAX.Filter.shema[options.id];
            if (arr == null) arr = [];
            arr[arr.length] = options;
            SRAX.Filter.shema[options.id] = arr;
        },

        remove : function (options){
            if (options == null) options = {};
            if (options.id == null) options.id = 'document.body';
            
            var arr = SRAX.Filter.shema[options.id];
            if (arr == null) return;
            arrayRemoveOf(arr, options.url);
            SRAX.Filter.shema[options.id] = arr;
        },
        
        clear : function(id){
            if (id == null) id = 'document.body';
            SRAX.Filter.shema[id] = null;
        },

        clearAll : function(){
            for (var el in SRAX.Filter.shema){
                SRAX.Filter.shema[el] = null;
            }
        },

        getOptions : function(url, query, owner){
            var options = null;
            var lengthEquals = 0;
            for (var el in SRAX.Filter.shema){
                var arr = SRAX.Filter.shema[el];
                if (arr == null) continue;
                for (var i = 0; i < arr.length; i++){
                    var u = arr[i].url;
                    var t = arr[i].urlType;
                    var urlCondition = u != null && url != null && (u == '*' || 
                        ((t == null || t == 'contain') && url.indexOf(u) != -1) || 
                        (t == 'start' && url.startWith(u)) ||
                        (t == 'end' && url.endWith(u)));

                    var q = arr[i].query;
                    t = arr[i].queryType;
                    var queryCondition = q != null && query != null && (q == '*' || 
                        ((t == null || t == 'contain') && query.indexOf(q) != -1) || 
                        (t == 'start' && query.startWith(q)) ||
                        (t == 'end' && query.endWith(q)));

                    if (((arr[i].joinLogic == null || arr[i].joinLogic == 'or') && (urlCondition || queryCondition)) || 
                        (arr[i].joinLogic == 'and' && urlCondition && queryCondition)) {
                        
                        var equal = u ? u : q;
                        if (lengthEquals < equal.length) {
                            lengthEquals = equal.length;     
                            options = {};
                            for(var j in arr[i]) options[j] = arr[i][j];
                            options.filterSchemaId = el;
                            if (owner != null && owner.nodeName.toLowerCase() == 'form') {
                                if (owner.attributes['method']) options.method = owner.attributes['method'].nodeValue;
                                options.form = owner;
                            }                            
                        }
                    }
                }
            }
            return options;
        },
        
        parseStartUrl : function(url){
            var ind = url.indexOf('/', 1);
            return url.substring(0, ind);
        },

        getParentPath : function(){ 
            var ind = location.pathname.lastIndexOf('/');
            var parentDir = '';
            if (ind != -1) parentDir = location.pathname.substring(0,ind+1);
            return parentDir;	
        },	

        parseAxOptions : function(owner){
            if (owner.iswrapped) return;
            var options = null;
            for (var i = 0; i < owner.attributes.length; i++){
                if (owner.attributes[i] && owner.attributes[i].nodeName.startWith('ax:')){
                    var name = owner.attributes[i].nodeName.substring(3);
                    var val = owner.attributes[i].nodeValue;
                    if (val == '1' || val == 'true') val = true; else
                    if (val == '0' || val == 'false') val = false;
                    if (options == null) options = {}; 
                    options[name] = val;
                }
            }
            if (owner.nodeName.toLowerCase() == 'form' && options != null) {
                if (owner.attributes['method']) options.method = owner.attributes['method'].nodeValue;
                options.form = owner;
            }                            
            return options;
        },

        wrapAnchor : function (owner, options){
            if (owner.iswrapped) return;
            var url, query;
            if (owner.nodeName.toLowerCase() == 'form') {
                if(owner.attributes['action']) url = owner.attributes['action'].nodeValue;

                if (url == null) url = location;
                url = SRAX.parseUri(url);
                query = url.query;
                url = url.path;
            } else {
                if (owner.href == null || owner.href == '') return;
                url = owner.pathname;
                query = owner.search;
                if (query != null && query.startWith('?')) query = query.substring(1);            
                if (SRAX.browser.opera || SRAX.browser.msie) url = '/' + url;

                //var parent = SRAX.Filter.getParentPath();            
                //if (url.substring(0,parent.length) == parent) url = url.substring(parent.length);
                //var startUrl = SRAX.Filter.parseStartUrl(url);
            }
            
            var ops = SRAX.Filter.getOptions(url, query, owner);
            if (ops == null && options == null) return;
            if (ops == null) ops = {};
            if (options == null) options = {};
            for (var i in ops) if (!options[i]) options[i] = ops[i];

            if (options && !options.target && owner.attributes['target'] && owner.attributes['target'].nodeValue != '') return;

            if (options.filterSchemaId == 'document.body') options.id = null;
            SRAX.Filter.wrapFromOptions(owner, options);
        },
        
        /**
        * Функция отключения ссылок на пустой якорь<br>
        * используется для отключения ссылок типа href="#" для того чтобы они не сбивали историю
        * 
        * @param {layer} layer id элемента или сам эелемент
        */
        wrapSharp : function(owner, options, url){
            if (owner.iswrapped) return;
            var current = location.protocol + '//' + location.host + location.pathname + '#';
            var href;
            if (owner.nodeName.toLowerCase() == 'form'){
                if (owner.attributes['action']) href = owner.attributes['action'].nodeValue;
            } else {
                href = owner.href;
            }
            if (SRAX.browser.opera && href+'#' == current) href += '#';
            if (href != null && href.endWith('#')){
                if (!href.startWith(location.protocol)) {
                    href = location.protocol + '//' + location.host + href;
                }
                if (url){
                  var a = document.createElement('a');
                  a.href = url + '#';
                  url = a.href;
                  a = null; 
                  if (!url.startWith(location.protocol)) {
                      var dir = '';
                      if (!url.startWith('/')){
                        dir = SRAX.parseUri(location.href).directory;
                      }
                      url = location.protocol + '//' + location.host + dir + url;
                  }
                }
                if (href == current || href == url){
                    if (options == null) options = {}; 
                    options.sharp = true;
                    SRAX.Filter.wrapFromOptions(owner, options);
                }
            }
        },

        wrapFromOptions : function(owner, options){
                if (options == null) return;
                owner.options = options;
                owner.iswrapped = true;
                var wrapped = document.createAttribute("iswrapped");
                wrapped.nodeValue = true;
                owner.setAttributeNode(wrapped);

                if (owner.nodeName.toLowerCase() == 'form') {

                    if (!options.overwrite && !SRAX.Default.OVERWRITE){
                        if (SRAX.browser.msie){
                            if (owner.onsubmit != null) {
                                var onprev = document.createAttribute('onprevsubmit');
                                onprev.nodeValue = owner.attributes['onsubmit'].nodeValue;
                                owner.setAttributeNode(onprev);
                            }
                        } else {
                            owner.onprevsubmit = owner.onsubmit;
                        }
                    }             

                    owner.onsubmit = function(){
                        try{
                            if (SRAX.browser.msie){
                                if (this.attributes['onprevsubmit'] && this.attributes['onprevsubmit'].nodeValue) {
                                    eval('new function(){' + this.attributes['onprevsubmit'].nodeValue + '}');
                                }
                            } else {
                                if (this.onprevsubmit != null && (typeof this.onprevsubmit == 'function')) this.onprevsubmit();
                            }
                        } catch (ex){
                            error(ex);
                        }
                        if (this.enctype == 'multipart/form-data'){
                            if (typeof this.options.multipart == 'function') this.options.multipart(this);
                            return true;
                        } else
                        if (!this.options.sharp){
                            var href = this.action;
                            if (typeof this.options.urlChanger == 'function') href = this.options.urlChanger(href);
                            if (this.options.handler){
                                this.options.handler(this, this.options);
                            } else 
                            if (this.options.type == 'data') {
                                dax(href, this.options); 
                            } else {
                                hax(href, this.options); 
                            }
                        }           
                        return false;
                    }
                    owner.submit = owner.onsubmit;
                } else {
                    if (!options.overwrite && !SRAX.Default.OVERWRITE){
                        if (SRAX.browser.msie){
                            if (owner.onclick != null) {
                                var onprev = document.createAttribute("onprevclick");
                                onprev.nodeValue = owner.attributes['onclick'] ? owner.attributes['onclick'].nodeValue : owner.onclick;
                                if (!onprev.nodeValue) onprev.nodeValue = owner.onclick;
                                owner.setAttributeNode(onprev);
                            }
                        } else {
                            owner.onprevclick = owner.onclick;
                        }
                    }

                    owner.onclick = function(){
                        try{
                            if (SRAX.browser.msie){
                                if (this.attributes['onprevclick'] && this.attributes['onprevclick'].nodeValue) {
                                    var func = this.attributes['onprevclick'].nodeValue;
                                    if (typeof func == 'function') this.onprevclick();
                                    else eval('new function(){' + func + '}');
                                }
                            } else {
                                if (this.onprevclick != null && (typeof this.onprevclick == 'function')) this.onprevclick();
                            }
                        } catch (ex){
                            error(ex);
                        }

                        if (!this.options.sharp){
                            var href = this.href;
                            if (typeof this.options.urlChanger == 'function') href = this.options.urlChanger(href);
                            if (this.options.handler){
                                this.options.handler(this, this.options);
                            } else 
                            if (this.options.type == 'data') {
                                dax(href, this.options); 
                            } else {
                                hax(href, this.options); 
                            }

                        }
                        return false;
                    }
                }            
        },

        wrap : function(layer, url){

            if (layer == null) {
                layer = document;
                for (var blockId in SRAX.Filter.shema){                    
                    SRAX.Filter.wrap(blockId, url);
                }
            }

            layer = SRAX.get(layer);
            if (layer == null) return;

            var a = layer.getElementsByTagName('a');
            var form = layer.getElementsByTagName('form');
            var area = layer.getElementsByTagName('area');
            for (var i = 0; i < a.length + form.length + area.length ; i++){
                var obj; 
                if (i < a.length) obj = a[i]; else 
                if (i < a.length + form.length) obj = form[i - a.length]; else 
                if (i < a.length + form.length + area.length) obj = area[i - a.length - form.length];
                var axWrap = obj.attributes['ax:wrap'];
                var noWrap = axWrap == null || (axWrap.nodeValue != 'false' && axWrap.nodeValue != '0' && axWrap.nodeValue != false);
                if ((!obj.iswrapped || (!obj.onclick && !obj.onsubmit)) && noWrap) {            
                    var options = SRAX.Filter.parseAxOptions(obj);
                    SRAX.Filter.wrapSharp(obj, options, url);
                    SRAX.Filter.wrapAnchor(obj, options);
                }
            }            
        }
                
    },


    /**
    * Метод для добаления обьекту интерфейса модели событий
    *
    * @param {obj} обьект или конструктор обьекта
    *
    */

    addEventsListener : function(obj){
        if (obj.prototype) obj = obj.prototype;
        obj.on = function(event,func){
            if(!this.events) this.events = {};
            if (!this.events[event]) this.events[event] = [];
            this.events[event].push(func);
        }
        obj.un = function(event, func){        
            if (!func) return this.unall(event);
            var arr = this.events ? this.events[event]:null;
            if (arr) {
                for (var i = 0, len = arr.length; i < len; i++){                
                    if (arr[i] == func) arr.splice(i, 1);
                }
                this.events[event] = arr;
            }
        }
        obj.unall = function(event){
            if (this.events) {
                if (event) {
                    this.events[event] = null;             
                } else {
                    this.events = null;
                }
            }
        }
        obj.fireEvent = function(event, options){
            var arr = this.events ? this.events[event] : null;
            if (arr) {
                if (!options) options = {};
                var res = null;
                for (var i = 0; i < arr.length; i++){
                    var r = arr[i](options);
                    if (r != null) if (res == null) res = r; else res *= r;
                }
                return res;
            } 
        }
    },

    addContainerListener : function(obj){
        if (obj.prototype) obj = obj.prototype;
        var registered = {}; 
        var toall = {};
        obj.register = function(thread){
            var events = registered[thread.id];
            if (events){
                for (var i in events){
                    for (var j = 0; j < events[i].length; j++){
                        thread.on(i,events[i][j]);
                    }
                }
            }
            for (var i in toall){
                var events = toall[i];
                for (var j = 0; j < events.length; j++){
                    thread.on(i,events[j]);
                }
            }
        }

        obj.on = function(id, event, func){
            if (!registered[id]) registered[id] = {};
            if (!registered[id][event]) registered[id][event] = [];
            registered[id][event].push(func);
            if (SRAX.Html.thread[id]) SRAX.Html.thread[id].on(event, func);
        }

        obj.onall = function(event, func){
            if (!toall[event]) toall[event] = [];
            toall[event].push(func);
            var th = SRAX.Html.thread;
            for (var i in th){
                if (th[i]) th[i].on(event, func);
            }
        }

        obj.unall = function(event, func){
            if (event){
                if (func) {
                    var arr = toall[event];
                    for (var i = 0, len = arr.length; i < len; i++){                
                        if (arr[i] == func) arr.splice(i, 1);
                    }
                    toall[event] = arr;
                } else {
                   toall[event] = [];
                }
            } else {
                toall = {};
            }
            var th = SRAX.Html.thread;
            for (var i in th){
                if (th[i]) th[i].un(event, func);
            }
        }


        obj.un = function(id, event, func){
            if (!func) {
                if (id){
                    if (registered[id]) {
                        if (event) {
                            registered[id][event] = null;             
                        } else {
                            registered[id] = null;
                        }
                    }
                } else {
                    registered = {};
                }

                var list = {};            
                if (id) {
                    list[id] = SRAX.Html.thread[id]; 
                } else {
                    list = SRAX.Html.thread;
                }
                for (var i in list){
                    if (list[i]) list[i].unall(event);
                }
            } else {
                var arr = registered[id] ? registered[id][event]:null;
                if (arr) {
                    for (var i = 0, len = arr.length; i < len; i++){                
                        if (arr[i] == func) arr.splice(i, 1);
                    }
                    registered[id][event] = arr;
                }
                if (SRAX.Html.thread[id]) SRAX.Html.thread[id].un(event, func);
            }
        }

        obj.fireEvent = function(id, event, options){
            if (SRAX.Html.thread[id]) return SRAX.Html.thread[id].fireEvent(event, options);
        }
    },

    /**
    * Контейнер объектов процесса запроса HTML
    */
    Html : {
        thread : {},

        /**
        * Флаг асинхронного выполнения hax
        * @type Boolean 
        */
        ASYNCHRONOUS : true,

        /**
        * Хранилище для реализации синхронных hax
        * @type Array
        */
        storage :[]

    },

    /**
    * Контейнер объектов процесса запроса данных
    */
    Data : {
        thread : {}
    },

    /**
    * Функция проигрывания звуковых файлов
    * 
    * @param {src} путь к звуковому файлу
    * @param {timeout} интервал в секундах, через который произвести удаление елемента - по умолчанию 10сек( если timeout <= 0 остается навсегда)
    */
    playsound : function(src, timeout){
        var div = document.createElement('div');
        if (timeout == null) timeout = 10;
        div.setAttribute('style','position:absolute;top:-1000px;left:-1000px');
        if (window.ActiveXObject){   
            var sound = document.createElement('bgsound');sound.src = src;div.appendChild(sound);
        } else {
            div.innerHTML = '<embed src="'+src+'" loop="false" autostart="true" hidden="true" mastersound>';
        } 
        document.body.appendChild(div);
        if (timeout > 0)
            setTimeout(function(){div.firstChild.src = '';document.body.removeChild(div)}, timeout*1000);
    },

    /**
    * Экспериментальный метод включения режима чтения с любого адресса (это не безопасно) <br>
    * UniversalBrowserRead
    *  
    */    
    enableUBR : function(){
        netscape.security.PrivilegeManager.enablePrivilege ("UniversalBrowserRead"); //for Firefox
    },

    /**
    * Экспериментальный объект менеджер лоадеров-сигнализаторов
    */
    Loader : {
        show: function(){
            SRAX.showHaxLoading(true);
        },
        
        hide: function(){
            SRAX.showHaxLoading(false);
        }
    },

    parseUri : function (source, ops) { 
        var options = { 
            strictMode: false, 
            key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], 
            q: { 
                name: "queryKey", 
                parser: /(?:^|&)([^&=]*)=?([^&]*)/g 
            }, 
            parser: { 
                strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, 
                loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ 
            } 
        }
        var o = ops ? ops : options, value = o.parser[o.strictMode ? "strict" : "loose"].exec(source); 
        for (var i = 0, uri = {}; i < 14; i++) { uri[o.key[i]] = value[i] || ""; } 
        uri[o.q.name] = {}; 
        uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) { if ($1) uri[o.q.name][$1] = $2; }); return uri;         
    },

    /**
    * Функция для отображения ошибок запроса HTML страниц
    * @param {String} url URL адрес запроса
    * @param {Integer} status код сообщения
    * @return {String} statusText текст сообщения
    */
    showMessage : function(url, status, statusText){
        if (status == 0) return;
        alert('Error ' + status + ' : ' + url + '\n' + statusText);
    },

    /**
    * Функция replaceHtml по утверждению ее создателя быстрей чем innerHTML
    * @param {String/Element} el родительский элемент
    * @param {String} html текст HTML
    *
    * <br>
    * <a href="http://blog.stevenlevithan.com/archives/faster-than-innerhtml">http://blog.stevenlevithan.com/archives/faster-than-innerhtml</a>
    * <br>
    * This is much faster than using (el.innerHTML = value) when there are many
    * existing descendants, because in some browsers, innerHTML spends much longer
    * removing existing elements than it does creating new ones. 
    */
    replaceHtml : function (el, html) {
            var oldEl = (typeof el === "string" ? document.getElementById(el) : el);
            /* Pure innerHTML is slightly faster in IE
            oldEl.innerHTML = html;
            return oldEl; */

            var newEl = oldEl.cloneNode(false);
            newEl.innerHTML = html;
            oldEl.parentNode.replaceChild(newEl, oldEl);
            /* Since we just removed the old element from the DOM, return a reference
            to the new element, which can be used to restore variable references. */
            return newEl;
    },

    /**
    * Функция добавления HTML в родительский элемент
    * @param {String/Element} elem родительский элемент
    * @param {String} html текст HTML
    */
    addTo : function(html, elem){
        var x = elem ? x = SRAX.get(elem) : x = document.body;
        if (!x) {
            warn('Warning => addTo : element = ' + elem + ' not found');
            return;
        }
        var div = document.createElement('div');
        div.innerHTML = html;    
        var asm = x['ax:script:mark'];
        while (div.childNodes.length > 0){
            if(asm) x.parentNode.insertBefore(div.childNodes[0],x); else x.appendChild(div.childNodes[0]);
        }
    },

    /**
    * Функция рендеринга HTML в родительском элементе
    * @param {String/Element} elem родительский элемент
    * @param {String} html текст HTML
    */
    writeTo : function(html, elem){
        var x = elem ? x = SRAX.get(elem) : x = document.body;
        if (!x) {
            warn('Warning => writeTo : element = ' + elem + ' not found');
            return;
        }
        if (x['ax:script:mark']) SRAX.addTo(html,x); else x.innerHTML = html;
    },
    
    /**
    * Функция удаления элемента из родительского элемента
    * @param {String/Element} el удаляемый элемент
    */
    remove : function(el){
        el = SRAX.get(el);
        el.parentNode.removeChild(el);
    },

    /**
    * Функция замены элемента другим элементом
    * @param {String/Element} nEl новый элемент
    * @param {String/Element} oEl заменямый элемент
    */
    replace : function(nEl,oEl){
        nEl = SRAX.get(nEl);
        oEl = SRAX.get(oEl);
        return oEl.parentNode.replaceChild(nEl,oEl);
    },
    
    /**
    * Функция генерации уникального Id
    */
    genId : function(){
        return 'ax:genid:' + (this.lastGenId ? ++this.lastGenId : this.lastGenId=1);
    }


}


SRAX.init();


//deprecated methods
SRAX.escape = SRAX.encode;

} 




