cssesc.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /*! https://mths.be/cssesc v1.0.1 by @mathias */
  2. 'use strict';
  3. var object = {};
  4. var hasOwnProperty = object.hasOwnProperty;
  5. var merge = function merge(options, defaults) {
  6. if (!options) {
  7. return defaults;
  8. }
  9. var result = {};
  10. for (var key in defaults) {
  11. // `if (defaults.hasOwnProperty(key) { … }` is not needed here, since
  12. // only recognized option names are used.
  13. result[key] = hasOwnProperty.call(options, key) ? options[key] : defaults[key];
  14. }
  15. return result;
  16. };
  17. var regexAnySingleEscape = /[ -,\.\/;-@\[-\^`\{-~]/;
  18. var regexSingleEscape = /[ -,\.\/;-@\[\]\^`\{-~]/;
  19. var regexAlwaysEscape = /['"\\]/;
  20. var regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g;
  21. // https://mathiasbynens.be/notes/css-escapes#css
  22. var cssesc = function cssesc(string, options) {
  23. options = merge(options, cssesc.options);
  24. if (options.quotes != 'single' && options.quotes != 'double') {
  25. options.quotes = 'single';
  26. }
  27. var quote = options.quotes == 'double' ? '"' : '\'';
  28. var isIdentifier = options.isIdentifier;
  29. var firstChar = string.charAt(0);
  30. var output = '';
  31. var counter = 0;
  32. var length = string.length;
  33. while (counter < length) {
  34. var character = string.charAt(counter++);
  35. var codePoint = character.charCodeAt();
  36. var value = void 0;
  37. // If it’s not a printable ASCII character…
  38. if (codePoint < 0x20 || codePoint > 0x7E) {
  39. if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) {
  40. // It’s a high surrogate, and there is a next character.
  41. var extra = string.charCodeAt(counter++);
  42. if ((extra & 0xFC00) == 0xDC00) {
  43. // next character is low surrogate
  44. codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;
  45. } else {
  46. // It’s an unmatched surrogate; only append this code unit, in case
  47. // the next code unit is the high surrogate of a surrogate pair.
  48. counter--;
  49. }
  50. }
  51. value = '\\' + codePoint.toString(16).toUpperCase() + ' ';
  52. } else {
  53. if (options.escapeEverything) {
  54. if (regexAnySingleEscape.test(character)) {
  55. value = '\\' + character;
  56. } else {
  57. value = '\\' + codePoint.toString(16).toUpperCase() + ' ';
  58. }
  59. // Note: `:` could be escaped as `\:`, but that fails in IE < 8.
  60. } else if (/[\t\n\f\r\x0B:]/.test(character)) {
  61. if (!isIdentifier && character == ':') {
  62. value = character;
  63. } else {
  64. value = '\\' + codePoint.toString(16).toUpperCase() + ' ';
  65. }
  66. } else if (character == '\\' || !isIdentifier && (character == '"' && quote == character || character == '\'' && quote == character) || isIdentifier && regexSingleEscape.test(character)) {
  67. value = '\\' + character;
  68. } else {
  69. value = character;
  70. }
  71. }
  72. output += value;
  73. }
  74. if (isIdentifier) {
  75. if (/^_/.test(output)) {
  76. // Prevent IE6 from ignoring the rule altogether (in case this is for an
  77. // identifier used as a selector)
  78. output = '\\_' + output.slice(1);
  79. } else if (/^-[-\d]/.test(output)) {
  80. output = '\\-' + output.slice(1);
  81. } else if (/\d/.test(firstChar)) {
  82. output = '\\3' + firstChar + ' ' + output.slice(1);
  83. }
  84. }
  85. // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
  86. // since they’re redundant. Note that this is only possible if the escape
  87. // sequence isn’t preceded by an odd number of backslashes.
  88. output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) {
  89. if ($1 && $1.length % 2) {
  90. // It’s not safe to remove the space, so don’t.
  91. return $0;
  92. }
  93. // Strip the space.
  94. return ($1 || '') + $2;
  95. });
  96. if (!isIdentifier && options.wrap) {
  97. return quote + output + quote;
  98. }
  99. return output;
  100. };
  101. // Expose default options (so they can be overridden globally).
  102. cssesc.options = {
  103. 'escapeEverything': false,
  104. 'isIdentifier': false,
  105. 'quotes': 'single',
  106. 'wrap': false
  107. };
  108. cssesc.version = '1.0.1';
  109. module.exports = cssesc;