• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/lighttpd-1.4.39/external_file/js/davclient.js/dommer/
1/*  dommer.js - a (mostly) compliant subset of DOM level 2 for JS
2    (c) Guido Wesdorp 2004-2007
3    email johnny@debris.demon.nl
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19    dommer.js
20
21    This library provides a mostly compliant subset of the DOM API in core
22    JavaScript. A number of methods aren't implemented, and there are a few
23    semantic differences between the standard and this implementations, but
24    it provides most of DOM level 2's features and is usable in almost all JS
25    environments (also stand-alone ones).
26
27    I started writing this mainly because of IE's lack of proper namespace
28    support, and to have a portable, reliable DOM implementation.
29
30    Non-standard are:
31
32    - Whitespace is never ignored.
33
34    - Because of JS doesn't (by default) allow computing attributes on request,
35      this API doesn't create Element.nodeName on setting element.prefix,
36      therefore a new method was added: Element.setPrefix (note that this
37      is not required if the library is not used on browsers that don't
38      support __defineGetter__ and __defineSetter__ (such as IE)).
39
40    $Id: dommer.js,v 1.1.1.1 2011/11/09 06:36:04 sungmin Exp $
41
42*/
43
44// If the following switch is set to true, setting Element.prefix
45// will result in an exception. This serves to make sure scripts work
46// cross-browser: IE does not support __defineSetter__, which is used
47// to ensure Element.nodeName is updated if Element.prefix
48// is changed (and also to ensure Element.nodeName and
49// Element.localName can't be changed directly). The lack of this
50// method on IE means that on that platform it is possible to break
51// integrity (by setting .prefix directly, .nodeName will be out-of-date).
52// Note that this means that if you intend to use this lib only on Mozilla
53// (or other browsers that support dynamic properties), you can safely
54// set this to false and set .prefix without breaking integrity.
55var WARN_ON_PREFIX = true;
56
57// give this a namespace...
58try {
59    var global = window;
60} catch(e) {
61    var global = this;
62};
63global.dommer = new function() {
64    /* Exceptions */
65    function DOMException(errorcode, message) {
66        this.code = null;
67        this.error = null;
68        this.message = message
69        for (var attr in DOMException) {
70            if (DOMException[attr] == errorcode) {
71                this.error = attr;
72                break;
73            };
74        };
75        this.code = errorcode;
76        if (!this.error) {
77            this.error = 'Unknown';
78        };
79        this.stack = stack = createStack();
80        this.lineNumber = getLineNo(stack);
81        this.fileName = getFileName(stack);
82    };
83
84    this.DOMException = DOMException;
85
86    // error codes
87    // XXX should we make these global, like in the specs?
88    DOMException.INDEX_SIZE_ERR = 1,
89    DOMException.DOMSTRING_SIZE_ERR = 2;
90    DOMException.HIERARCHY_REQUEST_ERR = 3;
91    DOMException.WRONG_DOCUMENT_ERR = 4;
92    DOMException.INVALID_CHARACTER_ERR = 5;
93    DOMException.NO_DATA_ALLOWED_ERR = 6;
94    DOMException.NO_MODIFICATION_ALLOWED_ERR = 7;
95    DOMException.NOT_FOUND_ERR = 8;
96    DOMException.NOT_SUPPORTED_ERR = 9;
97    DOMException.INUSE_ATTRIBUTE_ERR = 10;
98    DOMException.INVALID_STATE_ERR = 11;
99    DOMException.SYNTAX_ERR = 12;
100    DOMException.INVALID_MODIFICATION_ERR = 13;
101    DOMException.NAMESPACE_ERR = 14;
102    DOMException.INVALID_ACCESS_ERR = 15;
103
104    DOMException.prototype.toString = function() {
105        var ret = 'DOMException: ' + this.error + ' (' + this.code + ')';
106        if (this.message) {
107            ret += ' - ' + this.message;
108        };
109        return ret;
110    };
111
112    /* Node interface */
113    function Node() {
114        this.ELEMENT_NODE = 1;
115        this.ATTRIBUTE_NODE = 2;
116        this.TEXT_NODE = 3;
117        this.CDATA_SECTION_NODE = 4;
118        this.ENTITY_REFERENCE_NODE = 5;
119        this.ENTITY_NODE = 6;
120        this.PROCESSING_INSTRUCTION_NODE = 7;
121        this.COMMENT_NODE = 8;
122        this.DOCUMENT_NODE = 9;
123        this.DOCUMENT_TYPE_NODE = 10;
124        this.DOCUMENT_FRAGMENT_NODE = 11;
125        this.NOTATION_NODE = 12;
126
127        // These are defined in-line rather than on .prototype to allow using
128        // them below, too. This way we don't have to check whether attributes
129        // are already protected while this constructor is ran or not (in JS,
130        // when you set 'Foo.prototype = new Bar;', the Bar constructor is
131        // actually ran, in our case this means that the state of the
132        // superclass changes).
133        this._protectAttribute = function(attr) {
134            /* make an attribute read-only */
135            this.__defineSetter__(attr,
136                function(value) {
137                    throw(
138                        (new DOMException(
139                            DOMException.NO_MODIFICATION_ALLOWED_ERR, attr))
140                    );
141                }
142            );
143            this.__defineGetter__(attr,
144                function() {
145                    return this['_' + attr];
146                }
147            );
148        };
149
150        this._setProtected = function(name, value) {
151            /* set a read-only attribute
152
153                THIS IS AN INTERNAL METHOD that should not get used as part
154                of the API
155            */
156            this['_' + name] = value;
157            if (!this.__defineSetter__) {
158                this[name] = value;
159            };
160        };
161
162        this.nodeValue = null;
163        if (this.__defineSetter__) {
164            // on browsers that support __define[GS]etter__, perform integrity
165            // checks
166            // nodeValue should be settable on certain nodeTypes
167            this.__defineSetter__('nodeValue',
168                function(nodeValue) {
169                    if (this.nodeType != this.TEXT_NODE &&
170                            this.nodeType != this.ATTRIBUTE_NODE &&
171                            this.nodeType != this.COMMENT_NODE) {
172                        throw(
173                            (new DOMException(
174                                DOMException.NO_DATA_ALLOWED_ERR,
175                                'nodeValue'))
176                        );
177                    };
178                    // XXX should check on allowed chars here, but not
179                    // sure which?
180                    this._nodeValue = nodeValue;
181                }
182            );
183            // XXX not sure if we should protect reading .nodeValue
184            this.__defineGetter__('nodeValue',
185                function() {
186                    if (this.nodeType != this.TEXT_NODE &&
187                            this.nodeType != this.ATTRIBUTE_NODE &&
188                            this.nodeType != this.COMMENT_NODE) {
189                        throw(
190                            (new DOMException(
191                                DOMException.NO_DATA_ALLOWED_ERR,
192                                'nodeValue'))
193                        );
194                    };
195                    return this._nodeValue;
196                }
197            );
198            var toprotect = ['nodeType', 'nodeName', 'parentNode',
199                                'childNodes', 'firstChild', 'lastChild',
200                                'previousSibling', 'nextSibling',
201                                'attributes', 'ownerDocument', 'namespaceURI',
202                                'localName'];
203            for (var i=0; i < toprotect.length; i++) {
204                this._protectAttribute(toprotect[i]);
205            };
206        };
207
208        this._setProtected('namespaceURI', null);
209        this._setProtected('prefix', null);
210        this._setProtected('nodeName', null);
211        this._setProtected('localName', null);
212        this._setProtected('parentNode', null);
213        // note that this is shared between subclass instances, so should be
214        // re-set in every .initialize() (so below is just for show)
215        this._setProtected('childNodes', []);
216        this._setProtected('firstChild', null);
217        this._setProtected('lastChild', null);
218        this._setProtected('previousSibling', null);
219        this._setProtected('nextSibling', null);
220        this._setProtected('ownerDocument', null);
221    };
222
223    this.Node = Node;
224
225    var thrownotsupported = function() {throw('not supported');};
226
227    // XXX these should be implemented at some point...
228    Node.prototype.normalize = thrownotsupported;
229    Node.prototype.isSupported = thrownotsupported; // hehehe...
230
231    // non-standard method, use this always instead of setting .prefix
232    // yourself, as this will update the .nodeName property too
233    Node.prototype.setPrefix = function(prefix) {
234        if (this.__defineSetter__) {
235            this._prefix = prefix;
236            this._nodeName = prefix + ':' + this.localName;
237        } else {
238            this.prefix = prefix;
239            this.nodeName = prefix + ':' + this.localName;
240        };
241    };
242
243    Node.prototype.cloneNode = function() {
244        throw(
245            (new DOMException(DOMException.NOT_SUPPORTED_ERR))
246        );
247    };
248
249    Node.prototype.hasChildNodes = function() {
250        return (this.childNodes && this.childNodes.length > 0);
251    };
252
253    Node.prototype.hasAttributes = function() {
254        return (this.attributes !== undefined && this.attributes.length);
255    };
256
257    Node.prototype.appendChild = function(newChild) {
258        this._checkModificationAllowed();
259        this._attach(newChild);
260    };
261
262    Node.prototype.removeChild = function(oldChild) {
263        this._checkModificationAllowed();
264        this._checkIsChild(oldChild);
265        var newChildren = new NodeList();
266        var found = false;
267        for (var i=0; i < this.childNodes.length; i++) {
268            if (this.childNodes[i] === oldChild) {
269                oldChild._setProtected('parentNode', null);
270                var previous = oldChild.previousSibling;
271                if (previous) {
272                    oldChild._setProtected('previousSibling', null);
273                    previous._setProtected('nextSibling',
274                        oldChild.nextSibling);
275                };
276                var next = oldChild.nextSibling;
277                if (next) {
278                    next._setProtected('previousSibling', previous);
279                    oldChild._setProtected('nextSibling', null);
280                };
281                continue;
282            };
283            newChildren.push(this.childNodes[i]);
284        };
285        this._setProtected('childNodes', newChildren);
286        this._setProtected('firstChild',
287                (this.childNodes.length > 0 ? this.childNodes[0] : null));
288        this._setProtected('lastChild', (
289                this.childNodes.length > 0 ?
290                    this.childNodes[this.childNodes.length - 1] : null));
291    };
292
293    Node.prototype.replaceChild = function(newChild, refChild) {
294        this._checkModificationAllowed();
295        this._checkIsChild(refChild);
296        this._attach(newChild, refChild, true);
297    };
298
299    Node.prototype.insertBefore = function(newChild, refChild) {
300        this._checkModificationAllowed();
301        this._checkIsChild(refChild);
302        this._attach(newChild, refChild);
303    };
304
305    Node.prototype._attach = function(newChild, refChild, replace) {
306        // see if the child is in the same document
307        if (newChild.ownerDocument != this.ownerDocument) {
308            throw(
309                (new DOMException(DOMException.WRONG_DOCUMENT_ERR))
310            );
311        };
312        // see if the child is of an allowed type
313        if (newChild.nodeType != newChild.ELEMENT_NODE &&
314                newChild.nodeType != newChild.TEXT_NODE &&
315                newChild.nodeType != newChild.CDATA_SECTION_NODE &&
316                newChild.nodeType != newChild.COMMENT_NODE) {
317            throw(
318                (new DOMException(DOMException.HIERARCHY_REQUEST_ERR))
319            );
320        };
321        // see if the child isn't a (grand)parent of ourselves
322        var currparent = this;
323        while (currparent && currparent.nodeType != newChild.DOCUMENT_NODE) {
324            if (currparent === newChild) {
325                throw(
326                    (new DOMException(DOMException.HIERARCHY_REQUEST_ERR))
327                );
328            };
329            currparent = currparent.parentNode;
330        };
331        // seems to be okay, add it
332        newChild._setProtected('parentNode', this);
333        if (!refChild) {
334            if (this.childNodes.length) {
335                this.childNodes[this.childNodes.length - 1]._setProtected(
336                    'nextSibling', newChild);
337                newChild._setProtected('previousSibling',
338                    this.childNodes[this.childNodes.length - 1]);
339            };
340            this.childNodes.push(newChild);
341        } else {
342            var newchildren = [];
343            var found = false;
344            for (var i=0; i < this.childNodes.length; i++) {
345                var currChild = this.childNodes[i];
346                if (currChild === refChild) {
347                    newchildren.push(newChild);
348                    var previous = this.childNodes[i - 1];
349                    if (previous) {
350                        newChild._setProtected('previousSibling', previous);
351                        previous._setProtected('nextSibling', newChild);
352                    };
353                    if (!replace) {
354                        newchildren.push(currChild);
355                        currChild._setProtected('previousSibling', newChild);
356                        newChild._setProtected('nextSibling', currChild);
357                    } else {
358                        currChild._setProtected('parentNode', null);
359                        currChild._setProtected('previousSibling', null);
360                        currChild._setProtected('nextSibling', null);
361                        var next = this.childNodes[i + 1];
362                        newChild._setProtected('nextSibling', next);
363                        next._setProtected('previousSibling', newChild);
364                    };
365                    found = true;
366                } else {
367                    newchildren.push(currChild);
368                };
369            };
370            if (!found) {
371                throw(
372                    (new DOMException(DOMException.NOT_FOUND_ERR))
373                );
374            };
375            this._setProtected('childNodes', newchildren);
376        };
377        this._setProtected('firstChild', this.childNodes[0]);
378        this._setProtected('lastChild',
379            this.childNodes[this.childNodes.length - 1]);
380    };
381
382    Node.prototype._checkModificationAllowed = function() {
383        if (this.nodeType != this.ELEMENT_NODE &&
384                this.nodeType != this.DOCUMENT_NODE &&
385                this.nodeType != this.DOCUMENT_FRAGMENT_NODE) {
386            throw(
387                (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR))
388            );
389        };
390    };
391
392    Node.prototype._checkIsChild = function(refChild) {
393        if (refChild.parentNode !== this) {
394            throw(
395                (new DOMException(DOMException.NOT_FOUND_ERR))
396            );
397        };
398    };
399
400    function DocumentFragment() {
401        this._setProtected('nodeType', 11);
402    };
403
404    DocumentFragment.prototype = new Node;
405    this.DocumentFragment = DocumentFragment;
406
407    function Element() {
408        this._setProtected('nodeType', 1);
409    };
410
411    Element.prototype = new Node;
412    this.Element = Element;
413
414    Element.prototype.initialize = function(namespaceURI, qname,
415                                                    ownerDocument) {
416        // XXX the specs are very vague about an id, it says the DOM
417        // implementation must have info about which attributes are of the id
418        // type, I'll just use the property here for now...
419        this.id = ''; // empty string like in Mozilla, seems weird to me though
420
421        this._setProtected('attributes', []);
422        this._setProtected('childNodes', []);
423        this._setProtected('ownerDocument', ownerDocument);
424
425        // try to ensure integrity by defining getters and setters for certain
426        // properties, since this only works in certain browsers it makes sense to
427        // test your applications on one of those platforms, see also
428        // WARN_ON_PREFIX in the top of the document
429        if (this.__defineSetter__) {
430            this._nodeName = this.nodeName;
431            this.__defineSetter__('nodeName', function() {
432                            throw(
433                                (new DOMException(
434                                    DOMException.NO_MODIFICATION_ALLOWED_ERR)))
435                            });
436            this.__defineGetter__('nodeName',
437                                    function() {return this._nodeName});
438            this.__defineSetter__('prefix',
439                                function(value) {
440                                    if (WARN_ON_PREFIX) {
441                                        throw('Setting prefix directly ' +
442                                                'breaks integrity of the ' +
443                                                'XML DOM in Internet ' +
444                                                'Explorer browsers!');
445                                    };
446                                    this._prefix = value;
447                                    this._nodeName = this._prefix +
448                                                        this._localName;
449                                });
450            this.__defineGetter__('prefix', function() {return this._prefix});
451        };
452        // XXX both the ns and qname need integrity checks
453        this._setProtected('namespaceURI', namespaceURI);
454        if (qname.indexOf(':') > -1) {
455            var tup = qname.split(':');
456            this.setPrefix(tup.shift());
457            this._setProtected('localName', tup.join(':'));
458        } else {
459            this.setPrefix(null);
460            this._setProtected('localName', qname);
461        };
462        if (this.prefix) {
463            this._setProtected('nodeName', this.prefix + ':' + this.localName);
464        } else {
465            this._setProtected('nodeName', this.localName);
466        };
467    };
468
469    Element.prototype.toString = function() {
470        return '<Element "' + this.nodeName + '" (type ' +
471                    this.nodeType + ')>';
472    };
473
474    Element.prototype.toXML = function(context) {
475        // context is used when toXML is called recursively
476        // marker
477        var no_prefix_id = '::no_prefix::';
478        if (!context) {
479            context = {
480                namespace_stack: []
481            };
482        };
483        var new_namespaces = {}; // any namespaces that weren't declared yet
484        var current_namespaces = {};
485        var last_namespaces = context.namespace_stack[
486                                    context.namespace_stack.length - 1];
487        context.namespace_stack.push(current_namespaces);
488        if (last_namespaces) {
489            for (var prefix in last_namespaces) {
490                current_namespaces[prefix] = last_namespaces[prefix];
491            };
492        };
493        var xml = '<' + this.nodeName;
494        var prefix = this.prefix || no_prefix_id;
495        if (this.namespaceURI &&
496                (current_namespaces[prefix] != this.namespaceURI)) {
497            current_namespaces[prefix] = this.namespaceURI;
498            new_namespaces[prefix] = this.namespaceURI;
499        };
500        for (var i=0; i < this.attributes.length; i++) {
501            var attr = this.attributes[i];
502            var aprefix = attr.prefix || no_prefix_id;
503            if (attr.namespaceURI &&
504                    current_namespaces[aprefix] != attr.namespaceURI) {
505                current_namespaces[aprefix] = attr.namespaceURI;
506                new_namespaces[aprefix] = attr.namespaceURI;
507            };
508            xml += ' ' + attr.nodeName + '="' +
509                    string.entitize(attr.nodeValue) + '"';
510        };
511
512        // take care of any new namespaces
513        for (var prefix in new_namespaces) {
514            xml += ' xmlns';
515            if (prefix != no_prefix_id) {
516                xml += ':' + prefix;
517            };
518            xml += '="' + string.entitize(new_namespaces[prefix]) + '"';
519        };
520
521        if (this.childNodes.length) {
522            xml += '>';
523            for (var i=0; i < this.childNodes.length; i++) {
524                xml += this.childNodes[i].toXML(context);
525            };
526            xml += '</' + this.nodeName + '>';
527        } else {
528            xml += ' />';
529        };
530        context.namespace_stack.pop();
531        return xml;
532    };
533
534    Element.prototype.cloneNode = function(deep) {
535        var el = new Element();
536        el.initialize(this.namespaceURI, this.nodeName, this.ownerDocument);
537        for (var i=0; i < this.attributes.length; i++) {
538            var clone = this.attributes[i].cloneNode();
539            clone._setProtected('ownerElement', el);
540            el.attributes.push(clone);
541        };
542        if (deep) {
543            for (var i=0; i < this.childNodes.length; i++) {
544                var clone = this.childNodes[i].cloneNode(true);
545                clone._setProtected('parentNode', el);
546                el.appendChild(clone);
547            };
548        };
549        return el;
550    };
551
552    Element.prototype.getAttributeNodeNS = function(namespaceURI, qname) {
553        for (var i=0; i < this.attributes.length; i++) {
554            var attr = this.attributes[i];
555            if (attr.namespaceURI == namespaceURI && attr.nodeName == qname) {
556                return attr;
557            };
558        };
559    };
560
561    Element.prototype.getAttributeNode = function(name) {
562        return this.getAttributeNodeNS(undefined, name);
563    };
564
565    Element.prototype.getAttribute = function(name) {
566        var attr = this.getAttributeNode(name)
567        return (attr ? attr.nodeValue : null);
568    };
569
570    Element.prototype.getAttributeNS = function(namespaceURI, name) {
571        var attr = this.getAttributeNodeNS(namespaceURI, name);
572        return (attr ? attr.nodeValue : null);
573    };
574
575    Element.prototype.hasAttributeNS = function(namespaceURI, name) {
576        return !!(this.getAttributeNS(namespaceURI, name));
577    };
578
579    Element.prototype.hasAttribute = function(name) {
580        return this.hasAttributeNS(this.namespaceURI, name);
581    };
582
583    Element.prototype.setAttributeNS = function(namespaceURI, name, value) {
584        for (var i=0; i < this.attributes.length; i++) {
585            var attr = this.attributes[i];
586            if (attr.namespaceURI == namespaceURI && attr.nodeName == name) {
587                attr.nodeValue = value;
588                return;
589            };
590        };
591        var attr = new Attribute();
592        attr.initialize(namespaceURI, name, value, this.ownerDocument);
593        attr._setProtected('ownerElement', this);
594        this.attributes.push(attr);
595    };
596
597    Element.prototype.setAttribute = function(name, value) {
598        this.setAttributeNS(undefined, name, value);
599    };
600
601    Element.prototype.setAttributeNodeNS = function(newAttr) {
602        for (var i=0; i < this.attributes.length; i++) {
603            var attr = this.attributes[i];
604            if (attr.namespaceURI == newAttr.namespaceURI &&
605                    attr.nodeName == newAttr.nodeName) {
606                throw(
607                    (new DOMException(DOMException.INUSE_ATTRIBUTE_ERR))
608                );
609            };
610        };
611        this.attributes.push(newAttr);
612    };
613
614    Element.prototype.setAttributeNode = function(newAttr) {
615        // XXX should this fail if no namespaceURI is available or something?
616        this.setAttributeNodeNS(newAttr);
617    };
618
619    Element.prototype.removeAttributeNS = function(namespaceURI, name) {
620        for (var i=0; i < this.attributes.length; i++) {
621            var attr = this.attributes[i];
622            if (attr.namespaceURI == namespaceURI && attr.nodeName == name) {
623                delete this.attributes[i];
624                return true;
625            };
626        };
627        return false;
628    };
629
630    Element.prototype.removeAttribute = function(name) {
631        return this.removeAttributeNS(this.namespaceURI, name);
632    };
633
634    Element.prototype.getElementsByTagNameNS = function(namespaceURI,
635                                                                name, ret) {
636        // XXX *very* slow!!!
637        // needs to be optimized later on (probably by using some mapping)
638        if (!ret) {
639            ret = [];
640        };
641        for (var i=0; i < this.childNodes.length; i++) {
642            var child = this.childNodes[i];
643            if (name == child.nodeName || name == '*') {
644                if ((!namespaceURI && !child.namespaceURI) ||
645                        (namespaceURI == child.namespaceURI)) {
646                    ret.push(child);
647                };
648            };
649            if (child.nodeType == 1) {
650                child.getElementsByTagNameNS(namespaceURI, name, ret);
651            };
652        };
653        return ret;
654    };
655
656    Element.prototype.getElementsByTagName = function(name) {
657        return this.getElementsByTagNameNS(this.namespaceURI, name);
658    };
659
660    Element.prototype.getElementById = function(id) {
661        // XXX *very* slow!!!
662        // needs to be optimized later on (probably by using some mapping)
663        if (this.id == id) {
664            return this;
665        };
666        for (var i=0; i < this.childNodes.length; i++) {
667            var child = this.childNodes[i];
668            if (child.id == id) {
669                return child;
670            };
671            if (child.nodeType == 1) {
672                var found = this.childNodes[i].getElementById(id);
673                if (found) {
674                    return found;
675                };
676            };
677        };
678    };
679
680    function TextNode() {
681        this._setProtected('nodeType', 3);
682        this._setProtected('nodeName', '#text');
683    };
684
685    TextNode.prototype = new Node;
686    this.TextNode = TextNode;
687
688    TextNode.prototype.initialize = function(data, ownerDocument) {
689        this._setProtected('ownerDocument', ownerDocument);
690        this._setProtected('childNodes', new NodeList());
691        // nodeValue is not protected
692        this.nodeValue = data;
693    };
694
695    TextNode.prototype.toXML = function() {
696        return string.entitize(this.nodeValue);
697    };
698
699    TextNode.prototype.cloneNode = function() {
700        var node = new TextNode();
701        node.initialize(this.nodeValue, this.ownerDocument);
702        return node;
703    };
704
705    function CommentNode() {
706        /* a comment node */
707        this._setProtected('nodeType', 8);
708        this._setProtected('nodeName', '#comment');
709    };
710
711    CommentNode.prototype = new TextNode;
712    this.CommentNode = CommentNode;
713
714    CommentNode.prototype.initialize = function(data, ownerDocument) {
715        this._setProtected('ownerDocument', ownerDocument);
716        this._setProtected('childNodes', []);
717        this._setProtected('nodeValue', data);
718    };
719
720    CommentNode.prototype.toXML = function() {
721        return "<!--" + this.nodeValue + "-->";
722    };
723
724    // Attribute, subclass of TextNode because of the nice implementation
725    function Attribute() {
726        /* an attribute node */
727        this._setProtected('nodeType', 2);
728    };
729
730    Attribute.prototype = new Node;
731    this.Attribute = Attribute;
732
733    Attribute.prototype.initialize = function(namespaceURI, qname, value,
734                                                    ownerDocument) {
735        // XXX some code duplication here...
736        if (qname.match(/[^a-zA-Z0-9_\-:]/g)) {
737            throw(
738                (new DOMException(DOMException.INVALID_CHARACTER_ERR))
739            );
740        };
741        this._setProtected('ownerDocument', ownerDocument);
742        this._setProtected('namespaceURI', namespaceURI);
743        this._setProtected('nodeValue', value);
744        this._setProtected('childNodes', []);
745
746        // try to ensure integrity by defining getters and setters for certain
747        // properties, since this only works in certain browsers it makes sense to
748        // test your applications on one of those platforms, see also
749        // WARN_ON_PREFIX in the top of the document
750        if (this.__defineSetter__) {
751            this._nodeName = this.nodeName;
752            this.__defineSetter__('nodeName', function() {
753                            throw(
754                                (new DOMException(
755                                    DOMException.NO_MODIFICATION_ALLOWED_ERR)))
756                            });
757            this.__defineGetter__('nodeName',
758                                    function() {return this._nodeName});
759            this.__defineSetter__('prefix',
760                                function(value) {
761                                    if (WARN_ON_PREFIX) {
762                                        throw('Setting prefix directly ' +
763                                                'breaks integrity of the ' +
764                                                'XML DOM in Internet ' +
765                                                'Explorer browsers!');
766                                    };
767                                    this._prefix = value;
768                                    this._nodeName = this._prefix +
769                                                        this._localName;
770                                });
771            this.__defineGetter__('prefix', function() {return this._prefix});
772            this._protectAttribute('ownerElement');
773        };
774        this._setProtected('ownerElement', null);
775        if (qname.indexOf(':') > -1) {
776            var tup = qname.split(':');
777            this.setPrefix(tup.shift());
778            this._setProtected('localName', tup.join(':'));
779        } else {
780            this.setPrefix(null);
781            this._setProtected('localName', qname);
782        };
783        if (this.prefix) {
784            this._setProtected('nodeName', this.prefix + ':' + this.localName);
785        } else {
786            this._setProtected('nodeName', this.localName);
787        };
788    };
789
790    Attribute.prototype.toXML = function() {
791        ret = this.nodeName + '="' + string.entitize(this.nodeValue) + '"';
792        return ret;
793    };
794
795    Attribute.prototype.cloneNode = function() {
796        var attr = new Attribute();
797        attr.initialize(this.namespaceURI, this.nodeName, this.nodeValue,
798                        this.ownerDocument);
799        return attr;
800    };
801
802    Attribute.prototype.toString = function() {
803        return this.nodeValue;
804    };
805
806    function Document() {
807        /* the document node */
808        this._setProtected('nodeType', 9);
809        this._setProtected('nodeName', '#document');
810    };
811
812    Document.prototype = new Node;
813    this.Document = Document;
814
815    Document.prototype.initialize = function() {
816        this._setProtected('ownerDocument', this);
817        this._setProtected('childNodes', []);
818        this.documentElement = null;
819        this.namespaceToPrefix = {};
820    };
821
822    Document.prototype.toXML = function() {
823        return this.documentElement.toXML();
824    };
825
826    Document.prototype.appendChild = function(newChild) {
827        if (this.documentElement) {
828            throw(
829                (new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
830                    'document already has a document element'))
831            );
832        };
833        this._checkModificationAllowed();
834        this._attach(newChild);
835        this.documentElement = newChild;
836    };
837
838
839    Document.prototype.createElement = function(nodeName) {
840        return this.createElementNS(this.namespaceURI, nodeName);
841    };
842
843    Document.prototype.createElementNS = function(namespaceURI, nodeName) {
844        var el = new Element();
845        el.initialize(namespaceURI, nodeName, this);
846        return el;
847    };
848
849    Document.prototype.createTextNode = function(data) {
850        var el = new TextNode();
851        el.initialize(string.deentitize(data), this);
852        return el;
853    };
854
855    Document.prototype.createAttributeNS = function(namespaceURI, nodeName) {
856        var el = new Attribute();
857        el.initialize(namespaceURI, nodeName, null, this);
858        return el;
859    };
860
861    Document.prototype.createAttribute = function(nodeName) {
862        return this.createAttributeNS(undefined, nodeName);
863    };
864
865    Document.prototype.createComment = function(data) {
866        var el = new CommentNode();
867        el.initialize(data, this);
868        return el;
869    };
870
871    Document.prototype.importNode = function(node) {
872        node._setProtected('ownerDocument', this);
873    };
874
875    function DOMHandler() {
876        /* SAX handler to convert a piece of XML to a DOM */
877    };
878
879    this.DOMHandler = DOMHandler;
880
881    DOMHandler.prototype.startDocument = function() {
882        this.document = new Document();
883        this.document.initialize();
884        this.current = null;
885        this.namespaces = new Array();
886        this.namespaceToPrefix = {};
887    };
888
889    DOMHandler.prototype.startElement = function(namespaceURI, nodename,
890                                                        attrs) {
891        if (namespaceURI && !array.contains(this.namespaces, namespaceURI)) {
892            this.namespaces.push(namespaceURI);
893            // update the mapping on the document just to be sure,
894            // that one and the one on this handler should always be in
895            // sync if a start tag is encountered, since instantiating a
896            // Element will set the prefix on that element
897            // XXX ??
898            this.document.namespaceToPrefix = this.namespaceToPrefix;
899        };
900        var node = this.document.createElementNS(namespaceURI, nodename);
901        var prefix = undefined;
902        if (namespaceURI) {
903            prefix = this.namespaceToPrefix[namespaceURI];
904            if (prefix) {
905                node.setPrefix(prefix);
906            };
907        };
908        for (var ans in attrs) {
909            // XXX can be optimized by using a dict and just setting the key
910            if (ans && ans != '' && !array.contains(this.namespaces, ans)) {
911                this.namespaces.push(ans);
912            };
913            var nsattrs = attrs[ans];
914            for (var aname in nsattrs) {
915                if (aname == 'prefix') {
916                    continue;
917                };
918                if (ans) {
919                    var attr = this.document.createAttributeNS(ans, aname);
920                    attr.setPrefix(this.namespaceToPrefix[ans]);
921                    attr.nodeValue = nsattrs[aname];
922                    node.setAttributeNodeNS(attr);
923                } else {
924                    var attr = this.document.createAttribute(aname);
925                    attr.nodeValue = nsattrs[aname];
926                    node.setAttributeNode(attr);
927                };
928            };
929        };
930        if (!this.current) {
931            this.document.documentElement = node;
932            this.document._setProtected('childNodes', [node]);
933            this.current = node;
934            this.current._setProtected('parentNode', this.document);
935            this.current._setProtected('ownerDocument', this.document);
936        } else {
937            this.current.appendChild(node);
938            this.current = node;
939        };
940    };
941
942    DOMHandler.prototype.characters = function(data) {
943        if (!this.current && string.strip(data) == '') {
944            return;
945        };
946        var node = this.document.createTextNode(data);
947        this.current.appendChild(node);
948    };
949
950    DOMHandler.prototype.comment = function(data) {
951        if (!this.current && string.strip(data) == '') {
952            return;
953        };
954        var node = this.document.createComment(data);
955        if (this.current) {
956            this.current.appendChild(node);
957        } else {
958            this.document.comment = node;
959        };
960    };
961
962    DOMHandler.prototype.endElement = function(namespaceURI, nodename) {
963        var prefix = this.namespaceToPrefix[namespaceURI];
964        if (nodename != this.current.localName ||
965                namespaceURI != this.current.namespaceURI) {
966            throw('non-matching end tag ' + namespaceURI + ':' +
967                    prefix + ':' + nodename + ' for start tag ' +
968                    this.current.namespaceURI + ':' + this.current.nodeName);
969        };
970        this.current = this.current.parentNode;
971    };
972
973    DOMHandler.prototype.endDocument = function() {
974    };
975
976    function DOM() {
977        /* The DOM API
978
979            Uses regular expressions to convert <xml> to a simple DOM
980
981            Provides:
982
983                DOM.parseXML(xml)
984                - parse the XML, return a document element
985
986                DOM.createDocument()
987                - contains the document node of the DOM (which in turn contains
988                    the documentElement)
989
990                DOM.toXML()
991                - returns a serialized XML string
992
993                DOM.buildFromHandler(handler)
994                - build and return a DOM document built from a MiniSAX handler
995        */
996    };
997
998    this.DOM = DOM;
999
1000    DOM.prototype.createDocument = function() {
1001        var document = new Document();
1002        document.initialize();
1003        return document;
1004    };
1005
1006    DOM.prototype.toXML = function(docOrEl, encoding) {
1007        /* serialize to XML */
1008        var xml = '<?xml version="1.0"';
1009        if (encoding) {
1010            xml += ' encoding="' + encoding + '"';
1011        };
1012        xml += '?>\n';
1013        return xml + docOrEl.toXML();
1014    };
1015
1016    DOM.prototype.parseXML = function(xml) {
1017        /* parse XML into a DOM
1018
1019            returns a Document node
1020        */
1021        var handler = new DOMHandler();
1022        var parser = new SAXParser();
1023        parser.initialize(xml, handler);
1024        parser.parse();
1025        var document = handler.document;
1026        this._copyNamespaceMapping(document, handler.namespaceToPrefix);
1027        return document;
1028    };
1029
1030    DOM.prototype.buildFromHandler = function(handler) {
1031        /* create a DOM from a SAX handler */
1032        var document = handler.document;
1033        this._copyNamespaceMapping(document, handler.namespaceToPrefix);
1034        return document;
1035    };
1036
1037    DOM.prototype._copyNamespaceMapping = function(document, namespaces) {
1038        document.namespaceToPrefix = namespaces;
1039    };
1040
1041    // an implementation of an array, exactly the same as the one in JS
1042    // (although incomplete) itself, this because friggin' IE has problems
1043    // using Array as prototype (it won't update .length on mutations)
1044    function BaseArray() {
1045        for (var i=0; i < arguments.length; i++) {
1046            this[i] = arguments[i];
1047        };
1048        this.length = arguments.length;
1049    };
1050
1051    BaseArray.prototype.concat = function() {
1052        throw('Not supported');
1053    };
1054
1055    BaseArray.prototype.join = function() {
1056        throw('Not supported');
1057    };
1058
1059    BaseArray.prototype.pop = function() {
1060        var item = this[this.length - 1];
1061        delete this[this.length - 1];
1062        this.length = this.length - 1;
1063        return item;
1064    };
1065
1066    BaseArray.prototype.push = function(item) {
1067        this[this.length] = item;
1068        this.length = this.length + 1;
1069        return item;
1070    };
1071
1072    BaseArray.prototype.reverse = function() {
1073        throw('Not supported');
1074    };
1075
1076    BaseArray.prototype.shift = function() {
1077        var item = this[0];
1078        for (var i=1; i < this.length; i++) {
1079            this[i-1] = this[i];
1080        };
1081        delete this[length - 1];
1082        this.length = this.length - 1;
1083        return item;
1084    };
1085
1086    BaseArray.prototype.unshift = function(item) {
1087        for (var i=0; i < this.length; i++ ) {
1088            this[this.length - i] = this[(this.length - i) - 1];
1089        };
1090        this[0] = item;
1091        this.length = this.length + 1;
1092        return ;
1093    };
1094
1095    BaseArray.prototype.splice = function() {
1096        // XXX we may want to support this later
1097        throw('Not supported');
1098    };
1099
1100    BaseArray.prototype.toString = function() {
1101        var ret = [];
1102        for (var i=1; i < this.length; i++) {
1103            ret.push(this[i].toString());
1104        };
1105        return ret.join(', ');
1106    };
1107
1108    // for subclassing and such...
1109    this.BaseArray = BaseArray;
1110
1111    function NodeList() {
1112    };
1113
1114    NodeList.prototype = new BaseArray;
1115    this.NodeList = NodeList;
1116
1117    NodeList.prototype.item = function(index) {
1118        return this[index];
1119    };
1120
1121    function NamedNodeMap() {
1122    };
1123
1124    NamedNodeMap.prototype = new BaseArray;
1125    this.NamedNodeMap = NamedNodeMap;
1126
1127    NamedNodeMap.prototype.item = function(index) {
1128        return this[index];
1129    };
1130
1131    NamedNodeMap.prototype.getNamedItem = function(name) {
1132        for (var i=0; i < this.length; i++) {
1133            if (this[i].nodeName == name) {
1134                return this[i];
1135            };
1136        };
1137        return undefined;
1138    };
1139
1140    NamedNodeMap.prototype.setNamedItem = function(arg) {
1141        // this should generate exceptions, but I'm not sure when...
1142        // XXX how 'bout when arg is not the proper type?!?
1143        for (var i=0; i < this.length; i++) {
1144            if (this[i].nodeName == arg.nodeName) {
1145                this[i] = arg;
1146                return;
1147            };
1148        };
1149        this.push(arg);
1150    };
1151
1152    NamedNodeMap.prototype.removeNamedItem = function(name) {
1153        // a bit nasty: deleting an element from an array will not actually
1154        // free the index, instead something like undefined or null will end
1155        // up in its place, so we walk the array here, move every element
1156        // behind the item to remove one up, and pop the last item when
1157        // we're done
1158        var delete_mode = false;
1159        for (var i=0; i < this.length; i++) {
1160            if (this[i] === name) {
1161                delete_mode = true;
1162            };
1163            if (delete_mode) {
1164                this[i] = this[i + 1];
1165            };
1166        };
1167        if (!delete_mode) {
1168            throw(
1169                (new DOMException(DOMException.NOT_FOUND_ERR))
1170            );
1171        };
1172        // the last element is now in the array twice
1173        this.pop();
1174    };
1175}();
1176
1177// XXX shouldn't we make these local?
1178function createStack() {
1179    // somewhat nasty trick to get a stack trace in Moz
1180    var stack = undefined;
1181    try {notdefined()} catch(e) {stack = e.stack};
1182    if (stack) {
1183        stack = stack.split('\n');
1184        stack.shift();
1185        stack.shift();
1186    };
1187    return stack ? stack.join('\n') : '';
1188};
1189
1190function getLineNo(stack) {
1191    /* tries to get the line no in Moz */
1192    if (!stack) {
1193        return;
1194    };
1195    stack = stack.toString().split('\n');
1196    var chunks = stack[0].split(':');
1197    var lineno = chunks[chunks.length - 1];
1198    if (lineno != '0') {
1199        return lineno;
1200    };
1201};
1202
1203function getFileName(stack) {
1204    /* tries to get the filename in Moz */
1205    if (!stack) {
1206        return;
1207    };
1208    stack = stack.toString().split('\n');
1209    var chunks = stack[0].split(':');
1210    var filename = chunks[chunks.length - 2];
1211    return filename;
1212};
1213
1214