Wanikani Multiple Answer Input (revamped)

Input multiple readings/meanings into Wanikani (revamped)

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Wanikani Multiple Answer Input (revamped)
// @namespace    buschwichtel
// @version      1.2.2
// @description  Input multiple readings/meanings into Wanikani (revamped)
// @author       Buschwichtel (original by Mempo)
// @match        https://www.wanikani.com/review/session
// @match        https://www.wanikani.com/lesson/session
// @match        https://www.wanikani.com/extra_study/session*
// @match        https://preview.wanikani.com/review/session
// @match        https://preview.wanikani.com/lesson/session
// @match        https://preview.wanikani.com/extra_study/session*
// @grant        none
// ==/UserScript==
/*jshint esversion: 6 */

const CONSOLE_PREFIX = "/// WKMAIR: ";
const SCRIPT_ID = "multiple_answer_input_rev";
const SCRIPT_NAME = "Multiple Answer Input (rev)";


(function() {
    'use strict';

    console.debug(CONSOLE_PREFIX + "INIT");

    setup();

    $('input#user-response').on('keydown',function(event){

        if (event.keyCode === 13){
            var isLesson = /^http.*:\/\/www\.wanikani\.com\/lesson/.test(location.href);
            var currentItem = $.jStorage.get(isLesson ? "l/currentQuizItem" : "currentItem");
            var questionType = $.jStorage.get(isLesson ? "l/questionType" : "questionType");

            if(!questionType){
                console.error(CONSOLE_PREFIX + "Invalid question, can't process.");
                return;
            }
            if(!currentItem){
                console.error(CONSOLE_PREFIX + "Invalid quiz item, can't process.");
                return;
            }

            // stops the event from bubbling up to the original wanikani script
            event.preventDefault();
            event.stopPropagation();

            var input = $('input#user-response')[0].value;
            var inputElements = input ? input.split(/[;]|[ ]{2,}/) : null;
            var wrongAnswers = [];
            var correctAnswers = [];

            if(inputElements){
                inputElements.forEach((element) => {
                    if(element !== "") {
                        var cleanedElement = cleanInput(element);

                        // this is wanikani's own result evaluation, so it should be identical to entering each input one at a time
                        var answer = answerChecker.evaluate(questionType, cleanedElement, currentItem);

                        console.debug(CONSOLE_PREFIX + "answerChecker evaluated: " + answer.passed + " for answer: " + cleanedElement);

                        if(answer.passed||answer.accurate){
                            correctAnswers.push(cleanedElement);
                        }
                        else {
                            wrongAnswers.push(cleanedElement);
                        }
                    }
                });

                var wrongAnswersString = "";
                var correctAnswersString = "";

                var anyWrongAnswers = wrongAnswers.length > 0;
                var anyCorrectAnswers = correctAnswers.length > 0;

                if(anyWrongAnswers) {
                    wrongAnswersString = wrongAnswers.join("; ");
                }
                if(anyCorrectAnswers) {
                    correctAnswersString = correctAnswers.join("; ");
                }

                console.info(CONSOLE_PREFIX + "Right answers given: " + (0 === correctAnswersString.length ? "none" : correctAnswersString) +
                             ", Wrong answers given: " + (0 === wrongAnswersString.length ? "none" : wrongAnswersString));

                if(wkof.settings.multiple_answer_input_rev.force_all_correct) {
                    if(anyWrongAnswers) {
                        // It doesn't matter how we send the answers, since they're wrong anyway.
                        // Note that any correct answers in the original input aren't part of the result here.
                        sendAnswer(wrongAnswersString);
                    }
                    else if(anyCorrectAnswers) {
                        // Since there were no wrong answer and at least one correct answer, all answers are correct.
                        // WaniKani only expects a single response, so we send only the first correct answer to wanikani, so it accepts it,
                        // then replace the result string with all of the answers again (since they are all correct)
                        sendAnswer(correctAnswers[0]);
                        $('input#user-response')[0].value = input;
                    }
                }
                else {
                    // If everything is wrong...
                    if(!anyCorrectAnswers) {
                        sendAnswer(wrongAnswersString);
                    }
                    // ...or everything is correct, we can just pass it off to the default
                    else if(!anyWrongAnswers && anyCorrectAnswers) {
                        sendAnswer(correctAnswers[0]);
                        $('input#user-response')[0].value = input;
                    }
                    else {
                        // put the input back into what the user actually entered
                        $('input#user-response')[0].value = input;

                        var msg = 'Not all of your answers were correct! Wrong: "' + wrongAnswersString + '"';

                        // show a message describing which answers were wrong & do the shaky thingy
                        if(!$("#answer-form form").is(":animated") && $('#user-response').val() !== '') {
                            $("#answer-form form").effect("shake", {}, 300, function() {
                                $("#additional-content").append($('<div id="answer-exception"><span>'+msg+'</span></div>').addClass("animated fadeInUp"));
                            });
                            $("#answer-form form").find("input").focus();
                        }
                    }
                }
            }
            else
            {
                // if we can't split it, just hand it off to the default script.
                // input is probably empty in this case, anyway.
                sendAnswer(input);
                console.log(CONSOLE_PREFIX + "No readable answers given.");
            }
        }
    });
})();

function setup() {
    if (!window.wkof) {
        alert(SCRIPT_NAME + ' requires Wanikani Open Framework.\nYou will now be forwarded to installation instructions.');
        window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
        return;
    }

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

    function loadSettings() {

        wkof.Menu.insert_script_link({name:SCRIPT_ID,submenu:'Settings',title:SCRIPT_NAME,on_click:openSettingsMenu});

        var defaults = {
            force_all_correct: true
        };
        wkof.Settings.load(SCRIPT_ID, defaults);
    }
}

function openSettingsMenu() {
    var dialog = new wkof.Settings({
        script_id: SCRIPT_ID,
        title: SCRIPT_NAME,
        content: {
            tabSettings: {type:'page', label:'Settings', content: {
                force_all_correct: {type:'checkbox', label:'Force all correct', default: true, hover_tip: 'When enabled, will fail you if a single answer given is incorrect.'}
            }}
        }
    });
    dialog.open();
}

function sendAnswer(answer) {
    $('input#user-response')[0].value = answer;
    $("#answer-form form button").click();
}

// Carbon copy of the first few lines of answerChecker.evaluate, sadly doesn't exist standalone :(
function cleanInput(input, questionType) {
    var normalizedResponse = normalizeResponse(input);
    if (questionType === "reading") {
        // normalizeReadingResponse DOES get exported, for some reason.
        normalizedResponse = answerChecker.normalizeReadingResponse(normalizedResponse);
    }
    return normalizedResponse;
}

// Carbon copy of answerChecker.normalizeResponse, but that doesn't get exported :(
function normalizeResponse(response) {
    return response
        .trim()
        .toLowerCase()
        .replace(/-/g, " ") // replace hyphen with space
        .replace(/\.|,|'|’|\/|:/g, ""); // replace periods, comma, colons, and apostrophe with blanks
}