Adds removing duplicate selectors (same body) within the same scope.
authorGoalSmashers <jakub@goalsmashers.com>
Sun, 27 Oct 2013 09:50:59 +0000 (10:50 +0100)
committerGoalSmashers <jakub@goalsmashers.com>
Sat, 2 Nov 2013 16:30:38 +0000 (17:30 +0100)
* Always preserve the last one as it overrides previous ones and intermediate overriding.

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

index cacef24..68de01b 100644 (file)
@@ -15,6 +15,33 @@ module.exports = function Optimizer(data) {
     return plain.join(',');
   };
 
+  var removeDuplicates = function(tokens) {
+    var matched = {};
+    var forRemoval = [];
+
+    for (var i = 0, l = tokens.length; i < l; i++) {
+      if (typeof(tokens[i]) == 'string' || tokens[i].block)
+        continue;
+
+      var selector = tokens[i].selector;
+      var body = tokens[i].body;
+      var id = body + '@' + selector;
+      var alreadyMatched = matched[id];
+
+      if (alreadyMatched) {
+        forRemoval.push(alreadyMatched[alreadyMatched.length - 1]);
+        alreadyMatched.push(i);
+      } else {
+        matched[id] = [i];
+      }
+    }
+
+    forRemoval = forRemoval.sort(function(a, b) { return a > b ? 1 : -1; });
+    for (var j = 0, n = forRemoval.length; j < n; j++) {
+      tokens.splice(forRemoval[j] - j, 1);
+    }
+  };
+
   var optimize = function(tokens) {
     tokens = (Array.isArray(tokens) ? tokens : [tokens]);
     for (var i = 0, l = tokens.length; i < l; i++) {
@@ -25,6 +52,8 @@ module.exports = function Optimizer(data) {
       if (token.block)
         optimize(token.body);
     }
+
+    removeDuplicates(tokens);
   };
 
   var rebuild = function(tokens) {
index 9e4a5ba..abadd09 100644 (file)
@@ -239,7 +239,6 @@ figure img,article img,.img_bord{border:1px solid #eef1f5;vertical-align:bottom}
 .pad_top20{padding-top:20px}
 .bord_top6_gris{border-top:6px solid #e9edf0;padding-top:16px}
 .bord_bot6_gris{border-bottom:6px solid #e9edf0}
-.bord_bot6_gris{border-bottom:6px solid #e9edf0}
 .bord_top3_gris{border-top:3px solid #e9edf0;padding-top:15px}
 .bord_top3_politique{border-top:3px solid #1f0d67}
 .bord_bot3_gris{border-bottom:3px solid #e9edf0}
@@ -248,7 +247,6 @@ figure img,article img,.img_bord{border:1px solid #eef1f5;vertical-align:bottom}
 .bord_top1_gris{border-top:1px solid #e9edf0;padding-top:10px}
 .bord_bot1_gris{border-bottom:1px solid #e9edf0;padding-bottom:10px}
 .bord_double_gris_blanc{display:inline-block;line-height:25px;font-size:12px;border:solid #d2d6db;border-width:1px 0}
-.bord_double_gris_blanc span{display:inline-block;border:solid #fff;border-width:1px 0}
 .bord1_gris_clair{border:1px solid #eef1f5}
 .bord_double_gris_blanc{display:inline-block;border:solid #d2d6db;border-width:1px 0}
 .bord_double_gris_blanc span{display:inline-block;border:solid #fff;border-width:1px 0}
@@ -603,11 +601,7 @@ img[height="97"]+.ico29x29{bottom:6%;left:3.5%}
 .carousel_petit .repere.actif{background-position:-11px -24px;cursor:pointer}
 .conteneur_pagination{background:#f8f9fb;font-weight:700;border:1px solid #d2d6db;border-radius:4px;height:26px;margin-top:20px}
 .pagination_large{margin-top:10px}
-.pagination a:hover,.pagination span:hover{cursor:pointer}
-.pagination li{display:block;float:left}
 .pagination .adroite{float:right}
-.pagination>li{background:#f8f9fb}
-.pagination>li:hover{background:#e4e6e9}
 .pagination .page{display:block;float:left;padding:0 9px;height:26px;border:solid #e4e6e9;border-width:0 0 0 1px;text-align:center;line-height:26px;font-size:12px}
 .conteneur_pagination .prev,.conteneur_pagination .next{display:block;float:left;width:27px;height:26px;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#fafafa;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fefefe),color-stop(25%,#fefefe),to(#e4e6e9));background-image:-webkit-linear-gradient(#fefefe,#fefefe 25%,#e4e6e9);background-image:-moz-linear-gradient(left,#fefefe,#fefefe 25%,#e4e6e9);background-image:-ms-linear-gradient(#fefefe,#fefefe 25%,#e4e6e9);background-image:-o-linear-gradient(#fefefe,#fefefe 25%,#e4e6e9);background-image:linear-gradient(#fefefe,#fefefe 25%,#e4e6e9);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fefefe', endColorstr='#e4e6e9', GradientType=0);text-align:center;line-height:26px;font-size:15px;color:#2e3942}
 .conteneur_pagination .prev:hover,.conteneur_pagination .next:hover{color:#2e3942;text-decoration:none;background-color:#e4e6e9;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}
@@ -925,7 +919,6 @@ label i{display:none;font-style:normal;display:none}
 .ie #nav_ariane a,#nav_ariane h1{font-size:12px}
 #nav_ariane .sous_rub{border-right:1px solid #e4e6e9}
 #nav_ariane .az{position:absolute;left:-9999px}
-#nav_ariane .ariane{background:url(/medias/web/img/sprites/sous_nav.png) no-repeat right -175px}
 #nav_ariane .ariane{position:relative;padding:0 13px 0 0;margin:0 0 0 -13px;border:0}
 #nav_ariane .ariane.z1{z-index:1}
 #nav_ariane .ariane.z2{z-index:2}
@@ -1089,7 +1082,6 @@ label i{display:none;font-style:normal;display:none}
 .bloc_part .footer{clear:both;height:21px;padding:4px 15px 0;text-align:right;background:#eef1f5;color:#5d666d;font-size:11px;font-weight:700}
 .bloc_part .logo_header{height:21px;color:#b9c0c5;font-size:11px;font-weight:400;float:right}
 .bloc_part .footer span,.bloc_part .footer img{vertical-align:middle}
-.bloc_part .footer img{margin:0 0 0 5px}
 .bloc_part .contenu.attractive{background:#e4e6e9}
 .global .bloc_part .bandeau{height:24px;padding:0 15px;background:#d2d6db!important;color:#16212c;line-height:22px;white-space:nowrap;overflow:hidden}
 .bloc_part .attractive.carrousel_petit .texte{width:125px;height:150px}
@@ -1099,12 +1091,10 @@ label i{display:none;font-style:normal;display:none}
 .bloc_part.attractive.format-text.exigeant .texte{padding:0 15px 9px;width:190px}
 .bloc_part.attractive.format-text.exigeant .btn_fonce{margin:0 0 26px 57px}
 .bloc_part.attractive.format-text.exigeant.grid_6 .btn_fonce{margin:0;right:15px}
-.bloc_part.attractive.text{height:208px}
 .bloc_part.attractive.temoignage .img,.bloc_part.attractive.escapade .img{margin-right:0;padding:15px 0 15px 15px}
 .bloc_part.attractive.temoignage.petit .img{margin-right:0;padding:15px 0 15px 15px}
 .bloc_part.attractive.temoignage .texte,.bloc_part.attractive.escapade .texte{height:166px;margin:0;width:142px}
 .bloc_part.attractive.temoignage.petit .texte{height:108px}
-.bloc_part.attractive.temoignage.petit .texte{height:108px}
 .bloc_part.attractive.text{height:208px}
 .bloc_part.attractive.format-text .img img{padding:15px 15px 9px}
 .services .bloc_part.gymglish.grid_12,.services .bloc_part.darqroom.grid_12.promo{background-color:#e9ecf0;background-image:-moz-linear-gradient(top,#fff,#e9ecf0);background-image:-ms-linear-gradient(top,#fafbfc #e9ecf0);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fafbfc),to(#e9ecf0));background-image:-webkit-linear-gradient(top,#fafbfc,#e9ecf0);background-image:-o-linear-gradient(top,#fafbfc,#e9ecf0);background-image:linear-gradient(top,#fff,#e9ecf0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fafbfc', endColorstr='#e9ecf0', GradientType=0)}
@@ -1594,7 +1584,6 @@ body.access-pre .block-ad,body.access-ess .block-ad{display:none!important}
 img.spacer{width:1px;height:1px}
 .visual-square-90{width:90px;height:90px}
 .visual-rect-0302-460{width:460px;height:307px}
-.visual-rect-0302-460{width:460px;height:307px}
 .visual-rect-0302-150{width:150px;height:100px}
 .visual-rect-2305-460{width:460px;height:100px}
 .txt-up-c{text-transform:uppercase}
@@ -2209,7 +2198,6 @@ a.god:hover{background:#3c3c3c;color:#fff;text-decoration:none}
 #core-liberation .block-search-head .advanced .searchform .source ul li{padding:0 20px 0 12px}
 #core-liberation .block-search-head .advanced .searchform .category select{margin-left:20px;outline:0}
 #core-liberation .block-search-head .advanced .searchform input[type=submit]{margin:10px 0 0 165px}
-#core-liberation .block-search-head .results{margin-bottom:15px}
 #core-liberation .block-search-head .results p{margin-bottom:15px}
 #core-liberation .block-search-head .results p.filters{text-align:right;margin-bottom:0}
 #core-liberation .block-search-results .block-top{margin-bottom:0}
@@ -2617,7 +2605,6 @@ body.access-ess #page-paywall .content .arguments .arg{float:none;margin:auto}
 .site-liberation .block-call-items .tpl-labo-podcast .emission p.subtitle{margin-bottom:10px}
 .site-liberation .block-call-items .tpl-labo-podcast .emission .visual{width:60px;height:40px}
 .site-liberation .block-call-items .tpl-labo-podcast .emission .subscribe{clear:both}
-.site-liberation .block-call-items .tpl-labo-podcast .emission .subscribe p{color:#e20000}
 .site-liberation .block-call-items .tpl-labo-podcast .emission .subscribe p.infos{font-weight:700;color:#222}
 .site-liberation .block-call-items .tpl-labo-podcast .emission .subscribe p.infos span{text-transform:uppercase}
 .site-liberation .block-call-items .tpl-labo-podcast .emission .subscribe .sb-podcasts{margin-top:10px}
@@ -2845,8 +2832,6 @@ body.slideshow .ad-top .megaban{background:#333}
 #core-liberation .flat-comments .comment>.comment_outer,#core-liberation .block-comments .comment_level_0>.comment_outer{border-color:#878787}
 #core-liberation .block-comments .block-content .detail_comment{border-top-color:#fe9900}
 #core-liberation .block-comments .block-content .detail_comment>.comment_outer{border-color:#fe9900}
-#core-liberation .block-comments .block-content .detail_comment{border-top-color:#fe9900}
-#core-liberation .block-comments .block-content .detail_comment>.comment_outer{border-color:#fe9900}
 #core-liberation .block-comments .block-content .meta .who,#core-liberation .block-comments .block-content .meta .who a{color:#878787}
 #core-liberation .block-comments .block-content .meta .details{color:#b2b2b2}
 #core-liberation .block-comments .block-content .meta .details a.profile{color:#b2b2b2}
@@ -3054,4 +3039,4 @@ html.js body.dummy div#mainContent div#core-liberation div.col9 div.block div.bl
 html.js body.dummy div#mainContent div#core-liberation div.col9 div.block div.block-content div.favorites-frontpages div.col-left div.cartridge{width:388px}
 html.js body.dummy div#mainContent div#core-liberation div.col9 div.block div.block-content div.favorites-folders div.block-call-items div.block-content div.mini-tpl div.cartridge{margin-left:0;width:129px}
 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 div.object-content{margin-right:0;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}
+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
index e95a92c..39d0972 100644 (file)
@@ -637,8 +637,8 @@ vows.describe('clean-units').addBatch({
       'a{background:url(/images/blank.png) 0 0 no-repeat}'
     ],
     'strip more': [
-      'a{background:url("/images/blank.png") 0 0 no-repeat}a{display:block}a{background:url("/images/blank.png") 0 0 no-repeat}',
-      'a{background:url(/images/blank.png) 0 0 no-repeat}a{display:block}a{background:url(/images/blank.png) 0 0 no-repeat}'
+      'a{background:url("/images/blank.png") 0 0 no-repeat}a{display:block}a{background:url("/images/blank2.png") 0 0 no-repeat}',
+      'a{background:url(/images/blank.png) 0 0 no-repeat}a{display:block}a{background:url(/images/blank2.png) 0 0 no-repeat}'
     ],
     'not strip comments if spaces inside': [
       'a{background:url("/images/long image name.png") 0 0 no-repeat}a{display:block}a{background:url("/images/no-spaces.png") 0 0 no-repeat}',
@@ -1057,5 +1057,35 @@ title']{display:block}",
       '@media screen{a,#foo[data-path^="bar bar"],p,#foo[data-path^="bar bar"]{color:red}}',
       '@media screen{a,#foo[data-path^="bar bar"],p{color:red}}'
     ]
+  }),
+  'duplicate selectors in a scope': cssContext({
+    'of two successive selectors': [
+      'a{color:red}a{color:red}',
+      'a{color:red}'
+    ],
+    'of two successive selectors with different body': [
+      'a{color:red}a{display:block}',
+      'a{color:red}a{display:block}'
+    ],
+    'of many successive selectors': [
+      'a{color:red}a{color:red}a{color:red}a{color:red}',
+      'a{color:red}'
+    ],
+    'of two non-successive selectors': [
+      'a{color:red}p{color:#fff}a{color:red}',
+      'p{color:#fff}a{color:red}'
+    ],
+    'of many non-successive selectors': [
+      'div{width:100%}a{color:red}a{color:red}p{color:#fff}div{width:100%}ol{margin:0}p{color:#fff}',
+      'a{color:red}div{width:100%}ol{margin:0}p{color:#fff}'
+    ],
+    'with global and media scope': [
+      'a{color:red}@media screen{a{color:red}p{width:100px}a{color:red}}',
+      'a{color:red}@media screen{p{width:100px}a{color:red}}'
+    ],
+    'with two media scopes': [
+      '@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}}'
+    ]
   })
 }).export(module);