module.exports = function Optimizer() {
+ var overridable = {
+ 'animation-delay': ['animation'],
+ 'animation-direction': ['animation'],
+ 'animation-duration': ['animation'],
+ 'animation-fill-mode': ['animation'],
+ 'animation-iteration-count': ['animation'],
+ 'animation-name': ['animation'],
+ 'animation-play-state': ['animation'],
+ 'animation-timing-function': ['animation'],
+ '-moz-animation-delay': ['-moz-animation'],
+ '-moz-animation-direction': ['-moz-animation'],
+ '-moz-animation-duration': ['-moz-animation'],
+ '-moz-animation-fill-mode': ['-moz-animation'],
+ '-moz-animation-iteration-count': ['-moz-animation'],
+ '-moz-animation-name': ['-moz-animation'],
+ '-moz-animation-play-state': ['-moz-animation'],
+ '-moz-animation-timing-function': ['-moz-animation'],
+ '-o-animation-delay': ['-o-animation'],
+ '-o-animation-direction': ['-o-animation'],
+ '-o-animation-duration': ['-o-animation'],
+ '-o-animation-fill-mode': ['-o-animation'],
+ '-o-animation-iteration-count': ['-o-animation'],
+ '-o-animation-name': ['-o-animation'],
+ '-o-animation-play-state': ['-o-animation'],
+ '-o-animation-timing-function': ['-o-animation'],
+ '-webkit-animation-delay': ['-webkit-animation'],
+ '-webkit-animation-direction': ['-webkit-animation'],
+ '-webkit-animation-duration': ['-webkit-animation'],
+ '-webkit-animation-fill-mode': ['-webkit-animation'],
+ '-webkit-animation-iteration-count': ['-webkit-animation'],
+ '-webkit-animation-name': ['-webkit-animation'],
+ '-webkit-animation-play-state': ['-webkit-animation'],
+ '-webkit-animation-timing-function': ['-webkit-animation'],
+ 'background-attachment': ['background'],
+ 'background-clip': ['background'],
+ 'background-color': ['background'],
+ 'background-image': ['background'],
+ 'background-origin': ['background'],
+ 'background-position': ['background'],
+ 'background-repeat': ['background'],
+ 'background-size': ['background'],
+ 'border-color': ['border'],
+ 'border-style': ['border'],
+ 'border-width': ['border'],
+ 'border-bottom': ['border'],
+ 'border-bottom-color': ['border-bottom', 'border-color', 'border'],
+ 'border-bottom-style': ['border-bottom', 'border-style', 'border'],
+ 'border-bottom-width': ['border-bottom', 'border-width', 'border'],
+ 'border-left': ['border'],
+ 'border-left-color': ['border-left', 'border-color', 'border'],
+ 'border-left-style': ['border-left', 'border-style', 'border'],
+ 'border-left-width': ['border-left', 'border-width', 'border'],
+ 'border-right': ['border'],
+ 'border-right-color': ['border-right', 'border-color', 'border'],
+ 'border-right-style': ['border-right', 'border-style', 'border'],
+ 'border-right-width': ['border-right', 'border-width', 'border'],
+ 'border-top': ['border'],
+ 'border-top-color': ['border-top', 'border-color', 'border'],
+ 'border-top-style': ['border-top', 'border-style', 'border'],
+ 'border-top-width': ['border-top', 'border-width', 'border'],
+ 'font-family': ['font'],
+ 'font-size': ['font'],
+ 'font-style': ['font'],
+ 'font-variant': ['font'],
+ 'font-weight': ['font'],
+ 'list-style-image': ['list'],
+ 'list-style-position': ['list'],
+ 'list-style-type': ['list'],
+ 'margin-bottom': ['margin'],
+ 'margin-left': ['margin'],
+ 'margin-right': ['margin'],
+ 'margin-top': ['margin'],
+ 'outline-color': ['outline'],
+ 'outline-style': ['outline'],
+ 'outline-width': ['outline'],
+ 'padding-bottom': ['padding'],
+ 'padding-left': ['padding'],
+ 'padding-right': ['padding'],
+ 'padding-top': ['padding'],
+ 'transition-delay': ['transition'],
+ 'transition-duration': ['transition'],
+ 'transition-property': ['transition'],
+ 'transition-timing-function': ['transition'],
+ '-moz-transition-delay': ['-moz-transition'],
+ '-moz-transition-duration': ['-moz-transition'],
+ '-moz-transition-property': ['-moz-transition'],
+ '-moz-transition-timing-function': ['-moz-transition'],
+ '-o-transition-delay': ['-o-transition'],
+ '-o-transition-duration': ['-o-transition'],
+ '-o-transition-property': ['-o-transition'],
+ '-o-transition-timing-function': ['-o-transition'],
+ '-webkit-transition-delay': ['-webkit-transition'],
+ '-webkit-transition-duration': ['-webkit-transition'],
+ '-webkit-transition-property': ['-webkit-transition'],
+ '-webkit-transition-timing-function': ['-webkit-transition']
+ };
+
+ var overrides = {};
+ for (var granular in overridable) {
+ for (var i = 0; i < overridable[granular].length; i++) {
+ var coarse = overridable[granular][i];
+ var list = overrides[coarse];
+
+ if (list)
+ list.push(granular);
+ else
+ overrides[coarse] = [granular];
+ }
+ }
+
var tokenize = function(body) {
var tokens = body.split(';');
var keyValues = [];
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
var firstColon = token.indexOf(':');
- keyValues.push([token.substring(0, firstColon), token.substring(firstColon + 1)]);
+ keyValues.push([
+ token.substring(0, firstColon),
+ token.substring(firstColon + 1),
+ token.indexOf('!important') > -1
+ ]);
}
return keyValues;
};
- var optimize = function(tokenized, allowAdjacent) {
+ var optimize = function(tokens, allowAdjacent) {
var merged = [];
var properties = [];
var lastProperty = null;
+ var rescanTrigger = {};
+
+ var removeOverridenBy = function(property, isImportant) {
+ var overrided = overrides[property];
+ for (var i = 0, l = overrided.length; i < l; i++) {
+ for (var j = 0; j < properties.length; j++) {
+ if (properties[j] != overrided[i] || (merged[j][2] && !isImportant))
+ continue;
- for (var i = 0, l = tokenized.length; i < l; i++) {
- var property = tokenized[i][0];
- var value = tokenized[i][1];
- var alreadyIn = properties.indexOf(property);
+ merged.splice(j, 1);
+ properties.splice(j, 1);
+ j -= 1;
+ }
+ }
+ };
- if (alreadyIn > -1 && merged[alreadyIn][1].indexOf('!important') > 0 && value.indexOf('!important') == -1)
+ for (var i = 0, l = tokens.length; i < l; i++) {
+ var token = tokens[i];
+ var property = token[0];
+ var isImportant = token[2];
+ var alreadyOnPosition = properties.indexOf(property);
+
+ if (alreadyOnPosition > -1 && merged[alreadyOnPosition][2] && !isImportant)
continue;
// comment is necessary - we assume that if two properties are one after another
// then it is intentional way of redefining property which may not be widely supported
- // however if `allowAdjacent` is set then the rule does not apply (see merging two adjacent selectors)
- if (alreadyIn > -1 && (allowAdjacent || lastProperty != property)) {
- merged.splice(alreadyIn, 1);
- properties.splice(alreadyIn, 1);
+ // e.g. a{display:inline-block;display:-moz-inline-box}
+ // however if `allowAdjacent` is set then the rule does not apply
+ // (e.g merging two adjacent selectors)
+ if (alreadyOnPosition > -1 && (allowAdjacent || lastProperty != property)) {
+ merged.splice(alreadyOnPosition, 1);
+ properties.splice(alreadyOnPosition, 1);
}
- merged.push([property, value]);
+ merged.push(token);
properties.push(property);
+ // certain properties (see values of `overridable`) should trigger removal of
+ // more granular properties (see keys of `overridable`)
+ if (rescanTrigger[property])
+ removeOverridenBy(property, isImportant);
+
+ // add rescan triggers - if certain property appears later in the list a rescan needs
+ // to be triggered, e.g 'border-top' triggers a rescan after 'border-top-width' and
+ // 'border-top-color' as they can be removed
+ for (var j = 0, list = overridable[property] || [], m = list.length; j < m; j++)
+ rescanTrigger[list[j]] = true;
+
lastProperty = property;
}
return merged;
};
- var rebuild = function(tokenized) {
+ var rebuild = function(tokens) {
var flat = [];
- for (var i = 0, l = tokenized.length; i < l; i++) {
- flat.push(tokenized[i][0] + ':' + tokenized[i][1]);
+ for (var i = 0, l = tokens.length; i < l; i++) {
+ flat.push(tokens[i][0] + ':' + tokens[i][1]);
}
return flat.join(';');
return {
process: function(body, allowAdjacent) {
- var tokenized = tokenize(body);
- if (tokenized.length < 2)
+ var tokens = tokenize(body);
+ if (tokens.length < 2)
return body;
- var optimized = optimize(tokenized, allowAdjacent);
+ var optimized = optimize(tokens, allowAdjacent);
return rebuild(optimized);
}
};
.bloc_abo{border-top:3px solid #ffd500}
img[width="642"],img[width="312"]{margin-bottom:6px}
img[width="202"]{margin-bottom:4px}
-.btn,.btn_abo,.btn_fonce,.btn_petit{display:inline-block;padding:4px 10px;margin-bottom:0;color:#000b15;text-align:center;font-weight:700;vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);border:1px solid #ccc;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);cursor:pointer}
+.btn,.btn_abo,.btn_fonce,.btn_petit{display:inline-block;padding:4px 10px;margin-bottom:0;color:#000b15;text-align:center;font-weight:700;vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(enabled=false);border:1px solid #ccc;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);cursor:pointer}
.bt_fonce a,.btn_fonce{color:#fff;background-color:#000b15;background-image:-moz-linear-gradient(top,#5d666d,#000b15);background-image:-ms-linear-gradient(top,#5d666d,#000b15);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5d666d),to(#000b15));background-image:-webkit-linear-gradient(top,#5d666d,#000b15);background-image:-o-linear-gradient(top,#5d666d,#000b15);background-image:linear-gradient(top,#5d666d,#000b15);background-repeat:repeat-x;border-color:#000b15;border-color:rgba(0,0,0,.1);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}
.btn_abo{color:#000b15;background-color:#ffc600;background-image:-moz-linear-gradient(top,#ffe562,#ffc600);background-image:-ms-linear-gradient(top,#ffe562,#ffc600);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ffe562),to(#ffc600));background-image:-webkit-linear-gradient(top,#ffe562,#ffc600);background-image:-o-linear-gradient(top,#ffe562,#ffc600);background-image:linear-gradient(top,#ffe562,#ffc600);background-repeat:repeat-x;border-color:#ffc600;border-color:rgba(0,0,0,.1);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}
.btn.large{width:100%;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box}
#nav{clear:both;height:32px;margin:0 auto;width:1000px;background:#fafbfc;border-top:3px solid transparent;border-bottom:1px solid #dddee0}
#nav .conteneur_bordure{width:998px;margin:-3px auto 0;border-top:3px solid #ffd500;border-left:1px solid #d2d6db;border-right:1px solid #d2d6db}
#nav.accueil{width:auto;border-top:solid 3px #2E3942;background:#fff}
-#nav ul{margin-top:-3px;overflow:hidden;width:1000px;margin:-3px auto 0}
+#nav ul{overflow:hidden;width:1000px;margin:-3px auto 0}
#nav.acceuil ul{width:998px}
#nav li{display:block;float:left;border-top-width:3px;border-top-style:solid}
#nav a,#nav span{display:inline-block;height:25px;padding:7px 10px 0 9px;border-left:1px solid #d2d6db;border-bottom:1px solid #d2d6db;font-size:12px;font-weight:700;text-transform:uppercase;color:#000}
return cssContext(shortenerContext);
};
+var redefineContext = function(redefinitions, options) {
+ var context = {};
+ var vendorPrefixes = ['', '-moz-', '-o-', '-webkit-']; // there is no -ms-animation nor -ms-transition.
+
+ for (var property in redefinitions) {
+ for (var i = 0; i < redefinitions[property].length; i++) {
+ var by = redefinitions[property][i];
+ var prefixes = options.vendorPrefixes.indexOf(by) > -1 ? vendorPrefixes : [''];
+
+ for (var j = 0, m = prefixes.length; j < m; j++) {
+ var prefixedProperty = prefixes[j] + property;
+ var prefixedBy = prefixes[j] + by;
+
+ context['should override ' + prefixedProperty + ' by ' + prefixedBy] = [
+ 'a{' + prefixedProperty + ':inherit;' + prefixedBy + ':0}',
+ 'a{' + prefixedBy + ':0}'
+ ];
+ context['should not override ' + prefixedBy + ' by ' + prefixedProperty] =
+ 'a{' + prefixedBy + ':0;' + prefixedProperty + ':inherit}';
+ }
+ }
+ }
+
+ return cssContext(context);
+};
+
vows.describe('clean-units').addBatch({
'identity': cssContext({
'preserve minified content': 'a{color:#f10}'
'font:700 .9rem Helvetica'
],
'multiple changes': [
- 'p{font-weight:bold;width:100%;font:normal 12px Helvetica}',
- 'p{font-weight:700;width:100%;font:400 12px Helvetica}'
+ 'p{font-weight:bold!important;width:100%;font:normal 12px Helvetica}',
+ 'p{font-weight:700!important;width:100%;font:400 12px Helvetica}'
],
'font weight in extended font declarations': 'font:normal normal normal 13px/20px Helvetica'
}),
],
'of supported and unsupported selector': '.one:first-child{color:red}.two:last-child{color:red}',
'of two unsupported selectors': '.one:nth-child(5){color:red}.two:last-child{color:red}'
- }, { selectorsMergeMode: 'ie8' })
+ }, { selectorsMergeMode: 'ie8' }),
+ 'redefined more granular properties': redefineContext({
+ 'animation-delay': ['animation'],
+ 'animation-direction': ['animation'],
+ 'animation-duration': ['animation'],
+ 'animation-fill-mode': ['animation'],
+ 'animation-iteration-count': ['animation'],
+ 'animation-name': ['animation'],
+ 'animation-play-state': ['animation'],
+ 'animation-timing-function': ['animation'],
+ 'background-attachment': ['background'],
+ 'background-clip': ['background'],
+ 'background-color': ['background'],
+ 'background-image': ['background'],
+ 'background-origin': ['background'],
+ 'background-position': ['background'],
+ 'background-repeat': ['background'],
+ 'background-size': ['background'],
+ 'border-color': ['border'],
+ 'border-style': ['border'],
+ 'border-width': ['border'],
+ 'border-bottom': ['border'],
+ 'border-bottom-color': ['border-bottom', 'border-color', 'border'],
+ 'border-bottom-style': ['border-bottom', 'border-style', 'border'],
+ 'border-bottom-width': ['border-bottom', 'border-width', 'border'],
+ 'border-left': ['border'],
+ 'border-left-color': ['border-left', 'border-color', 'border'],
+ 'border-left-style': ['border-left', 'border-style', 'border'],
+ 'border-left-width': ['border-left', 'border-width', 'border'],
+ 'border-right': ['border'],
+ 'border-right-color': ['border-right', 'border-color', 'border'],
+ 'border-right-style': ['border-right', 'border-style', 'border'],
+ 'border-right-width': ['border-right', 'border-width', 'border'],
+ 'border-top': ['border'],
+ 'border-top-color': ['border-top', 'border-color', 'border'],
+ 'border-top-style': ['border-top', 'border-style', 'border'],
+ 'border-top-width': ['border-top', 'border-width', 'border'],
+ 'font-family': ['font'],
+ 'font-size': ['font'],
+ 'font-style': ['font'],
+ 'font-variant': ['font'],
+ 'font-weight': ['font'],
+ 'list-style-image': ['list'],
+ 'list-style-position': ['list'],
+ 'list-style-type': ['list'],
+ 'margin-bottom': ['margin'],
+ 'margin-left': ['margin'],
+ 'margin-right': ['margin'],
+ 'margin-top': ['margin'],
+ 'outline-color': ['outline'],
+ 'outline-style': ['outline'],
+ 'outline-width': ['outline'],
+ 'padding-bottom': ['padding'],
+ 'padding-left': ['padding'],
+ 'padding-right': ['padding'],
+ 'padding-top': ['padding'],
+ 'transition-delay': ['transition'],
+ 'transition-duration': ['transition'],
+ 'transition-property': ['transition'],
+ 'transition-timing-function': ['transition']
+ }, { vendorPrefixes: ['animation', 'transition'] }),
+ 'complex granular properties': cssContext({
+ 'two granular properties': 'a{border-bottom:1px solid red;border-color:red}',
+ 'two same granular properties': 'a{border-color:rgba(0,0,0,.5);border-color:red}',
+ 'two same granular properties redefined': [
+ 'a{border-color:rgba(0,0,0,.5);border-color:red;border:0}',
+ 'a{border:0}'
+ ],
+ 'important granular property redefined': 'a{border-color:red!important;border:0}',
+ 'important granular property redefined with important': [
+ 'a{border-color:red!important;border:0!important}',
+ 'a{border:0!important}'
+ ],
+ 'mix of border properties': [
+ 'a{border-top:1px solid red;border-top-color:#0f0;color:red;border-top-width:2px;border-bottom-width:1px;border:0;border-left:1px solid red}',
+ 'a{color:red;border:0;border-left:1px solid red}'
+ ]
+ })
}).export(module);