/*
//	file	:	gae_xhrequest.js
//	version	:	0.01e
//	url		:	http://furyu.tea-nifty.com/script/gae_xhrequest.js
//	update	:	2009/02/24
//	author	:	furyu
//	site	:	http://furyu.tea-nifty.com/annex/
//				http://d.hatena.ne.jp/furyu-tei/
//	license	:	This program is free software. You can redistribute it and/or modify it.
*/

(function(){
var	w=window,d=w.document;

if (w.GAE_xmlhttpRequest) return;

var	LOCALDEBUG=false;
var	GAE_XHGET=(LOCALDEBUG)?'http://localhost:8080/xhget':'http://furyu-tei.appspot.com/xhget';
var	SetCookie=false;

var	clone=function(src){
	var	tgt={};
	for (var mem in src) {
		if (src[mem]) {
			switch(typeof src[mem]) {
				case	'number'	:
				case	'string'	:
					tgt[mem]=src[mem];
					break;
				default				:
					if (src[mem].constructor==Array) {
						tgt[mem]=[].concat(src[mem]);
					}
					else {
						tgt[mem]=clone(src[mem]);
					}
					break;
			}
		}
		else {
			tgt[mem]=src[mem];
		}
	}
	return tgt;
};	//	end of clone()

var	toJSON=function(aarray){
	var	strs=[];
	for (var mem in aarray) {
		strs[strs.length]='"'+mem+'":"'+aarray[mem].replace(/"/g,'\\"')+'"';
	}
	return '{'+strs.join(',')+'}'
};	//	end of toJSON()

var	httpStatusCodes={
	100: "Continue"
,	101: "Switching Protocols"
,	102: "Processing"
,	200: "OK"
,	201: "Created"
,	202: "Accepted"
,	203: "Non-Authoritative Information"
,	204: "No Content"
,	205: "Reset Content"
,	206: "Partial Content"
,	207: "Multi-Status"
,	226: "IM Used"
,	300: "Multiple Choices"
,	301: "Moved Permanently"
,	302: "Found"
,	303: "See Other"
,	304: "Not Modified"
,	305: "Use Proxy"
,	306: "(Unused)"
,	307: "Temporary Redirect"
,	400: "Bad Request"
,	401: "Unauthorized"
,	402: "Payment Required"
,	403: "Forbidden"
,	404: "Not Found"
,	405: "Method Not Allowed"
,	406: "Not Acceptable"
,	407: "Proxy Authentication Required"
,	408: "Request Timeout"
,	409: "Conflict"
,	410: "Gone"
,	411: "Length Required"
,	412: "Precondition Failed"
,	413: "Request Entity Too Large"
,	414: "Request-URI Too Long"
,	415: "Unsupported Media Type"
,	416: "Requested Range Not Satisfiable"
,	417: "Expectation Failed"
,	418: "I'm a teapot"
,	422: "Unprocessable Entity"
,	423: "Locked"
,	424: "Failed Dependency"
,	425: "(Unordered Collection)"
,	426: "Upgrade Required"
,	500: "Internal Server Error"
,	501: "Not Implemented"
,	502: "Bad Gateway"
,	503: "Service Unavailable"
,	504: "Gateway Timeout"
,	505: "HTTP Version Not Supported"
,	506: "Variant Also Negotiates"
,	507: "Insufficient Storage"
,	510: "Not Extended"
};

var	s1=/(expires=[A-Z][a-z]{2}),/g, t1='$1#';
var	s2=/(expires=[A-Z][a-z]{2})#/g, t2='$1,';

var	jcbBaseName='_gxh'+new Date().getTime();
var	jcbBaseObj=w[jcbBaseName]={};

w.GAE_xmlhttpRequest=function(details){
	var	method=details.method;			//	ignored('GET' only)
	var	url=details.url;
	var	reqheaders=details.headers;
	var	data=details.data;				//	ignored('POST' is not supported)
	var	onload=details.onload;
	var	onerror=details.onerror;
	var	onreadystatechange=details.onreadystatechange;
	var	timeout=details.timeout;		//	timeout(sec)
	var	ontimeout=details.ontimeout;
	
	var	responseDetails={
		status			:	0
	,	statusText		:	''
	,	responseHeaders	:	''	//	text
	,	responseText	:	''
	,	readyState		:	0
	,	rspheaders		:	{}	//	associative array
	};
	
	var	tid=null,script=null,jcb='_'+new Date().getTime();
	while (jcbBaseObj[jcb]) jcb+='_';
	
	var	callfnc=function(fnc){
		if (fnc) {
			var	res=clone(responseDetails);
			setTimeout(function(){fnc(res)},0);
		}
	};
	var	updateRstat=function(){
		responseDetails.readyState++;
		callfnc(onreadystatechange);
	};
	var	updateHtmlStat=function(statcode,statstr){
		responseDetails.status=Number(isNaN(statcode)?500:statcode);
		if (statstr) {
			responseDetails.statusText=statstr;
			return;
		}
		responseDetails.statusText=httpStatusCodes[statcode]?httpStatusCodes[statcode]:String(statcode);
	};
	var	delJcb=function(){
//		try {alert(jcbBaseObj.toSource());} catch(e) {alert(jcb in jcbBaseObj);}
		try {delete jcbBaseObj[jcb];} catch(e) {jcbBaseObj[jcb]=null;}
//		try {alert(jcbBaseObj.toSource());} catch(e) {alert(jcb in jcbBaseObj);}
		if (script) {
			script.parentNode.removeChild(script);
			script=null;
		}
	};
	var	execSetCookie=function(cookieStr){
		if (!SetCookie) return;
		var	cookies=cookieStr.replace(s1,t1).split(/,\s*/);
		for (var ci=0,len=cookies.length; ci<len; ci++) {
			d.cookie=cookies[ci].replace(s2,t2);
		}
	};
	jcbBaseObj[jcb]=function(json){
		if (tid) {
			clearTimeout(tid);
			tid=null;
		}
		updateRstat();	//	2 => 3
		
		var	htext=[],headers=json.headers,statcode=json.statcode;
		
		updateHtmlStat(statcode,json.errorinfo);
		
		for (var mem in headers) {
			var	tmphead=headers[mem];
			if (tmphead.constructor==Array) {
				for (var ci=0,len=tmphead.length; ci<len; ci++) {
					htext[htext.length]=mem+': '+tmphead[ci];
					if (mem=='Set-Cookie') execSetCookie(tmphead[ci]);
				}
			}
			else {
				htext[htext.length]=mem+': '+tmphead;
				if (mem=='Set-Cookie') execSetCookie(tmphead);
			}
		}
		responseDetails.responseHeaders=htext.join('\n');
		responseDetails.responseText=json.content;
		responseDetails.rspheaders=headers;
		responseDetails.finalUrl=json.final_url;
		
		updateRstat();	//	3 => 4
		
		if (200<=responseDetails.status&&responseDetails.status<300) {
			callfnc(onload);
		}
		else {
			callfnc(onerror);
		}
		delJcb();
	};
	if (timeout&&ontimeout) {
		tid=setTimeout(function(){
			jcbBaseObj[jcb]=function(){delJcb();};
			updateHtmlStat(408,'Request Timeout (local)');
			callfnc(ontimeout);
		},timeout*1000);
	}
	updateRstat();	//	0 => 1
	
	script=d.createElement('script');
	script.type='text/javascript';
	script.charset='utf-8';
	script.src=GAE_XHGET+'?callback='+jcbBaseName+'.'+jcb+'&url='+encodeURIComponent(url)+((reqheaders)?'&headers='+encodeURIComponent(toJSON(reqheaders)):'');
	(d.getElementsByTagName('head')[0]||d.body).appendChild(script);
	
	updateRstat();	//	1 => 2

}	//	end of GAE_xmlhttpRequest()

})();
