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

(html) Input field in mathjax rendered equation ? #2210

Closed
jhoobergs opened this issue Oct 4, 2019 · 6 comments
Closed

(html) Input field in mathjax rendered equation ? #2210

jhoobergs opened this issue Oct 4, 2019 · 6 comments
Labels
Accepted Issue has been reproduced by MathJax team Code Example Contains an illustrative code example, solution, or work-around Fixed Test Needed v3.0

Comments

@jhoobergs
Copy link

I'm currently trying to convert a piece of code (that I didn't write) from mathjax 2.7 tot mathjax 3.

At https://github.com/XimeraProject/server/blob/master/public/javascripts/main.js#L317 you will find code that (maybe in a hacky way?) makes it possible to add an \answer command which places an input field at that location.

For example: \frac {4}{(m+1)!}-\frac {1}{m!}=\frac {\answer {3-m}}{(m+1)!} gives
image

It uses a lot of mathjax 2 specific things it seems, so I can't find how to port this to mathjax 3. I tried reading the mathjax 3 api documentation, but the documentation doesn't seem to be done yet.

My question actually is: 'Is it possible to easily let a custom tex command add an html input tag inside mathjax ? Or is it somehow possible to add an input field ?'

@dpvc
Copy link
Member

dpvc commented Oct 5, 2019

Here is a quick and dirty example of a configuration that provides an input field in CommonHTML output (there is a bug in the SVG support for annotation-xml that prevents it from working there). There is also a bug in the TeX input handling of annotation-xml nodes, but the code below includes a patch for that, which will not be needed when that bug is fixed.

Of course, you would probably want to create a custom TeX extension rather than doing this in-line, as in this example, but this allows you to try it out without compiling or webpacking anything. The things referring to MathJax._ would normally be handled via import commands if you are making a custom extension, and you would load the extension using the loader block rather than setting a startup.ready() function.

Anyway, perhaps that can get you started.

MathJax = {
  tex: {packages: {'[+]': ['input']}},
  startup: {
    ready() {
      const Configuration = MathJax._.input.tex.Configuration.Configuration;
      const CommandMap = MathJax._.input.tex.SymbolMap.CommandMap;
      const TEXCLASS = MathJax._.core.MmlTree.MmlNode.TEXCLASS;
      
      MathJax._.input.tex.FilterUtil.default.cleanAttributes = 
        function (arg) {
          arg.data.root.walkTree((mml) => {
            const attribs = mml.attributes;
            if (!attribs) return;
            for (const key of attribs.getExplicitNames()) {
              if (attribs.attributes[key] === mml.attributes.getInherited(key)) {
                delete attribs.attributes[key];
              }
            }
          });
        };
      new CommandMap('input', {input: 'Input'}, {
        Input(parser, name) {
          const xml = parser.create('node', 'XML');
          xml.setXML(MathJax.startup.adaptor.node('input', {xmlns: 'http://www.w3.org/1999/xhtml'}));
          parser.Push(
            parser.create('node', 'TeXAtom', [
              parser.create('node', 'semantics', [
                parser.create('node', 'annotation-xml', [
                  xml
                ], {encoding: 'application/xhtml+xml'})
              ])
            ], {texClass: TEXCLASS.ORD})
          );
        }
      });
      Configuration.create('input', {handler: {macro: ['input']}});

      MathJax.startup.defaultReady();
    }
  }
};

@jhoobergs
Copy link
Author

@dpvc Thanks for your answer. I didn't get the time to test it yet.
I'm currently plauing around with the new server side rendering and i'm wondering how your answer would change for server side rendering ?

@dpvc
Copy link
Member

dpvc commented Oct 19, 2019

There is an issue with using this server-side, which is that MathJax needs to be able to measure the HTML in order to tell how big it is, and that is done by asking the browser to measure the bounding box for the HTML. That works in the browser, but not on the server (the LiteDOM doesn't do any actual layout).

The intended solution is to have you provide explicit size information in the <annotation-xml> node, but that currently isn't implemented.

On the other hand, the computation of the size of the <annotation-xml> also isn't implemented yet in the browser, either, so in that sense, it doesn't really matter. MathJax will not know the size of the input box, and that may cause layout issues in some expressions (whether it was created in the browser or the server at the moment).

dpvc added a commit to mathjax/MathJax-src that referenced this issue Nov 17, 2019
@dpvc dpvc added Accepted Issue has been reproduced by MathJax team Merged Merged into develop branch Test Needed labels Nov 17, 2019
@dpvc dpvc added this to the MathJax v3.0.1 milestone Dec 31, 2019
@dpvc dpvc added Fixed v3.0 and removed Merged Merged into develop branch labels Feb 10, 2020
@dpvc dpvc closed this as completed Feb 10, 2020
@baszczewski
Copy link

@dpvc
Hi. In my project I also need use input element inside equation. I just discovered your code:

 const xml = parser.create('node', 'XML');
      xml.setXML(MathJax.startup.adaptor.node('input', {xmlns: 'http://www.w3.org/1999/xhtml'}));
      parser.Push(
         parser.create('node', 'TeXAtom', [
            parser.create('node', 'semantics', [
               parser.create('node', 'annotation-xml', [
                  xml
               ], {encoding: 'application/xhtml+xml'})
            ])
         ], {texClass: TEXCLASS.ORD})
      );

Unfortunetly I ve got "Cannot read property 'outerHTML' of null" when use xml node inside this parser. Could you provide me any tips how to resolve it?

@dpvc
Copy link
Member

dpvc commented Nov 15, 2020

@baszczewski:

The update mentioned in the code turned out to require an additional parameter to the mml.setXML() command (to pass it the adaptor being used). That is why you are getting the outerHTML call of null error. So you need to change

xml.setXML(MathJax.startup.adaptor.node('input', {xmlns: 'http://www.w3.org/1999/xhtml'}));

to

xml.setXML(MathJax.startup.adaptor.node('input', {xmlns: 'http://www.w3.org/1999/xhtml'}), MathJax.startup.adaptor);

It turns out that the issue was arising because of the assistive MathML extension, which need to serialize the internal MathML in order to create the hidden MathML in the page for assistive technology to use. That also serializes the contents of the annotation-xml node, but as it is HTML, it ends up being serialized without closing tags for the input element, and that means it is not valid XML, and causes an additional error. To resolve that use

xml.setXML(MathJax.startup.adaptor.node('input', {xmlns: 'http://www.w3.org/1999/xhtml'}), MathJax.startup.adaptor);
xml.getSerializedXML = function () {return this.adaptor.outerHTML(this.xml) + '</input>'};

This is a hack to get the closing tag in this case, but a more general solution is needed to handle arbitrary annotation-xml content. That will be for a future update.

@dpvc dpvc added the Code Example Contains an illustrative code example, solution, or work-around label Apr 1, 2021
@evenstensberg
Copy link

evenstensberg commented Nov 29, 2021

Could you update the docs with an exact example and walkthrough?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Accepted Issue has been reproduced by MathJax team Code Example Contains an illustrative code example, solution, or work-around Fixed Test Needed v3.0
Projects
None yet
Development

No branches or pull requests

4 participants