/**
 * Subsys_JsHttpRequest_Js: JavaScript DHTML data loader.
 * (C) 2005 Dmitry Koterov, http://forum.dklab.ru/users/DmitryKoterov/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * See http://www.gnu.org/copyleft/lesser.html
 *
 * Do not remove this comment if you want to use script!
 * Ne udalyajte dannyj kommentarij, esli vy hotite ispol'zovat' skript!
 *
 * This library tries to use XMLHttpRequest (if available), and on
 * failure - use dynamically created <script> elements. Backend code
 * is the same for both cases.
 *
 * @author Dmitry Koterov
 * @version 3.20
 */

function Subsys_JsHttpRequest_Js() { this._construct() }
(function() { // to create local-scope variables
    var COUNT       = 0;
    var PENDING     = {};
    var CACHE       = {};

    Subsys_JsHttpRequest_Js.abort_all = function(){
        for(var k in PENDING)
            PENDING[k].abort();
    };

    // Called by server script on data load.
    Subsys_JsHttpRequest_Js.dataReady = function(id, text, js) {
        var th = PENDING[id];
        delete PENDING[id];
        if (th) {
            delete th._xmlReq;
            if (th.caching && !th.aborted) CACHE[th.hash] = [id, text, js];
            if(!th.aborted)
                th._dataReady(id, text, js);
        }
    };

    Subsys_JsHttpRequest_Js.prototype = {
        // Standard properties.
        onreadystatechange: null,
        readyState:         0,
        responseText:       null,
        responseXML:        null,
        status:             200,
        statusText:         "OK",
        badResponse:        false,
        last_query:         null,
        aborted:            false,

        // Additional properties.
        session_name:       "PHPSESSID",  // set to SID cookie or GET parameter name
        responseJS:         null,         // JavaScript response array/hash
        responseID:         null,         // response id
        caching:            false,        // need to use caching?
		responseCache:		false,		  // from cache

        // Internals.
        _span:              null,
        _id:                null,
        _xmlReq:            null,
        _openArg:           null,
        _reqHeaders:        null,
        _timeout:           null,
        _timeout_handler:   null,
        _timeout_id:        null,

		
		is_bad_response: function(){
			return this.badResponse;	
		},

        abort: function() {
            this.aborted = true;
            if (this._xmlReq) return this._xmlReq.abort();
            if (this._span) {
                this.readyState = 0;
                if (this.onreadystatechange) this.onreadystatechange();
                this._cleanupScript();
            }
        },

        open: function(method, url, asyncFlag, timeout, username, password) {
            this._openArg = {
                'method':    method,
                'url':       url,
                'asyncFlag': asyncFlag,
                'username':  username,
                'password':  password
            };
            this._id = null;
            this._xmlReq = null;
            this._reqHeaders = [];
            if(timeout){
                this._timeout = timeout.delay;
                this._timeout_handler = timeout.handler;
            }
            return true;
        },

        send: function(content) {
            var id = COUNT++;

            // Build QUERY_STRING from query hash.
            var query = this._hash2query(content);
            this.last_query = query;

            // Append SID to original URL now.
            var url = this._openArg.url;

            this.aborted = false;

            // Solve hash BEFORE appending ID.
            var hash = this.hash = url + '?' + query;
            if (this.caching && CACHE[hash]) {
                var c = CACHE[hash];
                this._id = c[0];
                this._dataReady(c[0], c[1], c[2], true);
                return false;
            }

            // Try to use XMLHttpRequest.
            if(("" + this._openArg.method).toUpperCase() != "SCRIPT" && ("" + this._openArg.method).toUpperCase() != "IFRAME")
                this._xmlReq = this._obtainXmlReq(id, url);

            // Pass data in URL (GET, HEAD etc.) or in request body (POST)?
            var hasSetHeader = this._xmlReq && (window.ActiveXObject || this._xmlReq.setRequestHeader);
            var body = null;
            var href = url + (url.indexOf('?') >= 0 ? '&' : '?') + query;

            // ispol'zuem metod POST, esli on byl yavno ukazan v zaprose
            // libo esli dlina zaprosa prevyshaet 2K
            if (hasSetHeader && (("" + this._openArg.method).toUpperCase() == "POST" || href.length > 1900)) {
                this._openArg.method = "POST";
                href = url;
                body = query;
            } else if (href.length > 1900) {
                dhtml_alert('Error', "You cannot send this request - it is too long."
                    + "<br><br><b>method:</b> " + this._openArg.method
                    + "<br><br><b>url:</b> " + url, 'error');
            }

            // Append ID: a=aaa&b=bbb&<id>
//          href = href + (href.indexOf('?')>=0? '&' : '?') + id;
            href = href + (href.indexOf('?') >= 0 ? '&' : '?') + 'js_id=' + id;

            if(this._timeout){
                var _req = this;
                this._timeout_id = setTimeout(
                    function() {
                       _req.abort();
                       if(_req._timeout_handler)
                           _req._timeout_handler();
                    },
                    this._timeout
                );
            }

            // Save loading script.
            PENDING[id] = this;
 
            if (this._xmlReq) {
                // Open request now & send it.
                // In XMLHttpRequest mode request URL MUST be ended with "&".
                var a = this._openArg;
                this._xmlReq.open(a.method, href+"&", a.asyncFlag, a.username, a.password);
                if (hasSetHeader) {
                    // Set non-default Content-type. We cannot use
                    // "application/x-www-form-urlencoded" here, because
                    // in PHP variable HTTP_RAW_POST_DATA is accessible only when
                    // enctype is not default (e.g., "application/octet-stream"
                    // is a good start). We parse POST data manually in backend
                    // library code.
                    this._xmlReq.setRequestHeader('Content-Type', 'application/octet-stream');
                    // Pass pending headers.
                    for (var i=0; i<this._reqHeaders.length; i++)
                        this._xmlReq.setRequestHeader(this._reqHeaders[i][0], this._reqHeaders[i][1]);
                }
                // Send the request.
                return this._xmlReq.send(body);
            } else if (("" + this._openArg.method).toUpperCase() == "IFRAME") {
                // Create <iframe> element and run it.				
                this._obtainIframe(id, href);
                return true;
            } else {
                // Create <script> element and run it.
                this._obtainScript(id, href);
                return true;
            }
        },

        getAllResponseHeaders: function() {
            if (this._xmlReq) return this._xmlReq.getAllResponseHeaders();
            return '';
        },

        getResponseHeader: function(label) {
            if (this._xmlReq) return this._xmlReq.getResponseHeader(label);
            return '';
        },

        setRequestHeader: function(label, value) {
            // Collect headers.
            this._reqHeaders[this._reqHeaders.length] = [label, value];
        },

        get_id: function() {
            return this._id;
        },

        //
        // Internal functions.
        //

        // Constructor.
        _construct: function() {},

        // Do all work when data is ready.
        _dataReady: function(id, text, js, cache) { with (this) {
            if (text !== null || js !== null) {
                readyState = 4;
                responseText = responseXML = text;
                badResponse = text == null;
                responseJS = js;
                responseID = id;
				responseCache = cache || false;
            } else {
                readyState = responseID = 0;
                responseText = responseXML = responseJS = null;
				responseCache = false;
            }
            if (onreadystatechange) onreadystatechange();
            _cleanupScript();
        }},

        // Create new XMLHttpRequest object.
        _obtainXmlReq: function(id, url) {
            // If url.domain specified, cannot use XMLHttpRequest!
            //if (url.match(new RegExp('^[a-z]+://', 'i'))) return null;

            // Try to use built-in loaders.
            var req = null;
            if (window.XMLHttpRequest) {
                try { req = new XMLHttpRequest() } catch(e) {}
            } else if (window.ActiveXObject) {
                try { req = new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}
                if (!req) try { req = new ActiveXObject("Msxml2.XMLHTTP") } catch (e) {}
            }
            if (req) {
                var th = this;
                req.onreadystatechange = function() {
                    if (req.readyState == 4) {
                        // obrabatyvaem plohie responsy - 502, 504, ...
                        //alert(req.responseText);
                        if(req.responseText.indexOf("Subsys_JsHttpRequest_Js.dataReady") == 0){
                            if(DEBUG_OUTPUT){
                                DEBUG_OUTPUT = false;
                                alert(req.responseText);
                            }
                            eval(req.responseText);
                        }else{
                            Subsys_JsHttpRequest_Js.dataReady(id, null, req.responseText);
                        }
                    } else {
                        th.readyState = req.readyState;
                        if (th.onreadystatechange) th.onreadystatechange()
                    }
                };
                this._id = id;
            }
            return req;
        },

        // Create new script element and start loading.
        _obtainScript: function(id, href) { with (document) {
            var span = null;
            // Oh shit! Damned stupid fucked Opera 7.23 does not allow to create SCRIPT
            // element over createElement (in HEAD or BODY section or in nested SPAN -
            // no matter): it is created deadly, and does not respons on href assignment.
            // So - always create SPAN.
            span = body.appendChild(createElement("SPAN"));
            span.style.display = 'none';
            span.innerHTML = 'Text for stupid IE.<s'+'cript></' + 'script>';
            setTimeout(function() {
                var s = span.getElementsByTagName("script")[0];
                s.language = "JavaScript";
                if (s.setAttribute) s.setAttribute('src', href); else s.src = href;
            }, 10);
            this._id = id;
            this._span = span;
        }},

        // Create new iframe element and start loading.
        _obtainIframe: function(id, href) { with (document) {
            document.domain = document.domain;
            var iframe = body.appendChild(createElement("IFRAME"));
            iframe.style.display = 'none';
            if (iframe.setAttribute) {
                iframe.setAttribute('src', href + '&js_method=iframe')
            } else {
                s.src = href;
            }
            this._id = id;
            this._span = iframe;
        }},

        // Remove last used script or iframe element (clean memory).
        _cleanupScript: function() {
            var span = this._span;
            if (span) {
                this._span = null;
                setTimeout(function() {
                    // without setTimeout - crash in IE 5.0!
                    span.parentNode.removeChild(span);
                }, 5000);
            }
            return false;
        },

        // Convert hash to QUERY_STRING.
        _hash2query: function(content) {
            var query = [];
            if (content instanceof Object) {
                // TODO: build more nested objects in PHP-style.
                for (var k in content) {
                    query[query.length] = escape(k) + "=" + escape(content[k]);
                }
            } else {
                query = [content];
            }
            return query.join('&');
        }
    }
})();

var DEBUG_OUTPUT = false;


if (window.register_load)
	register_load('JsHttpRequest');
