XML.js 31 KB

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