2017-07-14 20:27:28 +08:00
|
|
|
import { parseBBCodeTag } from "pretty-text/engines/discourse-markdown/bbcode-block";
|
2017-06-09 06:02:30 +08:00
|
|
|
|
2020-07-27 17:22:57 +08:00
|
|
|
function tokenizeBBCode(state, silent, ruler) {
|
2017-06-09 06:02:30 +08:00
|
|
|
let pos = state.pos;
|
|
|
|
|
|
|
|
// 91 = [
|
|
|
|
if (silent || state.src.charCodeAt(pos) !== 91) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tagInfo = parseBBCodeTag(state.src, pos, state.posMax);
|
|
|
|
|
|
|
|
if (!tagInfo) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-30 01:59:40 +08:00
|
|
|
let rule, i;
|
2017-06-23 23:36:45 +08:00
|
|
|
|
2017-07-01 03:19:07 +08:00
|
|
|
let ruleInfo = ruler.getRuleForTag(tagInfo.tag);
|
|
|
|
if (!ruleInfo) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
rule = ruleInfo.rule;
|
2017-06-23 23:36:45 +08:00
|
|
|
|
2017-06-30 01:59:40 +08:00
|
|
|
if (rule.replace) {
|
|
|
|
// special handling for replace
|
|
|
|
// we pass raw contents to callback so we simply need to greedy match to end tag
|
|
|
|
if (tagInfo.closing) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-06-09 06:02:30 +08:00
|
|
|
|
2017-06-30 01:59:40 +08:00
|
|
|
let closeTag = "[/" + tagInfo.tag + "]";
|
|
|
|
let found = false;
|
2017-06-09 06:02:30 +08:00
|
|
|
|
2017-06-30 01:59:40 +08:00
|
|
|
for (
|
|
|
|
i = state.pos + tagInfo.length;
|
|
|
|
i <= state.posMax - closeTag.length;
|
|
|
|
i++
|
|
|
|
) {
|
|
|
|
if (
|
|
|
|
state.src.charCodeAt(pos) === 91 &&
|
|
|
|
state.src.slice(i, i + closeTag.length).toLowerCase() === closeTag
|
2018-06-15 23:03:24 +08:00
|
|
|
) {
|
2017-06-30 01:59:40 +08:00
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let content = state.src.slice(state.pos + tagInfo.length, i);
|
2017-06-09 06:02:30 +08:00
|
|
|
|
2017-06-30 01:59:40 +08:00
|
|
|
if (rule.replace(state, tagInfo, content)) {
|
|
|
|
state.pos = i + closeTag.length;
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tagInfo.rule = rule;
|
|
|
|
|
|
|
|
let token = state.push("text", "", 0);
|
|
|
|
token.content = state.src.slice(pos, pos + tagInfo.length);
|
2017-07-14 20:27:28 +08:00
|
|
|
token.meta = "bbcode";
|
2017-06-30 01:59:40 +08:00
|
|
|
|
|
|
|
state.delimiters.push({
|
|
|
|
bbInfo: tagInfo,
|
|
|
|
marker: "bb" + tagInfo.tag,
|
|
|
|
open: !tagInfo.closing,
|
|
|
|
close: !!tagInfo.closing,
|
|
|
|
token: state.tokens.length - 1,
|
|
|
|
level: state.level,
|
|
|
|
end: -1,
|
|
|
|
jump: 0,
|
|
|
|
});
|
|
|
|
|
|
|
|
state.pos = pos + tagInfo.length;
|
|
|
|
return true;
|
|
|
|
}
|
2017-06-09 06:02:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function processBBCode(state, silent) {
|
|
|
|
let i,
|
|
|
|
startDelim,
|
|
|
|
endDelim,
|
|
|
|
token,
|
|
|
|
tagInfo,
|
|
|
|
delimiters = state.delimiters,
|
|
|
|
max = delimiters.length;
|
|
|
|
|
|
|
|
if (silent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < max - 1; i++) {
|
|
|
|
startDelim = delimiters[i];
|
|
|
|
tagInfo = startDelim.bbInfo;
|
|
|
|
|
|
|
|
if (!tagInfo) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (startDelim.end === -1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
endDelim = delimiters[startDelim.end];
|
|
|
|
|
|
|
|
token = state.tokens[startDelim.token];
|
2017-06-30 04:43:57 +08:00
|
|
|
let tag, className;
|
|
|
|
|
|
|
|
if (typeof tagInfo.rule.wrap === "function") {
|
2017-07-14 20:27:28 +08:00
|
|
|
let content = "";
|
|
|
|
for (let j = startDelim.token + 1; j < endDelim.token; j++) {
|
|
|
|
let inner = state.tokens[j];
|
|
|
|
if (inner.type === "text" && inner.meta !== "bbcode") {
|
|
|
|
content += inner.content;
|
|
|
|
}
|
2017-06-30 04:43:57 +08:00
|
|
|
}
|
2017-07-14 20:27:28 +08:00
|
|
|
tagInfo.rule.wrap(token, state.tokens[endDelim.token], tagInfo, content);
|
|
|
|
continue;
|
2017-06-30 04:43:57 +08:00
|
|
|
} else {
|
|
|
|
let split = tagInfo.rule.wrap.split(".");
|
|
|
|
tag = split[0];
|
|
|
|
className = split.slice(1).join(" ");
|
|
|
|
}
|
2017-06-23 23:36:45 +08:00
|
|
|
|
2017-06-09 06:02:30 +08:00
|
|
|
token.type = "bbcode_" + tagInfo.tag + "_open";
|
2017-06-23 23:36:45 +08:00
|
|
|
token.tag = tag;
|
|
|
|
if (className) {
|
|
|
|
token.attrs = [["class", className]];
|
|
|
|
}
|
2017-06-09 06:02:30 +08:00
|
|
|
token.nesting = 1;
|
|
|
|
token.markup = token.content;
|
|
|
|
token.content = "";
|
|
|
|
|
|
|
|
token = state.tokens[endDelim.token];
|
|
|
|
token.type = "bbcode_" + tagInfo.tag + "_close";
|
2017-06-23 23:36:45 +08:00
|
|
|
token.tag = tag;
|
2017-06-09 06:02:30 +08:00
|
|
|
token.nesting = -1;
|
|
|
|
token.markup = token.content;
|
|
|
|
token.content = "";
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setup(helper) {
|
2020-10-28 10:22:06 +08:00
|
|
|
helper.allowList([
|
2017-06-09 06:02:30 +08:00
|
|
|
"span.bbcode-b",
|
|
|
|
"span.bbcode-i",
|
|
|
|
"span.bbcode-u",
|
|
|
|
"span.bbcode-s",
|
|
|
|
]);
|
|
|
|
|
|
|
|
helper.registerOptions((opts) => {
|
|
|
|
opts.features["bbcode-inline"] = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
helper.registerPlugin((md) => {
|
2017-07-18 04:21:47 +08:00
|
|
|
const ruler = md.inline.bbcode.ruler;
|
2017-06-23 23:36:45 +08:00
|
|
|
|
|
|
|
md.inline.ruler.push("bbcode-inline", (state, silent) =>
|
2020-07-27 17:22:57 +08:00
|
|
|
tokenizeBBCode(state, silent, ruler)
|
2017-06-23 23:36:45 +08:00
|
|
|
);
|
2017-06-09 06:02:30 +08:00
|
|
|
md.inline.ruler2.before("text_collapse", "bbcode-inline", processBBCode);
|
2017-06-23 23:36:45 +08:00
|
|
|
|
2017-06-30 04:04:10 +08:00
|
|
|
ruler.push("code", {
|
|
|
|
tag: "code",
|
|
|
|
replace: function (state, tagInfo, content) {
|
|
|
|
let token;
|
|
|
|
token = state.push("code_inline", "code", 0);
|
|
|
|
token.content = content;
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-07-14 20:27:28 +08:00
|
|
|
const simpleUrlRegex = /^http[s]?:\/\//;
|
2017-06-30 01:59:40 +08:00
|
|
|
ruler.push("url", {
|
|
|
|
tag: "url",
|
2017-07-14 20:27:28 +08:00
|
|
|
wrap: function (startToken, endToken, tagInfo, content) {
|
|
|
|
const url = (tagInfo.attrs["_default"] || content).trim();
|
|
|
|
|
|
|
|
if (simpleUrlRegex.test(url)) {
|
|
|
|
startToken.type = "link_open";
|
|
|
|
startToken.tag = "a";
|
2019-11-15 23:34:26 +08:00
|
|
|
startToken.attrs = [
|
|
|
|
["href", url],
|
|
|
|
["data-bbcode", "true"],
|
|
|
|
];
|
2017-07-14 20:27:28 +08:00
|
|
|
startToken.content = "";
|
|
|
|
startToken.nesting = 1;
|
|
|
|
|
|
|
|
endToken.type = "link_close";
|
|
|
|
endToken.tag = "a";
|
|
|
|
endToken.content = "";
|
|
|
|
endToken.nesting = -1;
|
|
|
|
} else {
|
|
|
|
// just strip the bbcode tag
|
|
|
|
endToken.content = "";
|
|
|
|
startToken.content = "";
|
|
|
|
|
|
|
|
// edge case, we don't want this detected as a onebox if auto linked
|
|
|
|
// this ensures it is not stripped
|
|
|
|
startToken.type = "html_inline";
|
|
|
|
}
|
2017-07-13 06:10:07 +08:00
|
|
|
|
2017-07-14 20:27:28 +08:00
|
|
|
return false;
|
2017-06-30 01:59:40 +08:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
ruler.push("email", {
|
|
|
|
tag: "email",
|
|
|
|
replace: function (state, tagInfo, content) {
|
|
|
|
let token;
|
2017-07-14 20:27:28 +08:00
|
|
|
let email = tagInfo.attrs["_default"] || content;
|
2017-06-30 01:59:40 +08:00
|
|
|
|
|
|
|
token = state.push("link_open", "a", 1);
|
2019-11-15 23:34:26 +08:00
|
|
|
token.attrs = [
|
|
|
|
["href", "mailto:" + email],
|
|
|
|
["data-bbcode", "true"],
|
|
|
|
];
|
2017-06-30 01:59:40 +08:00
|
|
|
|
|
|
|
token = state.push("text", "", 0);
|
|
|
|
token.content = content;
|
|
|
|
|
2020-05-08 15:19:48 +08:00
|
|
|
state.push("link_close", "a", -1);
|
2017-06-30 01:59:40 +08:00
|
|
|
return true;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
ruler.push("image", {
|
|
|
|
tag: "img",
|
|
|
|
replace: function (state, tagInfo, content) {
|
|
|
|
let token = state.push("image", "img", 0);
|
2019-11-15 23:34:26 +08:00
|
|
|
token.attrs = [
|
|
|
|
["src", content],
|
|
|
|
["alt", ""],
|
|
|
|
];
|
2017-06-30 01:59:40 +08:00
|
|
|
token.children = [];
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-06-23 23:36:45 +08:00
|
|
|
ruler.push("bold", {
|
|
|
|
tag: "b",
|
|
|
|
wrap: "span.bbcode-b",
|
|
|
|
});
|
|
|
|
|
|
|
|
ruler.push("italic", {
|
|
|
|
tag: "i",
|
|
|
|
wrap: "span.bbcode-i",
|
|
|
|
});
|
|
|
|
|
|
|
|
ruler.push("underline", {
|
|
|
|
tag: "u",
|
|
|
|
wrap: "span.bbcode-u",
|
|
|
|
});
|
|
|
|
|
|
|
|
ruler.push("strike", {
|
|
|
|
tag: "s",
|
|
|
|
wrap: "span.bbcode-s",
|
|
|
|
});
|
2017-06-09 06:02:30 +08:00
|
|
|
});
|
|
|
|
}
|