No Description

validator.js 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Validates various CSS property values
  2. var Splitter = require('../text/splitter');
  3. module.exports = (function () {
  4. // Regexes used for stuff
  5. var widthKeywords = ['thin', 'thick', 'medium', 'inherit', 'initial'];
  6. var cssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(px|%|em|rem|in|cm|mm|ex|pt|pc|vw|vh|vmin|vmax|)|auto|inherit)';
  7. var cssCalcRegexStr = '(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)';
  8. var cssFunctionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(([A-Z]|[0-9]|\\ |\\,|\\#|\\+|\\-|\\%|\\.|\\(|\\))*\\)';
  9. var cssFunctionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(([A-Z]|[0-9]|\\ |\\,|\\#|\\+|\\-|\\%|\\.|\\(|\\))*\\)';
  10. var cssVariableRegexStr = 'var\\(\\-\\-[^\\)]+\\)';
  11. var cssFunctionAnyRegexStr = '(' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
  12. var cssUnitOrCalcRegexStr = '(' + cssUnitRegexStr + '|' + cssCalcRegexStr + ')';
  13. var cssUnitAnyRegexStr = '(none|' + widthKeywords.join('|') + '|' + cssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
  14. var cssFunctionNoVendorRegex = new RegExp('^' + cssFunctionNoVendorRegexStr + '$', 'i');
  15. var cssFunctionVendorRegex = new RegExp('^' + cssFunctionVendorRegexStr + '$', 'i');
  16. var cssVariableRegex = new RegExp('^' + cssVariableRegexStr + '$', 'i');
  17. var cssFunctionAnyRegex = new RegExp('^' + cssFunctionAnyRegexStr + '$', 'i');
  18. var cssUnitRegex = new RegExp('^' + cssUnitRegexStr + '$', 'i');
  19. var cssUnitOrCalcRegex = new RegExp('^' + cssUnitOrCalcRegexStr + '$', 'i');
  20. var cssUnitAnyRegex = new RegExp('^' + cssUnitAnyRegexStr + '$', 'i');
  21. var backgroundRepeatKeywords = ['repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'inherit'];
  22. var backgroundAttachmentKeywords = ['inherit', 'scroll', 'fixed', 'local'];
  23. var backgroundPositionKeywords = ['center', 'top', 'bottom', 'left', 'right'];
  24. var backgroundSizeKeywords = ['contain', 'cover'];
  25. 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'];
  26. var listStylePositionKeywords = ['inside', 'outside', 'inherit'];
  27. var outlineStyleKeywords = ['auto', 'inherit', 'hidden', 'none', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'];
  28. var validator = {
  29. isValidHexColor: function (s) {
  30. return (s.length === 4 || s.length === 7) && s[0] === '#';
  31. },
  32. isValidRgbaColor: function (s) {
  33. s = s.split(' ').join('');
  34. return s.length > 0 && s.indexOf('rgba(') === 0 && s.indexOf(')') === s.length - 1;
  35. },
  36. isValidHslaColor: function (s) {
  37. s = s.split(' ').join('');
  38. return s.length > 0 && s.indexOf('hsla(') === 0 && s.indexOf(')') === s.length - 1;
  39. },
  40. isValidNamedColor: function (s) {
  41. // We don't really check if it's a valid color value, but allow any letters in it
  42. return s !== 'auto' && (s === 'transparent' || s === 'inherit' || /^[a-zA-Z]+$/.test(s));
  43. },
  44. isValidVariable: function(s) {
  45. return cssVariableRegex.test(s);
  46. },
  47. isValidColor: function (s) {
  48. return validator.isValidNamedColor(s) || validator.isValidHexColor(s) || validator.isValidRgbaColor(s) || validator.isValidHslaColor(s) || validator.isValidVariable(s);
  49. },
  50. isValidUrl: function (s) {
  51. // NOTE: at this point all URLs are replaced with placeholders by clean-css, so we check for those placeholders
  52. return s.indexOf('__ESCAPED_URL_CLEAN_CSS') === 0;
  53. },
  54. isValidUnit: function (s) {
  55. return cssUnitAnyRegex.test(s);
  56. },
  57. isValidUnitWithoutFunction: function (s) {
  58. return cssUnitRegex.test(s);
  59. },
  60. isValidFunctionWithoutVendorPrefix: function (s) {
  61. return cssFunctionNoVendorRegex.test(s);
  62. },
  63. isValidFunctionWithVendorPrefix: function (s) {
  64. return cssFunctionVendorRegex.test(s);
  65. },
  66. isValidFunction: function (s) {
  67. return cssFunctionAnyRegex.test(s);
  68. },
  69. isValidBackgroundRepeat: function (s) {
  70. return backgroundRepeatKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
  71. },
  72. isValidBackgroundAttachment: function (s) {
  73. return backgroundAttachmentKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
  74. },
  75. isValidBackgroundPositionPart: function (s) {
  76. return backgroundPositionKeywords.indexOf(s) >= 0 || cssUnitOrCalcRegex.test(s) || validator.isValidVariable(s);
  77. },
  78. isValidBackgroundPosition: function (s) {
  79. if (s === 'inherit')
  80. return true;
  81. var parts = s.split(' ');
  82. for (var i = 0, l = parts.length; i < l; i++) {
  83. if (parts[i] === '')
  84. continue;
  85. if (validator.isValidBackgroundPositionPart(parts[i]) || validator.isValidVariable(parts[i]))
  86. continue;
  87. return false;
  88. }
  89. return true;
  90. },
  91. isValidBackgroundSizePart: function(s) {
  92. return backgroundSizeKeywords.indexOf(s) >= 0 || cssUnitRegex.test(s) || validator.isValidVariable(s);
  93. },
  94. isValidBackgroundPositionAndSize: function(s) {
  95. if (s.indexOf('/') < 0)
  96. return false;
  97. var twoParts = new Splitter('/').split(s);
  98. return validator.isValidBackgroundSizePart(twoParts.pop()) && validator.isValidBackgroundPositionPart(twoParts.pop());
  99. },
  100. isValidListStyleType: function (s) {
  101. return listStyleTypeKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
  102. },
  103. isValidListStylePosition: function (s) {
  104. return listStylePositionKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
  105. },
  106. isValidOutlineColor: function (s) {
  107. return s === 'invert' || validator.isValidColor(s) || validator.isValidVendorPrefixedValue(s);
  108. },
  109. isValidOutlineStyle: function (s) {
  110. return outlineStyleKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
  111. },
  112. isValidOutlineWidth: function (s) {
  113. return validator.isValidUnit(s) || widthKeywords.indexOf(s) >= 0 || validator.isValidVariable(s);
  114. },
  115. isValidVendorPrefixedValue: function (s) {
  116. return /^-([A-Za-z0-9]|-)*$/gi.test(s);
  117. },
  118. areSameFunction: function (a, b) {
  119. if (!validator.isValidFunction(a) || !validator.isValidFunction(b))
  120. return false;
  121. var f1name = a.substring(0, a.indexOf('('));
  122. var f2name = b.substring(0, b.indexOf('('));
  123. return f1name === f2name;
  124. }
  125. };
  126. return validator;
  127. })();