-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxrange.js
121 lines (103 loc) · 4.61 KB
/
xrange.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
var xRange = {
// Thank you, Mozilla
// https://developer.mozilla.org/en-US/docs/Using_XPath
getNodeFromXPath: function(aExpr) {
var aNode = document;
var xpe = new XPathEvaluator();
var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ? aNode.documentElement : aNode.ownerDocument.documentElement);
var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
var found = [];
var res;
while (res = result.iterateNext())
found.push(res);
return found[0];
},
/**
* Gets an XPath for an node which describes its hierarchical location.
* http://stackoverflow.com/questions/3454526/how-to-calculate-the-_xpath-position-of-an-element-using-javascript
*/
getXPathFromNode: function (node) {
"use strict";
// if (node && node.id) {
// return '//*[@id="' + node.id + '"]';
// }
var paths = [];
// Use nodeName (instead of localName) so namespace prefix is included (if any).
for (; node && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE); node = node.parentNode) {
var index = 0;
if (node.id) {
// if the document illegally re-uses an id, then we can't use it as a unique identifier
var selector = '[id="' + node.id + '"]';
// no jquery
var length = document.querySelectorAll(selector).length;
if (length === 1) {
// because the first item of the path array is prefixed with '/', this will become
// a double slash (select all elements). But as there's only one result, we can use [1]
// eg: //*[@id='something'][1]/div/text()
paths.splice(0, 0, '/*[@id="' + node.id + '"][1]');
break;
}
console.log("document contains " + length + " elements with selector " + selector + ". Ignoring");
}
for (var sibling = node.previousSibling; sibling; sibling = sibling.previousSibling) {
// Ignore document type declaration.
if (sibling.nodeType === Node.DOCUMENT_TYPE_NODE) {
continue;
}
if (sibling.nodeName === node.nodeName) {
index++;
}
}
var tagName = (node.nodeType === Node.ELEMENT_NODE ? node.nodeName.toLowerCase() : "text()");
var pathIndex = (index ? "[" + (index+1) + "]" : "");
paths.splice(0, 0, tagName + pathIndex);
}
return paths.length ? "/" + paths.join("/") : null;
},
/**
* Convert a standard Range object to an XPathRange
* @param {object} range Range object
* @return {object} (identifies containers by their _xpath)
*/
getXRangeFromRange: function (range) {
"use strict";
return {
startContainerPath: this.getXPathFromNode(range.startContainer),
startOffset: range.startOffset,
endContainerPath: this.getXPathFromNode(range.endContainer),
endOffset: range.endOffset,
collapsed: range.collapsed
};
},
/**
* Create a standard Range() object, given and XPathRange object
* @param xpathRange see {@link #createXPathRangeFromRange}
* @return {Range} range object, or null if start or end containers couldn't be evaluated
*/
getRangeFromXRange: function (xpathRange) {
"use strict";
var startContainer, endContainer, endOffset, evaluator = new XPathEvaluator();
// must have legal start and end container nodes
startContainer = evaluator.evaluate(xpathRange.startContainerPath,
document.documentElement, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (!startContainer.singleNodeValue) {
return null;
}
if (xpathRange.collapsed || !xpathRange.endContainerPath) {
endContainer = startContainer;
endOffset = xpathRange.startOffset;
} else {
endContainer = evaluator.evaluate(xpathRange.endContainerPath,
document.documentElement, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (!endContainer.singleNodeValue) {
return null;
}
endOffset = xpathRange.endOffset;
}
// map to range object
var range = document.createRange();
range.setStart(startContainer.singleNodeValue, xpathRange.startOffset);
range.setEnd(endContainer.singleNodeValue, endOffset);
return range;
}
};