1 |
// -------------------------------------------------------------------------- |
2 |
/// IWF - Interactive Website Framework. Javascript library for creating |
3 |
/// responsive thin client interfaces. |
4 |
/// |
5 |
/// Copyright (C) 2005 Brock Weaver brockweaver@users.sourceforge.net |
6 |
/// |
7 |
/// This library is free software; you can redistribute it and/or modify |
8 |
/// it under the terms of the GNU Lesser General Public License as published |
9 |
/// by the Free Software Foundation; either version 2.1 of the License, or |
10 |
/// (at your option) any later version. |
11 |
/// |
12 |
/// This library is distributed in the hope that it will be useful, but |
13 |
/// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
14 |
/// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
15 |
/// License for more details. |
16 |
/// |
17 |
/// You should have received a copy of the GNU Lesser General Public License |
18 |
/// along with this library; if not, write to the Free Software Foundation, |
19 |
/// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 |
/// |
21 |
/// Brock Weaver |
22 |
/// brockweaver@users.sourceforge.net |
23 |
/// 1605 NW Maple Pl |
24 |
/// Ankeny, IA 50021 |
25 |
/// |
26 |
//! http://iwf.sourceforge.net/ |
27 |
// -------------------------------------------------------------------------- |
28 |
//! NOTE: To minimize file size, strip all fluffy comments (except the LGPL, of course!) |
29 |
//! using the following regex (global flag and multiline on): |
30 |
//! ^\t*//([^/!].*|$) |
31 |
// |
32 |
// This reduces file size by about 30%, give or take. |
33 |
// |
34 |
//! To rip out only logging statements (commented or uncommented): |
35 |
//! ^/{0,2}iwfLog.* |
36 |
// -------------------------------------------------------------------------- |
37 |
|
38 |
// -------------------------------------------------------------------------- |
39 |
//! iwfxml.js |
40 |
// |
41 |
// Javascript-based xml parser |
42 |
// |
43 |
//! Dependencies: |
44 |
//! iwfcore.js |
45 |
// |
46 |
//! Brock Weaver - brockweaver@users.sourceforge.net |
47 |
//! v 0.2 - 2005-11-14 |
48 |
//! core bug patch |
49 |
// -------------------------------------------------------------------------- |
50 |
//! Brock Weaver - brockweaver@users.sourceforge.net |
51 |
//! v 0.1 - 2005-06-05 |
52 |
//! Initial release. |
53 |
// -------------------------------------------------------------------------- |
54 |
// This class is meant to ease the burden of parsing xml. |
55 |
// Xml is parsed into sets of arrays, in a hierarchical format. |
56 |
// Each node is an array (of length 1, if applicable) |
57 |
// The root node is an exception, as it is simply a node and not an array, |
58 |
// as there is always only a single root node. |
59 |
// Each attribute is a property of the current element in the node array. |
60 |
// Useful for loading xml from server into a nice, easy-to-use format |
61 |
// -------------------------------------------------------------------------- |
62 |
|
63 |
if (!window.iwfGetById){ |
64 |
iwfLog("IWF Dependency Error: iwfxml.js is dependent upon iwfcore.js, so you *must* reference that file first.\n\nExample:\n\n<script type='text/javascript' src='iwfcore.js'></script>\n<script type='text/javascript' src='iwfxml.js'></script>", true); |
65 |
} |
66 |
|
67 |
|
68 |
// ----------------------------------- |
69 |
// Begin: Xml Document Object |
70 |
// ----------------------------------- |
71 |
|
72 |
function iwfXmlDoc(xml){ |
73 |
if (!xml || xml.length == 0){ |
74 |
return; |
75 |
} |
76 |
this.loadXml(xml); |
77 |
} |
78 |
|
79 |
iwfXmlDoc.prototype.loadXml = function(xml){ |
80 |
|
81 |
// note this root node is not an array, as there will always |
82 |
// be exactly 1 root. |
83 |
var node = new iwfXmlNode(xml); |
84 |
this.childNodes = new Array(); |
85 |
this.childNodes.push(node); |
86 |
this[node.nodeName] = node; |
87 |
|
88 |
this._html = false; |
89 |
|
90 |
} |
91 |
|
92 |
iwfXmlDoc.prototype.toString = function(pretty){ |
93 |
this._html = false; |
94 |
return this.outerXml(pretty); |
95 |
} |
96 |
|
97 |
iwfXmlDoc.prototype.outerXml = function(pretty){ |
98 |
if (!this.childNodes || this.childNodes.length < 1){ |
99 |
return null; |
100 |
} else { |
101 |
var writer = new iwfWriter(true, pretty, this._html); |
102 |
return this.childNodes[0].outerXml(writer); |
103 |
} |
104 |
} |
105 |
|
106 |
// ----------------------------------- |
107 |
// End: Xml Document Object |
108 |
// ----------------------------------- |
109 |
|
110 |
|
111 |
// ----------------------------------- |
112 |
// Begin: Xml Node Object |
113 |
// ----------------------------------- |
114 |
|
115 |
|
116 |
// -------------------------------------------------------------------------- |
117 |
//! iwfXmlNode |
118 |
// |
119 |
//! Brock Weaver - brockweaver@users.sourceforge.net |
120 |
//! v 0.1 - 2005-06-05 |
121 |
//! Initial release. |
122 |
// -------------------------------------------------------------------------- |
123 |
// This class is used to represent a single xml node. |
124 |
// All xml is kept except processing instructions. |
125 |
// There are issues with "<" and ">" chars embedded within |
126 |
// the xml as text. This is technically not valid xml, so |
127 |
// it is technically not a problem I guess. |
128 |
|
129 |
function iwfXmlNode(xml, parent){ |
130 |
|
131 |
this.childNodes = new Array(); |
132 |
this._text = ''; |
133 |
this.attributes = new Array(); |
134 |
this.attributeNames = new Array(); |
135 |
this.attributeQuotes = new Array(); |
136 |
this.nodeName = 'UNKNOWN'; |
137 |
this._parent = parent; |
138 |
this._isEmpty = false; |
139 |
|
140 |
|
141 |
if (!xml || xml.length == 0){ |
142 |
return; |
143 |
} else { |
144 |
this._parseXml(xml); |
145 |
} |
146 |
|
147 |
this._htmlMode = false; |
148 |
|
149 |
|
150 |
} |
151 |
|
152 |
iwfXmlNode.prototype.toString = function(innerOnly){ |
153 |
if (innerOnly){ |
154 |
return this.innerXml(); |
155 |
} else { |
156 |
return this.outerXml(); |
157 |
} |
158 |
} |
159 |
|
160 |
iwfXmlNode.prototype.outerXml = function(writer){ |
161 |
return this._serialize(writer, false); |
162 |
} |
163 |
|
164 |
iwfXmlNode.prototype.innerXml = function(writer){ |
165 |
return this._serialize(writer, true) |
166 |
} |
167 |
|
168 |
iwfXmlNode.prototype.outerHtml = function() { |
169 |
this._htmlMode = true; |
170 |
var ret = this.outerXml(); |
171 |
this._htmlMode = false; |
172 |
return ret; |
173 |
} |
174 |
|
175 |
iwfXmlNode.prototype.innerHtml = function() { |
176 |
this._htmlMode = true; |
177 |
var ret = this.innerXml(); |
178 |
this._htmlMode = false; |
179 |
return ret; |
180 |
} |
181 |
|
182 |
iwfXmlNode.prototype._serialize = function(writer, innerOnly){ |
183 |
var pretty = false; |
184 |
if (typeof(writer) == 'boolean'){ |
185 |
pretty = writer; |
186 |
writer = false; |
187 |
} |
188 |
if (!writer){ |
189 |
writer = new iwfWriter(true, pretty, this._htmlMode); |
190 |
} |
191 |
|
192 |
if (!innerOnly){ |
193 |
writer.writeNodeOpen(this.nodeName); |
194 |
for(var i=0;i<this.attributes.length;i++){ |
195 |
writer.writeAttribute(this.attributeNames[i], this.attributes[i], this.attributeQuotes[i]); |
196 |
} |
197 |
} |
198 |
|
199 |
writer._writeRawText(this._text); |
200 |
for(var i=0;i<this.childNodes.length;i++){ |
201 |
this.childNodes[i].outerXml(writer); |
202 |
} |
203 |
|
204 |
if (!innerOnly){ |
205 |
writer.writeNodeClose(); |
206 |
} |
207 |
|
208 |
return writer.toString(); |
209 |
} |
210 |
|
211 |
iwfXmlNode.prototype.removeAttribute = function(name){ |
212 |
for(var i=0;i<this.attributeNames.length;i++){ |
213 |
if (this.attributeNames[i] == name){ |
214 |
|
215 |
// found the attribute. splice it out of all the arrays. |
216 |
this.attributeQuotes.splice(j, 1); |
217 |
this.attributeNames.splice(j, 1); |
218 |
this.attributes.splice(j, 1); |
219 |
// remove from our object and jump out of the loop |
220 |
delete this[name]; |
221 |
break; |
222 |
|
223 |
|
224 |
// // found the attribute. move all ones following it down one |
225 |
// for(var j=i;j<this.attributeNames.length-1;j++){ |
226 |
// this.attributeQuotes[j] = this.attributeQuotes[j+1]; |
227 |
// this.attributeNames[j] = this.attributeNames[j+1]; |
228 |
// this.attributes[j] = this.attributes[j+1]; |
229 |
// } |
230 |
// // pop off the last one, as it's now a duplicate |
231 |
// this.attributeQuotes.pop(); |
232 |
// this.attributeNames.pop(); |
233 |
// this.attributes.pop(); |
234 |
// delete this[name]; |
235 |
// break; |
236 |
} |
237 |
} |
238 |
} |
239 |
|
240 |
iwfXmlNode.prototype.removeChildNode = function(node){ |
241 |
for(var i=0;i<this.childNodes.length;i++){ |
242 |
if (node == this.childNodes[i]){ |
243 |
// remove from childNodes array |
244 |
this.childNodes.splice(k, 1); |
245 |
var arr = this[node.nodeName]; |
246 |
if (arr){ |
247 |
if (arr.length > 1){ |
248 |
var j = 0; |
249 |
for(j=0;i<arr.length;i++){ |
250 |
if (arr[j] == node){ |
251 |
arr.splice(j, 1); |
252 |
break; |
253 |
} |
254 |
} |
255 |
} else { |
256 |
delete arr; |
257 |
} |
258 |
} |
259 |
break; |
260 |
} |
261 |
} |
262 |
} |
263 |
|
264 |
iwfXmlNode.prototype.addAttribute = function(name, val, quotesToUse){ |
265 |
if (!quotesToUse){ |
266 |
// assume apostrophes |
267 |
quotesToUse = "'"; |
268 |
} |
269 |
if (!this[name]){ |
270 |
this.attributeQuotes.push(quotesToUse); |
271 |
this.attributeNames.push(name); |
272 |
this.attributes.push(val); |
273 |
} |
274 |
this[name] = val; |
275 |
} |
276 |
|
277 |
iwfXmlNode.prototype.addChildNode = function(node, nodeName){ |
278 |
if (nodeName && nodeName.length > 0 && nodeName.indexOf("#") == 0){ |
279 |
var txt = node; |
280 |
node = new iwfXmlNode('', this); |
281 |
node.nodeName = nodeName; |
282 |
node._text = txt; |
283 |
|
284 |
|
285 |
// push it onto the childNodes array |
286 |
this.childNodes.push(node); |
287 |
|
288 |
} else { |
289 |
|
290 |
// make a node object out of it if they gave us a string... |
291 |
if (typeof(node) == 'string'){ |
292 |
node = new iwfXmlNode(node, this); |
293 |
} |
294 |
|
295 |
if (node.nodeName.indexOf("#") == 0){ |
296 |
// is a special node -- duplicate names may exist... |
297 |
node._text = txt; |
298 |
|
299 |
// push it onto the childNodes array |
300 |
this.childNodes.push(node); |
301 |
|
302 |
} else { |
303 |
|
304 |
if (!this.childNodes[node.nodeName]){ |
305 |
// no other child node with this name exists yet. |
306 |
|
307 |
// make a new array off of our object |
308 |
this[node.nodeName] = new Array(); |
309 |
} |
310 |
|
311 |
// push it onto our object's array |
312 |
this[node.nodeName].push(node); |
313 |
|
314 |
// push it onto the childNodes array |
315 |
this.childNodes.push(node); |
316 |
} |
317 |
} |
318 |
} |
319 |
|
320 |
iwfXmlNode.prototype.getText = function(){ |
321 |
var txt = ''; |
322 |
if (this.nodeName.indexOf('#') == 0){ |
323 |
txt = this._text; |
324 |
} |
325 |
for(var i=0;i<this.childNodes.length;i++){ |
326 |
txt += this.childNodes[i].getText(); |
327 |
} |
328 |
return txt; |
329 |
} |
330 |
|
331 |
iwfXmlNode.prototype._parseXml = function(xml){ |
332 |
|
333 |
var remainingXml = xml; |
334 |
|
335 |
while(remainingXml.length > 0){ |
336 |
var type = this._determineNodeType(remainingXml); |
337 |
switch(type){ |
338 |
case 'open': |
339 |
remainingXml = this._parseName(remainingXml); |
340 |
remainingXml = this._parseAtts(remainingXml); |
341 |
if (this._parent){ |
342 |
this._parent.addChildNode(this, null); |
343 |
} |
344 |
|
345 |
if (!this._isEmpty){ |
346 |
remainingXml = this._parseChildren(remainingXml); |
347 |
// we still need to parse out the close node, so don't jump out yet! |
348 |
} else { |
349 |
return remainingXml; |
350 |
} |
351 |
break; |
352 |
|
353 |
case 'text': |
354 |
return this._parseText(remainingXml); |
355 |
|
356 |
case 'close': |
357 |
return this._parseClosing(remainingXml); |
358 |
|
359 |
case 'end': |
360 |
return ''; |
361 |
|
362 |
case 'pi': |
363 |
// return this._parsePI(remainingXml); |
364 |
remainingXml = this._parsePI(remainingXml); |
365 |
break; |
366 |
|
367 |
case 'comment': |
368 |
return this._parseComment(remainingXml); |
369 |
|
370 |
case 'cdata': |
371 |
return this._parseCDATA(remainingXml); |
372 |
|
373 |
case 'whitespace': |
374 |
remainingXml = this._parseWhitespace(remainingXml); |
375 |
if (this._parent){ |
376 |
return remainingXml; |
377 |
} |
378 |
break; |
379 |
|
380 |
default: |
381 |
iwfLog('IWF Xml Parsing Error: undefined type of ' + type + ' returned for xml starting with "' + remainingXml + '"', true); |
382 |
break; |
383 |
} |
384 |
|
385 |
} |
386 |
|
387 |
} |
388 |
|
389 |
iwfXmlNode.prototype._determineNodeType = function(xml){ |
390 |
|
391 |
|
392 |
if (!xml || xml.length == 0){ |
393 |
return 'end'; |
394 |
} |
395 |
|
396 |
var trimmed = this.ltrim(xml); |
397 |
|
398 |
var firstTrimmedLt = trimmed.indexOf("<"); |
399 |
|
400 |
switch(firstTrimmedLt){ |
401 |
case -1: |
402 |
// this is either insignificant whitespace or text |
403 |
if (trimmed.length == 0){ |
404 |
return 'whitespace'; |
405 |
} else { |
406 |
return 'text'; |
407 |
} |
408 |
case 0: |
409 |
// this is either an open node or insignificant whitespace. |
410 |
var firstLt = xml.indexOf("<"); |
411 |
if (firstLt > 0){ |
412 |
return 'whitespace' |
413 |
} else { |
414 |
switch(trimmed.charAt(1)){ |
415 |
case '?': |
416 |
return 'pi'; |
417 |
case '!': |
418 |
if (trimmed.substr(0,4) == '<!--'){ |
419 |
return 'comment'; |
420 |
} else if (trimmed.substr(0,9) == '<![CDATA[') { |
421 |
return 'cdata'; |
422 |
} else { |
423 |
return 'unknown: ' + trimmed.substr(0,10); |
424 |
} |
425 |
case '/': |
426 |
return 'close'; |
427 |
default: |
428 |
return 'open'; |
429 |
} |
430 |
} |
431 |
|
432 |
default: |
433 |
// this is a text node |
434 |
return 'text'; |
435 |
} |
436 |
} |
437 |
|
438 |
iwfXmlNode.prototype._parseName = function(xml){ |
439 |
// we know xml starts with <. |
440 |
|
441 |
var firstSpace = xml.indexOf(" "); |
442 |
var firstApos = xml.indexOf("'"); |
443 |
var firstQuote = xml.indexOf('"'); |
444 |
var firstGt = xml.indexOf(">"); |
445 |
|
446 |
if (firstGt == -1){ |
447 |
iwfLog("IWF Xml Parsing Error: Bad xml; no > found for an open node.", true); |
448 |
return ''; |
449 |
} |
450 |
|
451 |
// see if it's an empty node... |
452 |
if (xml.charAt(firstGt-1) == '/'){ |
453 |
this._isEmpty = true; |
454 |
} |
455 |
|
456 |
// see if there is a possibility that atts exist |
457 |
if (firstSpace > firstGt || firstSpace == -1){ |
458 |
if (this._isEmpty){ |
459 |
// <h1/> |
460 |
this.nodeName = xml.substr(1,firstGt-2); |
461 |
} else { |
462 |
// <h1> |
463 |
this.nodeName = xml.substr(1,firstGt-1); |
464 |
} |
465 |
// return starting at > so parseAtts knows there are no atts. |
466 |
return xml.substr(firstGt); |
467 |
} else { |
468 |
// <h1 > |
469 |
// <h1 att='val'> |
470 |
// <h1 att='val'/> |
471 |
this.nodeName = xml.substr(1,firstSpace-1); |
472 |
|
473 |
|
474 |
// eat everything up to the space, return the rest |
475 |
return xml.substr(firstSpace); |
476 |
} |
477 |
|
478 |
} |
479 |
|
480 |
iwfXmlNode.prototype._parseAtts = function(xml){ |
481 |
|
482 |
|
483 |
xml = this.ltrim(xml); |
484 |
var firstGt = xml.indexOf(">"); |
485 |
if (firstGt == -1){ |
486 |
iwfLog("IWF Xml Parsing Error: Bad xml; no > found when parsing atts for " + this.nodeName, true); |
487 |
return ''; |
488 |
} else if (firstGt == 0){ |
489 |
// no atts. |
490 |
return xml.substr(firstGt+1); |
491 |
} else { |
492 |
// at least one att exists. |
493 |
var attxml = xml.substr(0, firstGt); |
494 |
var re = /\s*?([^=]*?)=(['"])(.*?)(\2)/; |
495 |
var matches= re.exec(attxml) |
496 |
while(matches){ |
497 |
attxml = this.ltrim(attxml.substr(matches[0].length)); |
498 |
var attname = this.ltrim(matches[1]); |
499 |
var attval = matches[3]; |
500 |
var quotesToUse = matches[2]; |
501 |
this.addAttribute(attname, attval, quotesToUse); |
502 |
|
503 |
re = /\s*?([^=]*?)=(['"])(.*?)(\2)/; |
504 |
matches = re.exec(attxml); |
505 |
} |
506 |
|
507 |
|
508 |
// return everything after the end of the att list and the > which closes the start of the node. |
509 |
return xml.substr(firstGt+1); |
510 |
} |
511 |
} |
512 |
|
513 |
iwfXmlNode.prototype._parseChildren = function(xml){ |
514 |
// we are at the > which closes the open node. |
515 |
|
516 |
if (xml && xml.length > 0){ |
517 |
var endnode = "</" + this.nodeName + ">"; |
518 |
while (xml && xml.length > 0 && xml.indexOf(endnode) > 0){ |
519 |
|
520 |
// do not pass the xml to the constructor, as that may cause an infinite loop. |
521 |
var childNode = new iwfXmlNode('', this); |
522 |
xml = childNode._parseXml(xml); |
523 |
} |
524 |
|
525 |
// note we don't cut off the close node here, as the _parseXml function will do this for us. |
526 |
|
527 |
} |
528 |
|
529 |
return xml; |
530 |
} |
531 |
|
532 |
|
533 |
|
534 |
iwfXmlNode.prototype._parseClosing = function(xml){ |
535 |
var firstGt = xml.indexOf("</"); |
536 |
if (firstGt == -1){ |
537 |
iwfLog('IWF Xml Parsing Error: Bad xml; no </' + this.nodeName + ' found', true); |
538 |
return ''; |
539 |
} else { |
540 |
var firstLt = xml.indexOf(">",firstGt); |
541 |
if (firstLt == -1){ |
542 |
iwfLog('IWF Xml Parsing Error: Bad xml; no > found after </' + this.nodeName, true); |
543 |
return ''; |
544 |
} else { |
545 |
var result = xml.substr(firstLt+1); |
546 |
return result; |
547 |
} |
548 |
} |
549 |
} |
550 |
|
551 |
iwfXmlNode.prototype._parseText = function(xml){ |
552 |
return this._parsePoundNode(xml, "#text", "", "<", false); |
553 |
} |
554 |
|
555 |
iwfXmlNode.prototype._parsePI = function(xml){ |
556 |
var result = this._eatXml(xml, "?>", true); |
557 |
return result; |
558 |
} |
559 |
|
560 |
iwfXmlNode.prototype._parseComment = function(xml){ |
561 |
return this._parsePoundNode(xml, "#comment", "<!--", "-->", true); |
562 |
} |
563 |
|
564 |
iwfXmlNode.prototype._parseCDATA = function(xml){ |
565 |
return this._parsePoundNode(xml, "#cdata", "<![CDATA[", "]]>", true); |
566 |
} |
567 |
|
568 |
iwfXmlNode.prototype._parseWhitespace = function(xml){ |
569 |
var result = ''; |
570 |
if (this._parent && this._parent.nodeName.toLowerCase() == 'pre'){ |
571 |
// hack for supporting HTML's <pre> node. ugly, I know :) |
572 |
result = this._parsePoundNode(xml, "#text", "", "<", false); |
573 |
} else { |
574 |
// whitespace is insignificant otherwise |
575 |
result = this._eatXml(xml, "<", false); |
576 |
} |
577 |
return result; |
578 |
} |
579 |
|
580 |
iwfXmlNode.prototype._parsePoundNode = function(xml, nodeName, beginMoniker, endMoniker, eatMoniker){ |
581 |
// simply slurp everything up until the first endMoniker, starting after the beginMoniker |
582 |
var end = xml.indexOf(endMoniker); |
583 |
if (end == -1){ |
584 |
iwfLog("IWF Xml Parsing Error: Bad xml: " + nodeName + " does not have ending " + endMoniker, true); |
585 |
return ''; |
586 |
} else { |
587 |
var len = beginMoniker.length; |
588 |
var s = xml.substr(len, end - len); |
589 |
if (this._parent){ |
590 |
this._parent.addChildNode(s, nodeName); |
591 |
} |
592 |
var result = xml.substr(end + (eatMoniker ? endMoniker.length : 0)); |
593 |
return result; |
594 |
} |
595 |
} |
596 |
|
597 |
iwfXmlNode.prototype._eatXml = function(xml, moniker, eatMoniker){ |
598 |
var pos = xml.indexOf(moniker); |
599 |
|
600 |
if (eatMoniker){ |
601 |
pos += moniker.length; |
602 |
} |
603 |
|
604 |
return xml.substr(pos); |
605 |
|
606 |
} |
607 |
|
608 |
iwfXmlNode.prototype.trim = function(s){ |
609 |
return s.replace(/^\s*|\s*$/g,""); |
610 |
} |
611 |
|
612 |
iwfXmlNode.prototype.ltrim = function(s){ |
613 |
return s.replace(/^\s*/g,""); |
614 |
} |
615 |
|
616 |
iwfXmlNode.prototype.rtrim = function(s){ |
617 |
return s.replace(/\s*$/g,""); |
618 |
} |
619 |
|
620 |
// ----------------------------------- |
621 |
// End: Xml Node Object |
622 |
// ----------------------------------- |
623 |
|
624 |
|
625 |
|
626 |
|
627 |
|
628 |
|
629 |
|
630 |
// ----------------------------------- |
631 |
// Begin: Xml Writer Object |
632 |
// ----------------------------------- |
633 |
|
634 |
|
635 |
// -------------------------------------------------------------------------- |
636 |
//! iwfWriter |
637 |
// |
638 |
//! Brock Weaver - brockweaver@users.sourceforge.net |
639 |
// |
640 |
//! v 0.1 - 2005-06-05 |
641 |
//! Initial release. |
642 |
// -------------------------------------------------------------------------- |
643 |
// This class is meant to ease the creation of xml strings. |
644 |
// Note it is not a fully-compliant xml generator. |
645 |
// -------------------------------------------------------------------------- |
646 |
|
647 |
|
648 |
function iwfWriter(suppressProcessingInstruction, prettyPrint, htmlMode) { |
649 |
this._buffer = new String(); |
650 |
if (!suppressProcessingInstruction){ |
651 |
this.writeRaw("<?xml version='1.0' ?>"); |
652 |
} |
653 |
this._nodeStack = new Array(); |
654 |
this._nodeOpened = false; |
655 |
this._prettyPrint = prettyPrint; |
656 |
this._htmlMode = htmlMode |
657 |
} |
658 |
|
659 |
iwfWriter.prototype.writePretty = function(lfBeforeTabbing){ |
660 |
if (this._prettyPrint){ |
661 |
if (lfBeforeTabbing){ |
662 |
this.writeRaw('\n'); |
663 |
} |
664 |
|
665 |
// assumption is most xml won't have a maximum node depth exceeding 30... |
666 |
var tabs = '\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t'; |
667 |
while (tabs.length < this._nodeStack.length){ |
668 |
// but some xml might exceed node depth of 30, so keep tacking on another 30 tabs until we have enough... |
669 |
// I know this is an awkward way of doing it, but I wanted to avoid looping and concatenating for "most" xml... |
670 |
tabs += tabs; |
671 |
} |
672 |
this.writeRaw(tabs.substr(0,this._nodeStack.length)); |
673 |
} |
674 |
} |
675 |
|
676 |
iwfWriter.prototype.writeNode = function(name, txt){ |
677 |
this.writeNodeOpen(name); |
678 |
this.writeText(txt); |
679 |
this.writeNodeClose(); |
680 |
} |
681 |
|
682 |
iwfWriter.prototype.writeNodeOpen = function(name){ |
683 |
if (this._nodeOpened){ |
684 |
this.writeRaw(">"); |
685 |
} |
686 |
this._nodeOpened = false; |
687 |
|
688 |
this.writePretty(true); |
689 |
|
690 |
switch(name){ |
691 |
case '#pi': |
692 |
this.writeRaw('<?'); |
693 |
break; |
694 |
case '#text': |
695 |
// do nothing |
696 |
break; |
697 |
case '#cdata': |
698 |
this.writeRaw('<![CDATA['); |
699 |
break; |
700 |
case '#comment': |
701 |
this.writeRaw('<!--'); |
702 |
break; |
703 |
default: |
704 |
this.writeRaw("<" + name); |
705 |
this._nodeOpened = true; |
706 |
break; |
707 |
} |
708 |
this._nodeStack.push(name); |
709 |
} |
710 |
|
711 |
iwfWriter.prototype.writeAttribute = function(name, val, quotes){ |
712 |
if (!this._nodeOpened){ |
713 |
iwfLog("IWF Xml Parsing Error: Appending attribute '" + name + |
714 |
"' when no open node exists!", true); |
715 |
} |
716 |
var attval = val; |
717 |
|
718 |
|
719 |
if (!quotes){ |
720 |
quotes = "'"; |
721 |
} |
722 |
|
723 |
// browsers handle html attributes in a special way: |
724 |
// only the character that matches the html delimiter for the attribute should be xml-escaped. |
725 |
// the OTHER quote character (' or ", whichever) needs to be javascript-escaped, |
726 |
// |
727 |
// the iwfXmlNode class also needs the > char escaped to be functional -- I need to fix that! |
728 |
// But for right now, this hack gets things working at least. |
729 |
// |
730 |
// Like I said, I'm getting lazy :) |
731 |
// |
732 |
|
733 |
if (this._htmlMode){ |
734 |
// we're kicking out html, so xml escape only the quote character that matches the delimiter and the > sign. |
735 |
// We also need to javascript-escape the quote char that DOES NOT match the delimiter but is xml-escaped. |
736 |
if (quotes == "'"){ |
737 |
// we're using apostrophes to delimit html. |
738 |
attval = attval.replace(/"/gi, '\\"').replace(/'/gi, ''').replace(/>/gi, '>'); |
739 |
} else { |
740 |
// we're using quotes to delimit html |
741 |
attval = attval.replace(/'/gi, "\\'").replace(/"/gi, '"').replace(/>/gi, '>'); |
742 |
} |
743 |
} else { |
744 |
attval = iwfXmlEncode(attval); |
745 |
} |
746 |
|
747 |
this.writeRaw(" " + name + "=" + quotes + attval + quotes); |
748 |
} |
749 |
|
750 |
iwfWriter.prototype.writeAtt = function(name, val, quotes){ |
751 |
this.writeAttribute(name, val, quotes); |
752 |
} |
753 |
|
754 |
iwfWriter.prototype._writeRawText = function(txt){ |
755 |
if (!txt || txt.length == 0){ |
756 |
// no text to write. do nothing. |
757 |
return; |
758 |
} else { |
759 |
if (this._nodeOpened){ |
760 |
this.writeRaw(">"); |
761 |
this._nodeOpened = false; |
762 |
} |
763 |
|
764 |
this.writeRaw(txt); |
765 |
} |
766 |
} |
767 |
|
768 |
iwfWriter.prototype.writeText = function(txt){ |
769 |
if (!txt || txt.length == 0){ |
770 |
// no text to write. do nothing. |
771 |
return; |
772 |
} else { |
773 |
if (this._nodeOpened){ |
774 |
this.writeRaw(">"); |
775 |
this._nodeOpened = false; |
776 |
this.writePretty(true); |
777 |
} |
778 |
|
779 |
this.writeRaw(iwfXmlEncode(txt)); |
780 |
} |
781 |
} |
782 |
|
783 |
iwfWriter.prototype.writeNodeClose = function(){ |
784 |
if (this._nodeStack.length > 0){ |
785 |
var name = this._nodeStack.pop(); |
786 |
|
787 |
if (!this._nodeOpened && name != '#text'){ |
788 |
this.writePretty(true); |
789 |
} |
790 |
|
791 |
switch(name){ |
792 |
case '#pi': |
793 |
this.writeRaw("?>"); |
794 |
break; |
795 |
case '#text': |
796 |
// do nothing |
797 |
break; |
798 |
case '#cdata': |
799 |
this.writeRaw("]]>"); |
800 |
break; |
801 |
case '#comment': |
802 |
this.writeRaw("-->"); |
803 |
break; |
804 |
default: |
805 |
if (this._nodeOpened){ |
806 |
//! hack for <script /> and <div /> needing to be <script></script> or <div></div> |
807 |
switch(name){ |
808 |
case 'script': |
809 |
case 'div': |
810 |
this.writeRaw("></" + name + ">"); |
811 |
break; |
812 |
default: |
813 |
this.writeRaw("/>");; |
814 |
break; |
815 |
} |
816 |
} else { |
817 |
this.writeRaw("</" + name + ">"); |
818 |
} |
819 |
break; |
820 |
} |
821 |
this._nodeOpened = false; |
822 |
} |
823 |
} |
824 |
|
825 |
iwfWriter.prototype.writeRaw = function(xml){ |
826 |
this._buffer += xml; |
827 |
} |
828 |
|
829 |
iwfWriter.prototype.toString = function(){ |
830 |
return this._buffer; |
831 |
} |
832 |
|
833 |
iwfWriter.prototype.clear = function(){ |
834 |
this.buffer = new String(); |
835 |
} |
836 |
|
837 |
// ----------------------------------- |
838 |
// End: Xml Writer Object |
839 |
// ----------------------------------- |
840 |
|