backoffice/node_modules/clean-css/lib/properties/optimizer.js

278 lines
9.9 KiB
JavaScript

var processableInfo = require('./processable');
var overrideCompactor = require('./override-compactor');
var shorthandCompactor = require('./shorthand-compactor');
module.exports = function Optimizer(compatibility, aggressiveMerging, context) {
var overridable = {
'animation-delay': ['animation'],
'animation-direction': ['animation'],
'animation-duration': ['animation'],
'animation-fill-mode': ['animation'],
'animation-iteration-count': ['animation'],
'animation-name': ['animation'],
'animation-play-state': ['animation'],
'animation-timing-function': ['animation'],
'-moz-animation-delay': ['-moz-animation'],
'-moz-animation-direction': ['-moz-animation'],
'-moz-animation-duration': ['-moz-animation'],
'-moz-animation-fill-mode': ['-moz-animation'],
'-moz-animation-iteration-count': ['-moz-animation'],
'-moz-animation-name': ['-moz-animation'],
'-moz-animation-play-state': ['-moz-animation'],
'-moz-animation-timing-function': ['-moz-animation'],
'-o-animation-delay': ['-o-animation'],
'-o-animation-direction': ['-o-animation'],
'-o-animation-duration': ['-o-animation'],
'-o-animation-fill-mode': ['-o-animation'],
'-o-animation-iteration-count': ['-o-animation'],
'-o-animation-name': ['-o-animation'],
'-o-animation-play-state': ['-o-animation'],
'-o-animation-timing-function': ['-o-animation'],
'-webkit-animation-delay': ['-webkit-animation'],
'-webkit-animation-direction': ['-webkit-animation'],
'-webkit-animation-duration': ['-webkit-animation'],
'-webkit-animation-fill-mode': ['-webkit-animation'],
'-webkit-animation-iteration-count': ['-webkit-animation'],
'-webkit-animation-name': ['-webkit-animation'],
'-webkit-animation-play-state': ['-webkit-animation'],
'-webkit-animation-timing-function': ['-webkit-animation'],
'background-clip': ['background'],
'background-origin': ['background'],
'border-color': ['border'],
'border-style': ['border'],
'border-width': ['border'],
'border-bottom': ['border'],
'border-bottom-color': ['border-bottom', 'border-color', 'border'],
'border-bottom-style': ['border-bottom', 'border-style', 'border'],
'border-bottom-width': ['border-bottom', 'border-width', 'border'],
'border-left': ['border'],
'border-left-color': ['border-left', 'border-color', 'border'],
'border-left-style': ['border-left', 'border-style', 'border'],
'border-left-width': ['border-left', 'border-width', 'border'],
'border-right': ['border'],
'border-right-color': ['border-right', 'border-color', 'border'],
'border-right-style': ['border-right', 'border-style', 'border'],
'border-right-width': ['border-right', 'border-width', 'border'],
'border-top': ['border'],
'border-top-color': ['border-top', 'border-color', 'border'],
'border-top-style': ['border-top', 'border-style', 'border'],
'border-top-width': ['border-top', 'border-width', 'border'],
'font-family': ['font'],
'font-size': ['font'],
'font-style': ['font'],
'font-variant': ['font'],
'font-weight': ['font'],
'list-style-image': ['list-style'],
'list-style-position': ['list-style'],
'list-style-type': ['list-style'],
'margin-bottom': ['margin'],
'margin-left': ['margin'],
'margin-right': ['margin'],
'margin-top': ['margin'],
'outline-color': ['outline'],
'outline-style': ['outline'],
'outline-width': ['outline'],
'padding-bottom': ['padding'],
'padding-left': ['padding'],
'padding-right': ['padding'],
'padding-top': ['padding'],
'transition-delay': ['transition'],
'transition-duration': ['transition'],
'transition-property': ['transition'],
'transition-timing-function': ['transition'],
'-moz-transition-delay': ['-moz-transition'],
'-moz-transition-duration': ['-moz-transition'],
'-moz-transition-property': ['-moz-transition'],
'-moz-transition-timing-function': ['-moz-transition'],
'-o-transition-delay': ['-o-transition'],
'-o-transition-duration': ['-o-transition'],
'-o-transition-property': ['-o-transition'],
'-o-transition-timing-function': ['-o-transition'],
'-webkit-transition-delay': ['-webkit-transition'],
'-webkit-transition-duration': ['-webkit-transition'],
'-webkit-transition-property': ['-webkit-transition'],
'-webkit-transition-timing-function': ['-webkit-transition']
};
var IE_BACKSLASH_HACK = '\\9';
var overrides = {};
for (var granular in overridable) {
for (var i = 0; i < overridable[granular].length; i++) {
var coarse = overridable[granular][i];
var list = overrides[coarse];
if (list)
list.push(granular);
else
overrides[coarse] = [granular];
}
}
var tokenize = function(body, selector) {
var tokens = body.split(';');
var keyValues = [];
if (tokens.length === 0 || (tokens.length == 1 && tokens[0].indexOf(IE_BACKSLASH_HACK) == -1 && tokens[0][tokens[0].length - 1] != ':'))
return;
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
if (token === '')
continue;
var firstColon = token.indexOf(':');
var property = token.substring(0, firstColon);
var value = token.substring(firstColon + 1);
if (value === '') {
context.warnings.push('Empty property \'' + property + '\' inside \'' + selector + '\' selector. Ignoring.');
continue;
}
keyValues.push([
property,
value,
token.indexOf('!important') > -1,
token.indexOf(IE_BACKSLASH_HACK, firstColon + 1) === token.length - IE_BACKSLASH_HACK.length
]);
}
return keyValues;
};
var optimize = function(tokens, allowAdjacent) {
var merged = [];
var properties = [];
var lastProperty = null;
var rescanTrigger = {};
var removeOverridenBy = function(property, isImportant) {
var overrided = overrides[property];
for (var i = 0, l = overrided.length; i < l; i++) {
for (var j = 0; j < properties.length; j++) {
if (properties[j] != overrided[i] || (merged[j][2] && !isImportant))
continue;
merged.splice(j, 1);
properties.splice(j, 1);
j -= 1;
}
}
};
var mergeablePosition = function(position) {
if (allowAdjacent === false || allowAdjacent === true)
return allowAdjacent;
return allowAdjacent.indexOf(position) > -1;
};
tokensLoop:
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
var property = token[0];
var value = token[1];
var isImportant = token[2];
var isIEHack = token[3];
var _property = (property == '-ms-filter' || property == 'filter') ?
(lastProperty == 'background' || lastProperty == 'background-image' ? lastProperty : property) :
property;
var toOverridePosition = 0;
if (!compatibility && isIEHack)
continue;
// comment is necessary - we assume that if two properties are one after another
// then it is intentional way of redefining property which may not be widely supported
// e.g. a{display:inline-block;display:-moz-inline-box}
// however if `mergeablePosition` yields true then the rule does not apply
// (e.g merging two adjacent selectors: `a{display:block}a{display:block}`)
if (aggressiveMerging && _property != lastProperty || mergeablePosition(i)) {
while (true) {
toOverridePosition = properties.indexOf(_property, toOverridePosition);
if (toOverridePosition == -1)
break;
var lastToken = merged[toOverridePosition];
var wasImportant = lastToken[2];
var wasIEHack = lastToken[3];
if (wasImportant && !isImportant)
continue tokensLoop;
if (compatibility && !wasIEHack && isIEHack)
break;
var _info = processableInfo.processable[_property];
if (!isIEHack && !wasIEHack && _info && _info.canOverride && !_info.canOverride(tokens[toOverridePosition][1], value))
break;
merged.splice(toOverridePosition, 1);
properties.splice(toOverridePosition, 1);
}
}
merged.push(token);
properties.push(_property);
// certain properties (see values of `overridable`) should trigger removal of
// more granular properties (see keys of `overridable`)
if (rescanTrigger[_property])
removeOverridenBy(_property, isImportant);
// add rescan triggers - if certain property appears later in the list a rescan needs
// to be triggered, e.g 'border-top' triggers a rescan after 'border-top-width' and
// 'border-top-color' as they can be removed
for (var j = 0, list = overridable[_property] || [], m = list.length; j < m; j++)
rescanTrigger[list[j]] = true;
lastProperty = _property;
}
return merged;
};
var rebuild = function(tokens) {
var flat = [];
for (var i = 0, l = tokens.length; i < l; i++) {
flat.push(tokens[i][0] + ':' + tokens[i][1]);
}
return flat.join(';');
};
var compact = function (input) {
var processable = processableInfo.processable;
var Token = processableInfo.Token;
var tokens = Token.tokenize(input);
tokens = overrideCompactor.compactOverrides(tokens, processable, Token, compatibility);
tokens = shorthandCompactor.compactShorthands(tokens, false, processable, Token);
tokens = shorthandCompactor.compactShorthands(tokens, true, processable, Token);
return Token.detokenize(tokens);
};
return {
process: function(body, allowAdjacent, skipCompacting, selector) {
var result = body;
var tokens = tokenize(body, selector);
if (tokens) {
var optimized = optimize(tokens, allowAdjacent);
result = rebuild(optimized);
}
if (!skipCompacting && processableInfo.implementedFor.test(result)) {
result = compact(result);
}
return result;
}
};
};