-
Notifications
You must be signed in to change notification settings - Fork 6
/
index.ts
149 lines (129 loc) · 3.67 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*!
* XML-Query
* Copyright (c) 2016 Pedro Ladaria
* https://github.com/pladaria/xml-query
* MIT Licensed
*/
const flatMap = (arr: any[], fn: (v: any, i: number, a: any[]) => any) =>
Array.prototype.concat.apply([], arr.map(fn));
const xmlQuery = (ast: xmlQuery.XmlNode | xmlQuery.XmlNode[]) => {
const nodes = Array.isArray(ast) ? ast : (ast ? [ast] : []);
const length = nodes.length;
/**
* Retrieve one of the elements
*/
const get = (index: number) => nodes[index];
/**
* Returns a new xmlQuery object containing the children of the top level elements
*/
const children = () =>
xmlQuery(flatMap(nodes, (node) => node.children));
/**
* Recursively find by name starting in the provided node
*/
const findInNode = (node: xmlQuery.XmlNode, sel: string) => {
const res = (node.name === sel) ? [node] : [];
return res.concat(flatMap(node.children, (node) => findInNode(node, sel)));
};
/**
* Find by name. Including top level nodes and all its children.
*/
const find = (sel: string) =>
xmlQuery(flatMap(nodes, (node) => findInNode(node, sel)));
/**
* Returns true if it has the given element. Faster than find because it stops on first occurence.
*/
const has = (sel: string) => {
if (nodes.length === 0) {
return false;
}
if (nodes.some((node) => node.name === sel)) {
return true;
}
return children().has(sel);
}
/**
* Get all attributes. If a name is provided, it returns the value for that key
*/
const attr = (name?: string) => {
if (length) {
const attrs = nodes[0].attributes;
return name ? attrs[name] : attrs;
}
};
/**
* Returns a new XmlQuery object for the selected element by index
*/
const eq = (index: number) => xmlQuery(nodes[index]);
/**
* Returns a new XmlQuery object for the first element
*/
const first = () => eq(0);
/**
* Returns a new XmlQuery object for the last element
*/
const last = () => eq(length - 1);
/**
* Iterate over a xmlQuery object, executing a function for each element. Returns the results in an array.
*/
const map = (fn: (v: xmlQuery.XmlNode, i: number, a: xmlQuery.XmlNode[]) => any) => nodes.map(fn);
/**
* Iterate over a xmlQuery object, executing a function for each element
*/
const each = (fn: (v: xmlQuery.XmlNode, i: number, a: xmlQuery.XmlNode[]) => void) => nodes.forEach(fn);
/**
* Get length
*/
const size = () => length;
/**
* Get the value of a property for the first element in the set
*/
const prop = (name: string) => {
const node = get(0);
if (node) {
return node[name];
}
};
/**
* Get the combined text contents of each element, including their descendants
*/
const text = () => {
let res = '';
each(node => {
if (node.type === 'text') {
res += node.value;
} else {
res += xmlQuery(node).children().text();
}
});
return res;
};
return {
attr,
children,
each,
eq,
find,
has,
first,
get,
last,
length,
map,
prop,
size,
text,
ast,
};
};
namespace xmlQuery {
export interface XmlNode {
name: string;
type: string;
value: string;
parent: XmlNode;
attributes: { [name: string]: string };
children: XmlNode[];
}
}
export = xmlQuery;