/*
xml2json v 1.1
copyright 2005-2007 Thomas Frank
Heavily modified by Mpowered for use in FieldWorks apps (see xml2json.orig.js.nogo for original code)

This program is free software under the terms of the 
GNU General Public License version 2 as published by the Free 
Software Foundation. It is distributed without any warranty.
*/

xml2json={
	parser:function(xmlcode, debug, debugLogCtrlr){
		if (debug) {
			debugLogCtrlr.logEvent('xml2json: main start', true);
		}
		xmlcode=xmlcode.replace(/\s*\/>/g,'/>');
		xmlcode=xmlcode.replace(/<\?[^>]*>/g,"").replace(/<\![^>]*>/g,"");
		var x=this.no_fast_endings(xmlcode);
		x=this.attris_to_tags(x);
		x='<jsontagwrapper>'+x+'</jsontagwrapper>';
		this.xmlobject={};
		var y=this.xml_to_object(x, debug, debugLogCtrlr).jsontagwrapper;
		this.objemptytonull(y);
		return y;
	},

	objisempty: function(obj) { // determines if object is empty
		// null and undefined are empty
		if (obj === null) {return true;}
		// Assume if it has a length property with a non-zero value
		// that that property is correct.
		if (obj.length && obj.length > 0) {return false;}
		if (obj.length === 0) {return true;}
		for (var key in obj) {
			if (Object.prototype.hasOwnProperty.call(obj, key)) {return false;}
		}
		return true;
	},

	objemptytonull: function(obj) { // traverses all nodes of an object and converts {} to null
		for (var i in obj) {
			if (typeof(obj[i]) == 'object') {
				if (this.objisempty(obj[i])) {
					obj[i] = null;
				}
				else {
					this.objemptytonull(obj[i], this.func);
				}
			}
		}
	},

	xml_to_object:function(xmlcode, debug, debugLogCtrlr){
		if (debug) {
			debugLogCtrlr.logEvent('xml2json: sub start', true);
		}
		var x=xmlcode.replace(/<\//g,"§");
		x=x.split("<");
		var y=[];
		var level=0;
		var opentags=[];
		var i;
		var dotcount;
		
		// split the tags out
		dotcount = ''; // used for outputting a visual count ... dots equals number of loops
		for (i=1;i<x.length;i++){
			if (debug) {
				dotcount += '.';
				if (((i + 1) % 50) === 0) { // output every n
					debugLogCtrlr.logEvent('xml2json: splittags: ' + dotcount, true);
					dotcount = '';
				}
			}
			var tagname=x[i].split(">")[0];
			opentags.push(tagname);
			level++;
			y.push(level+"<"+x[i].split("§")[0]);
			while(x[i].indexOf("§"+opentags[opentags.length-1]+">")>=0){level--;opentags.pop();}
		}
		if (debug) {
			debugLogCtrlr.logEvent('xml2json: splittags: ' + dotcount, true);
		}

		// create the object
		var oldniva=-1;
		var objname="this.xmlobject";
		if (debug) {
			debugLogCtrlr.logEvent('xml2json: create object for ' + y.length + ' tags', true);
		}
		dotcount = ''; // used for outputting a visual count ... dots equals number of loops
		for (i=0;i<y.length;i++){
			if (debug) {
				dotcount += '.';
				if (((i + 1) % 50) === 0) { // output every n
					debugLogCtrlr.logEvent('xml2json: createobject: ' + dotcount, true);
					dotcount = '';
				}
			}
			var preeval="";
			var niva=y[i].split("<")[0];
			var tagnamn=y[i].split("<")[1].split(">")[0];
			var rest=y[i].split(">")[1];
			if(niva<=oldniva){
				var tabort=oldniva-niva+1;
				for (var j=0;j<tabort;j++){objname=objname.substring(0,objname.lastIndexOf("."));}
			}
			objname+="."+tagnamn;
			var pobject=objname.substring(0,objname.lastIndexOf("."));
			if (eval("typeof "+pobject) != "object"){preeval+=pobject+"={value:"+pobject+"};\n";}
			var objlast=objname.substring(objname.lastIndexOf(".")+1);
			var already=false;
			for (var k in eval(pobject)){if(k==objlast){already=true;}}
			// QA TESTING vvvvv
//			if (objname.indexOf('attachments.x') !== -1) {
//				debugger;
//			}
			// QA TESTING ^^^^^
			var onlywhites=false;
			if (!/[^\s]/.test(rest)) {rest="";onlywhites=true;} // regex test for purely whitespace
			if (rest!=="" && !onlywhites){
				// literally do not know what these 3 are for
//				rest=rest.replace(/\*\$\*\*\*/g,"</"); // *$*** -> </
//				rest=rest.replace(/\*\$\*\*/g,"<"); // *$** -> <
//				rest=rest.replace(/\*\*\$\*/g,">"); // **$* -> >
				// escape special chars - so toeval does not fail
				rest = rest.replace(/\\/g, '&bksl;').replace(/\n/g, '&newl;').replace(/\r/g, '&care;').replace(/\t/g, '&tab;');
				// quote the string and escape internal quotes
				rest="'"+rest.replace(/\'/g,"\\'")+"'";
			} 
			else {rest="{}";}
			if (already && !eval(objname+".sort")){preeval+=objname+"=["+objname+"];\n";} // make it into an array
			var before="=", after="";
			if (already){before=".push(";after=")";}
			var toeval=preeval+objname+before+rest+after;
			// QA TESTING vvvvv
//			console.log(toeval);
//			console.log(objname);
//			if (objname.indexOf('tasklist.t') !== -1) {
//				console.log(this.string_as_unicode_escape(rest.substring(465, 475)));
//				debugger;
//			}
			// QA TESTING ^^^^^
			eval(toeval);
			if(eval(objname+".sort")){objname+="["+eval(objname+".length-1")+"]";}
			oldniva=niva;
			// unescape special chars
			if (rest!=="" && !onlywhites){
				toeval=objname+'='+objname+ '.replace(/\\&bksl;/g, \'\\\\\').replace(/\\&newl;/g, \'\\n\').replace(/\\&care;/g, \'\\r\').replace(/\\&tab;/g, \'\\t\')';
				eval(toeval);
			}
		}
		if (debug) {
			debugLogCtrlr.logEvent('xml2json: createobject: ' + dotcount, true);
		}
		return (this.xmlobject);
	},

	// QA TESTING vvvvv
//	string_as_unicode_escape: function(input) {
//		var output = '';
//		for (var i = 0, l = input.length; i < l; i++) {
//			output += '\\u' + (input.charCodeAt(i) + 0x10000).toString(16).slice(1);
//		}
//		return output;
//	},
	// QA TESTING ^^^^^

	no_fast_endings:function(x){
		x=x.split("/>");
		for (var i=1;i<x.length;i++){
			var t=x[i-1].substring(x[i-1].lastIndexOf("<")+1).split(" ")[0];
			x[i]="></"+t+">"+x[i];
		}
		x=x.join("");
		return x;
	},

	attris_to_tags: function(x){
		var d=' ="\''.split(""), r;
		x=x.split(">");
		for (var i=0;i<x.length;i++){
			var temp=x[i].split("<");
			for (r=0;r<4;r++){temp[0]=temp[0].replace(new RegExp(d[r],"g"),"_jsonconvtemp"+r+"_");}
			if(temp[1]){
				temp[1]=temp[1].replace(/'/g,'"');
				temp[1]=temp[1].split('"');
				for (var j=1;j<temp[1].length;j+=2){
					for (r=0;r<4;r++){temp[1][j]=temp[1][j].replace(new RegExp(d[r],"g"),"_jsonconvtemp"+r+"_");}
				}
				temp[1]=temp[1].join('"');
			}
			x[i]=temp.join("<");
		}
		x=x.join(">");
		x=x.replace(/ ([^=]*)=([^ |>]*)/g,"><$1>$2</$1");
		x=x.replace(/>"/g,">").replace(/"</g,"<");
		for (r=0;r<4;r++){x=x.replace(new RegExp("_jsonconvtemp"+r+"_","g"),d[r]);}
		return x;
	}
};

if (!Array.prototype.push){
	Array.prototype.push=function(x){
		this[this.length]=x;
		return true;
	};
}

if (!Array.prototype.pop){
	Array.prototype.pop=function(){
  		var response = this[this.length-1];
  		this.length--;
  		return response;
	};
}
