/**
 *$Id: ajax.js 102 2009-01-15 13:25:32Z aanpilogov $
 *
 * Библиотека для выполнения 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.flashProxy == 'off' || !pf.flash || !pf.flash.httpRequest || !pf.flash.httpRequest.isOk) { 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();

