@litejs/dom 23.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/index.js +489 -0
- package/interactive.js +15 -0
- package/package.json +43 -0
- package/selector.js +127 -0
- package/xmlhttprequest.js +114 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
|
|
2
|
+
[1]: https://badgen.net/coveralls/c/github/litejs/dom
|
|
3
|
+
[2]: https://coveralls.io/r/litejs/dom
|
|
4
|
+
[3]: https://badgen.net/packagephobia/install/@litejs/dom
|
|
5
|
+
[4]: https://packagephobia.now.sh/result?p=@litejs/dom
|
|
6
|
+
[5]: https://badgen.net/badge/icon/Buy%20Me%20A%20Tea/orange?icon=kofi&label
|
|
7
|
+
[6]: https://www.buymeacoffee.com/lauriro
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
LiteJS DOM – [![Coverage][1]][2] [![Size][3]][4] [![Buy Me A Tea][5]][6]
|
|
11
|
+
==========
|
|
12
|
+
|
|
13
|
+
A small DOM library for server-side testing, rendering, and handling of HTML files.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
Examples
|
|
17
|
+
--------
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
const { document, DOMParser, XMLSerializer } = require("@litejs/dom");
|
|
21
|
+
const { XMLHttpRequest } = require("@litejs/dom/xmlhttprequest");
|
|
22
|
+
|
|
23
|
+
// Use XMLHttpRequest in server side
|
|
24
|
+
var xhr = new XMLHttpRequest()
|
|
25
|
+
xhr.open("GET", "https://litejs.com")
|
|
26
|
+
xhr.responseType = "document"
|
|
27
|
+
xhr.onload = function() {
|
|
28
|
+
var doc = xhr.responseXML
|
|
29
|
+
// Work with DOM in familiar way
|
|
30
|
+
console.log(doc.querySelector("title").textContent)
|
|
31
|
+
}
|
|
32
|
+
xhr.send()
|
|
33
|
+
|
|
34
|
+
// Build DOM manually
|
|
35
|
+
const el = document.createElement("h1");
|
|
36
|
+
el.id = 123;
|
|
37
|
+
el.className = "large";
|
|
38
|
+
|
|
39
|
+
const fragment = document.createDocumentFragment();
|
|
40
|
+
const text1 = document.createTextNode("hello");
|
|
41
|
+
const text2 = document.createTextNode(" world");
|
|
42
|
+
|
|
43
|
+
fragment.appendChild(text1);
|
|
44
|
+
fragment.appendChild(text2);
|
|
45
|
+
el.appendChild(fragment);
|
|
46
|
+
|
|
47
|
+
el.innerHTML;
|
|
48
|
+
// hello world
|
|
49
|
+
el.innerHTML = "<b>hello world</b>";
|
|
50
|
+
el.toString();
|
|
51
|
+
// <h1 id="123" class="large"><b>hello world</b></h1>
|
|
52
|
+
|
|
53
|
+
// minify output
|
|
54
|
+
el.toString(true);
|
|
55
|
+
// <h1 id=123 class=large><b>hello world</b></h1>
|
|
56
|
+
|
|
57
|
+
el.querySelectorAll("b");
|
|
58
|
+
// [ "<b>hello world</b>" ]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Contributing
|
|
62
|
+
|
|
63
|
+
Follow [Coding Style Guide](https://github.com/litejs/litejs/wiki/Style-Guide)
|
|
64
|
+
|
|
65
|
+
Run tests
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
npm install
|
|
69
|
+
npm test
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
## External links
|
|
74
|
+
|
|
75
|
+
[GitHub repo](https://github.com/litejs/dom) |
|
|
76
|
+
[npm package](https://npmjs.org/package/@litejs/dom) |
|
|
77
|
+
[DOM spec](https://dom.spec.whatwg.org/) |
|
|
78
|
+
[Selectors Level 3](http://www.w3.org/TR/selectors/) |
|
|
79
|
+
[Coveralls coverage][2]
|
|
80
|
+
[Buy Me A Tea][6]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Licence
|
|
84
|
+
|
|
85
|
+
Copyright (c) 2014-2023 Lauri Rooden <lauri@rooden.ee>
|
|
86
|
+
[The MIT License](http://lauri.rooden.ee/mit-license.txt)
|
|
87
|
+
|
|
88
|
+
|
package/index.js
ADDED
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @author Lauri Rooden <lauri@rooden.ee>
|
|
5
|
+
* @license MIT License
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
var boolAttrs = {
|
|
10
|
+
async:1, autoplay:1, loop:1, checked:1, defer:1, disabled:1, muted:1, multiple:1, nomodule:1, playsinline:1, readonly:1, required:1, selected:1
|
|
11
|
+
}
|
|
12
|
+
, defaultAttrs = {
|
|
13
|
+
"form method get":1, "input type text":1,
|
|
14
|
+
"script type text/javascript":1, "style type text/css": 1
|
|
15
|
+
}
|
|
16
|
+
, voidElements = {
|
|
17
|
+
AREA:1, BASE:1, BR:1, COL:1, EMBED:1, HR:1, IMG:1, INPUT:1, KEYGEN:1, LINK:1, MENUITEM:1, META:1, PARAM:1, SOURCE:1, TRACK:1, WBR:1
|
|
18
|
+
}
|
|
19
|
+
, rawTextElements = { SCRIPT: /<(?=\/script)/i, STYLE: /<(?=\/style)/i }
|
|
20
|
+
, rawTextEscape = { SCRIPT: /<(?=\/script|!--)/ig, STYLE: /<(?=\/style|!--)/ig }
|
|
21
|
+
, hasOwn = voidElements.hasOwnProperty
|
|
22
|
+
, selector = require("./selector.js")
|
|
23
|
+
, Node = {
|
|
24
|
+
ELEMENT_NODE: 1,
|
|
25
|
+
TEXT_NODE: 3,
|
|
26
|
+
PROCESSING_INSTRUCTION_NODE: 7,
|
|
27
|
+
COMMENT_NODE: 8,
|
|
28
|
+
DOCUMENT_NODE: 9,
|
|
29
|
+
DOCUMENT_TYPE_NODE: 10,
|
|
30
|
+
DOCUMENT_FRAGMENT_NODE: 11,
|
|
31
|
+
nodeName: null,
|
|
32
|
+
parentNode: null,
|
|
33
|
+
ownerDocument: null,
|
|
34
|
+
childNodes: null,
|
|
35
|
+
get nodeValue() {
|
|
36
|
+
return this.nodeType === 3 || this.nodeType === 8 ? this.data : null
|
|
37
|
+
},
|
|
38
|
+
set nodeValue(text) {
|
|
39
|
+
return this.nodeType === 3 || this.nodeType === 8 ? (this.data = text) : null
|
|
40
|
+
},
|
|
41
|
+
get textContent() {
|
|
42
|
+
return this.nodeType === 3 || this.nodeType === 8 ? this.data : this.childNodes.map(function(child) {
|
|
43
|
+
return child.textContent
|
|
44
|
+
}).join("")
|
|
45
|
+
},
|
|
46
|
+
set textContent(text) {
|
|
47
|
+
if (this.nodeType === 3 || this.nodeType === 8) return (this.data = text)
|
|
48
|
+
replaceChildren.call(this, this.ownerDocument.createTextNode(
|
|
49
|
+
rawTextEscape[this.tagName] ? text.replace(rawTextEscape[this.tagName], "<\\") : text
|
|
50
|
+
))
|
|
51
|
+
},
|
|
52
|
+
get firstChild() {
|
|
53
|
+
return this.childNodes && this.childNodes[0] || null
|
|
54
|
+
},
|
|
55
|
+
get lastChild() {
|
|
56
|
+
return this.childNodes && this.childNodes[ this.childNodes.length - 1 ] || null
|
|
57
|
+
},
|
|
58
|
+
get nextSibling() {
|
|
59
|
+
return getSibling(this, 1, 0)
|
|
60
|
+
},
|
|
61
|
+
get previousSibling() {
|
|
62
|
+
return getSibling(this, -1, 0)
|
|
63
|
+
},
|
|
64
|
+
// innerHTML and outerHTML should be extensions to the Element interface
|
|
65
|
+
get innerHTML() {
|
|
66
|
+
return Node.toString.call(this)
|
|
67
|
+
},
|
|
68
|
+
set innerHTML(html) {
|
|
69
|
+
var child, m, re, text
|
|
70
|
+
, node = this
|
|
71
|
+
, doc = node.ownerDocument || node
|
|
72
|
+
, tagRe = /<(!--([\s\S]*?)--!?|!\[CDATA\[([\s\S]*?)\]\]|[?!][\s\S]*?)>|<(\/?)([^ \/>]+)((?:("|')(?:\\\7|[\s\S])*?\7|[^>])*?)(\/?)>|[^<]+|</g
|
|
73
|
+
, attrRe = /([^=\s]+)(?:\s*=\s*(("|')((?:\\\3|[\s\S])*?)\3|[^\s"'`=<>]+)|)/g
|
|
74
|
+
, frag = doc.createDocumentFragment()
|
|
75
|
+
, tree = frag
|
|
76
|
+
|
|
77
|
+
for (; (m = tagRe.exec(html)); ) {
|
|
78
|
+
if (m[4]) {
|
|
79
|
+
tree = tree.parentNode || tree
|
|
80
|
+
} else if (m[5]) {
|
|
81
|
+
child = doc.contentType === "text/html" ? doc.createElement(m[5]) : doc.createElementNS(null, m[5])
|
|
82
|
+
if (m[6]) {
|
|
83
|
+
m[6].replace(attrRe, setAttr)
|
|
84
|
+
}
|
|
85
|
+
tree.appendChild(child)
|
|
86
|
+
if ((re = rawTextElements[child.tagName])) {
|
|
87
|
+
for (text = ""; (m = tagRe.exec(html)) && !re.test(m[0]); text += m[3] || m[2] || m[0]);
|
|
88
|
+
child.textContent = text.replace(unescRe, unescFn)
|
|
89
|
+
} else if (!voidElements[child.tagName] && !m[8]) tree = child
|
|
90
|
+
} else {
|
|
91
|
+
tree.appendChild(
|
|
92
|
+
m[2] ? doc.createComment(m[2].replace(unescRe, unescFn)) :
|
|
93
|
+
m[1] ? doc.createDocumentType(m[1]) :
|
|
94
|
+
doc.createTextNode(m[0].replace(unescRe, unescFn))
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
replaceChildren.call(node, frag)
|
|
99
|
+
|
|
100
|
+
return html
|
|
101
|
+
|
|
102
|
+
function setAttr(_, name, value, q, qvalue) {
|
|
103
|
+
child.setAttribute(name, (q ? qvalue : value || "").replace(unescRe, unescFn))
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
get outerHTML() {
|
|
107
|
+
return this.toString()
|
|
108
|
+
},
|
|
109
|
+
set outerHTML(html) {
|
|
110
|
+
var frag = this.ownerDocument.createDocumentFragment()
|
|
111
|
+
frag.innerHTML = html
|
|
112
|
+
this.parentNode.replaceChild(frag, this)
|
|
113
|
+
return html
|
|
114
|
+
},
|
|
115
|
+
get htmlFor() {
|
|
116
|
+
return this["for"]
|
|
117
|
+
},
|
|
118
|
+
set htmlFor(value) {
|
|
119
|
+
this["for"] = value
|
|
120
|
+
},
|
|
121
|
+
get className() {
|
|
122
|
+
return this["class"] || ""
|
|
123
|
+
},
|
|
124
|
+
set className(value) {
|
|
125
|
+
this["class"] = value
|
|
126
|
+
},
|
|
127
|
+
get style() {
|
|
128
|
+
return this.styleMap || (this.styleMap = new StyleMap())
|
|
129
|
+
},
|
|
130
|
+
set style(value) {
|
|
131
|
+
this.styleMap = new StyleMap(value)
|
|
132
|
+
},
|
|
133
|
+
contains: function (el) {
|
|
134
|
+
for (; el; el = el.parentNode) if (el === this) return true
|
|
135
|
+
return false
|
|
136
|
+
},
|
|
137
|
+
hasChildNodes: function() {
|
|
138
|
+
return this.childNodes && this.childNodes.length > 0
|
|
139
|
+
},
|
|
140
|
+
appendChild: function(el) {
|
|
141
|
+
return this.insertBefore(el)
|
|
142
|
+
},
|
|
143
|
+
insertBefore: function(el, ref) {
|
|
144
|
+
var node = this
|
|
145
|
+
, childs = node.childNodes
|
|
146
|
+
|
|
147
|
+
if (el.nodeType === 11) {
|
|
148
|
+
for (; el.firstChild; ) node.insertBefore(el.firstChild, ref)
|
|
149
|
+
} else {
|
|
150
|
+
if (el.parentNode) el.parentNode.removeChild(el)
|
|
151
|
+
el.parentNode = node
|
|
152
|
+
|
|
153
|
+
// If ref is null, insert el at the end of the list of children.
|
|
154
|
+
childs.splice(ref ? childs.indexOf(ref) : childs.length, 0, el)
|
|
155
|
+
if (node.nodeType === 9 && el.nodeType === 1) {
|
|
156
|
+
node.documentElement = el
|
|
157
|
+
node.body = el.querySelector("body")
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return el
|
|
161
|
+
},
|
|
162
|
+
removeChild: function(el) {
|
|
163
|
+
var node = this
|
|
164
|
+
, index = node.childNodes.indexOf(el)
|
|
165
|
+
if (index === -1) throw Error("NOT_FOUND_ERR")
|
|
166
|
+
|
|
167
|
+
node.childNodes.splice(index, 1)
|
|
168
|
+
el.parentNode = null
|
|
169
|
+
return el
|
|
170
|
+
},
|
|
171
|
+
replaceChild: function(el, ref) {
|
|
172
|
+
this.insertBefore(el, ref)
|
|
173
|
+
return this.removeChild(ref)
|
|
174
|
+
},
|
|
175
|
+
cloneNode: function(deep) {
|
|
176
|
+
var key
|
|
177
|
+
, node = this
|
|
178
|
+
, clone = new node.constructor(node.tagName || node.data)
|
|
179
|
+
clone.ownerDocument = node.ownerDocument
|
|
180
|
+
|
|
181
|
+
if (node.hasAttribute) {
|
|
182
|
+
for (key in node) if (node.hasAttribute(key)) clone[key] = node[key].valueOf()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (deep && node.hasChildNodes()) {
|
|
186
|
+
node.childNodes.forEach(function(child) {
|
|
187
|
+
clone.appendChild(child.cloneNode(deep))
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
return clone
|
|
191
|
+
},
|
|
192
|
+
toString: function(minify) {
|
|
193
|
+
return rawTextElements[this.tagName] ? this.textContent : this.hasChildNodes() ? this.childNodes.reduce(function(memo, node) {
|
|
194
|
+
return memo + node.toString(minify)
|
|
195
|
+
}, "") : ""
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
, Element = {
|
|
199
|
+
get firstElementChild() {
|
|
200
|
+
return getElement(this.childNodes, 0, 1, 1)
|
|
201
|
+
},
|
|
202
|
+
get lastElementChild() {
|
|
203
|
+
return getElement(this.childNodes, this.childNodes.length - 1, -1, 1)
|
|
204
|
+
},
|
|
205
|
+
get nextElementSibling() {
|
|
206
|
+
return getSibling(this, 1, 1)
|
|
207
|
+
},
|
|
208
|
+
get previousElementSibling() {
|
|
209
|
+
return getSibling(this, -1, 1)
|
|
210
|
+
},
|
|
211
|
+
replaceChildren: replaceChildren,
|
|
212
|
+
hasAttribute: function(name) {
|
|
213
|
+
name = escAttr(name)
|
|
214
|
+
return hasOwn.call(this, name === "style" ? "styleMap" : name)
|
|
215
|
+
},
|
|
216
|
+
getAttribute: function(name) {
|
|
217
|
+
return this.hasAttribute(name) ? "" + this[escAttr(name)] : null
|
|
218
|
+
},
|
|
219
|
+
setAttribute: function(name, value) {
|
|
220
|
+
this[escAttr(name)] = "" + value
|
|
221
|
+
},
|
|
222
|
+
removeAttribute: function(name) {
|
|
223
|
+
name = escAttr(name)
|
|
224
|
+
delete this[name === "style" ? "styleMap" : name]
|
|
225
|
+
},
|
|
226
|
+
getElementById: function(id) {
|
|
227
|
+
return selector.find(this, "#" + id, 1)
|
|
228
|
+
},
|
|
229
|
+
getElementsByTagName: function(tag) {
|
|
230
|
+
return selector.find(this, tag)
|
|
231
|
+
},
|
|
232
|
+
getElementsByClassName: function(sel) {
|
|
233
|
+
return selector.find(this, "." + sel.replace(/\s+/g, "."))
|
|
234
|
+
},
|
|
235
|
+
querySelector: function(sel) {
|
|
236
|
+
return selector.find(this, sel, 1)
|
|
237
|
+
},
|
|
238
|
+
querySelectorAll: function(sel) {
|
|
239
|
+
return selector.find(this, sel)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
, quotedAttrRe = /[\s"'`=<>]/
|
|
243
|
+
, escRe = /<|&(?=[a-z#])/gi
|
|
244
|
+
, unescRe = /&\w+;|&#(x|)([\da-f]+);/ig
|
|
245
|
+
, unescMap = {
|
|
246
|
+
"&": "&", "'": "'", "¢": "¢", "©": "©", "¤": "¤",
|
|
247
|
+
"°": "°", "€": "€", ">": ">", "<": "<", " ": " ",
|
|
248
|
+
"±": "±", "£": "£", """: "\"", "®": "®",
|
|
249
|
+
"§": "§", "²": "²", "³": "³", "¥": "¥"
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function escFn(chr) {
|
|
253
|
+
return chr === "<" ? "<" : "&"
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function unescFn(ent, hex, num) {
|
|
257
|
+
return num ? String.fromCharCode(parseInt(num, hex === "" ? 10 : 16)) : unescMap[ent] || ent
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
;["hasAttribute", "getAttribute", "setAttribute", "removeAttribute"].forEach(function(name) {
|
|
261
|
+
Element[name + "NS"] = function(ns, a, b) {
|
|
262
|
+
return this[name].call(this, a, b)
|
|
263
|
+
}
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
function Attr(node, name) {
|
|
267
|
+
this.ownerElement = node
|
|
268
|
+
this.name = name.toLowerCase()
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
Attr.prototype = {
|
|
272
|
+
get value() { return this.ownerElement.getAttribute(this.name) },
|
|
273
|
+
set value(val) { this.ownerElement.setAttribute(this.name, val) },
|
|
274
|
+
toString: function(minify) {
|
|
275
|
+
var value = this.value.replace(escRe, escFn)
|
|
276
|
+
if (this.ownerElement.ownerDocument.contentType !== "application/xml") {
|
|
277
|
+
if (hasOwn.call(boolAttrs, this.name)) return this.name
|
|
278
|
+
if (minify) {
|
|
279
|
+
if (hasOwn.call(defaultAttrs, (this.ownerElement.tagName + " " + this.name + " " + value).toLowerCase())) return
|
|
280
|
+
if (!quotedAttrRe.test(value)) return this.name + "=" + this.value
|
|
281
|
+
if (value.split("\"").length > value.split("'").length) return this.name + "='" + value.replace(/'/g, "'") + "'"
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return this.name + "=\"" + value.replace(/"/g, """) + "\""
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function StyleMap(style) {
|
|
289
|
+
for (var m, re = /(?:^|;)\s*([-a-z]+)\s*:((?:("|')(?:\\.|(?!\3)[^\\])*?\3|[^"';])+)(?=;|$)/ig; (m = re.exec(style)); ) {
|
|
290
|
+
this[m[1] === "float" ? "cssFloat" : camelCase(m[1])] = m[2].trim()
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
StyleMap.prototype.valueOf = function() {
|
|
295
|
+
return Object.keys(this).map(function(key) {
|
|
296
|
+
return (key === "cssFloat" ? "float:" : hyphenCase(key) + ":") + this[key]
|
|
297
|
+
}, this).join(";")
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function DocumentFragment() {
|
|
301
|
+
this.childNodes = []
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
extendNode(DocumentFragment, Element, {
|
|
305
|
+
nodeType: 11,
|
|
306
|
+
nodeName: "#document-fragment"
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
function HTMLElement(tag) {
|
|
310
|
+
var el = this
|
|
311
|
+
el.nodeName = el.tagName = tag.toUpperCase()
|
|
312
|
+
el.localName = tag.toLowerCase()
|
|
313
|
+
el.childNodes = []
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
extendNode(HTMLElement, Element, {
|
|
317
|
+
nodeType: 1,
|
|
318
|
+
get attributes() {
|
|
319
|
+
var key
|
|
320
|
+
, attrs = []
|
|
321
|
+
, el = this
|
|
322
|
+
for (key in el) if (key === escAttr(key) && el.hasAttribute(key))
|
|
323
|
+
attrs.push(new Attr(el, escAttr(key)))
|
|
324
|
+
return attrs
|
|
325
|
+
},
|
|
326
|
+
matches: function(sel) {
|
|
327
|
+
return selector.matches(this, sel)
|
|
328
|
+
},
|
|
329
|
+
closest: function(sel) {
|
|
330
|
+
return selector.closest(this, sel)
|
|
331
|
+
},
|
|
332
|
+
namespaceURI: "http://www.w3.org/1999/xhtml",
|
|
333
|
+
localName: null,
|
|
334
|
+
tagName: null,
|
|
335
|
+
styleMap: null,
|
|
336
|
+
toString: function(minify) {
|
|
337
|
+
var attrs = (minify ? this.attributes.map(toMinString).filter(Boolean) : this.attributes).join(" ")
|
|
338
|
+
return "<" + this.localName + (attrs ? " " + attrs + (attrs.slice(-1) === "/" ? " >" : ">") : ">") +
|
|
339
|
+
(voidElements[this.tagName] ? "" : Node.toString.call(this, minify) + "</" + this.localName + ">")
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
function ElementNS(namespace, tag) {
|
|
344
|
+
var el = this
|
|
345
|
+
el.namespaceURI = namespace
|
|
346
|
+
el.nodeName = el.tagName = el.localName = tag
|
|
347
|
+
el.childNodes = []
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
ElementNS.prototype = HTMLElement.prototype
|
|
351
|
+
|
|
352
|
+
function Text(data) {
|
|
353
|
+
this.data = data
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
extendNode(Text, {
|
|
357
|
+
nodeType: 3,
|
|
358
|
+
nodeName: "#text",
|
|
359
|
+
toString: function(minify) {
|
|
360
|
+
return (minify ? ("" + this.data).trim() : "" + this.data).replace(escRe, escFn)
|
|
361
|
+
}
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
function Comment(data) {
|
|
365
|
+
this.data = data
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
extendNode(Comment, {
|
|
369
|
+
nodeType: 8,
|
|
370
|
+
nodeName: "#comment",
|
|
371
|
+
toString: function(minify) {
|
|
372
|
+
return minify ? "" : "<!--" + this.data + "-->"
|
|
373
|
+
}
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
function DocumentType(data) {
|
|
377
|
+
this.data = data
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
extendNode(DocumentType, {
|
|
381
|
+
nodeType: 10,
|
|
382
|
+
toString: function() {
|
|
383
|
+
return "<" + this.data + ">"
|
|
384
|
+
// var node = document.doctype
|
|
385
|
+
// return "<!DOCTYPE " + node.name +
|
|
386
|
+
// (node.publicId ? ' PUBLIC "' + node.publicId + '"' : '') +
|
|
387
|
+
// (!node.publicId && node.systemId ? ' SYSTEM' : '') +
|
|
388
|
+
// (node.systemId ? ' "' + node.systemId + '"' : '') + '>'
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
function Document() {
|
|
393
|
+
this.childNodes = []
|
|
394
|
+
this
|
|
395
|
+
.appendChild(this.createElement("html"))
|
|
396
|
+
.appendChild(this.body = this.createElement("body"))
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
extendNode(Document, Element, {
|
|
400
|
+
nodeType: 9,
|
|
401
|
+
nodeName: "#document",
|
|
402
|
+
contentType: "text/html",
|
|
403
|
+
createElement: own(HTMLElement),
|
|
404
|
+
createElementNS: own(ElementNS),
|
|
405
|
+
createTextNode: own(Text),
|
|
406
|
+
createComment: own(Comment),
|
|
407
|
+
createDocumentType: own(DocumentType), //Should be document.implementation.createDocumentType(name, publicId, systemId)
|
|
408
|
+
createDocumentFragment: own(DocumentFragment)
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
function DOMParser() {}
|
|
412
|
+
function XMLSerializer() {}
|
|
413
|
+
|
|
414
|
+
DOMParser.prototype.parseFromString = function(str, mime) {
|
|
415
|
+
var doc = new Document()
|
|
416
|
+
doc.contentType = mime || "text/html"
|
|
417
|
+
doc.documentElement.outerHTML = str
|
|
418
|
+
return doc
|
|
419
|
+
}
|
|
420
|
+
XMLSerializer.prototype.serializeToString = function(doc) {
|
|
421
|
+
return doc.toString()
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
function own(Class) {
|
|
426
|
+
return function($1, $2) {
|
|
427
|
+
var node = new Class($1, $2)
|
|
428
|
+
node.ownerDocument = this
|
|
429
|
+
return node
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function extendNode(obj, extras) {
|
|
434
|
+
obj.prototype = Object.create(Node)
|
|
435
|
+
for (var descriptor, key, i = 1; (extras = arguments[i++]); ) {
|
|
436
|
+
for (key in extras) {
|
|
437
|
+
descriptor = Object.getOwnPropertyDescriptor(extras, key)
|
|
438
|
+
Object.defineProperty(obj.prototype, key, descriptor)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
obj.prototype.constructor = obj
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function replaceChildren() {
|
|
445
|
+
for (var arr = this.childNodes, i = 0, l = arr && arr.length; i < l; ) arr[i++].parentNode = null
|
|
446
|
+
for (i = arr.length = 0, l = arguments.length; i < l; ) this.insertBefore(arguments[i++])
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function getElement(childs, index, step, type) {
|
|
450
|
+
if (childs && index > -1) for (; childs[index]; index += step) {
|
|
451
|
+
if (childs[index].nodeType === type) return childs[index]
|
|
452
|
+
}
|
|
453
|
+
return null
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function getSibling(node, step, type) {
|
|
457
|
+
var silbings = node.parentNode && node.parentNode.childNodes
|
|
458
|
+
, index = silbings ? silbings.indexOf(node) : -1
|
|
459
|
+
return type > 0 ? getElement(silbings, index + step, step, type) : silbings[index + step] || null
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function escAttr(name) {
|
|
463
|
+
name = name.toLowerCase()
|
|
464
|
+
return name === "constructor" || name === "attributes" ? name.toUpperCase() : name
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function camelCase(str) {
|
|
468
|
+
return str.replace(/-([a-z])/g, function(_, a) { return a.toUpperCase() })
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function hyphenCase(str) {
|
|
472
|
+
return str.replace(/[A-Z]/g, "-$&").toLowerCase()
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function toMinString(item) {
|
|
476
|
+
return item.toString(true)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
module.exports = {
|
|
480
|
+
document: new Document(),
|
|
481
|
+
DOMParser: DOMParser,
|
|
482
|
+
XMLSerializer: XMLSerializer,
|
|
483
|
+
StyleMap: StyleMap,
|
|
484
|
+
Node: Node,
|
|
485
|
+
HTMLElement: HTMLElement,
|
|
486
|
+
DocumentFragment: DocumentFragment,
|
|
487
|
+
Document: Document
|
|
488
|
+
}
|
|
489
|
+
|
package/interactive.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
var DOM = module.exports = require(".")
|
|
4
|
+
, HTMLElementExtra = {
|
|
5
|
+
focus: function() {
|
|
6
|
+
this.ownerDocument.activeElement = this
|
|
7
|
+
},
|
|
8
|
+
blur: function() {
|
|
9
|
+
this.ownerDocument.activeElement = null
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
Object.assign(DOM.HTMLElement.prototype, HTMLElementExtra)
|
|
14
|
+
|
|
15
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@litejs/dom",
|
|
3
|
+
"version": "23.2.2",
|
|
4
|
+
"description": "A small DOM library for server-side testing, rendering, and handling of HTML files",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Lauri Rooden <lauri@rooden.ee>",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"document",
|
|
9
|
+
"dom",
|
|
10
|
+
"DOMParser",
|
|
11
|
+
"XMLHttpRequest",
|
|
12
|
+
"XMLSerializer",
|
|
13
|
+
"litejs"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
"*.js"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "lj test --coverage --ignore=xmlhttprequest.js --brief test/index.js && jshint *.js"
|
|
20
|
+
},
|
|
21
|
+
"repository": "github:litejs/dom",
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@litejs/cli": "23.2.6",
|
|
24
|
+
"jshint": "2.13.6"
|
|
25
|
+
},
|
|
26
|
+
"litejs": {
|
|
27
|
+
"build": false,
|
|
28
|
+
"global": false
|
|
29
|
+
},
|
|
30
|
+
"jshintConfig": {
|
|
31
|
+
"esversion": 5,
|
|
32
|
+
"asi": true,
|
|
33
|
+
"evil": true,
|
|
34
|
+
"laxcomma": true,
|
|
35
|
+
"maxdepth": 6,
|
|
36
|
+
"node": true,
|
|
37
|
+
"nonbsp": true,
|
|
38
|
+
"undef": true,
|
|
39
|
+
"unused": true,
|
|
40
|
+
"shadow": "outer",
|
|
41
|
+
"quotmark": "double"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/selector.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @version 20.2.0
|
|
6
|
+
* @author Lauri Rooden <lauri@rooden.ee>
|
|
7
|
+
* @license MIT License
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
!function(exports) {
|
|
13
|
+
var selectorCache = {
|
|
14
|
+
"": function() {}
|
|
15
|
+
}
|
|
16
|
+
, selectorRe = /([.#:[])([-\w]+)(?:\(((?:[^()]|\([^)]+\))+?)\)|([~^$*|]?)=(("|')(?:\\.|[^\\])*?\6|[-\w]+))?]?/g
|
|
17
|
+
, selectorLastRe = /([\s>+~]*)(?:("|')(?:\\.|[^\\])*?\2|\((?:[^()]|\([^()]+\))+?\)|~=|[^'"()\s>+~])+$/
|
|
18
|
+
, selectorSplitRe = /\s*,\s*(?=(?:[^'"()]|"(?:\\.|[^\\"])*?"|'(?:\\.|[^\\'])*?'|\((?:[^()]|\([^()]+\))+?\))+$)/
|
|
19
|
+
, selectorMap = {
|
|
20
|
+
"contains": "_.textContent.indexOf(v)>-1",
|
|
21
|
+
"empty": "!_.lastChild",
|
|
22
|
+
"enabled": "!m(_,':disabled')",
|
|
23
|
+
"first-child": "(a=_.parentNode)&&a.firstChild==_",
|
|
24
|
+
"first-of-type": "!p(_,_.tagName)",
|
|
25
|
+
"is": "m(_,v)",
|
|
26
|
+
"lang": "m(c(_,'[lang]'),'[lang|='+v+']')",
|
|
27
|
+
"last-child": "(a=_.parentNode)&&a.lastChild==_",
|
|
28
|
+
"last-of-type": "!n(_,_.tagName)",
|
|
29
|
+
"link": "m(_,'a[href]')",
|
|
30
|
+
"not": "!m(_,v)",
|
|
31
|
+
"nth-child": "(a=2,'odd'==v?b=1:'even'==v?b=0:a=1 in(v=v.split('n'))?(b=v[1],v[0]):(b=v[0],0),v=_.parentNode.childNodes,v=1+v.indexOf(_),0==a?v==b:('-'==a||0==(v-b)%a)&&(0<a||v<=b))",
|
|
32
|
+
"only-child": "(a=_.parentNode)&&a.firstChild==a.lastChild",
|
|
33
|
+
"only-of-type": "!p(_,_.tagName)&&!n(_,_.tagName)",
|
|
34
|
+
"optional": "!m(_,':required')",
|
|
35
|
+
"root": "(a=_.parentNode)&&!a.tagName",
|
|
36
|
+
".": "~_.className.split(/\\s+/).indexOf(a)",
|
|
37
|
+
"#": "_.id==a",
|
|
38
|
+
"^": "!a.indexOf(v)",
|
|
39
|
+
"|": "a.split('-')[0]==v",
|
|
40
|
+
"$": "a.slice(-v.length)==v",
|
|
41
|
+
"~": "~a.split(/\\s+/).indexOf(v)",
|
|
42
|
+
"*": "~a.indexOf(v)",
|
|
43
|
+
">>": "m(_.parentNode,v)",
|
|
44
|
+
"++": "m(_.previousSibling,v)",
|
|
45
|
+
"~~": "p(_,v)",
|
|
46
|
+
"": "c(_.parentNode,v)"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
selectorMap["nth-last-child"] = selectorMap["nth-child"].replace("1+", "v.length-")
|
|
50
|
+
|
|
51
|
+
function selectorFn(str) {
|
|
52
|
+
// jshint evil:true, unused:true, eqnull:true
|
|
53
|
+
if (str != null && typeof str !== "string") throw Error("Invalid selector")
|
|
54
|
+
return selectorCache[str || ""] ||
|
|
55
|
+
(selectorCache[str] = Function("m,c,n,p", "return function(_,v,a,b){return " +
|
|
56
|
+
str.split(selectorSplitRe).map(function(sel) {
|
|
57
|
+
var relation, from
|
|
58
|
+
, rules = ["_&&_.nodeType==1"]
|
|
59
|
+
, parentSel = sel.replace(selectorLastRe, function(_, _rel, a, start) {
|
|
60
|
+
from = start + _rel.length
|
|
61
|
+
relation = _rel.trim()
|
|
62
|
+
return ""
|
|
63
|
+
})
|
|
64
|
+
, tag = sel.slice(from).replace(selectorRe, function(_, op, key, subSel, fn, val, quotation) {
|
|
65
|
+
rules.push(
|
|
66
|
+
"((v='" +
|
|
67
|
+
(subSel || (quotation ? val.slice(1, -1) : val) || "").replace(/[\\']/g, "\\$&") +
|
|
68
|
+
"'),(a='" + key + "'),1)"
|
|
69
|
+
,
|
|
70
|
+
selectorMap[op == ":" ? key : op] ||
|
|
71
|
+
"(a=_.getAttribute(a))" +
|
|
72
|
+
(fn ? "&&" + selectorMap[fn] : val ? "==v" : "!==null")
|
|
73
|
+
)
|
|
74
|
+
return ""
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
if (tag && tag != "*") rules[0] += "&&_.tagName=='" + tag.toUpperCase() + "'"
|
|
78
|
+
if (parentSel) rules.push("(v='" + parentSel + "')", selectorMap[relation + relation])
|
|
79
|
+
return rules.join("&&")
|
|
80
|
+
}).join("||") + "}"
|
|
81
|
+
)(matches, closest, next, prev))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
function walk(next, first, el, sel, nextFn) {
|
|
86
|
+
var out = []
|
|
87
|
+
for (sel = selectorFn(sel); el; el = el[next] || nextFn && nextFn(el)) if (sel(el)) {
|
|
88
|
+
if (first) return el
|
|
89
|
+
out.push(el)
|
|
90
|
+
}
|
|
91
|
+
return first ? null : out
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function find(node, sel, first) {
|
|
95
|
+
return walk("firstChild", first, node.firstChild, sel, function(el) {
|
|
96
|
+
var next = el.nextSibling
|
|
97
|
+
while (!next && ((el = el.parentNode) !== node)) next = el.nextSibling
|
|
98
|
+
return next
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function matches(el, sel) {
|
|
103
|
+
return !!selectorFn(sel)(el)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function closest(el, sel) {
|
|
107
|
+
return walk("parentNode", 1, el, sel)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function next(el, sel) {
|
|
111
|
+
return walk("nextSibling", 1, el.nextSibling, sel)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function prev(el, sel) {
|
|
115
|
+
return walk("previousSibling", 1, el.previousSibling, sel)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
exports.find = find
|
|
120
|
+
exports.fn = selectorFn
|
|
121
|
+
exports.matches = matches
|
|
122
|
+
exports.closest = closest
|
|
123
|
+
exports.next = next
|
|
124
|
+
exports.prev = prev
|
|
125
|
+
exports.selectorMap = selectorMap
|
|
126
|
+
}(this) // jshint ignore:line
|
|
127
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* global unescape */
|
|
2
|
+
|
|
3
|
+
var DOM = require(".")
|
|
4
|
+
, parser = new DOM.DOMParser()
|
|
5
|
+
, dataUrlRe = /^data:([^;,]*?)(;[^,]+?|),(.*)$/
|
|
6
|
+
|
|
7
|
+
function XMLHttpRequest() {}
|
|
8
|
+
|
|
9
|
+
function setState(xhr, state) {
|
|
10
|
+
if (xhr.readyState !== state) {
|
|
11
|
+
xhr.readyState = state
|
|
12
|
+
if (xhr.onreadystatechange) xhr.onreadystatechange()
|
|
13
|
+
if (state === xhr.DONE && xhr.onload) xhr.onload()
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
XMLHttpRequest.prototype = {
|
|
18
|
+
UNSENT: 0,
|
|
19
|
+
OPENED: 1,
|
|
20
|
+
HEADERS_RECEIVED: 2,
|
|
21
|
+
LOADING: 3,
|
|
22
|
+
DONE: 4,
|
|
23
|
+
readyState: 0,
|
|
24
|
+
status: 0,
|
|
25
|
+
statusText: "",
|
|
26
|
+
response: "",
|
|
27
|
+
responseText: "",
|
|
28
|
+
responseType: "",
|
|
29
|
+
responseURL: "",
|
|
30
|
+
get responseXML() {
|
|
31
|
+
var xhr = this
|
|
32
|
+
, mime = (xhr.getResponseHeader("Content-Type") || "").split(";")[0]
|
|
33
|
+
return (
|
|
34
|
+
xhr.readyState !== xhr.DONE ? null :
|
|
35
|
+
// XMLHttpRequest spec originally supported only XML
|
|
36
|
+
mime !== "application/xml" && xhr.responseType !== "document" ? null :
|
|
37
|
+
parser.parseFromString(xhr.responseText, mime)
|
|
38
|
+
)
|
|
39
|
+
},
|
|
40
|
+
getAllResponseHeaders: function () {
|
|
41
|
+
var xhr = this
|
|
42
|
+
return xhr.readyState >= xhr.HEADERS_RECEIVED && Object.keys(xhr._headers).map(function(name) {
|
|
43
|
+
return name + ": " + xhr._headers[name] + "\r\n"
|
|
44
|
+
}).join("") || null
|
|
45
|
+
},
|
|
46
|
+
getResponseHeader: function (name) {
|
|
47
|
+
var xhr = this
|
|
48
|
+
return xhr.readyState >= xhr.HEADERS_RECEIVED && xhr._headers[name.toLowerCase()] || null
|
|
49
|
+
},
|
|
50
|
+
abort: function() {
|
|
51
|
+
throw Error("XMLHttpRequest abort/reuse not implemented")
|
|
52
|
+
},
|
|
53
|
+
open: function (method, url, async) {
|
|
54
|
+
var xhr = this
|
|
55
|
+
if (async === false) throw Error("XMLHttpRequest sync not implemented")
|
|
56
|
+
|
|
57
|
+
if (xhr.readyState > xhr.UNSENT) {
|
|
58
|
+
xhr.abort()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
xhr.method = method
|
|
62
|
+
xhr.responseURL = url
|
|
63
|
+
setState(xhr, xhr.OPENED)
|
|
64
|
+
},
|
|
65
|
+
send: function (data) {
|
|
66
|
+
var xhr = this
|
|
67
|
+
, url = xhr.responseURL
|
|
68
|
+
, proto = url.split(":", 1)[0]
|
|
69
|
+
, opts = {
|
|
70
|
+
method: xhr.method
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (proto === "http" || proto === "https") {
|
|
74
|
+
require(proto).request(url, opts, function(res) {
|
|
75
|
+
head(res.statusCode, res.statusMessage, res.headers)
|
|
76
|
+
res.on("data", body)
|
|
77
|
+
res.on("end", done)
|
|
78
|
+
}).end(data)
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
if (proto === "data") {
|
|
82
|
+
var match = dataUrlRe.exec(url)
|
|
83
|
+
if (!match) throw Error("Invalid URL: " + url)
|
|
84
|
+
setTimeout(function() {
|
|
85
|
+
head(200, "OK", { "content-type": match[1] || "text/plain" })
|
|
86
|
+
body(match[2] ? Buffer.from(match[3], "base64") : unescape(match[3]))
|
|
87
|
+
done()
|
|
88
|
+
}, 1)
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
throw Error("Unsuported protocol: " + proto)
|
|
93
|
+
|
|
94
|
+
function head(code, text, headers) {
|
|
95
|
+
xhr.status = code
|
|
96
|
+
xhr.statusText = text
|
|
97
|
+
xhr._headers = headers
|
|
98
|
+
setState(xhr, xhr.HEADERS_RECEIVED)
|
|
99
|
+
}
|
|
100
|
+
function body(chunk) {
|
|
101
|
+
xhr.responseText += chunk.toString("utf8")
|
|
102
|
+
setState(xhr, xhr.LOADING)
|
|
103
|
+
}
|
|
104
|
+
function done() {
|
|
105
|
+
setState(xhr, xhr.DONE)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
XMLHttpRequest: XMLHttpRequest
|
|
113
|
+
}
|
|
114
|
+
|