Is there a script that marks the fact that this is a “burn review”

So, my first burn is in 1 week and I was wondering if someone made a script, that marks the fact that you are about to burn. I know the timeline script shows that, but I wanted one that tells you directly in the review window, right before you answer (by changing the background or displaying an icon, idk) I tried searching on the forums and internet, no luck. Thank you ^ - ^

Something like this might exist (I’m not quite sure), but I think it’s a bad idea. Knowing that it’s a burn review gives you a hint: “I learned this kanji about 6 months ago!”. This means that if you learned similar kanji recently, you’ll immediately disregard them, and increase your chances of getting the burn review correct even though you don’t know it well enough to be able to differentiate it from similar looking kanji. The same can of course happen with vocab that have some kind of overlap in kanji, readings, or meanings, where it’s easy to mix up multiple words.

6 Likes

Yeah, it’s just that I don’t want to make a stupid mistake and use the override script on such an occasion

The Ultimate Timeline has the option to display a marker for reviews that have an item about to be burned. This shows only in the dashboard though. I am not aware of a script that marks burn reviews in the review page.

What @seanblue said still applies. Also knowing it is a burn review is not enough to avoid stupid mistakes. If it were you could handle every review like a burn review and never make stupid mistakes.

Perhaps what you need is Double-Check instead of Override. Double-Check has the features of Override and also gives you the option to retype an answer without revealing what the correct answer is. If you feel you made something stupid you can correct yourself. And if you can’t tell your first answer was stupid without seeing the correct answer then you don’t truly know your item and deserve a fail.

6 Likes

I always know when I have a burn coming.
Blew some easy ones with a bad keystroke.
Tsurukame app lets you give yourself credit for fat fingering.

1 Like

Edit: The original DoubleCheck script now supports these features and this fork is obsolete and may not work on the current WaniKani site. You should switch back to the original script if you have been using this one.

Here is a fork of the DoubleCheck script that supports the following extra features:

  • Option to require confirmation of burns (or only require confirmation of burns if you may have cheated) (this feature is optional)
  • Supports new shake animation messages recently added to WaniKani and lets you turn some of them off
  • Works with the new audio related changes WaniKani is implementing in the next month (and also still works with the old site)
  • fixes some issues present in the original version of DoubleCheck which caused incorrect behavior when the script tried to automatically change the outcome of an answer according to settings
// ==UserScript==
// @name        Wanikani Double-Check Fork
// @namespace   wkdoublecheck
// @description Allows retyping typo'd answers, or marking wrong when WK's typo tolerance is too lax. Also allows warning before burning.
// @include     /^https://(www|preview).wanikani.com/review/session/
// @version     2.2.21
// @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, enhanceAnswerChecker, lastItems, Srs, wanakana */

    var settings;

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

    let log = function () {};
    //log = console.log

    //------------------------------------------------------------------------
    // 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',
            kanji_reading_for_vocab_action: 'warn',
            kanji_meaning_for_vocab_action: 'warn',
            wrong_number_n_action: 'warn',
            small_kana_action: 'warn',
            delay_wrong: true,
            delay_multi_meaning: false,
            delay_slightly_off: false,
            delay_period: 1.5,
            warn_burn: 'never',
            burn_delay_period: 0,
            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 vice versa.'},
                        kanji_reading_for_vocab_action: {type:'dropdown',label:'Kanji reading entered for single character 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 entered for single character 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.'},
                        wrong_number_n_action: {type:'dropdown',label:'Wrong number of n\'s entered in certain reading questions',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:'Didn\'t enter small version of kana',default:'warn',content:{warn:'Warn/shake',wrong:'Mark wrong'},hover_tip:'Choose an action to take when you wrongly type the large version of a kana instead of the small one.'},
                    }},
                }},
                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.'},
                    }},
                }},
                tabBurnWarning: {type:'page',label:'Burn Warning',content:{
                    grpBurnWarning: {type:'group',label:'Warn Before Burning Items',content:{
                        warn_burn: {type:'dropdown',label:'Warn Before Burning',default:'ignore',content:{never:'never',cheated:'if you cheated',always:'always'},hover_tip:'Choose when to warn before burning items.'},
                        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"!';
        }
    }

    //------------------------------------------------------------------------
    // 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 === 'third_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', 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 == 'first_submit') return false;
        ignore_submit = 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(period, popup) {
        if (!period)
            period = settings.delay_period;
        ignore_submit = true;
        //if (!popup) {
            setTimeout(function() {
                ignore_submit = false;
                //$('#answer-form').css('');
            }, period*1000);
        /*} else {
            let css = '@keyframes CountDown {' +
                '0%{background: conic-gradient(green 0%, 0, grey 100%);}' +
                '100%{background: conic-gradient(green 0%, 0, grey 100%);}' +
                '}';

        }*/
    }

    //------------------------------------------------------------------------
    // return_new_answer() - Alternate answer checker that overrides our results.
    //------------------------------------------------------------------------
    function return_new_answer() {
        log('returning new answer', arguments, $.jStorage.get('currentItem'), $.jStorage.get('questionType'), 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) {
        new_answer = Object.assign({}, answer);

        if (!answer.passed && !answer.bad_input && !answer.reset && !answer.exception) {
            $.jStorage.set('confburn/' + item.id , true, {TTL:1000*3600*24*10});
        }

        // If user requested to retype answer, reset the question.
        if (answer.reset) {
            ignore_submit = false;
            $.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();
            // wkDisableAudio() is automatically triggered by something above
            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 (typeof answer.exception === "string") {
                            $("#answer-form form").append($('<div id="answer-exception" class="answer-exception-form"><span>' + answer.exception + '</span></div>').addClass("animated fadeInUp"));
                        } else 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 (answer.passed && 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(); // not sure why this is needed and it slows things down
        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').removeClass('correct').removeClass('confburn')
                if (!answer.confburn)
                    $("#answer-form fieldset").addClass("correct");
                else
                    $("#answer-form fieldset").addClass("confburn");
                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').removeClass('confburn').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);

        setTimeout(function () {
            if (state !== 'first_submit')
                $("#user-response").prop("disabled", !0);
            // pressing enter key causes #user-response to be re-enabled after this runs
            // so we wrap in setTimeout, but that might cause disabling input after moving
            // to next question, so we check that we are not in state first_submit at the
            // time timeout runs
        }, 1);

        // audio playback
        if (!answer.confburn) {
            // We remove the audio play from enableButtons() so we can control playback.
            // Here, we are installing the audio, but telling it not to play yet.
            $('audio').remove(); // Thanks to Sinyaven for the hotfix!!
            $('#option-audio').off('click');
            let oldAutoplay = window.audioAutoplay;
            window.audioAutoplay = false;
            additionalContent.enableButtons();
            window.audioAutoplay = oldAutoplay;

            var prependedAudio = $('audio');
            if (prependedAudio.length > 0) {
                $('#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 necessary
            if (prependedAudio.length > 0 && window.audioAutoplay && answer.passed) {
                prependedAudio.trigger('play');
            } else if (window.wkEnableAudio) {
                window.wkEnableAudio(window.audioAutoplay);
            }
        }

        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.custom_msg) {
                msg = answer.custom_msg;
            } else 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) {
        console.time();
        // 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).
                //var urv = $("#user-response").val();
                //answer = old_answer_checker(qtype, urv);
                var tmpAns = $("#user-response").val();
                answer = old_answer_checker(qtype, tmpAns);
                log('initial answer', Object.assign({}, answer), item, qtype, tmpAns );

                //if (typeof answer.exception == 'string')
                //   log('answer checker',qtype,urv,answer);

                // 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 = answer.exception || true;
                    answer.bad_input = true;
                }

                // Close but no cigar
                if (answer.passed && !answer.accurate) {
                    switch (settings.typo_action) {
                        case 'warn': answer.exception = answer.exception || true; break;
                        case 'wrong': answer.exception = false; 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 = to_kana($('#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 => to_kana(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 (answer.wrong_answer_type) {
                    if (settings.wrong_answer_type_action === 'warn') {
                        answer.exception = answer.exception || true;
                    } else {
                        answer.exception = false;
                        answer.passed = false;
                    }
                }

                // kanji reading used for single word vocab mixup
                if (answer.exception && typeof answer.exception === 'string')
                    if(answer.exception.toLowerCase().includes('want the vocabulary meaning, not the kanji meaning') && settings.kanji_meaning_for_vocab_action === 'wrong'
                        || answer.exception.toLowerCase().includes('want the vocabulary reading, not the kanji reading') && settings.kanji_reading_for_vocab_action === 'wrong'
                        || answer.exception.toLowerCase().includes('forget that ん') && settings.wrong_number_n_action === 'wrong'
                       || answer.exception.toLowerCase().includes('watch out for the small') && settings.small_kana_action === 'wrong'
                      ) {
                        answer.exception = false;
                        answer.passed = false;
                    }

                log('answer state set', Object.assign({}, answer));
                set_answer_state(answer, true /* show_msgs */, false /* no_redraw */);

                if (answer.exception) {
                    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))
                    )
                   )
                {
                    log('doing delay');
                    do_delay();
                    return false;
                } else if (!settings.lightning_enabled) {
                    return false;
                }

                /* no break */
                // eslint-disable-line no-fallthrough

            case 'second_submit':
                state = 'third_submit';
                var will_burn = new_answer.passed && !(item_status.mi || item_status.ri || item_status.ni) && (item.rad || item_status.mc || item_status.rc) && item.srs == 8;
                var cheated = $.jStorage.get('confburn/' + item.id) ? true : false;
                if (will_burn && (settings.warn_burn === 'always' || settings.warn_burn === 'cheated' && cheated)) {
                    // prompt before burnning and delay ~4s before allowing proceeding from prompt
                    do_delay(settings.burn_delay_period, true /* popup */);
                    new_answer.confburn = true;
                    if (cheated)
                        new_answer.custom_msg = ' You may have cheated on this item. It will be burned if you continue to the next item.';
                    else
                        new_answer.custom_msg = 'This item will be burned if you continue to the next item.'
                    set_answer_state(new_answer, true /* show_msgs */, false /* no_redraw */);
                    break;
                }
            case 'third_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(){};

                // disable starting audio playback since we already have started playing audio if necessary
                var old_additionalContent_audio = window.additionalContent.audio;
                window.additionalContent.audio = function() {};
                var old_wkEnableAudio = window.wkEnableAudio;
                window.wkEnableAudio = function() {};

                log('doing final submit phase 1', answer, new_answer);


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

                // re-enable starting audio playback
                window.wkEnableAudio = old_wkEnableAudio;
                window.additionalContent.audio = old_additionalContent_audio;

                log('doing final submit phase 2', answer, new_answer);

                // This is the second submit actually forwarded to Wanikani.
                // It will move on to the next question.
                var res = old_submit_handler.apply(this, arguments);
                log('done');
                console.timeEnd();
                return res;

            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() {
        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() {
        // 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 = enhanceAnswerChecker({evaluate: answerChecker.evaluate}).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;

        // Clear warning popups if question changes due to reasons outside of this script
        $.jStorage.listenKeyChange("currentItem", function(key, action){
            $("#answer-exception").remove();
        });

        // 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;}'+
            '#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>');
        answerChecker.evaluate = return_new_answer;
        window.enhanceAnswerChecker = function(x) { return x; };
    }

})(window.doublecheck);

Also, if you use other “cheating” scripts besides double check, you can have them notify the doublecheck script of that by adding the line

$.jStorage.set('confburn/' + item.id , true, {TTL:1000*3600*24*10});

in an appropriate place in the other script. This tells the doublecheck script that you “cheated” on this round of reviews for the item and to prompt before burning if applicable.

2 Likes

I thought this script is buggy and not actively maintained. Was I wrong?

I never had any strange experiences with it

2 Likes

The script has been updated to also work on the preview site and work with the new shake animation messages.