// 0.9.0.0

function Ajax() {
	var reqs = [];
	var dublicate = this;
	var adr, method, params, callBack, async;
	
	this.set = function(str_adr, str_method, mix_params, fn_callBack, bl_async) {
		adr = str_adr;
		method = str_method;
		params = dublicate.convertParam(mix_params, "obj");
		callBack = fn_callBack;
		async = bl_async;
		return dublicate;
	}
	
	this.get = function(what) {
		switch((what||"").toLowerCase()) {
			case "adr": return adr; break;
			case "method": return method; break;
			case "params": return params; break;
			case "callback": return callBack; break;
			case "async": return async; break;
		}
	}
	
	this.copy = function() {
		var ret = new Ajax();
		ret.set( dublicate.get("adr"), dublicate.get("method"), dublicate.get("params"), dublicate.get("callBack"), dublicate.get("async") );
		return ret;
	}
	
	this.send = function(obj_specify) {
		if (!obj_specify) obj_specify = {};
		var adr = obj_specify.adr || dublicate.get("adr");
		var method = obj_specify.method || dublicate.get("method");
		var params = obj_specify.params || dublicate.get("params");
		var callBack = obj_specify.callBack || dublicate.get("callBack");
		var async = !!(obj_specify.async || dublicate.get("async"));
		var strParams = dublicate.convertParam(params, "str");
		var retDate = {
			adr: adr,
			method: method,
			params: params,
			async: async,
			strParams: strParams
		}
		var req = typeof(ActiveXObject)=="undefined" ? new XMLHttpRequest() :  new ActiveXObject("Microsoft.XMLHTTP");
		reqs.push(req);
		req.onreadystatechange = function() {
			if (!callBack) return;
			var ar = ["uninitialized", "loading", "loaded", "interactive", "completed"];
			if (req.readyState<4) {
				check_callBack(ar[req.readyState], req.readyState, true);
			} else {
				switch(req.status) {
					case 200:
						if (!check_callBack("ok", 200, true)) {
							do_callBack(callBack);
						}
					break;
					default:
						if (req.status>=400 && req.status<500 && check_callBack("clientError", req.status, true)) {
						} else if (req.status>=500 && check_callBack("serverError", req.status, true)) {
						} else if (check_callBack("error", req.status, true)) {
						}
					break;
				}
			}
			function check_callBack(str, num, doCallBack) {
				if (callBack.constructor==Object) {
					if (callBack[num+""]) {
						if (doCallBack) do_callBack(callBack[num+""]);
						return true;
					} else if (callBack[str]) {
						if (doCallBack) do_callBack(callBack[str]);
						return true;
					}
				} else if (callBack.constructor==Array) {
					if (callBack[num]) {
						if (doCallBack) do_callBack(callBack[num]);
						return true;
					}
				}
				return false;
			}
			function do_callBack(mix) {
				switch(typeof(mix)) {
					case "function": 
						mix(req, retDate);
					break;
					case "object":
						var arg = mix.arg || [];
						if (mix.addArg) arg.unshift(req, retDate);
						mix.fn.apply(mix.context || null, arg);
					break;
				}
			}
		}
		if ((method || "get").toLowerCase()=="get") {
			req.open(method, adr +"?"+ strParams, async);
			req.send(null);
		} else {
			req.open(method, adr, async);
			req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      req.setRequestHeader("Content-length", strParams.length);
      req.setRequestHeader("Connection", "close");
      req.send(strParams);
		}
		return dublicate;
	}
	
	this.parseForm = function(mix_form, retOnlyParams) {
		var form;
		var pairs = {};
		if (!mix_form && mix_form!=0) return;
		if (typeof(mix_form)=="string") {
			var forms = document.getElementsByTagName("form");
			var i=-1;
			while(!form && forms[++i]) if (forms[i].name==mix_form) form = forms[i];
			if (!form) {
				form = document.getElementById(mix_form);
			}
		} else if (typeof(mix_form)=="number") {
			form = document.getElementsByTagName("form")[mix_form];
		} else if (mix_form.nodeType==1) {
			form = mix_form;
		}
		if (!form) return;
		var els = form.getElementsByTagName("*");
		for (var i=0, l=els.length; i<l; i++) {
			if (els[i].disabled) continue;
			switch( els[i].tagName.toLowerCase() ) {
				case "input":
					switch( (els[i].getAttribute("type") || "").toLowerCase() ) {
						case "radio": case "checkbox":
							if (els[i].checked) addPair(els[i].name, not0(els[i].value, "on"));
						break;
						default:
							addPair(els[i].name, els[i].value);
						break;
					}
				break;
				case "select":
					var opt = els[i].getElementsByTagName("option");
					var name = els[i].getAttribute("name");
					for (var i2=0, l2=opt.length; i2<l2; i2++) {
						if (opt[i2].selected && !opt[i2].disabled) addPair(name, not0(opt[i2].value, opt[i2].textContent, opt[i2].innerHTML));
					}
				break;
			}
		}
		return retOnlyParams ? pairs : { form: form, params: pairs };
		function addPair(name, value) {
			if ( typeof(pairs[name])!="undefined" ) {
				if (pairs[name].constructor!=Array)  pairs[name] = [ pairs[name] ];
				pairs[name].push(value);
			} else {
				pairs[name] = value;
			}
		}
		
		function not0() {
			for (var i=0, l=arguments.length; i<l; i++) {
				if (typeof(arguments[i])!="undefined") return arguments[i];
			}
		}
	}
	
	this.submitForm = function(mix_form, fn_callBack, obj_specify) {
		var parseForm = dublicate.parseForm(mix_form);
		if (!parseForm) return;
		var form = parseForm.form;
		if (!obj_specify) obj_specify = {};
		dublicate.set(obj_specify.action || form.action, obj_specify.method || form.method, parseForm.params, fn_callBack);
		dublicate.send( obj_specify );
	}
	
	this.convertParam = function(mix, to) {
		var ret;
		if (to=="obj") {
			ret = typeof(mix)=="string" ? toObj(mix) : typeof(mix)=="object" ? mix : emptyObj();
		} else if (to=="str") {
			ret = typeof(mix)=="object" ? toStr(mix) : typeof(mix)=="string" ? str : "";
		} else if (!to) {
			if (typeof(mix)=="object") {
				ret = toStr(mix);
			} else if (typeof(mix)=="string") {
				ret = toObj(mix);
			}
		}
		return ret;
		function toStr(obj) {
			if (obj.constructor!=Object) return "";
			var ret = "";
			for (var key in obj) {
				if (obj[key].constructor!=Array) {
					ret += "&"+ key +"="+ dublicate.escapeWin(obj[key]);
				} else {
					for (var i=0, l=obj[key].length; i<l; i++) {
						ret += "&"+ key +"="+ dublicate.escapeWin(obj[key][i]);
					}
				}
			}
			return ret.substring(1);
		}
		function toObj(str) {
			if (typeof(str)!="string") return emptyObj();
			if (str.indexOf("=")==-1) return emptyObj();
			var ret = {};
			var ar = str.split("&");
			for (var i=0, l=ar.length, item, name, value; i<l; i++) {
				item = ar[i].split("=");
				name = item[0];
				value = item[1];
				// ret[item[0]] = item[1];
				if ( typeof(ret[name])!="undefined" ) {
					if (ret[name].constructor!=Array)  ret[name] = [ ret[name] ];
					ret[name].push(value);
				} else {
					ret[name] = value;
				}
			}
			return ret;
		}
		function emptyObj() {
			return {};
		}
	}
	
	this.escapeWin = function(str) {
		// http://xpoint.ru/
		return window.escape(dublicate.unicodeToWin(str));
	}
	this.unicodeToWin = function(str) {
		// http://xpoint.ru/
		// Инициализируем таблицу перевода
		var trans = [];
		for (var i = 0x410; i <= 0x44F; i++) trans[i] = i - 0x350; // А-Яа-я
		trans[0x401] = 0xA8; // Ё
		trans[0x451] = 0xB8; // ё
		var ret = [];
		// Составляем массив кодов символов, попутно переводим кириллицу
		for (var i = 0; i < str.length; i++) {
			var n = str.charCodeAt(i);
			if (typeof trans[n] != 'undefined') n = trans[n];
			if (n <= 0xFF) ret.push(n);
		}
		return String.fromCharCode.apply(null, ret);
	}
	this.winToUnicode = function(str) {
		// http://xpoint.ru/
		var charmap = unescape(
			"%u0402%u0403%u201A%u0453%u201E%u2026%u2020%u2021%u20AC%u2030%u0409%u2039%u040A%u040C%u040B%u040F"+
			"%u0452%u2018%u2019%u201C%u201D%u2022%u2013%u2014%u0000%u2122%u0459%u203A%u045A%u045C%u045B%u045F"+
			"%u00A0%u040E%u045E%u0408%u00A4%u0490%u00A6%u00A7%u0401%u00A9%u0404%u00AB%u00AC%u00AD%u00AE%u0407"+
			"%u00B0%u00B1%u0406%u0456%u0491%u00B5%u00B6%u00B7%u0451%u2116%u0454%u00BB%u0458%u0405%u0455%u0457")
		var code2char = function(code) {
			if(code >= 0xC0 && code <= 0xFF) return String.fromCharCode(code - 0xC0 + 0x0410)
			if(code >= 0x80 && code <= 0xBF) return charmap.charAt(code - 0x80)
			return String.fromCharCode(code)
		}
		var res = ""
		for(var i = 0; i < str.length; i++) res = res + code2char(str.charCodeAt(i))
		return res;
	}
	
	function generateError(str) {
		throw new Error(str);
	}
}

function ajaxSend(adr, params, callBack, method, asyns) {
	var adr = adr || "";
	var params = params || {};
	var callBack = callBack || null;
	var method = method || "get";
	var asyns = asyns===false ? false : true;
	var a = new Ajax();
	a.set(adr, method, params, callBack, asyns);
	a.send();
}

function ajaxForm(formIndefier, method, callBack, asyns) {
	var formIndefier = formIndefier || 0;
	var method = method || "get";
	var callBack = callBack || null;
	var asyns = asyns===false ? false : true;
	var a = new Ajax();
	a.submitForm(formIndefier, false, {
		method: method,
		callBack: callBack,
		asyns: asyns
	} );
}

function ajaxMulti(send, onAllOk, onAllError, onMisc) {
	var a = {};
	for (var i=0, l=send.length, item, name, callBack, params, method, async, adr, sendParams; i<l; i++) {
		item = send[i];
		name = item.name || generateName()
		a[ name ] = {
			name: name,
			complete: false,
			isOk: null,
			ajax: new Ajax(),
			req: null,
			params: null
		};
		callBack = {
			"ok": {
				fn: reqOk,
				context: a[ name ],
				addArg: true
			},
			"error": {
				fn: reqEr,
				context: a[ name ],
				addArg: true
			}
		}
		sendParams = {};
		if (item.adr) sendParams.adr = item.adr;
		if (item.action) sendParams.action = item.action;
		if (item.method) sendParams.method = item.method;
		sendParams.callBack = callBack;
		sendParams.asyns = item.asyns===false ? false : true;
		if (typeof(item.form)!="undefined") {
			a[name].ajax.submitForm(item.form, false, sendParams );
		} else {
			a[name].ajax.set(sendParams.adr || "", sendParams.method || "get", item.params || {}, callBack, sendParams.asyns );
			a[name].ajax.send();
		}
	}
	function reqOk(req, params) {
		this.complete = true;
		this.isOk = true;
		this.req = req;
		this.params = params;
		checkAllReq();
	}
	function reqEr(req, params) {
		this.complete = true;
		this.isOk = false;
		this.req = req;
		this.params = params;
		checkAllReq();
	}
	function checkAllReq() {
		var allComplete = true;
		var allOk = true;
		var allEr = true;
		for (var key in a) {
			if (!a[key].complete) {
				allComplete = false;
				break;
			}
			if (!a[key].isOk) {
				allOk = false;
			} else {
				allEr = false;
			}
		}
		if (allComplete) {
			if (allOk) {
				if (typeof(onAllOk)=="function") onAllOk(a);
			} else if (allEr) {
				if (typeof(onAllError)=="function") onAllError(a);
			} else {
				if (typeof(onMisc)=="function") onMisc(a);
			}
		}
	}
	function generateName() {
		var s; 
		while( a[ s = "auto"+ Math.round( Math.random() * 1000 ) ] ) ;
		return s;
	}
}
