123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
-
- 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;
- }
- };
- };
|