chore: update deps

This commit is contained in:
Rim
2025-03-29 15:51:03 -04:00
parent e1fab5e7ef
commit a9cac1605b
2120 changed files with 369757 additions and 880 deletions

View File

@ -0,0 +1,644 @@
var InvalidPropertyError = require('./invalid-property-error');
var wrapSingle = require('../wrap-for-optimizing').single;
var Token = require('../../tokenizer/token');
var Marker = require('../../tokenizer/marker');
var formatPosition = require('../../utils/format-position');
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.isColor(value[1]) || validator.isPrefixed(value[1]);
};
}
function _styleFilter(validator) {
return function (value) {
return value[1] != 'inherit' && validator.isStyleKeyword(value[1]) && !validator.isColorFunction(value[1]);
};
}
function _wrapDefault(name, property, compactable) {
var descriptor = compactable[name];
if (descriptor.doubleValues && descriptor.defaultValue.length == 2) {
return wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, name],
[Token.PROPERTY_VALUE, descriptor.defaultValue[0]],
[Token.PROPERTY_VALUE, descriptor.defaultValue[1]]
]);
} else if (descriptor.doubleValues && descriptor.defaultValue.length == 1) {
return wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, name],
[Token.PROPERTY_VALUE, descriptor.defaultValue[0]]
]);
} else {
return wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, name],
[Token.PROPERTY_VALUE, descriptor.defaultValue]
]);
}
}
function _widthFilter(validator) {
return function (value) {
return value[1] != 'inherit' &&
(validator.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(property.name + '-duration', property, compactable);
var timing = _wrapDefault(property.name + '-timing-function', property, compactable);
var delay = _wrapDefault(property.name + '-delay', property, compactable);
var iteration = _wrapDefault(property.name + '-iteration-count', property, compactable);
var direction = _wrapDefault(property.name + '-direction', property, compactable);
var fill = _wrapDefault(property.name + '-fill-mode', property, compactable);
var play = _wrapDefault(property.name + '-play-state', property, compactable);
var name = _wrapDefault(property.name + '-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.isTimingFunction(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) {
var image = _wrapDefault('background-image', property, compactable);
var position = _wrapDefault('background-position', property, compactable);
var size = _wrapDefault('background-size', property, compactable);
var repeat = _wrapDefault('background-repeat', property, compactable);
var attachment = _wrapDefault('background-attachment', property, compactable);
var origin = _wrapDefault('background-origin', property, compactable);
var clip = _wrapDefault('background-clip', property, compactable);
var color = _wrapDefault('background-color', property, compactable);
var components = [image, position, size, repeat, attachment, origin, clip, color];
var values = property.value;
var positionSet = false;
var clipSet = false;
var originSet = false;
var repeatSet = false;
var anyValueSet = false;
if (property.value.length == 1 && property.value[0][1] == 'inherit') {
// NOTE: 'inherit' is not a valid value for background-attachment
color.value = image.value = repeat.value = position.value = size.value = origin.value = clip.value = property.value;
return components;
}
if (property.value.length == 1 && property.value[0][1] == '0 0') {
return components;
}
for (var i = values.length - 1; i >= 0; i--) {
var value = values[i];
if (validator.isBackgroundAttachmentKeyword(value[1])) {
attachment.value = [value];
anyValueSet = true;
} else if (validator.isBackgroundClipKeyword(value[1]) || validator.isBackgroundOriginKeyword(value[1])) {
if (clipSet) {
origin.value = [value];
originSet = true;
} else {
clip.value = [value];
clipSet = true;
}
anyValueSet = true;
} else if (validator.isBackgroundRepeatKeyword(value[1])) {
if (repeatSet) {
repeat.value.unshift(value);
} else {
repeat.value = [value];
repeatSet = true;
}
anyValueSet = true;
} 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] == Marker.FORWARD_SLASH) {
size.value = [value];
} else if (i > 1 && values[i - 2][1] == Marker.FORWARD_SLASH) {
size.value = [previousValue, value];
i -= 2;
} else {
if (!positionSet)
position.value = [];
position.value.unshift(value);
positionSet = true;
}
} else {
if (!positionSet)
position.value = [];
position.value.unshift(value);
positionSet = true;
}
anyValueSet = true;
} else if ((color.value[0][1] == compactable[color.name].defaultValue || color.value[0][1] == 'none') && (validator.isColor(value[1]) || validator.isPrefixed(value[1]))) {
color.value = [value];
anyValueSet = true;
} else if (validator.isUrl(value[1]) || validator.isFunction(value[1])) {
image.value = [value];
anyValueSet = true;
}
}
if (clipSet && !originSet)
origin.value = clip.value.slice(0);
if (!anyValueSet) {
throw new InvalidPropertyError('Invalid background value at ' + formatPosition(values[0][2][0]) + '. Ignoring.');
}
return components;
}
function borderRadius(property, compactable) {
var values = property.value;
var splitAt = -1;
for (var i = 0, l = values.length; i < l; i++) {
if (values[i][1] == Marker.FORWARD_SLASH) {
splitAt = i;
break;
}
}
if (splitAt === 0 || splitAt === values.length - 1) {
throw new InvalidPropertyError('Invalid border-radius value at ' + formatPosition(values[0][2][0]) + '. Ignoring.');
}
var target = _wrapDefault(property.name, property, compactable);
target.value = splitAt > -1 ?
values.slice(0, splitAt) :
values.slice(0);
target.components = fourValues(target, compactable);
var remainder = _wrapDefault(property.name, property, compactable);
remainder.value = splitAt > -1 ?
values.slice(splitAt + 1) :
values.slice(0);
remainder.components = fourValues(remainder, compactable);
for (var j = 0; j < 4; j++) {
target.components[j].multiplex = true;
target.components[j].value = target.components[j].value.concat(remainder.components[j].value);
}
return target.components;
}
function 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 && 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 && (validator.isFontKeyword(values[0][1]) || validator.isGlobal(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 < 2 || !_anyIsFontSize(values, validator) || !_anyIsFontFamily(values, validator)) {
throw new InvalidPropertyError('Invalid font values at ' + formatPosition(property.all[property.position][1][2][0]) + '. Ignoring.');
}
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 _anyIsFontSize(values, validator) {
var value;
var i, l;
for (i = 0, l = values.length; i < l; i++) {
value = values[i];
if (validator.isFontSizeKeyword(value[1]) || validator.isUnit(value[1]) && !validator.isDynamicUnit(value[1]) || validator.isFunction(value[1])) {
return true;
}
}
return false;
}
function _anyIsFontFamily(values, validator) {
var value;
var i, l;
for (i = 0, l = values.length; i < l; i++) {
value = values[i];
if (validator.isIdentifier(value[1])) {
return true;
}
}
return false;
}
function fourValues(property, compactable) {
var componentNames = compactable[property.name].components;
var components = [];
var value = property.value;
if (value.length < 1)
return [];
if (value.length < 2)
value[1] = value[0].slice(0);
if (value.length < 3)
value[2] = value[0].slice(0);
if (value.length < 4)
value[3] = value[1].slice(0);
for (var i = componentNames.length - 1; i >= 0; i--) {
var component = wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, componentNames[i]]
]);
component.value = [value[i]];
components.unshift(component);
}
return components;
}
function multiplex(splitWith) {
return function (property, compactable, validator) {
var splitsAt = [];
var values = property.value;
var i, j, l, m;
// find split commas
for (i = 0, l = values.length; i < l; i++) {
if (values[i][1] == ',')
splitsAt.push(i);
}
if (splitsAt.length === 0)
return splitWith(property, compactable, validator);
var splitComponents = [];
// split over commas, and into components
for (i = 0, l = splitsAt.length; i <= l; i++) {
var from = i === 0 ? 0 : splitsAt[i - 1] + 1;
var to = i < l ? splitsAt[i] : values.length;
var _property = _wrapDefault(property.name, property, compactable);
_property.value = values.slice(from, to);
splitComponents.push(splitWith(_property, compactable, validator));
}
var components = splitComponents[0];
// group component values from each split
for (i = 0, l = components.length; i < l; i++) {
components[i].multiplex = true;
for (j = 1, m = splitComponents.length; j < m; j++) {
components[i].value.push([Token.PROPERTY_VALUE, Marker.COMMA]);
Array.prototype.push.apply(components[i].value, splitComponents[j][i].value);
}
}
return components;
};
}
function listStyle(property, compactable, validator) {
var type = _wrapDefault('list-style-type', property, compactable);
var position = _wrapDefault('list-style-position', property, compactable);
var image = _wrapDefault('list-style-image', property, compactable);
var components = [type, position, image];
if (property.value.length == 1 && property.value[0][1] == 'inherit') {
type.value = position.value = image.value = [property.value[0]];
return components;
}
var values = property.value.slice(0);
var total = values.length;
var index = 0;
// `image` first...
for (index = 0, total = values.length; index < total; index++) {
if (validator.isUrl(values[index][1]) || values[index][1] == '0') {
image.value = [values[index]];
values.splice(index, 1);
break;
}
}
// ... then `position`
for (index = 0, total = values.length; index < total; index++) {
if (validator.isListStylePositionKeyword(values[index][1])) {
position.value = [values[index]];
values.splice(index, 1);
break;
}
}
// ... 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]];
}
return components;
}
function transition(property, compactable, validator) {
var prop = _wrapDefault(property.name + '-property', property, compactable);
var duration = _wrapDefault(property.name + '-duration', property, compactable);
var timing = _wrapDefault(property.name + '-timing-function', property, compactable);
var delay = _wrapDefault(property.name + '-delay', property, compactable);
var components = [prop, duration, timing, delay];
var values = property.value;
var value;
var durationSet = false;
var delaySet = false;
var propSet = false;
var timingSet = false;
var i;
var l;
if (property.value.length == 1 && property.value[0][1] == 'inherit') {
prop.value = duration.value = timing.value = delay.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.isTimingFunction(value[1])) && !timingSet) {
timing.value = [value];
timingSet = true;
} else if (validator.isIdentifier(value[1]) && !propSet) {
prop.value = [value];
propSet = true;
} else {
throw new InvalidPropertyError('Invalid animation value at ' + formatPosition(value[2][0]) + '. Ignoring.');
}
}
return components;
}
function widthStyleColor(property, compactable, validator) {
var descriptor = compactable[property.name];
var components = [
_wrapDefault(descriptor.components[0], property, compactable),
_wrapDefault(descriptor.components[1], property, compactable),
_wrapDefault(descriptor.components[2], property, compactable)
];
var color, style, width;
for (var i = 0; i < 3; i++) {
var component = components[i];
if (component.name.indexOf('color') > 0)
color = component;
else if (component.name.indexOf('style') > 0)
style = component;
else
width = component;
}
if ((property.value.length == 1 && property.value[0][1] == 'inherit') ||
(property.value.length == 3 && property.value[0][1] == 'inherit' && property.value[1][1] == 'inherit' && property.value[2][1] == 'inherit')) {
color.value = style.value = width.value = [property.value[0]];
return components;
}
var values = property.value.slice(0);
var match, matches;
// NOTE: usually users don't follow the required order of parts in this shorthand,
// so we'll try to parse it caring as little about order as possible
if (values.length > 0) {
matches = values.filter(_widthFilter(validator));
match = matches.length > 1 && (matches[0][1] == 'none' || matches[0][1] == 'auto') ? matches[1] : matches[0];
if (match) {
width.value = [match];
values.splice(values.indexOf(match), 1);
}
}
if (values.length > 0) {
match = values.filter(_styleFilter(validator))[0];
if (match) {
style.value = [match];
values.splice(values.indexOf(match), 1);
}
}
if (values.length > 0) {
match = values.filter(_colorFilter(validator))[0];
if (match) {
color.value = [match];
values.splice(values.indexOf(match), 1);
}
}
return components;
}
module.exports = {
animation: animation,
background: background,
border: widthStyleColor,
borderRadius: borderRadius,
font: font,
fourValues: fourValues,
listStyle: listStyle,
multiplex: multiplex,
outline: widthStyleColor,
transition: transition
};

View File

@ -0,0 +1,283 @@
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 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.isBackgroundPositionKeyword(value2) || validator.isGlobal(value2))) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} else if (validator.isBackgroundPositionKeyword(value2) || validator.isGlobal(value2)) {
return true;
}
return unit(validator, value1, value2);
}
function backgroundSize(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !(validator.isBackgroundSizeKeyword(value2) || validator.isGlobal(value2))) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} else if (validator.isBackgroundSizeKeyword(value2) || validator.isGlobal(value2)) {
return true;
}
return unit(validator, value1, value2);
}
function color(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isColor(value2)) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} else if (!validator.colorOpacity && (validator.isRgbColor(value1) || validator.isHslColor(value1))) {
return false;
} else if (!validator.colorOpacity && (validator.isRgbColor(value2) || validator.isHslColor(value2))) {
return false;
} else if (validator.isColor(value1) && validator.isColor(value2)) {
return true;
}
return sameFunctionOrValue(validator, value1, value2);
}
function components(overrideCheckers) {
return function (validator, value1, value2, position) {
return overrideCheckers[position](validator, value1, value2);
};
}
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.isImage(value2)) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} else if (validator.isImage(value2)) {
return true;
} else if (validator.isImage(value1)) {
return false;
}
return sameFunctionOrValue(validator, value1, value2);
}
function keyword(propertyName) {
return function(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isKeyword(propertyName)(value2)) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isKeyword(propertyName)(value2);
};
}
function keywordWithGlobal(propertyName) {
return function(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !(validator.isKeyword(propertyName)(value2) || validator.isGlobal(value2))) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isKeyword(propertyName)(value2) || validator.isGlobal(value2);
};
}
function propertyName(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isIdentifier(value2)) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isIdentifier(value2);
}
function sameFunctionOrValue(validator, value1, value2) {
return areSameFunction(validator, value1, value2) ?
true :
value1 === value2;
}
function textShadow(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !(validator.isUnit(value2) || validator.isColor(value2) || validator.isGlobal(value2))) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
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 timingFunction(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !(validator.isTimingFunction(value2) || validator.isGlobal(value2))) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isTimingFunction(value2) || validator.isGlobal(value2);
}
function unit(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isUnit(value2)) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} else if (validator.isUnit(value1) && !validator.isUnit(value2)) {
return false;
} else if (validator.isUnit(value2)) {
return true;
} else if (validator.isUnit(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 unitOrKeywordWithGlobal(propertyName) {
var byKeyword = keywordWithGlobal(propertyName);
return function(validator, value1, value2) {
return unit(validator, value1, value2) || byKeyword(validator, value1, value2);
};
}
function unitOrNumber(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !(validator.isUnit(value2) || validator.isNumber(value2))) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
} else if ((validator.isUnit(value1) || validator.isNumber(value1)) && !(validator.isUnit(value2) || validator.isNumber(value2))) {
return false;
} else if (validator.isUnit(value2) || validator.isNumber(value2)) {
return true;
} else if (validator.isUnit(value1) || validator.isNumber(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 zIndex(validator, value1, value2) {
if (!understandable(validator, value1, value2, 0, true) && !validator.isZIndex(value2)) {
return false;
} else if (validator.isVariable(value1) && validator.isVariable(value2)) {
return true;
}
return validator.isZIndex(value2);
}
module.exports = {
generic: {
color: color,
components: components,
image: image,
propertyName: propertyName,
time: time,
timingFunction: timingFunction,
unit: unit,
unitOrNumber: unitOrNumber
},
property: {
animationDirection: keywordWithGlobal('animation-direction'),
animationFillMode: keyword('animation-fill-mode'),
animationIterationCount: animationIterationCount,
animationName: animationName,
animationPlayState: keywordWithGlobal('animation-play-state'),
backgroundAttachment: keyword('background-attachment'),
backgroundClip: keywordWithGlobal('background-clip'),
backgroundOrigin: keyword('background-origin'),
backgroundPosition: backgroundPosition,
backgroundRepeat: keyword('background-repeat'),
backgroundSize: backgroundSize,
bottom: unitOrKeywordWithGlobal('bottom'),
borderCollapse: keyword('border-collapse'),
borderStyle: keywordWithGlobal('*-style'),
clear: keywordWithGlobal('clear'),
cursor: keywordWithGlobal('cursor'),
display: keywordWithGlobal('display'),
float: keywordWithGlobal('float'),
left: unitOrKeywordWithGlobal('left'),
fontFamily: fontFamily,
fontStretch: keywordWithGlobal('font-stretch'),
fontStyle: keywordWithGlobal('font-style'),
fontVariant: keywordWithGlobal('font-variant'),
fontWeight: keywordWithGlobal('font-weight'),
listStyleType: keywordWithGlobal('list-style-type'),
listStylePosition: keywordWithGlobal('list-style-position'),
outlineStyle: keywordWithGlobal('*-style'),
overflow: keywordWithGlobal('overflow'),
position: keywordWithGlobal('position'),
right: unitOrKeywordWithGlobal('right'),
textAlign: keywordWithGlobal('text-align'),
textDecoration: keywordWithGlobal('text-decoration'),
textOverflow: keywordWithGlobal('text-overflow'),
textShadow: textShadow,
top: unitOrKeywordWithGlobal('top'),
transform: sameFunctionOrValue,
verticalAlign: unitOrKeywordWithGlobal('vertical-align'),
visibility: keywordWithGlobal('visibility'),
whiteSpace: keywordWithGlobal('white-space'),
zIndex: zIndex
}
};

33
node_modules/clean-css/lib/optimizer/level-2/clone.js generated vendored Normal file
View File

@ -0,0 +1,33 @@
var wrapSingle = require('../wrap-for-optimizing').single;
var Token = require('../../tokenizer/token');
function deep(property) {
var cloned = shallow(property);
for (var i = property.components.length - 1; i >= 0; i--) {
var component = shallow(property.components[i]);
component.value = property.components[i].value.slice(0);
cloned.components.unshift(component);
}
cloned.dirty = true;
cloned.value = property.value.slice(0);
return cloned;
}
function shallow(property) {
var cloned = wrapSingle([
Token.PROPERTY,
[Token.PROPERTY_NAME, property.name]
]);
cloned.important = property.important;
cloned.hack = property.hack;
cloned.unused = false;
return cloned;
}
module.exports = {
deep: deep,
shallow: shallow
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
// This extractor is used in level 2 optimizations
// IMPORTANT: Mind Token class and this code is not related!
// Properties will be tokenized in one step, see #429
var Token = require('../../tokenizer/token');
var serializeRules = require('../../writer/one-time').rules;
var serializeValue = require('../../writer/one-time').value;
function extractProperties(token) {
var properties = [];
var inSpecificSelector;
var property;
var name;
var value;
var i, l;
if (token[0] == Token.RULE) {
inSpecificSelector = !/[\.\+>~]/.test(serializeRules(token[1]));
for (i = 0, l = token[2].length; i < l; i++) {
property = token[2][i];
if (property[0] != Token.PROPERTY)
continue;
name = property[1][1];
if (name.length === 0)
continue;
if (name.indexOf('--') === 0)
continue;
value = serializeValue(property, i);
properties.push([
name,
value,
findNameRoot(name),
token[2][i],
name + ':' + value,
token[1],
inSpecificSelector
]);
}
} else if (token[0] == Token.NESTED_BLOCK) {
for (i = 0, l = token[2].length; i < l; i++) {
properties = properties.concat(extractProperties(token[2][i]));
}
}
return properties;
}
function findNameRoot(name) {
if (name == 'list-style')
return name;
if (name.indexOf('-radius') > 0)
return 'border-radius';
if (name == 'border-collapse' || name == 'border-spacing' || name == 'border-image')
return name;
if (name.indexOf('border-') === 0 && /^border\-\w+\-\w+$/.test(name))
return name.match(/border\-\w+/)[0];
if (name.indexOf('border-') === 0 && /^border\-\w+$/.test(name))
return 'border';
if (name.indexOf('text-') === 0)
return name;
if (name == '-chrome-')
return name;
return name.replace(/^\-\w+\-/, '').match(/([a-zA-Z]+)/)[0].toLowerCase();
}
module.exports = extractProperties;

View File

@ -0,0 +1,10 @@
function InvalidPropertyError(message) {
this.name = 'InvalidPropertyError';
this.message = message;
this.stack = (new Error()).stack;
}
InvalidPropertyError.prototype = Object.create(Error.prototype);
InvalidPropertyError.prototype.constructor = InvalidPropertyError;
module.exports = InvalidPropertyError;

View File

@ -0,0 +1,259 @@
var Marker = require('../../tokenizer/marker');
var split = require('../../utils/split');
var DEEP_SELECTOR_PATTERN = /\/deep\//;
var DOUBLE_COLON_PATTERN = /^::/;
var NOT_PSEUDO = ':not';
var PSEUDO_CLASSES_WITH_ARGUMENTS = [
':dir',
':lang',
':not',
':nth-child',
':nth-last-child',
':nth-last-of-type',
':nth-of-type'
];
var RELATION_PATTERN = /[>\+~]/;
var UNMIXABLE_PSEUDO_CLASSES = [
':after',
':before',
':first-letter',
':first-line',
':lang'
];
var UNMIXABLE_PSEUDO_ELEMENTS = [
'::after',
'::before',
'::first-letter',
'::first-line'
];
var Level = {
DOUBLE_QUOTE: 'double-quote',
SINGLE_QUOTE: 'single-quote',
ROOT: 'root'
};
function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) {
var singleSelectors = split(selector, Marker.COMMA);
var singleSelector;
var i, l;
for (i = 0, l = singleSelectors.length; i < l; i++) {
singleSelector = singleSelectors[i];
if (singleSelector.length === 0 ||
isDeepSelector(singleSelector) ||
(singleSelector.indexOf(Marker.COLON) > -1 && !areMergeable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging))) {
return false;
}
}
return true;
}
function isDeepSelector(selector) {
return DEEP_SELECTOR_PATTERN.test(selector);
}
function extractPseudoFrom(selector) {
var list = [];
var character;
var buffer = [];
var level = Level.ROOT;
var roundBracketLevel = 0;
var isQuoted;
var isEscaped;
var isPseudo = false;
var isRelation;
var wasColon = false;
var index;
var len;
for (index = 0, len = selector.length; index < len; index++) {
character = selector[index];
isRelation = !isEscaped && RELATION_PATTERN.test(character);
isQuoted = level == Level.DOUBLE_QUOTE || level == Level.SINGLE_QUOTE;
if (isEscaped) {
buffer.push(character);
} else if (character == Marker.DOUBLE_QUOTE && level == Level.ROOT) {
buffer.push(character);
level = Level.DOUBLE_QUOTE;
} else if (character == Marker.DOUBLE_QUOTE && level == Level.DOUBLE_QUOTE) {
buffer.push(character);
level = Level.ROOT;
} else if (character == Marker.SINGLE_QUOTE && level == Level.ROOT) {
buffer.push(character);
level = Level.SINGLE_QUOTE;
} else if (character == Marker.SINGLE_QUOTE && level == Level.SINGLE_QUOTE) {
buffer.push(character);
level = Level.ROOT;
} else if (isQuoted) {
buffer.push(character);
} else if (character == Marker.OPEN_ROUND_BRACKET) {
buffer.push(character);
roundBracketLevel++;
} else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1 && isPseudo) {
buffer.push(character);
list.push(buffer.join(''));
roundBracketLevel--;
buffer = [];
isPseudo = false;
} else if (character == Marker.CLOSE_ROUND_BRACKET) {
buffer.push(character);
roundBracketLevel--;
} else if (character == Marker.COLON && roundBracketLevel === 0 && isPseudo && !wasColon) {
list.push(buffer.join(''));
buffer = [];
buffer.push(character);
} else if (character == Marker.COLON && roundBracketLevel === 0 && !wasColon) {
buffer = [];
buffer.push(character);
isPseudo = true;
} else if (character == Marker.SPACE && roundBracketLevel === 0 && isPseudo) {
list.push(buffer.join(''));
buffer = [];
isPseudo = false;
} else if (isRelation && roundBracketLevel === 0 && isPseudo) {
list.push(buffer.join(''));
buffer = [];
isPseudo = false;
} else {
buffer.push(character);
}
isEscaped = character == Marker.BACK_SLASH;
wasColon = character == Marker.COLON;
}
if (buffer.length > 0 && isPseudo) {
list.push(buffer.join(''));
}
return list;
}
function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) {
return areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) &&
needArguments(matches) &&
(matches.length < 2 || !someIncorrectlyChained(selector, matches)) &&
(matches.length < 2 || multiplePseudoMerging && allMixable(matches));
}
function areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) {
var match;
var name;
var i, l;
for (i = 0, l = matches.length; i < l; i++) {
match = matches[i];
name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ?
match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) :
match;
if (mergeablePseudoClasses.indexOf(name) === -1 && mergeablePseudoElements.indexOf(name) === -1) {
return false;
}
}
return true;
}
function needArguments(matches) {
var match;
var name;
var bracketOpensAt;
var hasArguments;
var i, l;
for (i = 0, l = matches.length; i < l; i++) {
match = matches[i];
bracketOpensAt = match.indexOf(Marker.OPEN_ROUND_BRACKET);
hasArguments = bracketOpensAt > -1;
name = hasArguments ?
match.substring(0, bracketOpensAt) :
match;
if (hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) == -1) {
return false;
}
if (!hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) > -1) {
return false;
}
}
return true;
}
function someIncorrectlyChained(selector, matches) {
var positionInSelector = 0;
var match;
var matchAt;
var nextMatch;
var nextMatchAt;
var name;
var nextName;
var areChained;
var i, l;
for (i = 0, l = matches.length; i < l; i++) {
match = matches[i];
nextMatch = matches[i + 1];
if (!nextMatch) {
break;
}
matchAt = selector.indexOf(match, positionInSelector);
nextMatchAt = selector.indexOf(match, matchAt + 1);
positionInSelector = nextMatchAt;
areChained = matchAt + match.length == nextMatchAt;
if (areChained) {
name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ?
match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) :
match;
nextName = nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ?
nextMatch.substring(0, nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET)) :
nextMatch;
if (name != NOT_PSEUDO || nextName != NOT_PSEUDO) {
return true;
}
}
}
return false;
}
function allMixable(matches) {
var unmixableMatches = 0;
var match;
var i, l;
for (i = 0, l = matches.length; i < l; i++) {
match = matches[i];
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 true;
}
function isPseudoElement(pseudo) {
return DOUBLE_COLON_PATTERN.test(pseudo);
}
module.exports = isMergeable;

View File

@ -0,0 +1,50 @@
var isMergeable = require('./is-mergeable');
var optimizeProperties = require('./properties/optimize');
var sortSelectors = require('../level-1/sort-selectors');
var tidyRules = require('../level-1/tidy-rules');
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
var Token = require('../../tokenizer/token');
function mergeAdjacent(tokens, context) {
var lastToken = [null, [], []];
var options = context.options;
var adjacentSpace = options.compatibility.selectors.adjacentSpace;
var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod;
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var mergeLimit = options.compatibility.selectors.mergeLimit;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
if (token[0] != Token.RULE) {
lastToken = [null, [], []];
continue;
}
if (lastToken[0] == Token.RULE && serializeRules(token[1]) == serializeRules(lastToken[1])) {
Array.prototype.push.apply(lastToken[2], token[2]);
optimizeProperties(lastToken[2], true, true, context);
token[2] = [];
} else if (lastToken[0] == Token.RULE && serializeBody(token[2]) == serializeBody(lastToken[2]) &&
isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) &&
isMergeable(serializeRules(lastToken[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) &&
lastToken[1].length < mergeLimit) {
lastToken[1] = tidyRules(lastToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings);
lastToken[1] = lastToken.length > 1 ? sortSelectors(lastToken[1], selectorsSortingMethod) : lastToken[1];
token[2] = [];
} else {
lastToken = token;
}
}
}
module.exports = mergeAdjacent;

View File

@ -0,0 +1,103 @@
var canReorder = require('./reorderable').canReorder;
var canReorderSingle = require('./reorderable').canReorderSingle;
var extractProperties = require('./extract-properties');
var rulesOverlap = require('./rules-overlap');
var serializeRules = require('../../writer/one-time').rules;
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var Token = require('../../tokenizer/token');
function mergeMediaQueries(tokens, context) {
var mergeSemantically = context.options.level[OptimizationLevel.Two].mergeSemantically;
var specificityCache = context.cache.specificity;
var candidates = {};
var reduced = [];
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
if (token[0] != Token.NESTED_BLOCK) {
continue;
}
var key = serializeRules(token[1]);
var candidate = candidates[key];
if (!candidate) {
candidate = [];
candidates[key] = candidate;
}
candidate.push(i);
}
for (var name in candidates) {
var positions = candidates[name];
positionLoop:
for (var j = positions.length - 1; j > 0; j--) {
var positionOne = positions[j];
var tokenOne = tokens[positionOne];
var positionTwo = positions[j - 1];
var tokenTwo = tokens[positionTwo];
directionLoop:
for (var direction = 1; direction >= -1; direction -= 2) {
var topToBottom = direction == 1;
var from = topToBottom ? positionOne + 1 : positionTwo - 1;
var to = topToBottom ? positionTwo : positionOne;
var delta = topToBottom ? 1 : -1;
var source = topToBottom ? tokenOne : tokenTwo;
var target = topToBottom ? tokenTwo : tokenOne;
var movedProperties = extractProperties(source);
while (from != to) {
var traversedProperties = extractProperties(tokens[from]);
from += delta;
if (mergeSemantically && allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache)) {
continue;
}
if (!canReorder(movedProperties, traversedProperties, specificityCache))
continue directionLoop;
}
target[2] = topToBottom ?
source[2].concat(target[2]) :
target[2].concat(source[2]);
source[2] = [];
reduced.push(target);
continue positionLoop;
}
}
}
return reduced;
}
function allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache) {
var movedProperty;
var movedRule;
var traversedProperty;
var traversedRule;
var i, l;
var j, m;
for (i = 0, l = movedProperties.length; i < l; i++) {
movedProperty = movedProperties[i];
movedRule = movedProperty[5];
for (j = 0, m = traversedProperties.length; j < m; j++) {
traversedProperty = traversedProperties[j];
traversedRule = traversedProperty[5];
if (rulesOverlap(movedRule, traversedRule, true) && !canReorderSingle(movedProperty, traversedProperty, specificityCache)) {
return false;
}
}
}
return true;
}
module.exports = mergeMediaQueries;

View File

@ -0,0 +1,80 @@
var isMergeable = require('./is-mergeable');
var sortSelectors = require('../level-1/sort-selectors');
var tidyRules = require('../level-1/tidy-rules');
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
var Token = require('../../tokenizer/token');
function unsafeSelector(value) {
return /\.|\*| :/.test(value);
}
function isBemElement(token) {
var asString = serializeRules(token[1]);
return asString.indexOf('__') > -1 || asString.indexOf('--') > -1;
}
function withoutModifier(selector) {
return selector.replace(/--[^ ,>\+~:]+/g, '');
}
function removeAnyUnsafeElements(left, candidates) {
var leftSelector = withoutModifier(serializeRules(left[1]));
for (var body in candidates) {
var right = candidates[body];
var rightSelector = withoutModifier(serializeRules(right[1]));
if (rightSelector.indexOf(leftSelector) > -1 || leftSelector.indexOf(rightSelector) > -1)
delete candidates[body];
}
}
function mergeNonAdjacentByBody(tokens, context) {
var options = context.options;
var mergeSemantically = options.level[OptimizationLevel.Two].mergeSemantically;
var adjacentSpace = options.compatibility.selectors.adjacentSpace;
var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod;
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
var candidates = {};
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
if (token[0] != Token.RULE)
continue;
if (token[2].length > 0 && (!mergeSemantically && unsafeSelector(serializeRules(token[1]))))
candidates = {};
if (token[2].length > 0 && mergeSemantically && isBemElement(token))
removeAnyUnsafeElements(token, candidates);
var candidateBody = serializeBody(token[2]);
var oldToken = candidates[candidateBody];
if (oldToken &&
isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) &&
isMergeable(serializeRules(oldToken[1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging)) {
if (token[2].length > 0) {
token[1] = tidyRules(oldToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings);
token[1] = token[1].length > 1 ? sortSelectors(token[1], selectorsSortingMethod) : token[1];
} else {
token[1] = oldToken[1].concat(token[1]);
}
oldToken[2] = [];
candidates[candidateBody] = null;
}
candidates[serializeBody(token[2])] = token;
}
}
module.exports = mergeNonAdjacentByBody;

View File

@ -0,0 +1,78 @@
var canReorder = require('./reorderable').canReorder;
var extractProperties = require('./extract-properties');
var optimizeProperties = require('./properties/optimize');
var serializeRules = require('../../writer/one-time').rules;
var Token = require('../../tokenizer/token');
function mergeNonAdjacentBySelector(tokens, context) {
var specificityCache = context.cache.specificity;
var allSelectors = {};
var repeatedSelectors = [];
var i;
for (i = tokens.length - 1; i >= 0; i--) {
if (tokens[i][0] != Token.RULE)
continue;
if (tokens[i][2].length === 0)
continue;
var selector = serializeRules(tokens[i][1]);
allSelectors[selector] = [i].concat(allSelectors[selector] || []);
if (allSelectors[selector].length == 2)
repeatedSelectors.push(selector);
}
for (i = repeatedSelectors.length - 1; i >= 0; i--) {
var positions = allSelectors[repeatedSelectors[i]];
selectorIterator:
for (var j = positions.length - 1; j > 0; j--) {
var positionOne = positions[j - 1];
var tokenOne = tokens[positionOne];
var positionTwo = positions[j];
var tokenTwo = tokens[positionTwo];
directionIterator:
for (var direction = 1; direction >= -1; direction -= 2) {
var topToBottom = direction == 1;
var from = topToBottom ? positionOne + 1 : positionTwo - 1;
var to = topToBottom ? positionTwo : positionOne;
var delta = topToBottom ? 1 : -1;
var moved = topToBottom ? tokenOne : tokenTwo;
var target = topToBottom ? tokenTwo : tokenOne;
var movedProperties = extractProperties(moved);
while (from != to) {
var traversedProperties = extractProperties(tokens[from]);
from += delta;
// traversed then moved as we move selectors towards the start
var reorderable = topToBottom ?
canReorder(movedProperties, traversedProperties, specificityCache) :
canReorder(traversedProperties, movedProperties, specificityCache);
if (!reorderable && !topToBottom)
continue selectorIterator;
if (!reorderable && topToBottom)
continue directionIterator;
}
if (topToBottom) {
Array.prototype.push.apply(moved[2], target[2]);
target[2] = moved[2];
} else {
Array.prototype.push.apply(target[2], moved[2]);
}
optimizeProperties(target[2], true, true, context);
moved[2] = [];
}
}
}
}
module.exports = mergeNonAdjacentBySelector;

View File

@ -0,0 +1,134 @@
var mergeAdjacent = require('./merge-adjacent');
var mergeMediaQueries = require('./merge-media-queries');
var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body');
var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector');
var reduceNonAdjacent = require('./reduce-non-adjacent');
var removeDuplicateFontAtRules = require('./remove-duplicate-font-at-rules');
var removeDuplicateMediaQueries = require('./remove-duplicate-media-queries');
var removeDuplicates = require('./remove-duplicates');
var removeUnusedAtRules = require('./remove-unused-at-rules');
var restructure = require('./restructure');
var optimizeProperties = require('./properties/optimize');
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var Token = require('../../tokenizer/token');
function removeEmpty(tokens) {
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
var isEmpty = false;
switch (token[0]) {
case Token.RULE:
isEmpty = token[1].length === 0 || token[2].length === 0;
break;
case Token.NESTED_BLOCK:
removeEmpty(token[2]);
isEmpty = token[2].length === 0;
break;
case Token.AT_RULE:
isEmpty = token[1].length === 0;
break;
case Token.AT_RULE_BLOCK:
isEmpty = token[2].length === 0;
}
if (isEmpty) {
tokens.splice(i, 1);
i--;
l--;
}
}
}
function recursivelyOptimizeBlocks(tokens, context) {
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
if (token[0] == Token.NESTED_BLOCK) {
var isKeyframes = /@(-moz-|-o-|-webkit-)?keyframes/.test(token[1][0][1]);
level2Optimize(token[2], context, !isKeyframes);
}
}
}
function recursivelyOptimizeProperties(tokens, context) {
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
switch (token[0]) {
case Token.RULE:
optimizeProperties(token[2], true, true, context);
break;
case Token.NESTED_BLOCK:
recursivelyOptimizeProperties(token[2], context);
}
}
}
function level2Optimize(tokens, context, withRestructuring) {
var levelOptions = context.options.level[OptimizationLevel.Two];
var reduced;
var i;
recursivelyOptimizeBlocks(tokens, context);
recursivelyOptimizeProperties(tokens, context);
if (levelOptions.removeDuplicateRules) {
removeDuplicates(tokens, context);
}
if (levelOptions.mergeAdjacentRules) {
mergeAdjacent(tokens, context);
}
if (levelOptions.reduceNonAdjacentRules) {
reduceNonAdjacent(tokens, context);
}
if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'body') {
mergeNonAdjacentBySelector(tokens, context);
}
if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'selector') {
mergeNonAdjacentByBody(tokens, context);
}
if (levelOptions.restructureRules && levelOptions.mergeAdjacentRules && withRestructuring) {
restructure(tokens, context);
mergeAdjacent(tokens, context);
}
if (levelOptions.restructureRules && !levelOptions.mergeAdjacentRules && withRestructuring) {
restructure(tokens, context);
}
if (levelOptions.removeDuplicateFontRules) {
removeDuplicateFontAtRules(tokens, context);
}
if (levelOptions.removeDuplicateMediaBlocks) {
removeDuplicateMediaQueries(tokens, context);
}
if (levelOptions.removeUnusedAtRules) {
removeUnusedAtRules(tokens, context);
}
if (levelOptions.mergeMedia) {
reduced = mergeMediaQueries(tokens, context);
for (i = reduced.length - 1; i >= 0; i--) {
level2Optimize(reduced[i][2], context, false);
}
}
if (levelOptions.removeEmpty) {
removeEmpty(tokens);
}
return tokens;
}
module.exports = level2Optimize;

View File

@ -0,0 +1,28 @@
var Marker = require('../../../tokenizer/marker');
function everyValuesPair(fn, left, right) {
var leftSize = left.value.length;
var rightSize = right.value.length;
var total = Math.max(leftSize, rightSize);
var lowerBound = Math.min(leftSize, rightSize) - 1;
var leftValue;
var rightValue;
var position;
for (position = 0; position < total; position++) {
leftValue = left.value[position] && left.value[position][1] || leftValue;
rightValue = right.value[position] && right.value[position][1] || rightValue;
if (leftValue == Marker.COMMA || rightValue == Marker.COMMA) {
continue;
}
if (!fn(leftValue, rightValue, position, position <= lowerBound)) {
return false;
}
}
return true;
}
module.exports = everyValuesPair;

View File

@ -0,0 +1,40 @@
var compactable = require('../compactable');
function findComponentIn(shorthand, longhand) {
var comparator = nameComparator(longhand);
return findInDirectComponents(shorthand, comparator) || findInSubComponents(shorthand, comparator);
}
function nameComparator(to) {
return function (property) {
return to.name === property.name;
};
}
function findInDirectComponents(shorthand, comparator) {
return shorthand.components.filter(comparator)[0];
}
function findInSubComponents(shorthand, comparator) {
var shorthandComponent;
var longhandMatch;
var i, l;
if (!compactable[shorthand.name].shorthandComponents) {
return;
}
for (i = 0, l = shorthand.components.length; i < l; i++) {
shorthandComponent = shorthand.components[i];
longhandMatch = findInDirectComponents(shorthandComponent, comparator);
if (longhandMatch) {
return longhandMatch;
}
}
return;
}
module.exports = findComponentIn;

View File

@ -0,0 +1,10 @@
function hasInherit(property) {
for (var i = property.value.length - 1; i >= 0; i--) {
if (property.value[i][1] == 'inherit')
return true;
}
return false;
}
module.exports = hasInherit;

View File

@ -0,0 +1,22 @@
var compactable = require('../compactable');
function isComponentOf(property1, property2, shallow) {
return isDirectComponentOf(property1, property2) ||
!shallow && !!compactable[property1.name].shorthandComponents && isSubComponentOf(property1, property2);
}
function isDirectComponentOf(property1, property2) {
var descriptor = compactable[property1.name];
return 'components' in descriptor && descriptor.components.indexOf(property2.name) > -1;
}
function isSubComponentOf(property1, property2) {
return property1
.components
.some(function (component) {
return isDirectComponentOf(component, property2);
});
}
module.exports = isComponentOf;

View File

@ -0,0 +1,11 @@
var Marker = require('../../../tokenizer/marker');
function isMergeableShorthand(shorthand) {
if (shorthand.name != 'font') {
return true;
}
return shorthand.value[0][1].indexOf(Marker.INTERNAL) == -1;
}
module.exports = isMergeableShorthand;

View File

@ -0,0 +1,445 @@
var everyValuesPair = require('./every-values-pair');
var hasInherit = require('./has-inherit');
var populateComponents = require('./populate-components');
var compactable = require('../compactable');
var deepClone = require('../clone').deep;
var 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 (componentName in components) {
if (undefined !== important && components[componentName].important != important) {
return true;
}
important = components[componentName].important;
}
return false;
}
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) {
var metadata = [];
var component;
var originalValue;
var componentMetadata;
var componentName;
for (componentName in components) {
component = components[componentName];
originalValue = component.all[component.position];
componentMetadata = originalValue[at][originalValue[at].length - 1];
Array.prototype.push.apply(metadata, componentMetadata);
}
return metadata.sort(metadataSorter);
}
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, shorthandName],
[Token.PROPERTY_VALUE, descriptor.defaultValue]
];
var all;
var newProperty = wrapSingle(newValuePlaceholder);
newProperty.shorthand = true;
newProperty.dirty = true;
populateComponents([newProperty], validator, []);
for (var i = 0, l = descriptor.components.length; i < l; i++) {
var component = candidateComponents[descriptor.components[i]];
newProperty.components[i] = deepClone(component);
newProperty.important = component.important;
all = component.all;
}
for (var componentName in candidateComponents) {
candidateComponents[componentName].unused = true;
}
nameMetadata = joinMetadata(candidateComponents, 1);
newValuePlaceholder[1].push(nameMetadata);
valueMetadata = joinMetadata(candidateComponents, 2);
newValuePlaceholder[2].push(valueMetadata);
newProperty.position = all.length;
newProperty.all = all;
newProperty.all.push(newValuePlaceholder);
properties.push(newProperty);
}
module.exports = mergeIntoShorthands;

View File

@ -0,0 +1,40 @@
var mergeIntoShorthands = require('./merge-into-shorthands');
var overrideProperties = require('./override-properties');
var populateComponents = require('./populate-components');
var restoreWithComponents = require('../restore-with-components');
var wrapForOptimizing = require('../../wrap-for-optimizing').all;
var removeUnused = require('../../remove-unused');
var restoreFromOptimizing = require('../../restore-from-optimizing');
var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel;
function optimizeProperties(properties, withOverriding, withMerging, context) {
var levelOptions = context.options.level[OptimizationLevel.Two];
var _properties = wrapForOptimizing(properties, false, levelOptions.skipProperties);
var _property;
var i, l;
populateComponents(_properties, context.validator, context.warnings);
for (i = 0, l = _properties.length; i < l; i++) {
_property = _properties[i];
if (_property.block) {
optimizeProperties(_property.value[0][1], withOverriding, withMerging, context);
}
}
if (withMerging && levelOptions.mergeIntoShorthands) {
mergeIntoShorthands(_properties, context.validator);
}
if (withOverriding && levelOptions.overrideProperties) {
overrideProperties(_properties, withMerging, context.options.compatibility, context.validator);
}
restoreFromOptimizing(_properties, restoreWithComponents);
removeUnused(_properties);
}
module.exports = optimizeProperties;

View File

@ -0,0 +1,484 @@
var hasInherit = require('./has-inherit');
var everyValuesPair = require('./every-values-pair');
var findComponentIn = require('./find-component-in');
var isComponentOf = require('./is-component-of');
var isMergeableShorthand = require('./is-mergeable-shorthand');
var overridesNonComponentShorthand = require('./overrides-non-component-shorthand');
var sameVendorPrefixesIn = require('./vendor-prefixes').same;
var compactable = require('../compactable');
var deepClone = require('../clone').deep;
var restoreWithComponents = require('../restore-with-components');
var shallowClone = require('../clone').shallow;
var restoreFromOptimizing = require('../../restore-from-optimizing');
var Token = require('../../../tokenizer/token');
var Marker = require('../../../tokenizer/marker');
var serializeProperty = require('../../../writer/one-time').property;
function wouldBreakCompatibility(property, validator) {
for (var i = 0; i < property.components.length; i++) {
var component = property.components[i];
var descriptor = compactable[component.name];
var canOverride = descriptor && descriptor.canOverride || canOverride.sameValue;
var _component = shallowClone(component);
_component.value = [[Token.PROPERTY_VALUE, descriptor.defaultValue]];
if (!everyValuesPair(canOverride.bind(null, validator), _component, component)) {
return true;
}
}
return false;
}
function overrideIntoMultiplex(property, by) {
by.unused = true;
turnIntoMultiplex(by, multiplexSize(property));
property.value = by.value;
}
function overrideByMultiplex(property, by) {
by.unused = true;
property.multiplex = true;
property.value = by.value;
}
function overrideSimple(property, by) {
by.unused = true;
property.value = by.value;
}
function override(property, by) {
if (by.multiplex)
overrideByMultiplex(property, by);
else if (property.multiplex)
overrideIntoMultiplex(property, by);
else
overrideSimple(property, by);
}
function overrideShorthand(property, by) {
by.unused = true;
for (var i = 0, l = property.components.length; i < l; i++) {
override(property.components[i], by.components[i], property.multiplex);
}
}
function turnIntoMultiplex(property, size) {
property.multiplex = true;
if (compactable[property.name].shorthand) {
turnShorthandValueIntoMultiplex(property, size);
} else {
turnLonghandValueIntoMultiplex(property, size);
}
}
function turnShorthandValueIntoMultiplex(property, size) {
var component;
var i, l;
for (i = 0, l = property.components.length; i < l; i++) {
component = property.components[i];
if (!component.multiplex) {
turnLonghandValueIntoMultiplex(component, size);
}
}
}
function turnLonghandValueIntoMultiplex(property, size) {
var descriptor = compactable[property.name];
var withRealValue = descriptor.intoMultiplexMode == 'real';
var withValue = descriptor.intoMultiplexMode == 'real' ?
property.value.slice(0) :
(descriptor.intoMultiplexMode == 'placeholder' ? descriptor.placeholderValue : descriptor.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) {
var size = 0;
for (var i = 0, l = component.value.length; i < l; i++) {
if (component.value[i][1] == Marker.COMMA)
size++;
}
return size + 1;
}
function lengthOf(property) {
var fakeAsArray = [
Token.PROPERTY,
[Token.PROPERTY_NAME, property.name]
].concat(property.value);
return serializeProperty([fakeAsArray], 0).length;
}
function moreSameShorthands(properties, startAt, name) {
// Since we run the main loop in `compactOverrides` backwards, at this point some
// properties may not be marked as unused.
// We should consider reverting the order if possible
var count = 0;
for (var i = startAt; i >= 0; i--) {
if (properties[i].name == name && !properties[i].unused)
count++;
if (count > 1)
break;
}
return count > 1;
}
function overridingFunction(shorthand, validator) {
for (var i = 0, l = shorthand.components.length; i < l; i++) {
if (!anyValue(validator.isUrl, shorthand.components[i]) && anyValue(validator.isFunction, shorthand.components[i])) {
return true;
}
}
return false;
}
function anyValue(fn, property) {
for (var i = 0, l = property.value.length; i < l; i++) {
if (property.value[i][1] == Marker.COMMA)
continue;
if (fn(property.value[i][1]))
return true;
}
return false;
}
function wouldResultInLongerValue(left, right) {
if (!left.multiplex && !right.multiplex || left.multiplex && right.multiplex)
return false;
var multiplex = left.multiplex ? left : right;
var simple = left.multiplex ? right : left;
var component;
var multiplexClone = deepClone(multiplex);
restoreFromOptimizing([multiplexClone], restoreWithComponents);
var simpleClone = deepClone(simple);
restoreFromOptimizing([simpleClone], restoreWithComponents);
var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone);
if (left.multiplex) {
component = findComponentIn(multiplexClone, simpleClone);
overrideIntoMultiplex(component, simpleClone);
} else {
component = findComponentIn(simpleClone, multiplexClone);
turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone));
overrideByMultiplex(component, multiplexClone);
}
restoreFromOptimizing([simpleClone], restoreWithComponents);
var lengthAfter = lengthOf(simpleClone);
return lengthBefore <= lengthAfter;
}
function isCompactable(property) {
return property.name in compactable;
}
function noneOverrideHack(left, right) {
return !left.multiplex &&
(left.name == 'background' || left.name == 'background-image') &&
right.multiplex &&
(right.name == 'background' || right.name == 'background-image') &&
anyLayerIsNone(right.value);
}
function anyLayerIsNone(values) {
var layers = intoLayers(values);
for (var i = 0, l = layers.length; i < l; i++) {
if (layers[i].length == 1 && layers[i][0][1] == 'none')
return true;
}
return false;
}
function intoLayers(values) {
var layers = [];
for (var i = 0, layer = [], l = values.length; i < l; i++) {
var value = values[i];
if (value[1] == Marker.COMMA) {
layers.push(layer);
layer = [];
} else {
layer.push(value);
}
}
layers.push(layer);
return layers;
}
function overrideProperties(properties, withMerging, compatibility, validator) {
var mayOverride, right, left, component;
var overriddenComponents;
var overriddenComponent;
var overridingComponent;
var overridable;
var i, j, k;
propertyLoop:
for (i = properties.length - 1; i >= 0; i--) {
right = properties[i];
if (!isCompactable(right))
continue;
if (right.block)
continue;
mayOverride = compactable[right.name].canOverride;
traverseLoop:
for (j = i - 1; j >= 0; j--) {
left = properties[j];
if (!isCompactable(left))
continue;
if (left.block)
continue;
if (left.unused || right.unused)
continue;
if (left.hack && !right.hack && !right.important || !left.hack && !left.important && right.hack)
continue;
if (left.important == right.important && left.hack[0] != right.hack[0])
continue;
if (left.important == right.important && (left.hack[0] != right.hack[0] || (left.hack[1] && left.hack[1] != right.hack[1])))
continue;
if (hasInherit(right))
continue;
if (noneOverrideHack(left, right))
continue;
if (right.shorthand && isComponentOf(right, left)) {
// maybe `left` can be overridden by `right` which is a shorthand?
if (!right.important && left.important)
continue;
if (!sameVendorPrefixesIn([left], right.components))
continue;
if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator))
continue;
if (!isMergeableShorthand(right)) {
left.unused = true;
continue;
}
component = findComponentIn(right, left);
mayOverride = compactable[left.name].canOverride;
if (everyValuesPair(mayOverride.bind(null, validator), left, component)) {
left.unused = true;
}
} else if (right.shorthand && overridesNonComponentShorthand(right, left)) {
// `right` is a shorthand while `left` can be overriden by it, think `border` and `border-top`
if (!right.important && left.important) {
continue;
}
if (!sameVendorPrefixesIn([left], right.components)) {
continue;
}
if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator)) {
continue;
}
overriddenComponents = left.shorthand ?
left.components:
[left];
for (k = overriddenComponents.length - 1; k >= 0; k--) {
overriddenComponent = overriddenComponents[k];
overridingComponent = findComponentIn(right, overriddenComponent);
mayOverride = compactable[overriddenComponent.name].canOverride;
if (!everyValuesPair(mayOverride.bind(null, validator), left, overridingComponent)) {
continue traverseLoop;
}
}
left.unused = true;
} else if (withMerging && left.shorthand && !right.shorthand && isComponentOf(left, right, true)) {
// maybe `right` can be pulled into `left` which is a shorthand?
if (right.important && !left.important)
continue;
if (!right.important && left.important) {
right.unused = true;
continue;
}
// Pending more clever algorithm in #527
if (moreSameShorthands(properties, i - 1, left.name))
continue;
if (overridingFunction(left, validator))
continue;
if (!isMergeableShorthand(left))
continue;
component = findComponentIn(left, right);
if (everyValuesPair(mayOverride.bind(null, validator), component, right)) {
var disabledBackgroundMerging =
!compatibility.properties.backgroundClipMerging && component.name.indexOf('background-clip') > -1 ||
!compatibility.properties.backgroundOriginMerging && component.name.indexOf('background-origin') > -1 ||
!compatibility.properties.backgroundSizeMerging && component.name.indexOf('background-size') > -1;
var nonMergeableValue = compactable[right.name].nonMergeableValue === right.value[0][1];
if (disabledBackgroundMerging || nonMergeableValue)
continue;
if (!compatibility.properties.merging && wouldBreakCompatibility(left, validator))
continue;
if (component.value[0][1] != right.value[0][1] && (hasInherit(left) || hasInherit(right)))
continue;
if (wouldResultInLongerValue(left, right))
continue;
if (!left.multiplex && right.multiplex)
turnIntoMultiplex(left, multiplexSize(right));
override(component, right);
left.dirty = true;
}
} else if (withMerging && left.shorthand && right.shorthand && left.name == right.name) {
// merge if all components can be merged
if (!left.multiplex && right.multiplex)
continue;
if (!right.important && left.important) {
right.unused = true;
continue propertyLoop;
}
if (right.important && !left.important) {
left.unused = true;
continue;
}
if (!isMergeableShorthand(right)) {
left.unused = true;
continue;
}
for (k = left.components.length - 1; k >= 0; k--) {
var leftComponent = left.components[k];
var rightComponent = right.components[k];
mayOverride = compactable[leftComponent.name].canOverride;
if (!everyValuesPair(mayOverride.bind(null, validator), leftComponent, rightComponent))
continue propertyLoop;
}
overrideShorthand(left, right);
left.dirty = true;
} else if (withMerging && left.shorthand && right.shorthand && isComponentOf(left, right)) {
// border is a shorthand but any of its components is a shorthand too
if (!left.important && right.important)
continue;
component = findComponentIn(left, right);
mayOverride = compactable[right.name].canOverride;
if (!everyValuesPair(mayOverride.bind(null, validator), component, right))
continue;
if (left.important && !right.important) {
right.unused = true;
continue;
}
var rightRestored = compactable[right.name].restore(right, compactable);
if (rightRestored.length > 1)
continue;
component = findComponentIn(left, right);
override(component, right);
right.dirty = true;
} else if (left.name == right.name) {
// two non-shorthands should be merged based on understandability
overridable = true;
if (right.shorthand) {
for (k = right.components.length - 1; k >= 0 && overridable; k--) {
overriddenComponent = left.components[k];
overridingComponent = right.components[k];
mayOverride = compactable[overridingComponent.name].canOverride;
overridable = overridable && everyValuesPair(mayOverride.bind(null, validator), overriddenComponent, overridingComponent);
}
} else {
mayOverride = compactable[right.name].canOverride;
overridable = everyValuesPair(mayOverride.bind(null, validator), left, right);
}
if (left.important && !right.important && overridable) {
right.unused = true;
continue;
}
if (!left.important && right.important && overridable) {
left.unused = true;
continue;
}
if (!overridable) {
continue;
}
left.unused = true;
}
}
}
}
module.exports = overrideProperties;

View File

@ -0,0 +1,9 @@
var compactable = require('../compactable');
function overridesNonComponentShorthand(property1, property2) {
return property1.name in compactable &&
'overridesShorthands' in compactable[property1.name] &&
compactable[property1.name].overridesShorthands.indexOf(property2.name) > -1;
}
module.exports = overridesNonComponentShorthand;

View File

@ -0,0 +1,42 @@
var compactable = require('../compactable');
var InvalidPropertyError = require('../invalid-property-error');
function populateComponents(properties, validator, warnings) {
var component;
var j, m;
for (var i = properties.length - 1; i >= 0; i--) {
var property = properties[i];
var descriptor = compactable[property.name];
if (descriptor && descriptor.shorthand) {
property.shorthand = true;
property.dirty = true;
try {
property.components = descriptor.breakUp(property, compactable, validator);
if (descriptor.shorthandComponents) {
for (j = 0, m = property.components.length; j < m; j++) {
component = property.components[j];
component.components = compactable[component.name].breakUp(component, compactable, validator);
}
}
} catch (e) {
if (e instanceof InvalidPropertyError) {
property.components = []; // this will set property.unused to true below
warnings.push(e.message);
} else {
throw e;
}
}
if (property.components.length > 0)
property.multiplex = property.components[0].multiplex;
else
property.unused = true;
}
}
}
module.exports = populateComponents;

View File

@ -0,0 +1,15 @@
var sameVendorPrefixes = require('./vendor-prefixes').same;
function understandable(validator, value1, value2, _position, isPaired) {
if (!sameVendorPrefixes(value1, value2)) {
return false;
}
if (isPaired && validator.isVariable(value1) !== validator.isVariable(value2)) {
return false;
}
return true;
}
module.exports = understandable;

View File

@ -0,0 +1,23 @@
var VENDOR_PREFIX_PATTERN = /(?:^|\W)(\-\w+\-)/g;
function unique(value) {
var prefixes = [];
var match;
while ((match = VENDOR_PREFIX_PATTERN.exec(value)) !== null) {
if (prefixes.indexOf(match[0]) == -1) {
prefixes.push(match[0]);
}
}
return prefixes;
}
function same(value1, value2) {
return unique(value1).sort().join(',') == unique(value2).sort().join(',');
}
module.exports = {
unique: unique,
same: same
};

View File

@ -0,0 +1,180 @@
var isMergeable = require('./is-mergeable');
var optimizeProperties = require('./properties/optimize');
var cloneArray = require('../../utils/clone-array');
var Token = require('../../tokenizer/token');
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
function reduceNonAdjacent(tokens, context) {
var options = context.options;
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
var candidates = {};
var repeated = [];
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
if (token[0] != Token.RULE) {
continue;
} else if (token[2].length === 0) {
continue;
}
var selectorAsString = serializeRules(token[1]);
var isComplexAndNotSpecial = token[1].length > 1 &&
isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging);
var wrappedSelectors = wrappedSelectorsFrom(token[1]);
var selectors = isComplexAndNotSpecial ?
[selectorAsString].concat(wrappedSelectors) :
[selectorAsString];
for (var j = 0, m = selectors.length; j < m; j++) {
var selector = selectors[j];
if (!candidates[selector])
candidates[selector] = [];
else
repeated.push(selector);
candidates[selector].push({
where: i,
list: wrappedSelectors,
isPartial: isComplexAndNotSpecial && j > 0,
isComplex: isComplexAndNotSpecial && j === 0
});
}
}
reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context);
reduceComplexNonAdjacentCases(tokens, candidates, options, context);
}
function wrappedSelectorsFrom(list) {
var wrapped = [];
for (var i = 0; i < list.length; i++) {
wrapped.push([list[i][1]]);
}
return wrapped;
}
function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context) {
function filterOut(idx, bodies) {
return data[idx].isPartial && bodies.length === 0;
}
function reduceBody(token, newBody, processedCount, tokenIdx) {
if (!data[processedCount - tokenIdx - 1].isPartial)
token[2] = newBody;
}
for (var i = 0, l = repeated.length; i < l; i++) {
var selector = repeated[i];
var data = candidates[selector];
reduceSelector(tokens, data, {
filterOut: filterOut,
callback: reduceBody
}, options, context);
}
}
function reduceComplexNonAdjacentCases(tokens, candidates, options, context) {
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
var localContext = {};
function filterOut(idx) {
return localContext.data[idx].where < localContext.intoPosition;
}
function collectReducedBodies(token, newBody, processedCount, tokenIdx) {
if (tokenIdx === 0)
localContext.reducedBodies.push(newBody);
}
allSelectors:
for (var complexSelector in candidates) {
var into = candidates[complexSelector];
if (!into[0].isComplex)
continue;
var intoPosition = into[into.length - 1].where;
var intoToken = tokens[intoPosition];
var reducedBodies = [];
var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) ?
into[0].list :
[complexSelector];
localContext.intoPosition = intoPosition;
localContext.reducedBodies = reducedBodies;
for (var j = 0, m = selectors.length; j < m; j++) {
var selector = selectors[j];
var data = candidates[selector];
if (data.length < 2)
continue allSelectors;
localContext.data = data;
reduceSelector(tokens, data, {
filterOut: filterOut,
callback: collectReducedBodies
}, options, context);
if (serializeBody(reducedBodies[reducedBodies.length - 1]) != serializeBody(reducedBodies[0]))
continue allSelectors;
}
intoToken[2] = reducedBodies[0];
}
}
function reduceSelector(tokens, data, context, options, outerContext) {
var bodies = [];
var bodiesAsList = [];
var processedTokens = [];
for (var j = data.length - 1; j >= 0; j--) {
if (context.filterOut(j, bodies))
continue;
var where = data[j].where;
var token = tokens[where];
var clonedBody = cloneArray(token[2]);
bodies = bodies.concat(clonedBody);
bodiesAsList.push(clonedBody);
processedTokens.push(where);
}
optimizeProperties(bodies, true, false, outerContext);
var processedCount = processedTokens.length;
var propertyIdx = bodies.length - 1;
var tokenIdx = processedCount - 1;
while (tokenIdx >= 0) {
if ((tokenIdx === 0 || (bodies[propertyIdx] && bodiesAsList[tokenIdx].indexOf(bodies[propertyIdx]) > -1)) && propertyIdx > -1) {
propertyIdx--;
continue;
}
var newBody = bodies.splice(propertyIdx + 1);
context.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
tokenIdx--;
}
}
module.exports = reduceNonAdjacent;

View File

@ -0,0 +1,30 @@
var Token = require('../../tokenizer/token');
var serializeAll = require('../../writer/one-time').all;
var FONT_FACE_SCOPE = '@font-face';
function removeDuplicateFontAtRules(tokens) {
var fontAtRules = [];
var token;
var key;
var i, l;
for (i = 0, l = tokens.length; i < l; i++) {
token = tokens[i];
if (token[0] != Token.AT_RULE_BLOCK && token[1][0][1] != FONT_FACE_SCOPE) {
continue;
}
key = serializeAll([token]);
if (fontAtRules.indexOf(key) > -1) {
token[2] = [];
} else {
fontAtRules.push(key);
}
}
}
module.exports = removeDuplicateFontAtRules;

View File

@ -0,0 +1,30 @@
var Token = require('../../tokenizer/token');
var serializeAll = require('../../writer/one-time').all;
var serializeRules = require('../../writer/one-time').rules;
function removeDuplicateMediaQueries(tokens) {
var candidates = {};
var candidate;
var token;
var key;
var i, l;
for (i = 0, l = tokens.length; i < l; i++) {
token = tokens[i];
if (token[0] != Token.NESTED_BLOCK) {
continue;
}
key = serializeRules(token[1]) + '%' + serializeAll(token[2]);
candidate = candidates[key];
if (candidate) {
candidate[2] = [];
}
candidates[key] = token;
}
}
module.exports = removeDuplicateMediaQueries;

View File

@ -0,0 +1,43 @@
var Token = require('../../tokenizer/token');
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
function removeDuplicates(tokens) {
var matched = {};
var moreThanOnce = [];
var id, token;
var body, bodies;
for (var i = 0, l = tokens.length; i < l; i++) {
token = tokens[i];
if (token[0] != Token.RULE)
continue;
id = serializeRules(token[1]);
if (matched[id] && matched[id].length == 1)
moreThanOnce.push(id);
else
matched[id] = matched[id] || [];
matched[id].push(i);
}
for (i = 0, l = moreThanOnce.length; i < l; i++) {
id = moreThanOnce[i];
bodies = [];
for (var j = matched[id].length - 1; j >= 0; j--) {
token = tokens[matched[id][j]];
body = serializeBody(token[2]);
if (bodies.indexOf(body) > -1)
token[2] = [];
else
bodies.push(body);
}
}
}
module.exports = removeDuplicates;

View File

@ -0,0 +1,249 @@
var populateComponents = require('./properties/populate-components');
var wrapForOptimizing = require('../wrap-for-optimizing').single;
var restoreFromOptimizing = require('../restore-from-optimizing');
var Token = require('../../tokenizer/token');
var animationNameRegex = /^(\-moz\-|\-o\-|\-webkit\-)?animation-name$/;
var animationRegex = /^(\-moz\-|\-o\-|\-webkit\-)?animation$/;
var keyframeRegex = /^@(\-moz\-|\-o\-|\-webkit\-)?keyframes /;
var importantRegex = /\s{0,31}!important$/;
var optionalMatchingQuotesRegex = /^(['"]?)(.*)\1$/;
function normalize(value) {
return value
.replace(optionalMatchingQuotesRegex, '$2')
.replace(importantRegex, '');
}
function removeUnusedAtRules(tokens, context) {
removeUnusedAtRule(tokens, matchCounterStyle, markCounterStylesAsUsed, context);
removeUnusedAtRule(tokens, matchFontFace, markFontFacesAsUsed, context);
removeUnusedAtRule(tokens, matchKeyframe, markKeyframesAsUsed, context);
removeUnusedAtRule(tokens, matchNamespace, markNamespacesAsUsed, context);
}
function removeUnusedAtRule(tokens, matchCallback, markCallback, context) {
var atRules = {};
var atRule;
var atRuleTokens;
var atRuleToken;
var zeroAt;
var i, l;
for (i = 0, l = tokens.length; i < l; i++) {
matchCallback(tokens[i], atRules);
}
if (Object.keys(atRules).length === 0) {
return;
}
markUsedAtRules(tokens, markCallback, atRules, context);
for (atRule in atRules) {
atRuleTokens = atRules[atRule];
for (i = 0, l = atRuleTokens.length; i < l; i++) {
atRuleToken = atRuleTokens[i];
zeroAt = atRuleToken[0] == Token.AT_RULE ? 1 : 2;
atRuleToken[zeroAt] = [];
}
}
}
function markUsedAtRules(tokens, markCallback, atRules, context) {
var boundMarkCallback = markCallback(atRules);
var i, l;
for (i = 0, l = tokens.length; i < l; i++) {
switch (tokens[i][0]) {
case Token.RULE:
boundMarkCallback(tokens[i], context);
break;
case Token.NESTED_BLOCK:
markUsedAtRules(tokens[i][2], markCallback, atRules, context);
}
}
}
function matchCounterStyle(token, atRules) {
var match;
if (token[0] == Token.AT_RULE_BLOCK && token[1][0][1].indexOf('@counter-style') === 0) {
match = token[1][0][1].split(' ')[1];
atRules[match] = atRules[match] || [];
atRules[match].push(token);
}
}
function markCounterStylesAsUsed(atRules) {
return function (token, context) {
var property;
var wrappedProperty;
var i, l;
for (i = 0, l = token[2].length; i < l; i++) {
property = token[2][i];
if (property[1][1] == 'list-style') {
wrappedProperty = wrapForOptimizing(property);
populateComponents([wrappedProperty], context.validator, context.warnings);
if (wrappedProperty.components[0].value[0][1] in atRules) {
delete atRules[property[2][1]];
}
restoreFromOptimizing([wrappedProperty]);
}
if (property[1][1] == 'list-style-type' && property[2][1] in atRules) {
delete atRules[property[2][1]];
}
}
};
}
function matchFontFace(token, atRules) {
var property;
var match;
var i, l;
if (token[0] == Token.AT_RULE_BLOCK && token[1][0][1] == '@font-face') {
for (i = 0, l = token[2].length; i < l; i++) {
property = token[2][i];
if (property[1][1] == 'font-family') {
match = normalize(property[2][1].toLowerCase());
atRules[match] = atRules[match] || [];
atRules[match].push(token);
break;
}
}
}
}
function markFontFacesAsUsed(atRules) {
return function (token, context) {
var property;
var wrappedProperty;
var component;
var normalizedMatch;
var i, l;
var j, m;
for (i = 0, l = token[2].length; i < l; i++) {
property = token[2][i];
if (property[1][1] == 'font') {
wrappedProperty = wrapForOptimizing(property);
populateComponents([wrappedProperty], context.validator, context.warnings);
component = wrappedProperty.components[6];
for (j = 0, m = component.value.length; j < m; j++) {
normalizedMatch = normalize(component.value[j][1].toLowerCase());
if (normalizedMatch in atRules) {
delete atRules[normalizedMatch];
}
}
restoreFromOptimizing([wrappedProperty]);
}
if (property[1][1] == 'font-family') {
for (j = 2, m = property.length; j < m; j++) {
normalizedMatch = normalize(property[j][1].toLowerCase());
if (normalizedMatch in atRules) {
delete atRules[normalizedMatch];
}
}
}
}
};
}
function matchKeyframe(token, atRules) {
var match;
if (token[0] == Token.NESTED_BLOCK && keyframeRegex.test(token[1][0][1])) {
match = token[1][0][1].split(' ')[1];
atRules[match] = atRules[match] || [];
atRules[match].push(token);
}
}
function markKeyframesAsUsed(atRules) {
return function (token, context) {
var property;
var wrappedProperty;
var component;
var i, l;
var j, m;
for (i = 0, l = token[2].length; i < l; i++) {
property = token[2][i];
if (animationRegex.test(property[1][1])) {
wrappedProperty = wrapForOptimizing(property);
populateComponents([wrappedProperty], context.validator, context.warnings);
component = wrappedProperty.components[7];
for (j = 0, m = component.value.length; j < m; j++) {
if (component.value[j][1] in atRules) {
delete atRules[component.value[j][1]];
}
}
restoreFromOptimizing([wrappedProperty]);
}
if (animationNameRegex.test(property[1][1])) {
for (j = 2, m = property.length; j < m; j++) {
if (property[j][1] in atRules) {
delete atRules[property[j][1]];
}
}
}
}
};
}
function matchNamespace(token, atRules) {
var match;
if (token[0] == Token.AT_RULE && token[1].indexOf('@namespace') === 0) {
match = token[1].split(' ')[1];
atRules[match] = atRules[match] || [];
atRules[match].push(token);
}
}
function markNamespacesAsUsed(atRules) {
var namespaceRegex = new RegExp(Object.keys(atRules).join('\\\||') + '\\\|', 'g');
return function (token) {
var match;
var scope;
var normalizedMatch;
var i, l;
var j, m;
for (i = 0, l = token[1].length; i < l; i++) {
scope = token[1][i];
match = scope[1].match(namespaceRegex);
for (j = 0, m = match.length; j < m; j++) {
normalizedMatch = match[j].substring(0, match[j].length - 1);
if (normalizedMatch in atRules) {
delete atRules[normalizedMatch];
}
}
}
};
}
module.exports = removeUnusedAtRules;

View File

@ -0,0 +1,93 @@
// TODO: it'd be great to merge it with the other canReorder functionality
var rulesOverlap = require('./rules-overlap');
var specificitiesOverlap = require('./specificities-overlap');
var FLEX_PROPERTIES = /align\-items|box\-align|box\-pack|flex|justify/;
var BORDER_PROPERTIES = /^border\-(top|right|bottom|left|color|style|width|radius)/;
function canReorder(left, right, cache) {
for (var i = right.length - 1; i >= 0; i--) {
for (var j = left.length - 1; j >= 0; j--) {
if (!canReorderSingle(left[j], right[i], cache))
return false;
}
}
return true;
}
function canReorderSingle(left, right, cache) {
var leftName = left[0];
var leftValue = left[1];
var leftNameRoot = left[2];
var leftSelector = left[5];
var leftInSpecificSelector = left[6];
var rightName = right[0];
var rightValue = right[1];
var rightNameRoot = right[2];
var rightSelector = right[5];
var rightInSpecificSelector = right[6];
if (leftName == 'font' && rightName == 'line-height' || rightName == 'font' && leftName == 'line-height')
return false;
if (FLEX_PROPERTIES.test(leftName) && FLEX_PROPERTIES.test(rightName))
return false;
if (leftNameRoot == rightNameRoot && unprefixed(leftName) == unprefixed(rightName) && (vendorPrefixed(leftName) ^ vendorPrefixed(rightName)))
return false;
if (leftNameRoot == 'border' && BORDER_PROPERTIES.test(rightNameRoot) && (leftName == 'border' || leftName == rightNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName))))
return false;
if (rightNameRoot == 'border' && BORDER_PROPERTIES.test(leftNameRoot) && (rightName == 'border' || rightName == leftNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName))))
return false;
if (leftNameRoot == 'border' && rightNameRoot == 'border' && leftName != rightName && (isSideBorder(leftName) && isStyleBorder(rightName) || isStyleBorder(leftName) && isSideBorder(rightName)))
return false;
if (leftNameRoot != rightNameRoot)
return true;
if (leftName == rightName && leftNameRoot == rightNameRoot && (leftValue == rightValue || withDifferentVendorPrefix(leftValue, rightValue)))
return true;
if (leftName != rightName && leftNameRoot == rightNameRoot && leftName != leftNameRoot && rightName != rightNameRoot)
return true;
if (leftName != rightName && leftNameRoot == rightNameRoot && leftValue == rightValue)
return true;
if (rightInSpecificSelector && leftInSpecificSelector && !inheritable(leftNameRoot) && !inheritable(rightNameRoot) && !rulesOverlap(rightSelector, leftSelector, false))
return true;
if (!specificitiesOverlap(leftSelector, rightSelector, cache))
return true;
return false;
}
function vendorPrefixed(name) {
return /^\-(?:moz|webkit|ms|o)\-/.test(name);
}
function unprefixed(name) {
return name.replace(/^\-(?:moz|webkit|ms|o)\-/, '');
}
function sameBorderComponent(name1, name2) {
return name1.split('-').pop() == name2.split('-').pop();
}
function isSideBorder(name) {
return name == 'border-top' || name == 'border-right' || name == 'border-bottom' || name == 'border-left';
}
function isStyleBorder(name) {
return name == 'border-color' || name == 'border-style' || name == 'border-width';
}
function withDifferentVendorPrefix(value1, value2) {
return vendorPrefixed(value1) && vendorPrefixed(value2) && value1.split('-')[1] != value2.split('-')[2];
}
function inheritable(name) {
// According to http://www.w3.org/TR/CSS21/propidx.html
// Others will be catched by other, preceeding rules
return name == 'font' || name == 'line-height' || name == 'list-style';
}
module.exports = {
canReorder: canReorder,
canReorderSingle: canReorderSingle
};

View File

@ -0,0 +1,13 @@
var compactable = require('./compactable');
function restoreWithComponents(property) {
var descriptor = compactable[property.name];
if (descriptor && descriptor.shorthand) {
return descriptor.restore(property, compactable);
} else {
return property.value;
}
}
module.exports = restoreWithComponents;

303
node_modules/clean-css/lib/optimizer/level-2/restore.js generated vendored Normal file
View File

@ -0,0 +1,303 @@
var shallowClone = require('./clone').shallow;
var Token = require('../../tokenizer/token');
var Marker = require('../../tokenizer/marker');
function isInheritOnly(values) {
for (var i = 0, l = values.length; i < l; i++) {
var value = values[i][1];
if (value != 'inherit' && value != Marker.COMMA && value != Marker.FORWARD_SLASH)
return false;
}
return true;
}
function background(property, compactable, lastInMultiplex) {
var components = property.components;
var restored = [];
var needsOne, needsBoth;
function restoreValue(component) {
Array.prototype.unshift.apply(restored, component.value);
}
function isDefaultValue(component) {
var descriptor = compactable[component.name];
if (descriptor.doubleValues && descriptor.defaultValue.length == 1) {
return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] == descriptor.defaultValue[0] : true);
} else if (descriptor.doubleValues && descriptor.defaultValue.length != 1) {
return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] : component.value[0][1]) == descriptor.defaultValue[1];
} else {
return component.value[0][1] == descriptor.defaultValue;
}
}
for (var i = components.length - 1; i >= 0; i--) {
var component = components[i];
var isDefault = isDefaultValue(component);
if (component.name == 'background-clip') {
var originComponent = components[i - 1];
var isOriginDefault = isDefaultValue(originComponent);
needsOne = component.value[0][1] == originComponent.value[0][1];
needsBoth = !needsOne && (
(isOriginDefault && !isDefault) ||
(!isOriginDefault && !isDefault) ||
(!isOriginDefault && isDefault && component.value[0][1] != originComponent.value[0][1]));
if (needsOne) {
restoreValue(originComponent);
} else if (needsBoth) {
restoreValue(component);
restoreValue(originComponent);
}
i--;
} else if (component.name == 'background-size') {
var positionComponent = components[i - 1];
var isPositionDefault = isDefaultValue(positionComponent);
needsOne = !isPositionDefault && isDefault;
needsBoth = !needsOne &&
(isPositionDefault && !isDefault || !isPositionDefault && !isDefault);
if (needsOne) {
restoreValue(positionComponent);
} else if (needsBoth) {
restoreValue(component);
restored.unshift([Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]);
restoreValue(positionComponent);
} else if (positionComponent.value.length == 1) {
restoreValue(positionComponent);
}
i--;
} else {
if (isDefault || compactable[component.name].multiplexLastOnly && !lastInMultiplex)
continue;
restoreValue(component);
}
}
if (restored.length === 0 && property.value.length == 1 && property.value[0][1] == '0')
restored.push(property.value[0]);
if (restored.length === 0)
restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]);
if (isInheritOnly(restored))
return [restored[0]];
return restored;
}
function borderRadius(property, compactable) {
if (property.multiplex) {
var horizontal = shallowClone(property);
var vertical = shallowClone(property);
for (var i = 0; i < 4; i++) {
var component = property.components[i];
var horizontalComponent = shallowClone(property);
horizontalComponent.value = [component.value[0]];
horizontal.components.push(horizontalComponent);
var verticalComponent = shallowClone(property);
// FIXME: only shorthand compactor (see breakup#borderRadius) knows that border radius
// longhands have two values, whereas tokenizer does not care about populating 2nd value
// if it's missing, hence this fallback
verticalComponent.value = [component.value[1] || component.value[0]];
vertical.components.push(verticalComponent);
}
var horizontalValues = fourValues(horizontal, compactable);
var verticalValues = fourValues(vertical, compactable);
if (horizontalValues.length == verticalValues.length &&
horizontalValues[0][1] == verticalValues[0][1] &&
(horizontalValues.length > 1 ? horizontalValues[1][1] == verticalValues[1][1] : true) &&
(horizontalValues.length > 2 ? horizontalValues[2][1] == verticalValues[2][1] : true) &&
(horizontalValues.length > 3 ? horizontalValues[3][1] == verticalValues[3][1] : true)) {
return horizontalValues;
} else {
return horizontalValues.concat([[Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]]).concat(verticalValues);
}
} else {
return fourValues(property, compactable);
}
}
function 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) {
var components = property.components;
var value1 = components[0].value[0];
var value2 = components[1].value[0];
var value3 = components[2].value[0];
var value4 = components[3].value[0];
if (value1[1] == value2[1] && value1[1] == value3[1] && value1[1] == value4[1]) {
return [value1];
} else if (value1[1] == value3[1] && value2[1] == value4[1]) {
return [value1, value2];
} else if (value2[1] == value4[1]) {
return [value1, value2, value3];
} else {
return [value1, value2, value3, value4];
}
}
function multiplex(restoreWith) {
return function (property, compactable) {
if (!property.multiplex)
return restoreWith(property, compactable, true);
var multiplexSize = 0;
var restored = [];
var componentMultiplexSoFar = {};
var i, l;
// At this point we don't know what's the multiplex size, e.g. how many background layers are there
for (i = 0, l = property.components[0].value.length; i < l; i++) {
if (property.components[0].value[i][1] == Marker.COMMA)
multiplexSize++;
}
for (i = 0; i <= multiplexSize; i++) {
var _property = shallowClone(property);
// We split multiplex into parts and restore them one by one
for (var j = 0, m = property.components.length; j < m; j++) {
var componentToClone = property.components[j];
var _component = shallowClone(componentToClone);
_property.components.push(_component);
// The trick is some properties has more than one value, so we iterate over values looking for
// a multiplex separator - a comma
for (var k = componentMultiplexSoFar[_component.name] || 0, n = componentToClone.value.length; k < n; k++) {
if (componentToClone.value[k][1] == Marker.COMMA) {
componentMultiplexSoFar[_component.name] = k + 1;
break;
}
_component.value.push(componentToClone.value[k]);
}
}
// No we can restore shorthand value
var lastInMultiplex = i == multiplexSize;
var _restored = restoreWith(_property, compactable, lastInMultiplex);
Array.prototype.push.apply(restored, _restored);
if (i < multiplexSize)
restored.push([Token.PROPERTY_VALUE, Marker.COMMA]);
}
return restored;
};
}
function withoutDefaults(property, compactable) {
var components = property.components;
var restored = [];
for (var i = components.length - 1; i >= 0; i--) {
var component = components[i];
var descriptor = compactable[component.name];
if (component.value[0][1] != descriptor.defaultValue || ('keepUnlessDefault' in descriptor) && !isDefault(components, compactable, descriptor.keepUnlessDefault)) {
restored.unshift(component.value[0]);
}
}
if (restored.length === 0)
restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]);
if (isInheritOnly(restored))
return [restored[0]];
return restored;
}
function isDefault(components, compactable, propertyName) {
var component;
var i, l;
for (i = 0, l = components.length; i < l; i++) {
component = components[i];
if (component.name == propertyName && component.value[0][1] == compactable[propertyName].defaultValue) {
return true;
}
}
return false;
}
module.exports = {
background: background,
borderRadius: borderRadius,
font: font,
fourValues: fourValues,
multiplex: multiplex,
withoutDefaults: withoutDefaults
};

View File

@ -0,0 +1,389 @@
var canReorderSingle = require('./reorderable').canReorderSingle;
var extractProperties = require('./extract-properties');
var isMergeable = require('./is-mergeable');
var tidyRuleDuplicates = require('./tidy-rule-duplicates');
var Token = require('../../tokenizer/token');
var cloneArray = require('../../utils/clone-array');
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
function naturalSorter(a, b) {
return a > b ? 1 : -1;
}
function cloneAndMergeSelectors(propertyA, propertyB) {
var cloned = cloneArray(propertyA);
cloned[5] = cloned[5].concat(propertyB[5]);
return cloned;
}
function restructure(tokens, context) {
var options = context.options;
var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
var mergeLimit = options.compatibility.selectors.mergeLimit;
var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
var specificityCache = context.cache.specificity;
var movableTokens = {};
var movedProperties = [];
var multiPropertyMoveCache = {};
var movedToBeDropped = [];
var maxCombinationsLevel = 2;
var ID_JOIN_CHARACTER = '%';
function sendToMultiPropertyMoveCache(position, movedProperty, allFits) {
for (var i = allFits.length - 1; i >= 0; i--) {
var fit = allFits[i][0];
var id = addToCache(movedProperty, fit);
if (multiPropertyMoveCache[id].length > 1 && processMultiPropertyMove(position, multiPropertyMoveCache[id])) {
removeAllMatchingFromCache(id);
break;
}
}
}
function addToCache(movedProperty, fit) {
var id = cacheId(fit);
multiPropertyMoveCache[id] = multiPropertyMoveCache[id] || [];
multiPropertyMoveCache[id].push([movedProperty, fit]);
return id;
}
function removeAllMatchingFromCache(matchId) {
var matchSelectors = matchId.split(ID_JOIN_CHARACTER);
var forRemoval = [];
var i;
for (var id in multiPropertyMoveCache) {
var selectors = id.split(ID_JOIN_CHARACTER);
for (i = selectors.length - 1; i >= 0; i--) {
if (matchSelectors.indexOf(selectors[i]) > -1) {
forRemoval.push(id);
break;
}
}
}
for (i = forRemoval.length - 1; i >= 0; i--) {
delete multiPropertyMoveCache[forRemoval[i]];
}
}
function cacheId(cachedTokens) {
var id = [];
for (var i = 0, l = cachedTokens.length; i < l; i++) {
id.push(serializeRules(cachedTokens[i][1]));
}
return id.join(ID_JOIN_CHARACTER);
}
function tokensToMerge(sourceTokens) {
var uniqueTokensWithBody = [];
var mergeableTokens = [];
for (var i = sourceTokens.length - 1; i >= 0; i--) {
if (!isMergeable(serializeRules(sourceTokens[i][1]), mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging)) {
continue;
}
mergeableTokens.unshift(sourceTokens[i]);
if (sourceTokens[i][2].length > 0 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1)
uniqueTokensWithBody.push(sourceTokens[i]);
}
return uniqueTokensWithBody.length > 1 ?
mergeableTokens :
[];
}
function shortenIfPossible(position, movedProperty) {
var name = movedProperty[0];
var value = movedProperty[1];
var key = movedProperty[4];
var valueSize = name.length + value.length + 1;
var allSelectors = [];
var qualifiedTokens = [];
var mergeableTokens = tokensToMerge(movableTokens[key]);
if (mergeableTokens.length < 2)
return;
var allFits = findAllFits(mergeableTokens, valueSize, 1);
var bestFit = allFits[0];
if (bestFit[1] > 0)
return sendToMultiPropertyMoveCache(position, movedProperty, allFits);
for (var i = bestFit[0].length - 1; i >=0; i--) {
allSelectors = bestFit[0][i][1].concat(allSelectors);
qualifiedTokens.unshift(bestFit[0][i]);
}
allSelectors = tidyRuleDuplicates(allSelectors);
dropAsNewTokenAt(position, [movedProperty], allSelectors, qualifiedTokens);
}
function fitSorter(fit1, fit2) {
return fit1[1] > fit2[1] ? 1 : (fit1[1] == fit2[1] ? 0 : -1);
}
function findAllFits(mergeableTokens, propertySize, propertiesCount) {
var combinations = allCombinations(mergeableTokens, propertySize, propertiesCount, maxCombinationsLevel - 1);
return combinations.sort(fitSorter);
}
function allCombinations(tokensVariant, propertySize, propertiesCount, level) {
var differenceVariants = [[tokensVariant, sizeDifference(tokensVariant, propertySize, propertiesCount)]];
if (tokensVariant.length > 2 && level > 0) {
for (var i = tokensVariant.length - 1; i >= 0; i--) {
var subVariant = Array.prototype.slice.call(tokensVariant, 0);
subVariant.splice(i, 1);
differenceVariants = differenceVariants.concat(allCombinations(subVariant, propertySize, propertiesCount, level - 1));
}
}
return differenceVariants;
}
function sizeDifference(tokensVariant, propertySize, propertiesCount) {
var allSelectorsSize = 0;
for (var i = tokensVariant.length - 1; i >= 0; i--) {
allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? serializeRules(tokensVariant[i][1]).length : -1;
}
return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1;
}
function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) {
var i, j, k, m;
var allProperties = [];
for (i = mergeableTokens.length - 1; i >= 0; i--) {
var mergeableToken = mergeableTokens[i];
for (j = mergeableToken[2].length - 1; j >= 0; j--) {
var mergeableProperty = mergeableToken[2][j];
for (k = 0, m = properties.length; k < m; k++) {
var property = properties[k];
var mergeablePropertyName = mergeableProperty[1][1];
var propertyName = property[0];
var propertyBody = property[4];
if (mergeablePropertyName == propertyName && serializeBody([mergeableProperty]) == propertyBody) {
mergeableToken[2].splice(j, 1);
break;
}
}
}
}
for (i = properties.length - 1; i >= 0; i--) {
allProperties.unshift(properties[i][3]);
}
var newToken = [Token.RULE, allSelectors, allProperties];
tokens.splice(position, 0, newToken);
}
function dropPropertiesAt(position, movedProperty) {
var key = movedProperty[4];
var toMove = movableTokens[key];
if (toMove && toMove.length > 1) {
if (!shortenMultiMovesIfPossible(position, movedProperty))
shortenIfPossible(position, movedProperty);
}
}
function shortenMultiMovesIfPossible(position, movedProperty) {
var candidates = [];
var propertiesAndMergableTokens = [];
var key = movedProperty[4];
var j, k;
var mergeableTokens = tokensToMerge(movableTokens[key]);
if (mergeableTokens.length < 2)
return;
movableLoop:
for (var value in movableTokens) {
var tokensList = movableTokens[value];
for (j = mergeableTokens.length - 1; j >= 0; j--) {
if (tokensList.indexOf(mergeableTokens[j]) == -1)
continue movableLoop;
}
candidates.push(value);
}
if (candidates.length < 2)
return false;
for (j = candidates.length - 1; j >= 0; j--) {
for (k = movedProperties.length - 1; k >= 0; k--) {
if (movedProperties[k][4] == candidates[j]) {
propertiesAndMergableTokens.unshift([movedProperties[k], mergeableTokens]);
break;
}
}
}
return processMultiPropertyMove(position, propertiesAndMergableTokens);
}
function processMultiPropertyMove(position, propertiesAndMergableTokens) {
var valueSize = 0;
var properties = [];
var property;
for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) {
property = propertiesAndMergableTokens[i][0];
var fullValue = property[4];
valueSize += fullValue.length + (i > 0 ? 1 : 0);
properties.push(property);
}
var mergeableTokens = propertiesAndMergableTokens[0][1];
var bestFit = findAllFits(mergeableTokens, valueSize, properties.length)[0];
if (bestFit[1] > 0)
return false;
var allSelectors = [];
var qualifiedTokens = [];
for (i = bestFit[0].length - 1; i >= 0; i--) {
allSelectors = bestFit[0][i][1].concat(allSelectors);
qualifiedTokens.unshift(bestFit[0][i]);
}
allSelectors = tidyRuleDuplicates(allSelectors);
dropAsNewTokenAt(position, properties, allSelectors, qualifiedTokens);
for (i = properties.length - 1; i >= 0; i--) {
property = properties[i];
var index = movedProperties.indexOf(property);
delete movableTokens[property[4]];
if (index > -1 && movedToBeDropped.indexOf(index) == -1)
movedToBeDropped.push(index);
}
return true;
}
function boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) {
var propertyName = property[0];
var movedPropertyName = movedProperty[0];
if (propertyName != movedPropertyName)
return false;
var key = movedProperty[4];
var toMove = movableTokens[key];
return toMove && toMove.indexOf(token) > -1;
}
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
var isRule;
var j, k, m;
var samePropertyAt;
if (token[0] == Token.RULE) {
isRule = true;
} else if (token[0] == Token.NESTED_BLOCK) {
isRule = false;
} else {
continue;
}
// We cache movedProperties.length as it may change in the loop
var movedCount = movedProperties.length;
var properties = extractProperties(token);
movedToBeDropped = [];
var unmovableInCurrentToken = [];
for (j = properties.length - 1; j >= 0; j--) {
for (k = j - 1; k >= 0; k--) {
if (!canReorderSingle(properties[j], properties[k], specificityCache)) {
unmovableInCurrentToken.push(j);
break;
}
}
}
for (j = properties.length - 1; j >= 0; j--) {
var property = properties[j];
var movedSameProperty = false;
for (k = 0; k < movedCount; k++) {
var movedProperty = movedProperties[k];
if (movedToBeDropped.indexOf(k) == -1 && (!canReorderSingle(property, movedProperty, specificityCache) && !boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) ||
movableTokens[movedProperty[4]] && movableTokens[movedProperty[4]].length === mergeLimit)) {
dropPropertiesAt(i + 1, movedProperty, token);
if (movedToBeDropped.indexOf(k) == -1) {
movedToBeDropped.push(k);
delete movableTokens[movedProperty[4]];
}
}
if (!movedSameProperty) {
movedSameProperty = property[0] == movedProperty[0] && property[1] == movedProperty[1];
if (movedSameProperty) {
samePropertyAt = k;
}
}
}
if (!isRule || unmovableInCurrentToken.indexOf(j) > -1)
continue;
var key = property[4];
if (movedSameProperty && movedProperties[samePropertyAt][5].length + property[5].length > mergeLimit) {
dropPropertiesAt(i + 1, movedProperties[samePropertyAt]);
movedProperties.splice(samePropertyAt, 1);
movableTokens[key] = [token];
movedSameProperty = false;
} else {
movableTokens[key] = movableTokens[key] || [];
movableTokens[key].push(token);
}
if (movedSameProperty) {
movedProperties[samePropertyAt] = cloneAndMergeSelectors(movedProperties[samePropertyAt], property);
} else {
movedProperties.push(property);
}
}
movedToBeDropped = movedToBeDropped.sort(naturalSorter);
for (j = 0, m = movedToBeDropped.length; j < m; j++) {
var dropAt = movedToBeDropped[j] - j;
movedProperties.splice(dropAt, 1);
}
}
var position = tokens[0] && tokens[0][0] == Token.AT_RULE && tokens[0][1].indexOf('@charset') === 0 ? 1 : 0;
for (; position < tokens.length - 1; position++) {
var isImportRule = tokens[position][0] === Token.AT_RULE && tokens[position][1].indexOf('@import') === 0;
var isComment = tokens[position][0] === Token.COMMENT;
if (!(isImportRule || isComment))
break;
}
for (i = 0; i < movedProperties.length; i++) {
dropPropertiesAt(position, movedProperties[i]);
}
}
module.exports = restructure;

View File

@ -0,0 +1,32 @@
var MODIFIER_PATTERN = /\-\-.+$/;
function rulesOverlap(rule1, rule2, bemMode) {
var scope1;
var scope2;
var i, l;
var j, m;
for (i = 0, l = rule1.length; i < l; i++) {
scope1 = rule1[i][1];
for (j = 0, m = rule2.length; j < m; j++) {
scope2 = rule2[j][1];
if (scope1 == scope2) {
return true;
}
if (bemMode && withoutModifiers(scope1) == withoutModifiers(scope2)) {
return true;
}
}
}
return false;
}
function withoutModifiers(scope) {
return scope.replace(MODIFIER_PATTERN, '');
}
module.exports = rulesOverlap;

View File

@ -0,0 +1,34 @@
var specificity = require('./specificity');
function specificitiesOverlap(selector1, selector2, cache) {
var specificity1;
var specificity2;
var i, l;
var j, m;
for (i = 0, l = selector1.length; i < l; i++) {
specificity1 = findSpecificity(selector1[i][1], cache);
for (j = 0, m = selector2.length; j < m; j++) {
specificity2 = findSpecificity(selector2[j][1], cache);
if (specificity1[0] === specificity2[0] && specificity1[1] === specificity2[1] && specificity1[2] === specificity2[2]) {
return true;
}
}
}
return false;
}
function findSpecificity(selector, cache) {
var value;
if (!(selector in cache)) {
cache[selector] = value = specificity(selector);
}
return value || cache[selector];
}
module.exports = specificitiesOverlap;

View File

@ -0,0 +1,77 @@
var Marker = require('../../tokenizer/marker');
var Selector = {
ADJACENT_SIBLING: '+',
DESCENDANT: '>',
DOT: '.',
HASH: '#',
NON_ADJACENT_SIBLING: '~',
PSEUDO: ':'
};
var LETTER_PATTERN = /[a-zA-Z]/;
var NOT_PREFIX = ':not(';
var SEPARATOR_PATTERN = /[\s,\(>~\+]/;
function specificity(selector) {
var result = [0, 0, 0];
var character;
var isEscaped;
var isSingleQuoted;
var isDoubleQuoted;
var roundBracketLevel = 0;
var couldIntroduceNewTypeSelector;
var withinNotPseudoClass = false;
var wasPseudoClass = false;
var i, l;
for (i = 0, l = selector.length; i < l; i++) {
character = selector[i];
if (isEscaped) {
// noop
} else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) {
isSingleQuoted = true;
} else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && isSingleQuoted) {
isSingleQuoted = false;
} else if (character == Marker.DOUBLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) {
isDoubleQuoted = true;
} else if (character == Marker.DOUBLE_QUOTE && isDoubleQuoted && !isSingleQuoted) {
isDoubleQuoted = false;
} else if (isSingleQuoted || isDoubleQuoted) {
continue;
} else if (roundBracketLevel > 0 && !withinNotPseudoClass) {
// noop
} else if (character == Marker.OPEN_ROUND_BRACKET) {
roundBracketLevel++;
} else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1) {
roundBracketLevel--;
withinNotPseudoClass = false;
} else if (character == Marker.CLOSE_ROUND_BRACKET) {
roundBracketLevel--;
} else if (character == Selector.HASH) {
result[0]++;
} else if (character == Selector.DOT || character == Marker.OPEN_SQUARE_BRACKET) {
result[1]++;
} else if (character == Selector.PSEUDO && !wasPseudoClass && !isNotPseudoClass(selector, i)) {
result[1]++;
withinNotPseudoClass = false;
} else if (character == Selector.PSEUDO) {
withinNotPseudoClass = true;
} else if ((i === 0 || couldIntroduceNewTypeSelector) && LETTER_PATTERN.test(character)) {
result[2]++;
}
isEscaped = character == Marker.BACK_SLASH;
wasPseudoClass = character == Selector.PSEUDO;
couldIntroduceNewTypeSelector = !isEscaped && SEPARATOR_PATTERN.test(character);
}
return result;
}
function isNotPseudoClass(selector, index) {
return selector.indexOf(NOT_PREFIX, index) === index;
}
module.exports = specificity;

View File

@ -0,0 +1,21 @@
function ruleSorter(s1, s2) {
return s1[1] > s2[1] ? 1 : -1;
}
function tidyRuleDuplicates(rules) {
var list = [];
var repeated = [];
for (var i = 0, l = rules.length; i < l; i++) {
var rule = rules[i];
if (repeated.indexOf(rule[1]) == -1) {
repeated.push(rule[1]);
list.push(rule);
}
}
return list.sort(ruleSorter);
}
module.exports = tidyRuleDuplicates;