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;