﻿/*
 * Library for Simple Ajax
 * 2009.08.10 KiHyunKim
 */

var SimpleAjax = {
	VERSION : "0.13"
};

// [method] Normal XH Request
// returnDataType (String)	: data type of callbackFunction to receive ["xmlhttp"|"e4x"]
// forceArray (Array)		: if (returnDataType="e4x") xml tag name what cast to array force
SimpleAjax.executeXHRequest = function (method, uri, async, postdata, callbackFunction, callbackArguments, returnDataType, forceArray) {
	var xmlHttp = this.createXHObject();
	if (xmlHttp) {
		xmlHttp.onreadystatechange = function() {
			if (xmlHttp.readyState==4) {
				if (xmlHttp.status==200) {
					if ((returnDataType) && (returnDataType.toLowerCase() == "e4x")) {
						SimpleAjax.callbackE4xXHRequest( xmlHttp, callbackFunction, callbackArguments, forceArray );
					}else{
						SimpleAjax.callbackDirectXHRequest( xmlHttp, callbackFunction, callbackArguments );
					}
				}
			}
			return;
		};
		xmlHttp.open(method.toLowerCase(), uri, async);
		if (method.toLowerCase()=='post') {
			xmlHttp.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
			xmlHttp.send(postdata);
		}else xmlHttp.send(null);
	}
};

// [method] XH Request from form (automation)
// form (String or Object)	: FORM object or FORM name or FORM id
// formElementNames (Array)	: FORM element names to make parameter
// returnDataType (String)	: data type of callbackFunction to receive ["xmlhttp"|"e4x"]
// forceArray (Array)		: if (returnDataType="e4x") xml tag name what cast to array force
SimpleAjax.executeXHFormRequest = function (method, uri, async, form, formElementNames, callbackFunction, callbackArguments, returnDataType, forceArray) {
	var params = this.createXHParamFromForm(form, formElementNames);
	if ((params != null) && (method.toLowerCase()!='post')) {
		if (uri.match(/^.+\?$/gi))
			uri += params;
		else if (uri.match(/^.+\?.+$/gi))
			uri += '&' + params;
		else
			uri += '?' + params;
	}
	if (method.toLowerCase()=='post')
		this.executeXHRequest('post', uri, async, params, callbackFunction, callbackArguments, returnDataType, forceArray);
	else
		this.executeXHRequest('get', uri, async, null, callbackFunction, callbackArguments, returnDataType, forceArray);
};

// [internal] Callback for direct XH data
SimpleAjax.callbackDirectXHRequest = function (xmlHttp, callbackFunction, callbackArguments) {
	if ((callbackFunction!=null)&&(callbackFunction.constructor == Function)) {
		callbackFunction(xmlHttp, callbackArguments);
	}
	xmlHttp = null;
};

// [internal] Callback for E4X data
SimpleAjax.callbackE4xXHRequest = function (xmlHttp, callbackFunction, callbackArguments, forceArray) {
	if ((callbackFunction!=null)&&(callbackFunction.constructor == Function)) {
		var e4xData = SimpleAjax.xml2object.parseXHResult( xmlHttp, forceArray );
		callbackFunction(e4xData, callbackArguments);
	}
	xmlHttp = null;
};

// [internal] Create XH
SimpleAjax.createXHObject = function () {
	if (window.ActiveXObject) {
		var aVersions = ['Msxml2.XMLHTTP.7.0', 'Msxml2.XMLHTTP.6.0','Msxml2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP'];
		for (var i=0; i<aVersions.length; i++) {
			try { var xmlHttp = new ActiveXObject(aVersions[i]); return xmlHttp; } catch(e) {}
		}
	}else if ( typeof(XMLHttpRequest) != 'undefined' ) {
		var xmlHttp = new XMLHttpRequest(); return xmlHttp;
	}
	return false;
};

// [internal] Make XH parameter from form
SimpleAjax.createXHParamFromForm = function ( form, elementNames ) {
	if (form == null) return null;

	var objForm;
	if (typeof(form) == "String") {
		objForm = document.forms[form];
		if (!objForm) objForm = document.getElementById(form);
		if (!objForm) return null;
	}else{
		objForm = form;
	}

	var params = "";
	if ((elementNames) && (elementNames.length)) {
		for (var i=0; i<elementNames.length; i++ ) {
			if (objForm[elementNames[i]]) {
				params += ((params=="")?"":"&") + encodeURIComponent(elementNames[i]) + "=" + encodeURIComponent(this.getFormValue(objForm[elementNames[i]]));
			}else{
				params += ((params=="")?"":"&") + encodeURIComponent(elementNames[i]) + "=";
			}
		}
	}
	return ((params=="") ? null : params);
};

// [internal] Get form element value
SimpleAjax.getFormValue = function ( element ) {
	if (!element) return "";
	if (!element.type) {
		if ( element[0] && ((element[0].type=='radio')||(element[0].type=='checkbox')) ) {
			for (var i=0; i<element.length; i++) {
				if (element[i].checked ) return element[i].value;
			}
		}
	}else if ((element.type=='text')||(element.type=='password')||(element.type=='textarea')||(element.type=='hidden')) {
		return element.value;
	}else if (element.type.match(/select*/g)) {
		if (element.selectedIndex >= 0) return element.options[element.selectedIndex].value;
	}else if (((element.type=='radio')||(element.type=='checkbox'))&&(element.checked)) {
		return element.value;
	}
	return '';
};

// get deep object's child value (like e4x object)
// usage : SimpleAjax.Value(result, 'code.message[3].value')
SimpleAjax.Value = function(obj,childString) {
	if ((obj==null) || (childString==null) || (childString=="")) return obj;
	var childs = childString.split(".");
	var arrReg = /^(.+)\[([0-9]+)\]$/i;
	var testObj = obj;
	for (var i=0; i<childs.length; i++) {
		if (!childs[i]) return null;
		var matchs = arrReg.exec(childs[i]);
		if (matchs!=null) {
			if (!testObj.hasOwnProperty(matchs[1])) return null;
			if (typeof(testObj[matchs[1]])=="String") return null;
			if (testObj[matchs[1]].length<=parseInt(matchs[2])) return null;
			testObj = testObj[matchs[1]][parseInt(matchs[2])];
		}else{
			if (!testObj.hasOwnProperty(childs[i])) return null;
			testObj = testObj[childs[i]];
		}
		if (!testObj) return testObj;
	}
	return testObj;
};

// [internal] Make E4X Library
SimpleAjax.xml2object = {

	attrPrefix : '@',
	forceArray : null,

	parseXHResult : function ( xmlHttp, forceArray ) {
		var resDOM, oRoot;
		this.forceArray = forceArray;
		if ( (resDOM = xmlHttp.responseXML) && (oRoot = resDOM.documentElement) ) {
			return this.parseDOM( oRoot );
		}
		return null;
	},

	parseDOM : function ( root ) {
		if ( ! root ) return;

		this.__force_array = {};
		if ( this.forceArray ) {
			for( var i=0; i<this.forceArray.length; i++ ) {
				this.__force_array[this.forceArray[i]] = 1;
			}
		}

		var json = this.parseElement( root );   // parse root node
		if ( this.__force_array[root.nodeName] ) {
			json = [ json ];
		}
		if ( root.nodeType != 11 ) {            // DOCUMENT_FRAGMENT_NODE
			var tmp = {};
			tmp[root.nodeName] = json;          // root nodeName
			json = tmp;
		}
		return json;
	},
	
	parseElement : function ( elem ) {
		//  COMMENT_NODE
		if ( elem.nodeType == 7 ) {
			return;
		}

		//  TEXT_NODE CDATA_SECTION_NODE
		if ( elem.nodeType == 3 || elem.nodeType == 4 ) {
			var bool = elem.nodeValue.match( /[^\x00-\x20]/ );
			if ( bool == null ) return;     // ignore white spaces
			return this.decCdata(elem.nodeValue);
		}

		var retval;
		var cnt = {};

		//  parse attributes
		if ( elem.attributes && elem.attributes.length ) {
			retval = {};
			for ( var i=0; i<elem.attributes.length; i++ ) {
				var key = elem.attributes[i].nodeName;
				if ( typeof(key) != "string" ) continue;
				var val = elem.attributes[i].nodeValue;
				if ( ! val ) continue;
				key = this.attrPrefix + key;
				if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;
				cnt[key] ++;
				this.addNode( retval, key, cnt[key], val );
			}
		}

		//  parse child nodes (recursive)
		if ( elem.childNodes && elem.childNodes.length ) {
			var textonly = true;
			if ( retval ) textonly = false;        // some attributes exists
			for ( var i=0; i<elem.childNodes.length && textonly; i++ ) {
				var ntype = elem.childNodes[i].nodeType;
				if ( ntype == 3 || ntype == 4 ) continue;
				textonly = false;
			}
			if ( textonly ) {
				if ( ! retval ) retval = "";
				for ( var i=0; i<elem.childNodes.length; i++ ) {
					retval += this.decCdata(elem.childNodes[i].nodeValue);
				}
			} else {
				if ( ! retval ) retval = {};
				for ( var i=0; i<elem.childNodes.length; i++ ) {
					var key = elem.childNodes[i].nodeName;
					if ( typeof(key) != "string" ) continue;
					var val = this.parseElement( elem.childNodes[i] );
					if ( ! val ) continue;
					if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;
					cnt[key] ++;
					this.addNode( retval, key, cnt[key], val );
				}
			}
		}
		return retval;
	},

	addNode : function ( hash, key, cnts, val ) {
		if ( this.__force_array[key] ) {
			if ( cnts == 1 ) hash[key] = [];
			hash[key][hash[key].length] = val;      // push
		} else if ( cnts == 1 ) {                   // 1st sibling
			hash[key] = val;
		} else if ( cnts == 2 ) {                   // 2nd sibling
			hash[key] = [ hash[key], val ];
		} else {                                    // 3rd sibling and more
			hash[key][hash[key].length] = val;
		}
	},

	encCdata : function (str) {
		var filter = new RegExp(String.fromCharCode(8203), "gi");
		var output = str.replace( filter, String.fromCharCode(32) );
		return output.replace(/]]>/gi, String.fromCharCode(8203)+"cdataend;");
	},
	
	decCdata : function (str) {
		var filter = new RegExp(String.fromCharCode(8203)+"cdataend;", "gi");
		return str.replace(filter, "]]>");
	}

};