MathJax + TinyMCE Integration

1,867 views
Skip to first unread message

John Wilson

unread,
Sep 2, 2011, 10:46:46 AM9/2/11
to MathJax Development
Hello,

I am working on a MathJax TinyMCE plugin and am having some trouble
getting it working in IE. The purpose of the plug-in is to display
typeset math in the design view of the editor. So there are two
conversions - one to convert MathML into typeset math (MathML in FF,
otherwise HTML+CSS) to be displayed in the editor, and the other to
convert back to the original math when extracting the content from the
editor.

In the plug-in, MathJax is loaded dynamically into tinyMCE's iFrame
and used to typeset the math in a temp node, and then the typeset math
is subsequently swapped into the editor. The tyepset math is wrapped
in a div and the original math is stored encoded in a data attribute
for future retrieval of the editor's HTML.

The plug-in successfully displays typeset math in both FF and Chrome.
In IE, the math is typeset and added to the editor's DOM (as I can see
it if I inspect the DOM), but is is not displayed. It seems like some
problem with styles/css, but I am not sure. This could also be
tinyMCE related. I am hoping that someone can take a look and perhaps
have some suggestion to get displaying in IE.

Thanks in advance!
John

Here's the plug-in code:


(function() {
var each = tinymce.each;

tinymce.create( 'tinymce.plugins.AcmeMathPlugin', {

/**
* Initializes the plugin, this will be executed after the plugin
has been created.
* @param {tinymce.Editor} ed Editor instance that the plugin is
initialized in.
* @param {string} url Absolute URL to where the plugin is located.
*/
init : function( ed, url ) {
var me = this;
me.editor = ed;
me.url = url;
me.mathTypesets;
me.mathIndex = 0;

ed.onPreInit.add( function( ed ) {
me.dom = ed.dom;
me.doc = ed.getDoc();

me._initializeMathJax();
} );

// dispatched when plugin initializes
ed.onInit.add( function() {
ed.dom.loadCSS( url + "/css/acme_math.css" );
});

// Note: "o" is a JavaScript object containing details, including
the content

// dispatched before content inserted into editor (ex. when closing
HTML source dialog)
ed.onBeforeSetContent.add( function( ed, o ) {
me._convertToTypesetPlaceholder( ed, o );
});

// dispatched after content inserted into editor (ex. when closing
HTML source dialog)
ed.onSetContent.add( function( ed, o ) {
me._convertToTypesetMath( ed, o );
} );

// dispatched when getting content from editor (ex. when opening
HTML source dialog)
ed.onPostProcess.add( function( ed, o ) {
me._convertToRawMath( ed, o );
} );
},

_convertToTypesetPlaceholder : function( ed, o ) {
var me = this;

me.mathTypesets = [];

var replaceMath = function( match ) {

me.mathIndex += 1;
var mathId = 'acme_math_' + me.mathIndex;

var tempNode = me.doc.createElement( 'div' );
tempNode.innerHTML = match;

MathJax.Hub.Queue( ["Typeset", MathJax.Hub, tempNode] );
MathJax.Hub.Queue( function() {
if ( tempNode.childNodes.length > 1 ) {

// remove the script element - we're only interested in the
typeset HTML
tempNode.removeChild( tempNode.childNodes[1] );

// store typeset HTML so that is can replace placeholder in
editor body later
me.mathTypesets.push( { id : mathId, html : tempNode.innerHTML,
math : match } );

}
} );

return '<span id="' + mathId + '" style="display:none;">Temp</
span>';
};

// replace math elements with a placeholder (but do not replace if
MathJax output was MathML... i.e. FF)
if ( o.content.indexOf( 'class="MathJax_MathML"' ) == -1 ) {
o.content = o.content.replace( /<math[^>]*>(.|\n|\r)*?<\/math>/gi,
replaceMath );
}

},

_convertToTypesetMath : function( ed, o ) {
var me = this;
var content = o.content;

MathJax.Hub.Queue( function() {

if ( me.mathTypesets == null || me.mathTypesets.length == 0 ) {
return;
}

// replace placeholder with the typset math, store the original
math in a data attribute
var tempContent = content;
for( var i=0; i<me.mathTypesets.length; i++ ) {
var regExp = new RegExp( '<span id="' + me.mathTypesets[i].id +
'"(.|\n|\r)*?</span>' );
tempContent = tempContent.replace( regExp, '<div class="acme-math-
p" data-acme-math="' + encodeURIComponent( me.mathTypesets[i].math ) +
'" style="display:inline;"><!--MathStart-->' + me.mathTypesets[i].html
+ '<!--MathEnd--></div>' );
}

// call back to tinymce to update the revised content
ed.setContent( tempContent );

} );

},

_convertToRawMath : function( ed, o ) {
var me = this;

if ( o.get ) {

var replaceMath = function( match ) {

// exract the original math from the data attribute
var startIndex = match.indexOf( 'data-acme-math="' );
if ( startIndex != -1 ) {
startIndex += 15;
var endIndex = match.indexOf( '"', startIndex );
return decodeURIComponent( match.substring( startIndex,
endIndex ) );
}

return match;
};

// replace the type set math with the original math
var regExp = new RegExp( '<div class="acme-math-p"(.|\n|\r)*?<!--
MathEnd--></div>', 'g' );
o.content = o.content.replace( regExp, replaceMath );

return;
}
},

_initializeMathJax : function() {
var me = this;
var dom = me.dom;
var doc = me.doc;

var config = 'MathJax.Hub.Config({showProcessingMessages:
false});MathJax.Hub.Startup.onload();';

var onLoadScript = dom.create( 'script', {
type : "text/x-mathjax-config"
}, config );

var script = dom.create( 'script', {
type : 'text/javascript',
src : '/acme/common/mathjax/1.1a/MathJax.js?config=MML_HTMLorMML,/
acme/math/display/config.js'
} );

var head = doc.getElementsByTagName( 'head' )[0];
head.appendChild( onLoadScript );
head.appendChild( script );

},

/**
* Creates control instances based in the incomming name.
*/
createControl : function( n, cm ) {
return null;
},

/**
* Returns information about the plugin as a name/value array.
*/
getInfo : function() {
return {
longname : 'Acme Insert',
author : 'Acme Inc.',
authorurl : 'http://www.acme.com',
infourl : 'http://www.acme.com',
version : "1.0"
};
}
});

// Register plugin
tinymce.PluginManager.add( 'acme_math',
tinymce.plugins.AcmeMathPlugin );
})();

And here is the content of the css file loaded by the plug-in:

.MathJax_Display {text-align: center; margin: 1em 0em; position:
relative; display: block; width: 100%; zoom:1;}
.MathJax .merror {background-color: #FFFF88; color: #CC0000; border:
1px solid #CC0000; padding: 1px 3px; font-family: serif; font-style:
normal; font-size: 90%;}
.MathJax_Preview {color: #888888;}
#MathJax_Tooltip {background-color: InfoBackground; color: InfoText;
border: 1px solid black; box-shadow: 2px 2px 5px #AAAAAA; -webkit-box-
shadow: 2px 2px 5px #AAAAAA; -moz-box-shadow: 2px 2px 5px #AAAAAA; -
khtml-box-shadow: 2px 2px 5px #AAAAAA; filter:
progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2,
Color='gray', Positive='true'); padding: 3px 4px; position: absolute;
left: 0; top: 0; width: auto; height: auto; display: none;}
.MathJax {display: inline; font-family: serif; font-style: normal;
font-weight: normal; line-height: normal; font-size: 100%; font-size-
adjust: none; text-indent: 0; text-align: left; text-transform: none;
letter-spacing: normal; word-spacing: normal; word-wrap: normal; white-
space: nowrap; float: none; direction: ltr; border: 0; padding: 0;
margin: 0;}
.MathJax img, .MathJax nobr, .MathJax a {border: 0; padding: 0;
margin: 0; max-width: none; max-height: none; vertical-align: 0; line-
height: normal; text-decoration: none}
img.MathJax_strut {border: 0 !important; padding: 0 !important;
margin: 0 !important; vertical-align: 0 !important;}
.MathJax span {display: inline; position: static; border: 0; padding:
0; margin: 0; vertical-align: 0; line-height: normal; text-decoration:
none;}
.MathJax nobr {white-space: nowrap;}
.MathJax img {display: inline ! important;}
.MathJax_Processing {visibility: hidden; position: fixed; width: 0;
height: 0; overflow: hidden;}
.MathJax .MathJax_HitBox {cursor: text;}
#MathJax_Tooltip * {filter: none; background: transparent;}
@font-face {font-family: MathJax_Main; src: url('/acme/common/mathjax/
1.1a/fonts/HTML-CSS/TeX/otf/MathJax_Main-Regular.otf');}
@font-face {font-family: MathJax_Main; src: url('/acme/common/mathjax/
1.1a/fonts/HTML-CSS/TeX/otf/MathJax_Main-Bold.otf'); font-weight:
bold;}
@font-face {font-family: MathJax_Main; src: url('/acme/common/mathjax/
1.1a/fonts/HTML-CSS/TeX/otf/MathJax_Main-Italic.otf'); font-style:
italic;}
@font-face {font-family: MathJax_Math; src: url('/acme/common/mathjax/
1.1a/fonts/HTML-CSS/TeX/otf/MathJax_Math-Italic.otf'); font-style:
italic;}
@font-face {font-family: MathJax_Caligraphic; src: url('/acme/common/
mathjax/1.1a/fonts/HTML-CSS/TeX/otf/MathJax_Caligraphic-
Regular.otf');}
@font-face {font-family: MathJax_Size1; src: url('/acme/common/mathjax/
1.1a/fonts/HTML-CSS/TeX/otf/MathJax_Size1-Regular.otf');}
@font-face {font-family: MathJax_Size2; src: url('/acme/common/mathjax/
1.1a/fonts/HTML-CSS/TeX/otf/MathJax_Size2-Regular.otf');}
@font-face {font-family: MathJax_Size3; src: url('/acme/common/mathjax/
1.1a/fonts/HTML-CSS/TeX/otf/MathJax_Size3-Regular.otf');}
@font-face {font-family: MathJax_Size4; src: url('/acme/common/mathjax/
1.1a/fonts/HTML-CSS/TeX/otf/MathJax_Size4-Regular.otf');}
#MathJax_getScales {font-family:
MathJax_Main,MathJax_Size1,MathJax_AMS;}
.MathJax .math span {font-family:
MathJax_Main,MathJax_Size1,MathJax_AMS;}

Davide P. Cervone

unread,
Sep 3, 2011, 4:24:49 PM9/3/11
to mathj...@googlegroups.com
I don't know anything about TinyMCE, really, so it is hard to give you
any good advice, and my questions may sound silly.

Is the editor and the temp node in the same iframe? If not, then
there are likely to be problems moving the HTML from the one to the
other, as MathJax relies on dynamically generated CSS that will not be
present in the outer editor window. (Also there is no guarantee that
the formatting will be right once you do this, as the layout is
dependent on the location where it was created, but you might be able
to get away with it.)

I see that you have included a CSS file that is loaded by the plugin,
and you may be trying to use that to overcome the missing CSS problem
that I mention above, but this isn't going to work, as the CSS used by
MathJax is not the same for all browsers (and may also depend on the
features used by the mathematics that is typeset). You seem to have
provided the CSS from Firefox or perhaps Chrome, which may be why it
isn't working for IE. In particular, the @font-face directives load
OTF files, but IE needs EOT files. It may be possible to modify the
CSS to use one of the "bullet-proof" font-face syntaxes, but I'm still
worried about moving the HTML by hand.

There seem to be several people trying to write such a plugin. See

http://groups.google.com/group/mathjax-users/browse_thread/thread/cbf3f7c6b4c65f37
http://groups.google.com/group/mathjax-dev/browse_thread/thread/8dd08f85a199862

I've had several communications with the latter, so you might want to
coordinate your efforts.

Best of luck!

Davide

max...@gmail.com

unread,
Feb 18, 2013, 8:09:39 PM2/18/13
to mathj...@googlegroups.com
John, 

I know this post is old now but im looking to integrate MathJax in my TinyMCE for my website. Im wondering if you would like to share your code with me. Is the code you posted is the most updated one? And also, is there any other code you didnt post I might need?

The ideal end result I want to achieve is having a custom TinyMCE button where the user has a menu of predefined formulas. Once the formula is chosen and filled with values, it is MathJax'ed in TinyMCE editor. Do you think this can be done? Do you have any suggestion (especially if a formula menu UI would exist somewhere already)?

Thanks for your help

Martin

max...@gmail.com

unread,
Feb 26, 2013, 9:24:57 PM2/26/13
to mathj...@googlegroups.com
John,

  I tried implementing your code in a custom TinyMCE plugin and I get "Uncaught ReferenceError: MathJax is not defined" (on line "MathJax.Hub.Queue( function() {") in Chrome. Wondering if im missing code or what you posted was enough? I tried with Mathjax1.1a and 2.1.

One question: is "acme/math/display/config.js" from your code the same than "/jax/output/NativeMML/config.js" from Mathjax?

Thanks you in advance

Martin
Reply all
Reply to author
Forward
0 new messages