# Wanikani Open Framework \[developer thread\]

**URL:** <https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231>\
**Category:** API And Third-Party Apps\
**Created:** [November 21, 2017, 5:07pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231 "2017-11-21T17:07:07Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![rfindley](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/rfindley/32/35202_2.png) [@rfindley](https://community.wanikani.com/u/rfindley)\
**Post date:** [November 21, 2017, 5:07pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/1 "2017-11-21T17:07:08Z")

</div>

# Wanikani Open Framework

Wanikani Open Framework (“`wkof`”) is a user-created framework for rapidly developing web browser userscripts for Wanikani.

### Features

- Simplifies interfacing to the [[Wanikani API](https://docs.api.wanikani.com/)], allowing rapid development of data-rich addons for Wanikani.
- Caches Wanikani API data to improve responsiveness.
- Integrates into the Wanikani menu, allowing userscripts to easily add links for Settings or opening a script interface.
- Provides a Settings Dialog API for creating stylish dialogs for editing your script’s settings, and functions to load and save those settings in browser storage. _(See [[Self-Study Quiz](https://community.wanikani.com/t/userscript-self-study-quiz/13191)] for examples)._
- Allows rapid data queries from the Javascript console via the global `wkof` object.

### Repository

[[Github](https://github.com/rfindley/wanikani-open-framework)]

[[Typescript types](https://github.com/Kumirei/Userscripts/blob/main/Wanikani/WKOF%20Types/wkof.d.ts)] by Kumirei

### Published scripts/sites currently using the framework

[Additional Filters](https://community.wanikani.com/t/userscript-wanikani-open-framework-additional-filters-currently-supported-recent-lessons-and-leech-training/30512) (seanblue)  
[Advanced Context Sentence](https://community.wanikani.com/t/userscript-advanced-context-sentence/35079) (abdullahalt)  
[Bulk Add Kanji User Synonyms](https://community.wanikani.com/t/userscript-wanikani-bulk-add-kanji-user-synonyms/29978) (normful)  
[Burn Manager](https://community.wanikani.com/t/userscript-burn-manager-review-resurrect-retire/13001) (rfindley)  
[ConfusionGuesser](https://community.wanikani.com/t/userscript-confusionguesser/38432) (Sinyaven)  
[Dashboard Leech Tables](https://community.wanikani.com/t/userscript-wanikani-dashboard-leech-tables-notice-your-leeches/33124) (Dani2)  
[Dashboard Progress Plus](https://community.wanikani.com/t/userscript-dashboard-progress-plus/8309) (rfindley)  
[Dashboard SRS and Leech Breakdown](https://community.wanikani.com/t/userscript-wanikani-dashboard-srs-and-leech-breakdown/32756) (seanblue)  
[Double-Check](https://community.wanikani.com/t/userscript-double-check-version-2x/31456) (rfindley)  
[Exact Review Time](https://community.wanikani.com/t/userscript-exact-review-time/32698) (Chrysus)  
[Expected Daily Reviews](https://community.wanikani.com/t/userscript-expected-daily-reviews/29206) (Kumirei)  
[Fast Abridged Wrong/Multiple Answer](https://community.wanikani.com/t/userscript-wanikani-fast-abridged-wrongmultiple-answer/19984) (DaisukeJigen)  
[Heatmap](https://community.wanikani.com/t/userscript-wanikani-heatmap/34947) (Kumirei)  
[JLPT, Joyo, Frequency filters](https://community.wanikani.com/t/userscript-open-framework-jlpt-joyo-and-frequency-filters/35096) (Kumirei)  
[Leaderboard](https://community.wanikani.com/t/userscript-wanikani-leaderboard/35084) (Dani2)  
[Leech List](https://community.wanikani.com/t/userscript-wanikani-leech-list/32783) (ukebox)  
[Lesson Cap](https://community.wanikani.com/t/userscript-wanikani-lesson-cap/21865) (valeth)  
[Lesson Examples Audio](https://community.wanikani.com/t/userscript-wanikani-lesson-examples-audio/31819) (seanblue)  
[Lesson Hover Details](https://community.wanikani.com/t/userscript-wanikani-lesson-hover-details/29312) (seanblue)  
[Levels By SRS](https://community.wanikani.com/t/userscript-levels-by-srs/37711) (Kumirei)  
[Level Duration](https://community.wanikani.com/t/userscript-level-duration-20/30668) (Kumirei)  
[More Reviews Text Replacer](https://community.wanikani.com/t/more-reviews-text-replacer/31117) (RysingDragon)  
[Notify](https://community.wanikani.com/t/userscript-wanikani-notify/14542) (DaisukeJigen)  
[Progress Percentages](https://community.wanikani.com/t/userscript-jlpt-percentages/35080) (Kumirei)  
[Radical & Kanji Mnemonic Tooltip](https://community.wanikani.com/t/userscript-radical-kanji-mnemnic-tooltip/36974) (irrelephant)  
[Reorder Ultimate 2](https://community.wanikani.com/t/wanikani-reorder-ultimate/8269/733) (xMunch, updated by rfindley)  
[Self-Study Hide Info](https://community.wanikani.com/t/userscript-self-study-hide-info/30557) (rfindley)  
[Self-Study Quiz](https://community.wanikani.com/t/userscript-self-study-quiz/13191) (rfindley)  
[SRS Distribution Charts](https://community.wanikani.com/t/userscript-srs-distribution-charts/31697) (Kumirei)  
[SRS Grid Details](https://community.wanikani.com/t/userscript-srs-grid-details/14250) (DaisukeJigen)  
[Ultimate Timeline](https://community.wanikani.com/t/userscript-wanikani-ultimate-timeline/10516) (rfindley)  
[Upcoming Lessons](https://community.wanikani.com/t/userscript-upcoming-lessons/32735) (Chrysus)  
[Visually Similar Kanji Filter](https://community.wanikani.com/t/userscript-open-framework-visually-similar-kanji-filter/35249) (Kumirei)  
[Vocab Beyond](https://community.wanikani.com/t/userscript-wanikani-vocab-beyond/33046) (normful)  
[WKStats Projections Page](https://community.wanikani.com/t/userscript-wkstats-projections-page/55260) (UInt2048)

…and more!

* * *

---

<div class="post-metadata">

**Author:** ![rfindley](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/rfindley/32/35202_2.png) [@rfindley](https://community.wanikani.com/u/rfindley)\
**Post date:** [November 21, 2017, 5:23pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/2 "2017-11-21T17:23:15Z")

</div>

A sample client script might look something like this:

```javascript
// ==UserScript==
// @name Wanikani Open Framework Sample Client
// @namespace rfindley
// @description wkof_sampleclient
// @version 1.00
// @include https://www.wanikani.com/*
// @require https://www.greasyfork.org/<URL of released framework>.js
// @copyright 2017+, Robin Findley
// @license MIT; http://opensource.org/licenses/MIT
// @run-at document-end
// @grant none
// ==/UserScript==

(function(global) {
    'use strict';

    // Modules that our script uses.
    var modules = 'apikey, item_data, user_data, settings';

    // Load the modules.
    wkof.include(modules);

    // When the settings module is ready, install our settings.
    wkof.ready('settings').then(install_settings);

    // When all modules are ready, run startup.
    wkof.ready(modules).then(startup);

    function install_settings() {
        wkof.Settings.install('My Sample Script', '/plugins/settings/my_sample_script', [
            {name:'time_fmt', label:'Time format', type:'dropdown', values:['24-hour','12-hour']},
            {name:'bkcolor', label:'Background color', type:'color_picker'},
            {name:'lessons_per_day', label:'Lessons per day', type:'integer', checker:lesson_range_checker}
        ]);
    }

    function startup() {
        // TODO: Your main script logic goes here
	}

})(window);

```

---

<div class="post-metadata">

**Author:** ![hitechbunny](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/hitechbunny/32/70848_2.png) [@hitechbunny](https://community.wanikani.com/u/hitechbunny)\
**Post date:** [November 21, 2017, 7:03pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/3 "2017-11-21T19:03:52Z")

</div>

I like this idea a lot. The API you’ve sketched out here looks solid too. This plan generates a number of ideas for me:

# 1

TamperMonkey caches the @require’d script aggressively. Which is great because there’s very little delay to the script, but bad if that script functioning means it has to fetch more script code (to work around the cache-invalidation of the script or to dynamically choose which script code to fetch). I wanted two things which I couldn’t get out of an @require’d script: keep my scripts as fast as possible AND update the @require’d script as soon as it changes.

# 2

Assuming either one of those two constraints above don’t apply (or that you’ve another approach that meets them), then a common pattern I have in my script is:

1. As soon as possible update the page based on the last-known state of the world.
2. Fetch data to update the state of the world.
3. Re-update the page based on the now-up-to-date state of the world.

# 3

I’ve implemented a smart cache of the WK API data. It’s faster to make a request to my site than to make even 3 parallel last-modified-at requests to WK API. I could quite easily put together an endpoints that served up a unified payload for subjects + assignments + review data + user + etc. Either complete data sets or deltas. Either data for all types, or for selected ones.

# 4

I’ve been thinking that centralizing state across browsers is pretty handy. E.g. for the app store I’m leaning towards reminding the user of scripts they have installed in other browsers that they don’t have installed in the one they are currently using. E.g. for leech training, I want to centralize keeping track of the state of training for each leech. I’m not sure if I’m proposing to offer some generalized store, I’m just saying that this is a need for the fancier end of the script spectrum.

# 5

Scripts authors miss a feedback mechanism. It would be great to know stats about how many installed (and more importantly _uninstalls_) a script has had. It would also be great to know what kinds of exceptions your script is throwing and in which browsers (e.g. I was missing a polyfill for older IE versions).

# 6

I can imagine it would be simple for this framework to make building a new page on top of a 404 page quite easy. There are a number of pieces to get right which I had to figure out for my app store script which are pretty much going to be the same for any similar effort.

# 7

The framework could present an API on top of the WK data. E.g. if I have an assignment in variable `a` the framework could make it so that `a.subject` or `a.subject()` returns the associated subject, etc.

* * *

I’m sure there’s more things that are interesting to consider. #1 & #2 are the most important to me as a script author. The other aspects are nice to have.

---

<div class="post-metadata">

**Author:** ![rfindley](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/rfindley/32/35202_2.png) [@rfindley](https://community.wanikani.com/u/rfindley)\
**Post date:** [November 21, 2017, 8:04pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/4 "2017-11-21T20:04:26Z")

</div>

@hitechbunny,  
Thanks for the thoughtful response! Some responses of my own:

> [@](#):
>
> ### 1a
> 
> I wanted two things which I couldn’t get out of an @require’d script:  
> a) keep my scripts as fast as possible [i.e. cache]

One option is for the bootstrap to store scripts in indexeddb or localStorage. If they are present and up to date, load them from there, otherwise let the browser decide whether to use page cache or not.

> [@](#):
>
> ### 1b
> 
> AND update the @require’d script as soon as it changes.

Do you mean rollout of script changes?

One of my personal requirements, for security purposes, is to not load code from a place where it can be changed without me knowing. With a script manager, it prompts you to update. And dynamically loaded code in the script should point to a versioned URL that can’t be updated. If the script needs to change one of its dynamically-loaded components, that should be reflected in the top-level script by changing the versioned URL of the dynamic code.

> [@](#):
>
> ### 2
> 
> 1. As soon as possible update the page based on the last-known state of the world.
> 2. Fetch data to update the state of the world.
> 3. Re-update the page based on the now-up-to-date state of the world.

I’m going to have state variables and events to let you know the status of the cache, so that should serve your purpose well.

> [@](#):
>
> ### 3
> 
> I’ve implemented a smart cache of the WK API data. It’s faster to make a request to my site than to make even 3 parallel last-modified-at requests to WK API.

Do you have a sense of whether it will it still be faster when you have thousands of users?  
As an aside, the framework could accept 3rd-party modules, too, so individual script writers can choose their data source by including the corresponding module.

> [@](#):
>
> I’ve been thinking that centralizing state across browsers is pretty handy.

Yes, absolutely. A ‘sync’ module is something I’ve thought about. You could make it as simple as “everything under `wkof.sync_data` will be sync’d”. And if individual scripts want to sync separately, that’s something they can implement.

> [@](#):
>
> ### 5
> 
> Scripts authors miss a feedback mechanism.

I agree. As long as any sort of data collection is opt-in and clearly spelled out.

> [@](#):
>
> ### 6
> 
> I can imagine it would be simple for this framework to make building a new page on top of a 404 page quite easy.

I have a prototype that creates a ready-to-use page (on top of a 404 url), including breadcrumbs and a link to return you to your previous location. This works on the forums and the main WK site.  
 ![image](https://global.discourse-cdn.com/wanikanicommunity/original/3X/c/6/c66b2ff5f1c4fca63ab4377343e98c799adad396.png)

> [@](#):
>
> ### 7
> 
> The framework could present an API on top of the WK data  
> [i.e. grab subject from an assignment via `a.subject()`]

I figure the data API will be the most-discussed module. I know people are interested in adding supplemental data to WK items, and add their own external item lists (e.g. kana-only vocab, or joyo kanji not covered by WK, etc). I’m open to any structures that retain that flexibility. 🙂

One of my biggest criteria is the ability to search for items by nearly any field, such as “kanji with same reading”, or “kanji with same radicals”, or “Level 1-10 items at Enlightened level that aren’t up for review for at least two weeks”.

---

<div class="post-metadata">

**Author:** ![Ryo](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/ryo/32/1043_2.png) [@Ryo](https://community.wanikani.com/u/Ryo)\
**Post date:** [November 21, 2017, 8:20pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/5 "2017-11-21T20:20:04Z")

</div>

I would love to join after my internship has ended, though it may be done by then.

---

<div class="post-metadata">

**Author:** ![rfindley](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/rfindley/32/35202_2.png) [@rfindley](https://community.wanikani.com/u/rfindley)\
**Post date:** [November 21, 2017, 8:20pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/6 "2017-11-21T20:20:42Z")

</div>

When does your internship end?

---

<div class="post-metadata">

**Author:** ![seanblue](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/seanblue/32/579844_2.png) [@seanblue](https://community.wanikani.com/u/seanblue)\
**Post date:** [November 21, 2017, 9:40pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/7 "2017-11-21T21:40:31Z")

</div>

> [@rfindley](#):
>
> One of my biggest criteria is the ability to search for items by nearly any field

Would it make sense to (also) allow the search criteria to be a function for more complex searches? Then you could search for all items in a list or all items where some calculated value from fields meets some criteria.

---

<div class="post-metadata">

**Author:** ![Ryo](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/ryo/32/1043_2.png) [@Ryo](https://community.wanikani.com/u/Ryo)\
**Post date:** [November 21, 2017, 10:03pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/8 "2017-11-21T22:03:16Z")

</div>

In June 2018, so it’ll be quite some time.

---

<div class="post-metadata">

**Author:** ![rfindley](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/rfindley/32/35202_2.png) [@rfindley](https://community.wanikani.com/u/rfindley)\
**Post date:** [November 21, 2017, 10:54pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/9 "2017-11-21T22:54:30Z")

</div>

> [@seanblue](#):
>
> Would it make sense to (also) allow the search criteria to be a function for more complex searches?

Indexeddb only lets you query by one field, but you’ll be able to use a cursor (database index pointer) which allows you to pull out records by additional fields. So yeah… I think that’s going to be possible. For simplicity, though, and since the record set is still actually pretty small as such things go, I may leave it to where you just put out the array by one criteria, then filter the records yourself afterward using the array.filter() function.

---

<div class="post-metadata">

**Author:** ![seanblue](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/seanblue/32/579844_2.png) [@seanblue](https://community.wanikani.com/u/seanblue)\
**Post date:** [November 21, 2017, 11:18pm UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/10 "2017-11-21T23:18:20Z")

</div>

> [@rfindley](#):
>
> I may leave it to where you just put out the array by one criteria

I guess it depends on how complicated you want the implementation to be, but I think there is a moderate benefit of taking more than one criteria.

The most flexible version I see is where the single parameter in the `search` method is an array, representing the list of queries to union together. Each entry in the array is an object where the key value pairs are the individual queries to intersect together. And one of the key options could be `custom` which takes a function (assuming that can work; otherwise having the caller do one last filter themselves may be reasonable).

So for example (pseudo code):

```auto
var searchCriteria = [
  {
    srs_level: 5,
    time_until_review_at_least: 5 days
  },
  {
    srs_level: 6,
    time_until_review_at_least: 10 days
  },
  {
    unlocked_less_than: 2 months ago
  }
]

var data = wkof.search(searchCriteria);

```

This search would get you the _distinct_ items that are either (srs 5 _and_ 5+ days until review) _or_ (srs 6 _and_ 10+ days until review) _or_ (unlocked less than two months ago).

If you take the list in the framework’s method, you can have one place that does the union (and therefore removes duplicate entries) as well as the intersection.

For what it’s worth, my motivation is being able to find all items from a leech list such that each item is a certain time until next review, but where that time is different depending on the SRS level (hence the first two conditions in my example).

---

<div class="post-metadata">

**Author:** ![rfindley](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/rfindley/32/35202_2.png) [@rfindley](https://community.wanikani.com/u/rfindley)\
**Post date:** [November 22, 2017, 12:22am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/11 "2017-11-22T00:22:41Z")

</div>

No worries, you’ll be able to do as fancy of criteria as you want. The question is whether to make the framework decide the best way to filter the data, or leave it to the script writer.

In your example, I think I would search by srs\_level between 5 and 6 (inclusive). Then, on the returned data, you’d simply run a filter:

```javascript
var data = wkof.search({
  keyname: 'srs_level',
  ge: 5, // Greater than or equal to
  le: 6 // Less than or equal to
});
// Apply the remaining criteria
data = data.filter(function(item){
  if ((item.unlocked_date > ago(2 months)) ||
      (item.srs_level===5 && item.available_date < ago(5 days)) ||
      (item.srs_level===6 && item.available_date < ago(10 days)))
    return true;
  else
    return false;
});

```

---

<div class="post-metadata">

**Author:** ![Subversity](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/subversity/32/55827_2.png) [@Subversity](https://community.wanikani.com/u/Subversity)\
**Post date:** [November 22, 2017, 12:38am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/12 "2017-11-22T00:38:35Z")

</div>

Not sure how you’ve set up your idb for use with IndexedDB, but I’ve had good experiences using [localForage](https://github.com/localForage/localForage) before.

---

<div class="post-metadata">

**Author:** ![VegasVed](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/vegasved/32/33664_2.png) [@VegasVed](https://community.wanikani.com/u/VegasVed)\
**Post date:** [November 22, 2017, 12:54am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/13 "2017-11-22T00:54:16Z")

</div>

It’s in times like these that I wish I had bothered with JS 😓.

---

<div class="post-metadata">

**Author:** ![rfindley](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/rfindley/32/35202_2.png) [@rfindley](https://community.wanikani.com/u/rfindley)\
**Post date:** [November 22, 2017, 12:57am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/14 "2017-11-22T00:57:42Z")

</div>

I looked at localForage and considered using it, but ultimately decided to make use of the indexing features of indexeddb. I’ve simplified its access significantly with a pretty small codebase. Using it looks something like this:

```javascript
var db = new Idb('wkdata',[
  radicals: 'slug,character,level',
  kanji: 'slug,reading,meaning,level'
  vocabulary: 'slug,reading,meaning,level'
]);
db.put('radicals',[
  {slug: 'stick', character: .....},
  {slug: 'leaf', character: .....}
]);
db.get('radicals', {keyname: 'level', eq: 5})
.then(function(data){
  // Do something with the data
});

```

---

<div class="post-metadata">

**Author:** ![seanblue](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/seanblue/32/579844_2.png) [@seanblue](https://community.wanikani.com/u/seanblue)\
**Post date:** [November 22, 2017, 1:26am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/15 "2017-11-22T01:26:10Z")

</div>

It’s not _quite_ the same since you can’t do an _OR_ with different criteria in the initial search. So your version only gives `unlocked_date > ago(2 months) && srs_level >= 5 && srs_level <= 6`, whereas mine would do `unlocked_date > ago(2 months)` without those other restrictions.

Of course, my original example can be achieved with your sample code by doing multiple requests to `wkof.search` for the `OR`’ed sub-queries and doing a union.

Either way, I can understand why one of your goals would be to keep the framework nice and light. 🙂

---

<div class="post-metadata">

**Author:** ![acm2010](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/acm2010/32/43822_2.png) [@acm2010](https://community.wanikani.com/u/acm2010)\
**Post date:** [November 22, 2017, 1:31am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/16 "2017-11-22T01:31:52Z")

</div>

For the settings I’m not sure what you saw, currently I’m adding buttons into the section headings, and store the settings locally. I inject the bootstrap.js button and modal styles so that it looks like on the WK content pages, however. For example @DaisukeJigen Fast Abridged script adds a settings menu to the WK menu itself.

Still, I was working on a few things that might be interesting for other scripts.

- Something called WKInteraction, it watches the page and generate events when a review was answered, new question loaded, reviews finished (leaving the page), the “more details page” in lessons and reviews loaded, etc. It can be done nicer by looking at WK’s javascript code itself, but it does the job.
- I added parts of bootstrap.js and the wanikani styles like character-grid to lessons and review pages

I’m currently working on a review timing script now and reuse some resources, I separated the styles and events by adding the userscript namespace to the identifiers, but they are loaded several times anyways. I didn’t want to bother with finding out how to make scripts dependent on each other in Tampermonkey.

The API stuff from @jeshuamorrissey also looked quite nifty, but I’m not using the API at the moment, I added my own static JSON DBs.

---

<div class="post-metadata">

**Author:** ![rfindley](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/rfindley/32/35202_2.png) [@rfindley](https://community.wanikani.com/u/rfindley)\
**Post date:** [November 22, 2017, 1:50am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/17 "2017-11-22T01:50:37Z")

</div>

Ahh, yeah, I was thinking of @DaisukeJigen’s scripts. I was looking at your Semantic/Phonetic code at the same time, so I guess I mixed them up.

The WKInteraction thing sounds interesting and useful. There are several scripts that interact on that page. Maybe having a common module could prevent some of the interference between scripts.

---

<div class="post-metadata">

**Author:** ![DaisukeJigen](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/daisukejigen/32/728834_2.png) [@DaisukeJigen](https://community.wanikani.com/u/DaisukeJigen)\
**Post date:** [November 22, 2017, 1:55am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/18 "2017-11-22T01:55:18Z")

</div>

> [@acm2010](#):
>
> For example @DaisukeJigen Fast Abridged script adds a settings menu to the WK menu itself.

I did make a ‘settings’ js file, utilizing JQuery UI, that puts settings under the WK menu. It’s an include script on greasemonkey. Does the job, but could definitely use some work. Little tidying up, add some more functionality, etc.  
But of course, the source is free for the taking.

---

<div class="post-metadata">

**Author:** ![acm2010](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/acm2010/32/43822_2.png) [@acm2010](https://community.wanikani.com/u/acm2010)\
**Post date:** [November 22, 2017, 1:55am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/19 "2017-11-22T01:55:49Z")

</div>

Yes, there are several scripts messing around with MutationObservers and listening to jStorage, and it’s hard to find out which elements to watch, and sometimes you need want a combined event … It’s better to have high level events and just worry about handling them.

---

<div class="post-metadata">

**Author:** ![rfindley](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/rfindley/32/35202_2.png) [@rfindley](https://community.wanikani.com/u/rfindley)\
**Post date:** [November 22, 2017, 2:33am UTC](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231/20 "2017-11-22T02:33:46Z")

</div>

> [@seanblue](#):
>
> It’s not quite the same since you can’t do an OR with different criteria in the initial search.

Ahh, yeah, I overlooked the fact that you weren’t limiting SRS level on the unlocked\_date.

So yes, it would require either multiple searches, or just return all records and filter. That’s one of the general limitations of the browser’s indexeddb: it accepts only one search criterion, with either a single value or an upper and/or lower bound.

[Next page](https://community.wanikani.com/t/wanikani-open-framework-developer-thread/22231.md?page=2)
