ParseOptions.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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 Factory generating maps to keep options for the TeX parser.
  19. *
  20. * @author v.sorge@mathjax.org (Volker Sorge)
  21. */
  22. import StackItemFactory from './StackItemFactory.js';
  23. import {Tags} from './Tags.js';
  24. import {SubHandlers} from './MapHandler.js';
  25. import {NodeFactory} from './NodeFactory.js';
  26. import NodeUtil from './NodeUtil.js';
  27. import {MmlNode} from '../../core/MmlTree/MmlNode.js';
  28. import TexParser from './TexParser.js';
  29. import {defaultOptions, OptionList} from '../../util/Options.js';
  30. import {ParserConfiguration} from './Configuration.js';
  31. /**
  32. * @class
  33. */
  34. export default class ParseOptions {
  35. /**
  36. * A set of sub handlers
  37. * @type {SubHandlers}
  38. */
  39. public handlers: SubHandlers;
  40. /**
  41. * A set of options, mapping names to string or boolean values.
  42. * @type {OptionList}
  43. */
  44. public options: OptionList = {};
  45. /**
  46. * The current item factory.
  47. * @type {StackItemFactory}
  48. */
  49. public itemFactory: StackItemFactory;
  50. /**
  51. * The current node factory.
  52. * @type {NodeFactory}
  53. */
  54. public nodeFactory: NodeFactory;
  55. /**
  56. * The current tagging object.
  57. * @type {Tags}
  58. */
  59. public tags: Tags;
  60. /**
  61. * Storage area for parser-specific package data (indexed by package name)
  62. * @type {Map<string, any>}
  63. */
  64. public packageData: Map<string, any> = new Map();
  65. // Fields for ephemeral options, i.e., options that will be cleared for each
  66. // run of the parser.
  67. /**
  68. * Stack of previous tex parsers. This is used to keep track of parser
  69. * settings when expressions are recursively parsed.
  70. * @type {TexParser[]}
  71. */
  72. public parsers: TexParser[] = [];
  73. /**
  74. * The current root node.
  75. * @type {MmlNode}
  76. */
  77. public root: MmlNode = null;
  78. /**
  79. * List of node lists saved with respect to some property or their kind.
  80. * @type {{[key: string]: MmlNode[]}}
  81. */
  82. public nodeLists: {[key: string]: MmlNode[]} = {};
  83. /**
  84. * Error state of the parser.
  85. * @type {boolean}
  86. */
  87. public error: boolean = false;
  88. /**
  89. * @constructor
  90. * @param {Configuration} configuration Configuration object of the current
  91. * TeX parser.
  92. * @param {OptionList[]} options [TeX options, Tag options, {packages}]
  93. */
  94. public constructor(configuration: ParserConfiguration, options: OptionList[] = []) {
  95. this.handlers = configuration.handlers;
  96. // Add node factory methods from packages.
  97. this.nodeFactory = new NodeFactory();
  98. this.nodeFactory.configuration = this;
  99. this.nodeFactory.setCreators(configuration.nodes);
  100. // Add stackitems from packages.
  101. this.itemFactory = new StackItemFactory(configuration.items);
  102. this.itemFactory.configuration = this;
  103. // Set default options for parser from packages and for tags.
  104. defaultOptions(this.options, ...options);
  105. defaultOptions(this.options, configuration.options);
  106. }
  107. // Methods for dealing with ephemeral fields.
  108. /**
  109. * Pushes a new tex parser onto the stack.
  110. * @param {TexParser} parser The new parser.
  111. */
  112. public pushParser(parser: TexParser) {
  113. this.parsers.unshift(parser);
  114. }
  115. /**
  116. * Pops a parser of the tex parser stack.
  117. */
  118. public popParser() {
  119. this.parsers.shift();
  120. }
  121. /**
  122. * @return {TexParser} The currently active tex parser.
  123. */
  124. public get parser(): TexParser {
  125. return this.parsers[0];
  126. }
  127. /**
  128. * Clears all the ephemeral options.
  129. */
  130. public clear() {
  131. this.parsers = [];
  132. this.root = null;
  133. this.nodeLists = {};
  134. this.error = false;
  135. this.tags.resetTag();
  136. }
  137. /**
  138. * Saves a tree node to a list of nodes for post processing.
  139. * @param {string} property The property name that will be used for
  140. * postprocessing.
  141. * @param {MmlNode} node The node to save.
  142. */
  143. public addNode(property: string, node: MmlNode) {
  144. let list = this.nodeLists[property];
  145. if (!list) {
  146. list = this.nodeLists[property] = [];
  147. }
  148. list.push(node);
  149. if (node.kind !== property) {
  150. //
  151. // If the list is not just for its kind, record that it is in this list
  152. // so that if it is copied, the copy can also be added to the list.
  153. //
  154. const inlists = (NodeUtil.getProperty(node, 'in-lists') as string || '');
  155. const lists = (inlists ? inlists.split(/,/) : []).concat(property).join(',');
  156. NodeUtil.setProperty(node, 'in-lists', lists);
  157. }
  158. }
  159. /**
  160. * Gets a saved node list with respect to a given property. It first ensures
  161. * that all the nodes are "live", i.e., actually live in the current
  162. * tree. Sometimes nodes are created, saved in the node list but discarded
  163. * later in the parsing. These will be filtered out here.
  164. *
  165. * NB: Do not use this method before the root field of the options is
  166. * set. Otherwise, your node list will always be empty!
  167. * @param {string} property The property for which to retrieve the node list.
  168. */
  169. public getList(property: string) {
  170. let list = this.nodeLists[property] || [];
  171. let result = [];
  172. for (let node of list) {
  173. if (this.inTree(node)) {
  174. result.push(node);
  175. }
  176. }
  177. this.nodeLists[property] = result;
  178. return result;
  179. }
  180. /**
  181. * Remove a list of nodes from a saved list (e.g., when a filter removes the
  182. * node from the DOM, like for munderover => munder).
  183. *
  184. * @param {string} property The property from which to remove nodes.
  185. * @param {MmlNode[]} nodes The nodes to remove.
  186. */
  187. public removeFromList(property: string, nodes: MmlNode[]) {
  188. const list = this.nodeLists[property] || [];
  189. for (const node of nodes) {
  190. const i = list.indexOf(node);
  191. if (i >= 0) {
  192. list.splice(i, 1);
  193. }
  194. }
  195. }
  196. /**
  197. * Tests if the node is in the tree spanned by the current root node.
  198. * @param {MmlNode} node The node to test.
  199. */
  200. private inTree(node: MmlNode) {
  201. while (node && node !== this.root) {
  202. node = node.parent;
  203. }
  204. return !!node;
  205. }
  206. }