Skip to content

Commit

Permalink
Fixes #375 - unit compatibility switches.
Browse files Browse the repository at this point in the history
Adds compatibility switches for `ch`, `vh`, `vm`, `vmax`, `vmin`,
and `vw` units.

Note we need #290 to fully support all use cases. Currently it'll be
just for implemented shorthands.
  • Loading branch information
jakubpawlowicz committed May 4, 2015
1 parent 29ab63f commit 6698f48
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 32 deletions.
1 change: 1 addition & 0 deletions History.md
Expand Up @@ -7,6 +7,7 @@
* Moves tokenizer code into lib/tokenizer.
* Moves URL scanner into lib/urls/reduce (was named incorrectly before).
* Moves URL rebasing & rewriting into lib/urls.
* Fixed issue [#375](https://github.com/jakubpawlowicz/clean-css/issues/375) - unit compatibility switches.
* Fixed issue [#436](https://github.com/jakubpawlowicz/clean-css/issues/436) - refactors URI rewriting.
* Fixed issue [#448](https://github.com/jakubpawlowicz/clean-css/issues/448) - rebasing no protocol URIs.
* Fixed issue [#517](https://github.com/jakubpawlowicz/clean-css/issues/517) - turning off color optimizations.
Expand Down
8 changes: 7 additions & 1 deletion README.md
Expand Up @@ -317,9 +317,15 @@ with the following options available:
* `'[+-]selectors.adjacentSpace'` - turn on / off extra space before `nav` element
* `'[+-]selectors.ie7Hack'` - turn on / off IE7 selector hack removal (`*+html...`)
* `'[+-]selectors.special'` - a regular expression with all special, unmergeable selectors (leave it empty unless you know what you are doing)
* `'[+-]units.ch'` - turn on / off treating `ch` as a proper unit
* `'[+-]units.rem'` - turn on / off treating `rem` as a proper unit
* `'[+-]units.vh'` - turn on / off treating `vh` as a proper unit
* `'[+-]units.vm'` - turn on / off treating `vm` as a proper unit
* `'[+-]units.vmax'` - turn on / off treating `vmax` as a proper unit
* `'[+-]units.vmin'` - turn on / off treating `vmin` as a proper unit
* `'[+-]units.vm'` - turn on / off treating `vm` as a proper unit

For example, this declaration `--compatibility 'ie8,+units.rem'` will ensure IE8 compatiblity while enabling `rem` units so the following style `margin:0px 0rem` can be shortened to `margin:0`, while in pure IE8 mode it can't be.
For example, using `--compatibility 'ie8,+units.rem'` will ensure IE8 compatiblity while enabling `rem` units so the following style `margin:0px 0rem` can be shortened to `margin:0`, while in pure IE8 mode it can't be.

To pass a single off (-) switch in CLI please use the following syntax `--compatibility *,-units.rem`.

Expand Down
21 changes: 8 additions & 13 deletions lib/properties/validator.js
Expand Up @@ -3,7 +3,7 @@
var Splitter = require('../utils/splitter');

var widthKeywords = ['thin', 'thick', 'medium', 'inherit', 'initial'];
var allUnits = ['px', '%', 'em', 'rem', 'in', 'cm', 'mm', 'ex', 'pt', 'pc', 'vw', 'vh', 'vmin', 'vmax'];
var allUnits = ['px', '%', 'em', 'in', 'cm', 'mm', 'ex', 'pt', 'pc', 'ch', 'rem', 'vh', 'vm', 'vmin', 'vmax', 'vw'];
var cssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + allUnits.join('|') + '|)|auto|inherit)';
var cssCalcRegexStr = '(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)';
var cssFunctionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(([A-Z]|[0-9]|\\ |\\,|\\#|\\+|\\-|\\%|\\.|\\(|\\))*\\)';
Expand Down Expand Up @@ -31,18 +31,13 @@ var listStyleTypeKeywords = ['armenian', 'circle', 'cjk-ideographic', 'decimal',
var listStylePositionKeywords = ['inside', 'outside', 'inherit'];

function Validator(compatibility) {
if (compatibility.units.rem) {
this.compatibleCssUnitRegex = cssUnitRegex;
this.compatibleCssUnitAnyRegex = cssUnitAnyRegex;
} else {
var validUnits = allUnits.slice(0).filter(function (value) {
return value != 'rem';
});

var compatibleCssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + ')|auto|inherit)';
this.compatibleCssUnitRegex = new RegExp('^' + compatibleCssUnitRegexStr + '$', 'i');
this.compatibleCssUnitAnyRegex = new RegExp('^(none|' + widthKeywords.join('|') + '|' + compatibleCssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')$', 'i');
}
var validUnits = allUnits.slice(0).filter(function (value) {
return !(value in compatibility.units) || compatibility.units[value] === true;
});

var compatibleCssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + '|)|auto|inherit)';
this.compatibleCssUnitRegex = new RegExp('^' + compatibleCssUnitRegexStr + '$', 'i');
this.compatibleCssUnitAnyRegex = new RegExp('^(none|' + widthKeywords.join('|') + '|' + compatibleCssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')$', 'i');
}

Validator.prototype.isValidHexColor = function (s) {
Expand Down
15 changes: 11 additions & 4 deletions lib/selectors/optimizers/simple.js
Expand Up @@ -16,9 +16,14 @@ function SimpleOptimizer(options) {
this.options = options;

var units = ['px', 'em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', '%'];
if (options.compatibility.units.rem)
units.push('rem');
options.unitsRegexp = new RegExp('(^|\\s|\\(|,)0(?:' + units.join('|') + ')', 'g');
var otherUnits = ['ch', 'rem', 'vh', 'vm', 'vmax', 'vmin', 'vw'];

otherUnits.forEach(function (unit) {
if (options.compatibility.units[unit])
units.push(unit);
});

options.unitsRegexp = new RegExp('(^|\\s|\\(|,)0(?:' + units.join('|') + ')(\\W|$)', 'g');

options.precision = {};
options.precision.value = options.roundingPrecision === undefined ?
Expand Down Expand Up @@ -106,7 +111,9 @@ function unitMinifier(_, value, unitsRegexp) {
if (/^(?:\-moz\-calc|\-webkit\-calc|calc)\(/.test(value))
return value;

return value.replace(unitsRegexp, '$1' + '0');
return value
.replace(unitsRegexp, '$1' + '0' + '$2')
.replace(unitsRegexp, '$1' + '0' + '$2');
}

function multipleZerosMinifier(property) {
Expand Down
24 changes: 21 additions & 3 deletions lib/utils/compatibility.js
Expand Up @@ -21,7 +21,13 @@ var DEFAULTS = {
special: /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:dir\([a-z-]*\)|:first(?![a-z-])|:fullscreen|:left|:read-only|:read-write|:right)/ // special selectors which prevent merging
},
units: {
rem: true
ch: true,
rem: true,
vh: true,
vm: true, // vm is vmin on IE9+ see https://developer.mozilla.org/en-US/docs/Web/CSS/length
vmax: true,
vmin: true,
vw: true
}
},
'ie8': {
Expand All @@ -44,7 +50,13 @@ var DEFAULTS = {
special: /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:root|:nth|:first\-of|:last|:only|:empty|:target|:checked|::selection|:enabled|:disabled|:not)/
},
units: {
rem: false
ch: false,
rem: false,
vh: false,
vm: false,
vmax: false,
vmin: false,
vw: false
}
},
'ie7': {
Expand All @@ -67,7 +79,13 @@ var DEFAULTS = {
special: /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:focus|:before|:after|:root|:nth|:first\-of|:last|:only|:empty|:target|:checked|::selection|:enabled|:disabled|:not)/
},
units: {
rem: false
ch: false,
rem: false,
vh: false,
vm: false,
vmax: false,
vmin: false,
vw: false,
}
}
};
Expand Down
4 changes: 0 additions & 4 deletions test/integration-test.js
Expand Up @@ -523,10 +523,6 @@ vows.describe('integration tests').addBatch({
'a{box-shadow:0 0 0 .15em #EBEBEB}'
],
'box shadow with three zeros and a value': 'a{box-shadow:0 0 0 15px #EBEBEB}',
'rems': [
'div{width:0rem;height:0rem}',
'div{width:0;height:0}'
],
'prefixed box shadow zeros': [
'a{-webkit-box-shadow:0 0 0 0; -moz-box-shadow:0 0 0 0}',
'a{-webkit-box-shadow:0 0;-moz-box-shadow:0 0}'
Expand Down
28 changes: 25 additions & 3 deletions test/properties/optimizer-test.js
Expand Up @@ -9,10 +9,11 @@ var Compatibility = require('../../lib/utils/compatibility');
var Validator = require('../../lib/properties/validator');
var addOptimizationMetadata = require('../../lib/selectors/optimization-metadata');

var compatibility = new Compatibility().toOptions();
var validator = new Validator(compatibility);

function _optimize(source, mergeAdjacent, aggressiveMerging) {
function _optimize(source, mergeAdjacent, aggressiveMerging, compatibilityOptions) {
var compatibility = new Compatibility(compatibilityOptions).toOptions();
var validator = new Validator(compatibility);

var tokens = tokenize(source, {
options: {},
sourceTracker: new SourceTracker(),
Expand Down Expand Up @@ -384,6 +385,27 @@ vows.describe(optimize)
[['border-top-width', false , false], ['calc(100%)']]
]);
}
},
'understandable - non adjacent units': {
'topic': 'a{margin-top:100px;padding-top:30px;margin-top:10vmin}',
'into': function (topic) {
assert.deepEqual(_optimize(topic, false, true), [
[['padding-top', false , false], ['30px']],
[['margin-top', false , false], ['10vmin']]
]);
}
}
})
.addBatch({
'understandable - non adjacent units in IE8 mode 123': {
'topic': 'a{margin-top:100px;padding-top:30px;margin-top:10vmin}',
'into': function (topic) {
assert.deepEqual(_optimize(topic, false, true, 'ie8'), [
[['margin-top', false , false], ['100px']],
[['padding-top', false , false], ['30px']],
[['margin-top', false , false], ['10vmin']]
]);
}
}
})
.export(module);
40 changes: 38 additions & 2 deletions test/selectors/optimizers/simple-test.js
Expand Up @@ -454,14 +454,46 @@ vows.describe(SimpleOptimizer)
'div{transform:rotate(10deg) skew(.5deg)}',
[['transform', 'rotate(10deg)', 'skew(.5deg)']]
],
'ch': [
'div{width:0ch;height:0ch}',
[['width', '0'], ['height', '0']]
],
'rem': [
'div{width:0rem;height:0rem}',
[['width', '0'], ['height', '0']]
],
'vh': [
'div{width:0vh;height:0vh}',
[['width', '0'], ['height', '0']]
],
'vm': [
'div{width:0vm;height:0vm}',
[['width', '0'], ['height', '0']]
],
'vmax': [
'div{width:0vmax;height:0vmax}',
[['width', '0'], ['height', '0']]
],
'vmin': [
'div{width:0vmin;height:0vmin}',
[['width', '0'], ['height', '0']]
],
'vw': [
'div{width:0vw;height:0vw}',
[['width', '0'], ['height', '0']]
],
'mixed units': [
'a{margin:0em 0rem 0px 0pt}',
[['margin', '0']]
],
'mixed vales': [
'mixed values #1': [
'a{padding:10px 0em 30% 0rem}',
[['padding', '10px', '0', '30%', '0']]
],
'mixed values #2': [
'a{padding:10ch 0vm 30vmin 0vw}',
[['padding', '10ch', '0', '30vmin', '0']]
],
'inside calc': [
'a{font-size:calc(100% + 0px)}',
[['font-size', 'calc(100% + 0px)']]
Expand All @@ -478,9 +510,13 @@ vows.describe(SimpleOptimizer)
'a{margin:0em 0rem 0px 0pt}',
[['margin', '0', '0rem', '0', '0']]
],
'mixed vales': [
'mixed values #1': [
'a{padding:10px 0em 30% 0rem}',
[['padding', '10px', '0', '30%', '0rem']]
],
'mixed values #2': [
'a{padding:10ch 0vm 30vmin 0vw}',
[['padding', '10ch', '0vm', '30vmin', '0vw']]
]
}, { compatibility: 'ie8' })
)
Expand Down
47 changes: 45 additions & 2 deletions test/utils/compatibility-test.js
Expand Up @@ -21,7 +21,13 @@ vows.describe(Compatibility)
assert.isFalse(options.selectors.adjacentSpace);
assert.isFalse(options.selectors.ie7Hack);
assert.deepEqual(options.selectors.special, /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:dir\([a-z-]*\)|:first(?![a-z-])|:fullscreen|:left|:read-only|:read-write|:right)/);
assert.isTrue(options.units.ch);
assert.isTrue(options.units.rem);
assert.isTrue(options.units.vh);
assert.isTrue(options.units.vm);
assert.isTrue(options.units.vmax);
assert.isTrue(options.units.vmin);
assert.isTrue(options.units.vw);
}
},
'not given': {
Expand All @@ -34,7 +40,7 @@ vows.describe(Compatibility)
},
'as a populated hash': {
'topic': function () {
return new Compatibility({ units: { rem: false }, properties: { prefix: true } }).toOptions();
return new Compatibility({ units: { rem: false, vmax: false }, properties: { prefix: true } }).toOptions();
},
'gets merged options': function(options) {
assert.isTrue(options.colors.opacity);
Expand All @@ -48,7 +54,13 @@ vows.describe(Compatibility)
assert.isFalse(options.selectors.adjacentSpace);
assert.isFalse(options.selectors.ie7Hack);
assert.deepEqual(options.selectors.special, /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:dir\([a-z-]*\)|:first(?![a-z-])|:fullscreen|:left|:read-only|:read-write|:right)/);
assert.isTrue(options.units.ch);
assert.isFalse(options.units.rem);
assert.isTrue(options.units.vh);
assert.isTrue(options.units.vm);
assert.isFalse(options.units.vmax);
assert.isTrue(options.units.vmin);
assert.isTrue(options.units.vw);
}
}
})
Expand All @@ -70,7 +82,13 @@ vows.describe(Compatibility)
assert.isFalse(options.selectors.adjacentSpace);
assert.isFalse(options.selectors.ie7Hack);
assert.deepEqual(options.selectors.special, /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:root|:nth|:first\-of|:last|:only|:empty|:target|:checked|::selection|:enabled|:disabled|:not)/);
assert.isFalse(options.units.ch);
assert.isFalse(options.units.rem);
assert.isFalse(options.units.vh);
assert.isFalse(options.units.vm);
assert.isFalse(options.units.vmax);
assert.isFalse(options.units.vmin);
assert.isFalse(options.units.vw);
}
},
'as an ie7 template': {
Expand All @@ -90,7 +108,13 @@ vows.describe(Compatibility)
assert.isFalse(options.selectors.adjacentSpace);
assert.isTrue(options.selectors.ie7Hack);
assert.deepEqual(options.selectors.special, /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:focus|:before|:after|:root|:nth|:first\-of|:last|:only|:empty|:target|:checked|::selection|:enabled|:disabled|:not)/);
assert.isFalse(options.units.ch);
assert.isFalse(options.units.rem);
assert.isFalse(options.units.vh);
assert.isFalse(options.units.vm);
assert.isFalse(options.units.vmax);
assert.isFalse(options.units.vmin);
assert.isFalse(options.units.vw);
}
},
'as an unknown template': {
Expand Down Expand Up @@ -120,7 +144,13 @@ vows.describe(Compatibility)
assert.isFalse(options.selectors.adjacentSpace);
assert.isFalse(options.selectors.ie7Hack);
assert.deepEqual(options.selectors.special, /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:root|:nth|:first\-of|:last|:only|:empty|:target|:checked|::selection|:enabled|:disabled|:not)/);
assert.isFalse(options.units.ch);
assert.isFalse(options.units.rem);
assert.isFalse(options.units.vh);
assert.isFalse(options.units.vm);
assert.isFalse(options.units.vmax);
assert.isFalse(options.units.vmin);
assert.isFalse(options.units.vw);
}
},
'as a single string value without group': {
Expand All @@ -140,7 +170,14 @@ vows.describe(Compatibility)
assert.isFalse(options.selectors.adjacentSpace);
assert.isFalse(options.selectors.ie7Hack);
assert.deepEqual(options.selectors.special, /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:dir\([a-z-]*\)|:first(?![a-z-])|:fullscreen|:left|:read-only|:read-write|:right)/);
assert.isTrue(options.units.rem); }
assert.isTrue(options.units.ch);
assert.isTrue(options.units.rem);
assert.isTrue(options.units.vh);
assert.isTrue(options.units.vm);
assert.isTrue(options.units.vmax);
assert.isTrue(options.units.vmin);
assert.isTrue(options.units.vw);
}
},
'as a complex string value without group': {
'topic': function () {
Expand All @@ -159,7 +196,13 @@ vows.describe(Compatibility)
assert.isFalse(options.selectors.adjacentSpace);
assert.isFalse(options.selectors.ie7Hack);
assert.deepEqual(options.selectors.special, /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:dir\([a-z-]*\)|:first(?![a-z-])|:fullscreen|:left|:read-only|:read-write|:right)/);
assert.isTrue(options.units.ch);
assert.isFalse(options.units.rem);
assert.isTrue(options.units.vh);
assert.isTrue(options.units.vm);
assert.isTrue(options.units.vmax);
assert.isTrue(options.units.vmin);
assert.isTrue(options.units.vw);
}
}
})
Expand Down

0 comments on commit 6698f48

Please sign in to comment.