123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- var everyValuesPair = require('./every-values-pair');
- var hasInherit = require('./has-inherit');
- var populateComponents = require('./populate-components');
- var compactable = require('../compactable');
- var deepClone = require('../clone').deep;
- var restoreWithComponents = require('../restore-with-components');
- var restoreFromOptimizing = require('../../restore-from-optimizing');
- var wrapSingle = require('../../wrap-for-optimizing').single;
- var serializeBody = require('../../../writer/one-time').body;
- var Token = require('../../../tokenizer/token');
- function mergeIntoShorthands(properties, validator) {
- var candidates = {};
- var descriptor;
- var componentOf;
- var property;
- var i, l;
- var j, m;
- // there is no shorthand property made up of less than 3 longhands
- if (properties.length < 3) {
- return;
- }
- for (i = 0, l = properties.length; i < l; i++) {
- property = properties[i];
- descriptor = compactable[property.name];
- if (property.unused) {
- continue;
- }
- if (property.hack) {
- continue;
- }
- if (property.block) {
- continue;
- }
- invalidateOrCompact(properties, i, candidates, validator);
- if (descriptor && descriptor.componentOf) {
- for (j = 0, m = descriptor.componentOf.length; j < m; j++) {
- componentOf = descriptor.componentOf[j];
- candidates[componentOf] = candidates[componentOf] || {};
- candidates[componentOf][property.name] = property;
- }
- }
- }
- invalidateOrCompact(properties, i, candidates, validator);
- }
- function invalidateOrCompact(properties, position, candidates, validator) {
- var invalidatedBy = properties[position];
- var shorthandName;
- var shorthandDescriptor;
- var candidateComponents;
- for (shorthandName in candidates) {
- if (undefined !== invalidatedBy && shorthandName == invalidatedBy.name) {
- continue;
- }
- shorthandDescriptor = compactable[shorthandName];
- candidateComponents = candidates[shorthandName];
- if (invalidatedBy && invalidates(candidates, shorthandName, invalidatedBy)) {
- delete candidates[shorthandName];
- continue;
- }
- if (shorthandDescriptor.components.length > Object.keys(candidateComponents).length) {
- continue;
- }
- if (mixedImportance(candidateComponents)) {
- continue;
- }
- if (!overridable(candidateComponents, shorthandName, validator)) {
- continue;
- }
- if (!mergeable(candidateComponents)) {
- continue;
- }
- if (mixedInherit(candidateComponents)) {
- replaceWithInheritBestFit(properties, candidateComponents, shorthandName, validator);
- } else {
- replaceWithShorthand(properties, candidateComponents, shorthandName, validator);
- }
- }
- }
- function invalidates(candidates, shorthandName, invalidatedBy) {
- var shorthandDescriptor = compactable[shorthandName];
- var invalidatedByDescriptor = compactable[invalidatedBy.name];
- var componentName;
- if ('overridesShorthands' in shorthandDescriptor && shorthandDescriptor.overridesShorthands.indexOf(invalidatedBy.name) > -1) {
- return true;
- }
- if (invalidatedByDescriptor && 'componentOf' in invalidatedByDescriptor) {
- for (componentName in candidates[shorthandName]) {
- if (invalidatedByDescriptor.componentOf.indexOf(componentName) > -1) {
- return true;
- }
- }
- }
- return false;
- }
- function mixedImportance(components) {
- var important;
- var componentName;
- for (componentName in components) {
- if (undefined !== important && components[componentName].important != important) {
- return true;
- }
- important = components[componentName].important;
- }
- return false;
- }
- function overridable(components, shorthandName, validator) {
- var descriptor = compactable[shorthandName];
- var newValuePlaceholder = [
- Token.PROPERTY,
- [Token.PROPERTY_NAME, shorthandName],
- [Token.PROPERTY_VALUE, descriptor.defaultValue]
- ];
- var newProperty = wrapSingle(newValuePlaceholder);
- var component;
- var mayOverride;
- var i, l;
- populateComponents([newProperty], validator, []);
- for (i = 0, l = descriptor.components.length; i < l; i++) {
- component = components[descriptor.components[i]];
- mayOverride = compactable[component.name].canOverride;
- if (!everyValuesPair(mayOverride.bind(null, validator), newProperty.components[i], component)) {
- return false;
- }
- }
- return true;
- }
- function mergeable(components) {
- var lastCount = null;
- var currentCount;
- var componentName;
- var component;
- var descriptor;
- var values;
- for (componentName in components) {
- component = components[componentName];
- descriptor = compactable[componentName];
- if (!('restore' in descriptor)) {
- continue;
- }
- restoreFromOptimizing([component.all[component.position]], restoreWithComponents);
- values = descriptor.restore(component, compactable);
- currentCount = values.length;
- if (lastCount !== null && currentCount !== lastCount) {
- return false;
- }
- lastCount = currentCount;
- }
- return true;
- }
- function mixedInherit(components) {
- var componentName;
- var lastValue = null;
- var currentValue;
- for (componentName in components) {
- currentValue = hasInherit(components[componentName]);
- if (lastValue !== null && lastValue !== currentValue) {
- return true;
- }
- lastValue = currentValue;
- }
- return false;
- }
- function replaceWithInheritBestFit(properties, candidateComponents, shorthandName, validator) {
- var viaLonghands = buildSequenceWithInheritLonghands(candidateComponents, shorthandName, validator);
- var viaShorthand = buildSequenceWithInheritShorthand(candidateComponents, shorthandName, validator);
- var longhandTokensSequence = viaLonghands[0];
- var shorthandTokensSequence = viaShorthand[0];
- var isLonghandsShorter = serializeBody(longhandTokensSequence).length < serializeBody(shorthandTokensSequence).length;
- var newTokensSequence = isLonghandsShorter ? longhandTokensSequence : shorthandTokensSequence;
- var newProperty = isLonghandsShorter ? viaLonghands[1] : viaShorthand[1];
- var newComponents = isLonghandsShorter ? viaLonghands[2] : viaShorthand[2];
- var all = candidateComponents[Object.keys(candidateComponents)[0]].all;
- var componentName;
- var oldComponent;
- var newComponent;
- var newToken;
- newProperty.position = all.length;
- newProperty.shorthand = true;
- newProperty.dirty = true;
- newProperty.all = all;
- newProperty.all.push(newTokensSequence[0]);
- properties.push(newProperty);
- for (componentName in candidateComponents) {
- oldComponent = candidateComponents[componentName];
- oldComponent.unused = true;
- if (oldComponent.name in newComponents) {
- newComponent = newComponents[oldComponent.name];
- newToken = findTokenIn(newTokensSequence, componentName);
- newComponent.position = all.length;
- newComponent.all = all;
- newComponent.all.push(newToken);
- properties.push(newComponent);
- }
- }
- }
- function buildSequenceWithInheritLonghands(components, shorthandName, validator) {
- var tokensSequence = [];
- var inheritComponents = {};
- var nonInheritComponents = {};
- var descriptor = compactable[shorthandName];
- var shorthandToken = [
- Token.PROPERTY,
- [Token.PROPERTY_NAME, shorthandName],
- [Token.PROPERTY_VALUE, descriptor.defaultValue]
- ];
- var newProperty = wrapSingle(shorthandToken);
- var component;
- var longhandToken;
- var newComponent;
- var nameMetadata;
- var i, l;
- populateComponents([newProperty], validator, []);
- for (i = 0, l = descriptor.components.length; i < l; i++) {
- component = components[descriptor.components[i]];
- if (hasInherit(component)) {
- longhandToken = component.all[component.position].slice(0, 2);
- Array.prototype.push.apply(longhandToken, component.value);
- tokensSequence.push(longhandToken);
- newComponent = deepClone(component);
- newComponent.value = inferComponentValue(components, newComponent.name);
- newProperty.components[i] = newComponent;
- inheritComponents[component.name] = deepClone(component);
- } else {
- newComponent = deepClone(component);
- newComponent.all = component.all;
- newProperty.components[i] = newComponent;
- nonInheritComponents[component.name] = component;
- }
- }
- nameMetadata = joinMetadata(nonInheritComponents, 1);
- shorthandToken[1].push(nameMetadata);
- restoreFromOptimizing([newProperty], restoreWithComponents);
- shorthandToken = shorthandToken.slice(0, 2);
- Array.prototype.push.apply(shorthandToken, newProperty.value);
- tokensSequence.unshift(shorthandToken);
- return [tokensSequence, newProperty, inheritComponents];
- }
- function inferComponentValue(components, propertyName) {
- var descriptor = compactable[propertyName];
- if ('oppositeTo' in descriptor) {
- return components[descriptor.oppositeTo].value;
- } else {
- return [[Token.PROPERTY_VALUE, descriptor.defaultValue]];
- }
- }
- function joinMetadata(components, at) {
- var metadata = [];
- var component;
- var originalValue;
- var componentMetadata;
- var componentName;
- for (componentName in components) {
- component = components[componentName];
- originalValue = component.all[component.position];
- componentMetadata = originalValue[at][originalValue[at].length - 1];
- Array.prototype.push.apply(metadata, componentMetadata);
- }
- return metadata.sort(metadataSorter);
- }
- function metadataSorter(metadata1, metadata2) {
- var line1 = metadata1[0];
- var line2 = metadata2[0];
- var column1 = metadata1[1];
- var column2 = metadata2[1];
- if (line1 < line2) {
- return -1;
- } else if (line1 === line2) {
- return column1 < column2 ? -1 : 1;
- } else {
- return 1;
- }
- }
- function buildSequenceWithInheritShorthand(components, shorthandName, validator) {
- var tokensSequence = [];
- var inheritComponents = {};
- var nonInheritComponents = {};
- var descriptor = compactable[shorthandName];
- var shorthandToken = [
- Token.PROPERTY,
- [Token.PROPERTY_NAME, shorthandName],
- [Token.PROPERTY_VALUE, 'inherit']
- ];
- var newProperty = wrapSingle(shorthandToken);
- var component;
- var longhandToken;
- var nameMetadata;
- var valueMetadata;
- var i, l;
- populateComponents([newProperty], validator, []);
- for (i = 0, l = descriptor.components.length; i < l; i++) {
- component = components[descriptor.components[i]];
- if (hasInherit(component)) {
- inheritComponents[component.name] = component;
- } else {
- longhandToken = component.all[component.position].slice(0, 2);
- Array.prototype.push.apply(longhandToken, component.value);
- tokensSequence.push(longhandToken);
- nonInheritComponents[component.name] = deepClone(component);
- }
- }
- nameMetadata = joinMetadata(inheritComponents, 1);
- shorthandToken[1].push(nameMetadata);
- valueMetadata = joinMetadata(inheritComponents, 2);
- shorthandToken[2].push(valueMetadata);
- tokensSequence.unshift(shorthandToken);
- return [tokensSequence, newProperty, nonInheritComponents];
- }
- function findTokenIn(tokens, componentName) {
- var i, l;
- for (i = 0, l = tokens.length; i < l; i++) {
- if (tokens[i][1][1] == componentName) {
- return tokens[i];
- }
- }
- }
- function replaceWithShorthand(properties, candidateComponents, shorthandName, validator) {
- var descriptor = compactable[shorthandName];
- var nameMetadata;
- var valueMetadata;
- var newValuePlaceholder = [
- Token.PROPERTY,
- [Token.PROPERTY_NAME, shorthandName],
- [Token.PROPERTY_VALUE, descriptor.defaultValue]
- ];
- var all;
- var newProperty = wrapSingle(newValuePlaceholder);
- newProperty.shorthand = true;
- newProperty.dirty = true;
- populateComponents([newProperty], validator, []);
- for (var i = 0, l = descriptor.components.length; i < l; i++) {
- var component = candidateComponents[descriptor.components[i]];
- newProperty.components[i] = deepClone(component);
- newProperty.important = component.important;
- all = component.all;
- }
- for (var componentName in candidateComponents) {
- candidateComponents[componentName].unused = true;
- }
- nameMetadata = joinMetadata(candidateComponents, 1);
- newValuePlaceholder[1].push(nameMetadata);
- valueMetadata = joinMetadata(candidateComponents, 2);
- newValuePlaceholder[2].push(valueMetadata);
- newProperty.position = all.length;
- newProperty.all = all;
- newProperty.all.push(newValuePlaceholder);
- properties.push(newProperty);
- }
- module.exports = mergeIntoShorthands;
|