├── .gitignore
├── .npmignore
├── docs
├── parsed expressions.md
├── XPathResult.md
├── xpath methods.md
├── namespace resolvers.md
├── XPathEvaluator.md
├── variable resolvers.md
└── function resolvers.md
├── package.json
├── LICENSE
├── xpath.d.ts
├── README.md
├── yarn.lock
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # .npmignore file
2 | tests/
3 | *.test.js
4 |
--------------------------------------------------------------------------------
/docs/parsed expressions.md:
--------------------------------------------------------------------------------
1 | # Using Parsed Expressions
2 |
3 | The `xpath.parse()` method allows pre-parsing an XPath expression and creating an XPath executor to evaluate the XPath as many times as needed.
4 |
5 | This can provide a performance benefit if you plan to evaluate the same XPath multiple times, because the expression only needs to be parsed once.
6 |
7 | This also provides access to additional features such as the use of variables and custom XPath functions, which are not available using the evaluation methods on the `xpath` object.
8 |
9 | #### xpath.parse(expression)
10 |
11 | Parses the specified XPath expression and returns an `XPathEvaluator`. See the [documentation page](XPathEvaluator.md) for API details.
12 |
13 | `expression` should be a string.
14 |
15 | Example usage:
16 |
17 | ```js
18 | var evaluator = xpath.parse('/book/characters');
19 | ```
20 |
21 |
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "xpath",
3 | "version": "0.0.34",
4 | "description": "DOM 3 XPath implemention and helper for node.js and the web",
5 | "engines": {
6 | "node": ">=0.6.0"
7 | },
8 | "author": {
9 | "name": "Cameron McCormack"
10 | },
11 | "contributors": [
12 | {
13 | "name": "goto100"
14 | },
15 | {
16 | "name": "James Rishe"
17 | }
18 | ],
19 | "devDependencies": {
20 | "@xmldom/xmldom": "^0.8.9",
21 | "es-check": "^7.1.1",
22 | "func-xml": "^0.0.10",
23 | "mocha": "^9.0.2"
24 | },
25 | "typings": "./xpath.d.ts",
26 | "scripts": {
27 | "test": "mocha",
28 | "validate": "es-check es5 xpath.js"
29 | },
30 | "repository": {
31 | "type": "git",
32 | "url": "https://github.com/goto100/xpath.git"
33 | },
34 | "main": "xpath.js",
35 | "files": [
36 | "xpath.d.ts"
37 | ],
38 | "keywords": [
39 | "xpath",
40 | "xml"
41 | ],
42 | "license": "MIT"
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Cameron McCormack
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/docs/XPathResult.md:
--------------------------------------------------------------------------------
1 | # XPathResult interface
2 |
3 | Represents the result of an XPath expression. This interface is used for the parameters passed into custom functions
4 | used in [function resolvers](function resolvers.md) and can represent a number, a string, a boolean value, or a node set.
5 |
6 | ## Methods
7 |
8 | ```js
9 | booleanValue() -> boolean
10 | ```
11 |
12 | Returns the boolean value of the result in accordance with the XPath 1.0 spec.
13 |
14 | ```js
15 | numberValue() -> number
16 | ```
17 |
18 | Returns the numeric value of the result in accordance with the XPath 1.0 spec.
19 |
20 | ```js
21 | stringValue() -> string
22 | ```
23 |
24 | Returns the string value of the result in accordance with the XPath 1.0 spec.
25 |
26 | ## Methods and properties that are only present on `XPathResult`s representing node sets
27 |
28 | ```js
29 | toArray() -> Array of nodes
30 | ```
31 |
32 | Returns an array of the nodes in the node set, in document order.
33 |
34 | ```js
35 | first() -> Node
36 | ```
37 |
38 | Returns the first node in the node set, in document order.
39 |
40 | ```js
41 | size -> number
42 | ```
43 |
44 | Returns the number of nodes in this node set
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/docs/xpath methods.md:
--------------------------------------------------------------------------------
1 | # xpath methods
2 |
3 | This page details the methods exposed on the `xpath` object.
4 |
5 | ### `xpath.parse(expression)`
6 |
7 | Creates a parsed expression. See the [documentation page](parsed%20expressions.md) for details.
8 |
9 | ### `xpath.select(expression[, node[, single]])`
10 |
11 | Evaluates an XPath expression and returns the result. The return value is determined based on the result type of the expression (which can always be predicted ahead of time based on the expression's syntax):
12 |
13 | - A boolean value if the expression evaluates to a boolean value.
14 | - A number if the expression evaluates to a numeric value.
15 | - A string if the expression evaluates to a string.
16 | - If the expression evaluates to a nodeset:
17 | - An array of 0 or more nodes if `single` is unspecified or falsy
18 | - A single node (the first node in document order) or `undefined` if `single` is truthy
19 |
20 | `node` is optional and if specified, is used as the context node for evaluating the expression. (It is necessary if the expression makes use of the current contex.)
21 |
22 | `single` is optional and is ignored if the expression evaluates to anything other than a nodeset.
23 |
24 | ### `xpath.select1(expression[, node])`
25 |
26 | Alias for [`xpath.select(expression, node, true)`](#xpathselectexpression-node-single). Selects a single node or value.
27 |
28 | ### `xpath.useNamespaces(mappings)`
29 |
30 | Produces a function with the same signature as [`xpath.select()`](#xpathselectexpression-node-single) that evaluates the provided xpath expression using the XML namespace definitions provided in `mapppings`.
31 |
32 | `mappings` should be an object with namespace prefixes as its property names and namespace URIs as its property values.
33 |
34 | Example usage:
35 |
36 | ```js
37 | var expr = xpath.useNamespaces({ hp: 'http://www.example.com/harryPotter', bk: 'http://www.example.com/books' });
38 | var result = expr('/bk:books/bk:book[@name = "Harry Potter and the Half-Blood Prince"]/hp:characters', myBooks);
39 | ```
40 |
--------------------------------------------------------------------------------
/docs/namespace resolvers.md:
--------------------------------------------------------------------------------
1 | # Namespace Resolvers
2 |
3 | The methods on the [XPathEvaluator](XPathEvaluator.md) type can optionally take a namespace resolver to resolve
4 | namespace references in the XPath expression being evaluated.
5 |
6 | There are three ways to specify a namespace resolver and you can use any one of them depending on which is
7 | most suited to your particular situation.
8 |
9 | ## Namespace Resolver Type 1: Plain object
10 |
11 | A plain object with namespace prefixes as the keys and namespace URIs as the values:
12 |
13 | Example usage:
14 |
15 | ```js
16 | var evaluator = xpath.parse('/bk:book/hp:characters');
17 | var characters = evaluator.select({
18 | node: myBookNode,
19 | namespaces: {
20 | 'bk': 'http://sample.org/books/',
21 | 'hp': 'http://sample.org/harrypotter/'
22 | }
23 | });
24 | ```
25 |
26 | ## Namespace Resolver Type 2: Function
27 |
28 | A function that takes a namespace prefix as a parameter and returns the corresponding namespace URI.
29 |
30 | Example usage:
31 |
32 | ```js
33 | var evaluator = xpath.parse('/bk:book/hp:characters');
34 | var characters = evaluator.select({
35 | node: myBookNode,
36 | namespaces: function (prefix) {
37 | if (prefix === 'bk') {
38 | return 'http://sample.org/books/';
39 | }
40 | if (prefix === 'hp') {
41 | return 'http://sample.org/books/';
42 | }
43 | }
44 | });
45 | ```
46 |
47 | ## Namespace Resolver Type 3: Object with `getNamespace` method
48 |
49 | An object with a method named `getNamespace` that works in the same way as the function-based namespace resolver
50 | described above.
51 |
52 | Example usage:
53 |
54 | ```js
55 | var evaluator = xpath.parse('/bk:book/hp:characters');
56 | var characters = evaluator.select({
57 | node: myBookNode,
58 | namespaces: {
59 | getNamespace: function (prefix) {
60 | if (prefix === 'bk') {
61 | return 'http://sample.org/books/';
62 | }
63 | if (prefix === 'hp') {
64 | return 'http://sample.org/books/';
65 | }
66 | }
67 | }
68 | });
69 | ```
70 |
--------------------------------------------------------------------------------
/docs/XPathEvaluator.md:
--------------------------------------------------------------------------------
1 | # `XPathEvaluator`
2 |
3 | The `xpath.parse()` method returns an `XPathEvaluator`, which contains the following methods.
4 |
5 | Each of these methods takes an optional `options` object, which can contain any of the following properties. See the links for each item for further details:
6 |
7 | - `namespaces` - a [namespace resolver](namespace%20resolvers.md)
8 |
9 | - `variables` - a [variable resolver](variable%20resolvers.md)
10 |
11 | - `functions` - a [function resolver](function%20resolvers.md)
12 |
13 | - `node` - the context node for evaluating the expression
14 |
15 | Example usage:
16 |
17 | ```js
18 | var evaluator = xpath.parse('/characters/character[@greeting = $greeting]');
19 | var character = evaluator.select1({
20 | node: myCharacterDoc,
21 | variables: {
22 | greeting: "Hello, I'm Harry, Harry Potter."
23 | }
24 | });
25 | ```
26 |
27 | ## `XPathEvaluator` methods
28 |
29 | `evaluate([options])`
30 |
31 | Evaluates the XPath expression and returns the result. The resulting type is determined based on the type of the expression, using the same criteria as [`xpath.select`](xpath%20methods.md).
32 |
33 | `evaluateNumber([options])`
34 |
35 | Evaluates the XPath expression and returns the result as a number.
36 |
37 | `evaluateString([options])`
38 |
39 | Evaluates the XPath expression and returns the result as a string.
40 |
41 | `evaluateBoolean([options])`
42 |
43 | Evaluates the XPath expression and returns the result as a boolean value.
44 |
45 | `evaluateNodeSet([options])`
46 |
47 | Evaluates the XPath expression and returns the result as an XNodeSet. See the [documentation page](#) for details on this interface.
48 |
49 | This is only valid for expressions that evaluate to a node set.
50 |
51 | `select([options])`
52 |
53 | Evaluates the XPath expression and returns an array of the resulting nodes, in document order.
54 |
55 | This is only valid for expressions that evaluate to a node set.
56 |
57 | `select1([options])`
58 |
59 | Evaluates the XPath expression and the first node in the resulting node set, in document order. Returns `undefined` if the resulting node set is empty.
60 |
61 | This is only valid for expressions that evaluate to a node set.
62 |
63 |
--------------------------------------------------------------------------------
/xpath.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | export type SelectedValue = Node | string | number | boolean | null;
4 |
5 | export type SelectReturnType = Array | SelectedValue;
6 | export type SelectSingleReturnType = SelectedValue;
7 |
8 | export interface XPathSelect {
9 | (expression: string, node: Node): SelectReturnType;
10 | (expression: string, node: Node, single: false): SelectReturnType;
11 | (expression: string, node: Node, single: true): SelectSingleReturnType;
12 | }
13 |
14 | /**
15 | * Evaluate an XPath expression against a DOM node.
16 | */
17 | export function select(expression: string, node: Node): SelectReturnType;
18 | export function select(expression: string, node: Node, single: false): SelectReturnType;
19 | export function select(expression: string, node: Node, single: true): SelectSingleReturnType;
20 |
21 | /**
22 | * Evaluate an xpath expression against a DOM node, returning the first result only.
23 | */
24 | export function select1(expression: string, node: Node): SelectSingleReturnType;
25 |
26 | /**
27 | * Evaluate an XPath expression against a DOM node using a given namespace resolver.
28 | */
29 | export function selectWithResolver(expression: string, node: Node, resolver?: XPathNSResolver | null): SelectReturnType;
30 | export function selectWithResolver(expression: string, node: Node, resolver: XPathNSResolver | null, single: false): SelectReturnType;
31 | export function selectWithResolver(expression: string, node: Node, resolver: XPathNSResolver | null, single: true): SelectSingleReturnType;
32 |
33 | /**
34 | * Creates a `select` function that uses the given namespace prefix to URI mappings when evaluating queries.
35 | * @param namespaceMap an object mapping namespace prefixes to namespace URIs. Each key is a prefix; each value is a URI.
36 | * @return a function with the same signature as `xpath.select`
37 | */
38 | export function useNamespaces(namespaceMap: Record): XPathSelect;
39 |
40 | // Type guards to narrow down the type of the selected type of a returned Node object
41 | export function isNodeLike(value: SelectedValue): value is Node;
42 | export function isArrayOfNodes(value: SelectReturnType): value is Node[];
43 | export function isElement(value: SelectedValue): value is Element;
44 | export function isAttribute(value: SelectedValue): value is Attr;
45 | export function isTextNode(value: SelectedValue): value is Text;
46 | export function isCDATASection(value: SelectedValue): value is CDATASection;
47 | export function isProcessingInstruction(value: SelectedValue): value is ProcessingInstruction;
48 | export function isComment(value: SelectedValue): value is Comment;
49 | export function isDocumentNode(value: SelectedValue): value is Document;
50 | export function isDocumentTypeNode(value: SelectedValue): value is DocumentType;
51 | export function isDocumentFragment(value: SelectedValue): value is DocumentFragment;
52 |
--------------------------------------------------------------------------------
/docs/variable resolvers.md:
--------------------------------------------------------------------------------
1 | # Variable Resolvers
2 |
3 | The methods on the [XPathEvaluator](#) type can optionally take a variable resolver to resolve
4 | variable references in the XPath expression being evaluated.
5 |
6 | There are three ways to specify a variable resolver and you can use any one of them depending on which is
7 | most suited to your particular situation.
8 |
9 | Note that if your variables are in a namespace (e.g. `$myVars:var`), you must use the second or third
10 | type as the plain object implementation does not support namespaces.
11 |
12 | ## Variable values
13 |
14 | You can use any of five types of values to specify the values of variables:
15 |
16 | - string
17 | - number
18 | - boolean
19 | - single node (will be treated as a node set)
20 | - array of nodes or array-like collection of nodes (will be treated as a node set)
21 |
22 | ## Variable Resolver Type 1: Plain object
23 |
24 | A plain object with variable names as the keys and variable values as the values.
25 |
26 | Example usage:
27 |
28 | ````javascript
29 | var evaluator = xpath.parse('concat($character1, ", ", $character2, ", and ", $character3)');
30 | var mainCharacters = evaluator.evaluateString({
31 | variables: {
32 | character1: 'Harry',
33 | character2: 'Ron',
34 | character3: 'Hermione'
35 | }
36 | });
37 | ````
38 |
39 | ## Variable Resolver Type 2: Function
40 |
41 | A function that takes a variable name as its first parameter and an optional namespace URI as its second parameter
42 | and returns a value based on the name and namespace.
43 |
44 | Example usage:
45 |
46 | ````javascript
47 | var evaluator = xpath.parse('concat($hp:character1, ", ", $hp:character2, ", and ", $hp:character3)');
48 | var mainCharacters = evaluator.evaluateString({
49 | variables: function (name, namespace) {
50 | if (namespace === 'http://sample.org/harrypotter/') {
51 | switch (name) {
52 | case 'character1': return 'Harry';
53 | case 'character2': return 'Ron';
54 | case 'character3': return 'Hermione';
55 | }
56 | }
57 | },
58 | namespaces: {
59 | hp: 'http://sample.org/harrypotter/'
60 | }
61 | });
62 | ````
63 |
64 | ## Function Resolver Type 3: Object with `getFunction` method
65 |
66 | An object with a method named `getVariable` that works in the same way as the function-based variable resolver
67 | described above.
68 |
69 | Example usage:
70 |
71 | ````javascript
72 | var evaluator = xpath.parse('concat($hp:character1, ", ", $hp:character2, ", and ", $hp:character3)');
73 | var mainCharacters = evaluator.evaluateString({
74 | variables: {
75 | getVariable: function (name, namespace) {
76 | if (namespace === 'http://sample.org/harrypotter/') {
77 | switch (name) {
78 | case 'character1': return 'Harry';
79 | case 'character2': return 'Ron';
80 | case 'character3': return 'Hermione';
81 | }
82 | }
83 | }
84 | },
85 | namespaces: {
86 | hp: 'http://sample.org/harrypotter/'
87 | }
88 | });
89 | ````
90 |
--------------------------------------------------------------------------------
/docs/function resolvers.md:
--------------------------------------------------------------------------------
1 | # Function Resolvers
2 |
3 | The methods on the [XPathEvaluator](XPathEvaluator.md) type can optionally take a function resolver to resolve
4 | function references in the XPath expression being evaluated.
5 |
6 | There are three ways to specify a function resolver and you can use any one of them depending on which is
7 | most suited to your particular situation.
8 |
9 | Note that if your functions are in a namespace (e.g. `fn:myFunction()`), you must use the second or third
10 | type as the plain object implementation does not support namespaces.
11 |
12 | ## Function implementations
13 |
14 | Custom XPath functions are implemented as JavaScript functions taking one or more arguments.
15 |
16 | The first argument passed in is a context object containing a number of properties relating to the execution context,
17 | the most important being `contextNode`, the context in which the function is being evaluated.
18 |
19 | The remaining arguments are the arguments passed into the XPath function, as instances of the `XPathResult` interface.
20 | Please see [the documentation on that interface](XPathResult.md) for details.
21 |
22 | As the return value, you can return a string, number, boolean, single node, or array of nodes.
23 |
24 | ## Function Resolver Type 1: Plain object
25 |
26 | A plain object with function names as the keys and function implementations as the values.
27 |
28 |
29 | Example usage:
30 |
31 | ```js
32 | var evaluator = xpath.parse('squareRoot(10)');
33 | var aboutPi = evaluator.evaluateNumber({
34 | functions: {
35 | 'squareRoot': function (c, value) {
36 | return Math.sqrt(value.numberValue());
37 | }
38 | }
39 | });
40 | ```
41 |
42 | ## Function Resolver Type 2: Function
43 |
44 | A function that takes a function name as its first parameter and an optional namespace URI as its second parameter
45 | and returns a function based on the name and namespace.
46 |
47 | Example usage:
48 |
49 | ```js
50 | var evaluator = xpath.parse('math:squareRoot(10)');
51 | var aboutPi = evaluator.evaluateNumber({
52 | functions: function (name, namespace) {
53 | if (name === 'squareRoot' && namespace === 'http://sample.org/math/') {
54 | return function (c, value) {
55 | return Math.sqrt(value.numberValue());
56 | };
57 | }
58 | },
59 | namespaces: {
60 | math: 'http://sample.org/math/'
61 | }
62 | });
63 | ```
64 |
65 | ## Function Resolver Type 3: Object with `getFunction` method
66 |
67 | An object with a method named `getFunction` that works in the same way as the function-based function resolver
68 | described above.
69 |
70 | Example usage:
71 |
72 | ```js
73 | var evaluator = xpath.parse('math:squareRoot(10)');
74 | var aboutPi = evaluator.evaluateNumber({
75 | functions: {
76 | getFunction: function (name, namespace) {
77 | if (name === 'squareRoot' && namespace === 'http://sample.org/math/') {
78 | return function (c, value) {
79 | return Math.sqrt(value.numberValue());
80 | };
81 | }
82 | }
83 | },
84 | namespaces: {
85 | math: 'http://sample.org/math/'
86 | }
87 | });
88 | ```
89 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## xpath
2 | DOM 3 XPath 1.0 implemention and helper for JavaScript, with node.js support.
3 |
4 | Originally written by Cameron McCormack ([blog](http://mcc.id.au/xpathjs)).
5 |
6 | Additional contributions from
7 | Yaron Naveh ([blog](http://webservices20.blogspot.com/))
8 | goto100
9 | Thomas Weinert
10 | Jimmy Rishe
11 | and [others](https://github.com/goto100/xpath/graphs/contributors)
12 |
13 | ## Install
14 | Install with [npm](http://github.com/isaacs/npm):
15 |
16 | npm install xpath
17 |
18 | xpath is xml engine agnostic but we recommend [xmldom](https://github.com/xmldom/xmldom):
19 |
20 | npm install @xmldom/xmldom
21 |
22 | ## API Documentation
23 |
24 | Can be found [here](https://github.com/goto100/xpath/blob/master/docs/xpath%20methods.md). See below for example usage.
25 |
26 | ## Your first xpath:
27 | `````javascript
28 | var xpath = require('xpath');
29 | var dom = require('@xmldom/xmldom').DOMParser;
30 |
31 | var xml = "Harry Potter";
32 | var doc = new dom().parseFromString(xml, 'text/xml');
33 | var nodes = xpath.select("//title", doc);
34 |
35 | console.log(nodes[0].localName + ": " + nodes[0].firstChild.data);
36 | console.log("Node: " + nodes[0].toString());
37 | `````
38 | ➡
39 |
40 | title: Harry Potter
41 | Node: Harry Potter
42 |
43 | ### Alternatively
44 |
45 | Using the same interface you have on modern browsers ([MDN])
46 |
47 | `````javascript
48 | var node = null;
49 | var xml = "Harry Potter";
50 | var doc = new dom().parseFromString(xml, 'text/xml');
51 | var result = xpath.evaluate(
52 | "/book/title", // xpathExpression
53 | doc, // contextNode
54 | null, // namespaceResolver
55 | xpath.XPathResult.ANY_TYPE, // resultType
56 | null // result
57 | );
58 |
59 | node = result.iterateNext();
60 | while (node) {
61 | console.log(node.localName + ": " + node.firstChild.data);
62 | console.log("Node: " + node.toString());
63 |
64 | node = result.iterateNext();
65 | }
66 | `````
67 | ➡
68 |
69 | title: Harry Potter
70 | Node: Harry Potter
71 |
72 | ## Evaluate string values directly:
73 | `````javascript
74 | var xml = "Harry Potter";
75 | var doc = new dom().parseFromString(xml, 'text/xml');
76 | var title = xpath.select("string(//title)", doc);
77 |
78 | console.log(title);
79 | `````
80 | ➡
81 |
82 | Harry Potter
83 |
84 | ## Namespaces
85 | `````javascript
86 | var xml = "Harry Potter";
87 | var doc = new dom().parseFromString(xml, 'text/xml');
88 | var node = xpath.select("//*[local-name(.)='title' and namespace-uri(.)='myns']", doc)[0];
89 |
90 | console.log(node.namespaceURI);
91 | `````
92 | ➡
93 |
94 | myns
95 |
96 | ## Namespaces with easy mappings
97 | `````javascript
98 | var xml = "Harry Potter"
99 | var select = xpath.useNamespaces({"bookml": "http://example.com/book"});
100 |
101 | console.log(select('//bookml:title/text()', doc)[0].nodeValue);
102 | `````
103 | ➡
104 |
105 | Harry Potter
106 |
107 | ## Default namespace with mapping
108 | `````javascript
109 | var xml = "Harry Potter"
110 | var select = xpath.useNamespaces({"bookml": "http://example.com/book"});
111 |
112 | console.log(select('//bookml:title/text()', doc)[0].nodeValue);
113 | `````
114 | ➡
115 |
116 | Harry Potter
117 |
118 | ## Attributes
119 | `````javascript
120 | var xml = "Harry Potter";
121 | var doc = new dom().parseFromString(xml, 'text/xml');
122 | var author = xpath.select1("/book/@author", doc).value;
123 |
124 | console.log(author);
125 | `````
126 | ➡
127 |
128 | J. K. Rowling
129 |
130 | [MDN]: https://developer.mozilla.org/en/docs/Web/API/Document/evaluate
131 |
132 | ## License
133 | MIT
134 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@colors/colors@1.6.0", "@colors/colors@^1.6.0":
6 | version "1.6.0"
7 | resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0"
8 | integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==
9 |
10 | "@dabh/diagnostics@^2.0.2":
11 | version "2.0.3"
12 | resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a"
13 | integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==
14 | dependencies:
15 | colorspace "1.1.x"
16 | enabled "2.0.x"
17 | kuler "^2.0.0"
18 |
19 | "@nodelib/fs.scandir@2.1.5":
20 | version "2.1.5"
21 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
22 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
23 | dependencies:
24 | "@nodelib/fs.stat" "2.0.5"
25 | run-parallel "^1.1.9"
26 |
27 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
28 | version "2.0.5"
29 | resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
30 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
31 |
32 | "@nodelib/fs.walk@^1.2.3":
33 | version "1.2.8"
34 | resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
35 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
36 | dependencies:
37 | "@nodelib/fs.scandir" "2.1.5"
38 | fastq "^1.6.0"
39 |
40 | "@types/triple-beam@^1.3.2":
41 | version "1.3.5"
42 | resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c"
43 | integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==
44 |
45 | "@ungap/promise-all-settled@1.1.2":
46 | version "1.1.2"
47 | resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
48 | integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
49 |
50 | "@xmldom/xmldom@^0.8.9":
51 | version "0.8.10"
52 | resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99"
53 | integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==
54 |
55 | acorn@8.8.2:
56 | version "8.8.2"
57 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
58 | integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
59 |
60 | ansi-colors@4.1.1:
61 | version "4.1.1"
62 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
63 | integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
64 |
65 | ansi-regex@^5.0.1:
66 | version "5.0.1"
67 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
68 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
69 |
70 | ansi-styles@^4.0.0, ansi-styles@^4.1.0:
71 | version "4.3.0"
72 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
73 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
74 | dependencies:
75 | color-convert "^2.0.1"
76 |
77 | anymatch@~3.1.2:
78 | version "3.1.3"
79 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
80 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
81 | dependencies:
82 | normalize-path "^3.0.0"
83 | picomatch "^2.0.4"
84 |
85 | argparse@^2.0.1:
86 | version "2.0.1"
87 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
88 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
89 |
90 | async@^3.2.3:
91 | version "3.2.5"
92 | resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
93 | integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
94 |
95 | balanced-match@^1.0.0:
96 | version "1.0.2"
97 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
98 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
99 |
100 | binary-extensions@^2.0.0:
101 | version "2.2.0"
102 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
103 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
104 |
105 | brace-expansion@^1.1.7:
106 | version "1.1.11"
107 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
108 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
109 | dependencies:
110 | balanced-match "^1.0.0"
111 | concat-map "0.0.1"
112 |
113 | braces@^3.0.2, braces@~3.0.2:
114 | version "3.0.2"
115 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
116 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
117 | dependencies:
118 | fill-range "^7.0.1"
119 |
120 | browser-stdout@1.3.1:
121 | version "1.3.1"
122 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
123 | integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
124 |
125 | camelcase@^6.0.0:
126 | version "6.3.0"
127 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
128 | integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
129 |
130 | chalk@^4.1.0:
131 | version "4.1.2"
132 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
133 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
134 | dependencies:
135 | ansi-styles "^4.1.0"
136 | supports-color "^7.1.0"
137 |
138 | chokidar@3.5.3:
139 | version "3.5.3"
140 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
141 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
142 | dependencies:
143 | anymatch "~3.1.2"
144 | braces "~3.0.2"
145 | glob-parent "~5.1.2"
146 | is-binary-path "~2.1.0"
147 | is-glob "~4.0.1"
148 | normalize-path "~3.0.0"
149 | readdirp "~3.6.0"
150 | optionalDependencies:
151 | fsevents "~2.3.2"
152 |
153 | cliui@^7.0.2:
154 | version "7.0.4"
155 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
156 | integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
157 | dependencies:
158 | string-width "^4.2.0"
159 | strip-ansi "^6.0.0"
160 | wrap-ansi "^7.0.0"
161 |
162 | color-convert@^1.9.3:
163 | version "1.9.3"
164 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
165 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
166 | dependencies:
167 | color-name "1.1.3"
168 |
169 | color-convert@^2.0.1:
170 | version "2.0.1"
171 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
172 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
173 | dependencies:
174 | color-name "~1.1.4"
175 |
176 | color-name@1.1.3:
177 | version "1.1.3"
178 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
179 | integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
180 |
181 | color-name@^1.0.0, color-name@~1.1.4:
182 | version "1.1.4"
183 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
184 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
185 |
186 | color-string@^1.6.0:
187 | version "1.9.1"
188 | resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
189 | integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
190 | dependencies:
191 | color-name "^1.0.0"
192 | simple-swizzle "^0.2.2"
193 |
194 | color@^3.1.3:
195 | version "3.2.1"
196 | resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164"
197 | integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==
198 | dependencies:
199 | color-convert "^1.9.3"
200 | color-string "^1.6.0"
201 |
202 | colorspace@1.1.x:
203 | version "1.1.4"
204 | resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243"
205 | integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==
206 | dependencies:
207 | color "^3.1.3"
208 | text-hex "1.0.x"
209 |
210 | commander@10.0.0:
211 | version "10.0.0"
212 | resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1"
213 | integrity sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==
214 |
215 | concat-map@0.0.1:
216 | version "0.0.1"
217 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
218 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
219 |
220 | debug@4.3.3:
221 | version "4.3.3"
222 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
223 | integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
224 | dependencies:
225 | ms "2.1.2"
226 |
227 | decamelize@^4.0.0:
228 | version "4.0.0"
229 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
230 | integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
231 |
232 | diff@5.0.0:
233 | version "5.0.0"
234 | resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
235 | integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
236 |
237 | emoji-regex@^8.0.0:
238 | version "8.0.0"
239 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
240 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
241 |
242 | enabled@2.0.x:
243 | version "2.0.0"
244 | resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2"
245 | integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==
246 |
247 | es-check@^7.1.1:
248 | version "7.1.1"
249 | resolved "https://registry.yarnpkg.com/es-check/-/es-check-7.1.1.tgz#c2f8f609fd136d20e5047a4a185f80167e2498bf"
250 | integrity sha512-rgwR2wdJp437Exq28Emwc4x5+Qn6ORDliN9daWo0wTCg5jOQxJsIZieqxVi4AfDEIN4OwMwYhld9b13mnRocUQ==
251 | dependencies:
252 | acorn "8.8.2"
253 | commander "10.0.0"
254 | fast-glob "^3.2.12"
255 | supports-color "^8.1.1"
256 | winston "^3.8.2"
257 |
258 | escalade@^3.1.1:
259 | version "3.1.1"
260 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
261 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
262 |
263 | escape-string-regexp@4.0.0:
264 | version "4.0.0"
265 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
266 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
267 |
268 | fast-glob@^3.2.12:
269 | version "3.3.2"
270 | resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
271 | integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
272 | dependencies:
273 | "@nodelib/fs.stat" "^2.0.2"
274 | "@nodelib/fs.walk" "^1.2.3"
275 | glob-parent "^5.1.2"
276 | merge2 "^1.3.0"
277 | micromatch "^4.0.4"
278 |
279 | fastq@^1.6.0:
280 | version "1.15.0"
281 | resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
282 | integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
283 | dependencies:
284 | reusify "^1.0.4"
285 |
286 | fecha@^4.2.0:
287 | version "4.2.3"
288 | resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd"
289 | integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==
290 |
291 | fill-range@^7.0.1:
292 | version "7.0.1"
293 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
294 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
295 | dependencies:
296 | to-regex-range "^5.0.1"
297 |
298 | find-up@5.0.0:
299 | version "5.0.0"
300 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
301 | integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
302 | dependencies:
303 | locate-path "^6.0.0"
304 | path-exists "^4.0.0"
305 |
306 | flat@^5.0.2:
307 | version "5.0.2"
308 | resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
309 | integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
310 |
311 | fn.name@1.x.x:
312 | version "1.1.0"
313 | resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
314 | integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
315 |
316 | fs.realpath@^1.0.0:
317 | version "1.0.0"
318 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
319 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
320 |
321 | fsevents@~2.3.2:
322 | version "2.3.3"
323 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
324 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
325 |
326 | func-xml@^0.0.10:
327 | version "0.0.10"
328 | resolved "https://registry.yarnpkg.com/func-xml/-/func-xml-0.0.10.tgz#7409a439f0cdefba1ac23888501bc4077c531cdb"
329 | integrity sha512-/t0Y/SN4BBBl1oBEJ8tgDqgdxWeiLDl+nuZkafpvRYCLiIKzY8Ouzu9sKwH1R5jYK3t5yc6UngTR8I0GjTbIbQ==
330 | dependencies:
331 | ramda "0.29.0"
332 |
333 | get-caller-file@^2.0.5:
334 | version "2.0.5"
335 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
336 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
337 |
338 | glob-parent@^5.1.2, glob-parent@~5.1.2:
339 | version "5.1.2"
340 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
341 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
342 | dependencies:
343 | is-glob "^4.0.1"
344 |
345 | glob@7.2.0:
346 | version "7.2.0"
347 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
348 | integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
349 | dependencies:
350 | fs.realpath "^1.0.0"
351 | inflight "^1.0.4"
352 | inherits "2"
353 | minimatch "^3.0.4"
354 | once "^1.3.0"
355 | path-is-absolute "^1.0.0"
356 |
357 | growl@1.10.5:
358 | version "1.10.5"
359 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
360 | integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
361 |
362 | has-flag@^4.0.0:
363 | version "4.0.0"
364 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
365 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
366 |
367 | he@1.2.0:
368 | version "1.2.0"
369 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
370 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
371 |
372 | inflight@^1.0.4:
373 | version "1.0.6"
374 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
375 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
376 | dependencies:
377 | once "^1.3.0"
378 | wrappy "1"
379 |
380 | inherits@2, inherits@^2.0.3:
381 | version "2.0.4"
382 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
383 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
384 |
385 | is-arrayish@^0.3.1:
386 | version "0.3.2"
387 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
388 | integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
389 |
390 | is-binary-path@~2.1.0:
391 | version "2.1.0"
392 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
393 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
394 | dependencies:
395 | binary-extensions "^2.0.0"
396 |
397 | is-extglob@^2.1.1:
398 | version "2.1.1"
399 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
400 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
401 |
402 | is-fullwidth-code-point@^3.0.0:
403 | version "3.0.0"
404 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
405 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
406 |
407 | is-glob@^4.0.1, is-glob@~4.0.1:
408 | version "4.0.3"
409 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
410 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
411 | dependencies:
412 | is-extglob "^2.1.1"
413 |
414 | is-number@^7.0.0:
415 | version "7.0.0"
416 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
417 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
418 |
419 | is-plain-obj@^2.1.0:
420 | version "2.1.0"
421 | resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
422 | integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
423 |
424 | is-stream@^2.0.0:
425 | version "2.0.1"
426 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
427 | integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
428 |
429 | is-unicode-supported@^0.1.0:
430 | version "0.1.0"
431 | resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
432 | integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
433 |
434 | isexe@^2.0.0:
435 | version "2.0.0"
436 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
437 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
438 |
439 | js-yaml@4.1.0:
440 | version "4.1.0"
441 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
442 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
443 | dependencies:
444 | argparse "^2.0.1"
445 |
446 | kuler@^2.0.0:
447 | version "2.0.0"
448 | resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
449 | integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
450 |
451 | locate-path@^6.0.0:
452 | version "6.0.0"
453 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
454 | integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
455 | dependencies:
456 | p-locate "^5.0.0"
457 |
458 | log-symbols@4.1.0:
459 | version "4.1.0"
460 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
461 | integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
462 | dependencies:
463 | chalk "^4.1.0"
464 | is-unicode-supported "^0.1.0"
465 |
466 | logform@^2.3.2, logform@^2.4.0:
467 | version "2.6.0"
468 | resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5"
469 | integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==
470 | dependencies:
471 | "@colors/colors" "1.6.0"
472 | "@types/triple-beam" "^1.3.2"
473 | fecha "^4.2.0"
474 | ms "^2.1.1"
475 | safe-stable-stringify "^2.3.1"
476 | triple-beam "^1.3.0"
477 |
478 | merge2@^1.3.0:
479 | version "1.4.1"
480 | resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
481 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
482 |
483 | micromatch@^4.0.4:
484 | version "4.0.5"
485 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
486 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
487 | dependencies:
488 | braces "^3.0.2"
489 | picomatch "^2.3.1"
490 |
491 | minimatch@4.2.1:
492 | version "4.2.1"
493 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4"
494 | integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==
495 | dependencies:
496 | brace-expansion "^1.1.7"
497 |
498 | minimatch@^3.0.4:
499 | version "3.1.2"
500 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
501 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
502 | dependencies:
503 | brace-expansion "^1.1.7"
504 |
505 | mocha@^9.0.2:
506 | version "9.2.2"
507 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9"
508 | integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==
509 | dependencies:
510 | "@ungap/promise-all-settled" "1.1.2"
511 | ansi-colors "4.1.1"
512 | browser-stdout "1.3.1"
513 | chokidar "3.5.3"
514 | debug "4.3.3"
515 | diff "5.0.0"
516 | escape-string-regexp "4.0.0"
517 | find-up "5.0.0"
518 | glob "7.2.0"
519 | growl "1.10.5"
520 | he "1.2.0"
521 | js-yaml "4.1.0"
522 | log-symbols "4.1.0"
523 | minimatch "4.2.1"
524 | ms "2.1.3"
525 | nanoid "3.3.1"
526 | serialize-javascript "6.0.0"
527 | strip-json-comments "3.1.1"
528 | supports-color "8.1.1"
529 | which "2.0.2"
530 | workerpool "6.2.0"
531 | yargs "16.2.0"
532 | yargs-parser "20.2.4"
533 | yargs-unparser "2.0.0"
534 |
535 | ms@2.1.2:
536 | version "2.1.2"
537 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
538 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
539 |
540 | ms@2.1.3, ms@^2.1.1:
541 | version "2.1.3"
542 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
543 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
544 |
545 | nanoid@3.3.1:
546 | version "3.3.1"
547 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
548 | integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
549 |
550 | normalize-path@^3.0.0, normalize-path@~3.0.0:
551 | version "3.0.0"
552 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
553 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
554 |
555 | once@^1.3.0:
556 | version "1.4.0"
557 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
558 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
559 | dependencies:
560 | wrappy "1"
561 |
562 | one-time@^1.0.0:
563 | version "1.0.0"
564 | resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45"
565 | integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==
566 | dependencies:
567 | fn.name "1.x.x"
568 |
569 | p-limit@^3.0.2:
570 | version "3.1.0"
571 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
572 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
573 | dependencies:
574 | yocto-queue "^0.1.0"
575 |
576 | p-locate@^5.0.0:
577 | version "5.0.0"
578 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
579 | integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
580 | dependencies:
581 | p-limit "^3.0.2"
582 |
583 | path-exists@^4.0.0:
584 | version "4.0.0"
585 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
586 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
587 |
588 | path-is-absolute@^1.0.0:
589 | version "1.0.1"
590 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
591 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
592 |
593 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
594 | version "2.3.1"
595 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
596 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
597 |
598 | queue-microtask@^1.2.2:
599 | version "1.2.3"
600 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
601 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
602 |
603 | ramda@0.29.0:
604 | version "0.29.0"
605 | resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.29.0.tgz#fbbb67a740a754c8a4cbb41e2a6e0eb8507f55fb"
606 | integrity sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==
607 |
608 | randombytes@^2.1.0:
609 | version "2.1.0"
610 | resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
611 | integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
612 | dependencies:
613 | safe-buffer "^5.1.0"
614 |
615 | readable-stream@^3.4.0, readable-stream@^3.6.0:
616 | version "3.6.2"
617 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
618 | integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
619 | dependencies:
620 | inherits "^2.0.3"
621 | string_decoder "^1.1.1"
622 | util-deprecate "^1.0.1"
623 |
624 | readdirp@~3.6.0:
625 | version "3.6.0"
626 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
627 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
628 | dependencies:
629 | picomatch "^2.2.1"
630 |
631 | require-directory@^2.1.1:
632 | version "2.1.1"
633 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
634 | integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
635 |
636 | reusify@^1.0.4:
637 | version "1.0.4"
638 | resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
639 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
640 |
641 | run-parallel@^1.1.9:
642 | version "1.2.0"
643 | resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
644 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
645 | dependencies:
646 | queue-microtask "^1.2.2"
647 |
648 | safe-buffer@^5.1.0, safe-buffer@~5.2.0:
649 | version "5.2.1"
650 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
651 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
652 |
653 | safe-stable-stringify@^2.3.1:
654 | version "2.4.3"
655 | resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886"
656 | integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==
657 |
658 | serialize-javascript@6.0.0:
659 | version "6.0.0"
660 | resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
661 | integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
662 | dependencies:
663 | randombytes "^2.1.0"
664 |
665 | simple-swizzle@^0.2.2:
666 | version "0.2.2"
667 | resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
668 | integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==
669 | dependencies:
670 | is-arrayish "^0.3.1"
671 |
672 | stack-trace@0.0.x:
673 | version "0.0.10"
674 | resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
675 | integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==
676 |
677 | string-width@^4.1.0, string-width@^4.2.0:
678 | version "4.2.3"
679 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
680 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
681 | dependencies:
682 | emoji-regex "^8.0.0"
683 | is-fullwidth-code-point "^3.0.0"
684 | strip-ansi "^6.0.1"
685 |
686 | string_decoder@^1.1.1:
687 | version "1.3.0"
688 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
689 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
690 | dependencies:
691 | safe-buffer "~5.2.0"
692 |
693 | strip-ansi@^6.0.0, strip-ansi@^6.0.1:
694 | version "6.0.1"
695 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
696 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
697 | dependencies:
698 | ansi-regex "^5.0.1"
699 |
700 | strip-json-comments@3.1.1:
701 | version "3.1.1"
702 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
703 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
704 |
705 | supports-color@8.1.1, supports-color@^8.1.1:
706 | version "8.1.1"
707 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
708 | integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
709 | dependencies:
710 | has-flag "^4.0.0"
711 |
712 | supports-color@^7.1.0:
713 | version "7.2.0"
714 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
715 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
716 | dependencies:
717 | has-flag "^4.0.0"
718 |
719 | text-hex@1.0.x:
720 | version "1.0.0"
721 | resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
722 | integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==
723 |
724 | to-regex-range@^5.0.1:
725 | version "5.0.1"
726 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
727 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
728 | dependencies:
729 | is-number "^7.0.0"
730 |
731 | triple-beam@^1.3.0:
732 | version "1.4.1"
733 | resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984"
734 | integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==
735 |
736 | util-deprecate@^1.0.1:
737 | version "1.0.2"
738 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
739 | integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
740 |
741 | which@2.0.2:
742 | version "2.0.2"
743 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
744 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
745 | dependencies:
746 | isexe "^2.0.0"
747 |
748 | winston-transport@^4.5.0:
749 | version "4.6.0"
750 | resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.6.0.tgz#f1c1a665ad1b366df72199e27892721832a19e1b"
751 | integrity sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==
752 | dependencies:
753 | logform "^2.3.2"
754 | readable-stream "^3.6.0"
755 | triple-beam "^1.3.0"
756 |
757 | winston@^3.8.2:
758 | version "3.11.0"
759 | resolved "https://registry.yarnpkg.com/winston/-/winston-3.11.0.tgz#2d50b0a695a2758bb1c95279f0a88e858163ed91"
760 | integrity sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==
761 | dependencies:
762 | "@colors/colors" "^1.6.0"
763 | "@dabh/diagnostics" "^2.0.2"
764 | async "^3.2.3"
765 | is-stream "^2.0.0"
766 | logform "^2.4.0"
767 | one-time "^1.0.0"
768 | readable-stream "^3.4.0"
769 | safe-stable-stringify "^2.3.1"
770 | stack-trace "0.0.x"
771 | triple-beam "^1.3.0"
772 | winston-transport "^4.5.0"
773 |
774 | workerpool@6.2.0:
775 | version "6.2.0"
776 | resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b"
777 | integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==
778 |
779 | wrap-ansi@^7.0.0:
780 | version "7.0.0"
781 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
782 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
783 | dependencies:
784 | ansi-styles "^4.0.0"
785 | string-width "^4.1.0"
786 | strip-ansi "^6.0.0"
787 |
788 | wrappy@1:
789 | version "1.0.2"
790 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
791 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
792 |
793 | y18n@^5.0.5:
794 | version "5.0.8"
795 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
796 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
797 |
798 | yargs-parser@20.2.4:
799 | version "20.2.4"
800 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
801 | integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
802 |
803 | yargs-parser@^20.2.2:
804 | version "20.2.9"
805 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
806 | integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
807 |
808 | yargs-unparser@2.0.0:
809 | version "2.0.0"
810 | resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
811 | integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
812 | dependencies:
813 | camelcase "^6.0.0"
814 | decamelize "^4.0.0"
815 | flat "^5.0.2"
816 | is-plain-obj "^2.1.0"
817 |
818 | yargs@16.2.0:
819 | version "16.2.0"
820 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
821 | integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
822 | dependencies:
823 | cliui "^7.0.2"
824 | escalade "^3.1.1"
825 | get-caller-file "^2.0.5"
826 | require-directory "^2.1.1"
827 | string-width "^4.2.0"
828 | y18n "^5.0.5"
829 | yargs-parser "^20.2.2"
830 |
831 | yocto-queue@^0.1.0:
832 | version "0.1.0"
833 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
834 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
835 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | const { allChildEls } = require('func-xml');
2 | const xpath = require('./xpath.js');
3 | const dom = require('@xmldom/xmldom').DOMParser;
4 | const { strict: assert } = require('assert');
5 |
6 | var xhtmlNs = 'http://www.w3.org/1999/xhtml';
7 |
8 | const parser = new dom();
9 |
10 | const parseXml = (xml, mimeType = 'text/xml') => parser.parseFromString(xml, mimeType);
11 |
12 | describe('xpath', () => {
13 | describe('api', () => {
14 | it('should contain the correct methods', () => {
15 | assert.ok(xpath.evaluate, 'evaluate api ok.');
16 | assert.ok(xpath.select, 'select api ok.');
17 | assert.ok(xpath.parse, 'parse api ok.');
18 | });
19 |
20 | it('should support .evaluate()', () => {
21 | var xml = 'Harry Potter';
22 | var doc = parseXml(xml);
23 | var nodes = xpath.evaluate('//title', doc, null, xpath.XPathResult.ANY_TYPE, null).nodes;
24 |
25 | assert.strictEqual('title', nodes[0].localName);
26 | assert.strictEqual('Harry Potter', nodes[0].firstChild.data);
27 | assert.strictEqual('Harry Potter', nodes[0].toString());
28 | });
29 |
30 | it('should support .select()', () => {
31 | var xml = 'Harry Potter';
32 | var doc = parseXml(xml);
33 | var nodes = xpath.select('//title', doc);
34 | assert.strictEqual('title', nodes[0].localName);
35 | assert.strictEqual('Harry Potter', nodes[0].firstChild.data);
36 | assert.strictEqual('Harry Potter', nodes[0].toString());
37 |
38 | var nodes2 = xpath.select('//node()', doc);
39 | assert.strictEqual(7, nodes2.length);
40 |
41 | var pis = xpath.select("/processing-instruction('series')", doc);
42 | assert.strictEqual(2, pis.length);
43 | assert.strictEqual('books="7"', pis[1].data);
44 | });
45 | });
46 |
47 | describe('parsing', () => {
48 | it('should detect unterminated string literals', () => {
49 | function testUnterminated(path) {
50 | assert.throws(function () {
51 | xpath.evaluate('"hello');
52 | }, function (err) {
53 | return err.message.indexOf('Unterminated') !== -1;
54 | });
55 | }
56 |
57 | testUnterminated('"Hello');
58 | testUnterminated("'Hello");
59 | testUnterminated('self::text() = "\""');
60 | testUnterminated('"\""');
61 | });
62 | });
63 |
64 | describe('.select()', () => {
65 | it('should select single nodes', () => {
66 | var xml = 'Harry Potter';
67 | var doc = parseXml(xml);
68 |
69 | assert.strictEqual('title', xpath.select('//title[1]', doc)[0].localName);
70 | });
71 |
72 | it('should select text nodes', () => {
73 | var xml = 'HarryPotter';
74 | var doc = parseXml(xml);
75 |
76 | assert.deepEqual('book', xpath.select('local-name(/book)', doc));
77 | assert.deepEqual('Harry,Potter', xpath.select('//title/text()', doc).toString());
78 | });
79 |
80 | it('should select number values', () => {
81 | var xml = 'HarryPotter';
82 | var doc = parseXml(xml);
83 |
84 | assert.deepEqual(2, xpath.select('count(//title)', doc));
85 | });
86 |
87 | it('should select with namespaces', () => {
88 | var xml = 'Harry Potter';
89 | var doc = parseXml(xml);
90 |
91 | var nodes = xpath.select('//*[local-name(.)="title" and namespace-uri(.)="myns"]', doc);
92 | assert.strictEqual('title', nodes[0].localName);
93 | assert.strictEqual('myns', nodes[0].namespaceURI);
94 |
95 | var nodes2 = xpath.select('/*/title', doc);
96 |
97 | assert.strictEqual(0, nodes2.length);
98 | });
99 |
100 | it('should select with namespaces, using a resolver', () => {
101 | var xml = 'NarniaHarry PotterJKR';
102 | var doc = parseXml(xml);
103 |
104 | var resolver = {
105 | mappings: {
106 | 'testns': 'http://example.com/test'
107 | },
108 | lookupNamespaceURI: function (prefix) {
109 | return this.mappings[prefix];
110 | }
111 | };
112 |
113 | var nodes = xpath.selectWithResolver('//testns:title/text()', doc, resolver);
114 | assert.strictEqual('Harry Potter', nodes[0].nodeValue);
115 |
116 | assert.strictEqual('JKR', xpath.selectWithResolver('//testns:field[@testns:type="author"]/text()', doc, resolver)[0].nodeValue);
117 |
118 | var nodes2 = xpath.selectWithResolver('/*/testns:*', doc, resolver);
119 |
120 | assert.strictEqual(2, nodes2.length);
121 | });
122 |
123 | it('should select from xml with a default namespace, using a resolver', () => {
124 | var xml = 'Harry PotterJKR';
125 | var doc = parseXml(xml);
126 |
127 | var resolver = {
128 | mappings: {
129 | 'testns': 'http://example.com/test'
130 | },
131 | lookupNamespaceURI: function (prefix) {
132 | return this.mappings[prefix];
133 | }
134 | }
135 |
136 | var nodes = xpath.selectWithResolver('//testns:title/text()', doc, resolver);
137 | assert.strictEqual('Harry Potter', xpath.selectWithResolver('//testns:title/text()', doc, resolver)[0].nodeValue);
138 | assert.strictEqual('JKR', xpath.selectWithResolver('//testns:field[@type="author"]/text()', doc, resolver)[0].nodeValue);
139 | });
140 |
141 | it('should select with namespaces, prefixes different in xml and xpath, using a resolver', () => {
142 | var xml = 'Harry PotterJKR';
143 | var doc = parseXml(xml);
144 |
145 | var resolver = {
146 | mappings: {
147 | 'ns': 'http://example.com/test'
148 | },
149 | lookupNamespaceURI: function (prefix) {
150 | return this.mappings[prefix];
151 | }
152 | }
153 |
154 | var nodes = xpath.selectWithResolver('//ns:title/text()', doc, resolver);
155 | assert.strictEqual('Harry Potter', nodes[0].nodeValue);
156 |
157 | assert.strictEqual('JKR', xpath.selectWithResolver('//ns:field[@ns:type="author"]/text()', doc, resolver)[0].nodeValue);
158 | });
159 |
160 | it('should select with namespaces, using namespace mappings', () => {
161 | var xml = 'Harry PotterJKR';
162 | var doc = parseXml(xml);
163 | var select = xpath.useNamespaces({ 'testns': 'http://example.com/test' });
164 |
165 | assert.strictEqual('Harry Potter', select('//testns:title/text()', doc)[0].nodeValue);
166 | assert.strictEqual('JKR', select('//testns:field[@testns:type="author"]/text()', doc)[0].nodeValue);
167 | });
168 |
169 | it('should select attributes', () => {
170 | var xml = '';
171 | var doc = parseXml(xml);
172 |
173 | var author = xpath.select1('/author/@name', doc).value;
174 | assert.strictEqual('J. K. Rowling', author);
175 | });
176 | });
177 |
178 | describe('selection', () => {
179 | it('should select with multiple predicates', () => {
180 | var xml = '';
181 | var doc = parseXml(xml);
182 |
183 | var characters = xpath.select('/*/character[@sex = "M"][@age > 40]/@name', doc);
184 |
185 | assert.strictEqual(1, characters.length);
186 | assert.strictEqual('Snape', characters[0].textContent);
187 | });
188 |
189 | // https://github.com/goto100/xpath/issues/37
190 | it('should select multiple attributes', () => {
191 | var xml = '';
192 | var doc = parseXml(xml);
193 |
194 | var authors = xpath.select('/authors/author/@name', doc);
195 | assert.strictEqual(2, authors.length);
196 | assert.strictEqual('J. K. Rowling', authors[0].value);
197 |
198 | // https://github.com/goto100/xpath/issues/41
199 | doc = parseXml('');
200 | var nodes = xpath.select("/chapters/chapter/@v", doc);
201 | var values = nodes.map(function (n) { return n.value; });
202 |
203 | assert.strictEqual(3, values.length);
204 | assert.strictEqual("1", values[0]);
205 | assert.strictEqual("2", values[1]);
206 | assert.strictEqual("3", values[2]);
207 | });
208 |
209 | it('should compare string values of numbers with numbers', () => {
210 | assert.ok(xpath.select1('"000" = 0'), '000');
211 | assert.ok(xpath.select1('"45.0" = 45'), '45');
212 | });
213 |
214 | it('should correctly compare strings with booleans', () => {
215 | // string should downcast to boolean
216 | assert.strictEqual(false, xpath.select1('"false" = false()'), '"false" = false()');
217 | assert.strictEqual(true, xpath.select1('"a" = true()'), '"a" = true()');
218 | assert.strictEqual(true, xpath.select1('"" = false()'), '"" = false()');
219 | });
220 |
221 | it('should evaluate local-name() and name() on processing instructions', () => {
222 | var xml = 'Harry Potter';
223 | var doc = parseXml(xml);
224 | var expectedName = 'book-record';
225 | var localName = xpath.select('local-name(/processing-instruction())', doc);
226 | var name = xpath.select('name(/processing-instruction())', doc);
227 |
228 | assert.deepEqual(expectedName, localName, 'local-name() - "' + expectedName + '" !== "' + localName + '"');
229 | assert.deepEqual(expectedName, name, 'name() - "' + expectedName + '" !== "' + name + '"');
230 | });
231 |
232 | it('should support substring-after()', () => {
233 | var xml = 'Hermione';
234 | var doc = parseXml(xml);
235 |
236 | var part = xpath.select('substring-after(/classmate, "Her")', doc);
237 | assert.deepEqual('mione', part);
238 | });
239 |
240 | it('should support preceding:: on document fragments', () => {
241 | var doc = parseXml(''),
242 | df = doc.createDocumentFragment(),
243 | root = doc.createElement('book');
244 |
245 | df.appendChild(root);
246 |
247 | for (var i = 0; i < 10; i += 1) {
248 | root.appendChild(doc.createElement('chapter'));
249 | }
250 |
251 | var chapter = xpath.select1("book/chapter[5]", df);
252 |
253 | assert.ok(chapter, 'chapter');
254 |
255 | assert.strictEqual(xpath.select("count(preceding::chapter)", chapter), 4);
256 | });
257 |
258 | it('should allow getting sorted and unsorted arrays from nodesets', () => {
259 | const doc = parseXml('HarryRonHermione');
260 | const path = xpath.parse("/*/*[3] | /*/*[2] | /*/*[1]");
261 | const nset = path.evaluateNodeSet({ node: doc });
262 | const sorted = nset.toArray();
263 | const unsorted = nset.toUnsortedArray();
264 |
265 | assert.strictEqual(sorted.length, 3);
266 | assert.strictEqual(unsorted.length, 3);
267 |
268 | assert.strictEqual(sorted[0].textContent, 'Harry');
269 | assert.strictEqual(sorted[1].textContent, 'Ron');
270 | assert.strictEqual(sorted[2].textContent, 'Hermione');
271 |
272 | assert.notEqual(sorted[0], unsorted[0], "first nodeset element equal");
273 | });
274 |
275 | it('should compare nodesets to nodesets (=)', () => {
276 | var xml = '' +
277 | 'HarryHermione' +
278 | 'DracoCrabbe' +
279 | 'LunaCho' +
280 | '' +
281 | 'HermioneLuna';
282 |
283 | var doc = parseXml(xml);
284 | var houses = xpath.parse('/school/houses/house[student = /school/honorStudents/student]').select({ node: doc });
285 |
286 | assert.strictEqual(houses.length, 2);
287 |
288 | var houseNames = houses.map(function (node) { return node.getAttribute('name'); }).sort();
289 |
290 | assert.strictEqual(houseNames[0], 'Gryffindor');
291 | assert.strictEqual(houseNames[1], 'Ravenclaw');
292 | });
293 |
294 | it('should compare nodesets to nodesets (>=)', () => {
295 | var xml = '' +
296 | 'HarryHermione' +
297 | 'GoyleCrabbe' +
298 | 'LunaCho' +
299 | '' +
300 | 'DADACharms' +
301 | '';
302 |
303 | var doc = parseXml(xml);
304 | var houses = xpath.parse('/school/houses/house[student/@level >= /school/courses/course/@minLevel]').select({ node: doc });
305 |
306 | assert.strictEqual(houses.length, 2);
307 |
308 | var houseNames = houses.map(function (node) { return node.getAttribute('name'); }).sort();
309 |
310 | assert.strictEqual(houseNames[0], 'Gryffindor');
311 | assert.strictEqual(houseNames[1], 'Ravenclaw');
312 | });
313 |
314 | it('should support various inequality expressions on nodesets', () => {
315 | var xml = "";
316 | var doc = parseXml(xml);
317 |
318 | var options = { node: doc, variables: { theNumber: 3, theString: '3', theBoolean: true } };
319 |
320 | var numberPaths = [
321 | '/books/book[$theNumber <= @num]',
322 | '/books/book[$theNumber < @num]',
323 | '/books/book[$theNumber >= @num]',
324 | '/books/book[$theNumber > @num]'
325 | ];
326 |
327 | var stringPaths = [
328 | '/books/book[$theString <= @num]',
329 | '/books/book[$theString < @num]',
330 | '/books/book[$theString >= @num]',
331 | '/books/book[$theString > @num]'
332 | ];
333 |
334 | var booleanPaths = [
335 | '/books/book[$theBoolean <= @num]',
336 | '/books/book[$theBoolean < @num]',
337 | '/books/book[$theBoolean >= @num]',
338 | '/books/book[$theBoolean > @num]'
339 | ];
340 |
341 | var lhsPaths = [
342 | '/books/book[@num <= $theNumber]',
343 | '/books/book[@num < $theNumber]'
344 | ];
345 |
346 | function countNodes(paths) {
347 | return paths
348 | .map(xpath.parse)
349 | .map(function (path) { return path.select(options) })
350 | .map(function (arr) { return arr.length; });
351 | }
352 |
353 | assert.deepEqual(countNodes(numberPaths), [5, 4, 3, 2], 'numbers');
354 | assert.deepEqual(countNodes(stringPaths), [5, 4, 3, 2], 'strings');
355 | assert.deepEqual(countNodes(booleanPaths), [7, 6, 1, 0], 'numbers');
356 | assert.deepEqual(countNodes(lhsPaths), [3, 2], 'lhs');
357 | });
358 |
359 | it('should correctly evaluate context position', () => {
360 | var doc = parseXml(`
361 |
362 | The boy who lived
363 | The vanishing glass
364 |
365 |
366 | The worst birthday
367 | Dobby's warning
368 | The burrow
369 |
370 | `);
371 |
372 | var chapters = xpath.parse('/books/book/chapter[2]').select({ node: doc });
373 |
374 | assert.strictEqual(2, chapters.length);
375 | assert.strictEqual('The vanishing glass', chapters[0].textContent);
376 | assert.strictEqual("Dobby's warning", chapters[1].textContent);
377 |
378 | var lastChapters = xpath.parse('/books/book/chapter[last()]').select({ node: doc });
379 |
380 | assert.strictEqual(2, lastChapters.length);
381 | assert.strictEqual('The vanishing glass', lastChapters[0].textContent);
382 | assert.strictEqual("The burrow", lastChapters[1].textContent);
383 |
384 | var secondChapter = xpath.parse('(/books/book/chapter)[2]').select({ node: doc });
385 |
386 | assert.strictEqual(1, secondChapter.length);
387 | assert.strictEqual('The vanishing glass', chapters[0].textContent);
388 |
389 | var lastChapter = xpath.parse('(/books/book/chapter)[last()]').select({ node: doc });
390 |
391 | assert.strictEqual(1, lastChapter.length);
392 | assert.strictEqual("The burrow", lastChapter[0].textContent);
393 |
394 | // #135 - issues with context position
395 | const blockQuotes = parseXml(`
396 |
397 |
398 |
399 |
400 |
401 | This is a test!
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 | This is also a test!
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 | `);
434 |
435 | const x = xpath
436 | .parse(`(.//*[local-name(.)='blockquote'])[not(@class="gmail_quote") and not(ancestor::*[local-name() = 'blockquote'])][last()]`)
437 | .select({ node: blockQuotes });
438 |
439 | assert.strictEqual(1, x.length);
440 |
441 | assert.strictEqual('This is also a test!', x[0].textContent.trim());
442 | });
443 |
444 | it('should select and sort namespace nodes properly', () => {
445 | // #83
446 |
447 | const doc = parseXml('');
448 |
449 | const namespaces = xpath.parse('/*/namespace::*').select({ node: doc });
450 |
451 | assert.strictEqual(5, namespaces.length);
452 |
453 | assert.equal('http://www.w3.org/XML/1998/namespace', namespaces[0].nodeValue);
454 | assert.equal('xml', namespaces[0].localName);
455 |
456 | assert.equal('http://book.com', namespaces[1].nodeValue);
457 | assert.equal('b', namespaces[1].localName);
458 |
459 | assert.equal('default-book', namespaces[2].nodeValue);
460 | assert.equal('', namespaces[2].localName);
461 |
462 | assert.equal('http://author.com', namespaces[3].nodeValue);
463 | assert.equal('a', namespaces[3].localName);
464 |
465 | assert.equal('http://publisher', namespaces[4].nodeValue);
466 | assert.equal('p', namespaces[4].localName);
467 | });
468 |
469 | it('should allow using node positions', () => {
470 | const doc = parseXml(`
471 |
472 | Chapter 1
473 | Chapter 2
474 | Chapter 3
475 | Chapter 4
476 |
477 |
478 | 1章
479 | 2章
480 | 3章
481 | 4章
482 |
483 | `)
484 |
485 | assert.equal(
486 | xpath.parse('/*/book/chapter[1]').evaluateString({ node: doc }),
487 | 'Chapter 1',
488 | );
489 |
490 | assert.equal(
491 | xpath.parse('/*/book/chapter[2]').evaluateString({ node: doc }),
492 | 'Chapter 2',
493 | );
494 |
495 | assert.equal(
496 | xpath.parse('/*/book/chapter[3]').evaluateString({ node: doc }),
497 | 'Chapter 3',
498 | );
499 |
500 | assert.equal(
501 | xpath.parse('/*/book[2]/chapter[1]').evaluateString({ node: doc }),
502 | '1章',
503 | );
504 |
505 | assert.equal(
506 | xpath.parse('/*/book/chapter[5]').evaluateString({ node: doc }),
507 | '',
508 | );
509 |
510 | assert.equal(
511 | xpath.parse('(/*/book/chapter)[5]').evaluateString({ node: doc }),
512 | '1章',
513 | );
514 |
515 | const pos1Nodes = xpath.parse('/*/book/chapter[1]').select({ node: doc });
516 |
517 | assert.equal(pos1Nodes.length, 2);
518 |
519 | assert.equal(pos1Nodes[0].textContent, 'Chapter 1');
520 | assert.equal(pos1Nodes[1].textContent, '1章');
521 |
522 | const first3Nodes = xpath.parse('/*/book/chapter[position() <= 3]').select({ node: doc });
523 |
524 | assert.equal(first3Nodes.length, 6);
525 |
526 | assert.equal(first3Nodes[5].textContent, '3章');
527 | });
528 |
529 | it('should respect reverse axes', () => {
530 | const doc = parseXml(`
531 | Chapter 1
532 | Chapter 2
533 | Chapter 3
534 | Chapter 4
535 | `)
536 |
537 | assert.equal(
538 | xpath.parse('/*/chapter[last()]/preceding-sibling::*[1]').evaluateString({ node: doc }),
539 | 'Chapter 3',
540 | );
541 |
542 | assert.equal(
543 | xpath.parse('/*/chapter[last()]/preceding-sibling::*[2]').evaluateString({ node: doc }),
544 | 'Chapter 2',
545 | );
546 |
547 | assert.equal(
548 | xpath.parse('/*/chapter[last()]/preceding-sibling::*[3]').evaluateString({ node: doc }),
549 | 'Chapter 1',
550 | );
551 |
552 | assert.equal(
553 | xpath.parse('/*/chapter[last()]/preceding-sibling::*[4]').evaluateString({ node: doc }),
554 | '',
555 | );
556 |
557 | assert.equal(
558 | xpath.parse('/*/chapter[last()]/preceding-sibling::*[last()]').evaluateString({ node: doc }),
559 | 'Chapter 1',
560 | );
561 |
562 | assert.equal(
563 | xpath.parse('/*/chapter[last()]/preceding::chapter[last()]').evaluateString({ node: doc }),
564 | 'Chapter 1',
565 | );
566 |
567 | assert.equal(
568 | xpath.parse('/*/chapter[last()]/preceding::*[position() = 1]').evaluateString({ node: doc }),
569 | 'Chapter 3',
570 | );
571 |
572 | assert.equal(
573 | xpath.parse('/*/chapter[last()]/preceding::*[. != "Chapter 3"][1]').evaluateString({ node: doc }),
574 | 'Chapter 2',
575 | );
576 | });
577 | });
578 |
579 | describe('string()', () => {
580 | it('should work with no arguments', () => {
581 | var doc = parseXml('Harry Potter');
582 |
583 | var rootElement = xpath.select1('/book', doc);
584 | assert.ok(rootElement, 'rootElement is null');
585 |
586 | assert.strictEqual('Harry Potter', xpath.select1('string()', doc));
587 | });
588 |
589 | it('should work on document fragments', () => {
590 | var doc = parseXml('');
591 | var docFragment = doc.createDocumentFragment();
592 |
593 | var el = doc.createElement("book");
594 | docFragment.appendChild(el);
595 |
596 | var testValue = "Harry Potter";
597 |
598 | el.appendChild(doc.createTextNode(testValue));
599 |
600 | assert.strictEqual(testValue, xpath.select1("string()", docFragment));
601 | });
602 |
603 | it('should work correctly on boolean values', () => {
604 | assert.strictEqual('string', typeof xpath.select1('string(true())'));
605 | assert.strictEqual('string', typeof xpath.select1('string(false())'));
606 | assert.strictEqual('string', typeof xpath.select1('string(1 = 2)'));
607 | assert.ok(xpath.select1('"true" = string(true())'), '"true" = string(true())');
608 | });
609 |
610 | it('should work correctly on numbers', () => {
611 | assert.strictEqual('string', typeof xpath.select1('string(45)'));
612 | assert.ok(xpath.select1('"45" = string(45)'), '"45" = string(45)');
613 | });
614 | });
615 |
616 | describe('type conversion', () => {
617 | it('should convert strings to numbers correctly', () => {
618 | assert.strictEqual(45.2, xpath.select1('number("45.200")'));
619 | assert.strictEqual(55.0, xpath.select1('number("000055")'));
620 | assert.strictEqual(65.0, xpath.select1('number(" 65 ")'));
621 |
622 | assert.strictEqual(true, xpath.select1('"" != 0'), '"" != 0');
623 | assert.strictEqual(false, xpath.select1('"" = 0'), '"" = 0');
624 | assert.strictEqual(false, xpath.select1('0 = ""'), '0 = ""');
625 | assert.strictEqual(false, xpath.select1('0 = " "'), '0 = " "');
626 |
627 | assert.ok(Number.isNaN(xpath.select('number("")')), 'number("")');
628 | assert.ok(Number.isNaN(xpath.select('number("45.8g")')), 'number("45.8g")');
629 | assert.ok(Number.isNaN(xpath.select('number("2e9")')), 'number("2e9")');
630 | assert.ok(Number.isNaN(xpath.select('number("+33")')), 'number("+33")');
631 | });
632 |
633 | it('should convert numbers to strings correctly', () => {
634 | assert.strictEqual('0.0000000000000000000000005250000000000001', xpath.parse('0.525 div 1000000 div 1000000 div 1000000 div 1000000').evaluateString());
635 | assert.strictEqual('525000000000000000000000', xpath.parse('0.525 * 1000000 * 1000000 * 1000000 * 1000000').evaluateString());
636 | });
637 |
638 | it('should provide correct string value for cdata sections', () => {
639 | const xml = "Ron ";
640 | const doc = parseXml(xml);
641 |
642 | const person1 = xpath.parse("/people/person").evaluateString({ node: doc });
643 | const person2 = xpath.parse("/people/person/text()").evaluateString({ node: doc });
644 | const person3 = xpath.select("string(/people/person/text())", doc);
645 | const person4 = xpath.parse("/people/person[2]").evaluateString({ node: doc });
646 |
647 | assert.strictEqual(person1, 'Harry Potter');
648 | assert.strictEqual(person2, 'Harry Potter');
649 | assert.strictEqual(person3, 'Harry Potter');
650 | assert.strictEqual(person4, 'Ron Weasley');
651 | });
652 |
653 | it('should convert various node types to string values', () => {
654 | var xml = "Harry Potter",
655 | doc = parseXml(xml),
656 | allText = xpath.parse('.').evaluateString({ node: doc }),
657 | ns = xpath.parse('*/namespace::*[name() = "hp"]').evaluateString({ node: doc }),
658 | title = xpath.parse('*/title').evaluateString({ node: doc }),
659 | child = xpath.parse('*/*').evaluateString({ node: doc }),
660 | titleLang = xpath.parse('*/*/@lang').evaluateString({ node: doc }),
661 | pi = xpath.parse('*/processing-instruction()').evaluateString({ node: doc }),
662 | comment = xpath.parse('*/comment()').evaluateString({ node: doc });
663 |
664 | assert.strictEqual(allText, "Harry Potter & the Philosopher's StoneHarry Potter");
665 | assert.strictEqual(ns, 'http://harry');
666 | assert.strictEqual(title, "Harry Potter & the Philosopher's Stone");
667 | assert.strictEqual(child, "Harry Potter & the Philosopher's Stone");
668 | assert.strictEqual(titleLang, 'en');
669 | assert.strictEqual(pi.trim(), "name='J.K. Rowling'");
670 | assert.strictEqual(comment, ' This describes the Harry Potter Book ');
671 | });
672 |
673 | it('should convert booleans to numbers correctly', () => {
674 | var num = xpath.parse('"a" = "b"').evaluateNumber();
675 |
676 | assert.strictEqual(num, 0);
677 |
678 | var str = xpath.select('substring("expelliarmus", 1, "a" = "a")');
679 |
680 | assert.strictEqual(str, 'e');
681 | });
682 |
683 | it('should get string values from namespace nodes', () => {
684 | const doc = parseXml('');
685 |
686 | assert.equal(
687 | xpath.parse('string(/*/namespace::author)').evaluateString({ node: doc }),
688 | 'http://author'
689 | );
690 | assert.equal(
691 | xpath.parse('name(/*/namespace::author)').evaluateString({ node: doc }),
692 | 'author'
693 | );
694 | assert.equal(
695 | xpath.parse('local-name(/*/namespace::author)').evaluateString({ node: doc }),
696 | 'author'
697 | );
698 | assert.equal(
699 | xpath.parse('namespace-uri(/*/namespace::author)').evaluateString({ node: doc }),
700 | ''
701 | );
702 |
703 | assert.equal(
704 | xpath.parse('string(/*/namespace::*[not(local-name())])').evaluateString({ node: doc }),
705 | 'https://book'
706 | );
707 | assert.equal(
708 | xpath.parse('name(/*/namespace::*[not(local-name())])').evaluateString({ node: doc }),
709 | ''
710 | );
711 | assert.equal(
712 | xpath.parse('local-name(/*/namespace::*[not(local-name())])').evaluateString({ node: doc }),
713 | ''
714 | );
715 | assert.equal(
716 | xpath.parse('namespace-uri(/*/namespace::*[not(local-name())])').evaluateString({ node: doc }),
717 | ''
718 | );
719 | });
720 | });
721 |
722 | describe('parsed expressions', () => {
723 | it('should work with no options', () => {
724 | var parsed = xpath.parse('5 + 7');
725 |
726 | assert.strictEqual(typeof parsed, "object", "parse() should return an object");
727 | assert.strictEqual(typeof parsed.evaluate, "function", "parsed.evaluate should be a function");
728 | assert.strictEqual(typeof parsed.evaluateNumber, "function", "parsed.evaluateNumber should be a function");
729 |
730 | assert.strictEqual(parsed.evaluateNumber(), 12);
731 |
732 | // evaluating twice should yield the same result
733 | assert.strictEqual(parsed.evaluateNumber(), 12);
734 | });
735 |
736 | it('should support select1()', () => {
737 | var xml = 'Harry Potter';
738 | var doc = parseXml(xml);
739 | var parsed = xpath.parse('/*/title');
740 |
741 | assert.strictEqual(typeof parsed, 'object', 'parse() should return an object');
742 |
743 | assert.strictEqual(typeof parsed.select1, 'function', 'parsed.select1 should be a function');
744 |
745 | var single = parsed.select1({ node: doc });
746 |
747 | assert.strictEqual('title', single.localName);
748 | assert.strictEqual('Harry Potter', single.firstChild.data);
749 | assert.strictEqual('Harry Potter', single.toString());
750 | });
751 |
752 | it('should support select()', () => {
753 | var xml = 'Harry Potter';
754 | var doc = parseXml(xml);
755 | var parsed = xpath.parse('/*/title');
756 |
757 | assert.strictEqual(typeof parsed, 'object', 'parse() should return an object');
758 |
759 | assert.strictEqual(typeof parsed.select, 'function', 'parsed.select should be a function');
760 |
761 | var nodes = parsed.select({ node: doc });
762 |
763 | assert.ok(nodes, 'parsed.select() should return a value');
764 | assert.strictEqual(1, nodes.length);
765 | assert.strictEqual('title', nodes[0].localName);
766 | assert.strictEqual('Harry Potter', nodes[0].firstChild.data);
767 | assert.strictEqual('Harry Potter', nodes[0].toString());
768 | });
769 |
770 | it('should support .evaluateString() and .evaluateNumber()', () => {
771 | var xml = 'Harry Potter7';
772 | var doc = parseXml(xml);
773 | var parsed = xpath.parse('/*/numVolumes');
774 |
775 | assert.strictEqual(typeof parsed, 'object', 'parse() should return an object');
776 |
777 | assert.strictEqual(typeof parsed.evaluateString, 'function', 'parsed.evaluateString should be a function');
778 | assert.strictEqual('7', parsed.evaluateString({ node: doc }));
779 |
780 | assert.strictEqual(typeof parsed.evaluateBoolean, 'function', 'parsed.evaluateBoolean should be a function');
781 | assert.strictEqual(true, parsed.evaluateBoolean({ node: doc }));
782 |
783 | assert.strictEqual(typeof parsed.evaluateNumber, 'function', 'parsed.evaluateNumber should be a function');
784 | assert.strictEqual(7, parsed.evaluateNumber({ node: doc }));
785 | });
786 |
787 | it('should support .evaluateBoolean()', () => {
788 | var xml = 'Harry Potter';
789 | var doc = parseXml(xml);
790 | var context = { node: doc };
791 |
792 | function evaluate(path) {
793 | return xpath.parse(path).evaluateBoolean(context);
794 | }
795 |
796 | assert.strictEqual(false, evaluate('/*/myrtle'), 'boolean value of empty node set should be false');
797 |
798 | assert.strictEqual(true, evaluate('not(/*/myrtle)'), 'not() of empty nodeset should be true');
799 |
800 | assert.strictEqual(true, evaluate('/*/title'), 'boolean value of non-empty nodeset should be true');
801 |
802 | assert.strictEqual(true, evaluate('/*/title = "Harry Potter"'), 'title equals Harry Potter');
803 |
804 | assert.strictEqual(false, evaluate('/*/title != "Harry Potter"'), 'title != Harry Potter should be false');
805 |
806 | assert.strictEqual(false, evaluate('/*/title = "Percy Jackson"'), 'title should not equal Percy Jackson');
807 | });
808 |
809 | it('should support namespaces', () => {
810 | var xml = '' +
811 | 'QuirrellFluffy' +
812 | 'MyrtleTom Riddle' +
813 | '';
814 | var doc = parseXml(xml);
815 |
816 | var expr = xpath.parse('/characters/c:character');
817 | var countExpr = xpath.parse('count(/characters/c:character)');
818 | var csns = 'http://chamber-secrets.com';
819 |
820 | function resolve(prefix) {
821 | if (prefix === 'c') {
822 | return csns;
823 | }
824 | }
825 |
826 | function testContext(context, description) {
827 | try {
828 | var value = expr.evaluateString(context);
829 | var count = countExpr.evaluateNumber(context);
830 |
831 | assert.strictEqual('Myrtle', value, description + ' - string value - ' + value);
832 | assert.strictEqual(2, count, description + ' map - count - ' + count);
833 | } catch (e) {
834 | e.message = description + ': ' + (e.message || '');
835 | throw e;
836 | }
837 | }
838 |
839 | testContext({
840 | node: doc,
841 | namespaces: {
842 | c: csns
843 | }
844 | }, 'Namespace map');
845 |
846 | testContext({
847 | node: doc,
848 | namespaces: resolve
849 | }, 'Namespace function');
850 |
851 | testContext({
852 | node: doc,
853 | namespaces: {
854 | getNamespace: resolve
855 | }
856 | }, 'Namespace object');
857 | });
858 |
859 | it('should support custom functions', () => {
860 | var xml = 'Harry Potter';
861 | var doc = parseXml(xml);
862 |
863 | var parsed = xpath.parse('concat(double(/*/title), " is cool")');
864 |
865 | function doubleString(context, value) {
866 | assert.strictEqual(2, arguments.length);
867 | var str = value.stringValue();
868 | return str + str;
869 | }
870 |
871 | function functions(name, namespace) {
872 | if (name === 'double') {
873 | return doubleString;
874 | }
875 | return null;
876 | }
877 |
878 | function testContext(context, description) {
879 | try {
880 | var actual = parsed.evaluateString(context);
881 | var expected = 'Harry PotterHarry Potter is cool';
882 | assert.strictEqual(expected, actual, description + ' - ' + expected + ' != ' + actual);
883 | } catch (e) {
884 | e.message = description + ": " + (e.message || '');
885 | throw e;
886 | }
887 | }
888 |
889 | testContext({
890 | node: doc,
891 | functions: functions
892 | }, 'Functions function');
893 |
894 | testContext({
895 | node: doc,
896 | functions: {
897 | getFunction: functions
898 | }
899 | }, 'Functions object');
900 |
901 | testContext({
902 | node: doc,
903 | functions: {
904 | double: doubleString
905 | }
906 | }, 'Functions map');
907 | });
908 |
909 | it('should support custom functions in namespaces', () => {
910 | var xml = 'Harry PotterRonHermioneNeville';
911 | var doc = parseXml(xml);
912 |
913 | var parsed = xpath.parse('concat(hp:double(/*/title), " is 2 cool ", hp:square(2), " school")');
914 | var hpns = 'http://harry-potter.com';
915 |
916 | var namespaces = {
917 | hp: hpns
918 | };
919 |
920 | var context = {
921 | node: doc,
922 | namespaces: {
923 | hp: hpns
924 | },
925 | functions: function (name, namespace) {
926 | if (namespace === hpns) {
927 | switch (name) {
928 | case "double":
929 | return function (context, value) {
930 | assert.strictEqual(2, arguments.length);
931 | var str = value.stringValue();
932 | return str + str;
933 | };
934 | case "square":
935 | return function (context, value) {
936 | var num = value.numberValue();
937 | return num * num;
938 | };
939 |
940 | case "xor":
941 | return function (context, l, r) {
942 | assert.strictEqual(3, arguments.length);
943 | var lbool = l.booleanValue();
944 | var rbool = r.booleanValue();
945 | return (lbool || rbool) && !(lbool && rbool);
946 | };
947 |
948 | case "second":
949 | return function (context, nodes) {
950 | var nodesArr = nodes.toArray();
951 | var second = nodesArr[1];
952 | return second ? [second] : [];
953 | };
954 | }
955 | }
956 | return null;
957 | }
958 | };
959 |
960 | assert.strictEqual('Harry PotterHarry Potter is 2 cool 4 school', parsed.evaluateString(context));
961 |
962 | assert.strictEqual(false, xpath.parse('hp:xor(false(), false())').evaluateBoolean(context));
963 | assert.strictEqual(true, xpath.parse('hp:xor(false(), true())').evaluateBoolean(context));
964 | assert.strictEqual(true, xpath.parse('hp:xor(true(), false())').evaluateBoolean(context));
965 | assert.strictEqual(false, xpath.parse('hp:xor(true(), true())').evaluateBoolean(context));
966 |
967 | assert.strictEqual('Hermione', xpath.parse('hp:second(/*/friend)').evaluateString(context));
968 | assert.strictEqual(1, xpath.parse('count(hp:second(/*/friend))').evaluateNumber(context));
969 | assert.strictEqual(0, xpath.parse('count(hp:second(/*/friendz))').evaluateNumber(context));
970 | });
971 |
972 | it('should support xpath variables', () => {
973 | var xml = 'Harry Potter7';
974 | var doc = parseXml(xml);
975 |
976 | var variables = {
977 | title: 'Harry Potter',
978 | notTitle: 'Percy Jackson',
979 | houses: 4
980 | };
981 |
982 | function variableFunction(name) {
983 | return variables[name];
984 | }
985 |
986 | function testContext(context, description) {
987 | try {
988 | assert.strictEqual(true, xpath.parse('$title = /*/title').evaluateBoolean(context));
989 | assert.strictEqual(false, xpath.parse('$notTitle = /*/title').evaluateBoolean(context));
990 | assert.strictEqual(11, xpath.parse('$houses + /*/volumes').evaluateNumber(context));
991 | } catch (e) {
992 | e.message = description + ": " + (e.message || '');
993 | throw e;
994 | }
995 | }
996 |
997 | testContext({
998 | node: doc,
999 | variables: variableFunction
1000 | }, 'Variables function');
1001 |
1002 | testContext({
1003 | node: doc,
1004 | variables: {
1005 | getVariable: variableFunction
1006 | }
1007 | }, 'Variables object');
1008 |
1009 | testContext({
1010 | node: doc,
1011 | variables: variables
1012 | }, 'Variables map');
1013 |
1014 | });
1015 |
1016 | it('should support variables with namespaces', () => {
1017 | var xml = 'Harry Potter7';
1018 | var doc = parseXml(xml);
1019 | var hpns = 'http://harry-potter.com';
1020 |
1021 | var context = {
1022 | node: doc,
1023 | namespaces: {
1024 | hp: hpns
1025 | },
1026 | variables: function (name, namespace) {
1027 | if (namespace === hpns) {
1028 | switch (name) {
1029 | case 'title': return 'Harry Potter';
1030 | case 'houses': return 4;
1031 | case 'false': return false;
1032 | case 'falseStr': return 'false';
1033 | }
1034 | } else if (namespace === '') {
1035 | switch (name) {
1036 | case 'title': return 'World';
1037 | }
1038 | }
1039 |
1040 | return null;
1041 | }
1042 | };
1043 |
1044 | assert.strictEqual(true, xpath.parse('$hp:title = /*/title').evaluateBoolean(context));
1045 | assert.strictEqual(false, xpath.parse('$title = /*/title').evaluateBoolean(context));
1046 | assert.strictEqual('World', xpath.parse('$title').evaluateString(context));
1047 | assert.strictEqual(false, xpath.parse('$hp:false').evaluateBoolean(context));
1048 | assert.notEqual(false, xpath.parse('$hp:falseStr').evaluateBoolean(context));
1049 | assert.throws(function () {
1050 | xpath.parse('$hp:hello').evaluateString(context);
1051 | }, function (err) {
1052 | return err.message === 'Undeclared variable: $hp:hello';
1053 | });
1054 | });
1055 |
1056 | it('should support .toString()', () => {
1057 | var parser = new xpath.XPathParser();
1058 |
1059 | var simpleStep = parser.parse('my:book');
1060 |
1061 | assert.strictEqual(simpleStep.toString(), 'child::my:book');
1062 |
1063 | var precedingSib = parser.parse('preceding-sibling::my:chapter');
1064 |
1065 | assert.strictEqual(precedingSib.toString(), 'preceding-sibling::my:chapter');
1066 |
1067 | var withPredicates = parser.parse('book[number > 3][contains(title, "and the")]');
1068 |
1069 | assert.strictEqual(withPredicates.toString(), "child::book[(child::number > 3)][contains(child::title, 'and the')]");
1070 |
1071 | var parenthesisWithPredicate = parser.parse('(/books/book/chapter)[7]');
1072 |
1073 | assert.strictEqual(parenthesisWithPredicate.toString(), '(/child::books/child::book/child::chapter)[7]');
1074 |
1075 | var charactersOver20 = parser.parse('heroes[age > 20] | villains[age > 20]');
1076 |
1077 | assert.strictEqual(charactersOver20.toString(), 'child::heroes[(child::age > 20)] | child::villains[(child::age > 20)]');
1078 | });
1079 | });
1080 |
1081 | describe('html-mode support', () => {
1082 | it('should allow null namespaces for nodes with no prefix', () => {
1083 | var markup = `
1084 |
1085 |
1086 | Hi Ron!
1087 | Hi Draco!
1088 | Hi Hermione!
1089 |
1090 | `;
1091 |
1092 | var docHtml = parseXml(markup, 'text/html');
1093 |
1094 | var noPrefixPath = xpath.parse('/html/body/p[2]');
1095 |
1096 | var greetings1 = noPrefixPath.select({ node: docHtml, allowAnyNamespaceForNoPrefix: false });
1097 |
1098 | assert.strictEqual(0, greetings1.length);
1099 |
1100 | var allowAnyNamespaceOptions = { node: docHtml, allowAnyNamespaceForNoPrefix: true };
1101 |
1102 | // if allowAnyNamespaceForNoPrefix specified, allow using prefix-less node tests to match nodes with no prefix
1103 | var greetings2 = noPrefixPath.select(allowAnyNamespaceOptions);
1104 |
1105 | assert.strictEqual(1, greetings2.length);
1106 | assert.strictEqual('Hi Hermione!', greetings2[0].textContent);
1107 |
1108 | var allGreetings = xpath.parse('/html/body/p').select(allowAnyNamespaceOptions);
1109 |
1110 | assert.strictEqual(2, allGreetings.length);
1111 |
1112 | var nsm = { html: xhtmlNs, other: 'http://www.example.com/other' };
1113 |
1114 | var prefixPath = xpath.parse('/html:html/body/html:p');
1115 | var optionsWithNamespaces = { node: docHtml, allowAnyNamespaceForNoPrefix: true, namespaces: nsm };
1116 |
1117 | // if the path uses prefixes, they have to match
1118 | var greetings3 = prefixPath.select(optionsWithNamespaces);
1119 |
1120 | assert.strictEqual(2, greetings3.length);
1121 |
1122 | var badPrefixPath = xpath.parse('/html:html/other:body/html:p');
1123 |
1124 | var greetings4 = badPrefixPath.select(optionsWithNamespaces);
1125 |
1126 | assert.strictEqual(0, greetings4.length);
1127 | });
1128 |
1129 | it('should support the isHtml option', () => {
1130 | var markup = 'Hi Ron!
Hi Draco!Hi Hermione!
';
1131 | var docHtml = parseXml(markup, 'text/html');
1132 |
1133 | var ns = { h: xhtmlNs };
1134 |
1135 | // allow matching on unprefixed nodes
1136 | var greetings1 = xpath.parse('/html/body/p').select({ node: docHtml, isHtml: true });
1137 |
1138 | assert.strictEqual(2, greetings1.length);
1139 |
1140 | // allow case insensitive match
1141 | var greetings2 = xpath.parse('/h:html/h:bOdY/h:p').select({ node: docHtml, namespaces: ns, isHtml: true });
1142 |
1143 | assert.strictEqual(2, greetings2.length);
1144 |
1145 | // non-html mode: allow select if case and namespaces match
1146 | var greetings3 = xpath.parse('/h:html/h:body/h:p').select({ node: docHtml, namespaces: ns });
1147 |
1148 | assert.strictEqual(2, greetings3.length);
1149 |
1150 | // non-html mode: require namespaces
1151 | var greetings4 = xpath.parse('/html/body/p').select({ node: docHtml, namespaces: ns });
1152 |
1153 | assert.strictEqual(0, greetings4.length);
1154 |
1155 | // non-html mode: require case to match
1156 | var greetings5 = xpath.parse('/h:html/h:bOdY/h:p').select({ node: docHtml, namespaces: ns });
1157 |
1158 | assert.strictEqual(0, greetings5.length);
1159 | });
1160 | });
1161 |
1162 | describe('functions', () => {
1163 | it('should provide a meaningful error for invalid functions', () => {
1164 | var path = xpath.parse('invalidFunc()');
1165 |
1166 | assert.throws(function () {
1167 | path.evaluateString();
1168 | }, function (err) {
1169 | return err.message.indexOf('invalidFunc') !== -1;
1170 | });
1171 |
1172 | var path2 = xpath.parse('funcs:invalidFunc()');
1173 |
1174 | assert.throws(function () {
1175 | path2.evaluateString({
1176 | namespaces: {
1177 | funcs: 'myfunctions'
1178 | }
1179 | });
1180 | }, function (err) {
1181 | return err.message.indexOf('invalidFunc') !== -1;
1182 | });
1183 | });
1184 |
1185 | // https://github.com/goto100/xpath/issues/32
1186 | it('should support the contains() function on attributes', () => {
1187 | var doc = parseXml(""),
1188 | andTheBooks = xpath.select("/books/book[contains(@title, ' ')]", doc),
1189 | secretBooks = xpath.select("/books/book[contains(@title, 'Secrets')]", doc);
1190 |
1191 | assert.strictEqual(andTheBooks.length, 2);
1192 | assert.strictEqual(secretBooks.length, 1);
1193 | });
1194 |
1195 | it('should support builtin functions', () => {
1196 | var translated = xpath.parse('translate("hello", "lhho", "yHb")').evaluateString();
1197 |
1198 | assert.strictEqual('Heyy', translated);
1199 |
1200 | var characters = parseXml(`
1201 | Harry
1202 | Ron
1203 | Hermione
1204 | `);
1205 |
1206 | var firstTwo = xpath.parse('/characters/character[position() <= 2]').select({ node: characters });
1207 |
1208 | assert.strictEqual(2, firstTwo.length);
1209 | assert.strictEqual('Harry', firstTwo[0].textContent);
1210 | assert.strictEqual('Ron', firstTwo[1].textContent);
1211 |
1212 | const last = xpath.parse('/characters/character[last()]').select({ node: characters });
1213 |
1214 | assert.strictEqual(1, last.length);
1215 | assert.strictEqual('Hermione', last[0].textContent);
1216 |
1217 | const lastPrefiltered = xpath.parse('/characters/character[. != "Hermione"][last()]').select({ node: characters });
1218 |
1219 | assert.strictEqual(1, lastPrefiltered.length);
1220 | assert.strictEqual('Ron', lastPrefiltered[0].textContent);
1221 |
1222 | const lastStrict = xpath.parse('/characters/character[last() = 3]').select({ node: characters, });
1223 |
1224 | assert.equal(3, lastStrict.length);
1225 |
1226 | const lastStrictMiss = xpath.parse('/characters/character[last() = 2]').select({ node: characters, });
1227 |
1228 | assert.equal(0, lastStrictMiss.length);
1229 | });
1230 | });
1231 |
1232 | describe('.parse()', () => {
1233 | it('should correctly set types on path steps', () => {
1234 | const parsed = xpath.parse('./my:*/my:name');
1235 |
1236 | const steps = parsed.expression.expression.locationPath.steps;
1237 |
1238 | const step0 = steps[0];
1239 |
1240 | assert.strictEqual(xpath.NodeTest.NODE, step0.nodeTest.type);
1241 |
1242 | const step1 = steps[1];
1243 |
1244 | assert.strictEqual(xpath.NodeTest.NAMETESTPREFIXANY, step1.nodeTest.type);
1245 | assert.strictEqual('my', step1.nodeTest.prefix);
1246 |
1247 | const step2 = steps[2];
1248 |
1249 | assert.strictEqual(xpath.NodeTest.NAMETESTQNAME, step2.nodeTest.type);
1250 | assert.strictEqual('my', step2.nodeTest.prefix);
1251 | assert.strictEqual('name', step2.nodeTest.localName);
1252 | assert.strictEqual('my:name', step2.nodeTest.name);
1253 | });
1254 | })
1255 |
1256 | describe('miscellaneous', () => {
1257 | it('should create XPathExceptions that act like Errors', () => {
1258 | try {
1259 | xpath.evaluate('1', null, null, null);
1260 | assert.fail(null, null, 'evaluate() should throw exception');
1261 | } catch (e) {
1262 | assert.ok('code' in e, 'must have a code');
1263 | assert.ok('stack' in e, 'must have a stack');
1264 | }
1265 | });
1266 |
1267 | it('should expose custom types', () => {
1268 | assert.ok(xpath.XPath, "xpath.XPath");
1269 | assert.ok(xpath.XPathParser, "xpath.XPathParser");
1270 | assert.ok(xpath.XPathResult, "xpath.XPathResult");
1271 |
1272 | assert.ok(xpath.Step, "xpath.Step");
1273 | assert.ok(xpath.NodeTest, "xpath.NodeTest");
1274 |
1275 | assert.ok(xpath.OrOperation, "xpath.OrOperation");
1276 | assert.ok(xpath.AndOperation, "xpath.AndOperation");
1277 |
1278 | assert.ok(xpath.BarOperation, "xpath.BarOperation");
1279 |
1280 | assert.ok(xpath.NamespaceResolver, "xpath.NamespaceResolver");
1281 | assert.ok(xpath.FunctionResolver, "xpath.FunctionResolver");
1282 | assert.ok(xpath.VariableResolver, "xpath.VariableResolver");
1283 |
1284 | assert.ok(xpath.Utilities, "xpath.Utilities");
1285 |
1286 | assert.ok(xpath.XPathContext, "xpath.XPathContext");
1287 | assert.ok(xpath.XNodeSet, "xpath.XNodeSet");
1288 | assert.ok(xpath.XBoolean, "xpath.XBoolean");
1289 | assert.ok(xpath.XString, "xpath.XString");
1290 | assert.ok(xpath.XNumber, "xpath.XNumber");
1291 | });
1292 |
1293 | it('should work with nodes created using DOM1 createElement()', () => {
1294 | var doc = parseXml('');
1295 |
1296 | doc.documentElement.appendChild(doc.createElement('characters'));
1297 |
1298 | assert.ok(xpath.select1('/book/characters', doc));
1299 |
1300 | assert.strictEqual(xpath.select1('local-name(/book/characters)', doc), 'characters');
1301 | });
1302 | });
1303 |
1304 | describe('error handling', () => {
1305 | it('should reject unspecified expression', () => {
1306 | for (let expr of [null, undefined, '']) {
1307 | assert.throws(() => xpath.parse(expr), {
1308 | message: 'XPath expression unspecified.',
1309 | });
1310 | }
1311 | });
1312 |
1313 | it('should reject non-string expression', () => {
1314 | for (let expr of [{}, []]) {
1315 | assert.throws(() => xpath.parse(expr), {
1316 | message: 'XPath expression must be a string.',
1317 | });
1318 | }
1319 | });
1320 | it('should reject non-nodes', () => {
1321 | for (let node of ['', 0, 45, true, false, [], {}]) {
1322 | assert.throws(() => xpath.parse('/*').select({ node }), {
1323 | message: 'Context node does not appear to be a valid DOM node.',
1324 | });
1325 | }
1326 | });
1327 |
1328 | it('should handle unspecified nodes', () => {
1329 | assert.throws(
1330 | () => xpath.parse('my:field').select(), {
1331 | message: 'Context node not found when evaluating XPath step: child::my:field',
1332 | });
1333 |
1334 | assert.throws(
1335 | () => xpath.parse('/*').select(), {
1336 | message: 'Context node not found when determining document root.',
1337 | });
1338 | })
1339 | });
1340 |
1341 | describe('Node type tests', () => {
1342 | it('should correctly identify a Node of type Element', () => {
1343 | var doc = parseXml('');
1344 | var element = doc.createElement('characters');
1345 |
1346 | assert.ok(xpath.isNodeLike(element));
1347 | assert.ok(xpath.isElement(element));
1348 | assert.ok(!xpath.isAttribute(doc));
1349 | });
1350 |
1351 | it('should correctly identify a Node of type Attribute', () => {
1352 | var doc = parseXml('');
1353 | var attribute = doc.createAttribute('name');
1354 |
1355 | assert.ok(xpath.isNodeLike(attribute));
1356 | assert.ok(xpath.isAttribute(attribute));
1357 | assert.ok(!xpath.isTextNode(attribute));
1358 | });
1359 |
1360 | it('should correctly identify a Node of type Text', () => {
1361 | var doc = parseXml('');
1362 | var text = doc.createTextNode('Harry Potter');
1363 |
1364 | assert.ok(xpath.isNodeLike(text));
1365 | assert.ok(xpath.isTextNode(text));
1366 | assert.ok(!xpath.isCDATASection(text));
1367 | });
1368 |
1369 | it('should correctly identify a Node of type CDATASection', () => {
1370 | var doc = parseXml('');
1371 | var cdata = doc.createCDATASection('Harry Potter');
1372 |
1373 | assert.ok(xpath.isNodeLike(cdata));
1374 | assert.ok(xpath.isCDATASection(cdata));
1375 | assert.ok(!xpath.isProcessingInstruction(cdata));
1376 | });
1377 |
1378 | it('should correctly identify a Node of type ProcessingInstruction', () => {
1379 | var doc = parseXml('');
1380 | var pi = doc.createProcessingInstruction('xml-stylesheet', 'href="mycss.css" type="text/css"');
1381 |
1382 | // This test fails due to a bug in @xmldom/xmldom@0.8.8
1383 | // assert.ok(xpath.isNodeLike(pi));
1384 | assert.ok(xpath.isProcessingInstruction(pi));
1385 | assert.ok(!xpath.isComment(pi));
1386 | });
1387 |
1388 | it('should correctly identify a Node of type Comment', () => {
1389 | var doc = parseXml('');
1390 | var comment = doc.createComment('Harry Potter');
1391 |
1392 | assert.ok(xpath.isNodeLike(comment));
1393 | assert.ok(xpath.isComment(comment));
1394 | assert.ok(!xpath.isDocumentNode(comment));
1395 | });
1396 |
1397 | it('should correctly identify a Node of type Document', () => {
1398 | var doc = parseXml('');
1399 |
1400 | assert.ok(xpath.isNodeLike(doc));
1401 | assert.ok(xpath.isDocumentNode(doc));
1402 | assert.ok(!xpath.isDocumentTypeNode(doc));
1403 | });
1404 |
1405 | it('should correctly identify a Node of type DocumentType', () => {
1406 | var doc = parseXml('');
1407 | var doctype = doc.implementation.createDocumentType('book', null, null);
1408 |
1409 | assert.ok(xpath.isNodeLike(doctype));
1410 | assert.ok(xpath.isDocumentTypeNode(doctype));
1411 | assert.ok(!xpath.isDocumentFragment(doctype));
1412 | });
1413 |
1414 | it('should correctly identify a Node of type DocumentFragment', () => {
1415 | var doc = parseXml('');
1416 | var fragment = doc.createDocumentFragment();
1417 |
1418 | assert.ok(xpath.isNodeLike(fragment));
1419 | assert.ok(xpath.isDocumentFragment(fragment));
1420 | assert.ok(!xpath.isElement(fragment));
1421 | });
1422 |
1423 | it('should not identify a string as a Node', () => {
1424 | assert.ok(!xpath.isNodeLike('Harry Potter'));
1425 | });
1426 |
1427 | it('should not identify a number as a Node', () => {
1428 | assert.ok(!xpath.isNodeLike(45));
1429 | });
1430 |
1431 | it('should not identify a boolean as a Node', () => {
1432 | assert.ok(!xpath.isNodeLike(true));
1433 | });
1434 |
1435 | it('should not identify null as a Node', () => {
1436 | assert.ok(!xpath.isNodeLike(null));
1437 | });
1438 |
1439 | it('should not identify undefined as a Node', () => {
1440 | assert.ok(!xpath.isNodeLike(undefined));
1441 | });
1442 |
1443 | it('should not identify an array as a Node', () => {
1444 | assert.ok(!xpath.isNodeLike([]));
1445 | });
1446 |
1447 | it('should identify an array of Nodes as such', () => {
1448 | var doc = parseXml('');
1449 | var fragment = doc.createDocumentFragment();
1450 | var nodes = [doc, fragment];
1451 |
1452 | assert.ok(xpath.isArrayOfNodes(nodes));
1453 | assert.ok(!xpath.isNodeLike(nodes));
1454 | });
1455 |
1456 | it('should not identify an array of non-Nodes as an array of Nodes', () => {
1457 | var nodes = ['Harry Potter', 45];
1458 |
1459 | assert.ok(!xpath.isArrayOfNodes(nodes));
1460 | assert.ok(!xpath.isNodeLike(nodes));
1461 | });
1462 |
1463 | it('should not identify an array of mixed Nodes and non-Nodes as an array of Nodes', () => {
1464 | var doc = parseXml('');
1465 | var fragment = doc.createDocumentFragment();
1466 | var nodes = [doc, fragment, 'Harry Potter'];
1467 |
1468 | assert.ok(!xpath.isArrayOfNodes(nodes));
1469 | assert.ok(!xpath.isNodeLike(nodes));
1470 | });
1471 | });
1472 | });
--------------------------------------------------------------------------------