import Renderer from './renderer' import InlineLexer from './inlineLexer' import Slugger from './slugger' import TextRenderer from './textRenderer' import defaultOptions from './options' /** * Parsing & Compiling */ function Parser (options) { this.tokens = [] this.token = null this.options = options || defaultOptions this.options.renderer = this.options.renderer || new Renderer() this.renderer = this.options.renderer this.renderer.options = this.options this.slugger = new Slugger() } /** * Parse Loop */ Parser.prototype.parse = function (src) { this.inline = new InlineLexer(src.links, this.options) // use an InlineLexer with a TextRenderer to extract pure text this.inlineText = new InlineLexer( src.links, Object.assign({}, this.options, {renderer: new TextRenderer()}) ) this.tokens = src.reverse() let out = '' while (this.next()) { out += this.tok() } return out } /** * Next Token */ Parser.prototype.next = function () { this.token = this.tokens.pop() return this.token } /** * Preview Next Token */ Parser.prototype.peek = function () { return this.tokens[this.tokens.length - 1] || 0 } /** * Parse Text Tokens */ Parser.prototype.parseText = function () { let body = this.token.text while (this.peek().type === 'text') { body += '\n' + this.next().text } return this.inline.output(body) } /** * Parse Current Token */ Parser.prototype.tok = function () { switch (this.token.type) { case 'frontmatter': { return this.renderer.frontmatter(this.token.text) } case 'space': { return '' } case 'hr': { return this.renderer.hr() } case 'heading': { return this.renderer.heading( this.inline.output(this.token.text), this.token.depth, unescape(this.inlineText.output(this.token.text)), this.slugger, this.token.headingStyle ) } case 'multiplemath': { const { text } = this.token return this.renderer.multiplemath(text) } case 'code': { const { codeBlockStyle, text, lang, escaped } = this.token return this.renderer.code(text, lang, escaped, codeBlockStyle) } case 'table': { let header = '' let body = '' let i let row let cell let j // header cell = '' for (i = 0; i < this.token.header.length; i++) { cell += this.renderer.tablecell( this.inline.output(this.token.header[i]), { header: true, align: this.token.align[i] } ) } header += this.renderer.tablerow(cell) for (i = 0; i < this.token.cells.length; i++) { row = this.token.cells[i] cell = '' for (j = 0; j < row.length; j++) { cell += this.renderer.tablecell( this.inline.output(row[j]), { header: false, align: this.token.align[j] } ) } body += this.renderer.tablerow(cell) } return this.renderer.table(header, body) } case 'blockquote_start': { let body = '' while (this.next().type !== 'blockquote_end') { body += this.tok() } return this.renderer.blockquote(body) } case 'list_start': { let body = '' let taskList = false const { ordered, start } = this.token while (this.next().type !== 'list_end') { if (this.token.checked !== undefined) { taskList = true } body += this.tok() } return this.renderer.list(body, ordered, start, taskList) } case 'list_item_start': { let body = '' const { checked } = this.token while (this.next().type !== 'list_item_end') { body += this.token.type === 'text' ? this.parseText() : this.tok() } return this.renderer.listitem(body, checked) } case 'loose_item_start': { let body = '' const { checked } = this.token while (this.next().type !== 'list_item_end') { body += this.tok() } return this.renderer.listitem(body, checked) } case 'html': { // TODO parse inline content if parameter markdown=1 return this.renderer.html(this.token.text) } case 'paragraph': { return this.renderer.paragraph(this.inline.output(this.token.text)) } case 'text': { return this.renderer.paragraph(this.parseText()) } default: { let errMsg = 'Token with "' + this.token.type + '" type was not found.' if (this.options.silent) { console.error(errMsg) } else { throw new Error(errMsg) } } } } export default Parser