mirror of
https://github.com/discourse/discourse.git
synced 2025-05-01 03:04:37 +08:00

This commit adds two custom markdown rules: - chat-html-block - chat-html-inline For now it only allows `<kbd>` for inline and `<details>` for block.
115 lines
3.1 KiB
JavaScript
115 lines
3.1 KiB
JavaScript
// inspired from https://github.com/markdown-it/markdown-it/blob/master/lib/rules_block/html_block.mjs
|
|
// note that allow lister will run on top of it, so if a tag is allowed here but not on
|
|
// the allow list, then it won't show up
|
|
|
|
const block_names = ["details"];
|
|
|
|
const attr_name = "[a-zA-Z_:][a-zA-Z0-9:._-]*";
|
|
const unquoted = "[^\"'=<>`\\x00-\\x20]+";
|
|
const single_quoted = "'[^']*'";
|
|
const double_quoted = '"[^"]*"';
|
|
const attr_value =
|
|
"(?:" + unquoted + "|" + single_quoted + "|" + double_quoted + ")";
|
|
const attribute = "(?:\\s+" + attr_name + "(?:\\s*=\\s*" + attr_value + ")?)";
|
|
const open_tag = "<[A-Za-z][A-Za-z0-9\\-]*" + attribute + "*\\s*\\/?>";
|
|
const close_tag = "<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>";
|
|
|
|
const HTML_OPEN_CLOSE_TAG_RE = new RegExp(
|
|
"^(?:" + open_tag + "|" + close_tag + ")"
|
|
);
|
|
|
|
// An array of opening and corresponding closing sequences for html tags,
|
|
// last argument defines whether it can terminate a paragraph or not
|
|
//
|
|
const HTML_SEQUENCES = [
|
|
[
|
|
/^<(script|pre|style|textarea)(?=(\s|>|$))/i,
|
|
/<\/(script|pre|style|textarea)>/i,
|
|
true,
|
|
],
|
|
[/^<!--/, /-->/, true],
|
|
[/^<\?/, /\?>/, true],
|
|
[/^<![A-Z]/, />/, true],
|
|
[/^<!\[CDATA\[/, /\]\]>/, true],
|
|
[
|
|
new RegExp("^</?(" + block_names.join("|") + ")(?=(\\s|/?>|$))", "i"),
|
|
/^$/,
|
|
true,
|
|
],
|
|
[new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + "\\s*$"), /^$/, false],
|
|
];
|
|
|
|
function chatHtmlBlock(state, startLine, endLine, silent) {
|
|
let pos = state.bMarks[startLine] + state.tShift[startLine];
|
|
let max = state.eMarks[startLine];
|
|
|
|
// if it's indented more than 3 spaces, it should be a code block
|
|
if (state.sCount[startLine] - state.blkIndent >= 4) {
|
|
return false;
|
|
}
|
|
|
|
if (!state.md.options.html) {
|
|
return false;
|
|
}
|
|
|
|
if (state.src.charCodeAt(pos) !== 0x3c /* < */) {
|
|
return false;
|
|
}
|
|
|
|
let lineText = state.src.slice(pos, max);
|
|
|
|
let i = 0;
|
|
for (; i < HTML_SEQUENCES.length; i++) {
|
|
if (HTML_SEQUENCES[i][0].test(lineText)) {
|
|
break;
|
|
}
|
|
}
|
|
if (i === HTML_SEQUENCES.length) {
|
|
return false;
|
|
}
|
|
|
|
if (silent) {
|
|
// true if this sequence can be a terminator, false otherwise
|
|
return HTML_SEQUENCES[i][2];
|
|
}
|
|
|
|
let nextLine = startLine + 1;
|
|
|
|
// If we are here - we detected HTML block.
|
|
// Let's roll down till block end.
|
|
if (!HTML_SEQUENCES[i][1].test(lineText)) {
|
|
for (; nextLine < endLine; nextLine++) {
|
|
if (state.sCount[nextLine] < state.blkIndent) {
|
|
break;
|
|
}
|
|
|
|
pos = state.bMarks[nextLine] + state.tShift[nextLine];
|
|
max = state.eMarks[nextLine];
|
|
lineText = state.src.slice(pos, max);
|
|
|
|
if (HTML_SEQUENCES[i][1].test(lineText)) {
|
|
if (lineText.length !== 0) {
|
|
nextLine++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
state.line = nextLine;
|
|
|
|
const token = state.push("html_block", "", 0);
|
|
token.map = [startLine, nextLine];
|
|
token.content = state.getLines(startLine, nextLine, state.blkIndent, true);
|
|
|
|
return true;
|
|
}
|
|
|
|
export function setup(helper) {
|
|
helper.registerPlugin((md) => {
|
|
if (md.options.discourse.features["chat-html-block"]) {
|
|
md.block.ruler.push("chat-html-block", chatHtmlBlock);
|
|
}
|
|
});
|
|
}
|