Wanikani Open Framework [developer thread]

Yeah, that was an unfortunate configuration decision by Firefox. IndexedDB is the only non-server-side storage choice for web applications that need a decent amount of storage.

There’s a workaround, though your data will have to load from scratch every time, and it won’t store configuration for userscripts. At the top of the Open Framework script:

	var ignore_missing_indexeddb = false;

Set that to true.

1 Like

Another new script to add to your growing list! :smile:

1 Like

Very nice use of the Settings dialogs!

edit: It’s up to you, of course, but I’d recommend setting up your menu link as “User → Settings → Vocab Beyond” rather than “User → Vocab Beyond → Settings”. The logic behind that is, when users have a lot of scripts, the menu can get crowded. Grouping things into common top-level menus like “Settings” and “Open” helps keep the menu size down.

Thanks for the feedback. Fixed!

Hey rfindley

I’m completely ignorant of how this scripting system works for all these WK mods.
I’ve been trying to mess around with the code to get this one working again:
https://community.wanikani.com/t/userscript-wanikani-item-annotator-super-duper-explosions-of-colortastic-flavors/3904/88
And I just can’t figure it out. I don’t know enough about the system, but it seems pretty basic.
Any chance of doing a version of this that works with your framework?

If you don’t have time, would you be able to direct me to a site that gives a primer on how this coding works when you need to use the API so I could just try to fix that specific script?

Cheers!

Edit: Mempo took care of it. : )

@rfindley

New script! Thank you again for WKOF, it made things so much easier.
Also, can you add the github page in the OP?

I’ve redone the top-post, added the gihub link, and also turned it into a wiki.

edit: And renamed the thread.

1 Like

Not sure if this is an issue, but when you use path overrides for settings the elements in the settings dialogue still have IDs. I’m using identical layouts for two tabs of settings, and it makes sense for me to name the settings objects the same way when I’m using path overrides already. As such I get identical IDs on the settings in the two tabs.

Not an issue for me, I just noticed that there were two elements with the same ID.

edit:

Also noticed in Settings.js that you used the Open the settings dialog. comment twice where it does not belong.


//------------------------------
	// Open the settings dialog.
	//------------------------------
	function save_settings(context) {
		var script_id = (typeof context === 'string' ? context : context.cfg.script_id);
		var settings = wkof.settings[script_id];
		if (!settings) return Promise.resolve();
		return wkof.file_cache.save('wkof.settings.'+script_id, settings);
	}

	//------------------------------
	// Open the settings dialog.
	//------------------------------
	function load_settings(context, defaults) {
		var script_id = (typeof context === 'string' ? context : context.cfg.script_id);
		return wkof.file_cache.load('wkof.settings.'+script_id)
		.then(finish, finish.bind(null,{}));

		function finish(settings) {
			if (defaults)
				wkof.settings[script_id] = $.extend(true, {}, defaults, settings);
			else
				wkof.settings[script_id] = settings;
			return wkof.settings[script_id];
		}
	}

edit2: Would it make sense for a button in the settings dialogue to send the dialogue object to the callback function? I want to change some settings once a button is pressed, then refresh, but for that I need the dialog object. I’ll find a way around it, but it would be neater to just get the dialog in the callback.


edit3: @rfindley I saw that you were here. I am having trouble getting dialog.refresh() to work. I can get the dialog just fine, but whatever I do it doesn’t change anything. I am changing the data in wkof.settings.wanikani_heatmap then calling the refresh method, but nothing changes. Any idea of what I’m doing wrong?

The settings don’t need to be saved before the user presses the save button, so if there’s some other way to make sure it saves when they do, then I can just change the dialog directly.


edit4: Ah. I see that you can’t have settings with the same names anyway because then the defaults won’t load. Guess I’ll prepend “lesson_” and “review_” to the setting names again.

@Kumirei,

I’ll respond piecewise as I get time to investigate.

re: dialog object
Currently, inside the callbacks, ‘this’ points to the config object that you passed to ‘new wkof.Settings(config)’.

If no one is using ‘this’ in their callbacks (they probably aren’t), I can change it to be the dialog object itself, which makes more sense anyway.

In the meantime, here’s a solution that will work before and after such a change:

var config = {
    on_save: save_settings,
    ...
};
var dialog = new wkof.Settings(config);
config.dialog = dialog; // Store a reference to the dialog in the config.

function save_settings(new_settings) {
    // 'this' could be either the dialog itselt or the dialog's config.
    // If it's the config, extract the dialog reference that we stored in the config.
    var dialog = this;
    if (!(this instanceof wkof.Settings)) dialog = this.dialog;
}
1 Like

I tried replicating your issue with Dashboard Progress Plus.
When I open the dialog, I save reference to the dialog so I can play with it on the console:

(inside the script…)

        var settings_dialog = new wkof.Settings(config);
        window.dpp_dialog = settings_dialog;

Here’s the dialog before:
image

Next, on the console, I confirm the value of the “Show Item Name” checkbox:
image

Then, I change the value and call refresh():
image

image

1 Like

I emptied the settings for the script to get rid of old settings and had everything redefined and now it works fine. I don’t know if I changed anything else since I last tried, but at least it’s working. Thank you!

1 Like

I’ll play with this Friday night (U.S. Eastern time)

There’s no hurry, it’s working fine as it is. Just tell me if you change something, because I am using the IDs.

For my button I’m getting a MouseEvent from this in the callback function

What kind of button are you talking about? The Save button? Or a config item of type ‘button’?

Either way, looking at the wkof code, it looks like ‘this’ should currently be the config :thinking:

A config item of type ‘button’ with an ‘on_click’ callback function.

image

what I get

Ahh, I see now. I was misreading my code:

if (typeof item.on_click === 'function')
	item.on_click.call(e, name, item, setting_changed.bind(context, context, e));
                      ^^^                                  ^^^^^^^

I was looking at the .bind() instead of the .call()

Hmm… trying to think of the “right” way to do this.
In normal javascript, the Event is the first parameter passed to an event handler, but I’m already not following that convention.

I can do a survey of scripts to see if anyone else has started using the ‘button’ type yet. If not, we are free to change it however we want. If someone is using it already, we’ll have to figure the best alternative. Maybe add the dialog object as the last parameter, though that doesn’t ‘feel’ like the right solution.

When I get some time tomorrow night, I’ll search the list of published scripts in the top-post for wkof.Settings, and see if anyone’s configs are using the ‘button’ type.

1 Like

FYI, althought I haven’t document them yet, wkof has several debugging features to help script developers. I’ll add this to the README when I get time, but for now, here’s a quick summary:

Testing your script with another user’s API key

If you want to test a script with another user’s data, run the following line in the Javascript console with that user’s APIv2 key inserted:

localStorage["apiv2_key_override"] = 'insert_key_here';

When you are done:

localStorage["apiv2_key_override"]

Add files to a do-not-cache list

If your userscript loads external resources via one of the following:

  • wkof.load_css()
  • wkof.load_script()
  • wkof.load_file()

and you want to temporarily disable caching of that resource, you can add the specific URL to a do-not-cache list:

localStorage['wkof.load_file.nocache'] = ['url1', 'url2', 'url3'];

And, of course, you can remove files from the list by editing the array.

3 Likes

Is there a way to store data in an item’s cache in such a way that if you request an item through ItemData that data is also included? I’m working on a script that uses JLPT levels for kanji and had the idea that maybe I could make a script that adds JLPT data to WKOF in case anyone else wanted to use that info as well.


edit after 7h:

dialog.refresh() isn’t calling any on_change callbacks. That’s a bug, right?

I think it was intentional, because dialog.refresh() is likely to be called from an on_change(), which could result in an infinite callback look.

You could argue, though, that if the programmer is trying to programmatically modify the same field that triggered the .on_change() in the first place, it’s their own fault if they end up with an infinite loop.

I’ll revisit the Settings.js code to see if I can find a good way to trigger the .on_change(). Maybe I’ll add a boolean argument:

dialog.refresh(true /* trigger_change_events */)
1 Like