Block answer if romaji accidentally typed in english box

I go through my reviews super fast, sometimes I’ll type the Japanese in the box for the English answer and get it wrong. Would it be possible to block the answer, like when you type English in the Japanese response? Would be very nice, thanks.

4 Likes

This gets trickier than the “type English in Japanese response” case since a stray Roman letter when giving a Japanese response is categorically incorrect. What you’re asking for is a bit more interpretive and would very likely never become part of the core WK functionality. There’s also the matter of being multiple ways to type/decide romaji (is Tokyo also Toukyou and Toukyo and Tokyou? すし can be typed “sushi” or “susi”. Et cetera.)

Without having checked, it wouldn’t surprise me if someone’s made a script for this. You could also just go the route of the Ignore button (don’t abuse it yadda yadda)

There’s also a couple answers where the romaji is actually an accepted answer, though it’s not like that edge case couldn’t be accounted for.

The real answer is slow down and pay attention :wink:

2 Likes

This issue could be easily fixed. There are already algorithms that convert romaji to kana. In fact, Wanikani already uses one, which is why we don’t have to type using a Japanese keyboard. All you have to do is add a function that runs that algorithm over the inputted text to see if the result matches the reading of the word and boom, the problem is solved. It’s not half as complicated as you described.

hah! I just typed in “go” for noon during my review session. Lol.

I don’t think they were arguing that it’s programmatically complicated.

If you type something like “express” in the field for 表, you get えxpれss, which clearly could never be interpreted as a correct answer in Japanese.

If you type “hyou” into the English meaning field for 表, that’s not as blatantly obvious. We have loanwords from Japanese in English, so sometimes we do genuinely use something that looks like romaji for the English word. Sometimes we don’t. If someone can’t remember whether we do or not (in some cases it might be a non-English speaker or something, who knows) then I would say it’s fine to mark that as incorrect, rather than not letting them input it. We don’t know why someone entered romaji. We can’t know for certain that they just did it out of haste, they may have actually thought the word was loaned into English.

But as was stated, there are already scripts for undoing answers, and the feedback forum’s pinned thread says not to ask about adding “undo” functionality.

4 Likes

Yep, that’s what I do in Self-Study Quiz for extra robustness in answer-checking. It eliminates the issue of multiple romaji options.

2 Likes

Yeah tbh I didn’t really think of this - was mostly just vomiting out every relevant idea that came to mind. Leebo correctly identified that it wasn’t really the crux of my point, but just a sort of added passing thought.

If you are doing your reviews on a device where you are able to install userscripts, you can use this modified version of the Double-Check script. If you enter the correct reading on a meaning review, the input box shakes and asks you to try again (the feature has to be activated in the Double-Check settings dialog box).

2 Likes

I really need to get that merged… :sweat_smile:

1 Like

That was part of the reason why I posted the hotfix for the Double-Check audio bug a week ago - I thought I might be able to sneak in my modification together with the hotfix :stuck_out_tongue:

@Sinyaven,

Do you see any case where someone might want to take a different action if it was "reading instead of meaning" versus "meaning instead of reading"?

I’m thinking of making that a single setting, "wrong answer type".

Yes, this can probably be merged to a single setting. I don’t know if someone would want it separated; but I also would not need this feature at all, so I’m not really the target audience ^^

I type fast too, and I don’t have any problems now. I complete 100 reviews around 25-30 mins. At first levels I was mistakenly type japanese into english box a lot. Now I am somewhat used to it and it occurs rarely. I think background color change helps a lot. dark for japanese light for english.

It’s now merged (with some modifications). Thanks!
It’s a single option in the settings now, and I set the default to “warn” instead of “wrong”.
I also accounted for some additional cases involving capitalization.

1 Like

Can it be that you have used an outdated version of my changes? I had already changed the wanakana.toKana(wanakana.toRomaji(input).toLowerCase()) part, but Double-Script version 2.2.7 now contains it.

I will look a bit more into it.

Also two other things: I have never seen it written “visa versa”, but only “vice versa”.
And I have to admit that I might have made the code lines a bit too long :sweat_smile: When I first tried to create variables in this place, it complained that this is not possible within a switch statement. So instead of storing results in temporary variables, I have just packed it all into a single codeline ^^ It seems that using var instead of let would have been allowed.

EDIT: I have posted version 2.2.7 with two bugfixes below:

  • answer “とうきょう” to 東京 reading question is not rejected anymore (severe).
  • answer “せっtれめんt” to 慰謝料 reading question is correctly recognized as meaning instead reading.
// ==UserScript==
// @name        Wanikani Double-Check
// @namespace   wkdoublecheck
// @description Allows retyping typo'd answers, or marking wrong when WK's typo tolerance is too lax.
// @include     https://www.wanikani.com/review/session*
// @version     2.2.7
// @author      Robin Findley
// @copyright   2017+, Robin Findley
// @license     MIT; http://opensource.org/licenses/MIT
// @run-at      document-end
// @grant       none
// ==/UserScript==

// CREDITS: This is a replacement for an original script by Wanikani user @Ethan.
// Ethan's script stopped working due to some Wanikani changes.  The code below is
// 100% my own, but it closely replicates the functionality of Ethan's original script.

// 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, additionalContent, answerChecker, lastItems, Srs, wanakana */

    var settings;

    wkof.include('Menu,Settings');
    wkof.ready('document,Menu,Settings').then(setup);

    //------------------------------------------------------------------------
    // 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});

        var defaults = {
            allow_retyping: true,
            allow_change_correct: false,
            allow_change_incorrect: false,
            typo_action: 'ignore',
            wrong_answer_type_action: 'warn',
            delay_wrong: true,
            delay_multi_meaning: false,
            delay_slightly_off: false,
            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() {
        var dialog = new wkof.Settings({
            script_id: 'doublecheck',
            title: 'Double-Check Settings',
            on_save: init_ui,
            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_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.'},
                        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.'},
                    }},
                    grpCarelessMistakes: {type:'group',label:'Careless Mistakes',content:{
                        typo_action: {type:'dropdown',label:'Action for typos',default:'ignore',content:{ignore:'Ignore',warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when answer 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 visa versa.'},
                    }},
                }},
                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_meaning: {type:'checkbox',label:'Delay when multiple meanings',default:false,hover_tip:'If the item has multiple meanings, 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.'},
                    }},
                }},
                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"!';
        }
    }

    //------------------------------------------------------------------------
    // init_ui() - Initialize the user interface.
    //------------------------------------------------------------------------
    var first_time = true;
    function init_ui() {
        settings = wkof.settings.doublecheck;

        if (first_time) {
            first_time = false;
            startup();
        }

        // Migrate 'lightning' setting from localStorage.
        var 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.
        if (settings.lightning_enabled) {
            $('#lightning-mode').addClass('active');
        } else {
            $('#lightning-mode').removeClass('active');
        }
        $('#lightning-mode').prop('hidden', !settings.show_lightning_button);

        setClass('#option-double-check', 'hidden', !(settings.allow_change_correct || settings.allow_change_incorrect));
        setClass('#option-retype', 'hidden', !settings.allow_retyping);
        resize_buttons();

        if (state === 'second_submit') {
            setClass('#option-double-check', 'disabled', !(
                (new_answer.passed && settings.allow_change_incorrect) || (!new_answer.passed && settings.allow_change_correct)
            ));
            setClass('#option-retype', 'disabled', !settings.allow_retyping);
        }
    }

    var old_submit_handler, old_answer_checker, ignore_submit = false, state = 'first_submit', play_audio, show_srs, srs_load;
    var item, itype, item_id, item_status, qtype, valid_answers, wrong_cnt, question_cnt, completed_cnt, answer, new_answer;

    //------------------------------------------------------------------------
    // lightning_clicked() - Lightning button handler.
    //------------------------------------------------------------------------
    function lightning_clicked() {
        settings.lightning_enabled = !settings.lightning_enabled;
        wkof.Settings.save('doublecheck');
        $('#lightning-mode').toggleClass('active', settings.lightning_enabled);
        return false;
    }

    //------------------------------------------------------------------------
    // toggle_result() - Toggle an answer from right->wrong or wrong->right.
    //------------------------------------------------------------------------
    function toggle_result(new_state) {
        if (new_state === 'toggle') new_state = (new_answer.passed ? 'incorrect' : 'correct');
        if (state !== 'second_submit') return false;
        switch (new_state) {
            case 'correct':
                if (!settings.allow_change_correct || new_answer.passed) return false;
                new_answer = {passed:true, accurate:true, multipleAnswers:false, exception:false};
                set_answer_state(new_answer, false /* show_msgs */);
                break;
            case 'incorrect':
                if (!settings.allow_change_incorrect || !new_answer.passed) return false;
                new_answer = {passed:false, accurate:false, multipleAnswers:false, exception:false};
                set_answer_state(new_answer, false /* show_msgs */);
                break;
            case 'retype':
                if (!settings.allow_retyping) return false;
                set_answer_state({reset:true}, false /* show_msgs */);
                break;
        }
    }

    //------------------------------------------------------------------------
    // do_delay() - Disable the submit button briefly to prevent clicking past wrong answers.
    //------------------------------------------------------------------------
    function do_delay() {
        ignore_submit = true;
        setTimeout(function() {
            ignore_submit = false;
        }, settings.delay_period*1000);
    }

    //------------------------------------------------------------------------
    // return_new_answer() - Alternate answer checker that overrides our results.
    //------------------------------------------------------------------------
    function return_new_answer() {
        return new_answer;
    }

    //------------------------------------------------------------------------
    // set_answer_state() - Update the screen to show results of answer-check.
    //------------------------------------------------------------------------
    function set_answer_state(answer, show_msgs, no_redraw) {
        // If user requested to retype answer, reset the question.
        if (answer.reset) {
            $.jStorage.set('wrongCount', wrong_cnt);
            $.jStorage.set('questionCount', question_cnt);
            $.jStorage.set('completedCount', completed_cnt);
            $.jStorage.set('currentItem', item);
            $("#answer-exception").remove();
            $('#option-double-check').addClass('disabled').find('span').attr('title','Mark Right').find('i').attr('class','icon-thumbs-up');
            $('#option-retype').addClass('disabled');
            Srs.remove();
            state = 'first_submit';
            return;
        }

        // If answer is invalid for some reason, do the shake thing.
        if (answer.exception) {
            $("#answer-exception").remove();
            if (!$("#answer-form form").is(":animated")) {
                $("#reviews").css("overflow-x", "hidden");
                var xlat = {onyomi:"on'yomi", kunyomi:"kun'yomi", nanori:"nanori"};
                var emph = xlat[item.emph];
                $("#answer-form form").effect("shake", {}, 300, function() {
                    $("#reviews").css("overflow-x", "visible");
                    if (!answer.accurate && $('#user-response').val() !== '') {
                        if (settings.wrong_answer_type_action === 'warn' && answer.wrong_answer_type) {
                            $("#answer-form form").append($('<div id="answer-exception" class="answer-exception-form"><span>WaniKani is looking for the '+(qtype === 'reading' ? 'Reading' : 'Meaning')+'.</span></div>').addClass("animated fadeInUp"));
                        } else if (!answer.bad_input && qtype === 'reading') {
                            $("#answer-form form").append($('<div id="answer-exception" class="answer-exception-form"><span>WaniKani is looking for the '+emph+" reading</span></div>").addClass("animated fadeInUp"));
                        } else if (settings.typo_action === 'warn') {
                            $("#answer-form form").append($('<div id="answer-exception" class="answer-exception-form"><span>Your answer was a bit off. Check the meaning to make sure you are correct.</span></div>').addClass("animated fadeInUp"));
                        }
                    }
                }).find("input").focus();
            }
            return;
        }

        // Draw 'correct' or 'incorrect' results, enable Double-Check button, and calculate updated statistics.
        var new_wrong_cnt = wrong_cnt, new_completed_cnt = completed_cnt;
        $("#user-response").blur();
        if (settings.allow_retyping) $('#option-retype').removeClass('disabled');
        var new_status = Object.assign({},item_status);
        var dblchk = $('#option-double-check');
        var retype = $('#option-retype');
        setClass(retype, 'disabled', !settings.allow_retyping);
        if (answer.passed) {
            if (no_redraw !== true) {
                $("#answer-form fieldset").removeClass('incorrect').addClass("correct");
                dblchk.find('span').attr('title','Mark Wrong').find('i').attr('class','icon-thumbs-down');
            }
            setClass(dblchk, 'disabled', !settings.allow_change_incorrect);
            if (qtype === 'meaning') {
                new_status.mc = (new_status.mc || 0) + 1;
            } else {
                new_status.rc = (new_status.rc || 0) + 1;
            }
        } else {
            $("#answer-form fieldset").removeClass('correct').addClass("incorrect");
            dblchk.find('span').attr('title','Mark Right').find('i').attr('class','icon-thumbs-up');
            setClass(dblchk, 'disabled', !settings.allow_change_correct);
            new_wrong_cnt++;
        }
        if ((itype === 'r' || ((new_status.rc || 0) >= 1)) && ((new_status.mc || 0) >= 1)) {
            new_completed_cnt++;
            if (show_srs) {
                if (settings.lightning_enabled) {
                    if (settings.srs_msg_period > 0) {
                        var status = Object.assign({},new_status);
                        var srs = item.srs;
                        setTimeout(Srs.load.bind(Srs, status, srs), 100);
                        setTimeout(Srs.remove, settings.srs_msg_period * 1000);
                    }
                } else {
                    Srs.load(new_status,item.srs);
                }
            }
        }
        $.jStorage.set('wrongCount', new_wrong_cnt);
        $.jStorage.set('questionCount', question_cnt + 1);
        $.jStorage.set('completedCount', new_completed_cnt);
        if (no_redraw !== true) {
            $("#user-response").prop("disabled", !0);
        }

        // We've removed the audio play from enableButtons() so we can control playback.
        // Here, we are installing the audio, but telling it not to play yet.
        additionalContent.enableButtons();
        $('audio').remove(); // Thanks to Sinyaven for the hotfix!!
        play_audio(false);

        var prependedAudio = $('audio');
        $('#option-audio').off('click');
        $('#option-audio').on('click', function() {
                if ($('#user-response').is(':disabled')) {
                prependedAudio.trigger('play');
                return false;
            }
        });

        // Now that the audio is installed, move it so WK doesn't delete it upon submitting an answer.
        $('body').prepend(prependedAudio);

        // And finally, play the audio (if autoplay is enabled).
        if (window.audioAutoplay && answer.passed) prependedAudio.trigger('play');

        lastItems.disableSessionStats();
        $("#answer-exception").remove();

        // Open item info, depending on settings.
        var showing_info = false;
        if (answer.passed && !settings.lightning_enabled &&
            (settings.autoinfo_correct ||
             (settings.autoinfo_slightly_off && !answer.accurate) ||
             (settings.autoinfo_multi_meaning && answer.multipleAnswers)
            )) {
            showing_info = true;
            $('#option-item-info').click();
        } else if (!answer.passed && !(settings.lightning_enabled && !settings.delay_wrong) && settings.autoinfo_incorrect) {
            showing_info = true;
            $('#option-item-info').click();
        }

        // When user is submitting an answer, display the on-screen message that Wanikani normally shows.
        if (show_msgs) {
            var msg;
            if (answer.passed) {
                if (!answer.accurate) {
                    msg = 'Your answer was a bit off. Check the '+qtype+' to make sure you are correct';
                } else if (answer.multipleAnswers) {
                    msg = 'Did you know this item has multiple possible '+qtype+'s?';
                }
            } else {
                msg = 'Need help? View the correct '+qtype+' and mnemonic';
            }
            if (msg) {
                if (showing_info) {
                    $("#information").prepend($('<div id="answer-exception" style="top:0;"><span>'+msg+'</span></div>').addClass("animated fadeInUp"));
                } else {
                    $("#additional-content").append($('<div id="answer-exception"><span>'+msg+'</span></div>').addClass("animated fadeInUp"));
                }
            }
        }
    }

    //------------------------------------------------------------------------
    // setClass() - Add or remove a class based on the 'enabled' state.
    //------------------------------------------------------------------------
    function setClass(elem, classname, enabled) {
        if (typeof elem === 'string') elem = $(elem);
        if (enabled) {
            elem.addClass(classname)
        } else {
            elem.removeClass(classname);
        }
    }

    //------------------------------------------------------------------------
    // new_submit_handler() - Intercept handler for 'submit' button.  Overrides default behavior as needed.
    //------------------------------------------------------------------------
    function new_submit_handler(e) {
        var fast_forward = false;

        // Don't process 'submit' if we are ignoring temporarily (to prevent double-tapping past important info)
        if (ignore_submit) {
            // If the user presses <enter> during delay period,
            // WK enables the user input field, which makes Item Info not work.
            // Let's make sure the input field is disabled.
            setTimeout(function(){
                $("#user-response").prop('disabled',!0);
            },1);
            return false;
        }

        // For more information about the state machine below,
        // see the "Theory of operation" info at the top of the script.
        switch(state) {
            case 'first_submit':
                // We intercept the first 'submit' click, and simulate normal Wanikani screen behavior.
                state = 'second_submit';

                // Capture the state of the system before submitting the answer.
                item = $.jStorage.get('currentItem');
                itype = (item.rad ? 'r' : (item.kan ? 'k' : 'v'));
                item_id = itype + item.id;
                item_status = $.jStorage.get(item_id) || {};
                qtype = $.jStorage.get('questionType');
                wrong_cnt = $.jStorage.get('wrongCount');
                question_cnt = $.jStorage.get('questionCount');
                completed_cnt = $.jStorage.get('completedCount');
                show_srs = $.jStorage.get('r/srsIndicator');

                // Ask Wanikani if the answer is right (but we don't actually submit the answer).
                answer = old_answer_checker(qtype, $("#user-response").val());

                // Update the screen to reflect the results of our checked answer.
                $("html, body").animate({scrollTop: 0}, 200);
                new_answer = Object.assign({},answer);

                var text = $('#user-response').val();
                if ((qtype === 'reading' && answerChecker.isNonKanaPresent(text)) ||
                    (qtype === 'meaning' && answerChecker.isKanaPresent(text)) ||
                    (text === '')) {
                    answer.exception = true;
                    answer.bad_input = true;
                }

                // Close but no cigar
                if (answer.passed && !answer.accurate) {
                    switch (settings.typo_action) {
                        case 'warn': answer.exception = true; break;
                        case 'wrong': answer.passed = false; break;
                    }
                }

                // Check for reading/meaning mixups
                answer.wrong_answer_type = false;
                if (!answer.passed) {
                    if (qtype === 'meaning') {
                        var accepted_readings = [].concat(item.kana, item.on, item.kun, item.nanori);
                        var answer_as_kana = wanakana.toKana($('#user-response').val());
                        if (accepted_readings.indexOf(answer_as_kana) >= 0) {
                            answer.wrong_answer_type = true;
                        }
                    } else {
                        var accepted_meanings = item.en.concat(item.syn, answerChecker.filterAuxiliaryMeanings(item.auxiliary_meanings, 'whitelist'));
                        var meanings_as_hiragana = accepted_meanings.map(m => wanakana.toHiragana(m.toLowerCase()).replace(/\s/g,''));
                        var answer_as_hiragana = Array.from($('#user-response').val().toLowerCase()).map(c => wanakana.toHiragana(c)).join('');
                        if (meanings_as_hiragana.indexOf(answer_as_hiragana) >= 0) {
                            answer.wrong_answer_type = true;
                        }
                    }
                }
                if ((settings.wrong_answer_type_action === 'warn') && answer.wrong_answer_type) answer.exception = true;

                if (answer.exception) {
                    set_answer_state(answer, true /* show_msgs */);
                    state = 'first_submit';
                    return false;
                }

                // Optionally (according to settings), temporarily ignore any additional clicks on the
                // 'submit' button to prevent the user from clicking past important info about the answer.
                if ((!answer.passed && settings.delay_wrong) ||
                    (answer.passed &&
                     ((!answer.accurate && settings.delay_slightly_off) || (answer.multipleAnswers && settings.delay_multi_meaning))
                    )
                   )
                {
                    set_answer_state(answer, true /* show_msgs */);
                    do_delay();
                    return false;
                } else {
                    set_answer_state(answer, true /* show_msgs */, settings.lightning_enabled /* no_redraw */);
                    if (settings.lightning_enabled) fast_forward = true;
                }

                if (!fast_forward) return false;
                /* no break */
                // eslint-disable-line no-fallthrough

            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.
                // Since Wanikani didn't see the first submit (because we intercepted it), now we need to simulate two submits for Wanikani:
                //   1. One for Wanikani to check the (possibly corrected) result, and
                //   2. One for Wanikani to move on to the next question.

                // Reset the screen to pre-submitted state, so Wanikani won't get confused when it tries to process the answer.
                // Wanikani code will then update the screen according to our forced answer-check result.
                $('#option-double-check').addClass('disabled').find('span').attr('title','Double-Check').find('i').attr('class','icon-thumbs-up');
                $('#option-retype').addClass('disabled');
                $('#user-response').removeAttr('disabled');
                $('#option-audio audio').remove();
                $.jStorage.set('wrongCount', wrong_cnt);
                $.jStorage.set('questionCount', question_cnt);
                $.jStorage.set('completedCount', completed_cnt);

                // Prevent WK from posting a second SRS notice.
                srs_load = Srs.load;
                Srs.load = function(){};

                // This is the first submit actually forwarded to Wanikani.
                // It will check our (possibly corrected) answer.
                var result = old_submit_handler.apply(this, arguments);

                // This is hidden third click from above, which Wanikani thinks is the second click.
                // Wanikani will move to the next question.
                state = 'first_submit';

                // We need to disable the input field, so Wanikani will see this as the second click.
                $('#user-response').attr('disabled','disabled');

                // Restore the SRS message function, which we disabled in second_submit above.
                Srs.load = srs_load;

                // This is the second submit actually forwarded to Wanikani.
                // It will move on to the next question.
                return old_submit_handler.apply(this, arguments);

            default:
                return false;
        }

        return false;
    }

    //------------------------------------------------------------------------
    // Resize the buttons according to how many are visible.
    //------------------------------------------------------------------------
    function resize_buttons() {
        var buttons = $('#additional-content ul>li');
        var btn_count = buttons.length - buttons.filter('.hidden,[hidden]').length;
        $('#additional-content ul > li').css('width',Math.floor(9900/btn_count)/100 + '%');
    }

    //------------------------------------------------------------------------
    // External hook for @polv's script, "WaniKani Disable Default Answers"
    //------------------------------------------------------------------------
    gobj.set_state = function(_state) {
        state = _state;
    };

    //------------------------------------------------------------------------
    // startup() - Install our intercept handlers, and add our Double-Check button and hotkey
    //------------------------------------------------------------------------
    function startup() {
        // Disconnect the audio player so we can control it manually.
        if (additionalContent.audio) {
            play_audio = additionalContent.audio;
            additionalContent.audio = function(){};
        }

        // Check if we can intercept the submit button handler.
        try {
            old_submit_handler = $._data( $('#answer-form button')[0], 'events').click[0].handler;
            old_answer_checker = answerChecker.evaluate;
        } catch(err) {}
        if (typeof old_submit_handler !== 'function' || typeof old_answer_checker !== 'function') {
            alert('Wanikani Double-Check script is not working.');
            return;
        }

        // Replace the handler.
        $._data( $('#answer-form button')[0], 'events').click[0].handler = new_submit_handler;

        // Install the Lightning Mode button.
        $('head').append('<style>#lightning-mode.active {color:#ff0; opacity:1.0;}</style>');
        $('#summary-button').append('<a id="lightning-mode" href="#" hidden ><i class="icon-bolt" title="Lightning Mode - When enabled, auto-\nadvance after answering correctly."></i></a>');
        $('#lightning-mode').on('click', lightning_clicked);

        // Install the Double-Check features.
        $('#additional-content ul').css('text-align','center').append(
            '<li id="option-double-check" class="disabled"><span title="Double Check"><i class="icon-thumbs-up"></i></span></li>'+
            '<li id="option-retype" class="disabled"><span title="Retype"><i class="icon-undo"></i></span></li></ul>'
        );
        $('#option-double-check').on('click', toggle_result.bind(null,'toggle'));
        $('#option-retype').on('click', toggle_result.bind(null,'retype'));
        $('body').on('keypress', function(event){
            if (event.which === 43) toggle_result('correct');
            if (event.which === 45) toggle_result('incorrect');
            return true;
        });
        $('body').on('keydown', function(event){
            if ((event.which === 27 || event.which === 8) &&
                (state !== 'first_submit') &&
                (event.target.nodeName === 'BODY') &&
                (!document.querySelector('#wkofs_doublecheck')))
            {
                toggle_result('retype');
            } else if (event.ctrlKey && event.key === 'l') {
                lightning_clicked();
                return false;
            }
            return true;
        });
        $('head').append(
            '<style>'+
            '#additional-content>ul>li.hidden {display:none;}'+
            '</style>');
        answerChecker.evaluate = return_new_answer;
    }

})(window.doublecheck);

@Sinyaven, fixed

1 Like

Sorry to bother you once more: in the current version, if I have the close but no cigar functionality enabled and answer “x” on any reading question, it will always display “Your answer was a bit off. Check the meaning to make sure you are correct.” This could be fixed by adding a check for either answer.passed or !answer.bad_input to the line } else if (settings.typo_action === 'warn') {

That’s the last bug report, I promise! :sweat_smile:

I think it’s much more likely that when a user inputs a valid romanization of the reading when asked for the meaning, it’s because they assumed WaniKani was asking for the reading. I think it’s worth rejecting accidents at the risk of rejecting what I believe to be a minuscule number of intentional incorrect answers.

I agree that it’s more likely to be a mistake than that they thought it was a loanword. It’s not as much as the other way, though.

I usually don’t like leading with this when we get threads like this, but the Feedback forum has a warning pinned to the top that says that asking for “undoing incorrect answers” isn’t worth posting about.

There’s already a solution to this made possible by userscripts / apps, and I think that’s as far as WaniKani is going to take it until they change that official position.

There’s definitely no reason to ask WK to do something like that when the obvious answer is to slow down.
WK isn’t a game or anything, why are you rushing through your reviews? You’re supposed to be learning, not running.