Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,56 +1,35 @@
// See common/templates/mathjax_include.html for info on Fast Preview mode.
var disableFastPreview = true,
vendorScript;
if (typeof MathJax === 'undefined') {
if (disableFastPreview) {
window.MathJax = {
menuSettings: {CHTMLpreview: false}
};
}

vendorScript = document.createElement('script');
vendorScript.onload = function() {
'use strict';

var MathJax = window.MathJax,
setMathJaxDisplayDivSettings;
MathJax.Hub.Config({
tex2jax: {
inlineMath: [
['\\(', '\\)'],
['[mathjaxinline]', '[/mathjaxinline]']
],
displayMath: [
['\\[', '\\]'],
['[mathjax]', '[/mathjax]']
]
}
});
if (disableFastPreview) {
MathJax.Hub.processSectionDelay = 0;
}
MathJax.Hub.signal.Interest(function(message) {
if (message[0] === 'End Math') {
setMathJaxDisplayDivSettings();
}
});
setMathJaxDisplayDivSettings = function() {
$('.MathJax_Display').each(function() {
this.setAttribute('tabindex', '0');
this.setAttribute('aria-live', 'off');
this.removeAttribute('role');
this.removeAttribute('aria-readonly');
});
};
};
// Automatic loading of Mathjax accessibility files
window.MathJax = {
menuSettings: {
collapsible: true,
autocollapse: false,
explorer: true
tex: {
inlineMath: [
['\\(', '\\)'],
['[mathjaxinline]', '[/mathjaxinline]']
],
displayMath: [
['\\[', '\\]'],
['[mathjax]', '[/mathjax]']
],
autoload: {
color: [],
colorv2: ['color']
},
packages: {'[+]': ['noerrors']}
},
options: {
ignoreHtmlClass: 'tex2jax_ignore',
processHtmlClass: 'tex2jax_process',
menuOptions: {
settings: {
collapsible: true,
explorer: true
},
},
},
loader: {
load: ['input/asciimath', '[tex]/noerrors']
}
};
vendorScript.src = 'https://cdn.jsdelivr.net/npm/mathjax@2.7.5/MathJax.js?config=TeX-MML-AM_HTMLorMML';
var vendorScript = document.createElement('script');
vendorScript.src = 'https://cdn.jsdelivr.net/npm/mathjax@4.1.2/tex-mml-chtml.js';
document.body.appendChild(vendorScript);
}
8 changes: 6 additions & 2 deletions xblocks_contrib/discussion/assets/static/js/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,8 +493,12 @@
};

DiscussionUtil.typesetMathJax = function(element) {
if (typeof MathJax !== 'undefined' && MathJax !== null && typeof MathJax.Hub !== 'undefined') {
MathJax.Hub.Queue(['Typeset', MathJax.Hub, element[0]]);
if (typeof MathJax !== 'undefined' && MathJax !== null &&
typeof MathJax.startup !== 'undefined' && MathJax.startup !== null &&
typeof MathJax.startup.promise !== 'undefined' &&
typeof MathJax.typesetPromise === 'function') {
MathJax.startup.promise
.then(() => MathJax.typesetPromise([element[0]]));
}
};

Expand Down
2 changes: 1 addition & 1 deletion xblocks_contrib/discussion/assets/static/js/customwmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Mostly adapted from math.stackexchange.com: http://cdn.sstatic.net/js/mathjax-ed
MathJaxProcessor.prototype.processMath = function(start, last, preProcess) {
var block, i, j, ref, ref1;
block = this.blocks.slice(start, last + 1).join("").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
if (MathJax.Hub.Browser.isMSIE) {
if (window.navigator && /msie|trident/i.test(window.navigator.userAgent)) {
block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n");
}
for (i = j = ref = start + 1, ref1 = last; ref <= ref1 ? j <= ref1 : j >= ref1; i = ref <= ref1 ? ++j : --j) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

MathJaxDelayRenderer.prototype.maxDelay = 3000;

MathJaxDelayRenderer.prototype.mathjaxRunning = false;

MathJaxDelayRenderer.prototype.elapsedTime = 0;

MathJaxDelayRenderer.prototype.mathjaxDelay = 0;
Expand Down Expand Up @@ -59,8 +57,15 @@
if (preprocessor != null) {
text = preprocessor(text);
}
if (typeof MathJax !== 'undefined' && MathJax !== null && typeof MathJax.typesetClear === 'function') {
MathJax.typesetClear([$(elem)[0]]);
}
$(elem).html(text); // xss-lint: disable=javascript-jquery-html
return MathJax.Hub.Queue(["Typeset", MathJax.Hub, $(elem).attr("id")]);
if (typeof MathJax !== 'undefined' && MathJax !== null && MathJax.startup &&
MathJax.startup.promise && typeof MathJax.typesetPromise === "function") {
return MathJax.startup.promise
.then(() => MathJax.typesetPromise([$(elem)[0]]));
}
} else {
if (this.mathjaxTimeout) {
window.clearTimeout(this.mathjaxTimeout);
Expand All @@ -70,31 +75,38 @@
renderer = (function(_this) {
return function() {
var curTime, prevTime;
if (_this.mathjaxRunning) {
return;
}
prevTime = getTime();
if (preprocessor != null) {
text = preprocessor(text);
}
if (typeof MathJax !== 'undefined' && MathJax !== null && typeof MathJax.typesetClear === 'function') {
MathJax.typesetClear([_this.$buffer[0]]);
}
_this.$buffer.html(text); // xss-lint: disable=javascript-jquery-html
curTime = getTime();
_this.elapsedTime = curTime - prevTime;
if (typeof MathJax !== "undefined" && MathJax !== null) {
if (typeof MathJax !== "undefined" && MathJax !== null && MathJax.startup &&
MathJax.startup.promise && typeof MathJax.typesetPromise === "function") {
prevTime = getTime();
_this.mathjaxRunning = true;
return MathJax.Hub.Queue(["Typeset", MathJax.Hub, _this.$buffer.attr("id")], function() {
_this.mathjaxRunning = false;
curTime = getTime();
_this.mathjaxDelay = curTime - prevTime;
if (previewSetter) {
return previewSetter($(_this.$buffer).html());
} else {
return $(elem).html($(_this.$buffer).html()); // xss-lint: disable=javascript-jquery-html
}
});
return MathJax.startup.promise
.then(
() => MathJax.typesetPromise([_this.$buffer[0]]).then(() => {
curTime = getTime();
_this.mathjaxDelay = curTime - prevTime;
if (previewSetter) {
return previewSetter($(_this.$buffer).html());
} else {
return $(elem).html($(_this.$buffer).html()); // xss-lint: disable=javascript-jquery-html
}
})
);
} else {
return _this.mathjaxDelay = 0;
_this.mathjaxDelay = 0;
if (previewSetter) {
return previewSetter($(_this.$buffer).html());
} else {
return $(elem).html($(_this.$buffer).html()); // xss-lint: disable=javascript-jquery-html
}
}
};
})(this);
Expand Down
12 changes: 11 additions & 1 deletion xblocks_contrib/html/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,17 @@ def student_view(self, _context):
"""Return a fragment that contains the html for the student view."""
frag = Fragment(self.get_html())
frag.add_css(resource_loader.load_unicode("static/css/html.css"))
frag.add_javascript("""function HtmlBlock(runtime, element){}""")
frag.add_javascript("""
function HtmlBlock(runtime, element) {
if (typeof MathJax !== "undefined" && MathJax !== null &&
MathJax.startup && MathJax.startup.promise &&
typeof MathJax.typesetPromise === "function") {
MathJax.startup.promise.then(function() {
MathJax.typesetPromise([element]);
});
}
}
""")
frag.initialize_js("HtmlBlock")
return frag

Expand Down
70 changes: 51 additions & 19 deletions xblocks_contrib/problem/assets/spec/display_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@ describe("Problem", function () {
const mockRuntime = {};

beforeEach(function () {
// Stub MathJax
// Stub MathJax v3/v4 API
window.MathJax = {
Hub: jasmine.createSpyObj("MathJax.Hub", ["getAllJax", "Queue"]),
Callback: jasmine.createSpyObj("MathJax.Callback", ["After"]),
startup: {
promise: { then: function(cb) { cb(); return this; }, catch: function() { return this; } },
document: {
getMathItemsWithin: jasmine.createSpy("getMathItemsWithin"),
},
toMML: jasmine.createSpy("startup.toMML"),
},
typesetPromise: jasmine.createSpy("typesetPromise"),
typesetClear: jasmine.createSpy("typesetClear"),
};
this.stubbedJax = { root: jasmine.createSpyObj("jax.root", ["toMathML"]) };
MathJax.Hub.getAllJax.and.returnValue([this.stubbedJax]);
this.stubbedJax = { root: {} };
MathJax.startup.document.getMathItemsWithin.and.returnValue([this.stubbedJax]);
MathJax.startup.toMML.and.returnValue("<MathML>");
MathJax.typesetPromise.and.returnValue(Promise.resolve());
window.update_schematics = function () {};
spyOn(SR, "readText");
spyOn(SR, "readTexts");
Expand Down Expand Up @@ -52,11 +61,10 @@ data-url='/problem/quiz/'> \
describe("bind", function () {
beforeEach(function () {
spyOn(window, "update_schematics");
MathJax.Hub.getAllJax.and.returnValue([this.stubbedJax]);
this.problem = new Problem(mockRuntime, $(".xblock-student_view"));
});

it("set mathjax typeset", () => expect(MathJax.Hub.Queue).toHaveBeenCalled());
it("set mathjax typeset", () => expect(MathJax.typesetPromise).toHaveBeenCalled());

it("update schematics", () => expect(window.update_schematics).toHaveBeenCalled());

Expand All @@ -83,12 +91,28 @@ data-url='/problem/quiz/'> \
it("bind the math input", function () {
expect($("input.math")).toHandleWith("keyup", this.problem.refreshMath);
});

it("does not bind refreshMath to formula equation inputs", function () {
// Formula-equation inputs are exclusively managed by formula_equation_preview.js.
$.ajax.and.callFake(function (settings) {
if (settings.url.match(/.+\/problem_get$/)) {
settings.success({
html: readFixtures("problem_content.html") +
'<section class="formulaequationinput"><input type="text" id="input_formula_1" />' +
'<div id="input_formula_1_preview" class="equation"></div></section>',
});
}
});
this.problem = new Problem(mockRuntime, $(".xblock-student_view"));
MathJax.typesetClear.calls.reset();
$(".formulaequationinput input").trigger("keyup");
expect(MathJax.typesetClear).not.toHaveBeenCalled();
});
});

describe("bind_with_custom_input_id", function () {
beforeEach(function () {
spyOn(window, "update_schematics");
MathJax.Hub.getAllJax.and.returnValue([this.stubbedJax]);
this.problem = new Problem(mockRuntime, $(".xblock-student_view"));
return $(this).html(readFixtures("problem_content_1240.html"));
});
Expand Down Expand Up @@ -993,38 +1017,46 @@ data-url='/problem/quiz/'> \
this.problem.refreshMath({ target: $("#input_example_1").get(0) });
});

it("should queue the conversion and MathML element update", function () {
expect(MathJax.Hub.Queue).toHaveBeenCalledWith(
["Text", this.stubbedJax, "E=mc^2"],
[this.problem.updateMathML, this.stubbedJax, $("#input_example_1").get(0)],
);
it("should trigger MathJax v4 typesetting via typesetClear + typesetPromise", function () {
expect(MathJax.typesetClear).toHaveBeenCalled();
expect(MathJax.typesetPromise).toHaveBeenCalled();
});

it("preserves backslash-delimited TeX without wrapping in AsciiMath backticks", function () {
$("#input_example_1").val("\\\(E=mc^2\\\)");
this.problem.refreshMath({ target: $("#input_example_1").get(0) });
expect($("#display_example_1").text()).toBe("\\\(E=mc^2\\\)");
});
});

describe("updateMathML", function () {
beforeEach(function () {
this.problem = new Problem(mockRuntime, $(".xblock-student_view"));
this.stubbedJax.root.toMathML.and.returnValue("<MathML>");
});

describe("when there is no exception", function () {
beforeEach(function () {
this.problem.updateMathML(this.stubbedJax, $("#input_example_1").get(0));
});

it("convert jax to MathML", () => expect($("#input_example_1_dynamath")).toHaveValue("<MathML>"));
it("convert jax to MathML via startup.toMML", function () {
expect(MathJax.startup.toMML).toHaveBeenCalledWith(this.stubbedJax.root);
expect($("#input_example_1_dynamath")).toHaveValue("<MathML>");
});
});

describe("when there is an exception", function () {
describe("when there is an exception with restart", function () {
beforeEach(function () {
const error = new Error();
error.restart = true;
this.stubbedJax.root.toMathML.and.throwError(error);
MathJax.startup.toMML.and.throwError(error);
this.problem.updateMathML(this.stubbedJax, $("#input_example_1").get(0));
});

it("should queue up the exception", function () {
expect(MathJax.Callback.After).toHaveBeenCalledWith([this.problem.refreshMath, this.stubbedJax], true);
it("should catch the error and not propagate", function () {
// v4: caught by try/catch, restart=true → startup.promise.then schedules refreshMath.
// Minimal assertion: toMML was invoked, no uncaught exception.
expect(MathJax.startup.toMML).toHaveBeenCalled();
});
});
});
Expand Down
Loading