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

MathView overzealously texifying #638

Closed
Alex-Jordan opened this issue Sep 23, 2015 · 22 comments
Closed

MathView overzealously texifying #638

Alex-Jordan opened this issue Sep 23, 2015 · 22 comments

Comments

@Alex-Jordan
Copy link
Contributor

MathView is turning "in" into the "element of" symbol as in \in. So for example, a student typing inf or infinity is seeing output of the form \in f or \in f\in ity.

Not sure if it there are other scenarios as common as "inf", but it also does this for other strings. So if there were a string answer "topology", it is showing you output from \top o\log y.

Is this just a configuration issue? If so, where is MathView configured?

Or maybe it's just assumed that with string answers, the student doesn't activate MathView?

@goehle
Copy link
Member

goehle commented Sep 23, 2015

MathView renders whatever students put in the answer box using MathJax. Unfortunately there isn't a lot of information available on the js side for MathJax to determine what strings are special for a given problem or what they should translate to. Basically, given that all different answers are typed into a text box, it is difficult to tell if a given sequence of characters should be rendered as mathematics or as text. Some options I can think of would be

  • Exporting some sort of information as data in the answer blank that MathJax could use to render stuff. This does make some of the PG components public (like what the possible strings are). This would be the more complicated solution.
  • Adding a list of common WeBWorK strings to the MathView mathjax configuration so that it will at least render things like inf correctly. This would be the simpler solution but would not cover everything.

@dpvc
Copy link
Member

dpvc commented Sep 23, 2015

I think the problem is actually that MathView uses MathJax's AsciiMath format to parse the student answer, and although AsciiMath is similar to WeBWorK's algebra notation, it is not identical. In particular, AsciiMath includes many more symbols than WeBWorK does. For example, AsciiMath has all the Greek letters (via alpha and beta, etc), and prod, sum and int, and RR for the set of reals, and -< for TeX's \prec, and in for \in, and oo for infinity, and a whole lot of others. So AsciiMath parses all kinds of things that WeBWorK doesn't. It also uses (: and :) for vectors rather than < and > (since those mean less-than and greater-than in AsciiMath). SO there are a number of significant differences between the two, and the AsciiMath results are only moderately equivalent to the MathObject results, in general.

A solution might be to have the MathJax configuration for MathView remove all the unwanted symbols from AsciiMath (and perhaps add "inf" and "infinity" for \infty, and perhaps set < and > as vector brackets). It would be rather extensive changes to the AsciiMath list, but it could be done.

@goehle
Copy link
Member

goehle commented Sep 23, 2015

I had not thought about removing the unwanted asciimath symbols, but that would also be necessary. I already change ** to a power and not a star, so the pathway for these changes are there.

@apizer
Copy link
Member

apizer commented Oct 1, 2015

One has exactly the same issues with "Show Past Answers" for the same reason. See the discussion at http://webwork.maa.org/moodle/mod/forum/discuss.php?d=3771
As far as "Show Past Answers" is concerned, I and others would much prefer to see exactly what the student entered, not a typeset version of their answer which, in the original case that let to the above post, was confusing for the instructor.

@dpvc
Copy link
Member

dpvc commented Oct 1, 2015

I agree with Arnie; I prefer to see what the student actually typed, not a typeset version.

@goehle
Copy link
Member

goehle commented Oct 1, 2015

Sorry. I just thought that having it easier to read would be worth it
overall. I'll make a pull request to change it back.

@dpvc
Copy link
Member

dpvc commented Oct 1, 2015

Could it be a choice? Perhaps a toggle on the page that would switch between the views? I'm sure there are people who prefer the typeset version (or at times do).

@mgage
Copy link
Member

mgage commented Oct 2, 2015

The other possibility is an overlay. In responding to entries for the current problem I chose one version (usually typeset mode) and allowed you to get the direct input as a popup by hovering over the field. This wasn't enough for some people :-) but it allowed access to the alternative format.

@apizer
Copy link
Member

apizer commented Oct 2, 2015

One problem, and the one Gavin first reported, is that in some cases you can not tell the difference between typeset mode and direct input if you are not specifically testing for this (and most instructors would have no reason to). If you look at the forum post, Gavin did not figure out what was going on (he didn't look hard) and it took me quite a while and a wrong guess to become aware that MathJax was the "problem" which was confirmed by Davide. In Gavin's case, Past Answers showed the answers sin(.25x), sin e(.25x), sin(.25x), and sin(.25x) and only the last answer was marked correct by WeBWorK. As Gavin reported the actual answers the student submitted were Sin(.25x), Sine(.25x), Sin(.25x), and sin(.25x). An instructor less knowledgeable about WeBWorK than Gavin would probably conclude the student entered the correct answer (sin(.25x)) and WeBWorK marked it wrong and, for some strange reason, the last time the student entered the correct answer, it was accepted. As noted in one of my and David's forum posts, right now you can easily see the actual answer the student entered, but it's not an obvious thing for an instructor to do (Gavin didn't do it and neither did I until the very end). So, if we do have a typeset mode, I would definitely want the raw input mode to be the default.

@apizer
Copy link
Member

apizer commented Oct 2, 2015

Another comment is whether (especially for MathView) it is feasible to substitute the WeBWorK's parser for MathJax's AsciiMath parser?

@dpvc
Copy link
Member

dpvc commented Oct 2, 2015

MathJax runs in the browser, which means it is Javascript, but WeBWorK's parser is on the server in Perl. It would require a complete port of MathObjects to javascript to make that possible.

With MathJax v2.6 (currently in beta), it would be possible to use MathJax-node to process the math on the server to produce the HTML that MathJax would create in the browser. That could be cached just like the current image mode results. That doesn't help MathView, but it could be useful for past answers, as well as for problems in general.

@goehle
Copy link
Member

goehle commented Oct 2, 2015

The main issue with having a "switch" to turn typesetting on and off is that it doesn't really solve Arnie/Gavin's problem. If you suspect that its the actual answer text that is the issue you can already right click on the typeset formula to see the original answer. The issue is that unless you know or suspect that the raw answer is the problem you would never think to do that, and you wouldn't think to turn off typesetting either. Maybe building a typeset mode for mathjax which more closely mimics PG would solve the issue because it would fail on things like Sin(.25).

@dpvc
Copy link
Member

dpvc commented Oct 2, 2015

What I had in mind was more of a preference so that people could see the form they wanted, not something intended to solve Gavin's problem directly.

As for a MathJax input that is more like MathObjects, the difficulty is that what parses and what doesn't is dependent on the answer's Context object, and that varies considerably from one context to another. E.g., in the vector contexts, "<" and ">" are delimiters for vectors, but in the Inequalities context, these are less-than and greater-than. You can't tell which way "<" and ">" are to be treated without knowing the context, so in order to make that work, you would need to have a tighter connection between the page displaying the math and the problem from which the answers came. My main objection to typesetting the answer has always been that you don't have that connection, so will misrepresent answers that are not in the basic numeric contexts. Things like currency, or scientific notation, or permutations, or other special-purpose contexts may not render correctly without the specialized knowledge of the context that the problem has but the instructor page doesn't.

@Alex-Jordan
Copy link
Contributor Author

Returning to this after a long hiatus. @dpvc suggested that to address the MathView issue, we could remove all the unwanted symbols from AsciiMath and add a few WW-specific items back. I've looked around the MathJax and AsciiMath documentation, but I can't find how to remove things that the AsciiMath parser recognizes. Can you point me in the right direction?

I did find how to change things that the parser already recognizes, following Geoff's note above about **. However, the lines in webwork2/htdocs/js/apps/MathView/mathview.js, that are intended to make it recognize ** and output ^ are broken. Typing ** still makes an asterisk for me. I removed the var from line 24 of that file, and then typing ** makes a caret. I don't understand javascript well enough to explain this, but is this a bug/typo, and is removing var the right fix?

I also believe I have found how to add new things, like make it recognize inf and behave like oo nomrally behaves. However I can't see how to make it be case insensitive. Will I need to add INF, Inf, etc. separately?

@dpvc
Copy link
Member

dpvc commented Mar 25, 2018

@Alex-Jordan, I think if you replace lines 23 to 32 by

MathJax.Hub.Register.StartupHook("AsciiMath Jax Config", function () {
  var AM = MathJax.InputJax.AsciiMath.AM;
  AM.symbols.splice(0,AM.symbols.length); // remove all definitions
  AM.symbols.push.apply(AM.symbols,[
    {input:"(", tag:"mo", output:"(", tex:"left(", ttype:AM.TOKEN.LEFTBRACKET},
    {input:")", tag:"mo", output:")", tex:"right)", ttype:AM.TOKEN.RIGHTBRACKET},
    {input:"[", tag:"mo", output:"[", tex:"left[", ttype:AM.TOKEN.LEFTBRACKET},
    {input:"]", tag:"mo", output:"]", tex:"right]", ttype:AM.TOKEN.RIGHTBRACKET},
    {input:"{", tag:"mo", output:"{", tex:null, ttype:AM.TOKEN.LEFTBRACKET},
    {input:"}", tag:"mo", output:"}", tex:null, ttype:AM.TOKEN.RIGHTBRACKET},
    {input:"|", tag:"mo", output:"|", tex:null, ttype:AM.TOKEN.LEFTRIGHT},
    {input:"<", tag:"mo", output:"\u2329", tex:"langle", ttype:AM.TOKEN.LEFTBRACKET},
    {input:">", tag:"mo", output:"\u232A", tex:"rangle", ttype:AM.TOKEN.RIGHTBRACKET},
    {input:"inf", tag:"mo", output:"\u221E", tex:"infty", ttype:AM.TOKEN.CONST},
    {input:"Inf", tag:"mo", output:"\u221E", tex:"infty", ttype:AM.TOKEN.CONST},
    {input:"INF", tag:"mo", output:"\u221E", tex:"infty", ttype:AM.TOKEN.CONST},
    {input:"infinity", tag:"mo", output:"\u221E", tex:"infty", ttype:AM.TOKEN.CONST},
    {input:"Infinity", tag:"mo", output:"\u221E", tex:"infty", ttype:AM.TOKEN.CONST},
    {input:"INFINITY", tag:"mo", output:"\u221E", tex:"infty", ttype:AM.TOKEN.CONST},
    {input:"none", tag:"mtext", output:"NONE", tex:null, ttype:AM.TOKEN.CONST},
    {input:"None", tag:"mtext", output:"NONE", tex:null, ttype:AM.TOKEN.CONST},
    {input:"NONE", tag:"mtext", output:"NONE", tex:null, ttype:AM.TOKEN.CONST},
    {input:"dne",  tag:"mtext", output:"DNE", tex:null, ttype:AM.TOKEN.CONST},
    {input:"Dne",  tag:"mtext", output:"DNE", tex:null, ttype:AM.TOKEN.CONST},
    {input:"DNE",  tag:"mtext", output:"DNE", tex:null, ttype:AM.TOKEN.CONST},
    {input:"sin",  tag:"mi", output:"sin", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"cos",  tag:"mi", output:"cos", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"tan",  tag:"mi", output:"tan", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"cot",  tag:"mi", output:"cot", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"sec",  tag:"mi", output:"sec", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"csc",  tag:"mi", output:"csc", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"asin", tag:"mi", output:"asin", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"acos", tag:"mi", output:"acos", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"atan", tag:"mi", output:"atan", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"acot", tag:"mi", output:"acot", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"asec", tag:"mi", output:"asec", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"acsc", tag:"mi", output:"acsc", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"asin", tag:"mi", output:"asin", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arccos", tag:"mi", output:"arccos", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arctan", tag:"mi", output:"arctan", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arccot", tag:"mi", output:"arccot", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arcsec", tag:"mi", output:"arcsec", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arccsc", tag:"mi", output:"arccsc", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"sinh", tag:"mi", output:"sinh", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"cosh", tag:"mi", output:"cosh", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"tanh", tag:"mi", output:"tanh", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"coth", tag:"mi", output:"coth", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"sech", tag:"mi", output:"sech", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"csch", tag:"mi", output:"csch", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"asinh", tag:"mi", output:"asinh", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"acosh", tag:"mi", output:"acosh", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"atanh", tag:"mi", output:"atanh", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"acoth", tag:"mi", output:"acoth", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"asech", tag:"mi", output:"asech", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"acsch", tag:"mi", output:"acsch", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"asinh", tag:"mi", output:"asinh", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arccosh", tag:"mi", output:"arccosh", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arctanh", tag:"mi", output:"arctanh", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arccoth", tag:"mi", output:"arccoth", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arcsech", tag:"mi", output:"arcsech", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arccsch", tag:"mi", output:"arccsch", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"exp",  tag:"mi", output:"exp", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"int",  tag:"mi", output:"int", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"sgn",  tag:"mi", output:"sgn", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"atan2",tag:"mi", output:"sgn", tex:"atan_2", ttype:AM.TOKEN.UNARY, func:true},
    {input:"abs",  tag:"mi", output:"abs", tex:null, ttype:AM.TOKEN.UNARY, rewriteleftright:["|","|"]},
    {input:"norm", tag:"mi", output:"norm", tex:null, ttype:AM.TOKEN.UNARY, rewriteleftright:["|","|"]},
    {input:"unit", tag:"mi", output:"unit", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"arg",  tag:"mi", output:"arg", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"mod",  tag:"mi", output:"mod", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"Re",   tag:"mi", output:"Re", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"Im",   tag:"mi", output:"Im", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"cong", tag:"mover", output:"\u00AF",  tex:"overline", ttype:AM.TOKEN.UNARY, acc:true},
    {input:"log",  tag:"mi", output:"log", tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"log10",tag:"mi", output:"log10", tex:"log_{10}", ttype:AM.TOKEN.UNARY, func:true},
    {input:"ln",   tag:"mi", output:"ln",  tex:null, ttype:AM.TOKEN.UNARY, func:true},
    {input:"sqrt", tag:"msqrt",output:"sqrt", tex:null, ttype:AM.TOKEN.UNARY},
    {input:"*",    tag:"mo",   output:"\u22C5", tex:"cdot", ttype:AM.TOKEN.CONST},
    {input:"/",    tag:"mfrac",output:"/", tex:null, ttype:AM.TOKEN.INFIX},
    {input:"^",    tag:"msup", output:"^", tex:null, ttype:AM.TOKEN.INFIX},
    {input:"**",   tag:"msup", output:"^", tex:null, ttype:AM.TOKEN.INFIX},
    {input:"U",    tag:"mo",   output:"\u222A", tex:"cup", ttype:AM.TOKEN.CONST},
    {input:"><",   tag:"mo",   output:"\u00D7", tex:"times", ttype:AM.TOKEN.CONST}
  ]);
});

Note that line 32 should be deleted, as it is both unnecessary and syntactically incorrect.

That should remove all the existing symbols in AsciiMath and replace them with the ones that correspond to the standard MathObjects "Full" context. It should cover the needs of all the default contexts, but won't properly handle ones like inequalities (since that defines additional operators for the relations <, >, and so on). It also won't include any new strings that get added to the context by the problem author.

Yes, you do need to do the various capitalizations, as AsciiMath doesn't have a case-insensitive mode. I've included inf, Inf, and INF (and the other values like that).

See if that doesn't do more what you want.

@Alex-Jordan
Copy link
Contributor Author

Thanks Davide. I was going to go through that list in Default.pm myself once I had the basics, but you spared me that. I added pi and a few other things like inequality symbols, even though they won't be recognized by WW for many answers. (And I commented out the vector brackets.)

I thought I had this all working, but then at the end it appears to work with Chrome, but not Firefox or Safari. Chrome is honoring the MathJax configuration for MathView separately from the configuration for the rest of the WW page. But with both Safari and Firefox, MathView's use of MathJax appears to use the ambient MathJax configuration instead of this specially built one.

You can try this live problem in the different browsers and see. In Safari, the draggability is broken, so instead of using the menus, just open MathView and type infinity. With Chrome I see the infinity symbol, but with the others I see \in f\in ity. Assuming you see the same, can anything be done to get it to honor the right configuration?

@dpvc
Copy link
Member

dpvc commented Mar 25, 2018

@alex, I expect the issue is that the configuration is coming at the wrong time; configuration blocks are supposed to appear before the script that loads MathJax.js itself, and different browsers act differently if that isn't the case. I see that MathView.js is loaded after MathJax.js, and that it calls MathJax.Hub.Register.StartupHook() directly itself (that is, it doesn't make a MathJax configuration block). Since neither script call as the async attribute, MathJax will load and begin running before MathView is even loaded. Depending on the state of the cache, and which browser you are using, MathJax may pause while loading a file, allowing MathView to load and run, and it may get its configuration in place before AsciiMath initializes itself; but I may not, and that is what you are seeing in the different cases. If you are using a late enough copy of MathJax (2.7.0 or later), then we could get this to work even as it stands, but earlier versions of MathJax contain a copy of AsciiMath that doesn't allow changes after it is initially loaded.

In any case, I think the way that MathJax is being handled in these pages may need to be rethought. Fir one thing, there are two script tags that load MathJax. While MathJax is smart enough to ignore the second call, it does cause some browsers to load the MathJax.js file more than once, which is inefficient.

MathJax can be configured to not do its start up until all the configuration has been completed, so it may be worth switching to that mode (it would also involve adding a call at the bottom of the page to tell MathJax that all the configuration is complete). Alternatively, more care could be made to insert the configuration as actual configuration blocks, and make sure they MathJax.js is loaded after that has occurred. That would take a little more care.

@Alex-Jordan
Copy link
Contributor Author

OK, for one thing, I pulled MathJax. It was on some 2.6 version.

The "free-standing" problem at my earlier link is loaded using the framework of webwork2/lib/WebworkClient/simple_format.pl, which doesn't include anything for MathView in the distribution. But I have customized it to add the MathView css and js calls. (Perhaps this will ultimately be somewhere else than simple_format.pl, but I'm experimenting.) After your message, I rearranged the order and deleted the extra MathJax call. And for good measure I enabled the delay until the configuration is complete. Things worked for me in Chrome and Firefox. After scrubbing Safari's cache, things work there too.

It's one thing to do this with simple_format.pl, but will be at least a little harder over in webwork2/lib/WeBWorK/ContentGenerator/Problem.pm, which builds the page for "regular" problems (as opposed to "free-standing"). But I think I can work on it now, leading to a pull request. For us at my school, I think MathView never took off because of the issues that started this thread. I'm hopeful that we're reaching a resolution now. It's too bad that it might come just as WIRIS enters the picture. But WIRIS is still commercial, so enhancements to MathView will be worth the effort.

Is there any downside to the delay-until-configured scheme?

@dpvc
Copy link
Member

dpvc commented Mar 27, 2018

Is there any downside to the delay-until-configured scheme?

Well, it does mean that MathJax won't start loading its dependencies (like the config file and any font files) until the end of the page is reached (as opposed to right away when its script tag is processed). But because WeBWorK problems are generally pretty short and don't have a lot of other scripts to load, I don't think this will be an issue. If there were lots of images, scripts, and other external resources, since most browsers limit the number of simultaneous connections they will make, it could mean Mathias has to wait for other content to arrive before it can start downloading its configuration and other files, and so delay the processing of Math.

That is really the only drawback that I can see. I think this is probably the easiest approach to getting git to work.

On the other hand, the MathView configuration is still technically being done in the wrong way, and you are relying on some delicate timing interactions to make it work. It is running in a jQuery ready function, and you are counting on MathJax having to wait for its configuration files and so on to cause a delay long enough to allow the ready function to run before MathJax does its configuration. There is no guarantee that that will happen.

It would be better if MathView.js would insert a <script type="text/x-mathjax-config"> containing its configuration (and do that outside of the ready function) so that the configuration is in place before the MathJax.Hub.Configured() call is made. Currently, the MathJax.Hub.Configured() call is coming before the MathView configuration is done (since the jQuery ready function won't fire until after that script is run), so you are really telling MathJax that the configuration is ready before it really is, and are relying on internal delays in MathJax to get it all to work.

@Alex-Jordan
Copy link
Contributor Author

Alex-Jordan commented Apr 2, 2018 via email

@dpvc
Copy link
Member

dpvc commented Apr 2, 2018 via email

@Alex-Jordan
Copy link
Contributor Author

#2296 addresses the important issue here, with users trying to write infinity. I'm going to close this (currently oldest open) issue.

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

5 participants