Spaces:
Sleeping
Sleeping
| var sortSelectors = require('./sort-selectors'); | |
| var tidyRules = require('./tidy-rules'); | |
| var tidyBlock = require('./tidy-block'); | |
| var tidyAtRule = require('./tidy-at-rule'); | |
| var Hack = require('../hack'); | |
| var removeUnused = require('../remove-unused'); | |
| var restoreFromOptimizing = require('../restore-from-optimizing'); | |
| var wrapForOptimizing = require('../wrap-for-optimizing').all; | |
| var configuration = require('../configuration'); | |
| var optimizers = require('./value-optimizers'); | |
| var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; | |
| var Token = require('../../tokenizer/token'); | |
| var Marker = require('../../tokenizer/marker'); | |
| var formatPosition = require('../../utils/format-position'); | |
| var serializeRules = require('../../writer/one-time').rules; | |
| var CHARSET_TOKEN = '@charset'; | |
| var CHARSET_REGEXP = new RegExp('^' + CHARSET_TOKEN, 'i'); | |
| var DEFAULT_ROUNDING_PRECISION = require('../../options/rounding-precision').DEFAULT; | |
| var VARIABLE_PROPERTY_NAME_PATTERN = /^--\S+$/; | |
| var PROPERTY_NAME_PATTERN = /^(?:-chrome-|-[\w-]+\w|\w[\w-]+\w|\w{1,})$/; | |
| var IMPORT_PREFIX_PATTERN = /^@import/i; | |
| var URL_PREFIX_PATTERN = /^url\(/i; | |
| function startsAsUrl(value) { | |
| return URL_PREFIX_PATTERN.test(value); | |
| } | |
| function isImport(token) { | |
| return IMPORT_PREFIX_PATTERN.test(token[1]); | |
| } | |
| function isLegacyFilter(property) { | |
| var value; | |
| if (property.name == 'filter' || property.name == '-ms-filter') { | |
| value = property.value[0][1]; | |
| return value.indexOf('progid') > -1 | |
| || value.indexOf('alpha') === 0 | |
| || value.indexOf('chroma') === 0; | |
| } | |
| return false; | |
| } | |
| function noop() {} | |
| function noopValueOptimizer(_name, value, _options) { return value; } | |
| function optimizeBody(rule, properties, context) { | |
| var options = context.options; | |
| var valueOptimizers; | |
| var property, name, type, value; | |
| var propertyToken; | |
| var propertyOptimizer; | |
| var serializedRule = serializeRules(rule); | |
| var _properties = wrapForOptimizing(properties); | |
| var pluginValueOptimizers = context.options.plugins.level1Value; | |
| var pluginPropertyOptimizers = context.options.plugins.level1Property; | |
| var isVariable; | |
| var i, l; | |
| for (i = 0, l = _properties.length; i < l; i++) { | |
| var j, k, m, n; | |
| property = _properties[i]; | |
| name = property.name; | |
| propertyOptimizer = configuration[name] && configuration[name].propertyOptimizer || noop; | |
| valueOptimizers = configuration[name] && configuration[name].valueOptimizers || [optimizers.whiteSpace]; | |
| isVariable = VARIABLE_PROPERTY_NAME_PATTERN.test(name); | |
| if (isVariable) { | |
| valueOptimizers = options.variableOptimizers.length > 0 | |
| ? options.variableOptimizers | |
| : [optimizers.whiteSpace]; | |
| } | |
| if (!isVariable && !PROPERTY_NAME_PATTERN.test(name)) { | |
| propertyToken = property.all[property.position]; | |
| context.warnings.push('Invalid property name \'' + name + '\' at ' + formatPosition(propertyToken[1][2][0]) + '. Ignoring.'); | |
| property.unused = true; | |
| continue; | |
| } | |
| if (property.value.length === 0) { | |
| propertyToken = property.all[property.position]; | |
| context.warnings.push('Empty property \'' + name + '\' at ' + formatPosition(propertyToken[1][2][0]) + '. Ignoring.'); | |
| property.unused = true; | |
| continue; | |
| } | |
| if (property.hack && ( | |
| (property.hack[0] == Hack.ASTERISK || property.hack[0] == Hack.UNDERSCORE) | |
| && !options.compatibility.properties.iePrefixHack | |
| || property.hack[0] == Hack.BACKSLASH && !options.compatibility.properties.ieSuffixHack | |
| || property.hack[0] == Hack.BANG && !options.compatibility.properties.ieBangHack)) { | |
| property.unused = true; | |
| continue; | |
| } | |
| if (!options.compatibility.properties.ieFilters && isLegacyFilter(property)) { | |
| property.unused = true; | |
| continue; | |
| } | |
| if (property.block) { | |
| optimizeBody(rule, property.value[0][1], context); | |
| continue; | |
| } | |
| for (j = 0, m = property.value.length; j < m; j++) { | |
| type = property.value[j][0]; | |
| value = property.value[j][1]; | |
| if (type == Token.PROPERTY_BLOCK) { | |
| property.unused = true; | |
| context.warnings.push('Invalid value token at ' + formatPosition(value[0][1][2][0]) + '. Ignoring.'); | |
| break; | |
| } | |
| if (startsAsUrl(value) && !context.validator.isUrl(value)) { | |
| property.unused = true; | |
| context.warnings.push('Broken URL \'' + value + '\' at ' + formatPosition(property.value[j][2][0]) + '. Ignoring.'); | |
| break; | |
| } | |
| for (k = 0, n = valueOptimizers.length; k < n; k++) { | |
| value = valueOptimizers[k](name, value, options); | |
| } | |
| for (k = 0, n = pluginValueOptimizers.length; k < n; k++) { | |
| value = pluginValueOptimizers[k](name, value, options); | |
| } | |
| property.value[j][1] = value; | |
| } | |
| propertyOptimizer(serializedRule, property, options); | |
| for (j = 0, m = pluginPropertyOptimizers.length; j < m; j++) { | |
| pluginPropertyOptimizers[j](serializedRule, property, options); | |
| } | |
| } | |
| restoreFromOptimizing(_properties); | |
| removeUnused(_properties); | |
| removeComments(properties, options); | |
| } | |
| function removeComments(tokens, options) { | |
| var token; | |
| var i; | |
| for (i = 0; i < tokens.length; i++) { | |
| token = tokens[i]; | |
| if (token[0] != Token.COMMENT) { | |
| continue; | |
| } | |
| optimizeComment(token, options); | |
| if (token[1].length === 0) { | |
| tokens.splice(i, 1); | |
| i--; | |
| } | |
| } | |
| } | |
| function optimizeComment(token, options) { | |
| if (token[1][2] == Marker.EXCLAMATION && (options.level[OptimizationLevel.One].specialComments == 'all' || options.commentsKept < options.level[OptimizationLevel.One].specialComments)) { | |
| options.commentsKept++; | |
| return; | |
| } | |
| token[1] = []; | |
| } | |
| function cleanupCharsets(tokens) { | |
| var hasCharset = false; | |
| for (var i = 0, l = tokens.length; i < l; i++) { | |
| var token = tokens[i]; | |
| if (token[0] != Token.AT_RULE) { continue; } | |
| if (!CHARSET_REGEXP.test(token[1])) { continue; } | |
| if (hasCharset || token[1].indexOf(CHARSET_TOKEN) == -1) { | |
| tokens.splice(i, 1); | |
| i--; | |
| l--; | |
| } else { | |
| hasCharset = true; | |
| tokens.splice(i, 1); | |
| tokens.unshift([Token.AT_RULE, token[1].replace(CHARSET_REGEXP, CHARSET_TOKEN)]); | |
| } | |
| } | |
| } | |
| function buildUnitRegexp(options) { | |
| var units = ['px', 'em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', '%']; | |
| var otherUnits = ['ch', 'rem', 'vh', 'vm', 'vmax', 'vmin', 'vw']; | |
| otherUnits.forEach(function(unit) { | |
| if (options.compatibility.units[unit]) { | |
| units.push(unit); | |
| } | |
| }); | |
| return new RegExp('(^|\\s|\\(|,)0(?:' + units.join('|') + ')(\\W|$)', 'g'); | |
| } | |
| function buildPrecisionOptions(roundingPrecision) { | |
| var precisionOptions = { | |
| matcher: null, | |
| units: {} | |
| }; | |
| var optimizable = []; | |
| var unit; | |
| var value; | |
| for (unit in roundingPrecision) { | |
| value = roundingPrecision[unit]; | |
| if (value != DEFAULT_ROUNDING_PRECISION) { | |
| precisionOptions.units[unit] = {}; | |
| precisionOptions.units[unit].value = value; | |
| precisionOptions.units[unit].multiplier = 10 ** value; | |
| optimizable.push(unit); | |
| } | |
| } | |
| if (optimizable.length > 0) { | |
| precisionOptions.enabled = true; | |
| precisionOptions.decimalPointMatcher = new RegExp('(\\d)\\.($|' + optimizable.join('|') + ')($|\\W)', 'g'); | |
| precisionOptions.zeroMatcher = new RegExp('(\\d*)(\\.\\d+)(' + optimizable.join('|') + ')', 'g'); | |
| } | |
| return precisionOptions; | |
| } | |
| function buildVariableOptimizers(options) { | |
| return options.level[OptimizationLevel.One].variableValueOptimizers.map(function(optimizer) { | |
| if (typeof (optimizer) == 'string') { | |
| return optimizers[optimizer] || noopValueOptimizer; | |
| } | |
| return optimizer; | |
| }); | |
| } | |
| function level1Optimize(tokens, context) { | |
| var options = context.options; | |
| var levelOptions = options.level[OptimizationLevel.One]; | |
| var ie7Hack = options.compatibility.selectors.ie7Hack; | |
| var adjacentSpace = options.compatibility.selectors.adjacentSpace; | |
| var spaceAfterClosingBrace = options.compatibility.properties.spaceAfterClosingBrace; | |
| var format = options.format; | |
| var mayHaveCharset = false; | |
| var afterRules = false; | |
| options.unitsRegexp = options.unitsRegexp || buildUnitRegexp(options); | |
| options.precision = options.precision || buildPrecisionOptions(levelOptions.roundingPrecision); | |
| options.commentsKept = options.commentsKept || 0; | |
| options.variableOptimizers = options.variableOptimizers || buildVariableOptimizers(options); | |
| for (var i = 0, l = tokens.length; i < l; i++) { | |
| var token = tokens[i]; | |
| switch (token[0]) { | |
| case Token.AT_RULE: | |
| token[1] = isImport(token) && afterRules ? '' : token[1]; | |
| token[1] = levelOptions.tidyAtRules ? tidyAtRule(token[1]) : token[1]; | |
| mayHaveCharset = true; | |
| break; | |
| case Token.AT_RULE_BLOCK: | |
| optimizeBody(token[1], token[2], context); | |
| afterRules = true; | |
| break; | |
| case Token.NESTED_BLOCK: | |
| token[1] = levelOptions.tidyBlockScopes ? tidyBlock(token[1], spaceAfterClosingBrace) : token[1]; | |
| level1Optimize(token[2], context); | |
| afterRules = true; | |
| break; | |
| case Token.COMMENT: | |
| optimizeComment(token, options); | |
| break; | |
| case Token.RULE: | |
| token[1] = levelOptions.tidySelectors | |
| ? tidyRules(token[1], !ie7Hack, adjacentSpace, format, context.warnings) | |
| : token[1]; | |
| token[1] = token[1].length > 1 ? sortSelectors(token[1], levelOptions.selectorsSortingMethod) : token[1]; | |
| optimizeBody(token[1], token[2], context); | |
| afterRules = true; | |
| break; | |
| } | |
| if (token[0] == Token.COMMENT | |
| && token[1].length === 0 | |
| || levelOptions.removeEmpty | |
| && (token[1].length === 0 || (token[2] && token[2].length === 0))) { | |
| tokens.splice(i, 1); | |
| i--; | |
| l--; | |
| } | |
| } | |
| if (levelOptions.cleanupCharsets && mayHaveCharset) { | |
| cleanupCharsets(tokens); | |
| } | |
| return tokens; | |
| } | |
| module.exports = level1Optimize; | |