backoffice/node_modules/clean-css/lib/properties/override-compactor.js

161 lines
6.4 KiB
JavaScript

// 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
};
})();