123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
-
- // Compacts the given tokens according to their ability to override each other.
-
- var validator = require('./validator');
-
- module.exports = (function () {
- // Default override function: only allow overrides when the two values are the same
- var sameValue = function (val1, val2) {
- return val1 === val2;
- };
-
- var compactOverrides = function (tokens, processable, Token, compatibility) {
- var result, can, token, t, i, ii, iiii, oldResult, matchingComponent;
-
- // Used when searching for a component that matches token
- var nameMatchFilter1 = function (x) {
- return x.prop === token.prop;
- };
- // Used when searching for a component that matches t
- var nameMatchFilter2 = function (x) {
- return x.prop === t.prop;
- };
-
- function willResultInShorterValue (shorthand, token) {
- var shorthandCopy = shorthand.clone();
- shorthandCopy.isDirty = true;
- shorthandCopy.isShorthand = true;
- shorthandCopy.components = [];
-
- shorthand.components.forEach(function (component) {
- var componentCopy = component.clone();
- if (component.prop == token.prop)
- componentCopy.value = token.value;
-
- shorthandCopy.components.push(componentCopy);
- });
-
- return Token.getDetokenizedLength([shorthand, token]) >= Token.getDetokenizedLength([shorthandCopy]);
- }
-
- // Go from the end and always take what the current token can't override as the new result set
- // NOTE: can't cache result.length here because it will change with every iteration
- for (result = tokens, i = 0; (ii = result.length - 1 - i) >= 0; i++) {
- token = result[ii];
- can = (processable[token.prop] && processable[token.prop].canOverride) || sameValue;
- oldResult = result;
- result = [];
-
- // Special flag which indicates that the current token should be removed
- var removeSelf = false;
- var oldResultLength = oldResult.length;
-
- for (var iii = 0; iii < oldResultLength; iii++) {
- t = oldResult[iii];
-
- // A token can't override itself (checked by reference, not by value)
- // NOTE: except when we explicitly tell it to remove itself
- if (t === token && !removeSelf) {
- result.push(t);
- continue;
- }
-
- // Only an important token can even try to override tokens that come after it
- if (iii > ii && !token.isImportant) {
- result.push(t);
- continue;
- }
-
- // A nonimportant token can never override an important one
- if (t.isImportant && !token.isImportant) {
- result.push(t);
- continue;
- }
-
- if (token.isShorthand && !t.isShorthand && t.isComponentOf(token)) {
- // token (a shorthand) is trying to override t (a component)
-
- // Find the matching component in the shorthand
- matchingComponent = token.components.filter(nameMatchFilter2)[0];
- can = (processable[t.prop] && processable[t.prop].canOverride) || sameValue;
- if (!can(t.value, matchingComponent.value)) {
- // The shorthand can't override the component
- result.push(t);
- }
- } else if (t.isShorthand && !token.isShorthand && token.isComponentOf(t)) {
- // token (a component) is trying to override a component of t (a shorthand)
-
- // Find the matching component in the shorthand
- matchingComponent = t.components.filter(nameMatchFilter1)[0];
- if (can(matchingComponent.value, token.value)) {
- // The component can override the matching component in the shorthand
-
- if (compatibility) {
- // in compatibility mode check if shorthand in not less understandable than merged-in value
- var wouldBreakCompatibility = false;
- for (iiii = 0; iiii < t.components.length; iiii++) {
- var o = processable[t.components[iiii].prop];
- can = (o && o.canOverride) || sameValue;
-
- if (!can(o.defaultValue, t.components[iiii].value)) {
- wouldBreakCompatibility = true;
- break;
- }
- }
-
- if (wouldBreakCompatibility) {
- result.push(t);
- continue;
- }
- }
-
- if ((!token.isImportant || token.isImportant && matchingComponent.isImportant) && willResultInShorterValue(t, token)) {
- // The overriding component is non-important which means we can simply include it into the shorthand
- // NOTE: stuff that can't really be included, like inherit, is taken care of at the final step, not here
- matchingComponent.value = token.value;
- // We use the special flag to get rid of the component
- removeSelf = true;
- } else {
- // The overriding component is important; sadly we can't get rid of it,
- // but we can still mark the matching component in the shorthand as irrelevant
- matchingComponent.isIrrelevant = true;
- }
- t.isDirty = true;
- }
- result.push(t);
- } else if (token.isShorthand && t.isShorthand && token.prop === t.prop) {
- // token is a shorthand and is trying to override another instance of the same shorthand
-
- // Can only override other shorthand when each of its components can override each of the other's components
- for (iiii = 0; iiii < t.components.length; iiii++) {
- can = (processable[t.components[iiii].prop] && processable[t.components[iiii].prop].canOverride) || sameValue;
- if (!can(t.components[iiii].value, token.components[iiii].value)) {
- result.push(t);
- break;
- }
- if (t.components[iiii].isImportant && token.components[iiii].isImportant && (validator.isValidFunction(t.components[iiii].value) ^ validator.isValidFunction(token.components[iiii].value))) {
- result.push(t);
- break;
- }
- }
- } else if (t.prop !== token.prop || !can(t.value, token.value)) {
- // in every other case, use the override mechanism
- result.push(t);
- } else if (t.isImportant && token.isImportant && (validator.isValidFunction(t.value) ^ validator.isValidFunction(token.value))) {
- result.push(t);
- }
- }
- if (removeSelf) {
- i--;
- }
- }
-
- return result;
- };
-
- return {
- compactOverrides: compactOverrides
- };
-
- })();
|