Try [Reviews SRS Preview] and let me know what you think. To keep things simple, it just reuses the existing post-answer SRS popup, but without the pass/fail indicators.

Try [Reviews SRS Preview] and let me know what you think. To keep things simple, it just reuses the existing post-answer SRS popup, but without the pass/fail indicators.

Thank you, I’ve been using it for several days now and I have no notes at all—fantastic!
It has occurred to me on to-be-Burned items that it gives me a bit of a hint, in that I sometimes have thought a “long-ago word” was in fact one I just learned because of similarity in shape or meaning (and also because vocab especially but occasionally kanji too get moved around in the course from a level I already learned them in to one I’m just doing now). Seeing it’s Enlightened warns me that no, it’s not the word I just learned.
OTOH, “The Week Without Double-Check” (I’m trademarking that) taught me that I was being a bit too lenient in trusting myself when I thought “oh, I just flubbed typing じ — I meant to type し, honest!”
Which does bring me to a future feature request for Double-Check: a list of inputted answers (for my purposes just for the current card in front of me; I don’t care especially about the previous time(s) I saw it and got it wrong). Here’s the use case from an actual experience last night:
Oh, wait—did I think it was きょうえいしん? Maybe I did. Once I stopped to think about it, I remembered the mnemonic about the void in Little Kyoto and was certain about 心 being read しん, but I convinced myself that “えい” used its kun’yomi here for some reason. Did I remember some mnemonic about the vanity of the light making it change readings or something?
Maybe I typed “きょうえいしん”. If I lengthened 虚 into きょう, I definitely want to mark it wrong, but if I just typo’d “きょいえしん”, I want to mark it right. Oh well, better safe than sorry, I’ll mark it wrong.
This happens to me for readings with some frequency; I make guesses after I’ve decided to mark it wrong (I’ll type - if I get it) before just looking, and every now and then I give up, look, and go “wait—that’s what I thought of already!” Or did I? Was it the first thing I tried but I typo’d? Did I type a different okurigana than the one right in front of me because I use this word in some other conjugated form more often? Who knows?
Maybe I’m the only one who uses Double-Check this way, to try multiple readings rather than just revealing them, but it would be quite useful. (I also do this for “you were a little off” meanings, or to try variant meanings to see if I remember them; it would be helpful for that, too.)
I realize this would be a pretty major addition, but I’m just throwing it out there since it would have to be part of Double-Check!
Ah, I just thought of something: maybe it wouldn’t be too large an addition if it just got added to the recall cycling you can get by repeatedly pressing + — you’d just tap it until you saw the first red answer and you’d know it’s what you typed. That’s just as good as some list format, and wouldn’t require totally new elements.
In case what I’m imagining is hard to visualize, I’ll step through how I imagine using it in my use case above (with 虚栄心); you can expand to read it if you like.
(If it had been a truly silly mistake like an okurigana misread I might just press + until I see the correct answer again—in this case, once extra to cycle past my second incorrect guess of 「きょさくしん」 and then ⏎ to mark it correct and move on.)
Anyone else have this problem and hopefully solved it. I used to have double check working through tampermonkey but recently have this error in the console:
Uncaught (in promise) ReferenceError: Unhandled Promise Rejection: wkof is not defined
at userscript.html?name=Wanikani-Double-Check.user.js&id=ef39a9b0-3b36-41fd-8f44-f63bf8cb1ecb:32:5
at Object. (userscript.html?name=Wanikani-Double-Check.user.js&id=ef39a9b0-3b36-41fd-8f44-f63bf8cb1ecb:947:3)
at St (:9:89)
at userscript.html?name=Wanikani-Double-Check.user.js&id=ef39a9b0-3b36-41fd-8f44-f63bf8cb1ecb:1:90
at window.__f__ljg3x8cp.e8l (userscript.html?name=Wanikani-Double-Check.user.js&id=ef39a9b0-3b36-41fd-8f44-f63bf8cb1ecb:1:325)
at St (:9:89)
at s (:72:214)
at :75:107
at g (:69:364)
For the life of me I can’t figure out why - turned off ad blockers etc.
Mac M2 Pro on Ventura with Chrome 114
This looks like the script is trying to use the wanikani open framework and it is not there. Usually this is handled gracefully with a warning popup though. Have you checked that you have installed wkof and it is above doublecheck in the script load order of tampermonkey?
legend, thank you. It was the ordering of the scripts
I’m having an issue with the script - it doesn’t load. Here’s the console output:
userscript.html?name…33-9ed23ae928cb:390 indexedDB could not open!
review:1 Uncaught (in promise) undefined
Promise.then (async)
a.then @ review:7
process_result @ userscript.html?name…33-9ed23ae928cb:208
nrWrapper @ review:7
XMLHttpRequest.send (async)
nrWrapper @ review:7
fetch_url @ userscript.html?name…33-9ed23ae928cb:199
nrWrapper @ review:7
Promise.then (async)
a.then @ review:7
load_file @ userscript.html?name…33-9ed23ae928cb:189
load_and_append @ userscript.html?name…33-9ed23ae928cb:221
load_script @ userscript.html?name…33-9ed23ae928cb:243
include @ userscript.html?name…33-9ed23ae928cb:136
load_script @ userscript.html?name…266-d8f42c75161a:63
(anonymous) @ userscript.html?name…33-9ed23ae928cb:367
handle_page_events @ userscript.html?name…33-9ed23ae928cb:365
(anonymous) @ userscript.html?name…33-9ed23ae928cb:620
Promise.then (async)
startup @ userscript.html?name…33-9ed23ae928cb:620
(anonymous) @ userscript.html?name…33-9ed23ae928cb:634
(anonymous) @ userscript.html?name…33-9ed23ae928cb:636
St @ VM2085:9
(anonymous) @ userscript.html?name…b533-9ed23ae928cb:1
window.__f__ljpr55bf.sug @ userscript.html?name…b533-9ed23ae928cb:1
St @ VM2085:9
s @ VM2085:72
(anonymous) @ VM2085:73
set @ VM2085:82
(anonymous) @ userscript.html?name…b533-9ed23ae928cb:1
(anonymous) @ VM2083 content.js:21
eval @ VM2083 content.js:22
(anonymous) @ VM2083 content.js:41
then @ VM2083 content.js:15
w @ VM2083 content.js:41
Io @ VM2083 content.js:43
(anonymous) @ VM2083 content.js:42
Mt @ VM2083 content.js:8
t @ VM2083 content.js:9
xo @ VM2083 content.js:38
Ko @ VM2083 content.js:55
(anonymous) @ VM2083 content.js:54
(anonymous) @ VM2083 content.js:15
Mt @ VM2083 content.js:8
t @ VM2083 content.js:9
(anonymous) @ VM2083 content.js:15
(anonymous) @ VM2083 content.js:51
I’m seeing this on my PC, but it works perfectly fine on my laptop. Both are running the newest version of Chrome, both with all extensions other than Tampermonkey disabled and only WOF and Double-Check enabled.
This is generally one of two problems:
That did the trick. Thank you!
@rfindley
I’ve been using my own modified version of the script for quite some time and been manually updating it every so often to keep up with your updates. But I thought maybe others could be interested in the same functionality and hope that you consider implementing these changes into the official script. Here’s what I changed:
// ==UserScript==
// @name Wanikani Double-Check
// @namespace wkdoublecheck
// @description Allows retyping typo'd answers, or marking wrong when WK's typo tolerance is too lax.
// @match https://www.wanikani.com/*
// @match https://preview.wanikani.com/*
// @version 3.1.9
// @author Robin Findley
// @copyright 2017-2023, Robin Findley
// @license MIT; http://opensource.org/licenses/MIT
// @run-at document-end
// @grant none
// ==/UserScript==
// HOTKEYS:
// "+" - Marks answer as 'correct'.
// "-" - Marks answer as 'incorrect'.
// "Escape" or "Backspace" - Resets question, allowing you to retype.
// SEE SETTINGS BELOW.
window.doublecheck = {};
(function(gobj) {
/* global wkof, Stimulus, WaniKani, importShim, $ */
let script_name = 'Double-Check';
let wkof_version_needed = '1.1.3';
wkof.on_page_event({
urls: [
'https://*.wanikani.com/subjects/review',
'https://*.wanikani.com/subjects/extra_study*',
],
load: load_script,
unload: unload_script,
});
let settings;
let quiz_input, quiz_queue, additional_content, item_info, quiz_audio, quiz_stats, quiz_progress, quiz_header, response_helpers, wanakana;
let answer_checker, answer_check, subject_stats, subject_stats_cache, session_stats;
let old_submit_handler, ignore_submit, state, delay_timer, end_of_session_delay;
let subject, synonyms, accepted_meanings, accepted_readings, srs_mgr;
let qtype, new_answer_check, first_answer_check;
function promise(){let a,b,c=new Promise(function(d,e){a=d;b=e;});c.resolve=a;c.reject=b;return c;}
function load_script() {
if (!window.wkof) {
if (confirm(script_name+' requires Wanikani Open Framework.\nDo you want to be forwarded to the installation instructions?')) {
window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
}
return;
}
if (wkof.version.compare_to(wkof_version_needed) === 'older') {
if (confirm(script_name+' requires Wanikani Open Framework version '+wkof_version_needed+'.\nDo you want to be forwarded to the update page?')) {
window.location.href = 'https://greasyfork.org/en/scripts/38582-wanikani-open-framework';
}
return;
}
wkof.include('Menu,Settings');
wkof.ready('document,Menu,Settings').then(setup);
}
function unload_script() {
document.querySelector('style[name="doublecheck"]')?.remove();
}
//------------------------------------------------------------------------
// setup() - Set up the menu link and default settings.
//------------------------------------------------------------------------
function setup() {
wkof.Menu.insert_script_link({name:'doublecheck',submenu:'Settings',title:'Double-Check',on_click:open_settings});
let defaults = {
allow_retyping: true,
allow_change_correct: false,
show_corrected_answer: false,
allow_change_incorrect: false,
typo_action: 'ignore',
wrong_answer_type_action: 'warn',
wrong_number_n_action: 'warn',
small_kana_action: 'warn',
kanji_reading_for_vocab_action: 'warn',
kanji_meaning_for_vocab_action: 'warn',
delay_wrong: true,
delay_multi_reading: false,
delay_slightly_off: false,
delay_period: 1.5,
warn_burn: 'never',
burn_delay_period: 1.5,
show_lightning_button: true,
lightning_enabled: false,
srs_msg_period: 1.2,
autoinfo_correct: false,
autoinfo_incorrect: false,
autoinfo_multi_meaning: false,
autoinfo_slightly_off: false
}
return wkof.Settings.load('doublecheck', defaults)
.then(init_ui.bind(null, true /* first_time */));
}
//------------------------------------------------------------------------
// open_settings() - Open the Settings dialog.
//------------------------------------------------------------------------
function open_settings() {
let dialog = new wkof.Settings({
script_id: 'doublecheck',
title: 'Double-Check Settings',
on_save: init_ui,
pre_open: settings_preopen,
content: {
tabAnswers: {type:'page',label:'Answers',content:{
grpChangeAnswers: {type:'group',label:'Change Answer',content:{
allow_retyping: {type:'checkbox',label:'Allow retyping answer',default:true,hover_tip:'When enabled, you can retype your answer by pressing Escape or Backspace.'},
allow_change_incorrect: {type:'checkbox',label:'Allow changing to "incorrect"',default:true,hover_tip:'When enabled, you can change your answer\nto "incorrect" by pressing the "-" key.'},
allow_change_correct: {type:'checkbox',label:'Allow changing to "correct"',default:true,hover_tip:'When enabled, you can change your answer\nto "correct" by pressing the "+" key.'},
show_corrected_answer: {type:'checkbox',label:'Show corrected answer',default:false,hover_tip:'When enabled, pressing \'+\' to correct your answer puts the\ncorrected answer in the input field. Pressing \'+\' multiple\ntimes cycles through all acceptable answers.'},
}},
grpCarelessMistakes: {type:'group',label:'Careless Mistakes',content:{
typo_action: {type:'dropdown',label:'Typos in meaning',default:'ignore',content:{ignore:'Ignore',warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when meaning contains typos.'},
wrong_answer_type_action: {type:'dropdown',label:'Wrong answer type',default:'warn',content:{warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when reading was entered instead of meaning, or vice versa.'},
wrong_number_n_action: {type:'dropdown',label:'Wrong number of n\'s',default:'warn',content:{warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when you type the wrong number of n\'s in certain reading questions.'},
small_kana_action: {type:'dropdown',label:'Big kana instead of small',default:'warn',content:{warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when you type a big kana instead of small (e.g. ゆ instead of ゅ).'},
kanji_reading_for_vocab_action: {type:'dropdown',label:'Kanji reading instead of vocab',default:'warn',content:{warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when the reading of a kanji is entered for a single character vocab word instead of the correct vocab reading.'},
kanji_meaning_for_vocab_action: {type:'dropdown',label:'Kanji meaning instead of vocab',default:'warn',content:{warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when the meaning of a kanji is entered for a single character vocab word instead of the correct vocab meaning.'},
}},
}},
tabMistakeDelay: {type:'page',label:'Mistake Delay',content:{
grpDelay: {type:'group',label:'Delay Next Question',content:{
delay_wrong: {type:'checkbox',label:'Delay when wrong',default:true,refresh_on_change:true,hover_tip:'If your answer is wrong, you cannot advance\nto the next question for at least N seconds.'},
delay_multi_reading: {type:'checkbox',label:'Delay when multiple readings',default:false,hover_tip:'If the item has multiple readings, you cannot advance\nto the next question for at least N seconds.'},
delay_slightly_off: {type:'checkbox',label:'Delay when answer has typos',default:false,hover_tip:'If your answer contains typos, you cannot advance\nto the next question for at least N seconds.'},
delay_period: {type:'number',label:'Delay period (in seconds)',default:1.5,hover_tip:'Number of seconds to delay before allowing\nyou to advance to the next question.'},
}},
}},
tabBurnReviews: {type:'page',label:'Burn Reviews',content:{
grpBurnReviews: {type:'group',label:'Burn Reviews',content:{
warn_burn: {type:'dropdown',label:'Warn before burning',default:'never',content:{never:'Never',cheated:'If you changed answer',always:'Always'},hover_tip:'Choose when to warn before burning an item.'},
burn_delay_period: {type:'number',label:'Delay after warning (in seconds)',default:1.5,hover_tip:'Number of seconds to delay before allowing\nyou to advance to the next question after seeing a burn warning.'},
}},
}},
tabLightning: {type:'page',label:'Lightning',content:{
grpLightning: {type:'group',label:'Lightning Mode',content:{
show_lightning_button: {type:'checkbox',label:'Show "Lightning Mode" button',default:true,hover_tip:'Show the "Lightning Mode" toggle\nbutton on the review screen.'},
lightning_enabled: {type:'checkbox',label:'Enable "Lightning Mode"',default:true,refresh_on_change:true,hover_tip:'Enable "Lightning Mode", which automatically advances to\nthe next question if you answer correctly.'},
srs_msg_period: {type:'number',label:'SRS popup time (in seconds)',default:1.2,min:0,hover_tip:'How long to show SRS up/down popup when in lightning mode. (0 = don\'t show)'},
}},
}},
tabAutoInfo: {type:'page',label:'Item Info',content:{
grpAutoInfo: {type:'group',label:'Show Item Info',content:{
autoinfo_correct: {type:'checkbox',label:'After correct answer',default:false,hover_tip:'Automatically show the Item Info after correct answers.', validate:validate_autoinfo_correct},
autoinfo_incorrect: {type:'checkbox',label:'After incorrect answer',default:false,hover_tip:'Automatically show the Item Info after incorrect answers.', validate:validate_autoinfo_incorrect},
autoinfo_multi_meaning: {type:'checkbox',label:'When multiple meanings',default:false,hover_tip:'Automatically show the Item Info when an item has multiple meanings.', validate:validate_autoinfo_correct},
autoinfo_slightly_off: {type:'checkbox',label:'When answer has typos',default:false,hover_tip:'Automatically show the Item Info when your answer has typos.', validate:validate_autoinfo_correct},
}},
}},
}
});
dialog.open();
}
//------------------------------------------------------------------------
// validate_autoinfo_correct() - Notify user if iteminfo and lightning are both enabled.
//------------------------------------------------------------------------
function validate_autoinfo_correct(enabled) {
if (enabled && settings.lightning_enabled) {
return 'Disable "Lightning Mode"!';
}
}
//------------------------------------------------------------------------
// validate_autoinfo_incorrect() - Notify user if iteminfo and lightning are both enabled, and wrong_delay disabled.
//------------------------------------------------------------------------
function validate_autoinfo_incorrect(enabled) {
if (enabled && settings.lightning_enabled && !settings.delay_wrong) {
return 'Disable "Lightning Mode", or<br>enable "Delay when wrong"!';
}
}
//------------------------------------------------------------------------
// settings_preopen() - Notify user if iteminfo and lightning are both enabled.
//------------------------------------------------------------------------
function settings_preopen(dialog) {
dialog.dialog({width:525});
}
//------------------------------------------------------------------------
// init_ui() - Initialize the user interface.
//------------------------------------------------------------------------
let first_time = true;
async function init_ui() {
settings = wkof.settings.doublecheck;
if (first_time) {
first_time = false;
await startup();
}
// Migrate 'lightning' setting from localStorage.
let lightning = localStorage.getItem('lightning');
if (lightning === 'false' || lightning === 'true') {
localStorage.removeItem('lightning');
settings.lightning_enabled = lightning;
wkof.Settings.save('doublecheck');
}
// Initialize the Lightning Mode button.
document.querySelector('#lightning-mode').classList.toggle('doublecheck-active', settings.lightning_enabled);
document.querySelector('#lightning-mode').hidden = !settings.show_lightning_button;
document.querySelector('#option-toggle-rightwrong').classList.toggle('hidden', !(settings.allow_change_correct || settings.allow_change_incorrect));
document.querySelector('#option-retype').classList.toggle('hidden', !settings.allow_retyping);
resize_buttons();
additional_content = get_controller('additional-content');
if (state === 'second_submit') {
document.querySelector('#option-toggle-rightwrong a').classList.toggle(additional_content.toggleDisabledClass, !(
(new_answer_check.passed && (settings.allow_change_incorrect || !first_answer_check.passed)) ||
(!new_answer_check.passed && (settings.allow_change_correct || first_answer_check.passed))
));
document.querySelector('#option-retype a').classList.toggle(additional_content.toggleDisabledClass, !settings.allow_retyping);
} else {
document.querySelector('#option-toggle-rightwrong a').classList.add(additional_content.toggleDisabledClass);
}
}
//------------------------------------------------------------------------
// lightning_clicked() - Lightning button handler.
//------------------------------------------------------------------------
function lightning_clicked(e) {
e.preventDefault();
settings.lightning_enabled = !settings.lightning_enabled;
wkof.Settings.save('doublecheck');
document.querySelector('#lightning-mode').classList.toggle('doublecheck-active', settings.lightning_enabled);
return false;
}
//------------------------------------------------------------------------
// get_correct_answers() - Returns an array of acceptable answers.
//------------------------------------------------------------------------
function get_correct_answers() {
if (qtype === 'reading') {
if (subject.type === 'Kanji') {
return subject[subject.primary_reading_type];
} else {
return [].concat(
subject.readings.map((r) => r.reading),
subject.auxiliary_readings.filter((r) => r.type === 'whitelist').map((r) => r.reading)
).filter((r) => typeof r === 'string');
}
} else {
return [].concat(
synonyms,
subject.meanings,
subject.auxiliary_meanings.filter((m) => m.type === 'whitelist').map((m) => m.meaning),
);
}
}
//------------------------------------------------------------------------
// get_next_correct_answer() - Returns the next acceptable answer from the
// array returned by get_correct_answers().
//------------------------------------------------------------------------
function get_next_correct_answer() {
let result = first_answer_check.correct_answers[first_answer_check.correct_answer_index];
first_answer_check.correct_answer_index = (first_answer_check.correct_answer_index + 1) % first_answer_check.correct_answers.length;
return result;
}
//------------------------------------------------------------------------
// toggle_result() - Toggle an answer from right->wrong or wrong->right.
//------------------------------------------------------------------------
function toggle_result(new_state) {
if (new_state === 'toggle') new_state = (new_answer_check.passed ? 'incorrect' : 'correct');
if (state !== 'second_submit') return false;
let input = quiz_input.inputTarget;
let current_state = (quiz_input.inputContainerTarget.getAttribute('correct') === 'true' ? 'correct' : 'incorrect');
let answer_to_show, answer_to_grade;
clear_delay();
switch (new_state) {
case 'correct':
if (!settings.allow_change_correct) {
if (!first_answer_check.passed) return;
answer_to_grade = first_answer_check.answer;
answer_to_show = answer_to_grade;
} else if (current_state === 'correct') {
answer_to_grade = get_next_correct_answer();
answer_to_show = answer_to_grade;
} else {
first_answer_check.correct_answer_index = 0;
answer_to_grade = get_next_correct_answer();
answer_to_show = (settings.show_corrected_answer ? answer_to_grade : first_answer_check.answer);
}
input.value = answer_to_grade;
new_answer_check = {
action:'pass',
message:null,
passed:true,
accurate:true,
multipleAnswers:false,
exception:false,
answer:answer_to_grade
};
set_answer_state(new_answer_check);
input.value = answer_to_show;
break;
case 'incorrect':
if (!settings.allow_change_incorrect) {
if (first_answer_check.passed) return;
answer_to_show = first_answer_check.answer;
} else {
answer_to_show = (settings.show_corrected_answer ? 'xxxxxx' : first_answer_check.answer);
}
answer_to_grade = 'xxxxxx';
input.value = answer_to_grade;
new_answer_check = {
action:'fail',
message:{
type:'itemInfoException',
text:`Need help? View the correct ${qtype} and mnemonic.`
},
passed:false,
accurate:false,
multipleAnswers:false,
exception:false,
answer:answer_to_grade
};
set_answer_state(new_answer_check);
input.value = answer_to_show;
break;
case 'retype':
if (!settings.allow_retyping) return false;
set_answer_state({reset:true, retype:true});
break;
}
}
//------------------------------------------------------------------------
// do_delay() - Disable the submit button briefly to prevent clicking past wrong answers.
//------------------------------------------------------------------------
function do_delay(period) {
if (period === undefined) period = settings.delay_period;
ignore_submit = true;
delay_timer = setTimeout(function() {
delay_timer = -1;
ignore_submit = false;
}, period*1000);
}
//------------------------------------------------------------------------
// clear_delay() - Clear the delay timer.
//------------------------------------------------------------------------
function clear_delay() {
if (delay_timer) {
ignore_submit = false;
clearTimeout(delay_timer);
delay_timer = undefined;
}
}
//------------------------------------------------------------------------
function show_exception(message) {
if (typeof message !== 'string') return;
quiz_input.exceptionTarget.textContent = message;
quiz_input.exceptionContainerTarget.hidden = false;
}
//------------------------------------------------------------------------
function hide_exception() {
quiz_input.exceptionContainerTarget.hidden = true;
quiz_input.exceptionTarget.textContent = '';
}
//------------------------------------------------------------------------
function set_answer_state(results, final_submit) {
quiz_stats = get_controller('quiz-statistics');
quiz_queue = get_controller('quiz-queue');
additional_content = get_controller('additional-content');
item_info = get_controller('item-info');
quiz_progress = get_controller('quiz-progress');
quiz_audio = get_controller('quiz-audio');
quiz_header = get_controller('quiz-header');
if (!final_submit) {
if (results.exception) {
quiz_input.shakeForm();
show_exception(answer_check.exception);
quiz_input.inputEnabled = true;
quiz_input.inputTarget.focus();
return;
}
let rightwrong = document.querySelector('#option-toggle-rightwrong a');
let rightwrong_text = rightwrong.querySelector('.additional-content__item-text');
let rightwrong_icon = rightwrong.querySelector('.additional-content__item-icon');
let retype = document.querySelector('#option-retype a');
if (!results.passed || (results.reset === true)) {
rightwrong.classList.toggle(additional_content.toggleDisabledClass, (results.reset === true) || !(settings.allow_change_correct || first_answer_check.passed));
rightwrong_text.innerText = 'Mark Right';
rightwrong_icon.classList.remove('fa-thumbs-down');
rightwrong_icon.classList.add('fa-thumbs-up');
} else {
rightwrong.classList.toggle(additional_content.toggleDisabledClass, (results.reset === true) || !(settings.allow_change_incorrect || !first_answer_check.passed));
rightwrong_text.innerText = 'Mark Wrong';
rightwrong_icon.classList.remove('fa-thumbs-up');
rightwrong_icon.classList.add('fa-thumbs-down');
}
retype.classList.toggle(additional_content.toggleDisabledClass, (results.reset === true));
if (results.reset) {
additional_content.close();
item_info.disable();
quiz_audio.playButtonTarget.classList.add(quiz_audio.disabledClass)
quiz_input.inputContainerTarget.removeAttribute('correct');
quiz_input.inputTarget.value = '';
quiz_input.inputChars = '';
window.dispatchEvent(new CustomEvent('didUnanswerQuestion'));
quiz_input.inputEnabled = true;
quiz_input.inputTarget.focus();
quiz_stats.completeCountTarget.innerText = session_stats.complete.toString();
quiz_stats.remainingCountTarget.innerText = session_stats.remaining.toString();
let percent_complete = Math.round(100*session_stats.complete/(session_stats.complete + session_stats.remaining));
quiz_progress.updateProgress({detail:{percentComplete:percent_complete}});
quiz_stats.percentCorrectTarget.innerText = (session_stats.answered ? Math.round(100 * session_stats.correct / session_stats.answered).toString() + '%' : '100%');
if (quiz_header.hasSrsContainerTarget) quiz_header.srsContainerTarget.hidden = true;
state = 'first_submit';
return;
}
quiz_input.inputEnabled = false;
quiz_input.inputContainerTarget.setAttribute('correct', results.passed);
}
subject_stats = JSON.parse(subject_stats_cache.get(subject.id) || JSON.stringify({
meaning:{
incorrect:0,
complete:false
},
reading:{
incorrect:0,
complete:(['Radical','KanaVocabulary'].indexOf(quiz_input.currentSubject.type) >= 0)
}
}));
if (results.passed) {
subject_stats[quiz_input.currentQuestionType].complete = true;
} else {
subject_stats[quiz_input.currentQuestionType].incorrect++;
}
if (final_submit) {
subject_stats_cache.set(subject.id, JSON.stringify(subject_stats));
}
if (session_stats.remaining == null) {
session_stats = {
complete: 0,
remaining: Number(quiz_stats.remainingCountTarget.innerText),
correct: 0,
answered: 0
}
}
let temp_session_stats = Object.assign({}, session_stats);
temp_session_stats.answered++;
if (results.passed) temp_session_stats.correct++;
if (subject_stats.meaning.complete && subject_stats.reading.complete) {
temp_session_stats.complete++;
temp_session_stats.remaining--;
}
end_of_session_delay = false;
if (final_submit) {
Object.assign(session_stats, temp_session_stats);
if (session_stats.remaining === 0) end_of_session_delay = true;
} else {
quiz_stats.completeCountTarget.innerText = temp_session_stats.complete.toString();
quiz_stats.remainingCountTarget.innerText = temp_session_stats.remaining.toString();
let percent_complete = Math.round(100*temp_session_stats.complete/(temp_session_stats.complete + temp_session_stats.remaining));
quiz_progress.updateProgress({detail:{percentComplete:percent_complete}});
quiz_stats.percentCorrectTarget.innerText = Math.round(100 * temp_session_stats.correct / temp_session_stats.answered).toString() + '%';
quiz_stats.disconnect();
let event = {detail:{
subjectWithStats:{subject:subject,stats:subject_stats},
questionType:quiz_input.currentQuestionType,
answer:quiz_input.inputTarget.value,
results:results
}};
window.dispatchEvent(new CustomEvent('didAnswerQuestion',event));
quiz_stats.connect();
if (subject_stats.meaning.complete && subject_stats.reading.complete) {
if (srs_mgr && !(settings.lightning_enabled && answer_check.passed)) {
srs_mgr.updateSRS({subject:subject,stats:subject_stats});
}
} else {
if (quiz_header.hasSrsContainerTarget) quiz_header.srsContainerTarget.hidden = true;
}
if ((results.passed && settings.autoinfo_correct && !settings.lightning_enabled) ||
(!results.passed && settings.autoinfo_incorrect) ||
(results.passed && results.multipleAnswers && settings.autoinfo_multi_meaning && !settings.lightning_enabled) ||
(results.passed && !results.accurate && settings.autoinfo_slightly_off && !settings.lightning_enabled))
{
item_info.toggleTarget.click();
if (results.passed) item_info.showException(qtype,results)
}
}
}
//------------------------------------------------------------------------
// new_submit_handler() - Intercept handler for 'submit' button. Overrides default behavior as needed.
//------------------------------------------------------------------------
function new_submit_handler(e) {
// Don't process 'submit' if we are ignoring temporarily (to prevent double-tapping past important info)
if (ignore_submit) return;
hide_exception();
let input = quiz_input.inputTarget;
qtype = quiz_input.currentQuestionType;
subject = quiz_input.currentSubject;
let submitted_immediately = false;
switch (state) {
case 'first_submit': {
debugger;
// We intercept the first 'submit' click, and simulate normal Wanikani screen behavior.
// Do WK's standard checks for shake.
let answer = quiz_input.inputTarget.value.trim();
let answerArray, partialAnswerCheck, finalAnswerCheck;
synonyms = quiz_input.quizUserSynonymsOutlet.synonymsForSubjectId(subject.id);
function updateAnswerCheck(newAnswerCheck) {
if (finalAnswerCheck === undefined) {
finalAnswerCheck = newAnswerCheck;
finalAnswerCheck.passed = true;
finalAnswerCheck.accurate = true;
finalAnswerCheck.exception = false;
}
if (newAnswerCheck.hasOwnProperty('action')) {
if (newAnswerCheck.action === 'retry') {
finalAnswerCheck.passed = false;
finalAnswerCheck.accurate = false;
finalAnswerCheck.multipleAnswers = false;
finalAnswerCheck.exception = newAnswerCheck.message.text;
} else {
finalAnswerCheck.passed = finalAnswerCheck.passed && (newAnswerCheck.action === 'pass');
if (newAnswerCheck.message === null) {
finalAnswerCheck.accurate = finalAnswerCheck.accurate && true;
finalAnswerCheck.multipleAnswers = false;
finalAnswerCheck.exception = finalAnswerCheck.exception || false;
} else if (/has multiple/.test(newAnswerCheck.message.text)) {
finalAnswerCheck.accurate = finalAnswerCheck.accurate && true;
finalAnswerCheck.multipleAnswers = true;
finalAnswerCheck.exception = finalAnswerCheck.exception || false;
} else if (/one of your synonyms/.test(newAnswerCheck.message.text)) {
finalAnswerCheck.accurate = false;
finalAnswerCheck.multipleAnswers = false;
finalAnswerCheck.exception = newAnswerCheck.message.text;
} else if (/a bit off/.test(newAnswerCheck.message.text)) {
finalAnswerCheck.accurate = false;
finalAnswerCheck.multipleAnswers = false;
finalAnswerCheck.exception = false;
}
}
}
}
if (answer.includes('.')) {
answerArray = answer.split('.');
answerArray.forEach((element) => {
partialAnswerCheck = answer_checker.evaluate({questionType:qtype, response:element, item:subject, userSynonyms:synonyms, inputChars:quiz_input.inputChars});
updateAnswerCheck(partialAnswerCheck);
});
answer_check = finalAnswerCheck;
input.value = answer;
quiz_input.inputEnabled = false;
quiz_input.lastAnswer = answerArray[0];
} else if (answer.includes('。')) {
answerArray = answer.split('。');
answerArray.forEach((element) => {
partialAnswerCheck = answer_checker.evaluate({questionType:qtype, response:response_helpers.normalizeReadingResponse(element), item:subject, userSynonyms:synonyms, inputChars:quiz_input.inputChars});
updateAnswerCheck(partialAnswerCheck);
});
answer_check = finalAnswerCheck;
input.value = answer;
quiz_input.inputEnabled = false;
quiz_input.lastAnswer = answerArray[0];
} else {
if (qtype === 'reading') {
answer = response_helpers.normalizeReadingResponse(answer);
input.value = answer;
}
if (!response_helpers.questionTypeAndResponseMatch(qtype, answer) || (answer.length === 0)) {
quiz_input.shakeForm();
quiz_input.inputEnabled = true;
quiz_input.inputTarget.focus();
return;
}
quiz_input.inputEnabled = false;
quiz_input.lastAnswer = answer;
// Do WK's standard answer evaluation.
answer_check = answer_checker.evaluate({questionType:qtype, response:answer, item:subject, userSynonyms:synonyms, inputChars:quiz_input.inputChars});
if (answer_check.hasOwnProperty('action')) {
if (answer_check.action === 'retry') {
answer_check.passed = false;
answer_check.accurate = false;
answer_check.multipleAnswers = false;
answer_check.exception = answer_check.message.text;
} else {
answer_check.passed = (answer_check.action === 'pass');
if (answer_check.message === null) {
answer_check.accurate = true;
answer_check.multipleAnswers = false;
answer_check.exception = false;
} else if (/has multiple/.test(answer_check.message.text)) {
answer_check.accurate = true;
answer_check.multipleAnswers = true;
answer_check.exception = false;
} else if (/one of your synonyms/.test(answer_check.message.text)) {
answer_check.accurate = false;
answer_check.multipleAnswers = false;
answer_check.exception = answer_check.message.text;
} else if (/a bit off/.test(answer_check.message.text)) {
answer_check.accurate = false;
answer_check.multipleAnswers = false;
answer_check.exception = false;
}
}
}
}
// Process typos according to settings.
if (answer_check.passed && !answer_check.accurate) {
switch (settings.typo_action) {
case 'warn': answer_check.exception = 'Your answer was close, but not exact'; break;
case 'wrong': answer_check.passed = false; answer_check.custom_msg = 'Your answer was not exact, as required by your settings.'; break;
}
}
// Process answer-type errors according to settings.
if (!answer_check.passed) {
if (qtype === 'meaning') {
// Although Wanikani checks for readings entered as meanings, it only
// checks the 'preferred' reading. Here, we check all readings.
if (subject.type === 'KanaVocabulary') {
accepted_readings = [subject.characters];
} else {
accepted_readings = [].concat(
subject.readings?.map((r)=>r.reading),
subject.auxiliary_readings?.filter((r)=>r.type==='whitelist').map((r)=>r.reading),
subject.onyomi,
subject.kunyomi,
subject.nanori
);
}
let answer_as_kana = to_kana(answer);
if (accepted_readings.indexOf(answer_as_kana) >= 0) {
if (settings.wrong_answer_type_action === 'warn') {
answer_check.exception = answer_check.exception || 'Oops, we want the meaning, not the reading.';
} else {
answer_check.exception = false;
}
}
} else {
accepted_meanings = [].concat(
subject.meanings,
subject.auxiliary_meanings?.filter((r)=>r.type==='whitelist').map((r)=>r.meaning),
synonyms
).filter((s) => typeof s === 'string').map((s) => s.trim().toLowerCase().replace(/\s\s+/g,' '));
let meanings_as_hiragana = accepted_meanings.map(m => to_kana(m));
let answer_as_hiragana = Array.from(answer.toLowerCase()).map(c => wanakana.toHiragana(c)).join('');
if (meanings_as_hiragana.indexOf(answer_as_hiragana) >= 0) {
if (settings.wrong_answer_type_action === 'warn') {
answer_check.exception = 'Oops, we want the reading, not the meaning.';
} else {
answer_check.exception = false;
}
}
}
}
// Process all other exceptions according to settings.
if (typeof answer_check.exception === 'string') {
if (((settings.kanji_meaning_for_vocab_action === 'wrong') && answer_check.exception.toLowerCase().includes('want the vocabulary meaning, not the kanji meaning')) ||
((settings.kanji_reading_for_vocab_action === 'wrong') && answer_check.exception.toLowerCase().includes('want the vocabulary reading, not the kanji reading')) ||
((settings.wrong_number_n_action === 'wrong') && answer_check.exception.toLowerCase().includes('forget that ん')) ||
((settings.small_kana_action === 'wrong') && answer_check.exception.toLowerCase().includes('watch out for the small')))
{
answer_check.exception = false;
answer_check.passed = false;
}
}
// Remain in 'first_submit' if there was an exceptions.
if (answer_check.exception) {
set_answer_state(answer_check);
return false;
}
state = 'second_submit';
new_answer_check = Object.assign({answer:answer}, answer_check);
first_answer_check = Object.assign({
answer:answer,
correct_answers:get_correct_answers(),
correct_answer_index: 0,
}, answer_check);
// Process "Mistake Delay" according to settings.
if ((!answer_check.passed && settings.delay_wrong) ||
(answer_check.passed &&
((!answer_check.accurate && settings.delay_slightly_off) ||
(qtype === 'reading' && answer_check.multipleAnswers && settings.delay_multi_reading))
)
)
{
set_answer_state(new_answer_check);
do_delay();
return false;
}
set_answer_state(answer_check);
// Process lightning mode according to settings.
if (settings.lightning_enabled && answer_check.passed) {
new_submit_handler(e);
return false;
}
return false;
}
case 'second_submit': {
// We intercepted the first submit, allowing the user to optionally modify their answer.
// Now, either the user has clicked submit again, or lightning is enabled and we are automatically clicking submit again.
let answer = new_answer_check.answer;
input.value = answer;
set_answer_state(new_answer_check, true /* final_submit */);
delete new_answer_check.answer;
// Nasty hack to prevent audio from playing twice or stopping upon next question.
let audio = quiz_audio.audioTarget;
audio.setAttribute('data-quiz-audio-target', 'noplay');
audio.insertAdjacentHTML('afterend', '<audio class="quiz-audio__audio dblchk" data-quiz-audio-target="audio"></audio>');
let tmp_audio = document.querySelector('audio.dblchk');
quiz_audio.disconnect();
quiz_queue.submitAnswer(answer, new_answer_check);
// Nasty audio hack, continued.
setTimeout(() => {
tmp_audio.remove();
audio.setAttribute('data-quiz-audio-target', 'audio');
quiz_audio.connect();
}, 1);
if (end_of_session_delay) {
setTimeout(next_item, 500);
} else {
next_item();
}
function next_item() {
quiz_queue.nextItem();
set_answer_state({reset:true});
quiz_header = get_controller('quiz-header');
if (quiz_header.hasSrsContainerTarget && settings.lightning_enabled && new_answer_check.passed &&
subject_stats.meaning.complete && subject_stats.reading.complete && srs_mgr) {
setTimeout(() => {
srs_mgr.updateSRS({subject:subject,stats:subject_stats});
setTimeout(()=>{
quiz_header.srsContainerTarget.hidden = true;
}, 1000 * settings.srs_msg_period);
}, 1);
}
state = 'first_submit';
}
return false;
}
default:
return false;
}
return false;
}
//------------------------------------------------------------------------
// Simulate input character by character and convert with WanaKana to kana
// -- Contributed by user @Sinyaven
//------------------------------------------------------------------------
function to_kana(text) {
return Array.from(text).reduce((total, c) => wanakana.toKana(total + c, {IMEMode: true}), "").replace(/n$/, String.fromCharCode(12435));
}
//------------------------------------------------------------------------
// Resize the buttons according to how many are visible.
//------------------------------------------------------------------------
function resize_buttons() {
let buttons = Array.from(document.querySelectorAll('#additional-content .additional-content__menu-item'));
let visible_buttons = buttons.filter((elem)=>!elem.matches('.hidden,[hidden]'));
let btn_count = visible_buttons.length;
for (let btn of visible_buttons) {
let percent = Math.floor(10000/btn_count)/100 + '%';
btn.style.width = `calc(${percent} - 10px)`;
btn.style.flex = `0 0 calc(${percent} - 10px)`;
btn.style.marginRight = '10px';
}
visible_buttons.slice(-1)[0].style.marginRight = '0px';
}
//------------------------------------------------------------------------
// External hook for @polv's script, "WaniKani Disable Default Answers"
//------------------------------------------------------------------------
gobj.set_state = function(_state) {
state = _state;
};
function get_controller(name) {
return Stimulus.getControllerForElementAndIdentifier(document.querySelector(`[data-controller~="${name}"]`),name);
}
//------------------------------------------------------------------------
// startup() - Install our intercept handlers, and add our Double-Check button and hotkey
//------------------------------------------------------------------------
async function startup() {
// Intercept the submit button handler.
let p = promise();
quiz_input = undefined;
quiz_queue = undefined;
additional_content = undefined;
item_info = undefined;
quiz_audio = undefined;
quiz_stats = undefined;
quiz_progress = undefined;
quiz_header = undefined;
answer_checker = undefined;
async function get_controllers() {
try {
// Check if all of our hooks into WK are valid, just in case something changed.
if (!quiz_input) {
quiz_input = get_controller('quiz-input');
if (!quiz_input) throw 'Controller "quiz-input" not found.';
}
if (!quiz_queue) {
quiz_queue = get_controller('quiz-queue');
if (!quiz_queue) throw 'Controller "quiz-queue" not found.';
}
if (!additional_content) {
additional_content = get_controller('additional-content');
if (!additional_content) throw 'Controller "additional-content" not found.';
}
if (!item_info) {
item_info = get_controller('item-info');
if (!item_info) throw 'Controller "item-info" not found.';
}
if (!quiz_audio) {
quiz_audio = get_controller('quiz-audio');
if (!quiz_audio) throw 'Controller "quiz-audio" not found.';
}
if (!quiz_stats) {
quiz_stats = get_controller('quiz-statistics');
if (!quiz_stats) throw 'Controller "quiz-statistics" not found.';
}
if (!quiz_progress) {
quiz_progress = get_controller('quiz-progress');
if (!quiz_progress) throw 'Controller "quiz-progress" not found.';
}
if (!quiz_header) {
quiz_header = get_controller('quiz-header');
if (!quiz_header) throw 'Controller "quiz-header" not found.';
}
if (!response_helpers) {
response_helpers = await importShim('lib/answer_checker/utils/response_helpers');
if (!response_helpers) throw 'Import "lib/answer_checker/utils/response_helpers" failed.';
}
if (!wanakana) {
wanakana = await importShim('wanakana');
if (!wanakana) throw 'Import "wanakana" failed.';
}
if (!answer_checker) answer_checker = Stimulus.controllers.find((c)=>c.answerChecker)?.answerChecker;
if (!answer_checker) {
let AnswerChecker = (await importShim('lib/answer_checker/answer_checker')).default;
if (!AnswerChecker) throw 'Import "lib/answer_checker/answer_checker" failed.';
answer_checker = new AnswerChecker;
}
if (quiz_queue.hasSubjectIdsWithSRSTarget) {
srs_mgr = quiz_queue.quizQueue.srsManager;
} else {
srs_mgr = undefined;
}
if (quiz_input.submitAnswer !== new_submit_handler) {
old_submit_handler = quiz_input.submitAnswer;
quiz_input.submitAnswer = new_submit_handler;
}
p.resolve();
} catch(err) {
console.log('Double-Check:', err, ' Retrying...');
setTimeout(get_controllers, 250);
}
return p;
}
await get_controllers();
subject_stats_cache = new Map();
session_stats = {};
state = 'first_submit';
ignore_submit = false;
// Install the Lightning Mode button.
let scripts_menu = document.getElementById('scripts-menu');
// Insert CSS
document.head.insertAdjacentHTML('beforeend',
`<style name="doublecheck">
#lightning-mode.doublecheck-active i {color:#ff0; opacity:1.0;}
</style>`
);
// Insert lightning button
scripts_menu.insertAdjacentHTML('afterend',
`<div id="lightning-mode" class="character-header__menu-navigation-link" hidden>
<a class="lightning-mode summary-button" href="#"><i class="wk-icon fa-solid fa-bolt" title="Lightning Mode - When enabled, auto-\nadvance after answering correctly."></i></a>
</div>`
);
document.querySelector('.lightning-mode').addEventListener('click', lightning_clicked);
// Install the Double-Check features.
document.querySelector('#additional-content ul').style.textAlign = 'center';
document.querySelector('#additional-content ul').insertAdjacentHTML('beforeend',
`<li id="option-toggle-rightwrong" class="additional-content__menu-item additional-content__menu-item--5">
<a title="Mark Right" class="additional-content__item ${additional_content.toggleDisabledClass}">
<div class="additional-content__item-text">Mark Right</div>
<div class="additional-content__item-icon-container">
<i class="additional-content__item-icon fa-solid fa-thumbs-up"></i>
</div>
</a>
</li>
<li id="option-retype" class="additional-content__menu-item additional-content__menu-item--5">
<a title="Retype" class="additional-content__item ${additional_content.toggleDisabledClass}">
<div class="additional-content__item-text">Re-type</div>
<div class="additional-content__item-icon-container">
<i class="additional-content__item-icon fa-solid fa-undo"></i>
</div>
</a>
</li>`
);
document.querySelector('#option-toggle-rightwrong').addEventListener('click', toggle_result.bind(null,'toggle'));
document.querySelector('#option-retype').addEventListener('click', toggle_result.bind(null,'retype'));
let input = quiz_input.inputTarget;
document.body.addEventListener('keypress', handle_rightwrong_hotkey);
function handle_rightwrong_hotkey(event){
if (state !== 'first_submit') {
if (!document.querySelector('#wkofs_doublecheck')) {
if (event.which === 43) {
toggle_result('correct');
event.preventDefault();
event.stopPropagation();
}
if (event.which === 45) {
toggle_result('incorrect');
event.preventDefault();
event.stopPropagation();
}
}
}
};
document.body.addEventListener('keydown', handle_retype_hotkey);
function handle_retype_hotkey(event){
if (state !== 'first_submit') {
if (!document.querySelector('#wkofs_doublecheck') && (event.target === input || event.target === document.body)) {
if ((event.which === 27 || event.which === 8)) {
toggle_result('retype');
event.preventDefault();
event.stopPropagation();
} else if (event.ctrlKey && event.key === 'l') {
event.preventDefault();
event.stopPropagation();
lightning_clicked();
}
}
}
};
document.head.insertAdjacentHTML('beforeend',
`<style>
#additional-content>ul>li.hidden {display:none;}
#answer-form fieldset.confburn button, #answer-form fieldset.confburn input[type=text], #answer-form fieldset.confburn input[type=text]:disabled {
background-color: #000 !important;
color: #fff;
text-shadow: 2px 2px 0 rgba(0,0,0,0.2);
transition: background-color 0.1s ease-in;
opacity: 1 !important;
}
</style>`
);
}
})(window.doublecheck);
Thanks for the heads-up!
Regarding “delay_multi_meaning”… Is it actually delaying for readings?? I don’t think I’ve ever tested that. I’ve been assuming that the answerChecker won’t set the “.multipleAnswers” field unless you’re checking a meaning. Then again, maybe that changed in the WK update a few months ago.
I like your idea for an option to enter multiple answers. A question, though:
I generally prefer to have my scripts “do one thing and do it well”. I realize Double-Check is already an exception to that since it has Mistake Delay, Lightning, and a Close-but-no-cigar built in. The reason I combined those is because I didn’t see (at the time) a good way of making those scripts separate while still being compatible with Double-Check’s main functionality. However, I’ve been thinking about making an API interface for Double-Check that would allow other scripts to inject some answer-related customizations, and I think your “multiple answers” would work well for that. If I were to add an API, would you be interested in turning your changes into a standalone script that uses the API?
Also, I had some ideas for other configurable options for “multiple answers”:
If you don’t think you’d want to work on a standalone version that interfaces to Double-Check, I understand… and maybe I’d implement your changes myself into a standalone.
Anyway, it’s probably going to be a few weeks before I do anything with it.
I have made and used Multiple Answer for a while, alongside No Cigar. However, it seems that Answer Checker can only be hacked once, so had to make a shared library.
It’s been working for years, actually. I hadn’t tested it since the recent API change, but I just went to see and it still works
.
Sure, I’d turn it into a standalone version. Just send me a notification or something once it’s ready for testing :).
I like those, I’m sure there are people who’d prefer an option that requires all readings (and maybe some especially crazy people also all meanings). The separator option would be nice; however I’m not sure if it works 100%. WaniKani rejects answers that contain non-allowed characters, so setting input.value = answer could cause issues (at least it has in the past). A workaround might be that a user inputs an individual separator that automatically gets converted to one that WK allows.
Sure, no problem, just notify me when you do
.
Hello, I followed all the instructions but it seems like the script is not working.
Seems like its not even activated on wk even though I enabled it on tamper monkey.
I assume you have installed WaniKani Open Framework and moved it to the #1 position.
Do you see a gear icon in the top-left corner of the Reviews screen? If so, open the Double-Check settings there, and enable at least one of the options to retype or change answers. If there’s no gear icon, open the Javascript console (press F12 and click on the Console tab) and post any errors you see there.
lockdown-install.js:1 Removing intrinsics.JSON.rawJSON
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.JSON.isRawJSON
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.toReversed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.toSorted
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.toSpliced
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.with
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.@@unscopables.toReversed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.@@unscopables.toSorted
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.@@unscopables.toSpliced
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayBufferPrototype%.transferToFixedLength
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayBufferPrototype%.detached
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%StringPrototype%.isWellFormed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%StringPrototype%.toWellFormed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%RegExpPrototype%.unicodeSets
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%TypedArrayPrototype%.toReversed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%TypedArrayPrototype%.toSorted
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%TypedArrayPrototype%.with
visitProperties @ lockdown-install.js:1
chat.bundle.js:1 [FrontChat][Front] SDK Version: 4.98.1
(anonymous) @ chat.bundle.js:1
lockdown-install.js:1 Removing intrinsics.JSON.rawJSON
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.JSON.isRawJSON
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.toReversed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.toSorted
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.toSpliced
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.with
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.@@unscopables.toReversed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.@@unscopables.toSorted
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.@@unscopables.toSpliced
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayBufferPrototype%.transferToFixedLength
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayBufferPrototype%.detached
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%StringPrototype%.isWellFormed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%StringPrototype%.toWellFormed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%RegExpPrototype%.unicodeSets
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%TypedArrayPrototype%.toReversed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%TypedArrayPrototype%.toSorted
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%TypedArrayPrototype%.with
visitProperties @ lockdown-install.js:1
app.bundle.js?v=b69e287:2 [FrontChat][Front] FrontChat Version: 4.98.1
test.e._isBreadcrumbTypeEnabled.console. @ app.bundle.js?v=b69e287:2
lockdown-install.js:1 Removing intrinsics.JSON.rawJSON
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.JSON.isRawJSON
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.toReversed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.toSorted
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.toSpliced
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.with
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.@@unscopables.toReversed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.@@unscopables.toSorted
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayPrototype%.@@unscopables.toSpliced
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayBufferPrototype%.transferToFixedLength
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%ArrayBufferPrototype%.detached
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%StringPrototype%.isWellFormed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%StringPrototype%.toWellFormed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%RegExpPrototype%.unicodeSets
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%TypedArrayPrototype%.toReversed
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%TypedArrayPrototype%.toSorted
visitProperties @ lockdown-install.js:1
lockdown-install.js:1 Removing intrinsics.%TypedArrayPrototype%.with
visitProperties @ lockdown-install.js:1
assets.wanikani.com/assets/manifest-21ae8157353cc224fa7432eb625d8a3bfffaba55053bd2c372990e63421e313a.json:1 Manifest: property ‘start_url’ ignored, should be same origin as document.
app.bundle.js?v=b69e287:2 WebSocket connection to ‘wss://front-us-realtime.ably.io/?access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6InY3Zl9iQS5mY1Nta3cifQ.eyJ4LWFibHktY2xpZW50SWQiOiI0OTZkYTliMDdiOTdiZjM0Mjk4YzY4YTI1MmM5NjNiMTAwN2FhZjY3NzQ3NzIxOWRmYWU5MWVhNjQ2YTBmZGFlIiwieC1hYmx5LWNhcGFiaWxpdHkiOiJ7XCJwcmVzZW5jZS1jaGF0LWNvbnZlcnNhdGlvbi0wMzUxYjU4NS0zMWI3LTQ4MTYtODQxOS0xNmYzMGJiZDg2NjZcIjpbXCJzdWJzY3JpYmVcIixcInB1Ymxpc2hcIixcInByZXNlbmNlXCJdfSIsImlhdCI6MTY4OTUzNDQxNSwiZXhwIjoxNjg5NTM4MDE1fQ.UOGc_6mJE3XV3JwcIz_ixF85oO1lY9qzoOYK79Z5pHs&format=json&heartbeats=true&v=1.2&agent=ably-js%2F1.2.29%20browser’ failed: WebSocket is closed before the connection is established.
(anonymous) @ app.bundle.js?v=b69e287:2
DevTools failed to load source map: Could not load content for chrome-extension://bmnlcjabgnpnenekpadlanbbkooimhnj/browser-polyfill.js.map: System error: net::ERR_BLOCKED_BY_CLIENT
review:1 The resource https://assets.wanikani.com/assets/es-module-shims.min-30d12edd042ff977278b3381a4ad15d5dfbdae6b39ed05776d4d9b94fb949e2c.js was preloaded using link preload but not used within a few seconds from the window’s load event. Please make sure it has an appropriate as value and it is preloaded intentionally.
review:1 The resource https://assets.wanikani.com/assets/es-module-shims.min-30d12edd042ff977278b3381a4ad15d5dfbdae6b39ed05776d4d9b94fb949e2c.js was preloaded using link preload but not used within a few seconds from the window’s load event. Please make sure it has an appropriate as value and it is preloaded intentionally.
review:1 The resource https://assets.wanikani.com/assets/es-module-shims.min-30d12edd042ff977278b3381a4ad15d5dfbdae6b39ed05776d4d9b94fb949e2c.js was preloaded using link preload but not used within a few seconds from the window’s load event. Please make sure it has an appropriate as value and it is preloaded intentionally.
I don’t recognize the references to “lockdown-install.js”. Do you have any browser extentions (probably security or privacy related) that might be interfering with TamperMonkey? Do you have any other scripts installed that are working okay?
In the Javascript console, try typing “wkof” (without the quotes) and press enter. Does it say that wkof is undefined? Or does it show some info about wkof? That will tell me if the Open Framework is even running.
wkof
{on_page_event: ƒ, include: ƒ, ready: ƒ, load_file: ƒ, load_css: ƒ, …}
I have not tried any other scripts yet, seems like the .js is from a crypto wallet that is an extension.
Hmm. Try typing ‘doublecheck’ in the Javascript console (again, without quotes) to see if the double-check script is at least loading.
Are you seeing a gear menu at all on the Reviews page?
Try this on the Javascript console (while on the Reviews page):
wkof.Menu.insert_script_link({name:'testlink',submenu:'Settings',title:'Test Link',on_click:function(){console.log('Test link clicked')}})
That should add a test link to the gear menu, and it should print a message in the Javascript console when you click the test link.