@qooxdoo/framework
Version:
The JS Framework for Coders
366 lines (320 loc) • 11.8 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Sebastian Werner (wpbasti)
* Andreas Ecker (ecker)
* Fabian Jakobs (fjakobs)
************************************************************************ */
/**
* Cross browser XML Element API
*
* API to select, query and serialize XML elements.
*
* Further information:
*
* * <a href="https://developer.mozilla.org/en-US/docs/Parsing_and_serializing_XML">MDN Parsing and Serializing XML</a>
*
* Please note that nodes selected using the <code>selectSingleNode()</code> and
* <code>selectNodes()</code> methods remain in their document context so
* <code>qx.xml.Element.selectNodes(foo, "//bar");</code>
* will search the entire document for any nodes named "bar", not just the
* <code>foo</code> node.
*/
qx.Class.define("qx.xml.Element", {
statics: {
__xpe: null,
/**
* @type {Boolean} <code>true</code> if the native XMLSerializer should be used,
* <code>false</code> otherwise.
*/
XML_SERIALIZER: false,
/**
* The subtree rooted by the specified element or document is serialized to a string.
*
* @param element {Element | Document} The root of the subtree to be serialized. This could be any node, including a Document.
* @return {String} Serialized subtree
*/
serialize(element) {
if (qx.dom.Node.isDocument(element)) {
element = element.documentElement;
}
if (this.XML_SERIALIZER) {
return new XMLSerializer().serializeToString(element);
} else {
return element.xml || element.outerHTML;
}
},
/**
* Selects the first XmlNode that matches the XPath expression.
*
* @param element {Element | Document} root element for the search
* @param query {String} XPath query
* @param namespaces {Map} optional map of prefixes and their namespace URIs
* @return {Element} first matching element
*/
selectSingleNode(element, query, namespaces) {
if (qx.core.Environment.get("html.xpath")) {
if (!this.__xpe) {
this.__xpe = new XPathEvaluator();
}
var xpe = this.__xpe;
var resolver;
if (namespaces) {
resolver = function (prefix) {
return namespaces[prefix] || null;
};
} else {
resolver = xpe.createNSResolver(element);
}
try {
return xpe.evaluate(
query,
element,
resolver,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
} catch (err) {
throw new Error(
"selectSingleNode: query: " +
query +
", element: " +
element +
", error: " +
err
);
}
}
if (qx.core.Environment.get("xml.selectsinglenode")) {
if (namespaces) {
var namespaceString = "";
for (var prefix in namespaces) {
namespaceString +=
"xmlns:" + prefix + "='" + namespaces[prefix] + "' ";
}
// If the element is a node, set the selection namespace on its parent document.
if (element.ownerDocument) {
element.ownerDocument.setProperty(
"SelectionNamespaces",
namespaceString
);
}
// element is a document
else {
element.setProperty("SelectionNamespaces", namespaceString);
}
}
return element.selectSingleNode(query);
}
throw new Error("No XPath implementation available!");
},
/**
* Selects a list of nodes matching the XPath expression.
*
* @param element {Element | Document} root element for the search
* @param query {String} XPath query
* @param namespaces {Map} optional map of prefixes and their namespace URIs
* @return {Element[]} List of matching elements
*/
selectNodes(element, query, namespaces) {
if (qx.core.Environment.get("html.xpath")) {
var xpe = this.__xpe;
if (!xpe) {
this.__xpe = xpe = new XPathEvaluator();
}
var resolver;
if (namespaces) {
resolver = function (prefix) {
return namespaces[prefix] || null;
};
} else {
resolver = xpe.createNSResolver(element);
}
try {
var result = xpe.evaluate(
query,
element,
resolver,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
} catch (err) {
throw new Error(
"selectNodes: query: " +
query +
", element: " +
element +
", error: " +
err
);
}
var nodes = [];
for (var i = 0; i < result.snapshotLength; i++) {
nodes[i] = result.snapshotItem(i);
}
return nodes;
}
if (qx.core.Environment.get("xml.selectnodes")) {
if (namespaces) {
var namespaceString = "";
for (var prefix in namespaces) {
namespaceString +=
"xmlns:" + prefix + "='" + namespaces[prefix] + "' ";
}
// If the element is a node, set the selection namespace on its parent document.
if (element.ownerDocument) {
element.ownerDocument.setProperty(
"SelectionNamespaces",
namespaceString
);
}
// element is a document
else {
element.setProperty("SelectionNamespaces", namespaceString);
}
}
return element.selectNodes(query);
}
throw new Error("No XPath implementation available!");
},
/**
* Returns a list of elements with the given tag name belonging to the given namespace
*
* (See
* <a href="https://developer.mozilla.org/en-US/docs/DOM/element.getElementsByTagNameNS">MDN
* Reference</a>).
*
* @param element {Element | Document} the element from where the search should start.
* Note that only the descendants of this element are included in the search, not the node itself.
* @param namespaceURI {var} is the namespace URI of elements to look for . For example, if you need to look
* for XHTML elements, use the XHTML namespace URI, <tt>http://www.w3.org/1999/xhtml/</tt>.
* @param tagname {String} the tagname to look for
* @return {Element[]} a list of found elements in the order they appear in the tree.
*/
getElementsByTagNameNS(element, namespaceURI, tagname) {
if (qx.core.Environment.get("xml.getelementsbytagnamens")) {
return element.getElementsByTagNameNS(namespaceURI, tagname);
}
if (qx.core.Environment.get("xml.domproperties")) {
var doc = element.ownerDocument || element;
doc.setProperty("SelectionLanguage", "XPath");
doc.setProperty(
"SelectionNamespaces",
"xmlns:ns='" + namespaceURI + "'"
);
return qx.xml.Element.selectNodes(
element,
"descendant-or-self::ns:" + tagname
);
}
throw new Error("The client does not support this operation!");
},
/**
* Selects the first XmlNode that matches the XPath expression and returns the text content of the element
*
* @param element {Element|Document} root element for the search
* @param query {String} XPath query
* @return {String} the joined text content of the found element or null if not appropriate.
*/
getSingleNodeText(element, query) {
var node = this.selectSingleNode(element, query);
return qx.dom.Node.getText(node);
},
/**
* Adds or sets an attribute with the given namespace on a node
*
* @param document {Document} The node's parent document, created e.g. by
* {@link qx.xml.Document#create}
* @param element {Element} XML/DOM element to modify
* @param namespaceUri {String} Namespace URI
* @param name {String} Attribute name
* @param value {String} Attribute value
*/
setAttributeNS(document, element, namespaceUri, name, value) {
if (qx.core.Environment.get("xml.attributens")) {
element.setAttributeNS(namespaceUri, name, value);
} else if (qx.core.Environment.get("xml.createnode")) {
var attr = document.createNode(2, name, namespaceUri);
attr.nodeValue = value;
element.setAttributeNode(attr);
} else {
throw new Error("The client does not support this operation!");
}
},
/**
* Get the value of the attribute with the given namespace and name
*
* @param element {Element} XML/DOM element to modify
* @param namespaceUri {String} Namespace URI
* @param name {String} Attribute name
* @return {String} the value of the attribute, empty string if not found
*/
getAttributeNS(element, namespaceUri, name) {
if (qx.core.Environment.get("xml.attributens")) {
var value = element.getAttributeNS(namespaceUri, name);
return value === null ? "" : value;
}
if (qx.core.Environment.get("xml.getqualifieditem")) {
var attributes = element.attributes;
var value = null;
if (attributes) {
var attribute = attributes.getQualifiedItem(name, namespaceUri);
if (attribute) {
value = attribute.nodeValue;
}
}
return value === null ? "" : value;
}
throw new Error("The client does not support this operation!");
},
/**
* Creates an element with the given namespace and appends it to an existing
* element
*
* @param document {Document} The node's parent document, created e.g. by
* {@link qx.xml.Document#create}
* @param parent {Element} The parent element for the new sub-element
* @param name {String} The new element's name
* @param namespaceUri {String} Namespace URI for the new element
*
* @return {Element} The newly created sub-element
*/
createSubElementNS(document, parent, name, namespaceUri) {
if (qx.core.Environment.get("xml.createelementns")) {
// the "x" prefix has no importance. when there's a conflict,
// mozilla engine assigns an alternative prefix automatically.
// not putting a prefix means to assign default namespace prefix
// to the given namespace uri.
var node = document.createElementNS(namespaceUri, "x:" + name);
parent.appendChild(node);
return node;
}
if (qx.core.Environment.get("xml.createnode")) {
var node = document.createNode(1, name, namespaceUri);
parent.appendChild(node);
return node;
}
throw new Error("The client does not support this operation!");
}
},
/*
*****************************************************************************
DEFER
*****************************************************************************
*/
defer(statics) {
statics.XML_SERIALIZER =
window.XMLSerializer &&
!(
qx.core.Environment.get("engine.name") == "mshtml" &&
qx.core.Environment.get("engine.version") >= 9 &&
qx.core.Environment.get("browser.documentmode") >= 9
);
}
});