Adds merging duplicate properties within a single selector's body.
authorGoalSmashers <jakub@goalsmashers.com>
Mon, 28 Oct 2013 09:28:11 +0000 (10:28 +0100)
committerGoalSmashers <jakub@goalsmashers.com>
Sat, 2 Nov 2013 16:30:38 +0000 (17:30 +0100)
* Skips merging if two properties declared one after another, e.g. display:inline-block;display:-moz-inline-box;
* Respects !important when merging.

History.md
lib/selectors/optimizer.js
test/data/big-min.css
test/data/big.css
test/unit-test.js

index 6c48a01..15f271d 100644 (file)
@@ -9,6 +9,7 @@
 * Fixed issue [#162](https://github.com/GoalSmashers/clean-css/issues/162) - strip quotes from base64 encoded URLs.
 * Adds CSS tokenizer which will make it possible to optimize content by reordering and/or merging selectors.
 * Adds basic optimizer removing duplicate selectors from a list.
+* Adds merging duplicate properties within a single selector's body.
 
 1.1.7 / 2013-10-28
 ==================
index 68de01b..c4f6246 100644 (file)
@@ -15,6 +15,45 @@ module.exports = function Optimizer(data) {
     return plain.join(',');
   };
 
+  var mergeProperties = function(body) {
+    var merged = [];
+    var properties = [];
+    var flat = [];
+    var tokenized = body.split(';');
+    var lastKey = null;
+
+    if (tokenized.length == 1 && tokenized[0] === '')
+      return body;
+
+    for (var i = 0, l = tokenized.length; i < l; i++) {
+      var firstColon = tokenized[i].indexOf(':');
+      var key = tokenized[i].substring(0, firstColon);
+      var value = tokenized[i].substring(firstColon + 1);
+      var alreadyIn = properties.indexOf(key);
+
+      if (alreadyIn > -1 && merged[alreadyIn][1].indexOf('!important') > 0 && value.indexOf('!important') == -1)
+        continue;
+
+      // comment is necessary - we assume that if two keys are one after another
+      // then it is intentional way of redefining property which may not be widely supported
+      if (alreadyIn > -1 && lastKey != key) {
+        merged.splice(alreadyIn, 1);
+        properties.splice(alreadyIn, 1);
+      }
+
+      merged.push([key, value]);
+      properties.push(key);
+
+      lastKey = key;
+    }
+
+    for (var j = 0, m = merged.length; j < m; j++) {
+      flat.push(merged[j].join(':'));
+    }
+
+    return flat.join(';');
+  };
+
   var removeDuplicates = function(tokens) {
     var matched = {};
     var forRemoval = [];
@@ -47,10 +86,12 @@ module.exports = function Optimizer(data) {
     for (var i = 0, l = tokens.length; i < l; i++) {
       var token = tokens[i];
 
-      if (token.selector)
+      if (token.selector) {
         token.selector = stripRepeats(token.selector);
-      if (token.block)
+        token.body = mergeProperties(token.body);
+      } else if (token.block) {
         optimize(token.body);
+      }
     }
 
     removeDuplicates(tokens);
index abadd09..65132e9 100644 (file)
@@ -254,9 +254,9 @@ figure img,article img,.img_bord{border:1px solid #eef1f5;vertical-align:bottom}
 img[width="642"]{margin-bottom:6px}
 img[width="312"]{margin-bottom:6px}
 img[width="202"]{margin-bottom:4px}
-.btn,.btn_fonce,.btn_abo,.btn_petit{display:inline-block;padding:4px 10px;margin-bottom:0;color:#000b15;text-align:center;font-weight:700;vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);border:1px solid #ccc;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);cursor:pointer}
-.btn_fonce,.bt_fonce a{color:#fff;background-color:#000b15;background-image:-moz-linear-gradient(top,#5d666d,#000b15);background-image:-ms-linear-gradient(top,#5d666d,#000b15);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5d666d),to(#000b15));background-image:-webkit-linear-gradient(top,#5d666d,#000b15);background-image:-o-linear-gradient(top,#5d666d,#000b15);background-image:linear-gradient(top,#5d666d,#000b15);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5d666d', endColorstr='#000b15', GradientType=0);border-color:#000b15;border-color:rgba(0,0,0,.1);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}
-.btn_abo{color:#000b15;background-color:#ffc600;background-image:-moz-linear-gradient(top,#ffe562,#ffc600);background-image:-ms-linear-gradient(top,#ffe562,#ffc600);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ffe562),to(#ffc600));background-image:-webkit-linear-gradient(top,#ffe562,#ffc600);background-image:-o-linear-gradient(top,#ffe562,#ffc600);background-image:linear-gradient(top,#ffe562,#ffc600);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe562', endColorstr='#ffc600', GradientType=0);border-color:#ffc600;border-color:rgba(0,0,0,.1);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}
+.btn,.btn_fonce,.btn_abo,.btn_petit{display:inline-block;padding:4px 10px;margin-bottom:0;color:#000b15;text-align:center;font-weight:700;vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);border:1px solid #ccc;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);cursor:pointer}
+.btn_fonce,.bt_fonce a{color:#fff;background-color:#000b15;background-image:-moz-linear-gradient(top,#5d666d,#000b15);background-image:-ms-linear-gradient(top,#5d666d,#000b15);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5d666d),to(#000b15));background-image:-webkit-linear-gradient(top,#5d666d,#000b15);background-image:-o-linear-gradient(top,#5d666d,#000b15);background-image:linear-gradient(top,#5d666d,#000b15);background-repeat:repeat-x;border-color:#000b15;border-color:rgba(0,0,0,.1);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}
+.btn_abo{color:#000b15;background-color:#ffc600;background-image:-moz-linear-gradient(top,#ffe562,#ffc600);background-image:-ms-linear-gradient(top,#ffe562,#ffc600);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ffe562),to(#ffc600));background-image:-webkit-linear-gradient(top,#ffe562,#ffc600);background-image:-o-linear-gradient(top,#ffe562,#ffc600);background-image:linear-gradient(top,#ffe562,#ffc600);background-repeat:repeat-x;border-color:#ffc600;border-color:rgba(0,0,0,.1);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}
 .btn.large{width:100%;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box}
 .btn_petit{padding:2px 4px;font-size:11px;line-height:16px}
 .btn:hover,.btn_fonce:hover,.btn_abo:hover,.btn_petit:hover{text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-ms-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}
@@ -273,7 +273,7 @@ input[type=submit].disabled,.btn.disabled:hover{background-image:none;background
 input.btn,input.btn_petit,input.btn_abo,input.btn_fonce{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}
 button::-moz-focus-inner,input[type=submit]::-moz-focus-inner{padding:0;border:0}
 input[type=submit].btn_petit{*padding-top:3px;*padding-bottom:3px}
-.bt_abo{display:inline-block;padding:3px 12px;background:#ffd500;color:#000;font-weight:700;color:#650}
+.bt_abo{display:inline-block;padding:3px 12px;background:#ffd500;font-weight:700;color:#650}
 .bt_abo:hover{background:#ffc600;color:#000;font-weight:700;cursor:pointer;text-decoration:none}
 .titre_bt_fleche{display:inline-block;overflow:hidden;background:#f5f8f9}
 .titre_bt_fleche:hover{background:#e9edf0}
@@ -752,7 +752,7 @@ img[height="97"]+.ico29x29{bottom:6%;left:3.5%}
 .portfolio_appel_revolutionnaire .elt.shown .portfolio_data_container{-ms-filter:"alpha(Opacity=80)";opacity:.8}
 .portfolio_appel_revolutionnaire .portfolio_data_container .credits{opacity:.5;padding-left:4px}
 .portfolio_appel_revolutionnaire .carrousel .elt{width:644px;height:322px}
-.portfolio_appel_revolutionnaire.conteneur_carrousel .navigation .precedent,.portfolio_appel_revolutionnaire.conteneur_carrousel .navigation .suivant{position:absolute;top:0;left:0;width:165px;height:322px;background:#000;-ms-filter:"alpha(Opacity=60)";background:rgba(0,0,0,.6)}
+.portfolio_appel_revolutionnaire.conteneur_carrousel .navigation .precedent,.portfolio_appel_revolutionnaire.conteneur_carrousel .navigation .suivant{position:absolute;top:0;left:0;width:165px;height:322px;-ms-filter:"alpha(Opacity=60)";background:rgba(0,0,0,.6)}
 .portfolio_appel_revolutionnaire.conteneur_carrousel .navigation .precedent:hover,.portfolio_appel_revolutionnaire.conteneur_carrousel .navigation .suivant:hover{cursor:pointer}
 .portfolio_appel_revolutionnaire.conteneur_carrousel .navigation .precedent span,.portfolio_appel_revolutionnaire.conteneur_carrousel .navigation .suivant span{display:block;margin:111px 0 0;text-indent:0;font-size:72px;width:40px;height:100px;line-height:95px;text-align:center;background:#fff;background:-moz-linear-gradient(left,#eee 0,#fff 50%,#fff 100%);background:-webkit-gradient(linear,left center,right center,color-stop(0%,#eee),color-stop(50%,#fff),color-stop(100%,#fff));background:-webkit-linear-gradient(left,#eee 0,#fff 50%,#fff 100%);background:-o-linear-gradient(left,#eee 0,#fff 50%,#fff 100%);background:-ms-linear-gradient(left,#eee 0,#fff 50%,#fff 100%);background:linear-gradient(left,#eee 0,#fff 50%,#fff 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);border:solid #ddd;border-width:0 0 0 1px;box-shadow:0 0 1px 1px #000;-ms-filter:"alpha(Opacity=20)";opacity:.2;-webkit-transition:opacity 1s;-moz-transition:opacity 1s;-o-transition:opacity 1s;transition:opacity 1s}
 .portfolio_appel_revolutionnaire.conteneur_carrousel .navigation .suivant span{border-width:0 1px 0 0;margin:111px 0 0 124px;background:-moz-linear-gradient(left,#fff 0,#fff 55%,#eee 100%);background:-webkit-gradient(linear,left center,right center,color-stop(0%,#fff),color-stop(55%,#fff),color-stop(100%,#eee));background:-webkit-linear-gradient(left,#fff 0,#fff 55%,#eee 100%);background:-o-linear-gradient(left,#fff 0,#fff 55%,#eee 100%);background:-ms-linear-gradient(left,#fff 0,#fff 55%,#eee 100%);background:linear-gradient(left,#fff 0,#fff 55%,#eee 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0)}
@@ -851,7 +851,7 @@ img[height="97"]+.ico29x29{bottom:6%;left:3.5%}
 .loginbox label{display:block;margin:0 15px 5px 0;color:#747b83}
 .loginbox .choix{margin:15px 0;font-size:11px}
 .loginbox .choix label{float:left}
-label i{display:none;font-style:normal;display:none}
+label i{font-style:normal;display:none}
 .saisie_erreur label i{display:inline}
 .boite_formulaire .erreur{display:none}
 .loginbox .back{padding:0 15px;line-height:4rem;border-top:1px solid #d2d6db}
@@ -1120,7 +1120,7 @@ label i{display:none;font-style:normal;display:none}
 .bloc_part.empruntis .contenu{padding:0 15px;background:url(/medias/web/img/partenaires/empruntis/stylo.jpg) no-repeat}
 .bloc_part.empruntis .contenu .texte{box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;width:190px;color:#16212c}
 .bloc_part.empruntis .contenu .texte strong{display:block;color:#16212c}
-.bloc_part.empruntis .contenu .texte .lien_chevron{display:block;color:#a2a9ae;font-weight:700;color:#16212c}
+.bloc_part.empruntis .contenu .texte .lien_chevron{display:block;font-weight:700;color:#16212c}
 .bloc_part.empruntis .footer img{margin-top:-10px}
 .bloc_part.darqroom,.bloc_part.darqroom .texte{background:#000;color:#fff}
 .bloc_part.darqroom .footer{background:#16212c}
@@ -2340,7 +2340,7 @@ form .btn-rounded-degraded input[type=button]:hover,form .btn-rounded-degraded i
 .site-liberation .toolbox li a.twitter span{margin:1px 3px 0 0;float:left;width:15px;height:12px}
 .site-liberation .toolbox li.abo-1-euro{display:block;font-size:10px;line-height:1.25em;padding:0 7px;margin-left:0}
 .site-liberation .toolbox li.abo-1-euro span.ft-c{margin-top:1px}
-.site-liberation .toolbox li.fold-options{position:relative;display:block;font-size:11px;padding:0 7px;margin:0;font-size:12px}
+.site-liberation .toolbox li.fold-options{position:relative;display:block;padding:0 7px;margin:0;font-size:12px}
 .site-liberation .toolbox li.fold-options+li.fold-options{border-left:1px solid}
 .site-liberation .toolbox li.fold-options>a{display:inline-block;height:24px;padding:6px 0 0;font-weight:700}
 .site-liberation .toolbox li.fold-options ul{display:none;position:absolute;z-index:1000;top:30px;left:-1px;width:auto;min-width:100%;border:1px solid;border-top:0}
index dc29eee..5699448 100644 (file)
@@ -13791,4 +13791,4 @@ html.js body.dummy div#mainContent div#core-liberation div.col7 div.block-call-i
     min-height: 0;
     border-bottom: 0;
 }
-html.js body.dummy div#mainContent div#core-liberation div.col7 div.block-call-items div.block-content div.mini-tpl div.folder-on-demand { border-bottom: 1px solid #E7E7E7 }
\ No newline at end of file
+html.js body.dummy div#mainContent div#core-liberation div.col7 div.block-call-items div.block-content div.mini-tpl div.folder-on-demand { border-bottom: 1px solid #E7E7E7 }
index 39d0972..6736bae 100644 (file)
@@ -1087,5 +1087,28 @@ title']{display:block}",
       '@media (min-width:100px){a{color:red}}@media screen{a{color:red}p{width:100px}a{color:red}}',
       '@media (min-width:100px){a{color:red}}@media screen{p{width:100px}a{color:red}}'
     ]
+  }),
+  'duplicate properties': cssContext({
+    'of two properties one after another': 'a{display:-moz-inline-box;display:inline-block}',
+    'of two properties in one declaration': [
+      'a{display:inline-block;color:red;display:block}',
+      'a{color:red;display:block}'
+    ],
+    'of two properties in one declaration with former as !important': [
+      'a{display:inline-block!important;color:red;display:block}',
+      'a{display:inline-block!important;color:red}'
+    ],
+    'of two properties in one declaration with latter as !important': [
+      'a{display:inline-block;color:red;display:block!important}',
+      'a{color:red;display:block!important}'
+    ],
+    'of two properties in one declaration with both as !important': [
+      'a{display:inline-block!important;color:red;display:block!important}',
+      'a{color:red;display:block!important}'
+    ],
+    'of many properties in one declaration': [
+      'a{display:inline-block;color:red;font-weight:bolder;font-weight:700;display:block!important;color:#fff}',
+      'a{font-weight:bolder;font-weight:700;display:block!important;color:#fff}'
+    ]
   })
 }).export(module);