AmsCdMethods.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2018-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 Methods for the AMScd package.
  19. *
  20. * @author v.sorge@mathjax.org (Volker Sorge)
  21. */
  22. import TexParser from '../TexParser.js';
  23. import {ParseMethod} from '../Types.js';
  24. import {StackItem, EnvList} from '../StackItem.js';
  25. import {ArrayItem} from '../base/BaseItems.js';
  26. import {Other} from '../base/BaseConfiguration.js';
  27. import {MmlMunderover} from '../../../core/MmlTree/MmlNodes/munderover.js';
  28. import {TEXCLASS} from '../../../core/MmlTree/MmlNode.js';
  29. import NodeUtil from '../NodeUtil.js';
  30. // Namespace
  31. let AmsCdMethods: Record<string, ParseMethod> = {};
  32. /**
  33. * Handles CD environment for commutative diagrams.
  34. * @param {TexParser} parser The calling parser.
  35. * @param {StackItem} begin The opening stackitem.
  36. */
  37. AmsCdMethods.CD = function(parser: TexParser, begin: StackItem) {
  38. parser.Push(begin);
  39. let item = parser.itemFactory.create('array') as ArrayItem;
  40. let options = parser.configuration.options.amscd;
  41. item.setProperties({
  42. minw: parser.stack.env.CD_minw || options.harrowsize,
  43. minh: parser.stack.env.CD_minh || options.varrowsize
  44. });
  45. item.arraydef = {
  46. columnalign: 'center',
  47. columnspacing: options.colspace,
  48. rowspacing: options.rowspace,
  49. displaystyle: true
  50. };
  51. return item;
  52. };
  53. /**
  54. * Converts arrows.
  55. * @param {TexParser} parser The calling parser.
  56. * @param {string} name The macro name.
  57. */
  58. AmsCdMethods.arrow = function(parser: TexParser, name: string) {
  59. let c = parser.string.charAt(parser.i);
  60. if (!c.match(/[><VA.|=]/)) {
  61. return Other(parser, name);
  62. } else {
  63. parser.i++;
  64. }
  65. let first = parser.stack.Top();
  66. if (!first.isKind('array') || first.Size()) {
  67. AmsCdMethods.cell(parser, name);
  68. first = parser.stack.Top();
  69. }
  70. let top = first as ArrayItem;
  71. //
  72. // Add enough cells to place the arrow correctly
  73. //
  74. let arrowRow = ((top.table.length % 2) === 1);
  75. let n = (top.row.length + (arrowRow ? 0 : 1)) % 2;
  76. while (n) {
  77. AmsCdMethods.cell(parser, name);
  78. n--;
  79. }
  80. let mml;
  81. let hdef = {minsize: top.getProperty('minw'), stretchy: true},
  82. vdef = {minsize: top.getProperty('minh'),
  83. stretchy: true, symmetric: true, lspace: 0, rspace: 0};
  84. if (c === '.') {
  85. } else if (c === '|') {
  86. mml = parser.create('token', 'mo', vdef, '\u2225');
  87. } else if (c === '=') {
  88. mml = parser.create('token', 'mo', hdef, '=');
  89. } else {
  90. //
  91. // for @>>> @<<< @VVV and @AAA, get the arrow and labels
  92. //
  93. // TODO: cleanup!
  94. let arrow: string = ({
  95. '>': '\u2192', '<': '\u2190', 'V': '\u2193', 'A': '\u2191'} as {[key: string]: string}) [c];
  96. let a = parser.GetUpTo(name + c, c);
  97. let b = parser.GetUpTo(name + c, c);
  98. if (c === '>' || c === '<') {
  99. //
  100. // Lay out horizontal arrows with munderover if it has labels
  101. //
  102. mml = parser.create('token', 'mo', hdef, arrow);
  103. if (!a) {
  104. a = '\\kern ' + top.getProperty('minw');
  105. } // minsize needs work
  106. if (a || b) {
  107. let pad: EnvList = {width: '+.67em', lspace: '.33em'};
  108. mml = parser.create('node', 'munderover', [mml]) as MmlMunderover;
  109. if (a) {
  110. let nodeA = new TexParser(a, parser.stack.env, parser.configuration).mml();
  111. let mpadded = parser.create('node', 'mpadded', [nodeA], pad);
  112. NodeUtil.setAttribute(mpadded, 'voffset', '.1em');
  113. NodeUtil.setChild(mml, mml.over, mpadded);
  114. }
  115. if (b) {
  116. let nodeB = new TexParser(b, parser.stack.env, parser.configuration).mml();
  117. NodeUtil.setChild(mml, mml.under, parser.create('node', 'mpadded', [nodeB], pad));
  118. }
  119. if (parser.configuration.options.amscd.hideHorizontalLabels) {
  120. mml = parser.create('node', 'mpadded', mml, {depth: 0, height: '.67em'});
  121. }
  122. }
  123. } else {
  124. //
  125. // Lay out vertical arrows with mrow if there are labels
  126. //
  127. let arrowNode = parser.create('token', 'mo', vdef, arrow);
  128. mml = arrowNode;
  129. if (a || b) {
  130. mml = parser.create('node', 'mrow');
  131. if (a) {
  132. NodeUtil.appendChildren(
  133. mml, [new TexParser('\\scriptstyle\\llap{' + a + '}', parser.stack.env, parser.configuration).mml()]);
  134. }
  135. arrowNode.texClass = TEXCLASS.ORD;
  136. NodeUtil.appendChildren(mml, [arrowNode]);
  137. if (b) {
  138. NodeUtil.appendChildren(mml, [new TexParser('\\scriptstyle\\rlap{' + b + '}',
  139. parser.stack.env, parser.configuration).mml()]);
  140. }
  141. }
  142. }
  143. }
  144. if (mml) {
  145. parser.Push(mml);
  146. }
  147. AmsCdMethods.cell(parser, name);
  148. };
  149. /**
  150. * Converts a cell in the diagram.
  151. * @param {TexParser} parser The calling parser.
  152. * @param {string} name The macro name.
  153. */
  154. AmsCdMethods.cell = function(parser: TexParser, name: string) {
  155. let top = parser.stack.Top() as ArrayItem;
  156. if ((top.table || []).length % 2 === 0 && (top.row || []).length === 0) {
  157. //
  158. // Add a strut to the first cell in even rows to get
  159. // better spacing of arrow rows.
  160. //
  161. parser.Push(parser.create('node', 'mpadded', [], {height: '8.5pt', depth: '2pt'}));
  162. }
  163. parser.Push(parser.itemFactory.create('cell').setProperties({isEntry: true, name: name}));
  164. };
  165. /**
  166. * Sets minimal width for arrows.
  167. * @param {TexParser} parser The calling parser.
  168. * @param {string} name The macro name.
  169. */
  170. AmsCdMethods.minCDarrowwidth = function(parser: TexParser, name: string) {
  171. parser.stack.env.CD_minw = parser.GetDimen(name);
  172. };
  173. /**
  174. * Sets minimal height for arrows.
  175. * @param {TexParser} parser The calling parser.
  176. * @param {string} name The macro name.
  177. */
  178. AmsCdMethods.minCDarrowheight = function(parser: TexParser, name: string) {
  179. parser.stack.env.CD_minh = parser.GetDimen(name);
  180. };
  181. export default AmsCdMethods;