Wanikani Reorder Ultimate


Oh! Thanks a bunch!


1x1 means you review the reading and meaning together, one after the other.


I’m using Chrome, and I’ve been having a problem where the 2.2.4 reorder script freezes randomly or something and I can’t input anything. Usually it freezes on the second word, but sometimes it will freeze on the first word, especially if I simply reload the page to get it working again. Whether the answer is correct or not, whether inputting meaning or reading, WK rejects the answer and the answer does the wiggly thing without accepting the answer or moving to the next word.

I’ve checked and no other scripts other than Reorder 2 and the Open Framework should be running.


I installed the latest community update which seems to fix problems with the new wanikani update, but I wonder how I can get the script to revert back to WaniKani’s standard lesson ordering? Even if I uncheck everything in Reorder Ultimate and set it to “random”, it feels like it still randomizes everything.

I want to be able to set it back to the default setting within Reorder Ultimate when doing lessons. how do I do this? Even if I uncheck both “Sort types” and “Sort levels”, the script still sorts something. It’s not at default at all (vs. turning off the script).


Was having this problem to, finally figured out what was going on (for me)
Turns out the reviews were open in two different tabs and causing it to do exactly what you mentioned.

Edit: A lot of people are having this error. I actually doubt it’s the same thing I was having. Oh well.


So I’m trying to use WaniKani Reorder Ultimate 2 for my reviews, but all I get is a message at the bottom of the screen (not in a popup) that says “WKU::Please Wait::Currently updating UID list… 100%”

I’m using Windows, and all I have to do for this to happen is open my review queue. I can still review as normal, but I can’t reorder anything. I have already installed WaniKani Open Framework and ordered it at the top of my scripts, but that didn’t seem to do anything.

In my settings it says there’s an “uncaught TypeError” and that “property ‘substr’ of undefined” along with a lot of other code-y things I can’t understand.

It seems like a lot of other people are having a similar error, but the “currently updating” message is weird to me :thinking:


You need the version that works with Wanikani APIv2. [here]

(…which also requires the Open Framework, in case you don’t already have it installed)


Thanks! I got it to work now. :+1:


For everyone who might still have problems with the review session freezing:

I deactivated this script I had and am 100% sure it doesn’t work with Reorder Ultimate (anymore):

See the latest posts in that thread. I probably should have tried that sooner but the reorder script on its own works entirely fine.


Unfortunately I’m not sure why my script interferes with Reorder Ultimate and vice versa. I do have one guess though, and maybe @rfindley can chime in on my theory.

Both my script and Reorder Ultimate add a way to catch hide events, though Reorder Ultimate does it in a way I don’t understand. Specifically, this part:

$('div[id*="loading"]:visible').on('hide', items_ready.then(function() {
    (function ($) {$.each(['hide', 'addClass'], function (i, ev) { var el = $.fn[ev]; $.fn[ev] = function () { this.trigger(ev); return el.apply(this, arguments); }; }); })(jQuery);
    dataset = (location.pathname.match('review') ? reviewset : lessonset);
    window.Math.randomB = window.Math.random;
    if (dataset.quick === 'r') {
        window.Math.random = utilities.newRandom;

It looks like the “callback” function is going to get called all the time (when items_ready is ready), not just when div[id*="loading"]:visible is hidden. Additionally, the code to allow you catch the hide events to begin with (line two of the snippet) isn’t even called until the items_ready.then callback is executed. @rfindley Can you clear up how this code works? If items_ready.then returns a Promise, I’m not really sure what would happen when the hide event does finally get triggered.

To add one more observation. Neither of our scripts namespace the new events we’re triggering, so we will likely cause each other’s callbacks to be called twice.


@Uchihaha @SerahShirin Try this version of my script and let me know if both it and Reorder Ultimate work together.

// ==UserScript==
// @name          WaniKani Show Specific SRS Level in Reviews
// @namespace     https://www.wanikani.com
// @description   Show "Apprentice 3" instead of "Apprentice", etc.
// @author        seanblue
// @version       1.0.1
// @include       *://www.wanikani.com/review/session*
// @grant         none
// ==/UserScript==

const eventPrefix = 'show_specific_srs.';

// Catch additional events.
// http://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/
(function($) {$.each(['hide'], function(i, ev) { var el = $.fn[ev]; $.fn[ev] = function() { this.trigger(eventPrefix + ev); return el.apply(this, arguments); }; }); })(jQuery);

(function() {
	'use strict';

	const style =
		'<style>' +
			'.srs .srs-up.srs-apprentice1:after,.srs .srs-down.srs-apprentice1:after { content: \'Apprentice 1\' }' +
			'.srs .srs-up.srs-apprentice2:after,.srs .srs-down.srs-apprentice2:after { content: \'Apprentice 2\' }' +
			'.srs .srs-up.srs-apprentice3:after,.srs .srs-down.srs-apprentice3:after { content: \'Apprentice 3\' }' +
			'.srs .srs-up.srs-apprentice4:after,.srs .srs-down.srs-apprentice4:after { content: \'Apprentice 4\' }' +
			'.srs .srs-up.srs-guru1:after,.srs .srs-down.srs-guru1:after { content: \'Guru 1\' }' +
			'.srs .srs-up.srs-guru2:after,.srs .srs-down.srs-guru2:after { content: \'Guru 2\' }' +

	function addCss() {

	function updateSrsNames() {
		window.Srs.name = function(e) {
			switch (e) {
				case 1:
					return "apprentice1";
				case 2:
					return "apprentice2";
				case 3:
					return "apprentice3";
				case 4:
					return "apprentice4";
				case 5:
					return "guru1";
				case 6:
					return "guru2";
				case 7:
					return "master";
				case 8:
					return "enlighten";
				case 9:
					return "burn";

	(function() {
		$('#loading:visible').on(eventPrefix + 'hide', function() {


Unfortunately, I don’t know a lot about the internal workings of Reorder Ultimate. I only learned enough to plug in APIv2 / Open Framework. (And to embed the parts that were externally linked)


Yeah, but at least the one part I’m referred to is configured differently in your version. Specifically:

(function ($) {$.each(['hide', 'addClass'], function (i, ev) { var el = $.fn[ev]; $.fn[ev] = function () { this.trigger(ev); return el.apply(this, arguments); }; }); })(jQuery);

Was there a reason you moved that from the top of the script to be within your new callback method? And I think you probably want to execute your callback when items_ready is ready and when div[id*="loading"]:visible is hidden. It looks like right now it’s being executed simply when items_ready is ready. (I could be mistaken though; my understanding of promises and specifically the then method is shaky.)


Re: your second point, the code is waiting for both.

.then() just adds a callback that gets called after the promise is completed, or calls it immediately if the promise is already complete. But we’re not adding the callback until after the hide occurs.

If the hide occurs before items_ready, the callback is added to a not-yet-complete items_ready. So, when items_ready completes, it will call the callback.

If items_ready completes before the hide, the callback will still only be added after the hide, but in this case it sees that items_ready is already complete, so it calls the callback immediately.

In other words (pseudocode):

promise.then = function(my_callback) {
   if (promise.is_complete) {
      // Call the callback now
   } else {
      // Save the callback to call later,
      // when the promise is complete.
      promise.call_when_complete = my_callback;


And as for why I moved that code from the beginning into my callback…

I think it’s triggering events that update the gui, and those updates require info that isn’t present until items_ready is done. So I needed it to hold off until the info is loaded.

edit: To clarify… He’s adding a hook inside jQuery’s hide() and addClass() functions to send an event, so he’ll know when Wanikani (or anyone else) is calling those two functions. The problem is that those functions are being called before his script is fully set up, so it’s trying to respond to events before it’s ready to actually handle them. That may only be due to my changes, but I don’t remember.


That code is what allows hide events to be triggered, but it doesn’t really do anything until those events happen. This is partly why I’m confused about how the code works. The callback executes the code that allows you to even handle hide events… So either the callback never relies on the hide event and always gets called as soon as items_ready is done, or it does depend on the hide event and can never get called.

To achieve what you’re describing here, shouldn’t the code be this instead?
(Specifically, I moved the function that lets the code handle hide and addClass events out of the callback, and I added a function for the handler rather than referencing items_ready.then (a promise) directly.)

(function ($) {$.each(['hide', 'addClass'], function (i, ev) { var el = $.fn[ev]; $.fn[ev] = function () { this.trigger(ev); return el.apply(this, arguments); }; }); })(jQuery);

$('div[id*="loading"]:visible').on('hide', function() {
    items_ready.then(function() {
        // Remaining method contents

I believe this sample code demonstrates what I mean (tested by clicking your WK timeline for convenience):

var p = new Promise(function(resolve, reject) { resolve('Success!'); });
$('#timeline').on('click', p.then(function() { alert('hi'); }));

The alert “hi” appears as soon as I execute that code, not when I click the timeline.


Yep, you’re right. Since the .then() isn’t inside a function, it’s evaluated immediately, which adds a callback to the already-resolved promise.

So, Reorder Ultimate is working by double-bug that sort of cancels itself out… mostly.

Edit: I’m not sure if the ‘loading’ element is guaranteed to still be visible by the time the script runs. I think I’d be inclined to say, “if ‘loading’ is visible, then wait until it hides, otherwise run the following code immediately”.

Add to that your fix of wrapping the .then inside a function. And move the ‘hide’ capture back to the top, but keep the ‘addClass’ capture inside… or block the ‘addClass’ event until items_ready is complete.


Hopefully fixing that fixes these compatibility issues, but it could still be the fact that both our scripts are setting up to trigger the events so they get triggered twice. Hopefully @Uchihaha and @SerahShirin can verify that aspect of it with the modified version of my script I posted above.

Regarding your edit, I think the lesson and review pages load slowly enough that it shouldn’t be a problem. I’ve used this mechanism in multiple scripts (which I copied from multiple other scripts) and there’s never been an issue to my knowledge. I don’t know enough about the Reorder script to say how else moving the hide or addClass capture to the top could impact the script. But the old version did set up both of them at the very top.


Looking at the GreasyFork history, moving the hide/addClass thing was to fix a startup race condition:


Well, I guess just wait and see for now. If we’re lucky the fix to my script will resolve the compatibility issues and you won’t have to dig more into the timing issues.