-
-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathFront Template.html
More file actions
449 lines (398 loc) · 14.9 KB
/
Front Template.html
File metadata and controls
449 lines (398 loc) · 14.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
<!-- version b174588 -->
<div id="text">{{cloze:Text}}</div>
<br>
<!-- ############## Text-to-speech ##############
replace the arrows/dashes from the statement below with double curly brackets-->
<!--tts en_US voices=Apple_Samantha,Microsoft_Zira speed=1.4:cloze:Text-->
<script>
// ############## USER CONFIGURATION START ##############
// Auto flip to back when One by one mode.
var autoflip = false
// Timer config (timer length, timer finished message)
var minutes = 0
var seconds = 9
var timeOverMsg = "<span style='color:#CC5B5B'>!<br/>!<br/>!<br/>!<br/>!<br/>!</span>"
// ############## TAG SHORTCUT ##############
var toggleTagsShortcut = "C";
// ENTER THE TAG TERM WHICH, WHEN PRESENT, WILL TRIGGER A RED BACKGROUND
var tagID = "XXXYYYZZZ"
// WHETHER THE WHOLE TAG OR ONLY THE LAST PART SHOULD BE SHOWN
var numTagLevelsToShow = 0;
// ############## CLOZE ONE BY ONE ##############
var revealNextShortcut = "N"
var revealNextWordShortcut = "Shift + N"
var toggleAllShortcut = ","
// Changes how "Reveal Next" and clicking behaves. Either "cloze" or "word".
// "word" reveals word by word.
var revealNextClozeMode = "cloze"
// What cloze is hidden with
var clozeHider = (elem) => "👑"
/*
You can replace the above line with below examples. '█' or '_' works well for hiding clozes.
// Fixed length:
var clozeHider = (elem) => "███"
// Replace each character with "█":
var clozeHider = (elem) => "█".repeat(elem.textContent.length)
// Show whitespaces:
var clozeHider = (elem) => "[" + elem.textContent.split(" ").map((t) => "█".repeat(t.length)).join(" ") + "]"
// Color-filled box (doesn't hide images):
var clozeHider = (elem) => `<span style="background-color: red; color: transparent;">${elem.innerHTML}</span>`
*/
// enables selective cloze one-by-one (e.g. only c1 and c3)
// seperate wanted numbers by "," in one-by-one field
var selectiveOneByOne = false;
// if selective one-by-one is disabled, set this to select a min number of clozes necessary to activate 1b1
// can be set to any number to set lower bound, any falsy value (e.g. 0 or null) disables this setting
var minNumberOfClozes = 0;
// ############## USER CONFIGURATION END ##############
</script>
<!-- Credit: Anki Persistence, MIT: https://github.com/SimonLammer/anki-persistence/blob/8190116c95df202773aa692c5e9ae704fb407bc9/script.js -->
<script>
if (typeof(window.Persistence) === 'undefined') {
var _persistenceKey = 'github.com/SimonLammer/anki-persistence/';
var _defaultKey = '_default';
window.Persistence_sessionStorage = function() { // used in android, iOS, web
var isAvailable = false;
try {
if (typeof(window.sessionStorage) === 'object') {
isAvailable = true;
this.clear = function() {
for (var i = 0; i < sessionStorage.length; i++) {
var k = sessionStorage.key(i);
if (k.indexOf(_persistenceKey) == 0) {
sessionStorage.removeItem(k);
i--;
}
};
};
this.setItem = function(key, value) {
if (value == undefined) {
value = key;
key = _defaultKey;
}
sessionStorage.setItem(_persistenceKey + key, JSON.stringify(value));
};
this.getItem = function(key) {
if (key == undefined) {
key = _defaultKey;
}
return JSON.parse(sessionStorage.getItem(_persistenceKey + key));
};
this.removeItem = function(key) {
if (key == undefined) {
key = _defaultKey;
}
sessionStorage.removeItem(_persistenceKey + key);
};
this.getAllKeys = function () {
var keys = [];
var prefixedKeys = Object.keys(sessionStorage);
for (var i = 0; i < prefixedKeys.length; i++) {
var k = prefixedKeys[i];
if (k.indexOf(_persistenceKey) == 0) {
keys.push(k.substring(_persistenceKey.length, k.length));
}
};
return keys.sort()
}
}
} catch(err) {}
this.isAvailable = function() {
return isAvailable;
};
};
window.Persistence_windowKey = function(persistentKey) { // used in windows, linux, mac
var obj = window[persistentKey];
var isAvailable = false;
if (typeof(obj) === 'object') {
isAvailable = true;
this.clear = function() {
obj[_persistenceKey] = {};
};
this.setItem = function(key, value) {
if (value == undefined) {
value = key;
key = _defaultKey;
}
obj[_persistenceKey][key] = value;
};
this.getItem = function(key) {
if (key == undefined) {
key = _defaultKey;
}
return obj[_persistenceKey][key] == undefined ? null : obj[_persistenceKey][key];
};
this.removeItem = function(key) {
if (key == undefined) {
key = _defaultKey;
}
delete obj[_persistenceKey][key];
};
this.getAllKeys = function () {
return Object.keys(obj[_persistenceKey]);
}
if (obj[_persistenceKey] == undefined) {
this.clear();
}
}
this.isAvailable = function() {
return isAvailable;
};
};
/*
* client | sessionStorage | persistentKey | useful location |
* ----------|----------------|---------------|-----------------|
* web | YES | - | NO |
* windows | NO | py | NO |
* android | YES | - | NO |
* linux 2.0 | NO | qt | YES |
* linux 2.1 | NO | qt | YES |
* mac 2.0 | NO | py | NO |
* mac 2.1 | NO | qt | YES |
* iOS | YES | - | NO |
*/
window.Persistence = new Persistence_sessionStorage(); // android, iOS, web
if (!Persistence.isAvailable()) {
window.Persistence = new Persistence_windowKey("py"); // windows, mac (2.0)
}
if (!Persistence.isAvailable()) {
var titleStartIndex = window.location.toString().indexOf('title'); // if titleStartIndex > 0, window.location is useful
var titleContentIndex = window.location.toString().indexOf('main', titleStartIndex);
if (titleStartIndex > 0 && titleContentIndex > 0 && (titleContentIndex - titleStartIndex) < 10) {
window.Persistence = new Persistence_windowKey("qt"); // linux, mac (2.1)
}
}
}
</script>
<!-- Credit: Code for selective 1b1 clozes is adapted from the Ankizin project: https://ankiweb.net/shared/info/2058530482 -->
<!-- CLOZE ONE BY ONE FRONT -->
<div id="one-by-one" style="display: none;">{{One by one}}</div>
<script>
// enables cloze one-by-one even when one-by-one field is empty
// minNumberOfClozes is still considered in this case
// overridden in importance by selectiveOneByOne
var alwaysOneByOne = false;
var clozeOneByOneEnabled = true;
var oneByOneFieldNotEmpty = document.getElementById("one-by-one").textContent !== "";
clozeOneByOneEnabled = alwaysOneByOne || oneByOneFieldNotEmpty;
var clozeHints = [];
if (clozeOneByOneEnabled) {
document.getElementById("qa").classList.add('one-by-one');
// Save cloze hints to display in the back
let clozes = document.getElementsByClassName("cloze")
for(var i = 0; i < clozes.length; i++) {
clozes[i].classList.add("one-by-one");
if (clozes[i].innerHTML === "[...]") {
clozeHints.push("")
} else {
clozeHints.push(clozes[i].innerHTML)
}
}
// --- CHECK IF ONE BY ONE SHOULD BE ENABLED FOR THIS SPECIFIC CARD ---
/**
* Credit for the getCardNumber function goes to foenixx (GitHub) / ollevolle (AnkiWeb Forum)
*/
const getCardNumber = function () {
clz = document.body.className;
const regex = /card(\d+)/gm;
let m;
if ((m = regex.exec(clz)) !== null) {
return m[1];
} else {
// Should only fire if card is not cloze
console.error("Cannot find cardN class of body element!");
return "0";
}
}
function processSelective1b1() {
// parse the cloze numbers for which selectiveOneByOne is enabled
var clozeNumbers = document.getElementById("one-by-one").textContent.split(',').filter(element => element).map(Number)
var cardNumberIsOneByOne = !clozeNumbers.filter(n => !Number.isNaN(n)).length || clozeNumbers.includes(parseInt(getCardNumber()))
// check the amount of clozes -> disable OneByOne if less than minimum value wanted (minNumberOfClozes)
var numClozesForNumber = (minNumberOfClozes) ? document.querySelectorAll('.cloze').length : 0
// stop OneByOne if selectiveOneByOne is not enabled for this specific card OR if OneByOne is disabled some other way
// -> show normal backside
if (!alwaysOneByOne && ((selectiveOneByOne && !cardNumberIsOneByOne) || (oneByOneFieldNotEmpty && (numClozesForNumber < minNumberOfClozes)))) {
clozeOneByOneEnabled = false
}
if (autoflip && clozeOneByOneEnabled) {
if(window.pycmd || window.showAnswer) {
// avoid flickering. Must unset this in the back.
document.getElementById("qa").style.display = "none";
}
if (window.pycmd) {
pycmd("ans")
} else if (window.showAnswer) {
showAnswer()
}
}
// AnkiMobile JS API doesn't have one for show answer.
// Best alternative is to use Taps/Swipes to show answer.
}
function delayedProcessSelective1b1() {
if (window.requestAnimationFrame) window.requestAnimationFrame(processSelective1b1); // less flickering
else window.setTimeout(processSelective1b1, 0);
};
if (globalThis.onUpdateHook) {
onUpdateHook.push(delayedProcessSelective1b1);
}
else if (document.readyState === "complete") {
delayedProcessSelective1b1();
}
else {
document.addEventListener("DOMContentLoaded", delayedProcessSelective1b1);
}
}
Persistence.setItem("clozeHints", clozeHints);
</script>
<!-- NOT-PERSISTING EVENT LISTENER -->
<script>
if (window.ankingEventListeners) {
for (const listener of ankingEventListeners) {
const type = listener[0]
const handler = listener[1]
document.removeEventListener(type, handler)
}
}
window.ankingEventListeners = []
window.ankingAddEventListener = function(type, handler) {
document.addEventListener(type, handler)
window.ankingEventListeners.push([type, handler])
}
</script>
<!-- Shortcut Matcher Function -->
<script>
var specialCharCodes = {
"-": "minus",
"=": "equal",
"[": "bracketleft",
"]": "bracketright",
";": "semicolon",
"'": "quote",
"`": "backquote",
"\\": "backslash",
",": "comma",
".": "period",
"/": "slash",
};
// Returns function that match keyboard event to see if it matches given shortcut.
function shortcutMatcher(shortcut) {
let shortcutKeys = shortcut.toLowerCase().split(/[+]/).map(key => key.trim())
let mainKey = shortcutKeys[shortcutKeys.length - 1]
const mainKeyVariants = [];
if (mainKey.length === 1) {
if (/\d/.test(mainKey)) {
// Treat alphanumeric and numpad keys interchangeably
mainKeyVariants.push("digit" + mainKey)
mainKeyVariants.push("numpad" + mainKey)
} else if (/[a-zA-Z]/.test(mainKey)) {
mainKeyVariants.push("key" + mainKey)
} else {
let code = specialCharCodes[mainKey];
if (code) {
mainKey = code
mainKeyVariants.push(code)
} else {
mainKeyVariants.push(mainKey)
}
}
} else {
mainKeyVariants.push(mainKey)
}
let ctrl = shortcutKeys.includes("ctrl")
let shift = shortcutKeys.includes("shift")
let alt = shortcutKeys.includes("alt")
let matchShortcut = function (ctrl, shift, alt, mainKeyVariants, event) {
if (!mainKeyVariants.includes(event.code.toLowerCase())) return false
if (ctrl !== (event.ctrlKey || event.metaKey)) return false
if (shift !== event.shiftKey) return false
if (alt !== event.altKey) return false
return true
}.bind(window, ctrl, shift, alt, mainKeyVariants)
return matchShortcut
}
</script>
<!-- IMAGE BLUR -->
<script>
for (const image of document.querySelectorAll(".blur")) {
image.classList.add("tappable");
image.addEventListener("click", () => {
image.classList.toggle("blur");
});
}
</script>
<!-- COUNTDOWN TIMER -->
<div class="timer" id="s2"></div>
<script>
function countdown(elementName, minutes, seconds) {
var element, endTime, mins, msLeft, time;
function twoDigits( n ) {
return (n <= 9 ? "0" + n : n);
}
function updateTimer() {
msLeft = endTime - (+new Date);
if ( msLeft < 1000 ) {
element.innerHTML = timeOverMsg;
} else {
time = new Date( msLeft );
mins = time.getUTCMinutes();
element.innerHTML = mins + ':' + twoDigits(time.getUTCSeconds());
setTimeout( updateTimer, time.getUTCMilliseconds() + 500 );
}
}
element = document.getElementById(elementName);
endTime = (+new Date) + 1000 * (60*minutes + seconds) + 500;
updateTimer();
}
countdown("s2", minutes, seconds ); //2nd value is the minute, 3rd is the seconds
</script>
<!-- ANKING HYPERLINK IMAGE -->
<a href="https://www.ankingmed.com"><img src="_AnKingIconSmall.png" alt="The AnKing" id="pic"></a>
<!-- CLICKABLE COLORFUL TAGS -->
{{#Tags}}
<div id="tags-container">{{clickable::Tags}}</div>
<script>
var tagContainer = document.getElementById("tags-container")
var tagList;
if (tagContainer.childElementCount == 0) {
tagList = tagContainer.innerHTML.split(" ");
var kbdList = [];
var newTagContent = document.createElement("div");
for (var i = 0; i < tagList.length; i++) {
var newTag = document.createElement("kbd");
var tag = tagList[i];
// numTagLevelsToShow == 0 means the whole tag should be shown
if(numTagLevelsToShow != 0){
tag = tag.split('::').slice(-numTagLevelsToShow).join("::");
}
newTag.innerHTML = tag;
newTagContent.append(newTag)
}
tagContainer.innerHTML = newTagContent.innerHTML;
tagContainer.style.cursor = "default";
}
else {
tagList = Array.from(tagContainer.children).map(e => e.innerText);
}
globalThis.tagList = tagList.map(t => t.trim().toLowerCase());
if (tagContainer.innerHTML.indexOf(tagID) != -1) {
tagContainer.style.backgroundColor = "rgba(251,11,11,.15)";
}
function showtags() {
var tagContainerShortcut = document.getElementById("tags-container");
if (tagContainerShortcut.style.display
=== "none") {
tagContainerShortcut.style.display = "block";
} else {
tagContainerShortcut.style.display =
"none";
}
}
var isShortcut = shortcutMatcher(toggleTagsShortcut)
ankingAddEventListener('keyup', function (e) {
if (isShortcut(e)) {
showtags();
}
});
</script>
{{/Tags}}