XML DOM, XPath and XMLHttpRequest support for O::U::SpiderMonkey
[Evergreen.git] / Open-ILS / src / javascript / backend / libs / jsDOM.js
1 load_lib('jsOO.js')
2 load_lib('xpath.js')
3
4 function DOMException  (c) { this.code = c }
5 DOMException.INDEX_SIZE_ERR = 1;
6 DOMException.DOMSTRING_SIZE_ERR = 2;
7 DOMException.HIERARCHY_REQUEST_ERR = 3;
8 DOMException.WRONG_DOCUMENT_ERR = 4;
9 DOMException.INVALID_CHARACTER_ERR = 5;
10 DOMException.NO_DATA_ALLOWED_ERR = 6;
11 DOMException.NO_MODIFICATION_ALLOWED_ERR = 7;
12 DOMException.NOT_FOUND_ERR = 8;
13 DOMException.NOT_SUPPORTED_ERR = 9;
14 DOMException.INUSE_ATTRIBUTE_ERR = 10;
15 DOMException.INVALID_STATE_ERR = 11;
16 DOMException.SYNTAX_ERR = 12;
17 DOMException.INVALID_MODIFICATION_ERR = 13;
18 DOMException.NAMESPACE_ERR = 14;
19 DOMException.INVALID_ACCESS_ERR = 15;
20
21 function DOMImplementation () {
22         this._features = {};
23         this._features['CORE'] = {};
24         this._features['CORE']['any'] = true;
25         this._features['CORE']['1.0'] = true;
26         this._features['CORE']['2.0'] = true;
27         this._features['XML'] = {};
28         this._features['XML']['any'] = true;
29         this._features['XML']['1.0'] = true;
30         this._features['XML']['2.0'] = true;
31 }
32
33 DOMImplementation.method('hasFeature', function (f, v) {
34         if (!v) v = 'any';
35         if (this._features[f] && this._features[f][v])
36                 return this._features[f][v];
37         return false;
38 });
39 DOMImplementation.method('createDocumentType', function (n, p, s) {
40         return null;
41 });
42 DOMImplementation.method('createDocument', function (ns,qn,dt) {
43         var d = new Document(dt);
44         d.documentElement = d.createElement(qn);
45         d.appendChild(d.documentElement);
46         if (ns)
47                 d.documentElement.namespaceURI = ns;
48
49         installDOM3XPathSupport(d,new XPathParser());
50         return d;
51 });
52
53 var __XMLDOC = {};
54 var __XMLDOCid = 0;
55 DOMImplementation.parseString = function (xml) {
56         __XMLDOC['id' + __XMLDOCid] = {};
57         _OILS_FUNC_xml_parse_string(xml, '__XMLDOC.id' + __XMLDOCid);
58         var x = __XMLDOC['id' + __XMLDOCid];
59         __XMLDOCid++;
60         return x;
61 }
62
63
64 // NodeList interface
65 function NodeList () {
66         this.length = 0;
67         //perl_print(' --  NodeList constructor');
68 }
69
70 NodeList.method('item', function (idx) {
71         return this[idx];
72 });
73 NodeList.method('push', function (node) {
74         var idx = this.length;
75         this[idx] = node;
76         this.length++;
77         return this[idx];
78 });
79
80
81
82
83 // NamedNodeMap interface
84 function NamedNodeMap () {
85         this.length = 0;
86         this._nodes = {};
87         this._ns_nodes = {};
88         //perl_print(' --  NamedNodeMap constructor');
89 }
90
91 NamedNodeMap.method('item', function (idx) {
92         return this.getNamedItem(idx);
93 });
94
95 NamedNodeMap.method('removeNamedItemNS', function (ns, name) {
96         var x = this._ns_nodes[ns][name];
97         for (var i in this._nodes) {
98                 if (this._nodes[i] === x) {
99                         this._nodes[i] = null;
100                 }
101         }
102         this._ns_nodes[ns][name] = null;
103         return x;
104 });
105
106 NamedNodeMap.method('removeNamedItem', function (name) {
107         var x = this._nodes[name];
108         for (var i in this._nodes) {
109                 if (this._nodes[i] === x) {
110                         this._nodes[i] = null;
111                 }
112         }
113         return x;
114 });
115
116 NamedNodeMap.method('getNamedItem', function (name) {
117         return this._nodes[name];
118 });
119
120 NamedNodeMap.method('getNamedItemNS', function (ns,name) {
121         return this._ns_nodes[ns][name];
122 });
123
124 NamedNodeMap.method('setNamedItem', function (node) {
125         if (node.nodeName == 'length') return null;
126         this[node.nodeName] = node.value;
127         this[this.length] = node.value;
128         this._nodes[node.nodeName] = node;
129         this._nodes[this.length] = node;
130         this.length++;
131 });
132
133 NamedNodeMap.method('setNamedItemNS', function (node) {
134         if (node.nodeName == 'length') return null;
135         this[this.length] = node.value;
136         if (!this._ns_nodes[node.namespaceURI]) this._ns_nodes[node.namespaceURI] = {};
137         this._ns_nodes[node.namespaceURI][node.nodeName] = node;
138         this._nodes[this.length] = node;
139         this.length++;
140 });
141
142 // Node interface
143 function Node (name) {
144         this.nodeName = name;
145         this.nodeValue = null;
146         this.nodeType = null;
147         this.parentNode = null;
148         this.childNodes = new NodeList();
149         this.firstChild = null;
150         this.lastChild = null;
151         this.previousSibling = null;
152         this.nextSibling = null;
153         this.attributes = new NamedNodeMap();
154         this.ownerDocument = null;
155         this.namespaceURI = null;
156
157         if (name) {
158                 var p = name.indexOf(':');
159                 if (p != -1) {
160                         this.prefix = name.substring(0,p);
161                         this.localName = name.substring(p + 1);
162                 } else {
163                         this.localName = name;
164                 }
165         }
166
167         //perl_print(' --  Node constructor');
168 }
169 Node.ELEMENT_NODE = 1;
170 Node.ATTRIBUTE_NODE = 2;
171 Node.TEXT_NODE = 3;
172 Node.CDATA_SECTION_NODE = 4;
173 Node.ENTITY_REFERENCE_NODE = 5;
174 Node.ENTITY_NODE = 6;
175 Node.PROCESSING_INSTRUCTION_NODE = 7;
176 Node.COMMENT_NODE = 8;
177 Node.DOCUMENT_NODE = 9;
178 Node.DOCUMENT_TYPE_NODE = 10;
179 Node.DOCUMENT_FRAGMENT_NODE = 11;
180 Node.NOTATION_NODE = 12;
181 Node.NAMESPACE_DECL = 18;
182
183 Node.method('_childIndex', function (node) {
184         for (var i = 0; i < this.childNodes.length; i++)
185                 if (this.childNodes[i] === node) return i;
186 });
187 Node.method('insertBefore', function (node, target) {
188
189         if (node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
190                 for (var i = 0; i < node.childNodes.length; i++)
191                         this.insertBefore(node.childNodes.item(i));
192                 return node;
193         }
194
195         node.parentNode = this;
196         node.ownerDocument = this.ownerDocument;
197         var i = this._childIndex(target);
198
199         for (var j = this.childNodes.length; j > i; j--)
200                 this.childNodes[j] = this.childNodes[j - 1];
201
202         this.childNodes[i] = node;
203
204         if (i == 0) {
205                 this.firstChild = node;
206         } else {
207                 this.childNodes[i - 1].nextSibling = node;
208                 node.previousSibling = this.childNodes[i + 1];
209         }
210
211         this.childNodes[i + 1].previousSibling = node;
212         node.nextSibling = this.childNodes[i + 1];
213         
214         node._index = this.ownerDocument._nodes.length;
215         this.ownerDocument._nodes[this.ownerDocument._nodes.length] = node;
216
217         this.childNodes.length++;
218         return node;
219 });
220
221 Node.method('removeChild', function (node) {
222         node.parentNode = this;
223         node.ownerDocument = this.ownerDocument;
224         var i = this._childIndex(node);
225
226         if (node === this.firstChild) {
227                 this.firstChild = node.nextSibling;
228         } else {
229                 node.previousSibling.nextSibling = node.nextSibling;
230                 for (var j = i; j < this.childNodes.length; j++)
231                         this.childNodes[j - 1] = this.childNodes[j];
232         }
233
234         if (node === this.lastChild) {
235                 this.lastChild = node.previousSibling;
236         } else {
237                 node.nextSibling.previousSibling = node.previousSibling;
238         }
239
240         this.ownerDocument._nodes[node._index] = null;
241         
242         this.childNodes.length--;
243         return node;
244 });
245
246 Node.method('appendChild', function (node) {
247
248         if (node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
249                 for (var i = 0; i < node.childNodes.length; i++)
250                         this.appendChild(node.childNodes.item(i));
251                 return node;
252         }
253
254         node.parentNode = this;
255         this.childNodes.push( node );
256         this.lastChild = node;
257         if (this.childNodes.length == 1) {
258                 this.firstChild = node;
259         } else {
260                 this.lastChild.previousSibling = this.childNodes[this.childNodes.length - 2]
261                 this.lastChild.previousSibling.nextSibling = this.lastChild;
262         }
263
264         return node;
265 });
266
267 Node.method('hasChildNodes', function () {
268         if (this.childNodes.length) return true;
269         return false;
270 });
271
272 Node.method('hasAttributes', function () {
273         if (this.attributes.length) return true;
274         return false;
275 });
276
277 Node.method('cloneNode', function (deep) {
278         //perl_print(this.constructor);
279         var newNode = new this.constructor( this.nodeName );
280         newNode.ownerDocument = this.ownerDocument;
281         newNode.namespaceURI = this.namespaceURI;
282         newNode.prefix = this.prefix;
283
284         if (deep) {
285                 for (var i = 0; i < this.childNodes.length; i++)
286                         newNode.appendChild( this.childNodes.item(i).cloneNode(deep) );
287         }
288
289         for (var i = 0; i < this.attributes.length; i++)
290                 newNode.attributes.setNamedItem( this.attributes.item(i) );
291
292         return newNode;
293 });
294
295 function DocumentType (n,p,s) {
296         this.uber('constructor');
297         this.constructor = DocumentType;
298         this.nodeType = Node.DOCUMENT_TYPE_NODE;
299         this.name = n;
300         this.entities = new NamedNodeMap();
301         this.notations = new NamedNodeMap();
302         this.publicId = p;
303         this.systemId = s;
304         this.internalSubset = null;
305 }
306 DocumentType.inherits(Node);
307
308 function Notation (p, s) {
309         this.uber('constructor');
310         this.constructor = Notation;
311         this.nodeType = Node.NOTATION_NODE;
312         this.publicId = p;
313         this.systemId = s;
314 }
315 Notation.inherits(Node);
316
317 function ProcessingInstruction (target, data) {
318         this.uber('constructor');
319         this.constructor = ProcessingInstruction;
320         this.nodeType = Node.PROCESSING_INSTRUCTION_NODE;
321         this.target = target;
322         this.data = data;
323 }
324 ProcessingInstruction.inherits(Node);
325
326 function Entity (p, s, n) {
327         this.uber('constructor');
328         this.constructor = Entity;
329         this.nodeType = Node.ENTITY_NODE;
330         this.publicId = p;
331         this.systemId = s;
332         this.notationName = n;
333 }
334 Entity.inherits(Node);
335
336 function EntityReference () {
337         this.uber('constructor');
338         this.constructor = EntityReference;
339         this.nodeType = Node.ENTITY_REFERENCE_NODE;
340 }
341 EntityReference.inherits(Node);
342
343 // Document interface
344 function Document (dt) {
345         this.uber('constructor');
346         this.constructor = Document;
347         this.nodeType = Node.DOCUMENT_NODE;
348         this.doctype = dt;
349         this.documentElement = null;
350         this.implementation = new DOMImplementation();
351         this._nodes = [];
352         //perl_print(' --  Document constructor');
353 }
354
355 Document.inherits(Node);
356 Document.method('createAttribute', function (tagName) {
357         var node = new Attr(tagName);
358         node.ownerDocument = this;
359         return node;
360 });
361
362 Document.method('createAttributeNS', function (ns,tagName) {
363         var node = this.createAttribute(tagName);
364         node.namespaceURI = ns;
365         return node;
366 });
367
368 Document.method('createElement', function (tagName) {
369         var node = new Element(tagName);
370         node.ownerDocument = this;
371         node._index = this._nodes.length;
372         this._nodes[this._nodes.length] = node;
373         return node;
374 });
375
376 Document.method('createElementNS', function (ns,tagName) {
377         var node = this.createElement(tagName);
378         node.namespaceURI = ns;
379         return node;
380 });
381
382 Document.method('importNode', function (node, deep) {
383         var tmp = node.clone(deep);
384         tmp.ownerDocument = this;
385         var newNode = tmp.cloneNode(deep);
386         return newNode;
387 });
388
389 Document.method('createDocumentFragment', function () {
390         var x = new Node();
391         x.nodeType = Node.DOCUMENT_FRAGMENT_NODE;
392         x.ownerDocument = this;
393 });
394
395 Document.method('createTextNode', function (content) {
396         var node = new Text(content);
397         node.ownerDocument = this;
398         return node;
399 });
400
401 Document.method('createComment', function (content) {
402         var node = new Comment(content);
403         node.ownerDocument = this;
404         return node;
405 });
406
407 Document.method('createCDATASection', function (content) {
408         var node = new CDATASection(content);
409         node.ownerDocument = this;
410         return node;
411 });
412
413 Document.method('getElementById', function (id) {
414         for (var i in this._nodes) {
415                 //perl_print(' id = ' + this._nodes[i].attributes.id);
416                 if (this._nodes[i] && this._nodes[i].attributes.id == id)
417                         return this._nodes[i];
418         }
419         return null;
420 });
421
422 Document.method('getElementsByTagName', function (tname) {
423         var list = new NodeList();
424         this.documentElement.getElementsByTagName(tname, list);
425         return list;
426 });
427
428 Document.method('getElementsByTagNameNS', function (ns, tname) {
429         var list = new NodeList();
430         this.documentElement.getElementsByTagNameNS(ns, tname, list);
431         return list;
432 });
433
434 // Attr interface
435 function Attr ( name, value ) {
436         this.uber('constructor',name);
437         this.constructor = Attr;
438         this.nodeType = Node.ATTRIBUTE_NODE;
439         this.name = name;
440         this.value = this.nodeValue = value;
441         this.specified = (this.value ? true : false);
442         this.ownerElement = null;
443         //perl_print(' --  Attr constructor');
444 }
445 Attr.inherits(Node);
446
447
448 // Element interface
449 function Element ( name ) {
450         this.uber('constructor',name);
451         this.constructor = Element;
452         this.nodeType = Node.ELEMENT_NODE;
453         this.tagName = name;
454         //perl_print(' --  Element constructor')
455 }
456 Element.inherits(Node);
457
458 Element.method('getAttribute', function (aname) {
459         var x = this.attributes.getNamedItem(aname);
460         if (x) return x.value;
461         return null;
462 });
463
464 Element.method('setAttribute', function (aname,aval) {
465         var attr = new Attr(aname, aval);
466         attr.ownerElement = this;
467         return this.attributes.setNamedItem(attr);
468 });
469
470 Element.method('removeAttribute', function (aname) {
471         this.attributes.removeNamedItem(aname);
472         return null;
473 });
474
475 Element.method('getAttributeNode', function (aname) {
476         return this.attributes.getNamedItem(aname);
477 });
478
479 Element.method('setAttributeNode', function (attr) {
480         attr.ownerElement = this;
481         attr.namespaceURI = (attr.namespaceURI ? attr.namespaceURI : this.namespaceURI);
482         return this.attributes.setNamedItem(attr);
483 });
484
485 Element.method('removeAttributeNode', function (attr) {
486         if (attr.namespaceURI) {
487                 return this.attributes.removeNamedItemNS(attr.namespaceURI, attr.name);
488         } else {
489                 return this.attributes.removeNamedItem(attr.name);
490         }
491 });
492
493 Element.method('getAttributeNS', function (ns,aname) {
494         var x = this.attributes.getNamedItemNS(ns,aname);
495         if (x) return x.value;
496         return null;
497 });
498
499 Element.method('setAttributeNS', function (ns,aname,aval) {
500         var attr = new Attr(aname, aval);
501         attr.ownerElement = this;
502         attr.namespaceURI = ns;
503         return this.attributes.setNamedItem(ns,attr);
504 });
505
506 Element.method('removeAttributeNS', function (ns,aname) {
507         this.attributes.removeNamedItemNS(ns,aname);
508         return null;
509 });
510
511 Element.method('getAttributeNodeNS', function (ns, aname) {
512         return this.attributes.getNamedItemNS(ns,aname);
513 });
514
515 Element.method('setAttributeNodeNS', function (attr) {
516         attr.ownerElement = this;
517         attr.namespaceURI = (attr.namespaceURI ? attr.namespaceURI : this.namespaceURI);
518         return this.attributes.setNamedItemNS(attr);
519 });
520
521 Element.method('hasAttribute', function (name) {
522         return ( this.getAttribute(name) ? true : false );
523 });
524
525 Element.method('hasAttributeNS', function (ns, name) {
526         return ( this.getAttributeNS(ns, name) ? true : false );
527 });
528
529 Element.method('getElementsByTagName', function (tname, list) {
530         if (!list) list = new NodeList();
531         if (this.tagName == tname) list.push(this);
532         //perl_print(' --  ' + this.tagName + ' :: ' + this.childNodes.length);
533         for (var i = 0;  i < this.childNodes.length; i++) {
534                 if (this.childNodes.item(i).nodeType == 1)
535                         this.childNodes.item(i).getElementsByTagName(tname, list);
536         }
537         return list;
538 });
539
540 Element.method('getElementsByTagNameNS', function (ns, tname, list) {
541         if (!list) list = new NodeList();
542         if (this.localName == tname && this.namespaceURI == ns) list.push(this);
543         //perl_print(' --  {' + this.namespaceURI + '}:' + this.localName + ' :: ' + this.childNodes.length);
544         for (var i = 0;  i < this.childNodes.length; i++) {
545                 if (this.childNodes.item(i).nodeType == 1)
546                         this.childNodes.item(i).getElementsByTagNameNS(ns, tname, list);
547         }
548         return list;
549 });
550
551
552 // CharacterData interface
553 function CharacterData ( name, content ) {
554         this.uber('constructor', name);
555         this.constructor = CharacterData;
556         this.setData(content);
557         //perl_print(' --  CharacterData constructor');
558         //perl_print(' --  CharacterData length == ' + this.length + ' :: ' + this.data);
559 }
560 CharacterData.inherits(Node);
561
562 CharacterData.method('setData', function (content) {
563         this.data = this.nodeValue = content;
564         if (this.data)
565                 this.length = this.data.length;
566 });
567 CharacterData.method('substringData', function (offset,count) {
568         this.data.substring(offset,count);
569 });
570 CharacterData.method('appendData', function (txt) {
571         this.data = this.nodeValue = this.data + txt;
572         this.length = this.data.length;
573 });
574 CharacterData.method('insertData', function (offset,txt) {
575         var b = this.data.substring(0,offset);
576         var a = this.data.substring(offset);
577         this.data = this.nodeValue = b + txt + a;
578         this.length = this.data.length;
579 });
580 CharacterData.method('deleteData', function (offset,count) {
581         var b = this.data.substring(0,offset);
582         var a = this.data.substring(offset + count);
583         this.data = this.nodeValue = b + a;
584         this.length = this.data.length;
585 });
586 CharacterData.method('replaceData', function (offset,count,txt) {
587         var b = this.data.substring(0,offset);
588         var a = this.data.substring(offset + count);
589         this.data = this.nodeValue = b + txt +  a;
590         this.length = this.data.length;
591 });
592
593
594
595 // Text interface
596 function Text ( content ) {
597         this.superclass.constructor.call(this, '#text', content);
598         this.constructor = Text;
599         this.nodeType = Node.TEXT_NODE;
600         //perl_print(' --  Text constructor :: ' + this.data);
601 }
602 Text.inherits(CharacterData);
603
604
605 // CDATASection interface
606 function CDATASection ( content ) {
607         this.uber('constructor', '#cdata', content);
608         this.constructor = CDATASection;
609         this.nodeType = Node.COMMENT_NODE;
610         //perl_print(' --  Comment constructor');
611 }
612 CDATASection.inherits(Text);
613
614 // Comment interface
615 function Comment ( content ) {
616         this.uber('constructor', '#comment', content);
617         this.constructor = Comment;
618         this.nodeType = Node.COMMENT_NODE;
619         //perl_print(' --  Comment constructor');
620 }
621 Comment.inherits(Text);
622
623
624
625 //////////////////////// XPath stuff /////////////////////////
626
627 function XPathNamespaceResolver (data) {
628         this.data = data;
629 }
630 XPathNamespaceResolver.inherits(NamespaceResolver);
631
632 XPathNamespaceResolver.method('lookupNamespaceURI', function (prefix) {
633         return this.data[prefix];
634 });
635 XPathNamespaceResolver.method('setNamespaceURI', function (prefix, uri) {
636         this.data[prefix] = uri;
637 });
638