Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting started documentation #1

Open
dschwen opened this issue Jun 9, 2015 · 12 comments
Open

Getting started documentation #1

dschwen opened this issue Jun 9, 2015 · 12 comments

Comments

@dschwen
Copy link

dschwen commented Jun 9, 2015

I'm looking to use MathJax from one of my packages and having a single central MathJax package sure beats having a MathJax installation for each package.

But how does this work? Atom does not have package dependencies (as in a global package is installed rather than a nested package). Does your package just expose a global MathJax object? Should my package check if the global object exists and if not prompt the user to install the Plugin (or display a message to that effect on Settings page)?

A few lines added to the README.md would go a long way to clear up those questions.

@mangecoeur
Copy link

Same problem...

@Galadirith
Copy link
Owner

@dschwen @mangecoeur I'm so sorry that its taken me so long to get back to you both. I'll first try to answer your questions, then if you feel like reading on I'll explain how i'd like to improve the package :D

Answers to your questions

having a single central MathJax package sure beats having a MathJax installation for each package

100% agree, especially when a full installation of MathJax can be ~55MB.

Does your package just expose a global MathJax object?

mathjax-wrapper actually does nothing to load MathJax. I wanted a way to install a full distribution of MathJax onto an end users computer without having to ask to perform any actions outside of atom. So the only meaningful part of the package is in package.json and lib/main.coffee is about as minimal as you can get.

Does your package just expose a global MathJax object? Should my package check if the global object exists and if not prompt the user to install the Plugin

I originally designed this only as an extension to my package markdown-preview-plus and so all of the loading and calls to MathJax are done in lib/mathjax-helper. I realise how terrible that design was but at the time atom was younger and no one else wanted to bring MathJax to atom so I hacked away as best I could.

To explain the process that I currently use, within markdown-preview-plus (not mathjax-wrapper) I call loadMathJax (which has a hard coded path to the MathJax node module installed by mathjax-wrapper) , and then every time I want to convert (in my case) LaTeX blocks into symbolic equations I call mathProcessor. The LaTeX blocks are assumed to be DOM elements that contain child script elements as described in The MathJax Processing Model. I configure MathJax not to perform the preprocessing step described at the top of The MathJax Processing Model because for a dynamic environment like atom i've found I can write better scripts to identify LaTeX blocks then MathJax can; it was never designed for such an environment.

So what should your package do. You could copy mathjax-helper and perform those steps yourself in your own package, but if any other packages have already loaded a MathJax instance, will lead to conflicts and errors. You could add a condition to check to see if MathJax has already been loaded, but you then don't know how its been configured, or if its configured with the features you need. I'll talk about these problems below.

Questions for you

Do you need a full MathJax installation?

The reason why I wanted a full MathJax installation is that I wanted HTML rendering. But if you are happy with any other type output MathJax is able to do then without question your best option is mathjax-node which is maintained by the MathJax team.

What do you need a full MathJax installation for?

If the answer is yes to the last question then what would be incredibly helpful is what you would like a full installation of MathJax for so that I can improve mathjax-wrapper for your needs.

How to make this package better

So in its current state if other package's start using and potentially loading multiple instances of MathJax as i've described markdown-preview-plus does there will be lots of conflicts, and errors, so the most important thing is to move the loading of MathJax from markdown-preview-plus to mathjax-wrapper. Configuring MathJax can only be done when it loads (I believe).

(mathjax-node gets around both of these by using jsdom. I have been trying to get a full version of MathJax to work with jsdom by adapting what is in mathjax-node but so far iv had no joy, and its entirely possible that it just wont work.)

So my idea is to load MathJax with a minimal configuration very similar to here. In reality i've found such a configuration is not limiting at all, but I would very much like to hear your opinions on what you think should be included. Then I can expose a couple of API methods in mathjax-wrapper that could take in either a DOM element or a string of HTML and output a symbolic equation.

If you've made it to the end then thanks so much for taking the time to read everything. I plan to push on with the changes i've suggest and try to make mathjax-wrapper the package it should have been rather then the terrible hack that I made it, but please let me know your thoughts, and if you have any further question or need me to clarify anything I've said please let me know :D

@mangecoeur
Copy link

@Galadirith Thanks for the extensive reply! I also tried to use mathjax-node but it ended up being a rabbit-hole of bugs and incompatibility (starting with the version of jsdom they use is incompatible with the version of node using in atom sigh) .

I was wondering how you create the script tags for the Math code - do you just create a DOM element from a string? I've had a lot of difficulty understanding how MathJax does its thing - it seems really designed to process math present in an existing page, but while it should also be able to generate math just for a LaTeX string it seems unduly difficult to actually do so...

@mangecoeur
Copy link

ps. if you are interested in making a "proper" package, I think a better place to start would be the official MathJax-node package- but it needs some tweaks and updates to get it working within Atom (I got somewhere with my private fork of the project)

@Galadirith
Copy link
Owner

starting with the version of jsdom they use is incompatible with the version of node using in atom sigh

@mangecoeur I didn't know that about jsdom thanks for telling me, that's very useful to know.

it seems really designed to process math present in an existing page

I agree. But actually its not too difficult to make it process dynamic content.

(If you have the time I would strongly suggest reading The MathJax Processing Model. MathJax has a lot of features and configuration options, but this doc page for me was the most useful and important in understanding how MathJax' works, and I'll reference some of the processing stages they talk about on that page)

I was wondering how you create the script tags for the Math code

Creating DOM elements from a string its exactly where I start. You've then got to ask MathJax to process it. I'll give you the steps I use to get it to work:

  1. Create DOM element
    Take you LaTeX string and set it as the inner HTML of a script DOM element. The script element should have its type set to either type="math/tex" or type="math/tex; mode=display", and these correspond inline and displayed maths equations in a normal LaTeX document.

    So if you wanted to add a displayed equation to the body of a page you might do something like

    var equation = document.createElement("script");
    equaion.type   = "math/tex; mode=display";
    equation.textContent = latexString; // latexString being your LaTeX string ;D
    var appendedEquation = document.getElementsByTagName("body")[0].appendChild(equation);

    Now you can configure MathJax to parse DOM elements for delimiters like $$ and it will then convert them to these script tags for you. This is the preprocessing stage discussed in the MathJax doc page I suggested and specifically the TeX preprocess for MathJax is discussed here. I choose not to load the preprocessor for TeX which is why you have to create these script blocks manually. While its a little more effort on your part, I have found that for dynamic pages it saves a lot of headaches by having the coder identify where LaTeX blocks should be rather than MathJax.

  2. Process equation with MathJax
    So we've got our script elements added and now we need to get MathJax to convert them to typeset equations. In our example you would run:

    MathJax.Hub.Queue(["Typeset",MathJax.Hub,appendedEquation]);

    Running this on appendedEquation or any parent will search for the <script> elements we added and convert them to typeset equations.

    You can learn more about typesetting equations dynamically on the docs page Modifying Math on the Page.

ps. if you are interested in making a "proper" package, I think a better place to start would be the official MathJax-node package

I was originally using mathjax-node but I decided a full MathJax install just gives better results. You can find a discussion I had about that Galadirith/markdown-preview-plus#21. I would love to be able to add the features (specifically HTML rendering) to a fork of mathjax-node but I've tried and failed with that in the past so I don't have any plans to continue that right now. But I will be adding a small API to this package to basically simplify the steps I described to you above.

Sorry for another stupidly long post. I hope that answers you questions but please to post back if anything's unclear or you have more questions. Also I'd be more than happy to look at any of your MathJax code on GitHub if your having trouble getting it to work (although I can't promise I'll be able to make it work either :D)

@mangecoeur
Copy link

Thanks, I got it working nicely in the end (https://atom.io/packages/preview-inline) - in the end I bundled MathJax myself, copying your mathjax-helper and Mathjax tarball into my project, since Atom doesn't yet have a good way to auto-install dependent packages i though it might be more reliable than requiring an extra package install step.

I had a think about mathjax node - I think the JSDOM issue could be circumvented by making an atom-specific implementation, creating a hidden instance of atom's BrowserWindow instead of using jsdom's virtual window. If you look at the mathjax-node code there's only a small bit that inits jsdom that could be replaced with browser window.

The lack of HTML rendering is indeed annoying though, I'm not sure how this could work - from what I see MathJax relies on loading css and fonts dynamically, meaning the Mathjax object has to be available in the host page (you cant just generate an html chunk and pass that around).

@mangecoeur
Copy link

another note - according to https://github.com/mathjax/MathJax/wiki/Understanding-mathjax-performance it's possible to throw away most of what comes in the MathJax download if you assume you are using a modern browser, shrinking the package from 55MB to 5 MB. At these kinds of sizes it probably makes more sense to have an NPM package rather than an atom package, since the cost of embedding a mathjax install in each package that needs it is much smaller. If we are able to use atom's BrowserWindow then we also don't need JSDOM and friends shrinking the package even further. I'm quite optimistic about this, i will try hacking on it sometime soon to see what happens.

@dschwen
Copy link
Author

dschwen commented Jul 22, 2015

👍

@Galadirith
Copy link
Owner

Thanks, I got it working nicely in the end

@mangecoeur Fantastic :D

according to https://github.com/mathjax/MathJax/wiki/Understanding-mathjax-performance it's possible to throw away most of what comes in the MathJax download

I haven't seen that page before but that was some really exciting stuff. So a while ago I learnt about MathJax-grunt-cleaner which manages the task of removing the extra redundant stuff. I've just spun in up now, trimmed MathJax to what is needed and I've ended up with a drop in replacement for mathjax-complete-node that is ~2MB in size at 233 files rather than ~55MB as ~32000 files! and it works :D This is super awesome. Here are some reproducible steps:

  1. Grab MathJax 2.4.0
    Either download the release directly or checkout 2.4.0 from the repo (which you may already have cloned localy?)

    For some reason 2.5.x is exhibiting some unusual behaviour for me in Atom, and it appears to be slower. But please try out a 2.5.x version if like and let me know how it goes.

  2. Copy mathjax-grunt-cleaner to MathJax
    If you are basing this on MathJax 2.4.0 then I suggest using mathjax/MathJax-grunt-cleaner@d41e74c rather than the HEAD which was meant for cleaning 2.5.x. You could also grab the gruntfile.js that I used from my gist here. You'll also need its package.json and place both those files in the root of your MathJax directory.

    Run npm install to get grunt and its dependencies installed locally.

  3. Configure the cleaner
    Its a little (very :D) confusing gruntfile but basically the only bit that matters is the template task. You comment out the components of MathJax that you want to keep and then run grunt template.

  4. Copy the cleaner MathJax files
    Replace the contents of node_modules/MathJax with the cleaned file set.

creating a hidden instance of atom's BrowserWindow instead of using jsdom's virtual window

I had not thought about using a BrowserWindow but that's definitely a great idea. I guess it might be possible to just copy over the styles that MathJax loads to the parent window?

Do you think we really need to spin it off into an isolated window though? The reason to do this would be to allow anyone to configure MathJax as they need it to be. But I'm not really sure if this is something that most people need. Compared to KaTeX for example, KaTeX has practically no configurability, and people seem to be happy with it? I belive that using a single configuration similar to TeX-AMS_HTML (which is essentially what mathjax-helper just without mml output) is perfect for 99% of tasks.

But I would really love to hear you thoughts on why you thinks its important to spin out to another BrowserWindow.

@Galadirith
Copy link
Owner

@dschwen 👍 ;D

@mangecoeur And I'm so sorry I only just noticed in mangecoeur/preview-inline@51c9807 that you've already used mathjax-grunt-cleaner.

@Galadirith
Copy link
Owner

@mangecoeur nice work on mangecoeur/markdown-preview-plus ;D

To give an update I've been working on magnetite today which is to try capture what @mangecoeur has done with an MathJax-custom except as a node module. I haven't published this to npm yet as I haven't written any tests yet and its probablly buggy, but I would aim to publish it shortly. However please check it out if you would like to, let me know your thoughts, what you think should be improved.

Initially magnetite with simply guard against loading MathJax multiple times, but I would like to in future versions have MathJax load in to a separate render process as @mangecoeur suggest previous :D

@mangecoeur
Copy link

@Galadirith glad you mentioned your new work, I literally sat down last night to give the same thing a try! I did some research which I will capture as an issue on your magnetite project

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants