text: var functionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)';
  var functionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)';
  var variableRegexStr = 'var\\(\\-\\-[^\\)]+\\)';
  var functionAnyRegexStr = '(' + variableRegexStr + '|' + functionNoVendorRegexStr + '|' + functionVendorRegexStr + ')';
  
  var animationTimingFunctionRegex = /^(cubic\-bezier|steps)\([^\)]+\)$/;
  var calcRegex = new RegExp('^(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)$', 'i');
  var decimalRegex = /[0-9]/;
  var functionAnyRegex = new RegExp('^' + functionAnyRegexStr + '$', 'i');
  var hslColorRegex = /^hsl\(\s{0,31}[\-\.]?\d+\s{0,31},\s{0,31}\.?\d+%\s{0,31},\s{0,31}\.?\d+%\s{0,31}\)|hsla\(\s{0,31}[\-\.]?\d+\s{0,31},\s{0,31}\.?\d+%\s{0,31},\s{0,31}\.?\d+%\s{0,31},\s{0,31}\.?\d+\s{0,31}\)$/;
  var identifierRegex = /^(\-[a-z0-9_][a-z0-9\-_]*|[a-z][a-z0-9\-_]*)$/i;
  var longHexColorRegex = /^#[0-9a-f]{6}$/i;
  var namedEntityRegex = /^[a-z]+$/i;
  var prefixRegex = /^-([a-z0-9]|-)*$/i;
  var rgbColorRegex = /^rgb\(\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[\d]{1,3}\s{0,31}\)|rgba\(\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[\d]{1,3}\s{0,31},\s{0,31}[\.\d]+\s{0,31}\)$/;
  var shortHexColorRegex = /^#[0-9a-f]{3}$/i;
  var validTimeUnits = ['ms', 's'];
  var urlRegex = /^url\([\s\S]+\)$/i;
  var variableRegex = new RegExp('^' + variableRegexStr + '$', 'i');
  
  var DECIMAL_DOT = '.';
  var MINUS_SIGN = '-';
  var PLUS_SIGN = '+';
  
  var Keywords = {
    '^': [
      'inherit',
      'initial',
      'unset'
    ],
    '*-style': [
      'auto',
      'dashed',
      'dotted',
      'double',
      'groove',
      'hidden',
      'inset',
      'none',
      'outset',
      'ridge',
      'solid'
    ],
    'animation-direction': [
      'alternate',
      'alternate-reverse',
      'normal',
      'reverse'
    ],
    'animation-fill-mode': [
      'backwards',
      'both',
      'forwards',
      'none'
    ],
    'animation-iteration-count': [
      'infinite'
    ],
    'animation-name': [
      'none'
    ],
    'animation-play-state': [
      'paused',
      'running'
    ],
    'animation-timing-function': [
      'ease',
      'ease-in',
      'ease-in-out',
      'ease-out',
      'linear',
      'step-end',
      'step-start'
    ],
    'background-attachment': [
      'fixed',
      'inherit',
      'local',
      'scroll'
    ],
    'background-clip': [
      'border-box',
      'content-box',
      'inherit',
      'padding-box',
      'text'
    ],
    'background-origin': [
      'border-box',
      'content-box',
      'inherit',
      'padding-box'
    ],
    'background-position': [
      'bottom',
      'center',
      'left',
      'right',
      'top'
    ],
    'background-repeat': [
      'no-repeat',
      'inherit',
      'repeat',
      'repeat-x',
      'repeat-y',
      'round',
      'space'
    ],
    'background-size': [
      'auto',
      'cover',
      'contain'
    ],
    'border-collapse': [
      'collapse',
      'inherit',
      'separate'
    ],
    'bottom': [
      'auto'
    ],
    'clear': [
      'both',
      'left',
      'none',
      'right'
    ],
    'color': [
      'transparent'
    ],
    'cursor': [
      'all-scroll',
      'auto',
      'col-resize',
      'crosshair',
      'default',
      'e-resize',
      'help',
      'move',
      'n-resize',
      'ne-resize',
      'no-drop',
      'not-allowed',
      'nw-resize',
      'pointer',
      'progress',
      'row-resize',
      's-resize',
      'se-resize',
      'sw-resize',
      'text',
      'vertical-text',
      'w-resize',
      'wait'
    ],
    'display': [
      'block',
      'inline',
      'inline-block',
      'inline-table',
      'list-item',
      'none',
      'table',
      'table-caption',
      'table-cell',
      'table-column',
      'table-column-group',
      'table-footer-group',
      'table-header-group',
      'table-row',
      'table-row-group'
    ],
    'float': [
      'left',
      'none',
      'right'
    ],
    'left': [
      'auto'
    ],
    'font': [
      'caption',
      'icon',
      'menu',
      'message-box',
      'small-caption',
      'status-bar',
      'unset'
    ],
    'font-size': [
      'large',
      'larger',
      'medium',
      'small',
      'smaller',
      'x-large',
      'x-small',
      'xx-large',
      'xx-small'
    ],
    'font-stretch': [
      'condensed',
      'expanded',
      'extra-condensed',
      'extra-expanded',
      'normal',
      'semi-condensed',
      'semi-expanded',
      'ultra-condensed',
      'ultra-expanded'
    ],
    'font-style': [
      'italic',
      'normal',
      'oblique'
    ],
    'font-variant': [
      'normal',
      'small-caps'
    ],
    'font-weight': [
      '100',
      '200',
      '300',
      '400',
      '500',
      '600',
      '700',
      '800',
      '900',
      'bold',
      'bolder',
      'lighter',
      'normal'
    ],
    'line-height': [
      'normal'
    ],
    'list-style-position': [
      'inside',
      'outside'
    ],
    'list-style-type': [
      'armenian',
      'circle',
      'decimal',
      'decimal-leading-zero',
      'disc',
      'decimal|disc', // this is the default value of list-style-type, see comment in compactable.js
      'georgian',
      'lower-alpha',
      'lower-greek',
      'lower-latin',
      'lower-roman',
      'none',
      'square',
      'upper-alpha',
      'upper-latin',
      'upper-roman'
    ],
    'overflow': [
      'auto',
      'hidden',
      'scroll',
      'visible'
    ],
    'position': [
      'absolute',
      'fixed',
      'relative',
      'static'
    ],
    'right': [
      'auto'
    ],
    'text-align': [
      'center',
      'justify',
      'left',
      'left|right', // this is the default value of list-style-type, see comment in compactable.js
      'right'
    ],
    'text-decoration': [
      'line-through',
      'none',
      'overline',
      'underline'
    ],
    'text-overflow': [
      'clip',
      'ellipsis'
    ],
    'top': [
      'auto'
    ],
    'vertical-align': [
      'baseline',
      'bottom',
      'middle',
      'sub',
      'super',
      'text-bottom',
      'text-top',
      'top'
    ],
    'visibility': [
      'collapse',
      'hidden',
      'visible'
    ],
    'white-space': [
      'normal',
      'nowrap',
      'pre'
    ],
    'width': [
      'inherit',
      'initial',
      'medium',
      'thick',
      'thin'
    ]
  };
  
  var Units = [
    '%',
    'ch',
    'cm',
    'em',
    'ex',
    'in',
    'mm',
    'pc',
    'pt',
    'px',
    'rem',
    'vh',
    'vm',
    'vmax',
    'vmin',
    'vw'
  ];
  
  function isAnimationTimingFunction() {
    var isTimingFunctionKeyword = isKeyword('animation-timing-function');
  
    return function (value) {
      return isTimingFunctionKeyword(value) || animationTimingFunctionRegex.test(value);
    };
  }
  
  function isColor(value) {
    return value != 'auto' &&
      (
        isKeyword('color')(value) ||
        isHexColor(value) ||
        isColorFunction(value) ||
        isNamedEntity(value)
      );
  }
  
  function isColorFunction(value) {
    return isRgbColor(value) || isHslColor(value);
  }
  
  function isDynamicUnit(value) {
    return calcRegex.test(value);
  }
  
  function isFunction(value) {
    return functionAnyRegex.test(value);
  }
  
  function isHexColor(value) {
    return shortHexColorRegex.test(value) || longHexColorRegex.test(value);
  }
  
  function isHslColor(value) {
    return hslColorRegex.test(value);
  }
  
  function isIdentifier(value) {
    return identifierRegex.test(value);
  }
  
  function isImage(value) {
    return value == 'none' || value == 'inherit' || isUrl(value);
  }
  
  function isKeyword(propertyName) {
    return function(value) {
      return Keywords[propertyName].indexOf(value) > -1;
    };
  }
  
  function isNamedEntity(value) {
    return namedEntityRegex.test(value);
  }
  
  function isNumber(value) {
    return scanForNumber(value) == value.length;
  }
  
  function isRgbColor(value) {
    return rgbColorRegex.test(value);
  }
  
  function isPrefixed(value) {
    return prefixRegex.test(value);
  }
  
  function isPositiveNumber(value) {
    return isNumber(value) &&
      parseFloat(value) >= 0;
  }
  
  function isVariable(value) {
    return variableRegex.test(value);
  }
  
  function isTime(value) {
    var numberUpTo = scanForNumber(value);
  
    return numberUpTo == value.length && parseInt(value) === 0 ||
      numberUpTo > -1 && validTimeUnits.indexOf(value.slice(numberUpTo + 1)) > -1;
  }
  
  function isUnit(validUnits, value) {
    var numberUpTo = scanForNumber(value);
  
    return numberUpTo == value.length && parseInt(value) === 0 ||
      numberUpTo > -1 && validUnits.indexOf(value.slice(numberUpTo + 1)) > -1 ||
      value == 'auto' ||
      value == 'inherit';
  }
  
  function isUrl(value) {
    return urlRegex.test(value);
  }
  
  function isZIndex(value) {
    return value == 'auto' ||
      isNumber(value) ||
      isKeyword('^')(value);
  }
  
  function scanForNumber(value) {
    var hasDot = false;
    var hasSign = false;
    var character;
    var i, l;
  
    for (i = 0, l = value.length; i < l; i++) {
      character = value[i];
  
      if (i === 0 && (character == PLUS_SIGN || character == MINUS_SIGN)) {
        hasSign = true;
      } else if (i > 0 && hasSign && (character == PLUS_SIGN || character == MINUS_SIGN)) {
        return i - 1;
      } else if (character == DECIMAL_DOT && !hasDot) {
        hasDot = true;
      } else if (character == DECIMAL_DOT && hasDot) {
        return i - 1;
      } else if (decimalRegex.test(character)) {
        continue;
      } else {
        return i - 1;
      }
    }
  
    return i;
  }
  
  function validator(compatibility) {
    var validUnits = Units.slice(0).filter(function (value) {
      return !(value in compatibility.units) || compatibility.units[value] === true;
    });
  
    return {
      colorOpacity: compatibility.colors.opacity,
      isAnimationDirectionKeyword: isKeyword('animation-direction'),
      isAnimationFillModeKeyword: isKeyword('animation-fill-mode'),
      isAnimationIterationCountKeyword: isKeyword('animation-iteration-count'),
      isAnimationNameKeyword: isKeyword('animation-name'),
      isAnimationPlayStateKeyword: isKeyword('animation-play-state'),
      isAnimationTimingFunction: isAnimationTimingFunction(),
      isBackgroundAttachmentKeyword: isKeyword('background-attachment'),
      isBackgroundClipKeyword: isKeyword('background-clip'),
      isBackgroundOriginKeyword: isKeyword('background-origin'),
      isBackgroundPositionKeyword: isKeyword('background-position'),
      isBackgroundRepeatKeyword: isKeyword('background-repeat'),
      isBackgroundSizeKeyword: isKeyword('background-size'),
      isColor: isColor,
      isColorFunction: isColorFunction,
      isDynamicUnit: isDynamicUnit,
      isFontKeyword: isKeyword('font'),
      isFontSizeKeyword: isKeyword('font-size'),
      isFontStretchKeyword: isKeyword('font-stretch'),
      isFontStyleKeyword: isKeyword('font-style'),
      isFontVariantKeyword: isKeyword('font-variant'),
      isFontWeightKeyword: isKeyword('font-weight'),
      isFunction: isFunction,
      isGlobal: isKeyword('^'),
      isHslColor: isHslColor,
      isIdentifier: isIdentifier,
      isImage: isImage,
      isKeyword: isKeyword,
      isLineHeightKeyword: isKeyword('line-height'),
      isListStylePositionKeyword: isKeyword('list-style-position'),
      isListStyleTypeKeyword: isKeyword('list-style-type'),
      isNumber: isNumber,
      isPrefixed: isPrefixed,
      isPositiveNumber: isPositiveNumber,
      isRgbColor: isRgbColor,
      isStyleKeyword: isKeyword('*-style'),
      isTime: isTime,
      isUnit: isUnit.bind(null, validUnits),
      isUrl: isUrl,
      isVariable: isVariable,
      isWidth: isKeyword('width'),
      isZIndex: isZIndex
    };
  }
  
  module.exports = validator;
  