diff options
author | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 |
commit | de98e0b232509d5f40c135d540a70e415272ff85 (patch) | |
tree | a79222a5b58484ab3b80d18efcaaa7ccc4769b33 /node_modules/clean-css/lib/optimizer/level-2 | |
parent | e0c9d480a73fa629c1e4a47d3e721f1d2d345406 (diff) |
node_modules
Diffstat (limited to 'node_modules/clean-css/lib/optimizer/level-2')
35 files changed, 4112 insertions, 0 deletions
diff --git a/node_modules/clean-css/lib/optimizer/level-2/break-up.js b/node_modules/clean-css/lib/optimizer/level-2/break-up.js new file mode 100644 index 000000000..9e44d5a1f --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/break-up.js @@ -0,0 +1,362 @@ +var InvalidPropertyError = require('./invalid-property-error'); + +var wrapSingle = require('../wrap-for-optimizing').single; + +var Token = require('../../tokenizer/token'); + +var formatPosition = require('../../utils/format-position'); + +var MULTIPLEX_SEPARATOR = ','; + +function _colorFilter(validator) { + return function (value) { + return value[1] == 'invert' || validator.isValidColor(value[1]) || validator.isValidVendorPrefixedValue(value[1]); + }; +} + +function _styleFilter(validator) { + return function (value) { + return value[1] != 'inherit' && validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); + }; +} + +function _wrapDefault(name, property, compactable) { + var descriptor = compactable[name]; + if (descriptor.doubleValues && descriptor.defaultValue.length == 2) { + return wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, name], + [Token.PROPERTY_VALUE, descriptor.defaultValue[0]], + [Token.PROPERTY_VALUE, descriptor.defaultValue[1]] + ]); + } else if (descriptor.doubleValues && descriptor.defaultValue.length == 1) { + return wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, name], + [Token.PROPERTY_VALUE, descriptor.defaultValue[0]] + ]); + } else { + return wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, name], + [Token.PROPERTY_VALUE, descriptor.defaultValue] + ]); + } +} + +function _widthFilter(validator) { + return function (value) { + return value[1] != 'inherit' && validator.isValidWidth(value[1]) && !validator.isValidStyle(value[1]) && !validator.isValidColorValue(value[1]); + }; +} + +function background(property, compactable, validator) { + var image = _wrapDefault('background-image', property, compactable); + var position = _wrapDefault('background-position', property, compactable); + var size = _wrapDefault('background-size', property, compactable); + var repeat = _wrapDefault('background-repeat', property, compactable); + var attachment = _wrapDefault('background-attachment', property, compactable); + var origin = _wrapDefault('background-origin', property, compactable); + var clip = _wrapDefault('background-clip', property, compactable); + var color = _wrapDefault('background-color', property, compactable); + var components = [image, position, size, repeat, attachment, origin, clip, color]; + var values = property.value; + + var positionSet = false; + var clipSet = false; + var originSet = false; + var repeatSet = false; + + var anyValueSet = false; + + if (property.value.length == 1 && property.value[0][1] == 'inherit') { + // NOTE: 'inherit' is not a valid value for background-attachment + color.value = image.value = repeat.value = position.value = size.value = origin.value = clip.value = property.value; + return components; + } + + if (property.value.length == 1 && property.value[0][1] == '0 0') { + return components; + } + + for (var i = values.length - 1; i >= 0; i--) { + var value = values[i]; + + if (validator.isValidBackgroundAttachment(value[1])) { + attachment.value = [value]; + anyValueSet = true; + } else if (validator.isValidBackgroundClip(value[1]) || validator.isValidBackgroundOrigin(value[1])) { + if (clipSet) { + origin.value = [value]; + originSet = true; + } else { + clip.value = [value]; + clipSet = true; + } + anyValueSet = true; + } else if (validator.isValidBackgroundRepeat(value[1])) { + if (repeatSet) { + repeat.value.unshift(value); + } else { + repeat.value = [value]; + repeatSet = true; + } + anyValueSet = true; + } else if (validator.isValidBackgroundPositionPart(value[1]) || validator.isValidBackgroundSizePart(value[1])) { + if (i > 0) { + var previousValue = values[i - 1]; + + if (previousValue[1] == '/') { + size.value = [value]; + } else if (i > 1 && values[i - 2][1] == '/') { + size.value = [previousValue, value]; + i -= 2; + } else { + if (!positionSet) + position.value = []; + + position.value.unshift(value); + positionSet = true; + } + } else { + if (!positionSet) + position.value = []; + + position.value.unshift(value); + positionSet = true; + } + anyValueSet = true; + } else if ((color.value[0][1] == compactable[color.name].defaultValue || color.value[0][1] == 'none') && (validator.isValidColor(value[1]) || validator.isValidVendorPrefixedValue(value[1]))) { + color.value = [value]; + anyValueSet = true; + } else if (validator.isValidUrl(value[1]) || validator.isValidFunction(value[1])) { + image.value = [value]; + anyValueSet = true; + } + } + + if (clipSet && !originSet) + origin.value = clip.value.slice(0); + + if (!anyValueSet) { + throw new InvalidPropertyError('Invalid background value at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } + + return components; +} + +function borderRadius(property, compactable) { + var values = property.value; + var splitAt = -1; + + for (var i = 0, l = values.length; i < l; i++) { + if (values[i][1] == '/') { + splitAt = i; + break; + } + } + + if (splitAt === 0 || splitAt === values.length - 1) { + throw new InvalidPropertyError('Invalid border-radius value at ' + formatPosition(values[0][2][0]) + '. Ignoring.'); + } + + var target = _wrapDefault(property.name, property, compactable); + target.value = splitAt > -1 ? + values.slice(0, splitAt) : + values.slice(0); + target.components = fourValues(target, compactable); + + var remainder = _wrapDefault(property.name, property, compactable); + remainder.value = splitAt > -1 ? + values.slice(splitAt + 1) : + values.slice(0); + remainder.components = fourValues(remainder, compactable); + + for (var j = 0; j < 4; j++) { + target.components[j].multiplex = true; + target.components[j].value = target.components[j].value.concat(remainder.components[j].value); + } + + return target.components; +} + +function fourValues(property, compactable) { + var componentNames = compactable[property.name].components; + var components = []; + var value = property.value; + + if (value.length < 1) + return []; + + if (value.length < 2) + value[1] = value[0].slice(0); + if (value.length < 3) + value[2] = value[0].slice(0); + if (value.length < 4) + value[3] = value[1].slice(0); + + for (var i = componentNames.length - 1; i >= 0; i--) { + var component = wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, componentNames[i]] + ]); + component.value = [value[i]]; + components.unshift(component); + } + + return components; +} + +function multiplex(splitWith) { + return function (property, compactable, validator) { + var splitsAt = []; + var values = property.value; + var i, j, l, m; + + // find split commas + for (i = 0, l = values.length; i < l; i++) { + if (values[i][1] == ',') + splitsAt.push(i); + } + + if (splitsAt.length === 0) + return splitWith(property, compactable, validator); + + var splitComponents = []; + + // split over commas, and into components + for (i = 0, l = splitsAt.length; i <= l; i++) { + var from = i === 0 ? 0 : splitsAt[i - 1] + 1; + var to = i < l ? splitsAt[i] : values.length; + + var _property = _wrapDefault(property.name, property, compactable); + _property.value = values.slice(from, to); + + splitComponents.push(splitWith(_property, compactable, validator)); + } + + var components = splitComponents[0]; + + // group component values from each split + for (i = 0, l = components.length; i < l; i++) { + components[i].multiplex = true; + + for (j = 1, m = splitComponents.length; j < m; j++) { + components[i].value.push([Token.PROPERTY_VALUE, MULTIPLEX_SEPARATOR]); + Array.prototype.push.apply(components[i].value, splitComponents[j][i].value); + } + } + + return components; + }; +} + +function listStyle(property, compactable, validator) { + var type = _wrapDefault('list-style-type', property, compactable); + var position = _wrapDefault('list-style-position', property, compactable); + var image = _wrapDefault('list-style-image', property, compactable); + var components = [type, position, image]; + + if (property.value.length == 1 && property.value[0][1] == 'inherit') { + type.value = position.value = image.value = [property.value[0]]; + return components; + } + + var values = property.value.slice(0); + var total = values.length; + var index = 0; + + // `image` first... + for (index = 0, total = values.length; index < total; index++) { + if (validator.isValidUrl(values[index][1]) || values[index][1] == '0') { + image.value = [values[index]]; + values.splice(index, 1); + break; + } + } + + // ... then `type`... + for (index = 0, total = values.length; index < total; index++) { + if (validator.isValidListStyleType(values[index][1])) { + type.value = [values[index]]; + values.splice(index, 1); + break; + } + } + + // ... and what's left is a `position` + if (values.length > 0 && validator.isValidListStylePosition(values[0][1])) + position.value = [values[0]]; + + return components; +} + +function widthStyleColor(property, compactable, validator) { + var descriptor = compactable[property.name]; + var components = [ + _wrapDefault(descriptor.components[0], property, compactable), + _wrapDefault(descriptor.components[1], property, compactable), + _wrapDefault(descriptor.components[2], property, compactable) + ]; + var color, style, width; + + for (var i = 0; i < 3; i++) { + var component = components[i]; + + if (component.name.indexOf('color') > 0) + color = component; + else if (component.name.indexOf('style') > 0) + style = component; + else + width = component; + } + + if ((property.value.length == 1 && property.value[0][1] == 'inherit') || + (property.value.length == 3 && property.value[0][1] == 'inherit' && property.value[1][1] == 'inherit' && property.value[2][1] == 'inherit')) { + color.value = style.value = width.value = [property.value[0]]; + return components; + } + + var values = property.value.slice(0); + var match, matches; + + // NOTE: usually users don't follow the required order of parts in this shorthand, + // so we'll try to parse it caring as little about order as possible + + if (values.length > 0) { + matches = values.filter(_widthFilter(validator)); + match = matches.length > 1 && (matches[0][1] == 'none' || matches[0][1] == 'auto') ? matches[1] : matches[0]; + if (match) { + width.value = [match]; + values.splice(values.indexOf(match), 1); + } + } + + if (values.length > 0) { + match = values.filter(_styleFilter(validator))[0]; + if (match) { + style.value = [match]; + values.splice(values.indexOf(match), 1); + } + } + + if (values.length > 0) { + match = values.filter(_colorFilter(validator))[0]; + if (match) { + color.value = [match]; + values.splice(values.indexOf(match), 1); + } + } + + return components; +} + +module.exports = { + background: background, + border: widthStyleColor, + borderRadius: borderRadius, + fourValues: fourValues, + listStyle: listStyle, + multiplex: multiplex, + outline: widthStyleColor +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/can-override.js b/node_modules/clean-css/lib/optimizer/level-2/can-override.js new file mode 100644 index 000000000..2617234ca --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/can-override.js @@ -0,0 +1,180 @@ +var understandable = require('./properties/understandable'); + +function backgroundPosition(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-position', value2, true)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (validator.isValidKeywordValue('background-position', value2, true)) { + return true; + } + + return unit(validator, value1, value2); +} + +function backgroundSize(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue('background-size', value2, true)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (validator.isValidKeywordValue('background-size', value2, true)) { + return true; + } + + return unit(validator, value1, value2); +} + +function color(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidColor(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (!validator.colorOpacity && (validator.isValidRgbaColor(value1) || validator.isValidHslaColor(value1))) { + return false; + } else if (!validator.colorOpacity && (validator.isValidRgbaColor(value2) || validator.isValidHslaColor(value2))) { + return false; + } else if (validator.isValidColor(value1) && validator.isValidColor(value2)) { + return true; + } + + return sameFunctionOrValue(validator, value1, value2); +} + +function components(overrideCheckers) { + return function (validator, value1, value2, position) { + return overrideCheckers[position](validator, value1, value2); + }; +} + +function image(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidImage(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (validator.isValidImage(value2)) { + return true; + } else if (validator.isValidImage(value1)) { + return false; + } + + return sameFunctionOrValue(validator, value1, value2); +} + +function keyword(propertyName) { + return function(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } + + return validator.isValidKeywordValue(propertyName, value2, false); + }; +} + +function keywordWithGlobal(propertyName) { + return function(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidKeywordValue(propertyName, value2, true)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } + + return validator.isValidKeywordValue(propertyName, value2, true); + }; +} + +function sameFunctionOrValue(validator, value1, value2) { + return validator.areSameFunction(value1, value2) ? + true : + value1 === value2; +} + +function textShadow(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidTextShadow(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } + + return validator.isValidTextShadow(value2); +} + +function unit(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidUnitWithoutFunction(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } else if (validator.isValidUnitWithoutFunction(value1) && !validator.isValidUnitWithoutFunction(value2)) { + return false; + } else if (validator.isValidUnitWithoutFunction(value2)) { + return true; + } else if (validator.isValidUnitWithoutFunction(value1)) { + return false; + } else if (validator.isValidFunctionWithoutVendorPrefix(value1) && validator.isValidFunctionWithoutVendorPrefix(value2)) { + return true; + } + + return sameFunctionOrValue(validator, value1, value2); +} + +function unitOrKeywordWithGlobal(propertyName) { + var byKeyword = keywordWithGlobal(propertyName); + + return function(validator, value1, value2) { + return unit(validator, value1, value2) || byKeyword(validator, value1, value2); + }; +} + +function zIndex(validator, value1, value2) { + if (!understandable(validator, value1, value2, 0, true) && !validator.isValidZIndex(value2)) { + return false; + } else if (validator.isValidVariable(value1) && validator.isValidVariable(value2)) { + return true; + } + + return validator.isValidZIndex(value2); +} + +module.exports = { + generic: { + color: color, + components: components, + image: image, + unit: unit + }, + property: { + backgroundAttachment: keyword('background-attachment'), + backgroundClip: keywordWithGlobal('background-clip'), + backgroundOrigin: keyword('background-origin'), + backgroundPosition: backgroundPosition, + backgroundRepeat: keyword('background-repeat'), + backgroundSize: backgroundSize, + bottom: unitOrKeywordWithGlobal('bottom'), + borderCollapse: keyword('border-collapse'), + borderStyle: keywordWithGlobal('*-style'), + clear: keywordWithGlobal('clear'), + cursor: keywordWithGlobal('cursor'), + display: keywordWithGlobal('display'), + float: keywordWithGlobal('float'), + fontStyle: keywordWithGlobal('font-style'), + left: unitOrKeywordWithGlobal('left'), + fontWeight: keywordWithGlobal('font-weight'), + listStyleType: keywordWithGlobal('list-style-type'), + listStylePosition: keywordWithGlobal('list-style-position'), + outlineStyle: keywordWithGlobal('*-style'), + overflow: keywordWithGlobal('overflow'), + position: keywordWithGlobal('position'), + right: unitOrKeywordWithGlobal('right'), + textAlign: keywordWithGlobal('text-align'), + textDecoration: keywordWithGlobal('text-decoration'), + textOverflow: keywordWithGlobal('text-overflow'), + textShadow: textShadow, + top: unitOrKeywordWithGlobal('top'), + transform: sameFunctionOrValue, + verticalAlign: unitOrKeywordWithGlobal('vertical-align'), + visibility: keywordWithGlobal('visibility'), + whiteSpace: keywordWithGlobal('white-space'), + zIndex: zIndex + } +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/clone.js b/node_modules/clean-css/lib/optimizer/level-2/clone.js new file mode 100644 index 000000000..3830095e9 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/clone.js @@ -0,0 +1,33 @@ +var wrapSingle = require('../wrap-for-optimizing').single; + +var Token = require('../../tokenizer/token'); + +function deep(property) { + var cloned = shallow(property); + for (var i = property.components.length - 1; i >= 0; i--) { + var component = shallow(property.components[i]); + component.value = property.components[i].value.slice(0); + cloned.components.unshift(component); + } + + cloned.dirty = true; + cloned.value = property.value.slice(0); + + return cloned; +} + +function shallow(property) { + var cloned = wrapSingle([ + Token.PROPERTY, + [Token.PROPERTY_NAME, property.name] + ]); + cloned.important = property.important; + cloned.hack = property.hack; + cloned.unused = false; + return cloned; +} + +module.exports = { + deep: deep, + shallow: shallow +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/compactable.js b/node_modules/clean-css/lib/optimizer/level-2/compactable.js new file mode 100644 index 000000000..9cf334d44 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/compactable.js @@ -0,0 +1,787 @@ +// Contains the interpretation of CSS properties, as used by the property optimizer + +var breakUp = require('./break-up'); +var canOverride = require('./can-override'); +var restore = require('./restore'); + +var override = require('../../utils/override'); + +// Properties to process +// Extend this object in order to add support for more properties in the optimizer. +// +// Each key in this object represents a CSS property and should be an object. +// Such an object contains properties that describe how the represented CSS property should be handled. +// Possible options: +// +// * components: array (Only specify for shorthand properties.) +// Contains the names of the granular properties this shorthand compacts. +// +// * canOverride: function +// Returns whether two tokens of this property can be merged with each other. +// This property has no meaning for shorthands. +// +// * defaultValue: string +// Specifies the default value of the property according to the CSS standard. +// For shorthand, this is used when every component is set to its default value, therefore it should be the shortest possible default value of all the components. +// +// * shortestValue: string +// Specifies the shortest possible value the property can possibly have. +// (Falls back to defaultValue if unspecified.) +// +// * breakUp: function (Only specify for shorthand properties.) +// Breaks the shorthand up to its components. +// +// * restore: function (Only specify for shorthand properties.) +// Puts the shorthand together from its components. +// +var compactable = { + 'background': { + canOverride: canOverride.generic.components([ + canOverride.generic.image, + canOverride.property.backgroundPosition, + canOverride.property.backgroundSize, + canOverride.property.backgroundRepeat, + canOverride.property.backgroundAttachment, + canOverride.property.backgroundOrigin, + canOverride.property.backgroundClip, + canOverride.generic.color + ]), + components: [ + 'background-image', + 'background-position', + 'background-size', + 'background-repeat', + 'background-attachment', + 'background-origin', + 'background-clip', + 'background-color' + ], + breakUp: breakUp.multiplex(breakUp.background), + defaultValue: '0 0', + restore: restore.multiplex(restore.background), + shortestValue: '0', + shorthand: true + }, + 'background-attachment': { + canOverride: canOverride.property.backgroundAttachment, + componentOf: [ + 'background' + ], + defaultValue: 'scroll' + }, + 'background-clip': { + canOverride: canOverride.property.backgroundClip, + componentOf: [ + 'background' + ], + defaultValue: 'border-box', + shortestValue: 'border-box' + }, + 'background-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'background' + ], + defaultValue: 'transparent', + multiplexLastOnly: true, + nonMergeableValue: 'none', + shortestValue: 'red' + }, + 'background-image': { + canOverride: canOverride.generic.image, + componentOf: [ + 'background' + ], + defaultValue: 'none' + }, + 'background-origin': { + canOverride: canOverride.property.backgroundOrigin, + componentOf: [ + 'background' + ], + defaultValue: 'padding-box', + shortestValue: 'border-box' + }, + 'background-position': { + canOverride: canOverride.property.backgroundPosition, + componentOf: [ + 'background' + ], + defaultValue: ['0', '0'], + doubleValues: true, + shortestValue: '0' + }, + 'background-repeat': { + canOverride: canOverride.property.backgroundRepeat, + componentOf: [ + 'background' + ], + defaultValue: ['repeat'], + doubleValues: true + }, + 'background-size': { + canOverride: canOverride.property.backgroundSize, + componentOf: [ + 'background' + ], + defaultValue: ['auto'], + doubleValues: true, + shortestValue: '0 0' + }, + 'bottom': { + canOverride: canOverride.property.bottom, + defaultValue: 'auto' + }, + 'border': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-width', + 'border-style', + 'border-color' + ], + defaultValue: 'none', + overridesShorthands: [ + 'border-bottom', + 'border-left', + 'border-right', + 'border-top' + ], + restore: restore.withoutDefaults, + shorthand: true, + shorthandComponents: true + }, + 'border-bottom': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-bottom-width', + 'border-bottom-style', + 'border-bottom-color' + ], + defaultValue: 'none', + restore: restore.withoutDefaults, + shorthand: true + }, + 'border-bottom-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'border-bottom', + 'border-color' + ], + defaultValue: 'none' + }, + 'border-bottom-left-radius': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-radius' + ], + defaultValue: '0', + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-bottom-right-radius': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-radius' + ], + defaultValue: '0', + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-bottom-style': { + canOverride: canOverride.property.borderStyle, + componentOf: [ + 'border-bottom', + 'border-style' + ], + defaultValue: 'none' + }, + 'border-bottom-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-bottom', + 'border-width' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'border-collapse': { + canOverride: canOverride.property.borderCollapse, + defaultValue: 'separate' + }, + 'border-color': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.generic.color, + canOverride.generic.color, + canOverride.generic.color, + canOverride.generic.color + ]), + componentOf: ['border'], + components: [ + 'border-top-color', + 'border-right-color', + 'border-bottom-color', + 'border-left-color' + ], + defaultValue: 'none', + restore: restore.fourValues, + shortestValue: 'red', + shorthand: true + }, + 'border-left': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-left-width', + 'border-left-style', + 'border-left-color' + ], + defaultValue: 'none', + restore: restore.withoutDefaults, + shorthand: true + }, + 'border-left-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'border-color', + 'border-left' + ], + defaultValue: 'none' + }, + 'border-left-style': { + canOverride: canOverride.property.borderStyle, + componentOf: [ + 'border-left', + 'border-style' + ], + defaultValue: 'none' + }, + 'border-left-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-left', + 'border-width' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'border-radius': { + breakUp: breakUp.borderRadius, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit + ]), + components: [ + 'border-top-left-radius', + 'border-top-right-radius', + 'border-bottom-right-radius', + 'border-bottom-left-radius' + ], + defaultValue: '0', + restore: restore.borderRadius, + shorthand: true, + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-right': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-right-width', + 'border-right-style', + 'border-right-color' + ], + defaultValue: 'none', + restore: restore.withoutDefaults, + shorthand: true + }, + 'border-right-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'border-color', + 'border-right' + ], + defaultValue: 'none' + }, + 'border-right-style': { + canOverride: canOverride.property.borderStyle, + componentOf: [ + 'border-right', + 'border-style' + ], + defaultValue: 'none' + }, + 'border-right-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-right', + 'border-width' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'border-style': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.property.borderStyle, + canOverride.property.borderStyle, + canOverride.property.borderStyle, + canOverride.property.borderStyle + ]), + componentOf: [ + 'border' + ], + components: [ + 'border-top-style', + 'border-right-style', + 'border-bottom-style', + 'border-left-style' + ], + defaultValue: 'none', + restore: restore.fourValues, + shorthand: true + }, + 'border-top': { + breakUp: breakUp.border, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.property.borderStyle, + canOverride.generic.color + ]), + components: [ + 'border-top-width', + 'border-top-style', + 'border-top-color' + ], + defaultValue: 'none', + restore: restore.withoutDefaults, + shorthand: true + }, + 'border-top-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'border-color', + 'border-top' + ], + defaultValue: 'none' + }, + 'border-top-left-radius': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-radius' + ], + defaultValue: '0', + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-top-right-radius': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-radius' + ], + defaultValue: '0', + vendorPrefixes: [ + '-moz-', + '-o-' + ] + }, + 'border-top-style': { + canOverride: canOverride.property.borderStyle, + componentOf: [ + 'border-style', + 'border-top' + ], + defaultValue: 'none' + }, + 'border-top-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'border-top', + 'border-width' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'border-width': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit + ]), + components: [ + 'border-top-width', + 'border-right-width', + 'border-bottom-width', + 'border-left-width' + ], + defaultValue: 'medium', + restore: restore.fourValues, + shortestValue: '0', + shorthand: true + }, + 'clear': { + canOverride: canOverride.property.clear, + defaultValue: 'none' + }, + 'color': { + canOverride: canOverride.generic.color, + defaultValue: 'transparent', + shortestValue: 'red' + }, + 'cursor': { + canOverride: canOverride.property.cursor, + defaultValue: 'auto' + }, + 'display': { + canOverride: canOverride.property.display, + }, + 'float': { + canOverride: canOverride.property.float, + defaultValue: 'none' + }, + 'font-size': { + canOverride: canOverride.generic.unit, + defaultValue: 'medium', + shortestValue: '0' + }, + 'font-style': { + canOverride: canOverride.property.fontStyle, + defaultValue: 'normal' + }, + 'font-weight': { + canOverride: canOverride.property.fontWeight, + defaultValue: '400', + shortestValue: '400' + }, + 'height': { + canOverride: canOverride.generic.unit, + defaultValue: 'auto', + shortestValue: '0' + }, + 'left': { + canOverride: canOverride.property.left, + defaultValue: 'auto' + }, + 'line-height': { + canOverride: canOverride.generic.unit, + defaultValue: 'normal', + shortestValue: '0' + }, + 'list-style': { + canOverride: canOverride.generic.components([ + canOverride.property.listStyleType, + canOverride.property.listStylePosition, + canOverride.property.listStyleImage + ]), + components: [ + 'list-style-type', + 'list-style-position', + 'list-style-image' + ], + breakUp: breakUp.listStyle, + restore: restore.withoutDefaults, + defaultValue: 'outside', // can't use 'disc' because that'd override default 'decimal' for <ol> + shortestValue: 'none', + shorthand: true + }, + 'list-style-image' : { + canOverride: canOverride.generic.image, + componentOf: [ + 'list-style' + ], + defaultValue: 'none' + }, + 'list-style-position' : { + canOverride: canOverride.property.listStylePosition, + componentOf: [ + 'list-style' + ], + defaultValue: 'outside', + shortestValue: 'inside' + }, + 'list-style-type' : { + canOverride: canOverride.property.listStyleType, + componentOf: [ + 'list-style' + ], + // NOTE: we can't tell the real default value here, it's 'disc' for <ul> and 'decimal' for <ol> + // this is a hack, but it doesn't matter because this value will be either overridden or + // it will disappear at the final step anyway + defaultValue: 'decimal|disc', + shortestValue: 'none' + }, + 'margin': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit + ]), + components: [ + 'margin-top', + 'margin-right', + 'margin-bottom', + 'margin-left' + ], + defaultValue: '0', + restore: restore.fourValues, + shorthand: true + }, + 'margin-bottom': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'margin' + ], + defaultValue: '0' + }, + 'margin-left': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'margin' + ], + defaultValue: '0' + }, + 'margin-right': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'margin' + ], + defaultValue: '0' + }, + 'margin-top': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'margin' + ], + defaultValue: '0' + }, + 'outline': { + canOverride: canOverride.generic.components([ + canOverride.generic.color, + canOverride.property.outlineStyle, + canOverride.generic.unit + ]), + components: [ + 'outline-color', + 'outline-style', + 'outline-width' + ], + breakUp: breakUp.outline, + restore: restore.withoutDefaults, + defaultValue: '0', + shorthand: true + }, + 'outline-color': { + canOverride: canOverride.generic.color, + componentOf: [ + 'outline' + ], + defaultValue: 'invert', + shortestValue: 'red' + }, + 'outline-style': { + canOverride: canOverride.property.outlineStyle, + componentOf: [ + 'outline' + ], + defaultValue: 'none' + }, + 'outline-width': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'outline' + ], + defaultValue: 'medium', + shortestValue: '0' + }, + 'overflow': { + canOverride: canOverride.property.overflow, + defaultValue: 'visible' + }, + 'overflow-x': { + canOverride: canOverride.property.overflow, + defaultValue: 'visible' + }, + 'overflow-y': { + canOverride: canOverride.property.overflow, + defaultValue: 'visible' + }, + 'padding': { + breakUp: breakUp.fourValues, + canOverride: canOverride.generic.components([ + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit, + canOverride.generic.unit + ]), + components: [ + 'padding-top', + 'padding-right', + 'padding-bottom', + 'padding-left' + ], + defaultValue: '0', + restore: restore.fourValues, + shorthand: true + }, + 'padding-bottom': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'padding' + ], + defaultValue: '0' + }, + 'padding-left': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'padding' + ], + defaultValue: '0' + }, + 'padding-right': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'padding' + ], + defaultValue: '0' + }, + 'padding-top': { + canOverride: canOverride.generic.unit, + componentOf: [ + 'padding' + ], + defaultValue: '0' + }, + 'position': { + canOverride: canOverride.property.position, + defaultValue: 'static' + }, + 'right': { + canOverride: canOverride.property.right, + defaultValue: 'auto' + }, + 'text-align': { + canOverride: canOverride.property.textAlign, + // NOTE: we can't tell the real default value here, as it depends on default text direction + // this is a hack, but it doesn't matter because this value will be either overridden or + // it will disappear anyway + defaultValue: 'left|right' + }, + 'text-decoration': { + canOverride: canOverride.property.textDecoration, + defaultValue: 'none' + }, + 'text-overflow': { + canOverride: canOverride.property.textOverflow, + defaultValue: 'none' + }, + 'text-shadow': { + canOverride: canOverride.property.textShadow, + defaultValue: 'none' + }, + 'top': { + canOverride: canOverride.property.top, + defaultValue: 'auto' + }, + 'transform': { + canOverride: canOverride.property.transform, + vendorPrefixes: [ + '-moz-', + '-ms-', + '-webkit-' + ] + }, + 'vertical-align': { + canOverride: canOverride.property.verticalAlign, + defaultValue: 'baseline' + }, + 'visibility': { + canOverride: canOverride.property.visibility, + defaultValue: 'visible' + }, + 'white-space': { + canOverride: canOverride.property.whiteSpace, + defaultValue: 'normal' + }, + 'width': { + canOverride: canOverride.generic.unit, + defaultValue: 'auto', + shortestValue: '0' + }, + 'z-index': { + canOverride: canOverride.property.zIndex, + defaultValue: 'auto' + } +}; + +function cloneDescriptor(propertyName, prefix) { + var clonedDescriptor = override(compactable[propertyName], {}); + + if ('componentOf' in clonedDescriptor) { + clonedDescriptor.componentOf = clonedDescriptor.componentOf.map(function (shorthandName) { + return prefix + shorthandName; + }); + } + + if ('components' in clonedDescriptor) { + clonedDescriptor.components = clonedDescriptor.components.map(function (longhandName) { + return prefix + longhandName; + }); + } + + return clonedDescriptor; +} + +// generate vendor-prefixed properties +var vendorPrefixedCompactable = {}; + +for (var propertyName in compactable) { + var descriptor = compactable[propertyName]; + + if (!('vendorPrefixes' in descriptor)) { + continue; + } + + for (var i = 0; i < descriptor.vendorPrefixes.length; i++) { + var prefix = descriptor.vendorPrefixes[i]; + var clonedDescriptor = cloneDescriptor(propertyName, prefix); + delete clonedDescriptor.vendorPrefixes; + + vendorPrefixedCompactable[prefix + propertyName] = clonedDescriptor; + } + + delete descriptor.vendorPrefixes; +} + +module.exports = override(compactable, vendorPrefixedCompactable); diff --git a/node_modules/clean-css/lib/optimizer/level-2/extract-properties.js b/node_modules/clean-css/lib/optimizer/level-2/extract-properties.js new file mode 100644 index 000000000..8b9b1c2e0 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/extract-properties.js @@ -0,0 +1,73 @@ +// This extractor is used in level 2 optimizations +// IMPORTANT: Mind Token class and this code is not related! +// Properties will be tokenized in one step, see #429 + +var Token = require('../../tokenizer/token'); +var serializeRules = require('../../writer/one-time').rules; +var serializeValue = require('../../writer/one-time').value; + +function extractProperties(token) { + var properties = []; + var inSpecificSelector; + var property; + var name; + var value; + var i, l; + + if (token[0] == Token.RULE) { + inSpecificSelector = !/[\.\+>~]/.test(serializeRules(token[1])); + + for (i = 0, l = token[2].length; i < l; i++) { + property = token[2][i]; + + if (property[0] != Token.PROPERTY) + continue; + + name = property[1][1]; + if (name.length === 0) + continue; + + if (name.indexOf('--') === 0) + continue; + + value = serializeValue(property, i); + + properties.push([ + name, + value, + findNameRoot(name), + token[2][i], + name + ':' + value, + token[1], + inSpecificSelector + ]); + } + } else if (token[0] == Token.NESTED_BLOCK) { + for (i = 0, l = token[2].length; i < l; i++) { + properties = properties.concat(extractProperties(token[2][i])); + } + } + + return properties; +} + +function findNameRoot(name) { + if (name == 'list-style') + return name; + if (name.indexOf('-radius') > 0) + return 'border-radius'; + if (name == 'border-collapse' || name == 'border-spacing' || name == 'border-image') + return name; + if (name.indexOf('border-') === 0 && /^border\-\w+\-\w+$/.test(name)) + return name.match(/border\-\w+/)[0]; + if (name.indexOf('border-') === 0 && /^border\-\w+$/.test(name)) + return 'border'; + if (name.indexOf('text-') === 0) + return name; + if (name == '-chrome-') + return name; + + return name.replace(/^\-\w+\-/, '').match(/([a-zA-Z]+)/)[0].toLowerCase(); +} + +module.exports = extractProperties; diff --git a/node_modules/clean-css/lib/optimizer/level-2/invalid-property-error.js b/node_modules/clean-css/lib/optimizer/level-2/invalid-property-error.js new file mode 100644 index 000000000..86d5b5f9b --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/invalid-property-error.js @@ -0,0 +1,10 @@ +function InvalidPropertyError(message) { + this.name = 'InvalidPropertyError'; + this.message = message; + this.stack = (new Error()).stack; +} + +InvalidPropertyError.prototype = Object.create(Error.prototype); +InvalidPropertyError.prototype.constructor = InvalidPropertyError; + +module.exports = InvalidPropertyError; diff --git a/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js b/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js new file mode 100644 index 000000000..4a91a8331 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/is-mergeable.js @@ -0,0 +1,236 @@ +var Marker = require('../../tokenizer/marker'); +var split = require('../../utils/split'); + +var DEEP_SELECTOR_PATTERN = /\/deep\//; +var DOUBLE_COLON_PATTERN = /^::/; +var NOT_PSEUDO = ':not'; +var PSEUDO_CLASSES_WITH_ARGUMENTS = [ + ':dir', + ':lang', + ':not', + ':nth-child', + ':nth-last-child', + ':nth-last-of-type', + ':nth-of-type' +]; +var RELATION_PATTERN = /[>\+~]/; + +var Level = { + DOUBLE_QUOTE: 'double-quote', + SINGLE_QUOTE: 'single-quote', + ROOT: 'root' +}; + +function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements) { + var singleSelectors = split(selector, Marker.COMMA); + var singleSelector; + var i, l; + + for (i = 0, l = singleSelectors.length; i < l; i++) { + singleSelector = singleSelectors[i]; + + if (singleSelector.length === 0 || + isDeepSelector(singleSelector) || + (singleSelector.indexOf(Marker.COLON) > -1 && !areMergeable(singleSelector, extractPseudoFrom(singleSelector), mergeablePseudoClasses, mergeablePseudoElements))) { + return false; + } + } + + return true; +} + +function isDeepSelector(selector) { + return DEEP_SELECTOR_PATTERN.test(selector); +} + +function extractPseudoFrom(selector) { + var list = []; + var character; + var buffer = []; + var level = Level.ROOT; + var roundBracketLevel = 0; + var isQuoted; + var isEscaped; + var isPseudo = false; + var isRelation; + var wasColon = false; + var index; + var len; + + for (index = 0, len = selector.length; index < len; index++) { + character = selector[index]; + + isRelation = !isEscaped && RELATION_PATTERN.test(character); + isQuoted = level == Level.DOUBLE_QUOTE || level == Level.SINGLE_QUOTE; + + if (isEscaped) { + buffer.push(character); + } else if (character == Marker.DOUBLE_QUOTE && level == Level.ROOT) { + buffer.push(character); + level = Level.DOUBLE_QUOTE; + } else if (character == Marker.DOUBLE_QUOTE && level == Level.DOUBLE_QUOTE) { + buffer.push(character); + level = Level.ROOT; + } else if (character == Marker.SINGLE_QUOTE && level == Level.ROOT) { + buffer.push(character); + level = Level.SINGLE_QUOTE; + } else if (character == Marker.SINGLE_QUOTE && level == Level.SINGLE_QUOTE) { + buffer.push(character); + level = Level.ROOT; + } else if (isQuoted) { + buffer.push(character); + } else if (character == Marker.OPEN_ROUND_BRACKET) { + buffer.push(character); + roundBracketLevel++; + } else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1 && isPseudo) { + buffer.push(character); + list.push(buffer.join('')); + roundBracketLevel--; + buffer = []; + isPseudo = false; + } else if (character == Marker.CLOSE_ROUND_BRACKET) { + buffer.push(character); + roundBracketLevel--; + } else if (character == Marker.COLON && roundBracketLevel === 0 && isPseudo && !wasColon) { + list.push(buffer.join('')); + buffer = []; + buffer.push(character); + } else if (character == Marker.COLON && roundBracketLevel === 0 && !wasColon) { + buffer = []; + buffer.push(character); + isPseudo = true; + } else if (character == Marker.SPACE && roundBracketLevel === 0 && isPseudo) { + list.push(buffer.join('')); + buffer = []; + isPseudo = false; + } else if (isRelation && roundBracketLevel === 0 && isPseudo) { + list.push(buffer.join('')); + buffer = []; + isPseudo = false; + } else { + buffer.push(character); + } + + isEscaped = character == Marker.BACK_SLASH; + wasColon = character == Marker.COLON; + } + + if (buffer.length > 0 && isPseudo) { + list.push(buffer.join('')); + } + + return list; +} + +function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements) { + return areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) && + needArguments(matches) && + (matches.length < 2 || !someIncorrectlyChained(selector, matches)) && + (matches.length < 2 || !someMixed(matches)); +} + +function areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) { + var match; + var name; + var i, l; + + for (i = 0, l = matches.length; i < l; i++) { + match = matches[i]; + name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ? + match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) : + match; + + if (mergeablePseudoClasses.indexOf(name) === -1 && mergeablePseudoElements.indexOf(name) === -1) { + return false; + } + } + + return true; +} + +function needArguments(matches) { + var match; + var name; + var bracketOpensAt; + var hasArguments; + var i, l; + + for (i = 0, l = matches.length; i < l; i++) { + match = matches[i]; + + bracketOpensAt = match.indexOf(Marker.OPEN_ROUND_BRACKET); + hasArguments = bracketOpensAt > -1; + name = hasArguments ? + match.substring(0, bracketOpensAt) : + match; + + if (hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) == -1) { + return false; + } + + if (!hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) > -1) { + return false; + } + } + + return true; +} + +function someIncorrectlyChained(selector, matches) { + var positionInSelector = 0; + var match; + var matchAt; + var nextMatch; + var nextMatchAt; + var name; + var nextName; + var areChained; + var i, l; + + for (i = 0, l = matches.length; i < l; i++) { + match = matches[i]; + nextMatch = matches[i + 1]; + + if (!nextMatch) { + break; + } + + matchAt = selector.indexOf(match, positionInSelector); + nextMatchAt = selector.indexOf(match, matchAt + 1); + positionInSelector = nextMatchAt; + areChained = matchAt + match.length == nextMatchAt; + + if (areChained) { + name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ? + match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) : + match; + nextName = nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 ? + nextMatch.substring(0, nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET)) : + nextMatch; + + if (name != NOT_PSEUDO || nextName != NOT_PSEUDO) { + return true; + } + } + } + + return false; +} + +function someMixed(matches) { + var firstIsPseudoElement = DOUBLE_COLON_PATTERN.test(matches[0]); + var match; + var i, l; + + for (i = 0, l = matches.length; i < l; i++) { + match = matches[i]; + + if (DOUBLE_COLON_PATTERN.test(match) != firstIsPseudoElement) { + return true; + } + } + + return false; +} + +module.exports = isMergeable; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js b/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js new file mode 100644 index 000000000..7c0537501 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-adjacent.js @@ -0,0 +1,49 @@ +var isMergeable = require('./is-mergeable'); + +var optimizeProperties = require('./properties/optimize'); + +var sortSelectors = require('../level-1/sort-selectors'); +var tidyRules = require('../level-1/tidy-rules'); + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +var Token = require('../../tokenizer/token'); + +function mergeAdjacent(tokens, context) { + var lastToken = [null, [], []]; + var options = context.options; + var adjacentSpace = options.compatibility.selectors.adjacentSpace; + var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod; + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var mergeLimit = 8191; + + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + if (token[0] != Token.RULE) { + lastToken = [null, [], []]; + continue; + } + + if (lastToken[0] == Token.RULE && serializeRules(token[1]) == serializeRules(lastToken[1])) { + Array.prototype.push.apply(lastToken[2], token[2]); + optimizeProperties(lastToken[2], true, true, context); + token[2] = []; + } else if (lastToken[0] == Token.RULE && serializeBody(token[2]) == serializeBody(lastToken[2]) && + isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) && + isMergeable(serializeRules(lastToken[1]), mergeablePseudoClasses, mergeablePseudoElements) && + lastToken[1].length < mergeLimit) { + lastToken[1] = tidyRules(lastToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings); + lastToken[1] = lastToken.length > 1 ? sortSelectors(lastToken[1], selectorsSortingMethod) : lastToken[1]; + token[2] = []; + } else { + lastToken = token; + } + } +} + +module.exports = mergeAdjacent; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-media-queries.js b/node_modules/clean-css/lib/optimizer/level-2/merge-media-queries.js new file mode 100644 index 000000000..c3c60dc24 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-media-queries.js @@ -0,0 +1,103 @@ +var canReorder = require('./reorderable').canReorder; +var canReorderSingle = require('./reorderable').canReorderSingle; +var extractProperties = require('./extract-properties'); +var rulesOverlap = require('./rules-overlap'); + +var serializeRules = require('../../writer/one-time').rules; +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; +var Token = require('../../tokenizer/token'); + +function mergeMediaQueries(tokens, context) { + var mergeSemantically = context.options.level[OptimizationLevel.Two].mergeSemantically; + var specificityCache = context.cache.specificity; + var candidates = {}; + var reduced = []; + + for (var i = tokens.length - 1; i >= 0; i--) { + var token = tokens[i]; + if (token[0] != Token.NESTED_BLOCK) { + continue; + } + + var key = serializeRules(token[1]); + var candidate = candidates[key]; + if (!candidate) { + candidate = []; + candidates[key] = candidate; + } + + candidate.push(i); + } + + for (var name in candidates) { + var positions = candidates[name]; + + positionLoop: + for (var j = positions.length - 1; j > 0; j--) { + var positionOne = positions[j]; + var tokenOne = tokens[positionOne]; + var positionTwo = positions[j - 1]; + var tokenTwo = tokens[positionTwo]; + + directionLoop: + for (var direction = 1; direction >= -1; direction -= 2) { + var topToBottom = direction == 1; + var from = topToBottom ? positionOne + 1 : positionTwo - 1; + var to = topToBottom ? positionTwo : positionOne; + var delta = topToBottom ? 1 : -1; + var source = topToBottom ? tokenOne : tokenTwo; + var target = topToBottom ? tokenTwo : tokenOne; + var movedProperties = extractProperties(source); + + while (from != to) { + var traversedProperties = extractProperties(tokens[from]); + from += delta; + + if (mergeSemantically && allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache)) { + continue; + } + + if (!canReorder(movedProperties, traversedProperties, specificityCache)) + continue directionLoop; + } + + target[2] = topToBottom ? + source[2].concat(target[2]) : + target[2].concat(source[2]); + source[2] = []; + + reduced.push(target); + continue positionLoop; + } + } + } + + return reduced; +} + +function allSameRulePropertiesCanBeReordered(movedProperties, traversedProperties, specificityCache) { + var movedProperty; + var movedRule; + var traversedProperty; + var traversedRule; + var i, l; + var j, m; + + for (i = 0, l = movedProperties.length; i < l; i++) { + movedProperty = movedProperties[i]; + movedRule = movedProperty[5]; + + for (j = 0, m = traversedProperties.length; j < m; j++) { + traversedProperty = traversedProperties[j]; + traversedRule = traversedProperty[5]; + + if (rulesOverlap(movedRule, traversedRule, true) && !canReorderSingle(movedProperty, traversedProperty, specificityCache)) { + return false; + } + } + } + + return true; +} + +module.exports = mergeMediaQueries; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-body.js b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-body.js new file mode 100644 index 000000000..bdcf53ec0 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-body.js @@ -0,0 +1,79 @@ +var isMergeable = require('./is-mergeable'); + +var sortSelectors = require('../level-1/sort-selectors'); +var tidyRules = require('../level-1/tidy-rules'); + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +var Token = require('../../tokenizer/token'); + +function unsafeSelector(value) { + return /\.|\*| :/.test(value); +} + +function isBemElement(token) { + var asString = serializeRules(token[1]); + return asString.indexOf('__') > -1 || asString.indexOf('--') > -1; +} + +function withoutModifier(selector) { + return selector.replace(/--[^ ,>\+~:]+/g, ''); +} + +function removeAnyUnsafeElements(left, candidates) { + var leftSelector = withoutModifier(serializeRules(left[1])); + + for (var body in candidates) { + var right = candidates[body]; + var rightSelector = withoutModifier(serializeRules(right[1])); + + if (rightSelector.indexOf(leftSelector) > -1 || leftSelector.indexOf(rightSelector) > -1) + delete candidates[body]; + } +} + +function mergeNonAdjacentByBody(tokens, context) { + var options = context.options; + var mergeSemantically = options.level[OptimizationLevel.Two].mergeSemantically; + var adjacentSpace = options.compatibility.selectors.adjacentSpace; + var selectorsSortingMethod = options.level[OptimizationLevel.One].selectorsSortingMethod; + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var candidates = {}; + + for (var i = tokens.length - 1; i >= 0; i--) { + var token = tokens[i]; + if (token[0] != Token.RULE) + continue; + + if (token[2].length > 0 && (!mergeSemantically && unsafeSelector(serializeRules(token[1])))) + candidates = {}; + + if (token[2].length > 0 && mergeSemantically && isBemElement(token)) + removeAnyUnsafeElements(token, candidates); + + var candidateBody = serializeBody(token[2]); + var oldToken = candidates[candidateBody]; + if (oldToken && + isMergeable(serializeRules(token[1]), mergeablePseudoClasses, mergeablePseudoElements) && + isMergeable(serializeRules(oldToken[1]), mergeablePseudoClasses, mergeablePseudoElements)) { + + if (token[2].length > 0) { + token[1] = tidyRules(oldToken[1].concat(token[1]), false, adjacentSpace, false, context.warnings); + token[1] = token[1].length > 1 ? sortSelectors(token[1], selectorsSortingMethod) : token[1]; + } else { + token[1] = oldToken[1].concat(token[1]); + } + + oldToken[2] = []; + candidates[candidateBody] = null; + } + + candidates[serializeBody(token[2])] = token; + } +} + +module.exports = mergeNonAdjacentByBody; diff --git a/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-selector.js b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-selector.js new file mode 100644 index 000000000..5e23064eb --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/merge-non-adjacent-by-selector.js @@ -0,0 +1,78 @@ +var canReorder = require('./reorderable').canReorder; +var extractProperties = require('./extract-properties'); + +var optimizeProperties = require('./properties/optimize'); + +var serializeRules = require('../../writer/one-time').rules; + +var Token = require('../../tokenizer/token'); + +function mergeNonAdjacentBySelector(tokens, context) { + var specificityCache = context.cache.specificity; + var allSelectors = {}; + var repeatedSelectors = []; + var i; + + for (i = tokens.length - 1; i >= 0; i--) { + if (tokens[i][0] != Token.RULE) + continue; + if (tokens[i][2].length === 0) + continue; + + var selector = serializeRules(tokens[i][1]); + allSelectors[selector] = [i].concat(allSelectors[selector] || []); + + if (allSelectors[selector].length == 2) + repeatedSelectors.push(selector); + } + + for (i = repeatedSelectors.length - 1; i >= 0; i--) { + var positions = allSelectors[repeatedSelectors[i]]; + + selectorIterator: + for (var j = positions.length - 1; j > 0; j--) { + var positionOne = positions[j - 1]; + var tokenOne = tokens[positionOne]; + var positionTwo = positions[j]; + var tokenTwo = tokens[positionTwo]; + + directionIterator: + for (var direction = 1; direction >= -1; direction -= 2) { + var topToBottom = direction == 1; + var from = topToBottom ? positionOne + 1 : positionTwo - 1; + var to = topToBottom ? positionTwo : positionOne; + var delta = topToBottom ? 1 : -1; + var moved = topToBottom ? tokenOne : tokenTwo; + var target = topToBottom ? tokenTwo : tokenOne; + var movedProperties = extractProperties(moved); + + while (from != to) { + var traversedProperties = extractProperties(tokens[from]); + from += delta; + + // traversed then moved as we move selectors towards the start + var reorderable = topToBottom ? + canReorder(movedProperties, traversedProperties, specificityCache) : + canReorder(traversedProperties, movedProperties, specificityCache); + + if (!reorderable && !topToBottom) + continue selectorIterator; + if (!reorderable && topToBottom) + continue directionIterator; + } + + if (topToBottom) { + Array.prototype.push.apply(moved[2], target[2]); + target[2] = moved[2]; + } else { + Array.prototype.push.apply(target[2], moved[2]); + } + + optimizeProperties(target[2], true, true, context); + moved[2] = []; + } + } + } +} + +module.exports = mergeNonAdjacentBySelector; diff --git a/node_modules/clean-css/lib/optimizer/level-2/optimize.js b/node_modules/clean-css/lib/optimizer/level-2/optimize.js new file mode 100644 index 000000000..78cd44666 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/optimize.js @@ -0,0 +1,124 @@ +var mergeAdjacent = require('./merge-adjacent'); +var mergeMediaQueries = require('./merge-media-queries'); +var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body'); +var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector'); +var reduceNonAdjacent = require('./reduce-non-adjacent'); +var removeDuplicateFontAtRules = require('./remove-duplicate-font-at-rules'); +var removeDuplicateMediaQueries = require('./remove-duplicate-media-queries'); +var removeDuplicates = require('./remove-duplicates'); +var restructure = require('./restructure'); + +var optimizeProperties = require('./properties/optimize'); + +var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel; + +var Token = require('../../tokenizer/token'); + +function removeEmpty(tokens) { + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + var isEmpty = false; + + switch (token[0]) { + case Token.RULE: + isEmpty = token[1].length === 0 || token[2].length === 0; + break; + case Token.NESTED_BLOCK: + removeEmpty(token[2]); + isEmpty = token[2].length === 0; + break; + case Token.AT_RULE_BLOCK: + isEmpty = token[2].length === 0; + } + + if (isEmpty) { + tokens.splice(i, 1); + i--; + l--; + } + } +} + +function recursivelyOptimizeBlocks(tokens, context) { + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + if (token[0] == Token.NESTED_BLOCK) { + var isKeyframes = /@(-moz-|-o-|-webkit-)?keyframes/.test(token[1][0][1]); + level2Optimize(token[2], context, !isKeyframes); + } + } +} + +function recursivelyOptimizeProperties(tokens, context) { + for (var i = 0, l = tokens.length; i < l; i++) { + var token = tokens[i]; + + switch (token[0]) { + case Token.RULE: + optimizeProperties(token[2], true, true, context); + break; + case Token.NESTED_BLOCK: + recursivelyOptimizeProperties(token[2], context); + } + } +} + +function level2Optimize(tokens, context, withRestructuring) { + var levelOptions = context.options.level[OptimizationLevel.Two]; + var reduced; + var i; + + recursivelyOptimizeBlocks(tokens, context); + recursivelyOptimizeProperties(tokens, context); + + if (levelOptions.removeDuplicateRules) { + removeDuplicates(tokens, context); + } + + if (levelOptions.mergeAdjacentRules) { + mergeAdjacent(tokens, context); + } + + if (levelOptions.reduceNonAdjacentRules) { + reduceNonAdjacent(tokens, context); + } + + if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'body') { + mergeNonAdjacentBySelector(tokens, context); + } + + if (levelOptions.mergeNonAdjacentRules && levelOptions.mergeNonAdjacentRules != 'selector') { + mergeNonAdjacentByBody(tokens, context); + } + + if (levelOptions.restructureRules && levelOptions.mergeAdjacentRules && withRestructuring) { + restructure(tokens, context); + mergeAdjacent(tokens, context); + } + + if (levelOptions.restructureRules && !levelOptions.mergeAdjacentRules && withRestructuring) { + restructure(tokens, context); + } + + if (levelOptions.removeDuplicateFontRules) { + removeDuplicateFontAtRules(tokens, context); + } + + if (levelOptions.removeDuplicateMediaBlocks) { + removeDuplicateMediaQueries(tokens, context); + } + + if (levelOptions.mergeMedia) { + reduced = mergeMediaQueries(tokens, context); + for (i = reduced.length - 1; i >= 0; i--) { + level2Optimize(reduced[i][2], context, false); + } + } + + removeEmpty(tokens); + + return tokens; +} + +module.exports = level2Optimize; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/every-values-pair.js b/node_modules/clean-css/lib/optimizer/level-2/properties/every-values-pair.js new file mode 100644 index 000000000..44fcb7d53 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/every-values-pair.js @@ -0,0 +1,28 @@ +var Marker = require('../../../tokenizer/marker'); + +function everyValuesPair(fn, left, right) { + var leftSize = left.value.length; + var rightSize = right.value.length; + var total = Math.max(leftSize, rightSize); + var lowerBound = Math.min(leftSize, rightSize) - 1; + var leftValue; + var rightValue; + var position; + + for (position = 0; position < total; position++) { + leftValue = left.value[position] && left.value[position][1] || leftValue; + rightValue = right.value[position] && right.value[position][1] || rightValue; + + if (leftValue == Marker.COMMA || rightValue == Marker.COMMA) { + continue; + } + + if (!fn(leftValue, rightValue, position, position <= lowerBound)) { + return false; + } + } + + return true; +} + +module.exports = everyValuesPair; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/find-component-in.js b/node_modules/clean-css/lib/optimizer/level-2/properties/find-component-in.js new file mode 100644 index 000000000..dd7c719d5 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/find-component-in.js @@ -0,0 +1,40 @@ +var compactable = require('../compactable'); + +function findComponentIn(shorthand, longhand) { + var comparator = nameComparator(longhand); + + return findInDirectComponents(shorthand, comparator) || findInSubComponents(shorthand, comparator); +} + +function nameComparator(to) { + return function (property) { + return to.name === property.name; + }; +} + +function findInDirectComponents(shorthand, comparator) { + return shorthand.components.filter(comparator)[0]; +} + +function findInSubComponents(shorthand, comparator) { + var shorthandComponent; + var longhandMatch; + var i, l; + + if (!compactable[shorthand.name].shorthandComponents) { + return; + } + + for (i = 0, l = shorthand.components.length; i < l; i++) { + shorthandComponent = shorthand.components[i]; + longhandMatch = findInDirectComponents(shorthandComponent, comparator); + + if (longhandMatch) { + return longhandMatch; + } + } + + return; +} + +module.exports = findComponentIn; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/has-inherit.js b/node_modules/clean-css/lib/optimizer/level-2/properties/has-inherit.js new file mode 100644 index 000000000..84f220d32 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/has-inherit.js @@ -0,0 +1,10 @@ +function hasInherit(property) { + for (var i = property.value.length - 1; i >= 0; i--) { + if (property.value[i][1] == 'inherit') + return true; + } + + return false; +} + +module.exports = hasInherit; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/is-component-of.js b/node_modules/clean-css/lib/optimizer/level-2/properties/is-component-of.js new file mode 100644 index 000000000..237de7d10 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/is-component-of.js @@ -0,0 +1,22 @@ +var compactable = require('../compactable'); + +function isComponentOf(property1, property2, shallow) { + return isDirectComponentOf(property1, property2) || + !shallow && !!compactable[property1.name].shorthandComponents && isSubComponentOf(property1, property2); +} + +function isDirectComponentOf(property1, property2) { + var descriptor = compactable[property1.name]; + + return 'components' in descriptor && descriptor.components.indexOf(property2.name) > -1; +} + +function isSubComponentOf(property1, property2) { + return property1 + .components + .some(function (component) { + return isDirectComponentOf(component, property2); + }); +} + +module.exports = isComponentOf; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/merge-into-shorthands.js b/node_modules/clean-css/lib/optimizer/level-2/properties/merge-into-shorthands.js new file mode 100644 index 000000000..a75c4ed63 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/merge-into-shorthands.js @@ -0,0 +1,157 @@ +var everyValuesPair = require('./every-values-pair'); +var hasInherit = require('./has-inherit'); +var populateComponents = require('./populate-components'); + +var compactable = require('../compactable'); +var deepClone = require('../clone').deep; + +var wrapSingle = require('../../wrap-for-optimizing').single; + +var Token = require('../../../tokenizer/token'); + +function mixedImportance(components) { + var important; + + for (var name in components) { + if (undefined !== important && components[name].important != important) + return true; + + important = components[name].important; + } + + return false; +} + +function joinMetadata(components, at) { + var metadata = []; + var component; + var originalValue; + var componentMetadata; + var name; + + for (name in components) { + component = components[name]; + originalValue = component.all[component.position]; + componentMetadata = originalValue[at][originalValue[at].length - 1]; + + Array.prototype.push.apply(metadata, componentMetadata); + } + + return metadata; +} + +function replaceWithShorthand(properties, candidateComponents, name, validator) { + var descriptor = compactable[name]; + var nameMetadata; + var valueMetadata; + var newValuePlaceholder = [ + Token.PROPERTY, + [Token.PROPERTY_NAME, name], + [Token.PROPERTY_VALUE, descriptor.defaultValue] + ]; + var mayOverride; + var all; + + var newProperty = wrapSingle(newValuePlaceholder); + newProperty.shorthand = true; + newProperty.dirty = true; + + populateComponents([newProperty], validator, []); + + for (var i = 0, l = descriptor.components.length; i < l; i++) { + var component = candidateComponents[descriptor.components[i]]; + + if (hasInherit(component)) + return; + + mayOverride = compactable[component.name].canOverride; + if (!everyValuesPair(mayOverride.bind(null, validator), newProperty.components[i], component)) + return; + + newProperty.components[i] = deepClone(component); + newProperty.important = component.important; + + all = component.all; + } + + for (var componentName in candidateComponents) { + candidateComponents[componentName].unused = true; + } + + nameMetadata = joinMetadata(candidateComponents, 1); + newValuePlaceholder[1].push(nameMetadata); + + valueMetadata = joinMetadata(candidateComponents, 2); + newValuePlaceholder[2].push(valueMetadata); + + newProperty.position = all.length; + newProperty.all = all; + newProperty.all.push(newValuePlaceholder); + + properties.push(newProperty); +} + +function invalidateOrCompact(properties, position, candidates, validator) { + var property = properties[position]; + + for (var name in candidates) { + if (undefined !== property && name == property.name) + continue; + + var descriptor = compactable[name]; + var candidateComponents = candidates[name]; + if (descriptor.components.length > Object.keys(candidateComponents).length) { + delete candidates[name]; + continue; + } + + if (mixedImportance(candidateComponents)) + continue; + + replaceWithShorthand(properties, candidateComponents, name, validator); + } +} + +function mergeIntoShorthands(properties, validator) { + var candidates = {}; + var descriptor; + var componentOf; + var property; + var i, l; + var j, m; + + if (properties.length < 3) + return; + + for (i = 0, l = properties.length; i < l; i++) { + property = properties[i]; + + if (property.unused) + continue; + + if (property.hack) + continue; + + if (property.block) + continue; + + descriptor = compactable[property.name]; + if (!descriptor || !descriptor.componentOf) + continue; + + if (property.shorthand) { + invalidateOrCompact(properties, i, candidates, validator); + } else { + for (j = 0, m = descriptor.componentOf.length; j < m; j++) { + componentOf = descriptor.componentOf[j]; + + candidates[componentOf] = candidates[componentOf] || {}; + candidates[componentOf][property.name] = property; + } + } + } + + invalidateOrCompact(properties, i, candidates, validator); +} + +module.exports = mergeIntoShorthands; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js b/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js new file mode 100644 index 000000000..250b05fc4 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/optimize.js @@ -0,0 +1,39 @@ +var mergeIntoShorthands = require('./merge-into-shorthands'); +var overrideProperties = require('./override-properties'); +var populateComponents = require('./populate-components'); + +var restoreWithComponents = require('../restore-with-components'); + +var wrapForOptimizing = require('../../wrap-for-optimizing').all; +var removeUnused = require('../../remove-unused'); +var restoreFromOptimizing = require('../../restore-from-optimizing'); + +var OptimizationLevel = require('../../../options/optimization-level').OptimizationLevel; + +function optimizeProperties(properties, withOverriding, withMerging, context) { + var _properties = wrapForOptimizing(properties, false); + var _property; + var i, l; + + populateComponents(_properties, context.validator, context.warnings); + + for (i = 0, l = _properties.length; i < l; i++) { + _property = _properties[i]; + if (_property.block) { + optimizeProperties(_property.value[0][1], withOverriding, withMerging, context); + } + } + + if (withOverriding && context.options.level[OptimizationLevel.Two].overrideProperties) { + overrideProperties(_properties, withMerging, context.options.compatibility, context.validator); + } + + if (withMerging && context.options.level[OptimizationLevel.Two].mergeIntoShorthands) { + mergeIntoShorthands(_properties, context.validator); + } + + restoreFromOptimizing(_properties, restoreWithComponents); + removeUnused(_properties); +} + +module.exports = optimizeProperties; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/override-properties.js b/node_modules/clean-css/lib/optimizer/level-2/properties/override-properties.js new file mode 100644 index 000000000..3c9d8d2af --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/override-properties.js @@ -0,0 +1,441 @@ +var hasInherit = require('./has-inherit'); +var everyValuesPair = require('./every-values-pair'); +var findComponentIn = require('./find-component-in'); +var isComponentOf = require('./is-component-of'); +var overridesNonComponentShorthand = require('./overrides-non-component-shorthand'); +var sameVendorPrefixesIn = require('./vendor-prefixes').same; + +var compactable = require('../compactable'); +var deepClone = require('../clone').deep; +var deepClone = require('../clone').deep; +var restoreWithComponents = require('../restore-with-components'); +var shallowClone = require('../clone').shallow; + +var restoreFromOptimizing = require('../../restore-from-optimizing'); + +var Token = require('../../../tokenizer/token'); +var Marker = require('../../../tokenizer/marker'); + +var serializeProperty = require('../../../writer/one-time').property; + +function wouldBreakCompatibility(property, validator) { + for (var i = 0; i < property.components.length; i++) { + var component = property.components[i]; + var descriptor = compactable[component.name]; + var canOverride = descriptor && descriptor.canOverride || canOverride.sameValue; + + var _component = shallowClone(component); + _component.value = [[Token.PROPERTY_VALUE, descriptor.defaultValue]]; + + if (!everyValuesPair(canOverride.bind(null, validator), _component, component)) { + return true; + } + } + + return false; +} + +function overrideIntoMultiplex(property, by) { + by.unused = true; + + turnIntoMultiplex(by, multiplexSize(property)); + property.value = by.value; +} + +function overrideByMultiplex(property, by) { + by.unused = true; + property.multiplex = true; + property.value = by.value; +} + +function overrideSimple(property, by) { + by.unused = true; + property.value = by.value; +} + +function override(property, by) { + if (by.multiplex) + overrideByMultiplex(property, by); + else if (property.multiplex) + overrideIntoMultiplex(property, by); + else + overrideSimple(property, by); +} + +function overrideShorthand(property, by) { + by.unused = true; + + for (var i = 0, l = property.components.length; i < l; i++) { + override(property.components[i], by.components[i], property.multiplex); + } +} + +function turnIntoMultiplex(property, size) { + property.multiplex = true; + + for (var i = 0, l = property.components.length; i < l; i++) { + var component = property.components[i]; + if (component.multiplex) + continue; + + var value = component.value.slice(0); + + for (var j = 1; j < size; j++) { + component.value.push([Token.PROPERTY_VALUE, Marker.COMMA]); + Array.prototype.push.apply(component.value, value); + } + } +} + +function multiplexSize(component) { + var size = 0; + + for (var i = 0, l = component.value.length; i < l; i++) { + if (component.value[i][1] == Marker.COMMA) + size++; + } + + return size + 1; +} + +function lengthOf(property) { + var fakeAsArray = [ + Token.PROPERTY, + [Token.PROPERTY_NAME, property.name] + ].concat(property.value); + return serializeProperty([fakeAsArray], 0).length; +} + +function moreSameShorthands(properties, startAt, name) { + // Since we run the main loop in `compactOverrides` backwards, at this point some + // properties may not be marked as unused. + // We should consider reverting the order if possible + var count = 0; + + for (var i = startAt; i >= 0; i--) { + if (properties[i].name == name && !properties[i].unused) + count++; + if (count > 1) + break; + } + + return count > 1; +} + +function overridingFunction(shorthand, validator) { + for (var i = 0, l = shorthand.components.length; i < l; i++) { + if (anyValue(validator.isValidFunction, shorthand.components[i])) + return true; + } + + return false; +} + +function anyValue(fn, property) { + for (var i = 0, l = property.value.length; i < l; i++) { + if (property.value[i][1] == Marker.COMMA) + continue; + + if (fn(property.value[i][1])) + return true; + } + + return false; +} + +function wouldResultInLongerValue(left, right) { + if (!left.multiplex && !right.multiplex || left.multiplex && right.multiplex) + return false; + + var multiplex = left.multiplex ? left : right; + var simple = left.multiplex ? right : left; + var component; + + var multiplexClone = deepClone(multiplex); + restoreFromOptimizing([multiplexClone], restoreWithComponents); + + var simpleClone = deepClone(simple); + restoreFromOptimizing([simpleClone], restoreWithComponents); + + var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone); + + if (left.multiplex) { + component = findComponentIn(multiplexClone, simpleClone); + overrideIntoMultiplex(component, simpleClone); + } else { + component = findComponentIn(simpleClone, multiplexClone); + turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone)); + overrideByMultiplex(component, multiplexClone); + } + + restoreFromOptimizing([simpleClone], restoreWithComponents); + + var lengthAfter = lengthOf(simpleClone); + + return lengthBefore <= lengthAfter; +} + +function isCompactable(property) { + return property.name in compactable; +} + +function noneOverrideHack(left, right) { + return !left.multiplex && + (left.name == 'background' || left.name == 'background-image') && + right.multiplex && + (right.name == 'background' || right.name == 'background-image') && + anyLayerIsNone(right.value); +} + +function anyLayerIsNone(values) { + var layers = intoLayers(values); + + for (var i = 0, l = layers.length; i < l; i++) { + if (layers[i].length == 1 && layers[i][0][1] == 'none') + return true; + } + + return false; +} + +function intoLayers(values) { + var layers = []; + + for (var i = 0, layer = [], l = values.length; i < l; i++) { + var value = values[i]; + if (value[1] == Marker.COMMA) { + layers.push(layer); + layer = []; + } else { + layer.push(value); + } + } + + layers.push(layer); + return layers; +} + +function overrideProperties(properties, withMerging, compatibility, validator) { + var mayOverride, right, left, component; + var overriddenComponents; + var overriddenComponent; + var overridingComponent; + var overridable; + var i, j, k; + + propertyLoop: + for (i = properties.length - 1; i >= 0; i--) { + right = properties[i]; + + if (!isCompactable(right)) + continue; + + if (right.block) + continue; + + mayOverride = compactable[right.name].canOverride; + + traverseLoop: + for (j = i - 1; j >= 0; j--) { + left = properties[j]; + + if (!isCompactable(left)) + continue; + + if (left.block) + continue; + + if (left.unused || right.unused) + continue; + + if (left.hack && !right.hack && !right.important || !left.hack && !left.important && right.hack) + continue; + + if (left.important == right.important && left.hack[0] != right.hack[0]) + continue; + + if (left.important == right.important && (left.hack[0] != right.hack[0] || (left.hack[1] && left.hack[1] != right.hack[1]))) + continue; + + if (hasInherit(right)) + continue; + + if (noneOverrideHack(left, right)) + continue; + + if (right.shorthand && isComponentOf(right, left)) { + // maybe `left` can be overridden by `right` which is a shorthand? + if (!right.important && left.important) + continue; + + if (!sameVendorPrefixesIn([left], right.components)) + continue; + + if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) + continue; + + component = findComponentIn(right, left); + mayOverride = compactable[left.name].canOverride; + if (everyValuesPair(mayOverride.bind(null, validator), left, component)) { + left.unused = true; + } + } else if (right.shorthand && overridesNonComponentShorthand(right, left)) { + // `right` is a shorthand while `left` can be overriden by it, think `border` and `border-top` + if (!right.important && left.important) { + continue; + } + + if (!sameVendorPrefixesIn([left], right.components)) { + continue; + } + + if (!anyValue(validator.isValidFunction, left) && overridingFunction(right, validator)) { + continue; + } + + overriddenComponents = left.shorthand ? + left.components: + [left]; + + for (k = overriddenComponents.length - 1; k >= 0; k--) { + overriddenComponent = overriddenComponents[k]; + overridingComponent = findComponentIn(right, overriddenComponent); + mayOverride = compactable[overriddenComponent.name].canOverride; + + if (!everyValuesPair(mayOverride.bind(null, validator), left, overridingComponent)) { + continue traverseLoop; + } + } + + left.unused = true; + } else if (withMerging && left.shorthand && !right.shorthand && isComponentOf(left, right, true)) { + // maybe `right` can be pulled into `left` which is a shorthand? + if (right.important && !left.important) + continue; + + if (!right.important && left.important) { + right.unused = true; + continue; + } + + // Pending more clever algorithm in #527 + if (moreSameShorthands(properties, i - 1, left.name)) + continue; + + if (overridingFunction(left, validator)) + continue; + + component = findComponentIn(left, right); + if (everyValuesPair(mayOverride.bind(null, validator), component, right)) { + var disabledBackgroundMerging = + !compatibility.properties.backgroundClipMerging && component.name.indexOf('background-clip') > -1 || + !compatibility.properties.backgroundOriginMerging && component.name.indexOf('background-origin') > -1 || + !compatibility.properties.backgroundSizeMerging && component.name.indexOf('background-size') > -1; + var nonMergeableValue = compactable[right.name].nonMergeableValue === right.value[0][1]; + + if (disabledBackgroundMerging || nonMergeableValue) + continue; + + if (!compatibility.properties.merging && wouldBreakCompatibility(left, validator)) + continue; + + if (component.value[0][1] != right.value[0][1] && (hasInherit(left) || hasInherit(right))) + continue; + + if (wouldResultInLongerValue(left, right)) + continue; + + if (!left.multiplex && right.multiplex) + turnIntoMultiplex(left, multiplexSize(right)); + + override(component, right); + left.dirty = true; + } + } else if (withMerging && left.shorthand && right.shorthand && left.name == right.name) { + // merge if all components can be merged + + if (!left.multiplex && right.multiplex) + continue; + + if (!right.important && left.important) { + right.unused = true; + continue propertyLoop; + } + + if (right.important && !left.important) { + left.unused = true; + continue; + } + + for (k = left.components.length - 1; k >= 0; k--) { + var leftComponent = left.components[k]; + var rightComponent = right.components[k]; + + mayOverride = compactable[leftComponent.name].canOverride; + if (!everyValuesPair(mayOverride.bind(null, validator), leftComponent, rightComponent)) + continue propertyLoop; + } + + overrideShorthand(left, right); + left.dirty = true; + } else if (withMerging && left.shorthand && right.shorthand && isComponentOf(left, right)) { + // border is a shorthand but any of its components is a shorthand too + + if (!left.important && right.important) + continue; + + component = findComponentIn(left, right); + mayOverride = compactable[right.name].canOverride; + if (!everyValuesPair(mayOverride.bind(null, validator), component, right)) + continue; + + if (left.important && !right.important) { + right.unused = true; + continue; + } + + var rightRestored = compactable[right.name].restore(right, compactable); + if (rightRestored.length > 1) + continue; + + component = findComponentIn(left, right); + override(component, right); + right.dirty = true; + } else if (left.name == right.name) { + // two non-shorthands should be merged based on understandability + overridable = true; + + if (right.shorthand) { + for (k = right.components.length - 1; k >= 0 && overridable; k--) { + overriddenComponent = left.components[k]; + overridingComponent = right.components[k]; + mayOverride = compactable[overridingComponent.name].canOverride; + + overridable = overridable && everyValuesPair(mayOverride.bind(null, validator), overriddenComponent, overridingComponent); + } + } else { + mayOverride = compactable[right.name].canOverride; + overridable = everyValuesPair(mayOverride.bind(null, validator), left, right); + } + + if (left.important && !right.important && overridable) { + right.unused = true; + continue; + } + + if (!left.important && right.important && overridable) { + left.unused = true; + continue; + } + + if (!overridable) { + continue; + } + + left.unused = true; + } + } + } +} + +module.exports = overrideProperties; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/overrides-non-component-shorthand.js b/node_modules/clean-css/lib/optimizer/level-2/properties/overrides-non-component-shorthand.js new file mode 100644 index 000000000..c385218a9 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/overrides-non-component-shorthand.js @@ -0,0 +1,9 @@ +var compactable = require('../compactable'); + +function overridesNonComponentShorthand(property1, property2) { + return property1.name in compactable && + 'overridesShorthands' in compactable[property1.name] && + compactable[property1.name].overridesShorthands.indexOf(property2.name) > -1; +} + +module.exports = overridesNonComponentShorthand; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/populate-components.js b/node_modules/clean-css/lib/optimizer/level-2/properties/populate-components.js new file mode 100644 index 000000000..c587e83c4 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/populate-components.js @@ -0,0 +1,42 @@ +var compactable = require('../compactable'); +var InvalidPropertyError = require('../invalid-property-error'); + +function populateComponents(properties, validator, warnings) { + var component; + var j, m; + + for (var i = properties.length - 1; i >= 0; i--) { + var property = properties[i]; + var descriptor = compactable[property.name]; + + if (descriptor && descriptor.shorthand) { + property.shorthand = true; + property.dirty = true; + + try { + property.components = descriptor.breakUp(property, compactable, validator); + + if (descriptor.shorthandComponents) { + for (j = 0, m = property.components.length; j < m; j++) { + component = property.components[j]; + component.components = compactable[component.name].breakUp(component, compactable, validator); + } + } + } catch (e) { + if (e instanceof InvalidPropertyError) { + property.components = []; // this will set property.unused to true below + warnings.push(e.message); + } else { + throw e; + } + } + + if (property.components.length > 0) + property.multiplex = property.components[0].multiplex; + else + property.unused = true; + } + } +} + +module.exports = populateComponents; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js b/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js new file mode 100644 index 000000000..6c77db50d --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/understandable.js @@ -0,0 +1,15 @@ +var sameVendorPrefixes = require('./vendor-prefixes').same; + +function understandable(validator, value1, value2, _position, isPaired) { + if (!sameVendorPrefixes(value1, value2)) { + return false; + } + + if (isPaired && validator.isValidVariable(value1) !== validator.isValidVariable(value2)) { + return false; + } + + return true; +} + +module.exports = understandable; diff --git a/node_modules/clean-css/lib/optimizer/level-2/properties/vendor-prefixes.js b/node_modules/clean-css/lib/optimizer/level-2/properties/vendor-prefixes.js new file mode 100644 index 000000000..f9ab52728 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/properties/vendor-prefixes.js @@ -0,0 +1,23 @@ +var VENDOR_PREFIX_PATTERN = /(?:^|\W)(\-\w+\-)/g; + +function unique(value) { + var prefixes = []; + var match; + + while ((match = VENDOR_PREFIX_PATTERN.exec(value)) !== null) { + if (prefixes.indexOf(match[0]) == -1) { + prefixes.push(match[0]); + } + } + + return prefixes; +} + +function same(value1, value2) { + return unique(value1).sort().join(',') == unique(value2).sort().join(','); +} + +module.exports = { + unique: unique, + same: same +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/reduce-non-adjacent.js b/node_modules/clean-css/lib/optimizer/level-2/reduce-non-adjacent.js new file mode 100644 index 000000000..3461bb268 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/reduce-non-adjacent.js @@ -0,0 +1,178 @@ +var isMergeable = require('./is-mergeable'); + +var optimizeProperties = require('./properties/optimize'); + +var cloneArray = require('../../utils/clone-array'); + +var Token = require('../../tokenizer/token'); + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +function reduceNonAdjacent(tokens, context) { + var options = context.options; + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var candidates = {}; + var repeated = []; + + for (var i = tokens.length - 1; i >= 0; i--) { + var token = tokens[i]; + + if (token[0] != Token.RULE) { + continue; + } else if (token[2].length === 0) { + continue; + } + + var selectorAsString = serializeRules(token[1]); + var isComplexAndNotSpecial = token[1].length > 1 && + isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements); + var wrappedSelectors = wrappedSelectorsFrom(token[1]); + var selectors = isComplexAndNotSpecial ? + [selectorAsString].concat(wrappedSelectors) : + [selectorAsString]; + + for (var j = 0, m = selectors.length; j < m; j++) { + var selector = selectors[j]; + + if (!candidates[selector]) + candidates[selector] = []; + else + repeated.push(selector); + + candidates[selector].push({ + where: i, + list: wrappedSelectors, + isPartial: isComplexAndNotSpecial && j > 0, + isComplex: isComplexAndNotSpecial && j === 0 + }); + } + } + + reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context); + reduceComplexNonAdjacentCases(tokens, candidates, options, context); +} + +function wrappedSelectorsFrom(list) { + var wrapped = []; + + for (var i = 0; i < list.length; i++) { + wrapped.push([list[i][1]]); + } + + return wrapped; +} + +function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context) { + function filterOut(idx, bodies) { + return data[idx].isPartial && bodies.length === 0; + } + + function reduceBody(token, newBody, processedCount, tokenIdx) { + if (!data[processedCount - tokenIdx - 1].isPartial) + token[2] = newBody; + } + + for (var i = 0, l = repeated.length; i < l; i++) { + var selector = repeated[i]; + var data = candidates[selector]; + + reduceSelector(tokens, data, { + filterOut: filterOut, + callback: reduceBody + }, options, context); + } +} + +function reduceComplexNonAdjacentCases(tokens, candidates, options, context) { + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var localContext = {}; + + function filterOut(idx) { + return localContext.data[idx].where < localContext.intoPosition; + } + + function collectReducedBodies(token, newBody, processedCount, tokenIdx) { + if (tokenIdx === 0) + localContext.reducedBodies.push(newBody); + } + + allSelectors: + for (var complexSelector in candidates) { + var into = candidates[complexSelector]; + if (!into[0].isComplex) + continue; + + var intoPosition = into[into.length - 1].where; + var intoToken = tokens[intoPosition]; + var reducedBodies = []; + + var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements) ? + into[0].list : + [complexSelector]; + + localContext.intoPosition = intoPosition; + localContext.reducedBodies = reducedBodies; + + for (var j = 0, m = selectors.length; j < m; j++) { + var selector = selectors[j]; + var data = candidates[selector]; + + if (data.length < 2) + continue allSelectors; + + localContext.data = data; + + reduceSelector(tokens, data, { + filterOut: filterOut, + callback: collectReducedBodies + }, options, context); + + if (serializeBody(reducedBodies[reducedBodies.length - 1]) != serializeBody(reducedBodies[0])) + continue allSelectors; + } + + intoToken[2] = reducedBodies[0]; + } +} + +function reduceSelector(tokens, data, context, options, outerContext) { + var bodies = []; + var bodiesAsList = []; + var processedTokens = []; + + for (var j = data.length - 1; j >= 0; j--) { + if (context.filterOut(j, bodies)) + continue; + + var where = data[j].where; + var token = tokens[where]; + var clonedBody = cloneArray(token[2]); + + bodies = bodies.concat(clonedBody); + bodiesAsList.push(clonedBody); + processedTokens.push(where); + } + + optimizeProperties(bodies, true, false, outerContext); + + var processedCount = processedTokens.length; + var propertyIdx = bodies.length - 1; + var tokenIdx = processedCount - 1; + + while (tokenIdx >= 0) { + if ((tokenIdx === 0 || (bodies[propertyIdx] && bodiesAsList[tokenIdx].indexOf(bodies[propertyIdx]) > -1)) && propertyIdx > -1) { + propertyIdx--; + continue; + } + + var newBody = bodies.splice(propertyIdx + 1); + context.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx); + + tokenIdx--; + } +} + +module.exports = reduceNonAdjacent; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-font-at-rules.js b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-font-at-rules.js new file mode 100644 index 000000000..bc85d5d67 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-font-at-rules.js @@ -0,0 +1,30 @@ +var Token = require('../../tokenizer/token'); + +var serializeAll = require('../../writer/one-time').all; + +var FONT_FACE_SCOPE = '@font-face'; + +function removeDuplicateFontAtRules(tokens) { + var fontAtRules = []; + var token; + var key; + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + + if (token[0] != Token.AT_RULE_BLOCK && token[1][0][1] != FONT_FACE_SCOPE) { + continue; + } + + key = serializeAll([token]); + + if (fontAtRules.indexOf(key) > -1) { + token[2] = []; + } else { + fontAtRules.push(key); + } + } +} + +module.exports = removeDuplicateFontAtRules; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-media-queries.js b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-media-queries.js new file mode 100644 index 000000000..2c8f31d9d --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicate-media-queries.js @@ -0,0 +1,30 @@ +var Token = require('../../tokenizer/token'); + +var serializeAll = require('../../writer/one-time').all; +var serializeRules = require('../../writer/one-time').rules; + +function removeDuplicateMediaQueries(tokens) { + var candidates = {}; + var candidate; + var token; + var key; + var i, l; + + for (i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + if (token[0] != Token.NESTED_BLOCK) { + continue; + } + + key = serializeRules(token[1]) + '%' + serializeAll(token[2]); + candidate = candidates[key]; + + if (candidate) { + candidate[2] = []; + } + + candidates[key] = token; + } +} + +module.exports = removeDuplicateMediaQueries; diff --git a/node_modules/clean-css/lib/optimizer/level-2/remove-duplicates.js b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicates.js new file mode 100644 index 000000000..9aa6ace93 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/remove-duplicates.js @@ -0,0 +1,43 @@ +var Token = require('../../tokenizer/token'); + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +function removeDuplicates(tokens) { + var matched = {}; + var moreThanOnce = []; + var id, token; + var body, bodies; + + for (var i = 0, l = tokens.length; i < l; i++) { + token = tokens[i]; + if (token[0] != Token.RULE) + continue; + + id = serializeRules(token[1]); + + if (matched[id] && matched[id].length == 1) + moreThanOnce.push(id); + else + matched[id] = matched[id] || []; + + matched[id].push(i); + } + + for (i = 0, l = moreThanOnce.length; i < l; i++) { + id = moreThanOnce[i]; + bodies = []; + + for (var j = matched[id].length - 1; j >= 0; j--) { + token = tokens[matched[id][j]]; + body = serializeBody(token[2]); + + if (bodies.indexOf(body) > -1) + token[2] = []; + else + bodies.push(body); + } + } +} + +module.exports = removeDuplicates; diff --git a/node_modules/clean-css/lib/optimizer/level-2/reorderable.js b/node_modules/clean-css/lib/optimizer/level-2/reorderable.js new file mode 100644 index 000000000..4a3747a08 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/reorderable.js @@ -0,0 +1,93 @@ +// TODO: it'd be great to merge it with the other canReorder functionality + +var rulesOverlap = require('./rules-overlap'); +var specificitiesOverlap = require('./specificities-overlap'); + +var FLEX_PROPERTIES = /align\-items|box\-align|box\-pack|flex|justify/; +var BORDER_PROPERTIES = /^border\-(top|right|bottom|left|color|style|width|radius)/; + +function canReorder(left, right, cache) { + for (var i = right.length - 1; i >= 0; i--) { + for (var j = left.length - 1; j >= 0; j--) { + if (!canReorderSingle(left[j], right[i], cache)) + return false; + } + } + + return true; +} + +function canReorderSingle(left, right, cache) { + var leftName = left[0]; + var leftValue = left[1]; + var leftNameRoot = left[2]; + var leftSelector = left[5]; + var leftInSpecificSelector = left[6]; + var rightName = right[0]; + var rightValue = right[1]; + var rightNameRoot = right[2]; + var rightSelector = right[5]; + var rightInSpecificSelector = right[6]; + + if (leftName == 'font' && rightName == 'line-height' || rightName == 'font' && leftName == 'line-height') + return false; + if (FLEX_PROPERTIES.test(leftName) && FLEX_PROPERTIES.test(rightName)) + return false; + if (leftNameRoot == rightNameRoot && unprefixed(leftName) == unprefixed(rightName) && (vendorPrefixed(leftName) ^ vendorPrefixed(rightName))) + return false; + if (leftNameRoot == 'border' && BORDER_PROPERTIES.test(rightNameRoot) && (leftName == 'border' || leftName == rightNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName)))) + return false; + if (rightNameRoot == 'border' && BORDER_PROPERTIES.test(leftNameRoot) && (rightName == 'border' || rightName == leftNameRoot || (leftValue != rightValue && sameBorderComponent(leftName, rightName)))) + return false; + if (leftNameRoot == 'border' && rightNameRoot == 'border' && leftName != rightName && (isSideBorder(leftName) && isStyleBorder(rightName) || isStyleBorder(leftName) && isSideBorder(rightName))) + return false; + if (leftNameRoot != rightNameRoot) + return true; + if (leftName == rightName && leftNameRoot == rightNameRoot && (leftValue == rightValue || withDifferentVendorPrefix(leftValue, rightValue))) + return true; + if (leftName != rightName && leftNameRoot == rightNameRoot && leftName != leftNameRoot && rightName != rightNameRoot) + return true; + if (leftName != rightName && leftNameRoot == rightNameRoot && leftValue == rightValue) + return true; + if (rightInSpecificSelector && leftInSpecificSelector && !inheritable(leftNameRoot) && !inheritable(rightNameRoot) && !rulesOverlap(rightSelector, leftSelector, false)) + return true; + if (!specificitiesOverlap(leftSelector, rightSelector, cache)) + return true; + + return false; +} + +function vendorPrefixed(name) { + return /^\-(?:moz|webkit|ms|o)\-/.test(name); +} + +function unprefixed(name) { + return name.replace(/^\-(?:moz|webkit|ms|o)\-/, ''); +} + +function sameBorderComponent(name1, name2) { + return name1.split('-').pop() == name2.split('-').pop(); +} + +function isSideBorder(name) { + return name == 'border-top' || name == 'border-right' || name == 'border-bottom' || name == 'border-left'; +} + +function isStyleBorder(name) { + return name == 'border-color' || name == 'border-style' || name == 'border-width'; +} + +function withDifferentVendorPrefix(value1, value2) { + return vendorPrefixed(value1) && vendorPrefixed(value2) && value1.split('-')[1] != value2.split('-')[2]; +} + +function inheritable(name) { + // According to http://www.w3.org/TR/CSS21/propidx.html + // Others will be catched by other, preceeding rules + return name == 'font' || name == 'line-height' || name == 'list-style'; +} + +module.exports = { + canReorder: canReorder, + canReorderSingle: canReorderSingle +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restore-with-components.js b/node_modules/clean-css/lib/optimizer/level-2/restore-with-components.js new file mode 100644 index 000000000..caf7c4c94 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/restore-with-components.js @@ -0,0 +1,13 @@ +var compactable = require('./compactable'); + +function restoreWithComponents(property) { + var descriptor = compactable[property.name]; + + if (descriptor && descriptor.shorthand) { + return descriptor.restore(property, compactable); + } else { + return property.value; + } +} + +module.exports = restoreWithComponents; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restore.js b/node_modules/clean-css/lib/optimizer/level-2/restore.js new file mode 100644 index 000000000..149688c5b --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/restore.js @@ -0,0 +1,233 @@ +var shallowClone = require('./clone').shallow; + +var Token = require('../../tokenizer/token'); +var Marker = require('../../tokenizer/marker'); + +function isInheritOnly(values) { + for (var i = 0, l = values.length; i < l; i++) { + var value = values[i][1]; + + if (value != 'inherit' && value != Marker.COMMA && value != Marker.FORWARD_SLASH) + return false; + } + + return true; +} + +function background(property, compactable, lastInMultiplex) { + var components = property.components; + var restored = []; + var needsOne, needsBoth; + + function restoreValue(component) { + Array.prototype.unshift.apply(restored, component.value); + } + + function isDefaultValue(component) { + var descriptor = compactable[component.name]; + + if (descriptor.doubleValues && descriptor.defaultValue.length == 1) { + return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] == descriptor.defaultValue[0] : true); + } else if (descriptor.doubleValues && descriptor.defaultValue.length != 1) { + return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] : component.value[0][1]) == descriptor.defaultValue[1]; + } else { + return component.value[0][1] == descriptor.defaultValue; + } + } + + for (var i = components.length - 1; i >= 0; i--) { + var component = components[i]; + var isDefault = isDefaultValue(component); + + if (component.name == 'background-clip') { + var originComponent = components[i - 1]; + var isOriginDefault = isDefaultValue(originComponent); + + needsOne = component.value[0][1] == originComponent.value[0][1]; + + needsBoth = !needsOne && ( + (isOriginDefault && !isDefault) || + (!isOriginDefault && !isDefault) || + (!isOriginDefault && isDefault && component.value[0][1] != originComponent.value[0][1])); + + if (needsOne) { + restoreValue(originComponent); + } else if (needsBoth) { + restoreValue(component); + restoreValue(originComponent); + } + + i--; + } else if (component.name == 'background-size') { + var positionComponent = components[i - 1]; + var isPositionDefault = isDefaultValue(positionComponent); + + needsOne = !isPositionDefault && isDefault; + + needsBoth = !needsOne && + (isPositionDefault && !isDefault || !isPositionDefault && !isDefault); + + if (needsOne) { + restoreValue(positionComponent); + } else if (needsBoth) { + restoreValue(component); + restored.unshift([Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]); + restoreValue(positionComponent); + } else if (positionComponent.value.length == 1) { + restoreValue(positionComponent); + } + + i--; + } else { + if (isDefault || compactable[component.name].multiplexLastOnly && !lastInMultiplex) + continue; + + restoreValue(component); + } + } + + if (restored.length === 0 && property.value.length == 1 && property.value[0][1] == '0') + restored.push(property.value[0]); + + if (restored.length === 0) + restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]); + + if (isInheritOnly(restored)) + return [restored[0]]; + + return restored; +} + +function borderRadius(property, compactable) { + if (property.multiplex) { + var horizontal = shallowClone(property); + var vertical = shallowClone(property); + + for (var i = 0; i < 4; i++) { + var component = property.components[i]; + + var horizontalComponent = shallowClone(property); + horizontalComponent.value = [component.value[0]]; + horizontal.components.push(horizontalComponent); + + var verticalComponent = shallowClone(property); + // FIXME: only shorthand compactor (see breakup#borderRadius) knows that border radius + // longhands have two values, whereas tokenizer does not care about populating 2nd value + // if it's missing, hence this fallback + verticalComponent.value = [component.value[1] || component.value[0]]; + vertical.components.push(verticalComponent); + } + + var horizontalValues = fourValues(horizontal, compactable); + var verticalValues = fourValues(vertical, compactable); + + if (horizontalValues.length == verticalValues.length && + horizontalValues[0][1] == verticalValues[0][1] && + (horizontalValues.length > 1 ? horizontalValues[1][1] == verticalValues[1][1] : true) && + (horizontalValues.length > 2 ? horizontalValues[2][1] == verticalValues[2][1] : true) && + (horizontalValues.length > 3 ? horizontalValues[3][1] == verticalValues[3][1] : true)) { + return horizontalValues; + } else { + return horizontalValues.concat([[Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]]).concat(verticalValues); + } + } else { + return fourValues(property, compactable); + } +} + +function fourValues(property) { + var components = property.components; + var value1 = components[0].value[0]; + var value2 = components[1].value[0]; + var value3 = components[2].value[0]; + var value4 = components[3].value[0]; + + if (value1[1] == value2[1] && value1[1] == value3[1] && value1[1] == value4[1]) { + return [value1]; + } else if (value1[1] == value3[1] && value2[1] == value4[1]) { + return [value1, value2]; + } else if (value2[1] == value4[1]) { + return [value1, value2, value3]; + } else { + return [value1, value2, value3, value4]; + } +} + +function multiplex(restoreWith) { + return function (property, compactable) { + if (!property.multiplex) + return restoreWith(property, compactable, true); + + var multiplexSize = 0; + var restored = []; + var componentMultiplexSoFar = {}; + var i, l; + + // At this point we don't know what's the multiplex size, e.g. how many background layers are there + for (i = 0, l = property.components[0].value.length; i < l; i++) { + if (property.components[0].value[i][1] == Marker.COMMA) + multiplexSize++; + } + + for (i = 0; i <= multiplexSize; i++) { + var _property = shallowClone(property); + + // We split multiplex into parts and restore them one by one + for (var j = 0, m = property.components.length; j < m; j++) { + var componentToClone = property.components[j]; + var _component = shallowClone(componentToClone); + _property.components.push(_component); + + // The trick is some properties has more than one value, so we iterate over values looking for + // a multiplex separator - a comma + for (var k = componentMultiplexSoFar[_component.name] || 0, n = componentToClone.value.length; k < n; k++) { + if (componentToClone.value[k][1] == Marker.COMMA) { + componentMultiplexSoFar[_component.name] = k + 1; + break; + } + + _component.value.push(componentToClone.value[k]); + } + } + + // No we can restore shorthand value + var lastInMultiplex = i == multiplexSize; + var _restored = restoreWith(_property, compactable, lastInMultiplex); + Array.prototype.push.apply(restored, _restored); + + if (i < multiplexSize) + restored.push([Token.PROPERTY_VALUE, Marker.COMMA]); + } + + return restored; + }; +} + +function withoutDefaults(property, compactable) { + var components = property.components; + var restored = []; + + for (var i = components.length - 1; i >= 0; i--) { + var component = components[i]; + var descriptor = compactable[component.name]; + + if (component.value[0][1] != descriptor.defaultValue) + restored.unshift(component.value[0]); + } + + if (restored.length === 0) + restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]); + + if (isInheritOnly(restored)) + return [restored[0]]; + + return restored; +} + +module.exports = { + background: background, + borderRadius: borderRadius, + fourValues: fourValues, + multiplex: multiplex, + withoutDefaults: withoutDefaults +}; diff --git a/node_modules/clean-css/lib/optimizer/level-2/restructure.js b/node_modules/clean-css/lib/optimizer/level-2/restructure.js new file mode 100644 index 000000000..2635415ae --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/restructure.js @@ -0,0 +1,388 @@ +var canReorderSingle = require('./reorderable').canReorderSingle; +var extractProperties = require('./extract-properties'); +var isMergeable = require('./is-mergeable'); +var tidyRuleDuplicates = require('./tidy-rule-duplicates'); + +var Token = require('../../tokenizer/token'); + +var cloneArray = require('../../utils/clone-array'); + +var serializeBody = require('../../writer/one-time').body; +var serializeRules = require('../../writer/one-time').rules; + +function naturalSorter(a, b) { + return a > b ? 1 : -1; +} + +function cloneAndMergeSelectors(propertyA, propertyB) { + var cloned = cloneArray(propertyA); + cloned[5] = cloned[5].concat(propertyB[5]); + + return cloned; +} + +function restructure(tokens, context) { + var options = context.options; + var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses; + var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements; + var mergeLimit = 8191; + var specificityCache = context.cache.specificity; + var movableTokens = {}; + var movedProperties = []; + var multiPropertyMoveCache = {}; + var movedToBeDropped = []; + var maxCombinationsLevel = 2; + var ID_JOIN_CHARACTER = '%'; + + function sendToMultiPropertyMoveCache(position, movedProperty, allFits) { + for (var i = allFits.length - 1; i >= 0; i--) { + var fit = allFits[i][0]; + var id = addToCache(movedProperty, fit); + + if (multiPropertyMoveCache[id].length > 1 && processMultiPropertyMove(position, multiPropertyMoveCache[id])) { + removeAllMatchingFromCache(id); + break; + } + } + } + + function addToCache(movedProperty, fit) { + var id = cacheId(fit); + multiPropertyMoveCache[id] = multiPropertyMoveCache[id] || []; + multiPropertyMoveCache[id].push([movedProperty, fit]); + return id; + } + + function removeAllMatchingFromCache(matchId) { + var matchSelectors = matchId.split(ID_JOIN_CHARACTER); + var forRemoval = []; + var i; + + for (var id in multiPropertyMoveCache) { + var selectors = id.split(ID_JOIN_CHARACTER); + for (i = selectors.length - 1; i >= 0; i--) { + if (matchSelectors.indexOf(selectors[i]) > -1) { + forRemoval.push(id); + break; + } + } + } + + for (i = forRemoval.length - 1; i >= 0; i--) { + delete multiPropertyMoveCache[forRemoval[i]]; + } + } + + function cacheId(cachedTokens) { + var id = []; + for (var i = 0, l = cachedTokens.length; i < l; i++) { + id.push(serializeRules(cachedTokens[i][1])); + } + return id.join(ID_JOIN_CHARACTER); + } + + function tokensToMerge(sourceTokens) { + var uniqueTokensWithBody = []; + var mergeableTokens = []; + + for (var i = sourceTokens.length - 1; i >= 0; i--) { + if (!isMergeable(serializeRules(sourceTokens[i][1]), mergeablePseudoClasses, mergeablePseudoElements)) { + continue; + } + + mergeableTokens.unshift(sourceTokens[i]); + if (sourceTokens[i][2].length > 0 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1) + uniqueTokensWithBody.push(sourceTokens[i]); + } + + return uniqueTokensWithBody.length > 1 ? + mergeableTokens : + []; + } + + function shortenIfPossible(position, movedProperty) { + var name = movedProperty[0]; + var value = movedProperty[1]; + var key = movedProperty[4]; + var valueSize = name.length + value.length + 1; + var allSelectors = []; + var qualifiedTokens = []; + + var mergeableTokens = tokensToMerge(movableTokens[key]); + if (mergeableTokens.length < 2) + return; + + var allFits = findAllFits(mergeableTokens, valueSize, 1); + var bestFit = allFits[0]; + if (bestFit[1] > 0) + return sendToMultiPropertyMoveCache(position, movedProperty, allFits); + + for (var i = bestFit[0].length - 1; i >=0; i--) { + allSelectors = bestFit[0][i][1].concat(allSelectors); + qualifiedTokens.unshift(bestFit[0][i]); + } + + allSelectors = tidyRuleDuplicates(allSelectors); + dropAsNewTokenAt(position, [movedProperty], allSelectors, qualifiedTokens); + } + + function fitSorter(fit1, fit2) { + return fit1[1] > fit2[1] ? 1 : (fit1[1] == fit2[1] ? 0 : -1); + } + + function findAllFits(mergeableTokens, propertySize, propertiesCount) { + var combinations = allCombinations(mergeableTokens, propertySize, propertiesCount, maxCombinationsLevel - 1); + return combinations.sort(fitSorter); + } + + function allCombinations(tokensVariant, propertySize, propertiesCount, level) { + var differenceVariants = [[tokensVariant, sizeDifference(tokensVariant, propertySize, propertiesCount)]]; + if (tokensVariant.length > 2 && level > 0) { + for (var i = tokensVariant.length - 1; i >= 0; i--) { + var subVariant = Array.prototype.slice.call(tokensVariant, 0); + subVariant.splice(i, 1); + differenceVariants = differenceVariants.concat(allCombinations(subVariant, propertySize, propertiesCount, level - 1)); + } + } + + return differenceVariants; + } + + function sizeDifference(tokensVariant, propertySize, propertiesCount) { + var allSelectorsSize = 0; + for (var i = tokensVariant.length - 1; i >= 0; i--) { + allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? serializeRules(tokensVariant[i][1]).length : -1; + } + return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1; + } + + function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) { + var i, j, k, m; + var allProperties = []; + + for (i = mergeableTokens.length - 1; i >= 0; i--) { + var mergeableToken = mergeableTokens[i]; + + for (j = mergeableToken[2].length - 1; j >= 0; j--) { + var mergeableProperty = mergeableToken[2][j]; + + for (k = 0, m = properties.length; k < m; k++) { + var property = properties[k]; + + var mergeablePropertyName = mergeableProperty[1][1]; + var propertyName = property[0]; + var propertyBody = property[4]; + if (mergeablePropertyName == propertyName && serializeBody([mergeableProperty]) == propertyBody) { + mergeableToken[2].splice(j, 1); + break; + } + } + } + } + + for (i = properties.length - 1; i >= 0; i--) { + allProperties.unshift(properties[i][3]); + } + + var newToken = [Token.RULE, allSelectors, allProperties]; + tokens.splice(position, 0, newToken); + } + + function dropPropertiesAt(position, movedProperty) { + var key = movedProperty[4]; + var toMove = movableTokens[key]; + + if (toMove && toMove.length > 1) { + if (!shortenMultiMovesIfPossible(position, movedProperty)) + shortenIfPossible(position, movedProperty); + } + } + + function shortenMultiMovesIfPossible(position, movedProperty) { + var candidates = []; + var propertiesAndMergableTokens = []; + var key = movedProperty[4]; + var j, k; + + var mergeableTokens = tokensToMerge(movableTokens[key]); + if (mergeableTokens.length < 2) + return; + + movableLoop: + for (var value in movableTokens) { + var tokensList = movableTokens[value]; + + for (j = mergeableTokens.length - 1; j >= 0; j--) { + if (tokensList.indexOf(mergeableTokens[j]) == -1) + continue movableLoop; + } + + candidates.push(value); + } + + if (candidates.length < 2) + return false; + + for (j = candidates.length - 1; j >= 0; j--) { + for (k = movedProperties.length - 1; k >= 0; k--) { + if (movedProperties[k][4] == candidates[j]) { + propertiesAndMergableTokens.unshift([movedProperties[k], mergeableTokens]); + break; + } + } + } + + return processMultiPropertyMove(position, propertiesAndMergableTokens); + } + + function processMultiPropertyMove(position, propertiesAndMergableTokens) { + var valueSize = 0; + var properties = []; + var property; + + for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) { + property = propertiesAndMergableTokens[i][0]; + var fullValue = property[4]; + valueSize += fullValue.length + (i > 0 ? 1 : 0); + + properties.push(property); + } + + var mergeableTokens = propertiesAndMergableTokens[0][1]; + var bestFit = findAllFits(mergeableTokens, valueSize, properties.length)[0]; + if (bestFit[1] > 0) + return false; + + var allSelectors = []; + var qualifiedTokens = []; + for (i = bestFit[0].length - 1; i >= 0; i--) { + allSelectors = bestFit[0][i][1].concat(allSelectors); + qualifiedTokens.unshift(bestFit[0][i]); + } + + allSelectors = tidyRuleDuplicates(allSelectors); + dropAsNewTokenAt(position, properties, allSelectors, qualifiedTokens); + + for (i = properties.length - 1; i >= 0; i--) { + property = properties[i]; + var index = movedProperties.indexOf(property); + + delete movableTokens[property[4]]; + + if (index > -1 && movedToBeDropped.indexOf(index) == -1) + movedToBeDropped.push(index); + } + + return true; + } + + function boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) { + var propertyName = property[0]; + var movedPropertyName = movedProperty[0]; + if (propertyName != movedPropertyName) + return false; + + var key = movedProperty[4]; + var toMove = movableTokens[key]; + return toMove && toMove.indexOf(token) > -1; + } + + for (var i = tokens.length - 1; i >= 0; i--) { + var token = tokens[i]; + var isRule; + var j, k, m; + var samePropertyAt; + + if (token[0] == Token.RULE) { + isRule = true; + } else if (token[0] == Token.NESTED_BLOCK) { + isRule = false; + } else { + continue; + } + + // We cache movedProperties.length as it may change in the loop + var movedCount = movedProperties.length; + + var properties = extractProperties(token); + movedToBeDropped = []; + + var unmovableInCurrentToken = []; + for (j = properties.length - 1; j >= 0; j--) { + for (k = j - 1; k >= 0; k--) { + if (!canReorderSingle(properties[j], properties[k], specificityCache)) { + unmovableInCurrentToken.push(j); + break; + } + } + } + + for (j = properties.length - 1; j >= 0; j--) { + var property = properties[j]; + var movedSameProperty = false; + + for (k = 0; k < movedCount; k++) { + var movedProperty = movedProperties[k]; + + if (movedToBeDropped.indexOf(k) == -1 && (!canReorderSingle(property, movedProperty, specificityCache) && !boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) || + movableTokens[movedProperty[4]] && movableTokens[movedProperty[4]].length === mergeLimit)) { + dropPropertiesAt(i + 1, movedProperty, token); + + if (movedToBeDropped.indexOf(k) == -1) { + movedToBeDropped.push(k); + delete movableTokens[movedProperty[4]]; + } + } + + if (!movedSameProperty) { + movedSameProperty = property[0] == movedProperty[0] && property[1] == movedProperty[1]; + + if (movedSameProperty) { + samePropertyAt = k; + } + } + } + + if (!isRule || unmovableInCurrentToken.indexOf(j) > -1) + continue; + + var key = property[4]; + + if (movedSameProperty && movedProperties[samePropertyAt][5].length + property[5].length > mergeLimit) { + dropPropertiesAt(i + 1, movedProperties[samePropertyAt]); + movedProperties.splice(samePropertyAt, 1); + movableTokens[key] = [token]; + movedSameProperty = false; + } else { + movableTokens[key] = movableTokens[key] || []; + movableTokens[key].push(token); + } + + if (movedSameProperty) { + movedProperties[samePropertyAt] = cloneAndMergeSelectors(movedProperties[samePropertyAt], property); + } else { + movedProperties.push(property); + } + } + + movedToBeDropped = movedToBeDropped.sort(naturalSorter); + for (j = 0, m = movedToBeDropped.length; j < m; j++) { + var dropAt = movedToBeDropped[j] - j; + movedProperties.splice(dropAt, 1); + } + } + + var position = tokens[0] && tokens[0][0] == Token.AT_RULE && tokens[0][1].indexOf('@charset') === 0 ? 1 : 0; + for (; position < tokens.length - 1; position++) { + var isImportRule = tokens[position][0] === Token.AT_RULE && tokens[position][1].indexOf('@import') === 0; + var isComment = tokens[position][0] === Token.COMMENT; + if (!(isImportRule || isComment)) + break; + } + + for (i = 0; i < movedProperties.length; i++) { + dropPropertiesAt(position, movedProperties[i]); + } +} + +module.exports = restructure; diff --git a/node_modules/clean-css/lib/optimizer/level-2/rules-overlap.js b/node_modules/clean-css/lib/optimizer/level-2/rules-overlap.js new file mode 100644 index 000000000..811a517b2 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/rules-overlap.js @@ -0,0 +1,32 @@ +var MODIFIER_PATTERN = /\-\-.+$/; + +function rulesOverlap(rule1, rule2, bemMode) { + var scope1; + var scope2; + var i, l; + var j, m; + + for (i = 0, l = rule1.length; i < l; i++) { + scope1 = rule1[i][1]; + + for (j = 0, m = rule2.length; j < m; j++) { + scope2 = rule2[j][1]; + + if (scope1 == scope2) { + return true; + } + + if (bemMode && withoutModifiers(scope1) == withoutModifiers(scope2)) { + return true; + } + } + } + + return false; +} + +function withoutModifiers(scope) { + return scope.replace(MODIFIER_PATTERN, ''); +} + +module.exports = rulesOverlap; diff --git a/node_modules/clean-css/lib/optimizer/level-2/specificities-overlap.js b/node_modules/clean-css/lib/optimizer/level-2/specificities-overlap.js new file mode 100644 index 000000000..bde037446 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/specificities-overlap.js @@ -0,0 +1,34 @@ +var specificity = require('./specificity'); + +function specificitiesOverlap(selector1, selector2, cache) { + var specificity1; + var specificity2; + var i, l; + var j, m; + + for (i = 0, l = selector1.length; i < l; i++) { + specificity1 = findSpecificity(selector1[i][1], cache); + + for (j = 0, m = selector2.length; j < m; j++) { + specificity2 = findSpecificity(selector2[j][1], cache); + + if (specificity1[0] === specificity2[0] && specificity1[1] === specificity2[1] && specificity1[2] === specificity2[2]) { + return true; + } + } + } + + return false; +} + +function findSpecificity(selector, cache) { + var value; + + if (!(selector in cache)) { + cache[selector] = value = specificity(selector); + } + + return value || cache[selector]; +} + +module.exports = specificitiesOverlap; diff --git a/node_modules/clean-css/lib/optimizer/level-2/specificity.js b/node_modules/clean-css/lib/optimizer/level-2/specificity.js new file mode 100644 index 000000000..bbd224f15 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/specificity.js @@ -0,0 +1,77 @@ +var Marker = require('../../tokenizer/marker'); + +var Selector = { + ADJACENT_SIBLING: '+', + DESCENDANT: '>', + DOT: '.', + HASH: '#', + NON_ADJACENT_SIBLING: '~', + PSEUDO: ':' +}; + +var LETTER_PATTERN = /[a-zA-Z]/; +var NOT_PREFIX = ':not('; +var SEPARATOR_PATTERN = /[\s,\(>~\+]/; + +function specificity(selector) { + var result = [0, 0, 0]; + var character; + var isEscaped; + var isSingleQuoted; + var isDoubleQuoted; + var roundBracketLevel = 0; + var couldIntroduceNewTypeSelector; + var withinNotPseudoClass = false; + var wasPseudoClass = false; + var i, l; + + for (i = 0, l = selector.length; i < l; i++) { + character = selector[i]; + + if (isEscaped) { + // noop + } else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) { + isSingleQuoted = true; + } else if (character == Marker.SINGLE_QUOTE && !isDoubleQuoted && isSingleQuoted) { + isSingleQuoted = false; + } else if (character == Marker.DOUBLE_QUOTE && !isDoubleQuoted && !isSingleQuoted) { + isDoubleQuoted = true; + } else if (character == Marker.DOUBLE_QUOTE && isDoubleQuoted && !isSingleQuoted) { + isDoubleQuoted = false; + } else if (isSingleQuoted || isDoubleQuoted) { + continue; + } else if (roundBracketLevel > 0 && !withinNotPseudoClass) { + // noop + } else if (character == Marker.OPEN_ROUND_BRACKET) { + roundBracketLevel++; + } else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1) { + roundBracketLevel--; + withinNotPseudoClass = false; + } else if (character == Marker.CLOSE_ROUND_BRACKET) { + roundBracketLevel--; + } else if (character == Selector.HASH) { + result[0]++; + } else if (character == Selector.DOT || character == Marker.OPEN_SQUARE_BRACKET) { + result[1]++; + } else if (character == Selector.PSEUDO && !wasPseudoClass && !isNotPseudoClass(selector, i)) { + result[1]++; + withinNotPseudoClass = false; + } else if (character == Selector.PSEUDO) { + withinNotPseudoClass = true; + } else if ((i === 0 || couldIntroduceNewTypeSelector) && LETTER_PATTERN.test(character)) { + result[2]++; + } + + isEscaped = character == Marker.BACK_SLASH; + wasPseudoClass = character == Selector.PSEUDO; + couldIntroduceNewTypeSelector = !isEscaped && SEPARATOR_PATTERN.test(character); + } + + return result; +} + +function isNotPseudoClass(selector, index) { + return selector.indexOf(NOT_PREFIX, index) === index; +} + +module.exports = specificity; diff --git a/node_modules/clean-css/lib/optimizer/level-2/tidy-rule-duplicates.js b/node_modules/clean-css/lib/optimizer/level-2/tidy-rule-duplicates.js new file mode 100644 index 000000000..30a9c2177 --- /dev/null +++ b/node_modules/clean-css/lib/optimizer/level-2/tidy-rule-duplicates.js @@ -0,0 +1,21 @@ +function ruleSorter(s1, s2) { + return s1[1] > s2[1] ? 1 : -1; +} + +function tidyRuleDuplicates(rules) { + var list = []; + var repeated = []; + + for (var i = 0, l = rules.length; i < l; i++) { + var rule = rules[i]; + + if (repeated.indexOf(rule[1]) == -1) { + repeated.push(rule[1]); + list.push(rule); + } + } + + return list.sort(ruleSorter); +} + +module.exports = tidyRuleDuplicates; |