goog.provide('ol.parser.XML'); goog.require('goog.dom.xml'); goog.require('ol.parser.Parser'); /** * @constructor * @extends {ol.parser.Parser} * @todo stability experimental */ ol.parser.XML = function() { if (goog.global.ActiveXObject) { this.xmldom = new ActiveXObject('Microsoft.XMLDOM'); } this.regExes = { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }; }; goog.inherits(ol.parser.XML, ol.parser.Parser); /** * Shorthand for applying one of the named readers given the node * namespace and local name. Readers take two args (node, obj) and * generally extend or modify the second. * * @param {Element|Document} node The node to be read (required). * @param {Object} obj The object to be modified (optional). * @return {Object} The input object, modified (or a new one if none was * provided). */ ol.parser.XML.prototype.readNode = function(node, obj) { if (!obj) { obj = {}; } var group = this.readers[node.namespaceURI] || this.readers[this.defaultNamespaceURI]; if (group) { var local = node.localName || node.nodeName.split(':').pop(); var reader = group[local] || group['*']; if (reader) { reader.apply(this, [node, obj]); } } return obj; }; /** * Shorthand for applying the named readers to all children of a node. * For each child of type 1 (element), is called. * * @param {Element|Document} node The node to be read (required). * @param {Object} obj The object to be modified (optional). * @return {Object} The input object, modified. */ ol.parser.XML.prototype.readChildNodes = function(node, obj) { if (!obj) { obj = {}; } var children = node.childNodes; var child; for (var i = 0, len = children.length; i < len; ++i) { child = children[i]; if (child.nodeType == 1) { this.readNode(child, obj); } } return obj; }; /** * Get the textual value of the node if it exists, or return an * optional default string. Returns an empty string if no first child * exists and no default value is supplied. * * @param {Element} node The element used to look for a first child value. * @param {string} def Optional string to return in the event that no * first child value exists. * @return {string} The value of the first child of the given node. */ ol.parser.XML.prototype.getChildValue = function(node, def) { var value = def || ''; if (node) { for (var child = node.firstChild; child; child = child.nextSibling) { switch (child.nodeType) { case 3: // text node case 4: // cdata section value += child.nodeValue; break; default: break; } } } return value; }; /** * Get an attribute node given the namespace URI and local name. * * @param {Element} node Node on which to search for attribute nodes. * @param {string} uri Namespace URI. * @param {string} name Local name of the attribute (without the prefix). * @return {?Element} An attribute node or null if none found. */ ol.parser.XML.prototype.getAttributeNodeNS = function(node, uri, name) { var attributeNode = null; if (node.getAttributeNodeNS) { attributeNode = node.getAttributeNodeNS(uri, name); } else { var attributes = node.attributes; var potentialNode, fullName; for (var i = 0, len = attributes.length; i < len; ++i) { potentialNode = attributes[i]; if (potentialNode.namespaceURI == uri) { fullName = (potentialNode.prefix) ? (potentialNode.prefix + ':' + name) : name; if (fullName == potentialNode.nodeName) { attributeNode = potentialNode; break; } } } } return attributeNode; }; /** * Get an attribute value given the namespace URI and local name. * * @param {Element} node Node on which to search for an attribute. * @param {string} uri Namespace URI. * @param {string} name Local name of the attribute (without the prefix). * @return {string} An attribute value or and empty string if none found. */ ol.parser.XML.prototype.getAttributeNS = function(node, uri, name) { var attributeValue = ''; if (node.getAttributeNS) { attributeValue = node.getAttributeNS(uri, name) || ''; } else { var attributeNode = this.getAttributeNodeNS(node, uri, name); if (attributeNode) { attributeValue = attributeNode.nodeValue; } } return attributeValue; }; /** * Create a new element with namespace. This node can be appended to * another node with the standard node.appendChild method. For * cross-browser support, this method must be used instead of * document.createElementNS. * * @param {string} name The qualified name of the element (prefix:localname). * @param {string=} opt_uri Namespace URI for the element. * @return {Element} A DOM element with namespace. */ ol.parser.XML.prototype.createElementNS = function(name, opt_uri) { var uri = opt_uri ? opt_uri : this.defaultNamespaceURI; var element; if (this.xmldom) { element = this.xmldom.createNode(1, name, uri); } else { element = document.createElementNS(uri, name); } return element; }; /** * Shorthand for applying one of the named writers and appending the * results to a node. * * @param {string} name The name of a node to generate. Only use a local name. * @param {Object|string|number} obj Structure containing data for the writer. * @param {?string=} opt_uri The name space uri to which the node * belongs. * @param {Element=} opt_parent Result will be appended to this node. If no * parent is supplied, the node will not be appended to anything. * @return {?Element} The child node. */ ol.parser.XML.prototype.writeNode = function(name, obj, opt_uri, opt_parent) { var child = null; if (goog.isDef(this.writers)) { var uri = opt_uri ? opt_uri : this.defaultNamespaceURI; child = this.writers[uri][name].apply(this, [obj]); if (opt_parent && child) { opt_parent.appendChild(child); } } return child; }; /** * Create a text node. This node can be appended to another node with * the standard node.appendChild method. For cross-browser support, * this method must be used instead of document.createTextNode. * * @param {string} text The text of the node. * @return {Element} A DOM text node. */ ol.parser.XML.prototype.createTextNode = function(text) { var node; if (this.xmldom) { node = this.xmldom.createTextNode(text); } else { node = document.createTextNode(text); } return node; }; /** * Adds a new attribute or changes the value of an attribute with the given * namespace and name. * * @param {Element} node Element node on which to set the attribute. * @param {string} uri Namespace URI for the attribute. * @param {string} name Qualified name (prefix:localname) for the attribute. * @param {string} value Attribute value. */ ol.parser.XML.prototype.setAttributeNS = function(node, uri, name, value) { if (node.setAttributeNS) { node.setAttributeNS(uri, name, value); } else { if (this.xmldom) { if (uri) { var attribute = node.ownerDocument.createNode( 2, name, uri); attribute.nodeValue = value; node.setAttributeNode(attribute); } else { node.setAttribute(name, value); } } else { throw new Error('setAttributeNS not implemented'); } } }; /** * Serializes a node. * * @param {Element} node Element node to serialize. * @return {string} The serialized XML string. */ ol.parser.XML.prototype.serialize = function(node) { if (this.xmldom) { return node.xml; } else if (node.nodeType == 1) { // Add nodes to a document before serializing. Everything else // is serialized as is. This is also needed to get all namespaces // defined in some browsers such as Chrome (xmlns attributes). var doc = document.implementation.createDocument('', '', null); if (doc.importNode) { doc.appendChild(doc.importNode(node, true)); } else { doc.appendChild(node); } return goog.dom.xml.serialize(doc); } else { return goog.dom.xml.serialize(node); } }; /** * Create a document fragment node that can be appended to another node * created by createElementNS. This will call * document.createDocumentFragment outside of IE. In IE, the ActiveX * object's createDocumentFragment method is used. * * @return {Element} A document fragment. */ ol.parser.XML.prototype.createDocumentFragment = function() { var element; if (this.xmldom) { element = this.xmldom.createDocumentFragment(); } else { element = document.createDocumentFragment(); } return element; };