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