anki/qt/aqt/data/web/js/reviewer.ts

177 lines
4.4 KiB
TypeScript
Raw Normal View History

2019-02-05 04:59:03 +01:00
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
2020-12-29 13:43:47 +01:00
declare var MathJax: any;
type Callback = () => void | Promise<void>;
var ankiPlatform = "desktop";
var typeans: HTMLElement | undefined;
var _updatingQueue: Promise<void> = Promise.resolve();
var onUpdateHook: Array<Callback>;
var onShownHook: Array<Callback>;
function _runHook(arr: Array<Callback>): Promise<void[]> {
2021-02-02 18:11:16 +01:00
const promises = [];
2021-02-02 18:11:16 +01:00
for (let i = 0; i < arr.length; i++) {
promises.push(arr[i]());
}
return Promise.all(promises);
}
function _queueAction(action: Callback): void {
_updatingQueue = _updatingQueue.then(action);
}
2020-12-30 15:06:22 +01:00
async function _updateQA(
html: string,
onupdate: Callback,
onshown: Callback
2020-12-30 15:06:22 +01:00
): Promise<void> {
onUpdateHook = [onupdate];
onShownHook = [onshown];
2021-04-13 00:22:00 +02:00
const qa = document.getElementById("qa")!;
2021-02-02 18:11:16 +01:00
const renderError = (kind: string) => (error: Error): void => {
const errorMessage = String(error).substring(0, 2000);
const errorStack = String(error.stack).substring(0, 2000);
qa.innerHTML = `Invalid ${kind} on card: ${errorMessage}\n${errorStack}`.replace(
/\n/g,
"<br>"
);
2021-02-02 18:11:16 +01:00
};
2021-04-13 00:22:00 +02:00
// hide current card
qa.style.opacity = "0";
2020-12-29 13:32:04 +01:00
2021-04-13 00:22:00 +02:00
// update card
2020-12-29 13:32:04 +01:00
try {
2021-04-13 00:22:00 +02:00
qa.innerHTML = html;
2021-02-02 18:11:16 +01:00
} catch (error) {
renderError("HTML")(error);
2020-12-29 14:45:20 +01:00
}
2020-12-29 13:32:04 +01:00
2021-02-03 13:32:55 +01:00
await _runHook(onUpdateHook);
2020-12-29 13:43:47 +01:00
// wait for mathjax to ready
2021-02-02 18:11:16 +01:00
await MathJax.startup.promise
.then(() => {
// clear MathJax buffers from previous typesets
MathJax.typesetClear();
2021-04-13 00:22:00 +02:00
return MathJax.typesetPromise([qa]);
2021-02-02 18:11:16 +01:00
})
.catch(renderError("MathJax"));
2020-12-29 13:32:04 +01:00
// defer display for up to 100ms to allow images to load
await Promise.race([allImagesLoaded(), new Promise((r) => setTimeout(r, 100))]);
2021-04-13 00:22:00 +02:00
// and reveal card when processing is done
qa.style.opacity = "1";
2020-12-29 13:32:04 +01:00
await _runHook(onShownHook);
2017-07-28 08:48:49 +02:00
}
2020-12-30 15:06:22 +01:00
function _showQuestion(q: string, bodyclass: string): void {
_queueAction(() =>
_updateQA(
q,
function () {
// return to top of window
window.scrollTo(0, 0);
2019-12-18 07:12:39 +01:00
document.body.className = bodyclass;
2020-12-30 15:06:22 +01:00
},
function () {
// focus typing area if visible
typeans = document.getElementById("typeans");
if (typeans) {
typeans.focus();
}
2019-12-18 07:12:39 +01:00
}
2020-12-30 15:06:22 +01:00
)
);
}
2019-12-18 07:12:39 +01:00
2020-12-30 15:06:22 +01:00
function _showAnswer(a: string, bodyclass: string): void {
_queueAction(() =>
_updateQA(
a,
function () {
if (bodyclass) {
// when previewing
document.body.className = bodyclass;
}
// avoid scrolling to the answer until images load, even if it
// takes more than 100ms
allImagesLoaded().then(scrollToAnswer);
2020-12-30 15:06:22 +01:00
},
function () {}
)
);
}
2019-12-18 04:32:32 +01:00
const _flagColours = {
2018-11-01 05:58:41 +01:00
1: "#ff6666",
2: "#ff9900",
3: "#77ff77",
2019-12-18 07:12:39 +01:00
4: "#77aaff",
};
2021-01-01 14:14:50 +01:00
function _drawFlag(flag: 0 | 1 | 2 | 3 | 4): void {
const elem = document.getElementById("_flag");
if (flag === 0) {
elem.setAttribute("hidden", "");
return;
}
elem.removeAttribute("hidden");
elem.style.color = _flagColours[flag];
}
2020-12-30 15:06:22 +01:00
function _drawMark(mark: boolean): void {
const elem = document.getElementById("_mark");
if (!mark) {
elem.setAttribute("hidden", "");
} else {
elem.removeAttribute("hidden");
}
}
2020-12-30 15:06:22 +01:00
function _typeAnsPress(): void {
if ((window.event as KeyboardEvent).code === "Enter") {
pycmd("ans");
}
}
2020-12-30 15:06:22 +01:00
function _emulateMobile(enabled: boolean): void {
const list = document.documentElement.classList;
if (enabled) {
list.add("mobile");
} else {
list.remove("mobile");
}
}
function allImagesLoaded(): Promise<void[]> {
return Promise.all(
Array.from(document.getElementsByTagName("img")).map(imageLoaded)
);
}
function imageLoaded(img: HTMLImageElement): Promise<void> {
if (img.complete) {
return;
}
return new Promise((resolve) => {
img.addEventListener("load", () => resolve());
img.addEventListener("error", () => resolve());
});
}
function scrollToAnswer(): void {
document.getElementById("answer")?.scrollIntoView();
}