var canOverride = require('./can-override');
var compactable = require('./compactable');
-var shallowClone = require('./shallow-clone');
+var deepClone = require('./clone').deep;
+var shallowClone = require('./clone').shallow;
+var restoreShorthands = require('./restore-shorthands');
+
+var stringifyProperty = require('../stringifier/one-time').property;
+
+var MULTIPLEX_SEPARATOR = ',';
// Used when searching for a component that matches property
function nameMatchFilter(to) {
return compactable[shorthand.name].components.indexOf(longhand.name) > -1;
}
-function overrideSimple(property, by) {
+function overrideIntoMultiplex(property, by) {
by.unused = true;
- property.value = by.value;
+
+ for (var i = 0, l = property.value.length; i < l; i++) {
+ property.value[i] = by.value;
+ }
}
-function overrideMultiplex(property, by) {
+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; i < property.value.length; i++) {
- property.value[i] = by.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]);
}
}
+function overrideSimple(property, by) {
+ by.unused = true;
+ property.value = by.value;
+}
+
function override(property, by) {
- if (property.multiplex)
- overrideMultiplex(property, by);
+ if (by.multiplex)
+ overrideByMultiplex(property, by);
+ else if (property.multiplex)
+ overrideIntoMultiplex(property, by);
else
overrideSimple(property, by);
}
by.unused = true;
for (var i = 0, l = property.components.length; i < l; i++) {
- override(property.components[i], by.components[i]);
+ override(property.components[i], by.components[i], property.multiplex);
}
}
return false;
}
+function turnIntoMultiplex(property, size) {
+ property.multiplex = true;
+
+ for (var i = 0, l = property.components.length; i < l; i++) {
+ var component = property.components[i];
+ var value = component.value;
+ component.value = [];
+
+ for (var j = 0; j < size; j++) {
+ component.value.push(value);
+ }
+ }
+}
+
+function multiplexSize(component) {
+ var size = 0;
+
+ for (var i = 0, l = component.value.length; i < l; i++) {
+ if (component.value[i] == MULTIPLEX_SEPARATOR)
+ size++;
+ }
+
+ return size + 1;
+}
+
+function lengthOf(property) {
+ var fakeAsArray = [[property.name]].concat(property.value);
+ return stringifyProperty([fakeAsArray], 0).length;
+}
+
+function wouldResultInLongerValue(left, right) {
+ if (!left.multiplex && !right.multiplex || left.multiplex && right.multiplex)
+ return false;
+
+ var multiplex = left.multiplex ? left : right;
+ var simple = left.multiplex ? right : left;
+ var component;
+
+ var multiplexClone = deepClone(multiplex);
+ restoreShorthands([multiplexClone]);
+
+ var simpleClone = deepClone(simple);
+ restoreShorthands([simpleClone]);
+
+ var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone);
+
+ if (left.multiplex) {
+ component = multiplexClone.components.filter(nameMatchFilter(simpleClone))[0];
+ overrideIntoMultiplex(component, simpleClone);
+ } else {
+ component = simpleClone.components.filter(nameMatchFilter(multiplexClone))[0];
+ turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone));
+ overrideByMultiplex(component, multiplexClone);
+ }
+
+ restoreShorthands([simpleClone]);
+
+ var lengthAfter = lengthOf(simpleClone);
+
+ return lengthBefore < lengthAfter;
+}
+
function compactOverrides(properties, compatibility) {
var mayOverride, right, left, component;
var i, j, k;
if (!left.shorthand && right.shorthand && isComponentOf(right, left)) {
// maybe `left` can be overridden by `right` which is a shorthand?
- // TODO: this is actually more complex, as in some cases it's better to incorporate the value, e.g.
- // background:url(...); background-repeat:no-repeat,no-repeat;
- // background:url(...) no-repeat,no-repeat;
- if (!right.multiplex && left.multiplex)
- continue;
-
if (!right.important && left.important)
continue;
}
} else if (left.shorthand && !right.shorthand && isComponentOf(left, right)) {
// maybe `right` can be pulled into `left` which is a shorthand?
- // TODO - see above
- if (right.multiplex && !left.multiplex)
- continue;
-
if (right.important && !left.important)
continue;
if (component.value[0][0] != right.value[0][0] && (hasInherits(left) || hasInherits(right)))
continue;
+ if (wouldResultInLongerValue(left, right))
+ continue;
+
+ if (!left.multiplex && right.multiplex)
+ turnIntoMultiplex(left, multiplexSize(right));
+
override(component, right);
left.dirty = true;
}
var Compatibility = require('../../lib/utils/compatibility');
var addOptimizationMetadata = require('../../lib/selectors/optimization-metadata');
-function _optimize(source, compatibility) {
+function _optimize(source, compatibility, aggressiveMerging) {
var tokens = new Tokenizer({
options: {},
sourceTracker: new SourceTracker(),
}).toTokens(source);
compatibility = new Compatibility(compatibility).toOptions();
+ var options = {
+ aggressiveMerging: undefined === aggressiveMerging ? true : aggressiveMerging,
+ compatibility: compatibility,
+ shorthandCompacting: true
+ };
addOptimizationMetadata(tokens);
- optimize(tokens[0][1], tokens[0][2], false, { compatibility: compatibility, shorthandCompacting: true });
+ optimize(tokens[0][1], tokens[0][2], false, options);
return tokens[0][2];
}
]);
}
},
- 'longhand then shorthand - multiplex then simple': {
- 'topic': 'p{background-repeat:no-repeat,no-repeat;background:__ESCAPED_URL_CLEAN_CSS0__}',
- 'into': function (topic) {
- assert.deepEqual(_optimize(topic), [
- [['background-repeat', false , false], ['no-repeat'], [','], ['no-repeat']],
- [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__']]
- ]);
- }
- },
- 'longhand then shorthand - simple then multiplex': {
- 'topic': 'p{background-repeat:no-repeat;background:__ESCAPED_URL_CLEAN_CSS0__,__ESCAPED_URL_CLEAN_CSS1__}',
- 'into': function (topic) {
- assert.deepEqual(_optimize(topic), [
- [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__'], [','], ['__ESCAPED_URL_CLEAN_CSS1__']]
- ]);
- }
- },
'shorthand then longhand': {
'topic': 'p{background:__ESCAPED_URL_CLEAN_CSS0__ repeat;background-repeat:no-repeat}',
'into': function (topic) {
]);
}
},
- 'shorthand then longhand - multiple values': {
- 'topic': 'p{background:__ESCAPED_URL_CLEAN_CSS0__,__ESCAPED_URL_CLEAN_CSS1__;background-repeat:no-repeat}',
- 'into': function (topic) {
- assert.deepEqual(_optimize(topic), [
- [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__'], ['no-repeat'], [','], ['__ESCAPED_URL_CLEAN_CSS1__'], ['no-repeat']]
- ]);
- }
- },
- 'shorthand then longhand - single value then multi value': {
- 'topic': 'p{background:__ESCAPED_URL_CLEAN_CSS0__;background-repeat:no-repeat,no-repeat}',
- 'into': function (topic) {
- assert.deepEqual(_optimize(topic), [
- [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__']],
- [['background-repeat', false , false], ['no-repeat'], [','], ['no-repeat']]
- ]);
- }
- },
'shorthand then longhand - disabled background size merging': {
'topic': 'p{background:__ESCAPED_URL_CLEAN_CSS0__;background-size:50%}',
'into': function (topic) {
[['background', true , false], ['__ESCAPED_URL_CLEAN_CSS1__'], ['red']]
]);
}
+ },
+ 'with aggressive off': {
+ 'topic': 'a{background:white;color:red;background:red}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic, null, false), [
+ [['background', false , false], ['red']],
+ [['color', false , false], ['red']]
+ ]);
+ }
+ }
+ })
+ .addBatch({
+ 'shorthand then longhand multiplex': {
+ 'topic': 'p{background:top left;background-repeat:no-repeat,no-repeat}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['top'], ['left'], ['no-repeat'], [','], ['top'], ['left'], ['no-repeat']]
+ ]);
+ }
+ },
+ 'shorthand multiplex then longhand': {
+ 'topic': 'p{background:__ESCAPED_URL_CLEAN_CSS0__,__ESCAPED_URL_CLEAN_CSS1__;background-repeat:no-repeat}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__'], ['no-repeat'], [','], ['__ESCAPED_URL_CLEAN_CSS1__'], ['no-repeat']]
+ ]);
+ }
+ },
+ 'longhand then shorthand multiplex': {
+ 'topic': 'p{background-repeat:no-repeat;background:__ESCAPED_URL_CLEAN_CSS0__,__ESCAPED_URL_CLEAN_CSS1__}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__'], [','], ['__ESCAPED_URL_CLEAN_CSS1__']]
+ ]);
+ }
+ },
+ 'longhand multiplex then shorthand': {
+ 'topic': 'p{background-repeat:no-repeat,no-repeat;background:__ESCAPED_URL_CLEAN_CSS0__}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__']]
+ ]);
+ }
+ },
+ 'multiplex longhand into multiplex shorthand123': {
+ 'topic': 'p{background:no-repeat,no-repeat;background-position:top left,bottom left}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['top'], ['left'], ['no-repeat'], [','], ['bottom'], ['left'], ['no-repeat']]
+ ]);
+ }
+ },
+ 'not too long into multiplex #1': {
+ 'topic': 'p{background:top left;background-repeat:no-repeat,no-repeat}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['top'], ['left'], ['no-repeat'], [','], ['top'], ['left'], ['no-repeat']]
+ ]);
+ }
+ },
+ 'not too long into multiplex #2': {
+ 'topic': 'p{background:repeat content-box;background-repeat:no-repeat,no-repeat}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['no-repeat'], ['content-box'], [','], ['no-repeat'], ['content-box']]
+ ]);
+ }
+ },
+ 'not too long into multiplex - twice': {
+ 'topic': 'p{background:top left;background-repeat:no-repeat,no-repeat;background-image:__ESCAPED_URL_CLEAN_CSS0__,__ESCAPED_URL_CLEAN_CSS1__}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__'], ['top'], ['left'], ['no-repeat'], [','], ['__ESCAPED_URL_CLEAN_CSS1__'], ['top'], ['left'], ['no-repeat']]
+ ]);
+ }
+ },
+ 'not too long into multiplex - over a property': {
+ 'topic': 'p{background:top left;background-repeat:no-repeat,no-repeat;background-image:__ESCAPED_URL_CLEAN_CSS0__}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__'], ['top'], ['left']],
+ [['background-repeat', false , false], ['no-repeat'], [','], ['no-repeat']]
+ ]);
+ }
+ },
+ 'too long into multiplex #1': {
+ 'topic': 'p{background:__ESCAPED_URL_CLEAN_CSS0__;background-repeat:no-repeat,no-repeat}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__']],
+ [['background-repeat', false , false], ['no-repeat'], [','], ['no-repeat']]
+ ]);
+ }
+ },
+ 'too long into multiplex #2': {
+ 'topic': 'p{background:content-box padding-box;background-repeat:no-repeat,no-repeat}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['content-box'], ['padding-box']],
+ [['background-repeat', false , false], ['no-repeat'], [','], ['no-repeat']]
+ ]);
+ }
+ },
+ 'too long into multiplex #3': {
+ 'topic': 'p{background:top left / 20px 20px;background-repeat:no-repeat,no-repeat}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['top'], ['left'], ['/'], ['20px'], ['20px']],
+ [['background-repeat', false , false], ['no-repeat'], [','], ['no-repeat']]
+ ]);
+ }
+ },
+ 'background color into background': {
+ 'topic': 'p{background:red;background-repeat:__ESCAPED_URL_CLEAN_CSS0__,__ESCAPED_URL_CLEAN_CSS1__}',
+ 'into': function (topic) {
+ assert.deepEqual(_optimize(topic), [
+ [['background', false , false], ['__ESCAPED_URL_CLEAN_CSS0__'], ['red'], [','], ['__ESCAPED_URL_CLEAN_CSS1__'], ['red']],
+ ]);
+ }
}
-
- // 'aggressive off - overriddable': {
- // 'topic': 'a{background:white;color:red;background:red}',
- // 'into': function (topic) {
- // assert.deepEqual(optimize(topic, false, false), [
- // [['color', false , false], ['red']],
- // [['background', false , false], ['red']]
- // ]);
- // }
- // }
})
.export(module);