diff --git a/lib/optimizer/level-2/compactable.js b/lib/optimizer/level-2/compactable.js
index 88c3ea19f..e77c635d2 100644
--- a/lib/optimizer/level-2/compactable.js
+++ b/lib/optimizer/level-2/compactable.js
@@ -57,11 +57,17 @@ var compactable = {
},
'background-clip': {
canOverride: canOverride.always,
+ componentOf: [
+ 'background'
+ ],
defaultValue: 'border-box',
shortestValue: 'border-box'
},
'background-color': {
canOverride: canOverride.color,
+ componentOf: [
+ 'background'
+ ],
defaultValue: 'transparent',
multiplexLastOnly: true,
nonMergeableValue: 'none',
@@ -69,32 +75,50 @@ var compactable = {
},
'background-image': {
canOverride: canOverride.backgroundImage,
+ componentOf: [
+ 'background'
+ ],
defaultValue: 'none'
},
'background-origin': {
canOverride: canOverride.always,
+ componentOf: [
+ 'background'
+ ],
defaultValue: 'padding-box',
shortestValue: 'border-box'
},
'background-repeat': {
canOverride: canOverride.always,
+ componentOf: [
+ 'background'
+ ],
defaultValue: ['repeat'],
doubleValues: true
},
'background-position': {
canOverride: canOverride.alwaysButIntoFunction,
+ componentOf: [
+ 'background'
+ ],
defaultValue: ['0', '0'],
doubleValues: true,
shortestValue: '0'
},
'background-size': {
canOverride: canOverride.alwaysButIntoFunction,
+ componentOf: [
+ 'background'
+ ],
defaultValue: ['auto'],
doubleValues: true,
shortestValue: '0 0'
},
'background-attachment': {
canOverride: canOverride.always,
+ componentOf: [
+ 'background'
+ ],
defaultValue: 'scroll'
},
'border': {
@@ -106,22 +130,206 @@ var compactable = {
'border-color'
],
defaultValue: 'none',
+ overridesShorthands: [
+ 'border-bottom',
+ 'border-left',
+ 'border-right',
+ 'border-top'
+ ],
+ restore: restore.withoutDefaults,
+ shorthand: true,
+ shorthandComponents: true
+ },
+ 'border-bottom': {
+ breakUp: breakUp.border,
+ canOverride: canOverride.border,
+ components: [
+ 'border-bottom-width',
+ 'border-bottom-style',
+ 'border-bottom-color'
+ ],
+ defaultValue: 'none',
restore: restore.withoutDefaults,
shorthand: true
},
+ 'border-bottom-color': {
+ canOverride: canOverride.color,
+ componentOf: [
+ 'border-bottom',
+ 'border-color'
+ ],
+ defaultValue: 'none'
+ },
+ 'border-bottom-style': {
+ canOverride: canOverride.always,
+ componentOf: [
+ 'border-bottom',
+ 'border-style'
+ ],
+ defaultValue: 'none'
+ },
+ 'border-bottom-width': {
+ canOverride: canOverride.unit,
+ componentOf: [
+ 'border-bottom',
+ 'border-width'
+ ],
+ defaultValue: 'medium',
+ shortestValue: '0'
+ },
'border-color': {
+ breakUp: breakUp.fourValues,
+ canOverride: canOverride.color,
+ componentOf: ['border'],
+ components: [
+ 'border-top-color',
+ 'border-right-color',
+ 'border-bottom-color',
+ 'border-left-color'
+ ],
+ defaultValue: 'none',
+ restore: restore.fourValues,
+ shortestValue: 'red',
+ shorthand: true
+ },
+ 'border-left': {
+ breakUp: breakUp.border,
+ canOverride: canOverride.border,
+ components: [
+ 'border-left-width',
+ 'border-left-style',
+ 'border-left-color'
+ ],
+ defaultValue: 'none',
+ restore: restore.withoutDefaults,
+ shorthand: true
+ },
+ 'border-left-color': {
canOverride: canOverride.color,
+ componentOf: [
+ 'border-color',
+ 'border-left'
+ ],
+ defaultValue: 'none'
+ },
+ 'border-left-style': {
+ canOverride: canOverride.always,
+ componentOf: [
+ 'border-left',
+ 'border-style'
+ ],
+ defaultValue: 'none'
+ },
+ 'border-left-width': {
+ canOverride: canOverride.unit,
+ componentOf: [
+ 'border-left',
+ 'border-width'
+ ],
+ defaultValue: 'medium',
+ shortestValue: '0'
+ },
+ 'border-right': {
+ breakUp: breakUp.border,
+ canOverride: canOverride.border,
+ components: [
+ 'border-right-width',
+ 'border-right-style',
+ 'border-right-color'
+ ],
defaultValue: 'none',
+ restore: restore.withoutDefaults,
shorthand: true
},
+ 'border-right-color': {
+ canOverride: canOverride.color,
+ componentOf: [
+ 'border-color',
+ 'border-right'
+ ],
+ defaultValue: 'none'
+ },
+ 'border-right-style': {
+ canOverride: canOverride.always,
+ componentOf: [
+ 'border-right',
+ 'border-style'
+ ],
+ defaultValue: 'none'
+ },
+ 'border-right-width': {
+ canOverride: canOverride.unit,
+ componentOf: [
+ 'border-right',
+ 'border-width'
+ ],
+ defaultValue: 'medium',
+ shortestValue: '0'
+ },
'border-style': {
+ breakUp: breakUp.fourValues,
canOverride: canOverride.always,
+ componentOf: [
+ 'border'
+ ],
+ components: [
+ 'border-top-style',
+ 'border-right-style',
+ 'border-bottom-style',
+ 'border-left-style'
+ ],
defaultValue: 'none',
+ restore: restore.fourValues,
shorthand: true
},
+ 'border-top': {
+ breakUp: breakUp.border,
+ canOverride: canOverride.border,
+ components: [
+ 'border-top-width',
+ 'border-top-style',
+ 'border-top-color'
+ ],
+ defaultValue: 'none',
+ restore: restore.withoutDefaults,
+ shorthand: true
+ },
+ 'border-top-color': {
+ canOverride: canOverride.color,
+ componentOf: [
+ 'border-color',
+ 'border-top'
+ ],
+ defaultValue: 'none'
+ },
+ 'border-top-style': {
+ canOverride: canOverride.always,
+ componentOf: [
+ 'border-style',
+ 'border-top'
+ ],
+ defaultValue: 'none'
+ },
+ 'border-top-width': {
+ canOverride: canOverride.unit,
+ componentOf: [
+ 'border-top',
+ 'border-width'
+ ],
+ defaultValue: 'medium',
+ shortestValue: '0'
+ },
'border-width': {
+ breakUp: breakUp.fourValues,
canOverride: canOverride.unit,
+ components: [
+ 'border-top-width',
+ 'border-right-width',
+ 'border-bottom-width',
+ 'border-left-width'
+ ],
defaultValue: 'medium',
+ restore: restore.fourValues,
shortestValue: '0',
shorthand: true
},
@@ -150,6 +358,9 @@ var compactable = {
},
'list-style-type' : {
canOverride: canOverride.always,
+ componentOf: [
+ 'list-style'
+ ],
defaultValue: '__hack',
// NOTE: we can't tell the real default value here, it's 'disc' for
and 'decimal' for
// -- this is a hack, but it doesn't matter because this value will be either overridden or it will disappear at the final step anyway
@@ -157,11 +368,17 @@ var compactable = {
},
'list-style-position' : {
canOverride: canOverride.always,
+ componentOf: [
+ 'list-style'
+ ],
defaultValue: 'outside',
shortestValue: 'inside'
},
'list-style-image' : {
canOverride: canOverride.always,
+ componentOf: [
+ 'list-style'
+ ],
defaultValue: 'none'
},
'outline': {
@@ -177,15 +394,24 @@ var compactable = {
},
'outline-color': {
canOverride: canOverride.color,
+ componentOf: [
+ 'outline'
+ ],
defaultValue: 'invert',
shortestValue: 'red'
},
'outline-style': {
canOverride: canOverride.always,
+ componentOf: [
+ 'outline'
+ ],
defaultValue: 'none'
},
'outline-width': {
canOverride: canOverride.unit,
+ componentOf: [
+ 'outline'
+ ],
defaultValue: 'medium',
shortestValue: '0'
},
@@ -223,6 +449,7 @@ var addFourValueShorthand = function (prop, components, options) {
compactable[components[i]] = {
breakUp: options.breakUp || breakUp.fourValues,
canOverride: options.canOverride || canOverride.unit,
+ componentOf: options.componentOf,
defaultValue: options.defaultValue || '0',
shortestValue: options.shortestValue
};
@@ -237,64 +464,33 @@ var addFourValueShorthand = function (prop, components, options) {
prefix + 'border-bottom-left-radius'
], {
breakUp: breakUp.borderRadius,
+ componentOf: [
+ prefix + 'border-radius'
+ ],
restore: restore.borderRadius
});
});
-addFourValueShorthand('border-color', [
- 'border-top-color',
- 'border-right-color',
- 'border-bottom-color',
- 'border-left-color'
-], {
- breakUp: breakUp.fourValues,
- canOverride: canOverride.color,
- defaultValue: 'none',
- shortestValue: 'red'
-});
-
-addFourValueShorthand('border-style', [
- 'border-top-style',
- 'border-right-style',
- 'border-bottom-style',
- 'border-left-style'
-], {
- breakUp: breakUp.fourValues,
- canOverride: canOverride.always,
- defaultValue: 'none'
-});
-
-addFourValueShorthand('border-width', [
- 'border-top-width',
- 'border-right-width',
- 'border-bottom-width',
- 'border-left-width'
-], {
- defaultValue: 'medium',
- shortestValue: '0'
-});
-
addFourValueShorthand('padding', [
'padding-top',
'padding-right',
'padding-bottom',
'padding-left'
-]);
+], {
+ componentOf: [
+ 'padding'
+ ]
+});
addFourValueShorthand('margin', [
'margin-top',
'margin-right',
'margin-bottom',
'margin-left'
-]);
-
-// Adds `componentOf` field to all longhands
-for (var property in compactable) {
- if (compactable[property].shorthand) {
- for (var i = 0, l = compactable[property].components.length; i < l; i++) {
- compactable[compactable[property].components[i]].componentOf = property;
- }
- }
-}
+], {
+ componentOf: [
+ 'margin'
+ ]
+});
module.exports = compactable;
diff --git a/lib/optimizer/level-2/compacting/find-component-in.js b/lib/optimizer/level-2/compacting/find-component-in.js
new file mode 100644
index 000000000..dd7c719d5
--- /dev/null
+++ b/lib/optimizer/level-2/compacting/find-component-in.js
@@ -0,0 +1,40 @@
+var compactable = require('../compactable');
+
+function findComponentIn(shorthand, longhand) {
+ var comparator = nameComparator(longhand);
+
+ return findInDirectComponents(shorthand, comparator) || findInSubComponents(shorthand, comparator);
+}
+
+function nameComparator(to) {
+ return function (property) {
+ return to.name === property.name;
+ };
+}
+
+function findInDirectComponents(shorthand, comparator) {
+ return shorthand.components.filter(comparator)[0];
+}
+
+function findInSubComponents(shorthand, comparator) {
+ var shorthandComponent;
+ var longhandMatch;
+ var i, l;
+
+ if (!compactable[shorthand.name].shorthandComponents) {
+ return;
+ }
+
+ for (i = 0, l = shorthand.components.length; i < l; i++) {
+ shorthandComponent = shorthand.components[i];
+ longhandMatch = findInDirectComponents(shorthandComponent, comparator);
+
+ if (longhandMatch) {
+ return longhandMatch;
+ }
+ }
+
+ return;
+}
+
+module.exports = findComponentIn;
diff --git a/lib/optimizer/level-2/compacting/is-component-of.js b/lib/optimizer/level-2/compacting/is-component-of.js
new file mode 100644
index 000000000..237de7d10
--- /dev/null
+++ b/lib/optimizer/level-2/compacting/is-component-of.js
@@ -0,0 +1,22 @@
+var compactable = require('../compactable');
+
+function isComponentOf(property1, property2, shallow) {
+ return isDirectComponentOf(property1, property2) ||
+ !shallow && !!compactable[property1.name].shorthandComponents && isSubComponentOf(property1, property2);
+}
+
+function isDirectComponentOf(property1, property2) {
+ var descriptor = compactable[property1.name];
+
+ return 'components' in descriptor && descriptor.components.indexOf(property2.name) > -1;
+}
+
+function isSubComponentOf(property1, property2) {
+ return property1
+ .components
+ .some(function (component) {
+ return isDirectComponentOf(component, property2);
+ });
+}
+
+module.exports = isComponentOf;
diff --git a/lib/optimizer/level-2/compacting/optimize.js b/lib/optimizer/level-2/compacting/optimize.js
index 8f071d966..53e2af0dc 100644
--- a/lib/optimizer/level-2/compacting/optimize.js
+++ b/lib/optimizer/level-2/compacting/optimize.js
@@ -13,28 +13,6 @@ var OptimizationLevel = require('../../../options/optimization-level').Optimizat
var serializeProperty = require('../../../writer/one-time').property;
-var shorthands = {
- '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'],
-};
-
function _optimize(properties, mergeAdjacent, aggressiveMerging, validator) {
var overrideMapping = {};
var lastName = null;
@@ -123,18 +101,6 @@ function _optimize(properties, mergeAdjacent, aggressiveMerging, validator) {
} else {
overrideMapping[_name] = overrideMapping[_name] || [];
overrideMapping[_name].push(position);
-
- // TODO: to be removed with
- // certain shorthand (see values of `shorthands`) should trigger removal of
- // longhand properties (see keys of `shorthands`)
- var _shorthands = shorthands[_name];
- if (_shorthands) {
- for (j = _shorthands.length - 1; j >= 0; j--) {
- var shorthand = _shorthands[j];
- overrideMapping[shorthand] = overrideMapping[shorthand] || [];
- overrideMapping[shorthand].push(position);
- }
- }
}
lastName = _name;
@@ -142,7 +108,7 @@ function _optimize(properties, mergeAdjacent, aggressiveMerging, validator) {
}
}
-function compactorOptimize(selector, properties, mergeAdjacent, withCompacting, context) {
+function compactorOptimize(selector, properties, mergeAdjacent, withCompacting, overrideOptions, context) {
var validator = context.validator;
var warnings = context.warnings;
@@ -153,12 +119,15 @@ function compactorOptimize(selector, properties, mergeAdjacent, withCompacting,
for (var i = 0, l = _properties.length; i < l; i++) {
var _property = _properties[i];
if (_property.block) {
- compactorOptimize(selector, _property.value[0][1], mergeAdjacent, withCompacting, context);
+ compactorOptimize(selector, _property.value[0][1], mergeAdjacent, withCompacting, overrideOptions, context);
}
}
+ if (overrideOptions.enabled && context.options.level[OptimizationLevel.Two].compactShorthands) {
+ compactOverrides(_properties, context.options.compatibility, overrideOptions.merging, validator);
+ }
+
if (withCompacting && context.options.level[OptimizationLevel.Two].compactShorthands) {
- compactOverrides(_properties, context.options.compatibility, validator);
compactShorthands(_properties, validator);
}
diff --git a/lib/optimizer/level-2/compacting/override-compactor.js b/lib/optimizer/level-2/compacting/override-compactor.js
index 5d79c3e00..a0f074756 100644
--- a/lib/optimizer/level-2/compacting/override-compactor.js
+++ b/lib/optimizer/level-2/compacting/override-compactor.js
@@ -1,10 +1,14 @@
var hasInherit = require('./has-inherit');
var everyCombination = require('./every-combination');
+var findComponentIn = require('./find-component-in');
+var isComponentOf = require('./is-component-of');
+var overridesNonComponentShorthand = require('./overrides-non-component-shorthand');
var sameVendorPrefixesIn = require('./vendor-prefixes').same;
var canOverride = require('../can-override');
var compactable = require('../compactable');
var deepClone = require('../clone').deep;
+var deepClone = require('../clone').deep;
var restoreWithComponents = require('../restore-with-components');
var shallowClone = require('../clone').shallow;
@@ -15,13 +19,6 @@ var Marker = require('../../../tokenizer/marker');
var serializeProperty = require('../../../writer/one-time').property;
-// Used when searching for a component that matches property
-function nameMatchFilter(to) {
- return function (property) {
- return to.name === property.name;
- };
-}
-
function wouldBreakCompatibility(property, validator) {
for (var i = 0; i < property.components.length; i++) {
var component = property.components[i];
@@ -38,10 +35,6 @@ function wouldBreakCompatibility(property, validator) {
return false;
}
-function isComponentOf(shorthand, longhand) {
- return compactable[shorthand.name].components.indexOf(longhand.name) > -1;
-}
-
function overrideIntoMultiplex(property, by) {
by.unused = true;
@@ -167,10 +160,10 @@ function wouldResultInLongerValue(left, right) {
var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone);
if (left.multiplex) {
- component = multiplexClone.components.filter(nameMatchFilter(simpleClone))[0];
+ component = findComponentIn(multiplexClone, simpleClone);
overrideIntoMultiplex(component, simpleClone);
} else {
- component = simpleClone.components.filter(nameMatchFilter(multiplexClone))[0];
+ component = findComponentIn(simpleClone, multiplexClone);
turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone));
overrideByMultiplex(component, multiplexClone);
}
@@ -222,8 +215,11 @@ function intoLayers(values) {
return layers;
}
-function compactOverrides(properties, compatibility, validator) {
+function compactOverrides(properties, compatibility, merging, validator) {
var mayOverride, right, left, component;
+ var overriddenComponents;
+ var overriddenComponent;
+ var overridingComponent;
var i, j, k;
propertyLoop:
@@ -238,6 +234,7 @@ function compactOverrides(properties, compatibility, validator) {
mayOverride = compactable[right.name].canOverride || canOverride.sameValue;
+ traverseLoop:
for (j = i - 1; j >= 0; j--) {
left = properties[j];
@@ -259,7 +256,7 @@ function compactOverrides(properties, compatibility, validator) {
if (noneOverrideHack(left, right))
continue;
- if (!left.shorthand && right.shorthand && isComponentOf(right, left)) {
+ if (right.shorthand && isComponentOf(right, left)) {
// maybe `left` can be overridden by `right` which is a shorthand?
if (!right.important && left.important)
continue;
@@ -270,12 +267,41 @@ function compactOverrides(properties, compatibility, validator) {
if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator))
continue;
- component = right.components.filter(nameMatchFilter(left))[0];
+ component = findComponentIn(right, left);
mayOverride = (compactable[left.name] && compactable[left.name].canOverride) || canOverride.sameValue;
if (everyCombination(mayOverride, left, component, validator)) {
left.unused = true;
}
- } else if (left.shorthand && !right.shorthand && isComponentOf(left, right)) {
+ } else if (right.shorthand && overridesNonComponentShorthand(right, left)) {
+ // `right` is a shorthand while `left` can be overriden by it, think `border` and `border-top`
+ if (!right.important && left.important) {
+ continue;
+ }
+
+ if (!sameVendorPrefixesIn([left], right.components)) {
+ continue;
+ }
+
+ if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) {
+ continue;
+ }
+
+ overriddenComponents = left.shorthand ?
+ left.components:
+ [left];
+
+ for (k = overriddenComponents.length - 1; k >= 0; k--) {
+ overriddenComponent = overriddenComponents[k];
+ overridingComponent = findComponentIn(right, overriddenComponent);
+ mayOverride = (overriddenComponent.name in compactable && compactable[overriddenComponent.name].canOverride) || canOverride.sameValue;
+
+ if (!everyCombination(mayOverride, left, overridingComponent, validator)) {
+ continue traverseLoop;
+ }
+ }
+
+ left.unused = true;
+ } else if (merging && left.shorthand && !right.shorthand && isComponentOf(left, right, true)) {
// maybe `right` can be pulled into `left` which is a shorthand?
if (right.important && !left.important)
continue;
@@ -292,7 +318,7 @@ function compactOverrides(properties, compatibility, validator) {
if (overridingFunction(left, validator))
continue;
- component = left.components.filter(nameMatchFilter(right))[0];
+ component = findComponentIn(left, right);
if (everyCombination(mayOverride, component, right, validator)) {
var disabledBackgroundMerging =
!compatibility.properties.backgroundClipMerging && component.name.indexOf('background-clip') > -1 ||
@@ -318,7 +344,7 @@ function compactOverrides(properties, compatibility, validator) {
override(component, right);
left.dirty = true;
}
- } else if (left.shorthand && right.shorthand && left.name == right.name) {
+ } else if (merging && left.shorthand && right.shorthand && left.name == right.name) {
// merge if all components can be merged
if (!left.multiplex && right.multiplex)
@@ -347,13 +373,13 @@ function compactOverrides(properties, compatibility, validator) {
overrideShorthand(left, right);
left.dirty = true;
- } else if (left.shorthand && right.shorthand && isComponentOf(left, right)) {
+ } else if (merging && left.shorthand && right.shorthand && isComponentOf(left, right)) {
// border is a shorthand but any of its components is a shorthand too
if (!left.important && right.important)
continue;
- component = left.components.filter(nameMatchFilter(right))[0];
+ component = findComponentIn(left, right);
mayOverride = compactable[right.name].canOverride || canOverride.sameValue;
if (!everyCombination(mayOverride, component, right, validator))
continue;
@@ -367,7 +393,7 @@ function compactOverrides(properties, compatibility, validator) {
if (rightRestored.length > 1)
continue;
- component = left.components.filter(nameMatchFilter(right))[0];
+ component = findComponentIn(left, right);
override(component, right);
right.dirty = true;
} else if (left.name == right.name) {
diff --git a/lib/optimizer/level-2/compacting/overrides-non-component-shorthand.js b/lib/optimizer/level-2/compacting/overrides-non-component-shorthand.js
new file mode 100644
index 000000000..c385218a9
--- /dev/null
+++ b/lib/optimizer/level-2/compacting/overrides-non-component-shorthand.js
@@ -0,0 +1,9 @@
+var compactable = require('../compactable');
+
+function overridesNonComponentShorthand(property1, property2) {
+ return property1.name in compactable &&
+ 'overridesShorthands' in compactable[property1.name] &&
+ compactable[property1.name].overridesShorthands.indexOf(property2.name) > -1;
+}
+
+module.exports = overridesNonComponentShorthand;
diff --git a/lib/optimizer/level-2/compacting/populate-components.js b/lib/optimizer/level-2/compacting/populate-components.js
index 921d3c1d8..c587e83c4 100644
--- a/lib/optimizer/level-2/compacting/populate-components.js
+++ b/lib/optimizer/level-2/compacting/populate-components.js
@@ -2,6 +2,9 @@ var compactable = require('../compactable');
var InvalidPropertyError = require('../invalid-property-error');
function populateComponents(properties, validator, warnings) {
+ var component;
+ var j, m;
+
for (var i = properties.length - 1; i >= 0; i--) {
var property = properties[i];
var descriptor = compactable[property.name];
@@ -12,6 +15,13 @@ function populateComponents(properties, validator, warnings) {
try {
property.components = descriptor.breakUp(property, compactable, validator);
+
+ if (descriptor.shorthandComponents) {
+ for (j = 0, m = property.components.length; j < m; j++) {
+ component = property.components[j];
+ component.components = compactable[component.name].breakUp(component, compactable, validator);
+ }
+ }
} catch (e) {
if (e instanceof InvalidPropertyError) {
property.components = []; // this will set property.unused to true below
diff --git a/lib/optimizer/level-2/compacting/shorthand-compactor.js b/lib/optimizer/level-2/compacting/shorthand-compactor.js
index 04e14a043..34e8f0056 100644
--- a/lib/optimizer/level-2/compacting/shorthand-compactor.js
+++ b/lib/optimizer/level-2/compacting/shorthand-compactor.js
@@ -113,12 +113,18 @@ function invalidateOrCompact(properties, position, candidates, validator) {
function compactShortands(properties, validator) {
var candidates = {};
+ var descriptor;
+ var componentOf;
+ var property;
+ var i, l;
+ var j, m;
if (properties.length < 3)
return;
- for (var i = 0, l = properties.length; i < l; i++) {
- var property = properties[i];
+ for (i = 0, l = properties.length; i < l; i++) {
+ property = properties[i];
+
if (property.unused)
continue;
@@ -128,16 +134,19 @@ function compactShortands(properties, validator) {
if (property.block)
continue;
- var descriptor = compactable[property.name];
+ descriptor = compactable[property.name];
if (!descriptor || !descriptor.componentOf)
continue;
if (property.shorthand) {
invalidateOrCompact(properties, i, candidates, validator);
} else {
- var componentOf = descriptor.componentOf;
- candidates[componentOf] = candidates[componentOf] || {};
- candidates[componentOf][property.name] = property;
+ for (j = 0, m = descriptor.componentOf.length; j < m; j++) {
+ componentOf = descriptor.componentOf[j];
+
+ candidates[componentOf] = candidates[componentOf] || {};
+ candidates[componentOf][property.name] = property;
+ }
}
}
diff --git a/lib/optimizer/level-2/merge-adjacent.js b/lib/optimizer/level-2/merge-adjacent.js
index 8a93304df..386a0b37e 100644
--- a/lib/optimizer/level-2/merge-adjacent.js
+++ b/lib/optimizer/level-2/merge-adjacent.js
@@ -12,6 +12,11 @@ var serializeRules = require('../../writer/one-time').rules;
var Token = require('../../tokenizer/token');
+var OVERRIDE_OPTIONS = {
+ enabled: true,
+ merging: true
+};
+
function mergeAdjacent(tokens, context) {
var lastToken = [null, [], []];
var options = context.options;
@@ -31,7 +36,7 @@ function mergeAdjacent(tokens, context) {
if (lastToken[0] == Token.RULE && serializeRules(token[1]) == serializeRules(lastToken[1])) {
var joinAt = [lastToken[2].length];
Array.prototype.push.apply(lastToken[2], token[2]);
- compactorOptimize(token[1], lastToken[2], joinAt, true, context);
+ compactorOptimize(token[1], lastToken[2], joinAt, true, OVERRIDE_OPTIONS, context);
token[2] = [];
} else if (lastToken[0] == Token.RULE && serializeBody(token[2]) == serializeBody(lastToken[2]) &&
isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) &&
diff --git a/lib/optimizer/level-2/merge-non-adjacent-by-selector.js b/lib/optimizer/level-2/merge-non-adjacent-by-selector.js
index cba12fe93..c5b53558e 100644
--- a/lib/optimizer/level-2/merge-non-adjacent-by-selector.js
+++ b/lib/optimizer/level-2/merge-non-adjacent-by-selector.js
@@ -7,6 +7,11 @@ var serializeRules = require('../../writer/one-time').rules;
var Token = require('../../tokenizer/token');
+var OVERRIDE_OPTIONS = {
+ enabled: true,
+ merging: true
+};
+
function mergeNonAdjacentBySelector(tokens, context) {
var specificityCache = context.cache.specificity;
var allSelectors = {};
@@ -71,7 +76,7 @@ function mergeNonAdjacentBySelector(tokens, context) {
Array.prototype.push.apply(target[2], moved[2]);
}
- compactorOptimize(target[1], target[2], joinAt, true, context);
+ compactorOptimize(target[1], target[2], joinAt, true, OVERRIDE_OPTIONS, context);
moved[2] = [];
}
}
diff --git a/lib/optimizer/level-2/optimize.js b/lib/optimizer/level-2/optimize.js
index 3dae947e1..a84a76a4c 100644
--- a/lib/optimizer/level-2/optimize.js
+++ b/lib/optimizer/level-2/optimize.js
@@ -14,6 +14,11 @@ var OptimizationLevel = require('../../options/optimization-level').Optimization
var Token = require('../../tokenizer/token');
+var OVERRIDE_OPTIONS = {
+ enabled: true,
+ merging: true
+};
+
function removeEmpty(tokens) {
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
@@ -56,7 +61,7 @@ function recursivelyOptimizeProperties(tokens, context) {
switch (token[0]) {
case Token.RULE:
- compactorOptimize(token[1], token[2], false, true, context);
+ compactorOptimize(token[1], token[2], false, true, OVERRIDE_OPTIONS, context);
break;
case Token.BLOCK:
recursivelyOptimizeProperties(token[2], context);
diff --git a/lib/optimizer/level-2/reduce-non-adjacent.js b/lib/optimizer/level-2/reduce-non-adjacent.js
index b70022e63..c71b0b211 100644
--- a/lib/optimizer/level-2/reduce-non-adjacent.js
+++ b/lib/optimizer/level-2/reduce-non-adjacent.js
@@ -9,6 +9,11 @@ var Token = require('../../tokenizer/token');
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
+var OVERRIDE_OPTIONS = {
+ enabled: true,
+ merging: false
+};
+
function reduceNonAdjacent(tokens, context) {
var options = context.options;
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
@@ -162,7 +167,7 @@ function reduceSelector(tokens, selector, data, context, options, outerContext)
joinsAt.push((joinsAt.length > 0 ? joinsAt[joinsAt.length - 1] : 0) + bodiesAsList[j].length);
}
- compactorOptimize(selector, bodies, joinsAt, false, outerContext);
+ compactorOptimize(selector, bodies, joinsAt, false, OVERRIDE_OPTIONS, outerContext);
var processedCount = processedTokens.length;
var propertyIdx = bodies.length - 1;
diff --git a/test/fixtures/big-min.css b/test/fixtures/big-min.css
index 559f953d3..65ba94193 100644
--- a/test/fixtures/big-min.css
+++ b/test/fixtures/big-min.css
@@ -362,7 +362,7 @@ button::-moz-focus-inner,input[type=submit]::-moz-focus-inner{padding:0;border:0
.liste_carre_999.liste_une{overflow:hidden;margin:10px 0;padding:0 0 0 15px}
.liste_carre_999.liste_une li{float:left;width:45%;margin:0 0 10px}
article .liste_carre_999{margin-top:5px}
-.liste_horaire li:first-child{border-top:0 none}
+.liste_horaire li:first-child{border-top:0}
.liste_horaire li{border-top:1px solid #eef1f5;overflow:hidden;padding:10px 15px}
.liste_horaire .heure{color:#2e3942;float:left;font-size:11px;height:14px;line-height:15px;width:40px}
.liste_horaire .texte{float:left;width:240px}
@@ -795,7 +795,7 @@ img[height="97"]+.ico29x29{bottom:6%;left:3.5%}
.sociaux .obf,.sociaux .obf span,.sociaux .obf strong,.sociaux .obf:focus span,.sociaux .obf:focus strong,.sociaux .obf:hover span,.sociaux .obf:hover strong{color:#000;text-decoration:none}
.sociaux .obf{color:#036}
.sociaux .obf .obf:focus,.sociaux .obf:hover{color:#900}
-#header_utilisateur #header_twitter_contenu .txt15_140{margin:0 0 5px;padding:0 0 5px;color:#000;border-bottom:solid 1px #d2d6db}
+#header_utilisateur #header_twitter_contenu .txt15_140{margin:0 0 5px;padding:0 0 5px;color:#000;border-bottom:1px solid #d2d6db}
#header_twitter_contenu .liste_img_lien{margin:9px 0 0;overflow:hidden;white-space:nowrap}
#header_twitter_contenu .liste_img_lien .block{display:block;margin:0 0 5px}
#header_twitter_contenu{padding:10px;border:1px solid #d2d6db}
@@ -1186,6 +1186,7 @@ label i{font-style:normal}
.col_droite .boite_recherche .saisie{width:239px;float:right}
.col_droite .boite_recherche .saisie:first-child{width:274px;float:none}
.col_droite .bloc_abonnes{margin-bottom:25px;border:solid #eef1f5;border-width:0 1px 1px;background:#fafbfc}
+.previsu+.contenu,.titres_journal{border-top:1px solid #e6e7e8}
.col_droite .plus{font-weight:700}
.col_droite .bloc_abonnes .bt{text-align:center;margin:10px 0}
.col_droite .plus span{color:#747b83;font-size:110%;line-height:1}
@@ -1203,7 +1204,6 @@ label i{font-style:normal}
.previsu_contenu li{margin:0 0 3px}
.previsu_contenu .date{text-transform:uppercase;font-size:10px;color:#666}
.previsu .lire{font-size:16px;font-weight:700;margin:0 0 5px;display:inline-block}
-.previsu+.contenu{border-top:1px solid #e6e7e8}
.bloc_je{margin-bottom:20px}
.bloc_je .bt_push_abo{margin-top:15px}
.bloc_je .liste_ensembles{font-size:15px}
@@ -1229,10 +1229,10 @@ label i{font-style:normal}
.bloc_couvs.bloc_pls_couv .couv_petite{width:125px;height:86px;top:auto;left:auto;right:36px;bottom:42px}
.bloc_couvs.bloc_pls_couv .couv_petite.petite_2{right:18px;bottom:21px}
.bloc_couvs.bloc_pls_couv .couv_petite.petite_3{right:0;bottom:0}
-.nouveau_weekend{font-weight:700;border-bottom:solid 1px #e6e7e8}
+.nouveau_weekend{font-weight:700;border-bottom:1px solid #e6e7e8}
.nouveau_weekend strong{display:block;margin:0 0 3px;text-transform:lowercase;font-variant:small-caps;font-family:georgia,serif;font-size:1.1em}
.nouveau_weekend span{display:inline-block;margin:0 1px 0 0;padding:0 4px 1px;background:#bb0102;color:#fff;font-family:arial,sans-serif;font-size:.85em}
-.titres_journal{padding:10px;border-top:solid 1px #e6e7e8;text-align:left;background:#fff}
+.titres_journal{padding:10px;text-align:left;background:#fff}
.titres_journal .bull_gris_petit li{margin-bottom:2px}
.titres_journal .fle_abo{font-size:15px;margin-bottom:3px;font-weight:700;padding-left:14px}
#bandeau_bas{z-index:2147483647;position:fixed;bottom:0;width:100%;height:25px;border-bottom:1px solid #000;background:#000b15;color:#fff}
@@ -1486,7 +1486,7 @@ label i{font-style:normal}
.une_edito .grid_12 .boite_recherche>p{display:block;margin:5px 0 0}
.boite_inline .btn.adroite,.une_edito .btn.adroite{margin-top:5px}
.reception_alertes_resultats{border:1px solid #eef1f5}
-.reception_alertes_resultats h1{border-top:solid 3px #1f0d67}
+.reception_alertes_resultats h1{border-top:3px solid #1f0d67}
.reception_alertes_resultats .conteneur_hors_grille{margin:16px 54px 54px}
.reception_alertes_resultats .saisie{display:block;width:282px}
.reception_alertes_resultats .saisie.iblock{display:inline-block}
diff --git a/test/optimizer/level-2/break-up-test.js b/test/optimizer/level-2/break-up-test.js
index d573e85fb..fd220f3f7 100644
--- a/test/optimizer/level-2/break-up-test.js
+++ b/test/optimizer/level-2/break-up-test.js
@@ -256,15 +256,30 @@ vows.describe(breakUp)
},
'has border-width': function (components) {
assert.deepEqual(components[0].name, 'border-width');
- assert.deepEqual(components[0].value, [['property-value', 'inherit']]);
+ assert.deepEqual(components[0].value, [
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit']
+ ]);
},
'has border-style': function (components) {
assert.deepEqual(components[1].name, 'border-style');
- assert.deepEqual(components[1].value, [['property-value', 'inherit']]);
+ assert.deepEqual(components[1].value, [
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit']
+ ]);
},
'has border-color': function (components) {
assert.deepEqual(components[2].name, 'border-color');
- assert.deepEqual(components[2].value, [['property-value', 'inherit']]);
+ assert.deepEqual(components[2].value, [
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit']
+ ]);
}
},
'3 inherits': {
@@ -284,15 +299,30 @@ vows.describe(breakUp)
},
'has border-width': function (components) {
assert.deepEqual(components[0].name, 'border-width');
- assert.deepEqual(components[0].value, [['property-value', 'inherit']]);
+ assert.deepEqual(components[0].value, [
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit']
+ ]);
},
'has border-style': function (components) {
assert.deepEqual(components[1].name, 'border-style');
- assert.deepEqual(components[1].value, [['property-value', 'inherit']]);
+ assert.deepEqual(components[1].value, [
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit']
+ ]);
},
'has border-color': function (components) {
assert.deepEqual(components[2].name, 'border-color');
- assert.deepEqual(components[2].value, [['property-value', 'inherit']]);
+ assert.deepEqual(components[2].value, [
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit'],
+ ['property-value', 'inherit']
+ ]);
}
},
'all values in correct order': {
@@ -312,15 +342,30 @@ vows.describe(breakUp)
},
'has border-width': function (components) {
assert.deepEqual(components[0].name, 'border-width');
- assert.deepEqual(components[0].value, [['property-value', '1px']]);
+ assert.deepEqual(components[0].value, [
+ ['property-value', '1px'],
+ ['property-value', '1px'],
+ ['property-value', '1px'],
+ ['property-value', '1px']
+ ]);
},
'has border-style': function (components) {
assert.deepEqual(components[1].name, 'border-style');
- assert.deepEqual(components[1].value, [['property-value', 'solid']]);
+ assert.deepEqual(components[1].value, [
+ ['property-value', 'solid'],
+ ['property-value', 'solid'],
+ ['property-value', 'solid'],
+ ['property-value', 'solid']
+ ]);
},
'has border-color': function (components) {
assert.deepEqual(components[2].name, 'border-color');
- assert.deepEqual(components[2].value, [['property-value', 'red']]);
+ assert.deepEqual(components[2].value, [
+ ['property-value', 'red'],
+ ['property-value', 'red'],
+ ['property-value', 'red'],
+ ['property-value', 'red']
+ ]);
}
},
'all values in wrong order': {
@@ -340,15 +385,30 @@ vows.describe(breakUp)
},
'has border-width': function (components) {
assert.deepEqual(components[0].name, 'border-width');
- assert.deepEqual(components[0].value, [['property-value', '1px']]);
+ assert.deepEqual(components[0].value, [
+ ['property-value', '1px'],
+ ['property-value', '1px'],
+ ['property-value', '1px'],
+ ['property-value', '1px']
+ ]);
},
'has border-style': function (components) {
assert.deepEqual(components[1].name, 'border-style');
- assert.deepEqual(components[1].value, [['property-value', 'solid']]);
+ assert.deepEqual(components[1].value, [
+ ['property-value', 'solid'],
+ ['property-value', 'solid'],
+ ['property-value', 'solid'],
+ ['property-value', 'solid']
+ ]);
},
'has border-color': function (components) {
assert.deepEqual(components[2].name, 'border-color');
- assert.deepEqual(components[2].value, [['property-value', 'red']]);
+ assert.deepEqual(components[2].value, [
+ ['property-value', 'red'],
+ ['property-value', 'red'],
+ ['property-value', 'red'],
+ ['property-value', 'red']
+ ]);
}
},
'missing values': {
@@ -366,15 +426,30 @@ vows.describe(breakUp)
},
'has border-width': function (components) {
assert.deepEqual(components[0].name, 'border-width');
- assert.deepEqual(components[0].value, [['property-value', 'medium']]);
+ assert.deepEqual(components[0].value, [
+ ['property-value', 'medium'],
+ ['property-value', 'medium'],
+ ['property-value', 'medium'],
+ ['property-value', 'medium']
+ ]);
},
'has border-style': function (components) {
assert.deepEqual(components[1].name, 'border-style');
- assert.deepEqual(components[1].value, [['property-value', 'none']]);
+ assert.deepEqual(components[1].value, [
+ ['property-value', 'none'],
+ ['property-value', 'none'],
+ ['property-value', 'none'],
+ ['property-value', 'none']
+ ]);
},
'has border-color': function (components) {
assert.deepEqual(components[2].name, 'border-color');
- assert.deepEqual(components[2].value, [['property-value', 'red']]);
+ assert.deepEqual(components[2].value, [
+ ['property-value', 'red'],
+ ['property-value', 'red'],
+ ['property-value', 'red'],
+ ['property-value', 'red']
+ ]);
}
},
'missing width': {
@@ -393,15 +468,30 @@ vows.describe(breakUp)
},
'has border-width': function (components) {
assert.deepEqual(components[0].name, 'border-width');
- assert.deepEqual(components[0].value, [['property-value', 'medium']]);
+ assert.deepEqual(components[0].value, [
+ ['property-value', 'medium'],
+ ['property-value', 'medium'],
+ ['property-value', 'medium'],
+ ['property-value', 'medium']
+ ]);
},
'has border-style': function (components) {
assert.deepEqual(components[1].name, 'border-style');
- assert.deepEqual(components[1].value, [['property-value', 'solid']]);
+ assert.deepEqual(components[1].value, [
+ ['property-value', 'solid'],
+ ['property-value', 'solid'],
+ ['property-value', 'solid'],
+ ['property-value', 'solid']
+ ]);
},
'has border-color': function (components) {
assert.deepEqual(components[2].name, 'border-color');
- assert.deepEqual(components[2].value, [['property-value', 'rgba(0,0,0,0)']]);
+ assert.deepEqual(components[2].value, [
+ ['property-value', 'rgba(0,0,0,0)'],
+ ['property-value', 'rgba(0,0,0,0)'],
+ ['property-value', 'rgba(0,0,0,0)'],
+ ['property-value', 'rgba(0,0,0,0)']
+ ]);
}
}
},
diff --git a/test/optimizer/level-2/compacting/find-component-in-test.js b/test/optimizer/level-2/compacting/find-component-in-test.js
new file mode 100644
index 000000000..ad5a2fcff
--- /dev/null
+++ b/test/optimizer/level-2/compacting/find-component-in-test.js
@@ -0,0 +1,93 @@
+var assert = require('assert');
+var vows = require('vows');
+
+var wrapForOptimizing = require('../../../../lib/optimizer/wrap-for-optimizing').all;
+var compatibility = require('../../../../lib/utils/compatibility');
+var populateComponents = require('../../../../lib/optimizer/level-2/compacting/populate-components');
+var validator = require('../../../../lib/optimizer/validator');
+
+var findComponentIn = require('../../../../lib/optimizer/level-2/compacting/find-component-in');
+
+vows.describe(findComponentIn)
+ .addBatch({
+ 'missing': {
+ 'topic': function () {
+ var shorthand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'margin'],
+ ['property-value', '0px']
+ ]
+ ])[0];
+ var longhand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'padding-top'],
+ ['property-value', '0px']
+ ]
+ ])[0];
+
+ populateComponents([shorthand], validator(compatibility({})), []);
+
+ return [shorthand, longhand];
+ },
+ 'is null': function (topic) {
+ assert.isUndefined(findComponentIn.apply(null, topic));
+ }
+ },
+ 'present': {
+ 'topic': function () {
+ var shorthand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'margin'],
+ ['property-value', '0px']
+ ]
+ ])[0];
+ var longhand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'margin-top'],
+ ['property-value', '1px']
+ ]
+ ])[0];
+
+ populateComponents([shorthand], validator(compatibility({})), []);
+
+ return [shorthand, longhand];
+ },
+ 'is null': function (topic) {
+ assert.equal(findComponentIn.apply(null, topic).name, 'margin-top');
+ assert.deepEqual(findComponentIn.apply(null, topic).value, [['property-value', '0px']]);
+ }
+ },
+ 'subcomponent': {
+ 'topic': function () {
+ var shorthand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border'],
+ ['property-value', '1px'],
+ ['property-value', 'solid'],
+ ['property-value', 'red']
+ ]
+ ])[0];
+ var longhand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border-left-style'],
+ ['property-value', 'dotted']
+ ]
+ ])[0];
+
+ populateComponents([shorthand], validator(compatibility({})), []);
+
+ return [shorthand, longhand];
+ },
+ 'is null': function (topic) {
+ assert.equal(findComponentIn.apply(null, topic).name, 'border-left-style');
+ assert.deepEqual(findComponentIn.apply(null, topic).value, [['property-value', 'solid']]);
+ }
+ }
+ })
+ .export(module);
diff --git a/test/optimizer/level-2/compacting/is-component-of-test.js b/test/optimizer/level-2/compacting/is-component-of-test.js
new file mode 100644
index 000000000..5914ec1c4
--- /dev/null
+++ b/test/optimizer/level-2/compacting/is-component-of-test.js
@@ -0,0 +1,127 @@
+var assert = require('assert');
+var vows = require('vows');
+
+var wrapForOptimizing = require('../../../../lib/optimizer/wrap-for-optimizing').all;
+var compatibility = require('../../../../lib/utils/compatibility');
+var populateComponents = require('../../../../lib/optimizer/level-2/compacting/populate-components');
+var validator = require('../../../../lib/optimizer/validator');
+
+var isComponentOf = require('../../../../lib/optimizer/level-2/compacting/is-component-of');
+
+vows.describe(isComponentOf)
+ .addBatch({
+ 'not matching': {
+ 'topic': function () {
+ var shorthand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'margin'],
+ ['property-value', '0px']
+ ]
+ ])[0];
+ var longhand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'padding-top'],
+ ['property-value', '0px']
+ ]
+ ])[0];
+
+ populateComponents([shorthand], validator(compatibility({})), []);
+
+ return [shorthand, longhand];
+ },
+ 'longhand is not a component of shorthand': function (topic) {
+ assert.isFalse(isComponentOf.apply(null, topic));
+ },
+ 'shorthand is not a component of longhand': function (topic) {
+ assert.isFalse(isComponentOf.apply(null, topic.reverse()));
+ }
+ },
+ 'matching': {
+ 'topic': function () {
+ var shorthand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'margin'],
+ ['property-value', '0px']
+ ]
+ ])[0];
+ var longhand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'margin-top'],
+ ['property-value', '0px']
+ ]
+ ])[0];
+
+ populateComponents([shorthand], validator(compatibility({})), []);
+
+ return [shorthand, longhand];
+ },
+ 'longhand is a component of shorthand': function (topic) {
+ assert.isTrue(isComponentOf.apply(null, topic));
+ },
+ 'shorthand is not a component of longhand': function (topic) {
+ assert.isFalse(isComponentOf.apply(null, topic.reverse()));
+ }
+ },
+ 'subcomponents': {
+ 'topic': function () {
+ var shorthand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border'],
+ ['property-value', '1px'],
+ ['property-value', 'solid'],
+ ['property-value', 'red']
+ ]
+ ])[0];
+ var longhand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border-left-color'],
+ ['property-value', 'blue']
+ ]
+ ])[0];
+
+ populateComponents([shorthand], validator(compatibility({})), []);
+
+ return [shorthand, longhand];
+ },
+ 'longhand is a component of shorthand': function (topic) {
+ assert.isTrue(isComponentOf.apply(null, topic));
+ },
+ 'shorthand is not a component of longhand': function (topic) {
+ assert.isFalse(isComponentOf.apply(null, topic.reverse()));
+ }
+ },
+ 'subcomponents in shallow mode': {
+ 'topic': function () {
+ var shorthand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border'],
+ ['property-value', '1px'],
+ ['property-value', 'solid'],
+ ['property-value', 'red']
+ ]
+ ])[0];
+ var longhand = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border-left-color'],
+ ['property-value', 'blue']
+ ]
+ ])[0];
+
+ populateComponents([shorthand], validator(compatibility({})), []);
+
+ return [shorthand, longhand, true];
+ },
+ 'longhand is not a component of shorthand': function (topic) {
+ assert.isFalse(isComponentOf.apply(null, topic));
+ }
+ }
+ })
+ .export(module);
diff --git a/test/optimizer/level-2/compacting/longhand-overriding-test.js b/test/optimizer/level-2/compacting/longhand-overriding-test.js
index 38f050b40..fb58d8816 100644
--- a/test/optimizer/level-2/compacting/longhand-overriding-test.js
+++ b/test/optimizer/level-2/compacting/longhand-overriding-test.js
@@ -28,7 +28,14 @@ function _optimize(source) {
}
}
};
- optimize(tokens[0][1], tokens[0][2], false, true, { options: options, validator: validator(compat) });
+ optimize(
+ tokens[0][1],
+ tokens[0][2],
+ false,
+ true,
+ { enabled: true, merging: true },
+ { options: options, validator: validator(compat) }
+ );
return tokens[0][2];
}
@@ -73,7 +80,8 @@ function overrideContext(longhands) {
var vendorPrefixesFor = ['animation', 'transition'];
var defaultValues = {
'list-style-image': 'none',
- 'background': '0 0'
+ 'background': '0 0',
+ 'border-color': 'red'
};
for (var longhand in longhands) {
diff --git a/test/optimizer/level-2/compacting/optimize-test.js b/test/optimizer/level-2/compacting/optimize-test.js
index a3f5b6b6a..010d395a1 100644
--- a/test/optimizer/level-2/compacting/optimize-test.js
+++ b/test/optimizer/level-2/compacting/optimize-test.js
@@ -18,7 +18,7 @@ function _optimize(source, mergeAdjacent, aggressiveMerging, compatibilityOption
mergeMedia: false,
restructureRules: false,
mergeSemantically: false,
- compactShorthands: false
+ compactShorthands: true
}
}
};
@@ -28,7 +28,14 @@ function _optimize(source, mergeAdjacent, aggressiveMerging, compatibilityOption
warnings: []
});
- optimize(tokens[0][1], tokens[0][2], mergeAdjacent, true, { options: options, validator: validator(compat) });
+ optimize(
+ tokens[0][1],
+ tokens[0][2],
+ mergeAdjacent,
+ true,
+ { enabled: true, merging: true },
+ { options: options, validator: validator(compat) }
+ );
return tokens[0][2];
}
@@ -98,11 +105,6 @@ vows.describe(optimize)
},
'into': function (properties) {
assert.deepEqual(properties, [
- [
- 'property',
- ['property-name', 'margin', [[1, 2, undefined]]],
- ['property-value', '0', [[1, 9, undefined]]]
- ],
[
'property',
['property-name', 'margin', [[1, 11, undefined]]],
@@ -332,7 +334,7 @@ vows.describe(optimize)
]);
}
},
- 'longhand then shorthand': {
+ 'longhand then shorthand 123': {
'topic': function () {
return _optimize('p{border-left-style:solid;border:1px dotted red}', false, true);
},
@@ -642,7 +644,7 @@ vows.describe(optimize)
]);
}
},
- 'understandable - 2 adjacent properties, both !important, 2nd more understandable': {
+ 'understandable - 2 adjacent properties, both !important and understandable': {
'topic': function () {
return _optimize('a{background:rgba(0,255,0,.5)!important;background:red!important}', false, true);
},
@@ -651,11 +653,6 @@ vows.describe(optimize)
[
'property',
['property-name', 'background', [[1, 2, undefined]]],
- ['property-value', 'rgba(0,255,0,.5)!important', [[1, 13, undefined]]]
- ],
- [
- 'property',
- ['property-name', 'background', [[1, 40, undefined]]],
['property-value', 'red!important', [[1, 51, undefined]]]
]
]);
@@ -776,13 +773,6 @@ vows.describe(optimize)
},
'into': function (properties) {
assert.deepEqual(properties, [
- [
- 'property',
- ['property-name', 'border', [[1, 2, undefined]]],
- ['property-value', '1px', [[1, 9, undefined]]],
- ['property-value', 'solid', [[1, 13, undefined]]],
- ['property-value', '#fff', [[1, 19, undefined]]]
- ],
[
'property',
['property-name', 'border', [[1, 24, undefined]]],
@@ -922,6 +912,48 @@ vows.describe(optimize)
]
]);
}
+ },
+ 'understandable - 2 adjacent properties, both !important, 2nd more understandable in IE8 mode': {
+ 'topic': function () {
+ return _optimize('a{background:rgba(0,255,0,.5)!important;background:red!important}', false, true, 'ie8');
+ },
+ 'into': function (properties) {
+ assert.deepEqual(properties, [
+ [
+ 'property',
+ ['property-name', 'background', [[1, 2, undefined]]],
+ ['property-value', 'rgba(0,255,0,.5)!important', [[1, 13, undefined]]]
+ ],
+ [
+ 'property',
+ ['property-name', 'background', [[1, 40, undefined]]],
+ ['property-value', 'red!important', [[1, 51, undefined]]]
+ ]
+ ]);
+ }
+ },
+ 'understandable - border(hex) with border(rgba !important) in IE8 mode': {
+ 'topic': function () {
+ return _optimize('a{border:1px solid #fff!important;border:1px solid rgba(1,0,0,.5)!important}', false, true, 'ie8');
+ },
+ 'into': function (properties) {
+ assert.deepEqual(properties, [
+ [
+ 'property',
+ ['property-name', 'border', [[1, 2, undefined]]],
+ ['property-value', '1px', [[1, 9, undefined]]],
+ ['property-value', 'solid', [[1, 13, undefined]]],
+ ['property-value', '#fff!important', [[1, 19, undefined]]]
+ ],
+ [
+ 'property',
+ ['property-name', 'border', [[1, 34, undefined]]],
+ ['property-value', '1px', [[1, 41, undefined]]],
+ ['property-value', 'solid', [[1, 45, undefined]]],
+ ['property-value', 'rgba(1,0,0,.5)!important', [[1, 51, undefined]]]
+ ]
+ ]);
+ }
}
})
.export(module);
diff --git a/test/optimizer/level-2/compacting/override-compacting-test.js b/test/optimizer/level-2/compacting/override-compacting-test.js
index 6f288e399..b3b6829ee 100644
--- a/test/optimizer/level-2/compacting/override-compacting-test.js
+++ b/test/optimizer/level-2/compacting/override-compacting-test.js
@@ -25,7 +25,14 @@ function _optimize(source, compat, aggressiveMerging) {
}
}
};
- optimize(tokens[0][1], tokens[0][2], false, true, { options: options, validator: validator(compat) });
+ optimize(
+ tokens[0][1],
+ tokens[0][2],
+ false,
+ true,
+ { enabled: true, merging: true },
+ { options: options, validator: validator(compat) }
+ );
return tokens[0][2];
}
diff --git a/test/optimizer/level-2/compacting/overrides-non-component-shorthand-test.js b/test/optimizer/level-2/compacting/overrides-non-component-shorthand-test.js
new file mode 100644
index 000000000..53997f1f0
--- /dev/null
+++ b/test/optimizer/level-2/compacting/overrides-non-component-shorthand-test.js
@@ -0,0 +1,95 @@
+var assert = require('assert');
+var vows = require('vows');
+
+var wrapForOptimizing = require('../../../../lib/optimizer/wrap-for-optimizing').all;
+var compatibility = require('../../../../lib/utils/compatibility');
+var populateComponents = require('../../../../lib/optimizer/level-2/compacting/populate-components');
+var validator = require('../../../../lib/optimizer/validator');
+
+var overridesNonComponentShorthand = require('../../../../lib/optimizer/level-2/compacting/overrides-non-component-shorthand');
+
+vows.describe(overridesNonComponentShorthand)
+ .addBatch({
+ 'not matching': {
+ 'topic': function () {
+ var left = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'margin'],
+ ['property-value', '0px']
+ ]
+ ])[0];
+ var right = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'padding-top'],
+ ['property-value', '0px']
+ ]
+ ])[0];
+
+ populateComponents([left], validator(compatibility({})), []);
+
+ return [right, left];
+ },
+ 'is false': function (topic) {
+ assert.isFalse(overridesNonComponentShorthand.apply(null, topic));
+ }
+ },
+ 'border-color and border-': {
+ 'topic': function () {
+ var left = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border-color'],
+ ['property-value', 'red']
+ ]
+ ])[0];
+ var right = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border-top'],
+ ['property-value', '1px'],
+ ['property-value', 'solid'],
+ ['property-value', 'red']
+ ]
+ ])[0];
+
+ populateComponents([left], validator(compatibility({})), []);
+
+ return [right, left];
+ },
+ 'is false': function (topic) {
+ assert.isFalse(overridesNonComponentShorthand.apply(null, topic));
+ }
+ },
+ 'border- and border': {
+ 'topic': function () {
+ var left = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border-top'],
+ ['property-value', '1px'],
+ ['property-value', 'solid'],
+ ['property-value', 'red']
+ ]
+ ])[0];
+ var right = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border'],
+ ['property-value', '2px'],
+ ['property-value', 'solid'],
+ ['property-value', 'blue']
+ ]
+ ])[0];
+
+ populateComponents([left], validator(compatibility({})), []);
+
+ return [right, left];
+ },
+ 'is true': function (topic) {
+ assert.isTrue(overridesNonComponentShorthand.apply(null, topic));
+ }
+ }
+ })
+ .export(module);
diff --git a/test/optimizer/level-2/compacting/populate-components-test.js b/test/optimizer/level-2/compacting/populate-components-test.js
index b40335a03..ec0f139ce 100644
--- a/test/optimizer/level-2/compacting/populate-components-test.js
+++ b/test/optimizer/level-2/compacting/populate-components-test.js
@@ -2,6 +2,8 @@ var assert = require('assert');
var vows = require('vows');
var wrapForOptimizing = require('../../../../lib/optimizer/wrap-for-optimizing').all;
+var compatibility = require('../../../../lib/utils/compatibility');
+var validator = require('../../../../lib/optimizer/validator');
var populateComponents = require('../../../../lib/optimizer/level-2/compacting/populate-components');
@@ -20,7 +22,7 @@ vows.describe(populateComponents)
]
]);
- populateComponents(wrapped);
+ populateComponents(wrapped, validator(compatibility({})), []);
return wrapped;
},
'has one': function (wrapped) {
@@ -52,6 +54,85 @@ vows.describe(populateComponents)
assert.deepEqual(wrapped[0].components[3].value, [['property-value', '3px']]);
}
},
+ 'shorthand with shorthand components': {
+ 'topic': function () {
+ var wrapped = wrapForOptimizing([
+ [
+ 'property',
+ ['property-name', 'border'],
+ ['property-value', '1px'],
+ ['property-value', 'solid'],
+ ['property-value', 'red']
+ ]
+ ]);
+
+ populateComponents(wrapped, validator(compatibility({})), []);
+ return wrapped;
+ },
+ 'has one': function (wrapped) {
+ assert.lengthOf(wrapped, 1);
+ },
+ 'becomes shorthand': function (wrapped) {
+ assert.isTrue(wrapped[0].shorthand);
+ },
+ 'is dirty': function (wrapped) {
+ assert.isTrue(wrapped[0].dirty);
+ },
+ 'gets 3 components': function (wrapped) {
+ assert.lengthOf(wrapped[0].components, 3);
+ },
+ 'gets a border-width with subcomponents': function (wrapped) {
+ assert.deepEqual(wrapped[0].components[0].name, 'border-width');
+ assert.deepEqual(wrapped[0].components[0].value, [
+ ['property-value', '1px'],
+ ['property-value', '1px'],
+ ['property-value', '1px'],
+ ['property-value', '1px']
+ ]);
+
+ assert.lengthOf(wrapped[0].components[0].components, 4);
+ assert.deepEqual(wrapped[0].components[0].components.map(function (c) { return c.name; }), [
+ 'border-top-width',
+ 'border-right-width',
+ 'border-bottom-width',
+ 'border-left-width'
+ ]);
+ },
+ 'gets a border-style': function (wrapped) {
+ assert.deepEqual(wrapped[0].components[1].name, 'border-style');
+ assert.deepEqual(wrapped[0].components[1].value, [
+ ['property-value', 'solid'],
+ ['property-value', 'solid'],
+ ['property-value', 'solid'],
+ ['property-value', 'solid']
+ ]);
+
+ assert.lengthOf(wrapped[0].components[1].components, 4);
+ assert.deepEqual(wrapped[0].components[1].components.map(function (c) { return c.name; }), [
+ 'border-top-style',
+ 'border-right-style',
+ 'border-bottom-style',
+ 'border-left-style'
+ ]);
+ },
+ 'gets a border-color': function (wrapped) {
+ assert.deepEqual(wrapped[0].components[2].name, 'border-color');
+ assert.deepEqual(wrapped[0].components[2].value, [
+ ['property-value', 'red'],
+ ['property-value', 'red'],
+ ['property-value', 'red'],
+ ['property-value', 'red']
+ ]);
+
+ assert.lengthOf(wrapped[0].components[2].components, 4);
+ assert.deepEqual(wrapped[0].components[2].components.map(function (c) { return c.name; }), [
+ 'border-top-color',
+ 'border-right-color',
+ 'border-bottom-color',
+ 'border-left-color'
+ ]);
+ }
+ },
'longhand': {
'topic': function () {
var wrapped = wrapForOptimizing([
@@ -62,7 +143,7 @@ vows.describe(populateComponents)
]
]);
- populateComponents(wrapped);
+ populateComponents(wrapped, validator(compatibility({})), []);
return wrapped;
},
'has one': function (wrapped) {
@@ -81,7 +162,7 @@ vows.describe(populateComponents)
]
]);
- populateComponents(wrapped);
+ populateComponents(wrapped, validator(compatibility({})), []);
return wrapped;
},
'has one': function (wrapped) {
diff --git a/test/optimizer/level-2/compacting/shorthand-compacting-test.js b/test/optimizer/level-2/compacting/shorthand-compacting-test.js
index 33ed033b9..573950ecc 100644
--- a/test/optimizer/level-2/compacting/shorthand-compacting-test.js
+++ b/test/optimizer/level-2/compacting/shorthand-compacting-test.js
@@ -25,7 +25,14 @@ function _optimize(source) {
}
}
};
- optimize(tokens[0][1], tokens[0][2], false, true, { options: options, validator: validator(compat) });
+ optimize(
+ tokens[0][1],
+ tokens[0][2],
+ false,
+ true,
+ { enabled: true, merging: true },
+ { options: options, validator: validator(compat) }
+ );
return tokens[0][2];
}
diff --git a/test/optimizer/level-2/reduce-non-adjacent-test.js b/test/optimizer/level-2/reduce-non-adjacent-test.js
index 7cc3f5f65..1af450415 100644
--- a/test/optimizer/level-2/reduce-non-adjacent-test.js
+++ b/test/optimizer/level-2/reduce-non-adjacent-test.js
@@ -103,6 +103,14 @@ vows.describe('remove duplicates')
'multiple backgrounds': [
'.two{background-color:#000}.one,.two{background-color:#fff;background-image:url(x),-moz-linear-gradient(top,#aaa,#aaa);background-image:url(x),linear-gradient(to bottom,#aaa,#aaa)}.two{background-image:url(x),-moz-linear-gradient(top,#bbb,#bbb);background-image:url(x),linear-gradient(to bottom,#bbb,#bbb)}.one,.two{display:block}',
'.one,.two{background-color:#fff;background-image:url(x),-moz-linear-gradient(top,#aaa,#aaa);background-image:url(x),linear-gradient(to bottom,#aaa,#aaa);display:block}.two{background-image:url(x),-moz-linear-gradient(top,#bbb,#bbb);background-image:url(x),linear-gradient(to bottom,#bbb,#bbb)}'
+ ],
+ 'border-top shorthand': [
+ '.block1{border-top-width:3px;border-top-style:solid}.block1,.block2{border-top:3px solid red}',
+ '.block1,.block2{border-top:3px solid red}'
+ ],
+ 'non-reducible incomplete border shorthand': [
+ '.block1{border:3px solid}.block1,.block2{border-color:red}',
+ '.block1{border:3px solid}.block1,.block2{border-color:red}'
]
}, { level: 2 })
)