summaryrefslogtreecommitdiffstats
path: root/docs/pt/katex/contrib/render-a11y-string.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'docs/pt/katex/contrib/render-a11y-string.mjs')
-rw-r--r--docs/pt/katex/contrib/render-a11y-string.mjs753
1 files changed, 753 insertions, 0 deletions
diff --git a/docs/pt/katex/contrib/render-a11y-string.mjs b/docs/pt/katex/contrib/render-a11y-string.mjs
new file mode 100644
index 0000000..4eabb7b
--- /dev/null
+++ b/docs/pt/katex/contrib/render-a11y-string.mjs
@@ -0,0 +1,753 @@
+import katex from '../katex.mjs';
+
+/**
+ * renderA11yString returns a readable string.
+ *
+ * In some cases the string will have the proper semantic math
+ * meaning,:
+ * renderA11yString("\\frac{1}{2}"")
+ * -> "start fraction, 1, divided by, 2, end fraction"
+ *
+ * However, other cases do not:
+ * renderA11yString("f(x) = x^2")
+ * -> "f, left parenthesis, x, right parenthesis, equals, x, squared"
+ *
+ * The commas in the string aim to increase ease of understanding
+ * when read by a screenreader.
+ */
+const stringMap = {
+ "(": "left parenthesis",
+ ")": "right parenthesis",
+ "[": "open bracket",
+ "]": "close bracket",
+ "\\{": "left brace",
+ "\\}": "right brace",
+ "\\lvert": "open vertical bar",
+ "\\rvert": "close vertical bar",
+ "|": "vertical bar",
+ "\\uparrow": "up arrow",
+ "\\Uparrow": "up arrow",
+ "\\downarrow": "down arrow",
+ "\\Downarrow": "down arrow",
+ "\\updownarrow": "up down arrow",
+ "\\leftarrow": "left arrow",
+ "\\Leftarrow": "left arrow",
+ "\\rightarrow": "right arrow",
+ "\\Rightarrow": "right arrow",
+ "\\langle": "open angle",
+ "\\rangle": "close angle",
+ "\\lfloor": "open floor",
+ "\\rfloor": "close floor",
+ "\\int": "integral",
+ "\\intop": "integral",
+ "\\lim": "limit",
+ "\\ln": "natural log",
+ "\\log": "log",
+ "\\sin": "sine",
+ "\\cos": "cosine",
+ "\\tan": "tangent",
+ "\\cot": "cotangent",
+ "\\sum": "sum",
+ "/": "slash",
+ ",": "comma",
+ ".": "point",
+ "-": "negative",
+ "+": "plus",
+ "~": "tilde",
+ ":": "colon",
+ "?": "question mark",
+ "'": "apostrophe",
+ "\\%": "percent",
+ " ": "space",
+ "\\ ": "space",
+ "\\$": "dollar sign",
+ "\\angle": "angle",
+ "\\degree": "degree",
+ "\\circ": "circle",
+ "\\vec": "vector",
+ "\\triangle": "triangle",
+ "\\pi": "pi",
+ "\\prime": "prime",
+ "\\infty": "infinity",
+ "\\alpha": "alpha",
+ "\\beta": "beta",
+ "\\gamma": "gamma",
+ "\\omega": "omega",
+ "\\theta": "theta",
+ "\\sigma": "sigma",
+ "\\lambda": "lambda",
+ "\\tau": "tau",
+ "\\Delta": "delta",
+ "\\delta": "delta",
+ "\\mu": "mu",
+ "\\rho": "rho",
+ "\\nabla": "del",
+ "\\ell": "ell",
+ "\\ldots": "dots",
+ // TODO: add entries for all accents
+ "\\hat": "hat",
+ "\\acute": "acute"
+};
+const powerMap = {
+ "prime": "prime",
+ "degree": "degrees",
+ "circle": "degrees",
+ "2": "squared",
+ "3": "cubed"
+};
+const openMap = {
+ "|": "open vertical bar",
+ ".": ""
+};
+const closeMap = {
+ "|": "close vertical bar",
+ ".": ""
+};
+const binMap = {
+ "+": "plus",
+ "-": "minus",
+ "\\pm": "plus minus",
+ "\\cdot": "dot",
+ "*": "times",
+ "/": "divided by",
+ "\\times": "times",
+ "\\div": "divided by",
+ "\\circ": "circle",
+ "\\bullet": "bullet"
+};
+const relMap = {
+ "=": "equals",
+ "\\approx": "approximately equals",
+ "≠": "does not equal",
+ "\\geq": "is greater than or equal to",
+ "\\ge": "is greater than or equal to",
+ "\\leq": "is less than or equal to",
+ "\\le": "is less than or equal to",
+ ">": "is greater than",
+ "<": "is less than",
+ "\\leftarrow": "left arrow",
+ "\\Leftarrow": "left arrow",
+ "\\rightarrow": "right arrow",
+ "\\Rightarrow": "right arrow",
+ ":": "colon"
+};
+const accentUnderMap = {
+ "\\underleftarrow": "left arrow",
+ "\\underrightarrow": "right arrow",
+ "\\underleftrightarrow": "left-right arrow",
+ "\\undergroup": "group",
+ "\\underlinesegment": "line segment",
+ "\\utilde": "tilde"
+};
+
+const buildString = (str, type, a11yStrings) => {
+ if (!str) {
+ return;
+ }
+
+ let ret;
+
+ if (type === "open") {
+ ret = str in openMap ? openMap[str] : stringMap[str] || str;
+ } else if (type === "close") {
+ ret = str in closeMap ? closeMap[str] : stringMap[str] || str;
+ } else if (type === "bin") {
+ ret = binMap[str] || str;
+ } else if (type === "rel") {
+ ret = relMap[str] || str;
+ } else {
+ ret = stringMap[str] || str;
+ } // If the text to add is a number and there is already a string
+ // in the list and the last string is a number then we should
+ // combine them into a single number
+
+
+ if (/^\d+$/.test(ret) && a11yStrings.length > 0 && // TODO(kevinb): check that the last item in a11yStrings is a string
+ // I think we might be able to drop the nested arrays, which would make
+ // this easier to type - $FlowFixMe
+ /^\d+$/.test(a11yStrings[a11yStrings.length - 1])) {
+ a11yStrings[a11yStrings.length - 1] += ret;
+ } else if (ret) {
+ a11yStrings.push(ret);
+ }
+};
+
+const buildRegion = (a11yStrings, callback) => {
+ const regionStrings = [];
+ a11yStrings.push(regionStrings);
+ callback(regionStrings);
+};
+
+const handleObject = (tree, a11yStrings, atomType) => {
+ // Everything else is assumed to be an object...
+ switch (tree.type) {
+ case "accent":
+ {
+ buildRegion(a11yStrings, a11yStrings => {
+ buildA11yStrings(tree.base, a11yStrings, atomType);
+ a11yStrings.push("with");
+ buildString(tree.label, "normal", a11yStrings);
+ a11yStrings.push("on top");
+ });
+ break;
+ }
+
+ case "accentUnder":
+ {
+ buildRegion(a11yStrings, a11yStrings => {
+ buildA11yStrings(tree.base, a11yStrings, atomType);
+ a11yStrings.push("with");
+ buildString(accentUnderMap[tree.label], "normal", a11yStrings);
+ a11yStrings.push("underneath");
+ });
+ break;
+ }
+
+ case "accent-token":
+ {
+ // Used internally by accent symbols.
+ break;
+ }
+
+ case "atom":
+ {
+ const text = tree.text;
+
+ switch (tree.family) {
+ case "bin":
+ {
+ buildString(text, "bin", a11yStrings);
+ break;
+ }
+
+ case "close":
+ {
+ buildString(text, "close", a11yStrings);
+ break;
+ }
+ // TODO(kevinb): figure out what should be done for inner
+
+ case "inner":
+ {
+ buildString(tree.text, "inner", a11yStrings);
+ break;
+ }
+
+ case "open":
+ {
+ buildString(text, "open", a11yStrings);
+ break;
+ }
+
+ case "punct":
+ {
+ buildString(text, "punct", a11yStrings);
+ break;
+ }
+
+ case "rel":
+ {
+ buildString(text, "rel", a11yStrings);
+ break;
+ }
+
+ default:
+ {
+ tree.family;
+ throw new Error(`"${tree.family}" is not a valid atom type`);
+ }
+ }
+
+ break;
+ }
+
+ case "color":
+ {
+ const color = tree.color.replace(/katex-/, "");
+ buildRegion(a11yStrings, regionStrings => {
+ regionStrings.push("start color " + color);
+ buildA11yStrings(tree.body, regionStrings, atomType);
+ regionStrings.push("end color " + color);
+ });
+ break;
+ }
+
+ case "color-token":
+ {
+ // Used by \color, \colorbox, and \fcolorbox but not directly rendered.
+ // It's a leaf node and has no children so just break.
+ break;
+ }
+
+ case "delimsizing":
+ {
+ if (tree.delim && tree.delim !== ".") {
+ buildString(tree.delim, "normal", a11yStrings);
+ }
+
+ break;
+ }
+
+ case "genfrac":
+ {
+ buildRegion(a11yStrings, regionStrings => {
+ // genfrac can have unbalanced delimiters
+ const leftDelim = tree.leftDelim,
+ rightDelim = tree.rightDelim; // NOTE: Not sure if this is a safe assumption
+ // hasBarLine true -> fraction, false -> binomial
+
+ if (tree.hasBarLine) {
+ regionStrings.push("start fraction");
+ leftDelim && buildString(leftDelim, "open", regionStrings);
+ buildA11yStrings(tree.numer, regionStrings, atomType);
+ regionStrings.push("divided by");
+ buildA11yStrings(tree.denom, regionStrings, atomType);
+ rightDelim && buildString(rightDelim, "close", regionStrings);
+ regionStrings.push("end fraction");
+ } else {
+ regionStrings.push("start binomial");
+ leftDelim && buildString(leftDelim, "open", regionStrings);
+ buildA11yStrings(tree.numer, regionStrings, atomType);
+ regionStrings.push("over");
+ buildA11yStrings(tree.denom, regionStrings, atomType);
+ rightDelim && buildString(rightDelim, "close", regionStrings);
+ regionStrings.push("end binomial");
+ }
+ });
+ break;
+ }
+
+ case "kern":
+ {
+ // No op: we don't attempt to present kerning information
+ // to the screen reader.
+ break;
+ }
+
+ case "leftright":
+ {
+ buildRegion(a11yStrings, regionStrings => {
+ buildString(tree.left, "open", regionStrings);
+ buildA11yStrings(tree.body, regionStrings, atomType);
+ buildString(tree.right, "close", regionStrings);
+ });
+ break;
+ }
+
+ case "leftright-right":
+ {
+ // TODO: double check that this is a no-op
+ break;
+ }
+
+ case "lap":
+ {
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ case "mathord":
+ {
+ buildString(tree.text, "normal", a11yStrings);
+ break;
+ }
+
+ case "op":
+ {
+ const body = tree.body,
+ name = tree.name;
+
+ if (body) {
+ buildA11yStrings(body, a11yStrings, atomType);
+ } else if (name) {
+ buildString(name, "normal", a11yStrings);
+ }
+
+ break;
+ }
+
+ case "op-token":
+ {
+ // Used internally by operator symbols.
+ buildString(tree.text, atomType, a11yStrings);
+ break;
+ }
+
+ case "ordgroup":
+ {
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ case "overline":
+ {
+ buildRegion(a11yStrings, function (a11yStrings) {
+ a11yStrings.push("start overline");
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ a11yStrings.push("end overline");
+ });
+ break;
+ }
+
+ case "phantom":
+ {
+ a11yStrings.push("empty space");
+ break;
+ }
+
+ case "raisebox":
+ {
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ case "rule":
+ {
+ a11yStrings.push("rectangle");
+ break;
+ }
+
+ case "sizing":
+ {
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ case "spacing":
+ {
+ a11yStrings.push("space");
+ break;
+ }
+
+ case "styling":
+ {
+ // We ignore the styling and just pass through the contents
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ case "sqrt":
+ {
+ buildRegion(a11yStrings, regionStrings => {
+ const body = tree.body,
+ index = tree.index;
+
+ if (index) {
+ const indexString = flatten(buildA11yStrings(index, [], atomType)).join(",");
+
+ if (indexString === "3") {
+ regionStrings.push("cube root of");
+ buildA11yStrings(body, regionStrings, atomType);
+ regionStrings.push("end cube root");
+ return;
+ }
+
+ regionStrings.push("root");
+ regionStrings.push("start index");
+ buildA11yStrings(index, regionStrings, atomType);
+ regionStrings.push("end index");
+ return;
+ }
+
+ regionStrings.push("square root of");
+ buildA11yStrings(body, regionStrings, atomType);
+ regionStrings.push("end square root");
+ });
+ break;
+ }
+
+ case "supsub":
+ {
+ const base = tree.base,
+ sub = tree.sub,
+ sup = tree.sup;
+ let isLog = false;
+
+ if (base) {
+ buildA11yStrings(base, a11yStrings, atomType);
+ isLog = base.type === "op" && base.name === "\\log";
+ }
+
+ if (sub) {
+ const regionName = isLog ? "base" : "subscript";
+ buildRegion(a11yStrings, function (regionStrings) {
+ regionStrings.push(`start ${regionName}`);
+ buildA11yStrings(sub, regionStrings, atomType);
+ regionStrings.push(`end ${regionName}`);
+ });
+ }
+
+ if (sup) {
+ buildRegion(a11yStrings, function (regionStrings) {
+ const supString = flatten(buildA11yStrings(sup, [], atomType)).join(",");
+
+ if (supString in powerMap) {
+ regionStrings.push(powerMap[supString]);
+ return;
+ }
+
+ regionStrings.push("start superscript");
+ buildA11yStrings(sup, regionStrings, atomType);
+ regionStrings.push("end superscript");
+ });
+ }
+
+ break;
+ }
+
+ case "text":
+ {
+ // TODO: handle other fonts
+ if (tree.font === "\\textbf") {
+ buildRegion(a11yStrings, function (regionStrings) {
+ regionStrings.push("start bold text");
+ buildA11yStrings(tree.body, regionStrings, atomType);
+ regionStrings.push("end bold text");
+ });
+ break;
+ }
+
+ buildRegion(a11yStrings, function (regionStrings) {
+ regionStrings.push("start text");
+ buildA11yStrings(tree.body, regionStrings, atomType);
+ regionStrings.push("end text");
+ });
+ break;
+ }
+
+ case "textord":
+ {
+ buildString(tree.text, atomType, a11yStrings);
+ break;
+ }
+
+ case "smash":
+ {
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ case "enclose":
+ {
+ // TODO: create a map for these.
+ // TODO: differentiate between a body with a single atom, e.g.
+ // "cancel a" instead of "start cancel, a, end cancel"
+ if (/cancel/.test(tree.label)) {
+ buildRegion(a11yStrings, function (regionStrings) {
+ regionStrings.push("start cancel");
+ buildA11yStrings(tree.body, regionStrings, atomType);
+ regionStrings.push("end cancel");
+ });
+ break;
+ } else if (/box/.test(tree.label)) {
+ buildRegion(a11yStrings, function (regionStrings) {
+ regionStrings.push("start box");
+ buildA11yStrings(tree.body, regionStrings, atomType);
+ regionStrings.push("end box");
+ });
+ break;
+ } else if (/sout/.test(tree.label)) {
+ buildRegion(a11yStrings, function (regionStrings) {
+ regionStrings.push("start strikeout");
+ buildA11yStrings(tree.body, regionStrings, atomType);
+ regionStrings.push("end strikeout");
+ });
+ break;
+ }
+
+ throw new Error(`KaTeX-a11y: enclose node with ${tree.label} not supported yet`);
+ }
+
+ case "vphantom":
+ {
+ throw new Error("KaTeX-a11y: vphantom not implemented yet");
+ }
+
+ case "hphantom":
+ {
+ throw new Error("KaTeX-a11y: hphantom not implemented yet");
+ }
+
+ case "operatorname":
+ {
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ case "array":
+ {
+ throw new Error("KaTeX-a11y: array not implemented yet");
+ }
+
+ case "raw":
+ {
+ throw new Error("KaTeX-a11y: raw not implemented yet");
+ }
+
+ case "size":
+ {
+ // Although there are nodes of type "size" in the parse tree, they have
+ // no semantic meaning and should be ignored.
+ break;
+ }
+
+ case "url":
+ {
+ throw new Error("KaTeX-a11y: url not implemented yet");
+ }
+
+ case "tag":
+ {
+ throw new Error("KaTeX-a11y: tag not implemented yet");
+ }
+
+ case "verb":
+ {
+ buildString(`start verbatim`, "normal", a11yStrings);
+ buildString(tree.body, "normal", a11yStrings);
+ buildString(`end verbatim`, "normal", a11yStrings);
+ break;
+ }
+
+ case "environment":
+ {
+ throw new Error("KaTeX-a11y: environment not implemented yet");
+ }
+
+ case "horizBrace":
+ {
+ buildString(`start ${tree.label.slice(1)}`, "normal", a11yStrings);
+ buildA11yStrings(tree.base, a11yStrings, atomType);
+ buildString(`end ${tree.label.slice(1)}`, "normal", a11yStrings);
+ break;
+ }
+
+ case "infix":
+ {
+ // All infix nodes are replace with other nodes.
+ break;
+ }
+
+ case "includegraphics":
+ {
+ throw new Error("KaTeX-a11y: includegraphics not implemented yet");
+ }
+
+ case "font":
+ {
+ // TODO: callout the start/end of specific fonts
+ // TODO: map \BBb{N} to "the naturals" or something like that
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ case "href":
+ {
+ throw new Error("KaTeX-a11y: href not implemented yet");
+ }
+
+ case "cr":
+ {
+ // This is used by environments.
+ throw new Error("KaTeX-a11y: cr not implemented yet");
+ }
+
+ case "underline":
+ {
+ buildRegion(a11yStrings, function (a11yStrings) {
+ a11yStrings.push("start underline");
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ a11yStrings.push("end underline");
+ });
+ break;
+ }
+
+ case "xArrow":
+ {
+ throw new Error("KaTeX-a11y: xArrow not implemented yet");
+ }
+
+ case "mclass":
+ {
+ // \neq and \ne are macros so we let "htmlmathml" render the mathmal
+ // side of things and extract the text from that.
+ const atomType = tree.mclass.slice(1); // $FlowFixMe: drop the leading "m" from the values in mclass
+
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ case "mathchoice":
+ {
+ // TODO: track which which style we're using, e.g. dispaly, text, etc.
+ // default to text style if even that may not be the correct style
+ buildA11yStrings(tree.text, a11yStrings, atomType);
+ break;
+ }
+
+ case "htmlmathml":
+ {
+ buildA11yStrings(tree.mathml, a11yStrings, atomType);
+ break;
+ }
+
+ case "middle":
+ {
+ buildString(tree.delim, atomType, a11yStrings);
+ break;
+ }
+
+ case "internal":
+ {
+ // internal nodes are never included in the parse tree
+ break;
+ }
+
+ case "html":
+ {
+ buildA11yStrings(tree.body, a11yStrings, atomType);
+ break;
+ }
+
+ default:
+ tree.type;
+ throw new Error("KaTeX a11y un-recognized type: " + tree.type);
+ }
+};
+
+const buildA11yStrings = function buildA11yStrings(tree, a11yStrings, atomType) {
+ if (a11yStrings === void 0) {
+ a11yStrings = [];
+ }
+
+ if (tree instanceof Array) {
+ for (let i = 0; i < tree.length; i++) {
+ buildA11yStrings(tree[i], a11yStrings, atomType);
+ }
+ } else {
+ handleObject(tree, a11yStrings, atomType);
+ }
+
+ return a11yStrings;
+};
+
+const flatten = function flatten(array) {
+ let result = [];
+ array.forEach(function (item) {
+ if (item instanceof Array) {
+ result = result.concat(flatten(item));
+ } else {
+ result.push(item);
+ }
+ });
+ return result;
+};
+
+const renderA11yString = function renderA11yString(text, settings) {
+ const tree = katex.__parse(text, settings);
+
+ const a11yStrings = buildA11yStrings(tree, [], "normal");
+ return flatten(a11yStrings).join(", ");
+};
+
+export default renderA11yString;