From: Jakub Pawlowicz Date: Tue, 8 Jul 2014 23:28:03 +0000 (+0100) Subject: Fixes #304 - merging multiple backgrounds. X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=8ea1f11c855b1847023e1a5c2cdac4b7260f6cc9;p=clean-css.git Fixes #304 - merging multiple backgrounds. --- diff --git a/lib/properties/processable.js b/lib/properties/processable.js index 35fa82b4..0e961fbd 100644 --- a/lib/properties/processable.js +++ b/lib/properties/processable.js @@ -1,11 +1,11 @@ // Contains the interpretation of CSS properties, as used by the property optimizer - module.exports = (function () { var tokenModule = require('./token'); var validator = require('./validator'); + var CommaSplitter = require('../text/comma-splitter'); // Functions that decide what value can override what. // The main purpose is to disallow removing CSS fallbacks. @@ -177,6 +177,30 @@ module.exports = (function () { return result; }); // Breaks up a background property value + breakUp.commaSeparatedMulitpleValues = function (splitfunc) { + return function (token) { + if (token.value.indexOf(',') === -1) + return splitfunc(token); + + var values = new CommaSplitter(token.value).split(); + var components = []; + + for (var i = 0, l = values.length; i < l; i++) { + token.value = values[i]; + components.push(splitfunc(token)); + } + + for (var j = 0, m = components[0].length; j < m; j++) { + for (var k = 0, n = components.length, newValues = []; k < n; k++) { + newValues.push(components[k][j].value); + } + + components[0][j].value = newValues.join(','); + } + + return components[0]; + }; + }; breakUp.background = function (token) { // Default values var result = Token.makeDefaults(['background-color', 'background-image', 'background-repeat', 'background-position', 'background-attachment'], token.isImportant); @@ -462,6 +486,40 @@ module.exports = (function () { return result; }, + commaSeparatedMulitpleValues: function (assembleFunction) { + return function(prop, tokens, isImportant) { + var partsCount = new CommaSplitter(tokens[0].value).split(); + if (partsCount == 1) + return assembleFunction(prop, tokens, isImportant); + + var merged = []; + + for (var i = 0; i < partsCount.length; i++) { + merged.push([]); + + for (var j = 0; j < tokens.length; j++) { + merged[i].push(new CommaSplitter(tokens[j].value).split()[i]); + } + } + + var mergedValues = []; + var firstProcessed; + for (i = 0; i < partsCount; i++) { + for (var k = 0, n = merged[i].length; k < n; k++) { + tokens[k].value = merged[i][k]; + } + + var processed = assembleFunction(prop, tokens, isImportant); + mergedValues.push(processed.value); + + if (!firstProcessed) + firstProcessed = processed; + } + + firstProcessed.value = mergedValues.join(','); + return firstProcessed; + }; + }, // Handles the cases when some or all the fine-grained properties are set to inherit takeCareOfInherit: function (innerFunc) { return function (prop, tokens, isImportant) { @@ -580,8 +638,10 @@ module.exports = (function () { 'background-position', 'background-attachment' ], - breakUp: breakUp.background, - putTogether: putTogether.takeCareOfInherit(putTogether.bySpacesOmitDefaults), + breakUp: breakUp.commaSeparatedMulitpleValues(breakUp.background), + putTogether: putTogether.commaSeparatedMulitpleValues( + putTogether.takeCareOfInherit(putTogether.bySpacesOmitDefaults) + ), defaultValue: '0 0', shortestValue: '0' }, diff --git a/lib/text/comma-splitter.js b/lib/text/comma-splitter.js new file mode 100644 index 00000000..eae4f37b --- /dev/null +++ b/lib/text/comma-splitter.js @@ -0,0 +1,35 @@ +var Splitter = function CommaSplitter (value) { + this.value = value; +}; + +Splitter.prototype.split = function () { + if (this.value.indexOf(',') === -1) + return [this.value]; + + if (this.value.indexOf('(') === -1) + return this.value.split(','); + + var level = 0; + var cursor = 0; + var lastStart = 0; + var len = this.value.length; + var tokens = []; + + while (cursor++ < len) { + if (this.value[cursor] == '(') { + level++; + } else if (this.value[cursor] == ')') { + level--; + } else if (this.value[cursor] == ',' && level === 0) { + tokens.push(this.value.substring(lastStart, cursor)); + lastStart = cursor + 1; + } + } + + if (lastStart < cursor + 1) + tokens.push(this.value.substring(lastStart)); + + return tokens; +}; + +module.exports = Splitter; diff --git a/test/data/issue-304-min.css b/test/data/issue-304-min.css new file mode 100644 index 00000000..a72a144c --- /dev/null +++ b/test/data/issue-304-min.css @@ -0,0 +1 @@ +.test{background:url(top.png) no-repeat left 0 top -12px,url(bottom.png) no-repeat left 0 bottom -12px,url(middle.png) no-repeat left 0 top 0} diff --git a/test/data/issue-304.css b/test/data/issue-304.css new file mode 100644 index 00000000..35de5fd3 --- /dev/null +++ b/test/data/issue-304.css @@ -0,0 +1,4 @@ +.test { + background: url(top.png) no-repeat, url(bottom.png) no-repeat, url(middle.png) no-repeat; + background-position: left 0px top -12px, left 0px bottom -12px, left 0px top 0px; +} diff --git a/test/text/comma-splitter-test.js b/test/text/comma-splitter-test.js new file mode 100644 index 00000000..2e7539af --- /dev/null +++ b/test/text/comma-splitter-test.js @@ -0,0 +1,17 @@ +var vows = require('vows'); +var assert = require('assert'); +var CommaSplitter = require('../../lib/text/comma-splitter'); + +var split = function (value, expectedValue) { + return function () { + assert.deepEqual(new CommaSplitter(value).split(), expectedValue); + }; +}; + +vows.describe('comma-splitter').addBatch({ + 'empty': split('', ['']), + 'simple': split('none', ['none']), + 'comma separated - level 0': split('#000,#fff,#0f0', ['#000', '#fff', '#0f0']), + 'comma separated - level 1': split('rgb(0,0,0),#fff', ['rgb(0,0,0)', '#fff']), + 'comma separated - level 2': split('linear-gradient(0,#fff,rgba(0,0,0)),red', ['linear-gradient(0,#fff,rgba(0,0,0))', 'red']) +}).export(module);