[UserScript] WaniKani Lightning Mode

it would be nice to still see the level up box.
could you make a setting for this?

Hey guys! Since I like it when the audio plays after I type a correct answer, I wanted to know if there is a way/line to enable a 1,5 seconds pause before the script moves on to the next unit.

Second this. @rfindley can it be implemented? If possible then I may try to do it, but with minimum JS experience, I really doubt that.

Since the animations that control that box are deep inside some anonymous functions, I’m not sure whether you could change it cleanly. You’d have to detect if the box is supposed to display, then attempt to abort any animations scheduled for it (if they were scheduled with jquery), and create your own delayed animation to make the box disappear after a second or two since you’d already be displaying the next item by that time.

It’s not something I’d want to tackle myself at this point. I’m trying hard not to get distracted from my current project. :sweat_smile:

1 Like

It’s a dirty hack, but you can set line 113 to this

setTimeout(()=>{$('#answer-form button').click();},1500);

You may need to adjust line 128 to match, but I didn’t think about it until after I ran out of reviews. I really don’t recommend doing this, as it makes it “delayed auto-click” instead of lightning mode. Edit: forgot this also hits all answers, not just the ones that played audio. So more like “really slow lazy mode”

Aaaaand the man himself replied when I was typing this… fml

@ccookf, it looks like you were replying to the question about audio, which I apparently missed back when it was posted. I haven’t looked at the audio at all, so I’m not sure why it doesn’t still autoplay. I’m wondering if WK runs a pause() on the audio when the second click() is sent, or whether something about timing just prevents the autoplay from happening.

So after resetting the script I noticed the audio does play, but that might be because I have the settings ticked in my profile to autoplay audio. I can’t read minified js to save my life, but I don’t think it does anything to the audio other than replace the element + sources when getting a reading correct. If there’s an audio problem this script has nothing to do with it.

WK audio element js ``` this.additionalContent.audio = e = function(t) { var n, i, r, a; if (r = $.jStorage.get("currentItem"), a = $.jStorage.get("questionType"), $("audio").remove(), r.aud && "reading" === a) return i = $("#option-audio"), n = i.find("button"), t = t, $("#answer-form fieldset").hasClass("correct") || (t = !1), n.removeAttr("disabled"), e = $("", { autoplay: t }).appendTo(i.removeClass("disabled").children("span")), $("", { src: "https://cdn.wanikani.com/audio/" + r.aud, type: "audio/mpeg" }).appendTo(e), $("", { src: "https://cdn.wanikani.com/audio/" + r.aud.replace(".mp3", ".ogg"), type: "audio/ogg" }).appendTo(e), //and so on...

Also, seriously!? Javascript comma operators wtf!?
Why can’t we use semicolons to separate expressions???
Why do we need to evaluate multiple expressions in a return statement?
This seems totally unnecessary…


As for the level up box, is this referring to the colored box that says apprentice, guru, ect. in red or green after finishing an item? If so I might look into it when I have reviews and free time. It looks like most of it (including the content o.O) is controlled with a set of .srs classes, so there might be a set of dirty workarounds here.

On a tangent here, this is definitely my favorite review script now. Super convenient and keeps me from double tapping enter on a wrong item. Kudos for making this and helping out so much with all of the scripts. After fiddling with this I've realized I don't want to write userscripts with a 10ft pole despite being a web developer. This is seriously frustrating stuff to deal with.
1 Like

I made a workaround to get the level up box to show. When WK gets a correct answer it makes a div with .srs and a child div with the appropriate sub classes (.srs-up .srs-master, ect). As soon as it loads a new item it calls a remove on elements with the .srs class so… cue dirty hack I just copied the contents into a new div using a different class name so it won’t get deleted. I know, it’s cheap and ugly, but it works. There’s a timeout to delete the element after 1 second on line 134.

So edit here… Just noticed the unicode for the srs down arrow isn’t showing up on my end. Also, the very last review will hang when moving to the summary page so both the original .srs div and the new one will show up. The latter should be an easy fix, but I have no idea why the down arrow is on the fritz for me.

dirty hack ``` // ==UserScript== // @name Wanikani Lightning Mode // @namespace wklightning // @description Eliminates second Enter or Click for correct review answers. // @include https://www.wanikani.com/review/session* // @version 1.0.2 // @author Robin Findley // @copyright 2015+, Robin Findley // @license MIT; http://opensource.org/licenses/MIT // @run-at document-end // @grant none // ==/UserScript==

//==[ History ]======================================================
// 1.0.2 - Enable lightning mode by default for new installs.
// 1.0.1 - Added option to not auto-advance if answer is slightly off.
// Added option to not auto-advance if item has multiple answers.
// 1.0.0 - Initial release.

//==[ Settings ]=====================================================
// The following script configuration variables are available. You
// can enable them by pasting the corresponding line in the javascript
// console (press F12 in most browsers to open the console), or by
// removing the “//” before the corresponding “localStorage” line
// below. The setting will be saved in storage.
// To remove a setting from storage, enter the following line in the
// javascript console, with corresponding setting name replaced:
// delete localStorage.wkdpp_setting_name;
// Halt if answer is slightly off.
// localStorage.wklightning_halt_slightly_off = 1;
// Halt if answer has multiple meanings.
// localStorage.wklightning_halt_multiple = 1;

wklightning = {};


var lightning = false;
var observer;
var ignore;

function addStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (head) {
        style = document.createElement('style');
        style.setAttribute('type', 'text/css');
        style.textContent = css;
        return style;
    return null;

// Process stored configuration settings.
function process_settings() {
    function value_or_default(value, dflt) {return (value===undefined ? dflt : value);}

    // Halt if answer is slightly off.
    gobj.halt_slightly_off = value_or_default(localStorage.wklightning_halt_slightly_off, 0);

    // Halt if answer has multiple meanings.
    gobj.halt_multiple = value_or_default(localStorage.wklightning_halt_multiple, 0);

// main() - Runs after page is done loading.
function main() {
        '#lightning-mode.active {color:#ff0; opacity:1.0;}'+
        '#answer-form fieldset.WKLM_warn button, #answer-form fieldset.WKLM_warn input[type=text], #answer-form fieldset.WKLM_warn input[type=text]:disabled {background-color:#fa2 !important;}'+
        //A lot of styles for the srs box
        '.WKLM_SRS {position:absolute; top:-2.25em; left:0%; width:100%; font-size:1.5em; text-shadow:none; letter-spacing:-0.05em}'+
        '.WKLM_SRS .srs-up, .WKLM_SRS .srs-down {display:inline-block; padding-0.15em 0.3em; font-weight:bold; -webkit-border-radius:3px; -moz-border-radius:3px; border-radius:3px; -webkit-box-sizing:border-box; -moz-box-sizing:border-box; box-sizing: border-box}'+
        '.WKLM_SRS .srs-up:before, .WKLM_SRS .srsdown:before {margin-right: 0.25em; font-family: FontAwesome}'+
        '.WKLM_SRS .srs-up.srs-burn:after, .WKLM_SRS .srs-down.srs-burn:after {content: \'Burn\'}'+
        '.WKLM_SRS .srs-up.srs-enlighten:after, .WKLM_SRS .srs-down.srs-enlighten:after {content: \'Enlighten\'}'+
        '.WKLM_SRS .srs-up.srs-master:after, .WKLM_SRS .srs-down.srs-master:after {content: \'Master\'}'+
        '.WKLM_SRS .srs-up.srs-guru:after, .WKLM_SRS .srs-down.srs-guru:after {content: \'Guru\'}'+
        '.WKLM_SRS .srs-up.srs-apprentice:after, .WKLM_SRS .srs-down.srs-apprentice:after {content: \'Apprentice\'}'+
        '.WKLM_SRS .srs-up {background-color:#af0; color: #7ab700}'+
        '.WKLM_SRS .srs-down {background-color:#f03; color: #99001f}'+
        '.WKLM_SRS .srs-up:before {content:\'\\f01b\'}'+
        '.WKLM_SRS .srs-down:before {content:\'\\f01a\'}'

    lightning = localStorage.getItem('lightning');
    lightning = (lightning !== 'false');
    $('#summary-button').append('<a id="lightning-mode" href="#"'+(lightning?' class="active"':'')+'><i class="icon-bolt"></i></a>');
    $('#lightning-mode').click(function() {
        lightning = !lightning;
        console.log('Lightning mode '+(lightning?'en':'dis')+'abled!');
        localStorage.setItem('lightning', lightning);
        $(this).toggleClass('active', lightning);
        return false;
    ignore = false;
    observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (!lightning || ignore) return;
            window.rjf = mutation;
            switch (mutation.target.className) {
                case 'correct':
                    var exception = $('#answer-exception');
                    var advance = true;
                    if (exception.length > 0) {
                        var msg = exception.text();
                        // Show the item info.
                        if (msg.match('multiple') !== null && gobj.halt_multiple) {
                            advance = false;
                        } else if (msg.match('answer was a bit off') !== null && gobj.halt_slightly_off) {
                            advance = false;
                    //Set the SRS level box
                    if ($("div.srs").length > 0) {
                        var srsBox = $("div.srs")[0];
                        var srsClasses = srsBox.children[0].className;
                        var srsTitle = srsBox.children[0].title;
                        $("#question-type").append('<div class="WKLM_SRS" id="WKLM_SRS"></div>');
                        var srsLevel = $("#WKLM_SRS").append("<div class='" + srsClasses +
                                             "' title='" + srsTitle +
                        //I have no idea why I'm not allowed to use the shortcut selectors, but they break this next line
                    // Auto-advance.
                    if (advance) {
                        $('#answer-form button').click();
                    } else {
                        $("#answer-form fieldset").addClass("WKLM_warn");
                case 'warning':
                case '':
                    $('#additional-content #option-item-info').click();

            // Ignore additional changes for 100ms
            ignore = true;
            setTimeout(function(){ignore = false;}, 100);
    observer.observe(document.querySelector('#answer-form fieldset'), {attributes: true, subtree: true, attributeFilter: ['class']});

// Run main() upon load.
if (document.readyState === 'complete')
    window.addEventListener("load", main, false);


1 Like

Great work! I’ll try to merge it later tonight, with credit to you in the script comments and the version history in this thread.

v1.0.3 - Fix to restore SRS status popup. Thanks to @ccookf for contributions!

I changed the implementation a little:

.detach() the srs box
.click() the submit button (so it moves immediately to the next question)
.setTimeout() for 100ms to allow WK’s code to do its normal process
.append() the srs box back in
.setTimeout() for 1500ms
.fadeOut() the srs box
.remove() the srs box

This saved me from having to add any extra CSS.


Wow, that is so much simpler… You’re almost making me want to adopt JQuery here… Almost :wink:

Been using this for ages and it’s great, thanks @rfindley !

Small request: I just activated both the options for not auto-advancing, but I then couldn’t tell if my answer was slightly off or if the item had multiple answers - would it possible for you to add a further option so that these automatically open the item info box, like it does for incorrect answers?

:point_right: [ v1.0.4 ] - Added option to show item info when answer is slightly off or multi-answer. :point_left:

(see comments in header of script to enable the option)

Thank you! It works like a charm! :star_struck:

:point_right: [v1.0.5] - Added support for Lessons (not just Reviews)

Since the recent update I have been having a strange bug in lessons. It seems to go away when I disable the script, so I think that’s probably the cause.

After I do a standard batch of 5 lessons, the screen comes up that asks, continue or done for now?
I will hit the enter key to continue, and the screen flashes with new lessons.
But then the same screen comes up and asks, continue or done for now?
If I hit enter again, the screen will switch to a new batch of 5 lessons and the lesson counter will tick down by 5. If I keep going as usual, that counter will eventually hit zero even though I am missing 5+ lessons.

If I go back to the dashboard and reload lessons, the missing ones will reappear but the problem continues if I do more than one batch.

Any ideas?

Have tested it more, definitely the fault of lightning mode in lessons.
@rfindley because I’m not sure if you saw this

I’ll take a look.
Sorry for the delay… I originally was on my tablet when I read this, and subsequently forgot by the time I got to my PC.

1 Like

Can confirm, also having this exact problem. If needed I can log some stuff or help check what the problem is. Also, I noticed it’s only when you use enter, if you use the right arrow to continue, it does not occur.

1 Like

@MechFactions, @neicul,

:point_right: [v1.0.6] - Fixed issue with proceeding at end of lesson group.

1 Like

For some reason this script doesn’t work for me anymore. I just updated to your 1.0.6 version to see if that would fix it, but it didn’t change anything.

I have the script activated and I also see the lightning symbol in my review session, I also tried toggling it on and off but that does nothing as well. All my other scripts still work and yours worked flawlessly yesterday when I did my reviews.

Do you have any idea what it could be?