jsesc.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*! https://mths.be/jsesc v1.3.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 isNumber = function(value) {
  54. return typeof value == 'number' ||
  55. toString.call(value) == '[object Number]';
  56. };
  57. var isFunction = function(value) {
  58. // In a perfect world, the `typeof` check would be sufficient. However,
  59. // in Chrome 1–12, `typeof /x/ == 'object'`, and in IE 6–8
  60. // `typeof alert == 'object'` and similar for other host objects.
  61. return typeof value == 'function' ||
  62. toString.call(value) == '[object Function]';
  63. };
  64. var isMap = function(value) {
  65. return toString.call(value) == '[object Map]';
  66. };
  67. var isSet = function(value) {
  68. return toString.call(value) == '[object Set]';
  69. };
  70. /*--------------------------------------------------------------------------*/
  71. // https://mathiasbynens.be/notes/javascript-escapes#single
  72. var singleEscapes = {
  73. '"': '\\"',
  74. '\'': '\\\'',
  75. '\\': '\\\\',
  76. '\b': '\\b',
  77. '\f': '\\f',
  78. '\n': '\\n',
  79. '\r': '\\r',
  80. '\t': '\\t'
  81. // `\v` is omitted intentionally, because in IE < 9, '\v' == 'v'.
  82. // '\v': '\\x0B'
  83. };
  84. var regexSingleEscape = /["'\\\b\f\n\r\t]/;
  85. var regexDigit = /[0-9]/;
  86. var regexWhitelist = /[ !#-&\(-\[\]-~]/;
  87. var jsesc = function(argument, options) {
  88. // Handle options
  89. var defaults = {
  90. 'escapeEverything': false,
  91. 'escapeEtago': false,
  92. 'quotes': 'single',
  93. 'wrap': false,
  94. 'es6': false,
  95. 'json': false,
  96. 'compact': true,
  97. 'lowercaseHex': false,
  98. 'numbers': 'decimal',
  99. 'indent': '\t',
  100. '__indent__': '',
  101. '__inline1__': false,
  102. '__inline2__': false
  103. };
  104. var json = options && options.json;
  105. if (json) {
  106. defaults.quotes = 'double';
  107. defaults.wrap = true;
  108. }
  109. options = extend(defaults, options);
  110. if (options.quotes != 'single' && options.quotes != 'double') {
  111. options.quotes = 'single';
  112. }
  113. var quote = options.quotes == 'double' ? '"' : '\'';
  114. var compact = options.compact;
  115. var indent = options.indent;
  116. var lowercaseHex = options.lowercaseHex;
  117. var oldIndent = '';
  118. var inline1 = options.__inline1__;
  119. var inline2 = options.__inline2__;
  120. var newLine = compact ? '' : '\n';
  121. var result;
  122. var isEmpty = true;
  123. var useBinNumbers = options.numbers == 'binary';
  124. var useOctNumbers = options.numbers == 'octal';
  125. var useDecNumbers = options.numbers == 'decimal';
  126. var useHexNumbers = options.numbers == 'hexadecimal';
  127. if (json && argument && isFunction(argument.toJSON)) {
  128. argument = argument.toJSON();
  129. }
  130. if (!isString(argument)) {
  131. if (isMap(argument)) {
  132. if (argument.size == 0) {
  133. return 'new Map()';
  134. }
  135. if (!compact) {
  136. options.__inline1__ = true;
  137. }
  138. return 'new Map(' + jsesc(Array.from(argument), options) + ')';
  139. }
  140. if (isSet(argument)) {
  141. if (argument.size == 0) {
  142. return 'new Set()';
  143. }
  144. return 'new Set(' + jsesc(Array.from(argument), options) + ')';
  145. }
  146. if (isArray(argument)) {
  147. result = [];
  148. options.wrap = true;
  149. if (inline1) {
  150. options.__inline1__ = false;
  151. options.__inline2__ = true;
  152. } else {
  153. oldIndent = options.__indent__;
  154. indent += oldIndent;
  155. options.__indent__ = indent;
  156. }
  157. forEach(argument, function(value) {
  158. isEmpty = false;
  159. if (inline2) {
  160. options.__inline2__ = false;
  161. }
  162. result.push(
  163. (compact || inline2 ? '' : indent) +
  164. jsesc(value, options)
  165. );
  166. });
  167. if (isEmpty) {
  168. return '[]';
  169. }
  170. if (inline2) {
  171. return '[' + result.join(', ') + ']';
  172. }
  173. return '[' + newLine + result.join(',' + newLine) + newLine +
  174. (compact ? '' : oldIndent) + ']';
  175. } else if (isNumber(argument)) {
  176. if (json) {
  177. // Some number values (e.g. `Infinity`) cannot be represented in JSON.
  178. return JSON.stringify(argument);
  179. }
  180. if (useDecNumbers) {
  181. return String(argument);
  182. }
  183. if (useHexNumbers) {
  184. var tmp = argument.toString(16);
  185. if (!lowercaseHex) {
  186. tmp = tmp.toUpperCase();
  187. }
  188. return '0x' + tmp;
  189. }
  190. if (useBinNumbers) {
  191. return '0b' + argument.toString(2);
  192. }
  193. if (useOctNumbers) {
  194. return '0o' + argument.toString(8);
  195. }
  196. } else if (!isObject(argument)) {
  197. if (json) {
  198. // For some values (e.g. `undefined`, `function` objects),
  199. // `JSON.stringify(value)` returns `undefined` (which isn’t valid
  200. // JSON) instead of `'null'`.
  201. return JSON.stringify(argument) || 'null';
  202. }
  203. return String(argument);
  204. } else { // it’s an object
  205. result = [];
  206. options.wrap = true;
  207. oldIndent = options.__indent__;
  208. indent += oldIndent;
  209. options.__indent__ = indent;
  210. forOwn(argument, function(key, value) {
  211. isEmpty = false;
  212. result.push(
  213. (compact ? '' : indent) +
  214. jsesc(key, options) + ':' +
  215. (compact ? '' : ' ') +
  216. jsesc(value, options)
  217. );
  218. });
  219. if (isEmpty) {
  220. return '{}';
  221. }
  222. return '{' + newLine + result.join(',' + newLine) + newLine +
  223. (compact ? '' : oldIndent) + '}';
  224. }
  225. }
  226. var string = argument;
  227. // Loop over each code unit in the string and escape it
  228. var index = -1;
  229. var length = string.length;
  230. var first;
  231. var second;
  232. var codePoint;
  233. result = '';
  234. while (++index < length) {
  235. var character = string.charAt(index);
  236. if (options.es6) {
  237. first = string.charCodeAt(index);
  238. if ( // check if it’s the start of a surrogate pair
  239. first >= 0xD800 && first <= 0xDBFF && // high surrogate
  240. length > index + 1 // there is a next code unit
  241. ) {
  242. second = string.charCodeAt(index + 1);
  243. if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
  244. // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
  245. codePoint = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
  246. var hexadecimal = codePoint.toString(16);
  247. if (!lowercaseHex) {
  248. hexadecimal = hexadecimal.toUpperCase();
  249. }
  250. result += '\\u{' + hexadecimal + '}';
  251. index++;
  252. continue;
  253. }
  254. }
  255. }
  256. if (!options.escapeEverything) {
  257. if (regexWhitelist.test(character)) {
  258. // It’s a printable ASCII character that is not `"`, `'` or `\`,
  259. // so don’t escape it.
  260. result += character;
  261. continue;
  262. }
  263. if (character == '"') {
  264. result += quote == character ? '\\"' : character;
  265. continue;
  266. }
  267. if (character == '\'') {
  268. result += quote == character ? '\\\'' : character;
  269. continue;
  270. }
  271. }
  272. if (
  273. character == '\0' &&
  274. !json &&
  275. !regexDigit.test(string.charAt(index + 1))
  276. ) {
  277. result += '\\0';
  278. continue;
  279. }
  280. if (regexSingleEscape.test(character)) {
  281. // no need for a `hasOwnProperty` check here
  282. result += singleEscapes[character];
  283. continue;
  284. }
  285. var charCode = character.charCodeAt(0);
  286. var hexadecimal = charCode.toString(16);
  287. if (!lowercaseHex) {
  288. hexadecimal = hexadecimal.toUpperCase();
  289. }
  290. var longhand = hexadecimal.length > 2 || json;
  291. var escaped = '\\' + (longhand ? 'u' : 'x') +
  292. ('0000' + hexadecimal).slice(longhand ? -4 : -2);
  293. result += escaped;
  294. continue;
  295. }
  296. if (options.wrap) {
  297. result = quote + result + quote;
  298. }
  299. if (options.escapeEtago) {
  300. // https://mathiasbynens.be/notes/etago
  301. return result.replace(/<\/(script|style)/gi, '<\\/$1');
  302. }
  303. return result;
  304. };
  305. jsesc.version = '1.3.0';
  306. /*--------------------------------------------------------------------------*/
  307. // Some AMD build optimizers, like r.js, check for specific condition patterns
  308. // like the following:
  309. if (
  310. typeof define == 'function' &&
  311. typeof define.amd == 'object' &&
  312. define.amd
  313. ) {
  314. define(function() {
  315. return jsesc;
  316. });
  317. } else if (freeExports && !freeExports.nodeType) {
  318. if (freeModule) { // in Node.js or RingoJS v0.8.0+
  319. freeModule.exports = jsesc;
  320. } else { // in Narwhal or RingoJS v0.7.0-
  321. freeExports.jsesc = jsesc;
  322. }
  323. } else { // in Rhino or a web browser
  324. root.jsesc = jsesc;
  325. }
  326. }(this));