cssesc.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*! http://mths.be/cssesc v0.1.0 by @mathias */
  2. ;(function(root) {
  3. // Detect free variables `exports`
  4. var freeExports = typeof exports == 'object' && exports;
  5. // Detect free variable `module`
  6. var freeModule = typeof module == 'object' && module &&
  7. module.exports == freeExports && module;
  8. // Detect free variable `global`, from Node.js or Browserified code,
  9. // and use it as `root`
  10. var freeGlobal = typeof global == 'object' && global;
  11. if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
  12. root = freeGlobal;
  13. }
  14. /*--------------------------------------------------------------------------*/
  15. var object = {};
  16. var hasOwnProperty = object.hasOwnProperty;
  17. var merge = function(options, defaults) {
  18. if (!options) {
  19. return defaults;
  20. }
  21. var key;
  22. var result = {};
  23. for (key in defaults) {
  24. // `if (defaults.hasOwnProperty(key) { … }` is not needed here, since
  25. // only recognized option names are used
  26. result[key] = hasOwnProperty.call(options, key)
  27. ? options[key]
  28. : defaults[key];
  29. }
  30. return result;
  31. };
  32. /*--------------------------------------------------------------------------*/
  33. var regexAnySingleEscape = /[\x20-\x2C\x2E\x2F\x3B-\x40\x5B-\x5E\x60\x7B-\x7E]/;
  34. var regexSingleEscape = /[\x20\x21\x23-\x26\x28-\x2C\x2E\x2F\x3B-\x40\x5B\x5D\x5E\x60\x7B-\x7E]/;
  35. var regexAlwaysEscape = /['"\\]/;
  36. var regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g;
  37. // http://mathiasbynens.be/notes/css-escapes#css
  38. var cssesc = function(string, options) {
  39. // Handle options
  40. options = merge(options, cssesc.options);
  41. if (options.quotes != 'single' && options.quotes != 'double') {
  42. options.quotes = 'single';
  43. }
  44. var quote = options.quotes == 'double' ? '"' : '\'';
  45. var isIdentifier = options.isIdentifier;
  46. var firstChar = string.charAt(0);
  47. var output = '';
  48. var counter = 0;
  49. var length = string.length;
  50. var value;
  51. var character;
  52. var codePoint;
  53. var extra; // used for potential low surrogates
  54. while (counter < length) {
  55. character = string.charAt(counter++);
  56. codePoint = character.charCodeAt();
  57. // if it’s not a printable ASCII character
  58. if (codePoint < 0x20 || codePoint > 0x7E) {
  59. if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) {
  60. // high surrogate, and there is a next character
  61. extra = string.charCodeAt(counter++);
  62. if ((extra & 0xFC00) == 0xDC00) { // next character is low surrogate
  63. codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;
  64. } else {
  65. // unmatched surrogate; only append this code unit, in case the next
  66. // code unit is the high surrogate of a surrogate pair
  67. counter--;
  68. }
  69. }
  70. value = '\\' + codePoint.toString(16).toUpperCase() + ' ';
  71. } else {
  72. if (options.escapeEverything) {
  73. if (regexAnySingleEscape.test(character)) {
  74. value = '\\' + character;
  75. } else {
  76. value = '\\' + codePoint.toString(16).toUpperCase() + ' ';
  77. }
  78. // `:` can be escaped as `\:`, but that fails in IE < 8
  79. } else if (/[\t\n\f\r\x0B:]/.test(character)) {
  80. if (!isIdentifier && character == ':') {
  81. value = character;
  82. } else {
  83. value = '\\' + codePoint.toString(16).toUpperCase() + ' ';
  84. }
  85. } else if (
  86. character == '\\' ||
  87. (
  88. !isIdentifier &&
  89. (
  90. (character == '"' && quote == character) ||
  91. (character == '\'' && quote == character)
  92. )
  93. ) ||
  94. (isIdentifier && regexSingleEscape.test(character))
  95. ) {
  96. value = '\\' + character;
  97. } else {
  98. value = character;
  99. }
  100. }
  101. output += value;
  102. }
  103. if (isIdentifier) {
  104. if (/^_/.test(output)) {
  105. // Prevent IE6 from ignoring the rule altogether (in case this is for an
  106. // identifier used as a selector)
  107. output = '\\_' + output.slice(1);
  108. } else if (/^-[-\d]/.test(output)) {
  109. output = '\\-' + output.slice(1);
  110. } else if (/\d/.test(firstChar)) {
  111. output = '\\3' + firstChar + ' ' + output.slice(1);
  112. }
  113. }
  114. // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
  115. // since they’re redundant. Note that this is only possible if the escape
  116. // sequence isn’t preceded by an odd number of backslashes.
  117. output = output.replace(regexExcessiveSpaces, function($0, $1, $2) {
  118. if ($1 && $1.length % 2) {
  119. // it’s not safe to remove the space, so don’t
  120. return $0;
  121. }
  122. // strip the space
  123. return ($1 || '') + $2;
  124. });
  125. if (!isIdentifier && options.wrap) {
  126. return quote + output + quote;
  127. }
  128. return output;
  129. };
  130. // Expose default options (so they can be overridden globally)
  131. cssesc.options = {
  132. 'escapeEverything': false,
  133. 'isIdentifier': false,
  134. 'quotes': 'single',
  135. 'wrap': false
  136. };
  137. cssesc.version = '0.1.0';
  138. /*--------------------------------------------------------------------------*/
  139. // Some AMD build optimizers, like r.js, check for specific condition patterns
  140. // like the following:
  141. if (
  142. typeof define == 'function' &&
  143. typeof define.amd == 'object' &&
  144. define.amd
  145. ) {
  146. define(function() {
  147. return cssesc;
  148. });
  149. } else if (freeExports && !freeExports.nodeType) {
  150. if (freeModule) { // in Node.js or RingoJS v0.8.0+
  151. freeModule.exports = cssesc;
  152. } else { // in Narwhal or RingoJS v0.7.0-
  153. freeExports.cssesc = cssesc;
  154. }
  155. } else { // in Rhino or a web browser
  156. root.cssesc = cssesc;
  157. }
  158. }(this));