Hello!
I’m trying to be able to create cloze card with progressive reveal, i.e.
SQL commands can be divided into:
{{c1:: Data Definition Language }}
{{c1:: Data Manipulation Language }}
{{c1:: Data Control Language }}
So that clozes are revealed sequentially.
Following template provides this functionality in exactly the form I’m looking for https: //ankiweb .net/shared/info/1874787050
Front:
<script>
var logDiv = null;
function log(s) {
if (logDiv == null) {
logDiv = document.createElement("div");
logDiv.id = 'log_debug';
logDiv.style = 'position:absolute;left:0;top:0;z-index:-2;color:grey;font-size:small';
document.body.append(logDiv);
}
logDiv.insertAdjacentHTML('beforeend', s + '<br/>');
}
/*
function logSizes() {
let ovl = document.getElementById('tap_overlay');
let rect = ovl.getBoundingClientRect();
let vp = window.visualViewport;
log(`ovl: ${rect.width.toFixed(2)},${rect.height.toFixed(2)} vp: ${vp.width.toFixed(2)},${vp.height.toFixed(2)}`);
}
*/
function getCardNumber() {
clz = document.body.className;
const regex = /card(\d+)/gm;
let m;
if ((m = regex.exec(clz)) !== null) {
return m[1];
} else {
console.error("Cannot find cardN class of body element!");
return "0";
}
}
function getClozes(str, cardNumber) {
const regex = new RegExp(`\{\{c${cardNumber}::(.*?)(\}\}|::.*?\}\})`, 'gm')
//console.log(regex);
let m;
const clozes = [];
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
m.forEach((match, groupIndex) => {
//console.log(`Found match, group ${groupIndex}: ${match}`);
if (groupIndex == 1) {
clozes.push(match);
}
});
}
return clozes;
}
function clickHandler(e){
//console.log(`${e.target.tagName}(${e.target.id})`);
const tt = e.target
if ( tt instanceof HTMLElement &&
( tt.id === 'qa' ||
tt.tagName === 'HTML' ||
tt.tagName === 'LI' ||
tt.tagName === 'I' )) {
//log('reveal');
revealNextCloze();
}
}
var elements;
var clozes;
var revealed = [];
function revealCloze(i) {
if (!revealed[i]) {
elements[i].innerHTML = clozes[i];
revealed[i] = true;
}
}
function revealNextCloze() {
firstUnrevealed = revealed.findIndex ( el => !el );
//log(firstUnrevealed);
if (firstUnrevealed != -1) {
revealCloze(firstUnrevealed);
}
}
onUpdateHook.push(function() {
//console.log(`inside update hook`);
var text = document.getElementById("rawText").innerHTML ;
//console.log(text);
clozes = getClozes(text, getCardNumber());
//console.log(clozes);
elements = document.querySelectorAll(".cloze");
if (clozes.length != elements.length) {
console.error("Inconsistent cound of clozes found in original note text and in the card!");
return;
}
elements.forEach((el, i) => {
el.addEventListener('click', e => {
revealCloze(i);
//log(i);
})
});
revealed.length = elements.length;
revealed.fill(false);
window.addEventListener('click', clickHandler);
});
</script>
<script id="rawText" type="text/plain">
{{Text}}
</script>
{{cloze:Text}}
Back:
<script>
window.removeEventListener('click', clickHandler);
</script>
{{cloze:Text}}<br>
{{Extra}}
It works on Windows and Android, however, it doesn’t work on iOS - tapping reveals all clozes at once. In config, all the taps are set to default - “Show answer”.
I understand that I need to add tappable to the class (https:// docs .ankimobile.net/more.html#javascript), so far I’ve tried the following with no luck:
<script>
function revealCloze(evt) {
const clozeEl = evt.target.closest(".cloze");
if (clozeEl && !clozeEl.dataset.revealed) {
const clozeContent = clozes[clozeEl.dataset.index];
clozeEl.innerHTML = clozeContent;
clozeEl.dataset.revealed = "true";
}
}
function getCardNumber() {
const clz = document.body.className;
const match = clz.match(/card(\d+)/);
return match ? match[1] : "0";
}
function getClozes(str, cardNumber) {
const regex = new RegExp(`\{\{c${cardNumber}::(.*?)(\}\}|::.*?\}\})`, 'gm');
const clozes = [];
let match;
while ((match = regex.exec(str)) !== null) {
if (match[1]) {
clozes.push(match[1]);
}
}
return clozes;
}
var clozes;
onUpdateHook.push(function() {
const text = document.getElementById("rawText").innerHTML;
clozes = getClozes(text, getCardNumber());
const elements = document.querySelectorAll(".cloze");
elements.forEach((el, index) => {
el.dataset.index = index;
el.addEventListener("click", revealCloze);
el.classList.add('tappable');
});
});
</script>
<script id="rawText" type="text/plain">
{{Text}}
</script>
{{cloze:Text}}
Need help adjusting this for iOS.