|
|
// Contains the interpretation of CSS properties, as used by the property optimizer
var breakUp = require('./break-up'); var canOverride = require('./can-override'); var restore = require('./restore');
var override = require('../../utils/override');
// Properties to process
// Extend this object in order to add support for more properties in the optimizer.
//
// Each key in this object represents a CSS property and should be an object.
// Such an object contains properties that describe how the represented CSS property should be handled.
// Possible options:
//
// * components: array (Only specify for shorthand properties.)
// Contains the names of the granular properties this shorthand compacts.
//
// * canOverride: function
// Returns whether two tokens of this property can be merged with each other.
// This property has no meaning for shorthands.
//
// * defaultValue: string
// Specifies the default value of the property according to the CSS standard.
// For shorthand, this is used when every component is set to its default value, therefore it should be the shortest possible default value of all the components.
//
// * shortestValue: string
// Specifies the shortest possible value the property can possibly have.
// (Falls back to defaultValue if unspecified.)
//
// * breakUp: function (Only specify for shorthand properties.)
// Breaks the shorthand up to its components.
//
// * restore: function (Only specify for shorthand properties.)
// Puts the shorthand together from its components.
//
var compactable = { 'animation': { canOverride: canOverride.generic.components([ canOverride.generic.time, canOverride.generic.timingFunction, canOverride.generic.time, canOverride.property.animationIterationCount, canOverride.property.animationDirection, canOverride.property.animationFillMode, canOverride.property.animationPlayState, canOverride.property.animationName ]), components: [ 'animation-duration', 'animation-timing-function', 'animation-delay', 'animation-iteration-count', 'animation-direction', 'animation-fill-mode', 'animation-play-state', 'animation-name' ], breakUp: breakUp.multiplex(breakUp.animation), defaultValue: 'none', restore: restore.multiplex(restore.withoutDefaults), shorthand: true, vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'animation-delay': { canOverride: canOverride.generic.time, componentOf: [ 'animation' ], defaultValue: '0s', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'animation-direction': { canOverride: canOverride.property.animationDirection, componentOf: [ 'animation' ], defaultValue: 'normal', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'animation-duration': { canOverride: canOverride.generic.time, componentOf: [ 'animation' ], defaultValue: '0s', intoMultiplexMode: 'real', keepUnlessDefault: 'animation-delay', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'animation-fill-mode': { canOverride: canOverride.property.animationFillMode, componentOf: [ 'animation' ], defaultValue: 'none', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'animation-iteration-count': { canOverride: canOverride.property.animationIterationCount, componentOf: [ 'animation' ], defaultValue: '1', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'animation-name': { canOverride: canOverride.property.animationName, componentOf: [ 'animation' ], defaultValue: 'none', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'animation-play-state': { canOverride: canOverride.property.animationPlayState, componentOf: [ 'animation' ], defaultValue: 'running', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'animation-timing-function': { canOverride: canOverride.generic.timingFunction, componentOf: [ 'animation' ], defaultValue: 'ease', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'background': { canOverride: canOverride.generic.components([ canOverride.generic.image, canOverride.property.backgroundPosition, canOverride.property.backgroundSize, canOverride.property.backgroundRepeat, canOverride.property.backgroundAttachment, canOverride.property.backgroundOrigin, canOverride.property.backgroundClip, canOverride.generic.color ]), components: [ 'background-image', 'background-position', 'background-size', 'background-repeat', 'background-attachment', 'background-origin', 'background-clip', 'background-color' ], breakUp: breakUp.multiplex(breakUp.background), defaultValue: '0 0', restore: restore.multiplex(restore.background), shortestValue: '0', shorthand: true }, 'background-attachment': { canOverride: canOverride.property.backgroundAttachment, componentOf: [ 'background' ], defaultValue: 'scroll', intoMultiplexMode: 'real' }, 'background-clip': { canOverride: canOverride.property.backgroundClip, componentOf: [ 'background' ], defaultValue: 'border-box', intoMultiplexMode: 'real', shortestValue: 'border-box' }, 'background-color': { canOverride: canOverride.generic.color, componentOf: [ 'background' ], defaultValue: 'transparent', intoMultiplexMode: 'real', // otherwise real color will turn into default since color appears in last multiplex only
multiplexLastOnly: true, nonMergeableValue: 'none', shortestValue: 'red' }, 'background-image': { canOverride: canOverride.generic.image, componentOf: [ 'background' ], defaultValue: 'none', intoMultiplexMode: 'default' }, 'background-origin': { canOverride: canOverride.property.backgroundOrigin, componentOf: [ 'background' ], defaultValue: 'padding-box', intoMultiplexMode: 'real', shortestValue: 'border-box' }, 'background-position': { canOverride: canOverride.property.backgroundPosition, componentOf: [ 'background' ], defaultValue: ['0', '0'], doubleValues: true, intoMultiplexMode: 'real', shortestValue: '0' }, 'background-repeat': { canOverride: canOverride.property.backgroundRepeat, componentOf: [ 'background' ], defaultValue: ['repeat'], doubleValues: true, intoMultiplexMode: 'real' }, 'background-size': { canOverride: canOverride.property.backgroundSize, componentOf: [ 'background' ], defaultValue: ['auto'], doubleValues: true, intoMultiplexMode: 'real', shortestValue: '0 0' }, 'bottom': { canOverride: canOverride.property.bottom, defaultValue: 'auto' }, 'border': { breakUp: breakUp.border, canOverride: canOverride.generic.components([ canOverride.generic.unit, canOverride.property.borderStyle, canOverride.generic.color ]), components: [ 'border-width', 'border-style', '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.generic.components([ canOverride.generic.unit, canOverride.property.borderStyle, canOverride.generic.color ]), components: [ 'border-bottom-width', 'border-bottom-style', 'border-bottom-color' ], defaultValue: 'none', restore: restore.withoutDefaults, shorthand: true }, 'border-bottom-color': { canOverride: canOverride.generic.color, componentOf: [ 'border-bottom', 'border-color' ], defaultValue: 'none' }, 'border-bottom-left-radius': { canOverride: canOverride.generic.unit, componentOf: [ 'border-radius' ], defaultValue: '0', vendorPrefixes: [ '-moz-', '-o-' ] }, 'border-bottom-right-radius': { canOverride: canOverride.generic.unit, componentOf: [ 'border-radius' ], defaultValue: '0', vendorPrefixes: [ '-moz-', '-o-' ] }, 'border-bottom-style': { canOverride: canOverride.property.borderStyle, componentOf: [ 'border-bottom', 'border-style' ], defaultValue: 'none' }, 'border-bottom-width': { canOverride: canOverride.generic.unit, componentOf: [ 'border-bottom', 'border-width' ], defaultValue: 'medium', oppositeTo: 'border-top-width', shortestValue: '0' }, 'border-collapse': { canOverride: canOverride.property.borderCollapse, defaultValue: 'separate' }, 'border-color': { breakUp: breakUp.fourValues, canOverride: canOverride.generic.components([ canOverride.generic.color, canOverride.generic.color, canOverride.generic.color, canOverride.generic.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.generic.components([ canOverride.generic.unit, canOverride.property.borderStyle, canOverride.generic.color ]), components: [ 'border-left-width', 'border-left-style', 'border-left-color' ], defaultValue: 'none', restore: restore.withoutDefaults, shorthand: true }, 'border-left-color': { canOverride: canOverride.generic.color, componentOf: [ 'border-color', 'border-left' ], defaultValue: 'none' }, 'border-left-style': { canOverride: canOverride.property.borderStyle, componentOf: [ 'border-left', 'border-style' ], defaultValue: 'none' }, 'border-left-width': { canOverride: canOverride.generic.unit, componentOf: [ 'border-left', 'border-width' ], defaultValue: 'medium', oppositeTo: 'border-right-width', shortestValue: '0' }, 'border-radius': { breakUp: breakUp.borderRadius, canOverride: canOverride.generic.components([ canOverride.generic.unit, canOverride.generic.unit, canOverride.generic.unit, canOverride.generic.unit ]), components: [ 'border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius' ], defaultValue: '0', restore: restore.borderRadius, shorthand: true, vendorPrefixes: [ '-moz-', '-o-' ] }, 'border-right': { breakUp: breakUp.border, canOverride: canOverride.generic.components([ canOverride.generic.unit, canOverride.property.borderStyle, canOverride.generic.color ]), components: [ 'border-right-width', 'border-right-style', 'border-right-color' ], defaultValue: 'none', restore: restore.withoutDefaults, shorthand: true }, 'border-right-color': { canOverride: canOverride.generic.color, componentOf: [ 'border-color', 'border-right' ], defaultValue: 'none' }, 'border-right-style': { canOverride: canOverride.property.borderStyle, componentOf: [ 'border-right', 'border-style' ], defaultValue: 'none' }, 'border-right-width': { canOverride: canOverride.generic.unit, componentOf: [ 'border-right', 'border-width' ], defaultValue: 'medium', oppositeTo: 'border-left-width', shortestValue: '0' }, 'border-style': { breakUp: breakUp.fourValues, canOverride: canOverride.generic.components([ canOverride.property.borderStyle, canOverride.property.borderStyle, canOverride.property.borderStyle, canOverride.property.borderStyle ]), 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.generic.components([ canOverride.generic.unit, canOverride.property.borderStyle, canOverride.generic.color ]), components: [ 'border-top-width', 'border-top-style', 'border-top-color' ], defaultValue: 'none', restore: restore.withoutDefaults, shorthand: true }, 'border-top-color': { canOverride: canOverride.generic.color, componentOf: [ 'border-color', 'border-top' ], defaultValue: 'none' }, 'border-top-left-radius': { canOverride: canOverride.generic.unit, componentOf: [ 'border-radius' ], defaultValue: '0', vendorPrefixes: [ '-moz-', '-o-' ] }, 'border-top-right-radius': { canOverride: canOverride.generic.unit, componentOf: [ 'border-radius' ], defaultValue: '0', vendorPrefixes: [ '-moz-', '-o-' ] }, 'border-top-style': { canOverride: canOverride.property.borderStyle, componentOf: [ 'border-style', 'border-top' ], defaultValue: 'none' }, 'border-top-width': { canOverride: canOverride.generic.unit, componentOf: [ 'border-top', 'border-width' ], defaultValue: 'medium', oppositeTo: 'border-bottom-width', shortestValue: '0' }, 'border-width': { breakUp: breakUp.fourValues, canOverride: canOverride.generic.components([ canOverride.generic.unit, canOverride.generic.unit, canOverride.generic.unit, canOverride.generic.unit ]), componentOf: [ 'border' ], components: [ 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width' ], defaultValue: 'medium', restore: restore.fourValues, shortestValue: '0', shorthand: true }, 'clear': { canOverride: canOverride.property.clear, defaultValue: 'none' }, 'color': { canOverride: canOverride.generic.color, defaultValue: 'transparent', shortestValue: 'red' }, 'cursor': { canOverride: canOverride.property.cursor, defaultValue: 'auto' }, 'display': { canOverride: canOverride.property.display, }, 'float': { canOverride: canOverride.property.float, defaultValue: 'none' }, 'font': { breakUp: breakUp.font, canOverride: canOverride.generic.components([ canOverride.property.fontStyle, canOverride.property.fontVariant, canOverride.property.fontWeight, canOverride.property.fontStretch, canOverride.generic.unit, canOverride.generic.unit, canOverride.property.fontFamily ]), components: [ 'font-style', 'font-variant', 'font-weight', 'font-stretch', 'font-size', 'line-height', 'font-family' ], restore: restore.font, shorthand: true }, 'font-family': { canOverride: canOverride.property.fontFamily, defaultValue: 'user|agent|specific' }, 'font-size': { canOverride: canOverride.generic.unit, defaultValue: 'medium', shortestValue: '0' }, 'font-stretch': { canOverride: canOverride.property.fontStretch, defaultValue: 'normal' }, 'font-style': { canOverride: canOverride.property.fontStyle, defaultValue: 'normal' }, 'font-variant': { canOverride: canOverride.property.fontVariant, defaultValue: 'normal' }, 'font-weight': { canOverride: canOverride.property.fontWeight, defaultValue: 'normal', shortestValue: '400' }, 'height': { canOverride: canOverride.generic.unit, defaultValue: 'auto', shortestValue: '0' }, 'left': { canOverride: canOverride.property.left, defaultValue: 'auto' }, 'line-height': { canOverride: canOverride.generic.unitOrNumber, defaultValue: 'normal', shortestValue: '0' }, 'list-style': { canOverride: canOverride.generic.components([ canOverride.property.listStyleType, canOverride.property.listStylePosition, canOverride.property.listStyleImage ]), components: [ 'list-style-type', 'list-style-position', 'list-style-image' ], breakUp: breakUp.listStyle, restore: restore.withoutDefaults, defaultValue: 'outside', // can't use 'disc' because that'd override default 'decimal' for <ol>
shortestValue: 'none', shorthand: true }, 'list-style-image' : { canOverride: canOverride.generic.image, componentOf: [ 'list-style' ], defaultValue: 'none' }, 'list-style-position' : { canOverride: canOverride.property.listStylePosition, componentOf: [ 'list-style' ], defaultValue: 'outside', shortestValue: 'inside' }, 'list-style-type' : { canOverride: canOverride.property.listStyleType, componentOf: [ 'list-style' ], // NOTE: we can't tell the real default value here, it's 'disc' for <ul> and 'decimal' for <ol>
// 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
defaultValue: 'decimal|disc', shortestValue: 'none' }, 'margin': { breakUp: breakUp.fourValues, canOverride: canOverride.generic.components([ canOverride.generic.unit, canOverride.generic.unit, canOverride.generic.unit, canOverride.generic.unit ]), components: [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ], defaultValue: '0', restore: restore.fourValues, shorthand: true }, 'margin-bottom': { canOverride: canOverride.generic.unit, componentOf: [ 'margin' ], defaultValue: '0', oppositeTo: 'margin-top' }, 'margin-left': { canOverride: canOverride.generic.unit, componentOf: [ 'margin' ], defaultValue: '0', oppositeTo: 'margin-right' }, 'margin-right': { canOverride: canOverride.generic.unit, componentOf: [ 'margin' ], defaultValue: '0', oppositeTo: 'margin-left' }, 'margin-top': { canOverride: canOverride.generic.unit, componentOf: [ 'margin' ], defaultValue: '0', oppositeTo: 'margin-bottom' }, 'outline': { canOverride: canOverride.generic.components([ canOverride.generic.color, canOverride.property.outlineStyle, canOverride.generic.unit ]), components: [ 'outline-color', 'outline-style', 'outline-width' ], breakUp: breakUp.outline, restore: restore.withoutDefaults, defaultValue: '0', shorthand: true }, 'outline-color': { canOverride: canOverride.generic.color, componentOf: [ 'outline' ], defaultValue: 'invert', shortestValue: 'red' }, 'outline-style': { canOverride: canOverride.property.outlineStyle, componentOf: [ 'outline' ], defaultValue: 'none' }, 'outline-width': { canOverride: canOverride.generic.unit, componentOf: [ 'outline' ], defaultValue: 'medium', shortestValue: '0' }, 'overflow': { canOverride: canOverride.property.overflow, defaultValue: 'visible' }, 'overflow-x': { canOverride: canOverride.property.overflow, defaultValue: 'visible' }, 'overflow-y': { canOverride: canOverride.property.overflow, defaultValue: 'visible' }, 'padding': { breakUp: breakUp.fourValues, canOverride: canOverride.generic.components([ canOverride.generic.unit, canOverride.generic.unit, canOverride.generic.unit, canOverride.generic.unit ]), components: [ 'padding-top', 'padding-right', 'padding-bottom', 'padding-left' ], defaultValue: '0', restore: restore.fourValues, shorthand: true }, 'padding-bottom': { canOverride: canOverride.generic.unit, componentOf: [ 'padding' ], defaultValue: '0', oppositeTo: 'padding-top' }, 'padding-left': { canOverride: canOverride.generic.unit, componentOf: [ 'padding' ], defaultValue: '0', oppositeTo: 'padding-right' }, 'padding-right': { canOverride: canOverride.generic.unit, componentOf: [ 'padding' ], defaultValue: '0', oppositeTo: 'padding-left' }, 'padding-top': { canOverride: canOverride.generic.unit, componentOf: [ 'padding' ], defaultValue: '0', oppositeTo: 'padding-bottom' }, 'position': { canOverride: canOverride.property.position, defaultValue: 'static' }, 'right': { canOverride: canOverride.property.right, defaultValue: 'auto' }, 'text-align': { canOverride: canOverride.property.textAlign, // NOTE: we can't tell the real default value here, as it depends on default text direction
// this is a hack, but it doesn't matter because this value will be either overridden or
// it will disappear anyway
defaultValue: 'left|right' }, 'text-decoration': { canOverride: canOverride.property.textDecoration, defaultValue: 'none' }, 'text-overflow': { canOverride: canOverride.property.textOverflow, defaultValue: 'none' }, 'text-shadow': { canOverride: canOverride.property.textShadow, defaultValue: 'none' }, 'top': { canOverride: canOverride.property.top, defaultValue: 'auto' }, 'transform': { canOverride: canOverride.property.transform, vendorPrefixes: [ '-moz-', '-ms-', '-webkit-' ] }, 'transition': { breakUp: breakUp.multiplex(breakUp.transition), canOverride: canOverride.generic.components([ canOverride.property.transitionProperty, canOverride.generic.time, canOverride.generic.timingFunction, canOverride.generic.time ]), components: [ 'transition-property', 'transition-duration', 'transition-timing-function', 'transition-delay' ], defaultValue: 'none', restore: restore.multiplex(restore.withoutDefaults), shorthand: true, vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'transition-delay': { canOverride: canOverride.generic.time, componentOf: [ 'transition' ], defaultValue: '0s', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'transition-duration': { canOverride: canOverride.generic.time, componentOf: [ 'transition' ], defaultValue: '0s', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'transition-property': { canOverride: canOverride.generic.propertyName, componentOf: [ 'transition' ], defaultValue: 'all', intoMultiplexMode: 'placeholder', placeholderValue: '_', // it's a short value that won't match any property and still be a valid `transition-property`
vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'transition-timing-function': { canOverride: canOverride.generic.timingFunction, componentOf: [ 'transition' ], defaultValue: 'ease', intoMultiplexMode: 'real', vendorPrefixes: [ '-moz-', '-o-', '-webkit-' ] }, 'vertical-align': { canOverride: canOverride.property.verticalAlign, defaultValue: 'baseline' }, 'visibility': { canOverride: canOverride.property.visibility, defaultValue: 'visible' }, 'white-space': { canOverride: canOverride.property.whiteSpace, defaultValue: 'normal' }, 'width': { canOverride: canOverride.generic.unit, defaultValue: 'auto', shortestValue: '0' }, 'z-index': { canOverride: canOverride.property.zIndex, defaultValue: 'auto' } };
function cloneDescriptor(propertyName, prefix) { var clonedDescriptor = override(compactable[propertyName], {});
if ('componentOf' in clonedDescriptor) { clonedDescriptor.componentOf = clonedDescriptor.componentOf.map(function (shorthandName) { return prefix + shorthandName; }); }
if ('components' in clonedDescriptor) { clonedDescriptor.components = clonedDescriptor.components.map(function (longhandName) { return prefix + longhandName; }); }
if ('keepUnlessDefault' in clonedDescriptor) { clonedDescriptor.keepUnlessDefault = prefix + clonedDescriptor.keepUnlessDefault; }
return clonedDescriptor; }
// generate vendor-prefixed properties
var vendorPrefixedCompactable = {};
for (var propertyName in compactable) { var descriptor = compactable[propertyName];
if (!('vendorPrefixes' in descriptor)) { continue; }
for (var i = 0; i < descriptor.vendorPrefixes.length; i++) { var prefix = descriptor.vendorPrefixes[i]; var clonedDescriptor = cloneDescriptor(propertyName, prefix); delete clonedDescriptor.vendorPrefixes;
vendorPrefixedCompactable[prefix + propertyName] = clonedDescriptor; }
delete descriptor.vendorPrefixes; }
module.exports = override(compactable, vendorPrefixedCompactable);
|