Reworks multiplex components.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Sat, 11 Apr 2015 20:57:53 +0000 (21:57 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 12 Apr 2015 11:15:30 +0000 (12:15 +0100)
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
lib/properties/clone.js
lib/properties/compactable.js
lib/properties/override-compactor.js
lib/properties/populate-components.js
lib/properties/restore.js
test/properties/break-up-test.js
test/properties/restore-test.js

index 49d77d3..9a28029 100644 (file)
@@ -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
 };
index 1f4e73c..5be6441 100644 (file)
@@ -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;
 }
index 47bc0a3..05f5dec 100644 (file)
@@ -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
   },
index b15aee7..ff7fb30 100644 (file)
@@ -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++;
   }
 
index 9544d5a..3093071 100644 (file)
@@ -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;
     }
index 13823b2..5b66e37 100644 (file)
@@ -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
 };
index e3f1cfd..0914470 100644 (file)
@@ -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': {
index 9d1202f..951b9f7 100644 (file)
@@ -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;
 }