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}\)$/;
|
|
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}\)$/;
|
|
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;
|