CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual use to large groups and classes! Also, H100 GPUs starting at $2/hour.
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual use to large groups and classes! Also, H100 GPUs starting at $2/hour.
Path: blob/master/src/packages/frontend/markdown/checkbox-plugin.ts
Views: 505
/*1* LICENSE: MIT (same as upstream)2*/34// This code is inspired by https://github.com/mcecot/markdown-it-checkbox56// However it is meant to behave much like Github, in terms of parsing.78function checkboxReplace(_md, _options) {9let index = 0;10const pattern = /\[(X|\s)\](.*)/i;11function createTokens(12checked: boolean,13before: string,14after: string,15Token16) {17// before <input type="checkbox" data-index="{n}" checked="true"> after18const checkbox_token = new Token("checkbox_input", "input", 0);19checkbox_token.attrs = [20[21"style",22"margin: 0 0.2em 0.2em 0.2em; transform: scale(1.5); vertical-align: middle;",23],24["type", "checkbox"],25["data-index", `${index}`],26["disabled", "true"] /* disabled: anything in cocalc that is just directly27rendering this doesn't know how to change it.*/,28];29if (checked) {30checkbox_token.attrs.push(["checked", "true"]);31checkbox_token.checked = checked;32}3334const before_token = new Token("text", "", 0);35before_token.content = before;3637const after_token = new Token("text", "", 0);38after_token.content = after;39index += 1;40return [before_token, checkbox_token, after_token];41}4243function splitTextToken(original, Token) {44const markup = original.markup?.[0];45if (markup == "$" || markup == "`") {46// don't make checkboxes, e.g., inside of `code` like `R[x]` or math like $\QQ[x]$.47// NOTE: I did have this for *any* markup at all, but that breaks this:48// - foo \(stuff\)49// since \) is considered "markup" for some reason. So this fix may result in some50// subtle bug somewhere else, which will get fixed by adding another case to the if above.51// See https://github.com/sagemathinc/cocalc/issues/646452return null;53}54const text = original.content;55const match = text.match(pattern);56if (match === null) {57return null;58}59const before = text.slice(0, match.index);60const value = match[1];61const checked = value === "X" || value === "x";62const after = match[2];63return createTokens(checked, before, after, Token);64}6566return (state) => {67for (const token of state.tokens) {68if (token.type !== "inline") {69// fenced blocks, etc., should be ignored of course.70continue;71}72// Process all the children, setting has_checkboxes73// to true if any are found.74let has_checkboxes: boolean = false;75const v: any[] = [];76for (const child of token.children) {77const x = splitTextToken(child, state.Token);78if (x != null) {79has_checkboxes = true;80v.push(x);81} else {82v.push([child]);83}84}8586if (has_checkboxes) {87// Found at least one checkbox, so replace children. See88// https://stackoverflow.com/questions/5080028/what-is-the-most-efficient-way-to-concatenate-n-arrays89// for why we concat arrays this way.90token.children = [].concat.apply([], v);91}92}93};94}9596export function checkboxPlugin(md, options) {97md.core.ruler.push("checkbox", checkboxReplace(md, options));98}99100101