Socket
Socket
Sign inDemoInstall

clean-css

Package Overview
Dependencies
1
Maintainers
2
Versions
211
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 4.0.12 to 4.1.0

lib/optimizer/level-2/properties/is-mergeable-shorthand.js

2

lib/clean.js

@@ -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),

67

lib/optimizer/level-1/optimize.js

@@ -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",

@@ -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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc