150 lines
3.9 KiB
JavaScript
150 lines
3.9 KiB
JavaScript
var isStr = require('./isStr');
|
|
var Class = require('./Class');
|
|
exports = function(xpath, optimized) {
|
|
if (isStr(xpath)) {
|
|
return findEl(xpath);
|
|
} else {
|
|
return getXpath(xpath, optimized);
|
|
}
|
|
};
|
|
function findEl(xpath) {
|
|
var ret = [];
|
|
var nodesSnapshot = document.evaluate(
|
|
xpath,
|
|
document,
|
|
null,
|
|
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
|
|
null
|
|
);
|
|
for (var i = 0; i < nodesSnapshot.snapshotLength; i++) {
|
|
ret.push(nodesSnapshot.snapshotItem(i));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function getXpath(node) {
|
|
var optimized =
|
|
arguments.length > 1 && arguments[1] !== undefined
|
|
? arguments[1]
|
|
: false;
|
|
if (node.nodeType === Node.DOCUMENT_NODE) {
|
|
return '/';
|
|
}
|
|
var steps = [];
|
|
var contextNode = node;
|
|
while (contextNode) {
|
|
var step = xPathValue(contextNode, optimized);
|
|
if (!step) {
|
|
break;
|
|
}
|
|
steps.push(step);
|
|
if (step.optimized) {
|
|
break;
|
|
}
|
|
contextNode = contextNode.parentNode;
|
|
}
|
|
steps.reverse();
|
|
return (steps.length && steps[0].optimized ? '' : '/') + steps.join('/');
|
|
}
|
|
function xPathValue(node, optimized) {
|
|
var ownValue;
|
|
var ownIndex = xPathIndex(node);
|
|
if (ownIndex === -1) {
|
|
return null;
|
|
}
|
|
switch (node.nodeType) {
|
|
case Node.ELEMENT_NODE:
|
|
if (optimized && node.getAttribute('id')) {
|
|
return new Step(
|
|
'//*[@id="' + node.getAttribute('id') + '"]',
|
|
true
|
|
);
|
|
}
|
|
ownValue = node.localName;
|
|
break;
|
|
case Node.ATTRIBUTE_NODE:
|
|
ownValue = '@' + node.nodeName();
|
|
break;
|
|
case Node.TEXT_NODE:
|
|
case Node.CDATA_SECTION_NODE:
|
|
ownValue = 'text()';
|
|
break;
|
|
case Node.PROCESSING_INSTRUCTION_NODE:
|
|
ownValue = 'processing-instruction()';
|
|
break;
|
|
case Node.COMMENT_NODE:
|
|
ownValue = 'comment()';
|
|
break;
|
|
case Node.DOCUMENT_NODE:
|
|
ownValue = '';
|
|
break;
|
|
default:
|
|
ownValue = '';
|
|
break;
|
|
}
|
|
if (ownIndex > 0) {
|
|
ownValue += '[' + ownIndex + ']';
|
|
}
|
|
return new Step(ownValue, node.nodeType === Node.DOCUMENT_NODE);
|
|
}
|
|
function xPathIndex(node) {
|
|
function areNodesSimilar(left, right) {
|
|
if (left === right) {
|
|
return true;
|
|
}
|
|
if (
|
|
left.nodeType === Node.ELEMENT_NODE &&
|
|
right.nodeType === Node.ELEMENT_NODE
|
|
) {
|
|
return left.localName === right.localName;
|
|
}
|
|
if (left.nodeType === right.nodeType) {
|
|
return true;
|
|
}
|
|
var leftType =
|
|
left.nodeType === Node.CDATA_SECTION_NODE
|
|
? Node.TEXT_NODE
|
|
: left.nodeType;
|
|
var rightType =
|
|
right.nodeType === Node.CDATA_SECTION_NODE
|
|
? Node.TEXT_NODE
|
|
: right.nodeType;
|
|
return leftType === rightType;
|
|
}
|
|
var siblings = node.parentNode ? node.parentNode.children : null;
|
|
if (!siblings) {
|
|
return 0;
|
|
}
|
|
var hasSameNamedElements;
|
|
for (var i = 0; i < siblings.length; ++i) {
|
|
if (areNodesSimilar(node, siblings[i]) && siblings[i] !== node) {
|
|
hasSameNamedElements = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!hasSameNamedElements) {
|
|
return 0;
|
|
}
|
|
var ownIndex = 1;
|
|
for (var _i = 0; _i < siblings.length; ++_i) {
|
|
if (areNodesSimilar(node, siblings[_i])) {
|
|
if (siblings[_i] === node) {
|
|
return ownIndex;
|
|
}
|
|
++ownIndex;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
var Step = Class({
|
|
initialize: function(value, optimized) {
|
|
this.value = value;
|
|
this.optimized = optimized || false;
|
|
},
|
|
toString: function() {
|
|
return this.value;
|
|
}
|
|
});
|
|
|
|
module.exports = exports;
|