gallery.accords-library.com/client/js/util/markdown.js

207 lines
5.4 KiB
JavaScript
Raw Normal View History

'use strict';
const marked = require('marked');
const config = require('../config.js');
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(
/(?:<p>)?%%%SJIS(\d+)(?:<\/p>)?/,
(match, capture) => {
return '<div class="sjis">' + this.buf[capture] + '</div>';
});
}
}
// 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
let baseUrl = config.baseUrl.replace(/\/+$/, '');
text = text.replace(
new RegExp('\\b' + baseUrl + '/post/(\\d+)/?\\b', 'g'), '@$1');
text = text.replace(
new RegExp('\\b' + baseUrl + '/tag/([a-zA-Z0-9_-]+?)/?', 'g'),
'#$1');
text = text.replace(
new RegExp('\\b' + baseUrl + '/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,
'<a href="/posts/query=$1"><code>$1</code></a>');
}
}
class SpoilersWrapper extends BaseMarkdownWrapper {
postprocess(text) {
return text.replace(
/\[spoiler\]((?:[^\[]|\[(?!\/?spoiler\]))+)\[\/spoiler\]/ig,
'<span class="spoiler">$1</span>');
}
}
class SmallWrapper extends BaseMarkdownWrapper {
postprocess(text) {
return text.replace(
/\[small\]((?:[^\[]|\[(?!\/?small\]))+)\[\/small\]/ig,
'<small>$1</small>');
}
}
class StrikeThroughWrapper extends BaseMarkdownWrapper {
postprocess(text) {
text = text.replace(/(^|[^\\])(~~|~)([^~]+)\2/g, '$1<del>$3</del>');
return text.replace(/\\~/g, '~');
}
}
function createRenderer() {
function sanitize(str) {
2017-01-08 01:22:39 +00:00
return str.replace(/&<"/g, m => {
if (m === '&') {
return '&amp;';
}
if (m === '<') {
return '&lt;';
}
return '&quot;';
});
}
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 = '<img src="' + sanitize(url) + '" alt="' + sanitize(alt);
if (width) {
res += '" width="' + width;
}
if (height) {
res += '" height="' + height;
}
return res + '">';
2017-01-08 01:22:39 +00:00
};
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,
};