/**
* 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!
* Не удаляйте данный комментарий, если вы хотите использовать скрипт!
*
* 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.26
*/

/*
Small changes were made by InfoDesign-M
*/

function Subsys_JsHttpRequest_Js() { this._construct() }
(function() { // to create local-scope variables
var COUNT       = 0;
var PENDING     = {};
var CACHE       = {};

// Called by server script on data load.
Subsys_JsHttpRequest_Js.dataReady = function(id, text, js) {
	var undef;
	var th = PENDING[id];
	delete PENDING[id];
	if (th) {
		delete th._xmlReq;
		if (th.caching) CACHE[th.hash] = [text, js];
		th._dataReady(text, js);
	} else if (typeof(th) != typeof(undef)) {
		alert("ScriptLoader: unknown pending id: "+id);
	}
}

Subsys_JsHttpRequest_Js.prototype = {
	// Standard properties.
	onreadystatechange: null,
	readyState:         0,
	responseText:       null,
	responseXML:        null,
	status:             200,
	statusText:         "OK",

	// Additional properties.
	session_name:       "SID",  // set to SID cookie or GET parameter name
	responseJS:         null,   // JavaScript response array/hash
	caching:            false,  // need to use caching?

	// Internals.
	_span:              null,
	_id:                null,
	_xmlReq:            null,
	_openArg:           null,
	_reqHeaders:        null,

	abort: function() {
		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, username, password) {
		this._openArg = {
			'method':    method,
			'url':       url,
			'asyncFlag': asyncFlag,
			'username':  username,
			'password':  password
		};
		this._id = null;
		this._xmlReq = null;
		this._reqHeaders = [];
		return true;
	},

	send: function(content) {
		var id = (new Date().getTime()) + "" + COUNT++;

		// Build QUERY_STRING from query hash.
		var query = this._hash2query(content);

		// Append SID to original URL now.
		var url = this._openArg.url;
		var sid = this._getSid();
		if (sid) url += (url.indexOf('?')>=0? '&' : '?') + this.session_name + "=" + this.escape(sid);

		// Solve hash BEFORE appending ID.
		var hash = this.hash = url + '?' + query;
		if (this.caching && CACHE[hash]) {
			var c = CACHE[hash];
			this._dataReady(c[0], c[1]);
			return false;
		}

		// Try to use XMLHttpRequest.
		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 href, body;
		if (this._xmlReq && hasSetHeader && (""+this._openArg.method).toUpperCase() == "POST") {
			// Use POST method. Pass query in request body.
			// Opera 8.01 does not support setRequestHeader, so no POST method.
			this._openArg.method = "POST";
			href = url;
			body = query;
		} else {
			this._openArg.method = "GET";
			href = url + (url.indexOf('?')>=0? '&' : '?') + query;
			body = null;
		}

		// Append ID: a=aaa&b=bbb&<id>
		href = href + (href.indexOf('?')>=0? '&' : '?') + id;

		// Save loading script.
		PENDING[id] = this;

		if (this._xmlReq) {
			// Open request now & send it.
			// In XMLHttpRequest mode request URL MUST be ended with "<id>-xml".
			var a = this._openArg;
			this._xmlReq.open(a.method, href+"-xml", a.asyncFlag, a.username, a.password);
			if (hasSetHeader) {
				// Pass pending headers.
				for (var i=0; i<this._reqHeaders.length; i++)
				this._xmlReq.setRequestHeader(this._reqHeaders[i][0], this._reqHeaders[i][1]);
				// 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');
			}
			// Send the request.
			return this._xmlReq.send(body);
		} 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];
	},


	//
	// Internal functions.
	//

	// Constructor.
	_construct: function() {},

	// Do all work when data is ready.
	_dataReady: function(text, js) { with (this) {
		if (text !== null || js !== null) {
			readyState = 4;
			responseText = responseXML = text;
			responseJS = js;
		} else {
			readyState = 0;
			responseText = responseXML = responseJS = null;
		}
		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) {
					// Remove possible junk from response.
					var responseText = req.responseText;
					try {
						// Call associated dataReady().
						eval(responseText);
					} catch (e) {
						Subsys_JsHttpRequest_Js.dataReady(id, "JavaScript code generated by backend is invalid!<br />\n"+responseText, null);
					}
				} 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;
	}},

	// Remove last used script 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);
			}, 50);
		}
		return false;
	},

	// Convert hash to QUERY_STRING.
	_hash2query: function(content, prefix) {
		if (prefix == null) prefix = "";
		var query = [];
		if (content instanceof Object) {
			for (var k in content) {
				var v = content[k];
				if (v.constructor.prototype[k]) continue;
				var curPrefix = prefix? prefix+'['+this.escape(k)+']' : this.escape(k);
				if (v instanceof Object)
				query[query.length] = this._hash2query(v, curPrefix);
				else
				query[query.length] = curPrefix + "=" + this.escape(v);
			}
		} else {
			query = [content];
		}
		return query.join('&');
	},

	// Return value of SID based on QUERY_STRING or cookie
	// (PHP compatible sessions).
	_getSid: function() {
		var m = document.location.search.match(new RegExp('[&?]'+this.session_name+'=([^&?]*)'));
		var sid = null;
		if (m) {
			sid = m[1];
		} else {
			var m = document.cookie.match(new RegExp(s='(;|^)\\s*'+this.session_name+'=([^;]*)'));
			if (m) sid = m[2];
		}
		return sid;
	},

	// Stupid JS escape() does not quote '+'.
	escape: function(s) {
		return escape(s).replace(new RegExp('\\+','g'), '%2B');
	}
}
})();