/*
$Id: combined.js 136 2009-06-04 08:14:58Z pdidevich $
*/

/**
 *$Id: config.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 * Конфигурация
 */

// framework config //
var PuskFramework = {
    '_cfg': {
        'namespace': 'pf',
        'extendArray': true,
        'extendString': true,
        'extendDate': true,
        'globals' : true,
        'domain': window.location.host
    },
    'ext': {},
    'version': 0,
    '_initListeners': []
};
PuskFramework.version = '$Revision: 101 $'.replace(/[^\d]/g, '');
/**
 *$Id: globals.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 * global неймспейс
 */
PuskFramework.globals = new function()
{
    var pf = PuskFramework;

    /*
     * Подмена контекста вызова функции
     *
     * @param {Object} obj контекст (обычно this)
     * @return {Function}
     */
    Function.prototype.bind = function(obj)
    {
        var method = this;
        var temp = function()
        {
            return method.apply(obj, arguments);
        };
        return temp;
    };



    /**
     * Сокращенный вариант document.getElementById
     *
     * @param {Object} elementId
     * @return {Object}
     */
    this.$ = function(elementId)
    {
        return pf.elem.get(elementId);
    };

    /**
     * Конструктор элементов HTML
     *
     * @param {Object} tag тэг
     * @param {Object} props свойства элемента
     * @param {Object} cssStyle стиль элемента
     * @return {Object}
     */
    this.$$$ = function(tag, props, cssStyle)
    {
        return pf.elem.construct(tag, props, cssStyle);
    };

    /**
     * Удаление элемента из DOM
     *
     * @param {Object} elem ссылка на элемент
     * @return {Object} возвращает сам элемент
     */
    this.$_ = function(elem)
    {
        return pf.elem.remove(elem);
    };

    /**
     * Возвращает тип объекта
     *
     * @param {Object} obj
     * @return {String}
     */
    this.$type = function(obj)
    {
        if (obj === null) return null;
        if (obj && obj.tagName && !obj.length) 
        {          
          return 'element';
        }
        var type = typeof obj;
        if (type == 'object' && obj.nodeName)
        {
            switch (obj.nodeType)
            {
                case 1:                    
                    return 'element';
                case 3:
                    return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
            }
        }
        if (type == 'object' || type == 'function')
        {
            switch (obj.constructor)
            {
                case Array:
                    return 'array';
                case RegExp:
                    return 'regexp';
            }
            if (typeof obj.length == 'number')
            {
                if (obj.item) return 'collection';
            }
        }
        return type;
    };

    /**
     * Возвращает текущее время в формате timestamp или разницу между
     * текущим временем и временем, заданным в качестве аргумента
     *
     * @param {Object} startTime время для сдвига
     * @return {Integer}
     */
    this.$time = function(startTime)
    {
        var now = new Date().getTime();
        return startTime ? now - startTime : now;
    };

    /**
     * Функция предназначена для кроссбраузерной обработки любых событий
     * осуществляет выставление ключевых свойств
     *
     * @param {Object} evt объект типа event
     * @return {Object}
     */
    this.$event = function(evt)
    {
        evt = pf.evt.e(evt);
        if (evt && !evt.target)
        {
            evt.target  = evt.srcElement;

            if (evt.type == 'mouseover')
            {
                evt.relatedTarget = evt.fromElement;
            }
            else if (evt.type == 'mouseout')
            {
                evt.relatedTarget = evt.toElement;
            }
            evt.stopPropagation = function()
            {
                this.cancelBubble = true
            };
            evt.preventDefault = function()
            {
                this.returnValue = false
            };
        }
        return evt;
    };

    /**
     * Аналог empty() в PHP, предназначена для проверки ненулевого значения элемента
     *
     * @param {Object} smth элемент для проверки
     * @return {Bool}
     */
    this.$empty = function(smth)
    {
        switch (pf.$type(smth))
        {
            case 'object':
                for (var i in smth)
                { return false; }
                return true;
            case 'array':
                return smth.length == 0;
            default: return !smth;
        }
    };

    /**
     * Расширяет один объект всеми свойствами другого
     *
     * @param {Object} destination объект получатель
     * @param {Object} source объект донор
     * @return {Object}
     */
    this.$extend = function(destination, source)
    {
        for (var property in source)
            destination[property] = source[property];
        return destination;
    };

    /**
     * Унивесральный выбрасыватель исключений
     * 
     * @param {Object} параметры
     * @return {Object}     
     */                   
    this.$exGen = function(message, exceptionType, filename, line)
    {
      var exception;
      exceptionType = exceptionType || 'error';
      
      switch (exceptionType)
      {
        case 'type': exception = new TypeError(message);break;        
        default: exception = new Error(message);break;
      }
      
      return exception;
    };
    /**
     * Развертывание функций обработчиков, если в качестве обработчика задана не функция, а объект
     *
     * @param {Object} _callback
     * @return {Function}
     */
    pf._expandCallBack = function(_callback)
    {
        switch (true)
        {
            default:
            case (typeof(_callback) == 'function' || !_callback):
                return _callback;
            case (typeof(callback) == 'object'):
            {
                var _scope = _callback.scope || window;
                var _func = _callback.func;
                var _args = _callback.args;
                var _temp = function()
                {
                    if (_args)
                    {
                        for (var i = 0; i < _args.length; i++)
                            arguments.push(_args[i]);
                    }
                    return _func.apply(_scope, arguments);
                };
                return _temp;
            }
        }
    };
};

/**
 * Возвращает случайное целое число в диапазоне от min до max
 *
 * @param {Object} min минимальное значение
 * @param {Object} max максимальное значение
 * @return {Integer}
 */
Math.rand = function(min, max)
{
    var res = Math.random();
    return (min == undefined || max == undefined) ? res : Math.floor(res * (max - min + 1) + min);
};

/**
 * Преобразует целое число в строку в шестнадцатиричном формате
 *
 * @param {Object} dec
 * @param {Integer} signs минимальное число разрядов результата
 * @return {String}
 */
Math.dec2hex = function(dec, signs)
{
    signs = signs || 2;
    return pf.str.pad(Math.dec2any(dec, 16), signs, '0');
};

/**
 * Преобразует строку в шестнадцатиричном формате в целое число
 *
 * @param {Object} hex
 * @return {Integer}
 */
Math.hex2dec = function(hex)
{
    return Math.any2dec(hex,16);
};

/**
 * Преобразует целое число в строку в двоичном формате
 *
 * @param {Object} dec
 * @param {Object} signs минимальное число разрядов результата
 * @return {String}
 */
Math.dec2bin = function(dec, signs)
{
    signs = signs || 0;
    return pf.str.pad(Math.dec2any(dec, 2), signs, '0');
};

/**
 * Преобразует строку в двоичном формате в целое число
 *
 * @param {Object} bin
 * @return {Integer}
 */
Math.bin2dec = function(bin)
{
    return Math.any2dec(bin,2);
};

Math.any2dec = function(value, size)
{
    value = value || "0";
    size = parseInt(size) || 2;
    if (!value || size == 10) return parseInt(value);
    var megaChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if (size>1 && size<megaChars.length)
    {
        value = value.toString();
        var j = 1, decVal = 0;
        for (var i = value.length - 1; i >= 0; i--)
        {
            var ind = megaChars.indexOf(value.charAt(i));
            if (ind >= size) return null;
            decVal += megaChars.indexOf(value.charAt(i)) * j;
            j *= size;
        };
        return decVal;
    };
    return null
};

Math.dec2any = function(value, size)
{
    value = parseInt(value) || 0;
    size = parseInt(size);
    if (!value || size == 10) return value.toString();
    var megaChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if (size>1 && size<megaChars.length)
    {
        var res = '';
        var high = Math.floor(value/size);
        for (var i = 0; value >= size; i++)
        {
            res = megaChars.charAt(value % size) + res;
            value = Math.floor(value/size);
        }
        return megaChars.charAt(value % size) + res;
    };
    return null;
};

Math.any2any = function(value, sizeIn, sizeOut)
{
    return Math.dec2any(Math.any2dec(value, sizeIn),sizeOut);
};

/*
*$Id: browscap.js 101 2009-01-15 13:21:57Z aanpilogov $
*/
PuskFramework.browsCap = new function()
{
    var n = window.navigator;
    var na = n.userAgent;

    function isFlashEnabled()
    {
        if(n.plugins && n.plugins["Shockwave Flash"]) {return true};
        try {new ActiveXObject('ShockwaveFlash.ShockwaveFlash');return true;} catch(e){};
        return false;
    }

    this.isIE = na.indexOf('MSIE') != -1;
    this.isOpera = !!window.opera;
    this.isSafari = na.indexOf('Safari') != -1;
    this.isMozilla = !this.isSafari && (na.indexOf("Gecko") != -1);
    this.isChrome = na.indexOf('Chrome') != -1;
    this.isWebkit = this.isSafari || this.isChrome
    this.appVer = 0;
    this.hasFlash = isFlashEnabled();

    switch(true)
    {
        case this.isIE:
        {
            var match = /MSIE\s(\d(?:\.\d?))/.exec(na);
            this.appName = 'MSIE';
            break;
        }
        case this.isOpera:
        {
            this.appVer = parseFloat(window.opera.version());
            this.appName = 'Opera';
            break;
        }
        case this.isMozilla:
        {
            var match = /(?:SeaMonkey|Firefox)\/(\d(?:\.\d?)).*$/i.exec(na);
            this.appName = 'Mozilla';
            break;
        }
        case this.isChrome:
        {
            var match = /Chrome\/(\d(?:\.\d?))/i.exec(na);
            this.appName = 'Chrome';
            break;
        }
        case this.isSafari:
        {
            var match = /Version\/([\d.]+) Safari\//.exec(na);
            this.appName = 'Safari';
            break;
        }
        default:
        {
            this.appName = na.appName;
            this.appVer = parseFloat(na.appVer) || 0;
            break;
        }
    }
    if (!this.appVer && match) this.appVer = match[1] ? (parseFloat(match[1]) || 0) : 0;

};

/**
 *$Id: array.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 * Неймспейс для работы с массивами
 */
PuskFramework.arr = new function()
{
    var pf = PuskFramework;

    /**
     * Фукнкция возвращает индекс элемента в массиве
     *
     * @param {Object} array массив, в котором будет произведен поиск
     * @param {Object} item элемент для поиска
     * @return {Integer} индекс элемента, соответствующего аргументу
     */
    this.indexOf = function(array, item)
    {
        if (!array || !array.length || typeof(item) == 'undefined') return -1;
        for (var i = 0, len = array.length; i < len; i++)
        {
            if (array[i] === item) return i;
        }
        return -1;
    };

    /**
     * Реализация цикла foreach для массивов
     *
     * @param {Object} array массив для прохода
     * @param {Object} iterator функция, выполняющаяся над каждым элементом
     * @param {Object} context контекст вызова функции
     */
    this.forEach = function(array, iterator, context)
    {
        if (array && array.length && pf.$type(iterator) == "function")
        {
            for (var i = 0, len = array.length; i < len; i++)
            {
                iterator.call(context, array[i], i, array);
            }
        }
    };

    /**
     * Возвращает массив, отфильтрованный по условию (функции)
     *
     * @param {Object} array исходный массив
     * @param {Object} callback функция фильтрации
     * @param {Object} context контекст вызова функции
     * @return {Array}
     */
    this.filter = function(array, callback, context)
    {
        if (!array || !array.length || !callback || !pf.$type(callback) == "function") return array;
        var val, result = [];
        for (var i = 0, len = array.length; i < len; i++)
        {
            if (i in array)
            {
                val = array[i]; // in case fun mutates this
                if (callback.call(context, val, i, array))
                {
                    result.push(val);
                }
            }
        }
        return result;
    };

    /**
     * Создает копию массива
     *
     * @param {Object} array
     * @return {Array}
     */
    this.clone = function(array)
    {
        if (!array) return null;
        return [].concat(array);
    };

    /**
     * Возвращает значение последнего элемента массива
     *
     * @param {Object} array
     * @return {Object}
     */
    this.getLast = function(array)
    {
        return array ? array[array.length - 1] : null;
    };

    /**
     * Возвращает случайный элемент массива
     *
     * @param {Object} array
     * @return {Object}
     */
    this.getRandom = function(array)
    {
        return array ? array[Math.rand(0, array.length - 1)] : null;
    };

    /**
     * Удаляет заданный элемент из массива
     *
     * @param {Object} array входной массив
     * @param {Object} index индекс элемента
     * @return {Array}
     */
    this.remove = function(array, index)
    {
        if (pf.$type(array) != 'array') return array;
        array.splice(index, 1);
        return array;
    };

    /**
    * Сравнить два массива по содержимому
    */
    this.compare = function(first, second)
    {
        if (first && second && first.length == second.length)
        {
            for (var i=0, len=first.length; i<len; i++)
                if (first[i] != second[i]) return false;
            return true;
        };
        return false;
    }
};

/**
 *$Id: cookie.js 114 2009-02-12 16:09:40Z aanpilogov $
 *
 * Манипуляции с cookie
 */
PuskFramework.cookie = new function()
{
    var pf = PuskFramework;
    this.default_expires = 2592000;    // seconds, 30 days

    /**
     * Получить значение куки с заданным именем
     * @param {String} name
     * @return {String}
     */
    this.get = function(name)
    {
        if (!name) return null;
        var cookie = ' ' + document.cookie;
        var cname = ' ' + name + '=';
        var to, from  = cookie.indexOf(cname);
        if ( from != -1 )
        {
            from += cname.length;
            to    = cookie.indexOf(';', from );
            if ( to == -1 )
                to = cookie.length;
            return unescape( cookie.substring(from, to) );
        }
        return null;
    };

    /**
     * Установить куки с заданными параметрами
     *
     * @param {Object} name ключ
     * @param {Object} data значение
     * @param {Object} path путь для установки
     * @param {Object} expires время жизни
     * @param {Object} domain домен
     * @param {Object} secure флаг безопасности
     */
    this.set = function(name, data, path, expires, domain, secure)
    {
        if (!expires)
        {
            expires = new Date();
            expires.setTime( expires.getTime() + this.default_expires*1000);
        }
        document.cookie = name + "=" + escape(data)
            + ((expires == null) ? "" : "; expires=" + expires.toGMTString())
            + ((path == null)    ? "" : "; path=" + path)
            + ((domain == null)  ? "" : "; domain=" + domain)
            + ((secure == null)  ? "" : "; secure");
    };

    /**
     * Удалить куку. Внимание, удаление происходит при правильном повторении пути и домена
     * с которыми кука была установлена!
     *
     * @param {Object} name ключ
     * @param {Object} path путь
     * @param {Object} domain домен
     */
    this.remove = function(name, path, domain)
    {
        if (this.get(name))
        {
            document.cookie = name + "=" + ((path == null) ? "" : "; path=" + path) + ((domain == null) ? "" : "; domain=" + domain) + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
        }
    }
};
/**
 *$Id: date.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 * Манипуляции с датой и временем
 */
PuskFramework.date = new function()
{
/**
 * Добавляет к указанной дате указанный период в формате XML Interval
 * пример: P1Y2DT1H40M - прибавить 1 год, 2 дня, 1 час и 40 минут

 * @param {Object} date дата
 * @param {String} period интервал в формате XML
 */
this.add = function(date, period)
{
    if (!period || period == '') return false;
    var expr = /-?(P|T)(\d+Y)?(\d+M)?(\d+D)?(T)?(\d+H)?(\d+M)?(\d+S)?/ig;
    var parts = expr.exec(period);
    var scope = parts[1];
    if (!scope || !(scope == 'T' || scope == 'P')) return false;
    var dir = period.charAt(0) == '-' ? -1 : 1;
    for (var i = 2; i < parts.length; i++)
    {
        if (!parts[i] || !parts[i].length || parts[i].length < 1) continue;
        var metric = parts[i].charAt(parts[i].length-1);
        if (!metric) continue;
        if (metric == 'T') scope = 'T';
        var modifier = parseInt(parts[i])*dir;
        if (isNaN(modifier) || modifier == 0) continue;
        switch(metric)
        {
            case 'Y':
                date.setFullYear(date.getFullYear() + modifier);
                break;
            case 'M':
                if (scope == 'P')
                    date.setMonth(date.getMonth() + modifier);
                else
                    date.setMinutes(date.getMinutes() + modifier);
                break;
            case 'D':
                date.setDate(date.getDate() + modifier);
                break;
            case 'H':
                date.setHours(date.getHours() + modifier);
                break;
            case 'S':
                date.setSeconds(date.getSeconds() + modifier);
                break;
        }
    }
};


/**
 * Форматирует дату в формате, совместимом с PHP
 * NB: короткие имена дней недели и месяцев не в локали, ибо всякие GMT Date будут поломаны!!!
 *
 * Поддерживаемые макросы
 * a, A, B, c, d, D, F, g, G, h, H, i, I (uppercase i), j, l (lowecase L),
 * L, m, M, n, N, O, P, r, s, S, t, U, w, W, y, Y, z, Z
 *
 * Неподдерживаемые макросы
 * T, e, o
 *
 * @param {Object} date дата
 * @param {String} format формат
 * @return {String}
 */
this.format = function(date, format)
{
    var daysShort =   ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
    var monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

    var daysLong =    ["воскресенье", "понедельник", "вторник", "среда",
                       "четверг", "пятница", "суббота"];
    var monthsLong =  ["января", "февраля", "марта", "апреля",
                       "мая", "июня", "июля", "августа", "сентября",
                       "октября", "ноября", "декабря"];

    var switches = {

        a : function () {
            return date.getHours() > 11? "pm" : "am";
        },

        A : function () {
            return (this.a().toUpperCase ());
        },

        B : function (){
            var off = (date.getTimezoneOffset() + 60)*60;
            var theSeconds = (date.getHours() * 3600) +
                             (date.getMinutes() * 60) +
                              date.getSeconds() + off;
            var beat = Math.floor(theSeconds/86.4);
            if (beat > 1000) beat -= 1000;
            if (beat < 0) beat += 1000;
            if ((String(beat)).length == 1) beat = "00"+beat;
            if ((String(beat)).length == 2) beat = "0"+beat;
            return beat;
        },

        c : function () {
            return (this.Y() + "-" + this.m() + "-" + this.d() + "T" +
                    this.H() + ":" + this.i() + ":" + this.s() + this.P());
        },

        d : function () {
            var j = String(this.j());
            return (j.length == 1 ? "0"+j : j);
        },

        D : function () {
            return daysShort[date.getDay()];
        },

        F : function () {
            return monthsLong[date.getMonth()];
        },

        g : function () {
            return date.getHours() > 12? date.getHours()-12 : date.getHours();
        },

        G : function () {
            return date.getHours();
        },

        h : function () {
            var g = String(this.g());
            return (g.length == 1 ? "0"+g : g);
        },

        H : function () {
            var G = String(this.G());
            return (G.length == 1 ? "0"+G : G);
        },

        i : function () {
            var min = String (date.getMinutes ());
            return (min.length == 1 ? "0" + min : min);
        },

        I : function () {
            var noDST = new Date ("January 1 " + this.Y() + " 00:00:00");
            return (noDST.getTimezoneOffset () ==
                    date.getTimezoneOffset () ? 0 : 1);
        },

        j : function () {
            return date.getDate();
        },

        l : function () {
            return daysLong[date.getDay()];
        },

        L : function () {
            var Y = this.Y();
            if (
                (Y % 4 == 0 && Y % 100 != 0) ||
                (Y % 4 == 0 && Y % 100 == 0 && Y % 400 == 0)
                ) {
                return 1;
            } else {
                return 0;
            }
        },

        m : function () {
            var n = String(this.n());
            return (n.length == 1 ? "0"+n : n);
        },

        M : function () {
            return monthsShort[date.getMonth()];
        },

        n : function () {
            return date.getMonth()+1;
        },

        N : function () {
            var w = this.w();
            return (w == 0 ? 7 : w);
        },

        O : function () {
            var os = Math.abs(date.getTimezoneOffset());
            var h = String(Math.floor(os/60));
            var m = String(os%60);
            h.length == 1? h = "0"+h:1;
            m.length == 1? m = "0"+m:1;
            return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m;
        },

        P : function () {
            var O = this.O();
            return (O.substr(0, 3) + ":" + O.substr(3, 2));
        },

        r : function () {
            var r; // result
            //  Thu         ,     21               Dec              2000
            r = this.D() + ", " + this.d() + " " + this.M() + " " + this.Y() +
            //    16          :    01          :    07               0200
            " " + this.H() + ":" + this.i() + ":" + this.s() + " " + this.O();
            return r;
        },

        s : function () {
            var sec = String (date.getSeconds ());
            return (sec.length == 1 ? "0" + sec : sec);
        },

        S : function () {
            switch (date.getDate ()) {
                case  1: return ("st");
                case  2: return ("nd");
                case  3: return ("rd");
                case 21: return ("st");
                case 22: return ("nd");
                case 23: return ("rd");
                case 31: return ("st");
                default: return ("th");
            }
        },

        t : function () {
            var daysinmonths = [null,31,28,31,30,31,30,31,31,30,31,30,31];
            if (this.L()==1 && this.n()==2) return 29; // ~leap day
            return daysinmonths[this.n()];
        },

        U : function () {
            return Math.round(date.getTime()/1000);
        },

        w : function () {
            return date.getDay();
        },

        W : function () {
            var DoW = this.N ();
            var DoY = this.z ();

            var daysToNY = 364 + this.L () - DoY;
            if (daysToNY <= 2 && DoW <= (3 - daysToNY)) {
                return 1;
            }

            if (DoY <= 2 && DoW >= 5) {
                return new Date (this.Y () - 1, 11, 31).format ("W");
            }

            var nyDoW = new Date (this.Y (), 0, 1).getDay ();
            nyDoW = nyDoW != 0 ? nyDoW - 1 : 6;

            if (nyDoW <= 3)
            { return (1 + Math.floor((DoY + nyDoW) / 7)); }
            else
            { return (1 + Math.floor((DoY - (7 - nyDoW)) / 7)); }
        },

        y : function () {
            var y = String(this.Y());
            return y.substring(y.length-2,y.length);
        },

        Y : function () {
            if (date.getFullYear) {
                var newDate = new Date("January 1 2001 00:00:00 +0000");
                var x = newDate .getFullYear();
                if (x == 2001) {
                    return date.getFullYear();
                }
            }
            var x = date.getYear();
            var y = x % 100;
            y += (y < 38) ? 2000 : 1900;
            return y;
        },


        z : function () {
            var t = new Date("January 1 " + this.Y() + " 00:00:00");
            var diff = date.getTime() - t.getTime();
            return Math.floor(diff/1000/60/60/24);
        },

        Z : function () {
            return (date.getTimezoneOffset () * -60);
        }

    };

    function getSwitch(str) {
        if (switches[str] != undefined) {
            return switches[str]();
        } else {
            return str;
        }
    }

    var formatString = format.split("");
    var i = 0;
    while (i < formatString.length) {
        if (formatString[i] == "\\") {
            formatString.splice(i,1);
        } else {
            formatString[i] = getSwitch(formatString[i]);
        }
        i++;
    }
    return formatString.join("");
};

/**
 * Daylight Saving Time
 * @return {Number_of_minutes} 
 */
this.getDST = function(date)
{
    date = date || new Date();
    var march =  new Date(Date.UTC(date.getUTCFullYear(), 2, 31));
    var begin_st=new Date(Date.UTC(date.getUTCFullYear(), 2, 31 - march.getUTCDay()));
    var oct =    new Date(Date.UTC(date.getUTCFullYear(), 9, 31));
    var end_st = new Date(Date.UTC(date.getUTCFullYear(), 9, 31 - oct.getUTCDay()));
    return ((date >= begin_st) && (date < end_st)) ? 60 : 0;
};

/**
 * Конвертирует гринвичское время в локальное
 * @return {Date} 
 */
this.gmt2local = function(date)
{
    date = date || new Date();
    return new Date(date - date.getTimezoneOffset()*60000);
};
};

/**
 *$Id: element.js 137 2009-06-24 12:37:00Z abelezyak $
 *
 * Отвечает за методы по работе с DOM элементами
 */
PuskFramework.elem = new function()
{
    var pf = PuskFramework;

    /**
    * Замена document.getElementById
    */
    this.get = function(elementId)
    {
        return (typeof elementId == 'string') ? document.getElementById(elementId) : elementId;
    };

    /**
     * Конструирует DOM элемент с заданными свойствами и стилем
     *
     * @param {Object} elem тэг
     * @param {Object} attributes свойства элемента
     * @param {Object} cssStyle стиль
     * @return {Object}
     */
    this.construct = function(elem, attributes, cssStyle)
    {
        if (!elem) return false;
        var node, handlers = {};
        var IE = /*@cc_on!@*/false;
        if (typeof cssStyle == 'string') { cssStyle = pf.str.toHash(cssStyle, ';', ':'); }

        if (IE) { var str = '<' + elem } else { node = document.createElement(elem); }
            for (var i in attributes)
            {
            switch (true)
                {
                case (i == 'innerHTML'):
                    break;
                case (i.startsWith('on') && (typeof attributes[i] == "function")):
                    handlers[i.substr(2)] = attributes[i];
                        break;
                case (i == 'className'):
                    if (IE) { str += (' class="' + attributes[i] + '"') } else { node[i]=attributes[i]; }
                        break;
                    default:
                    if (IE) { str += (' ' + i + '="' + attributes[i] + '"') } else { node.setAttribute(i, attributes[i]); }
                        break;
                }
            }
        if (IE) {
            str += '>';
            node = document.createElement(str);
        };
        if (attributes && attributes.innerHTML) { node.innerHTML = attributes['innerHTML']; }

        for (var i in handlers)
                {
            pf.evt.add(node, i, handlers[i]);
        }
        for (var i in cssStyle)
        {
            node.style[pf.str.camelize(i)] = cssStyle[i];
        }

        return node;
    };

    /**
     * Переключает видимость элемента
     *
     * @param {Object} elem элемент
     * @param {Object} arg on|off
     * @return {Bool}
     */
    this.toggle = function(elem, arg)
    {
		if(!elem) return;
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.toggle(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem || elem.nodeType != 1) return false;
        switch (arg)
        {
            case 'on':
            case true:
                elem.style.display = '';
                return true;
            case 'off':
            case false:
                elem.style.display = 'none';
                return false;
            default:
                return (elem.style.display = (elem.style.display == 'none' ? '' : 'none')) ? false : true;
        }
    };

    /**
     * Возвращает стиль заданного элемента
     *
     * @param {Object} elem
     * @param {Object} CSSStyleProp
     * @return {String}
     */
    this.getStyle = function(elem, CSSStyleProp)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.getStyle(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem || elem.nodeType != 1) return false;
        if (elem.currentStyle)
        {
            var IEStyleProp = pf.str.camelize(CSSStyleProp);
            return elem.currentStyle[IEStyleProp];
        }
        else if (window.getComputedStyle)
        {
            var compStyle = window.getComputedStyle(elem, "");
            return compStyle.getPropertyValue(CSSStyleProp);
        }
        return '';
    };

    /**
     * Проверить, содержит ли заданный DOM элемент указанное имя класса
     *
     * @param {Object} elem элемент
     * @param {Object} cname имя класса
     * @return {Bool}
     */
    this.hasClass = function(elem, cname)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.hasClass(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem || elem.nodeType != 1) return false;
        var a = elem.className.split(/\s+/);
        for (var i = 0; i < a.length; i++)
        {
            if (a[i] == cname) return true;
        }
        return false;
    };

    /**
     * Добавляет указанное имя класса к элементу, не затрагивая другие
     *
     * @param {Object} elem элемент
     * @param {Object} cname имя класса
     * @return {Bool}
     */
    this.addClass = function(elem, cname)
    {
    	//console.log('Adding class "' + cname + '" to element ' + elem);
    	if(!elem){return false;}
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.addClass(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem) return false;
        if (pf.elem.hasClass(elem, cname)) return false;
        elem.className += ' ' + cname;
        return true;
    };

    /**
     * Удаляет указанное имя класса из списка классов элемента, не затрагивая другие
     *
     * @param {Object} elem элемент
     * @param {Object} cname имя класса
     * @return {Bool}
     */
    this.delClass = function(elem, cname)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.delClass(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem) return false;
        var a = elem.className.split(/\s+/);
        var newName = '';
        for (var i = 0; i < a.length; i++)
            if (a[i] != cname) newName += ' ' + a[i];
        elem.className = newName;
        return true;
    };

    /**
     * Ищет в дереве DOM наверх элемент с заданными критериями.
    * Установленный флаг choise указывает, что хотя бы одно условие должно быть выполнено
    * (по умолчанию все условия должны быть выполнены).
     *
     * @param {Object} elem
    * @param {Object} conditions
    * @param {Object} choise
     * @return {Object}
     */
   this.findAncestor = function(elem, conditions, choise)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.findAncestor(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');

        do
        {
            if (pf.$empty(elem)) { return null; }
            var ok = true;
            for (var key in conditions)
            {
                var cond = (key=='hasClass')
                    ? pf.elem.hasClass(elem, conditions[key])
                    : (elem[key] == conditions[key] || elem.getAttribute(key) == conditions[key]);
                if (cond && choise) return elem;
                if (!cond)
                {
                    ok = false;
                    if (!choise) break;
                }
            }
            if (ok) { return elem; }
        }
        while ((elem = elem.parentNode) != document.documentElement);

        return false;
    };

    /**
     * Установить прозрачность элемента в %
     *
     * @param {Object} elem элемент
     * @param {Object} opacity прозрачность (%)
     */
    this.setOpacity = function(elem, opacity)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.setOpacity(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem) return false;
        if (opacity == null) opacity = 100;
        if (isNaN(opacity)) opacity = parseFloat(opacity);
        if (opacity <= 1) opacity = opacity * 100;
        if (opacity < 0) opacity = 0;
        elem.style.filter = "alpha(opacity=" + opacity + ")";
        opacity = opacity / 100;
        elem.style.MozOpacity = opacity;
        elem.style.KhtmlOpacity = opacity;
        elem.style.opacity = opacity;
    };

    /**
     * Возвращает текстовую часть контента заданного элемента
     *
     * @param {Object} elem элемент
     * @return {String}
     */
    this.getText = function(elem)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.getText(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem) return '';
        return elem.text || elem.textContent || elem.innerText;
    };

    /**
     * Удаляет заданный элемент из DOM
     *
     * @param {Object} elem элемент
     * @return {Bool}
     */
    this.remove = function(elem)
    {
        elem = pf.$(elem);
        if (pf.$type(elem) == "string") elem = pf.$(elem);

        if (elem && elem.parentNode)
        {
            var p = elem.parentNode;
            return (p.removeChild(elem));
        }
        return (false);
    };

    /**
     * Заменяет один элемент DOM другим
     *
     * @param {Object} oldElem старый элемент
     * @param {Object} newElem новый элемент
     */
    this.replaceWith = function(oldElem, newElem)
    {
        if (pf.$type(oldElem) == "string") oldElem = pf.$(oldElem);
        if (pf.$type(oldElem) != "element") throw pf.$exGen('pf.element.replaceWith(): ' + 'typeof(oldElem) argument is ' + pf.$type(oldElem) + ', "element" expected');
        if (pf.$type(newElem) == "string") newElem = pf.$(newElem);
        if (pf.$type(newElem) != "element") throw pf.$exGen('pf.element.replaceWith(): ' + 'typeof(newElem) argument is ' + pf.$type(newElem) + ', "element" expected');
        if (!oldElem || oldElem.nodeType != 1 || !newElem || newElem.nodeType != 1) return false;
        oldElem.parentNode.insertBefore(newElem, oldElem);
        pf.elem.remove(oldElem);
    };

    /**
     * Загружает в заданный элемент innerHTML с определенного URL(внутри домена)
     * функция работает асинхронно!!!
     *
     * @param {Object} elem элемент
     * @param {Object} url адрес контента
     * @param {Object} callback функция, которую надо вызвать после завершения операции
     * @param {Object} onerror функция, которую надо вызвать при ошибке загрузки
     */
    this.loadContent = function(elem, url, callback, onerror)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.loadContent(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem || !url) return false;
        return pf.ajax.get({
            'url': url,
            'onSuccess': function(req)
            {
                try
                {
                    elem.innerHTML = req.responseText;
                    if (callback)
                    {
                        callback(req, url);
                    }
                }
                catch (err)
                {
                    debugError('не получается выполнить коллбэк! ' + err.name + ' : ' + err.message);
                    serverSetStatus('error');
                }
            },

            'onError': function(req)
            {
                debugError('Ошибка запроса!\nСтатус=' + req.statusText);
                serverSetStatus('error');
                if (onerror)
                {
                    onerror(req);
                }
            }
        });
    };

    /**
     * Возвращает все элементы заданного узла, являющиеся тэгами
     *
     * @param {Object} elem элемент
     * @param {Object} tag фильтрация по имени тэга
     * @return {Array}
     */
    this.getChildElements = function(elem, tag)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.getChildElements(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem || !elem.childNodes) return false;
        var ch = [];
        var el = elem.childNodes;
        for (var i = 0, len = el.length; i < len; i++)
        {
            if (el[i].tagName && el[i].tagName == tag)
            {
                ch.push(el[i]);
            }
        }
        return ch;
    };

    /**
     * Возвращает первый дочерний не #text элемент
     *
     * @param {Object} elem элемент
     * @return {Array}
     */
    this.getFirstChild = function(elem)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.element.getFirstChild(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        if (!elem || !elem.childNodes) return false;
        var ch = [];
        var el = elem.childNodes;
        for (var i = 0, len = el.length; i < len; i++)
        {
            if (el[i].tagName)
            {
                return(el[i]);
            }
        }
        return false;
    };

    /**
     * Дополнение к системной функции insertBefore.
     * Вставляет элемент после заданного элемента
     *
     * @param {Object} newNode элемент
     * @param {Object} existingNode существующий элемент
     * @return {Bool}
     */
    this.insertAfter = function(newNode, existingNode)
    {
        if (pf.$type(newNode) == "string") newNode = pf.$(newNode);
        if (pf.$type(newNode) != "element") throw pf.$exGen('pf.element.getFirstChild(): ' + 'typeof(newNode) argument is ' + pf.$type(newNode) + ', "element" expected');
        if (pf.$type(existingNode) == "string") existingNode = pf.$(existingNode);
        if (pf.$type(existingNode) != "element") throw pf.$exGen('pf.element.getFirstChild(): ' + 'typeof(existingNode) argument is ' + pf.$type(existingNode) + ', "element" expected');

        if (!existingNode || !existingNode.parentNode) return false;
        return existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
    };

    /**
     * Проверить, содержит ли заданный контейнер заданный элемент
     * @param {Object} container контейнер
     * @param {Object} containee элемент
     * @return {Bool}
     */
    this.containsDOM = function(container, containee)
    {
        if (!containee || !containee.parentNode) return false;
        var isParent = false;
        do
        {
            if (isParent = (container == containee)) break;
            containee = containee.parentNode;
        }
        while (containee != null);
        return isParent;
    };

    /**
     * Возвращает сдвиг заданного элемента, вызванный его уходом за скролл
     *
     * @param {Object} elem элемент
     * @return {Object}
     */
    this.getScrolls =  function(elem)
    {
        var docElem = document.documentElement, pos = {'x': 0, 'y': 0};
        if (!elem || elem.nodeType != 1) return pos;
        while (elem && elem != docElem)
        {
            pos.x += elem.scrollLeft;
            pos.y += elem.scrollTop;
            if (pf.elem.getStyle(elem, 'position') == 'fixed')
            {
                pos.x -= (docElem.scrollLeft || document.body.scrollLeft);
                pos.y -= (docElem.scrollTop  || document.body.scrollTop);
                return pos;
            }
            elem = elem.parentNode;
        }
        return pos;
    };

    /**
     * Возвращает абсолютные координаты заданного элемента
     *
     * @param {Object} elem элемент
     * @return {Object}
     */
    this.getPosition = function(elem)
    {
        var pos = {'x': 0, 'y': 0};

        var doc = elem.ownerDocument, body = doc.body, de = doc.documentElement;

        if (!elem || elem == de) return pos;

        if (body.getBoundingClientRect)
        {
            var rect = elem.getBoundingClientRect();

            pos = {
                'x': parseInt(rect.left) + (elem.pageXOffset || de.scrollLeft || body.scrollLeft) - (de.clientLeft || body.clientLeft || 0),
                'y': parseInt(rect.top)  + (elem.pageYOffset || de.scrollTop  || body.scrollTop ) - (de.clientTop  || body.clientTop  || 0)
            };
            return pos;
        }

        if (elem.offsetParent)
        {
            do {
                pos.x += elem.offsetLeft || 0;
                pos.y += elem.offsetTop  || 0;
            } while (elem = elem.offsetParent)
        }
        return pos;
    };

    /**
     * Выставляет заданному элементу класс, позволяющий визуально показать,
     * что происходит загрузка контента
     *
     * @param {Object} elem элемент
     * @param {Object} isLoading (on|off)
     * @return {String} (on|off)
     */
    this.setLoading = function(elem, isLoading)
    {
        if (!elem || elem.nodeType != 1) return false;
        var cname = 'loading';
        switch (isLoading)
        {
            case 'on':
                pf.elem.addClass(elem, cname);
                return true;
            case 'off':
                pf.elem.delClass(elem, cname);
                return false;
            default:
                return this.setloading(elem, pf.elem.hasClass(elem, cname) ? 'off' : 'on');
        }
    };
};
/**
 *$Id: event.js 137 2009-06-24 12:37:00Z abelezyak $
 *
 */
PuskFramework.evt = new function()
{
    var pf = PuskFramework;

    /*
     * Человекочитаемые названия клавиш
     */
    this.keys = {
        8:  'backspace',
        9:  'tab',
        13: 'enter',
        27: 'esc',
        37: 'left',
        38: 'up',
        39: 'right',
        40: 'down',
        46: 'delete',
        36: 'home',
        35: 'end',
        33: 'pageup',
        34: 'pagedown',
        45: 'insert'
    };

    /**
     * Получить событие кроссбраузерно
     *
     * @param {Object} evt событие или null
     * @return {Object}
     */
    this.e = function(evt) { return evt || window.event; };

    /**
     * Добавить обработчик события
     *
     * @param {Object} elem элемент
     * @param {Object} event тип события (без префикса on)
     * @param {Object} callback функция обработчик
     */
    this.add = function(elem, event, callback)
    {
        callback = pf._expandCallBack(callback);
        switch(event)
        {
            case 'mousewheel':
            {
                if (pf.browsCap.isMozilla) { event = 'DOMMouseScroll'; }
                break;
            }
            case 'dataavailable':
            {
                if (!pf.browsCap.isIE) { event = 'DOMContentLoaded'; }
                //else { event = 'load'; elem = window;}
                break;
            }
        }

        if(document.attachEvent)
        {
            elem.attachEvent('on'+event, callback);
        }
        else if(document.addEventListener)
        {
            elem.addEventListener(event, callback, true);
        }
    };

    /*
     * Алиас для совместимости
     */
    this.addListener = this.add;

    /**
     * Удалить обработчик события с элемента
     *
     * @param {Object} elem элемент
     * @param {Object} event событие (без префикса on)
     * @param {Object} callback обработчик
     */
    this.remove = function(elem, event, callback)
    {
        callback = pf._expandCallBack(callback);
        switch(event)
        {
            case 'mousewheel':
            {
                if (pf.browsCap.isMozilla) { event = 'DOMMouseScroll'; }
                break;
            }
            case 'dataavailable':
            {
                if (!pf.browsCap.isIE) { event = 'DOMContentLoaded'; }
                else { event = 'load'; elem = window;}
                break;
            }

        }

        if(document.detachEvent)
        {
            elem.detachEvent('on'+event, callback);
        }
        else if(document.removeEventListener)
        {
            elem.removeEventListener(event, callback, true);
        }
    };

    /*
     * Алиасы для удобства и совместимости
     */
    this.removeListener = this.remove;
    this.del            = this.remove;

    /**
     * Остановить развитие события и его последствий ;)
     *
     * @param {Object} evt событие
     */
    this.stop = function(evt)
    {
        evt = pf.$event(evt);
        evt.stopPropagation();
        evt.preventDefault();
        return false;
    };

    /**
     * Получить человекопонятное название клавиши по событию
     *
     * @param {Object} evt событие
     * @return {String}
     */
    this.getKeyName = function(evt)
    {
        evt = this.e(evt);
        var code = evt.keyCode;
        return this.keys[code] || code;
    };

    /**
     * Получить абсолютные координаты мыши по событию в виде {x,y}
     *
     * @param {Object} evt событие
     * @return {Object}
     */
    this.getPosition = function(evt)
    {
        evt = this.e(evt);
        var x = 0, y = 0;

        if (evt.pageX || evt.pageY)
        {
            x = evt.pageX;
            y = evt.pageY;
        } else if (evt.clientX || evt.clientY) {
            x = evt.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
            y = evt.clientY + (document.documentElement.scrollTop  || document.body.scrollTop)  - document.documentElement.clientTop;
        }

        return {'x':x, 'y':y};
    };

    /**
     * Получить состояние колеса прокрутки мыши
     * положительное/отрицательное значения говорят о направлении
     *
     * @param {Object} evt событие
     * @return {Integer}
     */
    this.getWheel = function(evt)
    {
        evt = this.e(evt);

        var delta = 0;
        if (evt.wheelDelta)
        {
            delta = evt.wheelDelta/120;
        }
        else if (evt.detail)
        {
            delta = -evt.detail/3;
        }

        this.stop(evt);
        return delta || false;
    };

    /**
     * Проверяет, находится ли курсор мыши внутри заданного элемента
     *
     * @param {Object} elem элемент
     * @param {Object} evt событие
     * @return {Bool}
     */
    this.mouseIn = function(elem, evt)
    {
        var mPos = this.getPosition(evt);
        var ePos = pf.elem.getPosition(elem);
        var w = parseInt(elem.offsetWidth);
        var h = parseInt(elem.offsetHeight);
        return ((mPos.x > ePos.x) && (mPos.x <= ePos.x+w) && (mPos.y > ePos.y) && (mPos.y <= ePos.y+h));
    };

    /**
     * Проверка, действительно ли имеет место событие mouseover
     * некоторые браузеры ошибочно генерят события over и out
     *
     * @param {Object} element элемент
     * @param {Object} evt событие
     * @return {Bool}
     */
    this.checkMouseOver = function(element, evt)
    {
        evt = this.e(evt);
        if (element.contains)  return !element.contains(evt.fromElement);
        if (evt.relatedTarget) return !pf.elem.containsDOM(element, evt.relatedTarget);
    };

    /**
     * Проверка, действительно ли имеет место событие mouseout
     * некоторые браузеры ошибочно генерят события over и out
     *
     * @param {Object} element элемент
     * @param {Object} evt событие
     * @return {Bool}
     */
    this.checkMouseOut = function(element, evt)
    {
        evt = this.e(evt);
        if (element.contains)  return !element.contains(evt.toElement);
        try{ // FF + input.parentNode = eggog
        if (evt.relatedTarget) return !pf.elem.containsDOM(element, evt.relatedTarget);
        } catch(e) { return true; }
    };
};
/**
 *$Id: form.js 126 2009-03-05 10:58:05Z aanpilogov $
 *
 */
PuskFramework.form = new function()
{
    var pf = PuskFramework;

    // form.elements не возвращает input.type == 'image'
    var tags = {
        'input':[],
        'button':[],
        'textarea':[],
        'select':[]
    };

    var eachFormElement = function(theform, callback)
    {                
        for (var tag in tags)
        {
            tags[tag] = theform.getElementsByTagName(tag);
            for (var i = 0, l = tags[tag].length; i < l; i++)
            {
                callback(tag, i);
            }
        }
    };

    /**
     * Возвращает набор значений элементов формы
     * @param {Object} theform форма
     * @return {Object}
     */
    this.getValues = function(theform)
    {        
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.getValues(): ' + 'typeof(theform) argument is ' + pf.$type(theform) + ', "element" expected');
        var els = theform.elements;
        if ((pf.$type(els) != "collection") && (pf.$type(els) != "element")) throw pf.$exGen('pf.form.getValues(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" or "element" expected');
     
        var ename, value, values = {}; 
        for (var i = 0, l = els.length; i < l; i++)
        {
            if (!(ename = els[i].name)) continue;
            value = pf.form.getElementValue(els[i]);
            if (value === null) continue;

            if (!values[ename])
            {
                values[ename] = value;
                continue;
            }

            if (typeof(values[ename]) == 'string')
            {
                values[ename] = [values[ename]];
            }
            
            if (typeof(value) == 'string')
            {
                values[ename].push(value);
            } 
            else 
            {
                values[ename] = values[ename].concat(value);
            }
        }
        return values;
    };

    /**
     * Сериализует форму в GET строку
     *
     * @param {Object} theform форма
     * @return {String}
     */
    this.serialize = function(theform)
    {                
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.serialize(): ' + 'typeof(theform) is ' + pf.$type(theform) + ', "element" expected');
        var els = theform.elements;
        if ((pf.$type(els) != "collection") && (pf.$type(els) != "element")) throw pf.$exGen('pf.form.serialize(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" expected');
        
        var ename, value, values = [];
        for (var i = 0, l = els.length; i < l; i++)
        {
            if (!(ename = els[i].name)) continue;
            value = pf.form.getElementValue(els[i]);
            if (value !== null)
            {
                if (typeof(value) == 'string')
                {
                    values.push(ename + '=' + encodeURIComponent(value));
                } else {
                    for (var j = 0, l2 = value.length; j < l2; j++) {
                        values.push(ename + '=' + encodeURIComponent(value[j]));
                    }
                }
            }
        }
        return values.join('&');
    };

    /**
     * Разблокирует все элементы формы
     *
     * @param {Object} theform форма
     */
    this.enable = function(theform)
    {
        
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.enable(): ' + 'typeof(theform) is ' + pf.$type(theform) + ', "element" expected');
        if ((pf.$type(theform.elements) != "collection") && (pf.$type(theform.elements) != "element")) throw pf.$exGen('pf.form.enable(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" or "element" expected');
        eachFormElement(theform, function(tag, j){tags[tag][j].removeAttribute('disabled')});
        return true;
    };

    /**
     * Блокирует элементы формы
     *
     * @param {Object} theform форма
     */
    this.disable = function(theform)
    {        
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.disable(): ' + 'typeof(theform) is ' + pf.$type(theform) + ', "element" expected');
        if ((pf.$type(theform.elements) != "collection") && (pf.$type(theform.elements) != "element")) throw pf.$exGen('pf.form.disable(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" or "element" expected');
        eachFormElement(theform, function(tag, j){tags[tag][j].setAttribute('disabled', 'true')});
        return true;
    };

    /**
     * Валидация формы согласно служебным атрибутам в элементах
     * validate = "mail|url|date|integer|float"
     * required = "Y|required"
     * mask = regexp
     * minVal, maxVal
     * проверяет совпадение значений ELEMENT_NAME и ELEMENT_NAME_copy
     * не проверяет select-multiple
     *
     * выставляет ошибочным элементам css-класс errorInput
     *
     * @param {Object} theform форма
     * @return {Bool}
     */
    this.validate = function(theform)
    {
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.validate(): ' + 'typeof(theform) is ' + pf.$type(theform) + ', "element" expected');
        if ((pf.$type(theform.elements) != "collection") && (pf.$type(theform.elements) != "element")) throw pf.$exGen('pf.form.validate(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" expected');
        
        var values = {};
        for (var i = 0, l = theform.elements.length; i < l; i++)
        {
            var elem = theform.elements[i];
            if (elem.disabled || (elem.type && elem.type.toLowerCase() == 'select-multiple')) { continue; }
            var value = pf.form.getElementValue(elem);
            values[elem.name] = value;
            
            /* Проверка обязательных для заполнения полей
            ======================================== */
            if ((elem.getAttribute('required')=="Y" || elem.getAttribute('required')=="required") && (value.trim() == ''))
            {
                return pf.form._markError(elem);
            }

            /* Проверка формата поля
            ======================================== */
            var validate = elem.getAttribute('validate');
            if (validate && (value != '' && value != elem.getAttribute('default')))
            {
                if (!check(value, validate))
                {
                    return pf.form._markError(elem);
                }
            }

            /* Проверка на минимальное и максимальное значение
            ======================================== */
            var minVal = elem.getAttribute('minVal');
            var maxVal = elem.getAttribute('maxVal');
            var maxminVal = minVal & maxVal;
            if (minVal)
                minVal = minVal > parseInt(value); // теперь переменная в качестве флага
            if (maxVal)
                maxVal = parseInt(value) > maxVal; // теперь переменная в качестве флага
            if (((minVal || maxVal) && !maxminVal) || ((minVal || maxVal) && maxminVal))
            {
                return pf.form._markError(elem);
            }

            /* Проверка на соответствие маске
            ======================================== */
            var mask = elem.getAttribute('mask');
            if (mask && value != '')
            {
                try {
                    var reg = new RegExp('^'+mask+'$');
                    mask = reg.test(value);
                    if (!mask)
                    {
                        return pf.form._markError(elem);
                    }
                } catch(e){}
            }
            
            pf.form._unmarkError(elem);
        }
        for (var prop in values)
        {
            if (values[prop+'_copy'] && (values[prop+'_copy'] != values[prop]))
            {
                return pf.form._markError(values[prop+'_copy']);
            }
        }
        return true;

        function check(val, type)
        {
            var regs = {
                'mail': /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
                /*  dd-mm-yyyy    d-m-yy
                    dd mm yyyy    d m yy
                    dd/mm/yyyy    d/m/yy
                    dd.mm.yyyy    d.m.yy */
                'date': /\b(0?[1-9]|[12][0-9]|3[01])[- /.](0?[1-9]|1[012])[- /.](19|20)?[0-9]{2}\b/,
                'integer':/^-?[0-9]+$/,
                'float':  /^-?[0-9]+(\.[0-9]+|)$/,
                'url':/^(?:https?:\/\/|ftp:\/\/|)[-A-Z0-9.]+\.[-A-Z0-9]{2,6}(?:\/[-A-Z0-9+&@#\/%=~_|!:,.;]*|)(\?[-A-Z0-9+&@#/%=~_|!:,.;]*|)$/i
            }
            if (!regs[type]) return true;
            return regs[type].test(val);
        }
    };

    this._markError = function(elem)
    {
        pf.elem.addClass(elem, 'errorInput');
        try {elem.focus();} catch(e){};
        return false;
    };
    this._unmarkError = function(elem)
    {
        if (pf.elem.hasClass(elem, 'errorInput'))
            pf.elem.delClass(elem, 'errorInput');
        return true;
    };
    
    /**
     * Возвращает значение элемента
     * @param {Object} element элемент формы
     * @return {String|Array}
     */
    this.getElementValue = function(element)
    {
        var value = null;
        if (!element.disabled && element.type) switch (element.type.toLowerCase())
        {
            case 'checkbox':
            case 'radio':
                if (!element.checked) break;
            case 'hidden':
            case 'password':
            case 'search':
            case 'text':
            case 'textarea':
                value = element.value;
                break;
            case 'select-one':
                if (element.selectedIndex >= 0)
                {
                    value = element.options[element.selectedIndex].value;
                }
                break;
            case 'select-multiple':
                value = [];
                for (var i = 0, l = element.options.length; i < l; i++) 
                {
                    if (element.options[i].selected) value.push(element.options[i].value);
                }
                break;
        }
        return value;
    };
};

/**
 *$Id: hash.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 * Функции для работы с объектами
 */
PuskFramework.hash = new function()
{
    var pf = PuskFramework;
     /**
     * Функция возвращает индекс элемента в хэше
     *
     * @param {Object} hash массив, в котором будет произведен поиск
     * @param {Object} value элемент для поиска
     * @return {Integer} индекс элемента, соответствующего аргументу
     */
    this.indexOf = function(hash, value)
    {
        if (typeof(hash) != 'object') return -1;
        for(var i in hash)
        {
            if (hash[i] == value) { return i; }
        }
        return -1;
    };

    /**
     * Реализация цикла foreach для объектов
     *
     * @param {Object} hash хеш для прохода
     * @param {Object} iterator функция, выполняющаяся над каждым элементом
     * @param {Object} context контекст вызова функции
     */
    this.forEach = function(hash, iterator, context)
    {
        if (typeof(hash) == 'object' && pf.$type(iterator) == "function")
        {
            for(var i in hash)
            {
                iterator.call(context, hash[i], i, hash);
            }
        }
    };

    /**
     * Создает несвязанную копию объекта
     *
     * @param {Object} hash
     * @return {Object}
     */
    this.clone = function(hash)
    {
        if (typeof(hash) != 'object') { return hash; }
        var newHash = hash.constructor();
        for (var key in hash) {
            newHash[key] = pf.hash.clone(hash[key]);
        }
        return newHash;
    };

    /**
     * Возвращает хеш, отфильтрованный по условию (функции)
     *
     * @param {Object} hash исходный массив
     * @param {Object} callback функция фильтрации
     * @param {Object} context контекст вызова функции
     * @return {Object}
     */
    this.filter = function(hash, callback, context)
    {
        if (typeof(hash) != 'object' || typeof(callback) != "function") return hash;
        context = context || null;
        var val, result = {};
        for (var i in hash)
        {
            val = hash[i];
            if (callback.call(context, val, i, this)) { result[i] = val; }
        }
        return result;
    };

    /**
     * Осуществляет слияние двух и боле объектов в один
     *
     * @param {Object} hash1
     * @param {Object} hash2
     * @return {Object}
     */
    this.merge = function(hash1, hash2 /* [, hash3, ...] */)
    {
        var mix = {};
        for (var i = 0, len=arguments.length; i < len; i++)
        {
            for (var property in arguments[i])
            {
                var ap = arguments[i][property];
                var mp = mix[property];
                if (mp && pf.$type(ap) == 'object' && pf.$type(mp) == 'object')
                {
                    mix[property] = this.merge(mp, ap);
                }
                else
                {
                    mix[property] = ap;
                }
            }
        }
        return mix;
    };

    /**
     * Возвращает длину хеша, можно исключить из списка свойств функции
     *
     * @param {Object} hash
     * @param {Bool} skipFunctions исключить функции
     * @return {Integer}
     */
    this.getLength = function(hash, skipFunctions)
    {
        if (typeof(hash) != 'object') { return 0; }
        var size = 0;
        for (var key in hash) if (!skipFunctions || typeof hash[key] != 'function') { size++; }
        return size;
    };

    /**
     * Возвращает массив с ключами хеша
     *
     * @param {Object} hash объект
     * @param {Object} skipFunctions пропустить функции
     * @return {Array}
     */
    this.getKeys = function(hash, skipFunctions)
    {
        if (typeof(hash) != 'object') { return [] }
        var keys = [];
        for (var key in hash) if (!skipFunctions || typeof hash[key] != 'function') { keys.push(key); }
        return keys;
    };

    /**
     * Возвращает массив с значениями хеша
     *
     * @param {Object} hash объект
     * @param {Object} skipFunctions пропустить функции
     * @return {Array}
     */
    this.getValues = function(hash, skipFunctions)
    {
        var values = [];
        for (var key in hash) if (!skipFunctions || typeof hash[key] != 'function') { values.push(hash[key]); }
        return values;
    };

    /**
     * Сериализует хеш в GET строку. Есть возможность задать префикс для имен.
     *
     * @param {Object} hash объект
     * @param {Object} prefix префикс, например data
     */
    this.serialize = function(hash, prefix)
    {
        if (prefix == null) { prefix = ''; }

        if (hash instanceof Object)
        {
            var qs = [];
            for(var property in hash)
            {
                var v = hash[property];
                if (v === null || v === undefined) v = '';
                if ( ((v.constructor||{}).prototype||{})[property]) { continue }
                var curPrefix = prefix ? prefix+'['+encodeURIComponent(property)+']' : encodeURIComponent(property);
                if (v instanceof Object)
                {
                    qs.push( pf.hash.serialize(v, curPrefix) );
                } else {
                    qs.push( curPrefix + "=" + encodeURIComponent(v) );
                }
            }
        } else {
            var qs = [hash];
        }
        return qs.join('&');
    };

    /**
    * Сравнить два объекта по содержимому
    */
    this.compare = function(first, second)
    {
        if (!first || !second) return false;
        if (!pf.arr.compare(pf.hash.getKeys(first), pf.hash.getKeys(second))) return false;
        for(var p in first)
        {
            if (typeof second[p] !== typeof first[p]) return false;

            if(first[p] instanceof Object)
            {
                switch (pf.$type(first[p]))
                {
                    case 'function':
                        if (first[p] != second[p]) return false;
                        break;
                    case 'array':
                        if (!pf.arr.compare(first[p], second[p])) return false;
                        break;
                    default:
                        if (!pf.hash.compare(first[p], second[p])) return false;
                        break;
                };
                continue;
            }

            if (first[p] !== second[p]) return false;
        }
        return true;
    };

    this.flatten = function(hash, prefix)
    {
        if (prefix == null) { prefix = ''; }

        if (hash instanceof Object)
        {
            var qs = {};
            for(var property in hash)
            {
                var v = hash[property];
                if ( ((v.constructor||{}).prototype||{})[property]) { continue }
                var curPrefix = prefix ? prefix+'['+property+']' : property;
                if (v instanceof Object)
                {
                    var tmp = (pf.hash.flatten(v, curPrefix));
                    for (var n in tmp)
                    {
                        qs[n] = tmp[n];
                    }
                } else {
                    qs[curPrefix] = v;
                }
            }
        } else {
            var qs = hash ? [hash] : [];
        }
        return qs;
    };
};
/**
 *$Id: string.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 * Работа со строками
 */
PuskFramework.str = new function()
{
    var pf = PuskFramework;

    /**
     * Дополнить строку до заданной длины
     *
     * @param {Object} string строка
     * @param {Object} l длина
     * @param {Object} s строка для дополнения
     * @param {Object} t тип дополнения (0/1/2)
     * @return {String}
     */
    this.pad = function(string, l, s, t)
    {
        string = string || '';
        return s || (s = " "), (l -= string.length) > 0 ? (s = new Array(Math.ceil(l / s.length)
            + 1).join(s)).substr(0, t = !t ? l : t == 1 ? 0 : Math.ceil(l / 2))
            + string + s.substr(0, l - t) : string;
    };

    /**
     * Удалить пробелы и другие пустые символы с начала и конца строки
     *
     * @param {Object} string строка
     * @return {String}
     */
    this.trim = function(string)
    {
        string = string || '';
        string = string.toString();
        string = string.replace(/^[\s\n]+/g, ''); // strip leading
        string = string.replace(/[\s\n]+$/g, ''); // strip trailing
        return string;
    };

    /**
     * Удалить тэги из строки
     *
     * @param {Object} string строка
     * @return {String}
     */
    this.stripTags = function(string)
    {
        string = string || '';
        return string.replace(/(<[^>]+>)/g, '');
    };

    /**
     * Экранировать HTML сущности
     *
     * @param {Object} string строка
     * @return {String}
     */
    this.escapeHtml = function(string)
    {
        string = string || '';
        return string.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
    };

    /**
     * Деэкранировать HTML сущности
     *
     * @param {Object} string строка
     * @return {String}
     */
    this.unescapeHtml = function(string)
    {
        string = string || '';
        return string.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&')
    };

    /**
     * Обрезать строку до заданной длины и дополнить подстрокой, если обрезание состоялось
     *
     * @param {Object} string строка
     * @param {Object} length длина
     * @param {Object} truncation дополнение
     * @return {String}
     */
    this.truncate = function(string, length, truncation)
    {
        string = string || '';
        length = length || 30;
        truncation = truncation || '...';
        return string.length > length ?
            string.slice(0, length) + truncation : string;
    };

    /**
     * Проверка наличия подстроки в строке
     *
     * @param {String} string строка
     * @param {String} needle подстрока
     * @param {Bool} caseInsensitive учитывать регистр?
     * @return {Bool}
     */
    this.contains = function(string, needle, caseInsensitive)
    {
        string = string || '';
        var haystack = string;
        if (caseInsensitive)
        {
            haystack = haystack.toLowerCase();
            needle   = needle.toLowerCase();
        }
        return haystack.indexOf(needle) > -1;
    };

    /**
     * Проверка на наличие подстроки в начале строки
     *
     * @param {String} string строка
     * @param {String} needle подстрока
     * @param {Bool} caseInsensitive учитывать регистр?
     * @return {Bool}
     */
    this.startsWith = function(string, needle, caseInsensitive)
    {
        string = string || '';
        needle = needle || '';
        var haystack = string;
        if (caseInsensitive)
        {
            haystack = haystack.toLowerCase();
            needle = needle.toLowerCase();
        }
        return haystack.indexOf(needle) === 0;
    };

    /**
     * Проверка на наличие подстроки в конце строки
     *
     * @param {String} string строка
     * @param {String} needle подстрока
     * @param {Bool} caseInsensitive учитывать регистр?
     * @return {Bool}
     */
    this.endsWith = function(string, needle, caseInsensitive)
    {
        string = string || '';
        needle = needle || '';
        var haystack = string;
        if (caseInsensitive)
        {
            haystack = haystack.toLowerCase();
            needle = needle.toLowerCase();
        }
        var d = haystack.length - needle.length;
        return d >= 0 && haystack.lastIndexOf(needle) === d;
    };

    /**
     * Отверблюдить строку
     *
     * @param {String} string строка
     * @return {String}
     */
    this.camelize = function(string)
    {
        if (!string) return '';
        var oStringList = string.split('-');
        if (oStringList.length == 1) return oStringList[0];

        var camelizedString = string.indexOf('-') == 0
            ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
            : oStringList[0];

        for (var i = 1, len = oStringList.length; i < len; i++) {
            var s = oStringList[i];
            camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
        }

        return camelizedString;
    };

    /**
     * Преобразовать GET строку в объект
     *
     * @param {String} str строка
     * @param {String} delimiter1
     * @param {String} delimiter2
     * @return {Object}
     */
    this.toHash = function(str, delimiter1, delimiter2)
    {
        if (!str) return '';
        delimiter1 = delimiter1 || '&';
        delimiter2 = delimiter2 || '=';
        var hash = {};
        var pair, pairs = str.split(delimiter1);
        for(var i=0, len=pairs.length; i<len; i++)
        {
            pair = (pairs[i]).split(delimiter2);
            if (pair[1] != undefined)
            {
                hash[pf.str.trim(decodeURIComponent(pair[0]))] = pf.str.trim(decodeURIComponent(pair[1]));
            }
        }
        return hash;
    };

    this.oneOf = function(str)
    {
        str = str || '';
        str = str.toString();
        if (arguments.length < 1) return false;
        for (var i = 1; i < arguments.length; i++)
        {
            if (str === arguments[i].toString()) return true;
        }
        return false;
    };
};
/**
 *$Id: window.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 * Работа с окном браузера
 */
PuskFramework.window = new function()
{
    var pf = PuskFramework;

    /**
     * Размер клиентской области окна по горизонтали
     * @return {Integer}
     */
    this.getClientWidth = function()
    {
        var w=0;
        if (document.body)
        {
            w = Math.min(document.body.clientWidth, document.body.scrollWidth);
        }
        return w;
    };

    /**
     * Размер клиентской области окна по вертикали
     * @return {Integer}
     */
    this.getClientHeight = function()
    {
        var h=0;
        if (self.innerHeight) h = self.innerHeight;
        else if (document.documentElement && document.documentElement.clientHeight) h = document.documentElement.clientHeight;
        else if (document.body) h = document.body.clientHeight;

        return h;
    };

    /**
     * Размер документа по горизонтали
     * @return {Integer}
     */
    this.getDocumentWidth = function()
    {
        var size = 0;
        var d = (document.documentElement && document.documentElement.offsetWidth) ? document.documentElement : document.body;
        return (d.scrollWidth > d.offsetWidth)?d.scrollWidth:d.offsetWidth-size;
    };

    /**
     * Размер документа по вертикали
     * @return {Integer}
     */
    this.getDocumentHeight = function()
    {
        var d = (document.documentElement && document.documentElement.offsetHeight) ? document.documentElement : document.body;
        return (d.scrollHeight > d.offsetHeight)?d.scrollHeight:d.offsetHeight;
    };

    /**
     * Скролл по вертикали
     * @return {Integer}
     */
    this.getBodyScrollTop = function()
    {
        return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop);
    };

    /**
     * Скролл по горизонтали
     * @return {Integer}
     */
    this.getBodyScrollLeft = function()
    {
        return self.pageXOffset || (document.documentElement && document.documentElement.scrollLeft) || (document.body && document.body.scrollLeft);
    };
};
/**
 *$Id: xml.js 132 2009-04-23 12:28:07Z aanpilogov $
 *
 * Работа с XML документами
 */
PuskFramework.xml = new function()
{
    var pf = PuskFramework;

    /**
     * Создание XML документа
     * @return {Document}
     */
    this.createDocument = function(root, ns)
    {
        root = root || '';
        ns = ns || null;
        if (window.ActiveXObject)
        {
            var nsStr = ns ? ' xmlns="'+ns+'"' : '';
            var xmlDocument = new ActiveXObject("Msxml2.DOMDocument.3.0");
            xmlDocument.async = false;
            if (root != '')
                xmlDocument.loadXML('<'+root+nsStr+'/>');
        } else {
            var xmlDocument = document.implementation.createDocument(ns, root, null);
        }
        return xmlDocument;
    };

    /**
    * Создает элемент и заполняет его указанным контентом или элементом
    */
    this.createElement = function(name, attr, value)
    {
        if (!name) throw {"message":"PuskFramework.xml.createElement: No element name"};
        value = value || false;
        var ownerDoc = this._doc;
        if (attr && attr.ownerDocument)
        {
            ownerDoc = attr.ownerDocument;
            delete attr.ownerDocument;
        }
        
        if (attr && (typeof attr.xmlns != 'undefined') && (typeof ownerDoc.createElementNS == 'function'))
        {
            var elem = ownerDoc.createElementNS(attr.xmlns, name);
        }
        else
        {
            var elem = ownerDoc.createElement(name);
        }

        if (attr) { this.setAttributes(elem, attr); }

        if (typeof value == 'string')
        {
            elem.appendChild(ownerDoc.createTextNode(value));
        }
        else if(value.tagName)
        {
            elem.appendChild(value);
        }
        else if(pf.$type(value) == 'array')
        {
            for(var i = 0; i < value.length; i++) elem.appendChild(value[i]);
        }
        return elem;
    };

    /**
     * Парсит текст как XML документ
     *
     * @param {String} txt xml текст
     * @return {Object}
     */
    this.parse = function(txt)
    {
        if (!txt) return pf.xml.createDocument();
        if (window.ActiveXObject)
        {
            var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
            xmlDoc.async = false;
            xmlDoc.validateOnParse = false;
            xmlDoc.loadXML(txt);
        }
        else
        {
            var parser = new DOMParser();
            var xmlDoc = parser.parseFromString(txt,"text/xml");
        }
        return xmlDoc;
    };

    /**
     * Сериализация узла документа в строку
     *
     * @param {Object} node узел
     * @return {String}
     */
    this.serialize = function(node)
    {
        if (!node) return false;
        if (typeof XMLSerializer != "undefined")
            return (new XMLSerializer()).serializeToString(node) ;
        else if (node.xml) return node.xml;
        else throw "XML.serialize is not supported or can't serialize " + node;
    };

    /**
    * Задать атрибуты узлу. Пакетом...
    */
    this.setAttributes = function(node, attributes)
    {
        if (node && node.nodeType == 1)
            for (var n in attributes)
            {
                if (attributes[n] != '')
                {
                    if (attributes[n] == null) attributes[n] = '';
                        node.setAttribute(n, attributes[n].toString ? attributes[n].toString() : attributes[n]);
                }
            }
    };

    /**
     * Преобразование фрагмента в документ
     *
     * @param {Object} fragment фрагмент
     * @param {Object} params параметры
     * @return {Document}
     */
    this.fragment2document = function(fragment, params)
    {
        var xmlDocument = pf.xml.createDocument('document');
        if (!fragment) return xmlDocument;
        params = params || {};
        
        var xmlRoot = xmlDocument.documentElement;
        if (params.id) xmlRoot.setAttribute('xhrId', params.id);
        if (params.instance) xmlRoot.setAttribute('instance', params.instance);

        for (var i=0, len = fragment.childNodes.length; i<len; i++)
        {
            xmlRoot.appendChild( fragment.childNodes[i].cloneNode(true) );
        }

        return xmlDocument;
    };

    this.get = function(elem, tagName, NS)
    {
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.xml.get(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        //if (!elem) return document.getElementsByTagName(null);
    
        if (NS && NS != '')
        {
            if (typeof elem.getElementsByTagNameNS == 'function')
                return elem.getElementsByTagNameNS(NS, tagName);
            else
            {
                elem.ownerDocument.setProperty('SelectionLanguage', 'XPath');
                return elem.selectNodes("*[local-name()='"+tagName+"' and namespace-uri()='"+NS+"']");
            }
        }
        else
        {
            return elem.getElementsByTagName(tagName);
        }
    };

    this.ns = function(elem)
    {
        //if (!elem) return null;
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.xml.ns(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        return (elem.namespaceURI || elem.getAttribute('xmlns') || null);
    };

    this.tag = function(elem)
    {
        //if (!elem) return null;
        if (pf.$type(elem) != "element") throw pf.$exGen('pf.xml.tag(): ' + 'typeof(elem) argument is ' + pf.$type(elem) + ', "element" expected');
        return (elem.localName || elem.nodeName || elem.tagName || null);
    };

    this._doc = this.createDocument('dummy');
    if (pf.browsCap.isSafari)
    {
        function _safariFix()
        {
            var _appendChild = Element.prototype.appendChild;
            Element.prototype.appendChild = function(elem)
            {
                if (this.ownerDocument == elem.ownerDocument)
                    return _appendChild.call(this, elem);
                return _appendChild.call(this, this.ownerDocument.adoptNode(elem));
            }
        }
        _safariFix();
    };
};

/**
 *$Id: xpath.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 */
if(window.XMLDocument && document.implementation.hasFeature("XPath", "3.0") && !XMLDocument.prototype.selectNodes)
{
    XMLDocument.prototype.selectNodes = function(cXPathString, xNode)
    {
        if( !xNode ) { xNode = this; }

        var oNSResolver = this.createNSResolver(this.documentElement);
        var aItems = this.evaluate(cXPathString, xNode, oNSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        var aResult = [];
        for( var i = 0; i < aItems.snapshotLength; i++)
        {
            aResult[i] =  aItems.snapshotItem(i);
        }

        return aResult;
    };

    XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode)
    {
        if( !xNode ) { xNode = this; }

        var oNSResolver = this.createNSResolver(this.documentElement);
        var xItems = this.evaluate(cXPathString, xNode, oNSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        if (xItems.snapshotLength > 0)
        {
            return xItems.snapshotItem(0);
        }
        else
        {
            return null;
        }
    };


    Element.prototype.selectNodes = function(cXPathString)
    {
        if(this.ownerDocument.selectNodes)
        {
            return this.ownerDocument.selectNodes(cXPathString, this);
        }
        else{throw "For XML Elements Only";}
    };

    Element.prototype.selectSingleNode = function(cXPathString)
    {

        if(this.ownerDocument.selectSingleNode)
        {
            return this.ownerDocument.selectSingleNode(cXPathString, this);
        }
        else{throw "For XML Elements Only";}
    };
};

/**
 *$Id: xslt.js 130 2009-03-18 15:38:57Z aanpilogov $
 *
 */
function CXSLTTransformer()
{
    this.XSLCacheIDX = [];
    this.XSLCacheXML = [];
    this.defaultOpts = {'async' : true, 'output' : 'string', 'callback' : null, 'container':null};
    var cachePointer = 0;
    var maxCacheItems = 20;
    var pf = PuskFramework;
    var me = this;

    function disableOutputEscaping(elem)
    {
        var container, doeyContainers = elem.getElementsByTagName('VAR');
        var toDelete = [];
        var cn = doeyContainers.length;
        for(var i=0; i<cn; i++)
        {
            container = doeyContainers[i];
            if (!pf.elem.hasClass(container, 'doey')) { continue; }
            var d = pf.$$$('div');
            d.innerHTML = pf.str.unescapeHtml(container.innerHTML);
            var p = container.parentNode;
            while(d.firstChild)
            {
                p.insertBefore(d.firstChild, container);
            }
            toDelete.push(container);
        }
        for(var i = 0; i < toDelete.length; i++)
        {
            pf.$_(toDelete[i]);
        }
        return elem;
    };

    function undoey(x)
    {
        var doeys = x.selectNodes('//*[@disable-output-escaping = "yes"]');
        for (var i = 0, l = doeys.length; i < l; i++)
        {
            var dummy = pf.$$$('var');
            dummy.setAttribute('class', 'doey');
            doeys[i].parentNode.insertBefore(dummy, doeys[i]);
            dummy.appendChild(doeys[i]);
        }
    };

    function getResType(res)
    {
        var type = pf.$type(res);
        switch(true)
        {
            case (type == 'object' || type == 'element'): return 'DOM';
            case (type == 'string' && pf.str.startsWith(res, '<?xml', true)): return 'XML';
            case (type == 'string' && res.length > 4): return 'URL';
        }
        return false;
    };

    function getXML(res, scope, callback)
    {
        scope = scope || 'xml';
        var resType = getResType(res);
        var async = (pf.$type(callback) == 'function');
        switch(resType)
        {
            case 'DOM':
            {
                return res;
            }
            case 'XML':
            {
                return pf.xml.parse(res);
            }
            case 'URL':
            {
                if (async)
                {
                    pf.ajax.get({'url': res, 'onSuccess': function(req){return callback(req.responseXML);}, 'generateUniqueUrl' : false});
                    return false;
                }
                else
                {
                    var req = pf.ajax.get({'url': res, 'generateUniqueUrl' : false, 'async' : false});
                    return req.responseXML;
                }
            }
            default:
            {
                throw pf.$exGen('xslt.transform(): typeof(xml) argument is unknown, expected object|element|string');
            }
        }
    };

    function getXSL(xsl, callback)
    {
        var async = (pf.$type(callback) == 'function');
        var resType = getResType(xsl);
        if (resType == 'URL')
        {
            var cacheRec = pf.arr.indexOf(me.XSLCacheIDX, xsl);
            if (me.XSLCacheIDX[cacheRec])
            {
                return me.XSLCacheXML[cacheRec];
            }
        }
        var xslObj = getXML(xsl, 'xsl', async ? _prepare : null);
        if (!async || xslObj) return _prepare(xslObj);

        function _prepare(xslObj)
        {
            if (pf.browsCap.isMozilla)
            {
                undoey(xslObj);
            }

            if (resType == 'URL')
            {
                if (cachePointer >= maxCacheItems)
                {
                    cachePointer = 0;
                    me.XSLCacheXML[cachePointer] = xslObj;
                    me.XSLCacheIDX[cachePointer] = xsl;
                }
                else
                {
                    me.XSLCacheXML.push(xslObj);
                    me.XSLCacheIDX.push(xsl);
                }
                cachePointer++;
            }
            return (async) ? callback(xslObj) : xslObj;
        }
    };

    function getOutputMethod(xsl)
    {
        var tags = xsl.documentElement.childNodes;
        var method = 'html';
        for (var i = 0; i < tags.length; i++)
        {
            if (tags[i].tagName == 'xsl:output')
            {
                method = tags[i].getAttribute('method');
                break;
            }
        }
        return method;
    };

    function doTransformNative(xml, xsl, arg, method, output)
    {
        var oXSLTProcessor = new XSLTProcessor();
        oXSLTProcessor.importStylesheet(xsl);

        for (var prop in arg)
        {
            if (arg[prop] === 0) arg[prop] = '0';
            if (prop != '' && arg[prop] && arg[prop].toString) oXSLTProcessor.setParameter("", prop, arg[prop].toString());
        }

        if (method == 'xml')
        {
            return (output == 'dom') ? oXSLTProcessor.transformToDocument(xml) : pf.xml.serialize(oXSLTProcessor.transformToDocument(xml));
        }

        var d = pf.$$$('div');
        d.appendChild(oXSLTProcessor.transformToFragment(xml, document));
        if (pf.browsCap.isMozilla)
        {
            d = disableOutputEscaping(d);
        }
        d.innerHTML = postProcessJs(d.innerHTML);

        switch(output)
        {
            default:
            case 'string':
            {
                return d.innerHTML;
            }
            case 'dom':
            {
                var f = document.createDocumentFragment();
                while(d.firstChild) f.appendChild(d.firstChild);
                return f;
            }
        }
    };

    function doTransformIE(xml, xsl, arg, method, output)
    {
        var oXSLTTemp = new ActiveXObject("Msxml2.XSLTemplate.3.0");
        var xslDocument = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.3.0");
        xslDocument.validateOnParse = false;
        xslDocument.loadXML(pf.xml.serialize(xsl));
        oXSLTTemp.stylesheet = xslDocument;

        var oXSLTProcessor = oXSLTTemp.createProcessor();
        oXSLTProcessor.input = xml;
        for (prop in arg)
        {
            if (arg[prop] === 0) arg[prop] = '0';
            if (prop != '' && arg[prop] && arg[prop].toString) oXSLTProcessor.addParameter(prop, arg[prop].toString());
        }
        oXSLTProcessor.transform();
        var content = oXSLTProcessor.output;
        if (method == 'xml')
        {
            if(output == 'dom')
            {
                var outDoc = new ActiveXObject("Microsoft.XMLDOM");
                outDoc.loadXML(content);
                return outDoc;
            }
            else
            {
                return postProcessJs(content);
            }
        }

        switch(output)
        {
            default:
            case 'string':
            {
                return postProcessJs(content);
            }
            case 'dom':
            {
                var f = document.createDocumentFragment();
                var d = pf.$$$('div');
                d.innerHTML = content;
                while (d.firstChild)
                {
                     f.appendChild(d.firstChild);
                }
                return f;
            }
        }
    };

    this.transform = function(xml, xsl, arg, opts)
    {
        switch(pf.$type(opts))
        {
            case 'function':
                opts = pf.hash.merge(this.defaultOpts, {'callback': opts});
                break;
            case 'string':
                opts = pf.hash.merge(this.defaultOpts, {'container': pf.$(opts)});
                break;
            case 'element':
                opts = pf.hash.merge(this.defaultOpts, {'container': opts});
                break;
            case 'object':
                opts = pf.hash.merge(this.defaultOpts, opts);
                break;
            default:
                throw pf.$exGen('xslt.transform(): typeof(options) argument is ' + pf.$type(opts) + ', expected function|string|element|hash');
        }

        var async = opts['async'];
        var Engine = null;

        if(typeof XSLTProcessor != "undefined")
        {
            Engine = doTransformNative;
        }
        else if(window.ActiveXObject)
        {
            Engine = doTransformIE;
        }
        else
        {
            throw pf.$exGen('xslt.transform(): no Engine found');
            return false;
        }

        try
        {
            var xmlObj = getXML(xml, 'xml', async ? _prepareXSL : null);
            var xslObj = null;

            if (!async || xmlObj) return(_prepareXSL(xmlObj));
        }
        catch(e)
        {
            debugError('Transform error. Message: '+e.message+' Description: '+e.description);
            return false;
        }

        function _prepareXSL(_xmlObj)
        {
            xmlObj = _xmlObj;
            xslObj = getXSL(xsl, async ? _doTransform : null);
            if (!async || xslObj) return(_doTransform(xslObj));
        }

        function _doTransform(_xslObj)
        {
            xslObj = _xslObj;
            if (!xmlObj || !xslObj) { debugError('Empty transf'); return false; }
            var output = Engine(xmlObj, xslObj, arg || {}, getOutputMethod(xslObj), opts['output']);
            if (!output) { output = ''; /* return false;*/ }
            if (opts['container']) { opts['container'].innerHTML = output; }
            return (async && pf.$type(opts['callback']) == 'function') ? opts['callback'](output) : output;
        }

    };

    this.transformXML = this.transform;

    function postProcessJs(input)
    {
        if ((typeof input != 'string') || (input.indexOf('jsFunction') == -1)) { return input; }

        function execJs(full, func, args)
        {
            return eval(func+'('+args+')')
        };
        return input.replace(/jsFunction\('([\w.]+)', (.*?')\)/g, execJs);
    };

}
window.XSLTTransformer = new CXSLTTransformer();

/**
 *$Id: ajax.js 135 2009-06-04 08:09:14Z pdidevich $
 *
 * Библиотека для выполнения AJAX запросов.
 */
PuskFramework.CAjax = function()
{
    this.cnActive = 0;
    this.maxActive = 3;
    this.maxActiveGroup = {};
    this.queue = [];
    this.reqCount = 0;
    this.registry = {};
    this.ignoreAll = false;
    var ajaxController = this;
    var pf = PuskFramework;


    /**
     * Вызывает метод GET с указанными аргументами
     *
     * @param {Object} args
     * @return {Integer} возвращает идентификатор запроса
     */
    this.get = function(args)
    {
        var request = _createRequest();
        if (!request) { return false; }
        request.method = 'GET';
        request.handleArguments(args);
        request.process();
        return request;
    };

    /**
     * Вызывает метод POST с указанными аргументами
     *
     * @param {Object} args
     * @return {Integer} возвращает идентификатор запроса
     */
    this.post = function(args)
    {
        var request = _createRequest();
        if (!request) { return false; }
        request.method = 'POST';
        request.handleArguments(args);
        request.process();
        return request;
    };

    /**
     * Вызывает пользовательский метод с указанными аргументами
     *
     * @param {Object} args
     * @return {Integer} возвращает идентификатор запроса
     */
    this.doRequest = function(method, args)
    {
        var request = _createRequest();
        if (!request) { return false; }
        request.method = method.toUpperCase();
        request.handleArguments(args);
        request.process();
        return request;
    };

    /**
     * Отправляет на сервер форму, метод и URL берутся непосредственно из формы
     *
     * @param {Object} theform
     * @param {Object} args
     * @return {Integer} возвращает идентификатор запроса
     */
    this.submit = function(theform, args)
    {
        var request = _createRequest();
        if (!request) { return false; }
        var serializedForm = pf.form.serialize(theform);
        request.method = theform.method.toUpperCase() || "GET";
        request.url = theform.action;
        request.handleArguments(args);
        request.queryString = serializedForm;
        request.process();
        return request;
    };

    /**
     * Отменяет указанный запрос
     *
     * @param {Object} reqId идентификатор запроса
     */
    this.abort = function(reqId)
    {
        if (this.registry[reqId])
        {
            try{
                _destruct(this.registry[reqId]);
            } catch(e){debugError(e.message);}
        }
    };

    /**
     * Отменяет все выполняющиеся и ожидающие запросы
     */
    this.abortAll = function()
    {
        this.queue = [];
        for (var reqId in this.registry)
        {
            this.abort(reqId);
        }
        this.cnActive = 0;
    };

    /**
     * Устанавливает прокси-сервер для всех вызовов
     */
    this.proxy = function(url)
    {
        return url;
    };

/*
 *  Private functions
 */
    function _createRequest()
    {
        if (ajaxController.ignoreAll) { return false; }
        var req = new XHRWrapper();
        req.id = ++ajaxController.reqCount;
        ajaxController.registry[req.id] = req;
        return req;
    };

    function _fetchNext()
    {
        if (ajaxController.queue.length == 0) return false;
        var request = ajaxController.queue.shift();
        if (!request) return false;
        request.process();
        return true;
    };

    function _useFlashProxy(args)
    {
		if(args.url.match(/xsl$/))
		{
			return false;
		}

        if (args.flashProxy == 'off' || !pf.flash || !pf.flash.httpRequest || !pf.flash.httpRequest.isLoaded) { return false; }
        if (args.flashProxy == 'on') { return true; }
        if (!args.url) { return false; }
        var out = args.url.match(/^https?:\/\/([^/]+)/i);
        if (!out || !out[1]) { return false; }
        return (out[1] != window.location.host);
    };

    function _getTransport(args)
    {
        if (_useFlashProxy(args))
        {
            return new FlashHttpRequest(args.id);
        }

        if (window.XMLHttpRequest) {
            return new XMLHttpRequest();
        }

        if (window.ActiveXObject) {
            try {
                return new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                try {
                    return new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e) {
                    this.ignoreAll = true;
                    return null;
                }
            }
        }
        this.ignoreAll = true;
        return null;
    };

    function _onLoadingInternal(req)
    {
        if (req.handled['loading']) { return false; }
        req.handled['loading'] = true;
        if (req.groupName!=null) {
            if (typeof(ajaxController.maxActiveGroup[req.groupName])=="undefined") {
                ajaxController.maxActiveGroup[req.groupName] = 0;
            }
            ajaxController.maxActiveGroup[req.groupName]++;
            if (ajaxController.maxActiveGroup[req.groupName]==1 && typeof(req.onGroupBegin)=="function")
            {
                try
                {
                    req.onGroupBegin(req.groupName);
                }
                catch(e)
                {
                    _onExceptionInternal(req, e, 'onGroupBegin');
                }
            }
        }
        if (typeof(req.onLoading)=="function")
        {
            try
            {
                req.onLoading(req);
            }
            catch(e)
            {
                _onExceptionInternal(req, e, 'onLoading');
            }
        }
    };

    function _onLoadedInternal(req)
    {
        if (req.handled['loading']) { return; }
        req.handled['loading'] = true;
        if (typeof(req.onLoaded)=="function")
        {
            try
            {
                req.onLoaded(req);
            }
            catch(e)
            {
                _onExceptionInternal(req, e, 'onLoaded');
            }
        }
    };

    function _onInteractiveInternal(req)
    {
        if (req.handled['interactive']) { return; }
        req.handled['interactive'] = true;
        if (typeof(req.onInteractive)=="function")
        {
            try
            {
                req.onInteractive(req);
            }
            catch(e)
            {
                _onExceptionInternal(req, e, 'onInteractive');
            }
        }
    };

    function _onCompleteInternal(req)
    {
        if (req.handled['complete'] || req.aborted) { return; }
        req.handled['complete'] = true;
        if (req.groupName!=null) {
            ajaxController.maxActiveGroup[req.groupName]--;
            if (ajaxController.maxActiveGroup[req.groupName]==0 && typeof(req.onGroupEnd)=="function")
            {
                try
                {
                    req.onGroupEnd(req.groupName);
                }
                catch(e)
                {
                    _onExceptionInternal(req, e, 'onGroupEnd');
                }
            }
        }

        try {
            req.status = req.xhr.status;
            req.statusText = req.xhr.statusText;
            req.responseText = req.xhr.responseText;
            req.responseXML = req.xhr.responseXML;
        } catch(e) { return; }

        if (typeof(req.onComplete)=="function")
        {
            try
                {
                    req.onComplete(req);
                }
                catch(e)
                {
                    _onExceptionInternal(req, e, 'onComplete');
                }

        }
        if (req.xhr.status==200 && typeof(req.onSuccess)=="function")
        {
            try
                {
                    if (req.responseXML && typeof(req.responseXML.setProperty) != 'undefined') req.responseXML.setProperty('SelectionLanguage', 'XPath');
                    req.onSuccess(req);
                }
                catch(e)
                {
                    _onExceptionInternal(req, e, 'onSuccess');
                }

        }
        else if (typeof(req.onError)=="function")
        {
            try
                {
                    req.onError(req);
                }
                catch(e)
                {
                    _onExceptionInternal(req, e, 'onError');
                }
        }

        // Clean up so IE doesn't leak memory
        _destruct(req);
    };

    function _onTimeoutInternal(req)
    {
        if (req!=null && req.xhr!=null && !req.handled['complete']) {
            req.aborted = true;
            req.xhr.abort();
            if (req.groupName!=null) {
                ajaxController.maxActiveGroup[req.groupName]--;
                if (ajaxController.maxActiveGroup[req.groupName]==0 && typeof(req.onGroupEnd)=="function")
                {
                    try
                    {
                        req.onGroupEnd(req.groupName);
                    }
                    catch(e)
                    {
                        _onExceptionInternal(req, e, 'onGroupEnd');
                    }
                }
            }
            if (typeof(req.onTimeout)=="function")
            {
                try
                {
                    req.onTimeout(req);
                }
                catch(e)
                {
                    _onExceptionInternal(req, e, 'onTimeout');
                }
            }
            _destruct(req);
        }
    };

    function _onExceptionInternal(req, err, context)
    {
        if (req.onException && typeof(req.onException) == "function")
        {
            req.onException(req, err, context);
            _destruct(req);
        }
        else
        {
            _destruct(req);
            throw err;
        }
    };

    function _destruct(req)
    {
        req.handled['complete'] = true;
        req.aborted = true;
        if (req.xhr)
        {
            delete req.xhr['onreadystatechange'];
            req.xhr.abort();
        }
        ajaxController.cnActive--;
        delete ajaxController.registry[req.id];
        window.clearTimeout(req.timer);
        req.xhr = null;

        if (ajaxController.queue.length > 0 && ajaxController.cnActive < ajaxController.maxActive)
        {
            window.setTimeout(_fetchNext, 10);
        }
    };


    XHRWrapper = function()
    {
        this.id = null;
        this.timeout = null;
        this.timer = null;
        this.generateUniqueUrl = true;
        this.cacheTime = 0; // minutes
        this.url = window.location.href;
        this.method = 'GET';
        this.async = true;
        this.username = null;
        this.password = null;
        this.parameters = {};
        this.rawData = '';
        this.groupName = null;
        this.queryString = '';
        this.responseText = null;
        this.responseXML = null;
        this.status = null;
        this.statusText = null;
        this.aborted = false;
        this.customHeaders = {};
        this.flashProxy = 'auto'; // ['on', 'off', 'auto']
        this.handled = {}; // list of internal events already handled
        this.xhr = null;

        this.onLoading = null;
        this.onLoaded = null;
        this.onInteractive = null;
        this.onComplete = null;
        this.onSuccess = null;
        this.onError = null;
        this.onTimeout = null;
        this.onException = null;
        this.onGroupBegin = null;
        this.onGroupEnd = null;

        var req = this;


        function _readyStateChange()
        {
            if (req==null || req.xhr==null) { return false; }
            switch (req.xhr.readyState)
            {
                case 1: return _onLoadingInternal(req);
                case 2: return _onLoadedInternal(req);
                case 3: return _onInteractiveInternal(req);
                case 4: return _onCompleteInternal(req);
            }
        };

        this.process = function()
        {
            if (ajaxController.cnActive >= ajaxController.maxActive)
            {
                ajaxController.queue.push(req);
                return;
            }

            req.xhr = _getTransport(req);
            if (req.xhr==null) { return null; }
            req.xhr.onreadystatechange = _readyStateChange;

            ajaxController.cnActive++;
            if (req.method=="GET")
            {
                if (req.cacheTime) {
                    var curDate = new Date();
                    var tail = curDate.getDate()*24*60 + curDate.getHours()*60 + curDate.getMinutes();
                    req.parameters["_URID"] = '' + curDate.getFullYear() + (curDate.getMonth()+1) + (tail - (tail % req.cacheTime));
                }
                else if (req.generateUniqueUrl) {
                    req.parameters["_URID"] = '' + pf.$time() + req.id;
                }
            }
            var content = null;

            req.queryString += (req.queryString.length>0 ? '&' : '') + pf.hash.serialize(req.parameters);

            if (req.method=="GET") {
                if (req.queryString.length>0) {
                    req.url += ((req.url.indexOf("?")>-1)?"&":"?") + req.queryString;
                }
            }

            req.url = ajaxController.proxy(req.url);
            if (req.username)
                req.xhr.open(req.method,req.url,req.async,req.username,req.password);
            else
                req.xhr.open(req.method,req.url,req.async);

            var ctOverride = false;
            for (var i in req['customHeaders']) {
                if (req['customHeaders'][i]['name'] && req['customHeaders'][i]['value'])
                {
                    req.xhr.setRequestHeader(req['customHeaders'][i]['name'], req['customHeaders'][i]['value']);
                    if (req['customHeaders'][i]['name'].toUpperCase() == 'CONTENT-TYPE') ctOverride = true;
                }
            }
//            if (window.Prj) { req.xhr.setRequestHeader("X-Client-API", window.Prj.name+' '+window.Prj.version); }
            req.xhr.setRequestHeader("X-Requested-With", 'XMLHttpRequest');
            if (req.method=="POST") {
                if (typeof(req.xhr.setRequestHeader)!="undefined" && !ctOverride) {
                    req.xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
                }
                content = req.rawData || req.queryString;
            }

            if (req.timeout>0) {
                req.timer = window.setTimeout('_onTimeoutInternal(req)', req.timeout);
            }
            req.xhr.send(content);
            if (!req.async && pf.browsCap.isMozilla)
            {
                _readyStateChange();
            }
            return true;
        };

        this.handleArguments = function(args)
        {
            if (pf.$empty(args)) { return false; }
            for (var i in args)
            {
                if (typeof(req[i])=="undefined") {
                    req.parameters[i] = args[i];
                }
                else {
                    req[i] = args[i];
                }
            }
        };

        this.getAllResponseHeaders = function()
        {
            if (req.handled['complete']) {
                return req.xhr.getAllResponseHeaders();
            }
            debugError("Cannot getAllResponseHeaders because a response has not yet been received");
        };

        this.getResponseHeader = function(headerName)
        {
            if (req.handled['complete']) {
                return req.xhr.getResponseHeader(headerName);
            }
            debugError("Cannot getResponseHeader because a response has not yet been received");
        };
    };
};
PuskFramework.ajax = new PuskFramework.CAjax();
/**
 * Управление CSS файлами
 *$Id: cssmanager.js 101 2009-01-15 13:21:57Z aanpilogov $
 */
PuskFramework.cssManager = new function()
{
    var me = this;
    me.registry = {};
    me.head = null;
    me.isIE = false;
    me.uriSuffix = '';
    var pf = PuskFramework;

    /**
     * Загрузить css файл с указанного uri
     *
     * @param {Object} uri путь к файлу
     * @param {Object} instance уникальный идентификатор группы
     * @param {Object} onReadyStateChange callback после загрузки
     */
    me.loadStyle = function(uri, instance, onReadyStateChange)
    {
        registerEntity(uri, instance, 'link', {
            'uri': uri,
            'onReadyStateChange': onReadyStateChange
        });
    };

    /**
     * Зугрузить инлайн стиль
     *
     * @param {Object} style css текст
     * @param {Object} instance уникальный идентификатор группы
     * @param {Object} id уникальный идентификатор стиля
     */
    me.loadInlineStyle = function(style, instance, id)
    {
        if (!id)
        {
            id = 'style_' + Math.round(Math.random(1, 1000) * 999);
        }
        registerEntity(id, instance, 'inline', {
            'style': style,
            'id': id
        });
    };

    /**
     * Выгрузить указанный стиль из документа
     *
     * @param {Object} id уникальный идентификатор
     * @param {Object} instance уникальный идентификатор группы (не выгружает стиль, если другая группа его использует)
     */
    me.releaseStyle = function(id, instance)
    {
        if (me.registry[id])
        {
            var index = pf.arr.indexOf(me.registry[id], instance);
            if (index > 0)
            {
                me.registry[id].splice(index, 1);
            }
            window.setTimeout(function()
            {
                if (me.registry[id] && me.registry[id].length == 1)
                {
                    if (me.isIE)
                    {
                        var importsCount = me.registry[id][0]['object'].parentStyleSheet.imports.length;
                        var uri = (me.registry[id][0]['type'] == 'inline') ? (me.fakeCss + '?' + id) : id;
                        for (var i = 0; i < importsCount; i++)
                        {
                            if (me.registry[id][0]['object'].parentStyleSheet.imports[i]['href'] == uri)
                            {
                                me.registry[id][0]['object'].cssText = '';
                                me.registry[id][0]['object'].parentStyleSheet.removeImport(i);
                                delete (me.registry[id]);
                                break;
                            }
                        }
                    }
                    else
                    {
                        pf.$_(me.registry[id][0]['object']);
                        delete (me.registry[id]);
                    }
                }
            }, 1000);
        }
    };

    /**
     * Выгружает все стили, подгруженные для заданной группы
     *
     * @param {Object} instance идентификатор группы
     */
    me.releaseInstance = function(instance)
    {
        for (var id in me.registry)
        {
            me.releaseStyle(id, instance);
        }
    };

    /**
     * @private
     */
    function init()
    {
        me.head = document.getElementsByTagName('head').item(0);
        me.isIE = document.createStyleSheet ? true : false;
        if (window.Prj)
        {
            me.uriSuffix = '?' + window.Prj.version;
        }
        if (me.isIE)
        {
            me.fakeCss = '/skin/fake.css';
            me.styleContainersCount = 4;
            me.styleContainers = [];
            me.styleTags = [];
            for (var i = 0; i < me.styleContainersCount; i++)
            {
                me.styleContainers[i] = createStyleContainer('styleContainer.' + i, i);
            }
        }
    };

    /**
     * @private
     */
    function createStyleContainer(title, i)
    {
        var newStyle = pf.$$$('STYLE');
        newStyle.setAttribute('type', 'text/css');
        newStyle.title = title;
        me.styleTags[i] = newStyle;
        me.head.appendChild(newStyle);
        var stylesCount = document.styleSheets.length;
        for (var i = 0; i < stylesCount; i++)
        {
            if (document.styleSheets[i]['title'] == title)
            { return (document.styleSheets[i]); }
        }
        return (false);
    };

    /**
     * @private
     */
    function registerEntity(id, instance, type, loaderOptions)
    {
        if (!me.registry[id])
        {
            me.registry[id] = [];
            me.registry[id][0] = {};
            me.registry[id][0]['type'] = type;
            switch (type)
            {
                case 'link':
                {
                    me.registry[id][0]['object'] = createStyle(loaderOptions['uri'], loaderOptions['onReadyStateChange']);
                }
;
break;
                case 'inline':
                {
                    me.registry[id][0]['object'] = createInlineStyle(loaderOptions['style'], loaderOptions['id']);
                }
;
break;
            }
        }
        if (pf.arr.indexOf(me.registry[id], instance) < 1)
        {
            me.registry[id][me.registry[id].length] = instance;
        }
        afterLoad();
    };

    /**
     * @private
     */
    function afterLoad()
    {
        if (me.isIE)
        {
            setTimeout(function()
            {
                if (pf.elem.hasClass(document.body, 'cssRenderFix'))
                {
                    pf.elem.delClass(document.body, 'cssRenderFix');
                }
                else
                {
                    pf.elem.addClass(document.body, 'cssRenderFix');
                }
            }, 200);
        }
    };

    /**
     * @private
     */
    function createInlineStyle(style, id)
    {
        var stylesheet;
        if (me.isIE)
        {
            var index;
            for (var i = 0; i < me.styleContainersCount; i++)
            {
                if (me.styleContainers[i].imports.length < 31)
                {
                    index = me.styleContainers[i].addImport(me.fakeCss + '?' + id);
                    stylesheet = me.styleContainers[i].imports[index];
                    pf.evt.add(me.styleTags[i], 'readystatechange', fillReadyContainer);

                    function fillReadyContainer(evt)
                    {
                        if (me.styleTags[i].readyState == 'complete')
                        {
                            stylesheet.cssText = style;
                            pf.evt.remove(me.styleTags[i], 'readystatechange', fillReadyContainer);
                        }
                    }
                    break;
                }
            }
        }
        else
        {
            stylesheet = pf.$$$('STYLE');
            stylesheet.setAttribute('type', 'text/css');
            stylesheet.setAttribute('media', 'screen');
            stylesheet.setAttribute('title', id);
            stylesheet.appendChild(document.createTextNode(style));
            me.head.appendChild(stylesheet);
            enableStyleSheet(id);
        }
        return (stylesheet);
    };

    /**
     * @private
     */
    function createStyle(uri, onReadyStateChange)
    {
        var stylesheet;
        if (me.isIE)
        {
            var index;
            for (var i = 0; i < me.styleContainersCount; i++)
            {
                if (me.styleContainers[i].imports.length < 31)
                {
                    index = me.styleContainers[i].addImport(uri + me.uriSuffix);
                    stylesheet = me.styleContainers[i].imports[index];
                    break;
                }
            }
        }
        else
        {
            stylesheet = pf.$$$('LINK');
            stylesheet.setAttribute('rel', 'stylesheet');
            stylesheet.setAttribute('type', 'text/css');
            stylesheet.setAttribute('href', uri + me.uriSuffix);
            if (onReadyStateChange != null)
            {
                pf.evt.add(stylesheet, 'readystatechange', onReadyStateChange);
            }
            document.getElementsByTagName('head').item(0).appendChild(stylesheet);
        }
        return (stylesheet);
    };

    /**
     * @private
     */
    function enableStyleSheet(id)
    {
        var stylesheet = getStyleSheetById(id);
        if (stylesheet)
        {
            stylesheet.disabled = false;
        }
    };

    /**
     * @private
     */
    function getStyleSheetById(id)
    {
        var stylesheetsCount = document.styleSheets.length - 1;
        for (var i = stylesheetsCount; i >= 0; i--)
        {
            if (document.styleSheets[i]['title'] == id)
            { return (document.styleSheets[i]); }
        }
        return (null);
    };
    
    PuskFramework._initListeners.push(init);
};
/**
 *$Id: srvapi.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 * Работа с AJAX запросами и ответами в формате Pusk XML Response
 */
PuskFramework.srvapi = new function()
{
    var pf = PuskFramework;
    this.scriptDispatcher = {};
    this.scriptLoading = false;
    this.scriptStack = [];     // общий стек - для режима jsSequental
    this._jsSequental = {};

    /**
     * Оборачивает запрос для проксирования через прозрачный сервер
     * @param {Object} url URL
     * @return {String}
     */
    this.getProxy = function(url)
    {        
        if (!url) url = 'http://';
        if (!url.match(/^http(s|):\/\//)) return ('/proxy/http/' + url);        
        var newurl = url.replace(/^http(s|):\/\//, "http$1/");
        return ('/proxy/' + newurl);
    };

    /**
     * Осуществить запрос к серверу. Ответ обрабатывается по умолчанию внутренним обработчиком.
     *
     * @param {Object} url URL
     * @param {Object} after callback
     * @param {Object} onerr callback в случае ошибки
     * @param {Object} sync флаг синхронности запроса
     */
    this.invoke = function(url, after, onerr, sync)
    {
        url = url || '';
        return this._invoke(url, 'get', {}, after, onerr, sync);
    };

    /**
     * Осуществить запрос к серверу. Ответ обрабатывается по умолчанию внутренним обработчиком.
     *
     * @param {Object} url URL
     * @param {Object} after callback
     * @param {Object} onerr callback в случае ошибки
     * @param {Object} sync флаг синхронности запроса
     */
    this.pinvoke = function(url, params, after, onerr, sync)
    {
        url = url || '';
        return this._invoke(url, 'post', params, after, onerr, sync);
    };

    this._invoke = function(url, method, params, after, onerr, sync)
    {
        params = params || {};
        method = method || 'get';
        after = after || this.parseResponse;
        _url = url+((url.indexOf("?")>-1)?"&":"?")+'xml=1';

        debugNotice('<a href="'+_url+'" target="_blank">Запрос</a>');
        var reqStime = pf.$time();
        serverSetStatus('busy');
        return pf.ajax[method](
        {
            'url': _url,
            'async' : !sync,
            'parameters': params,
            'onSuccess': function (req)
            {
                try {
                    debugResult('Пришел ответ от сервера, задержка '+ pf.$time(reqStime) + 'мс.');
                    after(req, url);
                } catch(err)
                {
                    debugError('не получается выполнить коллбэк! '+err.name + ' : '+ err.message);
                    serverSetStatus('error');
                }
             },
             'onError':function(req)
             {
                debugError('Ошибка запроса!\nСтатус='+req.statusText);
                serverSetStatus('error');
                if (onerr) { onerr(req); }
             }
        });
    };

    /**
     * Осуществить запрос к серверу, передав в качестве объекта действия application
     *
     * @param {Object} props параметры запроса
     * @param {Object} after callback
     * @return {String}
     */
    this.appInvoke = function(props, after)
    {
        props = props || new Array();
        props['object'] = 'application';
        return pf.srvapi.invoke( '/server/?' + pf.hash.serialize(props), after || null);
    };

    /**
     * обработка xml-ответа сервера в формате pusk.ru
     *
     * @param {Object} xmlrequest объект запроса
     * @param {Object} url адрес запроса
     */
    this.parseResponse = function(xmlrequest, url)
    {        
        if (pf.$type(xmlrequest) != "object") throw pf.$exGen('pf.srvapi.parseResponse(): ' + 'typeof(xmlrequest) argument is ' + pf.$type(xmlrequest) + ', "object" expected');
        if (pf.$type(xmlrequest.responseXML) != "object") throw pf.$exGen('pf.srvapi.parseResponse(): ' + 'typeof(xmlrequest.responseXML) is ' + pf.$type(xmlrequest.responseXML) + ', "object" expected');
                
        xml = xmlrequest.responseXML;
        if (!xml || !xml.documentElement)
        {
            debugError('No data @ <a href="'+url+'&xml=1" target="_blank">response</a>');
            return false;
        }

        xml = xml.documentElement;
        var responseId = Math.rand(1, 1000000);

        pf.srvapi._parseResultBlock(xml, responseId);
        pf.srvapi._parseInlineStyle(xml);
        pf.srvapi._parseLinkedStyle(xml);
        var htmlData = pf.srvapi._parseHtml(xml);
        var xmlData  = pf.srvapi._parseXml(xml, xmlrequest);
        pf.srvapi.scriptDispatcher[responseId] = {'htmlData':htmlData, 'xmlData':xmlData};

        if (pf.srvapi._jsSequental[responseId])
        {
            pf.srvapi._parseScriptsSequental(xml, responseId);
        } else {
            pf.srvapi._parseScripts(xml, responseId);
        }

        debugResult('<a href="'+url+'&xml=1" target="_blank">Запрос</a> успешно обработан');
    };

    /**
     * Считываем код ответа и process instructions
     *
     * @private
     * @param {Object} xml XML документ с ответом
     * @param {Object} responseId идентификатор ответа
     */
    this._parseResultBlock = function(xml, responseId)
    {             
        var result = xml.getElementsByTagName('result');
        if (!result) { return false; }
        result = result[0];

        var code = parseInt(result.getAttribute('code'));
        serverSetStatus(code);

        pf.srvapi._jsSequental[responseId] = false;
        var pi, instructions = result.getElementsByTagName('pi');
        for (var i=0, len=instructions.length; i<len; i++)
        {
            pi = instructions[i];
            switch(pi.getAttribute('name'))
            {
                case 'js_sequental':
                {
                    pf.srvapi._jsSequental[responseId] = (pi.getAttribute('value') == 'true');
                }
            }
        }
    };

    /**
     * Инлайн-стили, обработка и подгрузка
     *
     * @private
     * @param {Object} xml XML ответ
     */
    this._parseInlineStyle = function(xml)
    {
        var item, items = xml.getElementsByTagName('style');
        if(!items) { return false; }

        for (var i=0, len=items.length; i < len; i++)
        {
            item = items[i];
            var style = pf.elem.getText(item);
            var instance = item.getAttribute('instance') || 'system';
            pf.cssManager.loadInlineStyle(style, instance, item.getAttribute('id'));
        }
        return len;
    };

   /**
    * Внешние стили - обработка и подгрузка
    *
    * @private
    * @param {Object} xml XML ответ
    */
    this._parseLinkedStyle = function(xml)
    {
        var items = xml.getElementsByTagName('stylesheet');
        if (!items) { return false; }

        for (var i=0, len=items.length; i<len; i++)
        {
            var linkHref = items[i].getAttribute('href');
            var instance = items[i].getAttribute('instance');
            pf.cssManager.loadStyle(linkHref, instance);
        }
        return len;
    };

    /**
     * Парсинг HTML блоков
     *
     * @private
     * @param {Object} xml XML ответ
     */
    this._parseHtml = function(xml)
    {
        var item, items = xml.getElementsByTagName('html');
        if (!items) { return {}; }

        var htmlData = {};
        var itemId, parsed, instance;

        for (var i=0, len=items.length; i<len; i++)
        {
            item = items[i];
            parsed = '';
            for (var j=0, len2=item.childNodes.length; j<len2; j++)
            {
                if (item.childNodes[j].data != '')
                {
                    parsed += item.childNodes[j].data;
                }
            };

            instance = item.getAttribute('instance');
            parsed = parsed.replace(/__VDINSTANCE__/g, instance);
            itemId = item.getAttribute('id') || 'TMPContainer'+Math.rand(1, 1000);
            htmlData[itemId] = parsed;
        };
        return htmlData;
    };

    /**
     * Парсер инкапсулированного XML
     *
     * @private
     * @param {Object} xml XML ответ
     * @param {Object} xhr Объект запроса
     * @return {Object}
     */
    this._parseXml = function(xml, xhr)
    {
        var item, items = xml.getElementsByTagName('xml');
        if (!items) { return {}; }

        var xmlData = {};
        var itemId, instance;
        var params = {'id':xhr.parameters._URID};

        for (var i=0, len=items.length; i<len; i++)
        {
            item = items[i];
            itemId = item.getAttribute('id');
            params.instance = item.getAttribute('instance') || 0;
            xmlData[itemId] = pf.xml.fragment2document(item, params);
        };
        return xmlData;
    };

    /**
     * Парсер подключенных скриптов
     *
     * @private
     * @param {Object} xml XML ответ
     * @param {Object} responseId Идентификатор ответа
     */
    this._parseScripts = function(xml, responseId)
    {
        var item, items = xml.selectNodes('/response/jscript | /response/execute');
        if (!items) { return false; }

        pf.srvapi.scriptDispatcher[responseId].stack = [];
        pf.srvapi.scriptDispatcher[responseId].len = 0;
        for (var i=0, len=items.length; i<len; i++)
        {
            item = items[i];
            switch(item.tagName)
            {
                case 'execute':
                {
                    var code    = item.text || item.textContent;
                    if (item.getAttribute('instant') == 'true')
                    {
                        _eval(code, responseId, html, xmlData);
                        break;
                    }

                    pf.srvapi.scriptDispatcher[responseId].stack.push(code);
                    break;
                }
                case 'jscript':
                {
                    var existing;
                    if (existing = _isExistingScript(item))
                    {
                        if (existing.getAttribute('loaded') != 'true' && existing.getAttribute('responseId') != responseId)
                        {
                            function _onScriptLoadExt(evt)
                            {
                                evt = evt || event;
                                var elem = evt.currentTarget || evt.srcElement;
                                if (evt.type == 'readystatechange' && elem.readyState && !(elem.readyState == 'complete' || elem.readyState == 'loaded')) { return } // 4IE
                                pf.srvapi.scriptDispatcher[responseId].len--;
                                _runExecutes(responseId);
                            }
                            pf.srvapi.scriptDispatcher[responseId].len++;
                            if (!pf.browsCap.isOpera) { pf.evt.add(existing, 'readystatechange', _onScriptLoadExt) }
                            pf.evt.add(existing, 'load',  _onScriptLoadExt);
                            pf.evt.add(existing, 'error', _onScriptLoadExt);
                        }
                        break;
                    }

                    var script = pf.srvapi._createScriptElement(item, responseId);

                    pf.srvapi.scriptLoading = true;
                    pf.srvapi.scriptDispatcher[responseId].len++;
                    var postfix = (window.Prj && window.Prj.version) ? '?'+window.Prj.version : '';
                    void(script.src = item.getAttribute('src') + postfix);
                    pf._scriptContainer.appendChild(script);

                    break;
                }
            }
        }

        _runExecutes(responseId);
    };

    /**
     * Обработчик скриптов (последовательный)
     *
     * @private
     * @param {Object} xml XML ответ
     * @param {Object} responseId идентификатор ответа
     */
    this._parseScriptsSequental = function(xml, responseId)
    {
        var item, items = xml.selectNodes('/response/jscript | /response/execute');
        if (!items) { return false; }

        var runStack = (pf.srvapi.scriptStack.length == 0); // нужно ли запускать стек
        for (var i=0, len=items.length; i<len; i++)
        {
            item = items[i];
            switch(item.tagName)
            {
                case 'execute':
                {
                    var code    = item.text || item.textContent;
                    if (item.getAttribute('instant') == 'true')
                    {
                        _eval(code, responseId);
                        break;
                    }

                    pf.srvapi.scriptStack.push(code);
                    break;
                }
                case 'jscript':
                {
//debugNotice('script объявлен '+item.getAttribute('src'));
                    if (_isExistingScript(item)) { break; }
                    var script = pf.srvapi._createScriptElement(item, responseId);

                    if (!_isStacked(script, responseId))
                    {
                        pf.srvapi.scriptStack.push(script);
//debugNotice('stacked '+script.getAttribute('wsrc'));
                    }
                    break;
                }
            }
        }

        if (runStack) { _fetchNextScript(responseId); }
    };

    /**
     * Созданеие скрипта для подгрузки файла
     *
     * @private
     * @param {Object} item свойства
     * @param {Object} responseId идентификатор ответа
     * @return {Element}
     */
    this._createScriptElement = function(item, responseId)
    {
        var scr = pf.$$$('script');
        scr.id   = item.getAttribute('id');
        scr.type = 'text/javascript';
        scr.responseId = responseId;
        scr.setAttribute('responseId', responseId);
        scr.setAttribute('loaded', false);
        var charset = item.getAttribute('charset');
        if (charset) { scr.charset = charset; }
        if (!pf.browsCap.isOpera) { pf.evt.add(scr, 'readystatechange', _onScriptLoad) }
        pf.evt.add(scr, 'load',  _onScriptLoad);
        pf.evt.add(scr, 'error', _onScriptLoad);
        var postfix = (window.Prj && window.Prj.version) ? '?'+window.Prj.version : '';
        scr.setAttribute('wsrc', item.getAttribute('src') + postfix);
        return scr;
    };

    /**
     * Обработчик события загрузки скрипта
     *
     * @private
     * @param {Object} evt событие
     */
    var _onScriptLoad = function(evt)
    {
        evt = evt || event;
        var elem = evt.currentTarget || evt.srcElement;
        if (evt.type == 'readystatechange' && elem.readyState && !(elem.readyState == 'complete' || elem.readyState == 'loaded')) { return } // 4IE

        var responseId = parseInt(elem.getAttribute('responseId'));
        elem.setAttribute('loaded', 'true');

        pf.srvapi.scriptLoading = false;
        if (pf.srvapi._jsSequental[responseId])
        {
            _fetchNextScript(responseId);
        } else {
            pf.srvapi.scriptDispatcher[responseId].len--;
            _runExecutes(responseId);
        }
    };

    /**
     * Выполнение куска кода
     *
     * @private
     * @param {Object} jsCode код
     * @param {Object} responseId идентификатор ответа
     */
    var _eval = function(jsCode, responseId)
    {
        var html =    pf.srvapi.scriptDispatcher[responseId].htmlData;
        var xmlData = pf.srvapi.scriptDispatcher[responseId].xmlData;
        try { eval(jsCode); } catch(err) {debugError('Inline script error '+err.name+ ' : '+err.message); }
    };

    /**
     * Проверка, существует ли скрипт в документе
     *
     * @private
     * @param {Object} scrElem элемент
     */
    var _isExistingScript = function(scrElem)
    {
        var scriptSrc = scrElem.getAttribute('src');
        var scriptId  = scrElem.getAttribute('id');

        var scriptObjects = pf._scriptContainer.getElementsByTagName('SCRIPT');
        for (var i=0, len=scriptObjects.length; i<len; i++)
        {
            var existing = scriptObjects[i];
            if (existing.src == scriptSrc || existing.id == scriptId)
            {
                return existing;
            }
        }
        return false;
    };

    /**
     * @private
     * @param {Object} scr
     * @param {Object} responseId
     */
    var _isStacked = function(scr, responseId)
    {
        var script;
        for(var i=0, len=pf.srvapi.scriptStack.length; i<len; i++)
        {
            script = pf.srvapi.scriptStack[i];
            if (!script || !script.tagName) { continue; }
            if (script.getAttribute('wsrc') == scr.getAttribute('wsrc') || script.id == scr.id) { return true; }
        }
        return false;
    };

    /**
     * Подгружает следующий скрипт в очереди
     *
     * @private
     * @param {Object} responseId идентификатор ответа
     */
    var _fetchNextScript = function(responseId)
    {
        if (pf.srvapi.scriptStack.length)
        {
            var script = pf.srvapi.scriptStack.shift();
            if (script.tagName)
            {
                pf.srvapi.scriptLoading = true;
                void(script.src = script.getAttribute('wsrc'));
                pf._scriptContainer.appendChild(script);
            } else {
                _eval(script, responseId);
                _fetchNextScript(responseId);
            }
        } else { // GC
            pf.srvapi.scriptDispatcher = {};
            pf.srvapi._jsSequental = {};
        }
    };

    /**
     * @private
     * @param {Object} responseId
     */
    var _runExecutes = function(responseId)
    {
        if (pf.srvapi.scriptDispatcher[responseId].len > 0) { return false; }

        for (var i=0, len=pf.srvapi.scriptDispatcher[responseId].stack.length; i<len; i++)
        {
            _eval(pf.srvapi.scriptDispatcher[responseId].stack[i], responseId);
        }

        delete pf.srvapi.scriptDispatcher[responseId];
        delete pf.srvapi._jsSequental[responseId];
    };
};

/*
 *$Id: dummy.js 101 2009-01-15 13:21:57Z aanpilogov $
 *
 * Функции, подлежащие перекрытию в коде проекта
 */

// Оповещение о текущем состоянии
function serverSetStatus(status) { /* loading|ready|error */ };

//Отправляет ошибку в дебаггер
if (!window.debugError)
debugError = function(errString)
{
    if (window.console) console.error(errString);
};

//Отправляет строку результата действия в дебаггер
if (!window.debugResult)
debugResult = function(errString)
{
    if (window.console) console.log(errString);
};

//Отправляет обычный текст в дебаггер
if (!window.debugNotice)
debugNotice = function(errString)
{
    if (window.console) console.info(errString);
};


/**
 *$Id: framework_init.js 110 2009-02-12 15:33:34Z aanpilogov $
 *
 * Внедрение фреймворка и расширение системных прототипов
 */

/*
* Расширение pf глобальными методами и глобализация, если в конфиге так написано
*/
(function(){
for (var p in PuskFramework.globals)
{
    if ((typeof PuskFramework.globals[p] == 'function') && p.charAt(0) == '$')
    {
        if (PuskFramework._cfg.globals) window[p] = PuskFramework.globals[p];
        PuskFramework[p] = PuskFramework.globals[p];
    }
}
})();
/**
 * Конструктор адаптеров
 *
 * @param {Object} func функция
 * @param {Object} source прототип
 * @return {Function}
 */
PuskFramework._protoFactory = function(func, source)
{
    return function()
    {
        var args = [];
        for (var i = 0; i<arguments.length; i++)
            args.push(arguments[i]);
        args.unshift(this);
        return source[func].apply(this, args);
    }
};

/**
 * Прототипизация встроенных в фреймворк функций
 *
 * @param {Object} destination прототип
 * @param {Object} source расширение
 */
PuskFramework.prototypize = function(destination, source)
{
    for(var func in source)
    {
        if (!destination.prototype[func])
        {
            destination.prototype[func] = PuskFramework._protoFactory(func, source);
        }
    }
};

if (PuskFramework._cfg.extendArray)  { PuskFramework.prototypize(Array,  PuskFramework.arr); }
if (PuskFramework._cfg.extendString) { PuskFramework.prototypize(String, PuskFramework.str); }
if (PuskFramework._cfg.extendDate)   { PuskFramework.prototypize(Date,   PuskFramework.date); }

if (PuskFramework._cfg.namespace) 
{
    window[PuskFramework._cfg.namespace] = PuskFramework; 
} 
/*if (PuskFramework._cfg.globals))
{
    PuskFramework.globals.$extend(window, PuskFramework);
}*/

/*
 * Support for the DOMContentLoaded event in IE.
 * Based on work by Dean Edwards.
 * Usage: pf.evt.add(document, 'dataavailable', myHandler)
 */
(function(){
if (/*@cc_on!@*/false)
{
    fireContentLoadedEvent = function()
    {
        var event = document.createEventObject();
        event.eventType = 'ondataavailable';
        event.eventName = 'dataavailable';
        document.fireEvent(event.eventType, event);    
    };
    
    if (window == window.top)
    {
        document.write("<script id=__pfOnDOMContentLoaded defer src=javascript:void(0)><\/script>");
        var scr = PuskFramework.$("__pfOnDOMContentLoaded");
        if (scr) scr.onreadystatechange = function()
        {
            if (this.readyState == 'complete')
            {
                this.onreadystatechange = null;
                fireContentLoadedEvent();
            }
        };
    } else {
        window.attachEvent('onload', fireContentLoadedEvent);
    }
}
})();


PuskFramework.init = function()
{
    var listener = null;
    while(listener = PuskFramework._initListeners.shift()) { listener(); }
}
PuskFramework.evt.add(document, 'dataavailable', PuskFramework.init);

PuskFramework._initListeners.push(function()
{
    PuskFramework._scriptContainer = PuskFramework.$$$('DIV', {'id':'puskScriptContainer'}, {'display':'none'});
    document.body.insertBefore(PuskFramework._scriptContainer, document.body.firstChild);
});

var VSPWR = {
    BC: {
        strLink: '<a href="#link#" onclick="changePage(this); return false;">#name#</a>',
        strSpan: '<span>#name#</span>'
    },
    menu: {
        getCurrent: function() {
            var menu = $('top_menu');
            var menuPunkts = menu.getElementsByTagName('li');
            for (var i=0, len=menuPunkts.length; i<len; i++) {
                if (pf.str.contains(menuPunkts[i].className, 'current', false)) return menuPunkts[i];
            }
            return null;
        },
        current: false
    },
    loadBlock: '<center><img src="/images/vspomni_design_new/loading/loadbar_small_trans.gif" /></center>',
    hostname: location.protocol + '//' + location.host,
    RegEXP: /<script[^>]*>[^>]*\/script>/gi,
    loading: function() {
        var loadingDiv = $("progressLoading");
        if (loadingDiv) {
            if (loadingDiv.style.display == "block") {
                loadingDiv.style.display = "none";
                if (navigator.userAgent.indexOf('MSIE 6') != -1 || navigator.userAgent.indexOf('MSIE 5') != -1) {
                    document.body.style.backgroundImage = "";
                    document.body.style.backgroundAttachment = "";
                }
            }
            else {
                loadingDiv.style.display = "block";
                if (navigator.userAgent.indexOf('MSIE 6') != -1 || navigator.userAgent.indexOf('MSIE 5') != -1) {
                    document.body.style.backgroundImage = loadingDiv ? "url(/;-)/n.gif)" : "";
                    document.body.style.backgroundAttachment = loadingDiv ? 'fixed' : "";
                }
            }
        }
    },
    findPos: function(obj) {
        var curleft = curtop = 0;
        if (obj.offsetParent) {
            curleft = obj.offsetLeft
            curtop = obj.offsetTop
            while (obj = obj.offsetParent) {
                curleft += obj.offsetLeft
                curtop += obj.offsetTop
            }
        }
        return [curleft,curtop];
    }
};

var WorkUrl = {
    lastHash: location.href.replace(/.*#/, ''),
    currentHash: function() {return (location.href.search('#') != -1)? location.href.replace(/.*#/, ''): '';},
    change: function(newUrl) {
        
        if (pf.browsCap.isIE) {
            newUrl = newUrl.replace(/\"/g, '&quot;');
            newUrl = newUrl.replace(/\'/g, '&#8216;');
        }
        WorkUrl.lastHash = newUrl;
        unFocus.History.addHistory(newUrl);
    },
    onChange: function(historyHash) {
        if (!historyHash) return;
//        historyHash = historyHash.replace(scriptRE, '');

        var befAnchNew = historyHash.replace(/\?.+/, '');
        var anchNew = historyHash.replace(/^[^\?]+/, '').replace('?', '');
        var befAnchOld = WorkUrl.lastHash.replace(/\?.+/, '');
        var anchOld = WorkUrl.lastHash.replace(/^[^\?]+/, '').replace('?', '');
        if ((anchNew.search(/\[/) != -1) && (befAnchNew == befAnchOld) && (anchNew != anchOld)) {
            WorkUrl.reloadBlockWithParams(true);
        } else if ((befAnchOld != '') && (befAnchNew != befAnchOld)) {
            changePage({'prev_url': WorkUrl.lastHash, 'next_url': historyHash});
        }
    },
    scrollToBlock: function(hash) {
        var scrollingToBlock = hash.replace(/.*\?/, '');
        scrollingToBlock = pf.str.toHash(scrollingToBlock, '&', '=');
        scrollingToBlock = $(scrollingToBlock.anchor);
        if (!scrollingToBlock) return;
        var innerBlocks = scrollingToBlock.getElementsByTagName('div');
        if (innerBlocks.length > 0)
            innerBlocks[0].scrollIntoView(true);
        else
            scrollingToBlock.scrollIntoView(true);        
    },
    reloadBlockWithParams: function(post) {
        var hash = WorkUrl.currentHash();
        var params = hash.replace(/^[^\?]+/, '');
        if (!params) return;
        var newParams = {};
        if (Loading.showGlobal) Loading.showGlobal();
        params = pf.str.toHash(params.replace('?', ''), '&', '=');
        for (var i in params) {
            if (i.search(/\[/) != -1) {
                var blockName = i.replace(/\[[^\]]+\]/, '');
                var param = i.replace(/[^\[]+\[/, '').replace(']', '');
                if (blockName && !newParams[blockName]) newParams[blockName] = {};
                newParams[blockName][param] = params[i];
            }
        } 
        if (post) {
            for (var key in newParams) {
                paramsGet = pf.hash.serialize(newParams[key]);
                pf.srvapi.pinvoke(hash.replace(/\?.+/, ''), {'ajax': 1, 'block_name': key, 'params': paramsGet}, parseResponse, parseResponse);
            }
        } else {
            for (var key in newParams) {
                newParams[key].block_name = key;
                pf.srvapi.invoke(hash.replace(/\?.+/, '') + '?' + pf.hash.serialize(newParams[key]));
            }
        }
    }
}
if (window.unFocus)
unFocus.History.addEventListener('historyChange', WorkUrl.onChange);
var Prj = {name: location.host, version: 2};

var WorkForm = {
    getInputs: function(form, type) {
        var elems = [];
        if (typeof(form) == 'string') form = $(form);
        for (var i=0; i<form.elements.length; i++) {
            if (form.elements[i].type == type)
                elems.push(form.elements[i]);
        }
        return elems;
    },
    busy: false,
    callback: false,
    line: [],
    sendForm: function(form, action, block_name, callback, method) {
        if (!WorkForm.busy) {
            WorkForm.busy = true;
            if (typeof(form) == 'string') form = $(form);
            if (block_name) {
                var _hid = document.createElement('input');
                _hid.type = 'hidden';
                _hid.name = 'block_name';
                _hid.value = block_name;
                form.appendChild(_hid);
            }
            form.action = action;
            form.method = method || 'post';
            form.target = 'sendFormIframe';
            if (callback) WorkForm.callback = callback;
            form.submit();
        } else {
            line.push({form: form, action: action, callback: callback, method: method});
        }
    }
}

var Loading = {
    idGl: 'global_loading',
    showGlobal: function() {
        var el = $('progressLoading');
        if (el && el.style.display == 'none')
            VSPWR.loading();
    },
    show: function(divId) {
        var divA = $(divId);
        var divB = $('loading_' + divId);
        if (divA) divA.style.display = 'none';
        if (divB) divB.style.display = 'block';
    },
    hideGlobal: function() {
        var el = $('progressLoading');
        if (el && el.style.display == 'block')
            VSPWR.loading();
    },
    hide: function(divId) {
        var divA = $(divId);
        var divB = $('loading_' + divId);
        if (divA) divA.style.display = 'block';
        if (divB) divB.style.display = 'none';
    }
}

function changeMenu(newMenuId) {
    var newMenu = $(newMenuId);
    newMenu = newMenu? newMenu: null;
    if (VSPWR.menu.current == false)
        VSPWR.menu.current = VSPWR.menu.getCurrent();
    if (newMenu && !pf.str.contains(newMenu.className, ' current', false))
        newMenu.className += ' current';
    if (VSPWR.menu.current != null)
        VSPWR.menu.current.className = VSPWR.menu.current.className.replace(' current', '');
    VSPWR.menu.current = newMenu;
}

function ajaxBreadcrumbs(arr) {
    var bcDiv = $('breadcrumbs');
    if (!bcDiv) return;
    var str = '';
    if (arr) {
        for (var i = 0, len = arr.length; i < len; i++) {
            if (arr[i][1]) {
                if (arr[i][1].search('/video/') != -1)//TODO убрать
                    str += VSPWR.BC.strLink.replace('#link#', arr[i][1]).replace('#name#', arr[i][0]).replace('onclick="changePage(this); return false;"','');
                else
                    str += VSPWR.BC.strLink.replace('#link#', arr[i][1]).replace('#name#', arr[i][0]);
            } else {
                str += VSPWR.BC.strSpan.replace('#name#', arr[i][0]);
            }
        };
    }
    bcDiv.innerHTML = str;
    bcDiv.style.display = (str == '')? 'none': 'block';
}

//function insertHTML(insertToId, insertingFrom) {
//    var block = $(insertToId);
//    if (!block) return;
//    block.style.display = (insertingFrom == '')? 'none': 'block';
//    block.innerHTML = insertingFrom;
//};

function insertHTML(insertToId, insertingFrom) {
//    var block = $(insertToId);
//    if (!block) return;
//    block.style.display = (insertingFrom == '')? 'none': 'block';
//    block.innerHTML = insertingFrom;
    
    var block = $(insertToId);
    if (!block) return;
    insertingFrom = insertingFrom.replace(/^\s+/, '').replace(/\s+$/, ''); 
    block.style.display = (insertingFrom == '')? 'none': 'block';
    block.innerHTML = (pf.browsCap.isIE)? '<span id="nbspfm">&nbsp;</span>' + insertingFrom: insertingFrom;
    var scripts = block.getElementsByTagName('script');
    var len = scripts.length;
    if (pf.browsCap.isIE) {
        var nbsp = $('nbspfm'); 
        if (nbsp) nbsp.parentNode.removeChild(nbsp);
    };
    if (len < 1) return;
    for (var i=0; i < len; i++) {
        if (scripts[i].src) {
            var newScr = document.createElement('script');
            newScr.src = scripts[i].src + '?r=2';
            block.appendChild(newScr);
        } else {
            var newScr = document.createElement('script');
            newScr.text = scripts[i].text;        
            block.appendChild(newScr);                        
        }
    }
};

function insertAllHTML(insertingHTML) {
    for (var key in insertingHTML) {
        var el = $(key);
        if (!el) continue;
        insertingHTML[key] = insertingHTML[key].replace(/^\s+/, '').replace(/\s+$/, ''); 
        el.style.display = (insertingHTML[key] == '')? 'none': 'block';
        el.innerHTML = (pf.browsCap.isIE)? '<span id="nbspfm">&nbsp;</span>' + insertingHTML[key]: insertingHTML[key];
        var scripts = el.getElementsByTagName('script');
        var len = scripts.length;
        if (pf.browsCap.isIE) {
            var nbsp = $('nbspfm'); 
            if (nbsp) nbsp.parentNode.removeChild(nbsp);
        };
        if (len < 1) continue;
        for (var i=0; i < len; i++) {
            if (scripts[i].src) {
                var newScr = document.createElement('script');
                newScr.src = scripts[i].src + '?r=2';
                el.appendChild(newScr);
            } else {
                var newScr = document.createElement('script');
                newScr.text = scripts[i].text;        
                el.appendChild(newScr);                        
            }
        }
    }
    WorkUrl.scrollToBlock(WorkUrl.currentHash());
    if (Loading.hideGlobal) Loading.hideGlobal();
}

function sendAjax(params, url, after, onerr, sync) {
    if (params.href) {
        var str = params.href.replace(VSPWR.hostname, '');
        params = {'prev_url': WorkUrl.currentHash(), 'next_url': str};
    }
    if (url && !pf.str.contains(url, 'r=')) {
        url += (pf.str.contains(url, '?'))? '&reload=1&r=': '?reload=1&r=';
        url += Math.random();
    } else if (!url) {
        url = '?reload=1';
    }
    pf.srvapi.pinvoke(url, params, after, onerr, sync);
    if (window._rsSectionEvent) _rsSectionEvent(VSPWR.hostname + url);
}

function changePage(params, noloading) {
    if (!noloading) Loading.showGlobal();
    if (params.href) {
        var str = params.href.replace(VSPWR.hostname, '');
        var ch = WorkUrl.currentHash();
        if (!ch) {
            ch = location.pathname;
            unFocus.History.addHistory(ch);
            WorkUrl.lastHash = ch;
        }
        params = {'prev_url': ch, 'next_url': str};
    }
    var urlParams = params.next_url.replace(/^[^?]*/, '').replace('?', '&');
    var url = '/?reload=1' + urlParams;
    if (pf.browsCap.isIE && event) event.returnValue = false;
    pf.srvapi.pinvoke(url, params);
    if (window._rsPageEvent) _rsPageEvent(VSPWR.hostname + url);
    window.scrollTo(0, 0);
    refreshCounterIframe();
    updateLiveInternetCounter();
    // прячем календарь если он открыт
    if(($('calenderTable')) && ($('calenderTable').style.display == 'block'))
        closeCalender();
}

function changePage2(params, noloading) {
    if (!noloading) Loading.showGlobal();
    if (params.href) {
        var reg1 = new RegExp(VSPWR.hostname);
        var reg2 = new RegExp("" + VSPWR.hostname + "([^a-z]\/?[a-z0-9\-\._\/~\?&=%]+)#([a-z0-9\-\._\/~\?&=%]+)", "i");
        var str = (reg1.test(params.href))?(params.href.replace(reg2, "" + VSPWR.hostname + "$2")):(params.href);
        str = str.replace(VSPWR.hostname, '');
        params = {'prev_url': WorkUrl.currentHash(), 'next_url': str};
    }
    var url = '/?reload=1';
    if (pf.browsCap.isIE) event.returnValue = false;
    pf.srvapi.pinvoke(url, params);
    if (window._rsPageEvent) _rsPageEvent(VSPWR.hostname + url);
    updateLiveInternetCounter();
    // прячем календарь если он открыт
    if(($('calenderTable')) && ($('calenderTable').style.display == 'block'))
        closeCalender();
    
};

function refreshCounterIframe() {
    var ci = $('countersIframe');
    if (ci) ci.src = '/counters.html?r=2';
};

function updateLiveInternetCounter()
{
    var liCounter = new Image(1,1);
    liCounter.src = 'http://counter.yadro.ru/hit;start-qip-ru?r='+
    ((typeof(screen)=='undefined')?'':';s'+screen.width+
    '*'+screen.height+'*'+(screen.colorDepth?screen.colorDepth:
    screen.pixelDepth))+';u'+escape(document.URL)+
    ';h'+escape(document.title.substring(0,80))+';'+Math.random();
};

function reloadBlock(blockName, divId, prevUrl, nextUrl) {
    var div = $(divId);
     div.innerHTML = VSPWR.loadBlock;
    prevUrl = prevUrl || WorkUrl.currentHash();
     nextUrl = nextUrl || WorkUrl.currentHash();
    params = {'prev_url': prevUrl, 'next_url': nextUrl, 'reload_block': blockName, 'div_id': divId};
    var url = '/?reload=1';
     if (pf.browsCap.isIE && event) event.returnValue = false;
     pf.srvapi.pinvoke(url, params);
}


//Не отменяется аякс по esc
function preventScriptAbort(eventobject) 
{
    var evt;
    var key_code = 0;
    if (window.event) {
        evt = window.event;
    } else if (eventobject) {
        evt = eventobject;
    }
// выделяем нажатую клавишу 
    if (evt.keyCode){
        key_code = evt.keyCode;
    } else if (evt.charCode) {
        key_code = evt.charCode;
    } else if (evt.which) {
        key_code = evt.which;
    }
// пытаемся предотвратить выполнение действия по умолчанию
    if (evt && evt.preventDefault && key_code == 27) {
        evt.preventDefault(); 
    }
}
if (document.addEventListener) {
    document.addEventListener('keydown', preventScriptAbort, false);
} else if (document.attachEvent) {
    document.attachEvent('onkeydown', preventScriptAbort);
}

