How to add context menu in Non-Component-Based Examples?
ssine opened this issue · 12 comments
I'm working on a front-end project with typescript and webpack, codes below worked fine:
const { mathjax } = await import(/* webpackChunkName: "mathjax" */ 'mathjax-full/js/mathjax')
const { TeX } = await import(/* webpackChunkName: "mjx.tex" */ 'mathjax-full/js/input/tex')
const { SVG } = await import(/* webpackChunkName: "mjx.svg" */ 'mathjax-full/js/output/svg')
const { browserAdaptor } = await import(/* webpackChunkName: "mjx.browserAdaptor" */ 'mathjax-full/js/adaptors/browserAdaptor')
const { RegisterHTMLHandler } = await import(/* webpackChunkName: "mjx.html" */ 'mathjax-full/js/handlers/html')
const { AllPackages } = await import(/* webpackChunkName: "mjx.AllPackages" */ 'mathjax-full/js/input/tex/AllPackages')
const adapter = browserAdaptor()
RegisterHTMLHandler(adapter)
html = mathjax.document(document, {
InputJax: new TeX({
packages: AllPackages,
inlineMath: [ // start/end delimiter pairs for in-line math
['$', '$']
],
displayMath: [ // start/end delimiter pairs for display math
['$$', '$$'],
['\\[', '\\]']
],
}),
OutputJax: new SVG({
fontCache: 'none'
})
})
Then I tried to add context menu on right-click, but I did not find documents about it, after reading some source code, I added
const { MenuHandler } = await import(/* webpackChunkName: "mjx.menu" */ 'mathjax-full/js/ui/menu/MenuHandler')
MenuHandler(RegisterHTMLHandler(adapter))
, however, I'm getting the following error:
Uncaught (in promise) ReferenceError: ContextMenu is not defined
at Object../node_modules/mathjax-full/js/ui/menu/MJContextMenu.js (MJContextMenu.ts:34)
at __webpack_require__ (bootstrap:63)
at Object../node_modules/mathjax-full/js/ui/menu/Menu.js (Menu.ts:32)
at __webpack_require__ (bootstrap:63)
at Object../node_modules/mathjax-full/js/ui/menu/MenuHandler.js (MenuHandler.ts:34)
. So, what's the corrent way of adding context menu in Non-Component-Based Examples ? Thanks!
The ContextMenu
is a global object created my the mj-context-menu
npm package. It gets imported in the components/src/ui/menu/context-menu.js
, so you will need to add another import to get that loaded before calling the MenuHandler()
. Something like
await import('mj-context-menu/dist/context_menu.js');
should do it. (Add your webpackChunkName
with whatever name you need too use.)
See if that does the trick.
Thanks for the reply! I added that package and imported it as you said, but got another error at mathjax-full/js/ui/menu/Menu.js.Menu.initSettings (Menu.ts:368)
:
TypeError: Cannot read property '_' of undefined
...
if (window.MathJax._.a11y && window.MathJax._.a11y.explorer) {
...
It seems that menu module requires MathJax object to be added to window. I'm puzzled about where I can get this object, do you have any idea?
The menu system is fairly dependent on the components architecture, as it does dynamic loading of some components on demand (like the assistive tools, including the explorer that you are seeing this message about). The components architecture uses the global variable MathJax
as a means of communicating among the components, and the MathJax._
object is used to share the various exports from the modules among the components.
There are two possible options. One would be to subclass the Menu class and the MenuHandler (to use your new class) and override the methods that reference MathJax._
as they all have to do with the dynamic loading, which you won't be able to use without the components code.
Alternatively, you could define
window.MathJax = {_: {}};
which should get you past this problem. A quick look through the code indicates that Menu.ts
assumes the presence of window.MathJax
and window.MathJax._
, but checks for the presence of the sub-objects from there, so this should be enough to do it.
There is a pull request (mathjax/MathJax-src#458) that should resolve this, so another option would be to get a copy of the modified ts/util/menu/Menu.ts
file and use that instead for now.
Thanks for your advice! I added
window.MathJax = {_: {}};
and commented out
menu.findID('Accessibility', 'Explorer').disable();
menu.findID('Accessibility', 'AutoCollapse').disable();
menu.findID('Accessibility', 'Collapsible').disable();
in menu.js as they raises error because findID
returned null. However this is not enough, in ui/menu/MenuHandler.ts
line 124:
if (this.state() < STATE.CONTEXT_MENU) {
document.menu.addMenu(this);
this.state(STATE.CONTEXT_MENU);
}
the state code of current math item is 200, but STATE.CONTEXT_MENU is 170, so it was not getting added. After I commented the if statement, all the math disappeared.
I dig down into Menu.ts
, line 963:
public addMenu(math: HTMLMATHITEM) {
const element = math.typesetRoot;
element.addEventListener('contextmenu', () => this.menu.mathItem = math, true);
element.addEventListener('keydown', () => this.menu.mathItem = math, true);
element.addEventListener('click', (event: MouseEvent) => this.zoom(event, 'Click', math), true);
element.addEventListener('dblclick', (event: MouseEvent) => this.zoom(event, 'DoubleClick', math), true);
this.menu.getStore().insert(element);
}
It seems that this.menu.getStore().insert(element);
is not working, I can get the math with menu by appending the element to DOM tree, but getStore().insert
will cause the original math disappear and new math not displaied.
I'm not sure if I was using it the correct way. I just want a neat way to integrate MathJax in my project using Typescript+Webpack, but found litte information about it.
The issue with findID()
is only with the first call. It turns out that the menu was reorganized after that code was written, and the 'Explorer' menu was collapsed into the main accessibility item. It should now be 'Activate'
rather than 'Explorer'
in that call.
Once that is taken care of, everything works for me. This is my setup:
menu.js
import {ContextMenu} from './node_modules/mj-context-menu/dist/context_menu.js';
import {mathjax} from './node_modules/mathjax-full/js/mathjax.js';
import {TeX} from './node_modules/mathjax-full/js/input/tex.js';
import {SVG} from './node_modules/mathjax-full/js/output/svg.js';
import {browserAdaptor} from './node_modules/mathjax-full/js/adaptors/browserAdaptor.js';
import {RegisterHTMLHandler} from './node_modules/mathjax-full/js/handlers/html.js';
import {AllPackages} from './node_modules/mathjax-full/js/input/tex/AllPackages.js';
import {MenuHandler} from './node_modules/mathjax-full/js/ui/menu/MenuHandler.js';
import {Menu} from './node_modules/mathjax-full/js/ui/menu/Menu.js';
window.MathJax = {_: {}};
//
// This is to fix the Explorer => Activate issue
///
Menu.prototype.checkLoadableItems = function () {
const menu = this.menu;
for (const name of Object.keys(this.jax)) {
if (!this.jax[name]) {
menu.findID('Settings', 'Renderer', name).disable();
}
}
menu.findID('Accessibility', 'Activate').disable();
menu.findID('Accessibility', 'AutoCollapse').disable();
menu.findID('Accessibility', 'Collapsible').disable();
};
const adapter = browserAdaptor();
MenuHandler(RegisterHTMLHandler(adapter));
const html = mathjax.document(document, {
InputJax: new TeX({
packages: AllPackages,
inlineMath: [ // start/end delimiter pairs for in-line math
['$', '$']
],
displayMath: [ // start/end delimiter pairs for display math
['$$', '$$'],
['\\[', '\\]']
],
}),
OutputJax: new SVG({
fontCache: 'none'
})
});
window.onload = function () {
html.render();
}
menu.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Menu bundle test</title>
<script src="menu.bundle.js"></script>
</head>
<body>
$$\frac{x+ 1}{x-1}$$
</body>
</html>
webpack.config.js
module.exports = {
mode: 'development',
entry: './menu.js',
output: {
path: __dirname,
filename: 'menu.bundle.js'
}
};
With these files, the menu works fine for me. So there must be something else going on in your situation.
As for the state value, note that if you did something like console.log(this)
within the code that you cite above, the object being displayed would be live, and the state value would reflect the state at the time you look at the object, not the state at the time of the console.log()
call. You would have to use console.log(this.state())
in order to get the state at that time. So it may be that you are seeing the final state of the MathItem (which is want 200 would be), not the state at the time the if-then is performed.
If that's not it, then it could be that the MathItem has already been processed once, and you are trying to process its again. In that case, the state is 200 from the previous time through the typesetting process, and that doesn't change (unless you specifically set it down again). So depending on how you are calling MathJax's typesetting functions, that could be the issue you are seeing.
Anyway, I'm not able to reproduce the problem you seem to be having, so can't get any further with it than this. Sorry not to be able to help more.
Thank you very much! I figured out the remaining state code problem on my project:
The project is a single page application that display new contents dynamically, so I call html.render()
each time new contents are added. But it seems that after the first render()
, html
object remains in a completed state and cease rendering again. So I changed to use
html.processed.clear('findMath')
html.processed.clear('compile')
html.processed.clear('getMetrics')
html.processed.clear('typeset')
html.processed.clear('updateDocument')
html.processed.clear('context-menu')
html.findMath()
.compile()
.getMetrics()
.typeset()
.updateDocument()
.addMenu()
for each rendering. since .addMenu()
was added later I didn't notice that this operation should be done before .updateDocument()
, which causes the state code problem. Swapping this two call or simply use
html.processed.clear('findMath')
html.processed.clear('compile')
html.processed.clear('getMetrics')
html.processed.clear('typeset')
html.processed.clear('updateDocument')
html.processed.clear('context-menu')
html.render()
works fine.
Thanks again for your help!
Glad you have it worked out.
Note also that you can replace the multiple clear()
calls with
html.reset()
which clears all the processed markers at once.
That's elegant!
Is there any documentation or a Node demo on how to add the contextual menu?
@AustenLamacraft, it is best to start a new issue rather than ask a new question within a 2-year-old closed one.
Is there any documentation or a Node demo on how to add the contextual menu?
Can you be clearer about what you are asking to do? Are you preprocessing pages on the server and want the menu in the result? Or are you making a web application (as in the example above) and are calling the MathJax modules directly? Or is it something else?
As mentioned above, the menu is highly dependent on the component architecture, so calling MathJax modules directly will make it difficult to use the menu, though the discussion above gives the outline of how to do that (though some things have changed since then). It is best to use MathJax components rather than direct calls in this case.
If you are doing preprocessing and your final pages just include the MathJax output, then there is not current method for adding the MathJax menu to that.
Sorry, I wasn't too sure where to put this question. I'm doing static rendering (with eleventy static site generator) so I'm basically using the example of a direct call in this repo. So I guess there's no way to add the menu at this point.
We have plans to make a stand-alone menu feature, but that hasn't been done yet. So I'm afraid that there is no menu option for this at the moment.