@litejs/dom 24.12.0 → 25.5.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 CHANGED
@@ -10,13 +10,13 @@
10
10
  LiteJS DOM – [![Coverage][1]][2] [![Size][3]][4] [![Buy Me A Tea][5]][6]
11
11
  ==========
12
12
 
13
- Dependency-free DOM library for handling HTML files on server-side.
13
+ Dependency-free DOM library for handling HTML, XML and CSS files on server-side.
14
14
 
15
15
 
16
16
  ```javascript
17
- import { document, DOMParser, XMLSerializer } from "@litejs/dom";
18
- import { XMLHttpRequest } from "@litejs/dom/net.js";
19
17
  // const { document } = require("@litejs/dom");
18
+ import { document, CSSStyleSheet, DOMParser, XMLSerializer } from "@litejs/dom";
19
+ import { XMLHttpRequest } from "@litejs/dom/net.js";
20
20
 
21
21
  // Build DOM manually
22
22
  const el = document.createElement("h1");
@@ -27,13 +27,13 @@ const fragment = document.createDocumentFragment();
27
27
  fragment.appendChild(document.createTextNode("hello"));
28
28
  el.appendChild(fragment);
29
29
 
30
- // Replace the DOM tree with parsed HTML
30
+ // Replace the DOM tree with HTML
31
31
  el.innerHTML = "<b>hello world</b>";
32
- el.toString();
32
+ console.log(el.toString());
33
33
  // <h1 id="123" class="large"><b>hello world</b></h1>
34
34
 
35
- // minify output
36
- el.toString(true);
35
+ // Minify HTML
36
+ console.log(el.toString(true));
37
37
  // <h1 id=123 class=large><b>hello world</b></h1>
38
38
 
39
39
  el.querySelectorAll("b");
@@ -49,6 +49,12 @@ xhr.onload = function() {
49
49
  console.log(doc.querySelector("title").textContent);
50
50
  }
51
51
  xhr.send();
52
+
53
+ // Minify CSS
54
+ const sheet = new CSSStyleSheet({ min: { color: true } })
55
+ sheet.replaceSync(".a { color: hsl(0 0% 100%) }")
56
+ console.log(sheet.toString())
57
+ // .a{color:#fff}
52
58
  ```
53
59
 
54
60
  ## Contributing
@@ -57,7 +63,7 @@ Follow [Coding Style Guide](https://github.com/litejs/litejs/wiki/Style-Guide),
57
63
  run tests `npm install; npm test`.
58
64
 
59
65
 
60
- > Copyright (c) 2014-2024 Lauri Rooden &lt;lauri@rooden.ee&gt;
66
+ > Copyright (c) 2014-2025 Lauri Rooden &lt;lauri@rooden.ee&gt;
61
67
  [MIT License](https://litejs.com/MIT-LICENSE.txt) |
62
68
  [GitHub repo](https://github.com/litejs/dom) |
63
69
  [npm package](https://npmjs.org/package/@litejs/dom) |
package/css.js CHANGED
@@ -1,74 +1,162 @@
1
1
 
2
2
  /*! litejs.com/MIT-LICENSE.txt */
3
3
 
4
+ "use strict"
5
+
6
+ exports.selectorSplit = selectorSplit
4
7
  exports.CSSStyleDeclaration = CSSStyleDeclaration
5
8
  exports.CSSStyleSheet = CSSStyleSheet
6
9
 
7
- var clearFn = (_, q, str) => q ? (q == "\"" && str.indexOf("'") == -1 ? "'" + str + "'" : _) :
8
- _.trim().replace(/[\t\n]/g, " ")
10
+ var fs = require("fs")
11
+ , path = require("path")
12
+ , read = (sheet, url, enc = "utf8") => fs.readFileSync(path.resolve(sheet.min.root || "", sheet.baseURI, url).split(/[+#]/)[0], enc)
13
+ , plugins = exports.plugins = {
14
+ "data-uri": function(sheet, v) {
15
+ var { DOMParser } = require("./dom.js")
16
+ return v.replace(urlRe, function(_, q1, q2, url) {
17
+ if (q1) return _
18
+ var frag = url.split("#").pop()
19
+ , ext = url.split(/[?#]/)[0].split(".").pop()
20
+ , enc = ext === "svg" ? "utf8" : "base64"
21
+ url = read(sheet, url, enc)
22
+ if (ext === "svg") {
23
+ url = new DOMParser().parseFromString(url, "application/xml")
24
+ if (frag && (frag = url.getElementById(frag))) {
25
+ frag.removeAttribute("id")
26
+ url.documentElement.childNodes = frag.tagName === "g" ? frag.childNodes : [ frag ]
27
+ }
28
+ url = url.toString(true).replace(/#/g, "%23")
29
+ enc = ""
30
+ ext += "+xml"
31
+ }
32
+ return "url('data:image/" + ext + ";" + enc + "," + url + "')"
33
+ })
34
+ }
35
+ }
36
+ , urlRe = /(["']).*?\1|url\((['"]?)(?!\/|data:|https?:)(.*?)\2\)/g
37
+ , clearFn = (_, q, str, c) =>
38
+ q ? (q = str.indexOf("'") == -1 ? "'" : "\"", q + str.replace(q === "'" ? /\\(")/g : /\\(')/g, "$1")) + q :
39
+ c ? "" :
40
+ _.replace(/[\t\n]+/g, " ")
9
41
  .replace(/ *([,;{}>~+\/]) */g, "$1")
10
- .replace(/^ +|;(?=})/g, "")
42
+ .replace(/;(?=})/g, "")
11
43
  .replace(/: +/g, ":")
12
- , clear = s => s.replace(/(["'])((?:\\\1|.)*?)\1|[^"']+/g, clearFn).replace(/url\(("|')([^'"()\s]+)\1\)/g, "url($2)")
44
+ .replace(/([ :,])0\.([0-9])/g, "$1.$2")
45
+ , clear = s => s
46
+ .replace(/("|')((?:\\\1|[^\1])*?)\1|\s*(\/)\*(?:[^*]|\*(?!\/))*\*\/\s*|(?:[^"'\/]|\/(?!\*))+/g, clearFn)
47
+ .replace(/(["']).*?\1|url\(("|')([^'"()\s]+)\2\)/g, (m,q1,q2,u) => q1 ? m : "url(" + u + ")")
48
+ , toRgb = {
49
+ rgb(r, g, b) {
50
+ var f = n => ((n | 0) + 256).toString(16).slice(1)
51
+ return f(r) + f(g) + f(b)
52
+ },
53
+ hsl(h, s, l) {
54
+ l /= 100
55
+ s /= 100 / (l < 0.5 ? l : 1 - l)
56
+ function f(n) {
57
+ n = (n + h / 30) % 12
58
+ return (0 | 256.5 + (255 * (l - s * (n < 2 || n > 10 ? -1 : n < 4 ? n - 3 : n > 8 ? 9 - n : 1)))).toString(16).slice(1)
59
+ }
60
+ return f(0) + f(8) + f(4)
61
+ }
62
+ }
63
+ , toRgba = (rgbHex, alpha) => ("rgba(" + rgbHex.replace(/../g, x => parseInt(x, 16)+",") + alpha + ")").replace("0.", ".")
64
+ , colorRe = /\b(rgb|hsl)a?\s*\(\s*(\d+)(?:deg)?[\s,]+(\d+)[\s,%]+(\d+)%?(?:[\s,\/]+(0?\.?\d+)(%?))?\s*\)/g
65
+ , colorFn = (_, name, a, b, c, d, p) => (_ = toRgb[name](a, b, c), (p ? d/=100 : d) < 1 ? toRgba(_, d) : "#" + _.replace(/(\w)\1(\w)\2(\w)\3/, "$1$2$3"))
13
66
  , styleHandler = {
14
67
  get(style, prop) {
15
68
  if (prop === "cssText") {
69
+ var min = style.parentRule && style.parentRule.parentStyleSheet.min
16
70
  for (var out = [], i = style.length; i--; ) {
17
- out[i] = style[i] + ":" + (style.__[i] || style[style[i]])
71
+ out[i] = joinProp(style[i], style.__[i] || style[style[i]])
18
72
  }
19
73
  return out.join(";")
20
74
  }
21
75
  return style[prop] || ""
76
+ function joinProp(name, value) {
77
+ if (min && min.color) value = value.replace(colorRe, colorFn)
78
+ return name + ":" + value
79
+ }
22
80
  },
23
81
  set(style, prop, val) {
24
82
  if (prop === "cssText") {
25
83
  var m, k
26
- , re = /(?:^|;)\s*([-a-z]+)\s*:((?:("|')(?:\\.|(?!\3)[^\\])*?\3|[^"';])+)(?=;|$)/ig
84
+ , sheet = style.parentRule && style.parentRule.parentStyleSheet
85
+ , min = sheet && sheet.min
86
+ , re = /([*_]?[-a-z]+)\s*:((?:("|')(?:\\.|(?!\3)[^\\])*?\3|[^"';])+)|\/\*!?((?:[^*]|\*(?!\/))*)\*\//ig
27
87
  , len = 0
28
88
  , lastIdx = {}
29
89
  for (; (m = re.exec(val)); ) {
30
- k = m[1]
31
- if (lastIdx[k] >= 0) style.__[lastIdx[k]] = style[k]
32
- style[style[lastIdx[k] = len++] = k] = style[
33
- k === "float" ? "cssFloat" : k.replace(/-([a-z])/g, (_, a) => a.toUpperCase())
34
- ] = clear(m[2])
90
+ if (m[4]) {
91
+ if (min && len && plugins[m[4] = m[4].trim()]) style[k = style[len - 1]] = clear(plugins[m[4]](sheet, style[k]))
92
+ continue
93
+ } else {
94
+ k = m[1]
95
+ if (lastIdx[k] >= 0) style.__[lastIdx[k]] = style[k]
96
+ style[style[lastIdx[k] = len++] = k] = style[
97
+ k === "float" ? "cssFloat" : k.replace(/-([a-z])/g, (_, a) => a.toUpperCase())
98
+ ] = clear(m[2]).trim()
99
+ }
35
100
  }
36
101
  style.length = len
37
102
  } else {
38
103
  if (!style[prop]) style[style.length++] = prop
39
104
  style[prop] = style[prop === "cssFloat" ? "float" : prop.replace(/[A-Z]/g, "-$&").toLowerCase()] = clear(val)
40
105
  }
106
+ return true
41
107
  }
42
108
  }
43
109
  , ruleTypes = {
44
110
  style: {
111
+ type: 1,
45
112
  get cssText() {
46
113
  return this.style.length > 0 ? this.selectorText + "{" + this.style.cssText + "}" : ""
47
114
  },
48
115
  set cssText(text) {
49
116
  var idx = text.indexOf("{")
50
- this.selectorText = text.slice(0, idx).trim()
117
+ this.selectorText = clear(text.slice(0, idx).trim())
51
118
  this.style = CSSStyleDeclaration(text.slice(idx + 1).replace(/}\s*/, ""), this)
52
119
  }
53
120
  },
54
121
  import: {
55
122
  get cssText() {
56
- return clear(this.text)
123
+ var sheet = this.parentStyleSheet
124
+ , min = sheet.min
125
+ , text = this.text
126
+ , urlFn = (m,q1,q2,u) => q1 ? m : "url('" + path.join(text.baseURI, u) + "')"
127
+ if (min && min.import) {
128
+ text = new CSSStyleSheet({
129
+ parentStyleSheet: sheet,
130
+ href: this.href,
131
+ min
132
+ }, read(sheet, this.href))
133
+ if (sheet.baseURI !== text.baseURI) {
134
+ text.rules.forEach(rule => {
135
+ if (rule.type === 1) for (let style = rule.style, i = style.length; i--; ) {
136
+ if (urlRe.test(style[style[i]])) style[style[i]] = style[style[i]].replace(urlRe, urlFn)
137
+ }
138
+ })
139
+ }
140
+ text += ""
141
+ }
142
+ return text
57
143
  },
58
144
  set cssText(text) {
59
- this.text = clear(text)
145
+ this.href = text.split(/['"()]+/)[1]
146
+ this.text = clear(text.replace(/url\(("|')(.+?)\1\)/g, "'$2'"))
60
147
  }
61
148
  },
62
149
  "}": {
63
150
  get cssText() {
64
- var body = "" + this.style
65
- return body.length > 0 ? this.mediaText + "{\n" + body + "\n}" : ""
151
+ var style = new CSSStyleSheet({})
152
+ style.replaceSync(this.text)
153
+ var body = "" + style
154
+ return body.length > 0 ? this.mediaText + "{" + body + "}" : ""
66
155
  },
67
156
  set cssText(text) {
68
157
  var idx = text.indexOf("{")
69
158
  this.mediaText = clear(text.slice(0, idx).trim())
70
- this.style = new CSSStyleSheet({})
71
- this.style.replaceSync(text.slice(idx + 1, -1))
159
+ this.text = text.slice(idx + 1, -1)
72
160
  }
73
161
  },
74
162
  ";": {
@@ -83,12 +171,12 @@ var clearFn = (_, q, str) => q ? (q == "\"" && str.indexOf("'") == -1 ? "'" + st
83
171
 
84
172
  function CSSRule(text, parentStyleSheet, atType, parentRule = null) {
85
173
  // Clear comments and trim
86
- text = text.replace(/^\s+|\/\*[^*]*\*+([^/*][^*]*\*+)*\/|\s+$/g, "")
174
+ text = text.trim()
87
175
  var type = text[0] === "@" && text.slice(1, text.indexOf(" ")) || "style"
88
- , rule = Object.create(ruleTypes[type] || ruleTypes[type === "font-face" || type === "counter-style" ? "style" : atType])
89
- rule.cssText = text
176
+ , rule = Object.create(ruleTypes[type] || ruleTypes[type === "page" || type === "font-face" || type === "counter-style" ? "style" : atType])
90
177
  rule.parentStyleSheet = parentStyleSheet
91
178
  rule.parentRule = parentRule
179
+ rule.cssText = rule.type === 1 ? text : text.replace(/\/\*(?:[^*]|\*(?!\/))*\*\//g, "")
92
180
  return rule
93
181
  }
94
182
 
@@ -98,12 +186,21 @@ function CSSStyleDeclaration(text, parentRule = null) {
98
186
  return style
99
187
  }
100
188
 
101
- function CSSStyleSheet(opts) {
189
+ function CSSStyleSheet(opts, text = "") {
102
190
  Object.assign(this, opts)
103
- this.replaceSync(this.ownerNode ? this.ownerNode.textContent : "")
191
+ if (opts && opts.href) {
192
+ this.baseURI = path.join(
193
+ (opts.parentStyleSheet || opts.ownerNode && opts.ownerNode.ownerDocument).baseURI || "",
194
+ opts.href,
195
+ ".."
196
+ )
197
+ }
198
+ this.replaceSync(text)
104
199
  }
105
200
 
106
201
  CSSStyleSheet.prototype = {
202
+ baseURI: "",
203
+ root: "",
107
204
  disabled: false,
108
205
  type: "text/css",
109
206
  deleteRule(idx) {
@@ -113,24 +210,61 @@ CSSStyleSheet.prototype = {
113
210
  this.rules.splice(idx > -1 ? idx : this.rules.length, 0, CSSRule(rule, this))
114
211
  },
115
212
  replaceSync(text) {
116
- var m
117
- , sheet = this
118
- , re = /((?:("|')(?:\\.|(?!\2)[^\\])*?\2|[^"'}{;])+)|./ig
119
- , block = 0
120
- , pos = 0
213
+ var qpos, sheet = this
121
214
  sheet.rules = sheet.cssRules = []
122
215
  sheet.warnings = []
123
- for (; (m = re.exec(text)); ) {
124
- if (m[0] === "{") {
125
- block++
126
- } else if (m[0] === ";" && block === 0 || m[0] === "}" && --block < 1) {
127
- sheet.rules.push(CSSRule(text.slice(pos, pos = re.lastIndex), sheet, m[0]))
216
+ for (var char, inQuote, depth = 0, start = 0, pos = 0, len = text.length; pos < len; pos++) {
217
+ char = text[pos]
218
+ if (char === "\\" && inQuote !== "/") {
219
+ pos++
220
+ } else if (inQuote) {
221
+ if (char === inQuote) {
222
+ if (char !== "/" || text[pos - 1] === "*") {
223
+ if (char === "/" && depth < 1) {
224
+ // Remove root level comments
225
+ text = text.slice(0, qpos) + text.slice(pos + 1)
226
+ pos = qpos - 1
227
+ len = text.length
228
+ }
229
+ inQuote = ""
230
+ }
231
+ }
232
+ } else if (char === "'" || char === "\"" || char === "/" && text[pos+1] === "*") {
233
+ inQuote = char
234
+ qpos = pos
235
+ } else if (char === "{") {
236
+ depth++
237
+ } else if (char === "}" && --depth < 1 || char === ";" && depth < 1) {
238
+ if (depth < 0) throw "Invalid css"
239
+ sheet.rules.push(CSSRule(text.slice(start, start = pos + 1), sheet, char))
128
240
  }
129
241
  }
130
242
  },
131
- toString() {
243
+ toString(min) {
244
+ if (min) this.min = min
132
245
  return this.rules.map(rule => rule.cssText).filter(Boolean).join("\n")
133
246
  }
134
247
  }
135
248
 
249
+ function selectorSplit(text) {
250
+ for (var char, inQuote, depth = 0, start = 0, pos = 0, len = text.length, out = []; pos < len; ) {
251
+ char = text[pos++]
252
+ if (char === "\\") {
253
+ pos++
254
+ } else if (inQuote) {
255
+ if (char === inQuote) inQuote = ""
256
+ } else if (char === "'" || char === "\"") {
257
+ inQuote = char
258
+ } else if (char === "(" || char === "[") {
259
+ depth++
260
+ } else if (char === ")" || char === "]") {
261
+ depth--
262
+ } else if (char === "," && depth === 0) {
263
+ out.push(text.slice(start, (start = pos) - 1).trim())
264
+ }
265
+ }
266
+ out.push(text.slice(start).trim())
267
+ return out
268
+ }
269
+
136
270
 
package/dom.js CHANGED
@@ -1,6 +1,8 @@
1
1
 
2
2
  /*! litejs.com/MIT-LICENSE.txt */
3
3
 
4
+ "use strict"
5
+
4
6
  var boolAttrs = {
5
7
  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
6
8
  }
@@ -13,10 +15,13 @@ var boolAttrs = {
13
15
  , voidElements = {
14
16
  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
15
17
  }
18
+ , svgVoidElements = {
19
+ circle:1, ellipse:1, image:1, line:1, path:1, polygon:1, polyline:1, rect:1, stop:1, use:1,
20
+ }
16
21
  , rawTextElements = { SCRIPT: /<(?=\/script)/i, STYLE: /<(?=\/style)/i }
17
22
  , rawTextEscape = { SCRIPT: /<(?=\/script|!--)/ig, STYLE: /<(?=\/style|!--)/ig }
18
23
  , hasOwn = voidElements.hasOwnProperty
19
- , { CSSStyleDeclaration, CSSStyleSheet } = require("./css.js")
24
+ , { selectorSplit, CSSStyleDeclaration, CSSStyleSheet } = require("./css.js")
20
25
  , selector = require("./selector.js")
21
26
  , Node = {
22
27
  ELEMENT_NODE: 1,
@@ -41,9 +46,12 @@ var boolAttrs = {
41
46
  },
42
47
  set textContent(text) {
43
48
  if (this.nodeType === 3 || this.nodeType === 8) this.data = text
44
- else replaceChildren.call(this, this.ownerDocument.createTextNode(
45
- rawTextEscape[this.tagName] ? text.replace(rawTextEscape[this.tagName], "<\\") : text
46
- ))
49
+ else {
50
+ removeChilds(this)
51
+ this.appendChild(this.ownerDocument.createTextNode(
52
+ rawTextEscape[this.tagName] ? text.replace(rawTextEscape[this.tagName], "<\\") : text
53
+ ))
54
+ }
47
55
  },
48
56
  get firstChild() {
49
57
  return this.childNodes && this.childNodes[0] || null
@@ -69,6 +77,7 @@ var boolAttrs = {
69
77
  , attrRe = /([^=\s]+)(?:\s*=\s*(("|')((?:\\\3|[\s\S])*?)\3|[^\s"'`=<>]+)|)/g
70
78
  , frag = doc.createDocumentFragment()
71
79
  , tree = frag
80
+ , voidEl = doc.documentElement.tagName === "svg" ? svgVoidElements : voidElements
72
81
 
73
82
  for (; (m = tagRe.exec(html)); ) {
74
83
  if (m[4]) {
@@ -83,7 +92,7 @@ var boolAttrs = {
83
92
  for (text = ""; (m = tagRe.exec(html)) && !re.test(m[0]); text += m[3] || m[0]);
84
93
  child.textContent = text.replace(unescRe, unescFn)
85
94
  if (!m) break
86
- } else if (!voidElements[child.tagName] && !m[8]) tree = child
95
+ } else if (!voidEl[child.tagName] && !m[8]) tree = child
87
96
  } else {
88
97
  tree.appendChild(
89
98
  m[2] ? doc.createComment(m[2].replace(unescRe, unescFn)) :
@@ -92,7 +101,8 @@ var boolAttrs = {
92
101
  )
93
102
  }
94
103
  }
95
- replaceChildren.call(node, frag)
104
+ removeChilds(node)
105
+ node.appendChild(frag)
96
106
 
97
107
  function setAttr(_, name, value, q, qvalue) {
98
108
  child.setAttribute(name, (q ? qvalue : value || "").replace(unescRe, unescFn))
@@ -107,10 +117,7 @@ var boolAttrs = {
107
117
  this.parentNode.replaceChild(frag, this)
108
118
  },
109
119
  get sheet() {
110
- if (this.tagName === "STYLE" || this.tagName === "LINK" && this.rel === "stylesheet" && this.href) return new CSSStyleSheet({
111
- href: this.href,
112
- ownerNode: this
113
- })
120
+ return makeSheet(this)
114
121
  },
115
122
  get style() {
116
123
  return this._style || (this._style = CSSStyleDeclaration(this.getAttribute("style") || ""))
@@ -118,6 +125,21 @@ var boolAttrs = {
118
125
  set style(value) {
119
126
  this.style.cssText = value
120
127
  },
128
+ appendChild(el) {
129
+ return this.insertBefore(el)
130
+ },
131
+ cloneNode(deep) {
132
+ var node = this
133
+ , clone = new node.constructor(node.tagName || node.data)
134
+ clone.ownerDocument = node.ownerDocument
135
+
136
+ mergeAttributes(node, clone)
137
+
138
+ if (deep && node.hasChildNodes()) {
139
+ node.childNodes.forEach(child => clone.appendChild(child.cloneNode(deep)))
140
+ }
141
+ return clone
142
+ },
121
143
  contains(el) {
122
144
  for (; el; el = el.parentNode) if (el === this) return true
123
145
  return false
@@ -125,12 +147,6 @@ var boolAttrs = {
125
147
  hasChildNodes() {
126
148
  return !!this.firstChild
127
149
  },
128
- getElementById(id) {
129
- return selector.find(this, "#" + id, 1)
130
- },
131
- appendChild(el) {
132
- return this.insertBefore(el)
133
- },
134
150
  insertBefore(el, ref) {
135
151
  var node = this
136
152
  , childs = node.childNodes
@@ -163,20 +179,6 @@ var boolAttrs = {
163
179
  this.insertBefore(el, ref)
164
180
  return this.removeChild(ref)
165
181
  },
166
- cloneNode(deep) {
167
- var node = this
168
- , clone = new node.constructor(node.tagName || node.data)
169
- clone.ownerDocument = node.ownerDocument
170
-
171
- if (node.attributes) {
172
- node.attributes.names().forEach(attr => clone.setAttribute(attr, node.getAttribute(attr)))
173
- }
174
-
175
- if (deep && node.hasChildNodes()) {
176
- node.childNodes.forEach(child => clone.appendChild(child.cloneNode(deep)))
177
- }
178
- return clone
179
- },
180
182
  querySelector(sel) {
181
183
  return selector.find(this, sel, 1)
182
184
  },
@@ -185,7 +187,7 @@ var boolAttrs = {
185
187
  },
186
188
  toString(min) {
187
189
  return rawTextElements[this.tagName] ? (
188
- this.tagName === "STYLE" && (min === true || min && min.css) ? "\n" + this.sheet.toString(min.css || true) + "\n" :
190
+ this.tagName === "STYLE" && (min === true || min && min.css) ? "\n" + makeSheet(this, min.css || true) + "\n" :
189
191
  this.textContent
190
192
  ) : this.childNodes.map(node => node.toString(min)).join("")
191
193
  }
@@ -203,7 +205,10 @@ var boolAttrs = {
203
205
  get previousElementSibling() {
204
206
  return getSibling(this, -1, 1)
205
207
  },
206
- replaceChildren: replaceChildren,
208
+ replaceChildren() {
209
+ removeChilds(this)
210
+ for (var i = 0, l = arguments.length; i < l; ) this.insertBefore(arguments[i++])
211
+ },
207
212
  hasAttribute(name) {
208
213
  return this.attributes.getNamedItem(name) != null
209
214
  },
@@ -236,18 +241,18 @@ var boolAttrs = {
236
241
  "&sect;": "§", "&sup2;": "²", "&sup3;": "³", "&yen;": "¥"
237
242
  }
238
243
 
239
- Object.keys(boolAttrs).forEach(addGetter, { isBool: true, readonly: "readOnly" })
240
- numAttrs.split(" ").forEach(addGetter, { isNum: true })
241
- strAttrs.split(" ").forEach(addGetter, { "for": "htmlFor", "class": "className" })
244
+ Object.keys(boolAttrs).forEach(key => addGetter(key, { isBool: true, readonly: "readOnly" }))
245
+ numAttrs.split(" ").forEach(key => addGetter(key, { isNum: true }))
246
+ strAttrs.split(" ").forEach(key => addGetter(key, { "for": "htmlFor", "class": "className" }))
242
247
 
243
- function addGetter(key) {
248
+ function addGetter(key, opts) {
244
249
  var attr = key.toLowerCase()
245
- Object.defineProperty(Element, this[key] || key, {
250
+ Object.defineProperty(Element, opts[key] || key, {
246
251
  configurable: true,
247
252
  enumerable: true,
248
253
  get: (
249
- this.isBool ? function() { return this.hasAttribute(attr) } :
250
- this.isNum ? function() { return +this.getAttribute(attr) || 0 } :
254
+ opts.isBool ? function() { return this.hasAttribute(attr) } :
255
+ opts.isNum ? function() { return +this.getAttribute(attr) || 0 } :
251
256
  function() { return this.getAttribute(attr) || "" }
252
257
  ),
253
258
  set(value) {
@@ -264,7 +269,7 @@ function addGetter(key) {
264
269
 
265
270
  function Attr(node, name, value) {
266
271
  this.ownerElement = node
267
- this.name = name.toLowerCase()
272
+ this.name = name
268
273
  this.value = "" + value
269
274
  }
270
275
 
@@ -296,8 +301,7 @@ NamedNodeMap.prototype = {
296
301
  },
297
302
  setNamedItem(attr) {
298
303
  var oldAttr = this.getNamedItem(attr.name)
299
- if (attr.name === "style") attr.value = CSSStyleDeclaration(attr.value).cssText
300
- this[attr.name] = attr
304
+ this[attr.name.toLowerCase()] = attr
301
305
  return oldAttr
302
306
  },
303
307
  toString(minify) {
@@ -305,6 +309,7 @@ NamedNodeMap.prototype = {
305
309
  , tagName = map.ownerElement.tagName
306
310
  , isXml = map.ownerElement.ownerDocument.contentType === "application/xml"
307
311
  return map.names().map(loName => {
312
+ if (loName === "style" && minify && map.ownerElement.style) { /* Access to style makes _style */ }
308
313
  var attr = map.getNamedItem(loName)
309
314
  , name = attr.name
310
315
  , value = attr.value.replace(escRe, escFn)
@@ -316,6 +321,7 @@ NamedNodeMap.prototype = {
316
321
  if (!quotedAttrRe.test(value)) return name + "=" + value
317
322
  if (value.split("\"").length > value.split("'").length) return name + "='" + value.replace(/'/g, "&#39;") + "'"
318
323
  }
324
+ name = loName
319
325
  }
320
326
  return name + "=\"" + value.replace(/"/g, "&quot;") + "\""
321
327
  }).filter(Boolean).join(" ")
@@ -340,20 +346,29 @@ function HTMLElement(tag) {
340
346
  }
341
347
 
342
348
  extendNode(HTMLElement, Element, {
349
+ localName: null,
350
+ namespaceURI: "http://www.w3.org/1999/xhtml",
343
351
  nodeType: 1,
344
- matches(sel) {
345
- return selector.matches(this, sel)
352
+ tagName: null,
353
+ blur() {
354
+ this.ownerDocument.activeElement = null
346
355
  },
347
356
  closest(sel) {
348
357
  return selector.closest(this, sel)
349
358
  },
350
- namespaceURI: "http://www.w3.org/1999/xhtml",
351
- localName: null,
352
- tagName: null,
359
+ focus() {
360
+ this.ownerDocument.activeElement = this
361
+ },
362
+ matches(sel) {
363
+ return selector.matches(this, sel)
364
+ },
353
365
  toString(minify) {
354
366
  var attrs = this.attributes.toString(minify)
355
- return "<" + this.localName + (attrs ? " " + attrs + (attrs.slice(-1) === "/" ? " >" : ">") : ">") +
356
- (voidElements[this.tagName] ? "" : Node.toString.call(this, minify) + "</" + this.localName + ">")
367
+ , isXml = this.ownerDocument.contentType === "application/xml"
368
+ , voidEl = this.ownerDocument.documentElement.tagName === "svg" ? svgVoidElements : voidElements
369
+ return "<" + this.localName +
370
+ (attrs ? " " + (attrs.slice(-1) === "/" ? attrs + " " : attrs) : "") +
371
+ (voidEl[this.tagName] ? (isXml ? "/>" : ">") : ">" + Node.toString.call(this, minify) + "</" + this.localName + ">")
357
372
  }
358
373
  })
359
374
 
@@ -434,7 +449,10 @@ extendNode(Document, Element, {
434
449
  createTextNode: own(Text),
435
450
  createComment: own(Comment),
436
451
  createDocumentType: own(DocumentType), //Should be document.implementation.createDocumentType(name, publicId, systemId)
437
- createDocumentFragment: own(DocumentFragment)
452
+ createDocumentFragment: own(DocumentFragment),
453
+ getElementById(id) {
454
+ return selector.find(this, "#" + id, 1)
455
+ }
438
456
  })
439
457
 
440
458
  function DOMParser() {}
@@ -468,9 +486,9 @@ function extendNode(obj, extras) {
468
486
  obj.prototype.constructor = obj
469
487
  }
470
488
 
471
- function replaceChildren() {
472
- for (var arr = this.childNodes, i = 0, l = arr.length; i < l; ) arr[i++].parentNode = null
473
- for (i = arr.length = 0, l = arguments.length; i < l; ) this.insertBefore(arguments[i++])
489
+ function removeChilds(node) {
490
+ node.childNodes.forEach(child => child.parentNode = null)
491
+ node.childNodes.length = 0
474
492
  }
475
493
 
476
494
  function getElement(childs, index, step, type) {
@@ -486,8 +504,26 @@ function getSibling(node, step, type) {
486
504
  return type > 0 ? getElement(silbings, index + step, step, type) : silbings && silbings[index + step] || null
487
505
  }
488
506
 
507
+ function makeSheet(el, min) {
508
+ if (el.tagName === "STYLE" || el.tagName === "LINK" && el.rel === "stylesheet" && el.href) return new CSSStyleSheet({
509
+ href: el.href,
510
+ ownerNode: el,
511
+ min
512
+ }, el.tagName === "STYLE" && el.textContent)
513
+ }
514
+
515
+ function mergeAttributes(source, target) {
516
+ if (source && target && source.attributes) {
517
+ source.attributes.names().forEach(attr => target.setAttribute(attr, source.getAttribute(attr)))
518
+ }
519
+ }
520
+
489
521
  exports.document = new Document()
490
522
  exports.entities = entities
523
+ exports.mergeAttributes = mergeAttributes
524
+ exports.selectorSplit = selectorSplit
525
+ exports.CSSStyleDeclaration = CSSStyleDeclaration
526
+ exports.CSSStyleSheet = CSSStyleSheet
491
527
  exports.DOMParser = DOMParser
492
528
  exports.Document = Document
493
529
  exports.DocumentFragment = DocumentFragment
package/net.js CHANGED
@@ -1,6 +1,8 @@
1
1
 
2
2
  /*! litejs.com/MIT-LICENSE.txt */
3
3
 
4
+ "use strict"
5
+
4
6
  var DOM = require(".")
5
7
  , URL = require("url").URL
6
8
  , parser = new DOM.DOMParser()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litejs/dom",
3
- "version": "24.12.0",
3
+ "version": "25.5.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>",
@@ -20,11 +20,10 @@
20
20
  "*.js"
21
21
  ],
22
22
  "scripts": {
23
- "lint": "npx jshint -c .github/jshint.json *.js",
24
- "test": "lj t test/*.js"
23
+ "test": "lj t"
25
24
  },
26
25
  "repository": "github:litejs/dom",
27
26
  "devDependencies": {
28
- "@litejs/cli": "24.10.0"
27
+ "@litejs/cli": "25.1.0"
29
28
  }
30
29
  }
package/selector.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
-
3
2
  /*! litejs.com/MIT-LICENSE.txt */
4
3
 
4
+ "use strict"
5
5
 
6
6
  !function(exports) {
7
7
  var selectorCache = {
package/interactive.js DELETED
@@ -1,18 +0,0 @@
1
-
2
-
3
- /*! litejs.com/MIT-LICENSE.txt */
4
-
5
-
6
- var DOM = module.exports = require(".")
7
- , HTMLElementExtra = {
8
- focus() {
9
- this.ownerDocument.activeElement = this
10
- },
11
- blur() {
12
- this.ownerDocument.activeElement = null
13
- }
14
- }
15
-
16
- Object.assign(DOM.HTMLElement.prototype, HTMLElementExtra)
17
-
18
-