Restores compatibility in validator.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Sat, 11 Apr 2015 09:14:33 +0000 (10:14 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 12 Apr 2015 11:15:30 +0000 (12:15 +0100)
Fixes validator with proper OO as we need to handle state there to
keep a list of compatible units.

17 files changed:
lib/clean.js
lib/properties/break-up.js
lib/properties/can-override.js
lib/properties/optimizer.js
lib/properties/override-compactor.js
lib/properties/populate-components.js
lib/properties/validator.js
lib/selectors/optimizers/advanced.js
test/integration-test.js
test/properties/break-up-test.js
test/properties/longhand-overriding-test.js
test/properties/optimizer-test.js
test/properties/override-compacting-test.js
test/properties/restore-shorthands-test.js
test/properties/restore-test.js
test/selectors/optimizer-test.js
test/selectors/tokenizer-source-maps-test.js

index 2fa5563..a6bf97d 100644 (file)
@@ -21,6 +21,7 @@ var Compatibility = require('./utils/compatibility');
 var InputSourceMapTracker = require('./utils/input-source-map-tracker');
 var SourceTracker = require('./utils/source-tracker');
 var SourceReader = require('./utils/source-reader');
+var Validator = require('./properties/validator');
 
 var fs = require('fs');
 var path = require('path');
@@ -66,7 +67,8 @@ CleanCSS.prototype.minify = function(data, callback) {
     options: this.options,
     debug: this.options.debug,
     localOnly: !callback,
-    sourceTracker: new SourceTracker()
+    sourceTracker: new SourceTracker(),
+    validator: new Validator(this.options.compatibility)
   };
 
   if (context.options.sourceMap)
index e585d9a..49d77d3 100644 (file)
@@ -1,14 +1,17 @@
 var wrapSingle = require('./wrap-for-optimizing').single;
-var validator = require('./validator');
 
 var Splitter = require('../utils/splitter');
 
-function _colorFilter(value) {
-  return value[0] == 'invert' || validator.isValidColor(value[0]);
+function _colorFilter(validator) {
+  return function (value) {
+    return value[0] == 'invert' || validator.isValidColor(value[0]);
+  };
 }
 
-function _styleFilter(value) {
-  return value[0] != 'inherit' && validator.isValidStyle(value[0]);
+function _styleFilter(validator) {
+  return function (value) {
+    return value[0] != 'inherit' && validator.isValidStyle(value[0]);
+  };
 }
 
 function _wrapDefault(name, property, compactable) {
@@ -21,11 +24,13 @@ function _wrapDefault(name, property, compactable) {
     return wrapSingle([[name, property.important], [descriptor.defaultValue]]);
 }
 
-function _widthFilter(value) {
-  return value[0] != 'inherit' && validator.isValidWidth(value[0]);
+function _widthFilter(validator) {
+  return function (value) {
+    return value[0] != 'inherit' && validator.isValidWidth(value[0]);
+  };
 }
 
-function background(property, compactable) {
+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);
@@ -168,7 +173,7 @@ function fourValues(property, compactable) {
 }
 
 function multipleValues(splitWith) {
-  return function (property, compactable) {
+  return function (property, compactable, validator) {
     var splitsAt = [];
     var values = property.value;
     var i, j, l, m;
@@ -180,7 +185,7 @@ function multipleValues(splitWith) {
     }
 
     if (splitsAt.length === 0)
-      return splitWith(property, compactable);
+      return splitWith(property, compactable, validator);
 
     var splitComponents = [];
 
@@ -192,7 +197,7 @@ function multipleValues(splitWith) {
       var _property = _wrapDefault(property.name, property, compactable);
       _property.value = values.slice(from, to);
 
-      splitComponents.push(splitWith(_property, compactable));
+      splitComponents.push(splitWith(_property, compactable, validator));
     }
 
     var components = splitComponents[0];
@@ -211,7 +216,7 @@ function multipleValues(splitWith) {
   };
 }
 
-function listStyle(property, compactable) {
+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);
@@ -235,7 +240,7 @@ function listStyle(property, compactable) {
   return components;
 }
 
-function widthStyleColor(property, compactable) {
+function widthStyleColor(property, compactable, validator) {
   var descriptor = compactable[property.name];
   var components = [
     _wrapDefault(descriptor.components[0], property, compactable),
@@ -268,7 +273,7 @@ function widthStyleColor(property, compactable) {
   // so we'll try to parse it caring as little about order as possible
 
   if (values.length > 0) {
-    matches = values.filter(_widthFilter);
+    matches = values.filter(_widthFilter(validator));
     match = matches.length > 1 && matches[0] == 'none' ? matches[1] : matches[0];
     if (match) {
       width.value = [match];
@@ -277,7 +282,7 @@ function widthStyleColor(property, compactable) {
   }
 
   if (values.length > 0) {
-    match = values.filter(_styleFilter)[0];
+    match = values.filter(_styleFilter(validator))[0];
     if (match) {
       style.value = [match];
       values.splice(values.indexOf(match), 1);
@@ -285,7 +290,7 @@ function widthStyleColor(property, compactable) {
   }
 
   if (values.length > 0) {
-    match = values.filter(_colorFilter)[0];
+    match = values.filter(_colorFilter(validator))[0];
     if (match) {
       color.value = [match];
       values.splice(values.indexOf(match), 1);
index ffccfd2..eda0274 100644 (file)
@@ -1,5 +1,3 @@
-var validator = require('./validator');
-
 // Functions that decide what value can override what.
 // The main purpose is to disallow removing CSS fallbacks.
 // A separate implementation is needed for every different kind of CSS property.
@@ -16,7 +14,7 @@ function always() {
   return true;
 }
 
-function backgroundImage(property1, property2) {
+function backgroundImage(property1, property2, validator) {
   // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
   // Understandability: (none | url | inherit) > (same function) > (same value)
 
@@ -30,15 +28,15 @@ function backgroundImage(property1, property2) {
     return false;
 
   // Functions with the same name can override each other; same values can override each other
-  return sameFunctionOrValue(property1, property2);
+  return sameFunctionOrValue(property1, property2, validator);
 }
 
-function border(property1, property2) {
-  return color(property1.components[2], property2.components[2]);
+function border(property1, property2, validator) {
+  return color(property1.components[2], property2.components[2], validator);
 }
 
 // Use for color properties (color, background-color, border-color, etc.)
-function color(property1, property2) {
+function color(property1, property2, validator) {
   // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
   // Understandability: (hex | named) > (rgba | hsla) > (same function name) > anything else
   // NOTE: at this point rgb and hsl are replaced by hex values by clean-css
@@ -62,7 +60,7 @@ function color(property1, property2) {
   return sameFunctionOrValue(property1, property2);
 }
 
-function twoOptionalFunctions(property1, property2) {
+function twoOptionalFunctions(property1, property2, validator) {
   var value1 = _valueOf(property1);
   var value2 = _valueOf(property2);
 
@@ -76,7 +74,7 @@ function sameValue(property1, property2) {
   return value1 === value2;
 }
 
-function sameFunctionOrValue(property1, property2) {
+function sameFunctionOrValue(property1, property2, validator) {
   var value1 = _valueOf(property1);
   var value2 = _valueOf(property2);
 
@@ -88,7 +86,7 @@ function sameFunctionOrValue(property1, property2) {
 }
 
 // Use for properties containing CSS units (margin-top, padding-left, etc.)
-function unit(property1, property2) {
+function unit(property1, property2, validator) {
   // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
   // Understandability: (unit without functions) > (same functions | standard functions) > anything else
   // NOTE: there is no point in having different vendor-specific functions override each other or standard functions,
@@ -111,7 +109,7 @@ function unit(property1, property2) {
   }
 
   // Functions with the same name can override each other; same values can override each other
-  return sameFunctionOrValue(property1, property2);
+  return sameFunctionOrValue(property1, property2, validator);
 }
 
 module.exports = {
index ed3d33c..e55c568 100644 (file)
@@ -82,7 +82,7 @@ var shorthands = {
   '-webkit-transition-timing-function': ['-webkit-transition']
 };
 
-function _optimize(properties, mergeAdjacent, aggressiveMerging) {
+function _optimize(properties, mergeAdjacent, aggressiveMerging, validator) {
   var overrideMapping = {};
   var lastName = null;
   var j;
@@ -143,7 +143,7 @@ function _optimize(properties, mergeAdjacent, aggressiveMerging) {
         if (!wasImportant && (wasHack && !isHack || !wasHack && isHack))
           continue;
 
-        if (!wasHack && !isHack && !longhandToShorthand && canOverride && !canOverride(toRemove, property))
+        if (!wasHack && !isHack && !longhandToShorthand && canOverride && !canOverride(toRemove, property, validator))
           continue;
 
         if (wasImportant && !isImportant || wasImportant && isHack) {
@@ -180,13 +180,13 @@ function _optimize(properties, mergeAdjacent, aggressiveMerging) {
   }
 }
 
-function optimize(selector, properties, mergeAdjacent, withCompacting, options) {
+function optimize(selector, properties, mergeAdjacent, withCompacting, options, validator) {
   var _properties = wrapForOptimizing(properties);
-  populateComponents(_properties);
-  _optimize(_properties, mergeAdjacent, options.aggressiveMerging);
+  populateComponents(_properties, validator);
+  _optimize(_properties, mergeAdjacent, options.aggressiveMerging, validator);
 
   if (withCompacting && options.shorthandCompacting && !options.sourceMap) {
-    compactOverrides(_properties, options.compatibility);
+    compactOverrides(_properties, options.compatibility, validator);
     // compactShorthands(_properties, false, options.compatibility);
     // compactShorthands(_properties, true, options.compatibility);
   }
index 059a4c8..b15aee7 100644 (file)
@@ -15,7 +15,7 @@ function nameMatchFilter(to) {
   };
 }
 
-function wouldBreakCompatibility(property) {
+function wouldBreakCompatibility(property, validator) {
   for (var i = 0; i < property.components.length; i++) {
     var component = property.components[i];
     var descriptor = compactable[component.name];
@@ -24,7 +24,7 @@ function wouldBreakCompatibility(property) {
     var _component = shallowClone(component);
     _component.value = [[descriptor.defaultValue]];
 
-    if (!canOverride(_component, component))
+    if (!canOverride(_component, component, validator))
       return true;
   }
 
@@ -157,7 +157,7 @@ function wouldResultInLongerValue(left, right) {
   return lengthBefore < lengthAfter;
 }
 
-function compactOverrides(properties, compatibility) {
+function compactOverrides(properties, compatibility, validator) {
   var mayOverride, right, left, component;
   var i, j, k;
 
@@ -182,7 +182,7 @@ function compactOverrides(properties, compatibility) {
 
         component = right.components.filter(nameMatchFilter(left))[0];
         mayOverride = (compactable[left.name] && compactable[left.name].canOverride) || canOverride.sameValue;
-        if (mayOverride(left, component)) {
+        if (mayOverride(left, component, validator)) {
           left.unused = true;
         }
       } else if (left.shorthand && !right.shorthand && isComponentOf(left, right)) {
@@ -191,14 +191,14 @@ function compactOverrides(properties, compatibility) {
           continue;
 
         component = left.components.filter(nameMatchFilter(right))[0];
-        if (mayOverride(component, right)) {
+        if (mayOverride(component, right, validator)) {
           var disabledBackgroundSizeMerging = !compatibility.properties.backgroundSizeMerging && component.name.indexOf('background-size') > -1;
           var nonMergeableValue = compactable[right.name].nonMergeableValue === right.value[0][0];
 
           if (disabledBackgroundSizeMerging || nonMergeableValue)
             continue;
 
-          if (!compatibility.properties.merging && wouldBreakCompatibility(left))
+          if (!compatibility.properties.merging && wouldBreakCompatibility(left, validator))
             continue;
 
           if (component.value[0][0] != right.value[0][0] && (hasInherits(left) || hasInherits(right)))
@@ -231,7 +231,7 @@ function compactOverrides(properties, compatibility) {
           var rightComponent = right.components[k];
 
           mayOverride = compactable[leftComponent.name].canOverride || canOverride.sameValue;
-          if (!mayOverride(leftComponent, rightComponent) || !canOverride.twoOptionalFunctions(leftComponent, rightComponent))
+          if (!mayOverride(leftComponent, rightComponent, validator) || !canOverride.twoOptionalFunctions(leftComponent, rightComponent, validator))
             continue propertyLoop;
         }
 
index 8e4d203..9544d5a 100644 (file)
@@ -1,6 +1,6 @@
 var compactable = require('./compactable');
 
-function populateComponents(properties) {
+function populateComponents(properties, validator) {
   for (var i = properties.length - 1; i >= 0; i--) {
     var property = properties[i];
     var descriptor = compactable[property.name];
@@ -8,7 +8,7 @@ function populateComponents(properties) {
     if (descriptor && descriptor.shorthand) {
       property.shorthand = true;
       property.dirty = true;
-      property.components = descriptor.breakUp(property, compactable);
+      property.components = descriptor.breakUp(property, compactable, validator);
 
       if (property.components.length > 0)
         property.multiplex = Array.isArray(property.components[0].value[0][0]);
index b06b698..b995808 100644 (file)
@@ -30,86 +30,103 @@ var styleKeywords = ['auto', 'inherit', 'hidden', 'none', 'dotted', 'dashed', 's
 var listStyleTypeKeywords = ['armenian', 'circle', 'cjk-ideographic', 'decimal', 'decimal-leading-zero', 'disc', 'georgian', 'hebrew', 'hiragana', 'hiragana-iroha', 'inherit', 'katakana', 'katakana-iroha', 'lower-alpha', 'lower-greek', 'lower-latin', 'lower-roman', 'none', 'square', 'upper-alpha', 'upper-latin', 'upper-roman'];
 var listStylePositionKeywords = ['inside', 'outside', 'inherit'];
 
-// var compatibleCssUnitRegex;
-// var compatibleCssUnitAnyRegex;
-
-//   var validator = {
-//     // FIXME: we need a proper OO here
-//     setCompatibility: function (compatibility) {
-//       if (compatibility.units.rem) {
-//         compatibleCssUnitRegex = cssUnitRegex;
-//         compatibleCssUnitAnyRegex = cssUnitAnyRegex;
-//         return;
-//       }
-
-//       var validUnits = allUnits.slice(0).filter(function (value) {
-//         return value != 'rem';
-//       });
-
-//       var compatibleCssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + ')|auto|inherit)';
-//       compatibleCssUnitRegex = new RegExp('^' + compatibleCssUnitRegexStr + '$', 'i');
-//       compatibleCssUnitAnyRegex = new RegExp('^(none|' + widthKeywords.join('|') + '|' + compatibleCssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')$', 'i');
-//     }
-
-function isValidHexColor(s) {
-  return (s.length === 4 || s.length === 7) && s[0] === '#';
+function Validator(compatibility) {
+  if (compatibility.units.rem) {
+    this.compatibleCssUnitRegex = cssUnitRegex;
+    this.compatibleCssUnitAnyRegex = cssUnitAnyRegex;
+  } else {
+    var validUnits = allUnits.slice(0).filter(function (value) {
+      return value != 'rem';
+    });
+
+    var compatibleCssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + ')|auto|inherit)';
+    this.compatibleCssUnitRegex = new RegExp('^' + compatibleCssUnitRegexStr + '$', 'i');
+    this.compatibleCssUnitAnyRegex = new RegExp('^(none|' + widthKeywords.join('|') + '|' + compatibleCssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')$', 'i');
+  }
 }
-function isValidRgbaColor(s) {
+
+Validator.prototype.isValidHexColor = function (s) {
+  return (s.length === 4 || s.length === 7) && s[0] === '#';
+};
+
+Validator.prototype.isValidRgbaColor = function (s) {
   s = s.split(' ').join('');
   return s.length > 0 && s.indexOf('rgba(') === 0 && s.indexOf(')') === s.length - 1;
-}
-function isValidHslaColor(s) {
+};
+
+Validator.prototype.isValidHslaColor = function (s) {
   s = s.split(' ').join('');
   return s.length > 0 && s.indexOf('hsla(') === 0 && s.indexOf(')') === s.length - 1;
-}
-function isValidNamedColor(s) {
+};
+
+Validator.prototype.isValidNamedColor = function (s) {
   // We don't really check if it's a valid color value, but allow any letters in it
   return s !== 'auto' && (s === 'transparent' || s === 'inherit' || /^[a-zA-Z]+$/.test(s));
-}
-function isValidVariable(s) {
+};
+
+Validator.prototype.isValidVariable = function (s) {
   return cssVariableRegex.test(s);
-}
-function isValidColor(s) {
-  return isValidNamedColor(s) || isValidHexColor(s) || isValidRgbaColor(s) || isValidHslaColor(s) || isValidVariable(s) || isValidVendorPrefixedValue(s);
-}
-function isValidUrl(s) {
+};
+
+Validator.prototype.isValidColor = function (s) {
+  return this.isValidNamedColor(s) ||
+    this.isValidHexColor(s) ||
+    this.isValidRgbaColor(s) ||
+    this.isValidHslaColor(s) ||
+    this.isValidVariable(s) ||
+    this.isValidVendorPrefixedValue(s);
+};
+
+Validator.prototype.isValidUrl = function (s) {
   // NOTE: at this point all URLs are replaced with placeholders by clean-css, so we check for those placeholders
   return s.indexOf('__ESCAPED_URL_CLEAN_CSS') === 0;
-}
-function isValidUnit(s) {
-  return cssUnitAnyRegex.test(s);
-}
-function isValidUnitWithoutFunction(s) {
-  return cssUnitRegex.test(s);
-}
-function isValidAndCompatibleUnit(s) {
+};
+
+Validator.prototype.isValidUnit = function (s) {
   return cssUnitAnyRegex.test(s);
-}
-function isValidAndCompatibleUnitWithoutFunction(s) {
+};
+
+Validator.prototype.isValidUnitWithoutFunction = function (s) {
   return cssUnitRegex.test(s);
-}
-function isValidFunctionWithoutVendorPrefix(s) {
+};
+
+Validator.prototype.isValidAndCompatibleUnit = function (s) {
+  return this.compatibleCssUnitAnyRegex.test(s);
+};
+
+Validator.prototype.isValidAndCompatibleUnitWithoutFunction = function (s) {
+  return this.compatibleCssUnitRegex.test(s);
+};
+
+Validator.prototype.isValidFunctionWithoutVendorPrefix = function (s) {
   return cssFunctionNoVendorRegex.test(s);
-}
-function isValidFunctionWithVendorPrefix(s) {
+};
+
+Validator.prototype.isValidFunctionWithVendorPrefix = function (s) {
   return cssFunctionVendorRegex.test(s);
-}
-function isValidFunction(s) {
+};
+
+Validator.prototype.isValidFunction = function (s) {
   return cssFunctionAnyRegex.test(s);
-}
-function isValidBackgroundRepeat(s) {
-  return backgroundRepeatKeywords.indexOf(s) >= 0 || isValidVariable(s);
-}
-function isValidBackgroundAttachment(s) {
-  return backgroundAttachmentKeywords.indexOf(s) >= 0 || isValidVariable(s);
-}
-function isValidBackgroundBox(s) {
-  return backgroundBoxKeywords.indexOf(s) >= 0 || isValidVariable(s);
-}
-function isValidBackgroundPositionPart(s) {
-  return backgroundPositionKeywords.indexOf(s) >= 0 || cssUnitOrCalcRegex.test(s) || isValidVariable(s);
-}
-function isValidBackgroundPosition(s) {
+};
+
+Validator.prototype.isValidBackgroundRepeat = function (s) {
+  return backgroundRepeatKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundAttachment = function (s) {
+  return backgroundAttachmentKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundBox = function (s) {
+  return backgroundBoxKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundPositionPart = function (s) {
+  return backgroundPositionKeywords.indexOf(s) >= 0 || cssUnitOrCalcRegex.test(s) || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundPosition = function (s) {
   if (s === 'inherit')
     return true;
 
@@ -117,75 +134,55 @@ function isValidBackgroundPosition(s) {
   for (var i = 0, l = parts.length; i < l; i++) {
     if (parts[i] === '')
       continue;
-    if (isValidBackgroundPositionPart(parts[i]) || isValidVariable(parts[i]))
+    if (this.isValidBackgroundPositionPart(parts[i]) || this.isValidVariable(parts[i]))
       continue;
 
     return false;
   }
 
   return true;
-}
-function isValidBackgroundSizePart(s) {
-  return backgroundSizeKeywords.indexOf(s) >= 0 || cssUnitRegex.test(s) || isValidVariable(s);
-}
-function isValidBackgroundPositionAndSize(s) {
+};
+
+Validator.prototype.isValidBackgroundSizePart = function (s) {
+  return backgroundSizeKeywords.indexOf(s) >= 0 || cssUnitRegex.test(s) || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundPositionAndSize = function (s) {
   if (s.indexOf('/') < 0)
     return false;
 
   var twoParts = new Splitter('/').split(s);
-  return isValidBackgroundSizePart(twoParts.pop()) && isValidBackgroundPositionPart(twoParts.pop());
-}
-function isValidListStyleType(s) {
-  return listStyleTypeKeywords.indexOf(s) >= 0 || isValidVariable(s);
-}
-function isValidListStylePosition(s) {
-  return listStylePositionKeywords.indexOf(s) >= 0 || isValidVariable(s);
-}
-function isValidStyle(s) {
-  return styleKeywords.indexOf(s) >= 0 || isValidVariable(s);
-}
-function isValidWidth(s) {
-  return isValidUnit(s) || widthKeywords.indexOf(s) >= 0 || isValidVariable(s);
-}
-function isValidVendorPrefixedValue(s) {
+  return this.isValidBackgroundSizePart(twoParts.pop()) && this.isValidBackgroundPositionPart(twoParts.pop());
+};
+
+Validator.prototype.isValidListStyleType = function (s) {
+  return listStyleTypeKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidListStylePosition = function (s) {
+  return listStylePositionKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidStyle = function (s) {
+  return styleKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidWidth = function (s) {
+  return this.isValidUnit(s) || widthKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidVendorPrefixedValue = function (s) {
   return /^-([A-Za-z0-9]|-)*$/gi.test(s);
-}
-function areSameFunction(a, b) {
-  if (!isValidFunction(a) || !isValidFunction(b))
+};
+
+Validator.prototype.areSameFunction = function (a, b) {
+  if (!this.isValidFunction(a) || !this.isValidFunction(b))
     return false;
 
   var f1name = a.substring(0, a.indexOf('('));
   var f2name = b.substring(0, b.indexOf('('));
 
   return f1name === f2name;
-}
-
-module.exports = {
-  isValidHexColor: isValidHexColor,
-  isValidRgbaColor: isValidRgbaColor,
-  isValidHslaColor: isValidHslaColor,
-  isValidNamedColor: isValidNamedColor,
-  isValidVariable: isValidVariable,
-  isValidColor: isValidColor,
-  isValidUrl: isValidUrl,
-  isValidUnit: isValidUnit,
-  isValidUnitWithoutFunction: isValidUnitWithoutFunction,
-  isValidAndCompatibleUnit: isValidAndCompatibleUnit,
-  isValidAndCompatibleUnitWithoutFunction: isValidAndCompatibleUnitWithoutFunction,
-  isValidFunctionWithoutVendorPrefix: isValidFunctionWithoutVendorPrefix,
-  isValidFunctionWithVendorPrefix: isValidFunctionWithVendorPrefix,
-  isValidFunction: isValidFunction,
-  isValidBackgroundRepeat: isValidBackgroundRepeat,
-  isValidBackgroundAttachment: isValidBackgroundAttachment,
-  isValidBackgroundBox: isValidBackgroundBox,
-  isValidBackgroundPositionPart: isValidBackgroundPositionPart,
-  isValidBackgroundPosition: isValidBackgroundPosition,
-  isValidBackgroundSizePart: isValidBackgroundSizePart,
-  isValidBackgroundPositionAndSize: isValidBackgroundPositionAndSize,
-  isValidListStyleType: isValidListStyleType,
-  isValidListStylePosition: isValidListStylePosition,
-  isValidStyle: isValidStyle,
-  isValidWidth: isValidWidth,
-  isValidVendorPrefixedValue: isValidVendorPrefixedValue,
-  areSameFunction: areSameFunction
 };
+
+module.exports = Validator;
index 923b6e5..1df8946 100644 (file)
@@ -7,8 +7,9 @@ var canReorderSingle = require('../reorderable').canReorderSingle;
 var stringifyBody = require('../../stringifier/one-time').body;
 var stringifySelectors = require('../../stringifier/one-time').selectors;
 
-function AdvancedOptimizer(options) {
+function AdvancedOptimizer(options, context) {
   this.options = options;
+  this.validator = context.validator;
 }
 
 function unsafeSelector(value) {
@@ -75,7 +76,7 @@ AdvancedOptimizer.prototype.mergeAdjacent = function (tokens) {
     if (lastToken[0] == 'selector' && stringifySelectors(token[1]) == stringifySelectors(lastToken[1])) {
       var joinAt = [lastToken[2].length];
       Array.prototype.push.apply(lastToken[2], token[2]);
-      optimizeProperties(token[1], lastToken[2], joinAt, true, this.options);
+      optimizeProperties(token[1], lastToken[2], joinAt, true, this.options, this.validator);
       token[2] = [];
     } else if (lastToken[0] == 'selector' && stringifyBody(token[2]) == stringifyBody(lastToken[2]) &&
         !this.isSpecial(stringifySelectors(token[1])) && !this.isSpecial(stringifySelectors(lastToken[1]))) {
@@ -221,7 +222,7 @@ AdvancedOptimizer.prototype.reduceSelector = function (tokens, selector, data, o
       joinsAt.push((joinsAt[j - 1] || 0) + bodiesAsList[j].length);
   }
 
-  optimizeProperties(selector, bodies, joinsAt, false, this.options);
+  optimizeProperties(selector, bodies, joinsAt, false, this.options, this.validator);
 
   var processedCount = processedTokens.length;
   var propertyIdx = bodies.length - 1;
@@ -301,7 +302,7 @@ AdvancedOptimizer.prototype.mergeNonAdjacentBySelector = function (tokens) {
           Array.prototype.push.apply(target[2], moved[2]);
         }
 
-        optimizeProperties(target[1], target[2], joinAt, true, this.options);
+        optimizeProperties(target[1], target[2], joinAt, true, this.options, this.validator);
         moved[2] = [];
       }
     }
@@ -683,16 +684,16 @@ AdvancedOptimizer.prototype.removeEmpty = function (tokens) {
   }
 };
 
-function recursivelyOptimizeProperties(tokens, options) {
+function recursivelyOptimizeProperties(tokens, options, validator) {
   for (var i = 0, l = tokens.length; i < l; i++) {
     var token = tokens[i];
 
     switch (token[0]) {
       case 'selector':
-        optimizeProperties(token[1], token[2], false, true, options);
+        optimizeProperties(token[1], token[2], false, true, options, validator);
         break;
       case 'block':
-        recursivelyOptimizeProperties(token[2], options);
+        recursivelyOptimizeProperties(token[2], options, validator);
     }
   }
 }
@@ -708,7 +709,7 @@ AdvancedOptimizer.prototype.optimize = function (tokens) {
       }
     });
 
-    recursivelyOptimizeProperties(tokens, self.options);
+    recursivelyOptimizeProperties(tokens, self.options, self.validator);
 
     self.removeDuplicates(tokens);
     self.mergeAdjacent(tokens);
index a0077a1..0bd16b3 100644 (file)
@@ -1859,7 +1859,7 @@ title']{display:block}",
     ]
   }, { compatibility: { selectors: { adjacentSpace: true } } }),
   'units - IE8 compatibility': cssContext({
-    'rems': 'div{padding-top:16px;padding-top:1rem}'
+    'rems': 'div{padding-top:16px;color:red;padding-top:1rem}'
   }, { compatibility: 'ie8' }),
   'redefined more granular properties with property merging': cssContext({
     'should merge background with background-attachment': [
index dd39ada..e3f1cfd 100644 (file)
@@ -3,12 +3,15 @@ var assert = require('assert');
 
 var wrapForOptimizing = require('../../lib/properties/wrap-for-optimizing').all;
 var populateComponents = require('../../lib/properties/populate-components');
+var Validator = require('../../lib/properties/validator');
+var Compatibility = require('../../lib/utils/compatibility');
 
 var breakUp = require('../../lib/properties/break-up');
 
 function _breakUp(properties) {
+  var validator = new Validator(new Compatibility().toOptions());
   var wrapped = wrapForOptimizing(properties);
-  populateComponents(wrapped);
+  populateComponents(wrapped, validator);
 
   return wrapped[0].components;
 }
index e4990a8..001311e 100644 (file)
@@ -6,6 +6,7 @@ var optimize = require('../../lib/properties/optimizer');
 var Tokenizer = require('../../lib/selectors/tokenizer');
 var SourceTracker = require('../../lib/utils/source-tracker');
 var Compatibility = require('../../lib/utils/compatibility');
+var Validator = require('../../lib/properties/validator');
 var addOptimizationMetadata = require('../../lib/selectors/optimization-metadata');
 
 function _optimize(source) {
@@ -18,7 +19,8 @@ function _optimize(source) {
   addOptimizationMetadata(tokens);
 
   var compatibility = new Compatibility().toOptions();
-  optimize(tokens[0][1], tokens[0][2], false, true, { compatibility: compatibility, aggressiveMerging: true, shorthandCompacting: true });
+  var validator = new Validator(compatibility);
+  optimize(tokens[0][1], tokens[0][2], false, true, { compatibility: compatibility, aggressiveMerging: true, shorthandCompacting: true }, validator);
 
   return tokens[0][2];
 }
index ca3b8ac..4fca81e 100644 (file)
@@ -6,9 +6,11 @@ var optimize = require('../../lib/properties/optimizer');
 var Tokenizer = require('../../lib/selectors/tokenizer');
 var SourceTracker = require('../../lib/utils/source-tracker');
 var Compatibility = require('../../lib/utils/compatibility');
+var Validator = require('../../lib/properties/validator');
 var addOptimizationMetadata = require('../../lib/selectors/optimization-metadata');
 
 var compatibility = new Compatibility().toOptions();
+var validator = new Validator(compatibility);
 
 function _optimize(source, mergeAdjacent, aggressiveMerging) {
   var tokens = new Tokenizer({
@@ -18,7 +20,7 @@ function _optimize(source, mergeAdjacent, aggressiveMerging) {
   }).toTokens(source);
 
   addOptimizationMetadata(tokens);
-  optimize(tokens[0][1], tokens[0][2], mergeAdjacent, true, { compatibility: compatibility, aggressiveMerging: aggressiveMerging });
+  optimize(tokens[0][1], tokens[0][2], mergeAdjacent, true, { compatibility: compatibility, aggressiveMerging: aggressiveMerging }, validator);
 
   return tokens[0][2];
 }
index 616a017..d935225 100644 (file)
@@ -6,6 +6,7 @@ var optimize = require('../../lib/properties/optimizer');
 var Tokenizer = require('../../lib/selectors/tokenizer');
 var SourceTracker = require('../../lib/utils/source-tracker');
 var Compatibility = require('../../lib/utils/compatibility');
+var Validator = require('../../lib/properties/validator');
 var addOptimizationMetadata = require('../../lib/selectors/optimization-metadata');
 
 function _optimize(source, compatibility, aggressiveMerging) {
@@ -16,13 +17,14 @@ function _optimize(source, compatibility, aggressiveMerging) {
   }).toTokens(source);
   compatibility = new Compatibility(compatibility).toOptions();
 
+  var validator = new Validator(compatibility);
   var options = {
     aggressiveMerging: undefined === aggressiveMerging ? true : aggressiveMerging,
     compatibility: compatibility,
     shorthandCompacting: true
   };
   addOptimizationMetadata(tokens);
-  optimize(tokens[0][1], tokens[0][2], false, true, options);
+  optimize(tokens[0][1], tokens[0][2], false, true, options, validator);
 
   return tokens[0][2];
 }
@@ -310,7 +312,7 @@ vows.describe(optimize)
         ]);
       }
     },
-    'multiplex longhand into multiplex shorthand123': {
+    'multiplex longhand into multiplex shorthand': {
       'topic': 'p{background:no-repeat,no-repeat;background-position:top left,bottom left}',
       'into': function (topic) {
         assert.deepEqual(_optimize(topic), [
index 7a85254..c7ede63 100644 (file)
@@ -7,13 +7,18 @@ var shallowClone = require('../../lib/properties/clone').shallow;
 
 var restoreShorthands = require('../../lib/properties/restore-shorthands');
 
+var Compatibility = require('../../lib/utils/compatibility');
+var Validator = require('../../lib/properties/validator');
+
+var validator = new Validator(new Compatibility().toOptions());
+
 vows.describe(restoreShorthands)
   .addBatch({
     'longhands': {
       'topic': function () {
         var properties = ['/*comment */', [['margin-top', false, false], ['0']]];
         var _properties = wrapForOptimizing(properties);
-        populateComponents(_properties);
+        populateComponents(_properties, validator);
         restoreShorthands(_properties);
 
         return properties;
@@ -26,7 +31,7 @@ vows.describe(restoreShorthands)
       'topic': function () {
         var properties = ['/*comment */', [['background', false, false], ['url(image.png)']]];
         var _properties = wrapForOptimizing(properties);
-        populateComponents(_properties);
+        populateComponents(_properties, validator);
 
         properties[1].pop();
         _properties[0].dirty = true;
@@ -42,7 +47,7 @@ vows.describe(restoreShorthands)
       'topic': function () {
         var properties = [[['background', false, false], ['url(image.png)']]];
         var _properties = wrapForOptimizing(properties);
-        populateComponents(_properties);
+        populateComponents(_properties, validator);
 
         _properties[0].value = [];
         _properties[0].dirty = true;
@@ -58,7 +63,7 @@ vows.describe(restoreShorthands)
       'topic': function () {
         var properties = [[['background', false, false], ['url(image.png)']]];
         var _properties = wrapForOptimizing(properties);
-        populateComponents(_properties);
+        populateComponents(_properties, validator);
 
         var cloned = shallowClone(_properties[0]);
         cloned.components = _properties[0].components;
index 2d3f982..9d1202f 100644 (file)
@@ -3,13 +3,16 @@ var assert = require('assert');
 
 var wrapForOptimizing = require('../../lib/properties/wrap-for-optimizing').single;
 var compactable = require('../../lib/properties/compactable');
+var Compatibility = require('../../lib/utils/compatibility');
+var Validator = require('../../lib/properties/validator');
 
 var restore = require('../../lib/properties/restore');
 
 function _breakUp(property) {
+  var validator = new Validator(new Compatibility().toOptions());
   var descriptor = compactable[property[0][0]];
   var _property = wrapForOptimizing(property);
-  _property.components = descriptor.breakUp(_property, compactable);
+  _property.components = descriptor.breakUp(_property, compactable, validator);
   _property.multiplex = Array.isArray(_property.components[0].value[0][0]);
   return _property;
 }
index 332f019..a8ca567 100644 (file)
@@ -3,6 +3,7 @@ var assert = require('assert');
 var SelectorsOptimizer = require('../../lib/selectors/optimizer');
 var stringify = require('../../lib/stringifier/simple');
 var Compatibility = require('../../lib/utils/compatibility');
+var Validator = require('../../lib/properties/validator');
 var SourceTracker = require('../../lib/utils/source-tracker');
 
 function optimizerContext(group, specs, options) {
@@ -17,7 +18,8 @@ function optimizerContext(group, specs, options) {
   options.compatibility = new Compatibility(options.compatibility).toOptions();
   var outerContext = {
     options: {},
-    sourceTracker: new SourceTracker()
+    sourceTracker: new SourceTracker(),
+    validator: new Validator(options.compatibility)
   };
 
   function optimized(target) {
index 47c2476..cfabed9 100644 (file)
@@ -396,7 +396,7 @@ vows.describe('source-maps/analyzer')
           ]
         ]
       ],
-      'in properties123': [
+      'in properties': [
         'div{__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(2,5)__background:__ESCAPED_URL_CLEAN_CSS0(0,20)__;color:blue}a{font-family:__ESCAPED_FREE_TEXT_CLEAN_CSS0(1,3)__;color:red}',
         [
           [