]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/Format/XML.js
initial commit
[syp.git] / openlayers / lib / OpenLayers / Format / XML.js
1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2  * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3  * full text of the license. */
4
5 /**
6  * @requires OpenLayers/Format.js
7  */
8
9 /**
10  * Class: OpenLayers.Format.XML
11  * Read and write XML.  For cross-browser XML generation, use methods on an
12  *     instance of the XML format class instead of on <code>document<end>.
13  *     The DOM creation and traversing methods exposed here all mimic the
14  *     W3C XML DOM methods.  Create a new parser with the
15  *     <OpenLayers.Format.XML> constructor.
16  *
17  * Inherits from:
18  *  - <OpenLayers.Format>
19  */
20 OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
21     
22     /**
23      * Property: namespaces
24      * {Object} Mapping of namespace aliases to namespace URIs.  Properties
25      *     of this object should not be set individually.  Read-only.  All
26      *     XML subclasses should have their own namespaces object.  Use
27      *     <setNamespace> to add or set a namespace alias after construction.
28      */
29     namespaces: null,
30     
31     /**
32      * Property: namespaceAlias
33      * {Object} Mapping of namespace URI to namespace alias.  This object
34      *     is read-only.  Use <setNamespace> to add or set a namespace alias.
35      */
36     namespaceAlias: null,
37     
38     /**
39      * Property: defaultPrefix
40      * {String} The default namespace alias for creating element nodes.
41      */
42     defaultPrefix: null,
43     
44     /**
45      * Property: readers
46      * Contains public functions, grouped by namespace prefix, that will
47      *     be applied when a namespaced node is found matching the function
48      *     name.  The function will be applied in the scope of this parser
49      *     with two arguments: the node being read and a context object passed
50      *     from the parent.
51      */
52     readers: {},
53     
54     /**
55      * Property: writers
56      * As a compliment to the <readers> property, this structure contains public
57      *     writing functions grouped by namespace alias and named like the
58      *     node names they produce.
59      */
60     writers: {},
61
62     /**
63      * Property: xmldom
64      * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
65      *     object.  It is not intended to be a browser sniffing property.
66      *     Instead, the xmldom property is used instead of <code>document<end>
67      *     where namespaced node creation methods are not supported. In all
68      *     other browsers, this remains null.
69      */
70     xmldom: null,
71
72     /**
73      * Constructor: OpenLayers.Format.XML
74      * Construct an XML parser.  The parser is used to read and write XML.
75      *     Reading XML from a string returns a DOM element.  Writing XML from
76      *     a DOM element returns a string.
77      *
78      * Parameters:
79      * options - {Object} Optional object whose properties will be set on
80      *     the object.
81      */
82     initialize: function(options) {
83         if(window.ActiveXObject) {
84             this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
85         }
86         OpenLayers.Format.prototype.initialize.apply(this, [options]);
87         // clone the namespace object and set all namespace aliases
88         this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
89         this.namespaceAlias = {};
90         for(var alias in this.namespaces) {
91             this.namespaceAlias[this.namespaces[alias]] = alias;
92         }
93     },
94     
95     /**
96      * APIMethod: destroy
97      * Clean up.
98      */
99     destroy: function() {
100         this.xmldom = null;
101         OpenLayers.Format.prototype.destroy.apply(this, arguments);
102     },
103     
104     /**
105      * Method: setNamespace
106      * Set a namespace alias and URI for the format.
107      *
108      * Parameters:
109      * alias - {String} The namespace alias (prefix).
110      * uri - {String} The namespace URI.
111      */
112     setNamespace: function(alias, uri) {
113         this.namespaces[alias] = uri;
114         this.namespaceAlias[uri] = alias;
115     },
116
117     /**
118      * APIMethod: read
119      * Deserialize a XML string and return a DOM node.
120      *
121      * Parameters:
122      * text - {String} A XML string
123      
124      * Returns:
125      * {DOMElement} A DOM node
126      */
127     read: function(text) {
128         var index = text.indexOf('<');
129         if(index > 0) {
130             text = text.substring(index);
131         }
132         var node = OpenLayers.Util.Try(
133             OpenLayers.Function.bind((
134                 function() {
135                     var xmldom;
136                     /**
137                      * Since we want to be able to call this method on the prototype
138                      * itself, this.xmldom may not exist even if in IE.
139                      */
140                     if(window.ActiveXObject && !this.xmldom) {
141                         xmldom = new ActiveXObject("Microsoft.XMLDOM");
142                     } else {
143                         xmldom = this.xmldom;
144                         
145                     }
146                     xmldom.loadXML(text);
147                     return xmldom;
148                 }
149             ), this),
150             function() {
151                 return new DOMParser().parseFromString(text, 'text/xml');
152             },
153             function() {
154                 var req = new XMLHttpRequest();
155                 req.open("GET", "data:" + "text/xml" +
156                          ";charset=utf-8," + encodeURIComponent(text), false);
157                 if(req.overrideMimeType) {
158                     req.overrideMimeType("text/xml");
159                 }
160                 req.send(null);
161                 return req.responseXML;
162             }
163         );
164
165         if(this.keepData) {
166             this.data = node;
167         }
168
169         return node;
170     },
171
172     /**
173      * APIMethod: write
174      * Serialize a DOM node into a XML string.
175      * 
176      * Parameters:
177      * node - {DOMElement} A DOM node.
178      *
179      * Returns:
180      * {String} The XML string representation of the input node.
181      */
182     write: function(node) {
183         var data;
184         if(this.xmldom) {
185             data = node.xml;
186         } else {
187             var serializer = new XMLSerializer();
188             if (node.nodeType == 1) {
189                 // Add nodes to a document before serializing. Everything else
190                 // is serialized as is. This may need more work. See #1218 .
191                 var doc = document.implementation.createDocument("", "", null);
192                 if (doc.importNode) {
193                     node = doc.importNode(node, true);
194                 }
195                 doc.appendChild(node);
196                 data = serializer.serializeToString(doc);
197             } else {
198                 data = serializer.serializeToString(node);
199             }
200         }
201         return data;
202     },
203
204     /**
205      * APIMethod: createElementNS
206      * Create a new element with namespace.  This node can be appended to
207      *     another node with the standard node.appendChild method.  For
208      *     cross-browser support, this method must be used instead of
209      *     document.createElementNS.
210      *
211      * Parameters:
212      * uri - {String} Namespace URI for the element.
213      * name - {String} The qualified name of the element (prefix:localname).
214      * 
215      * Returns:
216      * {Element} A DOM element with namespace.
217      */
218     createElementNS: function(uri, name) {
219         var element;
220         if(this.xmldom) {
221             if(typeof uri == "string") {
222                 element = this.xmldom.createNode(1, name, uri);
223             } else {
224                 element = this.xmldom.createNode(1, name, "");
225             }
226         } else {
227             element = document.createElementNS(uri, name);
228         }
229         return element;
230     },
231
232     /**
233      * APIMethod: createTextNode
234      * Create a text node.  This node can be appended to another node with
235      *     the standard node.appendChild method.  For cross-browser support,
236      *     this method must be used instead of document.createTextNode.
237      * 
238      * Parameters:
239      * text - {String} The text of the node.
240      * 
241      * Returns: 
242      * {DOMElement} A DOM text node.
243      */
244     createTextNode: function(text) {
245         var node;
246         if(this.xmldom) {
247             node = this.xmldom.createTextNode(text);
248         } else {
249             node = document.createTextNode(text);
250         }
251         return node;
252     },
253
254     /**
255      * APIMethod: getElementsByTagNameNS
256      * Get a list of elements on a node given the namespace URI and local name.
257      *     To return all nodes in a given namespace, use '*' for the name
258      *     argument.  To return all nodes of a given (local) name, regardless
259      *     of namespace, use '*' for the uri argument.
260      * 
261      * Parameters:
262      * node - {Element} Node on which to search for other nodes.
263      * uri - {String} Namespace URI.
264      * name - {String} Local name of the tag (without the prefix).
265      * 
266      * Returns:
267      * {NodeList} A node list or array of elements.
268      */
269     getElementsByTagNameNS: function(node, uri, name) {
270         var elements = [];
271         if(node.getElementsByTagNameNS) {
272             elements = node.getElementsByTagNameNS(uri, name);
273         } else {
274             // brute force method
275             var allNodes = node.getElementsByTagName("*");
276             var potentialNode, fullName;
277             for(var i=0, len=allNodes.length; i<len; ++i) {
278                 potentialNode = allNodes[i];
279                 fullName = (potentialNode.prefix) ?
280                            (potentialNode.prefix + ":" + name) : name;
281                 if((name == "*") || (fullName == potentialNode.nodeName)) {
282                     if((uri == "*") || (uri == potentialNode.namespaceURI)) {
283                         elements.push(potentialNode);
284                     }
285                 }
286             }
287         }
288         return elements;
289     },
290
291     /**
292      * APIMethod: getAttributeNodeNS
293      * Get an attribute node given the namespace URI and local name.
294      * 
295      * Parameters:
296      * node - {Element} Node on which to search for attribute nodes.
297      * uri - {String} Namespace URI.
298      * name - {String} Local name of the attribute (without the prefix).
299      * 
300      * Returns:
301      * {DOMElement} An attribute node or null if none found.
302      */
303     getAttributeNodeNS: function(node, uri, name) {
304         var attributeNode = null;
305         if(node.getAttributeNodeNS) {
306             attributeNode = node.getAttributeNodeNS(uri, name);
307         } else {
308             var attributes = node.attributes;
309             var potentialNode, fullName;
310             for(var i=0, len=attributes.length; i<len; ++i) {
311                 potentialNode = attributes[i];
312                 if(potentialNode.namespaceURI == uri) {
313                     fullName = (potentialNode.prefix) ?
314                                (potentialNode.prefix + ":" + name) : name;
315                     if(fullName == potentialNode.nodeName) {
316                         attributeNode = potentialNode;
317                         break;
318                     }
319                 }
320             }
321         }
322         return attributeNode;
323     },
324
325     /**
326      * APIMethod: getAttributeNS
327      * Get an attribute value given the namespace URI and local name.
328      * 
329      * Parameters:
330      * node - {Element} Node on which to search for an attribute.
331      * uri - {String} Namespace URI.
332      * name - {String} Local name of the attribute (without the prefix).
333      * 
334      * Returns:
335      * {String} An attribute value or and empty string if none found.
336      */
337     getAttributeNS: function(node, uri, name) {
338         var attributeValue = "";
339         if(node.getAttributeNS) {
340             attributeValue = node.getAttributeNS(uri, name) || "";
341         } else {
342             var attributeNode = this.getAttributeNodeNS(node, uri, name);
343             if(attributeNode) {
344                 attributeValue = attributeNode.nodeValue;
345             }
346         }
347         return attributeValue;
348     },
349     
350     /**
351      * APIMethod: getChildValue
352      * Get the textual value of the node if it exists, or return an
353      *     optional default string.  Returns an empty string if no first child
354      *     exists and no default value is supplied.
355      *
356      * Parameters:
357      * node - {DOMElement} The element used to look for a first child value.
358      * def - {String} Optional string to return in the event that no
359      *     first child value exists.
360      *
361      * Returns:
362      * {String} The value of the first child of the given node.
363      */
364     getChildValue: function(node, def) {
365         var value = def || "";
366         if(node) {
367             for(var child=node.firstChild; child; child=child.nextSibling) {
368                 switch(child.nodeType) {
369                     case 3: // text node
370                     case 4: // cdata section
371                         value += child.nodeValue;
372                 }
373             }
374         }
375         return value;
376     },
377
378     /**
379      * APIMethod: concatChildValues
380      * *Deprecated*. Use <getChildValue> instead.
381      *
382      * Concatenate the value of all child nodes if any exist, or return an
383      *     optional default string.  Returns an empty string if no children
384      *     exist and no default value is supplied.  Not optimized for large
385      *     numbers of child nodes.
386      *
387      * Parameters:
388      * node - {DOMElement} The element used to look for child values.
389      * def - {String} Optional string to return in the event that no
390      *     child exist.
391      *
392      * Returns:
393      * {String} The concatenated value of all child nodes of the given node.
394      */
395     concatChildValues: function(node, def) {
396         var value = "";
397         var child = node.firstChild;
398         var childValue;
399         while(child) {
400             childValue = child.nodeValue;
401             if(childValue) {
402                 value += childValue;
403             }
404             child = child.nextSibling;
405         }
406         if(value == "" && def != undefined) {
407             value = def;
408         }
409         return value;
410     },
411     
412     /**
413      * APIMethod: isSimpleContent
414      * Test if the given node has only simple content (i.e. no child element
415      *     nodes).
416      *
417      * Parameters:
418      * node - {DOMElement} An element node.
419      *
420      * Returns:
421      * {Boolean} The node has no child element nodes (nodes of type 1). 
422      */
423     isSimpleContent: function(node) {
424         var simple = true;
425         for(var child=node.firstChild; child; child=child.nextSibling) {
426             if(child.nodeType === 1) {
427                 simple = false;
428                 break;
429             }
430         }
431         return simple;
432     },
433     
434     /**
435      * APIMethod: contentType
436      * Determine the content type for a given node.
437      *
438      * Parameters:
439      * node - {DOMElement}
440      *
441      * Returns:
442      * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}
443      *     if the node has no, simple, complex, or mixed content.
444      */
445     contentType: function(node) {
446         var simple = false,
447             complex = false;
448             
449         var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
450
451         for(var child=node.firstChild; child; child=child.nextSibling) {
452             switch(child.nodeType) {
453                 case 1: // element
454                     complex = true;
455                     break;
456                 case 8: // comment
457                     break;
458                 default:
459                     simple = true;
460             }
461             if(complex && simple) {
462                 break;
463             }
464         }
465         
466         if(complex && simple) {
467             type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
468         } else if(complex) {
469             return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
470         } else if(simple) {
471             return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
472         }
473         return type;
474     },
475
476     /**
477      * APIMethod: hasAttributeNS
478      * Determine whether a node has a particular attribute matching the given
479      *     name and namespace.
480      * 
481      * Parameters:
482      * node - {Element} Node on which to search for an attribute.
483      * uri - {String} Namespace URI.
484      * name - {String} Local name of the attribute (without the prefix).
485      * 
486      * Returns:
487      * {Boolean} The node has an attribute matching the name and namespace.
488      */
489     hasAttributeNS: function(node, uri, name) {
490         var found = false;
491         if(node.hasAttributeNS) {
492             found = node.hasAttributeNS(uri, name);
493         } else {
494             found = !!this.getAttributeNodeNS(node, uri, name);
495         }
496         return found;
497     },
498     
499     /**
500      * APIMethod: setAttributeNS
501      * Adds a new attribute or changes the value of an attribute with the given
502      *     namespace and name.
503      *
504      * Parameters:
505      * node - {Element} Element node on which to set the attribute.
506      * uri - {String} Namespace URI for the attribute.
507      * name - {String} Qualified name (prefix:localname) for the attribute.
508      * value - {String} Attribute value.
509      */
510     setAttributeNS: function(node, uri, name, value) {
511         if(node.setAttributeNS) {
512             node.setAttributeNS(uri, name, value);
513         } else {
514             if(this.xmldom) {
515                 if(uri) {
516                     var attribute = node.ownerDocument.createNode(
517                         2, name, uri
518                     );
519                     attribute.nodeValue = value;
520                     node.setAttributeNode(attribute);
521                 } else {
522                     node.setAttribute(name, value);
523                 }
524             } else {
525                 throw "setAttributeNS not implemented";
526             }
527         }
528     },
529
530     /**
531      * Method: createElementNSPlus
532      * Shorthand for creating namespaced elements with optional attributes and
533      *     child text nodes.
534      *
535      * Parameters:
536      * name - {String} The qualified node name.
537      * options - {Object} Optional object for node configuration.
538      *
539      * Valid options:
540      * uri - {String} Optional namespace uri for the element - supply a prefix
541      *     instead if the namespace uri is a property of the format's namespace
542      *     object.
543      * attributes - {Object} Optional attributes to be set using the
544      *     <setAttributes> method.
545      * value - {String} Optional text to be appended as a text node.
546      *
547      * Returns:
548      * {Element} An element node.
549      */
550     createElementNSPlus: function(name, options) {
551         options = options || {};
552         // order of prefix preference
553         // 1. in the uri option
554         // 2. in the prefix option
555         // 3. in the qualified name
556         // 4. from the defaultPrefix
557         var uri = options.uri || this.namespaces[options.prefix];
558         if(!uri) {
559             var loc = name.indexOf(":");
560             uri = this.namespaces[name.substring(0, loc)];
561         }
562         if(!uri) {
563             uri = this.namespaces[this.defaultPrefix];
564         }
565         var node = this.createElementNS(uri, name);
566         if(options.attributes) {
567             this.setAttributes(node, options.attributes);
568         }
569         var value = options.value;
570         if(value != null) {
571             if(typeof value == "boolean") {
572                 value = String(value);
573             }
574             node.appendChild(this.createTextNode(value));
575         }
576         return node;
577     },
578     
579     /**
580      * Method: setAttributes
581      * Set multiple attributes given key value pairs from an object.
582      *
583      * Parameters:
584      * node - {Element} An element node.
585      * obj - {Object || Array} An object whose properties represent attribute
586      *     names and values represent attribute values.  If an attribute name
587      *     is a qualified name ("prefix:local"), the prefix will be looked up
588      *     in the parsers {namespaces} object.  If the prefix is found,
589      *     setAttributeNS will be used instead of setAttribute.
590      */
591     setAttributes: function(node, obj) {
592         var value, uri;
593         for(var name in obj) {
594             if(obj[name] != null && obj[name].toString) {
595                 value = obj[name].toString();
596                 // check for qualified attribute name ("prefix:local")
597                 uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
598                 this.setAttributeNS(node, uri, name, value);
599             }
600         }
601     },
602
603     /**
604      * Method: readNode
605      * Shorthand for applying one of the named readers given the node
606      *     namespace and local name.  Readers take two args (node, obj) and
607      *     generally extend or modify the second.
608      *
609      * Parameters:
610      * node - {DOMElement} The node to be read (required).
611      * obj - {Object} The object to be modified (optional).
612      *
613      * Returns:
614      * {Object} The input object, modified (or a new one if none was provided).
615      */
616     readNode: function(node, obj) {
617         if(!obj) {
618             obj = {};
619         }
620         var group = this.readers[this.namespaceAlias[node.namespaceURI]];
621         if(group) {
622             var local = node.localName || node.nodeName.split(":").pop();
623             var reader = group[local] || group["*"];
624             if(reader) {
625                 reader.apply(this, [node, obj]);
626             }
627         }
628         return obj;
629     },
630
631     /**
632      * Method: readChildNodes
633      * Shorthand for applying the named readers to all children of a node.
634      *     For each child of type 1 (element), <readSelf> is called.
635      *
636      * Parameters:
637      * node - {DOMElement} The node to be read (required).
638      * obj - {Object} The object to be modified (optional).
639      *
640      * Returns:
641      * {Object} The input object, modified.
642      */
643     readChildNodes: function(node, obj) {
644         if(!obj) {
645             obj = {};
646         }
647         var children = node.childNodes;
648         var child;
649         for(var i=0, len=children.length; i<len; ++i) {
650             child = children[i];
651             if(child.nodeType == 1) {
652                 this.readNode(child, obj);
653             }
654         }
655         return obj;
656     },
657
658     /**
659      * Method: writeNode
660      * Shorthand for applying one of the named writers and appending the
661      *     results to a node.  If a qualified name is not provided for the
662      *     second argument (and a local name is used instead), the namespace
663      *     of the parent node will be assumed.
664      *
665      * Parameters:
666      * name - {String} The name of a node to generate.  If a qualified name
667      *     (e.g. "pre:Name") is used, the namespace prefix is assumed to be
668      *     in the <writers> group.  If a local name is used (e.g. "Name") then
669      *     the namespace of the parent is assumed.  If a local name is used
670      *     and no parent is supplied, then the default namespace is assumed.
671      * obj - {Object} Structure containing data for the writer.
672      * parent - {DOMElement} Result will be appended to this node.  If no parent
673      *     is supplied, the node will not be appended to anything.
674      *
675      * Returns:
676      * {DOMElement} The child node.
677      */
678     writeNode: function(name, obj, parent) {
679         var prefix, local;
680         var split = name.indexOf(":");
681         if(split > 0) {
682             prefix = name.substring(0, split);
683             local = name.substring(split + 1);
684         } else {
685             if(parent) {
686                 prefix = this.namespaceAlias[parent.namespaceURI];
687             } else {
688                 prefix = this.defaultPrefix;
689             }
690             local = name;
691         }
692         var child = this.writers[prefix][local].apply(this, [obj]);
693         if(parent) {
694             parent.appendChild(child);
695         }
696         return child;
697     },
698
699     /**
700      * APIMethod: getChildEl
701      * Get the first child element.  Optionally only return the first child
702      *     if it matches the given name and namespace URI.
703      *
704      * Parameters:
705      * node - {DOMElement} The parent node.
706      * name - {String} Optional node name (local) to search for.
707      * uri - {String} Optional namespace URI to search for.
708      *
709      * Returns:
710      * {DOMElement} The first child.  Returns null if no element is found, if
711      *     something significant besides an element is found, or if the element
712      *     found does not match the optional name and uri.
713      */
714     getChildEl: function(node, name, uri) {
715         return node && this.getThisOrNextEl(node.firstChild, name, uri);
716     },
717     
718     /**
719      * APIMethod: getNextEl
720      * Get the next sibling element.  Optionally get the first sibling only
721      *     if it matches the given local name and namespace URI.
722      *
723      * Parameters:
724      * node - {DOMElement} The node.
725      * name - {String} Optional local name of the sibling to search for.
726      * uri - {String} Optional namespace URI of the sibling to search for.
727      *
728      * Returns:
729      * {DOMElement} The next sibling element.  Returns null if no element is
730      *     found, something significant besides an element is found, or the
731      *     found element does not match the optional name and uri.
732      */
733     getNextEl: function(node, name, uri) {
734         return node && this.getThisOrNextEl(node.nextSibling, name, uri);
735     },
736     
737     /**
738      * Method: getThisOrNextEl
739      * Return this node or the next element node.  Optionally get the first
740      *     sibling with the given local name or namespace URI.
741      *
742      * Parameters:
743      * node - {DOMElement} The node.
744      * name - {String} Optional local name of the sibling to search for.
745      * uri - {String} Optional namespace URI of the sibling to search for.
746      *
747      * Returns:
748      * {DOMElement} The next sibling element.  Returns null if no element is
749      *     found, something significant besides an element is found, or the
750      *     found element does not match the query.
751      */
752     getThisOrNextEl: function(node, name, uri) {
753         outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) {
754             switch(sibling.nodeType) {
755                 case 1: // Element
756                     if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) &&
757                        (!uri || uri === sibling.namespaceURI)) {
758                         // matches
759                         break outer;
760                     }
761                     sibling = null;
762                     break outer;
763                 case 3: // Text
764                     if(/^\s*$/.test(sibling.nodeValue)) {
765                         break;
766                     }
767                 case 4: // CDATA
768                 case 6: // ENTITY_NODE
769                 case 12: // NOTATION_NODE
770                 case 10: // DOCUMENT_TYPE_NODE
771                 case 11: // DOCUMENT_FRAGMENT_NODE
772                     sibling = null;
773                     break outer;
774             } // ignore comments and processing instructions
775         }
776         return sibling || null;
777     },
778     
779     /**
780      * APIMethod: lookupNamespaceURI
781      * Takes a prefix and returns the namespace URI associated with it on the given
782      *     node if found (and null if not). Supplying null for the prefix will
783      *     return the default namespace.
784      *
785      * For browsers that support it, this calls the native lookupNamesapceURI
786      *     function.  In other browsers, this is an implementation of
787      *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
788      *
789      * For browsers that don't support the attribute.ownerElement property, this
790      *     method cannot be called on attribute nodes.
791      *     
792      * Parameters:
793      * node - {DOMElement} The node from which to start looking.
794      * prefix - {String} The prefix to lookup or null to lookup the default namespace.
795      * 
796      * Returns:
797      * {String} The namespace URI for the given prefix.  Returns null if the prefix
798      *     cannot be found or the node is the wrong type.
799      */
800     lookupNamespaceURI: function(node, prefix) {
801         var uri = null;
802         if(node) {
803             if(node.lookupNamespaceURI) {
804                 uri = node.lookupNamespaceURI(prefix);
805             } else {
806                 outer: switch(node.nodeType) {
807                     case 1: // ELEMENT_NODE
808                         if(node.namespaceURI !== null && node.prefix === prefix) {
809                             uri = node.namespaceURI;
810                             break outer;
811                         }
812                         var len = node.attributes.length;
813                         if(len) {
814                             var attr;
815                             for(var i=0; i<len; ++i) {
816                                 attr = node.attributes[i];
817                                 if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
818                                     uri = attr.value || null;
819                                     break outer;
820                                 } else if(attr.name === "xmlns" && prefix === null) {
821                                     uri = attr.value || null;
822                                     break outer;
823                                 }
824                             }
825                         }
826                         uri = this.lookupNamespaceURI(node.parentNode, prefix);
827                         break outer;
828                     case 2: // ATTRIBUTE_NODE
829                         uri = this.lookupNamespaceURI(node.ownerElement, prefix);
830                         break outer;
831                     case 9: // DOCUMENT_NODE
832                         uri = this.lookupNamespaceURI(node.documentElement, prefix);
833                         break outer;
834                     case 6: // ENTITY_NODE
835                     case 12: // NOTATION_NODE
836                     case 10: // DOCUMENT_TYPE_NODE
837                     case 11: // DOCUMENT_FRAGMENT_NODE
838                         break outer;
839                     default: 
840                         // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),
841                         // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)
842                         uri =  this.lookupNamespaceURI(node.parentNode, prefix);
843                         break outer;
844                 }
845             }
846         }
847         return uri;
848     },
849     
850     CLASS_NAME: "OpenLayers.Format.XML" 
851
852 });     
853
854 OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3};
855
856 /**
857  * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI
858  * Takes a prefix and returns the namespace URI associated with it on the given
859  *     node if found (and null if not). Supplying null for the prefix will
860  *     return the default namespace.
861  *
862  * For browsers that support it, this calls the native lookupNamesapceURI
863  *     function.  In other browsers, this is an implementation of
864  *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
865  *
866  * For browsers that don't support the attribute.ownerElement property, this
867  *     method cannot be called on attribute nodes.
868  *     
869  * Parameters:
870  * node - {DOMElement} The node from which to start looking.
871  * prefix - {String} The prefix to lookup or null to lookup the default namespace.
872  * 
873  * Returns:
874  * {String} The namespace URI for the given prefix.  Returns null if the prefix
875  *     cannot be found or the node is the wrong type.
876  */
877 OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(
878     OpenLayers.Format.XML.prototype.lookupNamespaceURI,
879     OpenLayers.Format.XML.prototype
880 );