function AJAXRequest(init) {
	var objPool = [], AJAX = this, _pool = AJAXRequest.__pool__ || (AJAXRequest.__pool__ = []);
	(function(obj) {
		// init xmlhttp pool, and some consts
		var emptyFun = function() { };

		// process inti parameter
		obj = obj ? obj : {};
		var prop = ['url', 'content', 'method', 'async', 'encode',      'timeout', 'ontimeout', 'onrequeststart', 'onrequestend', 'oncomplete', 'onexception'];
		var defs = ['',    '',        'GET',     true,    _GEC('UTF-8'), 600000,   emptyFun,    emptyFun,         emptyFun,       emptyFun,     emptyFun];
		var pc = prop.length;
		while(pc--) {
			AJAX[prop[pc]] = getp(obj[prop[pc]], defs[pc]);
		}
		// get the first xmlhttp, if failed then return false
		if(!getXHR()) { return false; }
	})(init);

	// get param or its default
	function getp(p, d) { return p != undefined ? p : d; }

	// get XMLHttpRequest from pool
	function getXHR() {
		var xhr, _vers = [window.XMLHttpRequest, "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
		for(var i = 0; i < _pool.length; i+=1) {
			if(_pool[i].readyState == 0 || _pool[i].readyState == 4) {
				return _pool[i];
			}
		}
		for(var i = 0; i < _vers.length; i+=1) {
			try {
				xhr = (_vers[i] && typeof(_vers[i]) == "function" ? new _vers[i] : new ActiveXObject(_vers[i]));
				break;
			} catch(e) {
				xhr = false;
				continue;
			}
		}
		if(!xhr) {
			throw 'Cannot init XMLHttpRequest object!';
			return false;
		}
		else {
			_pool[_pool.length] = xhr;
			return xhr;
		}
	}

	// get element by id
	function $(id) { return document.getElementById(id); }

	// convert anything to number
	function _N(d) { var n = d * 1; return(isNaN(n) ? 0 : n); }

	// convert anything to HtmlObject
	function _VO(v) { return (typeof(v) == "string" ? (v = $(v)) ? v : false : v); }

	// get an unique number
	function _GID(){return((new Date)*1);}

	// save update object
	function _SOP(id, ct) { objPool[id + ""] = ct; }

	// load update object
	function _LOP(id) { return(objPool[id + ""]); }

	// string replace function generator
	function _SRP(pre, reps, ps){
		return (function rep(str) {
					str = pre(str);
					for(var i = 0, n = reps.length; i < n; i+=1) {
						str = str.replace(reps[i], ps[i]);
					}
					return(str);
				});
	}

	// get encode method
	function _GEC(cs){
		if(cs.toUpperCase() == "UTF-8") {
			return(encodeURIComponent);
		}
		else {
			// replace sepcial chars: +
			return(_SRP(escape, [/\+/g], ["%2B"]));
		}
	}

	// set content to HtmlObject
	function _ST(obj, txt) {
		if(!obj.nodeName) {
			return;
		}
		var nn = "|" + obj.nodeName.toUpperCase() + "|";
		if("|INPUT|TEXTAREA|OPTION|".indexOf(nn) > -1) {
			obj.value = txt;
		}
		else {
			try {
				obj.innerHTML = txt;
			} catch(e) { };
		}
	}

	// generate a callback function
	function _CB(cb) {
		if(typeof(cb) == "function") {
			return cb;
		}
		else {
			cb = _VO(cb);
			if(cb) {
				return (function(obj) {
							_ST(cb, obj.responseText);
						});
			}
			else {
				return AJAX.oncomplete;
			}
		}
	}


	function $GP(v, d, f) {
		var i = 0, p = [];
		while(i < v.length) {
			p[i] = v[i] ? (f[i] ? f[i](v[i]) : v[i]) : d[i];
			i+=1;
		}
		while(i < d.length) {
			p[i]=d[i];
			i+=1;
		}
		return p;
	}

	// send request
	function send() {
		var ct, ctf = false, xhr = getXHR();
		// process parameters
		var p = $GP(arguments,
					[AJAX.url, AJAX.content, AJAX.oncomplete, AJAX.method, AJAX.async, null],
					[null, null, _CB, null, null, null]);
		var url = p[0], content = p[1], callback = p[2], method = p[3], async = p[4], extra = p[5];

		// is use POST method?
		var isPost = method.toUpperCase() == "POST" ? true : false;

		// check if url exists
		if(!url) {
			throw 'url is null';
			return false;
		}

		// event callback argument
		var ev = {
			url: url,
			content: content,
			method: method,
			params: extra
		};

		// append a timestamp to the url and open XMLHttpRequest
		if(!isPost) {
			url += (url.indexOf("?") > -1 ? "&" : "?") + "timestamp=" + _GID();
		}
		xhr.open(method, url, async);

		// request start event
		AJAX.onrequeststart(ev);

		// POST method needs a sepcial Content-Type
		if(isPost) {
			xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
		}
		xhr.setRequestHeader("X-Request-With", "XMLHttpRequest");

		// set a timeout to cancel request when timeout
		AJAX.ontimeout(ev);
		ct = setTimeout(function() {
							ctf = true;
							xhr.abort();
						},
						AJAX.timeout);
		var rc = function() {
			if(ctf) {
				AJAX.ontimeout(ev);
				AJAX.onrequestend(ev);
			}
			else if(xhr.readyState == 4) {
				clearTimeout(ct);
				ev.status = xhr.status;
				try{
					if(xhr.status == 200) {
						callback(xhr, extra);
					}
					else {
						AJAX.onexception(ev);
					}
				}
				catch(e) {
					AJAX.onexception(ev);
				}
				AJAX.onrequestend(ev);
			}
		}
		xhr.onreadystatechange = rc;
		if(isPost) { xhr.send(content); } else { xhr.send(""); }
		if(async == false) {
			rc();
		}
		return true;
	}

	this.setcharset = function(cs) {
		if(!AJAX.encode) {
			AJAX.encode = _GEC(cs);
		}
	}


	this.get = function(url, callback, extra) {
		return send(url, '', callback, 'GET', AJAX.async, extra);;
	}


	this.update = function(callback, url, interval, times, extra) {
		interval = _N(interval);
		times = _N(times);
		if(interval < 1) {
			times = 1;
		}
        else if(times < 1) {
            times = Number.POSITIVE_INFINITY;
        }
		var sendfoo = function() {
			send(url, "", callback, "GET", AJAX.async, extra);
		};
		var updateid = _GID();
		var updatefoo = function(updateCount) {
			sendfoo();
			updateCount--;
			if(updateCount > 0) {
				_SOP(updateid, setTimeout(function(){
										updatefoo(updateCount);
									}, interval));
			}
		}
		updatefoo(times);
		return updateid;
	}


	this.stopupdate = function(id) {
		clearTimeout(_LOP(id));
	}


	this.post = function(url, content, callback, extra) {
		return send(url, content, callback, "POST", AJAX.async, extra);
	}


	this.postf = function(formObj, callback, extra) {
		var p=[],vaf,pcbf,purl,pc,pm,ac=arguments.length,av=arguments;
		// check form legal
		formObj = formObj ? _VO(formObj) : false;
		if(!formObj || formObj.nodeName != "FORM") {
			return false;
		}
		// process form validate
		validfoo = formObj.getAttribute("onvalidate");
		validfoo = validfoo ? (typeof(validfoo)=="string" ? new Function(validfoo) : validfoo) : null;
		if(validfoo && !validfoo()) {
			return false;
		}
		// get url and method from formObj's attributes
		var url = formObj.getAttribute("action"), method = formObj.getAttribute("method");
		// check if content is empty
		var content = AJAX.formToStr(formObj);
		if(content.length == 0) {
			return false;
		}
		// send the request
		if(method.toUpperCase()=="POST") {
			return send(url, content, callback, "POST", true, extra);
		}
		else {
			url += (url.indexOf("?") > -1 ? "&" : "?") + content;
			return send(url, "", callback, "GET", true, extra);
		}
	}


	this.formToStr = function(formObj) {
		var qstr = "", and = "", elem, value;
		for(var i = 0; i< formObj.length; i+=1) {
			elem = formObj[i];
			if (elem.name!='') {
                value = undefined;
				switch(elem.type) {
					case "select-one":
						if(elem.selectedIndex > -1) {
							value = elem.options[elem.selectedIndex].value;
						}
						else {
							value = "";
						}
						break;
					case "checkbox":
					case "radio":
						if (elem.checked == true) {
							value = elem.value;
						}
						break;
					default:
						value = elem.value;
				}
                if(value != undefined) {
                    value = AJAX.encode(value);
                    qstr += and + elem.name + "=" + value;
                    and = "&";
                }
			}
		}
		return qstr;
	}
}
