utils.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. // Utilities
  2. //
  3. 'use strict';
  4. function _class(obj) { return Object.prototype.toString.call(obj); }
  5. function isString(obj) { return _class(obj) === '[object String]'; }
  6. var _hasOwnProperty = Object.prototype.hasOwnProperty;
  7. function has(object, key) {
  8. return _hasOwnProperty.call(object, key);
  9. }
  10. // Merge objects
  11. //
  12. function assign(obj /*from1, from2, from3, ...*/) {
  13. var sources = Array.prototype.slice.call(arguments, 1);
  14. sources.forEach(function (source) {
  15. if (!source) { return; }
  16. if (typeof source !== 'object') {
  17. throw new TypeError(source + 'must be object');
  18. }
  19. Object.keys(source).forEach(function (key) {
  20. obj[key] = source[key];
  21. });
  22. });
  23. return obj;
  24. }
  25. // Remove element from array and put another array at those position.
  26. // Useful for some operations with tokens
  27. function arrayReplaceAt(src, pos, newElements) {
  28. return [].concat(src.slice(0, pos), newElements, src.slice(pos + 1));
  29. }
  30. ////////////////////////////////////////////////////////////////////////////////
  31. function isValidEntityCode(c) {
  32. /*eslint no-bitwise:0*/
  33. // broken sequence
  34. if (c >= 0xD800 && c <= 0xDFFF) { return false; }
  35. // never used
  36. if (c >= 0xFDD0 && c <= 0xFDEF) { return false; }
  37. if ((c & 0xFFFF) === 0xFFFF || (c & 0xFFFF) === 0xFFFE) { return false; }
  38. // control codes
  39. if (c >= 0x00 && c <= 0x08) { return false; }
  40. if (c === 0x0B) { return false; }
  41. if (c >= 0x0E && c <= 0x1F) { return false; }
  42. if (c >= 0x7F && c <= 0x9F) { return false; }
  43. // out of range
  44. if (c > 0x10FFFF) { return false; }
  45. return true;
  46. }
  47. function fromCodePoint(c) {
  48. /*eslint no-bitwise:0*/
  49. if (c > 0xffff) {
  50. c -= 0x10000;
  51. var surrogate1 = 0xd800 + (c >> 10),
  52. surrogate2 = 0xdc00 + (c & 0x3ff);
  53. return String.fromCharCode(surrogate1, surrogate2);
  54. }
  55. return String.fromCharCode(c);
  56. }
  57. var UNESCAPE_MD_RE = /\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g;
  58. var ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi;
  59. var UNESCAPE_ALL_RE = new RegExp(UNESCAPE_MD_RE.source + '|' + ENTITY_RE.source, 'gi');
  60. var DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i;
  61. var entities = require('./entities');
  62. function replaceEntityPattern(match, name) {
  63. var code = 0;
  64. if (has(entities, name)) {
  65. return entities[name];
  66. }
  67. if (name.charCodeAt(0) === 0x23/* # */ && DIGITAL_ENTITY_TEST_RE.test(name)) {
  68. code = name[1].toLowerCase() === 'x' ?
  69. parseInt(name.slice(2), 16)
  70. :
  71. parseInt(name.slice(1), 10);
  72. if (isValidEntityCode(code)) {
  73. return fromCodePoint(code);
  74. }
  75. }
  76. return match;
  77. }
  78. /*function replaceEntities(str) {
  79. if (str.indexOf('&') < 0) { return str; }
  80. return str.replace(ENTITY_RE, replaceEntityPattern);
  81. }*/
  82. function unescapeMd(str) {
  83. if (str.indexOf('\\') < 0) { return str; }
  84. return str.replace(UNESCAPE_MD_RE, '$1');
  85. }
  86. function unescapeAll(str) {
  87. if (str.indexOf('\\') < 0 && str.indexOf('&') < 0) { return str; }
  88. return str.replace(UNESCAPE_ALL_RE, function (match, escaped, entity) {
  89. if (escaped) { return escaped; }
  90. return replaceEntityPattern(match, entity);
  91. });
  92. }
  93. ////////////////////////////////////////////////////////////////////////////////
  94. var HTML_ESCAPE_TEST_RE = /[&<>"]/;
  95. var HTML_ESCAPE_REPLACE_RE = /[&<>"]/g;
  96. var HTML_REPLACEMENTS = {
  97. '&': '&amp;',
  98. '<': '&lt;',
  99. '>': '&gt;',
  100. '"': '&quot;'
  101. };
  102. function replaceUnsafeChar(ch) {
  103. return HTML_REPLACEMENTS[ch];
  104. }
  105. function escapeHtml(str) {
  106. if (HTML_ESCAPE_TEST_RE.test(str)) {
  107. return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar);
  108. }
  109. return str;
  110. }
  111. ////////////////////////////////////////////////////////////////////////////////
  112. var REGEXP_ESCAPE_RE = /[.?*+^$[\]\\(){}|-]/g;
  113. function escapeRE(str) {
  114. return str.replace(REGEXP_ESCAPE_RE, '\\$&');
  115. }
  116. ////////////////////////////////////////////////////////////////////////////////
  117. function isSpace(code) {
  118. switch (code) {
  119. case 0x09:
  120. case 0x20:
  121. return true;
  122. }
  123. return false;
  124. }
  125. // Zs (unicode class) || [\t\f\v\r\n]
  126. function isWhiteSpace(code) {
  127. if (code >= 0x2000 && code <= 0x200A) { return true; }
  128. switch (code) {
  129. case 0x09: // \t
  130. case 0x0A: // \n
  131. case 0x0B: // \v
  132. case 0x0C: // \f
  133. case 0x0D: // \r
  134. case 0x20:
  135. case 0xA0:
  136. case 0x1680:
  137. case 0x202F:
  138. case 0x205F:
  139. case 0x3000:
  140. return true;
  141. }
  142. return false;
  143. }
  144. ////////////////////////////////////////////////////////////////////////////////
  145. /*eslint-disable max-len*/
  146. var UNICODE_PUNCT_RE = require('uc.micro/categories/P/regex');
  147. // Currently without astral characters support.
  148. function isPunctChar(ch) {
  149. return UNICODE_PUNCT_RE.test(ch);
  150. }
  151. // Markdown ASCII punctuation characters.
  152. //
  153. // !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~
  154. // http://spec.commonmark.org/0.15/#ascii-punctuation-character
  155. //
  156. // Don't confuse with unicode punctuation !!! It lacks some chars in ascii range.
  157. //
  158. function isMdAsciiPunct(ch) {
  159. switch (ch) {
  160. case 0x21/* ! */:
  161. case 0x22/* " */:
  162. case 0x23/* # */:
  163. case 0x24/* $ */:
  164. case 0x25/* % */:
  165. case 0x26/* & */:
  166. case 0x27/* ' */:
  167. case 0x28/* ( */:
  168. case 0x29/* ) */:
  169. case 0x2A/* * */:
  170. case 0x2B/* + */:
  171. case 0x2C/* , */:
  172. case 0x2D/* - */:
  173. case 0x2E/* . */:
  174. case 0x2F/* / */:
  175. case 0x3A/* : */:
  176. case 0x3B/* ; */:
  177. case 0x3C/* < */:
  178. case 0x3D/* = */:
  179. case 0x3E/* > */:
  180. case 0x3F/* ? */:
  181. case 0x40/* @ */:
  182. case 0x5B/* [ */:
  183. case 0x5C/* \ */:
  184. case 0x5D/* ] */:
  185. case 0x5E/* ^ */:
  186. case 0x5F/* _ */:
  187. case 0x60/* ` */:
  188. case 0x7B/* { */:
  189. case 0x7C/* | */:
  190. case 0x7D/* } */:
  191. case 0x7E/* ~ */:
  192. return true;
  193. default:
  194. return false;
  195. }
  196. }
  197. // Hepler to unify [reference labels].
  198. //
  199. function normalizeReference(str) {
  200. // use .toUpperCase() instead of .toLowerCase()
  201. // here to avoid a conflict with Object.prototype
  202. // members (most notably, `__proto__`)
  203. return str.trim().replace(/\s+/g, ' ').toUpperCase();
  204. }
  205. ////////////////////////////////////////////////////////////////////////////////
  206. // Re-export libraries commonly used in both markdown-it and its plugins,
  207. // so plugins won't have to depend on them explicitly, which reduces their
  208. // bundled size (e.g. a browser build).
  209. //
  210. exports.lib = {};
  211. exports.lib.mdurl = require('mdurl');
  212. exports.lib.ucmicro = require('uc.micro');
  213. exports.assign = assign;
  214. exports.isString = isString;
  215. exports.has = has;
  216. exports.unescapeMd = unescapeMd;
  217. exports.unescapeAll = unescapeAll;
  218. exports.isValidEntityCode = isValidEntityCode;
  219. exports.fromCodePoint = fromCodePoint;
  220. // exports.replaceEntities = replaceEntities;
  221. exports.escapeHtml = escapeHtml;
  222. exports.arrayReplaceAt = arrayReplaceAt;
  223. exports.isSpace = isSpace;
  224. exports.isWhiteSpace = isWhiteSpace;
  225. exports.isMdAsciiPunct = isMdAsciiPunct;
  226. exports.isPunctChar = isPunctChar;
  227. exports.escapeRE = escapeRE;
  228. exports.normalizeReference = normalizeReference;