jsesc.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /*! http://mths.be/jsesc v0.5.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 forOwn = function(object, callback) {
  18. var key;
  19. for (key in object) {
  20. if (hasOwnProperty.call(object, key)) {
  21. callback(key, object[key]);
  22. }
  23. }
  24. };
  25. var extend = function(destination, source) {
  26. if (!source) {
  27. return destination;
  28. }
  29. forOwn(source, function(key, value) {
  30. destination[key] = value;
  31. });
  32. return destination;
  33. };
  34. var forEach = function(array, callback) {
  35. var length = array.length;
  36. var index = -1;
  37. while (++index < length) {
  38. callback(array[index]);
  39. }
  40. };
  41. var toString = object.toString;
  42. var isArray = function(value) {
  43. return toString.call(value) == '[object Array]';
  44. };
  45. var isObject = function(value) {
  46. // This is a very simple check, but it’s good enough for what we need.
  47. return toString.call(value) == '[object Object]';
  48. };
  49. var isString = function(value) {
  50. return typeof value == 'string' ||
  51. toString.call(value) == '[object String]';
  52. };
  53. var isFunction = function(value) {
  54. // In a perfect world, the `typeof` check would be sufficient. However,
  55. // in Chrome 1–12, `typeof /x/ == 'object'`, and in IE 6–8
  56. // `typeof alert == 'object'` and similar for other host objects.
  57. return typeof value == 'function' ||
  58. toString.call(value) == '[object Function]';
  59. };
  60. /*--------------------------------------------------------------------------*/
  61. // http://mathiasbynens.be/notes/javascript-escapes#single
  62. var singleEscapes = {
  63. '"': '\\"',
  64. '\'': '\\\'',
  65. '\\': '\\\\',
  66. '\b': '\\b',
  67. '\f': '\\f',
  68. '\n': '\\n',
  69. '\r': '\\r',
  70. '\t': '\\t'
  71. // `\v` is omitted intentionally, because in IE < 9, '\v' == 'v'.
  72. // '\v': '\\x0B'
  73. };
  74. var regexSingleEscape = /["'\\\b\f\n\r\t]/;
  75. var regexDigit = /[0-9]/;
  76. var regexWhitelist = /[ !#-&\(-\[\]-~]/;
  77. var jsesc = function(argument, options) {
  78. // Handle options
  79. var defaults = {
  80. 'escapeEverything': false,
  81. 'quotes': 'single',
  82. 'wrap': false,
  83. 'es6': false,
  84. 'json': false,
  85. 'compact': true,
  86. 'indent': '\t',
  87. '__indent__': ''
  88. };
  89. var json = options && options.json;
  90. if (json) {
  91. defaults.quotes = 'double';
  92. defaults.wrap = true;
  93. }
  94. options = extend(defaults, options);
  95. if (options.quotes != 'single' && options.quotes != 'double') {
  96. options.quotes = 'single';
  97. }
  98. var quote = options.quotes == 'double' ? '"' : '\'';
  99. var compact = options.compact;
  100. var indent = options.indent;
  101. var oldIndent;
  102. var newLine = compact ? '' : '\n';
  103. var result;
  104. var isEmpty = true;
  105. if (json && argument && isFunction(argument.toJSON)) {
  106. argument = argument.toJSON();
  107. }
  108. if (!isString(argument)) {
  109. if (isArray(argument)) {
  110. result = [];
  111. options.wrap = true;
  112. oldIndent = options.__indent__;
  113. indent += oldIndent;
  114. options.__indent__ = indent;
  115. forEach(argument, function(value) {
  116. isEmpty = false;
  117. result.push(
  118. (compact ? '' : indent) +
  119. jsesc(value, options)
  120. );
  121. });
  122. if (isEmpty) {
  123. return '[]';
  124. }
  125. return '[' + newLine + result.join(',' + newLine) + newLine +
  126. (compact ? '' : oldIndent) + ']';
  127. } else if (!isObject(argument)) {
  128. if (json) {
  129. // For some values (e.g. `undefined`, `function` objects),
  130. // `JSON.stringify(value)` returns `undefined` (which isn’t valid
  131. // JSON) instead of `'null'`.
  132. return JSON.stringify(argument) || 'null';
  133. }
  134. return String(argument);
  135. } else { // it’s an object
  136. result = [];
  137. options.wrap = true;
  138. oldIndent = options.__indent__;
  139. indent += oldIndent;
  140. options.__indent__ = indent;
  141. forOwn(argument, function(key, value) {
  142. isEmpty = false;
  143. result.push(
  144. (compact ? '' : indent) +
  145. jsesc(key, options) + ':' +
  146. (compact ? '' : ' ') +
  147. jsesc(value, options)
  148. );
  149. });
  150. if (isEmpty) {
  151. return '{}';
  152. }
  153. return '{' + newLine + result.join(',' + newLine) + newLine +
  154. (compact ? '' : oldIndent) + '}';
  155. }
  156. }
  157. var string = argument;
  158. // Loop over each code unit in the string and escape it
  159. var index = -1;
  160. var length = string.length;
  161. var first;
  162. var second;
  163. var codePoint;
  164. result = '';
  165. while (++index < length) {
  166. var character = string.charAt(index);
  167. if (options.es6) {
  168. first = string.charCodeAt(index);
  169. if ( // check if it’s the start of a surrogate pair
  170. first >= 0xD800 && first <= 0xDBFF && // high surrogate
  171. length > index + 1 // there is a next code unit
  172. ) {
  173. second = string.charCodeAt(index + 1);
  174. if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
  175. // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
  176. codePoint = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
  177. result += '\\u{' + codePoint.toString(16).toUpperCase() + '}';
  178. index++;
  179. continue;
  180. }
  181. }
  182. }
  183. if (!options.escapeEverything) {
  184. if (regexWhitelist.test(character)) {
  185. // It’s a printable ASCII character that is not `"`, `'` or `\`,
  186. // so don’t escape it.
  187. result += character;
  188. continue;
  189. }
  190. if (character == '"') {
  191. result += quote == character ? '\\"' : character;
  192. continue;
  193. }
  194. if (character == '\'') {
  195. result += quote == character ? '\\\'' : character;
  196. continue;
  197. }
  198. }
  199. if (
  200. character == '\0' &&
  201. !json &&
  202. !regexDigit.test(string.charAt(index + 1))
  203. ) {
  204. result += '\\0';
  205. continue;
  206. }
  207. if (regexSingleEscape.test(character)) {
  208. // no need for a `hasOwnProperty` check here
  209. result += singleEscapes[character];
  210. continue;
  211. }
  212. var charCode = character.charCodeAt(0);
  213. var hexadecimal = charCode.toString(16).toUpperCase();
  214. var longhand = hexadecimal.length > 2 || json;
  215. var escaped = '\\' + (longhand ? 'u' : 'x') +
  216. ('0000' + hexadecimal).slice(longhand ? -4 : -2);
  217. result += escaped;
  218. continue;
  219. }
  220. if (options.wrap) {
  221. result = quote + result + quote;
  222. }
  223. return result;
  224. };
  225. jsesc.version = '0.5.0';
  226. /*--------------------------------------------------------------------------*/
  227. // Some AMD build optimizers, like r.js, check for specific condition patterns
  228. // like the following:
  229. if (
  230. typeof define == 'function' &&
  231. typeof define.amd == 'object' &&
  232. define.amd
  233. ) {
  234. define(function() {
  235. return jsesc;
  236. });
  237. } else if (freeExports && !freeExports.nodeType) {
  238. if (freeModule) { // in Node.js or RingoJS v0.8.0+
  239. freeModule.exports = jsesc;
  240. } else { // in Narwhal or RingoJS v0.7.0-
  241. freeExports.jsesc = jsesc;
  242. }
  243. } else { // in Rhino or a web browser
  244. root.jsesc = jsesc;
  245. }
  246. }(this));