Square CoCalc Logo
FeaturesSoftwarePricingInfoPoliciesShareSupport Try Sign InSign Up
(IconFonts not available)
sagemathinc
GitHub Repository: sagemathinc/cocalc
(IconFonts not available)
Path:
blob/master/src/packages/frontend/markdown/checkbox-plugin.ts
Views: 178
1
/*
2
* LICENSE: MIT (same as upstream)
3
*/
4
5
// This code is inspired by https://github.com/mcecot/markdown-it-checkbox
6
7
// However it is meant to behave much like Github, in terms of parsing.
8
9
function checkboxReplace(_md, _options) {
10
let index = 0;
11
const pattern = /\[(X|\s)\](.*)/i;
12
function createTokens(
13
checked: boolean,
14
before: string,
15
after: string,
16
Token
17
) {
18
// before <input type="checkbox" data-index="{n}" checked="true"> after
19
const checkbox_token = new Token("checkbox_input", "input", 0);
20
checkbox_token.attrs = [
21
[
22
"style",
23
"margin: 0 0.2em 0.2em 0.2em; transform: scale(1.5); vertical-align: middle;",
24
],
25
["type", "checkbox"],
26
["data-index", `${index}`],
27
[
28
"disabled",
29
"true",
30
] /* disabled: anything in cocalc that is just directly
31
rendering this doesn't know how to change it.*/,
32
];
33
if (checked) {
34
checkbox_token.attrs.push(["checked", "true"]);
35
checkbox_token.checked = checked;
36
}
37
38
const before_token = new Token("text", "", 0);
39
before_token.content = before;
40
41
const after_token = new Token("text", "", 0);
42
after_token.content = after;
43
index += 1;
44
return [before_token, checkbox_token, after_token];
45
}
46
47
function splitTextToken(original, Token) {
48
if (original.markup != "") return null; // don't make checkboxes, e.g., inside of `code` like `R[x]`.
49
const text = original.content;
50
const match = text.match(pattern);
51
if (match === null) {
52
return null;
53
}
54
const before = text.slice(0, match.index);
55
const value = match[1];
56
const checked = value === "X" || value === "x";
57
const after = match[2];
58
return createTokens(checked, before, after, Token);
59
}
60
61
return (state) => {
62
for (const token of state.tokens) {
63
if (token.type !== "inline") {
64
// fenced blocks, etc., should be ignored of course.
65
continue;
66
}
67
// Process all the children, setting has_checkboxes
68
// to true if any are found.
69
let has_checkboxes: boolean = false;
70
const v: any[] = [];
71
for (const child of token.children) {
72
const x = splitTextToken(child, state.Token);
73
if (x != null) {
74
has_checkboxes = true;
75
v.push(x);
76
} else {
77
v.push([child]);
78
}
79
}
80
81
if (has_checkboxes) {
82
// Found at least one checkbox, so replace children. See
83
// https://stackoverflow.com/questions/5080028/what-is-the-most-efficient-way-to-concatenate-n-arrays
84
// for why we concat arrays this way.
85
token.children = [].concat.apply([], v);
86
}
87
}
88
};
89
}
90
91
export function checkboxPlugin(md, options) {
92
md.core.ruler.push("checkbox", checkboxReplace(md, options));
93
}
94
95