AmsItems.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2009-2022 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview StackItems needed for parsing AMS math commands.
  19. *
  20. * @author v.sorge@mathjax.org (Volker Sorge)
  21. */
  22. import {ArrayItem, EqnArrayItem} from '../base/BaseItems.js';
  23. import ParseUtil from '../ParseUtil.js';
  24. import NodeUtil from '../NodeUtil.js';
  25. import TexError from '../TexError.js';
  26. import {TexConstant} from '../TexConstants.js';
  27. import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
  28. /**
  29. * Item dealing with multiline environments as a special case of arrays. Note,
  30. * that all other AMS equation environments (e.g., align, split) can be handled
  31. * by the regular EqnArrayItem class.
  32. *
  33. * Handles tagging information according to the given tagging style.
  34. */
  35. export class MultlineItem extends ArrayItem {
  36. /**
  37. * @override
  38. */
  39. constructor(factory: any, ...args: any[]) {
  40. super(factory);
  41. this.factory.configuration.tags.start('multline', true, args[0]);
  42. }
  43. /**
  44. * @override
  45. */
  46. get kind() {
  47. return 'multline';
  48. }
  49. /**
  50. * @override
  51. */
  52. public EndEntry() {
  53. if (this.table.length) {
  54. ParseUtil.fixInitialMO(this.factory.configuration, this.nodes);
  55. }
  56. const shove = this.getProperty('shove');
  57. const mtd = this.create('node',
  58. 'mtd', this.nodes, shove ? {columnalign: shove} : {});
  59. this.setProperty('shove', null);
  60. this.row.push(mtd);
  61. this.Clear();
  62. }
  63. /**
  64. * @override
  65. */
  66. public EndRow() {
  67. if (this.row.length !== 1) {
  68. // @test MultlineRowsOneCol
  69. throw new TexError(
  70. 'MultlineRowsOneCol',
  71. 'The rows within the %1 environment must have exactly one column',
  72. 'multline');
  73. }
  74. let row = this.create('node', 'mtr', this.row);
  75. this.table.push(row);
  76. this.row = [];
  77. }
  78. /**
  79. * @override
  80. */
  81. public EndTable() {
  82. super.EndTable();
  83. if (this.table.length) {
  84. let m = this.table.length - 1, label = -1;
  85. if (!NodeUtil.getAttribute(
  86. NodeUtil.getChildren(this.table[0])[0], 'columnalign')) {
  87. NodeUtil.setAttribute(NodeUtil.getChildren(this.table[0])[0],
  88. 'columnalign', TexConstant.Align.LEFT);
  89. }
  90. if (!NodeUtil.getAttribute(
  91. NodeUtil.getChildren(this.table[m])[0], 'columnalign')) {
  92. NodeUtil.setAttribute(NodeUtil.getChildren(this.table[m])[0],
  93. 'columnalign', TexConstant.Align.RIGHT);
  94. }
  95. let tag = this.factory.configuration.tags.getTag();
  96. if (tag) {
  97. label = (this.arraydef.side === TexConstant.Align.LEFT ? 0 : this.table.length - 1);
  98. const mtr = this.table[label];
  99. const mlabel = this.create('node', 'mlabeledtr',
  100. [tag].concat(NodeUtil.getChildren(mtr)));
  101. NodeUtil.copyAttributes(mtr, mlabel);
  102. this.table[label] = mlabel;
  103. }
  104. }
  105. this.factory.configuration.tags.end();
  106. }
  107. }
  108. /**
  109. * StackItem for handling flalign, xalignat, and xxalignat environments.
  110. */
  111. export class FlalignItem extends EqnArrayItem {
  112. /**
  113. * @override
  114. */
  115. get kind() {
  116. return 'flalign';
  117. }
  118. /**
  119. * @override
  120. */
  121. constructor(factory: any, public name: string, public numbered: boolean,
  122. public padded: boolean, public center: boolean) {
  123. super(factory);
  124. this.factory.configuration.tags.start(name, numbered, numbered);
  125. }
  126. /**
  127. * @override
  128. */
  129. public EndEntry() {
  130. super.EndEntry();
  131. const n = this.getProperty('xalignat') as number;
  132. if (!n) return;
  133. if (this.row.length > n) {
  134. throw new TexError('XalignOverflow', 'Extra %1 in row of %2', '&', this.name);
  135. }
  136. }
  137. /**
  138. * @override
  139. */
  140. public EndRow() {
  141. let cell: MmlNode;
  142. let row = this.row;
  143. //
  144. // For xalignat and xxalignat, pad the row to the expected number if it is too short.
  145. //
  146. const n = this.getProperty('xalignat') as number;
  147. while (row.length < n) {
  148. row.push(this.create('node', 'mtd'));
  149. }
  150. //
  151. // Insert padding cells between pairs of entries, as needed for "fit" columns,
  152. // and include initial and end cells if that is needed.
  153. //
  154. this.row = [];
  155. if (this.padded) {
  156. this.row.push(this.create('node', 'mtd'));
  157. }
  158. while ((cell = row.shift())) {
  159. this.row.push(cell);
  160. cell = row.shift();
  161. if (cell) this.row.push(cell);
  162. if (row.length || this.padded) {
  163. this.row.push(this.create('node', 'mtd'));
  164. }
  165. }
  166. //
  167. if (this.row.length > this.maxrow) {
  168. this.maxrow = this.row.length;
  169. }
  170. super.EndRow();
  171. //
  172. // For full-width environments with labels that aren't supposed to take up space,
  173. // move the label into a zero-width mpadded element that laps in the proper direction.
  174. //
  175. const mtr = this.table[this.table.length - 1];
  176. if (this.getProperty('zeroWidthLabel') && mtr.isKind('mlabeledtr')) {
  177. const mtd = NodeUtil.getChildren(mtr)[0];
  178. const side = this.factory.configuration.options['tagSide'];
  179. const def = {width: 0, ...(side === 'right' ? {lspace: '-1width'} : {})};
  180. const mpadded = this.create('node', 'mpadded', NodeUtil.getChildren(mtd), def);
  181. mtd.setChildren([mpadded]);
  182. }
  183. }
  184. /**
  185. * @override
  186. */
  187. public EndTable() {
  188. super.EndTable();
  189. if (this.center) {
  190. //
  191. // If there is only one equation (one pair):
  192. // Don't make it 100%, and don't change the indentalign.
  193. //
  194. if (this.maxrow <= 2) {
  195. const def = this.arraydef;
  196. delete def.width;
  197. delete this.global.indentalign;
  198. }
  199. }
  200. }
  201. }