123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987 |
- /*************************************************************
- *
- * Copyright (c) 2017-2022 The MathJax Consortium
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /**
- * @fileoverview Implements the interface and abstract class for MathDocument objects
- *
- * @author dpvc@mathjax.org (Davide Cervone)
- */
- import {userOptions, defaultOptions, OptionList, expandable} from '../util/Options.js';
- import {InputJax, AbstractInputJax} from './InputJax.js';
- import {OutputJax, AbstractOutputJax} from './OutputJax.js';
- import {MathList, AbstractMathList} from './MathList.js';
- import {MathItem, AbstractMathItem, STATE} from './MathItem.js';
- import {MmlNode, TextNode} from './MmlTree/MmlNode.js';
- import {MmlFactory} from '../core/MmlTree/MmlFactory.js';
- import {DOMAdaptor} from '../core/DOMAdaptor.js';
- import {BitField, BitFieldClass} from '../util/BitField.js';
- import {PrioritizedList} from '../util/PrioritizedList.js';
- /*****************************************************************/
- /**
- * A function to call while rendering a document (usually calls a MathDocument method)
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- export type RenderDoc<N, T, D> = (document: MathDocument<N, T, D>) => boolean;
- /**
- * A function to call while rendering a MathItem (usually calls one of its methods)
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- export type RenderMath<N, T, D> = (math: MathItem<N, T, D>, document: MathDocument<N, T, D>) => boolean;
- /**
- * The data for an action to perform during rendering or conversion
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- export type RenderData<N, T, D> = {
- id: string, // The name for the action
- renderDoc: RenderDoc<N, T, D>, // The action to take during a render() call
- renderMath: RenderMath<N, T, D>, // The action to take during a rerender() or convert() call
- convert: boolean // Whether the action is to be used during convert()
- };
- /**
- * The data used to define a render action in configurations and options objects
- * (the key is used as the id, the number in the data below is the priority, and
- * the remainind data is as described below; if no boolean is given, convert = true
- * by default)
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- export type RenderAction<N, T, D> =
- [number] | // id (i.e., key) is method name to use
- [number, string] | // string is method to call
- [number, string, string] | // the strings are methods names for doc and math
- [number, RenderDoc<N, T, D>, RenderMath<N, T, D>] | // explicit functions for doc and math
- [number, boolean] | // same as first above, with boolean for convert
- [number, string, boolean] | // same as second above, with boolean for convert
- [number, string, string, boolean] | // same as third above, with boolean for convert
- [number, RenderDoc<N, T, D>, RenderMath<N, T, D>, boolean]; // same as forth above, with boolean for convert
- /**
- * An object representing a collection of rendering actions (id's tied to priority-and-method data)
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- export type RenderActions<N, T, D> = {[id: string]: RenderAction<N, T, D>};
- /**
- * Implements a prioritized list of render actions. Extensions can add actions to the list
- * to make it easy to extend the normal typesetting and conversion operations.
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- export class RenderList<N, T, D> extends PrioritizedList<RenderData<N, T, D>> {
- /**
- * Creates a new RenderList from an initial list of rendering actions
- *
- * @param {RenderActions} actions The list of actions to take during render(), rerender(), and convert() calls
- * @returns {RenderList} The newly created prioritied list
- */
- public static create<N, T, D>(actions: RenderActions<N, T, D>): RenderList<N, T, D> {
- const list = new this<N, T, D>();
- for (const id of Object.keys(actions)) {
- const [action, priority] = this.action<N, T, D>(id, actions[id]);
- if (priority) {
- list.add(action, priority);
- }
- }
- return list;
- }
- /**
- * Parses a RenderAction to produce the correspinding RenderData item
- * (e.g., turn method names into actual functions that call the method)
- *
- * @param {string} id The id of the action
- * @param {RenderAction} action The RenderAction defining the action
- * @returns {[RenderData,number]} The corresponding RenderData definition for the action and its priority
- */
- public static action<N, T, D>(id: string, action: RenderAction<N, T, D>): [RenderData<N, T, D>, number] {
- let renderDoc, renderMath;
- let convert = true;
- let priority = action[0];
- if (action.length === 1 || typeof action[1] === 'boolean') {
- action.length === 2 && (convert = action[1] as boolean);
- [renderDoc, renderMath] = this.methodActions(id);
- } else if (typeof action[1] === 'string') {
- if (typeof action[2] === 'string') {
- action.length === 4 && (convert = action[3] as boolean);
- const [method1, method2] = action.slice(1) as [string, string];
- [renderDoc, renderMath] = this.methodActions(method1, method2);
- } else {
- action.length === 3 && (convert = action[2] as boolean);
- [renderDoc, renderMath] = this.methodActions(action[1] as string);
- }
- } else {
- action.length === 4 && (convert = action[3] as boolean);
- [renderDoc, renderMath] = action.slice(1) as [RenderDoc<N, T, D>, RenderMath<N, T, D>];
- }
- return [{id, renderDoc, renderMath, convert} as RenderData<N, T, D>, priority];
- }
- /**
- * Produces the doc and math actions for the given method name(s)
- * (a blank name is a no-op)
- *
- * @param {string} method1 The method to use for the render() call
- * @param {string} method1 The method to use for the rerender() and convert() calls
- */
- protected static methodActions(method1: string, method2: string = method1) {
- return [
- (document: any) => {method1 && document[method1](); return false; },
- (math: any, document: any) => {method2 && math[method2](document); return false; }
- ];
- }
- /**
- * Perform the document-level rendering functions
- *
- * @param {MathDocument} document The MathDocument whose methods are to be called
- * @param {number=} start The state at which to start rendering (default is UNPROCESSED)
- */
- public renderDoc(document: MathDocument<N, T, D>, start: number = STATE.UNPROCESSED) {
- for (const item of this.items) {
- if (item.priority >= start) {
- if (item.item.renderDoc(document)) return;
- }
- }
- }
- /**
- * Perform the MathItem-level rendering functions
- *
- * @param {MathItem} math The MathItem whose methods are to be called
- * @param {MathDocument} document The MathDocument to pass to the MathItem methods
- * @param {number=} start The state at which to start rendering (default is UNPROCESSED)
- */
- public renderMath(math: MathItem<N, T, D>, document: MathDocument<N, T, D>, start: number = STATE.UNPROCESSED) {
- for (const item of this.items) {
- if (item.priority >= start) {
- if (item.item.renderMath(math, document)) return;
- }
- }
- }
- /**
- * Perform the MathItem-level conversion functions
- *
- * @param {MathItem} math The MathItem whose methods are to be called
- * @param {MathDocument} document The MathDocument to pass to the MathItem methods
- * @param {number=} end The state at which to end rendering (default is LAST)
- */
- public renderConvert(math: MathItem<N, T, D>, document: MathDocument<N, T, D>, end: number = STATE.LAST) {
- for (const item of this.items) {
- if (item.priority > end) return;
- if (item.item.convert) {
- if (item.item.renderMath(math, document)) return;
- }
- }
- }
- /**
- * Find an entry in the list with a given ID
- *
- * @param {string} id The id to search for
- * @returns {RenderData|null} The data for the given id, if found, or null
- */
- public findID(id: string): RenderData<N, T, D> | null {
- for (const item of this.items) {
- if (item.item.id === id) {
- return item.item;
- }
- }
- return null;
- }
- }
- /*****************************************************************/
- /**
- * The ways of specifying a container (a selector string, an actual node,
- * or an array of those (e.g., the result of document.getElementsByTagName())
- *
- * @template N The HTMLElement node class
- */
- export type ContainerList<N> = string | N | (string | N | N[])[];
- /**
- * The options allowed for the reset() method
- */
- export type ResetList = {
- all?: boolean,
- processed?: boolean,
- inputJax?: any[],
- outputJax?: any[]
- };
- /**
- * The default option list for the reset() method
- */
- export const resetOptions: ResetList = {
- all: false,
- processed: false,
- inputJax: null,
- outputJax: null
- };
- /**
- * The option list for when all options are to be reset
- */
- export const resetAllOptions: ResetList = {
- all: true,
- processed: true,
- inputJax: [],
- outputJax: []
- };
- /*****************************************************************/
- /**
- * The MathDocument interface
- *
- * The MathDocument is created by MathJax.Document() and holds the
- * document, the math found in it, and so on. The methods of the
- * MathDocument all return the MathDocument itself, so you can
- * chain the method calls. E.g.,
- *
- * const html = MathJax.Document('<html>...</html>');
- * html.findMath()
- * .compile()
- * .getMetrics()
- * .typeset()
- * .updateDocument();
- *
- * The MathDocument is the main interface for page authors to
- * interact with MathJax.
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- export interface MathDocument<N, T, D> {
- /**
- * The document being processed (e.g., DOM document, or Markdown string)
- */
- document: D;
- /**
- * The kind of MathDocument (e.g., "HTML")
- */
- kind: string;
- /**
- * The options for the document
- */
- options: OptionList;
- /**
- * The list of MathItems found in this page
- */
- math: MathList<N, T, D>;
- /**
- * The list of actions to take during a render() or convert() call
- */
- renderActions: RenderList<N, T, D>;
- /**
- * This object tracks what operations have been performed, so that (when
- * asynchronous operations are used), the ones that have already been
- * completed won't be performed again.
- */
- processed: BitField;
- /**
- * An array of input jax to run on the document
- */
- inputJax: InputJax<N, T, D>[];
- /**
- * The output jax to use for the document
- */
- outputJax: OutputJax<N, T, D>;
- /**
- * The DOM adaptor to use for input and output
- */
- adaptor: DOMAdaptor<N, T, D>;
- /**
- * The MmlFactory to be used for input jax and error processing
- */
- mmlFactory: MmlFactory;
- /**
- * @param {string} id The id of the action to add
- * @param {any[]} action The RenderAction to take
- */
- addRenderAction(id: string, ...action: any[]): void;
- /**
- * @param {string} id The id of the action to remove
- */
- removeRenderAction(id: string): void;
- /**
- * Perform the renderActions on the document
- */
- render(): MathDocument<N, T, D>;
- /**
- * Rerender the MathItems on the page
- *
- * @param {number=} start The state to start rerendering at
- * @return {MathDocument} The math document instance
- */
- rerender(start?: number): MathDocument<N, T, D>;
- /**
- * Convert a math string to the document's output format
- *
- * @param {string} math The math string to convert
- * @params {OptionList} options The options for the conversion (e.g., format, ex, em, etc.)
- * @return {MmlNode|N} The MmlNode or N node for the converted content
- */
- convert(math: string, options?: OptionList): MmlNode | N;
- /**
- * Locates the math in the document and constructs the MathList
- * for the document.
- *
- * @param {OptionList} options The options for locating the math
- * @return {MathDocument} The math document instance
- */
- findMath(options?: OptionList): MathDocument<N, T, D>;
- /**
- * Calls the input jax to process the MathItems in the MathList
- *
- * @return {MathDocument} The math document instance
- */
- compile(): MathDocument<N, T, D>;
- /**
- * Gets the metric information for the MathItems
- *
- * @return {MathDocument} The math document instance
- */
- getMetrics(): MathDocument<N, T, D>;
- /**
- * Calls the output jax to process the compiled math in the MathList
- *
- * @return {MathDocument} The math document instance
- */
- typeset(): MathDocument<N, T, D>;
- /**
- * Updates the document to include the typeset math
- *
- * @return {MathDocument} The math document instance
- */
- updateDocument(): MathDocument<N, T, D>;
- /**
- * Removes the typeset math from the document
- *
- * @param {boolean} restore True if the original math should be put
- * back into the document as well
- * @return {MathDocument} The math document instance
- */
- removeFromDocument(restore?: boolean): MathDocument<N, T, D>;
- /**
- * Set the state of the document (allowing you to roll back
- * the state to a previous one, if needed).
- *
- * @param {number} state The new state of the document
- * @param {boolean} restore True if the original math should be put
- * back into the document during the rollback
- * @return {MathDocument} The math document instance
- */
- state(state: number, restore?: boolean): MathDocument<N, T, D>;
- /**
- * Clear the processed values so that the document can be reprocessed
- *
- * @param {ResetList} options The things to be reset
- * @return {MathDocument} The math document instance
- */
- reset(options?: ResetList): MathDocument<N, T, D>;
- /**
- * Reset the processed values and clear the MathList (so that new math
- * can be processed in the document).
- *
- * @return {MathDocument} The math document instance
- */
- clear(): MathDocument<N, T, D>;
- /**
- * Merges a MathList into the list for this document.
- *
- * @param {MathList} list The MathList to be merged into this document's list
- * @return {MathDocument} The math document instance
- */
- concat(list: MathList<N, T, D>): MathDocument<N, T, D>;
- /**
- * Clear the typeset MathItems that are within the given container
- * from the document's MathList. (E.g., when the content of the
- * container has been updated and you want to remove the
- * associated MathItems)
- *
- * @param {ContainerList<N>} elements The container DOM elements whose math items are to be removed
- * @return {MathItem<N,T,D>[]} The removed MathItems
- */
- clearMathItemsWithin(containers: ContainerList<N>): MathItem<N, T, D>[];
- /**
- * Get the typeset MathItems that are within a given container.
- *
- * @param {ContainerList<N>} elements The container DOM elements whose math items are to be found
- * @return {MathItem<N,T,D>[]} The list of MathItems within that container
- */
- getMathItemsWithin(elements: ContainerList<N>): MathItem<N, T, D>[];
- }
- /*****************************************************************/
- /**
- * Defaults used when input jax isn't specified
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- class DefaultInputJax<N, T, D> extends AbstractInputJax<N, T, D> {
- /**
- * @override
- */
- public compile(_math: MathItem<N, T, D>) {
- return null as MmlNode;
- }
- }
- /**
- * Defaults used when ouput jax isn't specified
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- class DefaultOutputJax<N, T, D> extends AbstractOutputJax<N, T, D> {
- /**
- * @override
- */
- public typeset(_math: MathItem<N, T, D>, _document: MathDocument<N, T, D> = null) {
- return null as N;
- }
- /**
- * @override
- */
- public escaped(_math: MathItem<N, T, D>, _document?: MathDocument<N, T, D>) {
- return null as N;
- }
- }
- /**
- * Default for the MathList when one isn't specified
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- class DefaultMathList<N, T, D> extends AbstractMathList<N, T, D> {}
- /**
- * Default for the Mathitem when one isn't specified
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- class DefaultMathItem<N, T, D> extends AbstractMathItem<N, T, D> {}
- /*****************************************************************/
- /**
- * Implements the abstract MathDocument class
- *
- * @template N The HTMLElement node class
- * @template T The Text node class
- * @template D The Document class
- */
- export abstract class AbstractMathDocument<N, T, D> implements MathDocument<N, T, D> {
- /**
- * The type of MathDocument
- */
- public static KIND: string = 'MathDocument';
- /**
- * The default options for the document
- */
- public static OPTIONS: OptionList = {
- OutputJax: null, // instance of an OutputJax for the document
- InputJax: null, // instance of an InputJax or an array of them
- MmlFactory: null, // instance of a MmlFactory for this document
- MathList: DefaultMathList, // constructor for a MathList to use for the document
- MathItem: DefaultMathItem, // constructor for a MathItem to use for the MathList
- compileError: (doc: AbstractMathDocument<any, any, any>, math: MathItem<any, any, any>, err: Error) => {
- doc.compileError(math, err);
- },
- typesetError: (doc: AbstractMathDocument<any, any, any>, math: MathItem<any, any, any>, err: Error) => {
- doc.typesetError(math, err);
- },
- renderActions: expandable({
- find: [STATE.FINDMATH, 'findMath', '', false],
- compile: [STATE.COMPILED],
- metrics: [STATE.METRICS, 'getMetrics', '', false],
- typeset: [STATE.TYPESET],
- update: [STATE.INSERTED, 'updateDocument', false]
- }) as RenderActions<any, any, any>
- };
- /**
- * A bit-field for the actions that have been processed
- */
- public static ProcessBits = BitFieldClass('findMath', 'compile', 'getMetrics', 'typeset', 'updateDocument');
- /**
- * The document managed by this MathDocument
- */
- public document: D;
- /**
- * The actual options for this document (with user-supplied ones merged in)
- */
- public options: OptionList;
- /**
- * The list of MathItems for this document
- */
- public math: MathList<N, T, D>;
- /**
- * The list of render actions
- */
- public renderActions: RenderList<N, T, D>;
- /**
- * The bit-field used to tell what steps have been taken on the document (for retries)
- */
- public processed: BitField;
- /**
- * The list of input jax for the document
- */
- public inputJax: InputJax<N, T, D>[];
- /**
- * The output jax for the document
- */
- public outputJax: OutputJax<N, T, D>;
- /**
- * The DOM adaptor for the document
- */
- public adaptor: DOMAdaptor<N, T, D>;
- /**
- * The MathML node factory for the internal MathML representation
- */
- public mmlFactory: MmlFactory;
- /**
- * @param {any} document The document (HTML string, parsed DOM, etc.) to be processed
- * @param {DOMAdaptor} adaptor The DOM adaptor for this document
- * @param {OptionList} options The options for this document
- * @constructor
- */
- constructor (document: D, adaptor: DOMAdaptor<N, T, D>, options: OptionList) {
- let CLASS = this.constructor as typeof AbstractMathDocument;
- this.document = document;
- this.options = userOptions(defaultOptions({}, CLASS.OPTIONS), options);
- this.math = new (this.options['MathList'] || DefaultMathList)();
- this.renderActions = RenderList.create<N, T, D>(this.options['renderActions']);
- this.processed = new AbstractMathDocument.ProcessBits();
- this.outputJax = this.options['OutputJax'] || new DefaultOutputJax<N, T, D>();
- let inputJax = this.options['InputJax'] || [new DefaultInputJax<N, T, D>()];
- if (!Array.isArray(inputJax)) {
- inputJax = [inputJax];
- }
- this.inputJax = inputJax;
- //
- // Pass the DOM adaptor to the jax
- //
- this.adaptor = adaptor;
- this.outputJax.setAdaptor(adaptor);
- this.inputJax.map(jax => jax.setAdaptor(adaptor));
- //
- // Pass the MmlFactory to the jax
- //
- this.mmlFactory = this.options['MmlFactory'] || new MmlFactory();
- this.inputJax.map(jax => jax.setMmlFactory(this.mmlFactory));
- //
- // Do any initialization that requires adaptors or factories
- //
- this.outputJax.initialize();
- this.inputJax.map(jax => jax.initialize());
- }
- /**
- * @return {string} The kind of document
- */
- public get kind(): string {
- return (this.constructor as typeof AbstractMathDocument).KIND;
- }
- /**
- * @override
- */
- public addRenderAction(id: string, ...action: any[]) {
- const [fn, p] = RenderList.action<N, T, D>(id, action as RenderAction<N, T, D>);
- this.renderActions.add(fn, p);
- }
- /**
- * @override
- */
- public removeRenderAction(id: string) {
- const action = this.renderActions.findID(id);
- if (action) {
- this.renderActions.remove(action);
- }
- }
- /**
- * @override
- */
- public render() {
- this.renderActions.renderDoc(this);
- return this;
- }
- /**
- * @override
- */
- public rerender(start: number = STATE.RERENDER) {
- this.state(start - 1);
- this.render();
- return this;
- }
- /**
- * @override
- */
- public convert(math: string, options: OptionList = {}) {
- let {format, display, end, ex, em, containerWidth, lineWidth, scale, family} = userOptions({
- format: this.inputJax[0].name, display: true, end: STATE.LAST,
- em: 16, ex: 8, containerWidth: null, lineWidth: 1000000, scale: 1, family: ''
- }, options);
- if (containerWidth === null) {
- containerWidth = 80 * ex;
- }
- const jax = this.inputJax.reduce((jax, ijax) => (ijax.name === format ? ijax : jax), null);
- const mitem = new this.options.MathItem(math, jax, display);
- mitem.start.node = this.adaptor.body(this.document);
- mitem.setMetrics(em, ex, containerWidth, lineWidth, scale);
- if (this.outputJax.options.mtextInheritFont) {
- mitem.outputData.mtextFamily = family;
- }
- if (this.outputJax.options.merrorInheritFont) {
- mitem.outputData.merrorFamily = family;
- }
- mitem.convert(this, end);
- return (mitem.typesetRoot || mitem.root);
- }
- /**
- * @override
- */
- public findMath(_options: OptionList = null) {
- this.processed.set('findMath');
- return this;
- }
- /**
- * @override
- */
- public compile() {
- if (!this.processed.isSet('compile')) {
- //
- // Compile all the math in the list
- //
- const recompile = [];
- for (const math of this.math) {
- this.compileMath(math);
- if (math.inputData.recompile !== undefined) {
- recompile.push(math);
- }
- }
- //
- // If any were added to the recompile list,
- // compile them again
- //
- for (const math of recompile) {
- const data = math.inputData.recompile;
- math.state(data.state);
- math.inputData.recompile = data;
- this.compileMath(math);
- }
- this.processed.set('compile');
- }
- return this;
- }
- /**
- * @param {MathItem} math The item to compile
- */
- protected compileMath(math: MathItem<N, T, D>) {
- try {
- math.compile(this);
- } catch (err) {
- if (err.retry || err.restart) {
- throw err;
- }
- this.options['compileError'](this, math, err);
- math.inputData['error'] = err;
- }
- }
- /**
- * Produce an error using MmlNodes
- *
- * @param {MathItem} math The MathItem producing the error
- * @param {Error} err The Error object for the error
- */
- public compileError(math: MathItem<N, T, D>, err: Error) {
- math.root = this.mmlFactory.create('math', null, [
- this.mmlFactory.create('merror', {'data-mjx-error': err.message, title: err.message}, [
- this.mmlFactory.create('mtext', null, [
- (this.mmlFactory.create('text') as TextNode).setText('Math input error')
- ])
- ])
- ]);
- if (math.display) {
- math.root.attributes.set('display', 'block');
- }
- math.inputData.error = err.message;
- }
- /**
- * @override
- */
- public typeset() {
- if (!this.processed.isSet('typeset')) {
- for (const math of this.math) {
- try {
- math.typeset(this);
- } catch (err) {
- if (err.retry || err.restart) {
- throw err;
- }
- this.options['typesetError'](this, math, err);
- math.outputData['error'] = err;
- }
- }
- this.processed.set('typeset');
- }
- return this;
- }
- /**
- * Produce an error using HTML
- *
- * @param {MathItem} math The MathItem producing the error
- * @param {Error} err The Error object for the error
- */
- public typesetError(math: MathItem<N, T, D>, err: Error) {
- math.typesetRoot = this.adaptor.node('mjx-container', {
- class: 'MathJax mjx-output-error',
- jax: this.outputJax.name,
- }, [
- this.adaptor.node('span', {
- 'data-mjx-error': err.message,
- title: err.message,
- style: {
- color: 'red',
- 'background-color': 'yellow',
- 'line-height': 'normal'
- }
- }, [
- this.adaptor.text('Math output error')
- ])
- ]);
- if (math.display) {
- this.adaptor.setAttributes(math.typesetRoot, {
- style: {
- display: 'block',
- margin: '1em 0',
- 'text-align': 'center'
- }
- });
- }
- math.outputData.error = err.message;
- }
- /**
- * @override
- */
- public getMetrics() {
- if (!this.processed.isSet('getMetrics')) {
- this.outputJax.getMetrics(this);
- this.processed.set('getMetrics');
- }
- return this;
- }
- /**
- * @override
- */
- public updateDocument() {
- if (!this.processed.isSet('updateDocument')) {
- for (const math of this.math.reversed()) {
- math.updateDocument(this);
- }
- this.processed.set('updateDocument');
- }
- return this;
- }
- /**
- * @override
- */
- public removeFromDocument(_restore: boolean = false) {
- return this;
- }
- /**
- * @override
- */
- public state(state: number, restore: boolean = false) {
- for (const math of this.math) {
- math.state(state, restore);
- }
- if (state < STATE.INSERTED) {
- this.processed.clear('updateDocument');
- }
- if (state < STATE.TYPESET) {
- this.processed.clear('typeset');
- this.processed.clear('getMetrics');
- }
- if (state < STATE.COMPILED) {
- this.processed.clear('compile');
- }
- return this;
- }
- /**
- * @override
- */
- public reset(options: ResetList = {processed: true}) {
- options = userOptions(Object.assign({}, resetOptions), options);
- options.all && Object.assign(options, resetAllOptions);
- options.processed && this.processed.reset();
- options.inputJax && this.inputJax.forEach(jax => jax.reset(...options.inputJax));
- options.outputJax && this.outputJax.reset(...options.outputJax);
- return this;
- }
- /**
- * @override
- */
- public clear() {
- this.reset();
- this.math.clear();
- return this;
- }
- /**
- * @override
- */
- public concat(list: MathList<N, T, D>) {
- this.math.merge(list);
- return this;
- }
- /**
- * @override
- */
- public clearMathItemsWithin(containers: ContainerList<N>) {
- const items = this.getMathItemsWithin(containers);
- this.math.remove(...items);
- return items;
- }
- /**
- * @override
- */
- public getMathItemsWithin(elements: ContainerList<N>) {
- if (!Array.isArray(elements)) {
- elements = [elements];
- }
- const adaptor = this.adaptor;
- const items = [] as MathItem<N, T, D>[];
- const containers = adaptor.getElements(elements, this.document);
- ITEMS:
- for (const item of this.math) {
- for (const container of containers) {
- if (item.start.node && adaptor.contains(container, item.start.node)) {
- items.push(item);
- continue ITEMS;
- }
- }
- }
- return items;
- }
- }
- /**
- * The constructor type for a MathDocument
- *
- * @template D The MathDocument type this constructor is for
- */
- export interface MathDocumentConstructor<D extends MathDocument<any, any, any>> {
- KIND: string;
- OPTIONS: OptionList;
- ProcessBits: typeof BitField;
- new (...args: any[]): D;
- }
|