'use strict'; const marked = require('marked'); class BaseMarkdownWrapper { preprocess(text) { return text; } postprocess(text) { return text; } } class SjisWrapper extends BaseMarkdownWrapper { constructor() { super(); this.buf = []; } preprocess(text) { return text.replace( /\[sjis\]((?:[^\[]|\[(?!\/?sjis\]))+)\[\/sjis\]/ig, (match, capture) => { var ret = '%%%SJIS' + this.buf.length; this.buf.push(capture); return ret; }); } postprocess(text) { return text.replace( /(?:

)?%%%SJIS(\d+)(?:<\/p>)?/, (match, capture) => { return '

' + this.buf[capture] + '
'; }); } } // fix \ before ~ being stripped away class TildeWrapper extends BaseMarkdownWrapper { preprocess(text) { return text.replace(/\\~/g, '%%%T'); } postprocess(text) { return text.replace(/%%%T/g, '\\~'); } } //prevent ^#... from being treated as headers, due to tag permalinks class TagPermalinkFixWrapper extends BaseMarkdownWrapper { preprocess(text) { return text.replace(/^#/g, '%%%#'); } postprocess(text) { return text.replace(/%%%#/g, '#'); } } //post, user and tags permalinks class EntityPermalinkWrapper extends BaseMarkdownWrapper { preprocess(text) { // URL-based permalinks text = text.replace( new RegExp('\\b/post/(\\d+)/?\\b', 'g'), '@$1'); text = text.replace( new RegExp('\\b/tag/([a-zA-Z0-9_-]+?)/?', 'g'), '#$1'); text = text.replace( new RegExp('\\b/user/([a-zA-Z0-9_-]+?)/?', 'g'), '+$1'); text = text.replace( /(^|^\(|(?:[^\]])\(|[\s<>\[\]\)])([+#@][a-zA-Z0-9_-]+)/g, '$1[$2]($2)'); text = text.replace(/\]\(@(\d+)\)/g, '](/post/$1)'); text = text.replace(/\]\(\+([a-zA-Z0-9_-]+)\)/g, '](/user/$1)'); text = text.replace(/\]\(#([a-zA-Z0-9_-]+)\)/g, '](/posts/query=$1)'); return text; } } class SearchPermalinkWrapper extends BaseMarkdownWrapper { postprocess(text) { return text.replace( /\[search\]((?:[^\[]|\[(?!\/?search\]))+)\[\/search\]/ig, '$1'); } } class SpoilersWrapper extends BaseMarkdownWrapper { postprocess(text) { return text.replace( /\[spoiler\]((?:[^\[]|\[(?!\/?spoiler\]))+)\[\/spoiler\]/ig, '$1'); } } class SmallWrapper extends BaseMarkdownWrapper { postprocess(text) { return text.replace( /\[small\]((?:[^\[]|\[(?!\/?small\]))+)\[\/small\]/ig, '$1'); } } class StrikeThroughWrapper extends BaseMarkdownWrapper { postprocess(text) { text = text.replace(/(^|[^\\])(~~|~)([^~]+)\2/g, '$1$3'); return text.replace(/\\~/g, '~'); } } function createRenderer() { function sanitize(str) { return str.replace(/&<"/g, m => { if (m === '&') { return '&'; } if (m === '<') { return '<'; } return '"'; }); } const renderer = new marked.Renderer(); renderer.image = (href, title, alt) => { let [_, url, width, height] = /^(.+?)(?:\s=\s*(\d*)\s*x\s*(\d*)\s*)?$/.exec(href); let res = '' + sanitize(alt);
        if (width) {
            res += ''; }; return renderer; } function formatMarkdown(text) { const renderer = createRenderer(); const options = { renderer: renderer, breaks: true, sanitize: true, smartypants: true, }; let wrappers = [ new SjisWrapper(), new TildeWrapper(), new TagPermalinkFixWrapper(), new EntityPermalinkWrapper(), new SearchPermalinkWrapper(), new SpoilersWrapper(), new SmallWrapper(), new StrikeThroughWrapper(), ]; for (let wrapper of wrappers) { text = wrapper.preprocess(text); } text = marked(text, options); wrappers.reverse(); for (let wrapper of wrappers) { text = wrapper.postprocess(text); } return text; } function formatInlineMarkdown(text) { const renderer = createRenderer(); const options = { renderer: renderer, breaks: true, sanitize: true, smartypants: true, }; let wrappers = [ new TildeWrapper(), new EntityPermalinkWrapper(), new SearchPermalinkWrapper(), new SpoilersWrapper(), new SmallWrapper(), new StrikeThroughWrapper(), ]; for (let wrapper of wrappers) { text = wrapper.preprocess(text); } text = marked.inlineLexer(text, [], options); wrappers.reverse(); for (let wrapper of wrappers) { text = wrapper.postprocess(text); } return text; } module.exports = { formatMarkdown: formatMarkdown, formatInlineMarkdown: formatInlineMarkdown, };