Skip to content

Commit 9ecabca

Browse files
authored
handle empty class attributes (#922)
fixes #921
1 parent a5cb4ea commit 9ecabca

File tree

2 files changed

+28
-21
lines changed

2 files changed

+28
-21
lines changed

src/htmlminifier.js

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ var UglifyJS = require('uglify-js');
99
var utils = require('./utils');
1010

1111
function trimWhitespace(str) {
12-
if (typeof str !== 'string') {
13-
return str;
14-
}
15-
return str.replace(/^[ \n\r\t\f]+/, '').replace(/[ \n\r\t\f]+$/, '');
12+
return str && str.replace(/^[ \n\r\t\f]+/, '').replace(/[ \n\r\t\f]+$/, '');
1613
}
1714

1815
function collapseWhitespaceAll(str) {
@@ -258,7 +255,7 @@ function isSrcset(attrName, tag) {
258255
}
259256

260257
function cleanAttributeValue(tag, attrName, attrValue, options, attrs) {
261-
if (attrValue && isEventAttribute(attrName, options)) {
258+
if (isEventAttribute(attrName, options)) {
262259
attrValue = trimWhitespace(attrValue).replace(/^javascript:\s*/i, '');
263260
return options.minifyJS(attrValue, true);
264261
}
@@ -314,7 +311,7 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs) {
314311
return (+numString).toString();
315312
});
316313
}
317-
else if (attrValue && options.customAttrCollapse && options.customAttrCollapse.test(attrName)) {
314+
else if (options.customAttrCollapse && options.customAttrCollapse.test(attrName)) {
318315
attrValue = attrValue.replace(/\n+|\r+|\s{2,}/g, '');
319316
}
320317
else if (tag === 'script' && attrName === 'type') {
@@ -522,7 +519,7 @@ function canTrimWhitespace(tag) {
522519
}
523520

524521
function normalizeAttr(attr, attrs, tag, options) {
525-
var attrName = options.caseSensitive ? attr.name : attr.name.toLowerCase(),
522+
var attrName = options.name(attr.name),
526523
attrValue = attr.value;
527524

528525
if (options.decodeEntities && attrValue) {
@@ -538,7 +535,9 @@ function normalizeAttr(attr, attrs, tag, options) {
538535
return;
539536
}
540537

541-
attrValue = cleanAttributeValue(tag, attrName, attrValue, options, attrs);
538+
if (attrValue) {
539+
attrValue = cleanAttributeValue(tag, attrName, attrValue, options, attrs);
540+
}
542541

543542
if (options.removeEmptyAttributes &&
544543
canDeleteEmptyAttribute(tag, attrName, attrValue, options)) {
@@ -615,6 +614,9 @@ function identity(value) {
615614

616615
function processOptions(values) {
617616
var options = {
617+
name: function(name) {
618+
return name.toLowerCase();
619+
},
618620
canCollapseWhitespace: canCollapseWhitespace,
619621
canTrimWhitespace: canTrimWhitespace,
620622
html5: true,
@@ -631,7 +633,12 @@ function processOptions(values) {
631633
};
632634
Object.keys(values).forEach(function(key) {
633635
var value = values[key];
634-
if (key === 'log') {
636+
if (key === 'caseSensitive') {
637+
if (value) {
638+
options.name = identity;
639+
}
640+
}
641+
else if (key === 'log') {
635642
if (typeof value === 'function') {
636643
options.log = value;
637644
}
@@ -732,7 +739,7 @@ function createSortFns(value, options, uidIgnore, uidAttr) {
732739

733740
function attrNames(attrs) {
734741
return attrs.map(function(attr) {
735-
return options.caseSensitive ? attr.name : attr.name.toLowerCase();
742+
return options.name(attr.name);
736743
});
737744
}
738745

@@ -756,7 +763,7 @@ function createSortFns(value, options, uidIgnore, uidAttr) {
756763
}
757764
for (var i = 0, len = attrs.length; i < len; i++) {
758765
var attr = attrs[i];
759-
if (classChain && (options.caseSensitive ? attr.name : attr.name.toLowerCase()) === 'class') {
766+
if (classChain && attr.value && options.name(attr.name) === 'class') {
760767
classChain.add(trimWhitespace(attr.value).split(/[ \t\n\f\r]+/).filter(shouldSkipUIDs));
761768
}
762769
else if (options.processScripts && attr.name.toLowerCase() === 'type') {
@@ -946,16 +953,13 @@ function minify(value, options, partialMarkup) {
946953
html5: options.html5,
947954

948955
start: function(tag, attrs, unary, unarySlash, autoGenerated) {
949-
var lowerTag = tag.toLowerCase();
950-
951-
if (lowerTag === 'svg') {
956+
if (tag.toLowerCase() === 'svg') {
952957
options = Object.create(options);
953-
options.keepClosingSlash = true;
954958
options.caseSensitive = true;
959+
options.keepClosingSlash = true;
960+
options.name = identity;
955961
}
956-
957-
tag = options.caseSensitive ? tag : lowerTag;
958-
962+
tag = options.name(tag);
959963
currentTag = tag;
960964
charsPrevTag = tag;
961965
if (!inlineTextTags(tag)) {
@@ -1035,11 +1039,10 @@ function minify(value, options, partialMarkup) {
10351039
}
10361040
},
10371041
end: function(tag, attrs, autoGenerated) {
1038-
var lowerTag = tag.toLowerCase();
1039-
if (lowerTag === 'svg') {
1042+
if (tag.toLowerCase() === 'svg') {
10401043
options = Object.getPrototypeOf(options);
10411044
}
1042-
tag = options.caseSensitive ? tag : lowerTag;
1045+
tag = options.name(tag);
10431046

10441047
// check if current tag is in a whitespace stack
10451048
if (options.collapseWhitespace) {

tests/minifier.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3229,6 +3229,10 @@ QUnit.test('sort style classes', function(assert) {
32293229
removeAttributeQuotes: true,
32303230
sortClassName: true
32313231
}), output);
3232+
3233+
input = '<div class></div>';
3234+
assert.equal(minify(input, { sortClassName: false }), input);
3235+
assert.equal(minify(input, { sortClassName: true }), input);
32323236
});
32333237

32343238
QUnit.test('decode entity characters', function(assert) {

0 commit comments

Comments
 (0)