diff options
Diffstat (limited to 'node_modules/clean-css/lib/optimizer/level-1')
31 files changed, 1610 insertions, 0 deletions
diff --git a/node_modules/clean-css/lib/optimizer/level-1/optimize.js b/node_modules/clean-css/lib/optimizer/level-1/optimize.js new file mode 100644 index 00000000..d0461e06 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/optimize.js @@ -0,0 +1,325 @@ +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; diff --git a/node_modules/clean-css/lib/optimizer/level-1/property-optimizers.js b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers.js new file mode 100644 index 00000000..4cbc8ca2 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers.js @@ -0,0 +1,10 @@ +module.exports = { + background: require('./property-optimizers/background').level1.property, + boxShadow: require('./property-optimizers/box-shadow').level1.property, + borderRadius: require('./property-optimizers/border-radius').level1.property, + filter: require('./property-optimizers/filter').level1.property, + fontWeight: require('./property-optimizers/font-weight').level1.property, + margin: require('./property-optimizers/margin').level1.property, + outline: require('./property-optimizers/outline').level1.property, + padding: require('./property-optimizers/padding').level1.property +}; diff --git a/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/background.js b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/background.js new file mode 100644 index 00000000..831cb163 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/background.js @@ -0,0 +1,23 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var plugin = { + level1: { + property: function background(_rule, property, options) { + var values = property.value; + + if (!options.level[OptimizationLevel.One].optimizeBackground) { + return; + } + + if (values.length == 1 && values[0][1] == 'none') { + values[0][1] = '0 0'; + } + + if (values.length == 1 && values[0][1] == 'transparent') { + values[0][1] = '0 0'; + } + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/border-radius.js b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/border-radius.js new file mode 100644 index 00000000..856f0a8e --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/border-radius.js @@ -0,0 +1,29 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var plugin = { + level1: { + property: function borderRadius(_rule, property, options) { + var values = property.value; + + if (!options.level[OptimizationLevel.One].optimizeBorderRadius) { + return; + } + + if (values.length == 3 && values[1][1] == '/' && values[0][1] == values[2][1]) { + property.value.splice(1); + property.dirty = true; + } else if (values.length == 5 && values[2][1] == '/' && values[0][1] == values[3][1] && values[1][1] == values[4][1]) { + property.value.splice(2); + property.dirty = true; + } else if (values.length == 7 && values[3][1] == '/' && values[0][1] == values[4][1] && values[1][1] == values[5][1] && values[2][1] == values[6][1]) { + property.value.splice(3); + property.dirty = true; + } else if (values.length == 9 && values[4][1] == '/' && values[0][1] == values[5][1] && values[1][1] == values[6][1] && values[2][1] == values[7][1] && values[3][1] == values[8][1]) { + property.value.splice(4); + property.dirty = true; + } + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/box-shadow.js b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/box-shadow.js new file mode 100644 index 00000000..b12a0a67 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/box-shadow.js @@ -0,0 +1,15 @@ +var plugin = { + level1: { + property: function boxShadow(_rule, property) { + var values = property.value; + + // remove multiple zeros + if (values.length == 4 && values[0][1] === '0' && values[1][1] === '0' && values[2][1] === '0' && values[3][1] === '0') { + property.value.splice(2); + property.dirty = true; + } + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/filter.js b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/filter.js new file mode 100644 index 00000000..60b3848c --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/filter.js @@ -0,0 +1,34 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var ALPHA_OR_CHROMA_FILTER_PATTERN = /progid:DXImageTransform\.Microsoft\.(Alpha|Chroma)(\W)/; +var NO_SPACE_AFTER_COMMA_PATTERN = /,(\S)/g; +var WHITESPACE_AROUND_EQUALS_PATTERN = / ?= ?/g; + +var plugin = { + level1: { + property: function filter(_rule, property, options) { + if (!options.compatibility.properties.ieFilters) { + return; + } + + if (!options.level[OptimizationLevel.One].optimizeFilter) { + return; + } + + if (property.value.length == 1) { + property.value[0][1] = property.value[0][1].replace( + ALPHA_OR_CHROMA_FILTER_PATTERN, + function(match, filter, suffix) { + return filter.toLowerCase() + suffix; + } + ); + } + + property.value[0][1] = property.value[0][1] + .replace(NO_SPACE_AFTER_COMMA_PATTERN, ', $1') + .replace(WHITESPACE_AROUND_EQUALS_PATTERN, '='); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/font-weight.js b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/font-weight.js new file mode 100644 index 00000000..41b548df --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/font-weight.js @@ -0,0 +1,23 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var plugin = { + level1: { + property: function fontWeight(_rule, property, options) { + var value = property.value[0][1]; + + if (!options.level[OptimizationLevel.One].optimizeFontWeight) { + return; + } + + if (value == 'normal') { + value = '400'; + } else if (value == 'bold') { + value = '700'; + } + + property.value[0][1] = value; + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/margin.js b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/margin.js new file mode 100644 index 00000000..b35e8cad --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/margin.js @@ -0,0 +1,21 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var plugin = { + level1: { + property: function margin(_rule, property, options) { + var values = property.value; + + if (!options.level[OptimizationLevel.One].replaceMultipleZeros) { + return; + } + + // remove multiple zeros + if (values.length == 4 && values[0][1] === '0' && values[1][1] === '0' && values[2][1] === '0' && values[3][1] === '0') { + property.value.splice(1); + property.dirty = true; + } + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/outline.js b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/outline.js new file mode 100644 index 00000000..ca5c009e --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/outline.js @@ -0,0 +1,19 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var plugin = { + level1: { + property: function outline(_rule, property, options) { + var values = property.value; + + if (!options.level[OptimizationLevel.One].optimizeOutline) { + return; + } + + if (values.length == 1 && values[0][1] == 'none') { + values[0][1] = '0'; + } + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/padding.js b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/padding.js new file mode 100644 index 00000000..001258e8 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/property-optimizers/padding.js @@ -0,0 +1,32 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +function isNegative(value) { + return value && value[1][0] == '-' && parseFloat(value[1]) < 0; +} + +var plugin = { + level1: { + property: function padding(_rule, property, options) { + var values = property.value; + + // remove multiple zeros + if (values.length == 4 && values[0][1] === '0' && values[1][1] === '0' && values[2][1] === '0' && values[3][1] === '0') { + property.value.splice(1); + property.dirty = true; + } + + // remove negative paddings + if (options.level[OptimizationLevel.One].removeNegativePaddings + && ( + isNegative(property.value[0]) + || isNegative(property.value[1]) + || isNegative(property.value[2]) + || isNegative(property.value[3]) + )) { + property.unused = true; + } + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js b/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js new file mode 100644 index 00000000..e8d39c14 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js @@ -0,0 +1,23 @@ +var naturalCompare = require('../../utils/natural-compare'); + +function naturalSorter(scope1, scope2) { + return naturalCompare(scope1[1], scope2[1]); +} + +function standardSorter(scope1, scope2) { + return scope1[1] > scope2[1] ? 1 : -1; +} + +function sortSelectors(selectors, method) { + switch (method) { + case 'natural': + return selectors.sort(naturalSorter); + case 'standard': + return selectors.sort(standardSorter); + case 'none': + case false: + return selectors; + } +} + +module.exports = sortSelectors; diff --git a/node_modules/clean-css/lib/optimizer/level-1/tidy-at-rule.js b/node_modules/clean-css/lib/optimizer/level-1/tidy-at-rule.js new file mode 100644 index 00000000..a7b149fb --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/tidy-at-rule.js @@ -0,0 +1,9 @@ +function tidyAtRule(value) { + return value + .replace(/\s+/g, ' ') + .replace(/url\(\s+/g, 'url(') + .replace(/\s+\)/g, ')') + .trim(); +} + +module.exports = tidyAtRule; diff --git a/node_modules/clean-css/lib/optimizer/level-1/tidy-block.js b/node_modules/clean-css/lib/optimizer/level-1/tidy-block.js new file mode 100644 index 00000000..0cbbd99f --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/tidy-block.js @@ -0,0 +1,34 @@ +var SUPPORTED_COMPACT_BLOCK_MATCHER = /^@media\W/; +var SUPPORTED_QUOTE_REMOVAL_MATCHER = /^@(?:keyframes|-moz-keyframes|-o-keyframes|-webkit-keyframes)\W/; + +function tidyBlock(values, spaceAfterClosingBrace) { + var withoutSpaceAfterClosingBrace; + var withoutQuotes; + var i; + + for (i = values.length - 1; i >= 0; i--) { + withoutSpaceAfterClosingBrace = !spaceAfterClosingBrace && SUPPORTED_COMPACT_BLOCK_MATCHER.test(values[i][1]); + withoutQuotes = SUPPORTED_QUOTE_REMOVAL_MATCHER.test(values[i][1]); + + values[i][1] = values[i][1] + .replace(/\n|\r\n/g, ' ') + .replace(/\s+/g, ' ') + .replace(/(,|:|\() /g, '$1') + .replace(/ \)/g, ')'); + + if (withoutQuotes) { + values[i][1] = values[i][1] + .replace(/'([a-zA-Z][a-zA-Z\d\-_]+)'/, '$1') + .replace(/"([a-zA-Z][a-zA-Z\d\-_]+)"/, '$1'); + } + + if (withoutSpaceAfterClosingBrace) { + values[i][1] = values[i][1] + .replace(/\) /g, ')'); + } + } + + return values; +} + +module.exports = tidyBlock; diff --git a/node_modules/clean-css/lib/optimizer/level-1/tidy-rules.js b/node_modules/clean-css/lib/optimizer/level-1/tidy-rules.js new file mode 100644 index 00000000..bb467e29 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/tidy-rules.js @@ -0,0 +1,265 @@ +var Spaces = require('../../options/format').Spaces; +var Marker = require('../../tokenizer/marker'); +var formatPosition = require('../../utils/format-position'); + +var CASE_ATTRIBUTE_PATTERN = /[\s"'][iI]\s*\]/; +var CASE_RESTORE_PATTERN = /([\d\w])([iI])\]/g; +var DOUBLE_QUOTE_CASE_PATTERN = /="([a-zA-Z][a-zA-Z\d\-_]+)"([iI])/g; +var DOUBLE_QUOTE_PATTERN = /="([a-zA-Z][a-zA-Z\d\-_]+)"(\s|\])/g; +var HTML_COMMENT_PATTERN = /^(?:(?:<!--|-->)\s*)+/; +var SINGLE_QUOTE_CASE_PATTERN = /='([a-zA-Z][a-zA-Z\d\-_]+)'([iI])/g; +var SINGLE_QUOTE_PATTERN = /='([a-zA-Z][a-zA-Z\d\-_]+)'(\s|\])/g; +var RELATION_PATTERN = /[>+~]/; +var WHITESPACE_PATTERN = /\s/; + +var ASTERISK_PLUS_HTML_HACK = '*+html '; +var ASTERISK_FIRST_CHILD_PLUS_HTML_HACK = '*:first-child+html '; +var LESS_THAN = '<'; + +var PSEUDO_CLASSES_WITH_SELECTORS = [ + ':current', + ':future', + ':has', + ':host', + ':host-context', + ':is', + ':not', + ':past', + ':where' +]; + +function hasInvalidCharacters(value) { + var isEscaped; + var isInvalid = false; + var character; + var isQuote = false; + var i, l; + + for (i = 0, l = value.length; i < l; i++) { + character = value[i]; + + if (isEscaped) { + // continue as always + } else if (character == Marker.SINGLE_QUOTE || character == Marker.DOUBLE_QUOTE) { + isQuote = !isQuote; + } else if (!isQuote + && (character == Marker.CLOSE_CURLY_BRACKET + || character == Marker.EXCLAMATION + || character == LESS_THAN + || character == Marker.SEMICOLON) + ) { + isInvalid = true; + break; + } else if (!isQuote && i === 0 && RELATION_PATTERN.test(character)) { + isInvalid = true; + break; + } + + isEscaped = character == Marker.BACK_SLASH; + } + + return isInvalid; +} + +function removeWhitespace(value, format) { + var stripped = []; + var character; + var isNewLineNix; + var isNewLineWin; + var isEscaped; + var wasEscaped; + var isQuoted; + var isSingleQuoted; + var isDoubleQuoted; + var isAttribute; + var isRelation; + var isWhitespace; + var isSpaceAwarePseudoClass; + var roundBracketLevel = 0; + var wasComma = false; + var wasRelation = false; + var wasWhitespace = false; + var withCaseAttribute = CASE_ATTRIBUTE_PATTERN.test(value); + var spaceAroundRelation = format && format.spaces[Spaces.AroundSelectorRelation]; + var i, l; + + for (i = 0, l = value.length; i < l; i++) { + character = value[i]; + + isNewLineNix = character == Marker.NEW_LINE_NIX; + isNewLineWin = character == Marker.NEW_LINE_NIX && value[i - 1] == Marker.CARRIAGE_RETURN; + isQuoted = isSingleQuoted || isDoubleQuoted; + isRelation = !isAttribute && !isEscaped && roundBracketLevel === 0 && RELATION_PATTERN.test(character); + isWhitespace = WHITESPACE_PATTERN.test(character); + isSpaceAwarePseudoClass = roundBracketLevel == 1 && character == Marker.CLOSE_ROUND_BRACKET + ? false + : isSpaceAwarePseudoClass + || (roundBracketLevel === 0 && character == Marker.COLON && isPseudoClassWithSelectors(value, i)); + + if (wasEscaped && isQuoted && isNewLineWin) { + // swallow escaped new windows lines in comments + stripped.pop(); + stripped.pop(); + } else if (isEscaped && isQuoted && isNewLineNix) { + // swallow escaped new *nix lines in comments + stripped.pop(); + } else if (isEscaped) { + stripped.push(character); + } else if (character == Marker.OPEN_SQUARE_BRACKET && !isQuoted) { + stripped.push(character); + isAttribute = true; + } else if (character == Marker.CLOSE_SQUARE_BRACKET && !isQuoted) { + stripped.push(character); + isAttribute = false; + } else if (character == Marker.OPEN_ROUND_BRACKET && !isQuoted) { + stripped.push(character); + roundBracketLevel++; + } else if (character == Marker.CLOSE_ROUND_BRACKET && !isQuoted) { + stripped.push(character); + roundBracketLevel--; + } else if (character == Marker.SINGLE_QUOTE && !isQuoted) { + stripped.push(character); + isSingleQuoted = true; + } else if (character == Marker.DOUBLE_QUOTE && !isQuoted) { + stripped.push(character); + isDoubleQuoted = true; + } else if (character == Marker.SINGLE_QUOTE && isQuoted) { + stripped.push(character); + isSingleQuoted = false; + } else if (character == Marker.DOUBLE_QUOTE && isQuoted) { + stripped.push(character); + isDoubleQuoted = false; + } else if (isWhitespace && wasRelation && !spaceAroundRelation) { + continue; + } else if (!isWhitespace && wasRelation && spaceAroundRelation) { + stripped.push(Marker.SPACE); + stripped.push(character); + } else if (isWhitespace && !wasWhitespace && wasComma && roundBracketLevel > 0 && isSpaceAwarePseudoClass) { + // skip space + } else if (isWhitespace && !wasWhitespace && roundBracketLevel > 0 && isSpaceAwarePseudoClass) { + stripped.push(character); + } else if (isWhitespace && (isAttribute || roundBracketLevel > 0) && !isQuoted) { + // skip space + } else if (isWhitespace && wasWhitespace && !isQuoted) { + // skip extra space + } else if ((isNewLineWin || isNewLineNix) && (isAttribute || roundBracketLevel > 0) && isQuoted) { + // skip newline + } else if (isRelation && wasWhitespace && !spaceAroundRelation) { + stripped.pop(); + stripped.push(character); + } else if (isRelation && !wasWhitespace && spaceAroundRelation) { + stripped.push(Marker.SPACE); + stripped.push(character); + } else if (isWhitespace) { + stripped.push(Marker.SPACE); + } else { + stripped.push(character); + } + + wasEscaped = isEscaped; + isEscaped = character == Marker.BACK_SLASH; + wasRelation = isRelation; + wasWhitespace = isWhitespace; + wasComma = character == Marker.COMMA; + } + + return withCaseAttribute + ? stripped.join('').replace(CASE_RESTORE_PATTERN, '$1 $2]') + : stripped.join(''); +} + +function isPseudoClassWithSelectors(value, colonPosition) { + var pseudoClass = value.substring(colonPosition, value.indexOf(Marker.OPEN_ROUND_BRACKET, colonPosition)); + + return PSEUDO_CLASSES_WITH_SELECTORS.indexOf(pseudoClass) > -1; +} + +function removeQuotes(value) { + if (value.indexOf('\'') == -1 && value.indexOf('"') == -1) { + return value; + } + + return value + .replace(SINGLE_QUOTE_CASE_PATTERN, '=$1 $2') + .replace(SINGLE_QUOTE_PATTERN, '=$1$2') + .replace(DOUBLE_QUOTE_CASE_PATTERN, '=$1 $2') + .replace(DOUBLE_QUOTE_PATTERN, '=$1$2'); +} + +function replacePseudoClasses(value) { + return value + .replace('nth-child(1)', 'first-child') + .replace('nth-of-type(1)', 'first-of-type') + .replace('nth-of-type(even)', 'nth-of-type(2n)') + .replace('nth-child(even)', 'nth-child(2n)') + .replace('nth-of-type(2n+1)', 'nth-of-type(odd)') + .replace('nth-child(2n+1)', 'nth-child(odd)') + .replace('nth-last-child(1)', 'last-child') + .replace('nth-last-of-type(1)', 'last-of-type') + .replace('nth-last-of-type(even)', 'nth-last-of-type(2n)') + .replace('nth-last-child(even)', 'nth-last-child(2n)') + .replace('nth-last-of-type(2n+1)', 'nth-last-of-type(odd)') + .replace('nth-last-child(2n+1)', 'nth-last-child(odd)'); +} + +function tidyRules(rules, removeUnsupported, adjacentSpace, format, warnings) { + var list = []; + var repeated = []; + + function removeHTMLComment(rule, match) { + warnings.push('HTML comment \'' + match + '\' at ' + formatPosition(rule[2][0]) + '. Removing.'); + return ''; + } + + for (var i = 0, l = rules.length; i < l; i++) { + var rule = rules[i]; + var reduced = rule[1]; + + reduced = reduced.replace(HTML_COMMENT_PATTERN, removeHTMLComment.bind(null, rule)); + + if (hasInvalidCharacters(reduced)) { + warnings.push('Invalid selector \'' + rule[1] + '\' at ' + formatPosition(rule[2][0]) + '. Ignoring.'); + continue; + } + + reduced = removeWhitespace(reduced, format); + reduced = removeQuotes(reduced); + + if (adjacentSpace && reduced.indexOf('nav') > 0) { + reduced = reduced.replace(/\+nav(\S|$)/, '+ nav$1'); + } + + if (removeUnsupported && reduced.indexOf(ASTERISK_PLUS_HTML_HACK) > -1) { + continue; + } + + if (removeUnsupported && reduced.indexOf(ASTERISK_FIRST_CHILD_PLUS_HTML_HACK) > -1) { + continue; + } + + if (reduced.indexOf('*') > -1) { + reduced = reduced + .replace(/\*([:#.[])/g, '$1') + .replace(/^(:first-child)?\+html/, '*$1+html'); + } + + if (repeated.indexOf(reduced) > -1) { + continue; + } + + reduced = replacePseudoClasses(reduced); + + rule[1] = reduced; + repeated.push(reduced); + list.push(rule); + } + + if (list.length == 1 && list[0][1].length === 0) { + warnings.push('Empty selector \'' + list[0][1] + '\' at ' + formatPosition(list[0][2][0]) + '. Ignoring.'); + list = []; + } + + return list; +} + +module.exports = tidyRules; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers.js new file mode 100644 index 00000000..03bd0f31 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers.js @@ -0,0 +1,14 @@ +module.exports = { + color: require('./value-optimizers/color').level1.value, + degrees: require('./value-optimizers/degrees').level1.value, + fraction: require('./value-optimizers/fraction').level1.value, + precision: require('./value-optimizers/precision').level1.value, + textQuotes: require('./value-optimizers/text-quotes').level1.value, + time: require('./value-optimizers/time').level1.value, + unit: require('./value-optimizers/unit').level1.value, + urlPrefix: require('./value-optimizers/url-prefix').level1.value, + urlQuotes: require('./value-optimizers/url-quotes').level1.value, + urlWhiteSpace: require('./value-optimizers/url-whitespace').level1.value, + whiteSpace: require('./value-optimizers/whitespace').level1.value, + zero: require('./value-optimizers/zero').level1.value +}; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color.js new file mode 100644 index 00000000..58018c90 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color.js @@ -0,0 +1,89 @@ +var shortenHex = require('./color/shorten-hex'); +var shortenHsl = require('./color/shorten-hsl'); +var shortenRgb = require('./color/shorten-rgb'); + +var split = require('../../../utils/split'); + +var ANY_COLOR_FUNCTION_PATTERN = /(rgb|rgba|hsl|hsla)\(([^()]+)\)/gi; +var COLOR_PREFIX_PATTERN = /#|rgb|hsl/gi; +var HEX_LONG_PATTERN = /(^|[^='"])#([0-9a-f]{6})/gi; +var HEX_SHORT_PATTERN = /(^|[^='"])#([0-9a-f]{3})/gi; +var HEX_VALUE_PATTERN = /[0-9a-f]/i; +var HSL_PATTERN = /hsl\((-?\d+),(-?\d+)%?,(-?\d+)%?\)/gi; +var RGBA_HSLA_PATTERN = /(rgb|hsl)a?\((-?\d+),(-?\d+%?),(-?\d+%?),(0*[1-9]+[0-9]*(\.?\d*)?)\)/gi; +var RGB_PATTERN = /rgb\((-?\d+),(-?\d+),(-?\d+)\)/gi; +var TRANSPARENT_FUNCTION_PATTERN = /(?:rgba|hsla)\(0,0%?,0%?,0\)/g; + +var plugin = { + level1: { + value: function color(name, value, options) { + if (!options.compatibility.properties.colors) { + return value; + } + + if (!value.match(COLOR_PREFIX_PATTERN)) { + return shortenHex(value); + } + + value = value + .replace(RGBA_HSLA_PATTERN, function(match, colorFn, p1, p2, p3, alpha) { + return (parseInt(alpha) >= 1 ? colorFn + '(' + [p1, p2, p3].join(',') + ')' : match); + }) + .replace(RGB_PATTERN, function(match, red, green, blue) { + return shortenRgb(red, green, blue); + }) + .replace(HSL_PATTERN, function(match, hue, saturation, lightness) { + return shortenHsl(hue, saturation, lightness); + }) + .replace(HEX_LONG_PATTERN, function(match, prefix, color, at, inputValue) { + var suffix = inputValue[at + match.length]; + + if (suffix && HEX_VALUE_PATTERN.test(suffix)) { + return match; + } if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) { + return (prefix + '#' + color[0] + color[2] + color[4]).toLowerCase(); + } + return (prefix + '#' + color).toLowerCase(); + }) + .replace(HEX_SHORT_PATTERN, function(match, prefix, color) { + return prefix + '#' + color.toLowerCase(); + }) + .replace(ANY_COLOR_FUNCTION_PATTERN, function(match, colorFunction, colorDef) { + var tokens = colorDef.split(','); + var colorFnLowercase = colorFunction && colorFunction.toLowerCase(); + var applies = (colorFnLowercase == 'hsl' && tokens.length == 3) + || (colorFnLowercase == 'hsla' && tokens.length == 4) + || (colorFnLowercase == 'rgb' && tokens.length === 3 && colorDef.indexOf('%') > 0) + || (colorFnLowercase == 'rgba' && tokens.length == 4 && tokens[0].indexOf('%') > 0); + + if (!applies) { + return match; + } + + if (tokens[1].indexOf('%') == -1) { + tokens[1] += '%'; + } + + if (tokens[2].indexOf('%') == -1) { + tokens[2] += '%'; + } + + return colorFunction + '(' + tokens.join(',') + ')'; + }); + + if (options.compatibility.colors.opacity && name.indexOf('background') == -1) { + value = value.replace(TRANSPARENT_FUNCTION_PATTERN, function(match) { + if (split(value, ',').pop().indexOf('gradient(') > -1) { + return match; + } + + return 'transparent'; + }); + } + + return shortenHex(value); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color/shorten-hex.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color/shorten-hex.js new file mode 100644 index 00000000..448ffd85 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color/shorten-hex.js @@ -0,0 +1,189 @@ +var COLORS = { + aliceblue: '#f0f8ff', + antiquewhite: '#faebd7', + aqua: '#0ff', + aquamarine: '#7fffd4', + azure: '#f0ffff', + beige: '#f5f5dc', + bisque: '#ffe4c4', + black: '#000', + blanchedalmond: '#ffebcd', + blue: '#00f', + blueviolet: '#8a2be2', + brown: '#a52a2a', + burlywood: '#deb887', + cadetblue: '#5f9ea0', + chartreuse: '#7fff00', + chocolate: '#d2691e', + coral: '#ff7f50', + cornflowerblue: '#6495ed', + cornsilk: '#fff8dc', + crimson: '#dc143c', + cyan: '#0ff', + darkblue: '#00008b', + darkcyan: '#008b8b', + darkgoldenrod: '#b8860b', + darkgray: '#a9a9a9', + darkgreen: '#006400', + darkgrey: '#a9a9a9', + darkkhaki: '#bdb76b', + darkmagenta: '#8b008b', + darkolivegreen: '#556b2f', + darkorange: '#ff8c00', + darkorchid: '#9932cc', + darkred: '#8b0000', + darksalmon: '#e9967a', + darkseagreen: '#8fbc8f', + darkslateblue: '#483d8b', + darkslategray: '#2f4f4f', + darkslategrey: '#2f4f4f', + darkturquoise: '#00ced1', + darkviolet: '#9400d3', + deeppink: '#ff1493', + deepskyblue: '#00bfff', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1e90ff', + firebrick: '#b22222', + floralwhite: '#fffaf0', + forestgreen: '#228b22', + fuchsia: '#f0f', + gainsboro: '#dcdcdc', + ghostwhite: '#f8f8ff', + gold: '#ffd700', + goldenrod: '#daa520', + gray: '#808080', + green: '#008000', + greenyellow: '#adff2f', + grey: '#808080', + honeydew: '#f0fff0', + hotpink: '#ff69b4', + indianred: '#cd5c5c', + indigo: '#4b0082', + ivory: '#fffff0', + khaki: '#f0e68c', + lavender: '#e6e6fa', + lavenderblush: '#fff0f5', + lawngreen: '#7cfc00', + lemonchiffon: '#fffacd', + lightblue: '#add8e6', + lightcoral: '#f08080', + lightcyan: '#e0ffff', + lightgoldenrodyellow: '#fafad2', + lightgray: '#d3d3d3', + lightgreen: '#90ee90', + lightgrey: '#d3d3d3', + lightpink: '#ffb6c1', + lightsalmon: '#ffa07a', + lightseagreen: '#20b2aa', + lightskyblue: '#87cefa', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#b0c4de', + lightyellow: '#ffffe0', + lime: '#0f0', + limegreen: '#32cd32', + linen: '#faf0e6', + magenta: '#ff00ff', + maroon: '#800000', + mediumaquamarine: '#66cdaa', + mediumblue: '#0000cd', + mediumorchid: '#ba55d3', + mediumpurple: '#9370db', + mediumseagreen: '#3cb371', + mediumslateblue: '#7b68ee', + mediumspringgreen: '#00fa9a', + mediumturquoise: '#48d1cc', + mediumvioletred: '#c71585', + midnightblue: '#191970', + mintcream: '#f5fffa', + mistyrose: '#ffe4e1', + moccasin: '#ffe4b5', + navajowhite: '#ffdead', + navy: '#000080', + oldlace: '#fdf5e6', + olive: '#808000', + olivedrab: '#6b8e23', + orange: '#ffa500', + orangered: '#ff4500', + orchid: '#da70d6', + palegoldenrod: '#eee8aa', + palegreen: '#98fb98', + paleturquoise: '#afeeee', + palevioletred: '#db7093', + papayawhip: '#ffefd5', + peachpuff: '#ffdab9', + peru: '#cd853f', + pink: '#ffc0cb', + plum: '#dda0dd', + powderblue: '#b0e0e6', + purple: '#800080', + rebeccapurple: '#663399', + red: '#f00', + rosybrown: '#bc8f8f', + royalblue: '#4169e1', + saddlebrown: '#8b4513', + salmon: '#fa8072', + sandybrown: '#f4a460', + seagreen: '#2e8b57', + seashell: '#fff5ee', + sienna: '#a0522d', + silver: '#c0c0c0', + skyblue: '#87ceeb', + slateblue: '#6a5acd', + slategray: '#708090', + slategrey: '#708090', + snow: '#fffafa', + springgreen: '#00ff7f', + steelblue: '#4682b4', + tan: '#d2b48c', + teal: '#008080', + thistle: '#d8bfd8', + tomato: '#ff6347', + turquoise: '#40e0d0', + violet: '#ee82ee', + wheat: '#f5deb3', + white: '#fff', + whitesmoke: '#f5f5f5', + yellow: '#ff0', + yellowgreen: '#9acd32' +}; + +var toHex = {}; +var toName = {}; + +for (var name in COLORS) { + var hex = COLORS[name]; + + if (name.length < hex.length) { + toName[hex] = name; + } else { + toHex[name] = hex; + } +} + +var toHexPattern = new RegExp('(^| |,|\\))(' + Object.keys(toHex).join('|') + ')( |,|\\)|$)', 'ig'); +var toNamePattern = new RegExp('(' + Object.keys(toName).join('|') + ')([^a-f0-9]|$)', 'ig'); + +function hexConverter(match, prefix, colorValue, suffix) { + return prefix + toHex[colorValue.toLowerCase()] + suffix; +} + +function nameConverter(match, colorValue, suffix) { + return toName[colorValue.toLowerCase()] + suffix; +} + +function shortenHex(value) { + var hasHex = value.indexOf('#') > -1; + var shortened = value.replace(toHexPattern, hexConverter); + + if (shortened != value) { + shortened = shortened.replace(toHexPattern, hexConverter); + } + + return hasHex + ? shortened.replace(toNamePattern, nameConverter) + : shortened; +} + +module.exports = shortenHex; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color/shorten-hsl.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color/shorten-hsl.js new file mode 100644 index 00000000..883bf69e --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color/shorten-hsl.js @@ -0,0 +1,54 @@ +// HSL to RGB converter. Both methods adapted from: +// http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + +function hslToRgb(h, s, l) { + var r, g, b; + + // normalize hue orientation b/w 0 and 360 degrees + h %= 360; + if (h < 0) { h += 360; } + h = ~~h / 360; + + if (s < 0) { s = 0; } else if (s > 100) { s = 100; } + s = ~~s / 100; + + if (l < 0) { l = 0; } else if (l > 100) { l = 100; } + l = ~~l / 100; + + if (s === 0) { + r = g = b = l; // achromatic + } else { + var q = l < 0.5 + ? l * (1 + s) + : l + s - l * s; + var p = 2 * l - q; + r = hueToRgb(p, q, h + 1 / 3); + g = hueToRgb(p, q, h); + b = hueToRgb(p, q, h - 1 / 3); + } + + return [~~(r * 255), ~~(g * 255), ~~(b * 255)]; +} + +function hueToRgb(p, q, t) { + if (t < 0) { t += 1; } + if (t > 1) { t -= 1; } + if (t < 1 / 6) { return p + (q - p) * 6 * t; } + if (t < 1 / 2) { return q; } + if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; } + return p; +} + +function shortenHsl(hue, saturation, lightness) { + var asRgb = hslToRgb(hue, saturation, lightness); + var redAsHex = asRgb[0].toString(16); + var greenAsHex = asRgb[1].toString(16); + var blueAsHex = asRgb[2].toString(16); + + return '#' + + ((redAsHex.length == 1 ? '0' : '') + redAsHex) + + ((greenAsHex.length == 1 ? '0' : '') + greenAsHex) + + ((blueAsHex.length == 1 ? '0' : '') + blueAsHex); +} + +module.exports = shortenHsl; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color/shorten-rgb.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color/shorten-rgb.js new file mode 100644 index 00000000..3c0a5fa3 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/color/shorten-rgb.js @@ -0,0 +1,10 @@ +function shortenRgb(red, green, blue) { + var normalizedRed = Math.max(0, Math.min(parseInt(red), 255)); + var normalizedGreen = Math.max(0, Math.min(parseInt(green), 255)); + var normalizedBlue = Math.max(0, Math.min(parseInt(blue), 255)); + + // Credit: Asen http://jsbin.com/UPUmaGOc/2/edit?js,console + return '#' + ('00000' + (normalizedRed << 16 | normalizedGreen << 8 | normalizedBlue).toString(16)).slice(-6); +} + +module.exports = shortenRgb; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/degrees.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/degrees.js new file mode 100644 index 00000000..29b7672a --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/degrees.js @@ -0,0 +1,19 @@ +var ZERO_DEG_PATTERN = /\(0deg\)/g; + +var plugin = { + level1: { + value: function degrees(_name, value, options) { + if (!options.compatibility.properties.zeroUnits) { + return value; + } + + if (value.indexOf('0deg') == -1) { + return value; + } + + return value.replace(ZERO_DEG_PATTERN, '(0)'); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/fraction.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/fraction.js new file mode 100644 index 00000000..c29a04c3 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/fraction.js @@ -0,0 +1,72 @@ +var split = require('../../../utils/split'); +var startsAsUrl = require('./starts-as-url'); + +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var EXPRESSION_PATTERN = /^expression\(.*\)$/; +var ANY_FUNCTION_PATTERN = /^(-(?:moz|ms|o|webkit)-[a-z-]+|[a-z-]+)\((.+)\)$/; +var TOKEN_SEPARATOR_PATTERN = /([\s,/])/; + +var DOT_ZERO_PATTERN = /(^|\D)\.0+(\D|$)/g; +var FRACTION_PATTERN = /\.([1-9]*)0+(\D|$)/g; +var LEADING_ZERO_FRACTION_PATTERN = /(^|\D)0\.(\d)/g; +var MINUS_ZERO_FRACTION_PATTERN = /([^\w\d-]|^)-0([^.]|$)/g; +var ZERO_PREFIXED_UNIT_PATTERN = /(^|\s)0+([1-9])/g; + +function optimizeRecursively(value) { + var functionTokens; + var tokens; + + if (startsAsUrl(value)) { + return value; + } + + if (EXPRESSION_PATTERN.test(value)) { + return value; + } + + functionTokens = ANY_FUNCTION_PATTERN.exec(value); + + if (!functionTokens) { + return optimizeFractions(value); + } + + tokens = split(functionTokens[2], TOKEN_SEPARATOR_PATTERN) + .map(function(token) { return optimizeRecursively(token); }); + + return functionTokens[1] + '(' + tokens.join('') + ')'; +} + +function optimizeFractions(value) { + if (value.indexOf('0') == -1) { + return value; + } + + if (value.indexOf('-') > -1) { + value = value + .replace(MINUS_ZERO_FRACTION_PATTERN, '$10$2') + .replace(MINUS_ZERO_FRACTION_PATTERN, '$10$2'); + } + + return value + .replace(ZERO_PREFIXED_UNIT_PATTERN, '$1$2') + .replace(DOT_ZERO_PATTERN, '$10$2') + .replace(FRACTION_PATTERN, function(match, nonZeroPart, suffix) { + return (nonZeroPart.length > 0 ? '.' : '') + nonZeroPart + suffix; + }) + .replace(LEADING_ZERO_FRACTION_PATTERN, '$1.$2'); +} + +var plugin = { + level1: { + value: function fraction(name, value, options) { + if (!options.level[OptimizationLevel.One].replaceZeroUnits) { + return value; + } + + return optimizeRecursively(value); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/precision.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/precision.js new file mode 100644 index 00000000..6cbc359b --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/precision.js @@ -0,0 +1,22 @@ +var plugin = { + level1: { + value: function precision(_name, value, options) { + if (!options.precision.enabled || value.indexOf('.') === -1) { + return value; + } + + return value + .replace(options.precision.decimalPointMatcher, '$1$2$3') + .replace(options.precision.zeroMatcher, function(match, integerPart, fractionPart, unit) { + var multiplier = options.precision.units[unit].multiplier; + var parsedInteger = parseInt(integerPart); + var integer = Number.isNaN(parsedInteger) ? 0 : parsedInteger; + var fraction = parseFloat(fractionPart); + + return Math.round((integer + fraction) * multiplier) / multiplier + unit; + }); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/starts-as-url.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/starts-as-url.js new file mode 100644 index 00000000..7b7f1477 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/starts-as-url.js @@ -0,0 +1,7 @@ +var URL_PREFIX_PATTERN = /^url\(/i; + +function startsAsUrl(value) { + return URL_PREFIX_PATTERN.test(value); +} + +module.exports = startsAsUrl; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/text-quotes.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/text-quotes.js new file mode 100644 index 00000000..09923428 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/text-quotes.js @@ -0,0 +1,31 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var LOCAL_PREFIX_PATTERN = /^local\(/i; +var QUOTED_PATTERN = /^('.*'|".*")$/; +var QUOTED_BUT_SAFE_PATTERN = /^['"][a-zA-Z][a-zA-Z\d\-_]+['"]$/; +// eslint-disable-next-line max-len +var GENERIC_FONT_FAMILY_PATTERN = /^['"](?:cursive|default|emoji|fangsong|fantasy|inherit|initial|math|monospace|revert|revert-layer|sans-serif|serif|system-ui|ui-monospace|ui-rounded|ui-sans-serif|ui-serif|unset)['"]$/; + +var plugin = { + level1: { + value: function textQuotes(name, value, options) { + if ((name == 'font-family' || name == 'font') && GENERIC_FONT_FAMILY_PATTERN.test(value)) { + return value; + } + + if (!options.level[OptimizationLevel.One].removeQuotes) { + return value; + } + + if (!QUOTED_PATTERN.test(value) && !LOCAL_PREFIX_PATTERN.test(value)) { + return value; + } + + return QUOTED_BUT_SAFE_PATTERN.test(value) + ? value.substring(1, value.length - 1) + : value; + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/time.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/time.js new file mode 100644 index 00000000..8fb2021c --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/time.js @@ -0,0 +1,31 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var TIME_VALUE = /^(-?[\d.]+)(m?s)$/; + +var plugin = { + level1: { + value: function time(name, value, options) { + if (!options.level[OptimizationLevel.One].replaceTimeUnits) { + return value; + } + + if (!TIME_VALUE.test(value)) { + return value; + } + + return value.replace(TIME_VALUE, function(match, val, unit) { + var newValue; + + if (unit == 'ms') { + newValue = parseInt(val) / 1000 + 's'; + } else if (unit == 's') { + newValue = parseFloat(val) * 1000 + 'ms'; + } + + return newValue.length < match.length ? newValue : match; + }); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/unit.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/unit.js new file mode 100644 index 00000000..df2cda59 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/unit.js @@ -0,0 +1,46 @@ +var WHOLE_PIXEL_VALUE = /(?:^|\s|\()(-?\d+)px/; + +var plugin = { + level1: { + value: function unit(_name, value, options) { + if (!WHOLE_PIXEL_VALUE.test(value)) { + return value; + } + + return value.replace(WHOLE_PIXEL_VALUE, function(match, val) { + var newValue; + var intVal = parseInt(val); + + if (intVal === 0) { + return match; + } + + if (options.compatibility.properties.shorterLengthUnits + && options.compatibility.units.pt + && intVal * 3 % 4 === 0) { + newValue = intVal * 3 / 4 + 'pt'; + } + + if (options.compatibility.properties.shorterLengthUnits + && options.compatibility.units.pc + && intVal % 16 === 0) { + newValue = intVal / 16 + 'pc'; + } + + if (options.compatibility.properties.shorterLengthUnits + && options.compatibility.units.in + && intVal % 96 === 0) { + newValue = intVal / 96 + 'in'; + } + + if (newValue) { + newValue = match.substring(0, match.indexOf(val)) + newValue; + } + + return newValue && newValue.length < match.length ? newValue : match; + }); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/url-prefix.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/url-prefix.js new file mode 100644 index 00000000..8ae7e508 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/url-prefix.js @@ -0,0 +1,23 @@ +var startsAsUrl = require('./starts-as-url'); + +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var URL_PREFIX_PATTERN = /^url\(/i; + +var plugin = { + level1: { + value: function urlPrefix(_name, value, options) { + if (!options.level[OptimizationLevel.One].normalizeUrls) { + return value; + } + + if (!startsAsUrl(value)) { + return value; + } + + return value.replace(URL_PREFIX_PATTERN, 'url('); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/url-quotes.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/url-quotes.js new file mode 100644 index 00000000..022b80b1 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/url-quotes.js @@ -0,0 +1,22 @@ +var QUOTED_URL_PATTERN = /^url\(['"].+['"]\)$/; +var QUOTED_URL_WITH_WHITESPACE_PATTERN = /^url\(['"].*[*\s()'"].*['"]\)$/; +var QUOTES_PATTERN = /["']/g; +var URL_DATA_PATTERN = /^url\(['"]data:[^;]+;charset/; + +var plugin = { + level1: { + value: function urlQuotes(_name, value, options) { + if (options.compatibility.properties.urlQuotes) { + return value; + } + + return QUOTED_URL_PATTERN.test(value) + && !QUOTED_URL_WITH_WHITESPACE_PATTERN.test(value) + && !URL_DATA_PATTERN.test(value) + ? value.replace(QUOTES_PATTERN, '') + : value; + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/url-whitespace.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/url-whitespace.js new file mode 100644 index 00000000..bd1aaff9 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/url-whitespace.js @@ -0,0 +1,22 @@ +var startsAsUrl = require('./starts-as-url'); + +var WHITESPACE_PATTERN = /\\?\n|\\?\r\n/g; +var WHITESPACE_PREFIX_PATTERN = /(\()\s+/g; +var WHITESPACE_SUFFIX_PATTERN = /\s+(\))/g; + +var plugin = { + level1: { + value: function urlWhitespace(_name, value) { + if (!startsAsUrl(value)) { + return value; + } + + return value + .replace(WHITESPACE_PATTERN, '') + .replace(WHITESPACE_PREFIX_PATTERN, '$1') + .replace(WHITESPACE_SUFFIX_PATTERN, '$1'); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/whitespace.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/whitespace.js new file mode 100644 index 00000000..bf776301 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/whitespace.js @@ -0,0 +1,48 @@ +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +var Marker = require('../../../tokenizer/marker'); + +var CALC_DIVISION_WHITESPACE_PATTERN = /\) ?\/ ?/g; +var COMMA_AND_SPACE_PATTERN = /, /g; +var LINE_BREAK_PATTERN = /\r?\n/g; +var MULTI_WHITESPACE_PATTERN = /\s+/g; +var FUNCTION_CLOSING_BRACE_WHITESPACE_PATTERN = /\s+(;?\))/g; +var FUNCTION_OPENING_BRACE_WHITESPACE_PATTERN = /(\(;?)\s+/g; +var VARIABLE_NAME_PATTERN = /^--\S+$/; +var VARIABLE_VALUE_PATTERN = /^var\(\s*--\S+\s*\)$/; + +var plugin = { + level1: { + value: function whitespace(name, value, options) { + if (!options.level[OptimizationLevel.One].removeWhitespace) { + return value; + } + + if (VARIABLE_NAME_PATTERN.test(name) && !VARIABLE_VALUE_PATTERN.test(value)) { + return value; + } + + if ((value.indexOf(' ') == -1 && value.indexOf('\n') == -1) || value.indexOf('expression') === 0) { + return value; + } + + if (value.indexOf(Marker.SINGLE_QUOTE) > -1 || value.indexOf(Marker.DOUBLE_QUOTE) > -1) { + return value; + } + + value = value.replace(LINE_BREAK_PATTERN, ''); + value = value.replace(MULTI_WHITESPACE_PATTERN, ' '); + + if (value.indexOf('calc') > -1) { + value = value.replace(CALC_DIVISION_WHITESPACE_PATTERN, ')/ '); + } + + return value + .replace(FUNCTION_OPENING_BRACE_WHITESPACE_PATTERN, '$1') + .replace(FUNCTION_CLOSING_BRACE_WHITESPACE_PATTERN, '$1') + .replace(COMMA_AND_SPACE_PATTERN, ','); + } + } +}; + +module.exports = plugin; diff --git a/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/zero.js b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/zero.js new file mode 100644 index 00000000..0e60346a --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/value-optimizers/zero.js @@ -0,0 +1,49 @@ +var split = require('../../../utils/split'); + +var ANY_FUNCTION_PATTERN = /^(-(?:moz|ms|o|webkit)-[a-z-]+|[a-z-]+)\((.+)\)$/; +var SKIP_FUNCTION_PATTERN = /^(?:-moz-calc|-webkit-calc|calc|rgb|hsl|rgba|hsla|min|max|clamp|expression)\(/; +var TOKEN_SEPARATOR_PATTERN = /([\s,/])/; + +function removeRecursively(value, options) { + var functionTokens; + var tokens; + + if (SKIP_FUNCTION_PATTERN.test(value)) { + return value; + } + + functionTokens = ANY_FUNCTION_PATTERN.exec(value); + + if (!functionTokens) { + return removeZeros(value, options); + } + + tokens = split(functionTokens[2], TOKEN_SEPARATOR_PATTERN) + .map(function(token) { return removeRecursively(token, options); }); + + return functionTokens[1] + '(' + tokens.join('') + ')'; +} + +function removeZeros(value, options) { + return value + .replace(options.unitsRegexp, '$10$2') + .replace(options.unitsRegexp, '$10$2'); +} + +var plugin = { + level1: { + value: function zero(name, value, options) { + if (!options.compatibility.properties.zeroUnits) { + return value; + } + + if (value.indexOf('%') > 0 && (name == 'height' || name == 'max-height' || name == 'width' || name == 'max-width')) { + return value; + } + + return removeRecursively(value, options); + } + } +}; + +module.exports = plugin; |
