html.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. 'use strict'
  2. var decodeEntity = require('parse-entities/decode-entity.js')
  3. var codes = require('../character/codes.js')
  4. var assign = require('../constant/assign.js')
  5. var constants = require('../constant/constants.js')
  6. var hasOwnProperty = require('../constant/has-own-property.js')
  7. var types = require('../constant/types.js')
  8. var combineHtmlExtensions = require('../util/combine-html-extensions.js')
  9. var chunkedPush = require('../util/chunked-push.js')
  10. var miniflat = require('../util/miniflat.js')
  11. var normalizeIdentifier = require('../util/normalize-identifier.js')
  12. var normalizeUri = require('../util/normalize-uri.js')
  13. var safeFromInt = require('../util/safe-from-int.js')
  14. function _interopDefaultLegacy(e) {
  15. return e && typeof e === 'object' && 'default' in e ? e : {default: e}
  16. }
  17. var decodeEntity__default = /*#__PURE__*/ _interopDefaultLegacy(decodeEntity)
  18. // While micromark is a lexer/tokenizer, the common case of going from markdown
  19. // This ensures that certain characters which have special meaning in HTML are
  20. // dealt with.
  21. // Technically, we can skip `>` and `"` in many cases, but CM includes them.
  22. var characterReferences = {'"': 'quot', '&': 'amp', '<': 'lt', '>': 'gt'}
  23. // These two are allowlists of essentially safe protocols for full URLs in
  24. // respectively the `href` (on `<a>`) and `src` (on `<img>`) attributes.
  25. // They are based on what is allowed on GitHub,
  26. // <https://github.com/syntax-tree/hast-util-sanitize/blob/9275b21/lib/github.json#L31>
  27. var protocolHref = /^(https?|ircs?|mailto|xmpp)$/i
  28. var protocolSrc = /^https?$/i
  29. function compileHtml(options) {
  30. // Configuration.
  31. // Includes `htmlExtensions` (an array of extensions), `defaultLineEnding` (a
  32. // preferred EOL), `allowDangerousProtocol` (whether to allow potential
  33. // dangerous protocols), and `allowDangerousHtml` (whether to allow potential
  34. // dangerous HTML).
  35. var settings = options || {}
  36. // Tags is needed because according to markdown, links and emphasis and
  37. // whatnot can exist in images, however, as HTML doesn’t allow content in
  38. // images, the tags are ignored in the `alt` attribute, but the content
  39. // remains.
  40. var tags = true
  41. // An object to track identifiers to media (URLs and titles) defined with
  42. // definitions.
  43. var definitions = {}
  44. // A lot of the handlers need to capture some of the output data, modify it
  45. // somehow, and then deal with it.
  46. // We do that by tracking a stack of buffers, that can be opened (with
  47. // `buffer`) and closed (with `resume`) to access them.
  48. var buffers = [[]]
  49. // As we can have links in images and the other way around, where the deepest
  50. // ones are closed first, we need to track which one we’re in.
  51. var mediaStack = []
  52. // Same for tightness, which is specific to lists.
  53. // We need to track if we’re currently in a tight or loose container.
  54. var tightStack = []
  55. var defaultHandlers = {
  56. enter: {
  57. blockQuote: onenterblockquote,
  58. codeFenced: onentercodefenced,
  59. codeFencedFenceInfo: buffer,
  60. codeFencedFenceMeta: buffer,
  61. codeIndented: onentercodeindented,
  62. codeText: onentercodetext,
  63. content: onentercontent,
  64. definition: onenterdefinition,
  65. definitionDestinationString: onenterdefinitiondestinationstring,
  66. definitionLabelString: buffer,
  67. definitionTitleString: buffer,
  68. emphasis: onenteremphasis,
  69. htmlFlow: onenterhtmlflow,
  70. htmlText: onenterhtml,
  71. image: onenterimage,
  72. label: buffer,
  73. link: onenterlink,
  74. listItemMarker: onenterlistitemmarker,
  75. listItemValue: onenterlistitemvalue,
  76. listOrdered: onenterlistordered,
  77. listUnordered: onenterlistunordered,
  78. paragraph: onenterparagraph,
  79. reference: buffer,
  80. resource: onenterresource,
  81. resourceDestinationString: onenterresourcedestinationstring,
  82. resourceTitleString: buffer,
  83. setextHeading: onentersetextheading,
  84. strong: onenterstrong
  85. },
  86. exit: {
  87. atxHeading: onexitatxheading,
  88. atxHeadingSequence: onexitatxheadingsequence,
  89. autolinkEmail: onexitautolinkemail,
  90. autolinkProtocol: onexitautolinkprotocol,
  91. blockQuote: onexitblockquote,
  92. characterEscapeValue: onexitdata,
  93. characterReferenceMarkerHexadecimal: onexitcharacterreferencemarker,
  94. characterReferenceMarkerNumeric: onexitcharacterreferencemarker,
  95. characterReferenceValue: onexitcharacterreferencevalue,
  96. codeFenced: onexitflowcode,
  97. codeFencedFence: onexitcodefencedfence,
  98. codeFencedFenceInfo: onexitcodefencedfenceinfo,
  99. codeFencedFenceMeta: resume,
  100. codeFlowValue: onexitcodeflowvalue,
  101. codeIndented: onexitflowcode,
  102. codeText: onexitcodetext,
  103. codeTextData: onexitdata,
  104. data: onexitdata,
  105. definition: onexitdefinition,
  106. definitionDestinationString: onexitdefinitiondestinationstring,
  107. definitionLabelString: onexitdefinitionlabelstring,
  108. definitionTitleString: onexitdefinitiontitlestring,
  109. emphasis: onexitemphasis,
  110. hardBreakEscape: onexithardbreak,
  111. hardBreakTrailing: onexithardbreak,
  112. htmlFlow: onexithtml,
  113. htmlFlowData: onexitdata,
  114. htmlText: onexithtml,
  115. htmlTextData: onexitdata,
  116. image: onexitmedia,
  117. label: onexitlabel,
  118. labelText: onexitlabeltext,
  119. lineEnding: onexitlineending,
  120. link: onexitmedia,
  121. listOrdered: onexitlistordered,
  122. listUnordered: onexitlistunordered,
  123. paragraph: onexitparagraph,
  124. reference: resume,
  125. referenceString: onexitreferencestring,
  126. resource: resume,
  127. resourceDestinationString: onexitresourcedestinationstring,
  128. resourceTitleString: onexitresourcetitlestring,
  129. setextHeading: onexitsetextheading,
  130. setextHeadingLineSequence: onexitsetextheadinglinesequence,
  131. setextHeadingText: onexitsetextheadingtext,
  132. strong: onexitstrong,
  133. thematicBreak: onexitthematicbreak
  134. }
  135. }
  136. // Combine the HTML extensions with the default handlers.
  137. // An HTML extension is an object whose fields are either `enter` or `exit`
  138. // (reflecting whether a token is entered or exited).
  139. // The values at such objects are names of tokens mapping to handlers.
  140. // Handlers are called, respectively when a token is opener or closed, with
  141. // that token, and a context as `this`.
  142. var handlers = combineHtmlExtensions(
  143. [defaultHandlers].concat(miniflat(settings.htmlExtensions))
  144. )
  145. // Handlers do often need to keep track of some state.
  146. // That state is provided here as a key-value store (an object).
  147. var data = {tightStack: tightStack}
  148. // The context for handlers references a couple of useful functions.
  149. // In handlers from extensions, those can be accessed at `this`.
  150. // For the handlers here, they can be accessed directly.
  151. var context = {
  152. lineEndingIfNeeded: lineEndingIfNeeded,
  153. options: settings,
  154. encode: encode,
  155. raw: raw,
  156. tag: tag,
  157. buffer: buffer,
  158. resume: resume,
  159. setData: setData,
  160. getData: getData
  161. }
  162. // Generally, micromark copies line endings (`'\r'`, `'\n'`, `'\r\n'`) in the
  163. // markdown document over to the compiled HTML.
  164. // In some cases, such as `> a`, CommonMark requires that extra line endings
  165. // are added: `<blockquote>\n<p>a</p>\n</blockquote>`.
  166. // This variable hold the default line ending when given (or `undefined`),
  167. // and in the latter case will be updated to the first found line ending if
  168. // there is one.
  169. var lineEndingStyle = settings.defaultLineEnding
  170. // Return the function that handles a slice of events.
  171. return compile
  172. // Deal w/ a slice of events.
  173. // Return either the empty string if there’s nothing of note to return, or the
  174. // result when done.
  175. function compile(events) {
  176. // As definitions can come after references, we need to figure out the media
  177. // (urls and titles) defined by them before handling the references.
  178. // So, we do sort of what HTML does: put metadata at the start (in head), and
  179. // then put content after (`body`).
  180. var head = []
  181. var body = []
  182. var index
  183. var start
  184. var listStack
  185. var handler
  186. var result
  187. index = -1
  188. start = 0
  189. listStack = []
  190. while (++index < events.length) {
  191. // Figure out the line ending style used in the document.
  192. if (
  193. !lineEndingStyle &&
  194. (events[index][1].type === types.lineEnding ||
  195. events[index][1].type === types.lineEndingBlank)
  196. ) {
  197. lineEndingStyle = events[index][2].sliceSerialize(events[index][1])
  198. }
  199. // Preprocess lists to infer whether the list is loose or not.
  200. if (
  201. events[index][1].type === types.listOrdered ||
  202. events[index][1].type === types.listUnordered
  203. ) {
  204. if (events[index][0] === 'enter') {
  205. listStack.push(index)
  206. } else {
  207. prepareList(events.slice(listStack.pop(), index))
  208. }
  209. }
  210. // Move definitions to the front.
  211. if (events[index][1].type === types.definition) {
  212. if (events[index][0] === 'enter') {
  213. body = chunkedPush(body, events.slice(start, index))
  214. start = index
  215. } else {
  216. head = chunkedPush(head, events.slice(start, index + 1))
  217. start = index + 1
  218. }
  219. }
  220. }
  221. head = chunkedPush(head, body)
  222. head = chunkedPush(head, events.slice(start))
  223. result = head
  224. index = -1
  225. // Handle the start of the document, if defined.
  226. if (handlers.enter.null) {
  227. handlers.enter.null.call(context)
  228. }
  229. // Handle all events.
  230. while (++index < events.length) {
  231. handler = handlers[result[index][0]]
  232. if (hasOwnProperty.call(handler, result[index][1].type)) {
  233. handler[result[index][1].type].call(
  234. assign({sliceSerialize: result[index][2].sliceSerialize}, context),
  235. result[index][1]
  236. )
  237. }
  238. }
  239. // Handle the end of the document, if defined.
  240. if (handlers.exit.null) {
  241. handlers.exit.null.call(context)
  242. }
  243. return buffers[0].join('')
  244. }
  245. // Figure out whether lists are loose or not.
  246. function prepareList(slice) {
  247. var length = slice.length - 1 // Skip close.
  248. var index = 0 // Skip open.
  249. var containerBalance = 0
  250. var loose
  251. var atMarker
  252. var event
  253. while (++index < length) {
  254. event = slice[index]
  255. if (event[1]._container) {
  256. atMarker = undefined
  257. if (event[0] === 'enter') {
  258. containerBalance++
  259. } else {
  260. containerBalance--
  261. }
  262. } else if (event[1].type === types.listItemPrefix) {
  263. if (event[0] === 'exit') {
  264. atMarker = true
  265. }
  266. } else if (event[1].type === types.linePrefix);
  267. else if (event[1].type === types.lineEndingBlank) {
  268. if (event[0] === 'enter' && !containerBalance) {
  269. if (atMarker) {
  270. atMarker = undefined
  271. } else {
  272. loose = true
  273. }
  274. }
  275. } else {
  276. atMarker = undefined
  277. }
  278. }
  279. slice[0][1]._loose = loose
  280. }
  281. // Set data into the key-value store.
  282. function setData(key, value) {
  283. data[key] = value
  284. }
  285. // Get data from the key-value store.
  286. function getData(key) {
  287. return data[key]
  288. }
  289. // Capture some of the output data.
  290. function buffer() {
  291. buffers.push([])
  292. }
  293. // Stop capturing and access the output data.
  294. function resume() {
  295. return buffers.pop().join('')
  296. }
  297. // Output (parts of) HTML tags.
  298. function tag(value) {
  299. if (!tags) return
  300. setData('lastWasTag', true)
  301. buffers[buffers.length - 1].push(value)
  302. }
  303. // Output raw data.
  304. function raw(value) {
  305. setData('lastWasTag')
  306. buffers[buffers.length - 1].push(value)
  307. }
  308. // Output an extra line ending.
  309. function lineEnding() {
  310. raw(lineEndingStyle || '\n')
  311. }
  312. // Output an extra line ending if the previous value wasn’t EOF/EOL.
  313. function lineEndingIfNeeded() {
  314. var buffer = buffers[buffers.length - 1]
  315. var slice = buffer[buffer.length - 1]
  316. var previous = slice ? slice.charCodeAt(slice.length - 1) : codes.eof
  317. if (
  318. previous === codes.lf ||
  319. previous === codes.cr ||
  320. previous === codes.eof
  321. ) {
  322. return
  323. }
  324. lineEnding()
  325. }
  326. // Make a value safe for injection in HTML (except w/ `ignoreEncode`).
  327. function encode(value) {
  328. return getData('ignoreEncode') ? value : value.replace(/["&<>]/g, replace)
  329. function replace(value) {
  330. return '&' + characterReferences[value] + ';'
  331. }
  332. }
  333. // Make a value safe for injection as a URL.
  334. // This does encode unsafe characters with percent-encoding, skipping already
  335. // encoded sequences (`normalizeUri`).
  336. // Further unsafe characters are encoded as character references (`encode`).
  337. // Finally, if the URL includes an unknown protocol (such as a dangerous
  338. // example, `javascript:`), the value is ignored.
  339. function url(url, protocol) {
  340. var value = encode(normalizeUri(url || ''))
  341. var colon = value.indexOf(':')
  342. var questionMark = value.indexOf('?')
  343. var numberSign = value.indexOf('#')
  344. var slash = value.indexOf('/')
  345. if (
  346. settings.allowDangerousProtocol ||
  347. // If there is no protocol, it’s relative.
  348. colon < 0 ||
  349. // If the first colon is after a `?`, `#`, or `/`, it’s not a protocol.
  350. (slash > -1 && colon > slash) ||
  351. (questionMark > -1 && colon > questionMark) ||
  352. (numberSign > -1 && colon > numberSign) ||
  353. // It is a protocol, it should be allowed.
  354. protocol.test(value.slice(0, colon))
  355. ) {
  356. return value
  357. }
  358. return ''
  359. }
  360. //
  361. // Handlers.
  362. //
  363. function onenterlistordered(token) {
  364. tightStack.push(!token._loose)
  365. lineEndingIfNeeded()
  366. tag('<ol')
  367. setData('expectFirstItem', true)
  368. }
  369. function onenterlistunordered(token) {
  370. tightStack.push(!token._loose)
  371. lineEndingIfNeeded()
  372. tag('<ul')
  373. setData('expectFirstItem', true)
  374. }
  375. function onenterlistitemvalue(token) {
  376. var value
  377. if (getData('expectFirstItem')) {
  378. value = parseInt(this.sliceSerialize(token), constants.numericBaseDecimal)
  379. if (value !== 1) {
  380. tag(' start="' + encode(String(value)) + '"')
  381. }
  382. }
  383. }
  384. function onenterlistitemmarker() {
  385. if (getData('expectFirstItem')) {
  386. tag('>')
  387. } else {
  388. onexitlistitem()
  389. }
  390. lineEndingIfNeeded()
  391. tag('<li>')
  392. setData('expectFirstItem')
  393. // “Hack” to prevent a line ending from showing up if the item is empty.
  394. setData('lastWasTag')
  395. }
  396. function onexitlistordered() {
  397. onexitlistitem()
  398. tightStack.pop()
  399. lineEnding()
  400. tag('</ol>')
  401. }
  402. function onexitlistunordered() {
  403. onexitlistitem()
  404. tightStack.pop()
  405. lineEnding()
  406. tag('</ul>')
  407. }
  408. function onexitlistitem() {
  409. if (getData('lastWasTag') && !getData('slurpAllLineEndings')) {
  410. lineEndingIfNeeded()
  411. }
  412. tag('</li>')
  413. setData('slurpAllLineEndings')
  414. }
  415. function onenterblockquote() {
  416. tightStack.push(false)
  417. lineEndingIfNeeded()
  418. tag('<blockquote>')
  419. }
  420. function onexitblockquote() {
  421. tightStack.pop()
  422. lineEndingIfNeeded()
  423. tag('</blockquote>')
  424. setData('slurpAllLineEndings')
  425. }
  426. function onenterparagraph() {
  427. if (!tightStack[tightStack.length - 1]) {
  428. lineEndingIfNeeded()
  429. tag('<p>')
  430. }
  431. setData('slurpAllLineEndings')
  432. }
  433. function onexitparagraph() {
  434. if (tightStack[tightStack.length - 1]) {
  435. setData('slurpAllLineEndings', true)
  436. } else {
  437. tag('</p>')
  438. }
  439. }
  440. function onentercodefenced() {
  441. lineEndingIfNeeded()
  442. tag('<pre><code')
  443. setData('fencesCount', 0)
  444. }
  445. function onexitcodefencedfenceinfo() {
  446. var value = resume()
  447. tag(' class="language-' + value + '"')
  448. }
  449. function onexitcodefencedfence() {
  450. if (!getData('fencesCount')) {
  451. tag('>')
  452. setData('fencedCodeInside', true)
  453. setData('slurpOneLineEnding', true)
  454. }
  455. setData('fencesCount', getData('fencesCount') + 1)
  456. }
  457. function onentercodeindented() {
  458. lineEndingIfNeeded()
  459. tag('<pre><code>')
  460. }
  461. function onexitflowcode() {
  462. // Send an extra line feed if we saw data.
  463. if (getData('flowCodeSeenData')) lineEndingIfNeeded()
  464. tag('</code></pre>')
  465. if (getData('fencesCount') < 2) lineEndingIfNeeded()
  466. setData('flowCodeSeenData')
  467. setData('fencesCount')
  468. setData('slurpOneLineEnding')
  469. }
  470. function onenterimage() {
  471. mediaStack.push({image: true})
  472. tags = undefined // Disallow tags.
  473. }
  474. function onenterlink() {
  475. mediaStack.push({})
  476. }
  477. function onexitlabeltext(token) {
  478. mediaStack[mediaStack.length - 1].labelId = this.sliceSerialize(token)
  479. }
  480. function onexitlabel() {
  481. mediaStack[mediaStack.length - 1].label = resume()
  482. }
  483. function onexitreferencestring(token) {
  484. mediaStack[mediaStack.length - 1].referenceId = this.sliceSerialize(token)
  485. }
  486. function onenterresource() {
  487. buffer() // We can have line endings in the resource, ignore them.
  488. mediaStack[mediaStack.length - 1].destination = ''
  489. }
  490. function onenterresourcedestinationstring() {
  491. buffer()
  492. // Ignore encoding the result, as we’ll first percent encode the url and
  493. // encode manually after.
  494. setData('ignoreEncode', true)
  495. }
  496. function onexitresourcedestinationstring() {
  497. mediaStack[mediaStack.length - 1].destination = resume()
  498. setData('ignoreEncode')
  499. }
  500. function onexitresourcetitlestring() {
  501. mediaStack[mediaStack.length - 1].title = resume()
  502. }
  503. function onexitmedia() {
  504. var index = mediaStack.length - 1 // Skip current.
  505. var media = mediaStack[index]
  506. var context =
  507. media.destination === undefined
  508. ? definitions[normalizeIdentifier(media.referenceId || media.labelId)]
  509. : media
  510. tags = true
  511. while (index--) {
  512. if (mediaStack[index].image) {
  513. tags = undefined
  514. break
  515. }
  516. }
  517. if (media.image) {
  518. tag('<img src="' + url(context.destination, protocolSrc) + '" alt="')
  519. raw(media.label)
  520. tag('"')
  521. } else {
  522. tag('<a href="' + url(context.destination, protocolHref) + '"')
  523. }
  524. tag(context.title ? ' title="' + context.title + '"' : '')
  525. if (media.image) {
  526. tag(' />')
  527. } else {
  528. tag('>')
  529. raw(media.label)
  530. tag('</a>')
  531. }
  532. mediaStack.pop()
  533. }
  534. function onenterdefinition() {
  535. buffer()
  536. mediaStack.push({})
  537. }
  538. function onexitdefinitionlabelstring(token) {
  539. // Discard label, use the source content instead.
  540. resume()
  541. mediaStack[mediaStack.length - 1].labelId = this.sliceSerialize(token)
  542. }
  543. function onenterdefinitiondestinationstring() {
  544. buffer()
  545. setData('ignoreEncode', true)
  546. }
  547. function onexitdefinitiondestinationstring() {
  548. mediaStack[mediaStack.length - 1].destination = resume()
  549. setData('ignoreEncode')
  550. }
  551. function onexitdefinitiontitlestring() {
  552. mediaStack[mediaStack.length - 1].title = resume()
  553. }
  554. function onexitdefinition() {
  555. var id = normalizeIdentifier(mediaStack[mediaStack.length - 1].labelId)
  556. resume()
  557. if (!hasOwnProperty.call(definitions, id)) {
  558. definitions[id] = mediaStack[mediaStack.length - 1]
  559. }
  560. mediaStack.pop()
  561. }
  562. function onentercontent() {
  563. setData('slurpAllLineEndings', true)
  564. }
  565. function onexitatxheadingsequence(token) {
  566. // Exit for further sequences.
  567. if (getData('headingRank')) return
  568. setData('headingRank', this.sliceSerialize(token).length)
  569. lineEndingIfNeeded()
  570. tag('<h' + getData('headingRank') + '>')
  571. }
  572. function onentersetextheading() {
  573. buffer()
  574. setData('slurpAllLineEndings')
  575. }
  576. function onexitsetextheadingtext() {
  577. setData('slurpAllLineEndings', true)
  578. }
  579. function onexitatxheading() {
  580. tag('</h' + getData('headingRank') + '>')
  581. setData('headingRank')
  582. }
  583. function onexitsetextheadinglinesequence(token) {
  584. setData(
  585. 'headingRank',
  586. this.sliceSerialize(token).charCodeAt(0) === codes.equalsTo ? 1 : 2
  587. )
  588. }
  589. function onexitsetextheading() {
  590. var value = resume()
  591. lineEndingIfNeeded()
  592. tag('<h' + getData('headingRank') + '>')
  593. raw(value)
  594. tag('</h' + getData('headingRank') + '>')
  595. setData('slurpAllLineEndings')
  596. setData('headingRank')
  597. }
  598. function onexitdata(token) {
  599. raw(encode(this.sliceSerialize(token)))
  600. }
  601. function onexitlineending(token) {
  602. if (getData('slurpAllLineEndings')) {
  603. return
  604. }
  605. if (getData('slurpOneLineEnding')) {
  606. setData('slurpOneLineEnding')
  607. return
  608. }
  609. if (getData('inCodeText')) {
  610. raw(' ')
  611. return
  612. }
  613. raw(encode(this.sliceSerialize(token)))
  614. }
  615. function onexitcodeflowvalue(token) {
  616. raw(encode(this.sliceSerialize(token)))
  617. setData('flowCodeSeenData', true)
  618. }
  619. function onexithardbreak() {
  620. tag('<br />')
  621. }
  622. function onenterhtmlflow() {
  623. lineEndingIfNeeded()
  624. onenterhtml()
  625. }
  626. function onexithtml() {
  627. setData('ignoreEncode')
  628. }
  629. function onenterhtml() {
  630. if (settings.allowDangerousHtml) {
  631. setData('ignoreEncode', true)
  632. }
  633. }
  634. function onenteremphasis() {
  635. tag('<em>')
  636. }
  637. function onenterstrong() {
  638. tag('<strong>')
  639. }
  640. function onentercodetext() {
  641. setData('inCodeText', true)
  642. tag('<code>')
  643. }
  644. function onexitcodetext() {
  645. setData('inCodeText')
  646. tag('</code>')
  647. }
  648. function onexitemphasis() {
  649. tag('</em>')
  650. }
  651. function onexitstrong() {
  652. tag('</strong>')
  653. }
  654. function onexitthematicbreak() {
  655. lineEndingIfNeeded()
  656. tag('<hr />')
  657. }
  658. function onexitcharacterreferencemarker(token) {
  659. setData('characterReferenceType', token.type)
  660. }
  661. function onexitcharacterreferencevalue(token) {
  662. var value = this.sliceSerialize(token)
  663. value = getData('characterReferenceType')
  664. ? safeFromInt(
  665. value,
  666. getData('characterReferenceType') ===
  667. types.characterReferenceMarkerNumeric
  668. ? constants.numericBaseDecimal
  669. : constants.numericBaseHexadecimal
  670. )
  671. : decodeEntity__default['default'](value)
  672. raw(encode(value))
  673. setData('characterReferenceType')
  674. }
  675. function onexitautolinkprotocol(token) {
  676. var uri = this.sliceSerialize(token)
  677. tag('<a href="' + url(uri, protocolHref) + '">')
  678. raw(encode(uri))
  679. tag('</a>')
  680. }
  681. function onexitautolinkemail(token) {
  682. var uri = this.sliceSerialize(token)
  683. tag('<a href="' + url('mailto:' + uri, protocolHref) + '">')
  684. raw(encode(uri))
  685. tag('</a>')
  686. }
  687. }
  688. module.exports = compileHtml