From 2e0645dc3902c984b5ec4d33715392108996753e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Sat, 11 Apr 2015 21:57:53 +0100 Subject: [PATCH] Reworks multiplex components. So far values were handled like: `[[['repeat']], [['repeat']]]` which were incompatible with same properties but not components. From now on it's the same for both, e.g. `[['repeat'], [','], ['repeat']]`. --- lib/properties/break-up.js | 14 +++++---- lib/properties/clone.js | 4 +-- lib/properties/compactable.js | 4 +-- lib/properties/override-compactor.js | 37 ++++++++--------------- lib/properties/populate-components.js | 2 +- lib/properties/restore.js | 42 +++++++++++++++++++------- test/properties/break-up-test.js | 43 +++++++++++++++++---------- test/properties/restore-test.js | 2 +- 8 files changed, 85 insertions(+), 63 deletions(-) diff --git a/lib/properties/break-up.js b/lib/properties/break-up.js index 49d77d36..9a280290 100644 --- a/lib/properties/break-up.js +++ b/lib/properties/break-up.js @@ -1,6 +1,7 @@ var wrapSingle = require('./wrap-for-optimizing').single; var Splitter = require('../utils/splitter'); +var MULTIPLEX_SEPARATOR = ','; function _colorFilter(validator) { return function (value) { @@ -114,7 +115,7 @@ function background(property, compactable, validator) { } if (clipSet && !originSet) - origin.value = clip.value; + origin.value = clip.value.slice(0); return components; } @@ -142,7 +143,8 @@ function borderRadius(property, compactable) { remainder.components = fourValues(remainder, compactable); for (var j = 0; j < 4; j++) { - target.components[j].value = [target.components[j].value, remainder.components[j].value]; + target.components[j].multiplex = true; + target.components[j].value = target.components[j].value.concat([['/']]).concat(remainder.components[j].value); } return target.components; @@ -172,7 +174,7 @@ function fourValues(property, compactable) { return components; } -function multipleValues(splitWith) { +function multiplex(splitWith) { return function (property, compactable, validator) { var splitsAt = []; var values = property.value; @@ -204,11 +206,11 @@ function multipleValues(splitWith) { // group component values from each split for (i = 0, l = components.length; i < l; i++) { - components[i].value = [components[i].value]; components[i].multiplex = true; for (j = 1, m = splitComponents.length; j < m; j++) { - components[i].value.push(splitComponents[j][i].value); + components[i].value.push([MULTIPLEX_SEPARATOR]); + Array.prototype.push.apply(components[i].value, splitComponents[j][i].value); } } @@ -306,6 +308,6 @@ module.exports = { borderRadius: borderRadius, fourValues: fourValues, listStyle: listStyle, - multipleValues: multipleValues, + multiplex: multiplex, outline: widthStyleColor }; diff --git a/lib/properties/clone.js b/lib/properties/clone.js index 1f4e73ce..5be6441b 100644 --- a/lib/properties/clone.js +++ b/lib/properties/clone.js @@ -4,12 +4,12 @@ function deep(property) { var cloned = shallow(property); for (var i = property.components.length - 1; i >= 0; i--) { var component = shallow(property.components[i]); - component.value = property.components[i].value; + component.value = property.components[i].value.slice(0); cloned.components.unshift(component); } cloned.dirty = true; - cloned.value = property.value; + cloned.value = property.value.slice(0); return cloned; } diff --git a/lib/properties/compactable.js b/lib/properties/compactable.js index 47bc0a33..05f5dec8 100644 --- a/lib/properties/compactable.js +++ b/lib/properties/compactable.js @@ -49,9 +49,9 @@ var compactable = { 'background-clip', 'background-color' ], - breakUp: breakUp.multipleValues(breakUp.background), + breakUp: breakUp.multiplex(breakUp.background), defaultValue: '0 0', - restore: restore.multipleValues(restore.background), + restore: restore.multiplex(restore.background), shortestValue: '0', shorthand: true }, diff --git a/lib/properties/override-compactor.js b/lib/properties/override-compactor.js index b15aee77..ff7fb30f 100644 --- a/lib/properties/override-compactor.js +++ b/lib/properties/override-compactor.js @@ -38,30 +38,14 @@ function isComponentOf(shorthand, longhand) { function overrideIntoMultiplex(property, by) { by.unused = true; - for (var i = 0, l = property.value.length; i < l; i++) { - property.value[i] = by.value; - } + turnIntoMultiplex(by, multiplexSize(property)); + property.value = by.value; } function overrideByMultiplex(property, by) { - // FIXME: we store component multiplex values and normal multiplex property values' differently - // e.g [[['no-repeat']], [['no-repeat']]] - // vs - // [['no-repeat'], [','], ['no-repeat']] - // We should rather use the latter as it's the standard way - by.unused = true; - property.value = []; - - for (var i = 0, propertyIndex = 0, l = by.value.length; i < l; i++) { - if (by.value[i] == MULTIPLEX_SEPARATOR) { - propertyIndex++; - continue; - } - - property.value[propertyIndex] = property.value[propertyIndex] || []; - property.value[propertyIndex].push(by.value[i]); - } + property.multiplex = true; + property.value = by.value; } function overrideSimple(property, by) { @@ -100,11 +84,14 @@ function turnIntoMultiplex(property, size) { for (var i = 0, l = property.components.length; i < l; i++) { var component = property.components[i]; - var value = component.value; - component.value = []; + if (component.multiplex) + continue; + + var value = component.value.slice(0); - for (var j = 0; j < size; j++) { - component.value.push(value); + for (var j = 1; j < size; j++) { + component.value.push([MULTIPLEX_SEPARATOR]); + Array.prototype.push.apply(component.value, value); } } } @@ -113,7 +100,7 @@ function multiplexSize(component) { var size = 0; for (var i = 0, l = component.value.length; i < l; i++) { - if (component.value[i] == MULTIPLEX_SEPARATOR) + if (component.value[i][0] == MULTIPLEX_SEPARATOR) size++; } diff --git a/lib/properties/populate-components.js b/lib/properties/populate-components.js index 9544d5ac..30930714 100644 --- a/lib/properties/populate-components.js +++ b/lib/properties/populate-components.js @@ -11,7 +11,7 @@ function populateComponents(properties, validator) { property.components = descriptor.breakUp(property, compactable, validator); if (property.components.length > 0) - property.multiplex = Array.isArray(property.components[0].value[0][0]); + property.multiplex = property.components[0].multiplex; else property.unused = true; } diff --git a/lib/properties/restore.js b/lib/properties/restore.js index 13823b2e..5b66e373 100644 --- a/lib/properties/restore.js +++ b/lib/properties/restore.js @@ -1,4 +1,5 @@ var shallowClone = require('./clone').shallow; +var MULTIPLEX_SEPARATOR = ','; function background(property, compactable, lastInMultiplex) { var components = property.components; @@ -90,11 +91,11 @@ function borderRadius(property, compactable) { var component = property.components[i]; var horizontalComponent = shallowClone(property); - horizontalComponent.value = component.value[0]; + horizontalComponent.value = [component.value[0]]; horizontal.components.push(horizontalComponent); var verticalComponent = shallowClone(property); - verticalComponent.value = component.value[1]; + verticalComponent.value = [component.value[2]]; vertical.components.push(verticalComponent); } @@ -133,28 +134,49 @@ function fourValues(property) { } } -function multipleValues(restoreWith) { +function multiplex(restoreWith) { return function (property, compactable) { if (!property.multiplex) return restoreWith(property, compactable, true); - var repeatCounts = property.components[0].value.length; + var multiplexSize = 0; var restored = []; + var componentMultiplexSoFar = {}; + var i, l; - for (var i = 0; i < repeatCounts; i++) { + // At this point we don't know what's the multiplex size, e.g. how many background layers are there + for (i = 0, l = property.components[0].value.length; i < l; i++) { + if (property.components[0].value[i][0] == MULTIPLEX_SEPARATOR) + multiplexSize++; + } + + for (i = 0; i <= multiplexSize; i++) { var _property = shallowClone(property); + // We split multiplex into parts and restore them one by one for (var j = 0, m = property.components.length; j < m; j++) { - var _component = shallowClone(property.components[j]); - _component.value = property.components[j].value[i]; + var componentToClone = property.components[j]; + var _component = shallowClone(componentToClone); _property.components.push(_component); + + // The trick is some properties has more than one value, so we iterate over values looking for + // a multiplex separator - a comma + for (var k = componentMultiplexSoFar[_component.name] || 0, n = componentToClone.value.length; k < n; k++) { + if (componentToClone.value[k][0] == MULTIPLEX_SEPARATOR) { + componentMultiplexSoFar[_component.name] = k + 1; + break; + } + + _component.value.push(componentToClone.value[k]); + } } - var lastInMultiplex = i == repeatCounts - 1; + // No we can restore shorthand value + var lastInMultiplex = i == multiplexSize; var _restored = restoreWith(_property, compactable, lastInMultiplex); Array.prototype.push.apply(restored, _restored); - if (i < repeatCounts - 1) + if (i < multiplexSize) restored.push([',']); } @@ -184,6 +206,6 @@ module.exports = { background: background, borderRadius: borderRadius, fourValues: fourValues, - multipleValues: multipleValues, + multiplex: multiplex, withoutDefaults: withoutDefaults }; diff --git a/test/properties/break-up-test.js b/test/properties/break-up-test.js index e3f1cfde..09144702 100644 --- a/test/properties/break-up-test.js +++ b/test/properties/break-up-test.js @@ -316,19 +316,19 @@ vows.describe(breakUp) }, 'has border-top-left-radius': function (components) { assert.equal(components[0].name, 'border-top-left-radius'); - assert.deepEqual(components[0].value, [[['0px']], [['1px']]]); + assert.deepEqual(components[0].value, [['0px'], ['/'], ['1px']]); }, 'has border-top-right-radius': function (components) { assert.equal(components[1].name, 'border-top-right-radius'); - assert.deepEqual(components[1].value, [[['1px']], [['2px']]]); + assert.deepEqual(components[1].value, [['1px'], ['/'], ['2px']]); }, 'has border-bottom-right-radius': function (components) { assert.equal(components[2].name, 'border-bottom-right-radius'); - assert.deepEqual(components[2].value, [[['2px']], [['3px']]]); + assert.deepEqual(components[2].value, [['2px'], ['/'], ['3px']]); }, 'has border-bottom-left': function (components) { assert.equal(components[3].name, 'border-bottom-left-radius'); - assert.deepEqual(components[3].value, [[['3px']], [['4px']]]); + assert.deepEqual(components[3].value, [['3px'], ['/'], ['4px']]); } }, 'vendor prefix asymetrical horizontal vertical split': { @@ -340,19 +340,19 @@ vows.describe(breakUp) }, 'has border-top-left-radius': function (components) { assert.equal(components[0].name, '-webkit-border-top-left-radius'); - assert.deepEqual(components[0].value, [[['0px']], [['1px']]]); + assert.deepEqual(components[0].value, [['0px'], ['/'], ['1px']]); }, 'has border-top-right-radius': function (components) { assert.equal(components[1].name, '-webkit-border-top-right-radius'); - assert.deepEqual(components[1].value, [[['1px']], [['4px']]]); + assert.deepEqual(components[1].value, [['1px'], ['/'], ['4px']]); }, 'has border-bottom-right-radius': function (components) { assert.equal(components[2].name, '-webkit-border-bottom-right-radius'); - assert.deepEqual(components[2].value, [[['2px']], [['1px']]]); + assert.deepEqual(components[2].value, [['2px'], ['/'], ['1px']]); }, 'has border-bottom-left': function (components) { assert.equal(components[3].name, '-webkit-border-bottom-left-radius'); - assert.deepEqual(components[3].value, [[['1px']], [['4px']]]); + assert.deepEqual(components[3].value, [['1px'], ['/'], ['4px']]); } } }, @@ -534,44 +534,55 @@ vows.describe(breakUp) }, 'has background-image': function (components) { assert.deepEqual(components[0].name, 'background-image'); - assert.deepEqual(components[0].value, [[['__ESCAPED_URL_CLEAN_CSS0__']], [['url(image2.png)']]]); + assert.deepEqual(components[0].value, [['__ESCAPED_URL_CLEAN_CSS0__'], [','], ['url(image2.png)']]); assert.isTrue(components[0].multiplex); }, 'has background-position': function (components) { assert.deepEqual(components[1].name, 'background-position'); - assert.deepEqual(components[1].value, [[['0'], ['0']], [['2px'], ['3px']]]); + assert.deepEqual(components[1].value, [['0'], ['0'], [','], ['2px'], ['3px']]); assert.isTrue(components[0].multiplex); }, 'has background-size': function (components) { assert.deepEqual(components[2].name, 'background-size'); - assert.deepEqual(components[2].value, [[['auto']], [['50%'], ['60%']]]); + assert.deepEqual(components[2].value, [['auto'], [','], ['50%'], ['60%']]); assert.isTrue(components[0].multiplex); }, 'has background-repeat': function (components) { assert.deepEqual(components[3].name, 'background-repeat'); - assert.deepEqual(components[3].value, [[['repeat']], [['repeat'], ['no-repeat']]]); + assert.deepEqual(components[3].value, [['repeat'], [','], ['repeat'], ['no-repeat']]); assert.isTrue(components[0].multiplex); }, 'has background-attachment': function (components) { assert.deepEqual(components[4].name, 'background-attachment'); - assert.deepEqual(components[4].value, [[['scroll']], [['fixed']]]); + assert.deepEqual(components[4].value, [['scroll'], [','], ['fixed']]); assert.isTrue(components[0].multiplex); }, 'has background-origin': function (components) { assert.deepEqual(components[5].name, 'background-origin'); - assert.deepEqual(components[5].value, [[['padding-box']], [['content-box']]]); + assert.deepEqual(components[5].value, [['padding-box'], [','], ['content-box']]); assert.isTrue(components[0].multiplex); }, 'has background-clip': function (components) { assert.deepEqual(components[6].name, 'background-clip'); - assert.deepEqual(components[6].value, [[['border-box']], [['content-box']]]); + assert.deepEqual(components[6].value, [['border-box'], [','], ['content-box']]); assert.isTrue(components[0].multiplex); }, 'has background-color': function (components) { assert.deepEqual(components[7].name, 'background-color'); - assert.deepEqual(components[7].value, [[['#fff']], [['red']]]); + assert.deepEqual(components[7].value, [['#fff'], [','], ['red']]); assert.isTrue(components[0].multiplex); } + }, + 'background - clip & origin': { + 'topic': function () { + return _breakUp([[['background'], ['__ESCAPED_URL_CLEAN_CSS0__'], ['no-repeat'], ['padding-box'], [','], ['repeat'], ['red']]]); + }, + 'has background-origin': function (components) { + assert.deepEqual(components[5].value, [['padding-box'], [','], ['padding-box']]); + }, + 'has background-clip': function (components) { + assert.deepEqual(components[6].value, [['padding-box'], [','], ['border-box']]); + } } }, 'outline': { diff --git a/test/properties/restore-test.js b/test/properties/restore-test.js index 9d1202fd..951b9f7c 100644 --- a/test/properties/restore-test.js +++ b/test/properties/restore-test.js @@ -13,7 +13,7 @@ function _breakUp(property) { var descriptor = compactable[property[0][0]]; var _property = wrapForOptimizing(property); _property.components = descriptor.breakUp(_property, compactable, validator); - _property.multiplex = Array.isArray(_property.components[0].value[0][0]); + _property.multiplex = _property.components[0].multiplex; return _property; } -- 2.34.1