Adds better hack handling in property overriding and compacting too.
Also normalizes all suffix hacks to '\0' as IE treats them equally.
* Fixed issue [#508](https://github.com/jakubpawlowicz/clean-css/issues/508) - removing duplicate media queries.
* Fixed issue [#521](https://github.com/jakubpawlowicz/clean-css/issues/521) - unit optimizations inside `calc()`.
* Fixed issue [#526](https://github.com/jakubpawlowicz/clean-css/issues/526) - shorthand overriding into a function.
+* Fixed issue [#528](https://github.com/jakubpawlowicz/clean-css/issues/528) - better support for IE<9 hacks.
[3.1.9 / 2015-04-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.8...v3.1.9)
==================
if (!wasImportant && (wasHack && !isHack || !wasHack && isHack))
continue;
+ if (wasImportant && (isHack == 'star' || isHack == 'underscore'))
+ continue;
+
if (!wasHack && !isHack && !longhandToShorthand && canOverride && !canOverride(toRemove, property, validator))
continue;
if (left.unused || right.unused)
continue;
+ if (left.hack && !right.hack || !left.hack && right.hack)
+ continue;
+
if (hasInherit(right))
continue;
if (property.unused)
continue;
+ if (property.hack)
+ continue;
+
var descriptor = compactable[property.name];
if (!descriptor || !descriptor.componentOf)
continue;
// TODO: we should wrap it under `wrap for optimizing`
-var BACKSLASH_HACK = '\\9';
+var BACKSLASH_HACK = '\\';
var IMPORTANT = '!important';
var STAR_HACK = '*';
var UNDERSCORE_HACK = '_';
var name = property[0][0];
var lastValue = property[property.length - 1];
var isImportant = lastValue[0].indexOf(IMPORTANT) > 0;
- var isHack = name[0] == UNDERSCORE_HACK ||
- name[0] == STAR_HACK ||
- lastValue[0].indexOf(BACKSLASH_HACK) > 0 && lastValue[0].indexOf(BACKSLASH_HACK) == lastValue[0].length - BACKSLASH_HACK.length;
+ var hackType = false;
+
+ if (name[0] == UNDERSCORE_HACK) {
+ property[0][0] = name.substring(1);
+ hackType = 'underscore';
+ } else if (name[0] == STAR_HACK) {
+ property[0][0] = name.substring(1);
+ hackType = 'star';
+ } else if (lastValue[0].indexOf(BACKSLASH_HACK) > 0 && lastValue[0].indexOf(BACKSLASH_HACK) == lastValue[0].length - BACKSLASH_HACK.length - 1) {
+ lastValue[0] = lastValue[0].substring(0, lastValue[0].length - BACKSLASH_HACK.length - 1);
+ hackType = 'suffix';
+ }
// TODO: this should be done at tokenization step
// with adding importance info
if (isImportant)
lastValue[0] = lastValue[0].substring(0, lastValue[0].length - IMPORTANT.length);
- property[0].splice(1, 0, isImportant, isHack);
+ property[0].splice(1, 0, isImportant, hackType);
}
module.exports = addOptimizationMetadata;
name = property[0][0];
- if (property[0][2]) {
- var isPrefixHack = name[0] == '_';
- if (isPrefixHack && !options.compatibility.properties.iePrefixHack || !isPrefixHack && !options.compatibility.properties.ieSuffixHack)
+ var hackType = property[0][2];
+ if (hackType) {
+ if ((hackType == 'star' || hackType == 'underscore') && !options.compatibility.properties.iePrefixHack || hackType == 'suffix' && !options.compatibility.properties.ieSuffixHack)
unused = true;
}
var lineBreak = require('os').EOL;
+var STAR_HACK = '*';
+var SUFFIX_HACK = '\\0';
+var UNDERSCORE_HACK = '_';
+
function hasMoreProperties(tokens, index) {
for (var i = index, l = tokens.length; i < l; i++) {
if (typeof tokens[i] != 'string')
afterComma(token, valueIndex);
}
+function storePrefixHack(name, context) {
+ var hackType = name[2];
+ if (hackType == 'underscore')
+ context.store(UNDERSCORE_HACK, context);
+ else if (hackType == 'star')
+ context.store(STAR_HACK, context);
+}
+
function selectors(tokens, context) {
var store = context.store;
if (typeof token == 'string') {
store(token, context);
} else {
+ storePrefixHack(token[0], context);
store(token[0], context);
store(':', context);
value(tokens, position, isLast, context);
var store = context.store;
var token = tokens[position];
var isImportant = token[0][1];
+ var hackType = token[0][2];
for (var j = 1, m = token.length; j < m; j++) {
store(token[j], context);
+ if (j == m - 1 && hackType == 'suffix')
+ store(SUFFIX_HACK, context);
if (j == m - 1 && isImportant)
store('!important', context);
'IE hacks': cssContext({
'star': 'a{*color:#fff}',
'unserscore': 'a{_color:#fff}',
- 'backslash': 'a{color:#fff\\9}',
+ 'backslash': 'a{color:#fff\\0}',
'overriding by a star': 'a{color:red;display:block;*color:#fff}',
'overriding by a unserscore': 'a{color:red;display:block;_color:#fff}',
- 'overriding by a backslash': 'a{color:red;display:block;color:#fff\\9}',
- 'overriding !important by a star': 'a{color:red!important;display:block;*color:#fff}',
- 'overriding !important by a unserscore': 'a{color:red!important;display:block;_color:#fff}',
- 'overriding !important by a backslash': [
- 'a{color:red!important;display:block;color:#fff\\9}',
- 'a{color:red!important;display:block}'
- ],
+ 'overriding by a backslash': 'a{color:red;display:block;color:#fff\\0}',
'overriding a star': [
'a{*color:red;display:block;*color:#fff}',
'a{display:block;*color:#fff}'
],
'overriding a backslash': [
'a{color:red\\9;display:block;color:#fff\\9}',
- 'a{display:block;color:#fff\\9}'
+ 'a{display:block;color:#fff\\0}'
],
'overriding a star by a non-ajacent selector': 'a{color:red}.one{color:#000}a{*color:#fff}',
'overriding an underscore by a non-ajacent selector': 'a{color:red}.one{color:#000}a{_color:#fff}',
- 'overriding a backslash by a non-ajacent selector': 'a{color:red}.one{color:#fff}a{color:#fff\\9}',
+ 'overriding a backslash by a non-ajacent selector': 'a{color:red}.one{color:#fff}a{color:#fff\\0}',
+ 'preserving backslash in overriddable': 'a{border:1px solid #ccc\\0}',
'keeps rgba(0,0,0,0)': 'a{color:rgba(0,0,0,0)}',
'keeps rgba(255,255,255,0)': 'a{color:rgba(255,255,255,0)}',
'keeps hsla(120,100%,50%,0)': 'a{color:hsla(120,100%,50%,0)}'
assert.deepEqual(_optimize(topic, false, true), [
[['color', false , false], ['red']],
[['display', false , false], ['none']],
- [['color', false , true], ['#fff\\9']]
+ [['color', false , 'suffix'], ['#fff']]
]);
}
},
'topic': 'p{color:red\\9;display:none;color:#fff}',
'into': function (topic) {
assert.deepEqual(_optimize(topic, false, true), [
- [['color', false , true], ['red\\9']],
+ [['color', false , 'suffix'], ['red']],
[['display', false , false], ['none']],
[['color', false , false], ['#fff']]
]);
'into': function (topic) {
assert.deepEqual(_optimize(topic, false, true), [
[['display', false , false], ['none']],
- [['color', false , true], ['#fff\\9']]
+ [['color', false , 'suffix'], ['#fff']]
]);
}
}
}
}
})
+ .addBatch({
+ 'overriding !important by a star hack': {
+ 'topic': 'a{color:red!important;display:block;*color:#fff}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['color', true , false], ['red']],
+ [['display', false , false], ['block']],
+ [['color', false , 'star'], ['#fff']]
+ ]);
+ }
+ },
+ 'overriding !important by an underscore hack': {
+ 'topic': 'a{color:red!important;display:block;_color:#fff}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['color', true , false], ['red']],
+ [['display', false , false], ['block']],
+ [['color', false , 'underscore'], ['#fff']]
+ ]);
+ }
+ },
+ 'overriding !important by an backslash hack': {
+ 'topic': 'a{color:red!important;display:block;color:#fff\\0}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['color', true , false], ['red']],
+ [['display', false , false], ['block']]
+ ]);
+ }
+ }
+ })
.export(module);
[['padding', false, false], ['10px'], ['2px'], ['3px'], ['5px']]
]);
}
+ },
+ 'with hacks': {
+ 'topic': 'a{padding-top:10px;padding-left:5px;padding-bottom:3px;_padding-right:2px}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['padding-top', false, false], ['10px']],
+ [['padding-left', false, false], ['5px']],
+ [['padding-bottom', false, false], ['3px']],
+ [['padding-right', false, 'underscore'], ['2px']]
+ ]);
+ }
}
})
.addBatch({
'is unused': function (wrapped) {
assert.isTrue(wrapped[0].unused);
}
+ },
+ 'hack': {
+ 'topic': function () {
+ return wrapForOptimizing([[['margin', false, 'suffix']]]);
+ },
+ 'has one wrap': function (wrapped) {
+ assert.lengthOf(wrapped, 1);
+ },
+ 'is a hack': function (wrapped) {
+ assert.equal(wrapped[0].hack, 'suffix');
+ }
}
})
.export(module);
'topic': [['selector', ['a'], [[['_color'], ['red']]] ]],
'metadata': function (tokens) {
addOptimizationMetadata(tokens);
- assert.deepEqual(tokens, [['selector', ['a'], [[['_color', false, true], ['red']]] ]]);
+ assert.deepEqual(tokens, [['selector', ['a'], [[['color', false, 'underscore'], ['red']]] ]]);
}
},
'star hack': {
- 'topic': [['selector', ['a'], [[['_color'], ['red']]] ]],
+ 'topic': [['selector', ['a'], [[['*color'], ['red']]] ]],
'metadata': function (tokens) {
addOptimizationMetadata(tokens);
- assert.deepEqual(tokens, [['selector', ['a'], [[['_color', false, true], ['red']]] ]]);
+ assert.deepEqual(tokens, [['selector', ['a'], [[['color', false, 'star'], ['red']]] ]]);
}
},
'backslash hack': {
'topic': [['selector', ['a'], [[['color'], ['red\\9']]] ]],
'metadata': function (tokens) {
addOptimizationMetadata(tokens);
- assert.deepEqual(tokens, [['selector', ['a'], [[['color', false, true], ['red\\9']]] ]]);
+ assert.deepEqual(tokens, [['selector', ['a'], [[['color', false, 'suffix'], ['red']]] ]]);
}
},
'backslash hack - value of length 1': {
propertyContext('ie hacks in compatibility mode', {
'underscore': [
'a{_width:100px}',
- [['_width', '100px']]
+ [['width', '100px']]
],
'star': [
'a{*width:100px}',
- [['*width', '100px']]
+ [['width', '100px']]
],
'backslash': [
'a{width:100px\\9}',
- [['width', '100px\\9',]]
+ [['width', '100px']]
]
}, { compatibility: 'ie8' })
)