1e5c31af7Sopenharmony_ciimport katex from '../katex.mjs';
2e5c31af7Sopenharmony_ci
3e5c31af7Sopenharmony_ci/**
4e5c31af7Sopenharmony_ci * renderA11yString returns a readable string.
5e5c31af7Sopenharmony_ci *
6e5c31af7Sopenharmony_ci * In some cases the string will have the proper semantic math
7e5c31af7Sopenharmony_ci * meaning,:
8e5c31af7Sopenharmony_ci *   renderA11yString("\\frac{1}{2}"")
9e5c31af7Sopenharmony_ci *   -> "start fraction, 1, divided by, 2, end fraction"
10e5c31af7Sopenharmony_ci *
11e5c31af7Sopenharmony_ci * However, other cases do not:
12e5c31af7Sopenharmony_ci *   renderA11yString("f(x) = x^2")
13e5c31af7Sopenharmony_ci *   -> "f, left parenthesis, x, right parenthesis, equals, x, squared"
14e5c31af7Sopenharmony_ci *
15e5c31af7Sopenharmony_ci * The commas in the string aim to increase ease of understanding
16e5c31af7Sopenharmony_ci * when read by a screenreader.
17e5c31af7Sopenharmony_ci */
18e5c31af7Sopenharmony_ciconst stringMap = {
19e5c31af7Sopenharmony_ci  "(": "left parenthesis",
20e5c31af7Sopenharmony_ci  ")": "right parenthesis",
21e5c31af7Sopenharmony_ci  "[": "open bracket",
22e5c31af7Sopenharmony_ci  "]": "close bracket",
23e5c31af7Sopenharmony_ci  "\\{": "left brace",
24e5c31af7Sopenharmony_ci  "\\}": "right brace",
25e5c31af7Sopenharmony_ci  "\\lvert": "open vertical bar",
26e5c31af7Sopenharmony_ci  "\\rvert": "close vertical bar",
27e5c31af7Sopenharmony_ci  "|": "vertical bar",
28e5c31af7Sopenharmony_ci  "\\uparrow": "up arrow",
29e5c31af7Sopenharmony_ci  "\\Uparrow": "up arrow",
30e5c31af7Sopenharmony_ci  "\\downarrow": "down arrow",
31e5c31af7Sopenharmony_ci  "\\Downarrow": "down arrow",
32e5c31af7Sopenharmony_ci  "\\updownarrow": "up down arrow",
33e5c31af7Sopenharmony_ci  "\\leftarrow": "left arrow",
34e5c31af7Sopenharmony_ci  "\\Leftarrow": "left arrow",
35e5c31af7Sopenharmony_ci  "\\rightarrow": "right arrow",
36e5c31af7Sopenharmony_ci  "\\Rightarrow": "right arrow",
37e5c31af7Sopenharmony_ci  "\\langle": "open angle",
38e5c31af7Sopenharmony_ci  "\\rangle": "close angle",
39e5c31af7Sopenharmony_ci  "\\lfloor": "open floor",
40e5c31af7Sopenharmony_ci  "\\rfloor": "close floor",
41e5c31af7Sopenharmony_ci  "\\int": "integral",
42e5c31af7Sopenharmony_ci  "\\intop": "integral",
43e5c31af7Sopenharmony_ci  "\\lim": "limit",
44e5c31af7Sopenharmony_ci  "\\ln": "natural log",
45e5c31af7Sopenharmony_ci  "\\log": "log",
46e5c31af7Sopenharmony_ci  "\\sin": "sine",
47e5c31af7Sopenharmony_ci  "\\cos": "cosine",
48e5c31af7Sopenharmony_ci  "\\tan": "tangent",
49e5c31af7Sopenharmony_ci  "\\cot": "cotangent",
50e5c31af7Sopenharmony_ci  "\\sum": "sum",
51e5c31af7Sopenharmony_ci  "/": "slash",
52e5c31af7Sopenharmony_ci  ",": "comma",
53e5c31af7Sopenharmony_ci  ".": "point",
54e5c31af7Sopenharmony_ci  "-": "negative",
55e5c31af7Sopenharmony_ci  "+": "plus",
56e5c31af7Sopenharmony_ci  "~": "tilde",
57e5c31af7Sopenharmony_ci  ":": "colon",
58e5c31af7Sopenharmony_ci  "?": "question mark",
59e5c31af7Sopenharmony_ci  "'": "apostrophe",
60e5c31af7Sopenharmony_ci  "\\%": "percent",
61e5c31af7Sopenharmony_ci  " ": "space",
62e5c31af7Sopenharmony_ci  "\\ ": "space",
63e5c31af7Sopenharmony_ci  "\\$": "dollar sign",
64e5c31af7Sopenharmony_ci  "\\angle": "angle",
65e5c31af7Sopenharmony_ci  "\\degree": "degree",
66e5c31af7Sopenharmony_ci  "\\circ": "circle",
67e5c31af7Sopenharmony_ci  "\\vec": "vector",
68e5c31af7Sopenharmony_ci  "\\triangle": "triangle",
69e5c31af7Sopenharmony_ci  "\\pi": "pi",
70e5c31af7Sopenharmony_ci  "\\prime": "prime",
71e5c31af7Sopenharmony_ci  "\\infty": "infinity",
72e5c31af7Sopenharmony_ci  "\\alpha": "alpha",
73e5c31af7Sopenharmony_ci  "\\beta": "beta",
74e5c31af7Sopenharmony_ci  "\\gamma": "gamma",
75e5c31af7Sopenharmony_ci  "\\omega": "omega",
76e5c31af7Sopenharmony_ci  "\\theta": "theta",
77e5c31af7Sopenharmony_ci  "\\sigma": "sigma",
78e5c31af7Sopenharmony_ci  "\\lambda": "lambda",
79e5c31af7Sopenharmony_ci  "\\tau": "tau",
80e5c31af7Sopenharmony_ci  "\\Delta": "delta",
81e5c31af7Sopenharmony_ci  "\\delta": "delta",
82e5c31af7Sopenharmony_ci  "\\mu": "mu",
83e5c31af7Sopenharmony_ci  "\\rho": "rho",
84e5c31af7Sopenharmony_ci  "\\nabla": "del",
85e5c31af7Sopenharmony_ci  "\\ell": "ell",
86e5c31af7Sopenharmony_ci  "\\ldots": "dots",
87e5c31af7Sopenharmony_ci  // TODO: add entries for all accents
88e5c31af7Sopenharmony_ci  "\\hat": "hat",
89e5c31af7Sopenharmony_ci  "\\acute": "acute"
90e5c31af7Sopenharmony_ci};
91e5c31af7Sopenharmony_ciconst powerMap = {
92e5c31af7Sopenharmony_ci  "prime": "prime",
93e5c31af7Sopenharmony_ci  "degree": "degrees",
94e5c31af7Sopenharmony_ci  "circle": "degrees",
95e5c31af7Sopenharmony_ci  "2": "squared",
96e5c31af7Sopenharmony_ci  "3": "cubed"
97e5c31af7Sopenharmony_ci};
98e5c31af7Sopenharmony_ciconst openMap = {
99e5c31af7Sopenharmony_ci  "|": "open vertical bar",
100e5c31af7Sopenharmony_ci  ".": ""
101e5c31af7Sopenharmony_ci};
102e5c31af7Sopenharmony_ciconst closeMap = {
103e5c31af7Sopenharmony_ci  "|": "close vertical bar",
104e5c31af7Sopenharmony_ci  ".": ""
105e5c31af7Sopenharmony_ci};
106e5c31af7Sopenharmony_ciconst binMap = {
107e5c31af7Sopenharmony_ci  "+": "plus",
108e5c31af7Sopenharmony_ci  "-": "minus",
109e5c31af7Sopenharmony_ci  "\\pm": "plus minus",
110e5c31af7Sopenharmony_ci  "\\cdot": "dot",
111e5c31af7Sopenharmony_ci  "*": "times",
112e5c31af7Sopenharmony_ci  "/": "divided by",
113e5c31af7Sopenharmony_ci  "\\times": "times",
114e5c31af7Sopenharmony_ci  "\\div": "divided by",
115e5c31af7Sopenharmony_ci  "\\circ": "circle",
116e5c31af7Sopenharmony_ci  "\\bullet": "bullet"
117e5c31af7Sopenharmony_ci};
118e5c31af7Sopenharmony_ciconst relMap = {
119e5c31af7Sopenharmony_ci  "=": "equals",
120e5c31af7Sopenharmony_ci  "\\approx": "approximately equals",
121e5c31af7Sopenharmony_ci  "≠": "does not equal",
122e5c31af7Sopenharmony_ci  "\\geq": "is greater than or equal to",
123e5c31af7Sopenharmony_ci  "\\ge": "is greater than or equal to",
124e5c31af7Sopenharmony_ci  "\\leq": "is less than or equal to",
125e5c31af7Sopenharmony_ci  "\\le": "is less than or equal to",
126e5c31af7Sopenharmony_ci  ">": "is greater than",
127e5c31af7Sopenharmony_ci  "<": "is less than",
128e5c31af7Sopenharmony_ci  "\\leftarrow": "left arrow",
129e5c31af7Sopenharmony_ci  "\\Leftarrow": "left arrow",
130e5c31af7Sopenharmony_ci  "\\rightarrow": "right arrow",
131e5c31af7Sopenharmony_ci  "\\Rightarrow": "right arrow",
132e5c31af7Sopenharmony_ci  ":": "colon"
133e5c31af7Sopenharmony_ci};
134e5c31af7Sopenharmony_ciconst accentUnderMap = {
135e5c31af7Sopenharmony_ci  "\\underleftarrow": "left arrow",
136e5c31af7Sopenharmony_ci  "\\underrightarrow": "right arrow",
137e5c31af7Sopenharmony_ci  "\\underleftrightarrow": "left-right arrow",
138e5c31af7Sopenharmony_ci  "\\undergroup": "group",
139e5c31af7Sopenharmony_ci  "\\underlinesegment": "line segment",
140e5c31af7Sopenharmony_ci  "\\utilde": "tilde"
141e5c31af7Sopenharmony_ci};
142e5c31af7Sopenharmony_ci
143e5c31af7Sopenharmony_ciconst buildString = (str, type, a11yStrings) => {
144e5c31af7Sopenharmony_ci  if (!str) {
145e5c31af7Sopenharmony_ci    return;
146e5c31af7Sopenharmony_ci  }
147e5c31af7Sopenharmony_ci
148e5c31af7Sopenharmony_ci  let ret;
149e5c31af7Sopenharmony_ci
150e5c31af7Sopenharmony_ci  if (type === "open") {
151e5c31af7Sopenharmony_ci    ret = str in openMap ? openMap[str] : stringMap[str] || str;
152e5c31af7Sopenharmony_ci  } else if (type === "close") {
153e5c31af7Sopenharmony_ci    ret = str in closeMap ? closeMap[str] : stringMap[str] || str;
154e5c31af7Sopenharmony_ci  } else if (type === "bin") {
155e5c31af7Sopenharmony_ci    ret = binMap[str] || str;
156e5c31af7Sopenharmony_ci  } else if (type === "rel") {
157e5c31af7Sopenharmony_ci    ret = relMap[str] || str;
158e5c31af7Sopenharmony_ci  } else {
159e5c31af7Sopenharmony_ci    ret = stringMap[str] || str;
160e5c31af7Sopenharmony_ci  } // If the text to add is a number and there is already a string
161e5c31af7Sopenharmony_ci  // in the list and the last string is a number then we should
162e5c31af7Sopenharmony_ci  // combine them into a single number
163e5c31af7Sopenharmony_ci
164e5c31af7Sopenharmony_ci
165e5c31af7Sopenharmony_ci  if (/^\d+$/.test(ret) && a11yStrings.length > 0 && // TODO(kevinb): check that the last item in a11yStrings is a string
166e5c31af7Sopenharmony_ci  // I think we might be able to drop the nested arrays, which would make
167e5c31af7Sopenharmony_ci  // this easier to type - $FlowFixMe
168e5c31af7Sopenharmony_ci  /^\d+$/.test(a11yStrings[a11yStrings.length - 1])) {
169e5c31af7Sopenharmony_ci    a11yStrings[a11yStrings.length - 1] += ret;
170e5c31af7Sopenharmony_ci  } else if (ret) {
171e5c31af7Sopenharmony_ci    a11yStrings.push(ret);
172e5c31af7Sopenharmony_ci  }
173e5c31af7Sopenharmony_ci};
174e5c31af7Sopenharmony_ci
175e5c31af7Sopenharmony_ciconst buildRegion = (a11yStrings, callback) => {
176e5c31af7Sopenharmony_ci  const regionStrings = [];
177e5c31af7Sopenharmony_ci  a11yStrings.push(regionStrings);
178e5c31af7Sopenharmony_ci  callback(regionStrings);
179e5c31af7Sopenharmony_ci};
180e5c31af7Sopenharmony_ci
181e5c31af7Sopenharmony_ciconst handleObject = (tree, a11yStrings, atomType) => {
182e5c31af7Sopenharmony_ci  // Everything else is assumed to be an object...
183e5c31af7Sopenharmony_ci  switch (tree.type) {
184e5c31af7Sopenharmony_ci    case "accent":
185e5c31af7Sopenharmony_ci      {
186e5c31af7Sopenharmony_ci        buildRegion(a11yStrings, a11yStrings => {
187e5c31af7Sopenharmony_ci          buildA11yStrings(tree.base, a11yStrings, atomType);
188e5c31af7Sopenharmony_ci          a11yStrings.push("with");
189e5c31af7Sopenharmony_ci          buildString(tree.label, "normal", a11yStrings);
190e5c31af7Sopenharmony_ci          a11yStrings.push("on top");
191e5c31af7Sopenharmony_ci        });
192e5c31af7Sopenharmony_ci        break;
193e5c31af7Sopenharmony_ci      }
194e5c31af7Sopenharmony_ci
195e5c31af7Sopenharmony_ci    case "accentUnder":
196e5c31af7Sopenharmony_ci      {
197e5c31af7Sopenharmony_ci        buildRegion(a11yStrings, a11yStrings => {
198e5c31af7Sopenharmony_ci          buildA11yStrings(tree.base, a11yStrings, atomType);
199e5c31af7Sopenharmony_ci          a11yStrings.push("with");
200e5c31af7Sopenharmony_ci          buildString(accentUnderMap[tree.label], "normal", a11yStrings);
201e5c31af7Sopenharmony_ci          a11yStrings.push("underneath");
202e5c31af7Sopenharmony_ci        });
203e5c31af7Sopenharmony_ci        break;
204e5c31af7Sopenharmony_ci      }
205e5c31af7Sopenharmony_ci
206e5c31af7Sopenharmony_ci    case "accent-token":
207e5c31af7Sopenharmony_ci      {
208e5c31af7Sopenharmony_ci        // Used internally by accent symbols.
209e5c31af7Sopenharmony_ci        break;
210e5c31af7Sopenharmony_ci      }
211e5c31af7Sopenharmony_ci
212e5c31af7Sopenharmony_ci    case "atom":
213e5c31af7Sopenharmony_ci      {
214e5c31af7Sopenharmony_ci        const text = tree.text;
215e5c31af7Sopenharmony_ci
216e5c31af7Sopenharmony_ci        switch (tree.family) {
217e5c31af7Sopenharmony_ci          case "bin":
218e5c31af7Sopenharmony_ci            {
219e5c31af7Sopenharmony_ci              buildString(text, "bin", a11yStrings);
220e5c31af7Sopenharmony_ci              break;
221e5c31af7Sopenharmony_ci            }
222e5c31af7Sopenharmony_ci
223e5c31af7Sopenharmony_ci          case "close":
224e5c31af7Sopenharmony_ci            {
225e5c31af7Sopenharmony_ci              buildString(text, "close", a11yStrings);
226e5c31af7Sopenharmony_ci              break;
227e5c31af7Sopenharmony_ci            }
228e5c31af7Sopenharmony_ci          // TODO(kevinb): figure out what should be done for inner
229e5c31af7Sopenharmony_ci
230e5c31af7Sopenharmony_ci          case "inner":
231e5c31af7Sopenharmony_ci            {
232e5c31af7Sopenharmony_ci              buildString(tree.text, "inner", a11yStrings);
233e5c31af7Sopenharmony_ci              break;
234e5c31af7Sopenharmony_ci            }
235e5c31af7Sopenharmony_ci
236e5c31af7Sopenharmony_ci          case "open":
237e5c31af7Sopenharmony_ci            {
238e5c31af7Sopenharmony_ci              buildString(text, "open", a11yStrings);
239e5c31af7Sopenharmony_ci              break;
240e5c31af7Sopenharmony_ci            }
241e5c31af7Sopenharmony_ci
242e5c31af7Sopenharmony_ci          case "punct":
243e5c31af7Sopenharmony_ci            {
244e5c31af7Sopenharmony_ci              buildString(text, "punct", a11yStrings);
245e5c31af7Sopenharmony_ci              break;
246e5c31af7Sopenharmony_ci            }
247e5c31af7Sopenharmony_ci
248e5c31af7Sopenharmony_ci          case "rel":
249e5c31af7Sopenharmony_ci            {
250e5c31af7Sopenharmony_ci              buildString(text, "rel", a11yStrings);
251e5c31af7Sopenharmony_ci              break;
252e5c31af7Sopenharmony_ci            }
253e5c31af7Sopenharmony_ci
254e5c31af7Sopenharmony_ci          default:
255e5c31af7Sopenharmony_ci            {
256e5c31af7Sopenharmony_ci              tree.family;
257e5c31af7Sopenharmony_ci              throw new Error(`"${tree.family}" is not a valid atom type`);
258e5c31af7Sopenharmony_ci            }
259e5c31af7Sopenharmony_ci        }
260e5c31af7Sopenharmony_ci
261e5c31af7Sopenharmony_ci        break;
262e5c31af7Sopenharmony_ci      }
263e5c31af7Sopenharmony_ci
264e5c31af7Sopenharmony_ci    case "color":
265e5c31af7Sopenharmony_ci      {
266e5c31af7Sopenharmony_ci        const color = tree.color.replace(/katex-/, "");
267e5c31af7Sopenharmony_ci        buildRegion(a11yStrings, regionStrings => {
268e5c31af7Sopenharmony_ci          regionStrings.push("start color " + color);
269e5c31af7Sopenharmony_ci          buildA11yStrings(tree.body, regionStrings, atomType);
270e5c31af7Sopenharmony_ci          regionStrings.push("end color " + color);
271e5c31af7Sopenharmony_ci        });
272e5c31af7Sopenharmony_ci        break;
273e5c31af7Sopenharmony_ci      }
274e5c31af7Sopenharmony_ci
275e5c31af7Sopenharmony_ci    case "color-token":
276e5c31af7Sopenharmony_ci      {
277e5c31af7Sopenharmony_ci        // Used by \color, \colorbox, and \fcolorbox but not directly rendered.
278e5c31af7Sopenharmony_ci        // It's a leaf node and has no children so just break.
279e5c31af7Sopenharmony_ci        break;
280e5c31af7Sopenharmony_ci      }
281e5c31af7Sopenharmony_ci
282e5c31af7Sopenharmony_ci    case "delimsizing":
283e5c31af7Sopenharmony_ci      {
284e5c31af7Sopenharmony_ci        if (tree.delim && tree.delim !== ".") {
285e5c31af7Sopenharmony_ci          buildString(tree.delim, "normal", a11yStrings);
286e5c31af7Sopenharmony_ci        }
287e5c31af7Sopenharmony_ci
288e5c31af7Sopenharmony_ci        break;
289e5c31af7Sopenharmony_ci      }
290e5c31af7Sopenharmony_ci
291e5c31af7Sopenharmony_ci    case "genfrac":
292e5c31af7Sopenharmony_ci      {
293e5c31af7Sopenharmony_ci        buildRegion(a11yStrings, regionStrings => {
294e5c31af7Sopenharmony_ci          // genfrac can have unbalanced delimiters
295e5c31af7Sopenharmony_ci          const leftDelim = tree.leftDelim,
296e5c31af7Sopenharmony_ci                rightDelim = tree.rightDelim; // NOTE: Not sure if this is a safe assumption
297e5c31af7Sopenharmony_ci          // hasBarLine true -> fraction, false -> binomial
298e5c31af7Sopenharmony_ci
299e5c31af7Sopenharmony_ci          if (tree.hasBarLine) {
300e5c31af7Sopenharmony_ci            regionStrings.push("start fraction");
301e5c31af7Sopenharmony_ci            leftDelim && buildString(leftDelim, "open", regionStrings);
302e5c31af7Sopenharmony_ci            buildA11yStrings(tree.numer, regionStrings, atomType);
303e5c31af7Sopenharmony_ci            regionStrings.push("divided by");
304e5c31af7Sopenharmony_ci            buildA11yStrings(tree.denom, regionStrings, atomType);
305e5c31af7Sopenharmony_ci            rightDelim && buildString(rightDelim, "close", regionStrings);
306e5c31af7Sopenharmony_ci            regionStrings.push("end fraction");
307e5c31af7Sopenharmony_ci          } else {
308e5c31af7Sopenharmony_ci            regionStrings.push("start binomial");
309e5c31af7Sopenharmony_ci            leftDelim && buildString(leftDelim, "open", regionStrings);
310e5c31af7Sopenharmony_ci            buildA11yStrings(tree.numer, regionStrings, atomType);
311e5c31af7Sopenharmony_ci            regionStrings.push("over");
312e5c31af7Sopenharmony_ci            buildA11yStrings(tree.denom, regionStrings, atomType);
313e5c31af7Sopenharmony_ci            rightDelim && buildString(rightDelim, "close", regionStrings);
314e5c31af7Sopenharmony_ci            regionStrings.push("end binomial");
315e5c31af7Sopenharmony_ci          }
316e5c31af7Sopenharmony_ci        });
317e5c31af7Sopenharmony_ci        break;
318e5c31af7Sopenharmony_ci      }
319e5c31af7Sopenharmony_ci
320e5c31af7Sopenharmony_ci    case "kern":
321e5c31af7Sopenharmony_ci      {
322e5c31af7Sopenharmony_ci        // No op: we don't attempt to present kerning information
323e5c31af7Sopenharmony_ci        // to the screen reader.
324e5c31af7Sopenharmony_ci        break;
325e5c31af7Sopenharmony_ci      }
326e5c31af7Sopenharmony_ci
327e5c31af7Sopenharmony_ci    case "leftright":
328e5c31af7Sopenharmony_ci      {
329e5c31af7Sopenharmony_ci        buildRegion(a11yStrings, regionStrings => {
330e5c31af7Sopenharmony_ci          buildString(tree.left, "open", regionStrings);
331e5c31af7Sopenharmony_ci          buildA11yStrings(tree.body, regionStrings, atomType);
332e5c31af7Sopenharmony_ci          buildString(tree.right, "close", regionStrings);
333e5c31af7Sopenharmony_ci        });
334e5c31af7Sopenharmony_ci        break;
335e5c31af7Sopenharmony_ci      }
336e5c31af7Sopenharmony_ci
337e5c31af7Sopenharmony_ci    case "leftright-right":
338e5c31af7Sopenharmony_ci      {
339e5c31af7Sopenharmony_ci        // TODO: double check that this is a no-op
340e5c31af7Sopenharmony_ci        break;
341e5c31af7Sopenharmony_ci      }
342e5c31af7Sopenharmony_ci
343e5c31af7Sopenharmony_ci    case "lap":
344e5c31af7Sopenharmony_ci      {
345e5c31af7Sopenharmony_ci        buildA11yStrings(tree.body, a11yStrings, atomType);
346e5c31af7Sopenharmony_ci        break;
347e5c31af7Sopenharmony_ci      }
348e5c31af7Sopenharmony_ci
349e5c31af7Sopenharmony_ci    case "mathord":
350e5c31af7Sopenharmony_ci      {
351e5c31af7Sopenharmony_ci        buildString(tree.text, "normal", a11yStrings);
352e5c31af7Sopenharmony_ci        break;
353e5c31af7Sopenharmony_ci      }
354e5c31af7Sopenharmony_ci
355e5c31af7Sopenharmony_ci    case "op":
356e5c31af7Sopenharmony_ci      {
357e5c31af7Sopenharmony_ci        const body = tree.body,
358e5c31af7Sopenharmony_ci              name = tree.name;
359e5c31af7Sopenharmony_ci
360e5c31af7Sopenharmony_ci        if (body) {
361e5c31af7Sopenharmony_ci          buildA11yStrings(body, a11yStrings, atomType);
362e5c31af7Sopenharmony_ci        } else if (name) {
363e5c31af7Sopenharmony_ci          buildString(name, "normal", a11yStrings);
364e5c31af7Sopenharmony_ci        }
365e5c31af7Sopenharmony_ci
366e5c31af7Sopenharmony_ci        break;
367e5c31af7Sopenharmony_ci      }
368e5c31af7Sopenharmony_ci
369e5c31af7Sopenharmony_ci    case "op-token":
370e5c31af7Sopenharmony_ci      {
371e5c31af7Sopenharmony_ci        // Used internally by operator symbols.
372e5c31af7Sopenharmony_ci        buildString(tree.text, atomType, a11yStrings);
373e5c31af7Sopenharmony_ci        break;
374e5c31af7Sopenharmony_ci      }
375e5c31af7Sopenharmony_ci
376e5c31af7Sopenharmony_ci    case "ordgroup":
377e5c31af7Sopenharmony_ci      {
378e5c31af7Sopenharmony_ci        buildA11yStrings(tree.body, a11yStrings, atomType);
379e5c31af7Sopenharmony_ci        break;
380e5c31af7Sopenharmony_ci      }
381e5c31af7Sopenharmony_ci
382e5c31af7Sopenharmony_ci    case "overline":
383e5c31af7Sopenharmony_ci      {
384e5c31af7Sopenharmony_ci        buildRegion(a11yStrings, function (a11yStrings) {
385e5c31af7Sopenharmony_ci          a11yStrings.push("start overline");
386e5c31af7Sopenharmony_ci          buildA11yStrings(tree.body, a11yStrings, atomType);
387e5c31af7Sopenharmony_ci          a11yStrings.push("end overline");
388e5c31af7Sopenharmony_ci        });
389e5c31af7Sopenharmony_ci        break;
390e5c31af7Sopenharmony_ci      }
391e5c31af7Sopenharmony_ci
392e5c31af7Sopenharmony_ci    case "phantom":
393e5c31af7Sopenharmony_ci      {
394e5c31af7Sopenharmony_ci        a11yStrings.push("empty space");
395e5c31af7Sopenharmony_ci        break;
396e5c31af7Sopenharmony_ci      }
397e5c31af7Sopenharmony_ci
398e5c31af7Sopenharmony_ci    case "raisebox":
399e5c31af7Sopenharmony_ci      {
400e5c31af7Sopenharmony_ci        buildA11yStrings(tree.body, a11yStrings, atomType);
401e5c31af7Sopenharmony_ci        break;
402e5c31af7Sopenharmony_ci      }
403e5c31af7Sopenharmony_ci
404e5c31af7Sopenharmony_ci    case "rule":
405e5c31af7Sopenharmony_ci      {
406e5c31af7Sopenharmony_ci        a11yStrings.push("rectangle");
407e5c31af7Sopenharmony_ci        break;
408e5c31af7Sopenharmony_ci      }
409e5c31af7Sopenharmony_ci
410e5c31af7Sopenharmony_ci    case "sizing":
411e5c31af7Sopenharmony_ci      {
412e5c31af7Sopenharmony_ci        buildA11yStrings(tree.body, a11yStrings, atomType);
413e5c31af7Sopenharmony_ci        break;
414e5c31af7Sopenharmony_ci      }
415e5c31af7Sopenharmony_ci
416e5c31af7Sopenharmony_ci    case "spacing":
417e5c31af7Sopenharmony_ci      {
418e5c31af7Sopenharmony_ci        a11yStrings.push("space");
419e5c31af7Sopenharmony_ci        break;
420e5c31af7Sopenharmony_ci      }
421e5c31af7Sopenharmony_ci
422e5c31af7Sopenharmony_ci    case "styling":
423e5c31af7Sopenharmony_ci      {
424e5c31af7Sopenharmony_ci        // We ignore the styling and just pass through the contents
425e5c31af7Sopenharmony_ci        buildA11yStrings(tree.body, a11yStrings, atomType);
426e5c31af7Sopenharmony_ci        break;
427e5c31af7Sopenharmony_ci      }
428e5c31af7Sopenharmony_ci
429e5c31af7Sopenharmony_ci    case "sqrt":
430e5c31af7Sopenharmony_ci      {
431e5c31af7Sopenharmony_ci        buildRegion(a11yStrings, regionStrings => {
432e5c31af7Sopenharmony_ci          const body = tree.body,
433e5c31af7Sopenharmony_ci                index = tree.index;
434e5c31af7Sopenharmony_ci
435e5c31af7Sopenharmony_ci          if (index) {
436e5c31af7Sopenharmony_ci            const indexString = flatten(buildA11yStrings(index, [], atomType)).join(",");
437e5c31af7Sopenharmony_ci
438e5c31af7Sopenharmony_ci            if (indexString === "3") {
439e5c31af7Sopenharmony_ci              regionStrings.push("cube root of");
440e5c31af7Sopenharmony_ci              buildA11yStrings(body, regionStrings, atomType);
441e5c31af7Sopenharmony_ci              regionStrings.push("end cube root");
442e5c31af7Sopenharmony_ci              return;
443e5c31af7Sopenharmony_ci            }
444e5c31af7Sopenharmony_ci
445e5c31af7Sopenharmony_ci            regionStrings.push("root");
446e5c31af7Sopenharmony_ci            regionStrings.push("start index");
447e5c31af7Sopenharmony_ci            buildA11yStrings(index, regionStrings, atomType);
448e5c31af7Sopenharmony_ci            regionStrings.push("end index");
449e5c31af7Sopenharmony_ci            return;
450e5c31af7Sopenharmony_ci          }
451e5c31af7Sopenharmony_ci
452e5c31af7Sopenharmony_ci          regionStrings.push("square root of");
453e5c31af7Sopenharmony_ci          buildA11yStrings(body, regionStrings, atomType);
454e5c31af7Sopenharmony_ci          regionStrings.push("end square root");
455e5c31af7Sopenharmony_ci        });
456e5c31af7Sopenharmony_ci        break;
457e5c31af7Sopenharmony_ci      }
458e5c31af7Sopenharmony_ci
459e5c31af7Sopenharmony_ci    case "supsub":
460e5c31af7Sopenharmony_ci      {
461e5c31af7Sopenharmony_ci        const base = tree.base,
462e5c31af7Sopenharmony_ci              sub = tree.sub,
463e5c31af7Sopenharmony_ci              sup = tree.sup;
464e5c31af7Sopenharmony_ci        let isLog = false;
465e5c31af7Sopenharmony_ci
466e5c31af7Sopenharmony_ci        if (base) {
467e5c31af7Sopenharmony_ci          buildA11yStrings(base, a11yStrings, atomType);
468e5c31af7Sopenharmony_ci          isLog = base.type === "op" && base.name === "\\log";
469e5c31af7Sopenharmony_ci        }
470e5c31af7Sopenharmony_ci
471e5c31af7Sopenharmony_ci        if (sub) {
472e5c31af7Sopenharmony_ci          const regionName = isLog ? "base" : "subscript";
473e5c31af7Sopenharmony_ci          buildRegion(a11yStrings, function (regionStrings) {
474e5c31af7Sopenharmony_ci            regionStrings.push(`start ${regionName}`);
475e5c31af7Sopenharmony_ci            buildA11yStrings(sub, regionStrings, atomType);
476e5c31af7Sopenharmony_ci            regionStrings.push(`end ${regionName}`);
477e5c31af7Sopenharmony_ci          });
478e5c31af7Sopenharmony_ci        }
479e5c31af7Sopenharmony_ci
480e5c31af7Sopenharmony_ci        if (sup) {
481e5c31af7Sopenharmony_ci          buildRegion(a11yStrings, function (regionStrings) {
482e5c31af7Sopenharmony_ci            const supString = flatten(buildA11yStrings(sup, [], atomType)).join(",");
483e5c31af7Sopenharmony_ci
484e5c31af7Sopenharmony_ci            if (supString in powerMap) {
485e5c31af7Sopenharmony_ci              regionStrings.push(powerMap[supString]);
486e5c31af7Sopenharmony_ci              return;
487e5c31af7Sopenharmony_ci            }
488e5c31af7Sopenharmony_ci
489e5c31af7Sopenharmony_ci            regionStrings.push("start superscript");
490e5c31af7Sopenharmony_ci            buildA11yStrings(sup, regionStrings, atomType);
491e5c31af7Sopenharmony_ci            regionStrings.push("end superscript");
492e5c31af7Sopenharmony_ci          });
493e5c31af7Sopenharmony_ci        }
494e5c31af7Sopenharmony_ci
495e5c31af7Sopenharmony_ci        break;
496e5c31af7Sopenharmony_ci      }
497e5c31af7Sopenharmony_ci
498e5c31af7Sopenharmony_ci    case "text":
499e5c31af7Sopenharmony_ci      {
500e5c31af7Sopenharmony_ci        // TODO: handle other fonts
501e5c31af7Sopenharmony_ci        if (tree.font === "\\textbf") {
502e5c31af7Sopenharmony_ci          buildRegion(a11yStrings, function (regionStrings) {
503e5c31af7Sopenharmony_ci            regionStrings.push("start bold text");
504e5c31af7Sopenharmony_ci            buildA11yStrings(tree.body, regionStrings, atomType);
505e5c31af7Sopenharmony_ci            regionStrings.push("end bold text");
506e5c31af7Sopenharmony_ci          });
507e5c31af7Sopenharmony_ci          break;
508e5c31af7Sopenharmony_ci        }
509e5c31af7Sopenharmony_ci
510e5c31af7Sopenharmony_ci        buildRegion(a11yStrings, function (regionStrings) {
511e5c31af7Sopenharmony_ci          regionStrings.push("start text");
512e5c31af7Sopenharmony_ci          buildA11yStrings(tree.body, regionStrings, atomType);
513e5c31af7Sopenharmony_ci          regionStrings.push("end text");
514e5c31af7Sopenharmony_ci        });
515e5c31af7Sopenharmony_ci        break;
516e5c31af7Sopenharmony_ci      }
517e5c31af7Sopenharmony_ci
518e5c31af7Sopenharmony_ci    case "textord":
519e5c31af7Sopenharmony_ci      {
520e5c31af7Sopenharmony_ci        buildString(tree.text, atomType, a11yStrings);
521e5c31af7Sopenharmony_ci        break;
522e5c31af7Sopenharmony_ci      }
523e5c31af7Sopenharmony_ci
524e5c31af7Sopenharmony_ci    case "smash":
525e5c31af7Sopenharmony_ci      {
526e5c31af7Sopenharmony_ci        buildA11yStrings(tree.body, a11yStrings, atomType);
527e5c31af7Sopenharmony_ci        break;
528e5c31af7Sopenharmony_ci      }
529e5c31af7Sopenharmony_ci
530e5c31af7Sopenharmony_ci    case "enclose":
531e5c31af7Sopenharmony_ci      {
532e5c31af7Sopenharmony_ci        // TODO: create a map for these.
533e5c31af7Sopenharmony_ci        // TODO: differentiate between a body with a single atom, e.g.
534e5c31af7Sopenharmony_ci        // "cancel a" instead of "start cancel, a, end cancel"
535e5c31af7Sopenharmony_ci        if (/cancel/.test(tree.label)) {
536e5c31af7Sopenharmony_ci          buildRegion(a11yStrings, function (regionStrings) {
537e5c31af7Sopenharmony_ci            regionStrings.push("start cancel");
538e5c31af7Sopenharmony_ci            buildA11yStrings(tree.body, regionStrings, atomType);
539e5c31af7Sopenharmony_ci            regionStrings.push("end cancel");
540e5c31af7Sopenharmony_ci          });
541e5c31af7Sopenharmony_ci          break;
542e5c31af7Sopenharmony_ci        } else if (/box/.test(tree.label)) {
543e5c31af7Sopenharmony_ci          buildRegion(a11yStrings, function (regionStrings) {
544e5c31af7Sopenharmony_ci            regionStrings.push("start box");
545e5c31af7Sopenharmony_ci            buildA11yStrings(tree.body, regionStrings, atomType);
546e5c31af7Sopenharmony_ci            regionStrings.push("end box");
547e5c31af7Sopenharmony_ci          });
548e5c31af7Sopenharmony_ci          break;
549e5c31af7Sopenharmony_ci        } else if (/sout/.test(tree.label)) {
550e5c31af7Sopenharmony_ci          buildRegion(a11yStrings, function (regionStrings) {
551e5c31af7Sopenharmony_ci            regionStrings.push("start strikeout");
552e5c31af7Sopenharmony_ci            buildA11yStrings(tree.body, regionStrings, atomType);
553e5c31af7Sopenharmony_ci            regionStrings.push("end strikeout");
554e5c31af7Sopenharmony_ci          });
555e5c31af7Sopenharmony_ci          break;
556e5c31af7Sopenharmony_ci        }
557e5c31af7Sopenharmony_ci
558e5c31af7Sopenharmony_ci        throw new Error(`KaTeX-a11y: enclose node with ${tree.label} not supported yet`);
559e5c31af7Sopenharmony_ci      }
560e5c31af7Sopenharmony_ci
561e5c31af7Sopenharmony_ci    case "vphantom":
562e5c31af7Sopenharmony_ci      {
563e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: vphantom not implemented yet");
564e5c31af7Sopenharmony_ci      }
565e5c31af7Sopenharmony_ci
566e5c31af7Sopenharmony_ci    case "hphantom":
567e5c31af7Sopenharmony_ci      {
568e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: hphantom not implemented yet");
569e5c31af7Sopenharmony_ci      }
570e5c31af7Sopenharmony_ci
571e5c31af7Sopenharmony_ci    case "operatorname":
572e5c31af7Sopenharmony_ci      {
573e5c31af7Sopenharmony_ci        buildA11yStrings(tree.body, a11yStrings, atomType);
574e5c31af7Sopenharmony_ci        break;
575e5c31af7Sopenharmony_ci      }
576e5c31af7Sopenharmony_ci
577e5c31af7Sopenharmony_ci    case "array":
578e5c31af7Sopenharmony_ci      {
579e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: array not implemented yet");
580e5c31af7Sopenharmony_ci      }
581e5c31af7Sopenharmony_ci
582e5c31af7Sopenharmony_ci    case "raw":
583e5c31af7Sopenharmony_ci      {
584e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: raw not implemented yet");
585e5c31af7Sopenharmony_ci      }
586e5c31af7Sopenharmony_ci
587e5c31af7Sopenharmony_ci    case "size":
588e5c31af7Sopenharmony_ci      {
589e5c31af7Sopenharmony_ci        // Although there are nodes of type "size" in the parse tree, they have
590e5c31af7Sopenharmony_ci        // no semantic meaning and should be ignored.
591e5c31af7Sopenharmony_ci        break;
592e5c31af7Sopenharmony_ci      }
593e5c31af7Sopenharmony_ci
594e5c31af7Sopenharmony_ci    case "url":
595e5c31af7Sopenharmony_ci      {
596e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: url not implemented yet");
597e5c31af7Sopenharmony_ci      }
598e5c31af7Sopenharmony_ci
599e5c31af7Sopenharmony_ci    case "tag":
600e5c31af7Sopenharmony_ci      {
601e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: tag not implemented yet");
602e5c31af7Sopenharmony_ci      }
603e5c31af7Sopenharmony_ci
604e5c31af7Sopenharmony_ci    case "verb":
605e5c31af7Sopenharmony_ci      {
606e5c31af7Sopenharmony_ci        buildString(`start verbatim`, "normal", a11yStrings);
607e5c31af7Sopenharmony_ci        buildString(tree.body, "normal", a11yStrings);
608e5c31af7Sopenharmony_ci        buildString(`end verbatim`, "normal", a11yStrings);
609e5c31af7Sopenharmony_ci        break;
610e5c31af7Sopenharmony_ci      }
611e5c31af7Sopenharmony_ci
612e5c31af7Sopenharmony_ci    case "environment":
613e5c31af7Sopenharmony_ci      {
614e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: environment not implemented yet");
615e5c31af7Sopenharmony_ci      }
616e5c31af7Sopenharmony_ci
617e5c31af7Sopenharmony_ci    case "horizBrace":
618e5c31af7Sopenharmony_ci      {
619e5c31af7Sopenharmony_ci        buildString(`start ${tree.label.slice(1)}`, "normal", a11yStrings);
620e5c31af7Sopenharmony_ci        buildA11yStrings(tree.base, a11yStrings, atomType);
621e5c31af7Sopenharmony_ci        buildString(`end ${tree.label.slice(1)}`, "normal", a11yStrings);
622e5c31af7Sopenharmony_ci        break;
623e5c31af7Sopenharmony_ci      }
624e5c31af7Sopenharmony_ci
625e5c31af7Sopenharmony_ci    case "infix":
626e5c31af7Sopenharmony_ci      {
627e5c31af7Sopenharmony_ci        // All infix nodes are replace with other nodes.
628e5c31af7Sopenharmony_ci        break;
629e5c31af7Sopenharmony_ci      }
630e5c31af7Sopenharmony_ci
631e5c31af7Sopenharmony_ci    case "includegraphics":
632e5c31af7Sopenharmony_ci      {
633e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: includegraphics not implemented yet");
634e5c31af7Sopenharmony_ci      }
635e5c31af7Sopenharmony_ci
636e5c31af7Sopenharmony_ci    case "font":
637e5c31af7Sopenharmony_ci      {
638e5c31af7Sopenharmony_ci        // TODO: callout the start/end of specific fonts
639e5c31af7Sopenharmony_ci        // TODO: map \BBb{N} to "the naturals" or something like that
640e5c31af7Sopenharmony_ci        buildA11yStrings(tree.body, a11yStrings, atomType);
641e5c31af7Sopenharmony_ci        break;
642e5c31af7Sopenharmony_ci      }
643e5c31af7Sopenharmony_ci
644e5c31af7Sopenharmony_ci    case "href":
645e5c31af7Sopenharmony_ci      {
646e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: href not implemented yet");
647e5c31af7Sopenharmony_ci      }
648e5c31af7Sopenharmony_ci
649e5c31af7Sopenharmony_ci    case "cr":
650e5c31af7Sopenharmony_ci      {
651e5c31af7Sopenharmony_ci        // This is used by environments.
652e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: cr not implemented yet");
653e5c31af7Sopenharmony_ci      }
654e5c31af7Sopenharmony_ci
655e5c31af7Sopenharmony_ci    case "underline":
656e5c31af7Sopenharmony_ci      {
657e5c31af7Sopenharmony_ci        buildRegion(a11yStrings, function (a11yStrings) {
658e5c31af7Sopenharmony_ci          a11yStrings.push("start underline");
659e5c31af7Sopenharmony_ci          buildA11yStrings(tree.body, a11yStrings, atomType);
660e5c31af7Sopenharmony_ci          a11yStrings.push("end underline");
661e5c31af7Sopenharmony_ci        });
662e5c31af7Sopenharmony_ci        break;
663e5c31af7Sopenharmony_ci      }
664e5c31af7Sopenharmony_ci
665e5c31af7Sopenharmony_ci    case "xArrow":
666e5c31af7Sopenharmony_ci      {
667e5c31af7Sopenharmony_ci        throw new Error("KaTeX-a11y: xArrow not implemented yet");
668e5c31af7Sopenharmony_ci      }
669e5c31af7Sopenharmony_ci
670e5c31af7Sopenharmony_ci    case "mclass":
671e5c31af7Sopenharmony_ci      {
672e5c31af7Sopenharmony_ci        // \neq and \ne are macros so we let "htmlmathml" render the mathmal
673e5c31af7Sopenharmony_ci        // side of things and extract the text from that.
674e5c31af7Sopenharmony_ci        const atomType = tree.mclass.slice(1); // $FlowFixMe: drop the leading "m" from the values in mclass
675e5c31af7Sopenharmony_ci
676e5c31af7Sopenharmony_ci        buildA11yStrings(tree.body, a11yStrings, atomType);
677e5c31af7Sopenharmony_ci        break;
678e5c31af7Sopenharmony_ci      }
679e5c31af7Sopenharmony_ci
680e5c31af7Sopenharmony_ci    case "mathchoice":
681e5c31af7Sopenharmony_ci      {
682e5c31af7Sopenharmony_ci        // TODO: track which which style we're using, e.g. dispaly, text, etc.
683e5c31af7Sopenharmony_ci        // default to text style if even that may not be the correct style
684e5c31af7Sopenharmony_ci        buildA11yStrings(tree.text, a11yStrings, atomType);
685e5c31af7Sopenharmony_ci        break;
686e5c31af7Sopenharmony_ci      }
687e5c31af7Sopenharmony_ci
688e5c31af7Sopenharmony_ci    case "htmlmathml":
689e5c31af7Sopenharmony_ci      {
690e5c31af7Sopenharmony_ci        buildA11yStrings(tree.mathml, a11yStrings, atomType);
691e5c31af7Sopenharmony_ci        break;
692e5c31af7Sopenharmony_ci      }
693e5c31af7Sopenharmony_ci
694e5c31af7Sopenharmony_ci    case "middle":
695e5c31af7Sopenharmony_ci      {
696e5c31af7Sopenharmony_ci        buildString(tree.delim, atomType, a11yStrings);
697e5c31af7Sopenharmony_ci        break;
698e5c31af7Sopenharmony_ci      }
699e5c31af7Sopenharmony_ci
700e5c31af7Sopenharmony_ci    default:
701e5c31af7Sopenharmony_ci      tree.type;
702e5c31af7Sopenharmony_ci      throw new Error("KaTeX a11y un-recognized type: " + tree.type);
703e5c31af7Sopenharmony_ci  }
704e5c31af7Sopenharmony_ci};
705e5c31af7Sopenharmony_ci
706e5c31af7Sopenharmony_ciconst buildA11yStrings = function buildA11yStrings(tree, a11yStrings, atomType) {
707e5c31af7Sopenharmony_ci  if (a11yStrings === void 0) {
708e5c31af7Sopenharmony_ci    a11yStrings = [];
709e5c31af7Sopenharmony_ci  }
710e5c31af7Sopenharmony_ci
711e5c31af7Sopenharmony_ci  if (tree instanceof Array) {
712e5c31af7Sopenharmony_ci    for (let i = 0; i < tree.length; i++) {
713e5c31af7Sopenharmony_ci      buildA11yStrings(tree[i], a11yStrings, atomType);
714e5c31af7Sopenharmony_ci    }
715e5c31af7Sopenharmony_ci  } else {
716e5c31af7Sopenharmony_ci    handleObject(tree, a11yStrings, atomType);
717e5c31af7Sopenharmony_ci  }
718e5c31af7Sopenharmony_ci
719e5c31af7Sopenharmony_ci  return a11yStrings;
720e5c31af7Sopenharmony_ci};
721e5c31af7Sopenharmony_ci
722e5c31af7Sopenharmony_ciconst flatten = function flatten(array) {
723e5c31af7Sopenharmony_ci  let result = [];
724e5c31af7Sopenharmony_ci  array.forEach(function (item) {
725e5c31af7Sopenharmony_ci    if (item instanceof Array) {
726e5c31af7Sopenharmony_ci      result = result.concat(flatten(item));
727e5c31af7Sopenharmony_ci    } else {
728e5c31af7Sopenharmony_ci      result.push(item);
729e5c31af7Sopenharmony_ci    }
730e5c31af7Sopenharmony_ci  });
731e5c31af7Sopenharmony_ci  return result;
732e5c31af7Sopenharmony_ci};
733e5c31af7Sopenharmony_ci
734e5c31af7Sopenharmony_ciconst renderA11yString = function renderA11yString(text, settings) {
735e5c31af7Sopenharmony_ci  const tree = katex.__parse(text, settings);
736e5c31af7Sopenharmony_ci
737e5c31af7Sopenharmony_ci  const a11yStrings = buildA11yStrings(tree, [], "normal");
738e5c31af7Sopenharmony_ci  return flatten(a11yStrings).join(", ");
739e5c31af7Sopenharmony_ci};
740e5c31af7Sopenharmony_ci
741e5c31af7Sopenharmony_ciexport default renderA11yString;
742