html-minifier 1.4.0 → 1.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 +42 -34
- package/cli.js +218 -282
- package/package.json +4 -8
- package/sample-cli-config-file.conf +27 -18
- package/src/htmllint.js +67 -55
- package/src/htmlminifier.js +223 -114
- package/src/htmlparser.js +132 -130
- package/src/tokenchain.js +58 -0
- package/CHANGELOG.md +0 -120
- package/dist/htmlminifier.js +0 -32753
- package/dist/htmlminifier.min.js +0 -18
package/src/htmlminifier.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var CleanCSS = require('clean-css');
|
|
4
|
+
var decode = require('he').decode;
|
|
4
5
|
var HTMLParser = require('./htmlparser').HTMLParser;
|
|
5
6
|
var RelateUrl = require('relateurl');
|
|
7
|
+
var TokenChain = require('./tokenchain');
|
|
6
8
|
var UglifyJS = require('uglify-js');
|
|
7
9
|
var utils = require('./utils');
|
|
8
10
|
|
|
@@ -17,20 +19,17 @@ else {
|
|
|
17
19
|
log = function() {};
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
var trimWhitespace = function(str) {
|
|
22
|
+
var trimWhitespace = String.prototype.trim ? function(str) {
|
|
23
|
+
if (typeof str !== 'string') {
|
|
24
|
+
return str;
|
|
25
|
+
}
|
|
26
|
+
return str.trim();
|
|
27
|
+
} : function(str) {
|
|
21
28
|
if (typeof str !== 'string') {
|
|
22
29
|
return str;
|
|
23
30
|
}
|
|
24
31
|
return str.replace(/^\s+/, '').replace(/\s+$/, '');
|
|
25
32
|
};
|
|
26
|
-
if (String.prototype.trim) {
|
|
27
|
-
trimWhitespace = function(str) {
|
|
28
|
-
if (typeof str !== 'string') {
|
|
29
|
-
return str;
|
|
30
|
-
}
|
|
31
|
-
return str.trim();
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
33
|
|
|
35
34
|
function compressWhitespace(spaces) {
|
|
36
35
|
return spaces === '\t' ? spaces : ' ';
|
|
@@ -119,14 +118,12 @@ function isEventAttribute(attrName, options) {
|
|
|
119
118
|
}
|
|
120
119
|
return false;
|
|
121
120
|
}
|
|
122
|
-
|
|
123
|
-
return (/^on[a-z]{3,}$/).test(attrName);
|
|
124
|
-
}
|
|
121
|
+
return /^on[a-z]{3,}$/.test(attrName);
|
|
125
122
|
}
|
|
126
123
|
|
|
127
124
|
function canRemoveAttributeQuotes(value) {
|
|
128
125
|
// http://mathiasbynens.be/notes/unquoted-attribute-values
|
|
129
|
-
return
|
|
126
|
+
return /^[^ \t\n\f\r"'`=<>]+$/.test(value);
|
|
130
127
|
}
|
|
131
128
|
|
|
132
129
|
function attributesInclude(attributes, attribute) {
|
|
@@ -142,29 +139,29 @@ function isAttributeRedundant(tag, attrName, attrValue, attrs) {
|
|
|
142
139
|
attrValue = attrValue ? trimWhitespace(attrValue.toLowerCase()) : '';
|
|
143
140
|
|
|
144
141
|
return (
|
|
145
|
-
|
|
142
|
+
tag === 'script' &&
|
|
146
143
|
attrName === 'language' &&
|
|
147
|
-
attrValue === 'javascript'
|
|
144
|
+
attrValue === 'javascript' ||
|
|
148
145
|
|
|
149
|
-
|
|
146
|
+
tag === 'form' &&
|
|
150
147
|
attrName === 'method' &&
|
|
151
|
-
attrValue === 'get'
|
|
148
|
+
attrValue === 'get' ||
|
|
152
149
|
|
|
153
|
-
|
|
150
|
+
tag === 'input' &&
|
|
154
151
|
attrName === 'type' &&
|
|
155
|
-
attrValue === 'text'
|
|
152
|
+
attrValue === 'text' ||
|
|
156
153
|
|
|
157
|
-
|
|
154
|
+
tag === 'script' &&
|
|
158
155
|
attrName === 'charset' &&
|
|
159
|
-
!attributesInclude(attrs, 'src')
|
|
156
|
+
!attributesInclude(attrs, 'src') ||
|
|
160
157
|
|
|
161
|
-
|
|
158
|
+
tag === 'a' &&
|
|
162
159
|
attrName === 'name' &&
|
|
163
|
-
attributesInclude(attrs, 'id')
|
|
160
|
+
attributesInclude(attrs, 'id') ||
|
|
164
161
|
|
|
165
|
-
|
|
162
|
+
tag === 'area' &&
|
|
166
163
|
attrName === 'shape' &&
|
|
167
|
-
attrValue === 'rect'
|
|
164
|
+
attrValue === 'rect'
|
|
168
165
|
);
|
|
169
166
|
}
|
|
170
167
|
|
|
@@ -179,12 +176,9 @@ var executableScriptsMimetypes = utils.createMap([
|
|
|
179
176
|
'application/ecmascript'
|
|
180
177
|
]);
|
|
181
178
|
|
|
182
|
-
function isScriptTypeAttribute(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
attrName === 'type' &&
|
|
186
|
-
executableScriptsMimetypes(trimWhitespace(attrValue.toLowerCase()))
|
|
187
|
-
);
|
|
179
|
+
function isScriptTypeAttribute(attrValue) {
|
|
180
|
+
attrValue = trimWhitespace(attrValue.split(/;/, 2)[0]).toLowerCase();
|
|
181
|
+
return attrValue === '' || executableScriptsMimetypes(attrValue);
|
|
188
182
|
}
|
|
189
183
|
|
|
190
184
|
function isExecutableScript(tag, attrs) {
|
|
@@ -194,13 +188,17 @@ function isExecutableScript(tag, attrs) {
|
|
|
194
188
|
for (var i = 0, len = attrs.length; i < len; i++) {
|
|
195
189
|
var attrName = attrs[i].name.toLowerCase();
|
|
196
190
|
if (attrName === 'type') {
|
|
197
|
-
|
|
198
|
-
return attrValue === '' || executableScriptsMimetypes(attrValue);
|
|
191
|
+
return isScriptTypeAttribute(attrs[i].value);
|
|
199
192
|
}
|
|
200
193
|
}
|
|
201
194
|
return true;
|
|
202
195
|
}
|
|
203
196
|
|
|
197
|
+
function isStyleLinkTypeAttribute(attrValue) {
|
|
198
|
+
attrValue = trimWhitespace(attrValue).toLowerCase();
|
|
199
|
+
return attrValue === '' || attrValue === 'text/css';
|
|
200
|
+
}
|
|
201
|
+
|
|
204
202
|
function isStyleSheet(tag, attrs) {
|
|
205
203
|
if (tag !== 'style') {
|
|
206
204
|
return false;
|
|
@@ -208,21 +206,12 @@ function isStyleSheet(tag, attrs) {
|
|
|
208
206
|
for (var i = 0, len = attrs.length; i < len; i++) {
|
|
209
207
|
var attrName = attrs[i].name.toLowerCase();
|
|
210
208
|
if (attrName === 'type') {
|
|
211
|
-
|
|
212
|
-
return attrValue === '' || attrValue === 'text/css';
|
|
209
|
+
return isStyleLinkTypeAttribute(attrs[i].value);
|
|
213
210
|
}
|
|
214
211
|
}
|
|
215
212
|
return true;
|
|
216
213
|
}
|
|
217
214
|
|
|
218
|
-
function isStyleLinkTypeAttribute(tag, attrName, attrValue) {
|
|
219
|
-
return (
|
|
220
|
-
(tag === 'style' || tag === 'link') &&
|
|
221
|
-
attrName === 'type' &&
|
|
222
|
-
trimWhitespace(attrValue.toLowerCase()) === 'text/css'
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
215
|
var isSimpleBoolean = createMapFromString('allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible');
|
|
227
216
|
var isBooleanValue = createMapFromString('true,false');
|
|
228
217
|
|
|
@@ -232,28 +221,28 @@ function isBooleanAttribute(attrName, attrValue) {
|
|
|
232
221
|
|
|
233
222
|
function isUriTypeAttribute(attrName, tag) {
|
|
234
223
|
return (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
224
|
+
/^(?:a|area|link|base)$/.test(tag) && attrName === 'href' ||
|
|
225
|
+
tag === 'img' && /^(?:src|longdesc|usemap)$/.test(attrName) ||
|
|
226
|
+
tag === 'object' && /^(?:classid|codebase|data|usemap)$/.test(attrName) ||
|
|
227
|
+
tag === 'q' && attrName === 'cite' ||
|
|
228
|
+
tag === 'blockquote' && attrName === 'cite' ||
|
|
229
|
+
(tag === 'ins' || tag === 'del') && attrName === 'cite' ||
|
|
230
|
+
tag === 'form' && attrName === 'action' ||
|
|
231
|
+
tag === 'input' && (attrName === 'src' || attrName === 'usemap') ||
|
|
232
|
+
tag === 'head' && attrName === 'profile' ||
|
|
233
|
+
tag === 'script' && (attrName === 'src' || attrName === 'for')
|
|
245
234
|
);
|
|
246
235
|
}
|
|
247
236
|
|
|
248
237
|
function isNumberTypeAttribute(attrName, tag) {
|
|
249
238
|
return (
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
(
|
|
239
|
+
/^(?:a|area|object|button)$/.test(tag) && attrName === 'tabindex' ||
|
|
240
|
+
tag === 'input' && (attrName === 'maxlength' || attrName === 'tabindex') ||
|
|
241
|
+
tag === 'select' && (attrName === 'size' || attrName === 'tabindex') ||
|
|
242
|
+
tag === 'textarea' && /^(?:rows|cols|tabindex)$/.test(attrName) ||
|
|
243
|
+
tag === 'colgroup' && attrName === 'span' ||
|
|
244
|
+
tag === 'col' && attrName === 'span' ||
|
|
245
|
+
(tag === 'th' || tag === 'td') && (attrName === 'rowspan' || attrName === 'colspan')
|
|
257
246
|
);
|
|
258
247
|
}
|
|
259
248
|
|
|
@@ -281,7 +270,14 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs) {
|
|
|
281
270
|
return attrValue;
|
|
282
271
|
}
|
|
283
272
|
else if (attrName === 'class') {
|
|
284
|
-
|
|
273
|
+
attrValue = trimWhitespace(attrValue);
|
|
274
|
+
if (options.sortClassName) {
|
|
275
|
+
attrValue = options.sortClassName(attrValue);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
attrValue = collapseWhitespaceAll(attrValue);
|
|
279
|
+
}
|
|
280
|
+
return attrValue;
|
|
285
281
|
}
|
|
286
282
|
else if (isUriTypeAttribute(attrName, tag)) {
|
|
287
283
|
attrValue = trimWhitespace(attrValue);
|
|
@@ -295,8 +291,8 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs) {
|
|
|
295
291
|
}
|
|
296
292
|
else if (attrName === 'style') {
|
|
297
293
|
attrValue = trimWhitespace(attrValue);
|
|
298
|
-
if (attrValue) {
|
|
299
|
-
attrValue = attrValue.replace(/\s
|
|
294
|
+
if (attrValue && /;$/.test(attrValue) && !/&#?[0-9a-zA-Z]+;$/.test(attrValue)) {
|
|
295
|
+
attrValue = attrValue.replace(/\s*;$/, '');
|
|
300
296
|
}
|
|
301
297
|
if (options.minifyCSS) {
|
|
302
298
|
return minifyCSS(attrValue, options.minifyCSS, true);
|
|
@@ -342,9 +338,7 @@ function unwrapCSS(text) {
|
|
|
342
338
|
if (matches && matches[1]) {
|
|
343
339
|
return matches[1];
|
|
344
340
|
}
|
|
345
|
-
|
|
346
|
-
return text;
|
|
347
|
-
}
|
|
341
|
+
return text;
|
|
348
342
|
}
|
|
349
343
|
|
|
350
344
|
function cleanConditionalComment(comment, options) {
|
|
@@ -453,11 +447,9 @@ var reEmptyAttribute = new RegExp(
|
|
|
453
447
|
'?:down|up|over|move|out)|key(?:press|down|up)))$');
|
|
454
448
|
|
|
455
449
|
function canDeleteEmptyAttribute(tag, attrName, attrValue) {
|
|
456
|
-
var isValueEmpty = !attrValue ||
|
|
450
|
+
var isValueEmpty = !attrValue || /^\s*$/.test(attrValue);
|
|
457
451
|
if (isValueEmpty) {
|
|
458
|
-
return (
|
|
459
|
-
(tag === 'input' && attrName === 'value') ||
|
|
460
|
-
reEmptyAttribute.test(attrName));
|
|
452
|
+
return tag === 'input' && attrName === 'value' || reEmptyAttribute.test(attrName);
|
|
461
453
|
}
|
|
462
454
|
return false;
|
|
463
455
|
}
|
|
@@ -502,50 +494,67 @@ function canRemoveElement(tag, attrs) {
|
|
|
502
494
|
}
|
|
503
495
|
|
|
504
496
|
function canCollapseWhitespace(tag) {
|
|
505
|
-
return
|
|
497
|
+
return !/^(?:script|style|pre|textarea)$/.test(tag);
|
|
506
498
|
}
|
|
507
499
|
|
|
508
500
|
function canTrimWhitespace(tag) {
|
|
509
|
-
return
|
|
501
|
+
return !/^(?:pre|textarea)$/.test(tag);
|
|
510
502
|
}
|
|
511
503
|
|
|
512
|
-
function
|
|
513
|
-
|
|
504
|
+
function normalizeAttr(attr, attrs, tag, options) {
|
|
514
505
|
var attrName = options.caseSensitive ? attr.name : attr.name.toLowerCase(),
|
|
515
|
-
attrValue = attr.value
|
|
516
|
-
attrQuote = attr.quote,
|
|
517
|
-
attrFragment,
|
|
518
|
-
emittedAttrValue;
|
|
506
|
+
attrValue = attr.value;
|
|
519
507
|
|
|
520
|
-
if (
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
||
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
508
|
+
if (options.decodeEntities && attrValue) {
|
|
509
|
+
attrValue = decode(attrValue, { isAttributeValue: true });
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (options.removeRedundantAttributes &&
|
|
513
|
+
isAttributeRedundant(tag, attrName, attrValue, attrs) ||
|
|
514
|
+
options.removeScriptTypeAttributes && tag === 'script' &&
|
|
515
|
+
attrName === 'type' && isScriptTypeAttribute(attrValue) ||
|
|
516
|
+
options.removeStyleLinkTypeAttributes && (tag === 'style' || tag === 'link') &&
|
|
517
|
+
attrName === 'type' && isStyleLinkTypeAttribute(attrValue)) {
|
|
518
|
+
return;
|
|
529
519
|
}
|
|
530
520
|
|
|
531
521
|
attrValue = cleanAttributeValue(tag, attrName, attrValue, options, attrs);
|
|
532
522
|
|
|
533
523
|
if (options.removeEmptyAttributes &&
|
|
534
524
|
canDeleteEmptyAttribute(tag, attrName, attrValue)) {
|
|
535
|
-
return
|
|
525
|
+
return;
|
|
536
526
|
}
|
|
537
527
|
|
|
538
|
-
if (
|
|
528
|
+
if (options.decodeEntities && attrValue) {
|
|
529
|
+
attrValue = attrValue.replace(/&(#?[0-9a-zA-Z]+;)/g, '&$1');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return {
|
|
533
|
+
attr: attr,
|
|
534
|
+
name: attrName,
|
|
535
|
+
value: attrValue
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function buildAttr(normalized, hasUnarySlash, options, isLast) {
|
|
540
|
+
var attrName = normalized.name,
|
|
541
|
+
attrValue = normalized.value,
|
|
542
|
+
attr = normalized.attr,
|
|
543
|
+
attrQuote = attr.quote,
|
|
544
|
+
attrFragment,
|
|
545
|
+
emittedAttrValue;
|
|
546
|
+
|
|
547
|
+
if (typeof attrValue !== 'undefined' && !options.removeAttributeQuotes ||
|
|
539
548
|
!canRemoveAttributeQuotes(attrValue)) {
|
|
540
549
|
if (!options.preventAttributesEscaping) {
|
|
541
|
-
if (options.quoteCharacter
|
|
542
|
-
attrQuote = options.quoteCharacter === '\'' ? '\'' : '"';
|
|
543
|
-
}
|
|
544
|
-
else {
|
|
550
|
+
if (typeof options.quoteCharacter === 'undefined') {
|
|
545
551
|
var apos = (attrValue.match(/'/g) || []).length;
|
|
546
552
|
var quot = (attrValue.match(/"/g) || []).length;
|
|
547
553
|
attrQuote = apos < quot ? '\'' : '"';
|
|
548
554
|
}
|
|
555
|
+
else {
|
|
556
|
+
attrQuote = options.quoteCharacter === '\'' ? '\'' : '"';
|
|
557
|
+
}
|
|
549
558
|
if (attrQuote === '"') {
|
|
550
559
|
attrValue = attrValue.replace(/"/g, '"');
|
|
551
560
|
}
|
|
@@ -566,8 +575,8 @@ function normalizeAttribute(attr, attrs, tag, hasUnarySlash, index, options, isL
|
|
|
566
575
|
emittedAttrValue = attrValue + ' ';
|
|
567
576
|
}
|
|
568
577
|
|
|
569
|
-
if (attrValue === undefined ||
|
|
570
|
-
isBooleanAttribute(attrName.toLowerCase(), attrValue.toLowerCase()))
|
|
578
|
+
if (typeof attrValue === 'undefined' || options.collapseBooleanAttributes &&
|
|
579
|
+
isBooleanAttribute(attrName.toLowerCase(), attrValue.toLowerCase())) {
|
|
571
580
|
attrFragment = attrName;
|
|
572
581
|
if (!isLast) {
|
|
573
582
|
attrFragment += ' ';
|
|
@@ -646,9 +655,7 @@ function minifyCSS(text, options, inline) {
|
|
|
646
655
|
if (inline) {
|
|
647
656
|
return unwrapCSS(cleanCSS.minify(wrapCSS(style)).styles);
|
|
648
657
|
}
|
|
649
|
-
|
|
650
|
-
return cleanCSS.minify(style).styles;
|
|
651
|
-
}
|
|
658
|
+
return cleanCSS.minify(style).styles;
|
|
652
659
|
}
|
|
653
660
|
catch (err) {
|
|
654
661
|
log(err);
|
|
@@ -664,13 +671,96 @@ function uniqueId(value) {
|
|
|
664
671
|
return id;
|
|
665
672
|
}
|
|
666
673
|
|
|
674
|
+
var specialContentTags = createMapFromString('script,style');
|
|
675
|
+
|
|
676
|
+
function createSortFns(value, options, uidIgnore, uidAttr) {
|
|
677
|
+
var attrChains = options.sortAttributes && Object.create(null);
|
|
678
|
+
var classChain = options.sortClassName && new TokenChain();
|
|
679
|
+
|
|
680
|
+
function attrNames(attrs) {
|
|
681
|
+
return attrs.map(function(attr) {
|
|
682
|
+
return options.caseSensitive ? attr.name : attr.name.toLowerCase();
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function shouldSkipUID(token, uid) {
|
|
687
|
+
return !uid || token.indexOf(uid) === -1;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function shouldSkipUIDs(token) {
|
|
691
|
+
return shouldSkipUID(token, uidIgnore) && shouldSkipUID(token, uidAttr);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function scan(input) {
|
|
695
|
+
var currentTag, currentType;
|
|
696
|
+
new HTMLParser(input, {
|
|
697
|
+
start: function(tag, attrs) {
|
|
698
|
+
if (attrChains) {
|
|
699
|
+
if (!attrChains[tag]) {
|
|
700
|
+
attrChains[tag] = new TokenChain();
|
|
701
|
+
}
|
|
702
|
+
attrChains[tag].add(attrNames(attrs).filter(shouldSkipUIDs));
|
|
703
|
+
}
|
|
704
|
+
for (var i = 0, len = attrs.length; i < len; i++) {
|
|
705
|
+
var attr = attrs[i];
|
|
706
|
+
if (classChain && (options.caseSensitive ? attr.name : attr.name.toLowerCase()) === 'class') {
|
|
707
|
+
classChain.add(trimWhitespace(attr.value).split(/\s+/).filter(shouldSkipUIDs));
|
|
708
|
+
}
|
|
709
|
+
else if (options.processScripts && attr.name.toLowerCase() === 'type') {
|
|
710
|
+
currentTag = tag;
|
|
711
|
+
currentType = attr.value;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
end: function() {
|
|
716
|
+
currentTag = '';
|
|
717
|
+
},
|
|
718
|
+
chars: function(text) {
|
|
719
|
+
if (options.processScripts && specialContentTags(currentTag) &&
|
|
720
|
+
options.processScripts.indexOf(currentType) > -1) {
|
|
721
|
+
scan(text);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
options.sortAttributes = false;
|
|
728
|
+
options.sortClassName = false;
|
|
729
|
+
scan(minify(value, options));
|
|
730
|
+
if (attrChains) {
|
|
731
|
+
var attrSorters = Object.create(null);
|
|
732
|
+
for (var tag in attrChains) {
|
|
733
|
+
attrSorters[tag] = attrChains[tag].createSorter();
|
|
734
|
+
}
|
|
735
|
+
options.sortAttributes = function(tag, attrs) {
|
|
736
|
+
var sorter = attrSorters[tag];
|
|
737
|
+
if (sorter) {
|
|
738
|
+
var attrMap = Object.create(null);
|
|
739
|
+
var names = attrNames(attrs);
|
|
740
|
+
names.forEach(function(name, index) {
|
|
741
|
+
(attrMap[name] || (attrMap[name] = [])).push(attrs[index]);
|
|
742
|
+
});
|
|
743
|
+
sorter.sort(names).forEach(function(name, index) {
|
|
744
|
+
attrs[index] = attrMap[name].shift();
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
if (classChain) {
|
|
750
|
+
var sorter = classChain.createSorter();
|
|
751
|
+
options.sortClassName = function(value) {
|
|
752
|
+
return sorter.sort(value.split(/\s+/)).join(' ');
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
667
757
|
function minify(value, options, partialMarkup) {
|
|
668
758
|
options = options || {};
|
|
669
759
|
var optionsStack = [];
|
|
670
760
|
processOptions(options);
|
|
671
761
|
value = options.collapseWhitespace ? trimWhitespace(value) : value;
|
|
672
762
|
|
|
673
|
-
var buffer = [
|
|
763
|
+
var buffer = [],
|
|
674
764
|
charsPrevTag,
|
|
675
765
|
currentChars = '',
|
|
676
766
|
hasChars,
|
|
@@ -682,8 +772,8 @@ function minify(value, options, partialMarkup) {
|
|
|
682
772
|
optionalEndTag = '',
|
|
683
773
|
lint = options.lint,
|
|
684
774
|
t = Date.now(),
|
|
685
|
-
ignoredMarkupChunks = [
|
|
686
|
-
ignoredCustomMarkupChunks = [
|
|
775
|
+
ignoredMarkupChunks = [],
|
|
776
|
+
ignoredCustomMarkupChunks = [],
|
|
687
777
|
uidIgnore,
|
|
688
778
|
uidAttr;
|
|
689
779
|
|
|
@@ -719,6 +809,12 @@ function minify(value, options, partialMarkup) {
|
|
|
719
809
|
});
|
|
720
810
|
}
|
|
721
811
|
|
|
812
|
+
if (options.sortAttributes && typeof options.sortAttributes !== 'function' ||
|
|
813
|
+
options.sortClassName && typeof options.sortClassName !== 'function') {
|
|
814
|
+
createSortFns(value, options, uidIgnore, uidAttr);
|
|
815
|
+
lint = null;
|
|
816
|
+
}
|
|
817
|
+
|
|
722
818
|
function _canCollapseWhitespace(tag, attrs) {
|
|
723
819
|
return canCollapseWhitespace(tag) || options.canCollapseWhitespace(tag, attrs);
|
|
724
820
|
}
|
|
@@ -764,7 +860,7 @@ function minify(value, options, partialMarkup) {
|
|
|
764
860
|
|
|
765
861
|
new HTMLParser(value, {
|
|
766
862
|
partialMarkup: partialMarkup,
|
|
767
|
-
html5: typeof options.html5
|
|
863
|
+
html5: typeof options.html5 === 'undefined' ? true : options.html5,
|
|
768
864
|
|
|
769
865
|
start: function(tag, attrs, unary, unarySlash, autoGenerated) {
|
|
770
866
|
var lowerTag = tag.toLowerCase();
|
|
@@ -834,16 +930,20 @@ function minify(value, options, partialMarkup) {
|
|
|
834
930
|
lint.testElement(tag);
|
|
835
931
|
}
|
|
836
932
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
933
|
+
if (options.sortAttributes) {
|
|
934
|
+
options.sortAttributes(tag, attrs);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
var parts = [];
|
|
938
|
+
for (var i = attrs.length, isLast = true; --i >= 0;) {
|
|
939
|
+
var attr = attrs[i];
|
|
840
940
|
if (lint) {
|
|
841
|
-
lint.testAttribute(tag,
|
|
941
|
+
lint.testAttribute(tag, attr.name.toLowerCase(), attr.value);
|
|
842
942
|
}
|
|
843
|
-
|
|
844
|
-
if (
|
|
943
|
+
var normalized = normalizeAttr(attr, attrs, tag, options);
|
|
944
|
+
if (normalized) {
|
|
945
|
+
parts.unshift(buildAttr(normalized, hasUnarySlash, options, isLast));
|
|
845
946
|
isLast = false;
|
|
846
|
-
parts.unshift(token);
|
|
847
947
|
}
|
|
848
948
|
}
|
|
849
949
|
if (parts.length > 0) {
|
|
@@ -928,6 +1028,9 @@ function minify(value, options, partialMarkup) {
|
|
|
928
1028
|
chars: function(text, prevTag, nextTag) {
|
|
929
1029
|
prevTag = prevTag === '' ? 'comment' : prevTag;
|
|
930
1030
|
nextTag = nextTag === '' ? 'comment' : nextTag;
|
|
1031
|
+
if (options.decodeEntities && text && !specialContentTags(currentTag)) {
|
|
1032
|
+
text = decode(text);
|
|
1033
|
+
}
|
|
931
1034
|
if (options.collapseWhitespace) {
|
|
932
1035
|
if (!stackNoTrimWhitespace.length) {
|
|
933
1036
|
if (prevTag === 'comment') {
|
|
@@ -964,12 +1067,12 @@ function minify(value, options, partialMarkup) {
|
|
|
964
1067
|
text = prevTag && nextTag || nextTag === 'html' ? text : collapseWhitespaceAll(text);
|
|
965
1068
|
}
|
|
966
1069
|
}
|
|
967
|
-
if (options.processScripts && (currentTag
|
|
1070
|
+
if (options.processScripts && specialContentTags(currentTag)) {
|
|
968
1071
|
text = processScript(text, options, currentAttrs);
|
|
969
1072
|
}
|
|
970
1073
|
if (options.minifyJS && isExecutableScript(currentTag, currentAttrs)) {
|
|
971
1074
|
text = minifyJS(text, options.minifyJS);
|
|
972
|
-
if (
|
|
1075
|
+
if (/;$/.test(text)) {
|
|
973
1076
|
text = text.slice(0, -1);
|
|
974
1077
|
}
|
|
975
1078
|
}
|
|
@@ -991,6 +1094,11 @@ function minify(value, options, partialMarkup) {
|
|
|
991
1094
|
optionalEndTag = '';
|
|
992
1095
|
}
|
|
993
1096
|
charsPrevTag = /^\s*$/.test(text) ? prevTag : 'comment';
|
|
1097
|
+
if (options.decodeEntities && text && !specialContentTags(currentTag)) {
|
|
1098
|
+
// semi-colon can be omitted
|
|
1099
|
+
// https://mathiasbynens.be/notes/ambiguous-ampersands
|
|
1100
|
+
text = text.replace(/&(#?[0-9a-zA-Z]+;)/g, '&$1').replace(/</g, '<');
|
|
1101
|
+
}
|
|
994
1102
|
currentChars += text;
|
|
995
1103
|
if (text) {
|
|
996
1104
|
hasChars = true;
|
|
@@ -1025,6 +1133,9 @@ function minify(value, options, partialMarkup) {
|
|
|
1025
1133
|
buffer.push(text);
|
|
1026
1134
|
},
|
|
1027
1135
|
doctype: function(doctype) {
|
|
1136
|
+
if (lint) {
|
|
1137
|
+
lint.testDoctype(doctype);
|
|
1138
|
+
}
|
|
1028
1139
|
buffer.push(options.useShortDoctype ? '<!DOCTYPE html>' : collapseWhitespaceAll(doctype));
|
|
1029
1140
|
},
|
|
1030
1141
|
customAttrAssign: options.customAttrAssign,
|
|
@@ -1060,9 +1171,7 @@ function minify(value, options, partialMarkup) {
|
|
|
1060
1171
|
conservativeCollapse: true
|
|
1061
1172
|
}, /^\s/.test(chunk), /\s$/.test(chunk));
|
|
1062
1173
|
}
|
|
1063
|
-
|
|
1064
|
-
return chunk;
|
|
1065
|
-
}
|
|
1174
|
+
return chunk;
|
|
1066
1175
|
});
|
|
1067
1176
|
}
|
|
1068
1177
|
if (uidIgnore) {
|