From 2f8f100592908225c853997051f56bb87c35c20d Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Wed, 25 Feb 2015 20:35:31 +0000 Subject: [PATCH] Reworks same-selector merging to run both ways. This way it can optimize more use cases as content may not be movable one way but it could be other way, e.g.: ```css a{color:red} p{display:block} a{display:inline-block} ``` In this case the third selector can't be moved and merged with the first one, but it can be done the other way around. --- lib/selectors/optimizers/advanced.js | 50 +++++++++++++++++++--------- test/fixtures/big-min.css | 18 ++++------ test/integration-test.js | 23 ++++++++----- test/selectors/optimizer-test.js | 6 +++- 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/lib/selectors/optimizers/advanced.js b/lib/selectors/optimizers/advanced.js index 92078fb7..ec275bf6 100644 --- a/lib/selectors/optimizers/advanced.js +++ b/lib/selectors/optimizers/advanced.js @@ -367,24 +367,42 @@ AdvancedOptimizer.prototype.mergeNonAdjacentBySelector = function (tokens) { selectorIterator: for (var j = positions.length - 1; j > 0; j--) { - var targetPosition = positions[j - 1]; - var targetToken = tokens[targetPosition]; - var movedPosition = positions[j]; - var movedToken = tokens[movedPosition]; - var movedProperties = allProperties(movedToken); - - for (var k = movedPosition - 1; k > targetPosition; k--) { - var traversedProperties = allProperties(tokens[k]); + var positionOne = positions[j - 1]; + var tokenOne = tokens[positionOne]; + var positionTwo = positions[j]; + var tokenTwo = tokens[positionTwo]; + + directionIterator: + for (var direction = 1; direction >= -1; direction -= 2) { + var topToBottom = direction == 1; + var from = topToBottom ? positionOne + 1 : positionTwo - 1; + var to = topToBottom ? positionTwo : positionOne; + var delta = topToBottom ? 1 : -1; + var moved = topToBottom ? tokenOne : tokenTwo; + var target = topToBottom ? tokenTwo : tokenOne; + var movedProperties = allProperties(moved); + + while (from != to) { + var traversedProperties = allProperties(tokens[from]); + from += delta; + + // traversed then moved as we move selectors towards the start + var reorderable = topToBottom ? + canReorder(movedProperties, traversedProperties) : + canReorder(traversedProperties, movedProperties); + + if (!reorderable && !topToBottom) + continue selectorIterator; + if (!reorderable && topToBottom) + continue directionIterator; + } - // traversed then moved as we move selectors towards the start - if (!canReorder(traversedProperties, movedProperties)) - continue selectorIterator; + var joinAt = topToBottom ? [target.body.length] : [moved.body.length]; + var joinedBodies = topToBottom ? moved.body.concat(target.body) : target.body.concat(moved.body); + var newBody = this.propertyOptimizer.process(target.value, joinedBodies, joinAt, true); + changeBodyOf(target, newBody); + changeBodyOf(moved, { tokenized: [], list: [] }); } - - var joinAt = [movedToken.body.length]; - var newBody = this.propertyOptimizer.process(targetToken.value, targetToken.body.concat(movedToken.body), joinAt, true); - changeBodyOf(targetToken, newBody); - changeBodyOf(movedToken, { tokenized: [], list: [] }); } } }; diff --git a/test/fixtures/big-min.css b/test/fixtures/big-min.css index 1331563e..90daa90c 100644 --- a/test/fixtures/big-min.css +++ b/test/fixtures/big-min.css @@ -12,13 +12,11 @@ article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display audio,canvas,video{display:inline-block} [hidden],audio:not([controls]){display:none} .tt13_capital,.tt15_capital,.tt20,.tt24,.tt28,.tt32,.tt40{display:block} -html{-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%} button,html,input,select,textarea{font-family:sans-serif} body,figure,form{margin:0} a:focus{outline:dotted thin} a:active,a:hover{outline:0} h1,h2,h3,h4,h5,h6{margin:0;font-weight:700} -p{-webkit-margin-before:0;-webkit-margin-after:0} abbr[title]{border-bottom:1px dotted} b,strong{font-weight:700} .tt13_capital,.tt15_capital,.tt20,.tt24,.tt26_capital,.tt28,.tt32,.tt40{font-weight:400} @@ -189,7 +187,7 @@ table{border-spacing:0} .clear{overflow:hidden;width:0;height:0} .clearfix:after{clear:both;content:' ';display:block;font-size:0;line-height:0;width:0;height:0} * html .clearfix{height:1%} -html{font-size:62.5%} +html{-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;font-size:62.5%} body{font-size:1.3rem;font-size:13px;line-height:140%;color:#16212c;background:#e9edf0} .global{width:1000px;margin:0 auto;padding:20px 0 10px;background:#fff} .lmd-header{position:relative;z-index:15} @@ -202,7 +200,7 @@ a{color:#036} .bg_fonce a:focus,.bg_fonce a:hover{opacity:1} .obf{color:#036} .lien_interne,.lien_interne:hover{color:#000} -p{margin:0} +p{-webkit-margin-before:0;-webkit-margin-after:0;margin:0} article,figure,section{overflow:hidden} section article{margin:0 0 16px} .img_bord,article img,figure img{border:1px solid #eef1f5;vertical-align:bottom} @@ -257,9 +255,8 @@ section article{margin:0 0 16px} .bord_lrb1_gris_clair{border-left:1px solid #eef1f5;border-right:1px solid #eef1f5;border-bottom:1px solid #eef1f5} .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{line-height:25px;font-size:12px} .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{line-height:25px;font-size:12px;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} .bloc_abo{border-top:3px solid #ffd500} img[width="642"],img[width="312"]{margin-bottom:6px} @@ -310,7 +307,6 @@ button::-moz-focus-inner,input[type=submit]::-moz-focus-inner{padding:0;border:0 .nl span{display:inline-block;width:20px;height:20px;margin:0 0 0 5px;line-height:20px;background:url(/medias/web/img/sprites/pictos20x20.png)no-repeat} .nl:hover span{background-position:right -20px} .bt_fermer,.bt_ouvrir,.bt_ouvrir:hover,.croix_blanche,.croix_grise,.fb13x13,.fb13x13_blanc,.fb13x13_gris,.google13x13,.google13x13_blanc,.ico_annee_en_france,.lien_externe span,.linkedin13x13,.linkedin13x13_blanc,.nb_commentaires .pic,.nb_reactions .pic,.nl_blanc,.tw13x13,.tw13x13_blanc,.tw13x13_gris,.tw_bird{background:url(/medias/web/img/sprites/icos_petites.png)no-repeat;display:inline-block;text-indent:-9999px;height:13px;width:13px;cursor:pointer} -.tw_bird{cursor:default} .fb13x13,.fb13x13_blanc,.fb13x13_gris,.google13x13,.google13x13_blanc,.linkedin13x13,.linkedin13x13_blanc,.tw13x13,.tw13x13_blanc,.tw13x13_gris{margin:0 2px;vertical-align:middle} .fb13x13,.fb13x13_gris{background-position:0 -38px} .fb13x13_gris{background-position:0 -94px} @@ -326,7 +322,7 @@ button::-moz-focus-inner,input[type=submit]::-moz-focus-inner{padding:0;border:0 .linkedin13x13{background-position:0 -80px} .linkedin13x13_blanc{background-position:-14px -216px} .linkedin13x13:hover,.linkedin13x13_blanc:hover{background-position:-14px -80px} -.tw_bird{width:15px;height:11px;vertical-align:middle;background-position:-14px -131px} +.tw_bird{cursor:default;width:15px;height:11px;vertical-align:middle;background-position:-14px -131px} .nb_reactions,span.nb_reactions{font-size:10px;font-weight:700;font-family:arial,sans-serif;color:#a2a9ae;vertical-align:5%;white-space:nowrap} .nb_commentaires .pic,.nb_reactions .pic{vertical-align:text-bottom;margin:0 3px 0 0;width:12px;height:11px;background-position:-13px 0} .lien_externe{color:#5d666d;font-size:11px} @@ -2163,10 +2159,9 @@ a.god:hover{background:#3c3c3c;color:#fff} #core-liberation .block-search-results .block-content .article .object-picture,#core-liberation .block-search-results .block-content .article .object-picture img{height:116px} #core-liberation .block-search-results .block-content .object-picture .np{position:relative} #core-liberation .block-search-results .block-content .object-picture .np .p1,#core-liberation .block-search-results .block-content .object-picture .np .p2{position:absolute;width:79px;height:102px;border-style:solid;border-width:7px 4px} -#core-liberation .block-search-results .block-content .object-picture .np a.date{position:absolute;display:block;width:80px;top:10px;padding:2px 2px 3px 4px} #core-liberation .block-search-results .block-content .object-picture .np .p1{z-index:2000} #core-liberation .block-search-results .block-content .object-picture .np .p2{z-index:1000} -#core-liberation .block-search-results .block-content .object-picture .np a.date{z-index:500} +#core-liberation .block-search-results .block-content .object-picture .np a.date{position:absolute;display:block;width:80px;top:10px;padding:2px 2px 3px 4px;z-index:500} #core-liberation .block-https-inscription{width:418px;margin:0 auto 42px} #core-liberation .block-https-inscription .block-content h2{margin:5px 0 10px;text-align:center;font-size:16px;font-weight:700;font-family:Verdana,sans-serif} #core-liberation .block-https-inscription .block-content p{margin-bottom:15px;text-align:center;font-size:14px} @@ -2552,7 +2547,6 @@ body.access-ess #page-paywall .content .arguments .arg{float:none;margin:auto} .site-liberation .block-call-items .tpl-search-results .object-picture .np a.date{position:absolute;display:block;width:80px;top:10px;padding:2px 2px 3px 4px} .site-liberation .block-call-items .tpl-search-results .object-picture .np .p1{z-index:2000} .site-liberation .block-call-items .tpl-search-results .object-picture .np .p2{z-index:1000} -.site-liberation .block-call-items .tpl-search-results .object-picture .np a.date{z-index:500} .site-liberation .block-call-items .tpl-no-border{border-bottom:none} .site-liberation .block-call-items .tpl-visual-square-left .visual{display:block;float:left;margin:3px 14px 0 0;width:84px;height:84px} .site-liberation .block-call-items .tpl-visual-square-left .subtitle,.site-liberation .block-call-items .tpl-visual-square-left h3,.site-liberation .block-call-items .tpl-visual-square-left h4,.site-liberation .block-call-items .tpl-visual-square-left h5{margin-left:98px} @@ -2723,7 +2717,7 @@ body.slideshow .ad-top .megaban{background:#333} .site-liberation .block-call-items .tpl-search-results h4,.site-liberation .block-call-items .tpl-search-results h4 a{color:#959595} .site-liberation .block-call-items .tpl-search-results .object-picture .np .p1,.site-liberation .block-call-items .tpl-search-results .object-picture .np .p2{background-color:#fff;border-color:#c8c8c8} #core-liberation .block-comments .block-content .comment_outer,#core-liberation .block-item-read-more{border-color:#ddd} -.site-liberation .block-call-items .tpl-search-results .object-picture .np a.date{background-color:#c8c8c8} +.site-liberation .block-call-items .tpl-search-results .object-picture .np a.date{z-index:500;background-color:#c8c8c8} #core-liberation .block-item .object-picture .legende{color:#838383} .site-liberation .block-item .object-content a:hover{color:#e20000} #core-liberation .block-item-locked{border-top-color:#c1b0bb;border-bottom-color:#c1b0bb;background:#fff;background:-moz-linear-gradient(top,#dbdad6 0,#fff 2%,#fff 98%,#e1e0de 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dbdad6),color-stop(2%,#fff),color-stop(98%,#fff),color-stop(100%,#e1e0de));background:-o-linear-gradient(top,#dbdad6 0,#fff 2%,#fff 98%,#e1e0de 100%)} diff --git a/test/integration-test.js b/test/integration-test.js index 6d8299a9..ed0f2be6 100644 --- a/test/integration-test.js +++ b/test/integration-test.js @@ -1737,7 +1737,7 @@ title']{display:block}", 'when overriden with a browser specific selector': 'a{color:red}::-webkit-scrollbar,a{color:#fff}', 'two same selectors over a block': [ '.one{color:red}@media print{.two{display:block}}.one{display:none}', - '.one{color:red}@media print{.two{display:block}}.one{display:none}' + '@media print{.two{display:block}}.one{color:red;display:none}' ], 'two same bodies over a block': [ '.one{color:red}@media print{.two{display:block}}.three{color:red}', @@ -1745,18 +1745,17 @@ title']{display:block}", ] }), 'same non-adjacent selectors': cssContext({ - 'with different properties': 'a{color:red;display:block}.one{margin:5px}a{margin:2px}', 'with one redefined property': [ 'a{color:red;display:block}.one{color:red}a{color:#fff;margin:2px}', - 'a{display:block}.one{color:red}a{color:#fff;margin:2px}' + '.one{color:red}a{display:block;color:#fff;margin:2px}' ], 'with intentionally redefined properties on joins': [ 'a{display:inline-block;display:-moz-inline-box;color:red}.one{margin:12px}a{color:#fff;margin:2px}', - 'a{display:inline-block;display:-moz-inline-box}.one{margin:12px}a{color:#fff;margin:2px}' + '.one{margin:12px}a{display:inline-block;display:-moz-inline-box;color:#fff;margin:2px}' ], 'with intentionally redefined properties on multiple joins': [ 'a{color:red}.one{font-size:12px}a{color:#fff;margin:2px}.two{margin:10px}a{margin:0}', - '.one{font-size:12px}a{color:#fff}.two{margin:10px}a{margin:0}' + '.one{font-size:12px}.two{margin:10px}a{color:#fff;margin:0}' ], 'with all redefined properties': [ 'a{color:red;display:block}.one{font-size:12px}a{color:#fff;display:inline-block;margin:2px}', @@ -1764,7 +1763,7 @@ title']{display:block}", ], 'many with all redefined properties': [ 'a{padding:10px}.zero{color:transparent}a{color:red;display:block}.one{font-size:12px}a{color:#fff;display:inline-block;margin:2px}', - 'a{padding:10px}.zero{color:transparent}.one{font-size:12px}a{color:#fff;display:inline-block;margin:2px}' + '.zero{color:transparent}.one{font-size:12px}a{padding:10px;color:#fff;display:inline-block;margin:2px}' ], 'when overriden by an empty selector': [ 'a{padding:10px}.one{color:red}a{}', @@ -1787,7 +1786,10 @@ title']{display:block}", '.one>.two,.three{color:red;line-height:1rem}#zero,.one>.two,.three,.www{color:#fff;margin:0}a{color:red}.one>.two,.three{line-height:2rem;font-size:1.5rem}', '#zero,.one>.two,.three,.www{color:#fff;margin:0}a{color:red}.one>.two,.three{line-height:2rem;font-size:1.5rem}' ], - 'when undefined is used as a value': '.one{text-shadow:undefined}p{font-size:14px}.one{font-size:12px}', + 'when undefined is used as a value': [ + '.one{text-shadow:undefined}p{font-size:14px}.one{font-size:12px}', + 'p{font-size:14px}.one{text-shadow:undefined;font-size:12px}' + ], 'when undefined is used as a value with reduction': [ '.one{text-shadow:undefined}p{color:red}.one{font-size:12px;text-shadow:none}', 'p{color:red}.one{font-size:12px;text-shadow:none}' @@ -1797,14 +1799,17 @@ title']{display:block}", 'a,::-moz-selection{color:red}p{display:block}a,::-moz-selection{color:#fff}', 'p{display:block}::-moz-selection,a{color:#fff}' ], - 'with full property comparison': '.one{height:7rem}.two{color:#fff}.one{line-height:7rem;color:red}', + 'with full property comparison': [ + '.one{height:7rem}.two{color:#fff}.one{line-height:7rem;color:red}', + '.two{color:#fff}.one{height:7rem;line-height:7rem;color:red}' + ], 'with two intermediate, non-overriding selectors': [ '.one{color:red;margin:0}.two{color:#fff}.one{font-size:12px}', '.one{color:red;margin:0;font-size:12px}.two{color:#fff}' ], 'with two intermediate, overriding more specific selectors': [ '.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}' + '.two{font:12px serif}.one{color:red;margin:0;font-size:12px}' ], 'with granular selectors from the same shorthand': [ '.one{color:red;margin:0}.two{font-weight:700}.one{font-size:12px}', diff --git a/test/selectors/optimizer-test.js b/test/selectors/optimizer-test.js index d735476d..22b6e8b1 100644 --- a/test/selectors/optimizer-test.js +++ b/test/selectors/optimizer-test.js @@ -107,6 +107,10 @@ vows.describe(SelectorsOptimizer) '.one,.two,.three{color:red;display:block}div{margin:0}.four,.five,.six{color:red;display:block}', '.five,.four,.one,.six,.three,.two{color:red;display:block}div{margin:0}' ], + 'down until changed': [ + '.one{padding:0}.two{margin:0}.one{margin-bottom:3px}', + '.two{margin:0}.one{padding:0;margin-bottom:3px}' + ], 'over shorthands': [ 'div{margin-top:0}.one{margin:0}.two{display:block;margin-top:0}', '.two,div{margin-top:0}.one{margin:0}.two{display:block}' @@ -241,7 +245,7 @@ vows.describe(SelectorsOptimizer) ], 'non-adjacent': [ 'a{color:red;display:block}.one{margin:12px}a{color:#fff;margin:2px}', - 'a{display:block}.one{margin:12px}a{color:#fff;margin:2px}' + '.one{margin:12px}a{display:block;color:#fff;margin:2px}' ], 'non-adjacent with multi selectors': [ 'a{padding:10px;margin:0;color:red}.one{color:red}a,p{color:red;padding:0}', -- 2.34.1