1var functionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)'; 2var functionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)'; 3var variableRegexStr = 'var\\(\\-\\-[^\\)]+\\)'; 4var functionAnyRegexStr = '(' + variableRegexStr + '|' + functionNoVendorRegexStr + '|' + functionVendorRegexStr + ')'; 5 6var calcRegex = new RegExp('^(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)$', 'i'); 7var decimalRegex = /[0-9]/; 8var functionAnyRegex = new RegExp('^' + functionAnyRegexStr + '$', 'i'); 9var 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; 10var identifierRegex = /^(\-[a-z0-9_][a-z0-9\-_]*|[a-z][a-z0-9\-_]*)$/i; 11var namedEntityRegex = /^[a-z]+$/i; 12var prefixRegex = /^-([a-z0-9]|-)*$/i; 13var 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; 14var timingFunctionRegex = /^(cubic\-bezier|steps)\([^\)]+\)$/; 15var validTimeUnits = ['ms', 's']; 16var urlRegex = /^url\([\s\S]+\)$/i; 17var variableRegex = new RegExp('^' + variableRegexStr + '$', 'i'); 18 19var eightValueColorRegex = /^#[0-9a-f]{8}$/i; 20var fourValueColorRegex = /^#[0-9a-f]{4}$/i; 21var sixValueColorRegex = /^#[0-9a-f]{6}$/i; 22var threeValueColorRegex = /^#[0-9a-f]{3}$/i; 23 24var DECIMAL_DOT = '.'; 25var MINUS_SIGN = '-'; 26var PLUS_SIGN = '+'; 27 28var Keywords = { 29 '^': [ 30 'inherit', 31 'initial', 32 'unset' 33 ], 34 '*-style': [ 35 'auto', 36 'dashed', 37 'dotted', 38 'double', 39 'groove', 40 'hidden', 41 'inset', 42 'none', 43 'outset', 44 'ridge', 45 'solid' 46 ], 47 '*-timing-function': [ 48 'ease', 49 'ease-in', 50 'ease-in-out', 51 'ease-out', 52 'linear', 53 'step-end', 54 'step-start' 55 ], 56 'animation-direction': [ 57 'alternate', 58 'alternate-reverse', 59 'normal', 60 'reverse' 61 ], 62 'animation-fill-mode': [ 63 'backwards', 64 'both', 65 'forwards', 66 'none' 67 ], 68 'animation-iteration-count': [ 69 'infinite' 70 ], 71 'animation-name': [ 72 'none' 73 ], 74 'animation-play-state': [ 75 'paused', 76 'running' 77 ], 78 'background-attachment': [ 79 'fixed', 80 'inherit', 81 'local', 82 'scroll' 83 ], 84 'background-clip': [ 85 'border-box', 86 'content-box', 87 'inherit', 88 'padding-box', 89 'text' 90 ], 91 'background-origin': [ 92 'border-box', 93 'content-box', 94 'inherit', 95 'padding-box' 96 ], 97 'background-position': [ 98 'bottom', 99 'center', 100 'left', 101 'right', 102 'top' 103 ], 104 'background-repeat': [ 105 'no-repeat', 106 'inherit', 107 'repeat', 108 'repeat-x', 109 'repeat-y', 110 'round', 111 'space' 112 ], 113 'background-size': [ 114 'auto', 115 'cover', 116 'contain' 117 ], 118 'border-collapse': [ 119 'collapse', 120 'inherit', 121 'separate' 122 ], 123 'bottom': [ 124 'auto' 125 ], 126 'clear': [ 127 'both', 128 'left', 129 'none', 130 'right' 131 ], 132 'color': [ 133 'transparent' 134 ], 135 'cursor': [ 136 'all-scroll', 137 'auto', 138 'col-resize', 139 'crosshair', 140 'default', 141 'e-resize', 142 'help', 143 'move', 144 'n-resize', 145 'ne-resize', 146 'no-drop', 147 'not-allowed', 148 'nw-resize', 149 'pointer', 150 'progress', 151 'row-resize', 152 's-resize', 153 'se-resize', 154 'sw-resize', 155 'text', 156 'vertical-text', 157 'w-resize', 158 'wait' 159 ], 160 'display': [ 161 'block', 162 'inline', 163 'inline-block', 164 'inline-table', 165 'list-item', 166 'none', 167 'table', 168 'table-caption', 169 'table-cell', 170 'table-column', 171 'table-column-group', 172 'table-footer-group', 173 'table-header-group', 174 'table-row', 175 'table-row-group' 176 ], 177 'float': [ 178 'left', 179 'none', 180 'right' 181 ], 182 'left': [ 183 'auto' 184 ], 185 'font': [ 186 'caption', 187 'icon', 188 'menu', 189 'message-box', 190 'small-caption', 191 'status-bar', 192 'unset' 193 ], 194 'font-size': [ 195 'large', 196 'larger', 197 'medium', 198 'small', 199 'smaller', 200 'x-large', 201 'x-small', 202 'xx-large', 203 'xx-small' 204 ], 205 'font-stretch': [ 206 'condensed', 207 'expanded', 208 'extra-condensed', 209 'extra-expanded', 210 'normal', 211 'semi-condensed', 212 'semi-expanded', 213 'ultra-condensed', 214 'ultra-expanded' 215 ], 216 'font-style': [ 217 'italic', 218 'normal', 219 'oblique' 220 ], 221 'font-variant': [ 222 'normal', 223 'small-caps' 224 ], 225 'font-weight': [ 226 '100', 227 '200', 228 '300', 229 '400', 230 '500', 231 '600', 232 '700', 233 '800', 234 '900', 235 'bold', 236 'bolder', 237 'lighter', 238 'normal' 239 ], 240 'line-height': [ 241 'normal' 242 ], 243 'list-style-position': [ 244 'inside', 245 'outside' 246 ], 247 'list-style-type': [ 248 'armenian', 249 'circle', 250 'decimal', 251 'decimal-leading-zero', 252 'disc', 253 'decimal|disc', // this is the default value of list-style-type, see comment in compactable.js 254 'georgian', 255 'lower-alpha', 256 'lower-greek', 257 'lower-latin', 258 'lower-roman', 259 'none', 260 'square', 261 'upper-alpha', 262 'upper-latin', 263 'upper-roman' 264 ], 265 'overflow': [ 266 'auto', 267 'hidden', 268 'scroll', 269 'visible' 270 ], 271 'position': [ 272 'absolute', 273 'fixed', 274 'relative', 275 'static' 276 ], 277 'right': [ 278 'auto' 279 ], 280 'text-align': [ 281 'center', 282 'justify', 283 'left', 284 'left|right', // this is the default value of list-style-type, see comment in compactable.js 285 'right' 286 ], 287 'text-decoration': [ 288 'line-through', 289 'none', 290 'overline', 291 'underline' 292 ], 293 'text-overflow': [ 294 'clip', 295 'ellipsis' 296 ], 297 'top': [ 298 'auto' 299 ], 300 'vertical-align': [ 301 'baseline', 302 'bottom', 303 'middle', 304 'sub', 305 'super', 306 'text-bottom', 307 'text-top', 308 'top' 309 ], 310 'visibility': [ 311 'collapse', 312 'hidden', 313 'visible' 314 ], 315 'white-space': [ 316 'normal', 317 'nowrap', 318 'pre' 319 ], 320 'width': [ 321 'inherit', 322 'initial', 323 'medium', 324 'thick', 325 'thin' 326 ] 327}; 328 329var Units = [ 330 '%', 331 'ch', 332 'cm', 333 'em', 334 'ex', 335 'in', 336 'mm', 337 'pc', 338 'pt', 339 'px', 340 'rem', 341 'vh', 342 'vm', 343 'vmax', 344 'vmin', 345 'vw' 346]; 347 348function isColor(value) { 349 return value != 'auto' && 350 ( 351 isKeyword('color')(value) || 352 isHexColor(value) || 353 isColorFunction(value) || 354 isNamedEntity(value) 355 ); 356} 357 358function isColorFunction(value) { 359 return isRgbColor(value) || isHslColor(value); 360} 361 362function isDynamicUnit(value) { 363 return calcRegex.test(value); 364} 365 366function isFunction(value) { 367 return functionAnyRegex.test(value); 368} 369 370function isHexColor(value) { 371 return threeValueColorRegex.test(value) || fourValueColorRegex.test(value) || sixValueColorRegex.test(value) || eightValueColorRegex.test(value); 372} 373 374function isHslColor(value) { 375 return hslColorRegex.test(value); 376} 377 378function isIdentifier(value) { 379 return identifierRegex.test(value); 380} 381 382function isImage(value) { 383 return value == 'none' || value == 'inherit' || isUrl(value); 384} 385 386function isKeyword(propertyName) { 387 return function(value) { 388 return Keywords[propertyName].indexOf(value) > -1; 389 }; 390} 391 392function isNamedEntity(value) { 393 return namedEntityRegex.test(value); 394} 395 396function isNumber(value) { 397 return scanForNumber(value) == value.length; 398} 399 400function isRgbColor(value) { 401 return rgbColorRegex.test(value); 402} 403 404function isPrefixed(value) { 405 return prefixRegex.test(value); 406} 407 408function isPositiveNumber(value) { 409 return isNumber(value) && 410 parseFloat(value) >= 0; 411} 412 413function isVariable(value) { 414 return variableRegex.test(value); 415} 416 417function isTime(value) { 418 var numberUpTo = scanForNumber(value); 419 420 return numberUpTo == value.length && parseInt(value) === 0 || 421 numberUpTo > -1 && validTimeUnits.indexOf(value.slice(numberUpTo + 1)) > -1; 422} 423 424function isTimingFunction() { 425 var isTimingFunctionKeyword = isKeyword('*-timing-function'); 426 427 return function (value) { 428 return isTimingFunctionKeyword(value) || timingFunctionRegex.test(value); 429 }; 430} 431 432function isUnit(validUnits, value) { 433 var numberUpTo = scanForNumber(value); 434 435 return numberUpTo == value.length && parseInt(value) === 0 || 436 numberUpTo > -1 && validUnits.indexOf(value.slice(numberUpTo + 1)) > -1 || 437 value == 'auto' || 438 value == 'inherit'; 439} 440 441function isUrl(value) { 442 return urlRegex.test(value); 443} 444 445function isZIndex(value) { 446 return value == 'auto' || 447 isNumber(value) || 448 isKeyword('^')(value); 449} 450 451function scanForNumber(value) { 452 var hasDot = false; 453 var hasSign = false; 454 var character; 455 var i, l; 456 457 for (i = 0, l = value.length; i < l; i++) { 458 character = value[i]; 459 460 if (i === 0 && (character == PLUS_SIGN || character == MINUS_SIGN)) { 461 hasSign = true; 462 } else if (i > 0 && hasSign && (character == PLUS_SIGN || character == MINUS_SIGN)) { 463 return i - 1; 464 } else if (character == DECIMAL_DOT && !hasDot) { 465 hasDot = true; 466 } else if (character == DECIMAL_DOT && hasDot) { 467 return i - 1; 468 } else if (decimalRegex.test(character)) { 469 continue; 470 } else { 471 return i - 1; 472 } 473 } 474 475 return i; 476} 477 478function validator(compatibility) { 479 var validUnits = Units.slice(0).filter(function (value) { 480 return !(value in compatibility.units) || compatibility.units[value] === true; 481 }); 482 483 return { 484 colorOpacity: compatibility.colors.opacity, 485 isAnimationDirectionKeyword: isKeyword('animation-direction'), 486 isAnimationFillModeKeyword: isKeyword('animation-fill-mode'), 487 isAnimationIterationCountKeyword: isKeyword('animation-iteration-count'), 488 isAnimationNameKeyword: isKeyword('animation-name'), 489 isAnimationPlayStateKeyword: isKeyword('animation-play-state'), 490 isTimingFunction: isTimingFunction(), 491 isBackgroundAttachmentKeyword: isKeyword('background-attachment'), 492 isBackgroundClipKeyword: isKeyword('background-clip'), 493 isBackgroundOriginKeyword: isKeyword('background-origin'), 494 isBackgroundPositionKeyword: isKeyword('background-position'), 495 isBackgroundRepeatKeyword: isKeyword('background-repeat'), 496 isBackgroundSizeKeyword: isKeyword('background-size'), 497 isColor: isColor, 498 isColorFunction: isColorFunction, 499 isDynamicUnit: isDynamicUnit, 500 isFontKeyword: isKeyword('font'), 501 isFontSizeKeyword: isKeyword('font-size'), 502 isFontStretchKeyword: isKeyword('font-stretch'), 503 isFontStyleKeyword: isKeyword('font-style'), 504 isFontVariantKeyword: isKeyword('font-variant'), 505 isFontWeightKeyword: isKeyword('font-weight'), 506 isFunction: isFunction, 507 isGlobal: isKeyword('^'), 508 isHslColor: isHslColor, 509 isIdentifier: isIdentifier, 510 isImage: isImage, 511 isKeyword: isKeyword, 512 isLineHeightKeyword: isKeyword('line-height'), 513 isListStylePositionKeyword: isKeyword('list-style-position'), 514 isListStyleTypeKeyword: isKeyword('list-style-type'), 515 isNumber: isNumber, 516 isPrefixed: isPrefixed, 517 isPositiveNumber: isPositiveNumber, 518 isRgbColor: isRgbColor, 519 isStyleKeyword: isKeyword('*-style'), 520 isTime: isTime, 521 isUnit: isUnit.bind(null, validUnits), 522 isUrl: isUrl, 523 isVariable: isVariable, 524 isWidth: isKeyword('width'), 525 isZIndex: isZIndex 526 }; 527} 528 529module.exports = validator; 530