complexity.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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 Mixin that computes complexity of the internal MathML
  19. * and optionally marks collapsible items
  20. *
  21. * @author dpvc@mathjax.org (Davide Cervone)
  22. */
  23. import {Handler} from '../core/Handler.js';
  24. import {MathDocumentConstructor} from '../core/MathDocument.js';
  25. import {STATE, newState} from '../core/MathItem.js';
  26. import {MathML} from '../input/mathml.js';
  27. import {MmlNode} from '../core/MmlTree/MmlNode.js';
  28. import {EnrichHandler, EnrichedMathItem, EnrichedMathDocument} from './semantic-enrich.js';
  29. import {ComplexityVisitor} from './complexity/visitor.js';
  30. import {OptionList, selectOptionsFromKeys, expandable} from '../util/Options.js';
  31. /**
  32. * Generic constructor for Mixins
  33. */
  34. export type Constructor<T> = new(...args: any[]) => T;
  35. /**
  36. * Shorthands for constructors
  37. */
  38. export type EMItemC<N, T, D> = Constructor<EnrichedMathItem<N, T, D>>;
  39. export type CMItemC<N, T, D> = Constructor<ComplexityMathItem<N, T, D>>;
  40. export type EMDocC<N, T, D> = MathDocumentConstructor<EnrichedMathDocument<N, T, D>>;
  41. export type CMDocC<N, T, D> = Constructor<ComplexityMathDocument<N, T, D>>;
  42. /*==========================================================================*/
  43. /**
  44. * Add STATE value for having complexity added (after ENRICHED and before TYPESET)
  45. */
  46. newState('COMPLEXITY', 40);
  47. /**
  48. * The functions added to MathItem for complexity
  49. *
  50. * @template N The HTMLElement node class
  51. * @template T The Text node class
  52. * @template D The Document class
  53. */
  54. export interface ComplexityMathItem<N, T, D> extends EnrichedMathItem<N, T, D> {
  55. /**
  56. * @param {ComplexityMathDocument} document The MathDocument for the MathItem
  57. * @param {boolean} force True to force the computation even if enableComplexity is false
  58. */
  59. complexity(document: ComplexityMathDocument<N, T, D>, force?: boolean): void;
  60. }
  61. /**
  62. * The mixin for adding complexity to MathItems
  63. *
  64. * @param {B} BaseMathItem The MathItem class to be extended
  65. * @param {function(MmlNode): void} computeComplexity Method of complexity computation.
  66. * @return {ComplexityMathItem} The complexity MathItem class
  67. *
  68. * @template N The HTMLElement node class
  69. * @template T The Text node class
  70. * @template D The Document class
  71. * @template B The MathItem class to extend
  72. */
  73. export function ComplexityMathItemMixin<N, T, D, B extends
  74. EMItemC<N, T, D>>(BaseMathItem: B, computeComplexity: (node: MmlNode) => void): CMItemC<N, T, D> & B {
  75. return class extends BaseMathItem {
  76. /**
  77. * @param {ComplexityMathDocument} document The MathDocument for the MathItem
  78. * @param {boolean} force True to force the computation even if enableComplexity is false
  79. */
  80. public complexity(document: ComplexityMathDocument<N, T, D>, force: boolean = false) {
  81. if (this.state() >= STATE.COMPLEXITY) return;
  82. if (!this.isEscaped && (document.options.enableComplexity || force)) {
  83. this.enrich(document, true);
  84. computeComplexity(this.root);
  85. }
  86. this.state(STATE.COMPLEXITY);
  87. }
  88. };
  89. }
  90. /*==========================================================================*/
  91. /**
  92. * The functions added to MathDocument for complexity
  93. *
  94. * @template N The HTMLElement node class
  95. * @template T The Text node class
  96. * @template D The Document class
  97. */
  98. export interface ComplexityMathDocument<N, T, D> extends EnrichedMathDocument<N, T, D> {
  99. /**
  100. * Perform complexity computations on the MathItems in the MathDocument
  101. *
  102. * @return {ComplexityMathDocument} The MathDocument (so calls can be chained)
  103. */
  104. complexity(): ComplexityMathDocument<N, T, D>;
  105. }
  106. /**
  107. * The mixin for adding complexity to MathDocuments
  108. *
  109. * @param {B} BaseDocument The MathDocument class to be extended
  110. * @return {EnrichedMathDocument} The enriched MathDocument class
  111. *
  112. * @template N The HTMLElement node class
  113. * @template T The Text node class
  114. * @template D The Document class
  115. * @template B The MathDocument class to extend
  116. */
  117. export function ComplexityMathDocumentMixin<N, T, D, B extends
  118. EMDocC<N, T, D>>(BaseDocument: B): CMDocC<N, T, D> & B {
  119. return class extends BaseDocument {
  120. /**
  121. * The options for this type of document
  122. */
  123. public static OPTIONS: OptionList = {
  124. ...BaseDocument.OPTIONS,
  125. ...ComplexityVisitor.OPTIONS,
  126. enableComplexity: true,
  127. ComplexityVisitor: ComplexityVisitor,
  128. renderActions: expandable({
  129. ...BaseDocument.OPTIONS.renderActions,
  130. complexity: [STATE.COMPLEXITY]
  131. })
  132. };
  133. /**
  134. * The visitor that computes complexities
  135. */
  136. protected complexityVisitor: ComplexityVisitor;
  137. /**
  138. * Extend the MathItem class used for this MathDocument
  139. *
  140. * @override
  141. * @constructor
  142. */
  143. constructor(...args: any[]) {
  144. super(...args);
  145. const ProcessBits = (this.constructor as typeof BaseDocument).ProcessBits;
  146. if (!ProcessBits.has('complexity')) {
  147. ProcessBits.allocate('complexity');
  148. }
  149. const visitorOptions = selectOptionsFromKeys(this.options, this.options.ComplexityVisitor.OPTIONS);
  150. this.complexityVisitor = new this.options.ComplexityVisitor(this.mmlFactory, visitorOptions);
  151. const computeComplexity = ((node: MmlNode) => this.complexityVisitor.visitTree(node));
  152. this.options.MathItem =
  153. ComplexityMathItemMixin<N, T, D, EMItemC<N, T, D>>(
  154. this.options.MathItem, computeComplexity
  155. );
  156. }
  157. /**
  158. * Compute the complexity the MathItems in this MathDocument
  159. */
  160. public complexity() {
  161. if (!this.processed.isSet('complexity')) {
  162. if (this.options.enableComplexity) {
  163. for (const math of this.math) {
  164. (math as ComplexityMathItem<N, T, D>).complexity(this);
  165. }
  166. }
  167. this.processed.set('complexity');
  168. }
  169. return this;
  170. }
  171. /**
  172. * @override
  173. */
  174. public state(state: number, restore: boolean = false) {
  175. super.state(state, restore);
  176. if (state < STATE.COMPLEXITY) {
  177. this.processed.clear('complexity');
  178. }
  179. return this;
  180. }
  181. };
  182. }
  183. /*==========================================================================*/
  184. /**
  185. * Add complexity computations a Handler instance
  186. *
  187. * @param {Handler} handler The Handler instance to enhance
  188. * @param {MathML} MmlJax The MathML input jax to use for reading the enriched MathML
  189. * @return {Handler} The handler that was modified (for purposes of chainging extensions)
  190. *
  191. * @template N The HTMLElement node class
  192. * @template T The Text node class
  193. * @template D The Document class
  194. */
  195. export function ComplexityHandler<N, T, D>(
  196. handler: Handler<N, T, D>,
  197. MmlJax: MathML<N, T, D> = null
  198. ): Handler<N, T, D> {
  199. if (!handler.documentClass.prototype.enrich && MmlJax) {
  200. handler = EnrichHandler(handler, MmlJax);
  201. }
  202. handler.documentClass = ComplexityMathDocumentMixin<N, T, D, EMDocC<N, T, D>>(handler.documentClass as any);
  203. return handler;
  204. }