BaseConfiguration.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 Configuration for the Base LaTeX parser.
  19. *
  20. * @author v.sorge@mathjax.org (Volker Sorge)
  21. */
  22. import {Configuration} from '../Configuration.js';
  23. import {MapHandler} from '../MapHandler.js';
  24. import TexError from '../TexError.js';
  25. import NodeUtil from '../NodeUtil.js';
  26. import TexParser from '../TexParser.js';
  27. import {CharacterMap} from '../SymbolMap.js';
  28. import * as bitem from './BaseItems.js';
  29. import {AbstractTags} from '../Tags.js';
  30. import './BaseMappings.js';
  31. import {getRange} from '../../../core/MmlTree/OperatorDictionary.js';
  32. import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
  33. import ParseOptions from '../ParseOptions.js';
  34. /**
  35. * Remapping some ASCII characters to their Unicode operator equivalent.
  36. */
  37. new CharacterMap('remap', null, {
  38. '-': '\u2212',
  39. '*': '\u2217',
  40. '`': '\u2018' // map ` to back quote
  41. });
  42. /**
  43. * Default handling of characters (as <mo> elements).
  44. * @param {TexParser} parser The calling parser.
  45. * @param {string} char The character to parse.
  46. */
  47. export function Other(parser: TexParser, char: string) {
  48. const font = parser.stack.env['font'];
  49. let def = font ?
  50. // @test Other Font
  51. {mathvariant: parser.stack.env['font']} : {};
  52. const remap = (MapHandler.getMap('remap') as CharacterMap).lookup(char);
  53. const range = getRange(char);
  54. const type = (range ? range[3] : 'mo');
  55. // @test Other
  56. // @test Other Remap
  57. let mo = parser.create('token', type, def, (remap ? remap.char : char));
  58. range[4] && mo.attributes.set('mathvariant', range[4]);
  59. if (type === 'mo') {
  60. NodeUtil.setProperty(mo, 'fixStretchy', true);
  61. parser.configuration.addNode('fixStretchy', mo);
  62. }
  63. parser.Push(mo);
  64. }
  65. /**
  66. * Handle undefined control sequence.
  67. * @param {TexParser} parser The calling parser.
  68. * @param {string} name The name of the control sequence.
  69. */
  70. function csUndefined(_parser: TexParser, name: string) {
  71. // @test Undefined-CS
  72. throw new TexError('UndefinedControlSequence',
  73. 'Undefined control sequence %1', '\\' + name);
  74. }
  75. /**
  76. * Handle undefined environments.
  77. * @param {TexParser} parser The calling parser.
  78. * @param {string} name The name of the control sequence.
  79. */
  80. function envUndefined(_parser: TexParser, env: string) {
  81. // @test Undefined-Env
  82. throw new TexError('UnknownEnv', 'Unknown environment \'%1\'', env);
  83. }
  84. /**
  85. * Filter for removing spacing following \nonscript
  86. * @param{ParseOptions} data The active tex parser.
  87. */
  88. function filterNonscript({data}: {data: ParseOptions}) {
  89. for (const mml of data.getList('nonscript')) {
  90. //
  91. // This is the list of mspace elements or mrow > mstyle > mspace
  92. // that followed \nonscript macros to be tested for removal.
  93. //
  94. if (mml.attributes.get('scriptlevel') > 0) {
  95. //
  96. // The mspace needs to be removed, since we are in a script style.
  97. // Remove it from the DOM and from the list of mspace elements.
  98. //
  99. const parent = mml.parent;
  100. parent.childNodes.splice(parent.childIndex(mml), 1);
  101. data.removeFromList(mml.kind, [mml]);
  102. //
  103. // If it is an mrow > mstyle > mspace, then we have just
  104. // removed the mrow from its list, and must remove
  105. // the mstyle and mspace from their lists as well.
  106. //
  107. if (mml.isKind('mrow')) {
  108. const mstyle = mml.childNodes[0] as MmlNode;
  109. data.removeFromList('mstyle', [mstyle]);
  110. data.removeFromList('mspace', mstyle.childNodes[0].childNodes as MmlNode[]);
  111. }
  112. } else if (mml.isKind('mrow')) {
  113. //
  114. // This is an mrow > mstyle > mspace but we're not in a script
  115. // style, so remove the mrow that we had added in the NonscriptItem.
  116. //
  117. mml.parent.replaceChild(mml.childNodes[0], mml);
  118. data.removeFromList('mrow', [mml]);
  119. }
  120. }
  121. }
  122. /**
  123. * @constructor
  124. * @extends {AbstractTags}
  125. */
  126. export class BaseTags extends AbstractTags { }
  127. /**
  128. * The base configuration.
  129. * @type {Configuration}
  130. */
  131. export const BaseConfiguration: Configuration = Configuration.create(
  132. 'base', {
  133. handler: {
  134. character: ['command', 'special', 'letter', 'digit'],
  135. delimiter: ['delimiter'],
  136. // Note, that the position of the delimiters here is important!
  137. macro: ['delimiter', 'macros', 'mathchar0mi', 'mathchar0mo', 'mathchar7'],
  138. environment: ['environment']
  139. },
  140. fallback: {
  141. character: Other,
  142. macro: csUndefined,
  143. environment: envUndefined
  144. },
  145. items: {
  146. // BaseItems
  147. [bitem.StartItem.prototype.kind]: bitem.StartItem,
  148. [bitem.StopItem.prototype.kind]: bitem.StopItem,
  149. [bitem.OpenItem.prototype.kind]: bitem.OpenItem,
  150. [bitem.CloseItem.prototype.kind]: bitem.CloseItem,
  151. [bitem.PrimeItem.prototype.kind]: bitem.PrimeItem,
  152. [bitem.SubsupItem.prototype.kind]: bitem.SubsupItem,
  153. [bitem.OverItem.prototype.kind]: bitem.OverItem,
  154. [bitem.LeftItem.prototype.kind]: bitem.LeftItem,
  155. [bitem.Middle.prototype.kind]: bitem.Middle,
  156. [bitem.RightItem.prototype.kind]: bitem.RightItem,
  157. [bitem.BeginItem.prototype.kind]: bitem.BeginItem,
  158. [bitem.EndItem.prototype.kind]: bitem.EndItem,
  159. [bitem.StyleItem.prototype.kind]: bitem.StyleItem,
  160. [bitem.PositionItem.prototype.kind]: bitem.PositionItem,
  161. [bitem.CellItem.prototype.kind]: bitem.CellItem,
  162. [bitem.MmlItem.prototype.kind]: bitem.MmlItem,
  163. [bitem.FnItem.prototype.kind]: bitem.FnItem,
  164. [bitem.NotItem.prototype.kind]: bitem.NotItem,
  165. [bitem.NonscriptItem.prototype.kind]: bitem.NonscriptItem,
  166. [bitem.DotsItem.prototype.kind]: bitem.DotsItem,
  167. [bitem.ArrayItem.prototype.kind]: bitem.ArrayItem,
  168. [bitem.EqnArrayItem.prototype.kind]: bitem.EqnArrayItem,
  169. [bitem.EquationItem.prototype.kind]: bitem.EquationItem
  170. },
  171. options: {
  172. maxMacros: 1000,
  173. baseURL: (typeof(document) === 'undefined' ||
  174. document.getElementsByTagName('base').length === 0) ?
  175. '' : String(document.location).replace(/#.*$/, '')
  176. },
  177. tags: {
  178. base: BaseTags
  179. },
  180. postprocessors: [[filterNonscript, -4]]
  181. }
  182. );