replace the main futoji message formatter with markdown-it plugins

This commit is contained in:
brecert 2019-07-26 00:18:37 -04:00
parent 8904a9254b
commit 5db33c4e7c
5 changed files with 178 additions and 112 deletions

View file

@ -13,6 +13,8 @@
"futoji": "^0.5.0",
"highlight.js": "^9.15.8",
"jquery": "^3.4.0",
"markdown-it": "^9.0.1",
"markdown-it-chat-formatter": "^0.1.1",
"match-sorter": "^2.3.0",
"particles.js": "^2.0.0",
"socket.io": "^2.2.0",

View file

@ -452,11 +452,18 @@ export default {
.msg-link {
color: rgb(86, 159, 253);
}
pre {
padding: 0;
margin: 0;
}
.codeblock {
background-color: rgba(0, 0, 0, 0.397);
padding: 5px;
border-radius: 5px;
}
img.emoji {
height: 1.7em;
width: auto;

View file

@ -0,0 +1,92 @@
import config from "@/config.js";
/* 58: ':' */
/* 60: '<' */
/* 62: '>' */
function render_custom_emoji(tokens, idx) {
return ':3'
}
function parseUntil(state, fromPos, until) {
let max = state.posMax
let found = false
let oldPos = state.pos
// let start = nameStart + 1
state.pos = fromPos
let end = -1
while(state.pos++ < max) {
let marker = state.src.charCodeAt(state.pos)
if(marker === until) {
found = true;
break;
}
}
if(found) {
end = state.pos
}
state.pos = oldPos
return end
}
function parseEmojiName(state, nameStart) {
return parseUntil(state, nameStart, 58)
}
function replace_custom_emoji(state, silent) {
let pos = state.pos
let max = state.posMax
// if begins with <
if (state.src.charCodeAt(pos) !== 60) { return false; }
pos += 1
// if the next character is not ':' then it's not a custom emoji
if(state.src.charCodeAt(pos) !== 58) { return false; }
// parse the emoji name
let nameStart = pos + 1
let nameEnd = parseEmojiName(state, nameStart)
// parser failed to find another ':', so it's not a valid emoji
if(nameEnd < 0 || nameEnd - nameStart <= 1) { return false; }
let emojiName = state.src.slice(nameStart, nameEnd)
pos = nameEnd + 1
// parse until '>'
let idStart = pos
let idEnd = parseUntil(state, idStart, 62)
if(idEnd < 0 || idEnd - idStart <= 1) { return false; }
let emojiID = state.src.slice(idStart, idEnd)
if(!silent) {
state.pos = idStart
state.posMax = idEnd
let token = state.push('custom_emoji_open', 'img', 1);
token.attrs = [[ 'src', `${config.domain}/files/${emojiID}` ]]
// state.md.inline.tokenize(state)
}
state.pos = idEnd + 1
state.posMax = max
return true
}
export default function custom_emoji_plugin(md, opts) {
md.renderer.rules.custom_emoji_open = (tokens, idx) => {
let token = tokens[idx]
return `<${md.utils.escapeHtml(token.tag)} class="emoji" src=${md.utils.escapeHtml(token.attrs.find(([name]) => name === 'src')[1])} />`
}
md.inline.ruler.push('custom_emoji', replace_custom_emoji)
}

View file

@ -3,7 +3,30 @@ import twemoji from 'twemoji'
import emojiParser from '@/utils/emojiParser';
import config from "@/config.js";
const futoji = new Formatter();
import customEmoji from './markdown-it-plugins/replaceCustomEmoji'
import hljs from 'highlight.js'
import MarkdownIt from 'markdown-it'
import chatPlugin from 'markdown-it-chat-formatter/dist-src/plugin'
const markdown = new MarkdownIt({
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return '<div class="codeblock"><code>' +
hljs.highlight(lang, str, true).value +
'</code></div>';
} catch (err) {
console.error(err)
}
}
return '<div class="codeblock"><code>' + markdown.utils.escapeHtml(str) + '</code></div>';
}
}).use(chatPlugin)
.use(customEmoji);
const emojiFormatter = new Formatter();
emojiFormatter.addTransformer({
@ -23,116 +46,11 @@ function owo (text) {
}
futoji.addTransformer({
name: 'custom emoji',
symbol: ':',
padding: false,
recursive: false,
validate: text => /.+?&(.+?)/.test(text),
transformer: text => {
const formattedInner = emojiFormatter.format(text);
return owo(formattedInner);
}
})
futoji.addTransformer({
name: 'url',
open: 'http',
close: ' ',
recursive: false,
validate: text => /(https?:\/\/[^\s]+)/g.test('http' + text),
transformer: text => '<a class="msg-link" target="_blank" href="http' + text + '">http' + text + '</a> '
})
futoji.addTransformer({
name: 'bold-and-italic',
symbol: '***',
transformer: text => `<strong><em>${text}</em></strong>`
})
futoji.addTransformer({
name: 'bold',
symbol: '**',
transformer: text => `<strong>${text}</strong>`
})
futoji.addTransformer({
name: 'italic',
symbol: '*',
transformer: text => `<em>${text}</em>`
})
futoji.addTransformer({
name: 'underline',
symbol: '__',
transformer: text => `<u>${text}</u>`
})
futoji.addTransformer({
name: 'italic',
symbol: '_',
transformer: text => `<em>${text}</em>`
})
futoji.addTransformer({
name: 'srike',
symbol: '~~',
transformer: text => `<s>${text.trim()}</s>`
})
futoji.addTransformer({
name: 'code-block',
symbol: '```',
recursive: false,
transformer: text => `<div class="codeblock"><code>${formatCode(text).trim()}</code></div>`,
})
futoji.addTransformer({
name: 'code',
symbol: '`',
recursive: false,
transformer: text => `<code>${text}</code>`,
})
export default (message) => {
message = futoji.format(escapeHtml(message + ' ')).trim();
message = markdown.render(message).trim();
message = emojiParser.replaceEmojis(message);
return message;
}
/**
* format code to add syntax highlighting
*/
function formatCode(text) {
// matches if word until newline
// if spaces then it won't match
let nameRegex = new RegExp('^(\\w+)\\s')
if (nameRegex.test(text)) {
let language = nameRegex.exec(text)[1]
let newText = text.replace(nameRegex, '')
// TODO: format newText with language
return newText
}
return text
}
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

View file

@ -1552,10 +1552,10 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
axios@^0.18.0:
version "0.18.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3"
integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==
axios@^0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8"
integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==
dependencies:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
@ -3171,7 +3171,7 @@ enhanced-resolve@^4.1.0:
memory-fs "^0.4.0"
tapable "^1.0.0"
entities@^1.1.1:
entities@^1.1.1, entities@~1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
@ -4990,6 +4990,13 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
linkify-it@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==
dependencies:
uc.micro "^1.0.1"
load-json-file@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@ -5180,6 +5187,36 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
markdown-it-chat-formatter@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/markdown-it-chat-formatter/-/markdown-it-chat-formatter-0.1.1.tgz#76a24a6599925ae22cdbf179301c6152867b1848"
integrity sha512-RIGKubGe3wjvizsTqeeU+RIVS7b1bqAwZE0Xv7H15uxZxf3pdLQjpX+ATcInPkf1L2SY4Vl2do8nSliLBCutag==
dependencies:
markdown-it "^9.0.1"
markdown-it-underline "^1.0.1"
marked "^0.7.0"
markdown-it-underline@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/markdown-it-underline/-/markdown-it-underline-1.0.1.tgz#43a54a541d95d739b43157701f1da306d7c300b2"
integrity sha1-Q6VKVB2V1zm0MVdwHx2jBtfDALI=
markdown-it@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-9.0.1.tgz#aafe363c43718720b6575fd10625cde6e4ff2d47"
integrity sha512-XC9dMBHg28Xi7y5dPuLjM61upIGPJG8AiHNHYqIaXER2KNnn7eKnM5/sF0ImNnyoV224Ogn9b1Pck8VH4k0bxw==
dependencies:
argparse "^1.0.7"
entities "~1.1.1"
linkify-it "^2.0.0"
mdurl "^1.0.1"
uc.micro "^1.0.5"
marked@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e"
integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==
match-sorter@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-2.3.0.tgz#99eaf386689f75bf976f6bbf7f49afb9a7ffecc8"
@ -5201,6 +5238,11 @@ mdn-data@~1.1.0:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01"
integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==
mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@ -8151,6 +8193,11 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
uglify-js@3.4.x:
version "3.4.10"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"