@litejs/dom 24.6.0 → 24.8.0
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 +15 -17
- package/css.js +32 -0
- package/dom.js +9 -30
- package/net.js +26 -12
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
[1]: https://badgen.net/coveralls/c/github/litejs/dom
|
|
3
3
|
[2]: https://coveralls.io/r/litejs/dom
|
|
4
|
-
[3]: https://
|
|
4
|
+
[3]: https://packagephobia.now.sh/badge?p=@litejs/dom
|
|
5
5
|
[4]: https://packagephobia.now.sh/result?p=@litejs/dom
|
|
6
6
|
[5]: https://badgen.net/badge/icon/Buy%20Me%20A%20Tea/orange?icon=kofi&label
|
|
7
7
|
[6]: https://www.buymeacoffee.com/lauriro
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
LiteJS DOM – [![Coverage][1]][2] [![Size][3]][4] [![Buy Me A Tea][5]][6]
|
|
11
11
|
==========
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Dependency-free DOM library for handling HTML files on server-side.
|
|
14
14
|
[DOM spec](https://dom.spec.whatwg.org/) |
|
|
15
15
|
[Selectors Level 3](http://www.w3.org/TR/selectors/)
|
|
16
16
|
|
|
@@ -20,18 +20,6 @@ Examples
|
|
|
20
20
|
|
|
21
21
|
```javascript
|
|
22
22
|
const { document, DOMParser, XMLSerializer } = require("@litejs/dom");
|
|
23
|
-
const { XMLHttpRequest } = require("@litejs/dom/net.js");
|
|
24
|
-
|
|
25
|
-
// Use XMLHttpRequest in server side
|
|
26
|
-
var xhr = new XMLHttpRequest()
|
|
27
|
-
xhr.open("GET", "https://litejs.com")
|
|
28
|
-
xhr.responseType = "document"
|
|
29
|
-
xhr.onload = function() {
|
|
30
|
-
var doc = xhr.responseXML
|
|
31
|
-
// Work with DOM in familiar way
|
|
32
|
-
console.log(doc.querySelector("title").textContent)
|
|
33
|
-
}
|
|
34
|
-
xhr.send()
|
|
35
23
|
|
|
36
24
|
// Build DOM manually
|
|
37
25
|
const el = document.createElement("h1");
|
|
@@ -40,11 +28,9 @@ el.className = "large";
|
|
|
40
28
|
|
|
41
29
|
const fragment = document.createDocumentFragment();
|
|
42
30
|
fragment.appendChild(document.createTextNode("hello"));
|
|
43
|
-
fragment.appendChild(document.createTextNode(" world"));
|
|
44
31
|
el.appendChild(fragment);
|
|
45
32
|
|
|
46
|
-
|
|
47
|
-
// hello world
|
|
33
|
+
// Replace the DOM tree with parsed HTML
|
|
48
34
|
el.innerHTML = "<b>hello world</b>";
|
|
49
35
|
el.toString();
|
|
50
36
|
// <h1 id="123" class="large"><b>hello world</b></h1>
|
|
@@ -55,6 +41,18 @@ el.toString(true);
|
|
|
55
41
|
|
|
56
42
|
el.querySelectorAll("b");
|
|
57
43
|
// [ "<b>hello world</b>" ]
|
|
44
|
+
|
|
45
|
+
// Use XMLHttpRequest in server side
|
|
46
|
+
const { XMLHttpRequest } = require("@litejs/dom/net.js");
|
|
47
|
+
const xhr = new XMLHttpRequest();
|
|
48
|
+
xhr.open("GET", "https://litejs.com");
|
|
49
|
+
xhr.responseType = "document";
|
|
50
|
+
xhr.onload = function() {
|
|
51
|
+
const document = xhr.responseXML;
|
|
52
|
+
// Work with DOM in familiar way
|
|
53
|
+
console.log(document.querySelector("title").textContent);
|
|
54
|
+
}
|
|
55
|
+
xhr.send();
|
|
58
56
|
```
|
|
59
57
|
|
|
60
58
|
## Contributing
|
package/css.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
/*! litejs.com/MIT-LICENSE.txt */
|
|
3
|
+
|
|
4
|
+
exports.CSSStyleDeclaration = CSSStyleDeclaration
|
|
5
|
+
|
|
6
|
+
// CSSStyleDeclaration is a single CSS declaration block,
|
|
7
|
+
// accessible via HTMLElement.style for inline styles, document.styleSheets[0].cssRules[0].style, and getComputedStyle()
|
|
8
|
+
function CSSStyleDeclaration(style) {
|
|
9
|
+
this.cssText = style
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
CSSStyleDeclaration.prototype = {
|
|
13
|
+
get cssText() {
|
|
14
|
+
return Object.keys(this).map(function(key) {
|
|
15
|
+
return (key === "cssFloat" ? "float:" : hyphenCase(key) + ":") + this[key]
|
|
16
|
+
}, this).join(";")
|
|
17
|
+
},
|
|
18
|
+
set cssText(style) {
|
|
19
|
+
for (var m, re = /(?:^|;)\s*([-a-z]+)\s*:((?:("|')(?:\\.|(?!\3)[^\\])*?\3|[^"';])+)(?=;|$)/ig; (m = re.exec(style)); ) {
|
|
20
|
+
this[m[1] === "float" ? "cssFloat" : camelCase(m[1])] = m[2].trim()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function camelCase(str) {
|
|
26
|
+
return str.replace(/-([a-z])/g, function(_, a) { return a.toUpperCase() })
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function hyphenCase(str) {
|
|
30
|
+
return str.replace(/[A-Z]/g, "-$&").toLowerCase()
|
|
31
|
+
}
|
|
32
|
+
|
package/dom.js
CHANGED
|
@@ -16,6 +16,7 @@ var boolAttrs = {
|
|
|
16
16
|
, rawTextElements = { SCRIPT: /<(?=\/script)/i, STYLE: /<(?=\/style)/i }
|
|
17
17
|
, rawTextEscape = { SCRIPT: /<(?=\/script|!--)/ig, STYLE: /<(?=\/style|!--)/ig }
|
|
18
18
|
, hasOwn = voidElements.hasOwnProperty
|
|
19
|
+
, CSSStyleDeclaration = require("./css.js").CSSStyleDeclaration
|
|
19
20
|
, selector = require("./selector.js")
|
|
20
21
|
, Node = {
|
|
21
22
|
ELEMENT_NODE: 1,
|
|
@@ -111,7 +112,7 @@ var boolAttrs = {
|
|
|
111
112
|
return this._style || (this._style = new CSSStyleDeclaration(this.getAttribute("style") || ""))
|
|
112
113
|
},
|
|
113
114
|
set style(value) {
|
|
114
|
-
this.
|
|
115
|
+
this.style.cssText = value
|
|
115
116
|
},
|
|
116
117
|
contains: function (el) {
|
|
117
118
|
for (; el; el = el.parentNode) if (el === this) return true
|
|
@@ -210,7 +211,7 @@ var boolAttrs = {
|
|
|
210
211
|
return attr ? attr.value : null
|
|
211
212
|
},
|
|
212
213
|
setAttribute: function(name, value) {
|
|
213
|
-
this.attributes.setNamedItem(name, value)
|
|
214
|
+
this.attributes.setNamedItem(new Attr(this, name, value))
|
|
214
215
|
},
|
|
215
216
|
removeAttribute: function(name) {
|
|
216
217
|
this.attributes.removeNamedItem(name)
|
|
@@ -287,8 +288,7 @@ NamedNodeMap.prototype = {
|
|
|
287
288
|
, attr = this[loName] || null
|
|
288
289
|
if (loName === "style" && this.ownerElement._style) {
|
|
289
290
|
if (attr === null) attr = this[loName] = new Attr(this.ownerElement, name, "")
|
|
290
|
-
attr.value = this.ownerElement._style.
|
|
291
|
-
delete this.ownerElement._style
|
|
291
|
+
attr.value = this.ownerElement._style.cssText
|
|
292
292
|
}
|
|
293
293
|
return attr
|
|
294
294
|
},
|
|
@@ -299,11 +299,11 @@ NamedNodeMap.prototype = {
|
|
|
299
299
|
if (attr !== null) delete this[loName]
|
|
300
300
|
return attr
|
|
301
301
|
},
|
|
302
|
-
setNamedItem: function(
|
|
303
|
-
this.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
302
|
+
setNamedItem: function(attr) {
|
|
303
|
+
var oldAttr = this.getNamedItem(attr.name)
|
|
304
|
+
if (attr.name === "style") attr.value = new CSSStyleDeclaration(attr.value).cssText
|
|
305
|
+
this[attr.name] = attr
|
|
306
|
+
return oldAttr
|
|
307
307
|
},
|
|
308
308
|
toString: function(minify) {
|
|
309
309
|
var map = this
|
|
@@ -327,18 +327,6 @@ NamedNodeMap.prototype = {
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
function CSSStyleDeclaration(style) {
|
|
331
|
-
for (var m, re = /(?:^|;)\s*([-a-z]+)\s*:((?:("|')(?:\\.|(?!\3)[^\\])*?\3|[^"';])+)(?=;|$)/ig; (m = re.exec(style)); ) {
|
|
332
|
-
this[m[1] === "float" ? "cssFloat" : camelCase(m[1])] = m[2].trim()
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
CSSStyleDeclaration.prototype.valueOf = function() {
|
|
337
|
-
return Object.keys(this).map(function(key) {
|
|
338
|
-
return (key === "cssFloat" ? "float:" : hyphenCase(key) + ":") + this[key]
|
|
339
|
-
}, this).join(";")
|
|
340
|
-
}
|
|
341
|
-
|
|
342
330
|
function DocumentFragment() {
|
|
343
331
|
this.childNodes = []
|
|
344
332
|
}
|
|
@@ -502,17 +490,8 @@ function getSibling(node, step, type) {
|
|
|
502
490
|
return type > 0 ? getElement(silbings, index + step, step, type) : silbings && silbings[index + step] || null
|
|
503
491
|
}
|
|
504
492
|
|
|
505
|
-
function camelCase(str) {
|
|
506
|
-
return str.replace(/-([a-z])/g, function(_, a) { return a.toUpperCase() })
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function hyphenCase(str) {
|
|
510
|
-
return str.replace(/[A-Z]/g, "-$&").toLowerCase()
|
|
511
|
-
}
|
|
512
|
-
|
|
513
493
|
exports.document = new Document()
|
|
514
494
|
exports.entities = entities
|
|
515
|
-
exports.CSSStyleDeclaration = CSSStyleDeclaration
|
|
516
495
|
exports.DOMParser = DOMParser
|
|
517
496
|
exports.Document = Document
|
|
518
497
|
exports.DocumentFragment = DocumentFragment
|
package/net.js
CHANGED
|
@@ -12,6 +12,7 @@ exports.XMLHttpRequest = XMLHttpRequest
|
|
|
12
12
|
exports.defaultHeaders = {
|
|
13
13
|
accept: ["Accept", "*/*"]
|
|
14
14
|
}
|
|
15
|
+
exports.protocolHandler = {}
|
|
15
16
|
|
|
16
17
|
function XMLHttpRequest() {
|
|
17
18
|
this._reqHeaders = Object.assign({}, exports.defaultHeaders)
|
|
@@ -64,9 +65,9 @@ XMLHttpRequest.prototype = {
|
|
|
64
65
|
abort: function() {
|
|
65
66
|
throw Error("XMLHttpRequest abort/reuse not implemented")
|
|
66
67
|
},
|
|
67
|
-
open: function (method, url,
|
|
68
|
+
open: function (method, url, isAsync) {
|
|
68
69
|
var xhr = this
|
|
69
|
-
|
|
70
|
+
xhr._sync = isAsync === false
|
|
70
71
|
|
|
71
72
|
if (xhr.readyState > xhr.UNSENT) {
|
|
72
73
|
xhr.abort()
|
|
@@ -79,33 +80,36 @@ XMLHttpRequest.prototype = {
|
|
|
79
80
|
send: function (data) {
|
|
80
81
|
var xhr = this
|
|
81
82
|
, url = new URL(xhr.responseURL, XMLHttpRequest.base)
|
|
83
|
+
, proto = url.protocol.slice(0, -1)
|
|
82
84
|
|
|
83
|
-
if (
|
|
85
|
+
if (proto === "http" || proto === "https") {
|
|
86
|
+
if (xhr._sync) throw Error("XMLHttpRequest sync not implemented")
|
|
84
87
|
url.method = xhr.method
|
|
85
88
|
url.headers = Object.keys(xhr._reqHeaders).reduce(function(result, key) {
|
|
86
89
|
var entrie = xhr._reqHeaders[key]
|
|
87
90
|
result[entrie[0]] = entrie[1]
|
|
88
91
|
return result
|
|
89
92
|
}, {})
|
|
90
|
-
require(
|
|
93
|
+
var req = require(proto).request(url, function(res) {
|
|
91
94
|
head(res.statusCode, res.statusMessage, res.headers)
|
|
92
95
|
res.on("data", fillBody)
|
|
93
96
|
res.on("end", done)
|
|
94
|
-
})
|
|
97
|
+
})
|
|
98
|
+
req.on("error", done)
|
|
99
|
+
req.end(data)
|
|
95
100
|
return
|
|
96
101
|
}
|
|
97
|
-
if (
|
|
102
|
+
if (proto === "data") {
|
|
98
103
|
var match = dataUrlRe.exec(url.pathname)
|
|
99
|
-
if (
|
|
100
|
-
process.nextTick(function() {
|
|
104
|
+
if (match) {
|
|
101
105
|
head(200, "OK", { "content-type": match[1] || "text/plain" })
|
|
102
106
|
fillBody(match[2] ? Buffer.from(match[3], "base64") : unescape(match[3]))
|
|
103
107
|
done()
|
|
104
|
-
})
|
|
108
|
+
} else done(Error("Invalid URL: " + url))
|
|
105
109
|
return
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
if (
|
|
112
|
+
if (proto === "file") {
|
|
109
113
|
require("fs").readFile(url, function(err, chunk) {
|
|
110
114
|
if (err) {
|
|
111
115
|
head(404, "Not Found", {})
|
|
@@ -118,6 +122,11 @@ XMLHttpRequest.prototype = {
|
|
|
118
122
|
return
|
|
119
123
|
}
|
|
120
124
|
|
|
125
|
+
if (exports.protocolHandler[proto]) {
|
|
126
|
+
exports.protocolHandler[proto](url, head, fillBody, done)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
121
130
|
throw Error("Unsuported protocol in: " + url)
|
|
122
131
|
|
|
123
132
|
function head(code, text, headers) {
|
|
@@ -130,8 +139,13 @@ XMLHttpRequest.prototype = {
|
|
|
130
139
|
xhr.responseText += chunk.toString("utf8")
|
|
131
140
|
setState(xhr, xhr.LOADING)
|
|
132
141
|
}
|
|
133
|
-
function done() {
|
|
134
|
-
|
|
142
|
+
function done(err) {
|
|
143
|
+
if (err) {
|
|
144
|
+
if (xhr.onerror) xhr.onerror(err)
|
|
145
|
+
else throw err
|
|
146
|
+
}
|
|
147
|
+
else if (xhr._sync) setState(xhr, xhr.DONE)
|
|
148
|
+
else process.nextTick(setState, xhr, xhr.DONE)
|
|
135
149
|
}
|
|
136
150
|
}
|
|
137
151
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litejs/dom",
|
|
3
|
-
"version": "24.
|
|
3
|
+
"version": "24.8.0",
|
|
4
4
|
"description": "A small DOM library for server-side testing, rendering, and handling of HTML files",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Lauri Rooden <lauri@rooden.ee>",
|
|
@@ -18,12 +18,11 @@
|
|
|
18
18
|
"*.js"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
|
-
"lint": "jshint
|
|
22
|
-
"test": "lj t test
|
|
21
|
+
"lint": "npx jshint -c .github/jshint.json *.js",
|
|
22
|
+
"test": "lj t test/*.js"
|
|
23
23
|
},
|
|
24
24
|
"repository": "github:litejs/dom",
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@litejs/cli": "23.11.1"
|
|
27
|
-
"jshint": "2.13.6"
|
|
26
|
+
"@litejs/cli": "23.11.1"
|
|
28
27
|
}
|
|
29
28
|
}
|