From 1eec64024906b1d3be074b090eaabf83606c2965 Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Sat, 16 Apr 2016 20:15:51 +0800 Subject: [PATCH] improve trailing whitespace minification --- src/htmlminifier.js | 56 +++++++++++++++++++--------------------- tests/minifier.js | 63 +++++++++++++++++++++++++++++++-------------- 2 files changed, 69 insertions(+), 50 deletions(-) diff --git a/src/htmlminifier.js b/src/htmlminifier.js index 0418217..f3db4f5 100644 --- a/src/htmlminifier.js +++ b/src/htmlminifier.js @@ -850,30 +850,32 @@ function minify(value, options, partialMarkup) { buffer.length = Math.max(0, index); } + // look for trailing whitespaces, bypass any inline tags + function trimTrailingWhitespace(index, nextTag) { + for (var endTag = null; index >= 0 && _canTrimWhitespace(endTag); index--) { + var str = buffer[index]; + var match = str.match(/^<\/([\w:-]+)>$/); + if (match) { + endTag = match[1]; + } + else if (/>$/.test(str) || (buffer[index] = collapseWhitespaceSmart(str, null, nextTag, options))) { + break; + } + } + } + // look for trailing whitespaces from previously processed text // which may not be trimmed due to a following comment or an empty // element which has now been removed function squashTrailingWhitespace(nextTag) { - var charsIndex; - if (buffer.length > 1 && /^(?: 0 && /\s$/.test(buffer[buffer.length - 1])) { - charsIndex = buffer.length - 1; - } - if (charsIndex >= 0) { - buffer[charsIndex] = buffer[charsIndex].replace(/\s+$/, function(text) { - return collapseWhitespaceSmart(text, 'comment', nextTag, options); - }); - } - } - - function trimPrevCharsTrailingWhitespace() { - var charsIndex = buffer.length - 2; - if (charsIndex >= 0) { - buffer[charsIndex] = collapseWhitespace(buffer[charsIndex], options, false, true); + var charsIndex = buffer.length - 1; + if (buffer.length > 1) { + var item = buffer[buffer.length - 1]; + if (/^(?:= 0 && _canTrimWhitespace(endTag); index--) { - var str = buffer[index]; - var match = str.match(/^<\/([\w:-]+)>$/); - if (match) { - endTag = match[1]; - } - else if (/>$/.test(str) || (buffer[index] = collapseWhitespaceSmart(str, null, nextTag, options))) { - break; - } - } + trimTrailingWhitespace(buffer.length - 1, nextTag); } } if (!stackNoCollapseWhitespace.length) { @@ -1182,6 +1175,9 @@ function minify(value, options, partialMarkup) { removeEndTag(); } } + if (options.collapseWhitespace) { + squashTrailingWhitespace('br'); + } var str = joinResultSegments(buffer, options); diff --git a/tests/minifier.js b/tests/minifier.js index e07221d..0ca58cf 100644 --- a/tests/minifier.js +++ b/tests/minifier.js @@ -152,6 +152,14 @@ test('space normalization around text', function() { 'mark', 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'time', 'tt', 'u', 'var' ].forEach(function(el) { + equal(minify('foo <' + el + '>baz bar', { collapseWhitespace: true }), 'foo <' + el + '>baz bar'); + equal(minify('foo<' + el + '>bazbar', { collapseWhitespace: true }), 'foo<' + el + '>bazbar'); + equal(minify('foo <' + el + '>bazbar', { collapseWhitespace: true }), 'foo <' + el + '>bazbar'); + equal(minify('foo<' + el + '>baz bar', { collapseWhitespace: true }), 'foo<' + el + '>baz bar'); + equal(minify('foo <' + el + '> baz bar', { collapseWhitespace: true }), 'foo <' + el + '>baz bar'); + equal(minify('foo<' + el + '> baz bar', { collapseWhitespace: true }), 'foo<' + el + '> baz bar'); + equal(minify('foo <' + el + '> baz bar', { collapseWhitespace: true }), 'foo <' + el + '>baz bar'); + equal(minify('foo<' + el + '> baz bar', { collapseWhitespace: true }), 'foo<' + el + '> baz bar'); equal(minify('
foo <' + el + '>baz bar
', { collapseWhitespace: true }), '
foo <' + el + '>baz bar
'); equal(minify('
foo<' + el + '>bazbar
', { collapseWhitespace: true }), '
foo<' + el + '>bazbar
'); equal(minify('
foo <' + el + '>bazbar
', { collapseWhitespace: true }), '
foo <' + el + '>bazbar
'); @@ -164,6 +172,14 @@ test('space normalization around text', function() { [ 'bdi', 'bdo', 'button', 'cite', 'code', 'dfn', 'math', 'q', 'rt', 'rp', 'svg' ].forEach(function(el) { + equal(minify('foo <' + el + '>baz bar', { collapseWhitespace: true }), 'foo <' + el + '>baz bar'); + equal(minify('foo<' + el + '>bazbar', { collapseWhitespace: true }), 'foo<' + el + '>bazbar'); + equal(minify('foo <' + el + '>bazbar', { collapseWhitespace: true }), 'foo <' + el + '>bazbar'); + equal(minify('foo<' + el + '>baz bar', { collapseWhitespace: true }), 'foo<' + el + '>baz bar'); + equal(minify('foo <' + el + '> baz bar', { collapseWhitespace: true }), 'foo <' + el + '>baz bar'); + equal(minify('foo<' + el + '> baz bar', { collapseWhitespace: true }), 'foo<' + el + '>bazbar'); + equal(minify('foo <' + el + '> baz bar', { collapseWhitespace: true }), 'foo <' + el + '>bazbar'); + equal(minify('foo<' + el + '> baz bar', { collapseWhitespace: true }), 'foo<' + el + '>baz bar'); equal(minify('
foo <' + el + '>baz bar
', { collapseWhitespace: true }), '
foo <' + el + '>baz bar
'); equal(minify('
foo<' + el + '>bazbar
', { collapseWhitespace: true }), '
foo<' + el + '>bazbar
'); equal(minify('
foo <' + el + '>bazbar
', { collapseWhitespace: true }), '
foo <' + el + '>bazbar
'); @@ -173,26 +189,33 @@ test('space normalization around text', function() { equal(minify('
foo <' + el + '> baz bar
', { collapseWhitespace: true }), '
foo <' + el + '>bazbar
'); equal(minify('
foo<' + el + '> baz bar
', { collapseWhitespace: true }), '
foo<' + el + '>baz bar
'); }); - equal(minify('a', { collapseWhitespace: true }), 'a'); - equal(minify('a ', { collapseWhitespace: true }), 'a '); - equal(minify(' a', { collapseWhitespace: true }), 'a'); - equal(minify(' a ', { collapseWhitespace: true }), 'a '); - equal(minify('abc', { collapseWhitespace: true }), 'abc'); - equal(minify('ab c', { collapseWhitespace: true }), 'ab c'); - equal(minify('a bc', { collapseWhitespace: true }), 'a bc'); - equal(minify('a b c', { collapseWhitespace: true }), 'a b c'); - equal(minify('ab c', { collapseWhitespace: true }), 'ab c'); - equal(minify('ab c', { collapseWhitespace: true }), 'ab c'); - equal(minify('a b c', { collapseWhitespace: true }), 'a b c'); - equal(minify('a b c', { collapseWhitespace: true }), 'a b c'); - equal(minify('a bc', { collapseWhitespace: true }), 'a bc'); - equal(minify('a b c', { collapseWhitespace: true }), 'a b c'); - equal(minify('a bc', { collapseWhitespace: true }), 'a bc'); - equal(minify('a b c', { collapseWhitespace: true }), 'a b c'); - equal(minify('a b c', { collapseWhitespace: true }), 'a b c'); - equal(minify('a b c', { collapseWhitespace: true }), 'a b c'); - equal(minify('a b c', { collapseWhitespace: true }), 'a b c'); - equal(minify('a b c', { collapseWhitespace: true }), 'a b c'); + [ + [' foo ', 'foo'], + [' foo ', 'foo'], + ['a', 'a'], + ['a ', 'a'], + [' a', 'a'], + [' a ', 'a'], + ['abc', 'abc'], + ['ab c', 'ab c'], + ['a bc', 'a bc'], + ['a b c', 'a b c'], + ['ab c', 'ab c'], + ['ab c', 'ab c'], + ['a b c', 'a b c'], + ['a b c', 'a b c'], + ['a bc', 'a bc'], + ['a b c', 'a b c'], + ['a bc', 'a bc'], + ['a b c', 'a b c'], + ['a b c', 'a b c'], + ['a b c', 'a b c'], + ['a b c', 'a b c'], + ['a b c', 'a b c'] + ].forEach(function(inputs) { + equal(minify(inputs[0], { collapseWhitespace: true }), inputs[1]); + equal(minify('
' + inputs[0] + '
', { collapseWhitespace: true }), '
' + inputs[1] + '
'); + }); equal(minify('

foo bar

', { collapseWhitespace: true }), '

foo bar

'); equal(minify('

foobar

', { collapseWhitespace: true }), '

foobar

'); equal(minify('

foo bar

', { collapseWhitespace: true }), '

foo bar

'); -- 2.34.1