diff options
| author | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 | 
|---|---|---|
| committer | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 | 
| commit | de98e0b232509d5f40c135d540a70e415272ff85 (patch) | |
| tree | a79222a5b58484ab3b80d18efcaaa7ccc4769b33 /node_modules/clean-css/lib/optimizer | |
| parent | e0c9d480a73fa629c1e4a47d3e721f1d2d345406 (diff) | |
node_modules
Diffstat (limited to 'node_modules/clean-css/lib/optimizer')
49 files changed, 6127 insertions, 0 deletions
| diff --git a/node_modules/clean-css/lib/optimizer/hack.js b/node_modules/clean-css/lib/optimizer/hack.js new file mode 100644 index 000000000..812b5d5ec --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/hack.js @@ -0,0 +1,8 @@ +var Hack = { +  ASTERISK: 'asterisk', +  BANG: 'bang', +  BACKSLASH: 'backslash', +  UNDERSCORE: 'underscore' +}; + +module.exports = Hack; diff --git a/node_modules/clean-css/lib/optimizer/level-0/optimize.js b/node_modules/clean-css/lib/optimizer/level-0/optimize.js new file mode 100644 index 000000000..2a56f89c6 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-0/optimize.js @@ -0,0 +1,6 @@ +function level0Optimize(tokens) { +  // noop as level 0 means no optimizations! +  return tokens; +} + +module.exports = level0Optimize; 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 000000000..e7a9bbb47 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/optimize.js @@ -0,0 +1,734 @@ +var shortenHex = require('./shorten-hex'); +var shortenHsl = require('./shorten-hsl'); +var shortenRgb = require('./shorten-rgb'); +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 OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var Token = require('../../tokenizer/token'); +var Marker = require('../../tokenizer/marker'); + +var formatPosition = require('../../utils/format-position'); +var split = require('../../utils/split'); + +var IgnoreProperty = 'ignore-property'; + +var CHARSET_TOKEN = '@charset'; +var CHARSET_REGEXP = new RegExp('^' + CHARSET_TOKEN, 'i'); + +var DEFAULT_ROUNDING_PRECISION = require('../../options/rounding-precision').DEFAULT; + +var FONT_NUMERAL_WEIGHTS = ['100', '200', '300', '400', '500', '600', '700', '800', '900']; +var FONT_NAME_WEIGHTS = ['normal', 'bold', 'bolder', 'lighter']; +var FONT_NAME_WEIGHTS_WITHOUT_NORMAL = ['bold', 'bolder', 'lighter']; + +var WHOLE_PIXEL_VALUE = /(?:^|\s|\()(-?\d+)px/; +var TIME_VALUE = /^(\-?[\d\.]+)(m?s)$/; + +var PROPERTY_NAME_PATTERN = /^(?:\-chrome\-|\-[\w\-]+\w|\w[\w\-]+\w|\-\-\S+)$/; +var IMPORT_PREFIX_PATTERN = /^@import/i; +var QUOTED_PATTERN = /^('.*'|".*")$/; +var QUOTED_BUT_SAFE_PATTERN = /^['"][a-zA-Z][a-zA-Z\d\-_]+['"]$/; +var URL_PREFIX_PATTERN = /^url\(/i; +var VARIABLE_NAME_PATTERN = /^--\S+$/; + +function isNegative(value) { +  return value && value[1][0] == '-' && parseFloat(value[1]) < 0; +} + +function isQuoted(value) { +  return QUOTED_PATTERN.test(value); +} + +function isUrl(value) { +  return URL_PREFIX_PATTERN.test(value); +} + +function normalizeUrl(value) { +  return value +    .replace(URL_PREFIX_PATTERN, 'url(') +    .replace(/\\?\n|\\?\r\n/g, ''); +} + +function optimizeBackground(property) { +  var values = property.value; + +  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'; +  } +} + +function optimizeBorderRadius(property) { +  var values = property.value; +  var spliceAt; + +  if (values.length == 3 && values[1][1] == '/' && values[0][1] == values[2][1]) { +    spliceAt = 1; +  } else if (values.length == 5 && values[2][1] == '/' && values[0][1] == values[3][1] && values[1][1] == values[4][1]) { +    spliceAt = 2; +  } 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]) { +    spliceAt = 3; +  } 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]) { +    spliceAt = 4; +  } + +  if (spliceAt) { +    property.value.splice(spliceAt); +    property.dirty = true; +  } +} + +function optimizeColors(name, value, compatibility) { +  if (value.indexOf('#') === -1 && value.indexOf('rgb') == -1 && value.indexOf('hsl') == -1) { +    return shortenHex(value); +  } + +  value = value +    .replace(/rgb\((\-?\d+),(\-?\d+),(\-?\d+)\)/g, function (match, red, green, blue) { +      return shortenRgb(red, green, blue); +    }) +    .replace(/hsl\((-?\d+),(-?\d+)%?,(-?\d+)%?\)/g, function (match, hue, saturation, lightness) { +      return shortenHsl(hue, saturation, lightness); +    }) +    .replace(/(^|[^='"])#([0-9a-f]{6})/gi, function (match, prefix, color) { +      if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) { +        return (prefix + '#' + color[0] + color[2] + color[4]).toLowerCase(); +      } else { +        return (prefix + '#' + color).toLowerCase(); +      } +    }) +    .replace(/(^|[^='"])#([0-9a-f]{3})/gi, function (match, prefix, color) { +      return prefix + '#' + color.toLowerCase(); +    }) +    .replace(/(rgb|rgba|hsl|hsla)\(([^\)]+)\)/g, function (match, colorFunction, colorDef) { +      var tokens = colorDef.split(','); +      var applies = (colorFunction == 'hsl' && tokens.length == 3) || +        (colorFunction == 'hsla' && tokens.length == 4) || +        (colorFunction == 'rgb' && tokens.length == 3 && colorDef.indexOf('%') > 0) || +        (colorFunction == 'rgba' && tokens.length == 4 && colorDef.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 (compatibility.colors.opacity && name.indexOf('background') == -1) { +    value = value.replace(/(?:rgba|hsla)\(0,0%?,0%?,0\)/g, function (match) { +      if (split(value, ',').pop().indexOf('gradient(') > -1) { +        return match; +      } + +      return 'transparent'; +    }); +  } + +  return shortenHex(value); +} + +function optimizeFilter(property) { +  if (property.value.length == 1) { +    property.value[0][1] = property.value[0][1].replace(/progid:DXImageTransform\.Microsoft\.(Alpha|Chroma)(\W)/, function (match, filter, suffix) { +      return filter.toLowerCase() + suffix; +    }); +  } + +  property.value[0][1] = property.value[0][1] +    .replace(/,(\S)/g, ', $1') +    .replace(/ ?= ?/g, '='); +} + +function optimizeFont(property, options) { +  var values = property.value; +  var hasNumeral = FONT_NUMERAL_WEIGHTS.indexOf(values[0][1]) > -1 || +    values[1] && FONT_NUMERAL_WEIGHTS.indexOf(values[1][1]) > -1 || +    values[2] && FONT_NUMERAL_WEIGHTS.indexOf(values[2][1]) > -1; +  var canOptimizeFontWeight = options.level[OptimizationLevel.One].optimizeFontWeight; +  var normalCount = 0; +  var toOptimize; + +  if (!canOptimizeFontWeight) { +    return; +  } + +  if (hasNumeral) { +    return; +  } + +  if (values[1] && values[1][1] == '/') { +    return; +  } + +  if (values[0][1] == 'normal') { +    normalCount++; +  } + +  if (values[1] && values[1][1] == 'normal') { +    normalCount++; +  } + +  if (values[2] && values[2][1] == 'normal') { +    normalCount++; +  } + +  if (normalCount > 1) { +    return; +  } + +  if (FONT_NAME_WEIGHTS_WITHOUT_NORMAL.indexOf(values[0][1]) > -1) { +    toOptimize = 0; +  } else if (values[1] && FONT_NAME_WEIGHTS_WITHOUT_NORMAL.indexOf(values[1][1]) > -1) { +    toOptimize = 1; +  } else if (values[2] && FONT_NAME_WEIGHTS_WITHOUT_NORMAL.indexOf(values[2][1]) > -1) { +    toOptimize = 2; +  } else if (FONT_NAME_WEIGHTS.indexOf(values[0][1]) > -1) { +    toOptimize = 0; +  } else if (values[1] && FONT_NAME_WEIGHTS.indexOf(values[1][1]) > -1) { +    toOptimize = 1; +  } else if (values[2] && FONT_NAME_WEIGHTS.indexOf(values[2][1]) > -1) { +    toOptimize = 2; +  } + +  if (toOptimize !== undefined && canOptimizeFontWeight) { +    optimizeFontWeight(property, toOptimize); +    property.dirty = true; +  } +} + +function optimizeFontWeight(property, atIndex) { +  var value = property.value[atIndex][1]; + +  if (value == 'normal') { +    value = '400'; +  } else if (value == 'bold') { +    value = '700'; +  } + +  property.value[atIndex][1] = value; +} + +function optimizeMultipleZeros(property) { +  var values = property.value; +  var spliceAt; + +  if (values.length == 4 && values[0][1] === '0' && values[1][1] === '0' && values[2][1] === '0' && values[3][1] === '0') { +    if (property.name.indexOf('box-shadow') > -1) { +      spliceAt = 2; +    } else { +      spliceAt = 1; +    } +  } + +  if (spliceAt) { +    property.value.splice(spliceAt); +    property.dirty = true; +  } +} + +function optimizeOutline(property) { +  var values = property.value; + +  if (values.length == 1 && values[0][1] == 'none') { +    values[0][1] = '0'; +  } +} + +function optimizePixelLengths(_, value, compatibility) { +  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 (compatibility.properties.shorterLengthUnits && compatibility.units.pt && intVal * 3 % 4 === 0) { +      newValue = intVal * 3 / 4 + 'pt'; +    } + +    if (compatibility.properties.shorterLengthUnits && compatibility.units.pc && intVal % 16 === 0) { +      newValue = intVal / 16 + 'pc'; +    } + +    if (compatibility.properties.shorterLengthUnits && 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; +  }); +} + +function optimizePrecision(_, value, precisionOptions) { +  if (!precisionOptions.enabled || value.indexOf('.') === -1) { +    return value; +  } + +  return value +    .replace(precisionOptions.decimalPointMatcher, '$1$2$3') +    .replace(precisionOptions.zeroMatcher, function (match, integerPart, fractionPart, unit) { +      var multiplier = precisionOptions.units[unit].multiplier; +      var parsedInteger = parseInt(integerPart); +      var integer = isNaN(parsedInteger) ? 0 : parsedInteger; +      var fraction = parseFloat(fractionPart); + +      return Math.round((integer + fraction) * multiplier) / multiplier + unit; +    }); +} + +function optimizeTimeUnits(_, 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; +  }); +} + +function optimizeUnits(name, value, unitsRegexp) { +  if (/^(?:\-moz\-calc|\-webkit\-calc|calc|rgb|hsl|rgba|hsla)\(/.test(value)) { +    return value; +  } + +  if (name == 'flex' || name == '-ms-flex' || name == '-webkit-flex' || name == 'flex-basis' || name == '-webkit-flex-basis') { +    return value; +  } + +  if (value.indexOf('%') > 0 && (name == 'height' || name == 'max-height')) { +    return value; +  } + +  return value +    .replace(unitsRegexp, '$1' + '0' + '$2') +    .replace(unitsRegexp, '$1' + '0' + '$2'); +} + +function optimizeWhitespace(name, value) { +  if (name.indexOf('filter') > -1 || value.indexOf(' ') == -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(/\s+/g, ' '); + +  if (value.indexOf('calc') > -1) { +    value = value.replace(/\) ?\/ ?/g, ')/ '); +  } + +  return value +    .replace(/(\(;?)\s+/g, '$1') +    .replace(/\s+(;?\))/g, '$1') +    .replace(/, /g, ','); +} + +function optimizeZeroDegUnit(_, value) { +  if (value.indexOf('0deg') == -1) { +    return value; +  } + +  return value.replace(/\(0deg\)/g, '(0)'); +} + +function optimizeZeroUnits(name, value) { +  if (value.indexOf('0') == -1) { +    return value; +  } + +  if (value.indexOf('-') > -1) { +    value = value +      .replace(/([^\w\d\-]|^)\-0([^\.]|$)/g, '$10$2') +      .replace(/([^\w\d\-]|^)\-0([^\.]|$)/g, '$10$2'); +  } + +  return value +    .replace(/(^|\s)0+([1-9])/g, '$1$2') +    .replace(/(^|\D)\.0+(\D|$)/g, '$10$2') +    .replace(/(^|\D)\.0+(\D|$)/g, '$10$2') +    .replace(/\.([1-9]*)0+(\D|$)/g, function (match, nonZeroPart, suffix) { +      return (nonZeroPart.length > 0 ? '.' : '') + nonZeroPart + suffix; +    }) +    .replace(/(^|\D)0\.(\d)/g, '$1.$2'); +} + +function removeQuotes(name, value) { +  if (name == 'content' || name.indexOf('font-feature-settings') > -1 || name.indexOf('grid-') > -1) { +    return value; +  } + +  return QUOTED_BUT_SAFE_PATTERN.test(value) ? +    value.substring(1, value.length - 1) : +    value; +} + +function removeUrlQuotes(value) { +  return /^url\(['"].+['"]\)$/.test(value) && !/^url\(['"].*[\*\s\(\)'"].*['"]\)$/.test(value) && !/^url\(['"]data:[^;]+;charset/.test(value) ? +    value.replace(/["']/g, '') : +    value; +} + +function transformValue(propertyName, propertyValue, transformCallback) { +  var transformedValue = transformCallback(propertyName, propertyValue); + +  if (transformedValue === undefined) { +    return propertyValue; +  } else if (transformedValue === false) { +    return IgnoreProperty; +  } else { +    return transformedValue; +  } +} + +// + +function optimizeBody(properties, context) { +  var options = context.options; +  var levelOptions = options.level[OptimizationLevel.One]; +  var property, name, type, value; +  var valueIsUrl; +  var propertyToken; +  var _properties = wrapForOptimizing(properties, true); + +  propertyLoop: +  for (var i = 0, l = _properties.length; i < l; i++) { +    property = _properties[i]; +    name = property.name; + +    if (!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; +    } + +    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; +    } + +    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; +    } + +    if (levelOptions.removeNegativePaddings && name.indexOf('padding') === 0 && (isNegative(property.value[0]) || isNegative(property.value[1]) || isNegative(property.value[2]) || isNegative(property.value[3]))) { +      property.unused = true; +    } + +    if (!options.compatibility.properties.ieFilters && isLegacyFilter(property)) { +      property.unused = true; +    } + +    if (property.unused) { +      continue; +    } + +    if (property.block) { +      optimizeBody(property.value[0][1], context); +      continue; +    } + +    if (VARIABLE_NAME_PATTERN.test(name)) { +      continue; +    } + +    for (var j = 0, m = property.value.length; j < m; j++) { +      type = property.value[j][0]; +      value = property.value[j][1]; +      valueIsUrl = isUrl(value); + +      if (type == Token.PROPERTY_BLOCK) { +        property.unused = true; +        context.warnings.push('Invalid value token at ' + formatPosition(value[0][1][2][0]) + '. Ignoring.'); +        break; +      } + +      if (valueIsUrl && !context.validator.isValidUrl(value)) { +        property.unused = true; +        context.warnings.push('Broken URL \'' + value + '\' at ' + formatPosition(property.value[j][2][0]) + '. Ignoring.'); +        break; +      } + +      if (valueIsUrl) { +        value = levelOptions.normalizeUrls ? +          normalizeUrl(value) : +          value; +        value = !options.compatibility.properties.urlQuotes ? +          removeUrlQuotes(value) : +          value; +      } else if (isQuoted(value)) { +        value = levelOptions.removeQuotes ? +          removeQuotes(name, value) : +          value; +      } else { +        value = levelOptions.removeWhitespace ? +          optimizeWhitespace(name, value) : +          value; +        value = optimizePrecision(name, value, options.precision); +        value = optimizePixelLengths(name, value, options.compatibility); +        value = levelOptions.replaceTimeUnits ? +          optimizeTimeUnits(name, value) : +          value; +        value = levelOptions.replaceZeroUnits ? +          optimizeZeroUnits(name, value) : +          value; + +        if (options.compatibility.properties.zeroUnits) { +          value = optimizeZeroDegUnit(name, value); +          value = optimizeUnits(name, value, options.unitsRegexp); +        } + +        if (options.compatibility.properties.colors) { +          value = optimizeColors(name, value, options.compatibility); +        } +      } + +      value = transformValue(name, value, levelOptions.transform); + +      if (value === IgnoreProperty) { +        property.unused = true; +        continue propertyLoop; +      } + +      property.value[j][1] = value; +    } + +    if (levelOptions.replaceMultipleZeros) { +      optimizeMultipleZeros(property); +    } + +    if (name == 'background' && levelOptions.optimizeBackground) { +      optimizeBackground(property); +    } else if (name.indexOf('border') === 0 && name.indexOf('radius') > 0 && levelOptions.optimizeBorderRadius) { +      optimizeBorderRadius(property); +    } else if (name == 'filter'&& levelOptions.optimizeFilter && options.compatibility.properties.ieFilters) { +      optimizeFilter(property); +    } else if (name == 'font' && levelOptions.optimizeFont) { +      optimizeFont(property, options); +    } else if (name == 'font-weight' && levelOptions.optimizeFontWeight) { +      optimizeFontWeight(property, 0); +    } else if (name == 'outline' && levelOptions.optimizeOutline) { +      optimizeOutline(property); +    } +  } + +  restoreFromOptimizing(_properties); +  removeUnused(_properties); + +  if (_properties.length != properties.length) { +    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 = Math.pow(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 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; +  } else { +    return false; +  } +} + +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; + +  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[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[2], context); +        afterRules = true; +        break; +    } + +    if (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/shorten-hex.js b/node_modules/clean-css/lib/optimizer/level-1/shorten-hex.js new file mode 100644 index 000000000..3deea381c --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/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/shorten-hsl.js b/node_modules/clean-css/lib/optimizer/level-1/shorten-hsl.js new file mode 100644 index 000000000..fe98dfd39 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/shorten-hsl.js @@ -0,0 +1,61 @@ +// 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 = 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/shorten-rgb.js b/node_modules/clean-css/lib/optimizer/level-1/shorten-rgb.js new file mode 100644 index 000000000..3c0a5fa31 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/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/sort-selectors.js b/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js new file mode 100644 index 000000000..2f179e6ef --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/sort-selectors.js @@ -0,0 +1,25 @@ +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) { +  var sorter; + +  switch (method) { +    case 'natural': +      sorter = naturalSorter; +      break; +    case 'standard': +      sorter = standardSorter; +  } + +  return selectors.sort(sorter); +} + +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 000000000..a7b149fb5 --- /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 000000000..8322aeca7 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/tidy-block.js @@ -0,0 +1,23 @@ +var SUPPORTED_COMPACT_BLOCK_MATCHER = /^@media\W/; + +function tidyBlock(values, spaceAfterClosingBrace) { +  var withoutSpaceAfterClosingBrace; +  var i; + +  for (i = values.length - 1; i >= 0; i--) { +    withoutSpaceAfterClosingBrace = !spaceAfterClosingBrace && SUPPORTED_COMPACT_BLOCK_MATCHER.test(values[i][1]); + +    values[i][1] = values[i][1] +      .replace(/\n|\r\n/g, ' ') +      .replace(/\s+/g, ' ') +      .replace(/(,|:|\() /g, '$1') +      .replace(/ \)/g, ')') +      .replace(/'([a-zA-Z][a-zA-Z\d\-_]+)'/, '$1') +      .replace(/"([a-zA-Z][a-zA-Z\d\-_]+)"/, '$1') +      .replace(withoutSpaceAfterClosingBrace ? /\) /g : null, ')'); +  } + +  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 000000000..b47ed6b79 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-1/tidy-rules.js @@ -0,0 +1,213 @@ +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 = '<'; + +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 roundBracketLevel = 0; +  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.NEW_LINE_WIN; +    isQuoted = isSingleQuoted || isDoubleQuoted; +    isRelation = !isEscaped && roundBracketLevel === 0 && RELATION_PATTERN.test(character); +    isWhitespace = WHITESPACE_PATTERN.test(character); + +    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 && (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; +  } + +  return withCaseAttribute ? +    stripped.join('').replace(CASE_RESTORE_PATTERN, '$1 $2]') : +    stripped.join(''); +} + +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 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; +    } + +    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-2/break-up.js b/node_modules/clean-css/lib/optimizer/level-2/break-up.js new file mode 100644 index 000000000..9e44d5a1f --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/break-up.js @@ -0,0 +1,362 @@ +var InvalidPropertyError = require('./invalid-property-error'); + +var wrapSingle = require('../wrap-for-optimizing').single; + +var Token = require('../../tokenizer/token'); + +var formatPosition = require('../../utils/format-position'); + +var MULTIPLEX_SEPARATOR = ','; + +function _colorFilter(validator) { +  return function (value) { +    return value[1] == 'invert' || validator.isValidColor(value[1]) || validator.isValidVendorPrefixedValue(value[1]); +  }; +} + +function _styleFilter(validator) { +  return function (value) { +    return value[1] != 'inherit' && validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); +  }; +} + +function _wrapDefault(name, property, compactable) { +  var descriptor = compactable[name]; +  if (descriptor.doubleValues && descriptor.defaultValue.length == 2) { +    return wrapSingle([ +      Token.PROPERTY, +      [Token.PROPERTY_NAME, name], +      [Token.PROPERTY_VALUE, descriptor.defaultValue[0]], +      [Token.PROPERTY_VALUE, descriptor.defaultValue[1]] +    ]); +  } else if (descriptor.doubleValues && descriptor.defaultValue.length == 1) { +    return wrapSingle([ +      Token.PROPERTY, +      [Token.PROPERTY_NAME, name], +      [Token.PROPERTY_VALUE, descriptor.defaultValue[0]] +    ]); +  } else { +    return wrapSingle([ +      Token.PROPERTY, +      [Token.PROPERTY_NAME, name], +      [Token.PROPERTY_VALUE, descriptor.defaultValue] +    ]); +  } +} + +function _widthFilter(validator) { +  return function (value) { +    return value[1] != 'inherit' && validator.isValidWidth(value[1]) && !validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); +  }; +} + +function background(property, compactable, validator) { +  var image = _wrapDefault('background-image', property, compactable); +  var position = _wrapDefault('background-position', property, compactable); +  var size = _wrapDefault('background-size', property, compactable); +  var repeat = _wrapDefault('background-repeat', property, compactable); +  var attachment = _wrapDefault('background-attachment', property, compactable); +  var origin = _wrapDefault('background-origin', property, compactable); +  var clip = _wrapDefault('background-clip', property, compactable); +  var color = _wrapDefault('background-color', property, compactable); +  var components = [image, position, size, repeat, attachment, origin, clip, color]; +  var values = property.value; + +  var positionSet = false; +  var clipSet = false; +  var originSet = false; +  var repeatSet = false; + +  var anyValueSet = false; + +  if (property.value.length == 1 && property.value[0][1] == 'inherit') { +    // NOTE: 'inherit' is not a valid value for background-attachment +    color.value = image.value =  repeat.value = position.value = size.value = origin.value = clip.value = property.value; +    return components; +  } + +  if (property.value.length == 1 && property.value[0][1] == '0 0') { +    return components; +  } + +  for (var i = values.length - 1; i >= 0; i--) { +    var value = values[i]; + +    if (validator.isValidBackgroundAttachment(value[1])) { +      attachment.value = [value]; +      anyValueSet = true; +    } else if (validator.isValidBackgroundClip(value[1]) || validator.isValidBackgroundOrigin(value[1])) { +      if (clipSet) { +        origin.value = [value]; +        originSet = true; +      } else { +        clip.value = [value]; +        clipSet = true; +      } +      anyValueSet = true; +    } else if (validator.isValidBackgroundRepeat(value[1])) { +      if (repeatSet) { +        repeat.value.unshift(value); +      } else { +        repeat.value = [value]; +        repeatSet = true; +      } +      anyValueSet = true; +    } else if (validator.isValidBackgroundPositionPart(value[1]) || validator.isValidBackgroundSizePart(value[1])) { +      if (i > 0) { +        var previousValue = values[i - 1]; + +        if (previousValue[1] == '/') { +          size.value = [value]; +        } else if (i > 1 && values[i - 2][1] == '/') { +          size.value = [previousValue, value]; +          i -= 2; +        } else { +          if (!positionSet) +            position.value = []; + +          position.value.unshift(value); +          positionSet = true; +        } +      } else { +        if (!positionSet) +          position.value = []; + +        position.value.unshift(value); +        positionSet = true; +      } +      anyValueSet = true; +    } else if ((color.value[0][1] == compactable[color.name].defaultValue || color.value[0][1] == 'none') && (validator.isValidColor(value[1]) || validator.isValidVendorPrefixedValue(value[1]))) { +      color.value = [value]; +      anyValueSet = true; +    } else if (validator.isValidUrl(value[1]) || validator.isValidFunction(value[1])) { +      image.value = [value]; +      anyValueSet = true; +    } +  } + +  if (clipSet && !originSet) +    origin.value = clip.value.slice(0); + +  if (!anyValueSet) { +    throw new InvalidPropertyError('Invalid background value at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); +  } + +  return components; +} + +function borderRadius(property, compactable) { +  var values = property.value; +  var splitAt = -1; + +  for (var i = 0, l = values.length; i < l; i++) { +    if (values[i][1] == '/') { +      splitAt = i; +      break; +    } +  } + +  if (splitAt === 0 || splitAt === values.length - 1) { +    throw new InvalidPropertyError('Invalid border-radius value at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); +  } + +  var target = _wrapDefault(property.name, property, compactable); +  target.value = splitAt > -1 ? +    values.slice(0, splitAt) : +    values.slice(0); +  target.components = fourValues(target, compactable); + +  var remainder = _wrapDefault(property.name, property, compactable); +  remainder.value = splitAt > -1 ? +    values.slice(splitAt + 1) : +    values.slice(0); +  remainder.components = fourValues(remainder, compactable); + +  for (var j = 0; j < 4; j++) { +    target.components[j].multiplex = true; +    target.components[j].value = target.components[j].value.concat(remainder.components[j].value); +  } + +  return target.components; +} + +function fourValues(property, compactable) { +  var componentNames = compactable[property.name].components; +  var components = []; +  var value = property.value; + +  if (value.length < 1) +    return []; + +  if (value.length < 2) +    value[1] = value[0].slice(0); +  if (value.length < 3) +    value[2] = value[0].slice(0); +  if (value.length < 4) +    value[3] = value[1].slice(0); + +  for (var i = componentNames.length - 1; i >= 0; i--) { +    var component = wrapSingle([ +      Token.PROPERTY, +      [Token.PROPERTY_NAME, componentNames[i]] +    ]); +    component.value = [value[i]]; +    components.unshift(component); +  } + +  return components; +} + +function multiplex(splitWith) { +  return function (property, compactable, validator) { +    var splitsAt = []; +    var values = property.value; +    var i, j, l, m; + +    // find split commas +    for (i = 0, l = values.length; i < l; i++) { +      if (values[i][1] == ',') +        splitsAt.push(i); +    } + +    if (splitsAt.length === 0) +      return splitWith(property, compactable, validator); + +    var splitComponents = []; + +    // split over commas, and into components +    for (i = 0, l = splitsAt.length; i <= l; i++) { +      var from = i === 0 ? 0 : splitsAt[i - 1] + 1; +      var to = i < l ? splitsAt[i] : values.length; + +      var _property = _wrapDefault(property.name, property, compactable); +      _property.value = values.slice(from, to); + +      splitComponents.push(splitWith(_property, compactable, validator)); +    } + +    var components = splitComponents[0]; + +    // group component values from each split +    for (i = 0, l = components.length; i < l; i++) { +      components[i].multiplex = true; + +      for (j = 1, m = splitComponents.length; j < m; j++) { +        components[i].value.push([Token.PROPERTY_VALUE, MULTIPLEX_SEPARATOR]); +        Array.prototype.push.apply(components[i].value, splitComponents[j][i].value); +      } +    } + +    return components; +  }; +} + +function listStyle(property, compactable, validator) { +  var type = _wrapDefault('list-style-type', property, compactable); +  var position = _wrapDefault('list-style-position', property, compactable); +  var image = _wrapDefault('list-style-image', property, compactable); +  var components = [type, position, image]; + +  if (property.value.length == 1 && property.value[0][1] == 'inherit') { +    type.value = position.value = image.value = [property.value[0]]; +    return components; +  } + +  var values = property.value.slice(0); +  var total = values.length; +  var index = 0; + +  // `image` first... +  for (index = 0, total = values.length; index < total; index++) { +    if (validator.isValidUrl(values[index][1]) || values[index][1] == '0') { +      image.value = [values[index]]; +      values.splice(index, 1); +      break; +    } +  } + +  // ... then `type`... +  for (index = 0, total = values.length; index < total; index++) { +    if (validator.isValidListStyleType(values[index][1])) { +      type.value = [values[index]]; +      values.splice(index, 1); +      break; +    } +  } + +  // ... and what's left is a `position` +  if (values.length > 0 && validator.isValidListStylePosition(values[0][1])) +    position.value = [values[0]]; + +  return components; +} + +function widthStyleColor(property, compactable, validator) { +  var descriptor = compactable[property.name]; +  var components = [ +    _wrapDefault(descriptor.components[0], property, compactable), +    _wrapDefault(descriptor.components[1], property, compactable), +    _wrapDefault(descriptor.components[2], property, compactable) +  ]; +  var color, style, width; + +  for (var i = 0; i < 3; i++) { +    var component = components[i]; + +    if (component.name.indexOf('color') > 0) +      color = component; +    else if (component.name.indexOf('style') > 0) +      style = component; +    else +      width = component; +  } + +  if ((property.value.length == 1 && property.value[0][1] == 'inherit') || +      (property.value.length == 3 && property.value[0][1] == 'inherit' && property.value[1][1] == 'inherit' && property.value[2][1] == 'inherit')) { +    color.value = style.value = width.value = [property.value[0]]; +    return components; +  } + +  var values = property.value.slice(0); +  var match, matches; + +  // NOTE: usually users don't follow the required order of parts in this shorthand, +  // so we'll try to parse it caring as little about order as possible + +  if (values.length > 0) { +    matches = values.filter(_widthFilter(validator)); +    match = matches.length > 1 && (matches[0][1] == 'none' || matches[0][1] == 'auto') ? matches[1] : matches[0]; +    if (match) { +      width.value = [match]; +      values.splice(values.indexOf(match), 1); +    } +  } + +  if (values.length > 0) { +    match = values.filter(_styleFilter(validator))[0]; +    if (match) { +      style.value = [match]; +      values.splice(values.indexOf(match), 1); +    } +  } + +  if (values.length > 0) { +    match = values.filter(_colorFilter(validator))[0]; +    if (match) { +      color.value = [match]; +      values.splice(values.indexOf(match), 1); +    } +  } + +  return components; +} + +module.exports = { +  background: background, +  border: widthStyleColor, +  borderRadius: borderRadius, +  fourValues: fourValues, +  listStyle: listStyle, +  multiplex: multiplex, +  outline: widthStyleColor +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/can-override.js b/node_modules/clean-css/lib/optimizer/level-2/can-override.js new file mode 100644 index 000000000..2617234ca --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/can-override.js @@ -0,0 +1,180 @@ +var understandable = require('./properties/understandable'); + +function backgroundPosition(validator, value1, value2) { +  if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-position', value2, true)) { +    return false; +  } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { +    return true; +  } else if (validator.isValidKeywordValue('background-position', value2, true)) { +    return true; +  } + +  return unit(validator, value1, value2); +} + +function backgroundSize(validator, value1, value2) { +  if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-size', value2, true)) { +    return false; +  } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { +    return true; +  } else if (validator.isValidKeywordValue('background-size', value2, true)) { +    return true; +  } + +  return unit(validator, value1, value2); +} + +function color(validator, value1, value2) { +  if (!understandable(validator, value1, value2, 0, true) && !validator.isValidColor(value2)) { +    return false; +  } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { +    return true; +  } else if (!validator.colorOpacity && (validator.isValidRgbaColor(value1) || validator.isValidHslaColor(value1))) { +    return false; +  } else if (!validator.colorOpacity && (validator.isValidRgbaColor(value2) || validator.isValidHslaColor(value2))) { +    return false; +  } else if (validator.isValidColor(value1) && validator.isValidColor(value2)) { +    return true; +  } + +  return sameFunctionOrValue(validator, value1, value2); +} + +function components(overrideCheckers) { +  return function (validator, value1, value2, position) { +    return overrideCheckers[position](validator, value1, value2); +  }; +} + +function image(validator, value1, value2) { +  if (!understandable(validator, value1, value2, 0, true) && !validator.isValidImage(value2)) { +    return false; +  } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { +    return true; +  } else if (validator.isValidImage(value2)) { +    return true; +  } else if (validator.isValidImage(value1)) { +    return false; +  } + +  return sameFunctionOrValue(validator, value1, value2); +} + +function keyword(propertyName) { +  return function(validator, value1, value2) { +    if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2)) { +      return false; +    } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { +      return true; +    } + +    return validator.isValidKeywordValue(propertyName, value2, false); +  }; +} + +function keywordWithGlobal(propertyName) { +  return function(validator, value1, value2) { +    if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2, true)) { +      return false; +    } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { +      return true; +    } + +    return validator.isValidKeywordValue(propertyName, value2, true); +  }; +} + +function sameFunctionOrValue(validator, value1, value2) { +  return validator.areSameFunction(value1, value2) ? +    true : +    value1 === value2; +} + +function textShadow(validator, value1, value2) { +  if (!understandable(validator, value1, value2, 0, true) && !validator.isValidTextShadow(value2)) { +    return false; +  } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { +    return true; +  } + +  return validator.isValidTextShadow(value2); +} + +function unit(validator, value1, value2) { +  if (!understandable(validator, value1, value2, 0, true) && !validator.isValidUnitWithoutFunction(value2)) { +    return false; +  } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { +    return true; +  } else if (validator.isValidUnitWithoutFunction(value1) && !validator.isValidUnitWithoutFunction(value2)) { +    return false; +  } else if (validator.isValidUnitWithoutFunction(value2)) { +    return true; +  } else if (validator.isValidUnitWithoutFunction(value1)) { +    return false; +  } else if (validator.isValidFunctionWithoutVendorPrefix(value1) && validator.isValidFunctionWithoutVendorPrefix(value2)) { +    return true; +  } + +  return sameFunctionOrValue(validator, value1, value2); +} + +function unitOrKeywordWithGlobal(propertyName) { +  var byKeyword = keywordWithGlobal(propertyName); + +  return function(validator, value1, value2) { +    return unit(validator, value1, value2) || byKeyword(validator, value1, value2); +  }; +} + +function zIndex(validator, value1, value2) { +  if (!understandable(validator, value1, value2, 0, true) && !validator.isValidZIndex(value2)) { +    return false; +  } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { +    return true; +  } + +  return validator.isValidZIndex(value2); +} + +module.exports = { +  generic: { +    color: color, +    components: components, +    image: image, +    unit: unit +  }, +  property: { +    backgroundAttachment: keyword('background-attachment'), +    backgroundClip: keywordWithGlobal('background-clip'), +    backgroundOrigin: keyword('background-origin'), +    backgroundPosition: backgroundPosition, +    backgroundRepeat: keyword('background-repeat'), +    backgroundSize: backgroundSize, +    bottom: unitOrKeywordWithGlobal('bottom'), +    borderCollapse: keyword('border-collapse'), +    borderStyle: keywordWithGlobal('*-style'), +    clear: keywordWithGlobal('clear'), +    cursor: keywordWithGlobal('cursor'), +    display: keywordWithGlobal('display'), +    float: keywordWithGlobal('float'), +    fontStyle: keywordWithGlobal('font-style'), +    left: unitOrKeywordWithGlobal('left'), +    fontWeight: keywordWithGlobal('font-weight'), +    listStyleType: keywordWithGlobal('list-style-type'), +    listStylePosition: keywordWithGlobal('list-style-position'), +    outlineStyle: keywordWithGlobal('*-style'), +    overflow: keywordWithGlobal('overflow'), +    position: keywordWithGlobal('position'), +    right: unitOrKeywordWithGlobal('right'), +    textAlign: keywordWithGlobal('text-align'), +    textDecoration: keywordWithGlobal('text-decoration'), +    textOverflow: keywordWithGlobal('text-overflow'), +    textShadow: textShadow, +    top: unitOrKeywordWithGlobal('top'), +    transform: sameFunctionOrValue, +    verticalAlign: unitOrKeywordWithGlobal('vertical-align'), +    visibility: keywordWithGlobal('visibility'), +    whiteSpace: keywordWithGlobal('white-space'), +    zIndex: zIndex +  } +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/clone.js b/node_modules/clean-css/lib/optimizer/level-2/clone.js new file mode 100644 index 000000000..3830095e9 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/clone.js @@ -0,0 +1,33 @@ +var wrapSingle = require('../wrap-for-optimizing').single; + +var Token = require('../../tokenizer/token'); + +function deep(property) { +  var cloned = shallow(property); +  for (var i = property.components.length - 1; i >= 0; i--) { +    var component = shallow(property.components[i]); +    component.value = property.components[i].value.slice(0); +    cloned.components.unshift(component); +  } + +  cloned.dirty = true; +  cloned.value = property.value.slice(0); + +  return cloned; +} + +function shallow(property) { +  var cloned = wrapSingle([ +    Token.PROPERTY, +    [Token.PROPERTY_NAME, property.name] +  ]); +  cloned.important = property.important; +  cloned.hack = property.hack; +  cloned.unused = false; +  return cloned; +} + +module.exports = { +  deep: deep, +  shallow: shallow +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/compactable.js b/node_modules/clean-css/lib/optimizer/level-2/compactable.js new file mode 100644 index 000000000..9cf334d44 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/compactable.js @@ -0,0 +1,787 @@ +// Contains the interpretation of CSS properties, as used by the property optimizer + +var breakUp = require('./break-up'); +var canOverride = require('./can-override'); +var restore = require('./restore'); + +var override = require('../../utils/override'); + +// Properties to process +// Extend this object in order to add support for more properties in the optimizer. +// +// Each key in this object represents a CSS property and should be an object. +// Such an object contains properties that describe how the represented CSS property should be handled. +// Possible options: +// +// * components: array (Only specify for shorthand properties.) +//   Contains the names of the granular properties this shorthand compacts. +// +// * canOverride: function +//   Returns whether two tokens of this property can be merged with each other. +//   This property has no meaning for shorthands. +// +// * defaultValue: string +//   Specifies the default value of the property according to the CSS standard. +//   For shorthand, this is used when every component is set to its default value, therefore it should be the shortest possible default value of all the components. +// +// * shortestValue: string +//   Specifies the shortest possible value the property can possibly have. +//   (Falls back to defaultValue if unspecified.) +// +// * breakUp: function (Only specify for shorthand properties.) +//   Breaks the shorthand up to its components. +// +// * restore: function (Only specify for shorthand properties.) +//   Puts the shorthand together from its components. +// +var compactable = { +  'background': { +    canOverride: canOverride.generic.components([ +      canOverride.generic.image, +      canOverride.property.backgroundPosition, +      canOverride.property.backgroundSize, +      canOverride.property.backgroundRepeat, +      canOverride.property.backgroundAttachment, +      canOverride.property.backgroundOrigin, +      canOverride.property.backgroundClip, +      canOverride.generic.color +    ]), +    components: [ +      'background-image', +      'background-position', +      'background-size', +      'background-repeat', +      'background-attachment', +      'background-origin', +      'background-clip', +      'background-color' +    ], +    breakUp: breakUp.multiplex(breakUp.background), +    defaultValue: '0 0', +    restore: restore.multiplex(restore.background), +    shortestValue: '0', +    shorthand: true +  }, +  'background-attachment': { +    canOverride: canOverride.property.backgroundAttachment, +    componentOf: [ +      'background' +    ], +    defaultValue: 'scroll' +  }, +  'background-clip': { +    canOverride: canOverride.property.backgroundClip, +    componentOf: [ +      'background' +    ], +    defaultValue: 'border-box', +    shortestValue: 'border-box' +  }, +  'background-color': { +    canOverride: canOverride.generic.color, +    componentOf: [ +      'background' +    ], +    defaultValue: 'transparent', +    multiplexLastOnly: true, +    nonMergeableValue: 'none', +    shortestValue: 'red' +  }, +  'background-image': { +    canOverride: canOverride.generic.image, +    componentOf: [ +      'background' +    ], +    defaultValue: 'none' +  }, +  'background-origin': { +    canOverride: canOverride.property.backgroundOrigin, +    componentOf: [ +      'background' +    ], +    defaultValue: 'padding-box', +    shortestValue: 'border-box' +  }, +  'background-position': { +    canOverride: canOverride.property.backgroundPosition, +    componentOf: [ +      'background' +    ], +    defaultValue: ['0', '0'], +    doubleValues: true, +    shortestValue: '0' +  }, +  'background-repeat': { +    canOverride: canOverride.property.backgroundRepeat, +    componentOf: [ +      'background' +    ], +    defaultValue: ['repeat'], +    doubleValues: true +  }, +  'background-size': { +    canOverride: canOverride.property.backgroundSize, +    componentOf: [ +      'background' +    ], +    defaultValue: ['auto'], +    doubleValues: true, +    shortestValue: '0 0' +  }, +  'bottom': { +    canOverride: canOverride.property.bottom, +    defaultValue: 'auto' +  }, +  'border': { +    breakUp: breakUp.border, +    canOverride: canOverride.generic.components([ +      canOverride.generic.unit, +      canOverride.property.borderStyle, +      canOverride.generic.color +    ]), +    components: [ +      'border-width', +      'border-style', +      'border-color' +    ], +    defaultValue: 'none', +    overridesShorthands: [ +      'border-bottom', +      'border-left', +      'border-right', +      'border-top' +    ], +    restore: restore.withoutDefaults, +    shorthand: true, +    shorthandComponents: true +  }, +  'border-bottom': { +    breakUp: breakUp.border, +    canOverride: canOverride.generic.components([ +      canOverride.generic.unit, +      canOverride.property.borderStyle, +      canOverride.generic.color +    ]), +    components: [ +      'border-bottom-width', +      'border-bottom-style', +      'border-bottom-color' +    ], +    defaultValue: 'none', +    restore: restore.withoutDefaults, +    shorthand: true +  }, +  'border-bottom-color': { +    canOverride: canOverride.generic.color, +    componentOf: [ +      'border-bottom', +      'border-color' +    ], +    defaultValue: 'none' +  }, +  'border-bottom-left-radius': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'border-radius' +    ], +    defaultValue: '0', +    vendorPrefixes: [ +      '-moz-', +      '-o-' +    ] +  }, +  'border-bottom-right-radius': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'border-radius' +    ], +    defaultValue: '0', +    vendorPrefixes: [ +      '-moz-', +      '-o-' +    ] +  }, +  'border-bottom-style': { +    canOverride: canOverride.property.borderStyle, +    componentOf: [ +      'border-bottom', +      'border-style' +    ], +    defaultValue: 'none' +  }, +  'border-bottom-width': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'border-bottom', +      'border-width' +    ], +    defaultValue: 'medium', +    shortestValue: '0' +  }, +  'border-collapse': { +    canOverride: canOverride.property.borderCollapse, +    defaultValue: 'separate' +  }, +  'border-color': { +    breakUp: breakUp.fourValues, +    canOverride: canOverride.generic.components([ +      canOverride.generic.color, +      canOverride.generic.color, +      canOverride.generic.color, +      canOverride.generic.color +    ]), +    componentOf: ['border'], +    components: [ +      'border-top-color', +      'border-right-color', +      'border-bottom-color', +      'border-left-color' +    ], +    defaultValue: 'none', +    restore: restore.fourValues, +    shortestValue: 'red', +    shorthand: true +  }, +  'border-left': { +    breakUp: breakUp.border, +    canOverride: canOverride.generic.components([ +      canOverride.generic.unit, +      canOverride.property.borderStyle, +      canOverride.generic.color +    ]), +    components: [ +      'border-left-width', +      'border-left-style', +      'border-left-color' +    ], +    defaultValue: 'none', +    restore: restore.withoutDefaults, +    shorthand: true +  }, +  'border-left-color': { +    canOverride: canOverride.generic.color, +    componentOf: [ +      'border-color', +      'border-left' +    ], +    defaultValue: 'none' +  }, +  'border-left-style': { +    canOverride: canOverride.property.borderStyle, +    componentOf: [ +      'border-left', +      'border-style' +    ], +    defaultValue: 'none' +  }, +  'border-left-width': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'border-left', +      'border-width' +    ], +    defaultValue: 'medium', +    shortestValue: '0' +  }, +  'border-radius': { +    breakUp: breakUp.borderRadius, +    canOverride: canOverride.generic.components([ +      canOverride.generic.unit, +      canOverride.generic.unit, +      canOverride.generic.unit, +      canOverride.generic.unit +    ]), +    components: [ +      'border-top-left-radius', +      'border-top-right-radius', +      'border-bottom-right-radius', +      'border-bottom-left-radius' +    ], +    defaultValue: '0', +    restore: restore.borderRadius, +    shorthand: true, +    vendorPrefixes: [ +      '-moz-', +      '-o-' +    ] +  }, +  'border-right': { +    breakUp: breakUp.border, +    canOverride: canOverride.generic.components([ +      canOverride.generic.unit, +      canOverride.property.borderStyle, +      canOverride.generic.color +    ]), +    components: [ +      'border-right-width', +      'border-right-style', +      'border-right-color' +    ], +    defaultValue: 'none', +    restore: restore.withoutDefaults, +    shorthand: true +  }, +  'border-right-color': { +    canOverride: canOverride.generic.color, +    componentOf: [ +      'border-color', +      'border-right' +    ], +    defaultValue: 'none' +  }, +  'border-right-style': { +    canOverride: canOverride.property.borderStyle, +    componentOf: [ +      'border-right', +      'border-style' +    ], +    defaultValue: 'none' +  }, +  'border-right-width': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'border-right', +      'border-width' +    ], +    defaultValue: 'medium', +    shortestValue: '0' +  }, +  'border-style': { +    breakUp: breakUp.fourValues, +    canOverride: canOverride.generic.components([ +      canOverride.property.borderStyle, +      canOverride.property.borderStyle, +      canOverride.property.borderStyle, +      canOverride.property.borderStyle +    ]), +    componentOf: [ +      'border' +    ], +    components: [ +      'border-top-style', +      'border-right-style', +      'border-bottom-style', +      'border-left-style' +    ], +    defaultValue: 'none', +    restore: restore.fourValues, +    shorthand: true +  }, +  'border-top': { +    breakUp: breakUp.border, +    canOverride: canOverride.generic.components([ +      canOverride.generic.unit, +      canOverride.property.borderStyle, +      canOverride.generic.color +    ]), +    components: [ +      'border-top-width', +      'border-top-style', +      'border-top-color' +    ], +    defaultValue: 'none', +    restore: restore.withoutDefaults, +    shorthand: true +  }, +  'border-top-color': { +    canOverride: canOverride.generic.color, +    componentOf: [ +      'border-color', +      'border-top' +    ], +    defaultValue: 'none' +  }, +  'border-top-left-radius': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'border-radius' +    ], +    defaultValue: '0', +    vendorPrefixes: [ +      '-moz-', +      '-o-' +    ] +  }, +  'border-top-right-radius': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'border-radius' +    ], +    defaultValue: '0', +    vendorPrefixes: [ +      '-moz-', +      '-o-' +    ] +  }, +  'border-top-style': { +    canOverride: canOverride.property.borderStyle, +    componentOf: [ +      'border-style', +      'border-top' +    ], +    defaultValue: 'none' +  }, +  'border-top-width': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'border-top', +      'border-width' +    ], +    defaultValue: 'medium', +    shortestValue: '0' +  }, +  'border-width': { +    breakUp: breakUp.fourValues, +    canOverride: canOverride.generic.components([ +      canOverride.generic.unit, +      canOverride.generic.unit, +      canOverride.generic.unit, +      canOverride.generic.unit +    ]), +    components: [ +      'border-top-width', +      'border-right-width', +      'border-bottom-width', +      'border-left-width' +    ], +    defaultValue: 'medium', +    restore: restore.fourValues, +    shortestValue: '0', +    shorthand: true +  }, +  'clear': { +    canOverride: canOverride.property.clear, +    defaultValue: 'none' +  }, +  'color': { +    canOverride: canOverride.generic.color, +    defaultValue: 'transparent', +    shortestValue: 'red' +  }, +  'cursor': { +    canOverride: canOverride.property.cursor, +    defaultValue: 'auto' +  }, +  'display': { +    canOverride: canOverride.property.display, +  }, +  'float': { +    canOverride: canOverride.property.float, +    defaultValue: 'none' +  }, +  'font-size': { +    canOverride: canOverride.generic.unit, +    defaultValue: 'medium', +    shortestValue: '0' +  }, +  'font-style': { +    canOverride: canOverride.property.fontStyle, +    defaultValue: 'normal' +  }, +  'font-weight': { +    canOverride: canOverride.property.fontWeight, +    defaultValue: '400', +    shortestValue: '400' +  }, +  'height': { +    canOverride: canOverride.generic.unit, +    defaultValue: 'auto', +    shortestValue: '0' +  }, +  'left': { +    canOverride: canOverride.property.left, +    defaultValue: 'auto' +  }, +  'line-height': { +    canOverride: canOverride.generic.unit, +    defaultValue: 'normal', +    shortestValue: '0' +  }, +  'list-style': { +    canOverride: canOverride.generic.components([ +      canOverride.property.listStyleType, +      canOverride.property.listStylePosition, +      canOverride.property.listStyleImage +    ]), +    components: [ +      'list-style-type', +      'list-style-position', +      'list-style-image' +    ], +    breakUp: breakUp.listStyle, +    restore: restore.withoutDefaults, +    defaultValue: 'outside', // can't use 'disc' because that'd override default 'decimal' for <ol> +    shortestValue: 'none', +    shorthand: true +  }, +  'list-style-image' : { +    canOverride: canOverride.generic.image, +    componentOf: [ +      'list-style' +    ], +    defaultValue: 'none' +  }, +  'list-style-position' : { +    canOverride: canOverride.property.listStylePosition, +    componentOf: [ +      'list-style' +    ], +    defaultValue: 'outside', +    shortestValue: 'inside' +  }, +  'list-style-type' : { +    canOverride: canOverride.property.listStyleType, +    componentOf: [ +      'list-style' +    ], +    // NOTE: we can't tell the real default value here, it's 'disc' for <ul> and 'decimal' for <ol> +    // this is a hack, but it doesn't matter because this value will be either overridden or +    // it will disappear at the final step anyway +    defaultValue: 'decimal|disc', +    shortestValue: 'none' +  }, +  'margin': { +    breakUp: breakUp.fourValues, +    canOverride: canOverride.generic.components([ +      canOverride.generic.unit, +      canOverride.generic.unit, +      canOverride.generic.unit, +      canOverride.generic.unit +    ]), +    components: [ +      'margin-top', +      'margin-right', +      'margin-bottom', +      'margin-left' +    ], +    defaultValue: '0', +    restore: restore.fourValues, +    shorthand: true +  }, +  'margin-bottom': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'margin' +    ], +    defaultValue: '0' +  }, +  'margin-left': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'margin' +    ], +    defaultValue: '0' +  }, +  'margin-right': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'margin' +    ], +    defaultValue: '0' +  }, +  'margin-top': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'margin' +    ], +    defaultValue: '0' +  }, +  'outline': { +    canOverride: canOverride.generic.components([ +      canOverride.generic.color, +      canOverride.property.outlineStyle, +      canOverride.generic.unit +    ]), +    components: [ +      'outline-color', +      'outline-style', +      'outline-width' +    ], +    breakUp: breakUp.outline, +    restore: restore.withoutDefaults, +    defaultValue: '0', +    shorthand: true +  }, +  'outline-color': { +    canOverride: canOverride.generic.color, +    componentOf: [ +      'outline' +    ], +    defaultValue: 'invert', +    shortestValue: 'red' +  }, +  'outline-style': { +    canOverride: canOverride.property.outlineStyle, +    componentOf: [ +      'outline' +    ], +    defaultValue: 'none' +  }, +  'outline-width': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'outline' +    ], +    defaultValue: 'medium', +    shortestValue: '0' +  }, +  'overflow': { +    canOverride: canOverride.property.overflow, +    defaultValue: 'visible' +  }, +  'overflow-x': { +    canOverride: canOverride.property.overflow, +    defaultValue: 'visible' +  }, +  'overflow-y': { +    canOverride: canOverride.property.overflow, +    defaultValue: 'visible' +  }, +  'padding': { +    breakUp: breakUp.fourValues, +    canOverride: canOverride.generic.components([ +      canOverride.generic.unit, +      canOverride.generic.unit, +      canOverride.generic.unit, +      canOverride.generic.unit +    ]), +    components: [ +      'padding-top', +      'padding-right', +      'padding-bottom', +      'padding-left' +    ], +    defaultValue: '0', +    restore: restore.fourValues, +    shorthand: true +  }, +  'padding-bottom': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'padding' +    ], +    defaultValue: '0' +  }, +  'padding-left': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'padding' +    ], +    defaultValue: '0' +  }, +  'padding-right': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'padding' +    ], +    defaultValue: '0' +  }, +  'padding-top': { +    canOverride: canOverride.generic.unit, +    componentOf: [ +      'padding' +    ], +    defaultValue: '0' +  }, +  'position': { +    canOverride: canOverride.property.position, +    defaultValue: 'static' +  }, +  'right': { +    canOverride: canOverride.property.right, +    defaultValue: 'auto' +  }, +  'text-align': { +    canOverride: canOverride.property.textAlign, +    // NOTE: we can't tell the real default value here, as it depends on default text direction +    // this is a hack, but it doesn't matter because this value will be either overridden or +    // it will disappear anyway +    defaultValue: 'left|right' +  }, +  'text-decoration': { +    canOverride: canOverride.property.textDecoration, +    defaultValue: 'none' +  }, +  'text-overflow': { +    canOverride: canOverride.property.textOverflow, +    defaultValue: 'none' +  }, +  'text-shadow': { +    canOverride: canOverride.property.textShadow, +    defaultValue: 'none' +  }, +  'top': { +    canOverride: canOverride.property.top, +    defaultValue: 'auto' +  }, +  'transform': { +    canOverride: canOverride.property.transform, +    vendorPrefixes: [ +      '-moz-', +      '-ms-', +      '-webkit-' +    ] +  }, +  'vertical-align': { +    canOverride: canOverride.property.verticalAlign, +    defaultValue: 'baseline' +  }, +  'visibility': { +    canOverride: canOverride.property.visibility, +    defaultValue: 'visible' +  }, +  'white-space': { +    canOverride: canOverride.property.whiteSpace, +    defaultValue: 'normal' +  }, +  'width': { +    canOverride: canOverride.generic.unit, +    defaultValue: 'auto', +    shortestValue: '0' +  }, +  'z-index': { +    canOverride: canOverride.property.zIndex, +    defaultValue: 'auto' +  } +}; + +function cloneDescriptor(propertyName, prefix) { +  var clonedDescriptor = override(compactable[propertyName], {}); + +  if ('componentOf' in clonedDescriptor) { +    clonedDescriptor.componentOf = clonedDescriptor.componentOf.map(function (shorthandName) { +      return prefix + shorthandName; +    }); +  } + +  if ('components' in clonedDescriptor) { +    clonedDescriptor.components = clonedDescriptor.components.map(function (longhandName) { +      return prefix + longhandName; +    }); +  } + +  return clonedDescriptor; +} + +// generate vendor-prefixed properties +var vendorPrefixedCompactable = {}; + +for (var propertyName in compactable) { +  var descriptor = compactable[propertyName]; + +  if (!('vendorPrefixes' in descriptor)) { +    continue; +  } + +  for (var i = 0; i < descriptor.vendorPrefixes.length; i++) { +    var prefix = descriptor.vendorPrefixes[i]; +    var clonedDescriptor = cloneDescriptor(propertyName, prefix); +    delete clonedDescriptor.vendorPrefixes; + +    vendorPrefixedCompactable[prefix + propertyName] = clonedDescriptor; +  } + +  delete descriptor.vendorPrefixes; +} + +module.exports = override(compactable, vendorPrefixedCompactable); diff --git a/node_modules/clean-css/lib/optimizer/level-2/extract-properties.js b/node_modules/clean-css/lib/optimizer/level-2/extract-properties.js new file mode 100644 index 000000000..8b9b1c2e0 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/extract-properties.js @@ -0,0 +1,73 @@ +// This extractor is used in level 2 optimizations +// IMPORTANT: Mind Token class and this code is not related! +// Properties will be tokenized in one step, see #429 + +var Token = require('../../tokenizer/token'); +var serializeRules = require('../../writer/one-time').rules; +var serializeValue = require('../../writer/one-time').value; + +function extractProperties(token) { +  var properties = []; +  var inSpecificSelector; +  var property; +  var name; +  var value; +  var i, l; + +  if (token[0] == Token.RULE) { +    inSpecificSelector = !/[\.\+>~]/.test(serializeRules(token[1])); + +    for (i = 0, l = token[2].length; i < l; i++) { +      property = token[2][i]; + +      if (property[0] != Token.PROPERTY) +        continue; + +      name = property[1][1]; +      if (name.length === 0) +        continue; + +      if (name.indexOf('--') === 0) +        continue; + +      value = serializeValue(property, i); + +      properties.push([ +        name, +        value, +        findNameRoot(name), +        token[2][i], +        name + ':' + value, +        token[1], +        inSpecificSelector +      ]); +    } +  } else if (token[0] == Token.NESTED_BLOCK) { +    for (i = 0, l = token[2].length; i < l; i++) { +      properties = properties.concat(extractProperties(token[2][i])); +    } +  } + +  return properties; +} + +function findNameRoot(name) { +  if (name == 'list-style') +    return name; +  if (name.indexOf('-radius') > 0) +    return 'border-radius'; +  if (name == 'border-collapse' || name == 'border-spacing' || name == 'border-image') +    return name; +  if (name.indexOf('border-') === 0 && /^border\-\w+\-\w+$/.test(name)) +    return name.match(/border\-\w+/)[0]; +  if (name.indexOf('border-') === 0 && /^border\-\w+$/.test(name)) +    return 'border'; +  if (name.indexOf('text-') === 0) +    return name; +  if (name == '-chrome-') +    return name; + +  return name.replace(/^\-\w+\-/, '').match(/([a-zA-Z]+)/)[0].toLowerCase(); +} + +module.exports = extractProperties; diff --git a/node_modules/clean-css/lib/optimizer/level-2/invalid-property-error.js b/node_modules/clean-css/lib/optimizer/level-2/invalid-property-error.js new file mode 100644 index 000000000..86d5b5f9b --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/invalid-property-error.js @@ -0,0 +1,10 @@ +function InvalidPropertyError(message) { +  this.name = 'InvalidPropertyError'; +  this.message = message; +  this.stack = (new Error()).stack; +} + +InvalidPropertyError.prototype = Object.create(Error.prototype); +InvalidPropertyError.prototype.constructor = InvalidPropertyError; + +module.exports = InvalidPropertyError; diff --git a/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js b/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js new file mode 100644 index 000000000..4a91a8331 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js @@ -0,0 +1,236 @@ +var Marker = require('../../tokenizer/marker'); +var split = require('../../utils/split'); + +var DEEP_SELECTOR_PATTERN = /\/deep\//; +var DOUBLE_COLON_PATTERN = /^::/; +var NOT_PSEUDO = ':not'; +var PSEUDO_CLASSES_WITH_ARGUMENTS = [ +  ':dir', +  ':lang', +  ':not', +  ':nth-child', +  ':nth-last-child', +  ':nth-last-of-type', +  ':nth-of-type' +]; +var RELATION_PATTERN = /[>\+~]/; + +var Level = { +  DOUBLE_QUOTE: 'double-quote', +  SINGLE_QUOTE: 'single-quote', +  ROOT: 'root' +}; + +function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements) { +  var singleSelectors = split(selector, Marker.COMMA); +  var singleSelector; +  var i, l; + +  for (i = 0, l = singleSelectors.length; i < l; i++) { +    singleSelector = singleSelectors[i]; + +    if (singleSelector.length === 0 || +        isDeepSelector(singleSelector) || +        (singleSelector.indexOf(Marker.COLON) > -1 && !areMergeable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements))) { +      return false; +    } +  } + +  return true; +} + +function isDeepSelector(selector) { +  return DEEP_SELECTOR_PATTERN.test(selector); +} + +function extractPseudoFrom(selector) { +  var list = []; +  var character; +  var buffer = []; +  var level = Level.ROOT; +  var roundBracketLevel = 0; +  var isQuoted; +  var isEscaped; +  var isPseudo = false; +  var isRelation; +  var wasColon = false; +  var index; +  var len; + +  for (index = 0, len = selector.length; index < len; index++) { +    character = selector[index]; + +    isRelation = !isEscaped && RELATION_PATTERN.test(character); +    isQuoted = level == Level.DOUBLE_QUOTE || level == Level.SINGLE_QUOTE; + +    if (isEscaped) { +      buffer.push(character); +    } else if (character == Marker.DOUBLE_QUOTE && level == Level.ROOT) { +      buffer.push(character); +      level = Level.DOUBLE_QUOTE; +    } else if (character == Marker.DOUBLE_QUOTE && level == Level.DOUBLE_QUOTE) { +      buffer.push(character); +      level = Level.ROOT; +    } else if (character == Marker.SINGLE_QUOTE && level == Level.ROOT) { +      buffer.push(character); +      level = Level.SINGLE_QUOTE; +    } else if (character == Marker.SINGLE_QUOTE && level == Level.SINGLE_QUOTE) { +      buffer.push(character); +      level = Level.ROOT; +    } else if (isQuoted) { +      buffer.push(character); +    } else if (character == Marker.OPEN_ROUND_BRACKET) { +      buffer.push(character); +      roundBracketLevel++; +    } else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1 && isPseudo) { +      buffer.push(character); +      list.push(buffer.join('')); +      roundBracketLevel--; +      buffer = []; +      isPseudo = false; +    } else if (character == Marker.CLOSE_ROUND_BRACKET) { +      buffer.push(character); +      roundBracketLevel--; +    } else if (character == Marker.COLON && roundBracketLevel === 0 && isPseudo && !wasColon) { +      list.push(buffer.join('')); +      buffer = []; +      buffer.push(character); +    } else if (character == Marker.COLON && roundBracketLevel === 0 && !wasColon) { +      buffer = []; +      buffer.push(character); +      isPseudo = true; +    } else if (character == Marker.SPACE && roundBracketLevel === 0 && isPseudo) { +      list.push(buffer.join('')); +      buffer = []; +      isPseudo = false; +    } else if (isRelation && roundBracketLevel === 0 && isPseudo) { +      list.push(buffer.join('')); +      buffer = []; +      isPseudo = false; +    } else { +      buffer.push(character); +    } + +    isEscaped = character == Marker.BACK_SLASH; +    wasColon = character == Marker.COLON; +  } + +  if (buffer.length > 0 && isPseudo) { +    list.push(buffer.join('')); +  } + +  return list; +} + +function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements) { +  return areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) && +    needArguments(matches) && +    (matches.length < 2 || !someIncorrectlyChained(selector, matches)) && +    (matches.length < 2 || !someMixed(matches)); +} + +function areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) { +  var match; +  var name; +  var i, l; + +  for (i = 0, l = matches.length; i < l; i++) { +    match = matches[i]; +    name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ? +      match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) : +      match; + +    if (mergeablePseudoClasses.indexOf(name) === -1 && mergeablePseudoElements.indexOf(name) === -1) { +      return false; +    } +  } + +  return true; +} + +function needArguments(matches) { +  var match; +  var name; +  var bracketOpensAt; +  var hasArguments; +  var i, l; + +  for (i = 0, l = matches.length; i < l; i++) { +    match = matches[i]; + +    bracketOpensAt = match.indexOf(Marker.OPEN_ROUND_BRACKET); +    hasArguments = bracketOpensAt > -1; +    name = hasArguments ? +      match.substring(0, bracketOpensAt) : +      match; + +    if (hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) == -1) { +      return false; +    } + +    if (!hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) > -1) { +      return false; +    } +  } + +  return true; +} + +function someIncorrectlyChained(selector, matches) { +  var positionInSelector = 0; +  var match; +  var matchAt; +  var nextMatch; +  var nextMatchAt; +  var name; +  var nextName; +  var areChained; +  var i, l; + +  for (i = 0, l = matches.length; i < l; i++) { +    match = matches[i]; +    nextMatch = matches[i + 1]; + +    if (!nextMatch) { +      break; +    } + +    matchAt = selector.indexOf(match, positionInSelector); +    nextMatchAt = selector.indexOf(match, matchAt + 1); +    positionInSelector = nextMatchAt; +    areChained = matchAt + match.length == nextMatchAt; + +    if (areChained) { +      name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ? +        match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) : +        match; +      nextName = nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ? +        nextMatch.substring(0, nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET)) : +        nextMatch; + +      if (name != NOT_PSEUDO || nextName != NOT_PSEUDO) { +        return true; +      } +    } +  } + +  return false; +} + +function someMixed(matches) { +  var firstIsPseudoElement = DOUBLE_COLON_PATTERN.test(matches[0]); +  var match; +  var i, l; + +  for (i = 0, l = matches.length; i < l; i++) { +    match = matches[i]; + +    if (DOUBLE_COLON_PATTERN.test(match) != firstIsPseudoElement) { +      return true; +    } +  } + +  return false; +} + +module.exports = isMergeable; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js b/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js new file mode 100644 index 000000000..7c0537501 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js @@ -0,0 +1,49 @@ +var isMergeable = require('./is-mergeable'); + +var optimizeProperties = require('./properties/optimize'); + +var sortSelectors = require('../level-1/sort-selectors'); +var tidyRules = require('../level-1/tidy-rules'); + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +var Token = require('../../tokenizer/token'); + +function mergeAdjacent(tokens, context) { +  var lastToken = [null, [], []]; +  var options = context.options; +  var adjacentSpace = options.compatibility.selectors.adjacentSpace; +  var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod; +  var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; +  var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; +  var mergeLimit = 8191; + +  for (var i = 0, l = tokens.length; i < l; i++) { +    var token = tokens[i]; + +    if (token[0] != Token.RULE) { +      lastToken = [null, [], []]; +      continue; +    } + +    if (lastToken[0] == Token.RULE && serializeRules(token[1]) == serializeRules(lastToken[1])) { +      Array.prototype.push.apply(lastToken[2], token[2]); +      optimizeProperties(lastToken[2], true, true, context); +      token[2] = []; +    } else if (lastToken[0] == Token.RULE && serializeBody(token[2]) == serializeBody(lastToken[2]) && +        isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) && +        isMergeable(serializeRules(lastToken[1]), mergeablePseudoClasses, mergeablePseudoElements) && +        lastToken[1].length < mergeLimit) { +      lastToken[1] = tidyRules(lastToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings); +      lastToken[1] = lastToken.length > 1 ? sortSelectors(lastToken[1], selectorsSortingMethod) : lastToken[1]; +      token[2] = []; +    } else { +      lastToken = token; +    } +  } +} + +module.exports = mergeAdjacent; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-media-queries.js b/node_modules/clean-css/lib/optimizer/level-2/merge-media-queries.js new file mode 100644 index 000000000..c3c60dc24 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-media-queries.js @@ -0,0 +1,103 @@ +var canReorder = require('./reorderable').canReorder; +var canReorderSingle = require('./reorderable').canReorderSingle; +var extractProperties = require('./extract-properties'); +var rulesOverlap = require('./rules-overlap'); + +var serializeRules = require('../../writer/one-time').rules; +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; +var Token = require('../../tokenizer/token'); + +function mergeMediaQueries(tokens, context) { +  var mergeSemantically = context.options.level[OptimizationLevel.Two].mergeSemantically; +  var specificityCache = context.cache.specificity; +  var candidates = {}; +  var reduced = []; + +  for (var i = tokens.length - 1; i >= 0; i--) { +    var token = tokens[i]; +    if (token[0] != Token.NESTED_BLOCK) { +      continue; +    } + +    var key = serializeRules(token[1]); +    var candidate = candidates[key]; +    if (!candidate) { +      candidate = []; +      candidates[key] = candidate; +    } + +    candidate.push(i); +  } + +  for (var name in candidates) { +    var positions = candidates[name]; + +    positionLoop: +    for (var j = positions.length - 1; j > 0; j--) { +      var positionOne = positions[j]; +      var tokenOne = tokens[positionOne]; +      var positionTwo = positions[j - 1]; +      var tokenTwo = tokens[positionTwo]; + +      directionLoop: +      for (var direction = 1; direction >= -1; direction -= 2) { +        var topToBottom = direction == 1; +        var from = topToBottom ? positionOne + 1 : positionTwo - 1; +        var to = topToBottom ? positionTwo : positionOne; +        var delta = topToBottom ? 1 : -1; +        var source = topToBottom ? tokenOne : tokenTwo; +        var target = topToBottom ? tokenTwo : tokenOne; +        var movedProperties = extractProperties(source); + +        while (from != to) { +          var traversedProperties = extractProperties(tokens[from]); +          from += delta; + +          if (mergeSemantically && allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache)) { +            continue; +          } + +          if (!canReorder(movedProperties, traversedProperties, specificityCache)) +            continue directionLoop; +        } + +        target[2] = topToBottom ? +          source[2].concat(target[2]) : +          target[2].concat(source[2]); +        source[2] = []; + +        reduced.push(target); +        continue positionLoop; +      } +    } +  } + +  return reduced; +} + +function allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache) { +  var movedProperty; +  var movedRule; +  var traversedProperty; +  var traversedRule; +  var i, l; +  var j, m; + +  for (i = 0, l = movedProperties.length; i < l; i++) { +    movedProperty = movedProperties[i]; +    movedRule = movedProperty[5]; + +    for (j = 0, m = traversedProperties.length; j < m; j++) { +      traversedProperty = traversedProperties[j]; +      traversedRule = traversedProperty[5]; + +      if (rulesOverlap(movedRule, traversedRule, true) && !canReorderSingle(movedProperty, traversedProperty, specificityCache)) { +        return false; +      } +    } +  } + +  return true; +} + +module.exports = mergeMediaQueries; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-body.js b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-body.js new file mode 100644 index 000000000..bdcf53ec0 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-body.js @@ -0,0 +1,79 @@ +var isMergeable = require('./is-mergeable'); + +var sortSelectors = require('../level-1/sort-selectors'); +var tidyRules = require('../level-1/tidy-rules'); + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +var Token = require('../../tokenizer/token'); + +function unsafeSelector(value) { +  return /\.|\*| :/.test(value); +} + +function isBemElement(token) { +  var asString = serializeRules(token[1]); +  return asString.indexOf('__') > -1 || asString.indexOf('--') > -1; +} + +function withoutModifier(selector) { +  return selector.replace(/--[^ ,>\+~:]+/g, ''); +} + +function removeAnyUnsafeElements(left, candidates) { +  var leftSelector = withoutModifier(serializeRules(left[1])); + +  for (var body in candidates) { +    var right = candidates[body]; +    var rightSelector = withoutModifier(serializeRules(right[1])); + +    if (rightSelector.indexOf(leftSelector) > -1 || leftSelector.indexOf(rightSelector) > -1) +      delete candidates[body]; +  } +} + +function mergeNonAdjacentByBody(tokens, context) { +  var options = context.options; +  var mergeSemantically = options.level[OptimizationLevel.Two].mergeSemantically; +  var adjacentSpace = options.compatibility.selectors.adjacentSpace; +  var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod; +  var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; +  var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; +  var candidates = {}; + +  for (var i = tokens.length - 1; i >= 0; i--) { +    var token = tokens[i]; +    if (token[0] != Token.RULE) +      continue; + +    if (token[2].length > 0 && (!mergeSemantically && unsafeSelector(serializeRules(token[1])))) +      candidates = {}; + +    if (token[2].length > 0 && mergeSemantically && isBemElement(token)) +      removeAnyUnsafeElements(token, candidates); + +    var candidateBody = serializeBody(token[2]); +    var oldToken = candidates[candidateBody]; +    if (oldToken && +        isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) && +        isMergeable(serializeRules(oldToken[1]), mergeablePseudoClasses, mergeablePseudoElements)) { + +      if (token[2].length > 0) { +        token[1] = tidyRules(oldToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings); +        token[1] = token[1].length > 1 ? sortSelectors(token[1], selectorsSortingMethod) : token[1]; +      } else { +        token[1] = oldToken[1].concat(token[1]); +      } + +      oldToken[2] = []; +      candidates[candidateBody] = null; +    } + +    candidates[serializeBody(token[2])] = token; +  } +} + +module.exports = mergeNonAdjacentByBody; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-selector.js b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-selector.js new file mode 100644 index 000000000..5e23064eb --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-selector.js @@ -0,0 +1,78 @@ +var canReorder = require('./reorderable').canReorder; +var extractProperties = require('./extract-properties'); + +var optimizeProperties = require('./properties/optimize'); + +var serializeRules = require('../../writer/one-time').rules; + +var Token = require('../../tokenizer/token'); + +function mergeNonAdjacentBySelector(tokens, context) { +  var specificityCache = context.cache.specificity; +  var allSelectors = {}; +  var repeatedSelectors = []; +  var i; + +  for (i = tokens.length - 1; i >= 0; i--) { +    if (tokens[i][0] != Token.RULE) +      continue; +    if (tokens[i][2].length === 0) +      continue; + +    var selector = serializeRules(tokens[i][1]); +    allSelectors[selector] = [i].concat(allSelectors[selector] || []); + +    if (allSelectors[selector].length == 2) +      repeatedSelectors.push(selector); +  } + +  for (i = repeatedSelectors.length - 1; i >= 0; i--) { +    var positions = allSelectors[repeatedSelectors[i]]; + +    selectorIterator: +    for (var j = positions.length - 1; j > 0; j--) { +      var positionOne = positions[j - 1]; +      var tokenOne = tokens[positionOne]; +      var positionTwo = positions[j]; +      var tokenTwo = tokens[positionTwo]; + +      directionIterator: +      for (var direction = 1; direction >= -1; direction -= 2) { +        var topToBottom = direction == 1; +        var from = topToBottom ? positionOne + 1 : positionTwo - 1; +        var to = topToBottom ? positionTwo : positionOne; +        var delta = topToBottom ? 1 : -1; +        var moved = topToBottom ? tokenOne : tokenTwo; +        var target = topToBottom ? tokenTwo : tokenOne; +        var movedProperties = extractProperties(moved); + +        while (from != to) { +          var traversedProperties = extractProperties(tokens[from]); +          from += delta; + +          // traversed then moved as we move selectors towards the start +          var reorderable = topToBottom ? +            canReorder(movedProperties, traversedProperties, specificityCache) : +            canReorder(traversedProperties, movedProperties, specificityCache); + +          if (!reorderable && !topToBottom) +            continue selectorIterator; +          if (!reorderable && topToBottom) +            continue directionIterator; +        } + +        if (topToBottom) { +          Array.prototype.push.apply(moved[2], target[2]); +          target[2] = moved[2]; +        } else { +          Array.prototype.push.apply(target[2], moved[2]); +        } + +        optimizeProperties(target[2], true, true, context); +        moved[2] = []; +      } +    } +  } +} + +module.exports = mergeNonAdjacentBySelector; diff --git a/node_modules/clean-css/lib/optimizer/level-2/optimize.js b/node_modules/clean-css/lib/optimizer/level-2/optimize.js new file mode 100644 index 000000000..78cd44666 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/optimize.js @@ -0,0 +1,124 @@ +var mergeAdjacent = require('./merge-adjacent'); +var mergeMediaQueries = require('./merge-media-queries'); +var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body'); +var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector'); +var reduceNonAdjacent = require('./reduce-non-adjacent'); +var removeDuplicateFontAtRules = require('./remove-duplicate-font-at-rules'); +var removeDuplicateMediaQueries = require('./remove-duplicate-media-queries'); +var removeDuplicates = require('./remove-duplicates'); +var restructure = require('./restructure'); + +var optimizeProperties = require('./properties/optimize'); + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var Token = require('../../tokenizer/token'); + +function removeEmpty(tokens) { +  for (var i = 0, l = tokens.length; i < l; i++) { +    var token = tokens[i]; +    var isEmpty = false; + +    switch (token[0]) { +      case Token.RULE: +        isEmpty = token[1].length === 0 || token[2].length === 0; +        break; +      case Token.NESTED_BLOCK: +        removeEmpty(token[2]); +        isEmpty = token[2].length === 0; +        break; +      case Token.AT_RULE_BLOCK: +        isEmpty = token[2].length === 0; +    } + +    if (isEmpty) { +      tokens.splice(i, 1); +      i--; +      l--; +    } +  } +} + +function recursivelyOptimizeBlocks(tokens, context) { +  for (var i = 0, l = tokens.length; i < l; i++) { +    var token = tokens[i]; + +    if (token[0] == Token.NESTED_BLOCK) { +      var isKeyframes = /@(-moz-|-o-|-webkit-)?keyframes/.test(token[1][0][1]); +      level2Optimize(token[2], context, !isKeyframes); +    } +  } +} + +function recursivelyOptimizeProperties(tokens, context) { +  for (var i = 0, l = tokens.length; i < l; i++) { +    var token = tokens[i]; + +    switch (token[0]) { +      case Token.RULE: +        optimizeProperties(token[2], true, true, context); +        break; +      case Token.NESTED_BLOCK: +        recursivelyOptimizeProperties(token[2], context); +    } +  } +} + +function level2Optimize(tokens, context, withRestructuring) { +  var levelOptions = context.options.level[OptimizationLevel.Two]; +  var reduced; +  var i; + +  recursivelyOptimizeBlocks(tokens, context); +  recursivelyOptimizeProperties(tokens, context); + +  if (levelOptions.removeDuplicateRules) { +    removeDuplicates(tokens, context); +  } + +  if (levelOptions.mergeAdjacentRules) { +    mergeAdjacent(tokens, context); +  } + +  if (levelOptions.reduceNonAdjacentRules) { +    reduceNonAdjacent(tokens, context); +  } + +  if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'body') { +    mergeNonAdjacentBySelector(tokens, context); +  } + +  if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'selector') { +    mergeNonAdjacentByBody(tokens, context); +  } + +  if (levelOptions.restructureRules && levelOptions.mergeAdjacentRules && withRestructuring) { +    restructure(tokens, context); +    mergeAdjacent(tokens, context); +  } + +  if (levelOptions.restructureRules && !levelOptions.mergeAdjacentRules && withRestructuring) { +    restructure(tokens, context); +  } + +  if (levelOptions.removeDuplicateFontRules) { +    removeDuplicateFontAtRules(tokens, context); +  } + +  if (levelOptions.removeDuplicateMediaBlocks) { +    removeDuplicateMediaQueries(tokens, context); +  } + +  if (levelOptions.mergeMedia) { +    reduced = mergeMediaQueries(tokens, context); +    for (i = reduced.length - 1; i >= 0; i--) { +      level2Optimize(reduced[i][2], context, false); +    } +  } + +  removeEmpty(tokens); + +  return tokens; +} + +module.exports = level2Optimize; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/every-values-pair.js b/node_modules/clean-css/lib/optimizer/level-2/properties/every-values-pair.js new file mode 100644 index 000000000..44fcb7d53 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/every-values-pair.js @@ -0,0 +1,28 @@ +var Marker = require('../../../tokenizer/marker'); + +function everyValuesPair(fn, left, right) { +  var leftSize = left.value.length; +  var rightSize = right.value.length; +  var total = Math.max(leftSize, rightSize); +  var lowerBound = Math.min(leftSize, rightSize) - 1; +  var leftValue; +  var rightValue; +  var position; + +  for (position = 0; position < total; position++) { +    leftValue = left.value[position] && left.value[position][1] || leftValue; +    rightValue = right.value[position] && right.value[position][1] || rightValue; + +    if (leftValue == Marker.COMMA || rightValue == Marker.COMMA) { +      continue; +    } + +    if (!fn(leftValue, rightValue, position, position <= lowerBound)) { +      return false; +    } +  } + +  return true; +} + +module.exports = everyValuesPair; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/find-component-in.js b/node_modules/clean-css/lib/optimizer/level-2/properties/find-component-in.js new file mode 100644 index 000000000..dd7c719d5 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/find-component-in.js @@ -0,0 +1,40 @@ +var compactable = require('../compactable'); + +function findComponentIn(shorthand, longhand) { +  var comparator = nameComparator(longhand); + +  return findInDirectComponents(shorthand, comparator) || findInSubComponents(shorthand, comparator); +} + +function nameComparator(to) { +  return function (property) { +    return to.name === property.name; +  }; +} + +function findInDirectComponents(shorthand, comparator) { +  return shorthand.components.filter(comparator)[0]; +} + +function findInSubComponents(shorthand, comparator) { +  var shorthandComponent; +  var longhandMatch; +  var i, l; + +  if (!compactable[shorthand.name].shorthandComponents) { +    return; +  } + +  for (i = 0, l = shorthand.components.length; i < l; i++) { +    shorthandComponent = shorthand.components[i]; +    longhandMatch = findInDirectComponents(shorthandComponent, comparator); + +    if (longhandMatch) { +      return longhandMatch; +    } +  } + +  return; +} + +module.exports = findComponentIn; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/has-inherit.js b/node_modules/clean-css/lib/optimizer/level-2/properties/has-inherit.js new file mode 100644 index 000000000..84f220d32 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/has-inherit.js @@ -0,0 +1,10 @@ +function hasInherit(property) { +  for (var i = property.value.length - 1; i >= 0; i--) { +    if (property.value[i][1] == 'inherit') +      return true; +  } + +  return false; +} + +module.exports = hasInherit; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/is-component-of.js b/node_modules/clean-css/lib/optimizer/level-2/properties/is-component-of.js new file mode 100644 index 000000000..237de7d10 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/is-component-of.js @@ -0,0 +1,22 @@ +var compactable = require('../compactable'); + +function isComponentOf(property1, property2, shallow) { +  return isDirectComponentOf(property1, property2) || +    !shallow && !!compactable[property1.name].shorthandComponents && isSubComponentOf(property1, property2); +} + +function isDirectComponentOf(property1, property2) { +  var descriptor = compactable[property1.name]; + +  return 'components' in descriptor && descriptor.components.indexOf(property2.name) > -1; +} + +function isSubComponentOf(property1, property2) { +  return property1 +    .components +    .some(function (component) { +      return isDirectComponentOf(component, property2); +    }); +} + +module.exports = isComponentOf; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/merge-into-shorthands.js b/node_modules/clean-css/lib/optimizer/level-2/properties/merge-into-shorthands.js new file mode 100644 index 000000000..a75c4ed63 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/merge-into-shorthands.js @@ -0,0 +1,157 @@ +var everyValuesPair = require('./every-values-pair'); +var hasInherit = require('./has-inherit'); +var populateComponents = require('./populate-components'); + +var compactable = require('../compactable'); +var deepClone = require('../clone').deep; + +var wrapSingle = require('../../wrap-for-optimizing').single; + +var Token = require('../../../tokenizer/token'); + +function mixedImportance(components) { +  var important; + +  for (var name in components) { +    if (undefined !== important && components[name].important != important) +      return true; + +    important = components[name].important; +  } + +  return false; +} + +function joinMetadata(components, at) { +  var metadata = []; +  var component; +  var originalValue; +  var componentMetadata; +  var name; + +  for (name in components) { +    component = components[name]; +    originalValue = component.all[component.position]; +    componentMetadata = originalValue[at][originalValue[at].length - 1]; + +    Array.prototype.push.apply(metadata, componentMetadata); +  } + +  return metadata; +} + +function replaceWithShorthand(properties, candidateComponents, name, validator) { +  var descriptor = compactable[name]; +  var nameMetadata; +  var valueMetadata; +  var newValuePlaceholder = [ +    Token.PROPERTY, +    [Token.PROPERTY_NAME, name], +    [Token.PROPERTY_VALUE, descriptor.defaultValue] +  ]; +  var mayOverride; +  var all; + +  var newProperty = wrapSingle(newValuePlaceholder); +  newProperty.shorthand = true; +  newProperty.dirty = true; + +  populateComponents([newProperty], validator, []); + +  for (var i = 0, l = descriptor.components.length; i < l; i++) { +    var component = candidateComponents[descriptor.components[i]]; + +    if (hasInherit(component)) +      return; + +    mayOverride = compactable[component.name].canOverride; +    if (!everyValuesPair(mayOverride.bind(null, validator), newProperty.components[i], component)) +      return; + +    newProperty.components[i] = deepClone(component); +    newProperty.important = component.important; + +    all = component.all; +  } + +  for (var componentName in candidateComponents) { +    candidateComponents[componentName].unused = true; +  } + +  nameMetadata = joinMetadata(candidateComponents, 1); +  newValuePlaceholder[1].push(nameMetadata); + +  valueMetadata = joinMetadata(candidateComponents, 2); +  newValuePlaceholder[2].push(valueMetadata); + +  newProperty.position = all.length; +  newProperty.all = all; +  newProperty.all.push(newValuePlaceholder); + +  properties.push(newProperty); +} + +function invalidateOrCompact(properties, position, candidates, validator) { +  var property = properties[position]; + +  for (var name in candidates) { +    if (undefined !== property && name == property.name) +      continue; + +    var descriptor = compactable[name]; +    var candidateComponents = candidates[name]; +    if (descriptor.components.length > Object.keys(candidateComponents).length) { +      delete candidates[name]; +      continue; +    } + +    if (mixedImportance(candidateComponents)) +      continue; + +    replaceWithShorthand(properties, candidateComponents, name, validator); +  } +} + +function mergeIntoShorthands(properties, validator) { +  var candidates = {}; +  var descriptor; +  var componentOf; +  var property; +  var i, l; +  var j, m; + +  if (properties.length < 3) +    return; + +  for (i = 0, l = properties.length; i < l; i++) { +    property = properties[i]; + +    if (property.unused) +      continue; + +    if (property.hack) +      continue; + +    if (property.block) +      continue; + +    descriptor = compactable[property.name]; +    if (!descriptor || !descriptor.componentOf) +      continue; + +    if (property.shorthand) { +      invalidateOrCompact(properties, i, candidates, validator); +    } else { +      for (j = 0, m = descriptor.componentOf.length; j < m; j++) { +        componentOf = descriptor.componentOf[j]; + +        candidates[componentOf] = candidates[componentOf] || {}; +        candidates[componentOf][property.name] = property; +      } +    } +  } + +  invalidateOrCompact(properties, i, candidates, validator); +} + +module.exports = mergeIntoShorthands; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js b/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js new file mode 100644 index 000000000..250b05fc4 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js @@ -0,0 +1,39 @@ +var mergeIntoShorthands = require('./merge-into-shorthands'); +var overrideProperties = require('./override-properties'); +var populateComponents = require('./populate-components'); + +var restoreWithComponents = require('../restore-with-components'); + +var wrapForOptimizing = require('../../wrap-for-optimizing').all; +var removeUnused = require('../../remove-unused'); +var restoreFromOptimizing = require('../../restore-from-optimizing'); + +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +function optimizeProperties(properties, withOverriding, withMerging, context) { +  var _properties = wrapForOptimizing(properties, false); +  var _property; +  var i, l; + +  populateComponents(_properties, context.validator, context.warnings); + +  for (i = 0, l = _properties.length; i < l; i++) { +    _property = _properties[i]; +    if (_property.block) { +      optimizeProperties(_property.value[0][1], withOverriding, withMerging, context); +    } +  } + +  if (withOverriding && context.options.level[OptimizationLevel.Two].overrideProperties) { +    overrideProperties(_properties, withMerging, context.options.compatibility, context.validator); +  } + +  if (withMerging && context.options.level[OptimizationLevel.Two].mergeIntoShorthands) { +    mergeIntoShorthands(_properties, context.validator); +  } + +  restoreFromOptimizing(_properties, restoreWithComponents); +  removeUnused(_properties); +} + +module.exports = optimizeProperties; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/override-properties.js b/node_modules/clean-css/lib/optimizer/level-2/properties/override-properties.js new file mode 100644 index 000000000..3c9d8d2af --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/override-properties.js @@ -0,0 +1,441 @@ +var hasInherit = require('./has-inherit'); +var everyValuesPair = require('./every-values-pair'); +var findComponentIn = require('./find-component-in'); +var isComponentOf = require('./is-component-of'); +var overridesNonComponentShorthand = require('./overrides-non-component-shorthand'); +var sameVendorPrefixesIn = require('./vendor-prefixes').same; + +var compactable = require('../compactable'); +var deepClone = require('../clone').deep; +var deepClone = require('../clone').deep; +var restoreWithComponents = require('../restore-with-components'); +var shallowClone = require('../clone').shallow; + +var restoreFromOptimizing = require('../../restore-from-optimizing'); + +var Token = require('../../../tokenizer/token'); +var Marker = require('../../../tokenizer/marker'); + +var serializeProperty = require('../../../writer/one-time').property; + +function wouldBreakCompatibility(property, validator) { +  for (var i = 0; i < property.components.length; i++) { +    var component = property.components[i]; +    var descriptor = compactable[component.name]; +    var canOverride = descriptor && descriptor.canOverride || canOverride.sameValue; + +    var _component = shallowClone(component); +    _component.value = [[Token.PROPERTY_VALUE, descriptor.defaultValue]]; + +    if (!everyValuesPair(canOverride.bind(null, validator), _component, component)) { +      return true; +    } +  } + +  return false; +} + +function overrideIntoMultiplex(property, by) { +  by.unused = true; + +  turnIntoMultiplex(by, multiplexSize(property)); +  property.value = by.value; +} + +function overrideByMultiplex(property, by) { +  by.unused = true; +  property.multiplex = true; +  property.value = by.value; +} + +function overrideSimple(property, by) { +  by.unused = true; +  property.value = by.value; +} + +function override(property, by) { +  if (by.multiplex) +    overrideByMultiplex(property, by); +  else if (property.multiplex) +    overrideIntoMultiplex(property, by); +  else +    overrideSimple(property, by); +} + +function overrideShorthand(property, by) { +  by.unused = true; + +  for (var i = 0, l = property.components.length; i < l; i++) { +    override(property.components[i], by.components[i], property.multiplex); +  } +} + +function turnIntoMultiplex(property, size) { +  property.multiplex = true; + +  for (var i = 0, l = property.components.length; i < l; i++) { +    var component = property.components[i]; +    if (component.multiplex) +      continue; + +    var value = component.value.slice(0); + +    for (var j = 1; j < size; j++) { +      component.value.push([Token.PROPERTY_VALUE, Marker.COMMA]); +      Array.prototype.push.apply(component.value, value); +    } +  } +} + +function multiplexSize(component) { +  var size = 0; + +  for (var i = 0, l = component.value.length; i < l; i++) { +    if (component.value[i][1] == Marker.COMMA) +      size++; +  } + +  return size + 1; +} + +function lengthOf(property) { +  var fakeAsArray = [ +    Token.PROPERTY, +    [Token.PROPERTY_NAME, property.name] +  ].concat(property.value); +  return serializeProperty([fakeAsArray], 0).length; +} + +function moreSameShorthands(properties, startAt, name) { +  // Since we run the main loop in `compactOverrides` backwards, at this point some +  // properties may not be marked as unused. +  // We should consider reverting the order if possible +  var count = 0; + +  for (var i = startAt; i >= 0; i--) { +    if (properties[i].name == name && !properties[i].unused) +      count++; +    if (count > 1) +      break; +  } + +  return count > 1; +} + +function overridingFunction(shorthand, validator) { +  for (var i = 0, l = shorthand.components.length; i < l; i++) { +    if (anyValue(validator.isValidFunction, shorthand.components[i])) +      return true; +  } + +  return false; +} + +function anyValue(fn, property) { +  for (var i = 0, l = property.value.length; i < l; i++) { +    if (property.value[i][1] == Marker.COMMA) +      continue; + +    if (fn(property.value[i][1])) +      return true; +  } + +  return false; +} + +function wouldResultInLongerValue(left, right) { +  if (!left.multiplex && !right.multiplex || left.multiplex && right.multiplex) +    return false; + +  var multiplex = left.multiplex ? left : right; +  var simple = left.multiplex ? right : left; +  var component; + +  var multiplexClone = deepClone(multiplex); +  restoreFromOptimizing([multiplexClone], restoreWithComponents); + +  var simpleClone = deepClone(simple); +  restoreFromOptimizing([simpleClone], restoreWithComponents); + +  var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone); + +  if (left.multiplex) { +    component = findComponentIn(multiplexClone, simpleClone); +    overrideIntoMultiplex(component, simpleClone); +  } else { +    component = findComponentIn(simpleClone, multiplexClone); +    turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone)); +    overrideByMultiplex(component, multiplexClone); +  } + +  restoreFromOptimizing([simpleClone], restoreWithComponents); + +  var lengthAfter = lengthOf(simpleClone); + +  return lengthBefore <= lengthAfter; +} + +function isCompactable(property) { +  return property.name in compactable; +} + +function noneOverrideHack(left, right) { +  return !left.multiplex && +    (left.name == 'background' || left.name == 'background-image') && +    right.multiplex && +    (right.name == 'background' || right.name == 'background-image') && +    anyLayerIsNone(right.value); +} + +function anyLayerIsNone(values) { +  var layers = intoLayers(values); + +  for (var i = 0, l = layers.length; i < l; i++) { +    if (layers[i].length == 1 && layers[i][0][1] == 'none') +      return true; +  } + +  return false; +} + +function intoLayers(values) { +  var layers = []; + +  for (var i = 0, layer = [], l = values.length; i < l; i++) { +    var value = values[i]; +    if (value[1] == Marker.COMMA) { +      layers.push(layer); +      layer = []; +    } else { +      layer.push(value); +    } +  } + +  layers.push(layer); +  return layers; +} + +function overrideProperties(properties, withMerging, compatibility, validator) { +  var mayOverride, right, left, component; +  var overriddenComponents; +  var overriddenComponent; +  var overridingComponent; +  var overridable; +  var i, j, k; + +  propertyLoop: +  for (i = properties.length - 1; i >= 0; i--) { +    right = properties[i]; + +    if (!isCompactable(right)) +      continue; + +    if (right.block) +      continue; + +    mayOverride = compactable[right.name].canOverride; + +    traverseLoop: +    for (j = i - 1; j >= 0; j--) { +      left = properties[j]; + +      if (!isCompactable(left)) +        continue; + +      if (left.block) +        continue; + +      if (left.unused || right.unused) +        continue; + +      if (left.hack && !right.hack && !right.important || !left.hack && !left.important && right.hack) +        continue; + +      if (left.important == right.important && left.hack[0] != right.hack[0]) +        continue; + +      if (left.important == right.important && (left.hack[0] != right.hack[0] || (left.hack[1] && left.hack[1] != right.hack[1]))) +        continue; + +      if (hasInherit(right)) +        continue; + +      if (noneOverrideHack(left, right)) +        continue; + +      if (right.shorthand && isComponentOf(right, left)) { +        // maybe `left` can be overridden by `right` which is a shorthand? +        if (!right.important && left.important) +          continue; + +        if (!sameVendorPrefixesIn([left], right.components)) +          continue; + +        if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) +          continue; + +        component = findComponentIn(right, left); +        mayOverride = compactable[left.name].canOverride; +        if (everyValuesPair(mayOverride.bind(null, validator), left, component)) { +          left.unused = true; +        } +      } else if (right.shorthand && overridesNonComponentShorthand(right, left)) { +        // `right` is a shorthand while `left` can be overriden by it, think `border` and `border-top` +        if (!right.important && left.important) { +          continue; +        } + +        if (!sameVendorPrefixesIn([left], right.components)) { +          continue; +        } + +        if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) { +          continue; +        } + +        overriddenComponents = left.shorthand ? +          left.components: +          [left]; + +        for (k = overriddenComponents.length - 1; k >= 0; k--) { +          overriddenComponent = overriddenComponents[k]; +          overridingComponent = findComponentIn(right, overriddenComponent); +          mayOverride = compactable[overriddenComponent.name].canOverride; + +          if (!everyValuesPair(mayOverride.bind(null, validator), left, overridingComponent)) { +            continue traverseLoop; +          } +        } + +        left.unused = true; +      } else if (withMerging && left.shorthand && !right.shorthand && isComponentOf(left, right, true)) { +        // maybe `right` can be pulled into `left` which is a shorthand? +        if (right.important && !left.important) +          continue; + +        if (!right.important && left.important) { +          right.unused = true; +          continue; +        } + +        // Pending more clever algorithm in #527 +        if (moreSameShorthands(properties, i - 1, left.name)) +          continue; + +        if (overridingFunction(left, validator)) +          continue; + +        component = findComponentIn(left, right); +        if (everyValuesPair(mayOverride.bind(null, validator), component, right)) { +          var disabledBackgroundMerging = +            !compatibility.properties.backgroundClipMerging && component.name.indexOf('background-clip') > -1 || +            !compatibility.properties.backgroundOriginMerging && component.name.indexOf('background-origin') > -1 || +            !compatibility.properties.backgroundSizeMerging && component.name.indexOf('background-size') > -1; +          var nonMergeableValue = compactable[right.name].nonMergeableValue === right.value[0][1]; + +          if (disabledBackgroundMerging || nonMergeableValue) +            continue; + +          if (!compatibility.properties.merging && wouldBreakCompatibility(left, validator)) +            continue; + +          if (component.value[0][1] != right.value[0][1] && (hasInherit(left) || hasInherit(right))) +            continue; + +          if (wouldResultInLongerValue(left, right)) +            continue; + +          if (!left.multiplex && right.multiplex) +            turnIntoMultiplex(left, multiplexSize(right)); + +          override(component, right); +          left.dirty = true; +        } +      } else if (withMerging && left.shorthand && right.shorthand && left.name == right.name) { +        // merge if all components can be merged + +        if (!left.multiplex && right.multiplex) +          continue; + +        if (!right.important && left.important) { +          right.unused = true; +          continue propertyLoop; +        } + +        if (right.important && !left.important) { +          left.unused = true; +          continue; +        } + +        for (k = left.components.length - 1; k >= 0; k--) { +          var leftComponent = left.components[k]; +          var rightComponent = right.components[k]; + +          mayOverride = compactable[leftComponent.name].canOverride; +          if (!everyValuesPair(mayOverride.bind(null, validator), leftComponent, rightComponent)) +            continue propertyLoop; +        } + +        overrideShorthand(left, right); +        left.dirty = true; +      } else if (withMerging && left.shorthand && right.shorthand && isComponentOf(left, right)) { +        // border is a shorthand but any of its components is a shorthand too + +        if (!left.important && right.important) +          continue; + +        component = findComponentIn(left, right); +        mayOverride = compactable[right.name].canOverride; +        if (!everyValuesPair(mayOverride.bind(null, validator), component, right)) +          continue; + +        if (left.important && !right.important) { +          right.unused = true; +          continue; +        } + +        var rightRestored = compactable[right.name].restore(right, compactable); +        if (rightRestored.length > 1) +          continue; + +        component = findComponentIn(left, right); +        override(component, right); +        right.dirty = true; +      } else if (left.name == right.name) { +        // two non-shorthands should be merged based on understandability +        overridable = true; + +        if (right.shorthand) { +          for (k = right.components.length - 1; k >= 0 && overridable; k--) { +            overriddenComponent = left.components[k]; +            overridingComponent = right.components[k]; +            mayOverride = compactable[overridingComponent.name].canOverride; + +            overridable = overridable && everyValuesPair(mayOverride.bind(null, validator), overriddenComponent, overridingComponent); +          } +        } else { +          mayOverride = compactable[right.name].canOverride; +          overridable = everyValuesPair(mayOverride.bind(null, validator), left, right); +        } + +        if (left.important && !right.important && overridable) { +          right.unused = true; +          continue; +        } + +        if (!left.important && right.important && overridable) { +          left.unused = true; +          continue; +        } + +        if (!overridable) { +          continue; +        } + +        left.unused = true; +      } +    } +  } +} + +module.exports = overrideProperties; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/overrides-non-component-shorthand.js b/node_modules/clean-css/lib/optimizer/level-2/properties/overrides-non-component-shorthand.js new file mode 100644 index 000000000..c385218a9 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/overrides-non-component-shorthand.js @@ -0,0 +1,9 @@ +var compactable = require('../compactable'); + +function overridesNonComponentShorthand(property1, property2) { +  return property1.name in compactable && +    'overridesShorthands' in compactable[property1.name] && +    compactable[property1.name].overridesShorthands.indexOf(property2.name) > -1; +} + +module.exports = overridesNonComponentShorthand; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/populate-components.js b/node_modules/clean-css/lib/optimizer/level-2/properties/populate-components.js new file mode 100644 index 000000000..c587e83c4 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/populate-components.js @@ -0,0 +1,42 @@ +var compactable = require('../compactable'); +var InvalidPropertyError = require('../invalid-property-error'); + +function populateComponents(properties, validator, warnings) { +  var component; +  var j, m; + +  for (var i = properties.length - 1; i >= 0; i--) { +    var property = properties[i]; +    var descriptor = compactable[property.name]; + +    if (descriptor && descriptor.shorthand) { +      property.shorthand = true; +      property.dirty = true; + +      try { +        property.components = descriptor.breakUp(property, compactable, validator); + +        if (descriptor.shorthandComponents) { +          for (j = 0, m = property.components.length; j < m; j++) { +            component = property.components[j]; +            component.components = compactable[component.name].breakUp(component, compactable, validator); +          } +        } +      } catch (e) { +        if (e instanceof InvalidPropertyError) { +          property.components = []; // this will set property.unused to true below +          warnings.push(e.message); +        } else { +          throw e; +        } +      } + +      if (property.components.length > 0) +        property.multiplex = property.components[0].multiplex; +      else +        property.unused = true; +    } +  } +} + +module.exports = populateComponents; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js b/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js new file mode 100644 index 000000000..6c77db50d --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js @@ -0,0 +1,15 @@ +var sameVendorPrefixes = require('./vendor-prefixes').same; + +function understandable(validator, value1, value2, _position, isPaired) { +  if (!sameVendorPrefixes(value1, value2)) { +    return false; +  } + +  if (isPaired && validator.isValidVariable(value1) !== validator.isValidVariable(value2)) { +    return false; +  } + +  return true; +} + +module.exports = understandable; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/vendor-prefixes.js b/node_modules/clean-css/lib/optimizer/level-2/properties/vendor-prefixes.js new file mode 100644 index 000000000..f9ab52728 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/vendor-prefixes.js @@ -0,0 +1,23 @@ +var VENDOR_PREFIX_PATTERN = /(?:^|\W)(\-\w+\-)/g; + +function unique(value) { +  var prefixes = []; +  var match; + +  while ((match = VENDOR_PREFIX_PATTERN.exec(value)) !== null) { +    if (prefixes.indexOf(match[0]) == -1) { +      prefixes.push(match[0]); +    } +  } + +  return prefixes; +} + +function same(value1, value2) { +  return unique(value1).sort().join(',') == unique(value2).sort().join(','); +} + +module.exports = { +  unique: unique, +  same: same +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/reduce-non-adjacent.js b/node_modules/clean-css/lib/optimizer/level-2/reduce-non-adjacent.js new file mode 100644 index 000000000..3461bb268 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/reduce-non-adjacent.js @@ -0,0 +1,178 @@ +var isMergeable = require('./is-mergeable'); + +var optimizeProperties = require('./properties/optimize'); + +var cloneArray = require('../../utils/clone-array'); + +var Token = require('../../tokenizer/token'); + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +function reduceNonAdjacent(tokens, context) { +  var options = context.options; +  var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; +  var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; +  var candidates = {}; +  var repeated = []; + +  for (var i = tokens.length - 1; i >= 0; i--) { +    var token = tokens[i]; + +    if (token[0] != Token.RULE) { +      continue; +    } else if (token[2].length === 0) { +      continue; +    } + +    var selectorAsString = serializeRules(token[1]); +    var isComplexAndNotSpecial = token[1].length > 1 && +      isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements); +    var wrappedSelectors = wrappedSelectorsFrom(token[1]); +    var selectors = isComplexAndNotSpecial ? +      [selectorAsString].concat(wrappedSelectors) : +      [selectorAsString]; + +    for (var j = 0, m = selectors.length; j < m; j++) { +      var selector = selectors[j]; + +      if (!candidates[selector]) +        candidates[selector] = []; +      else +        repeated.push(selector); + +      candidates[selector].push({ +        where: i, +        list: wrappedSelectors, +        isPartial: isComplexAndNotSpecial && j > 0, +        isComplex: isComplexAndNotSpecial && j === 0 +      }); +    } +  } + +  reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context); +  reduceComplexNonAdjacentCases(tokens, candidates, options, context); +} + +function wrappedSelectorsFrom(list) { +  var wrapped = []; + +  for (var i = 0; i < list.length; i++) { +    wrapped.push([list[i][1]]); +  } + +  return wrapped; +} + +function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context) { +  function filterOut(idx, bodies) { +    return data[idx].isPartial && bodies.length === 0; +  } + +  function reduceBody(token, newBody, processedCount, tokenIdx) { +    if (!data[processedCount - tokenIdx - 1].isPartial) +      token[2] = newBody; +  } + +  for (var i = 0, l = repeated.length; i < l; i++) { +    var selector = repeated[i]; +    var data = candidates[selector]; + +    reduceSelector(tokens, data, { +      filterOut: filterOut, +      callback: reduceBody +    }, options, context); +  } +} + +function reduceComplexNonAdjacentCases(tokens, candidates, options, context) { +  var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; +  var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; +  var localContext = {}; + +  function filterOut(idx) { +    return localContext.data[idx].where < localContext.intoPosition; +  } + +  function collectReducedBodies(token, newBody, processedCount, tokenIdx) { +    if (tokenIdx === 0) +      localContext.reducedBodies.push(newBody); +  } + +  allSelectors: +  for (var complexSelector in candidates) { +    var into = candidates[complexSelector]; +    if (!into[0].isComplex) +      continue; + +    var intoPosition = into[into.length - 1].where; +    var intoToken = tokens[intoPosition]; +    var reducedBodies = []; + +    var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements) ? +      into[0].list : +      [complexSelector]; + +    localContext.intoPosition = intoPosition; +    localContext.reducedBodies = reducedBodies; + +    for (var j = 0, m = selectors.length; j < m; j++) { +      var selector = selectors[j]; +      var data = candidates[selector]; + +      if (data.length < 2) +        continue allSelectors; + +      localContext.data = data; + +      reduceSelector(tokens, data, { +        filterOut: filterOut, +        callback: collectReducedBodies +      }, options, context); + +      if (serializeBody(reducedBodies[reducedBodies.length - 1]) != serializeBody(reducedBodies[0])) +        continue allSelectors; +    } + +    intoToken[2] = reducedBodies[0]; +  } +} + +function reduceSelector(tokens, data, context, options, outerContext) { +  var bodies = []; +  var bodiesAsList = []; +  var processedTokens = []; + +  for (var j = data.length - 1; j >= 0; j--) { +    if (context.filterOut(j, bodies)) +      continue; + +    var where = data[j].where; +    var token = tokens[where]; +    var clonedBody = cloneArray(token[2]); + +    bodies = bodies.concat(clonedBody); +    bodiesAsList.push(clonedBody); +    processedTokens.push(where); +  } + +  optimizeProperties(bodies, true, false, outerContext); + +  var processedCount = processedTokens.length; +  var propertyIdx = bodies.length - 1; +  var tokenIdx = processedCount - 1; + +  while (tokenIdx >= 0) { +     if ((tokenIdx === 0 || (bodies[propertyIdx] && bodiesAsList[tokenIdx].indexOf(bodies[propertyIdx]) > -1)) && propertyIdx > -1) { +      propertyIdx--; +      continue; +    } + +    var newBody = bodies.splice(propertyIdx + 1); +    context.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx); + +    tokenIdx--; +  } +} + +module.exports = reduceNonAdjacent; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-font-at-rules.js b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-font-at-rules.js new file mode 100644 index 000000000..bc85d5d67 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-font-at-rules.js @@ -0,0 +1,30 @@ +var Token = require('../../tokenizer/token'); + +var serializeAll = require('../../writer/one-time').all; + +var FONT_FACE_SCOPE = '@font-face'; + +function removeDuplicateFontAtRules(tokens) { +  var fontAtRules = []; +  var token; +  var key; +  var i, l; + +  for (i = 0, l = tokens.length; i < l; i++) { +    token = tokens[i]; + +    if (token[0] != Token.AT_RULE_BLOCK && token[1][0][1] != FONT_FACE_SCOPE) { +      continue; +    } + +    key = serializeAll([token]); + +    if (fontAtRules.indexOf(key) > -1) { +      token[2] = []; +    } else { +      fontAtRules.push(key); +    } +  } +} + +module.exports = removeDuplicateFontAtRules; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-media-queries.js b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-media-queries.js new file mode 100644 index 000000000..2c8f31d9d --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-media-queries.js @@ -0,0 +1,30 @@ +var Token = require('../../tokenizer/token'); + +var serializeAll = require('../../writer/one-time').all; +var serializeRules = require('../../writer/one-time').rules; + +function removeDuplicateMediaQueries(tokens) { +  var candidates = {}; +  var candidate; +  var token; +  var key; +  var i, l; + +  for (i = 0, l = tokens.length; i < l; i++) { +    token = tokens[i]; +    if (token[0] != Token.NESTED_BLOCK) { +      continue; +    } + +    key = serializeRules(token[1]) + '%' + serializeAll(token[2]); +    candidate = candidates[key]; + +    if (candidate) { +      candidate[2] = []; +    } + +    candidates[key] = token; +  } +} + +module.exports = removeDuplicateMediaQueries; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-duplicates.js b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicates.js new file mode 100644 index 000000000..9aa6ace93 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicates.js @@ -0,0 +1,43 @@ +var Token = require('../../tokenizer/token'); + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +function removeDuplicates(tokens) { +  var matched = {}; +  var moreThanOnce = []; +  var id, token; +  var body, bodies; + +  for (var i = 0, l = tokens.length; i < l; i++) { +    token = tokens[i]; +    if (token[0] != Token.RULE) +      continue; + +    id = serializeRules(token[1]); + +    if (matched[id] && matched[id].length == 1) +      moreThanOnce.push(id); +    else +      matched[id] = matched[id] || []; + +    matched[id].push(i); +  } + +  for (i = 0, l = moreThanOnce.length; i < l; i++) { +    id = moreThanOnce[i]; +    bodies = []; + +    for (var j = matched[id].length - 1; j >= 0; j--) { +      token = tokens[matched[id][j]]; +      body = serializeBody(token[2]); + +      if (bodies.indexOf(body) > -1) +        token[2] = []; +      else +        bodies.push(body); +    } +  } +} + +module.exports = removeDuplicates; diff --git a/node_modules/clean-css/lib/optimizer/level-2/reorderable.js b/node_modules/clean-css/lib/optimizer/level-2/reorderable.js new file mode 100644 index 000000000..4a3747a08 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/reorderable.js @@ -0,0 +1,93 @@ +// TODO: it'd be great to merge it with the other canReorder functionality + +var rulesOverlap = require('./rules-overlap'); +var specificitiesOverlap = require('./specificities-overlap'); + +var FLEX_PROPERTIES = /align\-items|box\-align|box\-pack|flex|justify/; +var BORDER_PROPERTIES = /^border\-(top|right|bottom|left|color|style|width|radius)/; + +function canReorder(left, right, cache) { +  for (var i = right.length - 1; i >= 0; i--) { +    for (var j = left.length - 1; j >= 0; j--) { +      if (!canReorderSingle(left[j], right[i], cache)) +        return false; +    } +  } + +  return true; +} + +function canReorderSingle(left, right, cache) { +  var leftName = left[0]; +  var leftValue = left[1]; +  var leftNameRoot = left[2]; +  var leftSelector = left[5]; +  var leftInSpecificSelector = left[6]; +  var rightName = right[0]; +  var rightValue = right[1]; +  var rightNameRoot = right[2]; +  var rightSelector = right[5]; +  var rightInSpecificSelector = right[6]; + +  if (leftName == 'font' && rightName == 'line-height' || rightName == 'font' && leftName == 'line-height') +    return false; +  if (FLEX_PROPERTIES.test(leftName) && FLEX_PROPERTIES.test(rightName)) +    return false; +  if (leftNameRoot == rightNameRoot && unprefixed(leftName) == unprefixed(rightName) && (vendorPrefixed(leftName) ^ vendorPrefixed(rightName))) +    return false; +  if (leftNameRoot == 'border' && BORDER_PROPERTIES.test(rightNameRoot) && (leftName == 'border' || leftName == rightNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName)))) +    return false; +  if (rightNameRoot == 'border' && BORDER_PROPERTIES.test(leftNameRoot) && (rightName == 'border' || rightName == leftNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName)))) +    return false; +  if (leftNameRoot == 'border' && rightNameRoot == 'border' && leftName != rightName && (isSideBorder(leftName) && isStyleBorder(rightName) || isStyleBorder(leftName) && isSideBorder(rightName))) +    return false; +  if (leftNameRoot != rightNameRoot) +    return true; +  if (leftName == rightName && leftNameRoot == rightNameRoot && (leftValue == rightValue || withDifferentVendorPrefix(leftValue, rightValue))) +    return true; +  if (leftName != rightName && leftNameRoot == rightNameRoot && leftName != leftNameRoot && rightName != rightNameRoot) +    return true; +  if (leftName != rightName && leftNameRoot == rightNameRoot && leftValue == rightValue) +    return true; +  if (rightInSpecificSelector && leftInSpecificSelector && !inheritable(leftNameRoot) && !inheritable(rightNameRoot) && !rulesOverlap(rightSelector, leftSelector, false)) +    return true; +  if (!specificitiesOverlap(leftSelector, rightSelector, cache)) +    return true; + +  return false; +} + +function vendorPrefixed(name) { +  return /^\-(?:moz|webkit|ms|o)\-/.test(name); +} + +function unprefixed(name) { +  return name.replace(/^\-(?:moz|webkit|ms|o)\-/, ''); +} + +function sameBorderComponent(name1, name2) { +  return name1.split('-').pop() == name2.split('-').pop(); +} + +function isSideBorder(name) { +  return name == 'border-top' || name == 'border-right' || name == 'border-bottom' || name == 'border-left'; +} + +function isStyleBorder(name) { +  return name == 'border-color' || name == 'border-style' || name == 'border-width'; +} + +function withDifferentVendorPrefix(value1, value2) { +  return vendorPrefixed(value1) && vendorPrefixed(value2) && value1.split('-')[1] != value2.split('-')[2]; +} + +function inheritable(name) { +  // According to http://www.w3.org/TR/CSS21/propidx.html +  // Others will be catched by other, preceeding rules +  return name == 'font' || name == 'line-height' || name == 'list-style'; +} + +module.exports = { +  canReorder: canReorder, +  canReorderSingle: canReorderSingle +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restore-with-components.js b/node_modules/clean-css/lib/optimizer/level-2/restore-with-components.js new file mode 100644 index 000000000..caf7c4c94 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/restore-with-components.js @@ -0,0 +1,13 @@ +var compactable = require('./compactable'); + +function restoreWithComponents(property) { +  var descriptor = compactable[property.name]; + +  if (descriptor && descriptor.shorthand) { +    return descriptor.restore(property, compactable); +  } else { +    return property.value; +  } +} + +module.exports = restoreWithComponents; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restore.js b/node_modules/clean-css/lib/optimizer/level-2/restore.js new file mode 100644 index 000000000..149688c5b --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/restore.js @@ -0,0 +1,233 @@ +var shallowClone = require('./clone').shallow; + +var Token = require('../../tokenizer/token'); +var Marker = require('../../tokenizer/marker'); + +function isInheritOnly(values) { +  for (var i = 0, l = values.length; i < l; i++) { +    var value = values[i][1]; + +    if (value != 'inherit' && value != Marker.COMMA && value != Marker.FORWARD_SLASH) +      return false; +  } + +  return true; +} + +function background(property, compactable, lastInMultiplex) { +  var components = property.components; +  var restored = []; +  var needsOne, needsBoth; + +  function restoreValue(component) { +    Array.prototype.unshift.apply(restored, component.value); +  } + +  function isDefaultValue(component) { +    var descriptor = compactable[component.name]; + +    if (descriptor.doubleValues && descriptor.defaultValue.length == 1) { +      return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] == descriptor.defaultValue[0] : true); +    } else if (descriptor.doubleValues && descriptor.defaultValue.length != 1) { +      return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] : component.value[0][1]) == descriptor.defaultValue[1]; +    } else { +      return component.value[0][1] == descriptor.defaultValue; +    } +  } + +  for (var i = components.length - 1; i >= 0; i--) { +    var component = components[i]; +    var isDefault = isDefaultValue(component); + +    if (component.name == 'background-clip') { +      var originComponent = components[i - 1]; +      var isOriginDefault = isDefaultValue(originComponent); + +      needsOne = component.value[0][1] == originComponent.value[0][1]; + +      needsBoth = !needsOne && ( +        (isOriginDefault && !isDefault) || +        (!isOriginDefault && !isDefault) || +        (!isOriginDefault && isDefault && component.value[0][1] != originComponent.value[0][1])); + +      if (needsOne) { +        restoreValue(originComponent); +      } else if (needsBoth) { +        restoreValue(component); +        restoreValue(originComponent); +      } + +      i--; +    } else if (component.name == 'background-size') { +      var positionComponent = components[i - 1]; +      var isPositionDefault = isDefaultValue(positionComponent); + +      needsOne = !isPositionDefault && isDefault; + +      needsBoth = !needsOne && +        (isPositionDefault && !isDefault || !isPositionDefault && !isDefault); + +      if (needsOne) { +        restoreValue(positionComponent); +      } else if (needsBoth) { +        restoreValue(component); +        restored.unshift([Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]); +        restoreValue(positionComponent); +      } else if (positionComponent.value.length == 1) { +        restoreValue(positionComponent); +      } + +      i--; +    } else { +      if (isDefault || compactable[component.name].multiplexLastOnly && !lastInMultiplex) +        continue; + +      restoreValue(component); +    } +  } + +  if (restored.length === 0 && property.value.length == 1 && property.value[0][1] == '0') +    restored.push(property.value[0]); + +  if (restored.length === 0) +    restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]); + +  if (isInheritOnly(restored)) +    return [restored[0]]; + +  return restored; +} + +function borderRadius(property, compactable) { +  if (property.multiplex) { +    var horizontal = shallowClone(property); +    var vertical = shallowClone(property); + +    for (var i = 0; i < 4; i++) { +      var component = property.components[i]; + +      var horizontalComponent = shallowClone(property); +      horizontalComponent.value = [component.value[0]]; +      horizontal.components.push(horizontalComponent); + +      var verticalComponent = shallowClone(property); +      // FIXME: only shorthand compactor (see breakup#borderRadius) knows that border radius +      // longhands have two values, whereas tokenizer does not care about populating 2nd value +      // if it's missing, hence this fallback +      verticalComponent.value = [component.value[1] || component.value[0]]; +      vertical.components.push(verticalComponent); +    } + +    var horizontalValues = fourValues(horizontal, compactable); +    var verticalValues = fourValues(vertical, compactable); + +    if (horizontalValues.length == verticalValues.length && +        horizontalValues[0][1] == verticalValues[0][1] && +        (horizontalValues.length > 1 ? horizontalValues[1][1] == verticalValues[1][1] : true) && +        (horizontalValues.length > 2 ? horizontalValues[2][1] == verticalValues[2][1] : true) && +        (horizontalValues.length > 3 ? horizontalValues[3][1] == verticalValues[3][1] : true)) { +      return horizontalValues; +    } else { +      return horizontalValues.concat([[Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]]).concat(verticalValues); +    } +  } else { +    return fourValues(property, compactable); +  } +} + +function fourValues(property) { +  var components = property.components; +  var value1 = components[0].value[0]; +  var value2 = components[1].value[0]; +  var value3 = components[2].value[0]; +  var value4 = components[3].value[0]; + +  if (value1[1] == value2[1] && value1[1] == value3[1] && value1[1] == value4[1]) { +    return [value1]; +  } else if (value1[1] == value3[1] && value2[1] == value4[1]) { +    return [value1, value2]; +  } else if (value2[1] == value4[1]) { +    return [value1, value2, value3]; +  } else { +    return [value1, value2, value3, value4]; +  } +} + +function multiplex(restoreWith) { +  return function (property, compactable) { +    if (!property.multiplex) +      return restoreWith(property, compactable, true); + +    var multiplexSize = 0; +    var restored = []; +    var componentMultiplexSoFar = {}; +    var i, l; + +    // At this point we don't know what's the multiplex size, e.g. how many background layers are there +    for (i = 0, l = property.components[0].value.length; i < l; i++) { +      if (property.components[0].value[i][1] == Marker.COMMA) +        multiplexSize++; +    } + +    for (i = 0; i <= multiplexSize; i++) { +      var _property = shallowClone(property); + +      // We split multiplex into parts and restore them one by one +      for (var j = 0, m = property.components.length; j < m; j++) { +        var componentToClone = property.components[j]; +        var _component = shallowClone(componentToClone); +        _property.components.push(_component); + +        // The trick is some properties has more than one value, so we iterate over values looking for +        // a multiplex separator - a comma +        for (var k = componentMultiplexSoFar[_component.name] || 0, n = componentToClone.value.length; k < n; k++) { +          if (componentToClone.value[k][1] == Marker.COMMA) { +            componentMultiplexSoFar[_component.name] = k + 1; +            break; +          } + +          _component.value.push(componentToClone.value[k]); +        } +      } + +      // No we can restore shorthand value +      var lastInMultiplex = i == multiplexSize; +      var _restored = restoreWith(_property, compactable, lastInMultiplex); +      Array.prototype.push.apply(restored, _restored); + +      if (i < multiplexSize) +        restored.push([Token.PROPERTY_VALUE, Marker.COMMA]); +    } + +    return restored; +  }; +} + +function withoutDefaults(property, compactable) { +  var components = property.components; +  var restored = []; + +  for (var i = components.length - 1; i >= 0; i--) { +    var component = components[i]; +    var descriptor = compactable[component.name]; + +    if (component.value[0][1] != descriptor.defaultValue) +      restored.unshift(component.value[0]); +  } + +  if (restored.length === 0) +    restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]); + +  if (isInheritOnly(restored)) +    return [restored[0]]; + +  return restored; +} + +module.exports = { +  background: background, +  borderRadius: borderRadius, +  fourValues: fourValues, +  multiplex: multiplex, +  withoutDefaults: withoutDefaults +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restructure.js b/node_modules/clean-css/lib/optimizer/level-2/restructure.js new file mode 100644 index 000000000..2635415ae --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/restructure.js @@ -0,0 +1,388 @@ +var canReorderSingle = require('./reorderable').canReorderSingle; +var extractProperties = require('./extract-properties'); +var isMergeable = require('./is-mergeable'); +var tidyRuleDuplicates = require('./tidy-rule-duplicates'); + +var Token = require('../../tokenizer/token'); + +var cloneArray = require('../../utils/clone-array'); + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +function naturalSorter(a, b) { +  return a > b ? 1 : -1; +} + +function cloneAndMergeSelectors(propertyA, propertyB) { +  var cloned = cloneArray(propertyA); +  cloned[5] = cloned[5].concat(propertyB[5]); + +  return cloned; +} + +function restructure(tokens, context) { +  var options = context.options; +  var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; +  var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; +  var mergeLimit = 8191; +  var specificityCache = context.cache.specificity; +  var movableTokens = {}; +  var movedProperties = []; +  var multiPropertyMoveCache = {}; +  var movedToBeDropped = []; +  var maxCombinationsLevel = 2; +  var ID_JOIN_CHARACTER = '%'; + +  function sendToMultiPropertyMoveCache(position, movedProperty, allFits) { +    for (var i = allFits.length - 1; i >= 0; i--) { +      var fit = allFits[i][0]; +      var id = addToCache(movedProperty, fit); + +      if (multiPropertyMoveCache[id].length > 1 && processMultiPropertyMove(position, multiPropertyMoveCache[id])) { +        removeAllMatchingFromCache(id); +        break; +      } +    } +  } + +  function addToCache(movedProperty, fit) { +    var id = cacheId(fit); +    multiPropertyMoveCache[id] = multiPropertyMoveCache[id] || []; +    multiPropertyMoveCache[id].push([movedProperty, fit]); +    return id; +  } + +  function removeAllMatchingFromCache(matchId) { +    var matchSelectors = matchId.split(ID_JOIN_CHARACTER); +    var forRemoval = []; +    var i; + +    for (var id in multiPropertyMoveCache) { +      var selectors = id.split(ID_JOIN_CHARACTER); +      for (i = selectors.length - 1; i >= 0; i--) { +        if (matchSelectors.indexOf(selectors[i]) > -1) { +          forRemoval.push(id); +          break; +        } +      } +    } + +    for (i = forRemoval.length - 1; i >= 0; i--) { +      delete multiPropertyMoveCache[forRemoval[i]]; +    } +  } + +  function cacheId(cachedTokens) { +    var id = []; +    for (var i = 0, l = cachedTokens.length; i < l; i++) { +      id.push(serializeRules(cachedTokens[i][1])); +    } +    return id.join(ID_JOIN_CHARACTER); +  } + +  function tokensToMerge(sourceTokens) { +    var uniqueTokensWithBody = []; +    var mergeableTokens = []; + +    for (var i = sourceTokens.length - 1; i >= 0; i--) { +      if (!isMergeable(serializeRules(sourceTokens[i][1]), mergeablePseudoClasses, mergeablePseudoElements)) { +        continue; +      } + +      mergeableTokens.unshift(sourceTokens[i]); +      if (sourceTokens[i][2].length > 0 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1) +        uniqueTokensWithBody.push(sourceTokens[i]); +    } + +    return uniqueTokensWithBody.length > 1 ? +      mergeableTokens : +      []; +  } + +  function shortenIfPossible(position, movedProperty) { +    var name = movedProperty[0]; +    var value = movedProperty[1]; +    var key = movedProperty[4]; +    var valueSize = name.length + value.length + 1; +    var allSelectors = []; +    var qualifiedTokens = []; + +    var mergeableTokens = tokensToMerge(movableTokens[key]); +    if (mergeableTokens.length < 2) +      return; + +    var allFits = findAllFits(mergeableTokens, valueSize, 1); +    var bestFit = allFits[0]; +    if (bestFit[1] > 0) +      return sendToMultiPropertyMoveCache(position, movedProperty, allFits); + +    for (var i = bestFit[0].length - 1; i >=0; i--) { +      allSelectors = bestFit[0][i][1].concat(allSelectors); +      qualifiedTokens.unshift(bestFit[0][i]); +    } + +    allSelectors = tidyRuleDuplicates(allSelectors); +    dropAsNewTokenAt(position, [movedProperty], allSelectors, qualifiedTokens); +  } + +  function fitSorter(fit1, fit2) { +    return fit1[1] > fit2[1] ? 1 : (fit1[1] == fit2[1] ? 0 : -1); +  } + +  function findAllFits(mergeableTokens, propertySize, propertiesCount) { +    var combinations = allCombinations(mergeableTokens, propertySize, propertiesCount, maxCombinationsLevel - 1); +    return combinations.sort(fitSorter); +  } + +  function allCombinations(tokensVariant, propertySize, propertiesCount, level) { +    var differenceVariants = [[tokensVariant, sizeDifference(tokensVariant, propertySize, propertiesCount)]]; +    if (tokensVariant.length > 2 && level > 0) { +      for (var i = tokensVariant.length - 1; i >= 0; i--) { +        var subVariant = Array.prototype.slice.call(tokensVariant, 0); +        subVariant.splice(i, 1); +        differenceVariants = differenceVariants.concat(allCombinations(subVariant, propertySize, propertiesCount, level - 1)); +      } +    } + +    return differenceVariants; +  } + +  function sizeDifference(tokensVariant, propertySize, propertiesCount) { +    var allSelectorsSize = 0; +    for (var i = tokensVariant.length - 1; i >= 0; i--) { +      allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? serializeRules(tokensVariant[i][1]).length : -1; +    } +    return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1; +  } + +  function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) { +    var i, j, k, m; +    var allProperties = []; + +    for (i = mergeableTokens.length - 1; i >= 0; i--) { +      var mergeableToken = mergeableTokens[i]; + +      for (j = mergeableToken[2].length - 1; j >= 0; j--) { +        var mergeableProperty = mergeableToken[2][j]; + +        for (k = 0, m = properties.length; k < m; k++) { +          var property = properties[k]; + +          var mergeablePropertyName = mergeableProperty[1][1]; +          var propertyName = property[0]; +          var propertyBody = property[4]; +          if (mergeablePropertyName == propertyName && serializeBody([mergeableProperty]) == propertyBody) { +            mergeableToken[2].splice(j, 1); +            break; +          } +        } +      } +    } + +    for (i = properties.length - 1; i >= 0; i--) { +      allProperties.unshift(properties[i][3]); +    } + +    var newToken = [Token.RULE, allSelectors, allProperties]; +    tokens.splice(position, 0, newToken); +  } + +  function dropPropertiesAt(position, movedProperty) { +    var key = movedProperty[4]; +    var toMove = movableTokens[key]; + +    if (toMove && toMove.length > 1) { +      if (!shortenMultiMovesIfPossible(position, movedProperty)) +        shortenIfPossible(position, movedProperty); +    } +  } + +  function shortenMultiMovesIfPossible(position, movedProperty) { +    var candidates = []; +    var propertiesAndMergableTokens = []; +    var key = movedProperty[4]; +    var j, k; + +    var mergeableTokens = tokensToMerge(movableTokens[key]); +    if (mergeableTokens.length < 2) +      return; + +    movableLoop: +    for (var value in movableTokens) { +      var tokensList = movableTokens[value]; + +      for (j = mergeableTokens.length - 1; j >= 0; j--) { +        if (tokensList.indexOf(mergeableTokens[j]) == -1) +          continue movableLoop; +      } + +      candidates.push(value); +    } + +    if (candidates.length < 2) +      return false; + +    for (j = candidates.length - 1; j >= 0; j--) { +      for (k = movedProperties.length - 1; k >= 0; k--) { +        if (movedProperties[k][4] == candidates[j]) { +          propertiesAndMergableTokens.unshift([movedProperties[k], mergeableTokens]); +          break; +        } +      } +    } + +    return processMultiPropertyMove(position, propertiesAndMergableTokens); +  } + +  function processMultiPropertyMove(position, propertiesAndMergableTokens) { +    var valueSize = 0; +    var properties = []; +    var property; + +    for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) { +      property = propertiesAndMergableTokens[i][0]; +      var fullValue = property[4]; +      valueSize += fullValue.length + (i > 0 ? 1 : 0); + +      properties.push(property); +    } + +    var mergeableTokens = propertiesAndMergableTokens[0][1]; +    var bestFit = findAllFits(mergeableTokens, valueSize, properties.length)[0]; +    if (bestFit[1] > 0) +      return false; + +    var allSelectors = []; +    var qualifiedTokens = []; +    for (i = bestFit[0].length - 1; i >= 0; i--) { +      allSelectors = bestFit[0][i][1].concat(allSelectors); +      qualifiedTokens.unshift(bestFit[0][i]); +    } + +    allSelectors = tidyRuleDuplicates(allSelectors); +    dropAsNewTokenAt(position, properties, allSelectors, qualifiedTokens); + +    for (i = properties.length - 1; i >= 0; i--) { +      property = properties[i]; +      var index = movedProperties.indexOf(property); + +      delete movableTokens[property[4]]; + +      if (index > -1 && movedToBeDropped.indexOf(index) == -1) +        movedToBeDropped.push(index); +    } + +    return true; +  } + +  function boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) { +    var propertyName = property[0]; +    var movedPropertyName = movedProperty[0]; +    if (propertyName != movedPropertyName) +      return false; + +    var key = movedProperty[4]; +    var toMove = movableTokens[key]; +    return toMove && toMove.indexOf(token) > -1; +  } + +  for (var i = tokens.length - 1; i >= 0; i--) { +    var token = tokens[i]; +    var isRule; +    var j, k, m; +    var samePropertyAt; + +    if (token[0] == Token.RULE) { +      isRule = true; +    } else if (token[0] == Token.NESTED_BLOCK) { +      isRule = false; +    } else { +      continue; +    } + +    // We cache movedProperties.length as it may change in the loop +    var movedCount = movedProperties.length; + +    var properties = extractProperties(token); +    movedToBeDropped = []; + +    var unmovableInCurrentToken = []; +    for (j = properties.length - 1; j >= 0; j--) { +      for (k = j - 1; k >= 0; k--) { +        if (!canReorderSingle(properties[j], properties[k], specificityCache)) { +          unmovableInCurrentToken.push(j); +          break; +        } +      } +    } + +    for (j = properties.length - 1; j >= 0; j--) { +      var property = properties[j]; +      var movedSameProperty = false; + +      for (k = 0; k < movedCount; k++) { +        var movedProperty = movedProperties[k]; + +        if (movedToBeDropped.indexOf(k) == -1 && (!canReorderSingle(property, movedProperty, specificityCache) && !boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) || +            movableTokens[movedProperty[4]] && movableTokens[movedProperty[4]].length === mergeLimit)) { +          dropPropertiesAt(i + 1, movedProperty, token); + +          if (movedToBeDropped.indexOf(k) == -1) { +            movedToBeDropped.push(k); +            delete movableTokens[movedProperty[4]]; +          } +        } + +        if (!movedSameProperty) { +          movedSameProperty = property[0] == movedProperty[0] && property[1] == movedProperty[1]; + +          if (movedSameProperty) { +            samePropertyAt = k; +          } +        } +      } + +      if (!isRule || unmovableInCurrentToken.indexOf(j) > -1) +        continue; + +      var key = property[4]; + +      if (movedSameProperty && movedProperties[samePropertyAt][5].length + property[5].length > mergeLimit) { +        dropPropertiesAt(i + 1, movedProperties[samePropertyAt]); +        movedProperties.splice(samePropertyAt, 1); +        movableTokens[key] = [token]; +        movedSameProperty = false; +      } else { +        movableTokens[key] = movableTokens[key] || []; +        movableTokens[key].push(token); +      } + +      if (movedSameProperty) { +        movedProperties[samePropertyAt] = cloneAndMergeSelectors(movedProperties[samePropertyAt], property); +      } else { +        movedProperties.push(property); +      } +    } + +    movedToBeDropped = movedToBeDropped.sort(naturalSorter); +    for (j = 0, m = movedToBeDropped.length; j < m; j++) { +      var dropAt = movedToBeDropped[j] - j; +      movedProperties.splice(dropAt, 1); +    } +  } + +  var position = tokens[0] && tokens[0][0] == Token.AT_RULE && tokens[0][1].indexOf('@charset') === 0 ? 1 : 0; +  for (; position < tokens.length - 1; position++) { +    var isImportRule = tokens[position][0] === Token.AT_RULE && tokens[position][1].indexOf('@import') === 0; +    var isComment = tokens[position][0] === Token.COMMENT; +    if (!(isImportRule || isComment)) +      break; +  } + +  for (i = 0; i < movedProperties.length; i++) { +    dropPropertiesAt(position, movedProperties[i]); +  } +} + +module.exports = restructure; diff --git a/node_modules/clean-css/lib/optimizer/level-2/rules-overlap.js b/node_modules/clean-css/lib/optimizer/level-2/rules-overlap.js new file mode 100644 index 000000000..811a517b2 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/rules-overlap.js @@ -0,0 +1,32 @@ +var MODIFIER_PATTERN = /\-\-.+$/; + +function rulesOverlap(rule1, rule2, bemMode) { +  var scope1; +  var scope2; +  var i, l; +  var j, m; + +  for (i = 0, l = rule1.length; i < l; i++) { +    scope1 = rule1[i][1]; + +    for (j = 0, m = rule2.length; j < m; j++) { +      scope2 = rule2[j][1]; + +      if (scope1 == scope2) { +        return true; +      } + +      if (bemMode && withoutModifiers(scope1) == withoutModifiers(scope2)) { +        return true; +      } +    } +  } + +  return false; +} + +function withoutModifiers(scope) { +  return scope.replace(MODIFIER_PATTERN, ''); +} + +module.exports = rulesOverlap; diff --git a/node_modules/clean-css/lib/optimizer/level-2/specificities-overlap.js b/node_modules/clean-css/lib/optimizer/level-2/specificities-overlap.js new file mode 100644 index 000000000..bde037446 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/specificities-overlap.js @@ -0,0 +1,34 @@ +var specificity = require('./specificity'); + +function specificitiesOverlap(selector1, selector2, cache) { +  var specificity1; +  var specificity2; +  var i, l; +  var j, m; + +  for (i = 0, l = selector1.length; i < l; i++) { +    specificity1 = findSpecificity(selector1[i][1], cache); + +    for (j = 0, m = selector2.length; j < m; j++) { +      specificity2 = findSpecificity(selector2[j][1], cache); + +      if (specificity1[0] === specificity2[0] && specificity1[1] === specificity2[1] && specificity1[2] === specificity2[2]) { +        return true; +      } +    } +  } + +  return false; +} + +function findSpecificity(selector, cache) { +  var value; + +  if (!(selector in cache)) { +    cache[selector] = value = specificity(selector); +  } + +  return value || cache[selector]; +} + +module.exports = specificitiesOverlap; diff --git a/node_modules/clean-css/lib/optimizer/level-2/specificity.js b/node_modules/clean-css/lib/optimizer/level-2/specificity.js new file mode 100644 index 000000000..bbd224f15 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/specificity.js @@ -0,0 +1,77 @@ +var Marker = require('../../tokenizer/marker'); + +var Selector = { +  ADJACENT_SIBLING: '+', +  DESCENDANT: '>', +  DOT: '.', +  HASH: '#', +  NON_ADJACENT_SIBLING: '~', +  PSEUDO: ':' +}; + +var LETTER_PATTERN = /[a-zA-Z]/; +var NOT_PREFIX = ':not('; +var SEPARATOR_PATTERN = /[\s,\(>~\+]/; + +function specificity(selector) { +  var result = [0, 0, 0]; +  var character; +  var isEscaped; +  var isSingleQuoted; +  var isDoubleQuoted; +  var roundBracketLevel = 0; +  var couldIntroduceNewTypeSelector; +  var withinNotPseudoClass = false; +  var wasPseudoClass = false; +  var i, l; + +  for (i = 0, l = selector.length; i < l; i++) { +    character = selector[i]; + +    if (isEscaped) { +      // noop +    } else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) { +      isSingleQuoted = true; +    } else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && isSingleQuoted) { +      isSingleQuoted = false; +    } else if (character == Marker.DOUBLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) { +      isDoubleQuoted = true; +    } else if (character == Marker.DOUBLE_QUOTE && isDoubleQuoted && !isSingleQuoted) { +      isDoubleQuoted = false; +    } else if (isSingleQuoted || isDoubleQuoted) { +      continue; +    } else if (roundBracketLevel > 0 && !withinNotPseudoClass) { +      // noop +    } else if (character == Marker.OPEN_ROUND_BRACKET) { +      roundBracketLevel++; +    } else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1) { +      roundBracketLevel--; +      withinNotPseudoClass = false; +    } else if (character == Marker.CLOSE_ROUND_BRACKET) { +      roundBracketLevel--; +    } else if (character == Selector.HASH) { +      result[0]++; +    } else if (character == Selector.DOT || character == Marker.OPEN_SQUARE_BRACKET) { +      result[1]++; +    } else if (character == Selector.PSEUDO && !wasPseudoClass && !isNotPseudoClass(selector, i)) { +      result[1]++; +      withinNotPseudoClass = false; +    } else if (character == Selector.PSEUDO) { +      withinNotPseudoClass = true; +    } else if ((i === 0 || couldIntroduceNewTypeSelector) && LETTER_PATTERN.test(character)) { +      result[2]++; +    } + +    isEscaped = character == Marker.BACK_SLASH; +    wasPseudoClass = character == Selector.PSEUDO; +    couldIntroduceNewTypeSelector = !isEscaped && SEPARATOR_PATTERN.test(character); +  } + +  return result; +} + +function isNotPseudoClass(selector, index) { +  return selector.indexOf(NOT_PREFIX, index) === index; +} + +module.exports = specificity; diff --git a/node_modules/clean-css/lib/optimizer/level-2/tidy-rule-duplicates.js b/node_modules/clean-css/lib/optimizer/level-2/tidy-rule-duplicates.js new file mode 100644 index 000000000..30a9c2177 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/tidy-rule-duplicates.js @@ -0,0 +1,21 @@ +function ruleSorter(s1, s2) { +  return s1[1] > s2[1] ? 1 : -1; +} + +function tidyRuleDuplicates(rules) { +  var list = []; +  var repeated = []; + +  for (var i = 0, l = rules.length; i < l; i++) { +    var rule = rules[i]; + +    if (repeated.indexOf(rule[1]) == -1) { +      repeated.push(rule[1]); +      list.push(rule); +    } +  } + +  return list.sort(ruleSorter); +} + +module.exports = tidyRuleDuplicates; diff --git a/node_modules/clean-css/lib/optimizer/remove-unused.js b/node_modules/clean-css/lib/optimizer/remove-unused.js new file mode 100644 index 000000000..7b90c4079 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/remove-unused.js @@ -0,0 +1,11 @@ +function removeUnused(properties) { +  for (var i = properties.length - 1; i >= 0; i--) { +    var property = properties[i]; + +    if (property.unused) { +      property.all.splice(property.position, 1); +    } +  } +} + +module.exports = removeUnused; diff --git a/node_modules/clean-css/lib/optimizer/restore-from-optimizing.js b/node_modules/clean-css/lib/optimizer/restore-from-optimizing.js new file mode 100644 index 000000000..ebd69c2ac --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/restore-from-optimizing.js @@ -0,0 +1,69 @@ +var Hack = require('./hack'); + +var Marker = require('../tokenizer/marker'); + +var ASTERISK_HACK = '*'; +var BACKSLASH_HACK = '\\'; +var IMPORTANT_TOKEN = '!important'; +var UNDERSCORE_HACK = '_'; +var BANG_HACK = '!ie'; + +function restoreFromOptimizing(properties, restoreCallback) { +  var property; +  var restored; +  var current; +  var i; + +  for (i = properties.length - 1; i >= 0; i--) { +    property = properties[i]; + +    if (property.unused) { +      continue; +    } + +    if (!property.dirty && !property.important && !property.hack) { +      continue; +    } + +    if (restoreCallback) { +      restored = restoreCallback(property); +      property.value = restored; +    } else { +      restored = property.value; +    } + +    if (property.important) { +      restoreImportant(property); +    } + +    if (property.hack) { +      restoreHack(property); +    } + +    if ('all' in property) { +      current = property.all[property.position]; +      current[1][1] = property.name; + +      current.splice(2, current.length - 1); +      Array.prototype.push.apply(current, restored); +    } +  } +} + +function restoreImportant(property) { +  property.value[property.value.length - 1][1] += IMPORTANT_TOKEN; +} + +function restoreHack(property) { +  if (property.hack[0] == Hack.UNDERSCORE) { +    property.name = UNDERSCORE_HACK + property.name; +  } else if (property.hack[0] == Hack.ASTERISK) { +    property.name = ASTERISK_HACK + property.name; +  } else if (property.hack[0] == Hack.BACKSLASH) { +    property.value[property.value.length - 1][1] += BACKSLASH_HACK + property.hack[1]; +  } else if (property.hack[0] == Hack.BANG) { +    property.value[property.value.length - 1][1] += Marker.SPACE + BANG_HACK; +  } +} + +module.exports = restoreFromOptimizing; diff --git a/node_modules/clean-css/lib/optimizer/validator.js b/node_modules/clean-css/lib/optimizer/validator.js new file mode 100644 index 000000000..35f041ea9 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/validator.js @@ -0,0 +1,470 @@ +var Units = [ +  '%', +  'ch', +  'cm', +  'em', +  'ex', +  'in', +  'mm', +  'pc', +  'pt', +  'px', +  'rem', +  'vh', +  'vm', +  'vmax', +  'vmin', +  'vw' +]; +var cssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + Units.join('|') + '|)|auto|inherit)'; +var cssCalcRegexStr = '(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)'; +var cssFunctionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)'; +var cssFunctionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)'; +var cssVariableRegexStr = 'var\\(\\-\\-[^\\)]+\\)'; +var cssFunctionAnyRegexStr = '(' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')'; +var cssUnitOrCalcRegexStr = '(' + cssUnitRegexStr + '|' + cssCalcRegexStr + ')'; + +var cssFunctionNoVendorRegex = new RegExp('^' + cssFunctionNoVendorRegexStr + '$', 'i'); +var cssVariableRegex = new RegExp('^' + cssVariableRegexStr + '$', 'i'); +var cssFunctionAnyRegex = new RegExp('^' + cssFunctionAnyRegexStr + '$', 'i'); +var cssUnitRegex = new RegExp('^' + cssUnitRegexStr + '$', 'i'); +var cssUnitOrCalcRegex = new RegExp('^' + cssUnitOrCalcRegexStr + '$', 'i'); + +var urlRegex = /^url\([\s\S]+\)$/i; + +var globalKeywords = [ +  'inherit', +  'initial', +  'unset' +]; + +var Keywords = { +  '*-style': [ +    'auto', +    'dashed', +    'dotted', +    'double', +    'groove', +    'hidden', +    'inset', +    'none', +    'outset', +    'ridge', +    'solid' +  ], +  'background-attachment': [ +    'fixed', +    'inherit', +    'local', +    'scroll' +  ], +  'background-clip': [ +    'border-box', +    'content-box', +    'inherit', +    'padding-box', +    'text' +  ], +  'background-origin': [ +    'border-box', +    'content-box', +    'inherit', +    'padding-box' +  ], +  'background-position': [ +    'bottom', +    'center', +    'left', +    'right', +    'top' +  ], +  'background-repeat': [ +    'no-repeat', +    'inherit', +    'repeat', +    'repeat-x', +    'repeat-y', +    'round', +    'space' +  ], +  'background-size': [ +    'auto', +    'cover', +    'contain' +  ], +  'border-collapse': [ +    'collapse', +    'inherit', +    'separate' +  ], +  'bottom': [ +    'auto' +  ], +  'clear': [ +    'both', +    'left', +    'none', +    'right' +  ], +  'cursor': [ +    'all-scroll', +    'auto', +    'col-resize', +    'crosshair', +    'default', +    'e-resize', +    'help', +    'move', +    'n-resize', +    'ne-resize', +    'no-drop', +    'not-allowed', +    'nw-resize', +    'pointer', +    'progress', +    'row-resize', +    's-resize', +    'se-resize', +    'sw-resize', +    'text', +    'vertical-text', +    'w-resize', +    'wait' +  ], +  'display': [ +    'block', +    'inline', +    'inline-block', +    'inline-table', +    'list-item', +    'none', +    'table', +    'table-caption', +    'table-cell', +    'table-column', +    'table-column-group', +    'table-footer-group', +    'table-header-group', +    'table-row', +    'table-row-group' +  ], +  'float': [ +    'left', +    'none', +    'right' +  ], +  'left': [ +    'auto' +  ], +  'font-style': [ +    'italic', +    'normal', +    'oblique' +  ], +  'font-weight': [ +    '100', +    '200', +    '300', +    '400', +    '500', +    '600', +    '700', +    '800', +    '900', +    'bold', +    'bolder', +    'lighter', +    'normal' +  ], +  'list-style-position': [ +    'inside', +    'outside' +  ], +  'list-style-type': [ +    'armenian', +    'circle', +    'decimal', +    'decimal-leading-zero', +    'disc', +    'decimal|disc', // this is the default value of list-style-type, see comment in compactable.js +    'georgian', +    'lower-alpha', +    'lower-greek', +    'lower-latin', +    'lower-roman', +    'none', +    'square', +    'upper-alpha', +    'upper-latin', +    'upper-roman' +  ], +  'overflow': [ +    'auto', +    'hidden', +    'scroll', +    'visible' +  ], +  'position': [ +    'absolute', +    'fixed', +    'relative', +    'static' +  ], +  'right': [ +    'auto' +  ], +  'text-align': [ +    'center', +    'justify', +    'left', +    'left|right', // this is the default value of list-style-type, see comment in compactable.js +    'right' +  ], +  'text-decoration': [ +    'line-through', +    'none', +    'overline', +    'underline' +  ], +  'text-overflow': [ +    'clip', +    'ellipsis' +  ], +  'top': [ +    'auto' +  ], +  'vertical-align': [ +    'baseline', +    'bottom', +    'middle', +    'sub', +    'super', +    'text-bottom', +    'text-top', +    'top' +  ], +  'visibility': [ +    'collapse', +    'hidden', +    'visible' +  ], +  'white-space': [ +    'normal', +    'nowrap', +    'pre' +  ], +  'width': [ +    'inherit', +    'initial', +    'medium', +    'thick', +    'thin' +  ] +}; + +var VENDOR_PREFIX_PATTERN = /(^|\W)-\w+\-/; + +function areSameFunction(value1, value2) { +  if (!isValidFunction(value1) || !isValidFunction(value2)) { +    return false; +  } + +  var function1Name = value1.substring(0, value1.indexOf('(')); +  var function2Name = value2.substring(0, value2.indexOf('(')); + +  return function1Name === function2Name; +} + +function hasNoVendorPrefix(value) { +  return VENDOR_PREFIX_PATTERN.test(value); +} + +function isValidBackgroundAttachment(value) { +  return Keywords['background-attachment'].indexOf(value) > -1; +} + +function isValidBackgroundClip(value) { +  return Keywords['background-clip'].indexOf(value) > -1; +} + +function isValidBackgroundRepeat(value) { +  return Keywords['background-repeat'].indexOf(value) > -1; +} + +function isValidBackgroundOrigin(value) { +  return Keywords['background-origin'].indexOf(value) > -1; +} + +function isValidBackgroundPosition(value) { +  var parts; +  var i, l; + +  if (value === 'inherit') { +    return true; +  } + +  parts = value.split(' '); +  for (i = 0, l = parts.length; i < l; i++) { +    if (parts[i] === '') { +      continue; +    } else if (isValidBackgroundPositionPart(parts[i])) { +      continue; +    } + +    return false; +  } + +  return true; +} + +function isValidBackgroundPositionPart(value) { +  return Keywords['background-position'].indexOf(value) > -1 || cssUnitOrCalcRegex.test(value); +} + +function isValidBackgroundSizePart(value) { +  return Keywords['background-size'].indexOf(value) > -1 || cssUnitRegex.test(value); +} + +function isValidColor(value) { +  return isValidNamedColor(value) || +    isValidColorValue(value); +} + +function isValidColorValue(value) { +  return isValidHexColor(value) || +    isValidRgbaColor(value) || +    isValidHslaColor(value); +} + +function isValidFunction(value) { +  return !urlRegex.test(value) && cssFunctionAnyRegex.test(value); +} + +function isValidFunctionWithoutVendorPrefix(value) { +  return !urlRegex.test(value) && cssFunctionNoVendorRegex.test(value); +} + +function isValidGlobalValue(value) { +  return globalKeywords.indexOf(value) > -1; +} + +function isValidHexColor(value) { +  return (value.length === 4 || value.length === 7) && value[0] === '#'; +} + +function isValidHslaColor(value) { +  return value.length > 0 && value.indexOf('hsla(') === 0 && value.indexOf(')') === value.length - 1; +} + +function isValidImage(value) { +  return value == 'none' || value == 'inherit' || isValidUrl(value); +} + +function isValidKeywordValue(propertyName, value, includeGlobal) { +  return Keywords[propertyName].indexOf(value) > -1 || includeGlobal && isValidGlobalValue(value); +} + +function isValidListStyleType(value) { +  return Keywords['list-style-type'].indexOf(value) > -1; +} + +function isValidListStylePosition(value) { +  return Keywords['list-style-position'].indexOf(value) > -1; +} + +function isValidNamedColor(value) { +  // We don't really check if it's a valid color value, but allow any letters in it +  return value !== 'auto' && (value === 'transparent' || value === 'inherit' || /^[a-zA-Z]+$/.test(value)); +} + +function isValidRgbaColor(value) { +  return value.length > 0 && value.indexOf('rgba(') === 0 && value.indexOf(')') === value.length - 1; +} + +function isValidStyle(value) { +  return Keywords['*-style'].indexOf(value) > -1; +} + +function isValidTextShadow(compatibleCssUnitRegex, value) { +  return isValidUnitWithoutFunction(compatibleCssUnitRegex, value) || +    isValidColor(value) || +    isValidGlobalValue(value); +} + +function isValidUnit(compatibleCssUnitAnyRegex, value) { +  return compatibleCssUnitAnyRegex.test(value); +} + +function isValidUnitWithoutFunction(compatibleCssUnitRegex, value) { +  return compatibleCssUnitRegex.test(value); +} + +function isValidUrl(value) { +  return urlRegex.test(value); +} + +function isValidVariable(value) { +  return cssVariableRegex.test(value); +} + +function isValidVendorPrefixedValue(value) { +  return /^-([A-Za-z0-9]|-)*$/gi.test(value); +} + +function isValidWidth(compatibleCssUnitRegex, value) { +  return isValidUnit(compatibleCssUnitRegex, value) || Keywords.width.indexOf(value) > -1; +} + +function isValidZIndex(value) { +  return value == 'auto' || +    isValidGlobalValue(value) || +    value.length > 0 && value == ('' + parseInt(value)); +} + +function validator(compatibility) { +  var validUnits = Units.slice(0).filter(function (value) { +    return !(value in compatibility.units) || compatibility.units[value] === true; +  }); + +  var compatibleCssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + '|)|auto|inherit)'; +  var compatibleCssUnitRegex = new RegExp('^' + compatibleCssUnitRegexStr + '$', 'i'); +  var compatibleCssUnitAnyRegex = new RegExp('^(none|' + Keywords.width.join('|') + '|' + compatibleCssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')$', 'i'); +  var colorOpacity = compatibility.colors.opacity; + +  return { +    areSameFunction: areSameFunction, +    colorOpacity: colorOpacity, +    hasNoVendorPrefix: hasNoVendorPrefix, +    isValidBackgroundAttachment: isValidBackgroundAttachment, +    isValidBackgroundClip: isValidBackgroundClip, +    isValidBackgroundOrigin: isValidBackgroundOrigin, +    isValidBackgroundPosition: isValidBackgroundPosition, +    isValidBackgroundPositionPart: isValidBackgroundPositionPart, +    isValidBackgroundRepeat: isValidBackgroundRepeat, +    isValidBackgroundSizePart: isValidBackgroundSizePart, +    isValidColor: isValidColor, +    isValidColorValue: isValidColorValue, +    isValidFunction: isValidFunction, +    isValidFunctionWithoutVendorPrefix: isValidFunctionWithoutVendorPrefix, +    isValidGlobalValue: isValidGlobalValue, +    isValidHexColor: isValidHexColor, +    isValidHslaColor: isValidHslaColor, +    isValidImage: isValidImage, +    isValidKeywordValue: isValidKeywordValue, +    isValidListStylePosition: isValidListStylePosition, +    isValidListStyleType: isValidListStyleType, +    isValidNamedColor: isValidNamedColor, +    isValidRgbaColor: isValidRgbaColor, +    isValidStyle: isValidStyle, +    isValidTextShadow: isValidTextShadow.bind(null, compatibleCssUnitRegex), +    isValidUnit: isValidUnit.bind(null, compatibleCssUnitAnyRegex), +    isValidUnitWithoutFunction: isValidUnitWithoutFunction.bind(null, compatibleCssUnitRegex), +    isValidUrl: isValidUrl, +    isValidVariable: isValidVariable, +    isValidVendorPrefixedValue: isValidVendorPrefixedValue, +    isValidWidth: isValidWidth.bind(null, compatibleCssUnitRegex), +    isValidZIndex: isValidZIndex +  }; +} + +module.exports = validator; diff --git a/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js b/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js new file mode 100644 index 000000000..0fbfdb056 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/wrap-for-optimizing.js @@ -0,0 +1,187 @@ +var Hack = require('./hack'); + +var Marker = require('../tokenizer/marker'); +var Token = require('../tokenizer/token'); + +var Match = { +  ASTERISK: '*', +  BACKSLASH: '\\', +  BANG: '!', +  BANG_SUFFIX_PATTERN: /!\w+$/, +  IMPORTANT_TOKEN: '!important', +  IMPORTANT_TOKEN_PATTERN: new RegExp('!important$', 'i'), +  IMPORTANT_WORD: 'important', +  IMPORTANT_WORD_PATTERN: new RegExp('important$', 'i'), +  SUFFIX_BANG_PATTERN: /!$/, +  UNDERSCORE: '_', +  VARIABLE_REFERENCE_PATTERN: /var\(--.+\)$/ +}; + +function wrapAll(properties, includeVariable) { +  var wrapped = []; +  var single; +  var property; +  var i; + +  for (i = properties.length - 1; i >= 0; i--) { +    property = properties[i]; + +    if (property[0] != Token.PROPERTY) { +      continue; +    } + +    if (!includeVariable && someVariableReferences(property)) { +      continue; +    } + +    single = wrapSingle(property); +    single.all = properties; +    single.position = i; +    wrapped.unshift(single); +  } + +  return wrapped; +} + +function someVariableReferences(property) { +  var i, l; +  var value; + +  // skipping `property` and property name tokens +  for (i = 2, l = property.length; i < l; i++) { +    value = property[i]; + +    if (value[0] != Token.PROPERTY_VALUE) { +      continue; +    } + +    if (isVariableReference(value[1])) { +      return true; +    } +  } + +  return false; +} + +function isVariableReference(value) { +  return Match.VARIABLE_REFERENCE_PATTERN.test(value); +} + +function isMultiplex(property) { +  var value; +  var i, l; + +  for (i = 3, l = property.length; i < l; i++) { +    value = property[i]; + +    if (value[0] == Token.PROPERTY_VALUE && (value[1] == Marker.COMMA || value[1] == Marker.FORWARD_SLASH)) { +      return true; +    } +  } + +  return false; +} + +function hackFrom(property) { +  var match = false; +  var name = property[1][1]; +  var lastValue = property[property.length - 1]; + +  if (name[0] == Match.UNDERSCORE) { +    match = [Hack.UNDERSCORE]; +  } else if (name[0] == Match.ASTERISK) { +    match = [Hack.ASTERISK]; +  } else if (lastValue[1][0] == Match.BANG && !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN)) { +    match = [Hack.BANG]; +  } else if (lastValue[1].indexOf(Match.BANG) > 0 && !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN) && Match.BANG_SUFFIX_PATTERN.test(lastValue[1])) { +    match = [Hack.BANG]; +  } else if (lastValue[1].indexOf(Match.BACKSLASH) > 0 && lastValue[1].indexOf(Match.BACKSLASH) == lastValue[1].length - Match.BACKSLASH.length - 1) { +    match = [Hack.BACKSLASH, lastValue[1].substring(lastValue[1].indexOf(Match.BACKSLASH) + 1)]; +  } else if (lastValue[1].indexOf(Match.BACKSLASH) === 0 && lastValue[1].length == 2) { +    match = [Hack.BACKSLASH, lastValue[1].substring(1)]; +  } + +  return match; +} + +function isImportant(property) { +  if (property.length < 3) +    return false; + +  var lastValue = property[property.length - 1]; +  if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) { +    return true; +  } else if (Match.IMPORTANT_WORD_PATTERN.test(lastValue[1]) && Match.SUFFIX_BANG_PATTERN.test(property[property.length - 2][1])) { +    return true; +  } + +  return false; +} + +function stripImportant(property) { +  var lastValue = property[property.length - 1]; +  var oneButLastValue = property[property.length - 2]; + +  if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) { +    lastValue[1] = lastValue[1].replace(Match.IMPORTANT_TOKEN_PATTERN, ''); +  } else { +    lastValue[1] = lastValue[1].replace(Match.IMPORTANT_WORD_PATTERN, ''); +    oneButLastValue[1] = oneButLastValue[1].replace(Match.SUFFIX_BANG_PATTERN, ''); +  } + +  if (lastValue[1].length === 0) { +    property.pop(); +  } + +  if (oneButLastValue[1].length === 0) { +    property.pop(); +  } +} + +function stripPrefixHack(property) { +  property[1][1] = property[1][1].substring(1); +} + +function stripSuffixHack(property, hackFrom) { +  var lastValue = property[property.length - 1]; +  lastValue[1] = lastValue[1] +    .substring(0, lastValue[1].indexOf(hackFrom[0] == Hack.BACKSLASH ? Match.BACKSLASH : Match.BANG)) +    .trim(); + +  if (lastValue[1].length === 0) { +    property.pop(); +  } +} + +function wrapSingle(property) { +  var importantProperty = isImportant(property); +  if (importantProperty) { +    stripImportant(property); +  } + +  var whichHack = hackFrom(property); +  if (whichHack[0] == Hack.ASTERISK || whichHack[0] == Hack.UNDERSCORE) { +    stripPrefixHack(property); +  } else if (whichHack[0] == Hack.BACKSLASH || whichHack[0] == Hack.BANG) { +    stripSuffixHack(property, whichHack); +  } + +  return { +    block: property[2] && property[2][0] == Token.PROPERTY_BLOCK, +    components: [], +    dirty: false, +    hack: whichHack, +    important: importantProperty, +    name: property[1][1], +    multiplex: property.length > 3 ? isMultiplex(property) : false, +    position: 0, +    shorthand: false, +    unused: false, +    value: property.slice(2) +  }; +} + +module.exports = { +  all: wrapAll, +  single: wrapSingle +}; | 
