}
};
+AdvancedOptimizer.prototype.mergeMediaQueries = function (tokens) {
+ var candidates = {};
+ var reduced = [];
+
+ function allProperties(token) {
+ var properties = [];
+
+ if (token.kind == 'selector') {
+ for (var i = token.metadata.bodiesList.length - 1; i >= 0; i--) {
+ var property = token.metadata.bodiesList[i];
+ var splitAt = property.indexOf(':');
+ properties.push([
+ property.substring(0, splitAt),
+ property.substring(splitAt + 1)
+ ]);
+ }
+ } else if (token.kind == 'block') {
+ for (var j = token.body.length - 1; j >= 0; j--) {
+ properties = properties.concat(allProperties(token.body[j]));
+ }
+ }
+
+ return properties;
+ }
+
+ function breakingMove(moved, traversed) {
+ for (var i = traversed.length - 1; i >= 0; i--) {
+ for (var j = moved.length - 1; j >= 0; j--) {
+ var traversedName = traversed[i][0];
+ var traversedValue = traversed[i][1];
+ var movedName = moved[j][0];
+ var movedValue = moved[j][1];
+
+ if (traversedName == movedName && traversedValue != movedValue)
+ return true;
+ }
+ }
+ }
+
+ for (var i = tokens.length - 1; i >= 0; i--) {
+ var token = tokens[i];
+ if (token.kind != 'block' || token.isFlatBlock === true)
+ continue;
+
+ var candidate = candidates[token.value];
+ if (!candidate) {
+ candidate = [];
+ candidates[token.value] = candidate;
+ }
+
+ candidate.push(i);
+ }
+
+ for (var name in candidates) {
+ var positions = candidates[name];
+
+ positionLoop:
+ for (var j = positions.length - 1; j > 0; j--) {
+ var source = tokens[positions[j]];
+ var target = tokens[positions[j - 1]];
+ var movedProperties = allProperties(source);
+
+ for (var k = positions[j] + 1; k < positions[j - 1]; k++) {
+ var traversedProperties = allProperties(tokens[k]);
+ if (breakingMove(movedProperties, traversedProperties))
+ continue positionLoop;
+ }
+
+ target.body = source.body.concat(target.body);
+ source.body = [];
+
+ reduced.push(target);
+ }
+ }
+
+ return reduced;
+};
+
function optimizeProperties(tokens, propertyOptimizer) {
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
self.mergeNonAdjacentBySelector(tokens);
self.mergeNonAdjacentByBody(tokens);
+
+ var reduced = self.mergeMediaQueries(tokens);
+ for (var i = reduced.length - 1; i >= 0; i--) {
+ _optimize(reduced[i].body);
+ }
}
_optimize(tokens);
--- /dev/null
+var vows = require('vows');
+var assert = require('assert');
+var CleanCSS = require('../index');
+
+vows.describe('media queries')
+ .addBatch({
+ 'different ones': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}@media print{div{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media screen{a{color:red}}@media print{div{display:block}}');
+ }
+ },
+ 'other than @media': {
+ topic: new CleanCSS().minify('@font-face{font-family:A}@font-face{font-family:B}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@font-face{font-family:A}@font-face{font-family:B}');
+ }
+ }
+ })
+ .addBatch({
+ 'same two adjacent': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}@media screen{div{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media screen{a{color:red}div{display:block}}');
+ }
+ },
+ 'same three adjacent': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}@media screen{div{display:block}}@media screen{body{width:100%}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media screen{a{color:red}div{display:block}body{width:100%}}');
+ }
+ },
+ 'same two with selectors in between': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}body{width:100%}.one{height:100px}@media screen{div{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, 'body{width:100%}.one{height:100px}@media screen{a{color:red}div{display:block}}');
+ }
+ },
+ 'same two with other @media in between': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}@media (min-width:1024px){body{width:100%}}@media screen{div{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media (min-width:1024px){body{width:100%}}@media screen{a{color:red}div{display:block}}');
+ }
+ },
+ 'same two with breaking properties in between': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}.one{color:#00f}@media screen{div{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media screen{a{color:red}}.one{color:#00f}@media screen{div{display:block}}');
+ }
+ },
+ 'same two with breaking @media in between': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}@media (min-width:1024px){.one{color:#00f}}@media screen{div{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media screen{a{color:red}}@media (min-width:1024px){.one{color:#00f}}@media screen{div{display:block}}');
+ }
+ },
+ 'same two with breaking nested @media in between': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}@media (min-width:1024px){@media screen{.one{color:#00f}}}@media screen{div{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media screen{a{color:red}}@media (min-width:1024px){@media screen{.one{color:#00f}}}@media screen{div{display:block}}');
+ }
+ },
+ 'intermixed': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}@media (min-width:1024px){p{width:100%}}@media screen{div{display:block}}@media (min-width:1024px){body{height:100%}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media screen{a{color:red}div{display:block}}@media (min-width:1024px){p{width:100%}body{height:100%}}');
+ }
+ },
+ 'same two with same values as moved in between': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}@media (min-width:1024px){.one{color:red}}@media screen{div{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media (min-width:1024px){.one{color:red}}@media screen{a{color:red}div{display:block}}');
+ }
+ }
+ })
+ .addBatch({
+ 'further optimizations': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}@media screen{a{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '@media screen{a{color:red;display:block}}');
+ }
+ }
+ })
+ .addBatch({
+ 'with comments': {
+ topic: new CleanCSS().minify('@media screen{a{color:red}}/*! a comment */@media screen{a{display:block}}'),
+ 'get merged': function(minified) {
+ assert.equal(minified.styles, '/*! a comment */@media screen{a{color:red;display:block}}');
+ }
+ }
+ }).export(module);