1 |
|
2 | import buildCommon from "../buildCommon";
|
3 | import defineEnvironment from "../defineEnvironment";
|
4 | import defineFunction from "../defineFunction";
|
5 | import mathMLTree from "../mathMLTree";
|
6 | import ParseError from "../ParseError";
|
7 | import {assertNodeType, assertSymbolNodeType} from "../parseNode";
|
8 | import {checkNodeType, checkSymbolNodeType} from "../parseNode";
|
9 | import {calculateSize} from "../units";
|
10 | import utils from "../utils";
|
11 |
|
12 | import * as html from "../buildHTML";
|
13 | import * as mml from "../buildMathML";
|
14 |
|
15 | import type Parser from "../Parser";
|
16 | import type {ParseNode, AnyParseNode} from "../parseNode";
|
17 | import type {StyleStr} from "../types";
|
18 | import type {HtmlBuilder, MathMLBuilder} from "../defineFunction";
|
19 |
|
20 |
|
21 | export type AlignSpec = { type: "separator", separator: string } | {
|
22 | type: "align",
|
23 | align: string,
|
24 | pregap?: number,
|
25 | postgap?: number,
|
26 | };
|
27 |
|
28 |
|
29 | export type ColSeparationType = "align" | "alignat";
|
30 |
|
31 | function getHLines(parser: Parser): boolean[] {
|
32 |
|
33 |
|
34 | const hlineInfo = [];
|
35 | parser.consumeSpaces();
|
36 | let nxt = parser.nextToken.text;
|
37 | while (nxt === "\\hline" || nxt === "\\hdashline") {
|
38 | parser.consume();
|
39 | hlineInfo.push(nxt === "\\hdashline");
|
40 | parser.consumeSpaces();
|
41 | nxt = parser.nextToken.text;
|
42 | }
|
43 | return hlineInfo;
|
44 | }
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 | function parseArray(
|
53 | parser: Parser,
|
54 | {hskipBeforeAndAfter, addJot, cols, arraystretch, colSeparationType}: {|
|
55 | hskipBeforeAndAfter?: boolean,
|
56 | addJot?: boolean,
|
57 | cols?: AlignSpec[],
|
58 | arraystretch?: number,
|
59 | colSeparationType?: ColSeparationType,
|
60 | |},
|
61 | style: StyleStr,
|
62 | ): ParseNode<"array"> {
|
63 |
|
64 | parser.gullet.beginGroup();
|
65 | parser.gullet.macros.set("\\\\", "\\cr");
|
66 |
|
67 |
|
68 | if (!arraystretch) {
|
69 | const stretch = parser.gullet.expandMacroAsText("\\arraystretch");
|
70 | if (stretch == null) {
|
71 |
|
72 | arraystretch = 1;
|
73 | } else {
|
74 | arraystretch = parseFloat(stretch);
|
75 | if (!arraystretch || arraystretch < 0) {
|
76 | throw new ParseError(`Invalid \\arraystretch: ${stretch}`);
|
77 | }
|
78 | }
|
79 | }
|
80 |
|
81 | let row = [];
|
82 | const body = [row];
|
83 | const rowGaps = [];
|
84 | const hLinesBeforeRow = [];
|
85 |
|
86 |
|
87 | hLinesBeforeRow.push(getHLines(parser));
|
88 |
|
89 | while (true) {
|
90 | let cell = parser.parseExpression(false, "\\cr");
|
91 | cell = {
|
92 | type: "ordgroup",
|
93 | mode: parser.mode,
|
94 | body: cell,
|
95 | };
|
96 | if (style) {
|
97 | cell = {
|
98 | type: "styling",
|
99 | mode: parser.mode,
|
100 | style,
|
101 | body: [cell],
|
102 | };
|
103 | }
|
104 | row.push(cell);
|
105 | const next = parser.nextToken.text;
|
106 | if (next === "&") {
|
107 | parser.consume();
|
108 | } else if (next === "\\end") {
|
109 |
|
110 |
|
111 |
|
112 | if (row.length === 1 && cell.type === "styling" &&
|
113 | cell.body[0].body.length === 0) {
|
114 | body.pop();
|
115 | }
|
116 | if (hLinesBeforeRow.length < body.length + 1) {
|
117 | hLinesBeforeRow.push([]);
|
118 | }
|
119 | break;
|
120 | } else if (next === "\\cr") {
|
121 | const cr = assertNodeType(parser.parseFunction(), "cr");
|
122 | rowGaps.push(cr.size);
|
123 |
|
124 |
|
125 | hLinesBeforeRow.push(getHLines(parser));
|
126 |
|
127 | row = [];
|
128 | body.push(row);
|
129 | } else {
|
130 | throw new ParseError("Expected & or \\\\ or \\cr or \\end",
|
131 | parser.nextToken);
|
132 | }
|
133 | }
|
134 | parser.gullet.endGroup();
|
135 | return {
|
136 | type: "array",
|
137 | mode: parser.mode,
|
138 | addJot,
|
139 | arraystretch,
|
140 | body,
|
141 | cols,
|
142 | rowGaps,
|
143 | hskipBeforeAndAfter,
|
144 | hLinesBeforeRow,
|
145 | colSeparationType,
|
146 | };
|
147 | }
|
148 |
|
149 |
|
150 |
|
151 |
|
152 | function dCellStyle(envName): StyleStr {
|
153 | if (envName.substr(0, 1) === "d") {
|
154 | return "display";
|
155 | } else {
|
156 | return "text";
|
157 | }
|
158 | }
|
159 |
|
160 | type Outrow = {
|
161 | [idx: number]: *,
|
162 | height: number,
|
163 | depth: number,
|
164 | pos: number,
|
165 | };
|
166 |
|
167 | const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
168 | let r;
|
169 | let c;
|
170 | const nr = group.body.length;
|
171 | const hLinesBeforeRow = group.hLinesBeforeRow;
|
172 | let nc = 0;
|
173 | let body = new Array(nr);
|
174 | const hlines = [];
|
175 |
|
176 |
|
177 | const pt = 1 / options.fontMetrics().ptPerEm;
|
178 | const arraycolsep = 5 * pt;
|
179 |
|
180 |
|
181 | const baselineskip = 12 * pt;
|
182 |
|
183 |
|
184 | const jot = 3 * pt;
|
185 | const arrayskip = group.arraystretch * baselineskip;
|
186 | const arstrutHeight = 0.7 * arrayskip;
|
187 | const arstrutDepth = 0.3 * arrayskip;
|
188 |
|
189 | let totalHeight = 0;
|
190 |
|
191 |
|
192 | function setHLinePos(hlinesInGap: boolean[]) {
|
193 | for (let i = 0; i < hlinesInGap.length; ++i) {
|
194 | if (i > 0) {
|
195 | totalHeight += 0.25;
|
196 | }
|
197 | hlines.push({pos: totalHeight, isDashed: hlinesInGap[i]});
|
198 | }
|
199 | }
|
200 | setHLinePos(hLinesBeforeRow[0]);
|
201 |
|
202 | for (r = 0; r < group.body.length; ++r) {
|
203 | const inrow = group.body[r];
|
204 | let height = arstrutHeight;
|
205 | let depth = arstrutDepth;
|
206 |
|
207 | if (nc < inrow.length) {
|
208 | nc = inrow.length;
|
209 | }
|
210 |
|
211 | const outrow: Outrow = (new Array(inrow.length): any);
|
212 | for (c = 0; c < inrow.length; ++c) {
|
213 | const elt = html.buildGroup(inrow[c], options);
|
214 | if (depth < elt.depth) {
|
215 | depth = elt.depth;
|
216 | }
|
217 | if (height < elt.height) {
|
218 | height = elt.height;
|
219 | }
|
220 | outrow[c] = elt;
|
221 | }
|
222 |
|
223 | const rowGap = group.rowGaps[r];
|
224 | let gap = 0;
|
225 | if (rowGap) {
|
226 | gap = calculateSize(rowGap, options);
|
227 | if (gap > 0) {
|
228 | gap += arstrutDepth;
|
229 | if (depth < gap) {
|
230 | depth = gap;
|
231 | }
|
232 | gap = 0;
|
233 | }
|
234 | }
|
235 |
|
236 |
|
237 |
|
238 | if (group.addJot) {
|
239 | depth += jot;
|
240 | }
|
241 |
|
242 | outrow.height = height;
|
243 | outrow.depth = depth;
|
244 | totalHeight += height;
|
245 | outrow.pos = totalHeight;
|
246 | totalHeight += depth + gap;
|
247 | body[r] = outrow;
|
248 |
|
249 |
|
250 | setHLinePos(hLinesBeforeRow[r + 1]);
|
251 | }
|
252 |
|
253 | const offset = totalHeight / 2 + options.fontMetrics().axisHeight;
|
254 | const colDescriptions = group.cols || [];
|
255 | const cols = [];
|
256 | let colSep;
|
257 | let colDescrNum;
|
258 | for (c = 0, colDescrNum = 0;
|
259 |
|
260 |
|
261 | c < nc || colDescrNum < colDescriptions.length;
|
262 | ++c, ++colDescrNum) {
|
263 |
|
264 | let colDescr = colDescriptions[colDescrNum] || {};
|
265 |
|
266 | let firstSeparator = true;
|
267 | while (colDescr.type === "separator") {
|
268 |
|
269 |
|
270 | if (!firstSeparator) {
|
271 | colSep = buildCommon.makeSpan(["arraycolsep"], []);
|
272 | colSep.style.width =
|
273 | options.fontMetrics().doubleRuleSep + "em";
|
274 | cols.push(colSep);
|
275 | }
|
276 |
|
277 | if (colDescr.separator === "|") {
|
278 | const separator = buildCommon.makeSpan(
|
279 | ["vertical-separator"], [], options
|
280 | );
|
281 | separator.style.height = totalHeight + "em";
|
282 | separator.style.verticalAlign =
|
283 | -(totalHeight - offset) + "em";
|
284 |
|
285 | cols.push(separator);
|
286 | } else if (colDescr.separator === ":") {
|
287 | const separator = buildCommon.makeSpan(
|
288 | ["vertical-separator", "vs-dashed"], [], options
|
289 | );
|
290 | separator.style.height = totalHeight + "em";
|
291 | separator.style.verticalAlign =
|
292 | -(totalHeight - offset) + "em";
|
293 |
|
294 | cols.push(separator);
|
295 | } else {
|
296 | throw new ParseError(
|
297 | "Invalid separator type: " + colDescr.separator);
|
298 | }
|
299 |
|
300 | colDescrNum++;
|
301 | colDescr = colDescriptions[colDescrNum] || {};
|
302 | firstSeparator = false;
|
303 | }
|
304 |
|
305 | if (c >= nc) {
|
306 | continue;
|
307 | }
|
308 |
|
309 | let sepwidth;
|
310 | if (c > 0 || group.hskipBeforeAndAfter) {
|
311 | sepwidth = utils.deflt(colDescr.pregap, arraycolsep);
|
312 | if (sepwidth !== 0) {
|
313 | colSep = buildCommon.makeSpan(["arraycolsep"], []);
|
314 | colSep.style.width = sepwidth + "em";
|
315 | cols.push(colSep);
|
316 | }
|
317 | }
|
318 |
|
319 | let col = [];
|
320 | for (r = 0; r < nr; ++r) {
|
321 | const row = body[r];
|
322 | const elem = row[c];
|
323 | if (!elem) {
|
324 | continue;
|
325 | }
|
326 | const shift = row.pos - offset;
|
327 | elem.depth = row.depth;
|
328 | elem.height = row.height;
|
329 | col.push({type: "elem", elem: elem, shift: shift});
|
330 | }
|
331 |
|
332 | col = buildCommon.makeVList({
|
333 | positionType: "individualShift",
|
334 | children: col,
|
335 | }, options);
|
336 | col = buildCommon.makeSpan(
|
337 | ["col-align-" + (colDescr.align || "c")],
|
338 | [col]);
|
339 | cols.push(col);
|
340 |
|
341 | if (c < nc - 1 || group.hskipBeforeAndAfter) {
|
342 | sepwidth = utils.deflt(colDescr.postgap, arraycolsep);
|
343 | if (sepwidth !== 0) {
|
344 | colSep = buildCommon.makeSpan(["arraycolsep"], []);
|
345 | colSep.style.width = sepwidth + "em";
|
346 | cols.push(colSep);
|
347 | }
|
348 | }
|
349 | }
|
350 | body = buildCommon.makeSpan(["mtable"], cols);
|
351 |
|
352 |
|
353 | if (hlines.length > 0) {
|
354 | const line = buildCommon.makeLineSpan("hline", options, 0.05);
|
355 | const dashes = buildCommon.makeLineSpan("hdashline", options, 0.05);
|
356 | const vListElems = [{type: "elem", elem: body, shift: 0}];
|
357 | while (hlines.length > 0) {
|
358 | const hline = hlines.pop();
|
359 | const lineShift = hline.pos - offset;
|
360 | if (hline.isDashed) {
|
361 | vListElems.push({type: "elem", elem: dashes, shift: lineShift});
|
362 | } else {
|
363 | vListElems.push({type: "elem", elem: line, shift: lineShift});
|
364 | }
|
365 | }
|
366 | body = buildCommon.makeVList({
|
367 | positionType: "individualShift",
|
368 | children: vListElems,
|
369 | }, options);
|
370 | }
|
371 |
|
372 | return buildCommon.makeSpan(["mord"], [body], options);
|
373 | };
|
374 |
|
375 | const alignMap = {
|
376 | c: "center ",
|
377 | l: "left ",
|
378 | r: "right ",
|
379 | };
|
380 |
|
381 | const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
|
382 | const table = new mathMLTree.MathNode(
|
383 | "mtable", group.body.map(function(row) {
|
384 | return new mathMLTree.MathNode(
|
385 | "mtr", row.map(function(cell) {
|
386 | return new mathMLTree.MathNode(
|
387 | "mtd", [mml.buildGroup(cell, options)]);
|
388 | }));
|
389 | }));
|
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 |
|
403 |
|
404 | const gap = 0.16 + group.arraystretch - 1 + (group.addJot ? 0.09 : 0);
|
405 | table.setAttribute("rowspacing", gap + "em");
|
406 |
|
407 |
|
408 |
|
409 | let menclose = "";
|
410 | let align = "";
|
411 |
|
412 | if (group.cols) {
|
413 |
|
414 | const cols = group.cols;
|
415 | let columnLines = "";
|
416 | let prevTypeWasAlign = false;
|
417 | let iStart = 0;
|
418 | let iEnd = cols.length;
|
419 |
|
420 | if (cols[0].type === "separator") {
|
421 | menclose += "top ";
|
422 | iStart = 1;
|
423 | }
|
424 | if (cols[cols.length - 1].type === "separator") {
|
425 | menclose += "bottom ";
|
426 | iEnd -= 1;
|
427 | }
|
428 |
|
429 | for (let i = iStart; i < iEnd; i++) {
|
430 | if (cols[i].type === "align") {
|
431 | align += alignMap[cols[i].align];
|
432 |
|
433 | if (prevTypeWasAlign) {
|
434 | columnLines += "none ";
|
435 | }
|
436 | prevTypeWasAlign = true;
|
437 | } else if (cols[i].type === "separator") {
|
438 |
|
439 |
|
440 | if (prevTypeWasAlign) {
|
441 | columnLines += cols[i].separator === "|"
|
442 | ? "solid "
|
443 | : "dashed ";
|
444 | prevTypeWasAlign = false;
|
445 | }
|
446 | }
|
447 | }
|
448 |
|
449 | table.setAttribute("columnalign", align.trim());
|
450 |
|
451 | if (/[sd]/.test(columnLines)) {
|
452 | table.setAttribute("columnlines", columnLines.trim());
|
453 | }
|
454 | }
|
455 |
|
456 |
|
457 | if (group.colSeparationType === "align") {
|
458 | const cols = group.cols || [];
|
459 | let spacing = "";
|
460 | for (let i = 1; i < cols.length; i++) {
|
461 | spacing += i % 2 ? "0em " : "1em ";
|
462 | }
|
463 | table.setAttribute("columnspacing", spacing.trim());
|
464 | } else if (group.colSeparationType === "alignat") {
|
465 | table.setAttribute("columnspacing", "0em");
|
466 | } else {
|
467 | table.setAttribute("columnspacing", "1em");
|
468 | }
|
469 |
|
470 |
|
471 | let rowLines = "";
|
472 | const hlines = group.hLinesBeforeRow;
|
473 |
|
474 | menclose += hlines[0].length > 0 ? "left " : "";
|
475 | menclose += hlines[hlines.length - 1].length > 0 ? "right " : "";
|
476 |
|
477 | for (let i = 1; i < hlines.length - 1; i++) {
|
478 | rowLines += (hlines[i].length === 0)
|
479 | ? "none "
|
480 |
|
481 | : hlines[i][0] ? "dashed " : "solid ";
|
482 | }
|
483 | if (/[sd]/.test(rowLines)) {
|
484 | table.setAttribute("rowlines", rowLines.trim());
|
485 | }
|
486 |
|
487 | if (menclose === "") {
|
488 | return table;
|
489 | } else {
|
490 | const wrapper = new mathMLTree.MathNode("menclose", [table]);
|
491 | wrapper.setAttribute("notation", menclose.trim());
|
492 | return wrapper;
|
493 | }
|
494 | };
|
495 |
|
496 |
|
497 | const alignedHandler = function(context, args) {
|
498 | const cols = [];
|
499 | const res = parseArray(context.parser, {cols, addJot: true}, "display");
|
500 |
|
501 |
|
502 |
|
503 |
|
504 |
|
505 |
|
506 |
|
507 |
|
508 |
|
509 |
|
510 | let numMaths;
|
511 | let numCols = 0;
|
512 | const emptyGroup = {
|
513 | type: "ordgroup",
|
514 | mode: context.mode,
|
515 | body: [],
|
516 | };
|
517 | const ordgroup = checkNodeType(args[0], "ordgroup");
|
518 | if (ordgroup) {
|
519 | let arg0 = "";
|
520 | for (let i = 0; i < ordgroup.body.length; i++) {
|
521 | const textord = assertNodeType(ordgroup.body[i], "textord");
|
522 | arg0 += textord.text;
|
523 | }
|
524 | numMaths = Number(arg0);
|
525 | numCols = numMaths * 2;
|
526 | }
|
527 | const isAligned = !numCols;
|
528 | res.body.forEach(function(row) {
|
529 | for (let i = 1; i < row.length; i += 2) {
|
530 |
|
531 | const styling = assertNodeType(row[i], "styling");
|
532 | const ordgroup = assertNodeType(styling.body[0], "ordgroup");
|
533 | ordgroup.body.unshift(emptyGroup);
|
534 | }
|
535 | if (!isAligned) {
|
536 | const curMaths = row.length / 2;
|
537 | if (numMaths < curMaths) {
|
538 | throw new ParseError(
|
539 | "Too many math in a row: " +
|
540 | `expected ${numMaths}, but got ${curMaths}`,
|
541 | row[0]);
|
542 | }
|
543 | } else if (numCols < row.length) {
|
544 | numCols = row.length;
|
545 | }
|
546 | });
|
547 |
|
548 |
|
549 |
|
550 |
|
551 | for (let i = 0; i < numCols; ++i) {
|
552 | let align = "r";
|
553 | let pregap = 0;
|
554 | if (i % 2 === 1) {
|
555 | align = "l";
|
556 | } else if (i > 0 && isAligned) {
|
557 | pregap = 1;
|
558 | }
|
559 | cols[i] = {
|
560 | type: "align",
|
561 | align: align,
|
562 | pregap: pregap,
|
563 | postgap: 0,
|
564 | };
|
565 | }
|
566 | res.colSeparationType = isAligned ? "align" : "alignat";
|
567 | return res;
|
568 | };
|
569 |
|
570 |
|
571 |
|
572 |
|
573 |
|
574 | defineEnvironment({
|
575 | type: "array",
|
576 | names: ["array", "darray"],
|
577 | props: {
|
578 | numArgs: 1,
|
579 | },
|
580 | handler(context, args) {
|
581 |
|
582 |
|
583 |
|
584 |
|
585 | const symNode = checkSymbolNodeType(args[0]);
|
586 | const colalign: AnyParseNode[] =
|
587 | symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body;
|
588 | const cols = colalign.map(function(nde) {
|
589 | const node = assertSymbolNodeType(nde);
|
590 | const ca = node.text;
|
591 | if ("lcr".indexOf(ca) !== -1) {
|
592 | return {
|
593 | type: "align",
|
594 | align: ca,
|
595 | };
|
596 | } else if (ca === "|") {
|
597 | return {
|
598 | type: "separator",
|
599 | separator: "|",
|
600 | };
|
601 | } else if (ca === ":") {
|
602 | return {
|
603 | type: "separator",
|
604 | separator: ":",
|
605 | };
|
606 | }
|
607 | throw new ParseError("Unknown column alignment: " + ca, nde);
|
608 | });
|
609 | const res = {
|
610 | cols,
|
611 | hskipBeforeAndAfter: true,
|
612 | };
|
613 | return parseArray(context.parser, res, dCellStyle(context.envName));
|
614 | },
|
615 | htmlBuilder,
|
616 | mathmlBuilder,
|
617 | });
|
618 |
|
619 |
|
620 |
|
621 | defineEnvironment({
|
622 | type: "array",
|
623 | names: [
|
624 | "matrix",
|
625 | "pmatrix",
|
626 | "bmatrix",
|
627 | "Bmatrix",
|
628 | "vmatrix",
|
629 | "Vmatrix",
|
630 | ],
|
631 | props: {
|
632 | numArgs: 0,
|
633 | },
|
634 | handler(context) {
|
635 | const delimiters = {
|
636 | "matrix": null,
|
637 | "pmatrix": ["(", ")"],
|
638 | "bmatrix": ["[", "]"],
|
639 | "Bmatrix": ["\\{", "\\}"],
|
640 | "vmatrix": ["|", "|"],
|
641 | "Vmatrix": ["\\Vert", "\\Vert"],
|
642 | }[context.envName];
|
643 |
|
644 | const payload = {hskipBeforeAndAfter: false};
|
645 | const res: ParseNode<"array"> =
|
646 | parseArray(context.parser, payload, dCellStyle(context.envName));
|
647 | return delimiters ? {
|
648 | type: "leftright",
|
649 | mode: context.mode,
|
650 | body: [res],
|
651 | left: delimiters[0],
|
652 | right: delimiters[1],
|
653 | } : res;
|
654 | },
|
655 | htmlBuilder,
|
656 | mathmlBuilder,
|
657 | });
|
658 |
|
659 |
|
660 |
|
661 |
|
662 |
|
663 |
|
664 | defineEnvironment({
|
665 | type: "array",
|
666 | names: [
|
667 | "cases",
|
668 | "dcases",
|
669 | ],
|
670 | props: {
|
671 | numArgs: 0,
|
672 | },
|
673 | handler(context) {
|
674 | const payload = {
|
675 | arraystretch: 1.2,
|
676 | cols: [{
|
677 | type: "align",
|
678 | align: "l",
|
679 | pregap: 0,
|
680 |
|
681 |
|
682 |
|
683 |
|
684 | postgap: 1.0,
|
685 | }, {
|
686 | type: "align",
|
687 | align: "l",
|
688 | pregap: 0,
|
689 | postgap: 0,
|
690 | }],
|
691 | };
|
692 | const res: ParseNode<"array"> =
|
693 | parseArray(context.parser, payload, dCellStyle(context.envName));
|
694 | return {
|
695 | type: "leftright",
|
696 | mode: context.mode,
|
697 | body: [res],
|
698 | left: "\\{",
|
699 | right: ".",
|
700 | };
|
701 | },
|
702 | htmlBuilder,
|
703 | mathmlBuilder,
|
704 | });
|
705 |
|
706 |
|
707 |
|
708 |
|
709 |
|
710 | defineEnvironment({
|
711 | type: "array",
|
712 | names: ["aligned"],
|
713 | props: {
|
714 | numArgs: 0,
|
715 | },
|
716 | handler: alignedHandler,
|
717 | htmlBuilder,
|
718 | mathmlBuilder,
|
719 | });
|
720 |
|
721 |
|
722 |
|
723 |
|
724 | defineEnvironment({
|
725 | type: "array",
|
726 | names: ["gathered"],
|
727 | props: {
|
728 | numArgs: 0,
|
729 | },
|
730 | handler(context) {
|
731 | const res = {
|
732 | cols: [{
|
733 | type: "align",
|
734 | align: "c",
|
735 | }],
|
736 | addJot: true,
|
737 | };
|
738 | return parseArray(context.parser, res, "display");
|
739 | },
|
740 | htmlBuilder,
|
741 | mathmlBuilder,
|
742 | });
|
743 |
|
744 |
|
745 |
|
746 |
|
747 | defineEnvironment({
|
748 | type: "array",
|
749 | names: ["alignedat"],
|
750 |
|
751 |
|
752 |
|
753 | props: {
|
754 | numArgs: 1,
|
755 | },
|
756 | handler: alignedHandler,
|
757 | htmlBuilder,
|
758 | mathmlBuilder,
|
759 | });
|
760 |
|
761 |
|
762 | defineFunction({
|
763 | type: "text",
|
764 | names: ["\\hline", "\\hdashline"],
|
765 | props: {
|
766 | numArgs: 0,
|
767 | allowedInText: true,
|
768 | allowedInMath: true,
|
769 | },
|
770 | handler(context, args) {
|
771 | throw new ParseError(
|
772 | `${context.funcName} valid only within array environment`);
|
773 | },
|
774 | });
|