| var functionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)'; |
| var functionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)'; |
| var variableRegexStr = 'var\\(\\-\\-[^\\)]+\\)'; |
| var functionAnyRegexStr = '(' + variableRegexStr + '|' + functionNoVendorRegexStr + '|' + functionVendorRegexStr + ')'; |
| |
| 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}\)$/i; |
| var identifierRegex = /^(\-[a-z0-9_][a-z0-9\-_]*|[a-z][a-z0-9\-_]*)$/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}\)$/i; |
| var timingFunctionRegex = /^(cubic\-bezier|steps)\([^\)]+\)$/; |
| var validTimeUnits = ['ms', 's']; |
| var urlRegex = /^url\([\s\S]+\)$/i; |
| var variableRegex = new RegExp('^' + variableRegexStr + '$', 'i'); |
| |
| var eightValueColorRegex = /^#[0-9a-f]{8}$/i; |
| var fourValueColorRegex = /^#[0-9a-f]{4}$/i; |
| var sixValueColorRegex = /^#[0-9a-f]{6}$/i; |
| var threeValueColorRegex = /^#[0-9a-f]{3}$/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' |
| ], |
| '*-timing-function': [ |
| 'ease', |
| 'ease-in', |
| 'ease-in-out', |
| 'ease-out', |
| 'linear', |
| 'step-end', |
| 'step-start' |
| ], |
| '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' |
| ], |
| '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 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 threeValueColorRegex.test(value) || fourValueColorRegex.test(value) || sixValueColorRegex.test(value) || eightValueColorRegex.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 isTimingFunction() { |
| var isTimingFunctionKeyword = isKeyword('*-timing-function'); |
| |
| return function (value) { |
| return isTimingFunctionKeyword(value) || timingFunctionRegex.test(value); |
| }; |
| } |
| |
| 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'), |
| isTimingFunction: isTimingFunction(), |
| 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; |