123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865 |
-
- // Contains the interpretation of CSS properties, as used by the property optimizer
-
- module.exports = (function () {
-
- var tokenModule = require('./token');
- var validator = require('./validator');
- var Splitter = require('../text/splitter');
-
- // Functions that decide what value can override what.
- // The main purpose is to disallow removing CSS fallbacks.
- // A separate implementation is needed for every different kind of CSS property.
- // -----
- // The generic idea is that properties that have wider browser support are 'more understandable'
- // than others and that 'less understandable' values can't override more understandable ones.
- var canOverride = {
- // Use when two tokens of the same property can always be merged
- always: function () {
- // NOTE: We could have (val1, val2) parameters here but jshint complains because we don't use them
- return true;
- },
- // Use when two tokens of the same property can only be merged if they have the same value
- sameValue: function(val1, val2) {
- return val1 === val2;
- },
- sameFunctionOrValue: function(val1, val2) {
- // Functions with the same name can override each other
- if (validator.areSameFunction(val1, val2)) {
- return true;
- }
-
- return val1 === val2;
- },
- // Use for properties containing CSS units (margin-top, padding-left, etc.)
- unit: function(val1, val2) {
- // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
- // Understandability: (unit without functions) > (same functions | standard functions) > anything else
- // NOTE: there is no point in having different vendor-specific functions override each other or standard functions,
- // or having standard functions override vendor-specific functions, but standard functions can override each other
- // NOTE: vendor-specific property values are not taken into consideration here at the moment
-
- if (validator.isValidUnitWithoutFunction(val2))
- return true;
- if (validator.isValidUnitWithoutFunction(val1))
- return false;
-
- // Standard non-vendor-prefixed functions can override each other
- if (validator.isValidFunctionWithoutVendorPrefix(val2) && validator.isValidFunctionWithoutVendorPrefix(val1)) {
- return true;
- }
-
- // Functions with the same name can override each other; same values can override each other
- return canOverride.sameFunctionOrValue(val1, val2);
- },
- // Use for color properties (color, background-color, border-color, etc.)
- color: function(val1, val2) {
- // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
- // Understandability: (hex | named) > (rgba | hsla) > (same function name) > anything else
- // NOTE: at this point rgb and hsl are replaced by hex values by clean-css
-
- // (hex | named)
- if (validator.isValidNamedColor(val2) || validator.isValidHexColor(val2))
- return true;
- if (validator.isValidNamedColor(val1) || validator.isValidHexColor(val1))
- return false;
-
- // (rgba|hsla)
- if (validator.isValidRgbaColor(val2) || validator.isValidHslaColor(val2))
- return true;
- if (validator.isValidRgbaColor(val1) || validator.isValidHslaColor(val1))
- return false;
-
- // Functions with the same name can override each other; same values can override each other
- return canOverride.sameFunctionOrValue(val1, val2);
- },
- // Use for background-image
- backgroundImage: function(val1, val2) {
- // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
- // Understandability: (none | url | inherit) > (same function) > (same value)
-
- // (none | url)
- if (val2 === 'none' || val2 === 'inherit' || validator.isValidUrl(val2))
- return true;
- if (val1 === 'none' || val1 === 'inherit' || validator.isValidUrl(val1))
- return false;
-
- // Functions with the same name can override each other; same values can override each other
- return canOverride.sameFunctionOrValue(val1, val2);
- },
- border: function(val1, val2) {
- var brokenUp1 = breakUp.border(Token.tokenizeOne(val1));
- var brokenUp2 = breakUp.border(Token.tokenizeOne(val2));
-
- return canOverride.color(brokenUp1[2].value, brokenUp2[2].value);
- }
- };
- canOverride = Object.freeze(canOverride);
-
- // Functions for breaking up shorthands to components
- var breakUp = {};
- breakUp.takeCareOfFourValues = function (splitfunc) {
- return function (token) {
- var descriptor = processable[token.prop];
- var result = [];
- var splitval = splitfunc(token.value);
-
- if (splitval.length === 0 || (splitval.length < descriptor.components.length && descriptor.components.length > 4)) {
- // This token is malformed and we have no idea how to fix it. So let's just keep it intact
- return [token];
- }
-
- // Fix those that we do know how to fix
- if (splitval.length < descriptor.components.length && splitval.length < 2) {
- // foo{margin:1px} -> foo{margin:1px 1px}
- splitval[1] = splitval[0];
- }
- if (splitval.length < descriptor.components.length && splitval.length < 3) {
- // foo{margin:1px 2px} -> foo{margin:1px 2px 1px}
- splitval[2] = splitval[0];
- }
- if (splitval.length < descriptor.components.length && splitval.length < 4) {
- // foo{margin:1px 2px 3px} -> foo{margin:1px 2px 3px 2px}
- splitval[3] = splitval[1];
- }
-
- // Now break it up to its components
- for (var i = 0; i < descriptor.components.length; i++) {
- var t = new Token(descriptor.components[i], splitval[i], token.isImportant);
- result.push(t);
- }
-
- return result;
- };
- };
- // Use this when you simply want to break up four values along spaces
- breakUp.fourBySpaces = breakUp.takeCareOfFourValues(function (val) {
- return new Splitter(' ').split(val).filter(function (v) { return v; });
- });
- // Breaks up a background property value
- breakUp.commaSeparatedMulitpleValues = function (splitfunc) {
- return function (token) {
- if (token.value.indexOf(',') === -1)
- return splitfunc(token);
-
- var values = new Splitter(',').split(token.value);
- var components = [];
-
- for (var i = 0, l = values.length; i < l; i++) {
- token.value = values[i];
- components.push(splitfunc(token));
- }
-
- for (var j = 0, m = components[0].length; j < m; j++) {
- for (var k = 0, n = components.length, newValues = []; k < n; k++) {
- newValues.push(components[k][j].value);
- }
-
- components[0][j].value = newValues.join(',');
- }
-
- return components[0];
- };
- };
- breakUp.background = function (token) {
- // Default values
- var result = Token.makeDefaults(['background-image', 'background-position', 'background-size', 'background-repeat', 'background-attachment', 'background-color'], token.isImportant);
- var image = result[0];
- var position = result[1];
- var size = result[2];
- var repeat = result[3];
- var attachment = result[4];
- var color = result[5];
- var positionSet = false;
-
- // Take care of inherit
- if (token.value === 'inherit') {
- // NOTE: 'inherit' is not a valid value for background-attachment so there we'll leave the default value
- color.value = image.value = repeat.value = position.value = size.value = attachment.value = 'inherit';
- return result;
- }
-
- // Break the background up into parts
- var parts = new Splitter(' ').split(token.value);
- if (parts.length === 0)
- return result;
-
- // Iterate over all parts and try to fit them into positions
- for (var i = parts.length - 1; i >= 0; i--) {
- var currentPart = parts[i];
-
- if (validator.isValidBackgroundAttachment(currentPart)) {
- attachment.value = currentPart;
- } else if (validator.isValidBackgroundRepeat(currentPart)) {
- repeat.value = currentPart;
- } else if (validator.isValidBackgroundPositionPart(currentPart) || validator.isValidBackgroundSizePart(currentPart)) {
- if (i > 0) {
- var previousPart = parts[i - 1];
-
- if (previousPart.indexOf('/') > 0) {
- var twoParts = new Splitter('/').split(previousPart);
- size.value = twoParts.pop() + ' ' + currentPart;
- parts[i - 1] = twoParts.pop();
- } else if (i > 1 && parts[i - 2] == '/') {
- size.value = previousPart + ' ' + currentPart;
- i -= 2;
- } else if (parts[i - 1] == '/') {
- size.value = currentPart;
- } else {
- position.value = currentPart + (positionSet ? ' ' + position.value : '');
- positionSet = true;
- }
- } else {
- position.value = currentPart + (positionSet ? ' ' + position.value : '');
- positionSet = true;
- }
- } else if (validator.isValidBackgroundPositionAndSize(currentPart)) {
- var sizeValue = new Splitter('/').split(currentPart);
- size.value = sizeValue.pop();
- position.value = sizeValue.pop();
- } else if ((color.value == processable[color.prop].defaultValue || color.value == 'none') && validator.isValidColor(currentPart)) {
- color.value = currentPart;
- } else if (validator.isValidUrl(currentPart) || validator.isValidFunction(currentPart)) {
- image.value = currentPart;
- }
- }
-
- return result;
- };
- // Breaks up a list-style property value
- breakUp.listStyle = function (token) {
- // Default values
- var result = Token.makeDefaults(['list-style-type', 'list-style-position', 'list-style-image'], token.isImportant);
- var type = result[0], position = result[1], image = result[2];
-
- if (token.value === 'inherit') {
- type.value = position.value = image.value = 'inherit';
- return result;
- }
-
- var parts = new Splitter(' ').split(token.value);
- var ci = 0;
-
- // Type
- if (ci < parts.length && validator.isValidListStyleType(parts[ci])) {
- type.value = parts[ci];
- ci++;
- }
- // Position
- if (ci < parts.length && validator.isValidListStylePosition(parts[ci])) {
- position.value = parts[ci];
- ci++;
- }
- // Image
- if (ci < parts.length) {
- image.value = parts.splice(ci, parts.length - ci + 1).join(' ');
- }
-
- return result;
- };
-
- breakUp._widthStyleColor = function(token, prefix, order) {
- // Default values
- var components = order.map(function(prop) {
- return prefix + '-' + prop;
- });
- var result = Token.makeDefaults(components, token.isImportant);
- var color = result[order.indexOf('color')];
- var style = result[order.indexOf('style')];
- var width = result[order.indexOf('width')];
-
- // Take care of inherit
- if (token.value === 'inherit' || token.value === 'inherit inherit inherit') {
- color.value = style.value = width.value = 'inherit';
- return result;
- }
-
- // NOTE: usually users don't follow the required order of parts in this shorthand,
- // so we'll try to parse it caring as little about order as possible
-
- var parts = new Splitter(' ').split(token.value), w;
-
- if (parts.length === 0) {
- return result;
- }
-
- if (parts.length >= 1) {
- // Try to find -width, excluding inherit because that can be anything
- w = parts.filter(function(p) { return p !== 'inherit' && validator.isValidOutlineWidth(p); });
- if (w.length) {
- width.value = w[0];
- parts.splice(parts.indexOf(w[0]), 1);
- }
- }
- if (parts.length >= 1) {
- // Try to find -style, excluding inherit because that can be anything
- w = parts.filter(function(p) { return p !== 'inherit' && validator.isValidOutlineStyle(p); });
- if (w.length) {
- style.value = w[0];
- parts.splice(parts.indexOf(w[0]), 1);
- }
- }
- if (parts.length >= 1) {
- // Find -color but this time can catch inherit
- w = parts.filter(function(p) { return validator.isValidOutlineColor(p); });
- if (w.length) {
- color.value = w[0];
- parts.splice(parts.indexOf(w[0]), 1);
- }
- }
-
- return result;
- };
-
- breakUp.outline = function(token) {
- return breakUp._widthStyleColor(token, 'outline', ['color', 'style', 'width']);
- };
-
- breakUp.border = function(token) {
- return breakUp._widthStyleColor(token, 'border', ['width', 'style', 'color']);
- };
-
- breakUp.borderRadius = function(token) {
- var parts = token.value.split('/');
- if (parts.length == 1)
- return breakUp.fourBySpaces(token);
-
- var horizontalPart = token.clone();
- var verticalPart = token.clone();
-
- horizontalPart.value = parts[0];
- verticalPart.value = parts[1];
-
- var horizontalBreakUp = breakUp.fourBySpaces(horizontalPart);
- var verticalBreakUp = breakUp.fourBySpaces(verticalPart);
-
- for (var i = 0; i < 4; i++) {
- horizontalBreakUp[i].value = [horizontalBreakUp[i].value, verticalBreakUp[i].value];
- }
-
- return horizontalBreakUp;
- };
-
- // Contains functions that can put together shorthands from their components
- // NOTE: correct order of tokens is assumed inside these functions!
- var putTogether = {
- // Use this for properties which have four unit values (margin, padding, etc.)
- // NOTE: optimizes to shorter forms too (that only specify 1, 2, or 3 values)
- fourUnits: function (prop, tokens, isImportant) {
- // See about irrelevant tokens
- // NOTE: This will enable some crazy optimalizations for us.
- if (tokens[0].isIrrelevant)
- tokens[0].value = tokens[2].value;
- if (tokens[2].isIrrelevant)
- tokens[2].value = tokens[0].value;
- if (tokens[1].isIrrelevant)
- tokens[1].value = tokens[3].value;
- if (tokens[3].isIrrelevant)
- tokens[3].value = tokens[1].value;
-
- if (tokens[0].isIrrelevant && tokens[2].isIrrelevant) {
- if (tokens[1].value === tokens[3].value)
- tokens[0].value = tokens[2].value = tokens[1].value;
- else
- tokens[0].value = tokens[2].value = '0';
- }
- if (tokens[1].isIrrelevant && tokens[3].isIrrelevant) {
- if (tokens[0].value === tokens[2].value)
- tokens[1].value = tokens[3].value = tokens[0].value;
- else
- tokens[1].value = tokens[3].value = '0';
- }
-
- var result = new Token(prop, tokens[0].value, isImportant);
- result.granularValues = [];
- result.granularValues[tokens[0].prop] = tokens[0].value;
- result.granularValues[tokens[1].prop] = tokens[1].value;
- result.granularValues[tokens[2].prop] = tokens[2].value;
- result.granularValues[tokens[3].prop] = tokens[3].value;
-
- // If all of them are irrelevant
- if (tokens[0].isIrrelevant && tokens[1].isIrrelevant && tokens[2].isIrrelevant && tokens[3].isIrrelevant) {
- result.value = processable[prop].shortestValue || processable[prop].defaultValue;
- return result;
- }
-
- // 1-value short form: all four components are equal
- if (tokens[0].value === tokens[1].value && tokens[0].value === tokens[2].value && tokens[0].value === tokens[3].value) {
- return result;
- }
- result.value += ' ' + tokens[1].value;
- // 2-value short form: first and third; second and fourth values are equal
- if (tokens[0].value === tokens[2].value && tokens[1].value === tokens[3].value) {
- return result;
- }
- result.value += ' ' + tokens[2].value;
- // 3-value short form: second and fourth values are equal
- if (tokens[1].value === tokens[3].value) {
- return result;
- }
- // 4-value form (none of the above optimalizations could be accomplished)
- result.value += ' ' + tokens[3].value;
- return result;
- },
- // Puts together the components by spaces and omits default values (this is the case for most shorthands)
- bySpacesOmitDefaults: function (prop, tokens, isImportant, meta) {
- var result = new Token(prop, '', isImportant);
-
- // Get irrelevant tokens
- var irrelevantTokens = tokens.filter(function (t) { return t.isIrrelevant; });
-
- // If every token is irrelevant, return shortest possible value, fallback to default value
- if (irrelevantTokens.length === tokens.length) {
- result.isIrrelevant = true;
- result.value = processable[prop].shortestValue || processable[prop].defaultValue;
- return result;
- }
-
- // This will be the value of the shorthand if all the components are default
- var valueIfAllDefault = processable[prop].defaultValue;
-
- // Go through all tokens and concatenate their values as necessary
- for (var i = 0; i < tokens.length; i++) {
- var token = tokens[i];
-
- // Set granular value so that other parts of the code can use this for optimalization opportunities
- result.granularValues = result.granularValues || { };
- result.granularValues[token.prop] = token.value;
-
- // Use irrelevant tokens for optimalization opportunity
- if (token.isIrrelevant) {
- // Get shortest possible value, fallback to default value
- var tokenShortest = processable[token.prop].shortestValue || processable[token.prop].defaultValue;
- // If the shortest possible value of this token is shorter than the default value of the shorthand, use it instead
- if (tokenShortest.length < valueIfAllDefault.length) {
- valueIfAllDefault = tokenShortest;
- }
- }
-
- // Omit default / irrelevant value
- if (token.isIrrelevant || (processable[token.prop] && processable[token.prop].defaultValue === token.value)) {
- continue;
- }
-
- if (meta && meta.partsCount && meta.position < meta.partsCount - 1 && processable[token.prop].multiValueLastOnly)
- continue;
-
- var requiresPreceeding = processable[token.prop].shorthandFollows;
- if (requiresPreceeding && (tokens[i - 1].value == processable[requiresPreceeding].defaultValue)) {
- result.value += ' ' + tokens[i - 1].value;
- }
-
- result.value += (processable[token.prop].prefixShorthandValueWith || ' ') + token.value;
- }
-
- result.value = result.value.trim();
- if (!result.value) {
- result.value = valueIfAllDefault;
- }
-
- return result;
- },
- commaSeparatedMulitpleValues: function (assembleFunction) {
- return function(prop, tokens, isImportant) {
- var tokenSplitLengths = tokens.map(function (token) {
- return new Splitter(',').split(token.value).length;
- });
- var partsCount = Math.max.apply(Math, tokenSplitLengths);
-
- if (partsCount == 1)
- return assembleFunction(prop, tokens, isImportant);
-
- var merged = [];
-
- for (var i = 0; i < partsCount; i++) {
- merged.push([]);
-
- for (var j = 0; j < tokens.length; j++) {
- var split = new Splitter(',').split(tokens[j].value);
- merged[i].push(split[i] || split[0]);
- }
- }
-
- var mergedValues = [];
- var firstProcessed;
- for (i = 0; i < partsCount; i++) {
- var newTokens = [];
- for (var k = 0, n = merged[i].length; k < n; k++) {
- var newToken = tokens[k].clone();
- newToken.value = merged[i][k];
- newTokens.push(newToken);
- }
-
- var meta = {
- partsCount: partsCount,
- position: i
- };
- var processed = assembleFunction(prop, newTokens, isImportant, meta);
- mergedValues.push(processed.value);
-
- if (!firstProcessed)
- firstProcessed = processed;
- }
-
- firstProcessed.value = mergedValues.join(',');
- return firstProcessed;
- };
- },
- // Handles the cases when some or all the fine-grained properties are set to inherit
- takeCareOfInherit: function (innerFunc) {
- return function (prop, tokens, isImportant, meta) {
- // Filter out the inheriting and non-inheriting tokens in one iteration
- var inheritingTokens = [];
- var nonInheritingTokens = [];
- var result2Shorthandable = [];
- var i;
- for (i = 0; i < tokens.length; i++) {
- if (tokens[i].value === 'inherit') {
- inheritingTokens.push(tokens[i]);
-
- // Indicate that this property is irrelevant and its value can safely be set to anything else
- var r2s = new Token(tokens[i].prop, tokens[i].isImportant);
- r2s.isIrrelevant = true;
- result2Shorthandable.push(r2s);
- } else {
- nonInheritingTokens.push(tokens[i]);
- result2Shorthandable.push(tokens[i]);
- }
- }
-
- if (nonInheritingTokens.length === 0) {
- // When all the tokens are 'inherit'
- return new Token(prop, 'inherit', isImportant);
- } else if (inheritingTokens.length > 0) {
- // When some (but not all) of the tokens are 'inherit'
-
- // Result 1. Shorthand just the inherit values and have it overridden with the non-inheriting ones
- var result1 = [new Token(prop, 'inherit', isImportant)].concat(nonInheritingTokens);
-
- // Result 2. Shorthand every non-inherit value and then have it overridden with the inheriting ones
- var result2 = [innerFunc(prop, result2Shorthandable, isImportant, meta)].concat(inheritingTokens);
-
- // Return whichever is shorter
- var dl1 = Token.getDetokenizedLength(result1);
- var dl2 = Token.getDetokenizedLength(result2);
-
- return dl1 < dl2 ? result1 : result2;
- } else {
- // When none of tokens are 'inherit'
- return innerFunc(prop, tokens, isImportant, meta);
- }
- };
- },
- borderRadius: function (prop, tokens, isImportant) {
- var verticalTokens = [];
- var newTokens = [];
-
- for (var i = 0, l = tokens.length; i < l; i++) {
- var token = tokens[i];
- var newToken = token.clone();
- newTokens.push(newToken);
- if (!Array.isArray(token.value))
- continue;
-
- if (token.value.length > 1) {
- verticalTokens.push({
- prop: token.prop,
- value: token.value[1],
- isImportant: token.isImportant
- });
- }
-
- newToken.value = token.value[0];
- }
-
- var result = putTogether.takeCareOfInherit(putTogether.fourUnits)(prop, newTokens, isImportant);
- if (verticalTokens.length > 0) {
- var verticalResult = putTogether.takeCareOfInherit(putTogether.fourUnits)(prop, verticalTokens, isImportant);
- if (result.value != verticalResult.value)
- result.value += '/' + verticalResult.value;
- }
-
- return result;
- }
- };
-
- // 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 (Default is canOverride.sameValue - meaning that they'll only be merged if they have the same value.)
- // 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.
- //
- // * putTogether: function (Only specify for shorthand properties.)
- // Puts the shorthand together from its components.
- //
- var processable = {
- 'color': {
- canOverride: canOverride.color,
- defaultValue: 'transparent',
- shortestValue: 'red'
- },
- // background ------------------------------------------------------------------------------
- 'background': {
- components: [
- 'background-image',
- 'background-position',
- 'background-size',
- 'background-repeat',
- 'background-attachment',
- 'background-color'
- ],
- breakUp: breakUp.commaSeparatedMulitpleValues(breakUp.background),
- putTogether: putTogether.commaSeparatedMulitpleValues(
- putTogether.takeCareOfInherit(putTogether.bySpacesOmitDefaults)
- ),
- defaultValue: '0 0',
- shortestValue: '0'
- },
- 'background-color': {
- canOverride: canOverride.color,
- defaultValue: 'transparent',
- multiValueLastOnly: true,
- shortestValue: 'red'
- },
- 'background-image': {
- canOverride: canOverride.backgroundImage,
- defaultValue: 'none'
- },
- 'background-repeat': {
- canOverride: canOverride.always,
- defaultValue: 'repeat'
- },
- 'background-position': {
- canOverride: canOverride.always,
- defaultValue: '0 0',
- shortestValue: '0'
- },
- 'background-size': {
- canOverride: canOverride.always,
- defaultValue: 'auto',
- shortestValue: '0 0',
- prefixShorthandValueWith: '/',
- shorthandFollows: 'background-position'
- },
- 'background-attachment': {
- canOverride: canOverride.always,
- defaultValue: 'scroll'
- },
- 'border': {
- breakUp: breakUp.border,
- canOverride: canOverride.border,
- components: [
- 'border-width',
- 'border-style',
- 'border-color'
- ],
- defaultValue: 'none',
- putTogether: putTogether.takeCareOfInherit(putTogether.bySpacesOmitDefaults)
- },
- 'border-color': {
- canOverride: canOverride.color,
- defaultValue: 'none'
- },
- 'border-style': {
- canOverride: canOverride.always,
- defaultValue: 'none'
- },
- 'border-width': {
- canOverride: canOverride.unit,
- defaultValue: 'medium',
- shortestValue: '0'
- },
- // list-style ------------------------------------------------------------------------------
- 'list-style': {
- components: [
- 'list-style-type',
- 'list-style-position',
- 'list-style-image'
- ],
- canOverride: canOverride.always,
- breakUp: breakUp.listStyle,
- putTogether: putTogether.takeCareOfInherit(putTogether.bySpacesOmitDefaults),
- defaultValue: 'outside', // can't use 'disc' because that'd override default 'decimal' for <ol>
- shortestValue: 'none'
- },
- 'list-style-type' : {
- canOverride: canOverride.always,
- shortestValue: 'none',
- defaultValue: '__hack'
- // 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
- },
- 'list-style-position' : {
- canOverride: canOverride.always,
- defaultValue: 'outside',
- shortestValue: 'inside'
- },
- 'list-style-image' : {
- canOverride: canOverride.always,
- defaultValue: 'none'
- },
- // outline ------------------------------------------------------------------------------
- 'outline': {
- components: [
- 'outline-color',
- 'outline-style',
- 'outline-width'
- ],
- breakUp: breakUp.outline,
- putTogether: putTogether.takeCareOfInherit(putTogether.bySpacesOmitDefaults),
- defaultValue: '0'
- },
- 'outline-color': {
- canOverride: canOverride.color,
- defaultValue: 'invert',
- shortestValue: 'red'
- },
- 'outline-style': {
- canOverride: canOverride.always,
- defaultValue: 'none'
- },
- 'outline-width': {
- canOverride: canOverride.unit,
- defaultValue: 'medium',
- shortestValue: '0'
- },
- // transform
- '-moz-transform': {
- canOverride: canOverride.sameFunctionOrValue
- },
- '-ms-transform': {
- canOverride: canOverride.sameFunctionOrValue
- },
- '-webkit-transform': {
- canOverride: canOverride.sameFunctionOrValue
- },
- 'transform': {
- canOverride: canOverride.sameFunctionOrValue
- }
- };
-
- var addFourValueShorthand = function (prop, components, options) {
- options = options || {};
- processable[prop] = {
- components: components,
- breakUp: options.breakUp || breakUp.fourBySpaces,
- putTogether: options.putTogether || putTogether.takeCareOfInherit(putTogether.fourUnits),
- defaultValue: options.defaultValue || '0',
- shortestValue: options.shortestValue
- };
- for (var i = 0; i < components.length; i++) {
- processable[components[i]] = {
- breakUp: options.breakUp || breakUp.fourBySpaces,
- canOverride: options.canOverride || canOverride.unit,
- defaultValue: options.defaultValue || '0',
- shortestValue: options.shortestValue
- };
- }
- };
-
- ['', '-moz-', '-o-', '-webkit-'].forEach(function (prefix) {
- addFourValueShorthand(prefix + 'border-radius', [
- prefix + 'border-top-left-radius',
- prefix + 'border-top-right-radius',
- prefix + 'border-bottom-right-radius',
- prefix + 'border-bottom-left-radius'
- ], {
- breakUp: breakUp.borderRadius,
- putTogether: putTogether.borderRadius
- });
- });
-
- addFourValueShorthand('border-color', [
- 'border-top-color',
- 'border-right-color',
- 'border-bottom-color',
- 'border-left-color'
- ], {
- breakUp: breakUp.fourBySpaces,
- canOverride: canOverride.color,
- defaultValue: 'currentColor',
- shortestValue: 'red'
- });
-
- addFourValueShorthand('border-style', [
- 'border-top-style',
- 'border-right-style',
- 'border-bottom-style',
- 'border-left-style'
- ], {
- breakUp: breakUp.fourBySpaces,
- 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'
- ]);
-
- addFourValueShorthand('margin', [
- 'margin-top',
- 'margin-right',
- 'margin-bottom',
- 'margin-left'
- ]);
-
- // Set some stuff iteratively
- for (var proc in processable) {
- if (!processable.hasOwnProperty(proc))
- continue;
-
- var currDesc = processable[proc];
-
- if (!(currDesc.components instanceof Array) || currDesc.components.length === 0)
- continue;
-
- currDesc.isShorthand = true;
-
- for (var cI = 0; cI < currDesc.components.length; cI++) {
- if (!processable[currDesc.components[cI]]) {
- throw new Error('"' + currDesc.components[cI] + '" is defined as a component of "' + proc + '" but isn\'t defined in processable.');
- }
- processable[currDesc.components[cI]].componentOf = proc;
- }
- }
-
- var Token = tokenModule.createTokenPrototype(processable);
-
- return {
- implementedFor: /background|border|color|list|margin|outline|padding|transform/,
- processable: processable,
- Token: Token
- };
- })();
|