* Re-implemented most of property values' minifications.
* Extra charset(s) are stripped and first one is moved to the beginning.
--- /dev/null
+function HexNameShortener(value) {
+ this.value = value;
+}
+
+var COLORS = {
+ aliceblue: '#f0f8ff',
+ antiquewhite: '#faebd7',
+ aqua: '#0ff',
+ aquamarine: '#7fffd4',
+ azure: '#f0ffff',
+ beige: '#f5f5dc',
+ bisque: '#ffe4c4',
+ black: '#000',
+ blanchedalmond: '#ffebcd',
+ blue: '#00f',
+ blueviolet: '#8a2be2',
+ brown: '#a52a2a',
+ burlywood: '#deb887',
+ cadetblue: '#5f9ea0',
+ chartreuse: '#7fff00',
+ chocolate: '#d2691e',
+ coral: '#ff7f50',
+ cornflowerblue: '#6495ed',
+ cornsilk: '#fff8dc',
+ crimson: '#dc143c',
+ cyan: '#0ff',
+ darkblue: '#00008b',
+ darkcyan: '#008b8b',
+ darkgoldenrod: '#b8860b',
+ darkgray: '#a9a9a9',
+ darkgreen: '#006400',
+ darkgrey: '#a9a9a9',
+ darkkhaki: '#bdb76b',
+ darkmagenta: '#8b008b',
+ darkolivegreen: '#556b2f',
+ darkorange: '#ff8c00',
+ darkorchid: '#9932cc',
+ darkred: '#8b0000',
+ darksalmon: '#e9967a',
+ darkseagreen: '#8fbc8f',
+ darkslateblue: '#483d8b',
+ darkslategray: '#2f4f4f',
+ darkslategrey: '#2f4f4f',
+ darkturquoise: '#00ced1',
+ darkviolet: '#9400d3',
+ deeppink: '#ff1493',
+ deepskyblue: '#00bfff',
+ dimgray: '#696969',
+ dimgrey: '#696969',
+ dodgerblue: '#1e90ff',
+ firebrick: '#b22222',
+ floralwhite: '#fffaf0',
+ forestgreen: '#228b22',
+ fuchsia: '#f0f',
+ gainsboro: '#dcdcdc',
+ ghostwhite: '#f8f8ff',
+ gold: '#ffd700',
+ goldenrod: '#daa520',
+ gray: '#808080',
+ green: '#008000',
+ greenyellow: '#adff2f',
+ grey: '#808080',
+ honeydew: '#f0fff0',
+ hotpink: '#ff69b4',
+ indianred: '#cd5c5c',
+ indigo: '#4b0082',
+ ivory: '#fffff0',
+ khaki: '#f0e68c',
+ lavender: '#e6e6fa',
+ lavenderblush: '#fff0f5',
+ lawngreen: '#7cfc00',
+ lemonchiffon: '#fffacd',
+ lightblue: '#add8e6',
+ lightcoral: '#f08080',
+ lightcyan: '#e0ffff',
+ lightgoldenrodyellow: '#fafad2',
+ lightgray: '#d3d3d3',
+ lightgreen: '#90ee90',
+ lightgrey: '#d3d3d3',
+ lightpink: '#ffb6c1',
+ lightsalmon: '#ffa07a',
+ lightseagreen: '#20b2aa',
+ lightskyblue: '#87cefa',
+ lightslategray: '#778899',
+ lightslategrey: '#778899',
+ lightsteelblue: '#b0c4de',
+ lightyellow: '#ffffe0',
+ lime: '#0f0',
+ limegreen: '#32cd32',
+ linen: '#faf0e6',
+ magenta: '#ff00ff',
+ maroon: '#800000',
+ mediumaquamarine: '#66cdaa',
+ mediumblue: '#0000cd',
+ mediumorchid: '#ba55d3',
+ mediumpurple: '#9370db',
+ mediumseagreen: '#3cb371',
+ mediumslateblue: '#7b68ee',
+ mediumspringgreen: '#00fa9a',
+ mediumturquoise: '#48d1cc',
+ mediumvioletred: '#c71585',
+ midnightblue: '#191970',
+ mintcream: '#f5fffa',
+ mistyrose: '#ffe4e1',
+ moccasin: '#ffe4b5',
+ navajowhite: '#ffdead',
+ navy: '#000080',
+ oldlace: '#fdf5e6',
+ olive: '#808000',
+ olivedrab: '#6b8e23',
+ orange: '#ffa500',
+ orangered: '#ff4500',
+ orchid: '#da70d6',
+ palegoldenrod: '#eee8aa',
+ palegreen: '#98fb98',
+ paleturquoise: '#afeeee',
+ palevioletred: '#db7093',
+ papayawhip: '#ffefd5',
+ peachpuff: '#ffdab9',
+ peru: '#cd853f',
+ pink: '#ffc0cb',
+ plum: '#dda0dd',
+ powderblue: '#b0e0e6',
+ purple: '#800080',
+ rebeccapurple: '#663399',
+ red: '#f00',
+ rosybrown: '#bc8f8f',
+ royalblue: '#4169e1',
+ saddlebrown: '#8b4513',
+ salmon: '#fa8072',
+ sandybrown: '#f4a460',
+ seagreen: '#2e8b57',
+ seashell: '#fff5ee',
+ sienna: '#a0522d',
+ silver: '#c0c0c0',
+ skyblue: '#87ceeb',
+ slateblue: '#6a5acd',
+ slategray: '#708090',
+ slategrey: '#708090',
+ snow: '#fffafa',
+ springgreen: '#00ff7f',
+ steelblue: '#4682b4',
+ tan: '#d2b48c',
+ teal: '#008080',
+ thistle: '#d8bfd8',
+ tomato: '#ff6347',
+ turquoise: '#40e0d0',
+ violet: '#ee82ee',
+ wheat: '#f5deb3',
+ white: '#fff',
+ whitesmoke: '#f5f5f5',
+ yellow: '#ff0',
+ yellowgreen: '#9acd32'
+};
+
+var toHex = {};
+var toName = {};
+
+for (var name in COLORS) {
+ var hex = COLORS[name];
+ if (name.length < hex.length)
+ toName[hex] = name;
+ else
+ toHex[name] = hex;
+}
+
+HexNameShortener.prototype.shorten = function () {
+ var data = this.value;
+
+ [toHex, toName].forEach(function(conversion) {
+ var pattern = '(' + Object.keys(conversion).join('|') + ')';
+ var colorSwitcher = function(match, colorValue, suffix) {
+ return conversion[colorValue.toLowerCase()] + suffix;
+ };
+ data = data.replace(new RegExp(pattern + '( |,|\\)|$)', 'ig'), colorSwitcher);
+ });
+
+ return data;
+};
+
+module.exports = HexNameShortener;
+++ /dev/null
-module.exports = function HSLToHex(data) {
- // HSL to RGB converter. Both methods adapted from:
- // http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
- var hslToRgb = function(h, s, l) {
- var r, g, b;
-
- // normalize hue orientation b/w 0 and 360 degrees
- h = h % 360;
- if (h < 0)
- h += 360;
- h = ~~h / 360;
-
- if (s < 0)
- s = 0;
- else if (s > 100)
- s = 100;
- s = ~~s / 100;
-
- if (l < 0)
- l = 0;
- else if (l > 100)
- l = 100;
- l = ~~l / 100;
-
- if (s === 0) {
- r = g = b = l; // achromatic
- } else {
- var q = l < 0.5 ?
- l * (1 + s) :
- l + s - l * s;
- var p = 2 * l - q;
- r = hueToRgb(p, q, h + 1/3);
- g = hueToRgb(p, q, h);
- b = hueToRgb(p, q, h - 1/3);
- }
-
- return [~~(r * 255), ~~(g * 255), ~~(b * 255)];
- };
-
- var hueToRgb = function(p, q, t) {
- if (t < 0) t += 1;
- if (t > 1) t -= 1;
- if (t < 1/6) return p + (q - p) * 6 * t;
- if (t < 1/2) return q;
- if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
- return p;
- };
-
- return {
- process: function() {
- return data.replace(/hsl\((-?\d+),(-?\d+)%?,(-?\d+)%?\)/g, function(match, hue, saturation, lightness) {
- var asRgb = hslToRgb(hue, saturation, lightness);
- var redAsHex = asRgb[0].toString(16);
- var greenAsHex = asRgb[1].toString(16);
- var blueAsHex = asRgb[2].toString(16);
-
- return '#' +
- ((redAsHex.length == 1 ? '0' : '') + redAsHex) +
- ((greenAsHex.length == 1 ? '0' : '') + greenAsHex) +
- ((blueAsHex.length == 1 ? '0' : '') + blueAsHex);
- });
- }
- };
-};
--- /dev/null
+// HSL to RGB converter. Both methods adapted from:
+// http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+
+function HSLColor(hue, saturation, lightness) {
+ this.hue = hue;
+ this.saturation = saturation;
+ this.lightness = lightness;
+}
+
+function hslToRgb(h, s, l) {
+ var r, g, b;
+
+ // normalize hue orientation b/w 0 and 360 degrees
+ h = h % 360;
+ if (h < 0)
+ h += 360;
+ h = ~~h / 360;
+
+ if (s < 0)
+ s = 0;
+ else if (s > 100)
+ s = 100;
+ s = ~~s / 100;
+
+ if (l < 0)
+ l = 0;
+ else if (l > 100)
+ l = 100;
+ l = ~~l / 100;
+
+ if (s === 0) {
+ r = g = b = l; // achromatic
+ } else {
+ var q = l < 0.5 ?
+ l * (1 + s) :
+ l + s - l * s;
+ var p = 2 * l - q;
+ r = hueToRgb(p, q, h + 1/3);
+ g = hueToRgb(p, q, h);
+ b = hueToRgb(p, q, h - 1/3);
+ }
+
+ return [~~(r * 255), ~~(g * 255), ~~(b * 255)];
+}
+
+function hueToRgb(p, q, t) {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1/6) return p + (q - p) * 6 * t;
+ if (t < 1/2) return q;
+ if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+}
+
+HSLColor.prototype.toHex = function () {
+ var asRgb = hslToRgb(this.hue, this.saturation, this.lightness);
+ var redAsHex = asRgb[0].toString(16);
+ var greenAsHex = asRgb[1].toString(16);
+ var blueAsHex = asRgb[2].toString(16);
+
+ return '#' +
+ ((redAsHex.length == 1 ? '0' : '') + redAsHex) +
+ ((greenAsHex.length == 1 ? '0' : '') + greenAsHex) +
+ ((blueAsHex.length == 1 ? '0' : '') + blueAsHex);
+};
+
+module.exports = HSLColor;
+++ /dev/null
-module.exports = function LongToShortHex(data) {
- return {
- process: function() {
- return data.replace(/([,: \(])#([0-9a-f]{6})/gi, function(match, prefix, color) {
- if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5])
- return prefix + '#' + color[0] + color[2] + color[4];
- else
- return prefix + '#' + color;
- });
- }
- };
-};
+++ /dev/null
-module.exports = function RGBToHex(data) {
- return {
- process: function() {
- return data.replace(/rgb\((\-?\d+),(\-?\d+),(\-?\d+)\)/g, function(match, red, green, blue) {
- red = Math.max(0, Math.min(~~red, 255));
- green = Math.max(0, Math.min(~~green, 255));
- blue = Math.max(0, Math.min(~~blue, 255));
-
- // Credit: Asen http://jsbin.com/UPUmaGOc/2/edit?js,console
- return '#' + ('00000' + (red << 16 | green << 8 | blue).toString(16)).slice(-6);
- });
- }
- };
-};
--- /dev/null
+function RGB(red, green, blue) {
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+}
+
+RGB.prototype.toHex = function () {
+ var red = Math.max(0, Math.min(~~this.red, 255));
+ var green = Math.max(0, Math.min(~~this.green, 255));
+ var blue = Math.max(0, Math.min(~~this.blue, 255));
+
+ // Credit: Asen http://jsbin.com/UPUmaGOc/2/edit?js,console
+ return '#' + ('00000' + (red << 16 | green << 8 | blue).toString(16)).slice(-6);
+};
+
+module.exports = RGB;
+++ /dev/null
-module.exports = function Shortener(data) {
- var COLORS = {
- aliceblue: '#f0f8ff',
- antiquewhite: '#faebd7',
- aqua: '#0ff',
- aquamarine: '#7fffd4',
- azure: '#f0ffff',
- beige: '#f5f5dc',
- bisque: '#ffe4c4',
- black: '#000',
- blanchedalmond: '#ffebcd',
- blue: '#00f',
- blueviolet: '#8a2be2',
- brown: '#a52a2a',
- burlywood: '#deb887',
- cadetblue: '#5f9ea0',
- chartreuse: '#7fff00',
- chocolate: '#d2691e',
- coral: '#ff7f50',
- cornflowerblue: '#6495ed',
- cornsilk: '#fff8dc',
- crimson: '#dc143c',
- cyan: '#0ff',
- darkblue: '#00008b',
- darkcyan: '#008b8b',
- darkgoldenrod: '#b8860b',
- darkgray: '#a9a9a9',
- darkgreen: '#006400',
- darkgrey: '#a9a9a9',
- darkkhaki: '#bdb76b',
- darkmagenta: '#8b008b',
- darkolivegreen: '#556b2f',
- darkorange: '#ff8c00',
- darkorchid: '#9932cc',
- darkred: '#8b0000',
- darksalmon: '#e9967a',
- darkseagreen: '#8fbc8f',
- darkslateblue: '#483d8b',
- darkslategray: '#2f4f4f',
- darkslategrey: '#2f4f4f',
- darkturquoise: '#00ced1',
- darkviolet: '#9400d3',
- deeppink: '#ff1493',
- deepskyblue: '#00bfff',
- dimgray: '#696969',
- dimgrey: '#696969',
- dodgerblue: '#1e90ff',
- firebrick: '#b22222',
- floralwhite: '#fffaf0',
- forestgreen: '#228b22',
- fuchsia: '#f0f',
- gainsboro: '#dcdcdc',
- ghostwhite: '#f8f8ff',
- gold: '#ffd700',
- goldenrod: '#daa520',
- gray: '#808080',
- green: '#008000',
- greenyellow: '#adff2f',
- grey: '#808080',
- honeydew: '#f0fff0',
- hotpink: '#ff69b4',
- indianred: '#cd5c5c',
- indigo: '#4b0082',
- ivory: '#fffff0',
- khaki: '#f0e68c',
- lavender: '#e6e6fa',
- lavenderblush: '#fff0f5',
- lawngreen: '#7cfc00',
- lemonchiffon: '#fffacd',
- lightblue: '#add8e6',
- lightcoral: '#f08080',
- lightcyan: '#e0ffff',
- lightgoldenrodyellow: '#fafad2',
- lightgray: '#d3d3d3',
- lightgreen: '#90ee90',
- lightgrey: '#d3d3d3',
- lightpink: '#ffb6c1',
- lightsalmon: '#ffa07a',
- lightseagreen: '#20b2aa',
- lightskyblue: '#87cefa',
- lightslategray: '#778899',
- lightslategrey: '#778899',
- lightsteelblue: '#b0c4de',
- lightyellow: '#ffffe0',
- lime: '#0f0',
- limegreen: '#32cd32',
- linen: '#faf0e6',
- magenta: '#ff00ff',
- maroon: '#800000',
- mediumaquamarine: '#66cdaa',
- mediumblue: '#0000cd',
- mediumorchid: '#ba55d3',
- mediumpurple: '#9370db',
- mediumseagreen: '#3cb371',
- mediumslateblue: '#7b68ee',
- mediumspringgreen: '#00fa9a',
- mediumturquoise: '#48d1cc',
- mediumvioletred: '#c71585',
- midnightblue: '#191970',
- mintcream: '#f5fffa',
- mistyrose: '#ffe4e1',
- moccasin: '#ffe4b5',
- navajowhite: '#ffdead',
- navy: '#000080',
- oldlace: '#fdf5e6',
- olive: '#808000',
- olivedrab: '#6b8e23',
- orange: '#ffa500',
- orangered: '#ff4500',
- orchid: '#da70d6',
- palegoldenrod: '#eee8aa',
- palegreen: '#98fb98',
- paleturquoise: '#afeeee',
- palevioletred: '#db7093',
- papayawhip: '#ffefd5',
- peachpuff: '#ffdab9',
- peru: '#cd853f',
- pink: '#ffc0cb',
- plum: '#dda0dd',
- powderblue: '#b0e0e6',
- purple: '#800080',
- rebeccapurple: '#663399',
- red: '#f00',
- rosybrown: '#bc8f8f',
- royalblue: '#4169e1',
- saddlebrown: '#8b4513',
- salmon: '#fa8072',
- sandybrown: '#f4a460',
- seagreen: '#2e8b57',
- seashell: '#fff5ee',
- sienna: '#a0522d',
- silver: '#c0c0c0',
- skyblue: '#87ceeb',
- slateblue: '#6a5acd',
- slategray: '#708090',
- slategrey: '#708090',
- snow: '#fffafa',
- springgreen: '#00ff7f',
- steelblue: '#4682b4',
- tan: '#d2b48c',
- teal: '#008080',
- thistle: '#d8bfd8',
- tomato: '#ff6347',
- turquoise: '#40e0d0',
- violet: '#ee82ee',
- wheat: '#f5deb3',
- white: '#fff',
- whitesmoke: '#f5f5f5',
- yellow: '#ff0',
- yellowgreen: '#9acd32'
- };
-
- var toHex = {};
- var toName = {};
-
- for (var name in COLORS) {
- var color = COLORS[name];
- if (name.length < color.length)
- toName[color] = name;
- else
- toHex[name] = color;
- }
-
- return {
- toHex: toHex,
- toName: toName,
-
- // replace color name with hex values if shorter (or the other way around)
- process: function() {
- [toHex, toName].forEach(function(conversion) {
- var pattern = '(' + Object.keys(conversion).join('|') + ')';
- var colorSwitcher = function(match, prefix, colorValue, suffix) {
- return prefix + conversion[colorValue.toLowerCase()] + suffix;
- };
- data = data.replace(new RegExp('([ :,\\(])' + pattern + '([;\\}!\\) ])', 'ig'), colorSwitcher);
- data = data.replace(new RegExp('(,)' + pattern + '(,)', 'ig'), colorSwitcher);
- });
-
- return data;
- }
- };
-};
if (token.body.length === 0 || (token.body.length == 1 && token.body[0] === ''))
return '';
- return token.block ?
- token.block + '{' + rebuild(token.body, keepBreaks) + '}' :
- token.selector.join(',') + '{' + token.body.join(';') + '}';
+ if (token.block) {
+ var body = rebuild(token.body, keepBreaks, token.isFlatBlock);
+ return body.length > 0 ?
+ token.block + '{' + body + '}' :
+ '';
+ } else {
+ return token.selector.join(',') + '{' + token.body.join(';') + '}';
+ }
})
+ .filter(function (value) { return value.length > 0; })
.join(keepBreaks ? lineBreak : '')
.trim();
}
var PropertyOptimizer = require('../../properties/optimizer');
var CleanUp = require('./clean-up');
+var Splitter = require('../../utils/splitter');
+
+var RGB = require('../../colors/rgb');
+var HSL = require('../../colors/hsl');
+var HexNameShortener = require('../../colors/hex-name-shortener');
+
+var DEFAULT_ROUNDING_PRECISION = 2;
+var CHARSET_TOKEN = '@charset';
+var CHARSET_REGEXP = new RegExp('^' + CHARSET_TOKEN, 'i');
function SimpleOptimizer(options, context) {
this.options = options;
this.propertyOptimizer = new PropertyOptimizer(this.options.compatibility, this.options.aggressiveMerging, context);
}
-function minify(tokens) {
- for (var i = 0, l = tokens.length; i < l; i++) {
- var token = tokens[i];
+function removeUnsupported(token, compatibility) {
+ if (compatibility == 'ie7')
+ return;
+
+ var supported = [];
+ for (var i = 0, l = token.selector.length; i < l; i++) {
+ var selector = token.selector[i];
+
+ if (selector.indexOf('*+html ') === -1 && selector.indexOf('*:first-child+html ') === -1)
+ supported.push(selector);
+ }
+
+ token.selector = supported;
+}
+
+var valueMinifiers = {
+ 'background': function (value) {
+ return value == 'none' || value == 'transparent' ? '0 0' : value;
+ },
+ 'border-*-radius': function (value) {
+ if (value.indexOf('/') == -1)
+ return value;
- if (token.selector) {
- token.selector = CleanUp.selectors(token.selector);
- } else if (token.block) {
- token.block = CleanUp.block(token.block);
- minify(token.body);
+ var parts = value.split(/\s*\/\s*/);
+ if (parts[0] == parts[1])
+ return parts[0];
+ else
+ return parts[0] + '/' + parts[1];
+ },
+ 'filter': function (value) {
+ if (value.indexOf('DXImageTransform') === value.lastIndexOf('DXImageTransform')) {
+ value = value.replace(/progid:DXImageTransform\.Microsoft\.(Alpha|Chroma)/, function (match, filter) {
+ return filter.toLowerCase();
+ });
}
+
+ return value
+ .replace(/,(\S)/g, ', $1')
+ .replace(/ ?= ?/g, '=');
+ },
+ 'font': function (value) {
+ var parts = value.split(' ');
+
+ if (parts[1] != 'normal' && parts[1] != 'bold' && !/^[1-9]00/.test(parts[1]))
+ parts[0] = this['font-weight'](parts[0]);
+
+ return parts.join(' ');
+ },
+ 'font-weight': function (value) {
+ if (value == 'normal')
+ return '400';
+ else if (value == 'bold')
+ return '700';
+ else
+ return value;
+ },
+ 'outline': function (value) {
+ return value == 'none' ? '0' : value;
}
+};
+
+function zeroMinifier(_, value) {
+ return value
+ .replace(/\-0$/g, '0')
+ .replace(/\-0([^\.])/g, '0$1')
+ .replace(/(^|\s)0+([1-9])/g, '$1$2')
+ .replace(/(^|\D)\.0+(\D|$)/g, '$10$2')
+ .replace(/(^|\D)\.0+(\D|$)/g, '$10$2')
+ .replace(/\.([1-9]*)0+(\D|$)/g, function(match, nonZeroPart, suffix) {
+ return (nonZeroPart.length > 0 ? '.' : '') + nonZeroPart + suffix;
+ })
+ .replace(/(^|\D)0\.(\d)/g, '$1.$2');
+}
+
+function precisionMinifier(_, value, precision) {
+ if (precision === undefined)
+ precision = DEFAULT_ROUNDING_PRECISION;
+ var decimalMultiplier = Math.pow(10, precision);
+
+ return value
+ .replace(new RegExp('\\.(\\d{' + (precision + 1) + ',})px', 'g'), function(match, decimalPlaces) {
+ var newFraction = Math.round(parseFloat('.' + decimalPlaces) * decimalMultiplier) / decimalMultiplier;
+ return precision === 0 || newFraction === 0 ?
+ 'px' :
+ '.' + ('' + newFraction).substring('0.'.length) + 'px';
+ })
+ .replace(/(\d)\.($|\D)/g, '$1$2');
+}
+
+function unitMinifier(_, value, compatibility) {
+ var units = ['px', 'em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', '%'];
+ if (['ie7', 'ie8'].indexOf(compatibility) == -1)
+ units.push('rem');
+
+ return value.replace(new RegExp('(^|\\s|\\(|,)0(?:' + units.join('|') + ')', 'g'), '$1' + '0');
+}
+
+function multipleZerosMinifier(property, value) {
+ if (property.indexOf('box-shadow') > -1)
+ return value == '0 0 0 0' ? '0 0' : value;
+
+ return value.replace(/^0 0 0 0$/, '0');
+}
+
+function colorMininifier(property, value, compatibility) {
+ value = value
+ .replace(/rgb\((\-?\d+),(\-?\d+),(\-?\d+)\)/g, function (match, red, green, blue) {
+ return new RGB(red, green, blue).toHex();
+ })
+ .replace(/hsl\((-?\d+),(-?\d+)%?,(-?\d+)%?\)/g, function (match, hue, saturation, lightness) {
+ return new HSL(hue, saturation, lightness).toHex();
+ })
+ .replace(/(^|[^='"])#([0-9a-f]{6})/gi, function (match, prefix, color) {
+ if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5])
+ return prefix + '#' + color[0] + color[2] + color[4];
+ else
+ return prefix + '#' + color;
+ })
+ .replace(/(rgb|rgba|hsl|hsla)\(([^\)]+)\)/g, function(match, colorFunction, colorDef) {
+ var tokens = colorDef.split(',');
+ var applies = colorFunction == 'hsl' || colorFunction == 'hsla' || tokens[0].indexOf('%') > -1;
+ if (!applies)
+ return match;
+
+ if (tokens[1].indexOf('%') == -1)
+ tokens[1] += '%';
+ if (tokens[2].indexOf('%') == -1)
+ tokens[2] += '%';
+ return colorFunction + '(' + tokens.join(',') + ')';
+ });
+
+ if (!compatibility) {
+ value = value.replace(/(?:rgba|hsla)\(0,0%?,0%?,0\)/g, function (match) {
+ if (new Splitter(',').split(value).pop().indexOf('gradient(') > -1)
+ return match;
+
+ return 'transparent';
+ });
+ }
+
+ return new HexNameShortener(value).shorten();
+}
+
+function reduce(body, options) {
+ return body.map(function (token) {
+ var firstColon = token.indexOf(':');
+ var property = token.substring(0, firstColon);
+ var value = token.substring(firstColon + 1);
+ var important = false;
+
+ if (!options.compatibility && (property[0] == '_' || property[0] == '*'))
+ return '';
+
+ if (value.indexOf('!important') > 0 || value.indexOf('! important') > 0) {
+ value = value.substring(0, value.indexOf('!')).trim();
+ important = true;
+ }
+
+ if (property.indexOf('border') === 0 && property.indexOf('radius') > 0)
+ value = valueMinifiers['border-*-radius'](value);
+
+ if (valueMinifiers[property])
+ value = valueMinifiers[property](value);
+
+ value = zeroMinifier(property, value);
+ value = precisionMinifier(property, value, options.roundingPrecision);
+ value = unitMinifier(property, value, options.compatibility);
+ value = multipleZerosMinifier(property, value);
+ value = colorMininifier(property, value, options.compatibility);
+
+ return property + ':' + value + (important ? '!important' : '');
+ });
}
SimpleOptimizer.prototype.optimize = function(tokens) {
- minify(tokens);
+ var self = this;
+ var hasCharset = false;
+
+ function _optimize(tokens) {
+ for (var i = 0, l = tokens.length; i < l; i++) {
+ var token = tokens[i];
+ // FIXME: why it's so?
+ if (!token)
+ break;
+
+ if (token.selector) {
+ token.selector = CleanUp.selectors(token.selector);
+
+ removeUnsupported(token, self.options.compatibility);
+ if (token.selector.length === 0) {
+ tokens.splice(i, 1);
+ i--;
+ continue;
+ }
+
+ token.body = reduce(token.body, self.options);
+ } else if (token.block) {
+ token.block = CleanUp.block(token.block);
+ if (token.isFlatBlock)
+ token.body = reduce(token.body, self.options);
+ else
+ _optimize(token.body);
+ } else if (typeof token === 'string') {
+ if (CHARSET_REGEXP.test(token)) {
+ if (hasCharset || token.indexOf(CHARSET_TOKEN) == -1) {
+ tokens.splice(i, 1);
+ i++;
+ } else {
+ hasCharset = true;
+ tokens.splice(i, 1);
+ tokens.unshift(token.replace(CHARSET_REGEXP, CHARSET_TOKEN));
+ }
+ }
+ }
+ }
+ }
+
+ _optimize(tokens);
};
module.exports = SimpleOptimizer;
--- /dev/null
+var vows = require('vows');
+var assert = require('assert');
+var HexNameShortener = require('../../lib/colors/hex-name-shortener');
+
+var COLORS = {
+ aliceblue: '#f0f8ff',
+ antiquewhite: '#faebd7',
+ aqua: '#0ff',
+ aquamarine: '#7fffd4',
+ azure: '#f0ffff',
+ beige: '#f5f5dc',
+ bisque: '#ffe4c4',
+ black: '#000',
+ blanchedalmond: '#ffebcd',
+ blue: '#00f',
+ blueviolet: '#8a2be2',
+ brown: '#a52a2a',
+ burlywood: '#deb887',
+ cadetblue: '#5f9ea0',
+ chartreuse: '#7fff00',
+ chocolate: '#d2691e',
+ coral: '#ff7f50',
+ cornflowerblue: '#6495ed',
+ cornsilk: '#fff8dc',
+ crimson: '#dc143c',
+ cyan: '#0ff',
+ darkblue: '#00008b',
+ darkcyan: '#008b8b',
+ darkgoldenrod: '#b8860b',
+ darkgray: '#a9a9a9',
+ darkgreen: '#006400',
+ darkgrey: '#a9a9a9',
+ darkkhaki: '#bdb76b',
+ darkmagenta: '#8b008b',
+ darkolivegreen: '#556b2f',
+ darkorange: '#ff8c00',
+ darkorchid: '#9932cc',
+ darkred: '#8b0000',
+ darksalmon: '#e9967a',
+ darkseagreen: '#8fbc8f',
+ darkslateblue: '#483d8b',
+ darkslategray: '#2f4f4f',
+ darkslategrey: '#2f4f4f',
+ darkturquoise: '#00ced1',
+ darkviolet: '#9400d3',
+ deeppink: '#ff1493',
+ deepskyblue: '#00bfff',
+ dimgray: '#696969',
+ dimgrey: '#696969',
+ dodgerblue: '#1e90ff',
+ firebrick: '#b22222',
+ floralwhite: '#fffaf0',
+ forestgreen: '#228b22',
+ fuchsia: '#f0f',
+ gainsboro: '#dcdcdc',
+ ghostwhite: '#f8f8ff',
+ gold: '#ffd700',
+ goldenrod: '#daa520',
+ gray: '#808080',
+ green: '#008000',
+ greenyellow: '#adff2f',
+ grey: '#808080',
+ honeydew: '#f0fff0',
+ hotpink: '#ff69b4',
+ indianred: '#cd5c5c',
+ indigo: '#4b0082',
+ ivory: '#fffff0',
+ khaki: '#f0e68c',
+ lavender: '#e6e6fa',
+ lavenderblush: '#fff0f5',
+ lawngreen: '#7cfc00',
+ lemonchiffon: '#fffacd',
+ lightblue: '#add8e6',
+ lightcoral: '#f08080',
+ lightcyan: '#e0ffff',
+ lightgoldenrodyellow: '#fafad2',
+ lightgray: '#d3d3d3',
+ lightgreen: '#90ee90',
+ lightgrey: '#d3d3d3',
+ lightpink: '#ffb6c1',
+ lightsalmon: '#ffa07a',
+ lightseagreen: '#20b2aa',
+ lightskyblue: '#87cefa',
+ lightslategray: '#778899',
+ lightslategrey: '#778899',
+ lightsteelblue: '#b0c4de',
+ lightyellow: '#ffffe0',
+ lime: '#0f0',
+ limegreen: '#32cd32',
+ linen: '#faf0e6',
+ magenta: '#ff00ff',
+ maroon: '#800000',
+ mediumaquamarine: '#66cdaa',
+ mediumblue: '#0000cd',
+ mediumorchid: '#ba55d3',
+ mediumpurple: '#9370db',
+ mediumseagreen: '#3cb371',
+ mediumslateblue: '#7b68ee',
+ mediumspringgreen: '#00fa9a',
+ mediumturquoise: '#48d1cc',
+ mediumvioletred: '#c71585',
+ midnightblue: '#191970',
+ mintcream: '#f5fffa',
+ mistyrose: '#ffe4e1',
+ moccasin: '#ffe4b5',
+ navajowhite: '#ffdead',
+ navy: '#000080',
+ oldlace: '#fdf5e6',
+ olive: '#808000',
+ olivedrab: '#6b8e23',
+ orange: '#ffa500',
+ orangered: '#ff4500',
+ orchid: '#da70d6',
+ palegoldenrod: '#eee8aa',
+ palegreen: '#98fb98',
+ paleturquoise: '#afeeee',
+ palevioletred: '#db7093',
+ papayawhip: '#ffefd5',
+ peachpuff: '#ffdab9',
+ peru: '#cd853f',
+ pink: '#ffc0cb',
+ plum: '#dda0dd',
+ powderblue: '#b0e0e6',
+ purple: '#800080',
+ rebeccapurple: '#663399',
+ red: '#f00',
+ rosybrown: '#bc8f8f',
+ royalblue: '#4169e1',
+ saddlebrown: '#8b4513',
+ salmon: '#fa8072',
+ sandybrown: '#f4a460',
+ seagreen: '#2e8b57',
+ seashell: '#fff5ee',
+ sienna: '#a0522d',
+ silver: '#c0c0c0',
+ skyblue: '#87ceeb',
+ slateblue: '#6a5acd',
+ slategray: '#708090',
+ slategrey: '#708090',
+ snow: '#fffafa',
+ springgreen: '#00ff7f',
+ steelblue: '#4682b4',
+ tan: '#d2b48c',
+ teal: '#008080',
+ thistle: '#d8bfd8',
+ tomato: '#ff6347',
+ turquoise: '#40e0d0',
+ violet: '#ee82ee',
+ wheat: '#f5deb3',
+ white: '#fff',
+ whitesmoke: '#f5f5f5',
+ yellow: '#ff0',
+ yellowgreen: '#9acd32'
+};
+
+function colorShorteningContext() {
+ var context = {};
+
+ function shortened(target) {
+ return function (source) {
+ assert.equal(new HexNameShortener(source).shorten(), target);
+ };
+ }
+
+ for (var name in COLORS) {
+ var hex = COLORS[name];
+ var from, to;
+
+ if (hex == COLORS.gray)
+ name = 'grey';
+
+ if (hex.length <= name.length) {
+ from = name;
+ to = hex;
+ } else {
+ from = hex;
+ to = name;
+ }
+
+ context['should turn \'' + from + '\' into \'' + to + '\''] = {
+ topic: from,
+ shortened: shortened(to)
+ };
+ }
+
+ return context;
+}
+
+vows.describe(HexNameShortener)
+ .addBatch(colorShorteningContext())
+ .export(module);
var assert = require('assert');
var path = require('path');
var CleanCSS = require('../index');
-var ColorShortener = require('../lib/colors/shortener');
var lineBreak = process.platform == 'win32' ? '\r\n' : '\n';
var cssContext = function(groups, options) {
return context;
};
-var colorShorteningContext = function() {
- var shortenerContext = {};
- var shortener = new ColorShortener();
-
- ['toName', 'toHex'].forEach(function(type) {
- for (var from in shortener[type]) {
- var to = shortener[type][from];
- shortenerContext['should turn ' + from + ' into ' + to] = [
- 'a{color:' + from + '}',
- 'a{color:' + to + '}'
- ];
- }
- });
-
- return cssContext(shortenerContext);
-};
-
var redefineContext = function(redefinitions, options) {
var context = {};
var vendorPrefixes = ['', '-moz-', '-o-', '-webkit-']; // there is no -ms-animation nor -ms-transition.
'a{border-radius:5px}'
]
}),
- 'shortening colors': colorShorteningContext(),
'font weights': cssContext({
'font-weight:normal to 400': [
'p{font-weight:normal}',
],
'duplicates - same context': [
'a{color:red}div{color:blue}a{color:red}',
- 'div{color:blue}a{color:red}'
+ 'div{color:#00f}a{color:red}'
],
'duplicates - different contexts': [
'a{color:red}div{color:blue}@media screen{a{color:red}}',
- 'a{color:red}div{color:blue}@media screen{a{color:red}}'
+ 'a{color:red}div{color:#00f}@media screen{a{color:red}}'
],
'adjacent': [
'a{color:red}a{display:block;width:100px}div{color:#fff}',
],
'duplicates - same context': [
'a{color:red}div{color:blue}a{color:red}',
- 'a{color:red}div{color:blue}a{color:red}'
+ 'a{color:red}div{color:#00f}a{color:red}'
],
'duplicates - different contexts': [
'a{color:red}div{color:blue}@media screen{a{color:red}}',
- 'a{color:red}div{color:blue}@media screen{a{color:red}}'
+ 'a{color:red}div{color:#00f}@media screen{a{color:red}}'
],
'adjacent': [
'a{color:red}a{display:block;width:100px}div{color:#fff}',
]
}, { noAdvanced: true })
)
+ .addBatch(
+ optimizerContext('@charset', {
+ 'multiple': [
+ '@charset \'utf-8\';a{color:red}@charset \'utf-8\';',
+ '@charset \'utf-8\';a{color:red}'
+ ],
+ 'not at beginning': [
+ 'a{color:red}@charset \'utf-8\';',
+ '@charset \'utf-8\';a{color:red}'
+ ],
+ 'different case': [
+ 'a{color:red}@ChArSeT \'utf-8\';',
+ 'a{color:red}'
+ ]
+ })
+ )
+ .addBatch(
+ optimizerContext('@font-face', {
+ 'rebuilding': [
+ '@font-face{font-family:PublicVintage;src:url(/PublicVintage.otf) format(\'opentype\')}',
+ '@font-face{font-family:PublicVintage;src:url(/PublicVintage.otf) format(\'opentype\')}'
+ ]
+ })
+ )
.export(module);
var Tokenizer = require('../../../lib/selectors/tokenizer');
var SimpleOptimizer = require('../../../lib/selectors/optimizers/simple');
-function selectorContext(specs) {
+function selectorContext(group, specs, options) {
var context = {};
+ options = options || {};
function optimized(selectors) {
return function (source) {
var tokens = new Tokenizer().toTokens(source);
- new SimpleOptimizer({}).optimize(tokens);
+ new SimpleOptimizer(options).optimize(tokens);
- assert.deepEqual(tokens[0].selector, selectors);
+ assert.deepEqual(tokens[0] ? tokens[0].selector : null, selectors);
};
}
for (var name in specs) {
- context['selector - ' + name] = {
+ context['selector - ' + group + ' - ' + name] = {
+ topic: specs[name][0],
+ optimized: optimized(specs[name][1])
+ };
+ }
+
+ return context;
+}
+
+function propertyContext(group, specs, options) {
+ var context = {};
+ options = options || {};
+
+ function optimized(selectors) {
+ return function (source) {
+ var tokens = new Tokenizer().toTokens(source);
+ new SimpleOptimizer(options).optimize(tokens);
+
+ assert.deepEqual(tokens[0].body, selectors);
+ };
+ }
+
+ for (var name in specs) {
+ context['property - ' + group + ' - ' + name] = {
topic: specs[name][0],
optimized: optimized(specs[name][1])
};
vows.describe(SimpleOptimizer)
.addBatch(
- selectorContext({
+ selectorContext('default', {
'optimized': [
'a{}',
['a']
'line breaks': [
' div >\n\r\n span{}',
['div>span']
+ ],
+ '+html': [
+ '*+html .foo{display:inline}',
+ null
]
})
)
+ .addBatch(
+ selectorContext('ie8', {
+ '+html': [
+ '*+html .foo{display:inline}',
+ null
+ ],
+ '+first-child html': [
+ '*:first-child+html .foo{display:inline}',
+ null
+ ],
+ '+html - complex': [
+ '*+html .foo,.bar{display:inline}',
+ ['.bar']
+ ]
+ }, { compatibility: 'ie8' })
+ )
+ .addBatch(
+ selectorContext('ie7', {
+ '+html': [
+ '*+html .foo{display:inline}',
+ ['*+html .foo']
+ ],
+ '+html - complex': [
+ '*+html .foo,.bar{display:inline}',
+ ['*+html .foo', '.bar']
+ ]
+ }, { compatibility: 'ie7' })
+ )
+ .addBatch(
+ propertyContext('@background', {
+ 'none to 0 0': [
+ 'a{background:none}',
+ ['background:0 0']
+ ],
+ 'transparent to 0 0': [
+ 'a{background:transparent}',
+ ['background:0 0']
+ ],
+ 'any other': [
+ 'a{background:red}',
+ ['background:red']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('@border-*-radius', {
+ 'spaces around /': [
+ 'a{border-top-left-radius:2em / 1em}',
+ ['border-top-left-radius:2em/1em']
+ ],
+ 'symmetric expanded to shorthand': [
+ 'a{border-top-left-radius:1em 2em 3em 4em / 1em 2em 3em 4em}',
+ ['border-top-left-radius:1em 2em 3em 4em']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('@box-shadow', {
+ 'four zeros': [
+ 'a{box-shadow:0 0 0 0}',
+ ['box-shadow:0 0']
+ ],
+ 'four zeros in vendor prefixed': [
+ 'a{-webkit-box-shadow:0 0 0 0}',
+ ['-webkit-box-shadow:0 0']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('colors', {
+ 'rgb to hex': [
+ 'a{color:rgb(255,254,253)}',
+ ['color:#fffefd']
+ ],
+ 'rgba not to hex': [
+ 'a{color:rgba(255,254,253,.5)}',
+ ['color:rgba(255,254,253,.5)']
+ ],
+ 'hsl to hex': [
+ 'a{color:hsl(240,100%,50%)}',
+ ['color:#00f']
+ ],
+ 'hsla not to hex': [
+ 'a{color:hsla(240,100%,50%,.5)}',
+ ['color:hsla(240,100%,50%,.5)']
+ ],
+ 'long hex to short hex': [
+ 'a{color:#ff00ff}',
+ ['color:#f0f']
+ ],
+ 'hex to name': [
+ 'a{color:#f00}',
+ ['color:red']
+ ],
+ 'name to hex': [
+ 'a{color:white}',
+ ['color:#fff']
+ ],
+ 'transparent black rgba to transparent': [
+ 'a{color:rgba(0,0,0,0)}',
+ ['color:transparent']
+ ],
+ 'transparent non-black rgba': [
+ 'a{color:rgba(255,0,0,0)}',
+ ['color:rgba(255,0,0,0)']
+ ],
+ 'transparent black hsla to transparent': [
+ 'a{color:hsla(0,0%,0%,0)}',
+ ['color:transparent']
+ ],
+ 'transparent non-black hsla': [
+ 'a{color:rgba(240,0,0,0)}',
+ ['color:rgba(240,0,0,0)']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('colors - ie8 compatibility', {
+ 'transparent black rgba': [
+ 'a{color:rgba(0,0,0,0)}',
+ ['color:rgba(0,0,0,0)']
+ ],
+ 'transparent non-black rgba': [
+ 'a{color:rgba(255,0,0,0)}',
+ ['color:rgba(255,0,0,0)']
+ ],
+ 'transparent black hsla': [
+ 'a{color:hsla(0,0%,0%,0)}',
+ ['color:hsla(0,0%,0%,0)']
+ ],
+ 'transparent non-black hsla': [
+ 'a{color:rgba(240,0,0,0)}',
+ ['color:rgba(240,0,0,0)']
+ ]
+ }, { compatibility: 'ie8' })
+ )
+ .addBatch(
+ propertyContext('@filter', {
+ 'spaces after comma': [
+ 'a{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=\'#cccccc\',endColorstr=\'#000000\', enabled=true)}',
+ ['filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=\'#cccccc\', endColorstr=\'#000000\', enabled=true)']
+ ],
+ 'single Alpha filter': [
+ 'a{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80)}',
+ ['filter:alpha(Opacity=80)']
+ ],
+ 'single Chroma filter': [
+ 'a{filter:progid:DXImageTransform.Microsoft.Chroma(color=#919191)}',
+ ['filter:chroma(color=#919191)']
+ ],
+ 'multiple filters': [
+ 'a{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80) progid:DXImageTransform.Microsoft.Chroma(color=#919191)}',
+ ['filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80) progid:DXImageTransform.Microsoft.Chroma(color=#919191)']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('@font', {
+ 'in shorthand': [
+ 'a{font:normal 13px/20px sans-serif}',
+ ['font:400 13px/20px sans-serif']
+ ],
+ 'in shorthand with fractions': [
+ 'a{font:bold .9em sans-serif}',
+ ['font:700 .9em sans-serif']
+ ],
+ 'with font wariant and style': [
+ 'a{font:normal normal normal 13px/20px sans-serif}',
+ ['font:normal normal normal 13px/20px sans-serif']
+ ],
+ 'with mixed order of variant and style': [
+ 'a{font:normal 300 normal 13px/20px sans-serif}',
+ ['font:normal 300 normal 13px/20px sans-serif']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('@font-weight', {
+ 'normal to 400': [
+ 'a{font-weight:normal}',
+ ['font-weight:400']
+ ],
+ 'bold to 700': [
+ 'a{font-weight:bold}',
+ ['font-weight:700']
+ ],
+ 'any other': [
+ 'a{font-weight:bolder}',
+ ['font-weight:bolder']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('ie hacks', {
+ 'underscore': [
+ 'a{_width:100px}',
+ ['']
+ ],
+ 'star': [
+ 'a{*width:100px}',
+ ['']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('ie hacks in compatibility mode', {
+ 'underscore': [
+ 'a{_width:100px}',
+ ['_width:100px']
+ ],
+ 'star': [
+ 'a{*width:100px}',
+ ['*width:100px']
+ ]
+ }, { compatibility: 'ie8' })
+ )
+ .addBatch(
+ propertyContext('important', {
+ 'minified': [
+ 'a{color:red!important}',
+ ['color:red!important']
+ ],
+ 'space before !': [
+ 'a{color:red !important}',
+ ['color:red!important']
+ ],
+ 'space after !': [
+ 'a{color:red! important}',
+ ['color:red!important']
+ ]
+ }, { compatibility: 'ie8' })
+ )
+ .addBatch(
+ propertyContext('@outline', {
+ 'none to 0': [
+ 'a{outline:none}',
+ ['outline:0']
+ ],
+ 'any other': [
+ 'a{outline:10px}',
+ ['outline:10px']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('rounding', {
+ 'pixels': [
+ 'a{transform:translateY(123.31135px)}',
+ ['transform:translateY(123.311px)']
+ ],
+ 'percents': [
+ 'a{left:20.1231%}',
+ ['left:20.1231%']
+ ],
+ 'ems': [
+ 'a{left:1.1231em}',
+ ['left:1.1231em']
+ ]
+ }, { roundingPrecision: 3 })
+ )
+ .addBatch(
+ propertyContext('units', {
+ 'pixels': [
+ 'a{width:0px}',
+ ['width:0']
+ ],
+ 'mixed units': [
+ 'a{margin:0em 0rem 0px 0pt}',
+ ['margin:0']
+ ],
+ 'mixed vales': [
+ 'a{padding:10px 0em 30% 0rem}',
+ ['padding:10px 0 30% 0']
+ ]
+ })
+ )
+ .addBatch(
+ propertyContext('units in compatibility mode', {
+ 'pixels': [
+ 'a{width:0px}',
+ ['width:0']
+ ],
+ 'mixed units': [
+ 'a{margin:0em 0rem 0px 0pt}',
+ ['margin:0 0rem 0 0']
+ ],
+ 'mixed vales': [
+ 'a{padding:10px 0em 30% 0rem}',
+ ['padding:10px 0 30% 0rem']
+ ]
+ }, { compatibility: 'ie8' })
+ )
+ .addBatch(
+ propertyContext('zeros', {
+ '-0 to 0': [
+ 'a{margin:-0}',
+ ['margin:0']
+ ],
+ '-0px to 0': [
+ 'a{margin:-0px}',
+ ['margin:0']
+ ],
+ 'missing': [
+ 'a{opacity:1.}',
+ ['opacity:1']
+ ],
+ 'multiple': [
+ 'a{margin:-0 -0 -0 -0}',
+ ['margin:0']
+ ],
+ 'keeps negative non-zero': [
+ 'a{margin:-0.5em}',
+ ['margin:-.5em']
+ ],
+ 'strips leading from value': [
+ 'a{padding:010px 0015px}',
+ ['padding:10px 15px']
+ ],
+ 'strips leading from fractions': [
+ 'a{margin:-0.5em}',
+ ['margin:-.5em']
+ ],
+ 'strips trailing from opacity': [
+ 'a{opacity:1.0}',
+ ['opacity:1']
+ ],
+ '.0 to 0': [
+ 'a{margin:.0 .0 .0 .0}',
+ ['margin:0']
+ ],
+ 'fraction zeros': [
+ 'a{margin:10.0em 15.50em 10.01em 0.0em}',
+ ['margin:10em 15.5em 10.01em 0']
+ ],
+ 'fraction zeros after rounding': [
+ 'a{margin:10.0010px}',
+ ['margin:10px']
+ ],
+ 'four zeros into one': [
+ 'a{margin:0 0 0 0}',
+ ['margin:0']
+ ],
+ 'rect zeros': [
+ 'a{clip:rect(0px 0px 0px 0px)}',
+ ['clip:rect(0 0 0 0)']
+ ],
+ 'rect zeros with non-zero value': [
+ 'a{clip:rect(0.5% 0px 0px 0px)}',
+ ['clip:rect(.5% 0 0 0)']
+ ],
+ 'rect zeros with commas': [
+ 'a{clip:rect(0px, 0px, 0px, 0px)}',
+ ['clip:rect(0,0,0,0)']
+ ],
+ })
+ )
.export(module);