@litejs/dom 23.12.1 → 24.7.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.
Files changed (5) hide show
  1. package/README.md +15 -17
  2. package/css.js +32 -0
  3. package/dom.js +21 -32
  4. package/net.js +4 -2
  5. package/package.json +5 -5
package/README.md CHANGED
@@ -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
- A small DOM library for server-side testing, rendering, and handling of HTML files.
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
- el.innerHTML;
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
@@ -63,7 +61,7 @@ Follow [Coding Style Guide](https://github.com/litejs/litejs/wiki/Style-Guide),
63
61
  run tests `npm install; npm test`.
64
62
 
65
63
 
66
- > Copyright (c) 2014-2023 Lauri Rooden &lt;lauri@rooden.ee&gt;
64
+ > Copyright (c) 2014-2024 Lauri Rooden &lt;lauri@rooden.ee&gt;
67
65
  [MIT License](https://litejs.com/MIT-LICENSE.txt) |
68
66
  [GitHub repo](https://github.com/litejs/dom) |
69
67
  [npm package](https://npmjs.org/package/@litejs/dom) |
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,
@@ -83,6 +84,7 @@ var boolAttrs = {
83
84
  if ((re = rawTextElements[child.tagName])) {
84
85
  for (text = ""; (m = tagRe.exec(html)) && !re.test(m[0]); text += m[3] || m[0]);
85
86
  child.textContent = text.replace(unescRe, unescFn)
87
+ if (!m) break
86
88
  } else if (!voidElements[child.tagName] && !m[8]) tree = child
87
89
  } else {
88
90
  tree.appendChild(
@@ -110,7 +112,7 @@ var boolAttrs = {
110
112
  return this._style || (this._style = new CSSStyleDeclaration(this.getAttribute("style") || ""))
111
113
  },
112
114
  set style(value) {
113
- this.setAttribute("style", value)
115
+ this.style.cssText = value
114
116
  },
115
117
  contains: function (el) {
116
118
  for (; el; el = el.parentNode) if (el === this) return true
@@ -182,9 +184,9 @@ var boolAttrs = {
182
184
  return selector.find(this, sel)
183
185
  },
184
186
  toString: function(minify) {
185
- return rawTextElements[this.tagName] ? this.textContent : this.hasChildNodes() ? this.childNodes.reduce(function(memo, node) {
187
+ return rawTextElements[this.tagName] ? this.textContent : this.childNodes.reduce(function(memo, node) {
186
188
  return memo + node.toString(minify)
187
- }, "") : ""
189
+ }, "")
188
190
  }
189
191
  }
190
192
  , Element = {
@@ -209,7 +211,7 @@ var boolAttrs = {
209
211
  return attr ? attr.value : null
210
212
  },
211
213
  setAttribute: function(name, value) {
212
- this.attributes.setNamedItem(name, value)
214
+ this.attributes.setNamedItem(new Attr(this, name, value))
213
215
  },
214
216
  removeAttribute: function(name) {
215
217
  this.attributes.removeNamedItem(name)
@@ -286,8 +288,7 @@ NamedNodeMap.prototype = {
286
288
  , attr = this[loName] || null
287
289
  if (loName === "style" && this.ownerElement._style) {
288
290
  if (attr === null) attr = this[loName] = new Attr(this.ownerElement, name, "")
289
- attr.value = this.ownerElement._style.valueOf()
290
- delete this.ownerElement._style
291
+ attr.value = this.ownerElement._style.cssText
291
292
  }
292
293
  return attr
293
294
  },
@@ -298,11 +299,11 @@ NamedNodeMap.prototype = {
298
299
  if (attr !== null) delete this[loName]
299
300
  return attr
300
301
  },
301
- setNamedItem: function(name, value) {
302
- this.removeNamedItem(name)
303
- var loName = name.toLowerCase()
304
- if (loName === "style") value = new CSSStyleDeclaration(value).valueOf()
305
- this[loName] = new Attr(this.ownerElement, name, value)
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
306
307
  },
307
308
  toString: function(minify) {
308
309
  var map = this
@@ -315,6 +316,7 @@ NamedNodeMap.prototype = {
315
316
  if (!isXml) {
316
317
  if (hasOwn.call(boolAttrs, loName)) return name
317
318
  if (minify) {
319
+ value = loName.slice(0, 2) === "on" ? value.replace(/^[\s\uFEFF\xA0;]+|[\s\uFEFF\xA0;]+$/g, "") : value.replace(/\s+/g, " ").trim()
318
320
  if (hasOwn.call(defaultAttrs, (tagName + " " + name + " " + value).toLowerCase())) return
319
321
  if (!quotedAttrRe.test(value)) return name + "=" + value
320
322
  if (value.split("\"").length > value.split("'").length) return name + "='" + value.replace(/'/g, "&#39;") + "'"
@@ -325,18 +327,6 @@ NamedNodeMap.prototype = {
325
327
  }
326
328
  }
327
329
 
328
- function CSSStyleDeclaration(style) {
329
- for (var m, re = /(?:^|;)\s*([-a-z]+)\s*:((?:("|')(?:\\.|(?!\3)[^\\])*?\3|[^"';])+)(?=;|$)/ig; (m = re.exec(style)); ) {
330
- this[m[1] === "float" ? "cssFloat" : camelCase(m[1])] = m[2].trim()
331
- }
332
- }
333
-
334
- CSSStyleDeclaration.prototype.valueOf = function() {
335
- return Object.keys(this).map(function(key) {
336
- return (key === "cssFloat" ? "float:" : hyphenCase(key) + ":") + this[key]
337
- }, this).join(";")
338
- }
339
-
340
330
  function DocumentFragment() {
341
331
  this.childNodes = []
342
332
  }
@@ -430,6 +420,14 @@ function Document() {
430
420
  }
431
421
 
432
422
  extendNode(Document, Element, {
423
+ get title() {
424
+ var el = selector.find(this, "title", 1)
425
+ return el && el.textContent || ""
426
+ },
427
+ set title(text) {
428
+ var el = selector.find(this, "title", 1) || this.appendChild(this.createElement("title"))
429
+ el.textContent = text
430
+ },
433
431
  nodeType: 9,
434
432
  nodeName: "#document",
435
433
  contentType: "text/html",
@@ -492,17 +490,8 @@ function getSibling(node, step, type) {
492
490
  return type > 0 ? getElement(silbings, index + step, step, type) : silbings && silbings[index + step] || null
493
491
  }
494
492
 
495
- function camelCase(str) {
496
- return str.replace(/-([a-z])/g, function(_, a) { return a.toUpperCase() })
497
- }
498
-
499
- function hyphenCase(str) {
500
- return str.replace(/[A-Z]/g, "-$&").toLowerCase()
501
- }
502
-
503
493
  exports.document = new Document()
504
494
  exports.entities = entities
505
- exports.CSSStyleDeclaration = CSSStyleDeclaration
506
495
  exports.DOMParser = DOMParser
507
496
  exports.Document = Document
508
497
  exports.DocumentFragment = DocumentFragment
package/net.js CHANGED
@@ -87,11 +87,13 @@ XMLHttpRequest.prototype = {
87
87
  result[entrie[0]] = entrie[1]
88
88
  return result
89
89
  }, {})
90
- require(url.protocol.slice(0, -1)).request(url, function(res) {
90
+ var req = require(url.protocol.slice(0, -1)).request(url, function(res) {
91
91
  head(res.statusCode, res.statusMessage, res.headers)
92
92
  res.on("data", fillBody)
93
93
  res.on("end", done)
94
- }).end(data)
94
+ })
95
+ if (xhr.onerror) req.on("error", xhr.onerror)
96
+ req.end(data)
95
97
  return
96
98
  }
97
99
  if (url.protocol === "data:") {
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@litejs/dom",
3
- "version": "23.12.1",
3
+ "version": "24.7.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>",
7
7
  "keywords": [
8
8
  "document",
9
9
  "dom",
10
+ "html",
10
11
  "DOMParser",
11
12
  "XMLHttpRequest",
12
13
  "XMLSerializer",
@@ -17,12 +18,11 @@
17
18
  "*.js"
18
19
  ],
19
20
  "scripts": {
20
- "test": "lj test --coverage --brief test/index.js && node --import=@litejs/cli/test.js test/run.mjs && jshint --config .github/jshint.json *.js",
21
- "test-fast": "lj test --brief test/index.js"
21
+ "lint": "npx jshint -c .github/jshint.json *.js",
22
+ "test": "lj t test/*.js"
22
23
  },
23
24
  "repository": "github:litejs/dom",
24
25
  "devDependencies": {
25
- "@litejs/cli": "23.11.1",
26
- "jshint": "2.13.6"
26
+ "@litejs/cli": "23.11.1"
27
27
  }
28
28
  }