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