clean-css
Advanced tools
Comparing version 4.0.12 to 4.1.0
@@ -14,2 +14,3 @@ /** | ||
var compatibilityFrom = require('./options/compatibility'); | ||
var fetchFrom = require('./options/fetch'); | ||
var formatFrom = require('./options/format').formatFrom; | ||
@@ -35,2 +36,3 @@ var inlineFrom = require('./options/inline'); | ||
compatibility: compatibilityFrom(options.compatibility), | ||
fetch: fetchFrom(options.fetch), | ||
format: formatFrom(options.format), | ||
@@ -37,0 +39,0 @@ inline: inlineFrom(options.inline), |
@@ -29,6 +29,2 @@ var shortenHex = require('./shorten-hex'); | ||
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/; | ||
@@ -163,59 +159,2 @@ var TIME_VALUE = /^(\-?[\d\.]+)(m?s)$/; | ||
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) { | ||
@@ -488,3 +427,3 @@ var value = property.value[atIndex][1]; | ||
if (valueIsUrl && !context.validator.isValidUrl(value)) { | ||
if (valueIsUrl && !context.validator.isUrl(value)) { | ||
property.unused = true; | ||
@@ -549,4 +488,2 @@ context.warnings.push('Broken URL \'' + value + '\' at ' + formatPosition(property.value[j][2][0]) + '. Ignoring.'); | ||
optimizeFilter(property); | ||
} else if (name == 'font' && levelOptions.optimizeFont) { | ||
optimizeFont(property, options); | ||
} else if (name == 'font-weight' && levelOptions.optimizeFontWeight) { | ||
@@ -724,3 +661,3 @@ optimizeFontWeight(property, 0); | ||
if (token[1].length === 0 || (token[2] && token[2].length === 0)) { | ||
if (levelOptions.removeEmpty && (token[1].length === 0 || (token[2] && token[2].length === 0))) { | ||
tokens.splice(i, 1); | ||
@@ -727,0 +664,0 @@ i--; |
@@ -12,15 +12,13 @@ var naturalCompare = require('../../utils/natural-compare'); | ||
function sortSelectors(selectors, method) { | ||
var sorter; | ||
switch (method) { | ||
case 'natural': | ||
sorter = naturalSorter; | ||
break; | ||
return selectors.sort(naturalSorter); | ||
case 'standard': | ||
sorter = standardSorter; | ||
return selectors.sort(standardSorter); | ||
case 'none': | ||
case false: | ||
return selectors; | ||
} | ||
return selectors.sort(sorter); | ||
} | ||
module.exports = sortSelectors; |
@@ -6,10 +6,21 @@ var InvalidPropertyError = require('./invalid-property-error'); | ||
var Token = require('../../tokenizer/token'); | ||
var Marker = require('../../tokenizer/marker'); | ||
var formatPosition = require('../../utils/format-position'); | ||
var MULTIPLEX_SEPARATOR = ','; | ||
function _anyIsInherit(values) { | ||
var i, l; | ||
for (i = 0, l = values.length; i < l; i++) { | ||
if (values[i][1] == 'inherit') { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
function _colorFilter(validator) { | ||
return function (value) { | ||
return value[1] == 'invert' || validator.isValidColor(value[1]) || validator.isValidVendorPrefixedValue(value[1]); | ||
return value[1] == 'invert' || validator.isColor(value[1]) || validator.isPrefixed(value[1]); | ||
}; | ||
@@ -20,3 +31,3 @@ } | ||
return function (value) { | ||
return value[1] != 'inherit' && validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); | ||
return value[1] != 'inherit' && validator.isStyleKeyword(value[1]) && !validator.isColorFunction(value[1]); | ||
}; | ||
@@ -51,6 +62,76 @@ } | ||
return function (value) { | ||
return value[1] != 'inherit' && validator.isValidWidth(value[1]) && !validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); | ||
return value[1] != 'inherit' && | ||
(validator.isWidth(value[1]) || validator.isUnit(value[1]) && !validator.isDynamicUnit(value[1])) && | ||
!validator.isStyleKeyword(value[1]) && | ||
!validator.isColorFunction(value[1]); | ||
}; | ||
} | ||
function animation(property, compactable, validator) { | ||
var duration = _wrapDefault('animation-duration', property, compactable); | ||
var timing = _wrapDefault('animation-timing-function', property, compactable); | ||
var delay = _wrapDefault('animation-delay', property, compactable); | ||
var iteration = _wrapDefault('animation-iteration-count', property, compactable); | ||
var direction = _wrapDefault('animation-direction', property, compactable); | ||
var fill = _wrapDefault('animation-fill-mode', property, compactable); | ||
var play = _wrapDefault('animation-play-state', property, compactable); | ||
var name = _wrapDefault('animation-name', property, compactable); | ||
var components = [duration, timing, delay, iteration, direction, fill, play, name]; | ||
var values = property.value; | ||
var value; | ||
var durationSet = false; | ||
var timingSet = false; | ||
var delaySet = false; | ||
var iterationSet = false; | ||
var directionSet = false; | ||
var fillSet = false; | ||
var playSet = false; | ||
var nameSet = false; | ||
var i; | ||
var l; | ||
if (property.value.length == 1 && property.value[0][1] == 'inherit') { | ||
duration.value = timing.value = delay.value = iteration.value = direction.value = fill.value = play.value = name.value = property.value; | ||
return components; | ||
} | ||
if (values.length > 1 && _anyIsInherit(values)) { | ||
throw new InvalidPropertyError('Invalid animation values at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); | ||
} | ||
for (i = 0, l = values.length; i < l; i++) { | ||
value = values[i]; | ||
if (validator.isTime(value[1]) && !durationSet) { | ||
duration.value = [value]; | ||
durationSet = true; | ||
} else if (validator.isTime(value[1]) && !delaySet) { | ||
delay.value = [value]; | ||
delaySet = true; | ||
} else if ((validator.isGlobal(value[1]) || validator.isAnimationTimingFunction(value[1])) && !timingSet) { | ||
timing.value = [value]; | ||
timingSet = true; | ||
} else if ((validator.isAnimationIterationCountKeyword(value[1]) || validator.isPositiveNumber(value[1])) && !iterationSet) { | ||
iteration.value = [value]; | ||
iterationSet = true; | ||
} else if (validator.isAnimationDirectionKeyword(value[1]) && !directionSet) { | ||
direction.value = [value]; | ||
directionSet = true; | ||
} else if (validator.isAnimationFillModeKeyword(value[1]) && !fillSet) { | ||
fill.value = [value]; | ||
fillSet = true; | ||
} else if (validator.isAnimationPlayStateKeyword(value[1]) && !playSet) { | ||
play.value = [value]; | ||
playSet = true; | ||
} else if ((validator.isAnimationNameKeyword(value[1]) || validator.isIdentifier(value[1])) && !nameSet) { | ||
name.value = [value]; | ||
nameSet = true; | ||
} else { | ||
throw new InvalidPropertyError('Invalid animation value at ' + formatPosition(value[2][0]) + '. Ignoring.'); | ||
} | ||
} | ||
return components; | ||
} | ||
function background(property, compactable, validator) { | ||
@@ -88,6 +169,6 @@ var image = _wrapDefault('background-image', property, compactable); | ||
if (validator.isValidBackgroundAttachment(value[1])) { | ||
if (validator.isBackgroundAttachmentKeyword(value[1])) { | ||
attachment.value = [value]; | ||
anyValueSet = true; | ||
} else if (validator.isValidBackgroundClip(value[1]) || validator.isValidBackgroundOrigin(value[1])) { | ||
} else if (validator.isBackgroundClipKeyword(value[1]) || validator.isBackgroundOriginKeyword(value[1])) { | ||
if (clipSet) { | ||
@@ -101,3 +182,3 @@ origin.value = [value]; | ||
anyValueSet = true; | ||
} else if (validator.isValidBackgroundRepeat(value[1])) { | ||
} else if (validator.isBackgroundRepeatKeyword(value[1])) { | ||
if (repeatSet) { | ||
@@ -110,9 +191,9 @@ repeat.value.unshift(value); | ||
anyValueSet = true; | ||
} else if (validator.isValidBackgroundPositionPart(value[1]) || validator.isValidBackgroundSizePart(value[1])) { | ||
} else if (validator.isBackgroundPositionKeyword(value[1]) || validator.isBackgroundSizeKeyword(value[1]) || validator.isUnit(value[1]) || validator.isDynamicUnit(value[1])) { | ||
if (i > 0) { | ||
var previousValue = values[i - 1]; | ||
if (previousValue[1] == '/') { | ||
if (previousValue[1] == Marker.FORWARD_SLASH) { | ||
size.value = [value]; | ||
} else if (i > 1 && values[i - 2][1] == '/') { | ||
} else if (i > 1 && values[i - 2][1] == Marker.FORWARD_SLASH) { | ||
size.value = [previousValue, value]; | ||
@@ -135,6 +216,6 @@ i -= 2; | ||
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]))) { | ||
} else if ((color.value[0][1] == compactable[color.name].defaultValue || color.value[0][1] == 'none') && (validator.isColor(value[1]) || validator.isPrefixed(value[1]))) { | ||
color.value = [value]; | ||
anyValueSet = true; | ||
} else if (validator.isValidUrl(value[1]) || validator.isValidFunction(value[1])) { | ||
} else if (validator.isUrl(value[1]) || validator.isFunction(value[1])) { | ||
image.value = [value]; | ||
@@ -160,3 +241,3 @@ anyValueSet = true; | ||
for (var i = 0, l = values.length; i < l; i++) { | ||
if (values[i][1] == '/') { | ||
if (values[i][1] == Marker.FORWARD_SLASH) { | ||
splitAt = i; | ||
@@ -191,2 +272,118 @@ break; | ||
function font(property, compactable, validator) { | ||
var style = _wrapDefault('font-style', property, compactable); | ||
var variant = _wrapDefault('font-variant', property, compactable); | ||
var weight = _wrapDefault('font-weight', property, compactable); | ||
var stretch = _wrapDefault('font-stretch', property, compactable); | ||
var size = _wrapDefault('font-size', property, compactable); | ||
var height = _wrapDefault('line-height', property, compactable); | ||
var family = _wrapDefault('font-family', property, compactable); | ||
var components = [style, variant, weight, stretch, size, height, family]; | ||
var values = property.value; | ||
var fuzzyMatched = 4; // style, variant, weight, and stretch | ||
var index = 0; | ||
var isStretchSet = false; | ||
var isStretchValid; | ||
var isStyleSet = false; | ||
var isStyleValid; | ||
var isVariantSet = false; | ||
var isVariantValid; | ||
var isWeightSet = false; | ||
var isWeightValid; | ||
var isSizeSet = false; | ||
var appendableFamilyName = false; | ||
if (!values[index]) { | ||
throw new InvalidPropertyError('Missing font values at ' + formatPosition(property.all[property.position][1][2][0]) + '. Ignoring.'); | ||
} | ||
if (values.length == 1 && (validator.isFontKeyword(values[0][1]) || validator.isPrefixed(values[0][1]))) { | ||
values[0][1] = Marker.INTERNAL + values[0][1]; | ||
style.value = variant.value = weight.value = stretch.value = size.value = height.value = family.value = values; | ||
return components; | ||
} | ||
if (values.length == 1 && values[0][1] == 'inherit') { | ||
style.value = variant.value = weight.value = stretch.value = size.value = height.value = family.value = values; | ||
return components; | ||
} | ||
if (values.length > 1 && _anyIsInherit(values)) { | ||
throw new InvalidPropertyError('Invalid font values at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); | ||
} | ||
// fuzzy match style, variant, weight, and stretch on first elements | ||
while (index < fuzzyMatched) { | ||
isStretchValid = validator.isFontStretchKeyword(values[index][1]) || validator.isGlobal(values[index][1]); | ||
isStyleValid = validator.isFontStyleKeyword(values[index][1]) || validator.isGlobal(values[index][1]); | ||
isVariantValid = validator.isFontVariantKeyword(values[index][1]) || validator.isGlobal(values[index][1]); | ||
isWeightValid = validator.isFontWeightKeyword(values[index][1]) || validator.isGlobal(values[index][1]); | ||
if (isStyleValid && !isStyleSet) { | ||
style.value = [values[index]]; | ||
isStyleSet = true; | ||
} else if (isVariantValid && !isVariantSet) { | ||
variant.value = [values[index]]; | ||
isVariantSet = true; | ||
} else if (isWeightValid && !isWeightSet) { | ||
weight.value = [values[index]]; | ||
isWeightSet = true; | ||
} else if (isStretchValid && !isStretchSet) { | ||
stretch.value = [values[index]]; | ||
isStretchSet = true; | ||
} else if (isStyleValid && isStyleSet || isVariantValid && isVariantSet || isWeightValid && isWeightSet || isStretchValid && isStretchSet) { | ||
throw new InvalidPropertyError('Invalid font style / variant / weight / stretch value at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); | ||
} else { | ||
break; | ||
} | ||
index++; | ||
} | ||
// now comes font-size ... | ||
if (validator.isFontSizeKeyword(values[index][1]) || validator.isUnit(values[index][1]) && !validator.isDynamicUnit(values[index][1])) { | ||
size.value = [values[index]]; | ||
isSizeSet = true; | ||
index++; | ||
} else { | ||
throw new InvalidPropertyError('Missing font size at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); | ||
} | ||
if (!values[index]) { | ||
throw new InvalidPropertyError('Missing font family at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); | ||
} | ||
// ... and perhaps line-height | ||
if (isSizeSet && values[index] && values[index][1] == Marker.FORWARD_SLASH && values[index + 1] && (validator.isLineHeightKeyword(values[index + 1][1]) || validator.isUnit(values[index + 1][1]) || validator.isNumber(values[index + 1][1]))) { | ||
height.value = [values[index + 1]]; | ||
index++; | ||
index++; | ||
} | ||
// ... and whatever comes next is font-family | ||
family.value = []; | ||
while (values[index]) { | ||
if (values[index][1] == Marker.COMMA) { | ||
appendableFamilyName = false; | ||
} else { | ||
if (appendableFamilyName) { | ||
family.value[family.value.length - 1][1] += Marker.SPACE + values[index][1]; | ||
} else { | ||
family.value.push(values[index]); | ||
} | ||
appendableFamilyName = true; | ||
} | ||
index++; | ||
} | ||
if (family.value.length === 0) { | ||
throw new InvalidPropertyError('Missing font family at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); | ||
} | ||
return components; | ||
} | ||
function fourValues(property, compactable) { | ||
@@ -254,3 +451,3 @@ var componentNames = compactable[property.name].components; | ||
for (j = 1, m = splitComponents.length; j < m; j++) { | ||
components[i].value.push([Token.PROPERTY_VALUE, MULTIPLEX_SEPARATOR]); | ||
components[i].value.push([Token.PROPERTY_VALUE, Marker.COMMA]); | ||
Array.prototype.push.apply(components[i].value, splitComponents[j][i].value); | ||
@@ -281,3 +478,3 @@ } | ||
for (index = 0, total = values.length; index < total; index++) { | ||
if (validator.isValidUrl(values[index][1]) || values[index][1] == '0') { | ||
if (validator.isUrl(values[index][1]) || values[index][1] == '0') { | ||
image.value = [values[index]]; | ||
@@ -289,6 +486,6 @@ values.splice(index, 1); | ||
// ... then `type`... | ||
// ... then `position` | ||
for (index = 0, total = values.length; index < total; index++) { | ||
if (validator.isValidListStyleType(values[index][1])) { | ||
type.value = [values[index]]; | ||
if (validator.isListStylePositionKeyword(values[index][1])) { | ||
position.value = [values[index]]; | ||
values.splice(index, 1); | ||
@@ -299,5 +496,6 @@ break; | ||
// ... and what's left is a `position` | ||
if (values.length > 0 && validator.isValidListStylePosition(values[0][1])) | ||
position.value = [values[0]]; | ||
// ... and what's left is a `type` | ||
if (values.length > 0 && (validator.isListStyleTypeKeyword(values[0][1]) || validator.isIdentifier(values[0][1]))) { | ||
type.value = [values[0]]; | ||
} | ||
@@ -368,5 +566,7 @@ return components; | ||
module.exports = { | ||
animation: animation, | ||
background: background, | ||
border: widthStyleColor, | ||
borderRadius: borderRadius, | ||
font: font, | ||
fourValues: fourValues, | ||
@@ -373,0 +573,0 @@ listStyle: listStyle, |
var understandable = require('./properties/understandable'); | ||
function animationIterationCount(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !(validator.isAnimationIterationCountKeyword(value2) || validator.isPositiveNumber(value2))) { | ||
return false; | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} | ||
return validator.isAnimationIterationCountKeyword(value2) || validator.isPositiveNumber(value2); | ||
} | ||
function animationName(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !(validator.isAnimationNameKeyword(value2) || validator.isIdentifier(value2))) { | ||
return false; | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} | ||
return validator.isAnimationNameKeyword(value2) || validator.isIdentifier(value2); | ||
} | ||
function animationTimingFunction(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !(validator.isAnimationTimingFunction(value2) || validator.isGlobal(value2))) { | ||
return false; | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} | ||
return validator.isAnimationTimingFunction(value2) || validator.isGlobal(value2); | ||
} | ||
function areSameFunction(validator, value1, value2) { | ||
if (!validator.isFunction(value1) || !validator.isFunction(value2)) { | ||
return false; | ||
} | ||
var function1Name = value1.substring(0, value1.indexOf('(')); | ||
var function2Name = value2.substring(0, value2.indexOf('(')); | ||
return function1Name === function2Name; | ||
} | ||
function backgroundPosition(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-position', value2, true)) { | ||
if (!understandable(validator, value1, value2, 0, true) && !(validator.isBackgroundPositionKeyword(value2) || validator.isGlobal(value2))) { | ||
return false; | ||
} else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} else if (validator.isValidKeywordValue('background-position', value2, true)) { | ||
} else if (validator.isBackgroundPositionKeyword(value2) || validator.isGlobal(value2)) { | ||
return true; | ||
@@ -16,7 +57,7 @@ } | ||
function backgroundSize(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-size', value2, true)) { | ||
if (!understandable(validator, value1, value2, 0, true) && !(validator.isBackgroundSizeKeyword(value2) || validator.isGlobal(value2))) { | ||
return false; | ||
} else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} else if (validator.isValidKeywordValue('background-size', value2, true)) { | ||
} else if (validator.isBackgroundSizeKeyword(value2) || validator.isGlobal(value2)) { | ||
return true; | ||
@@ -29,11 +70,11 @@ } | ||
function color(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isValidColor(value2)) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isColor(value2)) { | ||
return false; | ||
} else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} else if (!validator.colorOpacity && (validator.isValidRgbaColor(value1) || validator.isValidHslaColor(value1))) { | ||
} else if (!validator.colorOpacity && (validator.isRgbColor(value1) || validator.isHslColor(value1))) { | ||
return false; | ||
} else if (!validator.colorOpacity && (validator.isValidRgbaColor(value2) || validator.isValidHslaColor(value2))) { | ||
} else if (!validator.colorOpacity && (validator.isRgbColor(value2) || validator.isHslColor(value2))) { | ||
return false; | ||
} else if (validator.isValidColor(value1) && validator.isValidColor(value2)) { | ||
} else if (validator.isColor(value1) && validator.isColor(value2)) { | ||
return true; | ||
@@ -51,10 +92,14 @@ } | ||
function fontFamily(validator, value1, value2) { | ||
return understandable(validator, value1, value2, 0, true); | ||
} | ||
function image(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isValidImage(value2)) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isImage(value2)) { | ||
return false; | ||
} else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} else if (validator.isValidImage(value2)) { | ||
} else if (validator.isImage(value2)) { | ||
return true; | ||
} else if (validator.isValidImage(value1)) { | ||
} else if (validator.isImage(value1)) { | ||
return false; | ||
@@ -68,9 +113,9 @@ } | ||
return function(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2)) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isKeyword(propertyName)(value2)) { | ||
return false; | ||
} else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} | ||
return validator.isValidKeywordValue(propertyName, value2, false); | ||
return validator.isKeyword(propertyName)(value2); | ||
}; | ||
@@ -81,9 +126,9 @@ } | ||
return function(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2, true)) { | ||
if (!understandable(validator, value1, value2, 0, true) && !(validator.isKeyword(propertyName)(value2) || validator.isGlobal(value2))) { | ||
return false; | ||
} else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} | ||
return validator.isValidKeywordValue(propertyName, value2, true); | ||
return validator.isKeyword(propertyName)(value2) || validator.isGlobal(value2); | ||
}; | ||
@@ -93,3 +138,3 @@ } | ||
function sameFunctionOrValue(validator, value1, value2) { | ||
return validator.areSameFunction(value1, value2) ? | ||
return areSameFunction(validator, value1, value2) ? | ||
true : | ||
@@ -99,24 +144,44 @@ value1 === value2; | ||
function textShadow(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isValidTextShadow(value2)) { | ||
if (!understandable(validator, value1, value2, 0, true) && !(validator.isUnit(value2) || validator.isColor(value2) || validator.isGlobal(value2))) { | ||
return false; | ||
} else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} | ||
return validator.isValidTextShadow(value2); | ||
return validator.isUnit(value2) || validator.isColor(value2) || validator.isGlobal(value2); | ||
} | ||
function time(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isTime(value2)) { | ||
return false; | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} else if (validator.isTime(value1) && !validator.isTime(value2)) { | ||
return false; | ||
} else if (validator.isTime(value2)) { | ||
return true; | ||
} else if (validator.isTime(value1)) { | ||
return false; | ||
} else if (validator.isFunction(value1) && !validator.isPrefixed(value1) && validator.isFunction(value2) && !validator.isPrefixed(value2)) { | ||
return true; | ||
} | ||
return sameFunctionOrValue(validator, value1, value2); | ||
} | ||
function unit(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isValidUnitWithoutFunction(value2)) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isUnit(value2)) { | ||
return false; | ||
} else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} else if (validator.isValidUnitWithoutFunction(value1) && !validator.isValidUnitWithoutFunction(value2)) { | ||
} else if (validator.isUnit(value1) && !validator.isUnit(value2)) { | ||
return false; | ||
} else if (validator.isValidUnitWithoutFunction(value2)) { | ||
} else if (validator.isUnit(value2)) { | ||
return true; | ||
} else if (validator.isValidUnitWithoutFunction(value1)) { | ||
} else if (validator.isUnit(value1)) { | ||
return false; | ||
} else if (validator.isValidFunctionWithoutVendorPrefix(value1) && validator.isValidFunctionWithoutVendorPrefix(value2)) { | ||
} else if (validator.isFunction(value1) && !validator.isPrefixed(value1) && validator.isFunction(value2) && !validator.isPrefixed(value2)) { | ||
return true; | ||
@@ -137,9 +202,9 @@ } | ||
function zIndex(validator, value1, value2) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isValidZIndex(value2)) { | ||
if (!understandable(validator, value1, value2, 0, true) && !validator.isZIndex(value2)) { | ||
return false; | ||
} else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { | ||
} else if (validator.isVariable(value1) && validator.isVariable(value2)) { | ||
return true; | ||
} | ||
return validator.isValidZIndex(value2); | ||
return validator.isZIndex(value2); | ||
} | ||
@@ -152,5 +217,12 @@ | ||
image: image, | ||
time: time, | ||
unit: unit | ||
}, | ||
property: { | ||
animationDirection: keywordWithGlobal('animation-direction'), | ||
animationFillMode: keyword('animation-fill-mode'), | ||
animationIterationCount: animationIterationCount, | ||
animationName: animationName, | ||
animationPlayState: keywordWithGlobal('animation-play-state'), | ||
animationTimingFunction: animationTimingFunction, | ||
backgroundAttachment: keyword('background-attachment'), | ||
@@ -169,4 +241,7 @@ backgroundClip: keywordWithGlobal('background-clip'), | ||
float: keywordWithGlobal('float'), | ||
left: unitOrKeywordWithGlobal('left'), | ||
fontFamily: fontFamily, | ||
fontStretch: keywordWithGlobal('font-stretch'), | ||
fontStyle: keywordWithGlobal('font-style'), | ||
left: unitOrKeywordWithGlobal('left'), | ||
fontVariant: keywordWithGlobal('font-variant'), | ||
fontWeight: keywordWithGlobal('font-weight'), | ||
@@ -173,0 +248,0 @@ listStyleType: keywordWithGlobal('list-style-type'), |
@@ -38,2 +38,137 @@ // Contains the interpretation of CSS properties, as used by the property optimizer | ||
var compactable = { | ||
'animation': { | ||
canOverride: canOverride.generic.components([ | ||
canOverride.generic.time, | ||
canOverride.property.animationTimingFunction, | ||
canOverride.generic.time, | ||
canOverride.property.animationIterationCount, | ||
canOverride.property.animationDirection, | ||
canOverride.property.animationFillMode, | ||
canOverride.property.animationPlayState, | ||
canOverride.property.animationName | ||
]), | ||
components: [ | ||
'animation-duration', | ||
'animation-timing-function', | ||
'animation-delay', | ||
'animation-iteration-count', | ||
'animation-direction', | ||
'animation-fill-mode', | ||
'animation-play-state', | ||
'animation-name' | ||
], | ||
breakUp: breakUp.multiplex(breakUp.animation), | ||
defaultValue: 'none', | ||
restore: restore.multiplex(restore.withoutDefaults), | ||
shorthand: true, | ||
vendorPrefixes: [ | ||
'-moz-', | ||
'-o-', | ||
'-webkit-' | ||
] | ||
}, | ||
'animation-delay': { | ||
canOverride: canOverride.generic.time, | ||
componentOf: [ | ||
'animation' | ||
], | ||
defaultValue: '0s', | ||
intoMultiplexMode: 'real', | ||
vendorPrefixes: [ | ||
'-moz-', | ||
'-o-', | ||
'-webkit-' | ||
] | ||
}, | ||
'animation-direction': { | ||
canOverride: canOverride.property.animationDirection, | ||
componentOf: [ | ||
'animation' | ||
], | ||
defaultValue: 'normal', | ||
intoMultiplexMode: 'real', | ||
vendorPrefixes: [ | ||
'-moz-', | ||
'-o-', | ||
'-webkit-' | ||
] | ||
}, | ||
'animation-duration': { | ||
canOverride: canOverride.generic.time, | ||
componentOf: [ | ||
'animation' | ||
], | ||
defaultValue: '0s', | ||
intoMultiplexMode: 'real', | ||
vendorPrefixes: [ | ||
'-moz-', | ||
'-o-', | ||
'-webkit-' | ||
] | ||
}, | ||
'animation-fill-mode': { | ||
canOverride: canOverride.property.animationFillMode, | ||
componentOf: [ | ||
'animation' | ||
], | ||
defaultValue: 'none', | ||
intoMultiplexMode: 'real', | ||
vendorPrefixes: [ | ||
'-moz-', | ||
'-o-', | ||
'-webkit-' | ||
] | ||
}, | ||
'animation-iteration-count': { | ||
canOverride: canOverride.property.animationIterationCount, | ||
componentOf: [ | ||
'animation' | ||
], | ||
defaultValue: '1', | ||
intoMultiplexMode: 'real', | ||
vendorPrefixes: [ | ||
'-moz-', | ||
'-o-', | ||
'-webkit-' | ||
] | ||
}, | ||
'animation-name': { | ||
canOverride: canOverride.property.animationName, | ||
componentOf: [ | ||
'animation' | ||
], | ||
defaultValue: 'none', | ||
intoMultiplexMode: 'real', | ||
vendorPrefixes: [ | ||
'-moz-', | ||
'-o-', | ||
'-webkit-' | ||
] | ||
}, | ||
'animation-play-state': { | ||
canOverride: canOverride.property.animationPlayState, | ||
componentOf: [ | ||
'animation' | ||
], | ||
defaultValue: 'running', | ||
intoMultiplexMode: 'real', | ||
vendorPrefixes: [ | ||
'-moz-', | ||
'-o-', | ||
'-webkit-' | ||
] | ||
}, | ||
'animation-timing-function': { | ||
canOverride: canOverride.property.animationTimingFunction, | ||
componentOf: [ | ||
'animation' | ||
], | ||
defaultValue: 'ease', | ||
intoMultiplexMode: 'real', | ||
vendorPrefixes: [ | ||
'-moz-', | ||
'-o-', | ||
'-webkit-' | ||
] | ||
}, | ||
'background': { | ||
@@ -71,3 +206,4 @@ canOverride: canOverride.generic.components([ | ||
], | ||
defaultValue: 'scroll' | ||
defaultValue: 'scroll', | ||
intoMultiplexMode: 'real' | ||
}, | ||
@@ -80,2 +216,3 @@ 'background-clip': { | ||
defaultValue: 'border-box', | ||
intoMultiplexMode: 'real', | ||
shortestValue: 'border-box' | ||
@@ -89,2 +226,3 @@ }, | ||
defaultValue: 'transparent', | ||
intoMultiplexMode: 'real', // otherwise real color will turn into default since color appears in last multiplex only | ||
multiplexLastOnly: true, | ||
@@ -99,3 +237,4 @@ nonMergeableValue: 'none', | ||
], | ||
defaultValue: 'none' | ||
defaultValue: 'none', | ||
intoMultiplexMode: 'default' | ||
}, | ||
@@ -108,2 +247,3 @@ 'background-origin': { | ||
defaultValue: 'padding-box', | ||
intoMultiplexMode: 'real', | ||
shortestValue: 'border-box' | ||
@@ -118,2 +258,3 @@ }, | ||
doubleValues: true, | ||
intoMultiplexMode: 'real', | ||
shortestValue: '0' | ||
@@ -127,3 +268,4 @@ }, | ||
defaultValue: ['repeat'], | ||
doubleValues: true | ||
doubleValues: true, | ||
intoMultiplexMode: 'real' | ||
}, | ||
@@ -137,2 +279,3 @@ 'background-size': { | ||
doubleValues: true, | ||
intoMultiplexMode: 'real', | ||
shortestValue: '0 0' | ||
@@ -228,2 +371,3 @@ }, | ||
defaultValue: 'medium', | ||
oppositeTo: 'border-top-width', | ||
shortestValue: '0' | ||
@@ -243,3 +387,5 @@ }, | ||
]), | ||
componentOf: ['border'], | ||
componentOf: [ | ||
'border' | ||
], | ||
components: [ | ||
@@ -295,2 +441,3 @@ 'border-top-color', | ||
defaultValue: 'medium', | ||
oppositeTo: 'border-right-width', | ||
shortestValue: '0' | ||
@@ -359,2 +506,3 @@ }, | ||
defaultValue: 'medium', | ||
oppositeTo: 'border-left-width', | ||
shortestValue: '0' | ||
@@ -444,2 +592,3 @@ }, | ||
defaultValue: 'medium', | ||
oppositeTo: 'border-bottom-width', | ||
shortestValue: '0' | ||
@@ -455,2 +604,5 @@ }, | ||
]), | ||
componentOf: [ | ||
'border' | ||
], | ||
components: [ | ||
@@ -487,2 +639,29 @@ 'border-top-width', | ||
}, | ||
'font': { | ||
breakUp: breakUp.font, | ||
canOverride: canOverride.generic.components([ | ||
canOverride.property.fontStyle, | ||
canOverride.property.fontVariant, | ||
canOverride.property.fontWeight, | ||
canOverride.property.fontStretch, | ||
canOverride.generic.unit, | ||
canOverride.generic.unit, | ||
canOverride.property.fontFamily | ||
]), | ||
components: [ | ||
'font-style', | ||
'font-variant', | ||
'font-weight', | ||
'font-stretch', | ||
'font-size', | ||
'line-height', | ||
'font-family' | ||
], | ||
restore: restore.font, | ||
shorthand: true | ||
}, | ||
'font-family': { | ||
canOverride: canOverride.property.fontFamily, | ||
defaultValue: 'user|agent|specific' | ||
}, | ||
'font-size': { | ||
@@ -493,2 +672,6 @@ canOverride: canOverride.generic.unit, | ||
}, | ||
'font-stretch': { | ||
canOverride: canOverride.property.fontStretch, | ||
defaultValue: 'normal' | ||
}, | ||
'font-style': { | ||
@@ -498,5 +681,9 @@ canOverride: canOverride.property.fontStyle, | ||
}, | ||
'font-variant': { | ||
canOverride: canOverride.property.fontVariant, | ||
defaultValue: 'normal' | ||
}, | ||
'font-weight': { | ||
canOverride: canOverride.property.fontWeight, | ||
defaultValue: '400', | ||
defaultValue: 'normal', | ||
shortestValue: '400' | ||
@@ -584,3 +771,4 @@ }, | ||
], | ||
defaultValue: '0' | ||
defaultValue: '0', | ||
oppositeTo: 'margin-top' | ||
}, | ||
@@ -592,3 +780,4 @@ 'margin-left': { | ||
], | ||
defaultValue: '0' | ||
defaultValue: '0', | ||
oppositeTo: 'margin-right' | ||
}, | ||
@@ -600,3 +789,4 @@ 'margin-right': { | ||
], | ||
defaultValue: '0' | ||
defaultValue: '0', | ||
oppositeTo: 'margin-left' | ||
}, | ||
@@ -608,3 +798,4 @@ 'margin-top': { | ||
], | ||
defaultValue: '0' | ||
defaultValue: '0', | ||
oppositeTo: 'margin-bottom' | ||
}, | ||
@@ -685,3 +876,4 @@ 'outline': { | ||
], | ||
defaultValue: '0' | ||
defaultValue: '0', | ||
oppositeTo: 'padding-top' | ||
}, | ||
@@ -693,3 +885,4 @@ 'padding-left': { | ||
], | ||
defaultValue: '0' | ||
defaultValue: '0', | ||
oppositeTo: 'padding-right' | ||
}, | ||
@@ -701,3 +894,4 @@ 'padding-right': { | ||
], | ||
defaultValue: '0' | ||
defaultValue: '0', | ||
oppositeTo: 'padding-left' | ||
}, | ||
@@ -709,3 +903,4 @@ 'padding-top': { | ||
], | ||
defaultValue: '0' | ||
defaultValue: '0', | ||
oppositeTo: 'padding-bottom' | ||
}, | ||
@@ -712,0 +907,0 @@ 'position': { |
@@ -17,2 +17,15 @@ var Marker = require('../../tokenizer/marker'); | ||
var RELATION_PATTERN = /[>\+~]/; | ||
var UNMIXABLE_PSEUDO_CLASSES = [ | ||
':after', | ||
':before', | ||
':first-letter', | ||
':first-line', | ||
':lang' | ||
]; | ||
var UNMIXABLE_PSEUDO_ELEMENTS = [ | ||
'::after', | ||
'::before', | ||
'::first-letter', | ||
'::first-line' | ||
]; | ||
@@ -25,3 +38,3 @@ var Level = { | ||
function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements) { | ||
function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) { | ||
var singleSelectors = split(selector, Marker.COMMA); | ||
@@ -36,3 +49,3 @@ var singleSelector; | ||
isDeepSelector(singleSelector) || | ||
(singleSelector.indexOf(Marker.COLON) > -1 && !areMergeable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements))) { | ||
(singleSelector.indexOf(Marker.COLON) > -1 && !areMergeable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging))) { | ||
return false; | ||
@@ -128,7 +141,7 @@ } | ||
function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements) { | ||
function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) { | ||
return areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) && | ||
needArguments(matches) && | ||
(matches.length < 2 || !someIncorrectlyChained(selector, matches)) && | ||
(matches.length < 2 || !someMixed(matches)); | ||
(matches.length < 2 || multiplePseudoMerging && allMixable(matches)); | ||
} | ||
@@ -224,4 +237,4 @@ | ||
function someMixed(matches) { | ||
var firstIsPseudoElement = DOUBLE_COLON_PATTERN.test(matches[0]); | ||
function allMixable(matches) { | ||
var unmixableMatches = 0; | ||
var match; | ||
@@ -233,10 +246,20 @@ var i, l; | ||
if (DOUBLE_COLON_PATTERN.test(match) != firstIsPseudoElement) { | ||
return true; | ||
if (isPseudoElement(match)) { | ||
unmixableMatches += UNMIXABLE_PSEUDO_ELEMENTS.indexOf(match) > -1 ? 1 : 0; | ||
} else { | ||
unmixableMatches += UNMIXABLE_PSEUDO_CLASSES.indexOf(match) > -1 ? 1 : 0; | ||
} | ||
if (unmixableMatches > 1) { | ||
return false; | ||
} | ||
} | ||
return false; | ||
return true; | ||
} | ||
function isPseudoElement(pseudo) { | ||
return DOUBLE_COLON_PATTERN.test(pseudo); | ||
} | ||
module.exports = isMergeable; |
@@ -22,3 +22,4 @@ var isMergeable = require('./is-mergeable'); | ||
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; | ||
var mergeLimit = 8191; | ||
var mergeLimit = options.compatibility.selectors.mergeLimit; | ||
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; | ||
@@ -38,4 +39,4 @@ for (var i = 0, l = tokens.length; i < l; i++) { | ||
} else if (lastToken[0] == Token.RULE && serializeBody(token[2]) == serializeBody(lastToken[2]) && | ||
isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) && | ||
isMergeable(serializeRules(lastToken[1]), mergeablePseudoClasses, mergeablePseudoElements) && | ||
isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) && | ||
isMergeable(serializeRules(lastToken[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) && | ||
lastToken[1].length < mergeLimit) { | ||
@@ -42,0 +43,0 @@ lastToken[1] = tidyRules(lastToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings); |
@@ -45,2 +45,3 @@ var isMergeable = require('./is-mergeable'); | ||
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; | ||
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; | ||
var candidates = {}; | ||
@@ -62,4 +63,4 @@ | ||
if (oldToken && | ||
isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) && | ||
isMergeable(serializeRules(oldToken[1]), mergeablePseudoClasses, mergeablePseudoElements)) { | ||
isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) && | ||
isMergeable(serializeRules(oldToken[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging)) { | ||
@@ -66,0 +67,0 @@ if (token[2].length > 0) { |
@@ -9,2 +9,3 @@ var mergeAdjacent = require('./merge-adjacent'); | ||
var removeDuplicates = require('./remove-duplicates'); | ||
var removeUnusedAtRules = require('./remove-unused-at-rules'); | ||
var restructure = require('./restructure'); | ||
@@ -31,2 +32,5 @@ | ||
break; | ||
case Token.AT_RULE: | ||
isEmpty = token[1].length === 0; | ||
break; | ||
case Token.AT_RULE_BLOCK: | ||
@@ -114,2 +118,6 @@ isEmpty = token[2].length === 0; | ||
if (levelOptions.removeUnusedAtRules) { | ||
removeUnusedAtRules(tokens, context); | ||
} | ||
if (levelOptions.mergeMedia) { | ||
@@ -122,3 +130,5 @@ reduced = mergeMediaQueries(tokens, context); | ||
removeEmpty(tokens); | ||
if (levelOptions.removeEmpty) { | ||
removeEmpty(tokens); | ||
} | ||
@@ -125,0 +135,0 @@ return tokens; |
@@ -7,15 +7,126 @@ var everyValuesPair = require('./every-values-pair'); | ||
var deepClone = require('../clone').deep; | ||
var restoreWithComponents = require('../restore-with-components'); | ||
var restoreFromOptimizing = require('../../restore-from-optimizing'); | ||
var wrapSingle = require('../../wrap-for-optimizing').single; | ||
var serializeBody = require('../../../writer/one-time').body; | ||
var Token = require('../../../tokenizer/token'); | ||
function mergeIntoShorthands(properties, validator) { | ||
var candidates = {}; | ||
var descriptor; | ||
var componentOf; | ||
var property; | ||
var i, l; | ||
var j, m; | ||
// there is no shorthand property made up of less than 3 longhands | ||
if (properties.length < 3) { | ||
return; | ||
} | ||
for (i = 0, l = properties.length; i < l; i++) { | ||
property = properties[i]; | ||
descriptor = compactable[property.name]; | ||
if (property.unused) { | ||
continue; | ||
} | ||
if (property.hack) { | ||
continue; | ||
} | ||
if (property.block) { | ||
continue; | ||
} | ||
invalidateOrCompact(properties, i, candidates, validator); | ||
if (descriptor && descriptor.componentOf) { | ||
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); | ||
} | ||
function invalidateOrCompact(properties, position, candidates, validator) { | ||
var invalidatedBy = properties[position]; | ||
var shorthandName; | ||
var shorthandDescriptor; | ||
var candidateComponents; | ||
for (shorthandName in candidates) { | ||
if (undefined !== invalidatedBy && shorthandName == invalidatedBy.name) { | ||
continue; | ||
} | ||
shorthandDescriptor = compactable[shorthandName]; | ||
candidateComponents = candidates[shorthandName]; | ||
if (invalidatedBy && invalidates(candidates, shorthandName, invalidatedBy)) { | ||
delete candidates[shorthandName]; | ||
continue; | ||
} | ||
if (shorthandDescriptor.components.length > Object.keys(candidateComponents).length) { | ||
continue; | ||
} | ||
if (mixedImportance(candidateComponents)) { | ||
continue; | ||
} | ||
if (!overridable(candidateComponents, shorthandName, validator)) { | ||
continue; | ||
} | ||
if (!mergeable(candidateComponents)) { | ||
continue; | ||
} | ||
if (mixedInherit(candidateComponents)) { | ||
replaceWithInheritBestFit(properties, candidateComponents, shorthandName, validator); | ||
} else { | ||
replaceWithShorthand(properties, candidateComponents, shorthandName, validator); | ||
} | ||
} | ||
} | ||
function invalidates(candidates, shorthandName, invalidatedBy) { | ||
var shorthandDescriptor = compactable[shorthandName]; | ||
var invalidatedByDescriptor = compactable[invalidatedBy.name]; | ||
var componentName; | ||
if ('overridesShorthands' in shorthandDescriptor && shorthandDescriptor.overridesShorthands.indexOf(invalidatedBy.name) > -1) { | ||
return true; | ||
} | ||
if (invalidatedByDescriptor && 'componentOf' in invalidatedByDescriptor) { | ||
for (componentName in candidates[shorthandName]) { | ||
if (invalidatedByDescriptor.componentOf.indexOf(componentName) > -1) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
function mixedImportance(components) { | ||
var important; | ||
var componentName; | ||
for (var name in components) { | ||
if (undefined !== important && components[name].important != important) | ||
for (componentName in components) { | ||
if (undefined !== important && components[componentName].important != important) { | ||
return true; | ||
} | ||
important = components[name].important; | ||
important = components[componentName].important; | ||
} | ||
@@ -26,2 +137,181 @@ | ||
function overridable(components, shorthandName, validator) { | ||
var descriptor = compactable[shorthandName]; | ||
var newValuePlaceholder = [ | ||
Token.PROPERTY, | ||
[Token.PROPERTY_NAME, shorthandName], | ||
[Token.PROPERTY_VALUE, descriptor.defaultValue] | ||
]; | ||
var newProperty = wrapSingle(newValuePlaceholder); | ||
var component; | ||
var mayOverride; | ||
var i, l; | ||
populateComponents([newProperty], validator, []); | ||
for (i = 0, l = descriptor.components.length; i < l; i++) { | ||
component = components[descriptor.components[i]]; | ||
mayOverride = compactable[component.name].canOverride; | ||
if (!everyValuesPair(mayOverride.bind(null, validator), newProperty.components[i], component)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
function mergeable(components) { | ||
var lastCount = null; | ||
var currentCount; | ||
var componentName; | ||
var component; | ||
var descriptor; | ||
var values; | ||
for (componentName in components) { | ||
component = components[componentName]; | ||
descriptor = compactable[componentName]; | ||
if (!('restore' in descriptor)) { | ||
continue; | ||
} | ||
restoreFromOptimizing([component.all[component.position]], restoreWithComponents); | ||
values = descriptor.restore(component, compactable); | ||
currentCount = values.length; | ||
if (lastCount !== null && currentCount !== lastCount) { | ||
return false; | ||
} | ||
lastCount = currentCount; | ||
} | ||
return true; | ||
} | ||
function mixedInherit(components) { | ||
var componentName; | ||
var lastValue = null; | ||
var currentValue; | ||
for (componentName in components) { | ||
currentValue = hasInherit(components[componentName]); | ||
if (lastValue !== null && lastValue !== currentValue) { | ||
return true; | ||
} | ||
lastValue = currentValue; | ||
} | ||
return false; | ||
} | ||
function replaceWithInheritBestFit(properties, candidateComponents, shorthandName, validator) { | ||
var viaLonghands = buildSequenceWithInheritLonghands(candidateComponents, shorthandName, validator); | ||
var viaShorthand = buildSequenceWithInheritShorthand(candidateComponents, shorthandName, validator); | ||
var longhandTokensSequence = viaLonghands[0]; | ||
var shorthandTokensSequence = viaShorthand[0]; | ||
var isLonghandsShorter = serializeBody(longhandTokensSequence).length < serializeBody(shorthandTokensSequence).length; | ||
var newTokensSequence = isLonghandsShorter ? longhandTokensSequence : shorthandTokensSequence; | ||
var newProperty = isLonghandsShorter ? viaLonghands[1] : viaShorthand[1]; | ||
var newComponents = isLonghandsShorter ? viaLonghands[2] : viaShorthand[2]; | ||
var all = candidateComponents[Object.keys(candidateComponents)[0]].all; | ||
var componentName; | ||
var oldComponent; | ||
var newComponent; | ||
var newToken; | ||
newProperty.position = all.length; | ||
newProperty.shorthand = true; | ||
newProperty.dirty = true; | ||
newProperty.all = all; | ||
newProperty.all.push(newTokensSequence[0]); | ||
properties.push(newProperty); | ||
for (componentName in candidateComponents) { | ||
oldComponent = candidateComponents[componentName]; | ||
oldComponent.unused = true; | ||
if (oldComponent.name in newComponents) { | ||
newComponent = newComponents[oldComponent.name]; | ||
newToken = findTokenIn(newTokensSequence, componentName); | ||
newComponent.position = all.length; | ||
newComponent.all = all; | ||
newComponent.all.push(newToken); | ||
properties.push(newComponent); | ||
} | ||
} | ||
} | ||
function buildSequenceWithInheritLonghands(components, shorthandName, validator) { | ||
var tokensSequence = []; | ||
var inheritComponents = {}; | ||
var nonInheritComponents = {}; | ||
var descriptor = compactable[shorthandName]; | ||
var shorthandToken = [ | ||
Token.PROPERTY, | ||
[Token.PROPERTY_NAME, shorthandName], | ||
[Token.PROPERTY_VALUE, descriptor.defaultValue] | ||
]; | ||
var newProperty = wrapSingle(shorthandToken); | ||
var component; | ||
var longhandToken; | ||
var newComponent; | ||
var nameMetadata; | ||
var i, l; | ||
populateComponents([newProperty], validator, []); | ||
for (i = 0, l = descriptor.components.length; i < l; i++) { | ||
component = components[descriptor.components[i]]; | ||
if (hasInherit(component)) { | ||
longhandToken = component.all[component.position].slice(0, 2); | ||
Array.prototype.push.apply(longhandToken, component.value); | ||
tokensSequence.push(longhandToken); | ||
newComponent = deepClone(component); | ||
newComponent.value = inferComponentValue(components, newComponent.name); | ||
newProperty.components[i] = newComponent; | ||
inheritComponents[component.name] = deepClone(component); | ||
} else { | ||
newComponent = deepClone(component); | ||
newComponent.all = component.all; | ||
newProperty.components[i] = newComponent; | ||
nonInheritComponents[component.name] = component; | ||
} | ||
} | ||
nameMetadata = joinMetadata(nonInheritComponents, 1); | ||
shorthandToken[1].push(nameMetadata); | ||
restoreFromOptimizing([newProperty], restoreWithComponents); | ||
shorthandToken = shorthandToken.slice(0, 2); | ||
Array.prototype.push.apply(shorthandToken, newProperty.value); | ||
tokensSequence.unshift(shorthandToken); | ||
return [tokensSequence, newProperty, inheritComponents]; | ||
} | ||
function inferComponentValue(components, propertyName) { | ||
var descriptor = compactable[propertyName]; | ||
if ('oppositeTo' in descriptor) { | ||
return components[descriptor.oppositeTo].value; | ||
} else { | ||
return [[Token.PROPERTY_VALUE, descriptor.defaultValue]]; | ||
} | ||
} | ||
function joinMetadata(components, at) { | ||
@@ -32,6 +322,6 @@ var metadata = []; | ||
var componentMetadata; | ||
var name; | ||
var componentName; | ||
for (name in components) { | ||
component = components[name]; | ||
for (componentName in components) { | ||
component = components[componentName]; | ||
originalValue = component.all[component.position]; | ||
@@ -43,15 +333,83 @@ componentMetadata = originalValue[at][originalValue[at].length - 1]; | ||
return metadata; | ||
return metadata.sort(metadataSorter); | ||
} | ||
function replaceWithShorthand(properties, candidateComponents, name, validator) { | ||
var descriptor = compactable[name]; | ||
function metadataSorter(metadata1, metadata2) { | ||
var line1 = metadata1[0]; | ||
var line2 = metadata2[0]; | ||
var column1 = metadata1[1]; | ||
var column2 = metadata2[1]; | ||
if (line1 < line2) { | ||
return -1; | ||
} else if (line1 === line2) { | ||
return column1 < column2 ? -1 : 1; | ||
} else { | ||
return 1; | ||
} | ||
} | ||
function buildSequenceWithInheritShorthand(components, shorthandName, validator) { | ||
var tokensSequence = []; | ||
var inheritComponents = {}; | ||
var nonInheritComponents = {}; | ||
var descriptor = compactable[shorthandName]; | ||
var shorthandToken = [ | ||
Token.PROPERTY, | ||
[Token.PROPERTY_NAME, shorthandName], | ||
[Token.PROPERTY_VALUE, 'inherit'] | ||
]; | ||
var newProperty = wrapSingle(shorthandToken); | ||
var component; | ||
var longhandToken; | ||
var nameMetadata; | ||
var valueMetadata; | ||
var i, l; | ||
populateComponents([newProperty], validator, []); | ||
for (i = 0, l = descriptor.components.length; i < l; i++) { | ||
component = components[descriptor.components[i]]; | ||
if (hasInherit(component)) { | ||
inheritComponents[component.name] = component; | ||
} else { | ||
longhandToken = component.all[component.position].slice(0, 2); | ||
Array.prototype.push.apply(longhandToken, component.value); | ||
tokensSequence.push(longhandToken); | ||
nonInheritComponents[component.name] = deepClone(component); | ||
} | ||
} | ||
nameMetadata = joinMetadata(inheritComponents, 1); | ||
shorthandToken[1].push(nameMetadata); | ||
valueMetadata = joinMetadata(inheritComponents, 2); | ||
shorthandToken[2].push(valueMetadata); | ||
tokensSequence.unshift(shorthandToken); | ||
return [tokensSequence, newProperty, nonInheritComponents]; | ||
} | ||
function findTokenIn(tokens, componentName) { | ||
var i, l; | ||
for (i = 0, l = tokens.length; i < l; i++) { | ||
if (tokens[i][1][1] == componentName) { | ||
return tokens[i]; | ||
} | ||
} | ||
} | ||
function replaceWithShorthand(properties, candidateComponents, shorthandName, validator) { | ||
var descriptor = compactable[shorthandName]; | ||
var nameMetadata; | ||
var valueMetadata; | ||
var newValuePlaceholder = [ | ||
Token.PROPERTY, | ||
[Token.PROPERTY_NAME, name], | ||
[Token.PROPERTY_NAME, shorthandName], | ||
[Token.PROPERTY_VALUE, descriptor.defaultValue] | ||
]; | ||
var mayOverride; | ||
var all; | ||
@@ -68,9 +426,2 @@ | ||
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); | ||
@@ -99,65 +450,2 @@ newProperty.important = component.important; | ||
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; |
@@ -14,3 +14,4 @@ var mergeIntoShorthands = require('./merge-into-shorthands'); | ||
function optimizeProperties(properties, withOverriding, withMerging, context) { | ||
var _properties = wrapForOptimizing(properties, false); | ||
var levelOptions = context.options.level[OptimizationLevel.Two]; | ||
var _properties = wrapForOptimizing(properties, false, levelOptions.skipProperties); | ||
var _property; | ||
@@ -28,8 +29,8 @@ var i, l; | ||
if (withOverriding && context.options.level[OptimizationLevel.Two].overrideProperties) { | ||
overrideProperties(_properties, withMerging, context.options.compatibility, context.validator); | ||
if (withMerging && levelOptions.mergeIntoShorthands) { | ||
mergeIntoShorthands(_properties, context.validator); | ||
} | ||
if (withMerging && context.options.level[OptimizationLevel.Two].mergeIntoShorthands) { | ||
mergeIntoShorthands(_properties, context.validator); | ||
if (withOverriding && levelOptions.overrideProperties) { | ||
overrideProperties(_properties, withMerging, context.options.compatibility, context.validator); | ||
} | ||
@@ -36,0 +37,0 @@ |
@@ -5,2 +5,3 @@ var hasInherit = require('./has-inherit'); | ||
var isComponentOf = require('./is-component-of'); | ||
var isMergeableShorthand = require('./is-mergeable-shorthand'); | ||
var overridesNonComponentShorthand = require('./overrides-non-component-shorthand'); | ||
@@ -77,12 +78,18 @@ var sameVendorPrefixesIn = require('./vendor-prefixes').same; | ||
for (var i = 0, l = property.components.length; i < l; i++) { | ||
var component = property.components[i]; | ||
if (component.multiplex) | ||
continue; | ||
if (compactable[property.name].shorthand) { | ||
turnShorthandValueIntoMultiplex(property, size); | ||
} else { | ||
turnLonghandValueIntoMultiplex(property, size); | ||
} | ||
} | ||
var value = component.value.slice(0); | ||
function turnShorthandValueIntoMultiplex(property, size) { | ||
var component; | ||
var i, l; | ||
for (var j = 1; j < size; j++) { | ||
component.value.push([Token.PROPERTY_VALUE, Marker.COMMA]); | ||
Array.prototype.push.apply(component.value, value); | ||
for (i = 0, l = property.components.length; i < l; i++) { | ||
component = property.components[i]; | ||
if (!component.multiplex) { | ||
turnLonghandValueIntoMultiplex(component, size); | ||
} | ||
@@ -92,2 +99,24 @@ } | ||
function turnLonghandValueIntoMultiplex(property, size) { | ||
var withRealValue = compactable[property.name].intoMultiplexMode == 'real'; | ||
var withValue = withRealValue ? | ||
property.value.slice(0) : | ||
compactable[property.name].defaultValue; | ||
var i = multiplexSize(property); | ||
var j; | ||
var m = withValue.length; | ||
for (; i < size; i++) { | ||
property.value.push([Token.PROPERTY_VALUE, Marker.COMMA]); | ||
if (Array.isArray(withValue)) { | ||
for (j = 0; j < m; j++) { | ||
property.value.push(withRealValue ? withValue[j] : [Token.PROPERTY_VALUE, withValue[j]]); | ||
} | ||
} else { | ||
property.value.push(withRealValue ? withValue : [Token.PROPERTY_VALUE, withValue]); | ||
} | ||
} | ||
} | ||
function multiplexSize(component) { | ||
@@ -130,4 +159,5 @@ var size = 0; | ||
for (var i = 0, l = shorthand.components.length; i < l; i++) { | ||
if (anyValue(validator.isValidFunction, shorthand.components[i])) | ||
if (!anyValue(validator.isUrl, shorthand.components[i]) && anyValue(validator.isFunction, shorthand.components[i])) { | ||
return true; | ||
} | ||
} | ||
@@ -278,5 +308,10 @@ | ||
if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) | ||
if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator)) | ||
continue; | ||
if (!isMergeableShorthand(right)) { | ||
left.unused = true; | ||
continue; | ||
} | ||
component = findComponentIn(right, left); | ||
@@ -297,3 +332,3 @@ mayOverride = compactable[left.name].canOverride; | ||
if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) { | ||
if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator)) { | ||
continue; | ||
@@ -334,2 +369,5 @@ } | ||
if (!isMergeableShorthand(left)) | ||
continue; | ||
component = findComponentIn(left, right); | ||
@@ -377,2 +415,7 @@ if (everyValuesPair(mayOverride.bind(null, validator), component, right)) { | ||
if (!isMergeableShorthand(right)) { | ||
left.unused = true; | ||
continue; | ||
} | ||
for (k = left.components.length - 1; k >= 0; k--) { | ||
@@ -379,0 +422,0 @@ var leftComponent = left.components[k]; |
@@ -8,3 +8,3 @@ var sameVendorPrefixes = require('./vendor-prefixes').same; | ||
if (isPaired && validator.isValidVariable(value1) !== validator.isValidVariable(value2)) { | ||
if (isPaired && validator.isVariable(value1) !== validator.isVariable(value2)) { | ||
return false; | ||
@@ -11,0 +11,0 @@ } |
@@ -16,2 +16,3 @@ var isMergeable = require('./is-mergeable'); | ||
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; | ||
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; | ||
var candidates = {}; | ||
@@ -31,3 +32,3 @@ var repeated = []; | ||
var isComplexAndNotSpecial = token[1].length > 1 && | ||
isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements); | ||
isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging); | ||
var wrappedSelectors = wrappedSelectorsFrom(token[1]); | ||
@@ -93,2 +94,3 @@ var selectors = isComplexAndNotSpecial ? | ||
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; | ||
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; | ||
var localContext = {}; | ||
@@ -115,3 +117,3 @@ | ||
var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements) ? | ||
var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) ? | ||
into[0].list : | ||
@@ -118,0 +120,0 @@ [complexSelector]; |
@@ -138,2 +138,55 @@ var shallowClone = require('./clone').shallow; | ||
function font(property, compactable) { | ||
var components = property.components; | ||
var restored = []; | ||
var component; | ||
var componentIndex = 0; | ||
var fontFamilyIndex = 0; | ||
if (property.value[0][1].indexOf(Marker.INTERNAL) === 0) { | ||
property.value[0][1] = property.value[0][1].substring(Marker.INTERNAL.length); | ||
return property.value; | ||
} | ||
// first four components are optional | ||
while (componentIndex < 4) { | ||
component = components[componentIndex]; | ||
if (component.value[0][1] != compactable[component.name].defaultValue) { | ||
Array.prototype.push.apply(restored, component.value); | ||
} | ||
componentIndex++; | ||
} | ||
// then comes font-size | ||
Array.prototype.push.apply(restored, components[componentIndex].value); | ||
componentIndex++; | ||
// then may come line-height | ||
if (components[componentIndex].value[0][1] != compactable[components[componentIndex].name].defaultValue) { | ||
Array.prototype.push.apply(restored, [[Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]]); | ||
Array.prototype.push.apply(restored, components[componentIndex].value); | ||
} | ||
componentIndex++; | ||
// then comes font-family | ||
while (components[componentIndex].value[fontFamilyIndex]) { | ||
restored.push(components[componentIndex].value[fontFamilyIndex]); | ||
if (components[componentIndex].value[fontFamilyIndex + 1]) { | ||
restored.push([Token.PROPERTY_VALUE, Marker.COMMA]); | ||
} | ||
fontFamilyIndex++; | ||
} | ||
if (isInheritOnly(restored)) { | ||
return [restored[0]]; | ||
} | ||
return restored; | ||
} | ||
function fourValues(property) { | ||
@@ -231,2 +284,3 @@ var components = property.components; | ||
borderRadius: borderRadius, | ||
font: font, | ||
fourValues: fourValues, | ||
@@ -233,0 +287,0 @@ multiplex: multiplex, |
@@ -28,3 +28,4 @@ var canReorderSingle = require('./reorderable').canReorderSingle; | ||
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; | ||
var mergeLimit = 8191; | ||
var mergeLimit = options.compatibility.selectors.mergeLimit; | ||
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging; | ||
var specificityCache = context.cache.specificity; | ||
@@ -90,3 +91,3 @@ var movableTokens = {}; | ||
for (var i = sourceTokens.length - 1; i >= 0; i--) { | ||
if (!isMergeable(serializeRules(sourceTokens[i][1]), mergeablePseudoClasses, mergeablePseudoElements)) { | ||
if (!isMergeable(serializeRules(sourceTokens[i][1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging)) { | ||
continue; | ||
@@ -93,0 +94,0 @@ } |
@@ -1,42 +0,26 @@ | ||
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 functionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)'; | ||
var functionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)'; | ||
var variableRegexStr = 'var\\(\\-\\-[^\\)]+\\)'; | ||
var functionAnyRegexStr = '(' + variableRegexStr + '|' + functionNoVendorRegexStr + '|' + functionVendorRegexStr + ')'; | ||
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 animationTimingFunctionRegex = /^(cubic\-bezier|steps)\([^\)]+\)$/; | ||
var calcRegex = new RegExp('^(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)$', 'i'); | ||
var functionAnyRegex = new RegExp('^' + functionAnyRegexStr + '$', 'i'); | ||
var hslColorRegex = /^hsl\(\s*[\-\.\d]+\s*,\s*[\.\d]+%\s*,\s*[\.\d]+%\s*\)|hsla\(\s*[\-\.\d]+\s*,\s*[\.\d]+%\s*,\s*[\.\d]+%\s*,\s*[\.\d]+\s*\)$/; | ||
var identifierRegex = /^(\-[a-z0-9_][a-z0-9\-_]*|[a-z][a-z0-9\-_]*)$/i; | ||
var longHexColorRegex = /^#[0-9a-f]{6}$/i; | ||
var namedEntityRegex = /^[a-z]+$/i; | ||
var prefixRegex = /^-([a-z0-9]|-)*$/i; | ||
var rgbColorRegex = /^rgb\(\s*[\d]{1,3}\s*,\s*[\d]{1,3}\s*,\s*[\d]{1,3}\s*\)|rgba\(\s*[\d]{1,3}\s*,\s*[\d]{1,3}\s*,\s*[\d]{1,3}\s*,\s*[\.\d]+\s*\)$/; | ||
var shortHexColorRegex = /^#[0-9a-f]{3}$/i; | ||
var timeRegex = new RegExp('^(\\-?\\+?\\.?\\d+\\.?\\d*(s|ms))$'); | ||
var urlRegex = /^url\([\s\S]+\)$/i; | ||
var variableRegex = new RegExp('^' + variableRegexStr + '$', 'i'); | ||
var globalKeywords = [ | ||
'inherit', | ||
'initial', | ||
'unset' | ||
]; | ||
var Keywords = { | ||
'^': [ | ||
'inherit', | ||
'initial', | ||
'unset' | ||
], | ||
'*-style': [ | ||
@@ -55,2 +39,33 @@ 'auto', | ||
], | ||
'animation-direction': [ | ||
'alternate', | ||
'alternate-reverse', | ||
'normal', | ||
'reverse' | ||
], | ||
'animation-fill-mode': [ | ||
'backwards', | ||
'both', | ||
'forwards', | ||
'none' | ||
], | ||
'animation-iteration-count': [ | ||
'infinite' | ||
], | ||
'animation-name': [ | ||
'none' | ||
], | ||
'animation-play-state': [ | ||
'paused', | ||
'running' | ||
], | ||
'animation-timing-function': [ | ||
'ease', | ||
'ease-in', | ||
'ease-in-out', | ||
'ease-out', | ||
'linear', | ||
'step-end', | ||
'step-start' | ||
], | ||
'background-attachment': [ | ||
@@ -110,2 +125,5 @@ 'fixed', | ||
], | ||
'color': [ | ||
'transparent' | ||
], | ||
'cursor': [ | ||
@@ -161,2 +179,32 @@ 'all-scroll', | ||
], | ||
'font': [ | ||
'caption', | ||
'icon', | ||
'menu', | ||
'message-box', | ||
'small-caption', | ||
'status-bar' | ||
], | ||
'font-size': [ | ||
'large', | ||
'larger', | ||
'medium', | ||
'small', | ||
'smaller', | ||
'x-large', | ||
'x-small', | ||
'xx-large', | ||
'xx-small' | ||
], | ||
'font-stretch': [ | ||
'condensed', | ||
'expanded', | ||
'extra-condensed', | ||
'extra-expanded', | ||
'normal', | ||
'semi-condensed', | ||
'semi-expanded', | ||
'ultra-condensed', | ||
'ultra-expanded' | ||
], | ||
'font-style': [ | ||
@@ -167,2 +215,6 @@ 'italic', | ||
], | ||
'font-variant': [ | ||
'normal', | ||
'small-caps' | ||
], | ||
'font-weight': [ | ||
@@ -183,2 +235,5 @@ '100', | ||
], | ||
'line-height': [ | ||
'normal' | ||
], | ||
'list-style-position': [ | ||
@@ -270,159 +325,114 @@ 'inside', | ||
var VENDOR_PREFIX_PATTERN = /(^|\W)-\w+\-/; | ||
var Units = [ | ||
'%', | ||
'ch', | ||
'cm', | ||
'em', | ||
'ex', | ||
'in', | ||
'mm', | ||
'pc', | ||
'pt', | ||
'px', | ||
'rem', | ||
'vh', | ||
'vm', | ||
'vmax', | ||
'vmin', | ||
'vw' | ||
]; | ||
function areSameFunction(value1, value2) { | ||
if (!isValidFunction(value1) || !isValidFunction(value2)) { | ||
return false; | ||
} | ||
function isAnimationTimingFunction() { | ||
var isTimingFunctionKeyword = isKeyword('animation-timing-function'); | ||
var function1Name = value1.substring(0, value1.indexOf('(')); | ||
var function2Name = value2.substring(0, value2.indexOf('(')); | ||
return function1Name === function2Name; | ||
return function (value) { | ||
return isTimingFunctionKeyword(value) || animationTimingFunctionRegex.test(value); | ||
}; | ||
} | ||
function hasNoVendorPrefix(value) { | ||
return VENDOR_PREFIX_PATTERN.test(value); | ||
function isColor(value) { | ||
return value != 'auto' && | ||
( | ||
isKeyword('color')(value) || | ||
isHexColor(value) || | ||
isColorFunction(value) || | ||
isNamedEntity(value) | ||
); | ||
} | ||
function isValidBackgroundAttachment(value) { | ||
return Keywords['background-attachment'].indexOf(value) > -1; | ||
function isColorFunction(value) { | ||
return isRgbColor(value) || isHslColor(value); | ||
} | ||
function isValidBackgroundClip(value) { | ||
return Keywords['background-clip'].indexOf(value) > -1; | ||
function isDynamicUnit(value) { | ||
return calcRegex.test(value); | ||
} | ||
function isValidBackgroundRepeat(value) { | ||
return Keywords['background-repeat'].indexOf(value) > -1; | ||
function isFunction(value) { | ||
return functionAnyRegex.test(value); | ||
} | ||
function isValidBackgroundOrigin(value) { | ||
return Keywords['background-origin'].indexOf(value) > -1; | ||
function isHexColor(value) { | ||
return shortHexColorRegex.test(value) || longHexColorRegex.test(value); | ||
} | ||
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 isHslColor(value) { | ||
return hslColorRegex.test(value); | ||
} | ||
function isValidBackgroundPositionPart(value) { | ||
return Keywords['background-position'].indexOf(value) > -1 || cssUnitOrCalcRegex.test(value); | ||
function isIdentifier(value) { | ||
return identifierRegex.test(value); | ||
} | ||
function isValidBackgroundSizePart(value) { | ||
return Keywords['background-size'].indexOf(value) > -1 || cssUnitRegex.test(value); | ||
function isImage(value) { | ||
return value == 'none' || value == 'inherit' || isUrl(value); | ||
} | ||
function isValidColor(value) { | ||
return isValidNamedColor(value) || | ||
isValidColorValue(value); | ||
function isKeyword(propertyName) { | ||
return function(value) { | ||
return Keywords[propertyName].indexOf(value) > -1; | ||
}; | ||
} | ||
function isValidColorValue(value) { | ||
return isValidHexColor(value) || | ||
isValidRgbaColor(value) || | ||
isValidHslaColor(value); | ||
function isNamedEntity(value) { | ||
return namedEntityRegex.test(value); | ||
} | ||
function isValidFunction(value) { | ||
return !urlRegex.test(value) && cssFunctionAnyRegex.test(value); | ||
function isNumber(value) { | ||
return value.length > 0 && ('' + parseFloat(value)) === value; | ||
} | ||
function isValidFunctionWithoutVendorPrefix(value) { | ||
return !urlRegex.test(value) && cssFunctionNoVendorRegex.test(value); | ||
function isRgbColor(value) { | ||
return rgbColorRegex.test(value); | ||
} | ||
function isValidGlobalValue(value) { | ||
return globalKeywords.indexOf(value) > -1; | ||
function isPrefixed(value) { | ||
return prefixRegex.test(value); | ||
} | ||
function isValidHexColor(value) { | ||
return (value.length === 4 || value.length === 7) && value[0] === '#'; | ||
function isPositiveNumber(value) { | ||
return isNumber(value) && | ||
parseFloat(value) >= 0; | ||
} | ||
function isValidHslaColor(value) { | ||
return value.length > 0 && value.indexOf('hsla(') === 0 && value.indexOf(')') === value.length - 1; | ||
function isVariable(value) { | ||
return variableRegex.test(value); | ||
} | ||
function isValidImage(value) { | ||
return value == 'none' || value == 'inherit' || isValidUrl(value); | ||
function isTime(value) { | ||
return timeRegex.test(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) { | ||
function isUnit(compatibleCssUnitRegex, value) { | ||
return compatibleCssUnitRegex.test(value); | ||
} | ||
function isValidUrl(value) { | ||
function isUrl(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) { | ||
function isZIndex(value) { | ||
return value == 'auto' || | ||
isValidGlobalValue(value) || | ||
value.length > 0 && value == ('' + parseInt(value)); | ||
isNumber(value) || | ||
isKeyword('^')(value); | ||
} | ||
@@ -435,40 +445,46 @@ | ||
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; | ||
var compatibleCssUnitRegex = new RegExp('^(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + '|)|auto|inherit)$', 'i'); | ||
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 | ||
colorOpacity: compatibility.colors.opacity, | ||
isAnimationDirectionKeyword: isKeyword('animation-direction'), | ||
isAnimationFillModeKeyword: isKeyword('animation-fill-mode'), | ||
isAnimationIterationCountKeyword: isKeyword('animation-iteration-count'), | ||
isAnimationNameKeyword: isKeyword('animation-name'), | ||
isAnimationPlayStateKeyword: isKeyword('animation-play-state'), | ||
isAnimationTimingFunction: isAnimationTimingFunction(), | ||
isBackgroundAttachmentKeyword: isKeyword('background-attachment'), | ||
isBackgroundClipKeyword: isKeyword('background-clip'), | ||
isBackgroundOriginKeyword: isKeyword('background-origin'), | ||
isBackgroundPositionKeyword: isKeyword('background-position'), | ||
isBackgroundRepeatKeyword: isKeyword('background-repeat'), | ||
isBackgroundSizeKeyword: isKeyword('background-size'), | ||
isColor: isColor, | ||
isColorFunction: isColorFunction, | ||
isDynamicUnit: isDynamicUnit, | ||
isFontKeyword: isKeyword('font'), | ||
isFontSizeKeyword: isKeyword('font-size'), | ||
isFontStretchKeyword: isKeyword('font-stretch'), | ||
isFontStyleKeyword: isKeyword('font-style'), | ||
isFontVariantKeyword: isKeyword('font-variant'), | ||
isFontWeightKeyword: isKeyword('font-weight'), | ||
isFunction: isFunction, | ||
isGlobal: isKeyword('^'), | ||
isHslColor: isHslColor, | ||
isIdentifier: isIdentifier, | ||
isImage: isImage, | ||
isKeyword: isKeyword, | ||
isLineHeightKeyword: isKeyword('line-height'), | ||
isListStylePositionKeyword: isKeyword('list-style-position'), | ||
isListStyleTypeKeyword: isKeyword('list-style-type'), | ||
isPrefixed: isPrefixed, | ||
isPositiveNumber: isPositiveNumber, | ||
isRgbColor: isRgbColor, | ||
isStyleKeyword: isKeyword('*-style'), | ||
isTime: isTime, | ||
isUnit: isUnit.bind(null, compatibleCssUnitRegex), | ||
isUrl: isUrl, | ||
isVariable: isVariable, | ||
isWidth: isKeyword('width'), | ||
isZIndex: isZIndex | ||
}; | ||
@@ -475,0 +491,0 @@ } |
@@ -20,3 +20,3 @@ var Hack = require('./hack'); | ||
function wrapAll(properties, includeVariable) { | ||
function wrapAll(properties, includeVariable, skipProperties) { | ||
var wrapped = []; | ||
@@ -38,2 +38,6 @@ var single; | ||
if (skipProperties && skipProperties.indexOf(property[1][1]) > -1) { | ||
continue; | ||
} | ||
single = wrapSingle(property); | ||
@@ -40,0 +44,0 @@ single.all = properties; |
@@ -59,3 +59,5 @@ var DEFAULTS = { | ||
'::first-line' | ||
] // selectors with these pseudo-elements can be merged as these are universally supported | ||
], // selectors with these pseudo-elements can be merged as these are universally supported | ||
mergeLimit: 8191, // number of rules that can be safely merged together | ||
multiplePseudoMerging: true | ||
}, | ||
@@ -62,0 +64,0 @@ units: { |
@@ -6,2 +6,6 @@ function inlineOptionsFrom(rules) { | ||
if (rules === false) { | ||
return ['none']; | ||
} | ||
return undefined === rules ? | ||
@@ -8,0 +12,0 @@ ['local'] : |
@@ -20,5 +20,5 @@ var roundingPrecisionFrom = require('./rounding-precision').roundingPrecisionFrom; | ||
optimizeFilter: true, | ||
optimizeFont: true, | ||
optimizeFontWeight: true, | ||
optimizeOutline: true, | ||
removeEmpty: true, | ||
removeNegativePaddings: true, | ||
@@ -45,2 +45,3 @@ removeQuotes: true, | ||
overrideProperties: true, | ||
removeEmpty: true, | ||
reduceNonAdjacentRules: true, | ||
@@ -50,3 +51,5 @@ removeDuplicateFontRules: true, | ||
removeDuplicateRules: true, | ||
restructureRules: false | ||
removeUnusedAtRules: false, | ||
restructureRules: false, | ||
skipProperties: [] | ||
}; | ||
@@ -61,2 +64,3 @@ | ||
var LIST_VALUE_SEPARATOR = ','; | ||
var OPTION_SEPARATOR = ';'; | ||
@@ -106,2 +110,6 @@ var OPTION_VALUE_SEPARATOR = ':'; | ||
if (Two in source && 'skipProperties' in source[Two] && typeof(source[Two].skipProperties) == 'string') { | ||
source[Two].skipProperties = source[Two].skipProperties.split(LIST_VALUE_SEPARATOR); | ||
} | ||
if (Zero in source || One in source || Two in source) { | ||
@@ -108,0 +116,0 @@ level[Zero] = override(level[Zero], source[Zero]); |
@@ -5,3 +5,2 @@ var fs = require('fs'); | ||
var isAllowedResource = require('./is-allowed-resource'); | ||
var loadRemoteResource = require('./load-remote-resource'); | ||
var matchDataUri = require('./match-data-uri'); | ||
@@ -21,2 +20,3 @@ var rebaseLocalMap = require('./rebase-local-map'); | ||
callback: callback, | ||
fetch: context.options.fetch, | ||
index: 0, | ||
@@ -156,3 +156,3 @@ inline: context.options.inline, | ||
loadRemoteResource(uri, applyContext.inlineRequest, applyContext.inlineTimeout, function (error, body) { | ||
applyContext.fetch(uri, applyContext.inlineRequest, applyContext.inlineTimeout, function (error, body) { | ||
if (error) { | ||
@@ -159,0 +159,0 @@ applyContext.warnings.push('Missing source map at "' + uri + '" - ' + error); |
@@ -5,3 +5,2 @@ var fs = require('fs'); | ||
var isAllowedResource = require('./is-allowed-resource'); | ||
var loadRemoteResource = require('./load-remote-resource'); | ||
@@ -14,2 +13,3 @@ var hasProtocol = require('../utils/has-protocol'); | ||
callback: callback, | ||
fetch: context.options.fetch, | ||
index: 0, | ||
@@ -104,3 +104,3 @@ inline: context.options.inline, | ||
loadRemoteResource(uri, loadContext.inlineRequest, loadContext.inlineTimeout, function (error, content) { | ||
loadContext.fetch(uri, loadContext.inlineRequest, loadContext.inlineTimeout, function (error, content) { | ||
if (error) { | ||
@@ -107,0 +107,0 @@ loadContext.warnings.push('Missing original source at "' + uri + '" - ' + error); |
@@ -8,3 +8,2 @@ var fs = require('fs'); | ||
var loadOriginalSources = require('./load-original-sources'); | ||
var loadRemoteResource = require('./load-remote-resource'); | ||
var normalizePath = require('./normalize-path'); | ||
@@ -54,7 +53,9 @@ var rebase = require('./rebase'); | ||
function fromArray(input, context, callback) { | ||
var inputAsImports = input.reduce(function (accumulator, uri) { | ||
var normalizedUri = normalizeUri(uri); | ||
var inputAsImports = input.reduce(function (accumulator, uriOrHash) { | ||
if (typeof uriOrHash === 'string') { | ||
return addStringSource(uriOrHash, accumulator); | ||
} else { | ||
return addHashSource(uriOrHash, context, accumulator); | ||
} | ||
accumulator.push(restoreAsImport(normalizedUri)); | ||
return accumulator; | ||
}, []); | ||
@@ -66,6 +67,15 @@ | ||
function fromHash(input, context, callback) { | ||
var inputAsImports = addHashSource(input, context, []); | ||
return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback); | ||
} | ||
function addStringSource(input, imports) { | ||
imports.push(restoreAsImport(normalizeUri(input))); | ||
return imports; | ||
} | ||
function addHashSource(input, context, imports) { | ||
var uri; | ||
var normalizedUri; | ||
var source; | ||
var inputAsImports = []; | ||
@@ -76,3 +86,3 @@ for (uri in input) { | ||
inputAsImports.push(restoreAsImport(normalizedUri)); | ||
imports.push(restoreAsImport(normalizedUri)); | ||
@@ -86,3 +96,3 @@ context.sourcesContent[normalizedUri] = source.styles; | ||
return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback); | ||
return imports; | ||
} | ||
@@ -160,2 +170,3 @@ | ||
externalContext: externalContext, | ||
fetch: externalContext.options.fetch, | ||
inlinedStylesheets: parentInlinerContext.inlinedStylesheets || externalContext.inlinedStylesheets, | ||
@@ -276,3 +287,3 @@ inline: parentInlinerContext.inline, | ||
whenLoaded(null, inlinerContext.externalContext.sourcesContent[uri]) : | ||
loadRemoteResource(uri, inlinerContext.inlineRequest, inlinerContext.inlineTimeout, whenLoaded); | ||
inlinerContext.fetch(uri, inlinerContext.inlineRequest, inlinerContext.inlineTimeout, whenLoaded); | ||
} | ||
@@ -279,0 +290,0 @@ |
@@ -94,3 +94,3 @@ var extractImportUrlAndMedia = require('./extract-import-url-and-media'); | ||
if (validator.isValidUrl(value)) { | ||
if (validator.isUrl(value)) { | ||
property[j][1] = rewriteUrl(value, rebaseConfig); | ||
@@ -97,0 +97,0 @@ } |
@@ -13,2 +13,3 @@ var Marker = { | ||
FORWARD_SLASH: '/', | ||
INTERNAL: '-clean-css-', | ||
NEW_LINE_NIX: '\n', | ||
@@ -15,0 +16,0 @@ NEW_LINE_WIN: '\r', |
{ | ||
"name": "clean-css", | ||
"version": "4.0.12", | ||
"version": "4.1.0", | ||
"author": "Jakub Pawlowicz <contact@jakubpawlowicz.com> (http://twitter.com/jakubpawlowicz)", | ||
@@ -37,3 +37,3 @@ "description": "A well-tested CSS minifier", | ||
"devDependencies": { | ||
"browserify": "13.x", | ||
"browserify": "^14.0.0", | ||
"http-proxy": "1.x", | ||
@@ -40,0 +40,0 @@ "jshint": "2.x", |
130
README.md
@@ -25,4 +25,6 @@ <h1 align="center"> | ||
* [Important: 4.0 breaking changes](#important-40-breaking-changes) | ||
* [What's new in version 4.1](#whats-new-in-version-41) | ||
* [Constructor options](#constructor-options) | ||
* [Compatibility modes](#compatibility-modes) | ||
* [Fetch option](#fetch-option) | ||
* [Formatting options](#formatting-options) | ||
@@ -36,2 +38,3 @@ * [Inlining options](#inlining-options) | ||
* [Promise interface](#promise-interface) | ||
* [CLI utility](#cli-utility) | ||
- [FAQ](#faq) | ||
@@ -95,2 +98,21 @@ * [How to optimize multiple files?](#how-to-optimize-multiple-files) | ||
## What's new in version 4.1 | ||
clean-css 4.1 introduces the following changes / features: | ||
* `inline: false` as an alias to `inline: ['none']`; | ||
* `multiplePseudoMerging` compatibility flag controlling merging of rules with multiple pseudo classes / elements; | ||
* `removeEmpty` flag in level 1 optimizations controlling removal of rules and nested blocks; | ||
* `removeEmpty` flag in level 2 optimizations controlling removal of rules and nested blocks; | ||
* `compatibility: { selectors: { mergeLimit: <number> } }` flag in compatibility settings controlling maximum number of selectors in a single rule; | ||
* `minify` method improved signature accepting a list of hashes for a predictable traversal; | ||
* `selectorsSortingMethod` level 1 optimization allows `false` or `'none'` for disabling selector sorting; | ||
* `fetch` option controlling a function for handling remote requests; | ||
* new `font` shorthand and `font-*` longhand optimizers; | ||
* removal of `optimizeFont` flag in level 1 optimizations due to new `font` shorthand optimizer; | ||
* `skipProperties` flag in level 2 optimizations controlling which properties won't be optimized; | ||
* new `animation` shorthand and `animation-*` longhand optimizers; | ||
* `removeUnusedAtRules` level 2 optimization controlling removal of unused `@counter-style`, `@font-face`, `@keyframes`, and `@namespace` at rules; | ||
* the [web interface](https://jakubpawlowicz.github.io/clean-css) gets an improved settings panel with "reset to defaults", instant option changes, and settings being persisted across sessions. | ||
## Constructor options | ||
@@ -101,2 +123,3 @@ | ||
* `compatibility` - controls compatibility mode used; defaults to `ie10+`; see [compatibility modes](#compatibility-modes) for examples; | ||
* `fetch` - controls a function for handling remote requests; see [fetch option](#fetch-option) for examples (since 4.1.0); | ||
* `format` - controls output CSS formatting; defaults to `false`; see [formatting options](#formatting-options) for examples; | ||
@@ -149,3 +172,5 @@ * `inline` - controls `@import` inlining rules; defaults to `'local'`; see [inlining options](#inlining-options) for examples; | ||
mergeablePseudoClasses: [':active', ...], // controls a whitelist of mergeable pseudo classes | ||
mergeablePseudoElements: ['::after', ...] // controls a whitelist of mergeable pseudo elements | ||
mergeablePseudoElements: ['::after', ...], // controls a whitelist of mergeable pseudo elements | ||
mergeLimit: 8191, // controls maximum number of selectors in a single rule (since 4.1.0) | ||
multiplePseudoMerging: true // controls merging of rules with multiple pseudo classes / elements (since 4.1.0) | ||
}, | ||
@@ -175,8 +200,51 @@ units: { | ||
## Fetch option | ||
The `fetch` option accepts a function which handles remote resource fetching, e.g. | ||
```js | ||
var request = require('request'); | ||
var source = '@import url(http://example.com/path/to/stylesheet.css);'; | ||
new CleanCSS({ | ||
fetch: function (uri, inlineRequest, inlineTimeout, callback) { | ||
request(uri, function (error, response, body) { | ||
if (error) { | ||
callback(error, null); | ||
} else if (response && response.statusCode != 200) { | ||
callback(response.statusCode, null); | ||
} else { | ||
callback(null, body); | ||
} | ||
}); | ||
} | ||
}).minify(source); | ||
``` | ||
This option provides a convenient way of overriding the default fetching logic if it doesn't support a particular feature, say CONNECT proxies. | ||
Unless given, the default [loadRemoteResource](https://github.com/jakubpawlowicz/clean-css/blob/master/lib/reader/load-remote-resource.js) logic is used. | ||
## Formatting options | ||
The `format` option accept the following options: | ||
By default output CSS is formatted without any whitespace unless a `format` option is given. | ||
First of all there are two shorthands: | ||
```js | ||
new CleanCSS({ | ||
format: 'beautify' // formats output in a really nice way | ||
}) | ||
``` | ||
and | ||
```js | ||
new CleanCSS({ | ||
format: 'keep-breaks' // formats output the default way but adds line breaks for improved readability | ||
}) | ||
``` | ||
however `format` option also accept a fine-grained set of options: | ||
```js | ||
new CleanCSS({ | ||
format: { | ||
@@ -212,3 +280,3 @@ breaks: { // controls where to insert breaks | ||
new CleanCSS({ | ||
inline: ['local'] // default | ||
inline: ['local'] // default; enables local inlining only | ||
}) | ||
@@ -219,3 +287,3 @@ ``` | ||
new CleanCSS({ | ||
inline: ['all'] // same as ['local', 'remote'] | ||
inline: ['none'] // disables all inlining | ||
}) | ||
@@ -225,4 +293,6 @@ ``` | ||
```js | ||
// introduced in clean-css 4.1.0 | ||
new CleanCSS({ | ||
inline: ['local', 'mydomain.example.com'] | ||
inline: false // disables all inlining (alias to `['none']`) | ||
}) | ||
@@ -233,6 +303,18 @@ ``` | ||
new CleanCSS({ | ||
inline: ['local', 'remote', '!fonts.googleapis.com'] | ||
inline: ['all'] // enables all inlining, same as ['local', 'remote'] | ||
}) | ||
``` | ||
```js | ||
new CleanCSS({ | ||
inline: ['local', 'mydomain.example.com'] // enables local inlining plus given remote source | ||
}) | ||
``` | ||
```js | ||
new CleanCSS({ | ||
inline: ['local', 'remote', '!fonts.googleapis.com'] // enables all inlining but from given remote source | ||
}) | ||
``` | ||
## Optimization levels | ||
@@ -268,8 +350,9 @@ | ||
normalizeUrls: true, // controls URL normalization; defaults to `true` | ||
optimizeBackground: true, // controls `background` property optimizatons; defaults to `true` | ||
optimizeBorderRadius: true, // controls `border-radius` property optimizatons; defaults to `true` | ||
optimizeFilter: true, // controls `filter` property optimizatons; defaults to `true` | ||
optimizeFont: true, // ontrols `font` property optimizatons; defaults to `true` | ||
optimizeFontWeight: true, // controls `font-weight` property optimizatons; defaults to `true` | ||
optimizeOutline: true, // controls `outline` property optimizatons; defaults to `true` | ||
optimizeBackground: true, // controls `background` property optimizations; defaults to `true` | ||
optimizeBorderRadius: true, // controls `border-radius` property optimizations; defaults to `true` | ||
optimizeFilter: true, // controls `filter` property optimizations; defaults to `true` | ||
optimizeFont: true, // controls `font` property optimizations; defaults to `true` | ||
optimizeFontWeight: true, // controls `font-weight` property optimizations; defaults to `true` | ||
optimizeOutline: true, // controls `outline` property optimizations; defaults to `true` | ||
removeEmpty: true, // controls removing empty rules and nested blocks; defaults to `true` | ||
removeNegativePaddings: true, // controls removing negative paddings; defaults to `true` | ||
@@ -282,3 +365,3 @@ removeQuotes: true, // controls removing quotes when unnecessary; defaults to `true` | ||
roundingPrecision: false, // rounds pixel values to `N` decimal places; `false` disables rounding; defaults to `false` | ||
selectorsSortingMethod: 'standard', // denotes selector sorting method; can be `natural` or `standard`; defaults to `standard` | ||
selectorsSortingMethod: 'standard', // denotes selector sorting method; can be `'natural'` or `'standard'`, `'none'`, or false (the last two since 4.1.0); defaults to `'standard'` | ||
specialComments: 'all', // denotes a number of /*! ... */ comments preserved; defaults to `all` | ||
@@ -325,2 +408,3 @@ tidyAtRules: true, // controls at-rules (e.g. `@charset`, `@import`) optimizing; defaults to `true` | ||
overrideProperties: true, // controls property overriding based on understandability; defaults to true | ||
removeEmpty: true, // controls removing empty rules and nested blocks; defaults to `true` | ||
reduceNonAdjacentRules: true, // controls non-adjacent rule reducing; defaults to true | ||
@@ -330,3 +414,5 @@ removeDuplicateFontRules: true, // controls duplicate `@font-face` removing; defaults to true | ||
removeDuplicateRules: true, // controls duplicate rules removing; defaults to true | ||
restructureRules: false // controls rule restructuring; defaults to false | ||
removeUnusedAtRules: false, // controls unused at rule removing; defaults to false (available since 4.1.0) | ||
restructureRules: false, // controls rule restructuring; defaults to false | ||
skipProperties: [] // controls which properties won't be optimized, defaults to `[]` which means all will be optimized (since 4.1.0) | ||
} | ||
@@ -396,2 +482,6 @@ } | ||
## CLI utility | ||
Clean-css has an associated command line utility that can be installed separately using `npm install clean-css-cli`. For more detailed information, please visit https://github.com/jakubpawlowicz/clean-css-cli. | ||
# FAQ | ||
@@ -401,3 +491,3 @@ | ||
It can be done either by passing an array of paths, or, when sources are already available, a hash: | ||
It can be done either by passing an array of paths, or, when sources are already available, a hash or an array of hashes: | ||
@@ -419,2 +509,11 @@ ```js | ||
```js | ||
new CleanCSS().minify([ | ||
{'path/to/file/one': {styles: 'contents of file one'}}, | ||
{'path/to/file/two': {styles: 'contents of file two'}} | ||
]); | ||
``` | ||
Passing an array of hashes allows you to explicitly specify the order in which the input files are concatenated. Whereas when you use a single hash the order is determined by the [traversal order of object properties](http://2ality.com/2015/10/property-traversal-order-es6.html) - available since 4.1.0. | ||
Important note - any `@import` rules already present in the hash will be resolved in memory. | ||
@@ -624,2 +723,3 @@ | ||
* [@facelessuser](https://github.com/facelessuser) (Isaac) for pointing out a flaw in clean-css' stateless mode; | ||
* [@grandrath](https://github.com/grandrath) (Martin Grandrath) for improving `minify` method source traversal in ES6; | ||
* [@jmalonzo](https://github.com/jmalonzo) (Jan Michael Alonzo) for a patch removing node.js' old `sys` package; | ||
@@ -626,0 +726,0 @@ * [@lukeapage](https://github.com/lukeapage) (Luke Page) for suggestions and testing the source maps feature; |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
413828
99
8788
720