validator.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Validates various CSS property values
  2. var split = require('../utils/split');
  3. var widthKeywords = ['thin', 'thick', 'medium', 'inherit', 'initial'];
  4. var allUnits = ['px', '%', 'em', 'in', 'cm', 'mm', 'ex', 'pt', 'pc', 'ch', 'rem', 'vh', 'vm', 'vmin', 'vmax', 'vw'];
  5. var cssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + allUnits.join('|') + '|)|auto|inherit)';
  6. var cssCalcRegexStr = '(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)';
  7. var cssFunctionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)';
  8. var cssFunctionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)';
  9. var cssVariableRegexStr = 'var\\(\\-\\-[^\\)]+\\)';
  10. var cssFunctionAnyRegexStr = '(' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
  11. var cssUnitOrCalcRegexStr = '(' + cssUnitRegexStr + '|' + cssCalcRegexStr + ')';
  12. var cssUnitAnyRegexStr = '(none|' + widthKeywords.join('|') + '|' + cssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
  13. var cssFunctionNoVendorRegex = new RegExp('^' + cssFunctionNoVendorRegexStr + '$', 'i');
  14. var cssFunctionVendorRegex = new RegExp('^' + cssFunctionVendorRegexStr + '$', 'i');
  15. var cssVariableRegex = new RegExp('^' + cssVariableRegexStr + '$', 'i');
  16. var cssFunctionAnyRegex = new RegExp('^' + cssFunctionAnyRegexStr + '$', 'i');
  17. var cssUnitRegex = new RegExp('^' + cssUnitRegexStr + '$', 'i');
  18. var cssUnitOrCalcRegex = new RegExp('^' + cssUnitOrCalcRegexStr + '$', 'i');
  19. var cssUnitAnyRegex = new RegExp('^' + cssUnitAnyRegexStr + '$', 'i');
  20. var backgroundRepeatKeywords = ['repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'inherit'];
  21. var backgroundAttachmentKeywords = ['inherit', 'scroll', 'fixed', 'local'];
  22. var backgroundPositionKeywords = ['center', 'top', 'bottom', 'left', 'right'];
  23. var backgroundSizeKeywords = ['contain', 'cover'];
  24. var backgroundBoxKeywords = ['border-box', 'content-box', 'padding-box'];
  25. var styleKeywords = ['auto', 'inherit', 'hidden', 'none', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'];
  26. var listStyleTypeKeywords = ['armenian', 'circle', 'cjk-ideographic', 'decimal', 'decimal-leading-zero', 'disc', 'georgian', 'hebrew', 'hiragana', 'hiragana-iroha', 'inherit', 'katakana', 'katakana-iroha', 'lower-alpha', 'lower-greek', 'lower-latin', 'lower-roman', 'none', 'square', 'upper-alpha', 'upper-latin', 'upper-roman'];
  27. var listStylePositionKeywords = ['inside', 'outside', 'inherit'];
  28. function Validator(compatibility) {
  29. var validUnits = allUnits.slice(0).filter(function (value) {
  30. return !(value in compatibility.units) || compatibility.units[value] === true;
  31. });
  32. var compatibleCssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + '|)|auto|inherit)';
  33. this.compatibleCssUnitRegex = new RegExp('^' + compatibleCssUnitRegexStr + '$', 'i');
  34. this.compatibleCssUnitAnyRegex = new RegExp('^(none|' + widthKeywords.join('|') + '|' + compatibleCssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')$', 'i');
  35. this.colorOpacity = compatibility.colors.opacity;
  36. }
  37. Validator.prototype.isValidHexColor = function (s) {
  38. return (s.length === 4 || s.length === 7) && s[0] === '#';
  39. };
  40. Validator.prototype.isValidRgbaColor = function (s) {
  41. s = s.split(' ').join('');
  42. return s.length > 0 && s.indexOf('rgba(') === 0 && s.indexOf(')') === s.length - 1;
  43. };
  44. Validator.prototype.isValidHslaColor = function (s) {
  45. s = s.split(' ').join('');
  46. return s.length > 0 && s.indexOf('hsla(') === 0 && s.indexOf(')') === s.length - 1;
  47. };
  48. Validator.prototype.isValidNamedColor = function (s) {
  49. // We don't really check if it's a valid color value, but allow any letters in it
  50. return s !== 'auto' && (s === 'transparent' || s === 'inherit' || /^[a-zA-Z]+$/.test(s));
  51. };
  52. Validator.prototype.isValidVariable = function (s) {
  53. return cssVariableRegex.test(s);
  54. };
  55. Validator.prototype.isValidColor = function (s) {
  56. return this.isValidNamedColor(s) ||
  57. this.isValidColorValue(s) ||
  58. this.isValidVariable(s) ||
  59. this.isValidVendorPrefixedValue(s);
  60. };
  61. Validator.prototype.isValidColorValue = function (s) {
  62. return this.isValidHexColor(s) ||
  63. this.isValidRgbaColor(s) ||
  64. this.isValidHslaColor(s);
  65. };
  66. Validator.prototype.isValidUrl = function (s) {
  67. // NOTE: at this point all URLs are replaced with placeholders by clean-css, so we check for those placeholders
  68. return s.indexOf('__ESCAPED_URL_CLEAN_CSS') === 0;
  69. };
  70. Validator.prototype.isValidUnit = function (s) {
  71. return cssUnitAnyRegex.test(s);
  72. };
  73. Validator.prototype.isValidUnitWithoutFunction = function (s) {
  74. return cssUnitRegex.test(s);
  75. };
  76. Validator.prototype.isValidAndCompatibleUnit = function (s) {
  77. return this.compatibleCssUnitAnyRegex.test(s);
  78. };
  79. Validator.prototype.isValidAndCompatibleUnitWithoutFunction = function (s) {
  80. return this.compatibleCssUnitRegex.test(s);
  81. };
  82. Validator.prototype.isValidFunctionWithoutVendorPrefix = function (s) {
  83. return cssFunctionNoVendorRegex.test(s);
  84. };
  85. Validator.prototype.isValidFunctionWithVendorPrefix = function (s) {
  86. return cssFunctionVendorRegex.test(s);
  87. };
  88. Validator.prototype.isValidFunction = function (s) {
  89. return cssFunctionAnyRegex.test(s);
  90. };
  91. Validator.prototype.isValidBackgroundRepeat = function (s) {
  92. return backgroundRepeatKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
  93. };
  94. Validator.prototype.isValidBackgroundAttachment = function (s) {
  95. return backgroundAttachmentKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
  96. };
  97. Validator.prototype.isValidBackgroundBox = function (s) {
  98. return backgroundBoxKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
  99. };
  100. Validator.prototype.isValidBackgroundPositionPart = function (s) {
  101. return backgroundPositionKeywords.indexOf(s) >= 0 || cssUnitOrCalcRegex.test(s) || this.isValidVariable(s);
  102. };
  103. Validator.prototype.isValidBackgroundPosition = function (s) {
  104. if (s === 'inherit')
  105. return true;
  106. var parts = s.split(' ');
  107. for (var i = 0, l = parts.length; i < l; i++) {
  108. if (parts[i] === '')
  109. continue;
  110. if (this.isValidBackgroundPositionPart(parts[i]) || this.isValidVariable(parts[i]))
  111. continue;
  112. return false;
  113. }
  114. return true;
  115. };
  116. Validator.prototype.isValidBackgroundSizePart = function (s) {
  117. return backgroundSizeKeywords.indexOf(s) >= 0 || cssUnitRegex.test(s) || this.isValidVariable(s);
  118. };
  119. Validator.prototype.isValidBackgroundPositionAndSize = function (s) {
  120. if (s.indexOf('/') < 0)
  121. return false;
  122. var twoParts = split(s, '/');
  123. return this.isValidBackgroundSizePart(twoParts.pop()) && this.isValidBackgroundPositionPart(twoParts.pop());
  124. };
  125. Validator.prototype.isValidListStyleType = function (s) {
  126. return listStyleTypeKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
  127. };
  128. Validator.prototype.isValidListStylePosition = function (s) {
  129. return listStylePositionKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
  130. };
  131. Validator.prototype.isValidStyle = function (s) {
  132. return this.isValidStyleKeyword(s) || this.isValidVariable(s);
  133. };
  134. Validator.prototype.isValidStyleKeyword = function (s) {
  135. return styleKeywords.indexOf(s) >= 0;
  136. };
  137. Validator.prototype.isValidWidth = function (s) {
  138. return this.isValidUnit(s) || this.isValidWidthKeyword(s) || this.isValidVariable(s);
  139. };
  140. Validator.prototype.isValidWidthKeyword = function (s) {
  141. return widthKeywords.indexOf(s) >= 0;
  142. };
  143. Validator.prototype.isValidVendorPrefixedValue = function (s) {
  144. return /^-([A-Za-z0-9]|-)*$/gi.test(s);
  145. };
  146. Validator.prototype.areSameFunction = function (a, b) {
  147. if (!this.isValidFunction(a) || !this.isValidFunction(b))
  148. return false;
  149. var f1name = a.substring(0, a.indexOf('('));
  150. var f2name = b.substring(0, b.indexOf('('));
  151. return f1name === f2name;
  152. };
  153. module.exports = Validator;