text: 
  // Validates various CSS property values
  
  var Splitter = require('../text/splitter');
  
  module.exports = (function () {
    // Regexes used for stuff
    var widthKeywords = ['thin', 'thick', 'medium', 'inherit', 'initial'];
    var cssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(px|%|em|rem|in|cm|mm|ex|pt|pc|vw|vh|vmin|vmax|)|auto|inherit)';
    var cssFunctionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(([A-Z]|[0-9]|\\ |\\,|\\#|\\+|\\-|\\%|\\.|\\(|\\))*\\)';
    var cssFunctionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(([A-Z]|[0-9]|\\ |\\,|\\#|\\+|\\-|\\%|\\.|\\(|\\))*\\)';
    var cssVariableRegexStr = 'var\\(\\-\\-[^\\)]+\\)';
    var cssFunctionAnyRegexStr = '(' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
    var cssUnitAnyRegexStr = '(none|' + widthKeywords.join('|') + '|' + cssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
  
    var cssFunctionNoVendorRegex = new RegExp('^' + cssFunctionNoVendorRegexStr + '$', 'i');
    var cssFunctionVendorRegex = new RegExp('^' + cssFunctionVendorRegexStr + '$', 'i');
    var cssVariableRegex = new RegExp('^' + cssVariableRegexStr + '$', 'i');
    var cssFunctionAnyRegex = new RegExp('^' + cssFunctionAnyRegexStr + '$', 'i');
    var cssUnitRegex = new RegExp('^' + cssUnitRegexStr + '$', 'i');
    var cssUnitAnyRegex = new RegExp('^' + cssUnitAnyRegexStr + '$', 'i');
  
    var backgroundRepeatKeywords = ['repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'inherit'];
    var backgroundAttachmentKeywords = ['inherit', 'scroll', 'fixed', 'local'];
    var backgroundPositionKeywords = ['center', 'top', 'bottom', 'left', 'right'];
    var backgroundSizeKeywords = ['contain', 'cover'];
    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 outlineStyleKeywords = ['auto', 'inherit', 'hidden', 'none', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'];
  
    var validator = {
      isValidHexColor: function (s) {
        return (s.length === 4 || s.length === 7) && s[0] === '#';
      },
      isValidRgbaColor: function (s) {
        s = s.split(' ').join('');
        return s.length > 0 && s.indexOf('rgba(') === 0 && s.indexOf(')') === s.length - 1;
      },
      isValidHslaColor: function (s) {
        s = s.split(' ').join('');
        return s.length > 0 && s.indexOf('hsla(') === 0 && s.indexOf(')') === s.length - 1;
      },
      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));
      },
      isValidVariable: function(s) {
        return cssVariableRegex.test(s);
      },
      isValidColor: function (s) {
        return validator.isValidNamedColor(s) || validator.isValidHexColor(s) || validator.isValidRgbaColor(s) || validator.isValidHslaColor(s) || validator.isValidVariable(s);
      },
      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;
      },
      isValidUnit: function (s) {
        return cssUnitAnyRegex.test(s);
      },
      isValidUnitWithoutFunction: function (s) {
        return cssUnitRegex.test(s);
      },
      isValidFunctionWithoutVendorPrefix: function (s) {
        return cssFunctionNoVendorRegex.test(s);
      },
      isValidFunctionWithVendorPrefix: function (s) {
        return cssFunctionVendorRegex.test(s);
      },
      isValidFunction: function (s) {
        return cssFunctionAnyRegex.test(s);
      },
      isValidBackgroundRepeat: function (s) {
        return backgroundRepeatKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
      },
      isValidBackgroundAttachment: function (s) {
        return backgroundAttachmentKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
      },
      isValidBackgroundPositionPart: function (s) {
        if (backgroundPositionKeywords.indexOf(s) >= 0)
          return true;
  
        return cssUnitRegex.test(s) || validator.isValidVariable(s);
      },
      isValidBackgroundPosition: function (s) {
        if (s === 'inherit')
          return true;
  
        var parts = s.split(' ');
        for (var i = 0, l = parts.length; i < l; i++) {
          if (parts[i] === '')
            continue;
          if (validator.isValidBackgroundPositionPart(parts[i]) || validator.isValidVariable(parts[i]))
            continue;
  
          return false;
        }
  
        return true;
      },
      isValidBackgroundSizePart: function(s) {
        return backgroundSizeKeywords.indexOf(s) >= 0 || cssUnitRegex.test(s) || validator.isValidVariable(s);
      },
      isValidBackgroundPositionAndSize: function(s) {
        if (s.indexOf('/') < 0)
          return false;
  
        var twoParts = new Splitter('/').split(s);
        return validator.isValidBackgroundSizePart(twoParts.pop()) && validator.isValidBackgroundPositionPart(twoParts.pop());
      },
      isValidListStyleType: function (s) {
        return listStyleTypeKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
      },
      isValidListStylePosition: function (s) {
        return listStylePositionKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
      },
      isValidOutlineColor: function (s) {
        return s === 'invert' || validator.isValidColor(s) || validator.isValidVendorPrefixedValue(s);
      },
      isValidOutlineStyle: function (s) {
        return outlineStyleKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
      },
      isValidOutlineWidth: function (s) {
        return validator.isValidUnit(s) || widthKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
      },
      isValidVendorPrefixedValue: function (s) {
        return /^-([A-Za-z0-9]|-)*$/gi.test(s);
      },
      areSameFunction: function (a, b) {
        if (!validator.isValidFunction(a) || !validator.isValidFunction(b))
          return false;
  
        var f1name = a.substring(0, a.indexOf('('));
        var f2name = b.substring(0, b.indexOf('('));
  
        return f1name === f2name;
      }
    };
  
    return validator;
  })();
  