html-minifier-next 2.1.3 → 2.1.5
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 +26 -24
- package/cli.js +0 -1
- package/dist/htmlminifier.cjs +46 -11
- package/dist/htmlminifier.esm.bundle.js +46 -11
- package/dist/htmlminifier.umd.bundle.js +46 -11
- package/dist/htmlminifier.umd.bundle.min.js +2 -2
- package/package.json +5 -6
- package/src/htmlminifier.js +0 -1
- package/src/htmlparser.js +46 -11
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# HTML Minifier Next
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/html-minifier-next)
|
|
4
|
-
[](https://github.com/j9t/html-minifier-next/actions)
|
|
5
5
|
|
|
6
|
-
HTML Minifier Next is a highly **configurable, well-tested, JavaScript-based HTML minifier**.
|
|
6
|
+
HTML Minifier Next (HMN) is a highly **configurable, well-tested, JavaScript-based HTML minifier**.
|
|
7
7
|
|
|
8
|
-
The project has been based on [Terser’s html-minifier-terser](https://github.com/terser/html-minifier-terser), which in turn had been based on [Juriy Zaytsev’s html-minifier](https://github.com/kangax/html-minifier). It was set up because as of 2025, both html-minifier-terser and html-minifier have been unmaintained for some time. As the project seems maintainable [to me, [Jens](https://meiert.com/)]—even more so with community support—, it
|
|
8
|
+
The project has been based on [Terser’s html-minifier-terser](https://github.com/terser/html-minifier-terser), which in turn had been based on [Juriy Zaytsev’s html-minifier](https://github.com/kangax/html-minifier) (HMN offers additional features, but is compatible with both). It was set up because as of 2025, both html-minifier-terser and html-minifier have been unmaintained for some time. As the project seems maintainable [to me, [Jens](https://meiert.com/)]—even more so with community support—, it’s being updated and documented further in this place.
|
|
9
9
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
@@ -127,27 +127,29 @@ For lint-like capabilities take a look at [HTMLLint](https://github.com/kangax/h
|
|
|
127
127
|
|
|
128
128
|
## Minification comparison
|
|
129
129
|
|
|
130
|
-
How does HTML Minifier Next compare to other solutions, like [minimize](https://github.com/Swaagie/minimize), [htmlcompressor.com](http://htmlcompressor.com/), [htmlnano](https://github.com/posthtml/htmlnano), and [minify-html](https://github.com/wilsonzlin/minify-html)?
|
|
130
|
+
How does HTML Minifier Next compare to other solutions, like [minimize](https://github.com/Swaagie/minimize), [htmlcompressor.com](http://htmlcompressor.com/), [htmlnano](https://github.com/posthtml/htmlnano), and [minify-html](https://github.com/wilsonzlin/minify-html)? (All with the most aggressive settings, but without [hyper-optimization](https://meiert.com/blog/the-ways-of-writing-html/#toc-hyper-optimized).)
|
|
131
131
|
|
|
132
|
-
| Site | Original size (KB) | HTML Minifier Next | minimize |
|
|
132
|
+
| Site | Original size (KB) | HTML Minifier Next | minimize | htmlcompressor.com | htmlnano | minify-html |
|
|
133
133
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
134
|
-
| [A List Apart](https://alistapart.com/) |
|
|
135
|
-
| [Amazon](https://www.amazon.com/) |
|
|
136
|
-
| [
|
|
137
|
-
| [
|
|
138
|
-
| [
|
|
139
|
-
| [
|
|
140
|
-
| [
|
|
141
|
-
| [FAZ](https://www.faz.net/aktuell/) |
|
|
142
|
-
| [Frontend Dogma](https://frontenddogma.com/) |
|
|
143
|
-
| [Google](https://www.google.com/) |
|
|
144
|
-
| [
|
|
145
|
-
| [
|
|
146
|
-
| [
|
|
147
|
-
| [
|
|
148
|
-
| [
|
|
149
|
-
| [
|
|
150
|
-
| [
|
|
134
|
+
| [A List Apart](https://alistapart.com/) | 63 | **54** | 58 | 57 | 55 | 56 |
|
|
135
|
+
| [Amazon](https://www.amazon.com/) | 5 | **3** | **3** | **3** | **3** | n/a |
|
|
136
|
+
| [Apple](https://www.apple.com/) | 212 | **171** | 198 | 195 | 189 | 194 |
|
|
137
|
+
| [BBC](https://www.bbc.co.uk/) | 742 | **683** | 736 | n/a | 698 | 699 |
|
|
138
|
+
| [CSS-Tricks](https://css-tricks.com/) | 11 | **6** | 11 | 10 | 7 | n/a |
|
|
139
|
+
| [ECMAScript](https://tc39.es/ecma262/) | 7205 | **6315** | 6585 | n/a | 6532 | 6538 |
|
|
140
|
+
| [EFF](https://www.eff.org/) | 60 | **51** | 55 | 55 | 54 | 53 |
|
|
141
|
+
| [FAZ](https://www.faz.net/aktuell/) | 1847 | 1728 | 1764 | n/a | **1630** | n/a |
|
|
142
|
+
| [Frontend Dogma](https://frontenddogma.com/) | 117 | **112** | 126 | 117 | 124 | 117 |
|
|
143
|
+
| [Google](https://www.google.com/) | 18 | **16** | 17 | 17 | **16** | n/a |
|
|
144
|
+
| [Ground News](https://ground.news/) | 1509 | **1287** | 1495 | n/a | 1386 | n/a |
|
|
145
|
+
| [HTML](https://html.spec.whatwg.org/multipage/) | 149 | **146** | 155 | 148 | 153 | 148 |
|
|
146
|
+
| [Leanpub](https://leanpub.com/) | 1361 | **1143** | 1355 | n/a | 1150 | n/a |
|
|
147
|
+
| [Mastodon](https://mastodon.social/explore) | 35 | **25** | 33 | 33 | 30 | 33 |
|
|
148
|
+
| [MDN](https://developer.mozilla.org/en-US/) | 104 | **62** | 67 | 68 | 64 | n/a |
|
|
149
|
+
| [Middle East Eye](https://www.middleeasteye.net/) | 221 | **194** | 201 | 202 | 201 | 199 |
|
|
150
|
+
| [SitePoint](https://www.sitepoint.com/) | 469 | **338** | 466 | n/a | 408 | 449 |
|
|
151
|
+
| [United Nations](https://www.un.org/en/) | 153 | **116** | 132 | 125 | 123 | 127 |
|
|
152
|
+
| [W3C](https://www.w3.org/) | 49 | **35** | 40 | 38 | 37 | 38 |
|
|
151
153
|
|
|
152
154
|
## Options quick reference
|
|
153
155
|
|
|
@@ -220,9 +222,9 @@ SVG elements are automatically recognized, and when they are minified, both case
|
|
|
220
222
|
|
|
221
223
|
HTML Minifier Next **can’t work with invalid or partial chunks of markup**. This is because it parses markup into a tree structure, then modifies it (removing anything that was specified for removal, ignoring anything that was specified to be ignored, etc.), then it creates a markup out of that tree and returns it.
|
|
222
224
|
|
|
223
|
-
|
|
225
|
+
_Input markup (e.g., `<p id="">foo`) → Internal representation of markup in a form of tree (e.g., `{ tag: "p", attr: "id", children: ["foo"] }`) → Transformation of internal representation (e.g., removal of `id` attribute) → Output of resulting markup (e.g., `<p>foo</p>`)_
|
|
224
226
|
|
|
225
|
-
|
|
227
|
+
HMN can’t know that the original markup represented only part of the tree. It parses a complete tree and, in doing so, loses information about the input being malformed or partial. As a result, it can’t emit a partial or malformed tree.
|
|
226
228
|
|
|
227
229
|
To validate HTML markup, use [the W3C validator](https://validator.w3.org/) or one of [several validator packages](https://meiert.com/blog/html-validator-packages/).
|
|
228
230
|
|
package/cli.js
CHANGED
package/dist/htmlminifier.cjs
CHANGED
|
@@ -21,7 +21,7 @@ async function replaceAsync(str, regex, asyncFn) {
|
|
|
21
21
|
|
|
22
22
|
/*!
|
|
23
23
|
* HTML Parser By John Resig (ejohn.org)
|
|
24
|
-
* Modified by Juriy
|
|
24
|
+
* Modified by Juriy “kangax” Zaytsev
|
|
25
25
|
* Original code by Erik Arvidsson, Mozilla Public License
|
|
26
26
|
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
|
27
27
|
*/
|
|
@@ -132,7 +132,7 @@ class HTMLParser {
|
|
|
132
132
|
if (!lastTag || !special.has(lastTag)) {
|
|
133
133
|
let textEnd = html.indexOf('<');
|
|
134
134
|
if (textEnd === 0) {
|
|
135
|
-
// Comment
|
|
135
|
+
// Comment
|
|
136
136
|
if (/^<!--/.test(html)) {
|
|
137
137
|
const commentEnd = html.indexOf('-->');
|
|
138
138
|
|
|
@@ -160,7 +160,7 @@ class HTMLParser {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
// Doctype
|
|
163
|
+
// Doctype
|
|
164
164
|
const doctypeMatch = html.match(doctype);
|
|
165
165
|
if (doctypeMatch) {
|
|
166
166
|
if (handler.doctype) {
|
|
@@ -171,7 +171,7 @@ class HTMLParser {
|
|
|
171
171
|
continue;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
// End tag
|
|
174
|
+
// End tag
|
|
175
175
|
const endTagMatch = html.match(endTag);
|
|
176
176
|
if (endTagMatch) {
|
|
177
177
|
html = html.substring(endTagMatch[0].length);
|
|
@@ -180,7 +180,7 @@ class HTMLParser {
|
|
|
180
180
|
continue;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
// Start tag
|
|
183
|
+
// Start tag
|
|
184
184
|
const startTagMatch = parseStartTag(html);
|
|
185
185
|
if (startTagMatch) {
|
|
186
186
|
html = startTagMatch.rest;
|
|
@@ -273,11 +273,41 @@ class HTMLParser {
|
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
function findTagInCurrentTable(tagName) {
|
|
277
|
+
let pos;
|
|
278
|
+
const needle = tagName.toLowerCase();
|
|
279
|
+
for (pos = stack.length - 1; pos >= 0; pos--) {
|
|
280
|
+
const currentTag = stack[pos].tag.toLowerCase();
|
|
281
|
+
if (currentTag === needle) {
|
|
282
|
+
return pos;
|
|
283
|
+
}
|
|
284
|
+
// Stop searching if we hit a table boundary
|
|
285
|
+
if (currentTag === 'table') {
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return -1;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function parseEndTagAt(pos) {
|
|
293
|
+
// Close all open elements up to pos (mirrors parseEndTag’s core branch)
|
|
294
|
+
for (let i = stack.length - 1; i >= pos; i--) {
|
|
295
|
+
if (handler.end) {
|
|
296
|
+
await handler.end(stack[i].tag, stack[i].attrs, true);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
stack.length = pos;
|
|
300
|
+
lastTag = pos && stack[pos - 1].tag;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function closeIfFoundInCurrentTable(tagName) {
|
|
304
|
+
const pos = findTagInCurrentTable(tagName);
|
|
305
|
+
if (pos >= 0) {
|
|
306
|
+
// Close at the specific index to avoid re-searching
|
|
307
|
+
await parseEndTagAt(pos);
|
|
279
308
|
return true;
|
|
280
309
|
}
|
|
310
|
+
return false;
|
|
281
311
|
}
|
|
282
312
|
|
|
283
313
|
async function handleStartTag(match) {
|
|
@@ -288,10 +318,15 @@ class HTMLParser {
|
|
|
288
318
|
if (lastTag === 'p' && nonPhrasing.has(tagName)) {
|
|
289
319
|
await parseEndTag('', lastTag);
|
|
290
320
|
} else if (tagName === 'tbody') {
|
|
291
|
-
await
|
|
321
|
+
await closeIfFoundInCurrentTable('thead');
|
|
292
322
|
} else if (tagName === 'tfoot') {
|
|
293
|
-
if (!await
|
|
294
|
-
await
|
|
323
|
+
if (!await closeIfFoundInCurrentTable('tbody')) {
|
|
324
|
+
await closeIfFoundInCurrentTable('thead');
|
|
325
|
+
}
|
|
326
|
+
} else if (tagName === 'thead') {
|
|
327
|
+
// If a `tbody` or `tfoot` is open in the current table, close it
|
|
328
|
+
if (!await closeIfFoundInCurrentTable('tbody')) {
|
|
329
|
+
await closeIfFoundInCurrentTable('tfoot');
|
|
295
330
|
}
|
|
296
331
|
}
|
|
297
332
|
if (tagName === 'col' && findTag('colgroup') < 0) {
|
|
@@ -60211,7 +60211,7 @@ async function replaceAsync(str, regex, asyncFn) {
|
|
|
60211
60211
|
|
|
60212
60212
|
/*!
|
|
60213
60213
|
* HTML Parser By John Resig (ejohn.org)
|
|
60214
|
-
* Modified by Juriy
|
|
60214
|
+
* Modified by Juriy “kangax” Zaytsev
|
|
60215
60215
|
* Original code by Erik Arvidsson, Mozilla Public License
|
|
60216
60216
|
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
|
60217
60217
|
*/
|
|
@@ -60322,7 +60322,7 @@ class HTMLParser {
|
|
|
60322
60322
|
if (!lastTag || !special.has(lastTag)) {
|
|
60323
60323
|
let textEnd = html.indexOf('<');
|
|
60324
60324
|
if (textEnd === 0) {
|
|
60325
|
-
// Comment
|
|
60325
|
+
// Comment
|
|
60326
60326
|
if (/^<!--/.test(html)) {
|
|
60327
60327
|
const commentEnd = html.indexOf('-->');
|
|
60328
60328
|
|
|
@@ -60350,7 +60350,7 @@ class HTMLParser {
|
|
|
60350
60350
|
}
|
|
60351
60351
|
}
|
|
60352
60352
|
|
|
60353
|
-
// Doctype
|
|
60353
|
+
// Doctype
|
|
60354
60354
|
const doctypeMatch = html.match(doctype);
|
|
60355
60355
|
if (doctypeMatch) {
|
|
60356
60356
|
if (handler.doctype) {
|
|
@@ -60361,7 +60361,7 @@ class HTMLParser {
|
|
|
60361
60361
|
continue;
|
|
60362
60362
|
}
|
|
60363
60363
|
|
|
60364
|
-
// End tag
|
|
60364
|
+
// End tag
|
|
60365
60365
|
const endTagMatch = html.match(endTag);
|
|
60366
60366
|
if (endTagMatch) {
|
|
60367
60367
|
html = html.substring(endTagMatch[0].length);
|
|
@@ -60370,7 +60370,7 @@ class HTMLParser {
|
|
|
60370
60370
|
continue;
|
|
60371
60371
|
}
|
|
60372
60372
|
|
|
60373
|
-
// Start tag
|
|
60373
|
+
// Start tag
|
|
60374
60374
|
const startTagMatch = parseStartTag(html);
|
|
60375
60375
|
if (startTagMatch) {
|
|
60376
60376
|
html = startTagMatch.rest;
|
|
@@ -60463,11 +60463,41 @@ class HTMLParser {
|
|
|
60463
60463
|
}
|
|
60464
60464
|
}
|
|
60465
60465
|
|
|
60466
|
-
|
|
60467
|
-
|
|
60468
|
-
|
|
60466
|
+
function findTagInCurrentTable(tagName) {
|
|
60467
|
+
let pos;
|
|
60468
|
+
const needle = tagName.toLowerCase();
|
|
60469
|
+
for (pos = stack.length - 1; pos >= 0; pos--) {
|
|
60470
|
+
const currentTag = stack[pos].tag.toLowerCase();
|
|
60471
|
+
if (currentTag === needle) {
|
|
60472
|
+
return pos;
|
|
60473
|
+
}
|
|
60474
|
+
// Stop searching if we hit a table boundary
|
|
60475
|
+
if (currentTag === 'table') {
|
|
60476
|
+
break;
|
|
60477
|
+
}
|
|
60478
|
+
}
|
|
60479
|
+
return -1;
|
|
60480
|
+
}
|
|
60481
|
+
|
|
60482
|
+
async function parseEndTagAt(pos) {
|
|
60483
|
+
// Close all open elements up to pos (mirrors parseEndTag’s core branch)
|
|
60484
|
+
for (let i = stack.length - 1; i >= pos; i--) {
|
|
60485
|
+
if (handler.end) {
|
|
60486
|
+
await handler.end(stack[i].tag, stack[i].attrs, true);
|
|
60487
|
+
}
|
|
60488
|
+
}
|
|
60489
|
+
stack.length = pos;
|
|
60490
|
+
lastTag = pos && stack[pos - 1].tag;
|
|
60491
|
+
}
|
|
60492
|
+
|
|
60493
|
+
async function closeIfFoundInCurrentTable(tagName) {
|
|
60494
|
+
const pos = findTagInCurrentTable(tagName);
|
|
60495
|
+
if (pos >= 0) {
|
|
60496
|
+
// Close at the specific index to avoid re-searching
|
|
60497
|
+
await parseEndTagAt(pos);
|
|
60469
60498
|
return true;
|
|
60470
60499
|
}
|
|
60500
|
+
return false;
|
|
60471
60501
|
}
|
|
60472
60502
|
|
|
60473
60503
|
async function handleStartTag(match) {
|
|
@@ -60478,10 +60508,15 @@ class HTMLParser {
|
|
|
60478
60508
|
if (lastTag === 'p' && nonPhrasing.has(tagName)) {
|
|
60479
60509
|
await parseEndTag('', lastTag);
|
|
60480
60510
|
} else if (tagName === 'tbody') {
|
|
60481
|
-
await
|
|
60511
|
+
await closeIfFoundInCurrentTable('thead');
|
|
60482
60512
|
} else if (tagName === 'tfoot') {
|
|
60483
|
-
if (!await
|
|
60484
|
-
await
|
|
60513
|
+
if (!await closeIfFoundInCurrentTable('tbody')) {
|
|
60514
|
+
await closeIfFoundInCurrentTable('thead');
|
|
60515
|
+
}
|
|
60516
|
+
} else if (tagName === 'thead') {
|
|
60517
|
+
// If a `tbody` or `tfoot` is open in the current table, close it
|
|
60518
|
+
if (!await closeIfFoundInCurrentTable('tbody')) {
|
|
60519
|
+
await closeIfFoundInCurrentTable('tfoot');
|
|
60485
60520
|
}
|
|
60486
60521
|
}
|
|
60487
60522
|
if (tagName === 'col' && findTag('colgroup') < 0) {
|
|
@@ -60217,7 +60217,7 @@
|
|
|
60217
60217
|
|
|
60218
60218
|
/*!
|
|
60219
60219
|
* HTML Parser By John Resig (ejohn.org)
|
|
60220
|
-
* Modified by Juriy
|
|
60220
|
+
* Modified by Juriy “kangax” Zaytsev
|
|
60221
60221
|
* Original code by Erik Arvidsson, Mozilla Public License
|
|
60222
60222
|
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
|
60223
60223
|
*/
|
|
@@ -60328,7 +60328,7 @@
|
|
|
60328
60328
|
if (!lastTag || !special.has(lastTag)) {
|
|
60329
60329
|
let textEnd = html.indexOf('<');
|
|
60330
60330
|
if (textEnd === 0) {
|
|
60331
|
-
// Comment
|
|
60331
|
+
// Comment
|
|
60332
60332
|
if (/^<!--/.test(html)) {
|
|
60333
60333
|
const commentEnd = html.indexOf('-->');
|
|
60334
60334
|
|
|
@@ -60356,7 +60356,7 @@
|
|
|
60356
60356
|
}
|
|
60357
60357
|
}
|
|
60358
60358
|
|
|
60359
|
-
// Doctype
|
|
60359
|
+
// Doctype
|
|
60360
60360
|
const doctypeMatch = html.match(doctype);
|
|
60361
60361
|
if (doctypeMatch) {
|
|
60362
60362
|
if (handler.doctype) {
|
|
@@ -60367,7 +60367,7 @@
|
|
|
60367
60367
|
continue;
|
|
60368
60368
|
}
|
|
60369
60369
|
|
|
60370
|
-
// End tag
|
|
60370
|
+
// End tag
|
|
60371
60371
|
const endTagMatch = html.match(endTag);
|
|
60372
60372
|
if (endTagMatch) {
|
|
60373
60373
|
html = html.substring(endTagMatch[0].length);
|
|
@@ -60376,7 +60376,7 @@
|
|
|
60376
60376
|
continue;
|
|
60377
60377
|
}
|
|
60378
60378
|
|
|
60379
|
-
// Start tag
|
|
60379
|
+
// Start tag
|
|
60380
60380
|
const startTagMatch = parseStartTag(html);
|
|
60381
60381
|
if (startTagMatch) {
|
|
60382
60382
|
html = startTagMatch.rest;
|
|
@@ -60469,11 +60469,41 @@
|
|
|
60469
60469
|
}
|
|
60470
60470
|
}
|
|
60471
60471
|
|
|
60472
|
-
|
|
60473
|
-
|
|
60474
|
-
|
|
60472
|
+
function findTagInCurrentTable(tagName) {
|
|
60473
|
+
let pos;
|
|
60474
|
+
const needle = tagName.toLowerCase();
|
|
60475
|
+
for (pos = stack.length - 1; pos >= 0; pos--) {
|
|
60476
|
+
const currentTag = stack[pos].tag.toLowerCase();
|
|
60477
|
+
if (currentTag === needle) {
|
|
60478
|
+
return pos;
|
|
60479
|
+
}
|
|
60480
|
+
// Stop searching if we hit a table boundary
|
|
60481
|
+
if (currentTag === 'table') {
|
|
60482
|
+
break;
|
|
60483
|
+
}
|
|
60484
|
+
}
|
|
60485
|
+
return -1;
|
|
60486
|
+
}
|
|
60487
|
+
|
|
60488
|
+
async function parseEndTagAt(pos) {
|
|
60489
|
+
// Close all open elements up to pos (mirrors parseEndTag’s core branch)
|
|
60490
|
+
for (let i = stack.length - 1; i >= pos; i--) {
|
|
60491
|
+
if (handler.end) {
|
|
60492
|
+
await handler.end(stack[i].tag, stack[i].attrs, true);
|
|
60493
|
+
}
|
|
60494
|
+
}
|
|
60495
|
+
stack.length = pos;
|
|
60496
|
+
lastTag = pos && stack[pos - 1].tag;
|
|
60497
|
+
}
|
|
60498
|
+
|
|
60499
|
+
async function closeIfFoundInCurrentTable(tagName) {
|
|
60500
|
+
const pos = findTagInCurrentTable(tagName);
|
|
60501
|
+
if (pos >= 0) {
|
|
60502
|
+
// Close at the specific index to avoid re-searching
|
|
60503
|
+
await parseEndTagAt(pos);
|
|
60475
60504
|
return true;
|
|
60476
60505
|
}
|
|
60506
|
+
return false;
|
|
60477
60507
|
}
|
|
60478
60508
|
|
|
60479
60509
|
async function handleStartTag(match) {
|
|
@@ -60484,10 +60514,15 @@
|
|
|
60484
60514
|
if (lastTag === 'p' && nonPhrasing.has(tagName)) {
|
|
60485
60515
|
await parseEndTag('', lastTag);
|
|
60486
60516
|
} else if (tagName === 'tbody') {
|
|
60487
|
-
await
|
|
60517
|
+
await closeIfFoundInCurrentTable('thead');
|
|
60488
60518
|
} else if (tagName === 'tfoot') {
|
|
60489
|
-
if (!await
|
|
60490
|
-
await
|
|
60519
|
+
if (!await closeIfFoundInCurrentTable('tbody')) {
|
|
60520
|
+
await closeIfFoundInCurrentTable('thead');
|
|
60521
|
+
}
|
|
60522
|
+
} else if (tagName === 'thead') {
|
|
60523
|
+
// If a `tbody` or `tfoot` is open in the current table, close it
|
|
60524
|
+
if (!await closeIfFoundInCurrentTable('tbody')) {
|
|
60525
|
+
await closeIfFoundInCurrentTable('tfoot');
|
|
60491
60526
|
}
|
|
60492
60527
|
}
|
|
60493
60528
|
if (tagName === 'col' && findTag('colgroup') < 0) {
|