1 |
dpavlin |
55 |
// -------------------------------------------------------------------------- |
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 |
dpavlin |
46 |
// |
32 |
dpavlin |
55 |
// This reduces file size by about 30%, give or take. |
33 |
dpavlin |
46 |
// |
34 |
dpavlin |
55 |
//! To rip out only logging statements (commented or uncommented): |
35 |
|
|
//! ^/{0,2}iwfLog.* |
36 |
|
|
// -------------------------------------------------------------------------- |
37 |
dpavlin |
46 |
|
38 |
|
|
// -------------------------------------------------------------------------- |
39 |
dpavlin |
55 |
//! iwfxml.js |
40 |
dpavlin |
46 |
// |
41 |
|
|
// Javascript-based xml parser |
42 |
|
|
// |
43 |
dpavlin |
55 |
//! Dependencies: |
44 |
|
|
//! iwfcore.js |
45 |
dpavlin |
46 |
// |
46 |
dpavlin |
55 |
//! Brock Weaver - brockweaver@users.sourceforge.net |
47 |
|
|
//! v 0.2 - 2005-11-14 |
48 |
|
|
//! core bug patch |
49 |
dpavlin |
46 |
// -------------------------------------------------------------------------- |
50 |
dpavlin |
55 |
//! Brock Weaver - brockweaver@users.sourceforge.net |
51 |
|
|
//! v 0.1 - 2005-06-05 |
52 |
|
|
//! Initial release. |
53 |
|
|
// -------------------------------------------------------------------------- |
54 |
dpavlin |
46 |
// 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 |
dpavlin |
55 |
//! iwfXmlNode |
118 |
dpavlin |
46 |
// |
119 |
dpavlin |
55 |
//! Brock Weaver - brockweaver@users.sourceforge.net |
120 |
|
|
//! v 0.1 - 2005-06-05 |
121 |
|
|
//! Initial release. |
122 |
dpavlin |
46 |
// -------------------------------------------------------------------------- |
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 |
dpavlin |
55 |
//! iwfWriter |
637 |
dpavlin |
46 |
// |
638 |
dpavlin |
55 |
//! Brock Weaver - brockweaver@users.sourceforge.net |
639 |
dpavlin |
46 |
// |
640 |
dpavlin |
55 |
//! v 0.1 - 2005-06-05 |
641 |
|
|
//! Initial release. |
642 |
dpavlin |
46 |
// -------------------------------------------------------------------------- |
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 |
dpavlin |
55 |
//! hack for <script /> and <div /> needing to be <script></script> or <div></div> |
807 |
dpavlin |
46 |
switch(name){ |
808 |
|
|
case 'script': |
809 |
dpavlin |
55 |
case 'div': |
810 |
dpavlin |
46 |
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 |
|
|
|