Improves selector traversal in advanced merging.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Tue, 10 Feb 2015 07:31:00 +0000 (07:31 +0000)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Tue, 10 Feb 2015 20:43:29 +0000 (20:43 +0000)
Selectors can be reordered if one of the following rules regarding
any pair of its properties is met:

* Property name root (`font` in `font-size`) is different, e.g.
  `margin:0` <-> `color:red`
* Property name root, name, and value are the same, e.g.
  `color:red` <-> `color:red`
  `margin-top:0` <-> `margin-top:0`
* Property name root is the same, names are different, and any name
  is not root, e.g
  `margin-top:0` <-> `margin-bottom:10px`

lib/selectors/optimizers/advanced.js
test/fixtures/big-min.css
test/integration-test.js
test/media-queries-test.js

index 11c4d45..4cd6fb3 100644 (file)
@@ -30,9 +30,13 @@ function allProperties(token) {
     for (var i = token.metadata.bodiesList.length - 1; i >= 0; i--) {
       var property = token.metadata.bodiesList[i];
       var splitAt = property.indexOf(':');
+      var name = property.substring(0, splitAt);
+      var nameRoot = name.match(/([a-z]+)/)[0];
+
       properties.push([
-        property.substring(0, splitAt).match(/([a-z]+)/)[0],
-        property.substring(splitAt + 1)
+        name,
+        property.substring(splitAt + 1),
+        nameRoot
       ]);
     }
   } else if (token.kind == 'block') {
@@ -49,11 +53,19 @@ function canReorder(left, right) {
     for (var j = left.length - 1; j >= 0; j--) {
       var rightName = right[i][0];
       var rightValue = right[i][1];
+      var rightNameRoot = right[i][2];
       var leftName = left[j][0];
       var leftValue = left[j][1];
+      var leftNameRoot = left[j][2];
+
+      if (rightNameRoot != leftNameRoot)
+        continue;
+      if (rightName == leftName && rightNameRoot == leftNameRoot && rightValue == leftValue)
+        continue;
+      if (rightName != leftName && rightNameRoot == leftNameRoot && rightName != rightNameRoot && leftName != leftNameRoot)
+        continue;
 
-      if (rightName == leftName && rightValue != leftValue)
-        return false;
+      return false;
     }
   }
 
@@ -334,7 +346,9 @@ AdvancedOptimizer.prototype.mergeNonAdjacentBySelector = function (tokens) {
 
         if (tokens[k].kind == 'selector') {
           var traversedProperties = allProperties(tokens[k]);
-          if (!canReorder(movedProperties, traversedProperties))
+
+          // traversed then moved as we move selectors towards the start
+          if (!canReorder(traversedProperties, movedProperties))
             continue selectorIterator;
         }
       }
@@ -403,6 +417,8 @@ AdvancedOptimizer.prototype.mergeMediaQueries = function (tokens) {
 
       for (var k = positions[j] + 1; k < positions[j - 1]; k++) {
         var traversedProperties = allProperties(tokens[k]);
+
+        // moved then traversed as we move @media towards the end
         if (!canReorder(movedProperties, traversedProperties))
           continue positionLoop;
       }
index 3ec3640..5023e4f 100644 (file)
@@ -1720,7 +1720,7 @@ body.iframe{padding-top:0}
 #header-liberation .header-base .links{display:block;width:280px;height:110px;padding-top:10px}
 #header-liberation .header-base .links .lnk1,#header-liberation .header-base .links .lnk2{float:left}
 #header-liberation .header-base .links .lnk1{position:relative;width:123px}
-#header-liberation .header-base .links .lnk2{width:136px;padding-left:21px}
+#header-liberation .header-base .links .lnk2{width:136px;padding-left:21px;box-shadow:-9px 0 10px -11px #6c6c6c;-moz-box-shadow:-9px 0 10px -11px #6c6c6c;-webkit-box-shadow:-9px 0 10px -11px #6c6c6c}
 #header-liberation .header-base .links h2{margin-bottom:3px}
 #header-liberation .header-base .links ul li{margin-bottom:1px;height:13px}
 #header-liberation .header-base .digitalpaper{position:relative;float:right;width:196px}
@@ -2638,7 +2638,6 @@ body.slideshow .ad-top .megaban{background:#333}
 .site-liberation .hot-topics li{background:#fff}
 .site-liberation .hot-topics .event{background:#b1b1b1}
 .site-liberation #footer-liberation .hot-topics .event a{color:#fff}
-#header-liberation .header-base .links .lnk2{box-shadow:-9px 0 10px -11px #6c6c6c;-moz-box-shadow:-9px 0 10px -11px #6c6c6c;-webkit-box-shadow:-9px 0 10px -11px #6c6c6c}
 #header-liberation .header-base .digitalpaper a.abo,#header-liberation .header-base .digitalpaper a.dl{color:#fff}
 #header-liberation .header-base .digitalpaper a.abo{background-color:#f2f2f2;color:#2e2e2e}
 #header-liberation .header-base .digitalpaper a.une{box-shadow:0 -1px 7px 0 grey;-webkit-box-shadow:0 -1px 7px 0 grey;-moz-box-shadow:0 -1px 7px 0 grey}
index 70f1dc6..e4adadd 100644 (file)
@@ -1806,6 +1806,10 @@ title']{display:block}",
       '.one{color:red;margin:0}.two{font:12px serif}.one{font-size:12px}',
       '.one{color:red;margin:0}.two{font:12px serif}.one{font-size:12px}'
     ],
+    'with granular selectors from the same shorthand': [
+      '.one{color:red;margin:0}.two{font-weight:700}.one{font-size:12px}',
+      '.one{color:red;margin:0;font-size:12px}.two{font-weight:700}'
+    ],
     'with three intermediate, non-overriding selectors': [
       '.one{color:red;margin:0}.two{color:#fff}.one{font-size:12px}.three{color:#000}.one{padding:0}',
       '.one{color:red;margin:0;font-size:12px;padding:0}.two{color:#fff}.three{color:#000}'
index abeff2e..4d411c2 100644 (file)
@@ -72,6 +72,12 @@ vows.describe('media queries')
         assert.equal(minified.styles, '@media screen{a{font-size:10px}}@media (min-width:1024px){.one{font:12px Helvetica}}@media screen{div{display:block}}');
       }
     },
+    'same two with different component property in between': {
+      topic: new CleanCSS().minify('@media screen{a{font-size:10px}}@media (min-width:1024px){.one{font-weight:700}}@media screen{div{display:block}}'),
+      'get merged': function(minified) {
+        assert.equal(minified.styles, '@media (min-width:1024px){.one{font-weight:700}}@media screen{a{font-size:10px}div{display:block}}');
+      }
+    },
     'same two with same values as moved in between': {
       topic: new CleanCSS().minify('@media screen{a{color:red}}@media (min-width:1024px){.one{color:red}}@media screen{div{display:block}}'),
       'get merged': function(minified) {