Fix stackNoTrimWhitespace and stackNoCollapseWhitespace housekeeping
authorAndreas Lind <andreas@one.com>
Wed, 24 May 2017 13:26:49 +0000 (15:26 +0200)
committerAndreas Lind <andreas@one.com>
Thu, 25 May 2017 06:35:10 +0000 (08:35 +0200)
Previously the first encountered end tag matching the top item would
clear it from the stack, regardless of whether the two were actually
a start + end pair.

src/htmlminifier.js
tests/minifier.js

index 5b9c9cf..d52498f 100644 (file)
@@ -998,10 +998,10 @@ function minify(value, options, partialMarkup) {
         if (!stackNoTrimWhitespace.length) {
           squashTrailingWhitespace(tag);
         }
-        if (!_canTrimWhitespace(tag, attrs)) {
+        if (!_canTrimWhitespace(tag, attrs) || stackNoTrimWhitespace.length) {
           stackNoTrimWhitespace.push(tag);
         }
-        if (!_canCollapseWhitespace(tag, attrs)) {
+        if (!_canCollapseWhitespace(tag, attrs) || stackNoCollapseWhitespace.length) {
           stackNoCollapseWhitespace.push(tag);
         }
       }
index c826396..bb38c25 100644 (file)
@@ -3245,3 +3245,43 @@ QUnit.test('tests from PHPTAL', function(assert) {
     }), tokens[0]);
   });
 });
+
+QUnit.test('canCollapseWhitespace and canTrimWhitespace hooks', function(assert) {
+  function canCollapseAndTrimWhitespace(tagName, attrs, defaultFn) {
+    if ((attrs || []).some(function(attr) { return attr.name === 'class' && attr.value === 'leaveAlone'; })) {
+      return false;
+    }
+    return defaultFn(tagName, attrs);
+  }
+
+  var input = '<div class="leaveAlone"><span> </span> foo  bar</div>';
+  var output = '<div class="leaveAlone"><span> </span> foo  bar</div>';
+
+  assert.equal(minify(input, {
+    collapseWhitespace: true,
+    canTrimWhitespace: canCollapseAndTrimWhitespace,
+    canCollapseWhitespace: canCollapseAndTrimWhitespace
+  }), output);
+
+  // Regression test: Previously the first </div> would clear the internal
+  // stackNo{Collapse,Trim}Whitespace, so that ' foo  bar' turned into ' foo bar'
+  input = '<div class="leaveAlone"><div></div><span> </span> foo  bar</div>';
+  output = '<div class="leaveAlone"><div></div><span> </span> foo  bar</div>';
+
+  assert.equal(minify(input, {
+    collapseWhitespace: true,
+    canTrimWhitespace: canCollapseAndTrimWhitespace,
+    canCollapseWhitespace: canCollapseAndTrimWhitespace
+  }), output);
+
+  // Make sure that the stack does get reset when leaving the element for which
+  // the hooks returned fales:
+  input = '<div class="leaveAlone"></div><div> foo  bar </div>';
+  output = '<div class="leaveAlone"></div><div>foo bar</div>';
+
+  assert.equal(minify(input, {
+    collapseWhitespace: true,
+    canTrimWhitespace: canCollapseAndTrimWhitespace,
+    canCollapseWhitespace: canCollapseAndTrimWhitespace
+  }), output);
+});