mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
When clicking a "checklist", we're parsing the raw to change the state of the checklist that was clicked. If there was an image URL with an empty alternative text in the raw "" it would consider the "[]" as an empty checklist item. Internal - t/124499
105 lines
2.5 KiB
JavaScript
105 lines
2.5 KiB
JavaScript
const REGEX = /\[( |x)?\]/gi;
|
|
|
|
function getClasses(str) {
|
|
switch (str) {
|
|
case "x":
|
|
return "checked fa fa-check-square-o fa-fw";
|
|
case "X":
|
|
return "checked permanent fa fa-check-square fa-fw";
|
|
default:
|
|
return "fa fa-square-o fa-fw";
|
|
}
|
|
}
|
|
|
|
function addCheckbox(result, content, match, state) {
|
|
const classes = getClasses(match[1]);
|
|
|
|
const checkOpenToken = new state.Token("check_open", "span", 1);
|
|
checkOpenToken.attrs = [["class", `chcklst-box ${classes}`]];
|
|
result.push(checkOpenToken);
|
|
|
|
const checkCloseToken = new state.Token("check_close", "span", -1);
|
|
result.push(checkCloseToken);
|
|
}
|
|
|
|
function applyCheckboxes(content, state) {
|
|
let match;
|
|
let result = null;
|
|
let pos = 0;
|
|
|
|
while ((match = REGEX.exec(content))) {
|
|
if (match.index > pos) {
|
|
result = result || [];
|
|
const token = new state.Token("text", "", 0);
|
|
token.content = content.slice(pos, match.index);
|
|
result.push(token);
|
|
}
|
|
|
|
pos = match.index + match[0].length;
|
|
|
|
result = result || [];
|
|
addCheckbox(result, content, match, state);
|
|
}
|
|
|
|
if (result && pos < content.length) {
|
|
const token = new state.Token("text", "", 0);
|
|
token.content = content.slice(pos);
|
|
result.push(token);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function processChecklist(state) {
|
|
let i,
|
|
j,
|
|
l,
|
|
tokens,
|
|
token,
|
|
blockTokens = state.tokens,
|
|
nesting = 0;
|
|
|
|
for (j = 0, l = blockTokens.length; j < l; j++) {
|
|
if (blockTokens[j].type !== "inline") {
|
|
continue;
|
|
}
|
|
tokens = blockTokens[j].children;
|
|
|
|
// We scan from the end, to keep position when new tags are added.
|
|
// Use reversed logic in links start/end match
|
|
for (i = tokens.length - 1; i >= 0; i--) {
|
|
token = tokens[i];
|
|
|
|
nesting += token.nesting;
|
|
|
|
if (token.type === "text" && nesting === 0) {
|
|
const processed = applyCheckboxes(token.content, state);
|
|
if (processed) {
|
|
blockTokens[j].children = tokens = state.md.utils.arrayReplaceAt(
|
|
tokens,
|
|
i,
|
|
processed
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function setup(helper) {
|
|
helper.registerOptions((opts, siteSettings) => {
|
|
opts.features["checklist"] = !!siteSettings.checklist_enabled;
|
|
});
|
|
|
|
helper.allowList([
|
|
"span.chcklst-stroked",
|
|
"span.chcklst-box fa fa-square-o fa-fw",
|
|
"span.chcklst-box checked fa fa-check-square-o fa-fw",
|
|
"span.chcklst-box checked permanent fa fa-check-square fa-fw",
|
|
]);
|
|
|
|
helper.registerPlugin((md) =>
|
|
md.core.ruler.push("checklist", processChecklist)
|
|
);
|
|
}
|