Continuing live TeXing

125 views
Skip to first unread message

Casey W. Stark

unread,
Oct 28, 2010, 10:31:35 PM10/28/10
to MathJax Users
[Previously posted at https://sourceforge.net/projects/mathjax/forums/forum/948700/topic/3720950]

freethought
2010-05-25 19:36:59 PDT

One very cool feature of http://mathoverflow.net is a live TeX
preview: when one types a new question, a preview panel appears, in
which the text in the textarea is continuously typeset. Needless to
say, that would be a very cool feature to have in email

Now, I have made for myself a little TeX button in gmail, so that when
I compose an email, a preview panel appears with the text in the
textarea typeset. I can press that TeX button again and again to have
the preview updated, and I can also bind it to the "Saved" button in
Gmail. But it is not continous. It simply copies the text of the
textarea, typesets it, and then scrolls to the saved place. While it
is doing it, it makes the panel temporarily invisible to hide all the
TeXing and scrolling that is taking place.

But is it possible to make it a live TeX preview? MathOverflow uses
jsMath, and it seems like a sophisticated piece of JavaScript. But is
that something that's really easy with MathJax? Maybe just setting up
some headers and issuing a secret command of which we don't know yet
(but will learn later once the documentation is in place)?

Or setting up a MathJax queue? I know there was some discussion about
this elsewhere in the forum but it would be nice to have actual
instructions for setting up such a queue.

Sorry for being impatient in trying to use this new cool technology.

----------

caseystark
2010-05-25 20:38:24 PDT

You could process on keyup. I believe this is what mathoverflow is
doing...

$('email').keyup(function() {
MathJax.Hub.Typeset('preview');
}).keyup();

----------

caseystark
2010-05-25 20:43:04 PDT

Sorry that should be...

$('email').keyup(function() {
$('preview').html($('email').html())
MathJax.Hub.Typeset('preview');
}).keyup();

----------

freethought
2010-05-25 22:37:14 PDT

This is a nice suggestion, but here is what I don't understand: I
cannot just set preview.innerHTML to the result of
MathJax.Hub.Typeset(textarea.value). If I understand correctly, I have
to copy texarea.value to preview.innerHTML first and then call the
function MathJax.Hub.Typeset(preview).

This leads to a lot of activity in the preview window: the text
changes to the literal copy of the textarea, then it gets MathJaxed,
then it is scrolled to the previously remembered point. All of this is
certainly not suitable for the live TeXing on each keystroke.

Is there an equivalent of the following call?

preview.innerHTML = MathJax.Hub.Typeset(textarea.value)


(I am not so strong on jQuery, so plain javascript would be better.)

----------

caseystark
2010-05-25 23:15:08 PDT

My bad. I'm so used to jQuery now sometimes I forget I'm using it.

I didn't know it scrolls to the previously remembered point. I don't
have any better ideas, but Davide might know something better. There
is probably another call for it. Sorry the docs aren't there yet!

I know that this is what mathoverflow used to do with jsMath because I
copied it earlier this year. Anton has clearly done some hacking on
the interface since.

----------

dpvc
2010-05-26 04:15:12 PDT


Is there an equivalent of the following call?

preview.innerHTML = MathJax.Hub.Typeset(textarea.value)


No, Typeset() does not work on an HTML string, and it doesn't return
an HTML string. The results of typesetting mathematics are dependent
on the location in the page (because MathJax tries to match the font
size, and other CSS attributes can affect the results as well), and so
it can't typeset anything without that context. So typesetting an
abstract string is not something that MathJax can do.

Also, MathJax.Hub.Typeset may return long before the mathematics is
actually typeset. That is because MathJax may need to load extensions
in order to process the mathematics, and that is done asynchronously.
(If only JavaScript included a "wait" command that could suspend a
function until some event occurred, all this synchronization would be
easy, but alas, that is not the case, so we have to work much harder
and use queues and callbacks instead. Sigh.) So the Typeset() call
may return after a file load is requested but before the mathematics
has been typeset.

What Typeset() returns is not the typeset mathematics, but a callback
function that will be run when the typesetting is complete. (You can
supply a callback as an argument to Typeset() in order to synchronize
an action with the completion of the typesetting). This return value
is used by the queuing methods to synchronize the actions of MathJax.

You are right that setting the innerHTML and re-typesetting it on
every keystroke will lead to a lot of activity in the preview (and for
large amounts of text, will probably be prohibitively slow; when I add
the equation cache to MathJax, that will help, however). One
possibility is to wrap the contents in a div with visibility:hidden
set while you change the contents and re-typeset (note that it must be
visibility:hidden and not display:none, as the sizing information that
MathJax relies on is not available when display is none). After the
typesetting is complete, you can change visibility to visible again.
Note that this would have to be properly synchronized using a MathJax
queue. (It would be possible to do this typesetting in a hidden
preview area and then transfer it to the visible preview area to avoid
displaying a blank preview while its contents is hidden. I suspect it
would be best to use DOM calls like removeChild and appendChild rather
than reading and setting innerHTML, since the DOM structure would not
have to be rebuilt that way.)

Finally, since the mathematics is typeset asynchronously, you would
not want to replace the innerHTML while the typesetting is actually
occurring (this would lead to older versions of the innerHTML being
kept in memory for no reason until MathJax has finished processing
them, and to multiple Typeset() calls running simultaneously, causing
unneeded slowdowns and memory usage). So you would need some more
complex logic to not queue a typeset until after the last one
finished, not automatically do it on each keystroke.

All of this is pretty complex, but could be done. It would require
careful timing and use of MathJax queues, which (as you know) are not
yet documented publicly.

Davide

----------

dpvc
2010-05-26 04:34:04 PDT

Here is a quick hint about queues. You should create a
MathJax.CallBack.Queue (once) that you use to process commands that
need to be synchronized with MathJax. You then push commands onto
that queue. These are in the form of MathJax.CallBack objects, which
can be specified in abut a dozen different forms. One is as a
function object, but you can also specify methods of objects to be
called, and arguments to be passed when the callback is called, which
is what I do below.

You can set up the queue as follows:

var queue =
MathJax.CallBack.Queue(MathJax.Hub.Register.StartupHook("End",{}));

This creates the queue and pushes a callback that gets called when the
startup process is complete. This means the rest of your queue will
not run until MathJax is ready (regardless of when you push new
commands).

Then when you want to process the mathematics in some DOM element, do

queue.Push(["Typeset",MathJax.Hub,DOMelement]);

This will wait to do MathJax.Hub.Typeset(DOMelement) until the
previous commands are complete.

If you want to do something after the typesetting is done (that relies
on the results of the typesetting), you can push your own function on
the queue:

queue.Push(function () {...whatever...});

Note that you should not push the RESULT of the function, but the
actual function itself, otherwise the function will be called at the
time you do the Push() rather than at the proper time within the
queue. You can push a function with arguments as follows:

var f = funciton () {...};
...
queue.Push([f,arg1,arg2]);

Any number of arguments can be passed. If the function is a method of
some object (e.g., obj.method(arg1,arg2)), then use

queue.Push(["method",obj,arg1,arg2]);

If your function or method returns a MathJax.CallBack object, the
queue will continue to wait for that to complete before going on. So
you could push a function that sets up the mathematics and returns the
result of MathJax.Hub.Typeset(), which is a CallBack object, and the
queue would wait for the typesetting to complete before going on. You
could push a second function that copies the typeset results to the
preview area or that sets the visibility to visible, and it would not
be performed until the typesetting was complete.

That's it in a nutshell. You will have to wait for more complete
documentation, but this can get you started, perhaps. Look in the
MathJax/MathJax.js file for the CallBack section, and read the
comments about the different formats for specifying CallBacks. It's
not much, but it's something.

Davide
Reply all
Reply to author
Forward
0 new messages