source: XmlTools2/trunk/libs/jsxml.js@ 906

Last change on this file since 906 was 906, checked in by s10k, 11 years ago
File size: 20.7 KB
Line 
1//////////////////////////////////////////////////////////
2////////// JSXML XML Tools ////////////
3////////// Ver 1.3 Aug 29 2009 ////////////
4////////// Copyright 2000-2009 Peter Tracey ////////////
5////////// http://levelthreesoltions.com/jsxml/
6////
7//// Objects:
8////
9//// REXML
10//// Regular Expression-based XML parser
11////
12//// JSXMLIterator
13//// Iterates through the tree structure without recursion
14////
15//// JSXMLBuilder
16//// Loads xml into a linear structure and provides
17//// interface for adding and removing elements
18//// and setting attributes, generates XML
19////
20//// Utility functions:
21////
22//// ParseAttribute
23//// Takes string of attibutes and attribute name
24//// Returns attribute value
25////
26//// Array_Remove
27//// Removes element in array
28////
29//// Array_Add
30//// Adds element to array
31////
32//// RepeatChar
33//// Repeats string specified number of times
34////
35///////////////////////////////////////////////////////////////
36
37
38function REXML(XML) {
39 this.XML = XML;
40
41 this.rootElement = null;
42
43 this.parse = REXML_parse;
44 if (this.XML && this.XML !== "") this.parse();
45}
46
47 function REXML_parse() {
48 var reTag = new RegExp("<([^>/ ]*)([^>]*)>","g"); // matches that tag name $1 and attribute string $2
49 var reTagText = new RegExp("<([^>/ ]*)([^>]*)>([^<]*)","g"); // matches tag name $1, attribute string $2, and text $3
50 var strType = "";
51 var strTag = "";
52 var strText = "";
53 var strAttributes = "";
54 var strOpen = "";
55 var strClose = "";
56 var iElements = 0;
57 var xmleLastElement = null;
58 if (this.XML.length === 0) return;
59 var arrElementsUnparsed = this.XML.match(reTag);
60 var arrElementsUnparsedText = this.XML.match(reTagText);
61 var i=0;
62 if (arrElementsUnparsed[0].replace(reTag, "$1") == "?xml") i++;
63
64 for (; i<arrElementsUnparsed.length; i++) {
65 strTag = arrElementsUnparsed[i].replace(reTag,"$1");
66 strAttributes = arrElementsUnparsed[i].replace(reTag,"$2");
67 strText = arrElementsUnparsedText[i].replace(reTagText,"$3").replace(/[\r\n\t ]+/g, " "); // remove white space
68 strClose = "";
69 if (strTag.indexOf("![CDATA[") === 0) {
70 strOpen = "<![CDATA[";
71 strClose = "]]>";
72 strType = "cdata";
73 } else if (strTag.indexOf("!--") === 0) {
74 strOpen = "<!--";
75 strClose = "-->";
76 strType = "comment";
77 } else if (strTag.indexOf("?") === 0) {
78 strOpen = "<?";
79 strClose = "?>";
80 strType = "pi";
81 } else strType = "element";
82 if (strClose !== "") {
83 strText = "";
84 if (arrElementsUnparsedText[i].indexOf(strClose) > -1) strText = arrElementsUnparsedText[i];
85 else {
86 for (; i<arrElementsUnparsed.length && arrElementsUnparsedText[i].indexOf(strClose) == -1; i++) {
87 strText += arrElementsUnparsedText[i];
88 }
89 strText += arrElementsUnparsedText[i];
90 }
91 if (strText.substring(strOpen.length, strText.indexOf(strClose)) !== "") {
92 xmleLastElement.childElements[xmleLastElement.childElements.length] = new REXML_XMLElement(strType, "","",xmleLastElement,strText.substring(strOpen.length, strText.indexOf(strClose)));
93 if (strType == "cdata") xmleLastElement.text += strText.substring(strOpen.length, strText.indexOf(strClose));
94 }
95 if (strText.indexOf(strClose)+ strClose.length < strText.length) {
96 xmleLastElement.childElements[xmleLastElement.childElements.length] = new REXML_XMLElement("text", "","",xmleLastElement,strText.substring(strText.indexOf(strClose)+ strClose.length, strText.length));
97 if (strType == "cdata") xmleLastElement.text += strText.substring(strText.indexOf(strClose)+ strClose.length, strText.length);
98 }
99 continue;
100 }
101 if (strText.replace(/ */, "") === "") strText = "";
102 if (arrElementsUnparsed[i].substring(1,2) != "/") {
103 if (iElements === 0) {
104 xmleLastElement = this.rootElement = new REXML_XMLElement(strType, strTag,strAttributes,null,strText);
105 iElements++;
106 if (strText !== "") xmleLastElement.childElements[xmleLastElement.childElements.length] = new REXML_XMLElement("text", "","",xmleLastElement,strText);
107 } else if (arrElementsUnparsed[i].substring(arrElementsUnparsed[i].length-2,arrElementsUnparsed[i].length-1) != "/") {
108 xmleLastElement = xmleLastElement.childElements[xmleLastElement.childElements.length] = new REXML_XMLElement(strType, strTag,strAttributes,xmleLastElement,"");
109 iElements++;
110 if (strText !== "") {
111 xmleLastElement.text += strText;
112 xmleLastElement.childElements[xmleLastElement.childElements.length] = new REXML_XMLElement("text", "","",xmleLastElement,strText);
113 }
114 } else {
115 xmleLastElement.childElements[xmleLastElement.childElements.length] = new REXML_XMLElement(strType, strTag,strAttributes,xmleLastElement,strText);
116 if (strText !== "") xmleLastElement.childElements[xmleLastElement.childElements.length] = new REXML_XMLElement("text", "","",xmleLastElement,strText);
117 }
118 } else {
119 xmleLastElement = xmleLastElement.parentElement;
120 iElements--;
121 if (xmleLastElement && strText !== "") {
122 xmleLastElement.text += strText;
123 xmleLastElement.childElements[xmleLastElement.childElements.length] = new REXML_XMLElement("text", "","",xmleLastElement,strText);
124 }
125 }
126 }
127 }
128
129 function REXML_XMLElement(strType, strName, strAttributes, xmlParent, strText) {
130 this.type = strType;
131 this.name = strName;
132 this.attributeString = strAttributes;
133 this.attributes = null;
134 this.childElements = [];
135 this.parentElement = xmlParent;
136 this.text = strText; // text of element
137
138 this.getText = REXML_XMLElement_getText; // text of element and child elements
139 this.childElement = REXML_XMLElement_childElement;
140 this.attribute = REXML_XMLElement_attribute;
141 }
142
143 function REXML_XMLElement_getText() {
144 if (this.type == "text" || this.type == "cdata") {
145 return this.text;
146 } else if (this.childElements.length) {
147 var L = "";
148 for (var i=0; i<this.childElements.length; i++) {
149 L += this.childElements[i].getText();
150 }
151 return L;
152 } else return "";
153 }
154
155 function REXML_XMLElement_childElement(strElementName) {
156 for (var i=0; i<this.childElements.length; i++) if (this.childElements[i].name == strElementName) return this.childElements[i];
157 return null;
158 }
159
160 function REXML_XMLElement_attribute(strAttributeName) {
161 if (!this.attributes) {
162 var reAttributes = new RegExp(" ([^= ]*)=","g"); // matches attributes
163 if (this.attributeString.match(reAttributes) && this.attributeString.match(reAttributes).length) {
164 var arrAttributes = this.attributeString.match(reAttributes);
165 if (!arrAttributes.length) arrAttributes = null;
166 else for (var j=0; j<arrAttributes.length; j++) {
167 arrAttributes[j] = new Array(
168 (arrAttributes[j]+"").replace(/[= ]/g,""),
169 ParseAttribute(this.attributeString, (arrAttributes[j]+"").replace(/[= ]/g,""))
170 );
171 }
172 this.attributes = arrAttributes;
173 }
174 }
175 if (this.attributes) for (var i=0; i<this.attributes.length; i++) if (this.attributes[i][0] == strAttributeName) return this.attributes[i][1];
176 return "";
177 }
178
179
180function JSXMLBuilder() {
181 this.XML = "";
182 this.elements = [];
183 Array.prototype.remove = Array_Remove;
184 Array.prototype.add = Array_Add;
185
186 this.load = JSXMLBuilder_load;
187 this.element = JSXMLBuilder_element;
188 this.addElementAt = JSXMLBuilder_addElementAt;
189 this.insertElementAt = JSXMLBuilder_insertElementAt;
190 this.removeElement = JSXMLBuilder_removeElement;
191 this.generateXML = JSXMLBuilder_generateXML;
192 this.moveElement = JSXMLBuilder_moveElement;
193}
194
195 function JSXMLBuilder_load(strXML, xmleElem) {
196 this.XML = strXML;
197
198 if (!xmleElem) {
199 if (strXML.length) xmleElem = (new REXML(strXML)).rootElement;
200 else return false;
201 }
202
203 var xmlBuilder = new JSXMLIterator(xmleElem);
204
205 while (true) {
206 if (xmlBuilder.xmleElem.type == "element") {
207 if (xmlBuilder.xmleElem.attributes) {
208 this.addElementAt(xmlBuilder.xmleElem.name,xmlBuilder.xmleElem.attributes, xmlBuilder.xmleElem.text, this.elements.length, xmlBuilder.iElemLevel);
209 } else {
210 this.addElementAt(xmlBuilder.xmleElem.name,xmlBuilder.xmleElem.attributeString, xmlBuilder.xmleElem.text, this.elements.length, xmlBuilder.iElemLevel);
211 }
212 }
213 if (!xmlBuilder.getNextNode(false)) break;
214 }
215 for (var i=0; i<this.elements.length; i++) this.elements[i].index = i;
216 }
217
218 function JSXMLBuilder_element(iIndex) {
219 return this.elements[iIndex];
220 }
221
222 function JSXMLBuilder_addElementAt(strElement,Attributes,strText,iElemIndex,iElemLevel) {
223 iElemIndex = parseInt(iElemIndex);
224 iElemLevel = parseInt(iElemLevel);
225 if (iElemIndex < 0 || typeof(iElemIndex) != "number" || isNaN(iElemIndex)) iElemIndex = (this.elements.length>0) ? this.elements.length-1 : 0;
226 if (iElemLevel < 0 || typeof(iElemLevel) != "number" || isNaN(iElemLevel)) iElemLevel = this.elements[iElemIndex-1].level;
227 if (!Attributes) Attributes = "";
228 var Elem = [];
229 var iAddIndex = iElemIndex;
230 if (iElemIndex > 0) {
231 for (var i=iElemIndex; i<this.elements.length; i++) if (this.elements[i].level > iElemLevel) iAddIndex++;
232 else if (this.elements[i].level <= this.elements[iElemIndex].level) break;
233 Elem = new JSXMLBuilder_XMLElement(strElement,Attributes,strText,iElemLevel+1,this);
234 } else {
235 Elem = new JSXMLBuilder_XMLElement(strElement,Attributes,strText,1,this);
236 }
237 this.elements = this.elements.add(iAddIndex,Elem);
238 for (var i=iAddIndex; i<this.elements.length; i++) this.elements[i].index = i;
239 }
240
241 function JSXMLBuilder_insertElementAt(strElement,Attributes,strText,iElemIndex,iElemLevel) {
242 iElemIndex = parseInt(iElemIndex);
243 iElemLevel = parseInt(iElemLevel);
244 if (iElemIndex < 0 || typeof(iElemIndex) != "number" || isNaN(iElemIndex)) iElemIndex = (this.elements.length>0) ? this.elements.length-1 : 0;
245 if (iElemLevel < 0 || typeof(iElemLevel) != "number" || isNaN(iElemLevel)) iElemLevel = this.elements[iElemIndex-1].level;
246 if (!Attributes) Attributes = "";
247 var Elem = null;
248 var iAddIndex = iElemIndex;
249 if (iElemIndex > 0 && iElemLevel > 0) {
250 Elem = new JSXMLBuilder_XMLElement(strElement,Attributes,strText,iElemLevel+1,this);
251 } else {
252 Elem = new JSXMLBuilder_XMLElement(strElement,Attributes,strText,1,this);
253 }
254 this.elements = this.elements.add(iAddIndex,Elem);
255 for (var i=iAddIndex; i<this.elements.length; i++) this.elements[i].index = i;
256 }
257
258
259 function JSXMLBuilder_removeElement(iElemIndex) {
260 iElemIndex = parseInt(iElemIndex);
261 for (var iAfterElem=iElemIndex+1; iAfterElem<this.elements.length; iAfterElem++) if (this.elements[iAfterElem].level < this.elements[iElemIndex].level+1) break;
262
263 this.elements = this.elements.slice(0,iElemIndex).concat(this.elements.slice(iAfterElem,this.elements.length));
264 for (var i=iElemIndex; i<this.elements.length; i++) this.elements[i].index = i;
265 }
266
267 function JSXMLBuilder_moveElement(iElem1Index,iElem2Index) {
268 var arrElem1Elements = new Array(this.elements[iElem1Index]);
269 var arrElem2Elements = new Array(this.elements[iElem2Index]);
270 for (var i=iElem1Index; i<this.elements.length; i++) if (this.elements[i].level > this.elements[iElem1Index].level) arrElem1Elements[arrElem1Elements.length] = this.elements[i]; else if (i>iElem1Index) break;
271 for (var i=iElem2Index; i<this.elements.length; i++) if (this.elements[i].level > this.elements[iElem2Index].level) arrElem2Elements[arrElem2Elements.length] = this.elements[i]; else if (i>iElem2Index) break;
272 var arrMovedElements = [];
273 if (iElem1Index < iElem2Index) {
274 for (i=0; i<iElem1Index; i++) arrMovedElements[arrMovedElements.length] = this.elements[i]; // start to the 1st element
275 for (i=iElem1Index+arrElem1Elements.length; i<iElem2Index+arrElem2Elements.length; i++) arrMovedElements[arrMovedElements.length] = this.elements[i]; // end of 1st element to end of 2nd element
276 for (i=0; i<arrElem1Elements.length; i++) arrMovedElements[arrMovedElements.length] = arrElem1Elements[i]; // 1st element and all child elements
277 for (i=iElem2Index+arrElem2Elements.length; i<this.elements.length; i++) arrMovedElements[arrMovedElements.length] = this.elements[i]; // end of 2nd element to end
278 this.elements = arrMovedElements;
279 } else {
280 for (i=0; i<iElem2Index; i++) arrMovedElements[arrMovedElements.length] = this.elements[i]; // start to the 2nd element
281 for (i=0; i<arrElem1Elements.length; i++) arrMovedElements[arrMovedElements.length] = arrElem1Elements[i]; // 1st element and all child elements
282 for (i=iElem2Index; i<iElem1Index; i++) arrMovedElements[arrMovedElements.length] = this.elements[i]; // 2nd element to 1st element
283 for (i=iElem1Index+arrElem1Elements.length; i<this.elements.length; i++) arrMovedElements[arrMovedElements.length] = this.elements[i]; // end of 1st element to end
284 this.elements = arrMovedElements;
285 }
286 for (var i=0; i<this.elements.length; i++) this.elements[i].index = i;
287 }
288
289
290 function JSXMLBuilder_generateXML(bXMLTag) {
291 var strXML = "";
292 var arrXML = [];
293 if (bXMLTag) strXML += '<?xml version="1.0"?>\n\n';
294 for (var i=0; i<this.elements.length; i++) {
295 strXML += RepeatChar("\t",this.elements[i].level-1);
296 strXML += "<" + this.element(i).name; // open tag
297 if (this.element(i).attributes) {
298 for (var j=0; j<this.element(i).attributes.length; j++) { // set attributes
299 if (this.element(i).attributes[j]) {
300 strXML += ' ' + this.element(i).attributes[j][0] + '="' + this.element(i).attributes[j][1] + '"';
301 }
302 }
303 } else strXML += this.element(i).attributeString.replace(/[\/>]$/gi, "");
304 if (((this.elements[i+1] && this.elements[i+1].level <= this.elements[i].level) || // next element is a lower or equal to
305 (!this.elements[i+1] && this.elements[i-1])) // no next element, previous element
306 && this.element(i).text === "") {
307 strXML += "/";
308 }
309 strXML += ">";
310 if (this.element(i).text !== "") strXML += this.element(i).text;
311 else strXML += "\n";
312 if (((this.elements[i+1] && this.elements[i+1].level <= this.elements[i].level) || // next element is a lower or equal to
313 (!this.elements[i+1] && this.elements[i-1])) // no next element, previous element
314 && this.element(i).text !== "") strXML += "</" + this.element(i).name + ">\n";
315 if (!this.elements[i+1]) {
316 var lastelem = i;
317 for (var j=i; j>-1; j--) {
318 if (this.elements[j].level >= this.elements[i].level) continue;
319 else {
320 if (this.elements[j].level < this.elements[lastelem].level) {
321 strXML += RepeatChar("\t",this.elements[j].level-1) + "</" + this.element(j).name + ">\n";
322 lastelem = j;
323 }
324 }
325 }
326 } else {
327 if (this.elements[i+1].level < this.elements[i].level) {
328 var lastelem = i;
329 for (var j=i; this.elements[j].level>=this.elements[i+1].level; j--) {
330 if (this.elements[i] && this.elements[j] && this.elements[j].level < this.elements[i].level && this.elements[j].level < this.elements[lastelem].level) {
331 strXML += RepeatChar("\t",this.elements[j].level-1) + "</" + this.element(j).name + ">\n";
332 lastelem = j;
333 }
334 }
335 }
336 }
337 if (strXML.length > 1000) {
338 arrXML[arrXML.length] = strXML;
339 strXML = "";
340 }
341 }
342 arrXML[arrXML.length] = strXML;
343 return arrXML.join("");
344 }
345
346 function JSXMLBuilder_XMLElement(strName,Attributes,strText,iLevel,xmlBuilder) {
347 this.type = "element";
348 this.name = strName;
349 this.attributes = (typeof(Attributes) != "string") ? Attributes : null;
350 this.attributeString = (typeof(Attributes) == "string") ? Attributes : "";
351 this.text = strText;
352 this.level = iLevel;
353 this.index = -1;
354 this.xmlBuilder = xmlBuilder;
355
356 this.parseAttributes = JSXMLBuilder_XMLElement_parseAttributes;
357 this.attribute = JSXMLBuilder_XMLElement_attribute;
358 this.setAttribute = JSXMLBuilder_XMLElement_setAttribute;
359 this.removeAttribute = JSXMLBuilder_XMLElement_removeAttribute;
360 this.parentElement = JSXMLBuilder_XMLElement_parentElement;
361 this.childElement = JSXMLBuilder_XMLElement_childElement;
362 }
363
364 function JSXMLBuilder_XMLElement_parseAttributes() {
365 if (!this.attributes) {
366 var reAttributes = new RegExp(" ([^= ]*)=","g"); // matches attributes
367 if (this.attributeString.match(reAttributes) && this.attributeString.match(reAttributes).length) {
368 var arrAttributes = this.attributeString.match(reAttributes);
369 if (!arrAttributes.length) arrAttributes = null;
370 else for (var j=0; j<arrAttributes.length; j++) {
371 arrAttributes[j] = new Array(
372 (arrAttributes[j]+"").replace(/[= ]/g,""),
373 ParseAttribute(this.attributeString, (arrAttributes[j]+"").replace(/[= ]/g,""))
374 );
375 }
376 this.attributes = arrAttributes;
377 }
378 }
379 }
380
381 function JSXMLBuilder_XMLElement_attribute(AttributeName) {
382 if (!this.attributes) this.parseAttributes();
383 if (this.attributes) for (var i=0; i<this.attributes.length; i++) if (this.attributes[i][0] == AttributeName) return this.attributes[i][1];
384 return "";
385 }
386
387 function JSXMLBuilder_XMLElement_setAttribute(AttributeName,Value) {
388 if (!this.attributes) this.parseAttributes();
389 if (this.attributes) for (var i=0; i<this.attributes.length; i++) if (this.attributes[i][0] == AttributeName) {
390 this.attributes[i][1] = Value;
391 return;
392 }
393 this.attributes[this.attributes.length] = new Array(AttributeName,Value);
394 }
395
396 function JSXMLBuilder_XMLElement_removeAttribute(AttributeName,Value) {
397 if (!this.attributes) this.parseAttributes();
398 if (this.attributes) for (var i=0; i<this.attributes.length; i++) if (this.attributes[i][0] == AttributeName) {
399 this.attributes = this.attributes.remove(i);
400 return;
401 }
402 }
403
404 function JSXMLBuilder_XMLElement_parentElement() {
405 for (var i=this.index; this.xmlBuilder.element(i) && this.xmlBuilder.element(i).level != this.level-1; i--);
406 return this.xmlBuilder.element(i);
407 }
408
409 function JSXMLBuilder_XMLElement_childElement(Child) {
410 var iFind = -1;
411 for (var i=this.index+1; i<this.xmlBuilder.elements.length; i++) {
412 if (this.xmlBuilder.elements[i].level == this.level+1) {
413 iFind++;
414 if (iFind == Child || this.xmlBuilder.elements[i].name == Child) return this.xmlBuilder.elements[i];
415 } else if (this.xmlBuilder.elements[i].level <= this.level) break;
416 }
417 return null;
418 }
419
420
421function JSXMLIterator(xmleElem) {
422 this.xmleElem = xmleElem;
423
424 this.iElemIndex = 0;
425 this.arrElemIndex = new Array(0);
426 this.iElemLevel = 0;
427 this.iElem = 0;
428 this.arrElemIndex[this.iElemLevel] = -1;
429
430 this.getNextNode = JSXMLIterator_getNextNode;
431}
432
433 function JSXMLIterator_getNextNode() {
434 if (!this.xmleElem || this.iElemLevel<0) return false;
435 if (this.xmleElem.childElements.length) { // move up
436 this.arrElemIndex[this.iElemLevel]++;
437 this.iElemIndex++;
438 this.iElemLevel++;
439 this.arrElemIndex[this.iElemLevel] = 0;
440 this.xmleElem = this.xmleElem.childElements[0];
441 } else { // move next
442 this.iElemIndex++;
443 this.arrElemIndex[this.iElemLevel]++;
444 if (this.xmleElem.parentElement && this.xmleElem.parentElement.childElements.length && this.arrElemIndex[this.iElemLevel] < this.xmleElem.parentElement.childElements.length) this.xmleElem = this.xmleElem.parentElement.childElements[this.arrElemIndex[this.iElemLevel]];
445 else {
446 if (this.iElemLevel>0) { // move down
447 for (; this.iElemLevel > 0; this.iElemLevel--) {
448 if (this.xmleElem.parentElement && this.xmleElem.parentElement.childElements[this.arrElemIndex[this.iElemLevel]]) {
449 this.xmleElem = this.xmleElem.parentElement.childElements[this.arrElemIndex[this.iElemLevel]];
450 this.iElemLevel++;
451 this.arrElemIndex = this.arrElemIndex.slice(0,this.iElemLevel+1);
452 break;
453 } else {
454 this.xmleElem = this.xmleElem.parentElement;
455 }
456 }
457 this.iElemLevel--;
458 } else {
459 return false;
460 }
461 }
462 }
463 return (typeof(this.xmleElem) == "object" && this.iElemLevel > -1);
464 }
465
466function ParseAttribute(str,Attribute) {
467 var str = str + ">";
468 if (str.indexOf(Attribute + "='")>-1) var Attr = new RegExp(".*" + Attribute + "='([^']*)'.*>");
469 else if (str.indexOf(Attribute + '="')>-1) var Attr = new RegExp(".*" + Attribute + '="([^"]*)".*>');
470 return str.replace(Attr, "$1");
471}
472
473function Array_Remove(c) {
474 var tmparr = [];
475 for (var i=0; i<this.length; i++) if (i!=c) tmparr[tmparr.length] = this[i];
476 return tmparr;
477}
478
479function Array_Add(c, cont) {
480 if (c == this.length) {
481 this[this.length] = cont;
482 return this;
483 }
484 var tmparr = [];
485 for (var i=0; i<this.length; i++) {
486 if (i==c) tmparr[tmparr.length] = cont;
487 tmparr[tmparr.length] = this[i];
488 }
489 if (!tmparr[c]) tmparr[c] = cont;
490 return tmparr;
491}
492
493function RepeatChar(sChar,iNum) {
494 var L = "";
495 for (var i=0; i<iNum; i++) L += sChar;
496 return L;
497}
Note: See TracBrowser for help on using the repository browser.