From 96096402eaaeb4947afa7ecacba0982381a244f4 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Fri, 21 Aug 2015 09:10:32 +0100 Subject: [PATCH] Fixes #596 - support for !ie IE<8 hack. !ie fix is like a backslash hack for IE<10. --- History.md | 1 + README.md | 1 + lib/properties/restore-from-optimizing.js | 5 ++- lib/properties/wrap-for-optimizing.js | 19 ++++++---- lib/selectors/simple.js | 5 ++- lib/utils/compatibility.js | 3 ++ test/integration-test.js | 10 +++++- test/properties/wrap-for-optimizing-test.js | 40 +++++++++++++++++++-- test/selectors/simple-test.js | 30 +++++++++++++++- test/utils/compatibility-test.js | 7 ++++ 10 files changed, 108 insertions(+), 13 deletions(-) diff --git a/History.md b/History.md index 61c53e61..a2781efc 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ * Adds unit compatibility switches to disable length optimizations. * Adds inferring proxy settings from HTTP_PROXY environment variable. * Unifies wrappers for simple & advanced optimizations. +* Fixed issue [#596](https://github.com/jakubpawlowicz/clean-css/issues/596) - support for !ie IE<8 hack. * Fixed issue [#599](https://github.com/jakubpawlowicz/clean-css/issues/599) - support for inlined source maps. * Fixed issue [#607](https://github.com/jakubpawlowicz/clean-css/issues/607) - adds better rule reordering. * Fixed issue [#612](https://github.com/jakubpawlowicz/clean-css/issues/612) - adds HTTP proxy support. diff --git a/README.md b/README.md index 1e8e330b..d129319c 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,7 @@ with the following options available: * `'[+-]properties.backgroundOriginMerging'` - turn on / off background-origin merging into shorthand * `'[+-]properties.backgroundSizeMerging'` - turn on / off background-size merging into shorthand * `'[+-]properties.colors'` - turn on / off any color optimizations +* `'[+-]properties.ieBangHack'` - turn on / off IE bang hack removal * `'[+-]properties.iePrefixHack'` - turn on / off IE prefix hack removal * `'[+-]properties.ieSuffixHack'` - turn on / off IE suffix hack removal * `'[+-]properties.merging'` - turn on / off property merging based on understandability diff --git a/lib/properties/restore-from-optimizing.js b/lib/properties/restore-from-optimizing.js index da0cbf60..f0dfc182 100644 --- a/lib/properties/restore-from-optimizing.js +++ b/lib/properties/restore-from-optimizing.js @@ -4,6 +4,7 @@ var BACKSLASH_HACK = '\\9'; var IMPORTANT_TOKEN = '!important'; var STAR_HACK = '*'; var UNDERSCORE_HACK = '_'; +var BANG_HACK = '!ie'; function restoreImportant(property) { property.value[property.value.length - 1][0] += IMPORTANT_TOKEN; @@ -14,8 +15,10 @@ function restoreHack(property) { property.name = UNDERSCORE_HACK + property.name; else if (property.hack == 'star') property.name = STAR_HACK + property.name; - else if (property.hack == 'suffix') + else if (property.hack == 'backslash') property.value[property.value.length - 1][0] += BACKSLASH_HACK; + else if (property.hack == 'bang') + property.value[property.value.length - 1][0] += ' ' + BANG_HACK; } function restoreFromOptimizing(properties, simpleMode) { diff --git a/lib/properties/wrap-for-optimizing.js b/lib/properties/wrap-for-optimizing.js index aa4b67b0..cf493e6d 100644 --- a/lib/properties/wrap-for-optimizing.js +++ b/lib/properties/wrap-for-optimizing.js @@ -2,6 +2,7 @@ var BACKSLASH_HACK = '\\'; var IMPORTANT_TOKEN = '!important'; var STAR_HACK = '*'; var UNDERSCORE_HACK = '_'; +var BANG_HACK = '!'; function wrapAll(properties) { var wrapped = []; @@ -37,10 +38,14 @@ function hackType(property) { type = 'underscore'; } else if (name[0] == STAR_HACK) { type = 'star'; + } else if (lastValue[0][0] == BANG_HACK && lastValue[0].indexOf('important') == -1) { + type = 'bang'; + } else if (lastValue[0].indexOf(BANG_HACK) > 0 && lastValue[0].indexOf('important') == -1) { + type = 'bang'; } else if (lastValue[0].indexOf(BACKSLASH_HACK) > 0 && lastValue[0].indexOf(BACKSLASH_HACK) == lastValue[0].length - BACKSLASH_HACK.length - 1) { - type = 'suffix'; + type = 'backslash'; } else if (lastValue[0].indexOf(BACKSLASH_HACK) === 0 && lastValue[0].length == 2) { - type = 'suffix'; + type = 'backslash'; } return type; @@ -61,9 +66,11 @@ function stripPrefixHack(property) { property[0][0] = property[0][0].substring(1); } -function stripSuffixHack(property) { +function stripSuffixHack(property, hackType) { var lastValue = property[property.length - 1]; - lastValue[0] = lastValue[0].substring(0, lastValue[0].length - BACKSLASH_HACK.length - 1); + lastValue[0] = lastValue[0] + .substring(0, lastValue[0].indexOf(hackType == 'backslash' ? BACKSLASH_HACK : BANG_HACK)) + .trim(); if (lastValue[0].length === 0) property.pop(); @@ -77,8 +84,8 @@ function wrapSingle(property) { var _hackType = hackType(property); if (_hackType == 'star' || _hackType == 'underscore') stripPrefixHack(property); - else if (_hackType == 'suffix') - stripSuffixHack(property); + else if (_hackType == 'backslash' || _hackType == 'bang') + stripSuffixHack(property, _hackType); return { components: [], diff --git a/lib/selectors/simple.js b/lib/selectors/simple.js index 203cd102..f431321c 100644 --- a/lib/selectors/simple.js +++ b/lib/selectors/simple.js @@ -299,7 +299,10 @@ function optimizeBody(properties, options) { property = _properties[i]; name = property.name; - if (property.hack && ((property.hack == 'star' || property.hack == 'underscore') && !options.compatibility.properties.iePrefixHack || property.hack == 'suffix' && !options.compatibility.properties.ieSuffixHack)) + if (property.hack && ( + (property.hack == 'star' || property.hack == 'underscore') && !options.compatibility.properties.iePrefixHack || + property.hack == 'backslash' && !options.compatibility.properties.ieSuffixHack || + property.hack == 'bang' && !options.compatibility.properties.ieBangHack)) property.unused = true; if (name.indexOf('padding') === 0 && (isNegative(property, 0) || isNegative(property, 1) || isNegative(property, 2) || isNegative(property, 3))) diff --git a/lib/utils/compatibility.js b/lib/utils/compatibility.js index a48e51b2..1f8aecb3 100644 --- a/lib/utils/compatibility.js +++ b/lib/utils/compatibility.js @@ -10,6 +10,7 @@ var DEFAULTS = { backgroundOriginMerging: false, // background-origin to shorthand backgroundSizeMerging: false, // background-size to shorthand colors: true, // any kind of color transformations, like `#ff00ff` to `#f0f` or `#fff` into `red` + ieBangHack: false, // !ie suffix hacks on IE<8 iePrefixHack: false, // underscore / asterisk prefix hacks on IE ieSuffixHack: true, // \9 suffix hacks on IE6-9 merging: true, // merging properties into one @@ -44,6 +45,7 @@ var DEFAULTS = { backgroundOriginMerging: false, backgroundSizeMerging: false, colors: true, + ieBangHack: false, iePrefixHack: true, ieSuffixHack: true, merging: false, @@ -78,6 +80,7 @@ var DEFAULTS = { backgroundOriginMerging: false, backgroundSizeMerging: false, colors: true, + ieBangHack: true, iePrefixHack: true, ieSuffixHack: true, merging: false, diff --git a/test/integration-test.js b/test/integration-test.js index ddad7eeb..0e3d6051 100644 --- a/test/integration-test.js +++ b/test/integration-test.js @@ -1566,7 +1566,7 @@ vows.describe('integration tests') }) ) .addBatch( - optimizerContext('IE hacks', { + optimizerContext('IE 8 hacks', { 'star': [ 'a{*color:#fff}', 'a{*color:#fff}' @@ -1633,6 +1633,14 @@ vows.describe('integration tests') ] }, { compatibility: 'ie8' }) ) + .addBatch( + optimizerContext('IE 7 hacks', { + 'keeps !ie hack 123': [ + 'a{list-style-type:none;list-style-type:decimal !ie}', + 'a{list-style-type:none;list-style-type:decimal !ie}' + ] + }, { compatibility: 'ie7' }) + ) .addBatch( optimizerContext('IE hacks without IE compatibility', { 'star': [ diff --git a/test/properties/wrap-for-optimizing-test.js b/test/properties/wrap-for-optimizing-test.js index 3beb90f5..67a05c92 100644 --- a/test/properties/wrap-for-optimizing-test.js +++ b/test/properties/wrap-for-optimizing-test.js @@ -140,7 +140,7 @@ vows.describe(wrapForOptimizing) assert.deepEqual(wrapped[0].value, [['0']]); }, 'is a hack': function (wrapped) { - assert.equal(wrapped[0].hack, 'suffix'); + assert.equal(wrapped[0].hack, 'backslash'); } }, 'backslash hack - single value': { @@ -168,7 +168,41 @@ vows.describe(wrapForOptimizing) assert.deepEqual(wrapped[0].value, [['0']]); }, 'is a hack': function (wrapped) { - assert.equal(wrapped[0].hack, 'suffix'); + assert.equal(wrapped[0].hack, 'backslash'); + } + }, + 'bang hack': { + 'topic': function () { + return wrapForOptimizing([[['margin'], ['0'], ['!ie']]]); + }, + 'has one wrap': function (wrapped) { + assert.lengthOf(wrapped, 1); + }, + 'has right value': function (wrapped) { + assert.deepEqual(wrapped[0].value, [['0']]); + }, + 'is a hack': function (wrapped) { + assert.equal(wrapped[0].hack, 'bang'); + }, + 'is not important': function (wrapped) { + assert.isFalse(wrapped[0].important); + } + }, + 'bang hack - space between values': { + 'topic': function () { + return wrapForOptimizing([[['margin'], ['0 !ie']]]); + }, + 'has one wrap': function (wrapped) { + assert.lengthOf(wrapped, 1); + }, + 'has right value': function (wrapped) { + assert.deepEqual(wrapped[0].value, [['0']]); + }, + 'is a hack': function (wrapped) { + assert.equal(wrapped[0].hack, 'bang'); + }, + 'is not important': function (wrapped) { + assert.isFalse(wrapped[0].important); } }, 'source map': { @@ -185,7 +219,7 @@ vows.describe(wrapForOptimizing) assert.isTrue(wrapped[0].important); }, 'is a hack': function (wrapped) { - assert.equal(wrapped[0].hack, 'suffix'); + assert.equal(wrapped[0].hack, 'backslash'); } } }) diff --git a/test/selectors/simple-test.js b/test/selectors/simple-test.js index 79bf7647..ceecce1f 100644 --- a/test/selectors/simple-test.js +++ b/test/selectors/simple-test.js @@ -365,11 +365,15 @@ vows.describe('simple optimizations') 'backslash': [ 'a{width:101px\\9}', [['width', '101px\\9']] + ], + 'bang': [ + 'a{color:red !ie}', + null ] }) ) .addBatch( - propertyContext('ie hacks in compatibility mode', { + propertyContext('ie hacks in IE8 mode', { 'underscore': [ 'a{_width:101px}', [['_width', '101px']] @@ -381,9 +385,33 @@ vows.describe('simple optimizations') 'backslash': [ 'a{width:101px\\9}', [['width', '101px\\9']] + ], + 'bang': [ + 'a{color:red !ie}', + null ] }, { compatibility: 'ie8' }) ) + .addBatch( + propertyContext('ie hacks in IE7 mode', { + 'underscore': [ + 'a{_width:101px}', + [['_width', '101px']] + ], + 'star': [ + 'a{*width:101px}', + [['*width', '101px']] + ], + 'backslash': [ + 'a{width:101px\\9}', + [['width', '101px\\9']] + ], + 'bang': [ + 'a{color:red !ie}', + [['color', 'red !ie']] + ] + }, { compatibility: 'ie7' }) + ) .addBatch( propertyContext('important', { 'minified': [ diff --git a/test/utils/compatibility-test.js b/test/utils/compatibility-test.js index a00c32a9..407dce27 100644 --- a/test/utils/compatibility-test.js +++ b/test/utils/compatibility-test.js @@ -14,6 +14,7 @@ vows.describe(Compatibility) assert.isFalse(options.properties.backgroundClipMerging); assert.isFalse(options.properties.backgroundOriginMerging); assert.isFalse(options.properties.backgroundSizeMerging); + assert.isFalse(options.properties.ieBangHack); assert.isFalse(options.properties.iePrefixHack); assert.isTrue(options.properties.ieSuffixHack); assert.isTrue(options.properties.merging); @@ -52,6 +53,7 @@ vows.describe(Compatibility) assert.isFalse(options.properties.backgroundOriginMerging); assert.isFalse(options.properties.backgroundSizeMerging); assert.isTrue(options.properties.colors); + assert.isFalse(options.properties.ieBangHack); assert.isFalse(options.properties.iePrefixHack); assert.isTrue(options.properties.ieSuffixHack); assert.isTrue(options.properties.merging); @@ -83,6 +85,7 @@ vows.describe(Compatibility) assert.isFalse(options.properties.backgroundOriginMerging); assert.isFalse(options.properties.backgroundSizeMerging); assert.isTrue(options.properties.colors); + assert.isFalse(options.properties.ieBangHack); assert.isTrue(options.properties.iePrefixHack); assert.isTrue(options.properties.ieSuffixHack); assert.isFalse(options.properties.merging); @@ -113,6 +116,7 @@ vows.describe(Compatibility) assert.isFalse(options.properties.backgroundOriginMerging); assert.isFalse(options.properties.backgroundSizeMerging); assert.isTrue(options.properties.colors); + assert.isTrue(options.properties.ieBangHack); assert.isTrue(options.properties.iePrefixHack); assert.isTrue(options.properties.ieSuffixHack); assert.isFalse(options.properties.merging); @@ -153,6 +157,7 @@ vows.describe(Compatibility) assert.isFalse(options.properties.backgroundOriginMerging); assert.isFalse(options.properties.backgroundSizeMerging); assert.isTrue(options.properties.colors); + assert.isFalse(options.properties.ieBangHack); assert.isFalse(options.properties.iePrefixHack); assert.isTrue(options.properties.ieSuffixHack); assert.isFalse(options.properties.merging); @@ -183,6 +188,7 @@ vows.describe(Compatibility) assert.isFalse(options.properties.backgroundClipMerging); assert.isFalse(options.properties.backgroundOriginMerging); assert.isFalse(options.properties.backgroundSizeMerging); + assert.isFalse(options.properties.ieBangHack); assert.isTrue(options.properties.iePrefixHack); assert.isTrue(options.properties.ieSuffixHack); assert.isTrue(options.properties.merging); @@ -213,6 +219,7 @@ vows.describe(Compatibility) assert.isFalse(options.properties.backgroundClipMerging); assert.isFalse(options.properties.backgroundOriginMerging); assert.isFalse(options.properties.backgroundSizeMerging); + assert.isFalse(options.properties.ieBangHack); assert.isTrue(options.properties.iePrefixHack); assert.isTrue(options.properties.ieSuffixHack); assert.isTrue(options.properties.merging); -- 2.34.1