PageRenderTime 5ms CodeModel.GetById 755ms app.highlight 462ms RepoModel.GetById 213ms app.codeStats 0ms

/interfaces/xml2json/xml2json.js

https://github.com/floviolleau/exchangecalendar
JavaScript | 953 lines | 871 code | 58 blank | 24 comment | 172 complexity | 574a027c987b3afa32d6303dd553ee9c MD5 | raw file
  1var Cc = Components.classes;
  2var Ci = Components.interfaces;
  3var Cu = Components.utils;
  4var Cr = Components.results;
  5var components = Components;
  6
  7function STACK(aDepth, aSkip) {
  8    let depth = aDepth || 10;
  9    let skip = aSkip || 0;
 10    let stack = "";
 11    let frame = components.stack.caller;
 12    for (let i = 1; i <= depth + skip && frame; i++) {
 13        if (i > skip) {
 14            stack += i + ": [" + frame.filename + ":" +
 15                     frame.lineNumber + "] " + frame.name + "\n";
 16        }
 17        frame = frame.caller;
 18    }
 19    return stack;
 20}
 21
 22function typeString(o) {
 23	if (typeof o != 'object')
 24		return typeof o;
 25
 26	if (o === null)
 27		return "null";
 28  //object, array, function, date, regexp, string, number, boolean, error
 29	var internalClass = Object.prototype.toString.call(o)
 30                                               .match(/\[object\s(\w+)\]/)[1];
 31	return internalClass.toLowerCase();
 32}
 33
 34function isObject(obj) {
 35	return (typeString(obj) == "object");
 36}
 37
 38function isArray(obj) {
 39	return (typeString(obj) == "array");
 40}
 41
 42function isInList(inArray, inStr)
 43{
 44	return (inArray[inStr] !== undefined);
 45}
 46
 47const specialChars1 = {	" ": true, 
 48			"\n" : true, 
 49			"\r" : true, 
 50			"\t" : true };
 51
 52function findCharacter(aXMLString, aSP, aChar)
 53{
 54	if (!aXMLString) return -1;
 55
 56	var pos = aSP;
 57	var strLength = aXMLString.length;
 58//	while ((pos < strLength) && (aXMLString[pos] != aChar)) {
 59	while ((pos < strLength) && (aXMLString.charAt(pos) != aChar)) {
 60		pos++;
 61	}
 62
 63	if (pos < strLength) {
 64		return pos;
 65	}
 66
 67	return -1;
 68}
 69
 70function findString(aXMLString, aSP, aNeedle)
 71{
 72	if (!aXMLString) return -1;
 73
 74	var pos = aSP;
 75	var needleLength = aNeedle.length;
 76	var strLength = aXMLString.length - needleLength + 1;
 77	while ((pos < strLength) && (aXMLString.substr(pos, needleLength) != aNeedle)) {
 78		pos++;
 79	}
 80
 81	if (pos < strLength) {
 82		return pos;
 83	}
 84
 85	return -1;
 86}
 87
 88function splitOnCharacter(aXMLString, aSP, aSplitCharacter)
 89{
 90	if (!aXMLString) {
 91		return null;
 92	}
 93
 94	var tmpPos = aSP;
 95	var result = "";
 96	var notClosed = true;
 97	var notQuoteOpen = true;
 98	var quotesUsed = "";
 99	var strLen = aXMLString.length;
100	var splitCharIsArray = isArray(aSplitCharacter);
101	while ((tmpPos < strLen) && (notClosed)) {
102//		if ((aXMLString[tmpPos] == "'") || (aXMLString[tmpPos] == '"')) {
103		if ((aXMLString.charAt(tmpPos) == "'") || (aXMLString.charAt(tmpPos) == '"')) {
104			// We found quotes. Do they belong to our string.
105			if (notQuoteOpen) {
106				quotesUsed = aXMLString.charAt(tmpPos);
107				notQuoteOpen = false;
108			}
109			else {
110//				if (aXMLString[tmpPos] == quotesUsed) {
111				if (aXMLString.charAt(tmpPos) == quotesUsed) {
112					quotesUsed = "";
113					notQuoteOpen = true;
114				}
115			}
116		}
117
118		var hitSplitCharacter = false;
119		if (notQuoteOpen) {
120			if (splitCharIsArray) {
121				for (var index in aSplitCharacter) {
122					if (aXMLString.substr(tmpPos,aSplitCharacter[index].length) == aSplitCharacter[index]) {
123						hitSplitCharacter = true;
124						break;
125					}
126				}
127			}
128			else {
129				if (aXMLString.substr(tmpPos,aSplitCharacter.length) == aSplitCharacter) {
130					hitSplitCharacter = true;
131				}
132			}
133		}
134
135		if (hitSplitCharacter) {
136			notClosed = false;
137		}
138		else {
139//			result += aXMLString[tmpPos];
140			result += aXMLString.charAt(tmpPos);
141		}
142		tmpPos++;
143	}
144
145	if (!notClosed) {
146		return result;
147	}
148	else {
149		return null;
150	}
151}
152
153var replaceFromXML = function _replaceFromXML(str, r1)
154{
155	var result = str;
156	if (r1[0] == "#") {
157		if (r1[1] == "x") {
158			// hexadecimal
159			result = String.fromCharCode(parseInt(r1.substr(2),16))
160		}
161		else {
162			// Decimal
163			result = String.fromCharCode(parseInt(r1.substr(1),10))
164		}
165	}
166	else {
167		switch (r1) {
168		case "amp": result = "&"; break;
169		case "quot": result = '"'; break;
170		case "apos": result = "'"; break;
171		case "lt": result = "<"; break;
172		case "gt": result = ">"; break;
173		}
174	}
175	return result;
176}
177
178function convertSpecialCharatersFromXML(aXMLString)
179{
180	if (!aXMLString) return aXMLString;
181
182	var result = aXMLString;
183	// Convert special characters
184	result = result.replace(/&(quot|apos|lt|gt|amp|#x[0123456789ancdefABCDEF][0123456789ancdefABCDEF]?[0123456789ancdefABCDEF]?[0123456789ancdefABCDEF]?|#[0123456789][0123456789]?[0123456789]?[0123456789]?);/g, replaceFromXML); 
185
186	return result;
187}
188
189var replaceToXML = function _replaceToXML(str, r1)
190{
191	var result = str;
192	switch (r1) {
193	case "&": result = "&amp;"; break;
194	case '"': result = "&quot;"; break;
195	case "'": result = "&apos;"; break;
196	case "<": result = "&lt;"; break;
197	case ">": result = "&gt;"; break;
198	}
199
200	return result;
201}
202
203function convertSpecialCharatersToXML(aXMLString)
204{
205	if ((aXMLString === null) || (aXMLString === undefined)) return aXMLString;
206
207	var result = aXMLString.toString();
208	// Convert special characters
209	result = result.replace(/(&|\x22|\x27|<|>)/g, replaceToXML);  
210
211	return result;
212}
213
214function trim(aValue)
215{
216	var strLength = aValue.length;
217	var leftPos = 0;
218	while ((leftPos < strLength) && (aValue.charAt(leftPos) == " ")) {
219		leftPos++;
220	}
221	var rightPos = strLength-1;
222	while ((rightPos >= 0) && (aValue.charAt(rightPos) == " ")) {
223		rightPos--;
224	}
225	return aValue.substr(leftPos, rightPos - leftPos + 1);
226}
227
228function hasXMLHeader(aStr, aSP)
229{
230	if (!aStr) return 0;
231
232	var pos = findCharacter(aStr, aSP, "<");
233	var strLength = aStr.length;
234	if (pos > -1) {
235		pos++;
236		var tc = aStr.charAt(pos);
237
238		if ( (pos < strLength) && (tc == "?")) {
239			pos++;
240			if (aStr.substr(pos, 4) == "xml ") {
241				var tmpPos = findString(aStr, pos, "?>");
242				if (tmpPos == -1) {throw -20;} // Error invalid special tag
243				else {
244					return (tmpPos+2);						
245				}
246			}
247			else {throw -21;} //ERR_MISSING_SPECIAL_TAG
248		}
249	}
250	return 0;
251}
252
253function splitTagName(aTagName) {
254	var ns = null;
255	var tn = null;
256	var sepPos = aTagName.indexOf(tsep);
257	if (sepPos > -1) {
258		ns = aTagName.substr(0,sepPos);
259		tn = aTagName.substr(sepPos+1);
260	}
261	else {
262		tn = aTagName;
263	}
264
265	return { tagName: tn, nameSpace: ns };
266}
267
268function convertComparisonPart(a, aJSONObject){
269	var r = [];
270	var tc = a[0];
271	if ((tc == "/") || (tc == ".") || (tc == "@")) {
272		r = realXPath(aJSONObject, a);
273	}
274	else {
275		r = [];
276		if ( (tc == "'") || (tc == '"')) {
277			r.push(a.substr(1,a.length-2));
278		}
279		else {
280			if (isNaN(a)) {
281				r = realXPath(aJSONObject, a);
282			}
283			else {
284				r.push(Number(a));
285			}
286		}
287	}
288	return r;
289}
290
291
292function ifFunction(aCondition, aJSONObject){
293	var level = 0;
294	var tmpCondition = trim(aCondition);
295	var compareList = [];
296	while (tmpCondition != "") {
297		var startPos = 0;
298		var weHaveSubCondition = false;
299		if (tmpCondition[0] == "(") {
300			var subCondition = splitOnCharacter(tmpCondition.substr(1), 0, ")");
301			if (subCondition) {
302				startPos = subCondition.length;
303				weHaveSubCondition = true;
304			}
305			else {
306				throw "XPath error: Did not find closing round bracket '"+aJSONObject.tagName+"' for condition:"+aCondition;
307			}
308		}
309
310		var splitPart = splitOnCharacter(tmpCondition.toLowerCase(), startPos, [" and ", " or "]);
311		var operator = null;
312		var comparison = null;
313		if (splitPart) {
314			splitPart = tmpCondition.substr(0, splitPart.length);
315			tmpCondition = tmpCondition.substr(splitPart.length + 1);
316
317			if (tmpCondition[0] == "a") {
318				operator = "and";
319				tmpCondition = tmpCondition.substr(4);
320			}
321			else {
322				operator = "or";
323				tmpCondition = tmpCondition.substr(3);
324			}
325		}
326		else {
327			splitPart = tmpCondition;
328			tmpCondition = "";
329		}
330		if (weHaveSubCondition) {
331			compareList.push( { left: subCondition, right: "", operator: operator, comparison: comparison, subCondition: subCondition} );
332		}
333		else {
334			var splitPart2 = splitOnCharacter(splitPart, 0, ["!=", "<=", ">=", "<", "=", ">"]);
335			if (splitPart2) {
336				// Get comparison type
337				var smallerThen = false;
338				var equalTo = false;
339				var biggerThen = false;
340				var splitPos2 = splitPart2.length;
341				switch (splitPart.charAt(splitPos2)) {
342					case "!":comparison = "!=";break;
343					case "<":comparison = "<";if (splitPart.charAt(splitPos2+1) == "=") comparison = "<=";break;
344					case "=":comparison = "=";break;
345					case ">":comparison = ">";if (splitPart.charAt(splitPos2+1) == "=") comparison = ">=";break;
346				}
347				compareList.push( { left: trim(splitPart2), right: trim(splitPart.substr(splitPart2.length+comparison.length)), operator: operator, comparison: comparison, subCondition: subCondition} );
348			}
349			else {
350				compareList.push( { left: trim(splitPart), right: "", operator: operator, comparison: comparison, subCondition: subCondition} );
351			}
352		}
353	}
354	var totalResult = true;
355	var lastOperator = null;
356	for (let index in compareList) {
357		var tmpResult = false;
358		if (compareList[index].subCondition) {tmpResult = ifFunction(compareList[index].left, aJSONObject);}
359		else {
360			let tmpLeft = convertComparisonPart(compareList[index].left, aJSONObject);
361			let tmpRight = convertComparisonPart(compareList[index].right, aJSONObject);
362			if (tmpLeft.length > 0) {
363				if (compareList[index].comparison) {
364					// Filter out ony the valid ones.
365					if (tmpRight.length > 0) {
366						var x = 0;
367						tmpResult = false;
368						while ((x < tmpLeft.length) && (!tmpResult)) {
369							if ((typeof tmpLeft[x] === "string") || (tmpLeft[x] instanceof String) || (typeof tmpLeft[x] === "number")) {
370								var evalConditionLeft = tmpLeft[x].toString();
371							}
372							else {
373								var evalConditionLeft = realGetValue(tmpLeft[x]).toString();
374							}
375							var y = 0;
376							while ((y < tmpRight.length) && (!tmpResult)) {
377								if ((typeof tmpRight[y] === "string") || (tmpRight[y] instanceof String) || (typeof tmpRight[y] === "number")) {
378									var evalConditionRight = tmpRight[y].toString();
379								}
380								else {var evalConditionRight = realGetValue(tmpRight[y]).toString();}
381								switch (compareList[index].comparison) {
382								case "!=":tmpResult = (evalConditionLeft != evalConditionRight);break;
383								case "<=":tmpResult = (evalConditionLeft <= evalConditionRight);break;
384								case ">=":tmpResult = (evalConditionLeft >= evalConditionRight);break;
385								case "<":tmpResult = (evalConditionLeft < evalConditionRight);break;
386								case "=":tmpResult = (evalConditionLeft==evalConditionRight);break;
387								case ">":tmpResult = (evalConditionLeft > evalConditionRight);break;
388								}
389								y++;
390							}
391							x++;
392						}
393					}
394				}
395				else {
396					tmpResult = true;
397				}
398			}
399			else {tmpResult = false;}
400			tmpLeft = null;
401			tmpRight = null;
402		}
403		switch (lastOperator) {
404		case "and":totalResult = (totalResult && tmpResult);break;
405		case "or":totalResult = (totalResult || tmpResult);break;
406		case null:totalResult = tmpResult;break;
407		}
408		if (compareList[index].operator) {
409			if ((compareList[index].operator == "and") && (!totalResult)) {return false;}
410			if ((compareList[index].operator == "or") && (totalResult)) {return true;}
411		}
412		lastOperator = compareList[index].operator;
413	}
414	return totalResult;
415}
416
417function realGetValue(aParent) {
418	if (!aParent) throw -60;
419	if (!aParent[tcontent]) return "";
420
421	let result = "";
422	let i = 0;
423	while (i < aParent[tcontent].length) {
424		result = result + aParent[tcontent][i];
425		i++;
426	}
427	return convertSpecialCharatersFromXML(result);
428}
429
430function realGetTags(aParent, aTagName) {
431	if ((!aParent) || (!aParent[telements])) throw -54;
432	let result = [];
433
434	var tmpTN = splitTagName(aTagName);
435
436	var i = 0;
437	while (i < aParent[telements].length) {
438//			if ((aParent[telements][i][tnamespace] == tmpTN.nameSpace) && (aParent[telements][i].tagName == tmpTN.tagName)) {
439		if (aParent[telements][i].tagName == tmpTN.tagName) {  // We ignore the namespace for now.
440			result.push(aParent[telements][i]);
441		}
442		i++;
443	}
444	return result;
445}
446
447
448function realXPath(aParent, aPath){
449	//dump("XPath:"+aPath+"("+STACK(6)+")\n");
450	//dump("aParent:"+JSON.stringify(aParent)+"\n");
451	//dump("..\n");
452	var tmpPath = aPath;
453	var result = [];
454	if (tmpPath[0] == "/") {
455		tmpPath = tmpPath.substr(1);
456	}
457
458	switch (tmpPath[0]) {
459	case "@" : // Find attribute within this element
460		let attrName = tmpPath.substr(1);
461		if ((aParent[tattributes]) && (aParent[tattributes][attrName])) {
462			//dump("XPath:found attribute. value="+String(aParent[tattributes][attrName])+"\n");
463			result.push(String(aParent[tattributes][attrName]));
464		}
465		//dump("XPath find attribute '"+attrName+"'. result.length='"+result.length+"'\n");
466		tmpPath = "";
467		break;
468	case "*" : // Wildcard. Will parse all children.
469		tmpPath = tmpPath.substr(1);
470		let i = 0;
471		while (i < aParent[telements].length) {
472			result.push(aParent[telements][i]);
473			i++;
474		}
475		break;
476	case "[" : // Compare/match function
477		let index = splitOnCharacter(tmpPath.substr(1), 0, "]");
478		if (!index) {throw "XPath error: Did not find closing square bracket. tagName:"+aParent.tagName+", tmpPath:"+tmpPath;}
479		tmpPath = tmpPath.substr(index.length+2);
480		index = trim(index); 
481		if (index != "") {
482			if (ifFunction(index, aParent)) {
483				result.push(aParent);
484			}
485			else {
486				return result;
487			}
488		}
489		else {
490			throw "XPath compare error:No Value between square brackets:"+aParent.tagName+"["+index+"]";
491		}
492		break;
493	default:
494		let bracketPos = tmpPath.indexOf("[");
495		let forwardSlashPos = tmpPath.indexOf("/");
496		let splitPos = tmpPath.length;
497		if ((bracketPos < splitPos) && (bracketPos > -1)) {splitPos = bracketPos;}
498		if ((forwardSlashPos < splitPos) && (forwardSlashPos > -1)) {splitPos = forwardSlashPos;}
499		let tmpPath2 = tmpPath.substr(0, splitPos);
500		tmpPath = tmpPath.substr(splitPos);
501		let equalTags = [];
502		equalTags = realGetTags(aParent,tmpPath2);
503		result = equalTags;
504	}
505
506	if ((result.length > 0) && (tmpPath != "")) {
507		let finalResult = [];
508		let i = 0;
509		while (i < result.length) {
510			var tmpResult = realXPath(result[i], tmpPath);
511			if (!isArray(tmpResult)) { // Check if answer is an Array or a String. String is returned on attribute search.
512				finalResult = tmpResult;
513			}
514			else {
515				let j = 0;
516				while (j < tmpResult.length) {
517					finalResult.push(tmpResult[j]);
518					j++;
519				}
520			}
521			i++;
522		}
523		result = finalResult;
524	}
525
526	return result;
527}
528
529function realElementToString(aElement) {
530	if (!aElement.tagName) return "";
531
532	//dump(" @@@@@:"+JSON.stringify(aElement)+"\n");
533
534	let result = "<";
535	if (aElement[tnamespace]) result = result + aElement[tnamespace] + tsep;
536	result = result + aElement.tagName;
537
538	if (aElement[tattributes]) {
539		for (var attrName in aElement[tattributes]) {
540			result = result + " " + attrName + "="+'"' + aElement[tattributes][attrName]+'"';
541		}
542	}
543
544	if ((aElement[tcontent]) || (aElement[telements].length > 0)) {
545		result = result + ">";
546		if ((aElement[tcontent]) && (aElement[tcontent].length > 0)) {
547			let i = 0;
548			while (i < aElement[tcontent].length) {
549				result = result + aElement[tcontent][i];
550				i++;
551			}
552		}
553
554		let i = 0;
555		while (i < aElement[telements].length) {
556			result = result + realElementToString(aElement[telements][i]);
557			i++;
558		}
559
560		result = result + "</";
561		if (aElement[tnamespace]) result = result + aElement[tnamespace] + tsep;
562		result = result + aElement.tagName + ">";
563		
564
565	}
566	else {
567		result = result + "/>";
568	}
569
570	return result;
571}
572
573function realSetAttribute(aParent, aName, aValue) {
574	if ((!aParent) || (!aParent[telements])) throw -51;
575	if (!aParent[tattributes]) {
576		aParent[tattributes] = {};
577	}
578	aParent[tattributes][aName] = aValue;
579}
580
581function realAddContent(aParent, aString) {
582	//dump("addContent: aString="+aString+"\n");
583	if ((!aParent) || (!aParent[telements])) throw -55;
584	if (!aParent[tcontent]) {
585		aParent[tcontent] = [];
586	}
587	aParent[tcontent].push(aString);
588}
589
590function realClosingTag(aParent, aTagName) {
591	//dump("closingTag: aTagName="+aTagName+"\n");
592	var tmpTN = splitTagName(aTagName);
593
594	let closingMatchesOpening = false;
595	if (tmpTN.tagName == aParent.tagName) {
596		if (tmpTN.nameSpace == aParent[tnamespace]) {
597			closingMatchesOpening = true;
598		}
599	}
600
601	if (!closingMatchesOpening) {
602		throw -5; // Closing element does not match opening element.
603	}
604	else {
605		if (aParent.parent) {
606			let result = aParent.parent;
607			aParent.parent = null;
608			delete aParent['parent'];
609			return result;
610		}
611		else {
612			throw -6; // We should never get here. But in case we do. Wee see a closing element for which no opening element was seen.
613		}
614	}
615}
616
617function realOpeningTag(aParent, aTagName) {
618	//dump("openingTag: aTagName="+aTagName+"\n");
619	var tmpTN = splitTagName(aTagName);
620	let tmpElement = { parent: aParent,
621				e: [],
622				n: tmpTN.nameSpace,
623				tagName: tmpTN.tagName};
624	aParent[telements].push(tmpElement);
625	return tmpElement;
626}
627
628function realSetAttributeStr(aParent, aString) {
629	//dump("setAttributeStr 1: aString="+aString+"\n");
630	aString = aString.replace(/\n/g, "").replace(/\r/g, "").replace(/\t/g, "");
631	var sp = aString.indexOf("=");
632	if (sp == -1) {
633		throw -13; // Equal sign not found.
634	}
635	var an = trim(aString.substr(0, sp));
636	var av = trim(aString.substr(sp+1));
637	var tc = av[0];
638	if ((tc == "'") || (tc == '"')) {
639		let vl = av.length;
640		if (tc == av.charAt(vl-1)) {
641			av = av.substr(1, vl-2);
642		}
643		else {
644			throw -14; // Did not find closing quote
645		}
646	}
647
648	//dump("setAttributeStr 2: name="+an+", value="+av+"\n");
649	realSetAttribute(aParent, an, av);
650}
651
652var EXPORTED_SYMBOLS = ["xml2json", "telements", "tattributes", "tcontent", "convertSpecialCharatersFromXML", "convertSpecialCharatersToXML"];
653
654const tsep = ":";
655const telements = "e";
656const tattributes = "a";
657const tcontent = "c";
658const tnamespace = "n";
659
660var xml2json = {
661
662	newJSON: function _newJSON() {
663		return { e: [] };
664	},
665
666	addTagObject: function _addTagObject(aParent, aChildObject) {
667		if ((!aParent) || (!aParent[telements])) throw -52;
668		aParent[telements].push(aChildObject);
669
670		return aParent[telements][aParent[telements].length-1];
671	},
672
673	getTags: function _getTags(aParent, aTagName) {
674		return this.clone(realGetTags(aParent, aTagName));
675	},
676
677	getValue: function _getValue(aParent) {
678		return realGetValue(aParent);
679	},
680
681	getTagValue: function _getTagValue(aParent, aTagName, aDefault) {
682		if ((!aParent) || (!aParent[telements]) || (!aTagName)) throw "-62 aParent:"+aParent+", aTagName="+aTagName;
683
684		let result = null;
685		let i = 0;
686		var tmpTN = splitTagName(aTagName);
687		while ((!result) && (i < aParent[telements].length)) {
688			if (aParent[telements][i].tagName == tmpTN.tagName) {  // We ignore the namespace for now.
689				result = realGetValue(aParent[telements][i]);
690			}
691			i++;
692		}
693		if (!result) result = aDefault;
694		return result;
695	},
696
697	clone: function _clone(aElement) {
698		return JSON.parse(JSON.stringify(aElement));
699	},
700
701	getTag: function _getTag(aParent, aTagName) {
702		let result = null;
703
704		var tmpTN = splitTagName(aTagName);
705
706		var i = 0;
707		while ((!result) && (i < aParent[telements].length)) {
708//			if ((aParent[telements][i][tnamespace] == tmpTN.nameSpace) && (aParent[telements][i].tagName == tmpTN.tagName)) {
709			if (aParent[telements][i].tagName == tmpTN.tagName) { // Namespace is ignored for now.
710				return this.clone(aParent[telements][i]);
711			}
712			i++;
713		}
714		return null;
715	},
716
717	addTag: function _addTag(aParent, aTagName, aNameSpace, aValue) {
718		if ((!aParent) || (!aParent[telements])) throw -53;
719		var tmpJson = {tagName: aTagName,
720				n: aNameSpace,
721				e: []};
722		if (aValue) {
723			realAddContent(tmpJson, convertSpecialCharatersToXML(aValue));
724		}
725		aParent[telements].push(tmpJson);
726
727		return aParent[telements][aParent[telements].length-1];
728	},
729
730	elementToString: function _elementToString(aElement) {
731		return realElementToString(aElement);
732	},
733
734	toString: function _toString(aParent) {
735		var result = "";
736		var i = 0;
737		//dump(" !!!!!:"+JSON.stringify(aParent)+"\n");
738		if (aParent["tagName"]) {
739			result = result + realElementToString(aParent);
740		}
741		else {
742			while (i < aParent[telements].length) {
743				result = result + realElementToString(aParent[telements][i]);
744				i++;
745			}
746		}
747
748		return result;
749	},
750
751	openingTag: function _openingTag(aParent, aTagName) {
752		return realOpeningTag(aParent, aTagName);
753	},
754
755	addContent: function _addContent(aParent, aString) {
756		realAddContent(aParent, aString);
757	},
758
759	closingTag: function _closingTag(aParent, aTagName) {
760		return realClosingTag(aParent, aTagName);
761	},
762
763	getAttributeByTag: function _getAttributeByTag(aParent, aTagName, aName) {
764//dump("getAttributeByTag 1: aParent:"+aParent+", aTagName="+aTagName+", aName="+aName+"\n");
765		if ((!aParent) || (!aParent[telements]) || (!aTagName)) throw -80;
766
767		let i = 0;
768		var tmpTN = splitTagName(aTagName);
769
770		while (i < aParent[telements].length) {
771			if ((tmpTN.tagName == aParent[telements][i].tagName) && (aParent[telements][i][tattributes])) { // We ignore namespace for now.
772				if (aParent[telements][i][tattributes][aName]) {
773					return aParent[telements][i][tattributes][aName];
774				}
775			}
776			i++;
777		}
778		return null;
779	},
780
781	getAttribute: function _getAttribute(aParent, aName, aDefault) {
782		if (!aParent) throw -70;
783
784		if ((!aParent[tattributes]) || (!aParent[tattributes][aName])) {
785			if (aDefault) {
786				return aDefault;
787			}
788			return null;
789		}
790
791		return convertSpecialCharatersFromXML(aParent[tattributes][aName]);
792	},
793
794	setAttribute: function _setAttribute(aParent, aName, aValue) {
795		realSetAttribute(aParent, aName, convertSpecialCharatersToXML(aValue));
796	},
797
798	setAttributeStr: function _setAttributeStr(aParent, aString) {
799		realSetAttributeStr(aParent, aString);
800	},
801
802	parseXML: function _parseXML(aJSONObject, aXMLString) {
803
804		var currentjson = aJSONObject;
805
806		if (!aXMLString) return;
807
808	try{
809		var pos = 0;
810		var xmlHeaderPos = hasXMLHeader(aXMLString, pos);
811		if (xmlHeaderPos > 0) {
812			//dump("We have an XML header. Going to strip it.\n");
813			pos = xmlHeaderPos;
814		}
815
816		var strLength = aXMLString.length;
817		while (pos < strLength) {
818			var tmpPos = findCharacter(aXMLString, pos, "<");
819			if (tmpPos > -1) {
820				// Found openingcharacter of element.
821				let skipped = tmpPos - pos;
822				if (skipped > 0) {
823					// Found data before element.
824					realAddContent(currentjson,aXMLString.substr(pos, skipped));
825				}
826				pos = tmpPos + 1;
827			}
828			//var tc = aXMLString[pos];
829			var tc = aXMLString.charAt(pos);
830
831			if ( (pos < strLength) && (tc == "/")) {
832				// Found character for closing element
833				pos++;
834				tmpPos = findCharacter(aXMLString, pos, ">");
835				if (tmpPos > -1) {
836					// found end character of closing element.
837					currentjson = realClosingTag(currentjson, aXMLString.substr(pos, tmpPos-pos)); 
838				}
839				else {
840					// Did not find end character of closing element. Error.
841					throw -7;
842				}
843				pos = tmpPos + 1;
844			}
845			else {
846				// Check element name
847				if (pos < strLength) {
848					let tmpPos = findCharacter(aXMLString, pos, ">");
849					if (tmpPos > -1) {
850						let elementName = "";
851						//tc = aXMLString[pos];
852						tc = aXMLString.charAt(pos);
853						while ((pos < strLength) && (tc != ">") && 
854							(tc != "/") && (!(isInList(specialChars1,tc)))) {
855							elementName = elementName + tc;
856							pos++;
857							//tc = aXMLString[pos];
858							tc = aXMLString.charAt(pos);
859						}
860						currentjson = realOpeningTag(currentjson, elementName);
861
862						// We have an element name. Let see if it contains data or is closed.
863						if ((pos < strLength) && (tc == "/")) {
864							// It is closed.
865							currentjson = realClosingTag(currentjson, elementName);
866							pos++; pos++;
867						}
868						else {
869							// Element is not closed. Let see if it contains attributes.
870							if ((pos < strLength) && (isInList(specialChars1,tc))) {
871								var attribute = "";
872								pos++;
873								//tc = aXMLString[pos];
874								tc = aXMLString.charAt(pos);
875								var quoteOpen = false;
876								var seenAttributeSeparator = false;
877								var quoteChar = "";
878								while ((pos < strLength) && 
879									(((tc != ">") && (tc != "/")) || (quoteOpen)) ) {
880									attribute = attribute + tc;
881									if ((!seenAttributeSeparator) && (tc == "=") && (!quoteOpen)){seenAttributeSeparator = true;}
882									else {
883										if (seenAttributeSeparator) {
884											if ((tc == '"') || (tc == "'")) {
885												if ((!quoteOpen) || ((quoteOpen) && (quoteChar == tc))) {
886													quoteOpen = !quoteOpen;
887													if (quoteOpen) {quoteChar = tc;}
888												}
889											}
890										}
891									}
892									pos++;
893									//tc = aXMLString[pos];
894									tc = aXMLString.charAt(pos);
895									if ((seenAttributeSeparator) && (pos < strLength) && (isInList(specialChars1,tc)) && (!quoteOpen)) {
896										realSetAttributeStr(currentjson, attribute);
897										attribute = "";
898										seenAttributeSeparator = false;
899										pos++;
900										//tc = aXMLString[pos];
901										tc = aXMLString.charAt(pos);
902									}
903								}
904								if ((seenAttributeSeparator) && (!quoteOpen) && (pos < strLength) && (attribute.length > 0)) {
905									realSetAttributeStr(currentjson, attribute);
906									seenAttributeSeparator = false;
907									attribute = "";
908								}
909								if ((pos < strLength) && (tc == "/")) {
910									// Found opening tag with attributes which is also closed.
911									currentjson = realClosingTag(currentjson, elementName);
912									pos++;
913									//tc = aXMLString[pos];
914									tc = aXMLString.charAt(pos);
915								}
916							
917								if (!((pos < strLength) && (tc == ">"))) {
918									throw -8; // Expected closing character of element field
919								}
920								pos++;
921							}
922							else {
923								if (!((pos < strLength) && (tc == ">"))) {
924									throw -9; // Expected closing character of element field
925								}
926								pos++;
927							}
928						}
929					}
930					else {
931						throw -10; // Expected closing character of element field but did not find it.
932					}
933				}
934				else {
935					throw -11; // No more characters left
936				}
937			}
938		} // While.
939	}
940	catch(err){ dump(" !! Err:"+err+"("+STACK()+"\n");}
941
942	},
943
944	XPath: function _XPath(aParent, aPath){
945		//dump("XPath:"+aPath+"\n");
946		//dump("aParent:"+JSON.stringify(aParent)+"\n");
947		//dump("..\n");
948
949		return this.clone(realXPath(aParent, aPath));
950	},
951
952}
953