Load MathJax

I’m trying to load MathJax, but not from its CDN, but from local files (the server will be offline).

So:

  1. I downloaded MathJax: http://docs.mathjax.org/en/latest/installation.html#obtaining-mathjax-via-an-archive
  2. Placed the whole (2.7MB )in web/static/vendor/js/
  3. Load in the App (/web/templates/layout/app.html.eex):
<script src="<%= static_path(@conn, "/vendor/mathjax/MathJax.js") %>"></script>
  1. Configure brunch-config.js to precompile it (am i right?)
stylesheets: {
      joinTo: "css/app.css",
      order: {
        before: [
                  "web/static/vendor/js/jquery.min.js",
                  "web/static/vendor/js/lodash.js",
                  "web/static/vendor/js/mathjax/MathJax.js"
                ],
        after: ["web/static/css/app.css"] // concat app.css last
      }
    }
  1. When I start the server, it takes too long:
08 Sep 11:54:29 - info: compiling...
08 Sep 11:54:33 - info: compiling
08 Sep 11:54:37 - info: compiling.
08 Sep 11:54:42 - info: compiling..
08 Sep 11:54:48 - info: still compiling...
08 Sep 11:54:51 - info: compiled 264 files into 2 files, copied 3 in 54.2 sec

and the it crashes:

GET /vendor/mathjax/MathJax.js
[debug] ** (Phoenix.Router.NoRouteError) no route found for GET /vendor/mathjax/MathJax.js (Project.Router)
    (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
    (cowboy) /vagrant/www/updevs_js/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

Why this happen? What do I need to do to load MathJax.js?

  1. You are bundling your JavaScript with your CSS.
  2. The default configuration bundles all JavaScript into the assets/js/app.js bundle. Bundled code isn’t accessed with a separate <script> element but via import in the JavaScript code itself.

Please have a look at my recent post here.

1 Like

Hi, @peerreynders, I read your post, however I don’t understand it too much (I’m still learning phoenix)

  1. Should I delete it from brunch-config.js then?
  2. How do I import it in app.js?
  3. Should I keep the mathjax folder in static/vendor?
  4. Should I remove <script src="<%= static_path(@conn, "/vendor/mathjax/MathJax.js") %>"></script>, from app.html.eex then?

To get the hang of things I would recommend you go through the motions of that post - using some working minimal html page with a minimal mathjax example - and then splice that into a phoenix practice project as described. mathjax is available through npm.

Basically this has less to do with Phoenix but more with being forced into the modern world of JavaScript package management with npm (or yarn) and ES6 (or CommonJS) modules.

1 Like

I see. MathJax npm doesn’t include PNG fonts. Is there a mechanics with loading the MathJax files from vendor, or any other path to be loaded without using npm?

I am loading MathJax from app.js as you said:

import MathJax from "mathjax/MathJax.js"

But the server keeps taking so much time, and also got this error (But the package is not and npm one, this is pretty confusing :icon_sad: )

error: Resolving deps of web/static/js/app.js failed. Could not load module '/MathJax.js' from '/vagrant/www/updevs_js/web/static/js'. Possible solution: add '' to package.json and `npm install`.

If you look in the default brunch-config.js:

  conventions: {
    // This option sets where we should place non-css and non-js assets in.
    // By default, we set this to "/assets/static". Files in this directory
    // will be copied to `paths.public`, which is "priv/static" by default.
    assets: /^(static)/
  },

So if you put mathjax somewhere under assets/static (like assets/static/vendor) it should be copied to the equivalent public location (e.g. /vendor) at which point it should become accessible legacy-style via a <script> tag. /js and /css are essentially destinations for the brunch bundles.

So if you have assets/static/vendor/js/mathjax/MathJax.js a script element should be able to pick it up at /vendor/js/mathjax/MathJax.js (you were missing the /js in your opening post).

The Static Assets – Phoenix v1.3.0 - HexDocs guide seems to be AWOL right now but Google still has a cached copy.

FYI: mathjax-node-svg2png seems to supply the PNG functionality.

1 Like

I am making progress, I decided to go your way and installed the npm package. It being loaded fast as a node module, however the server keeps asking for more extensions:

GET /extensions/MathMenu.js
[debug] ** (Phoenix.Router.NoRouteError) no route found for GET /extensions/MathMenu.js

What should I do to load complete mathjax and to configure it? Have you done it already?

I follow the requiremente using:

static/js/mathjax.js

import mathjax from "mathjax"
import MathMenu from "mathjax/extensions/MathMenu.js"
import MathZoom from "mathjax/extensions/MathZoom.js"

and in static/js/mathjax.js

import mathjax from "./mathjax"

Now there are no errors, but MathJax doesn’t render.

I’m getting the impression that MathJax’s entire architecture is inherently “Module hostile” (i.e. resistant to JS modernization - due to it’s size it heavily relies on lazy loading parts of itself) - support for bundling (browserify, webpack, etc.) was once a v3.0 milestone but has since been removed. Also the mathjax-node-* packages seem to be entirely targeted for server-side nodeJS use rather than “modularized browser JS”.

So I’m left with the impression that this has to be done “old-school” (though frankly I’d try to avoid this monster at all cost). I essentially dropped the entire massive archive into priv/static - i.e.

priv/static/MathJax-2.7.2

i.e. MathJax is so massive that you need to keep it away from brunch.

in lib/mjx_web/templates/layout/app.html.eex I placed:

    <title>Hello Mjx!</title>
    <link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">

    <script type="text/x-mathjax-config">
     MathJax.Hub.Config({
       extensions: ["tex2jax.js"],
       jax: ["input/TeX","output/HTML-CSS"],
       tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]}
     });
    </script>
    <script type="text/javascript" src="<%= static_path(@conn, "/MathJax-2.7.2/MathJax.js") %>"></script>
  </head>

In lib/mjx_web/templates/page/index.html.eex I placed (between the two col-lg-6 <div> elements):

  <p>
    \begin{align}
    \dot{x} & = \sigma(y-x) \\
    \dot{y} & = \rho x - y - xz \\
    \dot{z} & = -\beta z + xy
    \end{align}
  </p>

Finally I had to go into lib/mjx_web/endpoint.ex

  plug Plug.Static,
    at: "/", from: :mjx, gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)

and add the mathjax folder MathJax-2.7.2

  plug Plug.Static,
    at: "/", from: :mjx, gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt MathJax-2.7.2)

at that point the Lorenz Equations showed up on the home page.

However rendering is sluggish enough for the unrendered source to briefly show at times upon reloading.

Edit: Modification of endpoint.ex is unnecessary if instead of

priv/static/MathJax-2.7.2

you use

priv/static/js/MathJax-2.7.2 (and of course change the reference in the app.html.eex template).

4 Likes

Thanks! @peerreynders. I know that MathJax is not suited as a node module but the solution you gave is perfect :slight_smile:

Ultimately it has nothing to do with NodeJS - modules have become an essential component of modern JavaScript as a means of dependency management - once JavaScript was used to do more than just twiddle with the DOM and CSS.

Browserify was created in 2011 to leverage “dependency management” via modules on browsers - and with ES2015 modules have become an intrinsic part of JavaScript.

<script type="module"> is now supported in Chrome 61 but effectively modules have been available for in-browser development for the past 6 years through build tools (just like transpilers made it possible to use future ECMAScript features before browsers implemented them).

Now this is total speculation on my part but I suspect that the “MathJax consortium” has seen the writing on the wall. Back in 2009 it was probably OK to just cater to PC-based browsers with unmetered connections and co-opt precious processing time from the only available GUI thread to do a lot of background rendering work. These days performance could be improved by using web workers but that wouldn’t address the real issue - web content consumption through mobile devices via metered connections and power-conscious browsers.

So I interpret the proliferation of MathJax-node-* projects as a potential move toward “server-supported-rendering” - i.e. while the “math mark-up” may be assembled inside the browser, that mark-up is sent to the server for rendering and the resulting SVG is returned for inclusion in the page. That should allow for a minimally sized client-side MathJax library with the tradeoff of increased client-server chatter - and of course the need for server-side (NodeJS) support.

1 Like

Thanks, @peerreynders. Your step-by-step instructions were very helpful to me as well!

1 Like