/*
|
* @copyright
|
* Copyright © Microsoft Open Technologies, Inc.
|
*
|
* All Rights Reserved
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http: *www.apache.org/licenses/LICENSE-2.0
|
*
|
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
|
* OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
* ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A
|
* PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT.
|
*
|
* See the Apache License, Version 2.0 for the specific language
|
* governing permissions and limitations under the License.
|
*/
|
'use strict';
|
|
var _ = require('underscore');
|
var select = require('xpath.js');
|
var XMLSerializer = require('xmldom').XMLSerializer;
|
|
var constants = require('./constants');
|
|
/**
|
* @namespace XmlUtil
|
* @private
|
*/
|
|
var XPATH_PATH_TEMPLATE = '*[local-name() = \'LOCAL_NAME\' and namespace-uri() = \'NAMESPACE\']';
|
/**
|
* The xpath implementation being used does not have a way of matching expanded namespace.
|
* This method takes an xpath query and expands all of the namespaces involved. It then
|
* re-writes the query in to a longer form that directory matches the correct namespaces.
|
* @private
|
* @static
|
* @memberOf XmlUtil
|
* @param {string} xpath The expath query string to expand.
|
* @returns {string} An expanded xpath query.
|
*/
|
function expandQNames(xpath) {
|
var namespaces = constants.XmlNamespaces;
|
var pathParts = xpath.split('/');
|
for (var i=0; i < pathParts.length; i++) {
|
if (pathParts[i].indexOf(':') !== -1) {
|
var QNameParts = pathParts[i].split(':');
|
if (QNameParts.length !== 2) {
|
throw new Error('Unable to parse XPath string : ' + xpath + ' : with QName : ' + pathParts[i]);
|
}
|
var expandedPath = XPATH_PATH_TEMPLATE.replace('LOCAL_NAME', QNameParts[1]);
|
expandedPath = expandedPath.replace('NAMESPACE', namespaces[QNameParts[0]]);
|
pathParts[i] = expandedPath;
|
}
|
}
|
return pathParts.join('/');
|
}
|
|
var exports = {
|
|
/**
|
* Performs an xpath select that does appropriate namespace matching since the imported
|
* xpath module does not properly handle namespaces.
|
* @static
|
* @memberOf XmlUtil
|
* @param {object} dom A dom object created by the xmldom module
|
* @param {string} xpath An xpath expression
|
* @return {array} An array of matching dom nodes.
|
*/
|
xpathSelect : function (dom, xpath) {
|
return select(dom, expandQNames(xpath));
|
},
|
|
/**
|
* Given a dom node serializes all immediate children that are xml elements.
|
* @static
|
* @memberOf XmlUtil
|
* @param {object} node An xml dom node.
|
* @return {string} Serialized xml.
|
*/
|
serializeNodeChildren : function(node) {
|
var doc = '';
|
var sibling = node.firstChild;
|
var serializer = new XMLSerializer();
|
|
while (sibling) {
|
if (this.isElementNode(sibling)) {
|
doc += serializer.serializeToString(sibling);
|
}
|
sibling = sibling.nextSibling;
|
}
|
|
return doc !== '' ? doc : null;
|
},
|
|
/**
|
* Detects whether the passed in dom node represents an xml element.
|
* @static
|
* @memberOf XmlUtil
|
* @param {object} node An xml dom node.
|
* @return {Boolean} true if the node represents an element.
|
*/
|
isElementNode : function(node) {
|
return _.has(node, 'tagName');
|
},
|
|
/**
|
* Given an xmldom node this function returns any text data contained within.
|
* @static
|
* @memberOf XmlUtil
|
* @param {object} node An xmldom node from which the data should be extracted.
|
* @return {string} Any data found within the element or null if none is found.
|
*/
|
findElementText : function(node) {
|
var sibling = node.firstChild;
|
while (sibling && !sibling.data) {
|
sibling = sibling.nextSibling;
|
}
|
|
return sibling.data ? sibling.data : null;
|
}
|
};
|
|
module.exports = exports;
|