.. _startup-action: ################################# Performing Actions During Startup ################################# There are several ways to hook into the MathJax startup process so that you can do additional configuration, perform actions after the initial typesetting, and so on. The primary way to do this is to use one of two hooks that can be set in the :js:data:`startup` block of your configuration: the :func:`ready()` function and the :func:`pageReady()` function. The :func:`ready()` function is what MathJax calls when all the requested MathJax components have been loaded, and MathJax is ready to set up the internal objects needed to process the mathematics on the page (like the input and output jax). This function builds those structures, creates functions in the :js:data:`MathJax` object to make typesetting and format conversion easy for you, sets up the :js:meth:`pageReady()` call (described below), and creates a promise for when that is complete. You can override the :js:meth:`ready()` function with one of your own to replace that startup process completely, or to perform actions before or after the usual initialization. For example, you could do additional setup before MathJax creates the objects it needs, or you could hook into the typesetting promise to synchronize other actions with the completion of the initial typesetting. Examples of these are given below. The :js:meth:`pageReady()` function is performed when the HTML page is loaded and ready to be typeset, and MathJax itself is ready (all its components are loaded, and the internal objects have been created). The default is for :js:meth:`pageReady()` to perform the initial typesetting of the page, but you can override that to perform other actions instead, such as delaying the initial typesetting while other content is loaded dynamically, for example. The :js:meth:`ready()` function sets up the call to :js:meth:`pageReady()` as part of its default action. The return value of the default :js:meth:`pageReady()` is a promise that is resolved when the initial typesetting is finished. If you override the :js:meth:`pageReady()` method, your function should return a promise as well. For example, if your function calls :meth:`MathJax.startup.defaultPageReady()`, then you should return the promise that it returns (or a promise obtained from its :js:meth:`then()` or :js:meth:`catch()` methods). If you don't, then MathJax will think that the initial typesetting is complete even though it isn't, which can lead to incorrect behavior if other typesetting needs to be performed later. Using these two functions separately or in combination gives you full control over the actions that MathJax takes when it starts up, and allows you to customize MathJax's startup process to suit your needs. Several examples for common situations are given below. ----- .. _initialization-actions: Performing Actions During Initialization ======================================== If you want to perform actions after MathJax has loaded all the needed components, you can set the :js:meth:`ready()` function to a function that does the needed actions and calls :meth:`MathJax.startup.defaultReady()` to perform the usual startup process. Actions coming before the :meth:`MathJax.startup.defaultReady()` call are run before any initialization has been done. In particular, this is before any input or output jax are created, so this is where customization of the MathJax object definitions could be performed. For example, you could modify the configuration blocks at this point, or you could create subclasses of the MathJax objects that override some of their methods to produce custom behavior, and then register those subclasses with MathJax so they will be used in place of the originals. It is also possible to create TeX extensions on the fly and add them at this point. Actions coming after the :meth:`MathJax.startup.defaultReady()` call are run after initialization is complete. In particular, all the internal objects used by MathJax (e.g., the input and output jax, the math document, the DOM adaptor, etc) will have been created, and the typesetting and conversion methods will have been created in the :js:data:`MathJax` object. In addition, the variable :js:data:`MathJax.startup.promise` will hold a promise that is resolved when the initial typesetting is complete, but note that the typesetting has not yet been performed at this point. You can use this promise to set up actions that should occur after the initial typesetting is complete. This is discussed further in the next section. .. code-block:: javascript window.MathJax = { startup: { ready() { console.log("MathJax is loaded, but not yet initialized"); MathJax.startup.defaultReady(); console.log("MathJax is initialized and the initial typeset is queued, but hasn't run"); MathJax.startup.promise.then(() => { console.log("The initial typesetting is complete"); }); } } }; The console messages above indicate the MathJax's state at each point in the code. For example, you can't do any typesetting in the section before :js:meth:`MathJax.startup.defaultReady()`, while you potentially could after it, thought it is better to wait for the :js:data:`MathJax.startup.promise` before doing so, except in special circumstances where you know that the typesetting will not cause any extensions to be loaded dynamically. .. _full-width-numbers: Here is an example that uses the :js:meth:`ready()` function to convert the numbers in the full-width Unicode block to their ASCII counterparts for better formatting by MathJax. .. code-block:: javascript MathJax = { startup: { ready() { MathJax.startup.defaultReady(); MathJax.startup.document.inputJax.tex.preFilters.add( ({math}) => { math.math = math.math.replace(/[\uFF01-\uFF5E]/g, (c) => String.fromCodePoint(c.codePointAt(0) - 0xFF00 + 0x20)); } ); } } } This configuration adds a pre-filter to the TeX input jax that performs a substitution on the TeX source (``math.math``) for each expression being processed that looks for full-width numerals and replaces them with the corresponding ASCII numerals. .. _print-all-math: Here is an example that waits for the initial typesetting to complete and then prints to the console the TeX code for all the expressions on the page. .. code-block:: javascript MathJax = { startup: { ready() { MathJax.startup.defaultReady(); MathJax.startup.promise.then(() => { for (const item of MathJax.startup.document.math) { console.log(item.math); } }); } } } .. _asciimath-display-delimiters: Finally, here is an example that modifies the AsciiMath input jax to allow both in-line and display-mode equations by using ```...``` for in-line delimiters and `````` ``...`` `````` for display-mode delimiters. .. code-block:: javascript MathJax = { loader: {load: ['input/asciimath', 'output/chtml']}, output: {font: 'mathjax-newcm'}, asciimath: { delimiters: [['``','``'], ['`','`']] }, startup: { ready() { const {AsciiMath} = MathJax._.input.asciimath_ts; Object.assign(AsciiMath.prototype, { _compile: AsciiMath.prototype.compile, compile(math, document) { math.display = (math.start?.delim === '``'); const result = this._compile(math, document); const mstyle = result.childNodes[0].childNodes.pop(); mstyle.childNodes.forEach(child => result.appendChild(child)); if (math.display) { result.attributes.set('display', 'block'); } return result; } }); MathJax.startup.defaultReady(); } } }; This saves the old AsciiMath ``compile()`` function and replaces it with a new one that removes the original ``mstyle`` element created by AsciiMath that sets the display mode, and sets the mode on the outer ``math`` tag depending on the delimiter used. ----- .. _post-typesetting: Performing Actions After Typesetting ==================================== Often, you may need to wait for MathJax to finish typesetting the page before you perform some action. To accomplish this, you can override the :js:meth:`ready()` function, having it perform the :js:meth:`MathJax.startup.defaultReady()` action, and then use the :js:data:`MathJax.startup.promise` to queue your actions; these will be performed after the initial typesetting is complete. .. code-block:: javascript window.MathJax = { startup: { ready: () => { MathJax.startup.defaultReady(); MathJax.startup.promise.then(() => { console.log('MathJax initial typesetting complete'); }); } } }; As an alternative, you can override the :js:meth:`pageReady()` function, and use the promise returned from the :meth:`MathJax.startup.defaultPageReady()` function: .. code-block:: javascript window.MathJax = { startup: { pageReady: () => { return MathJax.startup.defaultPageReady().then(() => { console.log('MathJax initial typesetting complete'); }); } } }; Be sure that you return the promise that you obtain from :js:meth:`then()` method, otherwise :js:data:`MathJax.startup.promise` will resolve before the initial typesetting (and your code) has been performed, which may cause other code to run too soon. Our first example above shows how to use :js:data:`MathJax.startup.promise` within the :js:meth:`ready()` function, but that promise is set up as soon as MathJax is loaded, so it can be used outside of the :js:meth:`ready()` function. You must be careful, however, that MathJax is loaded before you try to use it. For example, if you use :attr:`defer` or :attr:`async` attributes on the script tag that loads MathJax, then you need to be sure your code that uses :js:meth:`MathJax.startup.promise` doesn't run until after MathJax has been loaded. One way to do that is to use :attr:`defer` on both the script that loads MathJax and the one that uses :js:data:`MathJax.startup.promise`, and to put your script **after** the one that loads MathJax. Since deferred scripts run in the order in which they appeared in the HTML document, that will guarantee that :js:data:`MathJax.startup.promise` will be defined when you use it. For example, .. code-block:: html where the :file:`mathjax-dependent-code.js` file contains the :js:data:`MathJax.startup.promise` reference, such as .. code-block:: javascript MathJax.startup.promise.then(() => { console.log('MathJax initial typesetting complete'); }); In this case, the MathJax-dependent code won't run until after MathJax is loaded. You can't use the :attr:`defer` attribute on a script tag without a :attr:`src` attribute, but if you want to use an in-line script that uses :js:data:`MathJax.startup.promise`, then you can use a script with :attr:`type="module"`, as these have the :attr:`defer` attribute by default. For example, .. code-block:: html will work to guarantee that the promise is defined when the script is executed. ----- .. _actions-summary: Summary ======= The following terms were discussed above: .. js:method:: ready() This is the function called when MathJax has loaded the needed components and is ready to start setting up the objects needed for typesetting the document. You can override it in the ``startup`` section of the :js:data:`MathJax` configuration object in order to perform customization when MathJax loads, or to set up actions to perform after the initial typesetting is complete. .. js:method:: MathJax.startup.defaultReady() This is the default for the :js:meth:`ready()` function above. You can call it from your ``ready()`` function in order to perform the usual ``ready()`` action. .. js:method:: pageReady() This is the function called when the page is loaded and MathJax is ready to perform typesetting. You can override it in the ``startup`` section of the :js:data:`MathJax` configuration object in order to do your own processing, or to set up actions to perform after the initial typesetting is complete. :returns: A promise that resolves when the actions taken by your function is complete. .. js:method:: MathJax.startup.defaultPageReady() This is the default for the :js:meth:`pageReady()` function above. You can call it from your ``pageReady()`` function in order to perform the usual ``pageReady()`` action. :returns: A promise that resolves when the initial page typesetting is complete. .. js:attribute:: MathJax.startup.promise A promise that resolves when MathJax completes its initially typesetting. |-----|