marked.esm.js 81 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950
  1. /**
  2. * marked v5.1.0 - a markdown parser
  3. * Copyright (c) 2011-2023, Christopher Jeffrey. (MIT Licensed)
  4. * https://github.com/markedjs/marked
  5. */
  6. /**
  7. * DO NOT EDIT THIS FILE
  8. * The code in this file is generated from files in ./src/
  9. */
  10. function getDefaults() {
  11. return {
  12. async: false,
  13. baseUrl: null,
  14. breaks: false,
  15. extensions: null,
  16. gfm: true,
  17. headerIds: true,
  18. headerPrefix: '',
  19. highlight: null,
  20. hooks: null,
  21. langPrefix: 'language-',
  22. mangle: true,
  23. pedantic: false,
  24. renderer: null,
  25. sanitize: false,
  26. sanitizer: null,
  27. silent: false,
  28. smartypants: false,
  29. tokenizer: null,
  30. walkTokens: null,
  31. xhtml: false
  32. };
  33. }
  34. let defaults = getDefaults();
  35. function changeDefaults(newDefaults) {
  36. defaults = newDefaults;
  37. }
  38. /**
  39. * Helpers
  40. */
  41. const escapeTest = /[&<>"']/;
  42. const escapeReplace = new RegExp(escapeTest.source, 'g');
  43. const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
  44. const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g');
  45. const escapeReplacements = {
  46. '&': '&amp;',
  47. '<': '&lt;',
  48. '>': '&gt;',
  49. '"': '&quot;',
  50. "'": '&#39;'
  51. };
  52. const getEscapeReplacement = (ch) => escapeReplacements[ch];
  53. function escape(html, encode) {
  54. if (encode) {
  55. if (escapeTest.test(html)) {
  56. return html.replace(escapeReplace, getEscapeReplacement);
  57. }
  58. } else {
  59. if (escapeTestNoEncode.test(html)) {
  60. return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
  61. }
  62. }
  63. return html;
  64. }
  65. const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
  66. /**
  67. * @param {string} html
  68. */
  69. function unescape(html) {
  70. // explicitly match decimal, hex, and named HTML entities
  71. return html.replace(unescapeTest, (_, n) => {
  72. n = n.toLowerCase();
  73. if (n === 'colon') return ':';
  74. if (n.charAt(0) === '#') {
  75. return n.charAt(1) === 'x'
  76. ? String.fromCharCode(parseInt(n.substring(2), 16))
  77. : String.fromCharCode(+n.substring(1));
  78. }
  79. return '';
  80. });
  81. }
  82. const caret = /(^|[^\[])\^/g;
  83. /**
  84. * @param {string | RegExp} regex
  85. * @param {string} opt
  86. */
  87. function edit(regex, opt) {
  88. regex = typeof regex === 'string' ? regex : regex.source;
  89. opt = opt || '';
  90. const obj = {
  91. replace: (name, val) => {
  92. val = val.source || val;
  93. val = val.replace(caret, '$1');
  94. regex = regex.replace(name, val);
  95. return obj;
  96. },
  97. getRegex: () => {
  98. return new RegExp(regex, opt);
  99. }
  100. };
  101. return obj;
  102. }
  103. const nonWordAndColonTest = /[^\w:]/g;
  104. const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
  105. /**
  106. * @param {boolean} sanitize
  107. * @param {string} base
  108. * @param {string} href
  109. */
  110. function cleanUrl(sanitize, base, href) {
  111. if (sanitize) {
  112. let prot;
  113. try {
  114. prot = decodeURIComponent(unescape(href))
  115. .replace(nonWordAndColonTest, '')
  116. .toLowerCase();
  117. } catch (e) {
  118. return null;
  119. }
  120. if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
  121. return null;
  122. }
  123. }
  124. if (base && !originIndependentUrl.test(href)) {
  125. href = resolveUrl(base, href);
  126. }
  127. try {
  128. href = encodeURI(href).replace(/%25/g, '%');
  129. } catch (e) {
  130. return null;
  131. }
  132. return href;
  133. }
  134. const baseUrls = {};
  135. const justDomain = /^[^:]+:\/*[^/]*$/;
  136. const protocol = /^([^:]+:)[\s\S]*$/;
  137. const domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
  138. /**
  139. * @param {string} base
  140. * @param {string} href
  141. */
  142. function resolveUrl(base, href) {
  143. if (!baseUrls[' ' + base]) {
  144. // we can ignore everything in base after the last slash of its path component,
  145. // but we might need to add _that_
  146. // https://tools.ietf.org/html/rfc3986#section-3
  147. if (justDomain.test(base)) {
  148. baseUrls[' ' + base] = base + '/';
  149. } else {
  150. baseUrls[' ' + base] = rtrim(base, '/', true);
  151. }
  152. }
  153. base = baseUrls[' ' + base];
  154. const relativeBase = base.indexOf(':') === -1;
  155. if (href.substring(0, 2) === '//') {
  156. if (relativeBase) {
  157. return href;
  158. }
  159. return base.replace(protocol, '$1') + href;
  160. } else if (href.charAt(0) === '/') {
  161. if (relativeBase) {
  162. return href;
  163. }
  164. return base.replace(domain, '$1') + href;
  165. } else {
  166. return base + href;
  167. }
  168. }
  169. const noopTest = { exec: function noopTest() {} };
  170. function splitCells(tableRow, count) {
  171. // ensure that every cell-delimiting pipe has a space
  172. // before it to distinguish it from an escaped pipe
  173. const row = tableRow.replace(/\|/g, (match, offset, str) => {
  174. let escaped = false,
  175. curr = offset;
  176. while (--curr >= 0 && str[curr] === '\\') escaped = !escaped;
  177. if (escaped) {
  178. // odd number of slashes means | is escaped
  179. // so we leave it alone
  180. return '|';
  181. } else {
  182. // add space before unescaped |
  183. return ' |';
  184. }
  185. }),
  186. cells = row.split(/ \|/);
  187. let i = 0;
  188. // First/last cell in a row cannot be empty if it has no leading/trailing pipe
  189. if (!cells[0].trim()) { cells.shift(); }
  190. if (cells.length > 0 && !cells[cells.length - 1].trim()) { cells.pop(); }
  191. if (cells.length > count) {
  192. cells.splice(count);
  193. } else {
  194. while (cells.length < count) cells.push('');
  195. }
  196. for (; i < cells.length; i++) {
  197. // leading or trailing whitespace is ignored per the gfm spec
  198. cells[i] = cells[i].trim().replace(/\\\|/g, '|');
  199. }
  200. return cells;
  201. }
  202. /**
  203. * Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
  204. * /c*$/ is vulnerable to REDOS.
  205. *
  206. * @param {string} str
  207. * @param {string} c
  208. * @param {boolean} invert Remove suffix of non-c chars instead. Default falsey.
  209. */
  210. function rtrim(str, c, invert) {
  211. const l = str.length;
  212. if (l === 0) {
  213. return '';
  214. }
  215. // Length of suffix matching the invert condition.
  216. let suffLen = 0;
  217. // Step left until we fail to match the invert condition.
  218. while (suffLen < l) {
  219. const currChar = str.charAt(l - suffLen - 1);
  220. if (currChar === c && !invert) {
  221. suffLen++;
  222. } else if (currChar !== c && invert) {
  223. suffLen++;
  224. } else {
  225. break;
  226. }
  227. }
  228. return str.slice(0, l - suffLen);
  229. }
  230. function findClosingBracket(str, b) {
  231. if (str.indexOf(b[1]) === -1) {
  232. return -1;
  233. }
  234. const l = str.length;
  235. let level = 0,
  236. i = 0;
  237. for (; i < l; i++) {
  238. if (str[i] === '\\') {
  239. i++;
  240. } else if (str[i] === b[0]) {
  241. level++;
  242. } else if (str[i] === b[1]) {
  243. level--;
  244. if (level < 0) {
  245. return i;
  246. }
  247. }
  248. }
  249. return -1;
  250. }
  251. function checkDeprecations(opt, callback) {
  252. if (!opt || opt.silent) {
  253. return;
  254. }
  255. if (callback) {
  256. console.warn('marked(): callback is deprecated since version 5.0.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/using_pro#async');
  257. }
  258. if (opt.sanitize || opt.sanitizer) {
  259. console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options');
  260. }
  261. if (opt.highlight || opt.langPrefix !== 'language-') {
  262. console.warn('marked(): highlight and langPrefix parameters are deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-highlight.');
  263. }
  264. if (opt.mangle) {
  265. console.warn('marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.');
  266. }
  267. if (opt.baseUrl) {
  268. console.warn('marked(): baseUrl parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-base-url.');
  269. }
  270. if (opt.smartypants) {
  271. console.warn('marked(): smartypants parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-smartypants.');
  272. }
  273. if (opt.xhtml) {
  274. console.warn('marked(): xhtml parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-xhtml.');
  275. }
  276. if (opt.headerIds || opt.headerPrefix) {
  277. console.warn('marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.');
  278. }
  279. }
  280. function outputLink(cap, link, raw, lexer) {
  281. const href = link.href;
  282. const title = link.title ? escape(link.title) : null;
  283. const text = cap[1].replace(/\\([\[\]])/g, '$1');
  284. if (cap[0].charAt(0) !== '!') {
  285. lexer.state.inLink = true;
  286. const token = {
  287. type: 'link',
  288. raw,
  289. href,
  290. title,
  291. text,
  292. tokens: lexer.inlineTokens(text)
  293. };
  294. lexer.state.inLink = false;
  295. return token;
  296. }
  297. return {
  298. type: 'image',
  299. raw,
  300. href,
  301. title,
  302. text: escape(text)
  303. };
  304. }
  305. function indentCodeCompensation(raw, text) {
  306. const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
  307. if (matchIndentToCode === null) {
  308. return text;
  309. }
  310. const indentToCode = matchIndentToCode[1];
  311. return text
  312. .split('\n')
  313. .map(node => {
  314. const matchIndentInNode = node.match(/^\s+/);
  315. if (matchIndentInNode === null) {
  316. return node;
  317. }
  318. const [indentInNode] = matchIndentInNode;
  319. if (indentInNode.length >= indentToCode.length) {
  320. return node.slice(indentToCode.length);
  321. }
  322. return node;
  323. })
  324. .join('\n');
  325. }
  326. /**
  327. * Tokenizer
  328. */
  329. class Tokenizer {
  330. constructor(options) {
  331. this.options = options || defaults;
  332. }
  333. space(src) {
  334. const cap = this.rules.block.newline.exec(src);
  335. if (cap && cap[0].length > 0) {
  336. return {
  337. type: 'space',
  338. raw: cap[0]
  339. };
  340. }
  341. }
  342. code(src) {
  343. const cap = this.rules.block.code.exec(src);
  344. if (cap) {
  345. const text = cap[0].replace(/^ {1,4}/gm, '');
  346. return {
  347. type: 'code',
  348. raw: cap[0],
  349. codeBlockStyle: 'indented',
  350. text: !this.options.pedantic
  351. ? rtrim(text, '\n')
  352. : text
  353. };
  354. }
  355. }
  356. fences(src) {
  357. const cap = this.rules.block.fences.exec(src);
  358. if (cap) {
  359. const raw = cap[0];
  360. const text = indentCodeCompensation(raw, cap[3] || '');
  361. return {
  362. type: 'code',
  363. raw,
  364. lang: cap[2] ? cap[2].trim().replace(this.rules.inline._escapes, '$1') : cap[2],
  365. text
  366. };
  367. }
  368. }
  369. heading(src) {
  370. const cap = this.rules.block.heading.exec(src);
  371. if (cap) {
  372. let text = cap[2].trim();
  373. // remove trailing #s
  374. if (/#$/.test(text)) {
  375. const trimmed = rtrim(text, '#');
  376. if (this.options.pedantic) {
  377. text = trimmed.trim();
  378. } else if (!trimmed || / $/.test(trimmed)) {
  379. // CommonMark requires space before trailing #s
  380. text = trimmed.trim();
  381. }
  382. }
  383. return {
  384. type: 'heading',
  385. raw: cap[0],
  386. depth: cap[1].length,
  387. text,
  388. tokens: this.lexer.inline(text)
  389. };
  390. }
  391. }
  392. hr(src) {
  393. const cap = this.rules.block.hr.exec(src);
  394. if (cap) {
  395. return {
  396. type: 'hr',
  397. raw: cap[0]
  398. };
  399. }
  400. }
  401. blockquote(src) {
  402. const cap = this.rules.block.blockquote.exec(src);
  403. if (cap) {
  404. const text = cap[0].replace(/^ *>[ \t]?/gm, '');
  405. const top = this.lexer.state.top;
  406. this.lexer.state.top = true;
  407. const tokens = this.lexer.blockTokens(text);
  408. this.lexer.state.top = top;
  409. return {
  410. type: 'blockquote',
  411. raw: cap[0],
  412. tokens,
  413. text
  414. };
  415. }
  416. }
  417. list(src) {
  418. let cap = this.rules.block.list.exec(src);
  419. if (cap) {
  420. let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine,
  421. line, nextLine, rawLine, itemContents, endEarly;
  422. let bull = cap[1].trim();
  423. const isordered = bull.length > 1;
  424. const list = {
  425. type: 'list',
  426. raw: '',
  427. ordered: isordered,
  428. start: isordered ? +bull.slice(0, -1) : '',
  429. loose: false,
  430. items: []
  431. };
  432. bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
  433. if (this.options.pedantic) {
  434. bull = isordered ? bull : '[*+-]';
  435. }
  436. // Get next list item
  437. const itemRegex = new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`);
  438. // Check if current bullet point can start a new List Item
  439. while (src) {
  440. endEarly = false;
  441. if (!(cap = itemRegex.exec(src))) {
  442. break;
  443. }
  444. if (this.rules.block.hr.test(src)) { // End list if bullet was actually HR (possibly move into itemRegex?)
  445. break;
  446. }
  447. raw = cap[0];
  448. src = src.substring(raw.length);
  449. line = cap[2].split('\n', 1)[0].replace(/^\t+/, (t) => ' '.repeat(3 * t.length));
  450. nextLine = src.split('\n', 1)[0];
  451. if (this.options.pedantic) {
  452. indent = 2;
  453. itemContents = line.trimLeft();
  454. } else {
  455. indent = cap[2].search(/[^ ]/); // Find first non-space char
  456. indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent
  457. itemContents = line.slice(indent);
  458. indent += cap[1].length;
  459. }
  460. blankLine = false;
  461. if (!line && /^ *$/.test(nextLine)) { // Items begin with at most one blank line
  462. raw += nextLine + '\n';
  463. src = src.substring(nextLine.length + 1);
  464. endEarly = true;
  465. }
  466. if (!endEarly) {
  467. const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`);
  468. const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`);
  469. const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`);
  470. const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`);
  471. // Check if following lines should be included in List Item
  472. while (src) {
  473. rawLine = src.split('\n', 1)[0];
  474. nextLine = rawLine;
  475. // Re-align to follow commonmark nesting rules
  476. if (this.options.pedantic) {
  477. nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' ');
  478. }
  479. // End list item if found code fences
  480. if (fencesBeginRegex.test(nextLine)) {
  481. break;
  482. }
  483. // End list item if found start of new heading
  484. if (headingBeginRegex.test(nextLine)) {
  485. break;
  486. }
  487. // End list item if found start of new bullet
  488. if (nextBulletRegex.test(nextLine)) {
  489. break;
  490. }
  491. // Horizontal rule found
  492. if (hrRegex.test(src)) {
  493. break;
  494. }
  495. if (nextLine.search(/[^ ]/) >= indent || !nextLine.trim()) { // Dedent if possible
  496. itemContents += '\n' + nextLine.slice(indent);
  497. } else {
  498. // not enough indentation
  499. if (blankLine) {
  500. break;
  501. }
  502. // paragraph continuation unless last line was a different block level element
  503. if (line.search(/[^ ]/) >= 4) { // indented code block
  504. break;
  505. }
  506. if (fencesBeginRegex.test(line)) {
  507. break;
  508. }
  509. if (headingBeginRegex.test(line)) {
  510. break;
  511. }
  512. if (hrRegex.test(line)) {
  513. break;
  514. }
  515. itemContents += '\n' + nextLine;
  516. }
  517. if (!blankLine && !nextLine.trim()) { // Check if current line is blank
  518. blankLine = true;
  519. }
  520. raw += rawLine + '\n';
  521. src = src.substring(rawLine.length + 1);
  522. line = nextLine.slice(indent);
  523. }
  524. }
  525. if (!list.loose) {
  526. // If the previous item ended with a blank line, the list is loose
  527. if (endsWithBlankLine) {
  528. list.loose = true;
  529. } else if (/\n *\n *$/.test(raw)) {
  530. endsWithBlankLine = true;
  531. }
  532. }
  533. // Check for task list items
  534. if (this.options.gfm) {
  535. istask = /^\[[ xX]\] /.exec(itemContents);
  536. if (istask) {
  537. ischecked = istask[0] !== '[ ] ';
  538. itemContents = itemContents.replace(/^\[[ xX]\] +/, '');
  539. }
  540. }
  541. list.items.push({
  542. type: 'list_item',
  543. raw,
  544. task: !!istask,
  545. checked: ischecked,
  546. loose: false,
  547. text: itemContents
  548. });
  549. list.raw += raw;
  550. }
  551. // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic
  552. list.items[list.items.length - 1].raw = raw.trimRight();
  553. list.items[list.items.length - 1].text = itemContents.trimRight();
  554. list.raw = list.raw.trimRight();
  555. const l = list.items.length;
  556. // Item child tokens handled here at end because we needed to have the final item to trim it first
  557. for (i = 0; i < l; i++) {
  558. this.lexer.state.top = false;
  559. list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []);
  560. if (!list.loose) {
  561. // Check if list should be loose
  562. const spacers = list.items[i].tokens.filter(t => t.type === 'space');
  563. const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => /\n.*\n/.test(t.raw));
  564. list.loose = hasMultipleLineBreaks;
  565. }
  566. }
  567. // Set all items to loose if list is loose
  568. if (list.loose) {
  569. for (i = 0; i < l; i++) {
  570. list.items[i].loose = true;
  571. }
  572. }
  573. return list;
  574. }
  575. }
  576. html(src) {
  577. const cap = this.rules.block.html.exec(src);
  578. if (cap) {
  579. const token = {
  580. type: 'html',
  581. block: true,
  582. raw: cap[0],
  583. pre: !this.options.sanitizer
  584. && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
  585. text: cap[0]
  586. };
  587. if (this.options.sanitize) {
  588. const text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]);
  589. token.type = 'paragraph';
  590. token.text = text;
  591. token.tokens = this.lexer.inline(text);
  592. }
  593. return token;
  594. }
  595. }
  596. def(src) {
  597. const cap = this.rules.block.def.exec(src);
  598. if (cap) {
  599. const tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
  600. const href = cap[2] ? cap[2].replace(/^<(.*)>$/, '$1').replace(this.rules.inline._escapes, '$1') : '';
  601. const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline._escapes, '$1') : cap[3];
  602. return {
  603. type: 'def',
  604. tag,
  605. raw: cap[0],
  606. href,
  607. title
  608. };
  609. }
  610. }
  611. table(src) {
  612. const cap = this.rules.block.table.exec(src);
  613. if (cap) {
  614. const item = {
  615. type: 'table',
  616. header: splitCells(cap[1]).map(c => { return { text: c }; }),
  617. align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
  618. rows: cap[3] && cap[3].trim() ? cap[3].replace(/\n[ \t]*$/, '').split('\n') : []
  619. };
  620. if (item.header.length === item.align.length) {
  621. item.raw = cap[0];
  622. let l = item.align.length;
  623. let i, j, k, row;
  624. for (i = 0; i < l; i++) {
  625. if (/^ *-+: *$/.test(item.align[i])) {
  626. item.align[i] = 'right';
  627. } else if (/^ *:-+: *$/.test(item.align[i])) {
  628. item.align[i] = 'center';
  629. } else if (/^ *:-+ *$/.test(item.align[i])) {
  630. item.align[i] = 'left';
  631. } else {
  632. item.align[i] = null;
  633. }
  634. }
  635. l = item.rows.length;
  636. for (i = 0; i < l; i++) {
  637. item.rows[i] = splitCells(item.rows[i], item.header.length).map(c => { return { text: c }; });
  638. }
  639. // parse child tokens inside headers and cells
  640. // header child tokens
  641. l = item.header.length;
  642. for (j = 0; j < l; j++) {
  643. item.header[j].tokens = this.lexer.inline(item.header[j].text);
  644. }
  645. // cell child tokens
  646. l = item.rows.length;
  647. for (j = 0; j < l; j++) {
  648. row = item.rows[j];
  649. for (k = 0; k < row.length; k++) {
  650. row[k].tokens = this.lexer.inline(row[k].text);
  651. }
  652. }
  653. return item;
  654. }
  655. }
  656. }
  657. lheading(src) {
  658. const cap = this.rules.block.lheading.exec(src);
  659. if (cap) {
  660. return {
  661. type: 'heading',
  662. raw: cap[0],
  663. depth: cap[2].charAt(0) === '=' ? 1 : 2,
  664. text: cap[1],
  665. tokens: this.lexer.inline(cap[1])
  666. };
  667. }
  668. }
  669. paragraph(src) {
  670. const cap = this.rules.block.paragraph.exec(src);
  671. if (cap) {
  672. const text = cap[1].charAt(cap[1].length - 1) === '\n'
  673. ? cap[1].slice(0, -1)
  674. : cap[1];
  675. return {
  676. type: 'paragraph',
  677. raw: cap[0],
  678. text,
  679. tokens: this.lexer.inline(text)
  680. };
  681. }
  682. }
  683. text(src) {
  684. const cap = this.rules.block.text.exec(src);
  685. if (cap) {
  686. return {
  687. type: 'text',
  688. raw: cap[0],
  689. text: cap[0],
  690. tokens: this.lexer.inline(cap[0])
  691. };
  692. }
  693. }
  694. escape(src) {
  695. const cap = this.rules.inline.escape.exec(src);
  696. if (cap) {
  697. return {
  698. type: 'escape',
  699. raw: cap[0],
  700. text: escape(cap[1])
  701. };
  702. }
  703. }
  704. tag(src) {
  705. const cap = this.rules.inline.tag.exec(src);
  706. if (cap) {
  707. if (!this.lexer.state.inLink && /^<a /i.test(cap[0])) {
  708. this.lexer.state.inLink = true;
  709. } else if (this.lexer.state.inLink && /^<\/a>/i.test(cap[0])) {
  710. this.lexer.state.inLink = false;
  711. }
  712. if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  713. this.lexer.state.inRawBlock = true;
  714. } else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  715. this.lexer.state.inRawBlock = false;
  716. }
  717. return {
  718. type: this.options.sanitize
  719. ? 'text'
  720. : 'html',
  721. raw: cap[0],
  722. inLink: this.lexer.state.inLink,
  723. inRawBlock: this.lexer.state.inRawBlock,
  724. block: false,
  725. text: this.options.sanitize
  726. ? (this.options.sanitizer
  727. ? this.options.sanitizer(cap[0])
  728. : escape(cap[0]))
  729. : cap[0]
  730. };
  731. }
  732. }
  733. link(src) {
  734. const cap = this.rules.inline.link.exec(src);
  735. if (cap) {
  736. const trimmedUrl = cap[2].trim();
  737. if (!this.options.pedantic && /^</.test(trimmedUrl)) {
  738. // commonmark requires matching angle brackets
  739. if (!(/>$/.test(trimmedUrl))) {
  740. return;
  741. }
  742. // ending angle bracket cannot be escaped
  743. const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
  744. if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
  745. return;
  746. }
  747. } else {
  748. // find closing parenthesis
  749. const lastParenIndex = findClosingBracket(cap[2], '()');
  750. if (lastParenIndex > -1) {
  751. const start = cap[0].indexOf('!') === 0 ? 5 : 4;
  752. const linkLen = start + cap[1].length + lastParenIndex;
  753. cap[2] = cap[2].substring(0, lastParenIndex);
  754. cap[0] = cap[0].substring(0, linkLen).trim();
  755. cap[3] = '';
  756. }
  757. }
  758. let href = cap[2];
  759. let title = '';
  760. if (this.options.pedantic) {
  761. // split pedantic href and title
  762. const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
  763. if (link) {
  764. href = link[1];
  765. title = link[3];
  766. }
  767. } else {
  768. title = cap[3] ? cap[3].slice(1, -1) : '';
  769. }
  770. href = href.trim();
  771. if (/^</.test(href)) {
  772. if (this.options.pedantic && !(/>$/.test(trimmedUrl))) {
  773. // pedantic allows starting angle bracket without ending angle bracket
  774. href = href.slice(1);
  775. } else {
  776. href = href.slice(1, -1);
  777. }
  778. }
  779. return outputLink(cap, {
  780. href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
  781. title: title ? title.replace(this.rules.inline._escapes, '$1') : title
  782. }, cap[0], this.lexer);
  783. }
  784. }
  785. reflink(src, links) {
  786. let cap;
  787. if ((cap = this.rules.inline.reflink.exec(src))
  788. || (cap = this.rules.inline.nolink.exec(src))) {
  789. let link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
  790. link = links[link.toLowerCase()];
  791. if (!link) {
  792. const text = cap[0].charAt(0);
  793. return {
  794. type: 'text',
  795. raw: text,
  796. text
  797. };
  798. }
  799. return outputLink(cap, link, cap[0], this.lexer);
  800. }
  801. }
  802. emStrong(src, maskedSrc, prevChar = '') {
  803. let match = this.rules.inline.emStrong.lDelim.exec(src);
  804. if (!match) return;
  805. // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
  806. if (match[3] && prevChar.match(/[\p{L}\p{N}]/u)) return;
  807. const nextChar = match[1] || match[2] || '';
  808. if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
  809. const lLength = match[0].length - 1;
  810. let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
  811. const endReg = match[0][0] === '*' ? this.rules.inline.emStrong.rDelimAst : this.rules.inline.emStrong.rDelimUnd;
  812. endReg.lastIndex = 0;
  813. // Clip maskedSrc to same section of string as src (move to lexer?)
  814. maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
  815. while ((match = endReg.exec(maskedSrc)) != null) {
  816. rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
  817. if (!rDelim) continue; // skip single * in __abc*abc__
  818. rLength = rDelim.length;
  819. if (match[3] || match[4]) { // found another Left Delim
  820. delimTotal += rLength;
  821. continue;
  822. } else if (match[5] || match[6]) { // either Left or Right Delim
  823. if (lLength % 3 && !((lLength + rLength) % 3)) {
  824. midDelimTotal += rLength;
  825. continue; // CommonMark Emphasis Rules 9-10
  826. }
  827. }
  828. delimTotal -= rLength;
  829. if (delimTotal > 0) continue; // Haven't found enough closing delimiters
  830. // Remove extra characters. *a*** -> *a*
  831. rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
  832. const raw = src.slice(0, lLength + match.index + rLength + 1);
  833. // Create `em` if smallest delimiter has odd char count. *a***
  834. if (Math.min(lLength, rLength) % 2) {
  835. const text = raw.slice(1, -1);
  836. return {
  837. type: 'em',
  838. raw,
  839. text,
  840. tokens: this.lexer.inlineTokens(text)
  841. };
  842. }
  843. // Create 'strong' if smallest delimiter has even char count. **a***
  844. const text = raw.slice(2, -2);
  845. return {
  846. type: 'strong',
  847. raw,
  848. text,
  849. tokens: this.lexer.inlineTokens(text)
  850. };
  851. }
  852. }
  853. }
  854. codespan(src) {
  855. const cap = this.rules.inline.code.exec(src);
  856. if (cap) {
  857. let text = cap[2].replace(/\n/g, ' ');
  858. const hasNonSpaceChars = /[^ ]/.test(text);
  859. const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
  860. if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
  861. text = text.substring(1, text.length - 1);
  862. }
  863. text = escape(text, true);
  864. return {
  865. type: 'codespan',
  866. raw: cap[0],
  867. text
  868. };
  869. }
  870. }
  871. br(src) {
  872. const cap = this.rules.inline.br.exec(src);
  873. if (cap) {
  874. return {
  875. type: 'br',
  876. raw: cap[0]
  877. };
  878. }
  879. }
  880. del(src) {
  881. const cap = this.rules.inline.del.exec(src);
  882. if (cap) {
  883. return {
  884. type: 'del',
  885. raw: cap[0],
  886. text: cap[2],
  887. tokens: this.lexer.inlineTokens(cap[2])
  888. };
  889. }
  890. }
  891. autolink(src, mangle) {
  892. const cap = this.rules.inline.autolink.exec(src);
  893. if (cap) {
  894. let text, href;
  895. if (cap[2] === '@') {
  896. text = escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
  897. href = 'mailto:' + text;
  898. } else {
  899. text = escape(cap[1]);
  900. href = text;
  901. }
  902. return {
  903. type: 'link',
  904. raw: cap[0],
  905. text,
  906. href,
  907. tokens: [
  908. {
  909. type: 'text',
  910. raw: text,
  911. text
  912. }
  913. ]
  914. };
  915. }
  916. }
  917. url(src, mangle) {
  918. let cap;
  919. if (cap = this.rules.inline.url.exec(src)) {
  920. let text, href;
  921. if (cap[2] === '@') {
  922. text = escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
  923. href = 'mailto:' + text;
  924. } else {
  925. // do extended autolink path validation
  926. let prevCapZero;
  927. do {
  928. prevCapZero = cap[0];
  929. cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
  930. } while (prevCapZero !== cap[0]);
  931. text = escape(cap[0]);
  932. if (cap[1] === 'www.') {
  933. href = 'http://' + cap[0];
  934. } else {
  935. href = cap[0];
  936. }
  937. }
  938. return {
  939. type: 'link',
  940. raw: cap[0],
  941. text,
  942. href,
  943. tokens: [
  944. {
  945. type: 'text',
  946. raw: text,
  947. text
  948. }
  949. ]
  950. };
  951. }
  952. }
  953. inlineText(src, smartypants) {
  954. const cap = this.rules.inline.text.exec(src);
  955. if (cap) {
  956. let text;
  957. if (this.lexer.state.inRawBlock) {
  958. text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0];
  959. } else {
  960. text = escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
  961. }
  962. return {
  963. type: 'text',
  964. raw: cap[0],
  965. text
  966. };
  967. }
  968. }
  969. }
  970. /**
  971. * Block-Level Grammar
  972. */
  973. const block = {
  974. newline: /^(?: *(?:\n|$))+/,
  975. code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,
  976. fences: /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,
  977. hr: /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,
  978. heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,
  979. blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
  980. list: /^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/,
  981. html: '^ {0,3}(?:' // optional indentation
  982. + '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
  983. + '|comment[^\\n]*(\\n+|$)' // (2)
  984. + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
  985. + '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
  986. + '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
  987. + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (6)
  988. + '|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) open tag
  989. + '|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag
  990. + ')',
  991. def: /^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/,
  992. table: noopTest,
  993. lheading: /^((?:(?!^bull ).|\n(?!\n|bull ))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
  994. // regex template, placeholders will be replaced according to different paragraph
  995. // interruption rules of commonmark and the original markdown spec:
  996. _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,
  997. text: /^[^\n]+/
  998. };
  999. block._label = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
  1000. block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
  1001. block.def = edit(block.def)
  1002. .replace('label', block._label)
  1003. .replace('title', block._title)
  1004. .getRegex();
  1005. block.bullet = /(?:[*+-]|\d{1,9}[.)])/;
  1006. block.listItemStart = edit(/^( *)(bull) */)
  1007. .replace('bull', block.bullet)
  1008. .getRegex();
  1009. block.list = edit(block.list)
  1010. .replace(/bull/g, block.bullet)
  1011. .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
  1012. .replace('def', '\\n+(?=' + block.def.source + ')')
  1013. .getRegex();
  1014. block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
  1015. + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
  1016. + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
  1017. + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
  1018. + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
  1019. + '|track|ul';
  1020. block._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
  1021. block.html = edit(block.html, 'i')
  1022. .replace('comment', block._comment)
  1023. .replace('tag', block._tag)
  1024. .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
  1025. .getRegex();
  1026. block.lheading = edit(block.lheading)
  1027. .replace(/bull/g, block.bullet) // lists can interrupt
  1028. .getRegex();
  1029. block.paragraph = edit(block._paragraph)
  1030. .replace('hr', block.hr)
  1031. .replace('heading', ' {0,3}#{1,6} ')
  1032. .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
  1033. .replace('|table', '')
  1034. .replace('blockquote', ' {0,3}>')
  1035. .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
  1036. .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
  1037. .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
  1038. .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
  1039. .getRegex();
  1040. block.blockquote = edit(block.blockquote)
  1041. .replace('paragraph', block.paragraph)
  1042. .getRegex();
  1043. /**
  1044. * Normal Block Grammar
  1045. */
  1046. block.normal = { ...block };
  1047. /**
  1048. * GFM Block Grammar
  1049. */
  1050. block.gfm = {
  1051. ...block.normal,
  1052. table: '^ *([^\\n ].*\\|.*)\\n' // Header
  1053. + ' {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?' // Align
  1054. + '(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
  1055. };
  1056. block.gfm.table = edit(block.gfm.table)
  1057. .replace('hr', block.hr)
  1058. .replace('heading', ' {0,3}#{1,6} ')
  1059. .replace('blockquote', ' {0,3}>')
  1060. .replace('code', ' {4}[^\\n]')
  1061. .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
  1062. .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
  1063. .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
  1064. .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
  1065. .getRegex();
  1066. block.gfm.paragraph = edit(block._paragraph)
  1067. .replace('hr', block.hr)
  1068. .replace('heading', ' {0,3}#{1,6} ')
  1069. .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
  1070. .replace('table', block.gfm.table) // interrupt paragraphs with table
  1071. .replace('blockquote', ' {0,3}>')
  1072. .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
  1073. .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
  1074. .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
  1075. .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
  1076. .getRegex();
  1077. /**
  1078. * Pedantic grammar (original John Gruber's loose markdown specification)
  1079. */
  1080. block.pedantic = {
  1081. ...block.normal,
  1082. html: edit(
  1083. '^ *(?:comment *(?:\\n|\\s*$)'
  1084. + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
  1085. + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
  1086. .replace('comment', block._comment)
  1087. .replace(/tag/g, '(?!(?:'
  1088. + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
  1089. + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
  1090. + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
  1091. .getRegex(),
  1092. def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
  1093. heading: /^(#{1,6})(.*)(?:\n+|$)/,
  1094. fences: noopTest, // fences not supported
  1095. lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
  1096. paragraph: edit(block.normal._paragraph)
  1097. .replace('hr', block.hr)
  1098. .replace('heading', ' *#{1,6} *[^\n]')
  1099. .replace('lheading', block.lheading)
  1100. .replace('blockquote', ' {0,3}>')
  1101. .replace('|fences', '')
  1102. .replace('|list', '')
  1103. .replace('|html', '')
  1104. .getRegex()
  1105. };
  1106. /**
  1107. * Inline-Level Grammar
  1108. */
  1109. const inline = {
  1110. escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
  1111. autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
  1112. url: noopTest,
  1113. tag: '^comment'
  1114. + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
  1115. + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
  1116. + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
  1117. + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
  1118. + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
  1119. link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
  1120. reflink: /^!?\[(label)\]\[(ref)\]/,
  1121. nolink: /^!?\[(ref)\](?:\[\])?/,
  1122. reflinkSearch: 'reflink|nolink(?!\\()',
  1123. emStrong: {
  1124. lDelim: /^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,
  1125. // (1) and (2) can only be a Right Delimiter. (3) and (4) can only be Left. (5) and (6) can be either Left or Right.
  1126. // | Skip orphan inside strong | Consume to delim | (1) #*** | (2) a***#, a*** | (3) #***a, ***a | (4) ***# | (5) #***# | (6) a***a
  1127. rDelimAst: /^[^_*]*?__[^_*]*?\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\*)[punct](\*+)(?=[\s]|$)|[^punct\s](\*+)(?!\*)(?=[punct\s]|$)|(?!\*)[punct\s](\*+)(?=[^punct\s])|[\s](\*+)(?!\*)(?=[punct])|(?!\*)[punct](\*+)(?!\*)(?=[punct])|[^punct\s](\*+)(?=[^punct\s])/,
  1128. rDelimUnd: /^[^_*]*?\*\*[^_*]*?_[^_*]*?(?=\*\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\s]|$)|[^punct\s](_+)(?!_)(?=[punct\s]|$)|(?!_)[punct\s](_+)(?=[^punct\s])|[\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])/ // ^- Not allowed for _
  1129. },
  1130. code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
  1131. br: /^( {2,}|\\)\n(?!\s*$)/,
  1132. del: noopTest,
  1133. text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,
  1134. punctuation: /^((?![*_])[\spunctuation])/
  1135. };
  1136. // list of unicode punctuation marks, plus any missing characters from CommonMark spec
  1137. inline._punctuation = '\\p{P}$+<=>`^|~';
  1138. inline.punctuation = edit(inline.punctuation, 'u').replace(/punctuation/g, inline._punctuation).getRegex();
  1139. // sequences em should skip over [title](link), `code`, <html>
  1140. inline.blockSkip = /\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g;
  1141. inline.anyPunctuation = /\\[punct]/g;
  1142. inline._escapes = /\\([punct])/g;
  1143. inline._comment = edit(block._comment).replace('(?:-->|$)', '-->').getRegex();
  1144. inline.emStrong.lDelim = edit(inline.emStrong.lDelim, 'u')
  1145. .replace(/punct/g, inline._punctuation)
  1146. .getRegex();
  1147. inline.emStrong.rDelimAst = edit(inline.emStrong.rDelimAst, 'gu')
  1148. .replace(/punct/g, inline._punctuation)
  1149. .getRegex();
  1150. inline.emStrong.rDelimUnd = edit(inline.emStrong.rDelimUnd, 'gu')
  1151. .replace(/punct/g, inline._punctuation)
  1152. .getRegex();
  1153. inline.anyPunctuation = edit(inline.anyPunctuation, 'gu')
  1154. .replace(/punct/g, inline._punctuation)
  1155. .getRegex();
  1156. inline._escapes = edit(inline._escapes, 'gu')
  1157. .replace(/punct/g, inline._punctuation)
  1158. .getRegex();
  1159. inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
  1160. inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
  1161. inline.autolink = edit(inline.autolink)
  1162. .replace('scheme', inline._scheme)
  1163. .replace('email', inline._email)
  1164. .getRegex();
  1165. inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
  1166. inline.tag = edit(inline.tag)
  1167. .replace('comment', inline._comment)
  1168. .replace('attribute', inline._attribute)
  1169. .getRegex();
  1170. inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
  1171. inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/;
  1172. inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
  1173. inline.link = edit(inline.link)
  1174. .replace('label', inline._label)
  1175. .replace('href', inline._href)
  1176. .replace('title', inline._title)
  1177. .getRegex();
  1178. inline.reflink = edit(inline.reflink)
  1179. .replace('label', inline._label)
  1180. .replace('ref', block._label)
  1181. .getRegex();
  1182. inline.nolink = edit(inline.nolink)
  1183. .replace('ref', block._label)
  1184. .getRegex();
  1185. inline.reflinkSearch = edit(inline.reflinkSearch, 'g')
  1186. .replace('reflink', inline.reflink)
  1187. .replace('nolink', inline.nolink)
  1188. .getRegex();
  1189. /**
  1190. * Normal Inline Grammar
  1191. */
  1192. inline.normal = { ...inline };
  1193. /**
  1194. * Pedantic Inline Grammar
  1195. */
  1196. inline.pedantic = {
  1197. ...inline.normal,
  1198. strong: {
  1199. start: /^__|\*\*/,
  1200. middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
  1201. endAst: /\*\*(?!\*)/g,
  1202. endUnd: /__(?!_)/g
  1203. },
  1204. em: {
  1205. start: /^_|\*/,
  1206. middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
  1207. endAst: /\*(?!\*)/g,
  1208. endUnd: /_(?!_)/g
  1209. },
  1210. link: edit(/^!?\[(label)\]\((.*?)\)/)
  1211. .replace('label', inline._label)
  1212. .getRegex(),
  1213. reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
  1214. .replace('label', inline._label)
  1215. .getRegex()
  1216. };
  1217. /**
  1218. * GFM Inline Grammar
  1219. */
  1220. inline.gfm = {
  1221. ...inline.normal,
  1222. escape: edit(inline.escape).replace('])', '~|])').getRegex(),
  1223. _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
  1224. url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
  1225. _backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
  1226. del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
  1227. text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/
  1228. };
  1229. inline.gfm.url = edit(inline.gfm.url, 'i')
  1230. .replace('email', inline.gfm._extended_email)
  1231. .getRegex();
  1232. /**
  1233. * GFM + Line Breaks Inline Grammar
  1234. */
  1235. inline.breaks = {
  1236. ...inline.gfm,
  1237. br: edit(inline.br).replace('{2,}', '*').getRegex(),
  1238. text: edit(inline.gfm.text)
  1239. .replace('\\b_', '\\b_| {2,}\\n')
  1240. .replace(/\{2,\}/g, '*')
  1241. .getRegex()
  1242. };
  1243. /**
  1244. * smartypants text replacement
  1245. * @param {string} text
  1246. */
  1247. function smartypants(text) {
  1248. return text
  1249. // em-dashes
  1250. .replace(/---/g, '\u2014')
  1251. // en-dashes
  1252. .replace(/--/g, '\u2013')
  1253. // opening singles
  1254. .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
  1255. // closing singles & apostrophes
  1256. .replace(/'/g, '\u2019')
  1257. // opening doubles
  1258. .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
  1259. // closing doubles
  1260. .replace(/"/g, '\u201d')
  1261. // ellipses
  1262. .replace(/\.{3}/g, '\u2026');
  1263. }
  1264. /**
  1265. * mangle email addresses
  1266. * @param {string} text
  1267. */
  1268. function mangle(text) {
  1269. let out = '',
  1270. i,
  1271. ch;
  1272. const l = text.length;
  1273. for (i = 0; i < l; i++) {
  1274. ch = text.charCodeAt(i);
  1275. if (Math.random() > 0.5) {
  1276. ch = 'x' + ch.toString(16);
  1277. }
  1278. out += '&#' + ch + ';';
  1279. }
  1280. return out;
  1281. }
  1282. /**
  1283. * Block Lexer
  1284. */
  1285. class Lexer {
  1286. constructor(options) {
  1287. this.tokens = [];
  1288. this.tokens.links = Object.create(null);
  1289. this.options = options || defaults;
  1290. this.options.tokenizer = this.options.tokenizer || new Tokenizer();
  1291. this.tokenizer = this.options.tokenizer;
  1292. this.tokenizer.options = this.options;
  1293. this.tokenizer.lexer = this;
  1294. this.inlineQueue = [];
  1295. this.state = {
  1296. inLink: false,
  1297. inRawBlock: false,
  1298. top: true
  1299. };
  1300. const rules = {
  1301. block: block.normal,
  1302. inline: inline.normal
  1303. };
  1304. if (this.options.pedantic) {
  1305. rules.block = block.pedantic;
  1306. rules.inline = inline.pedantic;
  1307. } else if (this.options.gfm) {
  1308. rules.block = block.gfm;
  1309. if (this.options.breaks) {
  1310. rules.inline = inline.breaks;
  1311. } else {
  1312. rules.inline = inline.gfm;
  1313. }
  1314. }
  1315. this.tokenizer.rules = rules;
  1316. }
  1317. /**
  1318. * Expose Rules
  1319. */
  1320. static get rules() {
  1321. return {
  1322. block,
  1323. inline
  1324. };
  1325. }
  1326. /**
  1327. * Static Lex Method
  1328. */
  1329. static lex(src, options) {
  1330. const lexer = new Lexer(options);
  1331. return lexer.lex(src);
  1332. }
  1333. /**
  1334. * Static Lex Inline Method
  1335. */
  1336. static lexInline(src, options) {
  1337. const lexer = new Lexer(options);
  1338. return lexer.inlineTokens(src);
  1339. }
  1340. /**
  1341. * Preprocessing
  1342. */
  1343. lex(src) {
  1344. src = src
  1345. .replace(/\r\n|\r/g, '\n');
  1346. this.blockTokens(src, this.tokens);
  1347. let next;
  1348. while (next = this.inlineQueue.shift()) {
  1349. this.inlineTokens(next.src, next.tokens);
  1350. }
  1351. return this.tokens;
  1352. }
  1353. /**
  1354. * Lexing
  1355. */
  1356. blockTokens(src, tokens = []) {
  1357. if (this.options.pedantic) {
  1358. src = src.replace(/\t/g, ' ').replace(/^ +$/gm, '');
  1359. } else {
  1360. src = src.replace(/^( *)(\t+)/gm, (_, leading, tabs) => {
  1361. return leading + ' '.repeat(tabs.length);
  1362. });
  1363. }
  1364. let token, lastToken, cutSrc, lastParagraphClipped;
  1365. while (src) {
  1366. if (this.options.extensions
  1367. && this.options.extensions.block
  1368. && this.options.extensions.block.some((extTokenizer) => {
  1369. if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
  1370. src = src.substring(token.raw.length);
  1371. tokens.push(token);
  1372. return true;
  1373. }
  1374. return false;
  1375. })) {
  1376. continue;
  1377. }
  1378. // newline
  1379. if (token = this.tokenizer.space(src)) {
  1380. src = src.substring(token.raw.length);
  1381. if (token.raw.length === 1 && tokens.length > 0) {
  1382. // if there's a single \n as a spacer, it's terminating the last line,
  1383. // so move it there so that we don't get unecessary paragraph tags
  1384. tokens[tokens.length - 1].raw += '\n';
  1385. } else {
  1386. tokens.push(token);
  1387. }
  1388. continue;
  1389. }
  1390. // code
  1391. if (token = this.tokenizer.code(src)) {
  1392. src = src.substring(token.raw.length);
  1393. lastToken = tokens[tokens.length - 1];
  1394. // An indented code block cannot interrupt a paragraph.
  1395. if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {
  1396. lastToken.raw += '\n' + token.raw;
  1397. lastToken.text += '\n' + token.text;
  1398. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  1399. } else {
  1400. tokens.push(token);
  1401. }
  1402. continue;
  1403. }
  1404. // fences
  1405. if (token = this.tokenizer.fences(src)) {
  1406. src = src.substring(token.raw.length);
  1407. tokens.push(token);
  1408. continue;
  1409. }
  1410. // heading
  1411. if (token = this.tokenizer.heading(src)) {
  1412. src = src.substring(token.raw.length);
  1413. tokens.push(token);
  1414. continue;
  1415. }
  1416. // hr
  1417. if (token = this.tokenizer.hr(src)) {
  1418. src = src.substring(token.raw.length);
  1419. tokens.push(token);
  1420. continue;
  1421. }
  1422. // blockquote
  1423. if (token = this.tokenizer.blockquote(src)) {
  1424. src = src.substring(token.raw.length);
  1425. tokens.push(token);
  1426. continue;
  1427. }
  1428. // list
  1429. if (token = this.tokenizer.list(src)) {
  1430. src = src.substring(token.raw.length);
  1431. tokens.push(token);
  1432. continue;
  1433. }
  1434. // html
  1435. if (token = this.tokenizer.html(src)) {
  1436. src = src.substring(token.raw.length);
  1437. tokens.push(token);
  1438. continue;
  1439. }
  1440. // def
  1441. if (token = this.tokenizer.def(src)) {
  1442. src = src.substring(token.raw.length);
  1443. lastToken = tokens[tokens.length - 1];
  1444. if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {
  1445. lastToken.raw += '\n' + token.raw;
  1446. lastToken.text += '\n' + token.raw;
  1447. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  1448. } else if (!this.tokens.links[token.tag]) {
  1449. this.tokens.links[token.tag] = {
  1450. href: token.href,
  1451. title: token.title
  1452. };
  1453. }
  1454. continue;
  1455. }
  1456. // table (gfm)
  1457. if (token = this.tokenizer.table(src)) {
  1458. src = src.substring(token.raw.length);
  1459. tokens.push(token);
  1460. continue;
  1461. }
  1462. // lheading
  1463. if (token = this.tokenizer.lheading(src)) {
  1464. src = src.substring(token.raw.length);
  1465. tokens.push(token);
  1466. continue;
  1467. }
  1468. // top-level paragraph
  1469. // prevent paragraph consuming extensions by clipping 'src' to extension start
  1470. cutSrc = src;
  1471. if (this.options.extensions && this.options.extensions.startBlock) {
  1472. let startIndex = Infinity;
  1473. const tempSrc = src.slice(1);
  1474. let tempStart;
  1475. this.options.extensions.startBlock.forEach(function(getStartIndex) {
  1476. tempStart = getStartIndex.call({ lexer: this }, tempSrc);
  1477. if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); }
  1478. });
  1479. if (startIndex < Infinity && startIndex >= 0) {
  1480. cutSrc = src.substring(0, startIndex + 1);
  1481. }
  1482. }
  1483. if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
  1484. lastToken = tokens[tokens.length - 1];
  1485. if (lastParagraphClipped && lastToken.type === 'paragraph') {
  1486. lastToken.raw += '\n' + token.raw;
  1487. lastToken.text += '\n' + token.text;
  1488. this.inlineQueue.pop();
  1489. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  1490. } else {
  1491. tokens.push(token);
  1492. }
  1493. lastParagraphClipped = (cutSrc.length !== src.length);
  1494. src = src.substring(token.raw.length);
  1495. continue;
  1496. }
  1497. // text
  1498. if (token = this.tokenizer.text(src)) {
  1499. src = src.substring(token.raw.length);
  1500. lastToken = tokens[tokens.length - 1];
  1501. if (lastToken && lastToken.type === 'text') {
  1502. lastToken.raw += '\n' + token.raw;
  1503. lastToken.text += '\n' + token.text;
  1504. this.inlineQueue.pop();
  1505. this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
  1506. } else {
  1507. tokens.push(token);
  1508. }
  1509. continue;
  1510. }
  1511. if (src) {
  1512. const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
  1513. if (this.options.silent) {
  1514. console.error(errMsg);
  1515. break;
  1516. } else {
  1517. throw new Error(errMsg);
  1518. }
  1519. }
  1520. }
  1521. this.state.top = true;
  1522. return tokens;
  1523. }
  1524. inline(src, tokens = []) {
  1525. this.inlineQueue.push({ src, tokens });
  1526. return tokens;
  1527. }
  1528. /**
  1529. * Lexing/Compiling
  1530. */
  1531. inlineTokens(src, tokens = []) {
  1532. let token, lastToken, cutSrc;
  1533. // String with links masked to avoid interference with em and strong
  1534. let maskedSrc = src;
  1535. let match;
  1536. let keepPrevChar, prevChar;
  1537. // Mask out reflinks
  1538. if (this.tokens.links) {
  1539. const links = Object.keys(this.tokens.links);
  1540. if (links.length > 0) {
  1541. while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
  1542. if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
  1543. maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
  1544. }
  1545. }
  1546. }
  1547. }
  1548. // Mask out other blocks
  1549. while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
  1550. maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
  1551. }
  1552. // Mask out escaped characters
  1553. while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {
  1554. maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);
  1555. }
  1556. while (src) {
  1557. if (!keepPrevChar) {
  1558. prevChar = '';
  1559. }
  1560. keepPrevChar = false;
  1561. // extensions
  1562. if (this.options.extensions
  1563. && this.options.extensions.inline
  1564. && this.options.extensions.inline.some((extTokenizer) => {
  1565. if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
  1566. src = src.substring(token.raw.length);
  1567. tokens.push(token);
  1568. return true;
  1569. }
  1570. return false;
  1571. })) {
  1572. continue;
  1573. }
  1574. // escape
  1575. if (token = this.tokenizer.escape(src)) {
  1576. src = src.substring(token.raw.length);
  1577. tokens.push(token);
  1578. continue;
  1579. }
  1580. // tag
  1581. if (token = this.tokenizer.tag(src)) {
  1582. src = src.substring(token.raw.length);
  1583. lastToken = tokens[tokens.length - 1];
  1584. if (lastToken && token.type === 'text' && lastToken.type === 'text') {
  1585. lastToken.raw += token.raw;
  1586. lastToken.text += token.text;
  1587. } else {
  1588. tokens.push(token);
  1589. }
  1590. continue;
  1591. }
  1592. // link
  1593. if (token = this.tokenizer.link(src)) {
  1594. src = src.substring(token.raw.length);
  1595. tokens.push(token);
  1596. continue;
  1597. }
  1598. // reflink, nolink
  1599. if (token = this.tokenizer.reflink(src, this.tokens.links)) {
  1600. src = src.substring(token.raw.length);
  1601. lastToken = tokens[tokens.length - 1];
  1602. if (lastToken && token.type === 'text' && lastToken.type === 'text') {
  1603. lastToken.raw += token.raw;
  1604. lastToken.text += token.text;
  1605. } else {
  1606. tokens.push(token);
  1607. }
  1608. continue;
  1609. }
  1610. // em & strong
  1611. if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
  1612. src = src.substring(token.raw.length);
  1613. tokens.push(token);
  1614. continue;
  1615. }
  1616. // code
  1617. if (token = this.tokenizer.codespan(src)) {
  1618. src = src.substring(token.raw.length);
  1619. tokens.push(token);
  1620. continue;
  1621. }
  1622. // br
  1623. if (token = this.tokenizer.br(src)) {
  1624. src = src.substring(token.raw.length);
  1625. tokens.push(token);
  1626. continue;
  1627. }
  1628. // del (gfm)
  1629. if (token = this.tokenizer.del(src)) {
  1630. src = src.substring(token.raw.length);
  1631. tokens.push(token);
  1632. continue;
  1633. }
  1634. // autolink
  1635. if (token = this.tokenizer.autolink(src, mangle)) {
  1636. src = src.substring(token.raw.length);
  1637. tokens.push(token);
  1638. continue;
  1639. }
  1640. // url (gfm)
  1641. if (!this.state.inLink && (token = this.tokenizer.url(src, mangle))) {
  1642. src = src.substring(token.raw.length);
  1643. tokens.push(token);
  1644. continue;
  1645. }
  1646. // text
  1647. // prevent inlineText consuming extensions by clipping 'src' to extension start
  1648. cutSrc = src;
  1649. if (this.options.extensions && this.options.extensions.startInline) {
  1650. let startIndex = Infinity;
  1651. const tempSrc = src.slice(1);
  1652. let tempStart;
  1653. this.options.extensions.startInline.forEach(function(getStartIndex) {
  1654. tempStart = getStartIndex.call({ lexer: this }, tempSrc);
  1655. if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); }
  1656. });
  1657. if (startIndex < Infinity && startIndex >= 0) {
  1658. cutSrc = src.substring(0, startIndex + 1);
  1659. }
  1660. }
  1661. if (token = this.tokenizer.inlineText(cutSrc, smartypants)) {
  1662. src = src.substring(token.raw.length);
  1663. if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
  1664. prevChar = token.raw.slice(-1);
  1665. }
  1666. keepPrevChar = true;
  1667. lastToken = tokens[tokens.length - 1];
  1668. if (lastToken && lastToken.type === 'text') {
  1669. lastToken.raw += token.raw;
  1670. lastToken.text += token.text;
  1671. } else {
  1672. tokens.push(token);
  1673. }
  1674. continue;
  1675. }
  1676. if (src) {
  1677. const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
  1678. if (this.options.silent) {
  1679. console.error(errMsg);
  1680. break;
  1681. } else {
  1682. throw new Error(errMsg);
  1683. }
  1684. }
  1685. }
  1686. return tokens;
  1687. }
  1688. }
  1689. /**
  1690. * Renderer
  1691. */
  1692. class Renderer {
  1693. constructor(options) {
  1694. this.options = options || defaults;
  1695. }
  1696. code(code, infostring, escaped) {
  1697. const lang = (infostring || '').match(/\S*/)[0];
  1698. if (this.options.highlight) {
  1699. const out = this.options.highlight(code, lang);
  1700. if (out != null && out !== code) {
  1701. escaped = true;
  1702. code = out;
  1703. }
  1704. }
  1705. code = code.replace(/\n$/, '') + '\n';
  1706. if (!lang) {
  1707. return '<pre><code>'
  1708. + (escaped ? code : escape(code, true))
  1709. + '</code></pre>\n';
  1710. }
  1711. return '<pre><code class="'
  1712. + this.options.langPrefix
  1713. + escape(lang)
  1714. + '">'
  1715. + (escaped ? code : escape(code, true))
  1716. + '</code></pre>\n';
  1717. }
  1718. /**
  1719. * @param {string} quote
  1720. */
  1721. blockquote(quote) {
  1722. return `<blockquote>\n${quote}</blockquote>\n`;
  1723. }
  1724. html(html, block) {
  1725. return html;
  1726. }
  1727. /**
  1728. * @param {string} text
  1729. * @param {string} level
  1730. * @param {string} raw
  1731. * @param {any} slugger
  1732. */
  1733. heading(text, level, raw, slugger) {
  1734. if (this.options.headerIds) {
  1735. const id = this.options.headerPrefix + slugger.slug(raw);
  1736. return `<h${level} id="${id}">${text}</h${level}>\n`;
  1737. }
  1738. // ignore IDs
  1739. return `<h${level}>${text}</h${level}>\n`;
  1740. }
  1741. hr() {
  1742. return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
  1743. }
  1744. list(body, ordered, start) {
  1745. const type = ordered ? 'ol' : 'ul',
  1746. startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
  1747. return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
  1748. }
  1749. /**
  1750. * @param {string} text
  1751. */
  1752. listitem(text) {
  1753. return `<li>${text}</li>\n`;
  1754. }
  1755. checkbox(checked) {
  1756. return '<input '
  1757. + (checked ? 'checked="" ' : '')
  1758. + 'disabled="" type="checkbox"'
  1759. + (this.options.xhtml ? ' /' : '')
  1760. + '> ';
  1761. }
  1762. /**
  1763. * @param {string} text
  1764. */
  1765. paragraph(text) {
  1766. return `<p>${text}</p>\n`;
  1767. }
  1768. /**
  1769. * @param {string} header
  1770. * @param {string} body
  1771. */
  1772. table(header, body) {
  1773. if (body) body = `<tbody>${body}</tbody>`;
  1774. return '<table>\n'
  1775. + '<thead>\n'
  1776. + header
  1777. + '</thead>\n'
  1778. + body
  1779. + '</table>\n';
  1780. }
  1781. /**
  1782. * @param {string} content
  1783. */
  1784. tablerow(content) {
  1785. return `<tr>\n${content}</tr>\n`;
  1786. }
  1787. tablecell(content, flags) {
  1788. const type = flags.header ? 'th' : 'td';
  1789. const tag = flags.align
  1790. ? `<${type} align="${flags.align}">`
  1791. : `<${type}>`;
  1792. return tag + content + `</${type}>\n`;
  1793. }
  1794. /**
  1795. * span level renderer
  1796. * @param {string} text
  1797. */
  1798. strong(text) {
  1799. return `<strong>${text}</strong>`;
  1800. }
  1801. /**
  1802. * @param {string} text
  1803. */
  1804. em(text) {
  1805. return `<em>${text}</em>`;
  1806. }
  1807. /**
  1808. * @param {string} text
  1809. */
  1810. codespan(text) {
  1811. return `<code>${text}</code>`;
  1812. }
  1813. br() {
  1814. return this.options.xhtml ? '<br/>' : '<br>';
  1815. }
  1816. /**
  1817. * @param {string} text
  1818. */
  1819. del(text) {
  1820. return `<del>${text}</del>`;
  1821. }
  1822. /**
  1823. * @param {string} href
  1824. * @param {string} title
  1825. * @param {string} text
  1826. */
  1827. link(href, title, text) {
  1828. href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
  1829. if (href === null) {
  1830. return text;
  1831. }
  1832. let out = '<a href="' + href + '"';
  1833. if (title) {
  1834. out += ' title="' + title + '"';
  1835. }
  1836. out += '>' + text + '</a>';
  1837. return out;
  1838. }
  1839. /**
  1840. * @param {string} href
  1841. * @param {string} title
  1842. * @param {string} text
  1843. */
  1844. image(href, title, text) {
  1845. href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
  1846. if (href === null) {
  1847. return text;
  1848. }
  1849. let out = `<img src="${href}" alt="${text}"`;
  1850. if (title) {
  1851. out += ` title="${title}"`;
  1852. }
  1853. out += this.options.xhtml ? '/>' : '>';
  1854. return out;
  1855. }
  1856. text(text) {
  1857. return text;
  1858. }
  1859. }
  1860. /**
  1861. * TextRenderer
  1862. * returns only the textual part of the token
  1863. */
  1864. class TextRenderer {
  1865. // no need for block level renderers
  1866. strong(text) {
  1867. return text;
  1868. }
  1869. em(text) {
  1870. return text;
  1871. }
  1872. codespan(text) {
  1873. return text;
  1874. }
  1875. del(text) {
  1876. return text;
  1877. }
  1878. html(text) {
  1879. return text;
  1880. }
  1881. text(text) {
  1882. return text;
  1883. }
  1884. link(href, title, text) {
  1885. return '' + text;
  1886. }
  1887. image(href, title, text) {
  1888. return '' + text;
  1889. }
  1890. br() {
  1891. return '';
  1892. }
  1893. }
  1894. /**
  1895. * Slugger generates header id
  1896. */
  1897. class Slugger {
  1898. constructor() {
  1899. this.seen = {};
  1900. }
  1901. /**
  1902. * @param {string} value
  1903. */
  1904. serialize(value) {
  1905. return value
  1906. .toLowerCase()
  1907. .trim()
  1908. // remove html tags
  1909. .replace(/<[!\/a-z].*?>/ig, '')
  1910. // remove unwanted chars
  1911. .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
  1912. .replace(/\s/g, '-');
  1913. }
  1914. /**
  1915. * Finds the next safe (unique) slug to use
  1916. * @param {string} originalSlug
  1917. * @param {boolean} isDryRun
  1918. */
  1919. getNextSafeSlug(originalSlug, isDryRun) {
  1920. let slug = originalSlug;
  1921. let occurenceAccumulator = 0;
  1922. if (this.seen.hasOwnProperty(slug)) {
  1923. occurenceAccumulator = this.seen[originalSlug];
  1924. do {
  1925. occurenceAccumulator++;
  1926. slug = originalSlug + '-' + occurenceAccumulator;
  1927. } while (this.seen.hasOwnProperty(slug));
  1928. }
  1929. if (!isDryRun) {
  1930. this.seen[originalSlug] = occurenceAccumulator;
  1931. this.seen[slug] = 0;
  1932. }
  1933. return slug;
  1934. }
  1935. /**
  1936. * Convert string to unique id
  1937. * @param {object} [options]
  1938. * @param {boolean} [options.dryrun] Generates the next unique slug without
  1939. * updating the internal accumulator.
  1940. */
  1941. slug(value, options = {}) {
  1942. const slug = this.serialize(value);
  1943. return this.getNextSafeSlug(slug, options.dryrun);
  1944. }
  1945. }
  1946. /**
  1947. * Parsing & Compiling
  1948. */
  1949. class Parser {
  1950. constructor(options) {
  1951. this.options = options || defaults;
  1952. this.options.renderer = this.options.renderer || new Renderer();
  1953. this.renderer = this.options.renderer;
  1954. this.renderer.options = this.options;
  1955. this.textRenderer = new TextRenderer();
  1956. this.slugger = new Slugger();
  1957. }
  1958. /**
  1959. * Static Parse Method
  1960. */
  1961. static parse(tokens, options) {
  1962. const parser = new Parser(options);
  1963. return parser.parse(tokens);
  1964. }
  1965. /**
  1966. * Static Parse Inline Method
  1967. */
  1968. static parseInline(tokens, options) {
  1969. const parser = new Parser(options);
  1970. return parser.parseInline(tokens);
  1971. }
  1972. /**
  1973. * Parse Loop
  1974. */
  1975. parse(tokens, top = true) {
  1976. let out = '',
  1977. i,
  1978. j,
  1979. k,
  1980. l2,
  1981. l3,
  1982. row,
  1983. cell,
  1984. header,
  1985. body,
  1986. token,
  1987. ordered,
  1988. start,
  1989. loose,
  1990. itemBody,
  1991. item,
  1992. checked,
  1993. task,
  1994. checkbox,
  1995. ret;
  1996. const l = tokens.length;
  1997. for (i = 0; i < l; i++) {
  1998. token = tokens[i];
  1999. // Run any renderer extensions
  2000. if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
  2001. ret = this.options.extensions.renderers[token.type].call({ parser: this }, token);
  2002. if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(token.type)) {
  2003. out += ret || '';
  2004. continue;
  2005. }
  2006. }
  2007. switch (token.type) {
  2008. case 'space': {
  2009. continue;
  2010. }
  2011. case 'hr': {
  2012. out += this.renderer.hr();
  2013. continue;
  2014. }
  2015. case 'heading': {
  2016. out += this.renderer.heading(
  2017. this.parseInline(token.tokens),
  2018. token.depth,
  2019. unescape(this.parseInline(token.tokens, this.textRenderer)),
  2020. this.slugger);
  2021. continue;
  2022. }
  2023. case 'code': {
  2024. out += this.renderer.code(token.text,
  2025. token.lang,
  2026. token.escaped);
  2027. continue;
  2028. }
  2029. case 'table': {
  2030. header = '';
  2031. // header
  2032. cell = '';
  2033. l2 = token.header.length;
  2034. for (j = 0; j < l2; j++) {
  2035. cell += this.renderer.tablecell(
  2036. this.parseInline(token.header[j].tokens),
  2037. { header: true, align: token.align[j] }
  2038. );
  2039. }
  2040. header += this.renderer.tablerow(cell);
  2041. body = '';
  2042. l2 = token.rows.length;
  2043. for (j = 0; j < l2; j++) {
  2044. row = token.rows[j];
  2045. cell = '';
  2046. l3 = row.length;
  2047. for (k = 0; k < l3; k++) {
  2048. cell += this.renderer.tablecell(
  2049. this.parseInline(row[k].tokens),
  2050. { header: false, align: token.align[k] }
  2051. );
  2052. }
  2053. body += this.renderer.tablerow(cell);
  2054. }
  2055. out += this.renderer.table(header, body);
  2056. continue;
  2057. }
  2058. case 'blockquote': {
  2059. body = this.parse(token.tokens);
  2060. out += this.renderer.blockquote(body);
  2061. continue;
  2062. }
  2063. case 'list': {
  2064. ordered = token.ordered;
  2065. start = token.start;
  2066. loose = token.loose;
  2067. l2 = token.items.length;
  2068. body = '';
  2069. for (j = 0; j < l2; j++) {
  2070. item = token.items[j];
  2071. checked = item.checked;
  2072. task = item.task;
  2073. itemBody = '';
  2074. if (item.task) {
  2075. checkbox = this.renderer.checkbox(checked);
  2076. if (loose) {
  2077. if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') {
  2078. item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
  2079. if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
  2080. item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
  2081. }
  2082. } else {
  2083. item.tokens.unshift({
  2084. type: 'text',
  2085. text: checkbox
  2086. });
  2087. }
  2088. } else {
  2089. itemBody += checkbox;
  2090. }
  2091. }
  2092. itemBody += this.parse(item.tokens, loose);
  2093. body += this.renderer.listitem(itemBody, task, checked);
  2094. }
  2095. out += this.renderer.list(body, ordered, start);
  2096. continue;
  2097. }
  2098. case 'html': {
  2099. out += this.renderer.html(token.text, token.block);
  2100. continue;
  2101. }
  2102. case 'paragraph': {
  2103. out += this.renderer.paragraph(this.parseInline(token.tokens));
  2104. continue;
  2105. }
  2106. case 'text': {
  2107. body = token.tokens ? this.parseInline(token.tokens) : token.text;
  2108. while (i + 1 < l && tokens[i + 1].type === 'text') {
  2109. token = tokens[++i];
  2110. body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
  2111. }
  2112. out += top ? this.renderer.paragraph(body) : body;
  2113. continue;
  2114. }
  2115. default: {
  2116. const errMsg = 'Token with "' + token.type + '" type was not found.';
  2117. if (this.options.silent) {
  2118. console.error(errMsg);
  2119. return;
  2120. } else {
  2121. throw new Error(errMsg);
  2122. }
  2123. }
  2124. }
  2125. }
  2126. return out;
  2127. }
  2128. /**
  2129. * Parse Inline Tokens
  2130. */
  2131. parseInline(tokens, renderer) {
  2132. renderer = renderer || this.renderer;
  2133. let out = '',
  2134. i,
  2135. token,
  2136. ret;
  2137. const l = tokens.length;
  2138. for (i = 0; i < l; i++) {
  2139. token = tokens[i];
  2140. // Run any renderer extensions
  2141. if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
  2142. ret = this.options.extensions.renderers[token.type].call({ parser: this }, token);
  2143. if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) {
  2144. out += ret || '';
  2145. continue;
  2146. }
  2147. }
  2148. switch (token.type) {
  2149. case 'escape': {
  2150. out += renderer.text(token.text);
  2151. break;
  2152. }
  2153. case 'html': {
  2154. out += renderer.html(token.text);
  2155. break;
  2156. }
  2157. case 'link': {
  2158. out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
  2159. break;
  2160. }
  2161. case 'image': {
  2162. out += renderer.image(token.href, token.title, token.text);
  2163. break;
  2164. }
  2165. case 'strong': {
  2166. out += renderer.strong(this.parseInline(token.tokens, renderer));
  2167. break;
  2168. }
  2169. case 'em': {
  2170. out += renderer.em(this.parseInline(token.tokens, renderer));
  2171. break;
  2172. }
  2173. case 'codespan': {
  2174. out += renderer.codespan(token.text);
  2175. break;
  2176. }
  2177. case 'br': {
  2178. out += renderer.br();
  2179. break;
  2180. }
  2181. case 'del': {
  2182. out += renderer.del(this.parseInline(token.tokens, renderer));
  2183. break;
  2184. }
  2185. case 'text': {
  2186. out += renderer.text(token.text);
  2187. break;
  2188. }
  2189. default: {
  2190. const errMsg = 'Token with "' + token.type + '" type was not found.';
  2191. if (this.options.silent) {
  2192. console.error(errMsg);
  2193. return;
  2194. } else {
  2195. throw new Error(errMsg);
  2196. }
  2197. }
  2198. }
  2199. }
  2200. return out;
  2201. }
  2202. }
  2203. class Hooks {
  2204. constructor(options) {
  2205. this.options = options || defaults;
  2206. }
  2207. static passThroughHooks = new Set([
  2208. 'preprocess',
  2209. 'postprocess'
  2210. ]);
  2211. /**
  2212. * Process markdown before marked
  2213. */
  2214. preprocess(markdown) {
  2215. return markdown;
  2216. }
  2217. /**
  2218. * Process HTML after marked is finished
  2219. */
  2220. postprocess(html) {
  2221. return html;
  2222. }
  2223. }
  2224. class Marked {
  2225. defaults = getDefaults();
  2226. options = this.setOptions;
  2227. parse = this.#parseMarkdown(Lexer.lex, Parser.parse);
  2228. parseInline = this.#parseMarkdown(Lexer.lexInline, Parser.parseInline);
  2229. Parser = Parser;
  2230. parser = Parser.parse;
  2231. Renderer = Renderer;
  2232. TextRenderer = TextRenderer;
  2233. Lexer = Lexer;
  2234. lexer = Lexer.lex;
  2235. Tokenizer = Tokenizer;
  2236. Slugger = Slugger;
  2237. Hooks = Hooks;
  2238. constructor(...args) {
  2239. this.use(...args);
  2240. }
  2241. walkTokens(tokens, callback) {
  2242. let values = [];
  2243. for (const token of tokens) {
  2244. values = values.concat(callback.call(this, token));
  2245. switch (token.type) {
  2246. case 'table': {
  2247. for (const cell of token.header) {
  2248. values = values.concat(this.walkTokens(cell.tokens, callback));
  2249. }
  2250. for (const row of token.rows) {
  2251. for (const cell of row) {
  2252. values = values.concat(this.walkTokens(cell.tokens, callback));
  2253. }
  2254. }
  2255. break;
  2256. }
  2257. case 'list': {
  2258. values = values.concat(this.walkTokens(token.items, callback));
  2259. break;
  2260. }
  2261. default: {
  2262. if (this.defaults.extensions && this.defaults.extensions.childTokens && this.defaults.extensions.childTokens[token.type]) { // Walk any extensions
  2263. this.defaults.extensions.childTokens[token.type].forEach((childTokens) => {
  2264. values = values.concat(this.walkTokens(token[childTokens], callback));
  2265. });
  2266. } else if (token.tokens) {
  2267. values = values.concat(this.walkTokens(token.tokens, callback));
  2268. }
  2269. }
  2270. }
  2271. }
  2272. return values;
  2273. }
  2274. use(...args) {
  2275. const extensions = this.defaults.extensions || { renderers: {}, childTokens: {} };
  2276. args.forEach((pack) => {
  2277. // copy options to new object
  2278. const opts = { ...pack };
  2279. // set async to true if it was set to true before
  2280. opts.async = this.defaults.async || opts.async || false;
  2281. // ==-- Parse "addon" extensions --== //
  2282. if (pack.extensions) {
  2283. pack.extensions.forEach((ext) => {
  2284. if (!ext.name) {
  2285. throw new Error('extension name required');
  2286. }
  2287. if (ext.renderer) { // Renderer extensions
  2288. const prevRenderer = extensions.renderers[ext.name];
  2289. if (prevRenderer) {
  2290. // Replace extension with func to run new extension but fall back if false
  2291. extensions.renderers[ext.name] = function(...args) {
  2292. let ret = ext.renderer.apply(this, args);
  2293. if (ret === false) {
  2294. ret = prevRenderer.apply(this, args);
  2295. }
  2296. return ret;
  2297. };
  2298. } else {
  2299. extensions.renderers[ext.name] = ext.renderer;
  2300. }
  2301. }
  2302. if (ext.tokenizer) { // Tokenizer Extensions
  2303. if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {
  2304. throw new Error("extension level must be 'block' or 'inline'");
  2305. }
  2306. if (extensions[ext.level]) {
  2307. extensions[ext.level].unshift(ext.tokenizer);
  2308. } else {
  2309. extensions[ext.level] = [ext.tokenizer];
  2310. }
  2311. if (ext.start) { // Function to check for start of token
  2312. if (ext.level === 'block') {
  2313. if (extensions.startBlock) {
  2314. extensions.startBlock.push(ext.start);
  2315. } else {
  2316. extensions.startBlock = [ext.start];
  2317. }
  2318. } else if (ext.level === 'inline') {
  2319. if (extensions.startInline) {
  2320. extensions.startInline.push(ext.start);
  2321. } else {
  2322. extensions.startInline = [ext.start];
  2323. }
  2324. }
  2325. }
  2326. }
  2327. if (ext.childTokens) { // Child tokens to be visited by walkTokens
  2328. extensions.childTokens[ext.name] = ext.childTokens;
  2329. }
  2330. });
  2331. opts.extensions = extensions;
  2332. }
  2333. // ==-- Parse "overwrite" extensions --== //
  2334. if (pack.renderer) {
  2335. const renderer = this.defaults.renderer || new Renderer(this.defaults);
  2336. for (const prop in pack.renderer) {
  2337. const prevRenderer = renderer[prop];
  2338. // Replace renderer with func to run extension, but fall back if false
  2339. renderer[prop] = (...args) => {
  2340. let ret = pack.renderer[prop].apply(renderer, args);
  2341. if (ret === false) {
  2342. ret = prevRenderer.apply(renderer, args);
  2343. }
  2344. return ret;
  2345. };
  2346. }
  2347. opts.renderer = renderer;
  2348. }
  2349. if (pack.tokenizer) {
  2350. const tokenizer = this.defaults.tokenizer || new Tokenizer(this.defaults);
  2351. for (const prop in pack.tokenizer) {
  2352. const prevTokenizer = tokenizer[prop];
  2353. // Replace tokenizer with func to run extension, but fall back if false
  2354. tokenizer[prop] = (...args) => {
  2355. let ret = pack.tokenizer[prop].apply(tokenizer, args);
  2356. if (ret === false) {
  2357. ret = prevTokenizer.apply(tokenizer, args);
  2358. }
  2359. return ret;
  2360. };
  2361. }
  2362. opts.tokenizer = tokenizer;
  2363. }
  2364. // ==-- Parse Hooks extensions --== //
  2365. if (pack.hooks) {
  2366. const hooks = this.defaults.hooks || new Hooks();
  2367. for (const prop in pack.hooks) {
  2368. const prevHook = hooks[prop];
  2369. if (Hooks.passThroughHooks.has(prop)) {
  2370. hooks[prop] = (arg) => {
  2371. if (this.defaults.async) {
  2372. return Promise.resolve(pack.hooks[prop].call(hooks, arg)).then(ret => {
  2373. return prevHook.call(hooks, ret);
  2374. });
  2375. }
  2376. const ret = pack.hooks[prop].call(hooks, arg);
  2377. return prevHook.call(hooks, ret);
  2378. };
  2379. } else {
  2380. hooks[prop] = (...args) => {
  2381. let ret = pack.hooks[prop].apply(hooks, args);
  2382. if (ret === false) {
  2383. ret = prevHook.apply(hooks, args);
  2384. }
  2385. return ret;
  2386. };
  2387. }
  2388. }
  2389. opts.hooks = hooks;
  2390. }
  2391. // ==-- Parse WalkTokens extensions --== //
  2392. if (pack.walkTokens) {
  2393. const walkTokens = this.defaults.walkTokens;
  2394. opts.walkTokens = function(token) {
  2395. let values = [];
  2396. values.push(pack.walkTokens.call(this, token));
  2397. if (walkTokens) {
  2398. values = values.concat(walkTokens.call(this, token));
  2399. }
  2400. return values;
  2401. };
  2402. }
  2403. this.defaults = { ...this.defaults, ...opts };
  2404. });
  2405. return this;
  2406. }
  2407. setOptions(opt) {
  2408. this.defaults = { ...this.defaults, ...opt };
  2409. return this;
  2410. }
  2411. #parseMarkdown(lexer, parser) {
  2412. return (src, opt, callback) => {
  2413. if (typeof opt === 'function') {
  2414. callback = opt;
  2415. opt = null;
  2416. }
  2417. const origOpt = { ...opt };
  2418. opt = { ...this.defaults, ...origOpt };
  2419. const throwError = this.#onError(opt.silent, opt.async, callback);
  2420. // throw error in case of non string input
  2421. if (typeof src === 'undefined' || src === null) {
  2422. return throwError(new Error('marked(): input parameter is undefined or null'));
  2423. }
  2424. if (typeof src !== 'string') {
  2425. return throwError(new Error('marked(): input parameter is of type '
  2426. + Object.prototype.toString.call(src) + ', string expected'));
  2427. }
  2428. checkDeprecations(opt, callback);
  2429. if (opt.hooks) {
  2430. opt.hooks.options = opt;
  2431. }
  2432. if (callback) {
  2433. const highlight = opt.highlight;
  2434. let tokens;
  2435. try {
  2436. if (opt.hooks) {
  2437. src = opt.hooks.preprocess(src);
  2438. }
  2439. tokens = lexer(src, opt);
  2440. } catch (e) {
  2441. return throwError(e);
  2442. }
  2443. const done = (err) => {
  2444. let out;
  2445. if (!err) {
  2446. try {
  2447. if (opt.walkTokens) {
  2448. this.walkTokens(tokens, opt.walkTokens);
  2449. }
  2450. out = parser(tokens, opt);
  2451. if (opt.hooks) {
  2452. out = opt.hooks.postprocess(out);
  2453. }
  2454. } catch (e) {
  2455. err = e;
  2456. }
  2457. }
  2458. opt.highlight = highlight;
  2459. return err
  2460. ? throwError(err)
  2461. : callback(null, out);
  2462. };
  2463. if (!highlight || highlight.length < 3) {
  2464. return done();
  2465. }
  2466. delete opt.highlight;
  2467. if (!tokens.length) return done();
  2468. let pending = 0;
  2469. this.walkTokens(tokens, (token) => {
  2470. if (token.type === 'code') {
  2471. pending++;
  2472. setTimeout(() => {
  2473. highlight(token.text, token.lang, (err, code) => {
  2474. if (err) {
  2475. return done(err);
  2476. }
  2477. if (code != null && code !== token.text) {
  2478. token.text = code;
  2479. token.escaped = true;
  2480. }
  2481. pending--;
  2482. if (pending === 0) {
  2483. done();
  2484. }
  2485. });
  2486. }, 0);
  2487. }
  2488. });
  2489. if (pending === 0) {
  2490. done();
  2491. }
  2492. return;
  2493. }
  2494. if (opt.async) {
  2495. return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
  2496. .then(src => lexer(src, opt))
  2497. .then(tokens => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens)
  2498. .then(tokens => parser(tokens, opt))
  2499. .then(html => opt.hooks ? opt.hooks.postprocess(html) : html)
  2500. .catch(throwError);
  2501. }
  2502. try {
  2503. if (opt.hooks) {
  2504. src = opt.hooks.preprocess(src);
  2505. }
  2506. const tokens = lexer(src, opt);
  2507. if (opt.walkTokens) {
  2508. this.walkTokens(tokens, opt.walkTokens);
  2509. }
  2510. let html = parser(tokens, opt);
  2511. if (opt.hooks) {
  2512. html = opt.hooks.postprocess(html);
  2513. }
  2514. return html;
  2515. } catch (e) {
  2516. return throwError(e);
  2517. }
  2518. };
  2519. }
  2520. #onError(silent, async, callback) {
  2521. return (e) => {
  2522. e.message += '\nPlease report this to https://github.com/markedjs/this.';
  2523. if (silent) {
  2524. const msg = '<p>An error occurred:</p><pre>'
  2525. + escape(e.message + '', true)
  2526. + '</pre>';
  2527. if (async) {
  2528. return Promise.resolve(msg);
  2529. }
  2530. if (callback) {
  2531. callback(null, msg);
  2532. return;
  2533. }
  2534. return msg;
  2535. }
  2536. if (async) {
  2537. return Promise.reject(e);
  2538. }
  2539. if (callback) {
  2540. callback(e);
  2541. return;
  2542. }
  2543. throw e;
  2544. };
  2545. }
  2546. }
  2547. const markedInstance = new Marked(defaults);
  2548. /**
  2549. * Marked
  2550. */
  2551. function marked(src, opt, callback) {
  2552. return markedInstance.parse(src, opt, callback);
  2553. }
  2554. /**
  2555. * Options
  2556. */
  2557. marked.options =
  2558. marked.setOptions = function(opt) {
  2559. markedInstance.setOptions(opt);
  2560. marked.defaults = markedInstance.defaults;
  2561. changeDefaults(marked.defaults);
  2562. return marked;
  2563. };
  2564. marked.getDefaults = getDefaults;
  2565. marked.defaults = defaults;
  2566. /**
  2567. * Use Extension
  2568. */
  2569. marked.use = function(...args) {
  2570. markedInstance.use(...args);
  2571. marked.defaults = markedInstance.defaults;
  2572. changeDefaults(marked.defaults);
  2573. return marked;
  2574. };
  2575. /**
  2576. * Run callback for every token
  2577. */
  2578. marked.walkTokens = function(tokens, callback) {
  2579. return markedInstance.walkTokens(tokens, callback);
  2580. };
  2581. /**
  2582. * Parse Inline
  2583. * @param {string} src
  2584. */
  2585. marked.parseInline = markedInstance.parseInline;
  2586. /**
  2587. * Expose
  2588. */
  2589. marked.Parser = Parser;
  2590. marked.parser = Parser.parse;
  2591. marked.Renderer = Renderer;
  2592. marked.TextRenderer = TextRenderer;
  2593. marked.Lexer = Lexer;
  2594. marked.lexer = Lexer.lex;
  2595. marked.Tokenizer = Tokenizer;
  2596. marked.Slugger = Slugger;
  2597. marked.Hooks = Hooks;
  2598. marked.parse = marked;
  2599. const options = marked.options;
  2600. const setOptions = marked.setOptions;
  2601. const use = marked.use;
  2602. const walkTokens = marked.walkTokens;
  2603. const parseInline = marked.parseInline;
  2604. const parse = marked;
  2605. const parser = Parser.parse;
  2606. const lexer = Lexer.lex;
  2607. export { Hooks, Lexer, Marked, Parser, Renderer, Slugger, TextRenderer, Tokenizer, defaults, getDefaults, lexer, marked, options, parse, parseInline, parser, setOptions, use, walkTokens };