[For Userscript Authors] WK Item Info Injector

Table of Contents

What Is It?

WK Item Info Injector is a user-created library script that can be used by other userscripts. It simplifies the addition/modification of item info in the user interface of WaniKani.

How Can I Use It?

WK Item Info Injector can be required in your userscript:

// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1416982

Alternatively, the end user can be instructed to install WK Item Info Injector manually ā€“ similar to WK Open Framework. The @require method is more comfortable for the end user, but comes with a few disadvantages:

  • The @require meta data has to be kept up-to-date manually (I donā€™t plan to do many updates for WK Item Info Injector, but they might be necessary every time WK changes its interface, which recently happens pretty often due to the transition to React).
  • The entire code of WK Item Info Injector is inserted into every script that @requires it, which can increase the parsing time by a few milliseconds.

If WK Item Info Injector is available (by @require or by manually installing it), you can use it via the wkItemInfo object in the global scope. For example:

window.wkItemInfo.append("Item ID", o => o.id);

inserts a section showing the item ID into the item info in lessons, the lesson quiz, reviews, extra studies, and the item page:

Documentation

Selectors

First, here is an example call that shows (almost) all available options in the selector chain:

wkItemInfo.on("lesson,lessonQuiz,review,extraStudy,itemPage").forType("radical,kanji,vocabulary,kanaVocabulary").under("composition,meaning,reading,examples").spoiling("composition,meaning,reading,examples").append("Info Heading", "Info Body");

Everything before append() is called the selector chain. The selector chain determines when your item info should be inserted. The order of the chain links (selectors) cannot be changed, but selectors that should just use the default arguments can be omitted. The example from above uses the default arguments everywhere, so omitting all selectors has the same result:

wkItemInfo.append("Info Heading", "Info Body");

If you want to inject only during the lesson quiz and the reviews, and only if the current item is a kanji, then you would write

wkItemInfo.on("lessonQuiz,review").forType("kanji").append("Info Heading", "Info Body");

The following table gives an overview of the four available selectors. Except in the case of the spoiling() selector, omitting the arguments or omitting the whole selector both default to all accepted values.

Selector Accepted Values Description Default
on() lesson
lessonQuiz
review
extraStudy
itemPage
Selects the pages on which your info should be injected. All five page types
forType() radical
kanji
vocabulary
kanaVocabulary
Selects the item types for which your info should be injected. All four item types
under() composition
meaning
reading
examples
Selects which section your injected info belongs to. By default, this also determines the location where exactly your section will appear. All four sections
spoiling() composition
meaning
reading
examples
   or
nothing
Defines the sections which your info spoils. spoiling("nothing") means that your info spoils no other item info. This selector can usually be omitted. Omit arguments: same as spoiling("nothing")
Omit selector: same sections as specified in under()

While the information in the table should be enough for basic usage, there are certain intricacies in the behavior of the under() selector in combination with the spoiling() selector that might be good to know for more control over the injection location and time. Therefore, here is a longer explanation for under() and spoiling():

under()

The behavior in lessons is the most obvious ā€“ during lessons, each itemā€™s info is split up into two or four tabs. The keywords map to the tabs of each item type in the following way:

composition meaning reading examples
Radical Name Examples
Kanji Radicals Meaning Readings Examples
Vocabulary Kanji Composition Meaning Reading Context
KanaVocabulary Meaning Context

For lessons, the under() selector specifies the tabs (sections) in which your info should be injected. During the lesson quiz, reviews, extra studies, and on the item page, multiple sections might be displayed at the same time. However, the selector still only matches once, so that your injected info is not duplicated.

Example: The selector is under("meaning,reading") and you visit a kanji item page: The page contains both meaning and reading sections, but the selector still only matches once.

During the lesson quiz, reviews, and extra studies, WK reveals the item info in two steps (for kanji and vocabulary).

For example, after a vocabulary meaning question, the item info first (step 1) only shows the sections Related Kanji [composition] and Meaning Explanation/Meaning Note [meaning]. Only after expanding (step 2), the sections Reading Explanation/Reading Notes [reading] and Context Sentence [examples] are also shown.

The under() selector matches in step 1 if any of the specified sections might appear on screen in step 1 or step 2. However, the spoiling() selector can delay the match until step 2.

With the vocabulary meaning question example from before, under("meaning,reading"), under("meaning"), and under("reading") would all match in step 1, but not in step 2 (no duplication).
Important: If the spoiling() selector is omitted and defaults to the same keywords as in under(), the matches for under("meaning,reading") and under("reading") will be delayed until step 2. Read about spoiling() for more details.

By default, the injected section will be placed in relation to the last applying keyword.

Example: For a radicalā€™s item page with the selector under("meaning,reading"), the last keyword does not apply, but the next-to-last does, so the info will be injected in relation to the meaning section.

spoiling()

Usually this can just be omitted, but here is a more detailed explanation if you want more control:

The spoiling() selector only affects the behavior during reviews, extra studies, and the lesson quiz. In these situations, the default WK interface initially hides some sections of the item info because they would spoil the answer to the opposite question type (at least thatā€™s my interpretation of the behavior ā€“ it might also be set up this way just to focus on the more relevant info first).

Example: After answering a vocabulary meaning question, you open the item info. It only shows the composition and the meaning sections ā€“ reading and examples are hidden, because they contain spoilers for the reading question.

Only after clicking Show All Information, the other sections become visible.

The spoiling() selector is able to delay the match until all sections are shown. As long as any of the sections specified in this selector are still hidden, the match will be delayed.

Continuing the example from before: You use the selector under("meaning,reading") and omit the spoiling() selector, which defaults to spoiling("meaning,reading"). Because reading is still hidden, the spoiling() selector prevents a match ā€“ the injection only happens after clicking Show All Information.

Actions

The selector chain determines when something should happen ā€“ the ā€œactionā€ that is appended to the end of the chain determines what should happen. The available actions are:

Action Description
append() Append the info below the section targeted by under().
appendSubsection() Insert the info as a subsection into the section targeted by under().
appendSideInfo() Only available for meaning or reading. In lessons, reviews, and extra studies, the side info is in a separate column at the left. The info will be appended under the targeted side info.
appendAtTop() Places the info above all the native WK info sections.
appendAtBottom() Places the info below all the native WK info sections.
appendSideInfoAtTop() Only available for meaning or reading. In lessons, reviews, and extra studies, the info is placed at the top of the left column. On the item page, itā€™s basically the same as appendAtTop().
appendSideInfoAtBottom() Only available for meaning or reading. In lessons, reviews, and extra studies, the info is placed at the bottom of the left column. On the item page, itā€™s basically the same as appendAtBottom().
notify() No injection, just calls the passed callback function.
notifyWhenVisible() Same as notify(), but the callback is only called once the section targeted by under() is visible. Use this if you want to modify native WK information.

All the append actions take two arguments: the info heading and the info body. They can be strings, or DOM elements, or arrays of strings and/or DOM elements. Alternatively, they can also be callback functions returning any of those types, or returning a promise resolving to any of those types.

The callback functions receive as argument the current state, which is an object containing the following properties:

Name Description Example Value
on The current page. Corresponds to selector on() "itemPage"
type The current itemā€™s type. Corresponds to selector forType() "kanji"
under The currently shown sections. Corresponds to selector under() ["meaning"]
hiddenSpoiler The sections currently hidden but (possibly) appearing later. ["reading", "examples"]
id The WaniKani item ID of the current item. 274
meaning The primary and alternative meanings of the current item. ["Narwhal"]
characters The characters of the current item. N/A for image radicals. "金ēŽ‰"
reading Accepted readings for the current item. N/A for radicals and kana vocabulary. ["恓恆", "恏"]
composition The WK item components of the current item. N/A for radicals and kana vocabulary. [{characters: "口",
meaning: ["Mouth"]}]
partOfSpeech The word types of the current vocabulary item. ["Noun"]
onyomi The onyomi readings of the current kanji item. ["恫悓", "恘悓"]
kunyomi The kunyomi readings of the current kanji item. ["ć²ćØ", "ćØ"]
nanori The nanori readings of the current kanji item. ["恋恚"]
emphasis The reading type taught in the current itemā€™s kanji lesson. "onyomi"

The same information can also be obtained with wkItemInfo.currentState, but I recommend to use the callback argument whenever possible to decouple the callback function from the actual state (just in case I ever decide to implement preloading, which is however likely not going to happen).

The callback function passed to the notify() or the notifyWhenVisible() action also gets called with an argument containing these properties. In addition to that, it comes with one more property: injector. This object also provides the five append actions that were listed before, but with some differences:

  • They donā€™t accept callback functions as arguments.
  • They accept an additional settings object as their third argument.
  • They can only be called as long as injector.active is true. For example, if the user proceeds to the next item, the old injectors will be deactivated.
  • They return the created DOM element that will be appended.

The additional settings object currently supports three settings:

Setting Description
injectImmediately Usually, the section returned by the append action is not yet inserted into the page. If this is set to true, the injection is enforced at this point.
under By default, the target section is specified by the under() selector. This setting allows to specify any of the available sections (listed in under and hiddenSpoiler) as the target section.
sectionName At the top of each item page is a list of links pointing to the available info sections. By default, WK Item Info Injector registers appended main sections (not subsections or side info) and uses the heading text content as the link name. This setting allows to choose a different name.

Aside from the append actions, the injector object also offers the function registerAppendedElement(). If you manually insert DOM elements (not using the append actions), you can use this function to tell WK Item Info Injector about them. React might not always remove your inserted elements when the page changes, but if you register the element, WK Item Info Injector will make sure that the element gets removed when the user proceeds to the next lesson tab or item.

Return Value

Registering an action returns an object with the following functions:

Function Description
remove() Removes the registered action entirely, including its corresponding inserted section.
renew() Removes the corresponding inserted section, generates the section anew and inserts it. Can be called after the user updated some settings to immediately reflect the changes.

In the case of notify actions, elements that were registered with registerAppendedElement() are removed as well.

Example for updating the section after a settings change (from the Rendaku Information userscript). When registering the action in wkItemInfo, the returned object is stored in this.itemInfoHandle for later use:

this.itemInfoHandle = wkItemInfo.forType("vocabulary").under("reading").appendSubsection("Rendaku Information", o => this.createRendakuSection(o.characters));

The settings dialog is created with WK Open Framework and set up to call this.itemInfoHandle.renew() when the settings are saved:

new wkof.Settings({
	script_id: "rendaku_information",
	title: "Rendaku Information Settings",
	on_save: () => this.itemInfoHandle.renew(),
	content: {
		hideTrivial: {type: "checkbox", label: "Hide trivial info"}
	}
});

Usage Examples

WK Item Info Injector is already used in several scripts, some of which are listed here with a short description when they insert their info and example code showing how they use wkItemInfo. The last example, ā€œOld Mnemonicsā€, shows a slightly more complex use case.

Mnemonic Artwork userscript

This script inserts its info for some radicals and kanji. The info consists of a visual mnemonic for the meaning and the reading (both together in one image), so it belongs to both the meaning and the reading section.

wkItemInfo.forType("radical,kanji").under("meaning,reading").append("Mnemonic Artwork", ({id}) => artworkSection(id));
Keisei userscript

This script inserts its info for radicals and kanji. During lessons, the info should appear in the ā€œReadingsā€ tab for kanji, and in the ā€œNameā€ tab for radicals. The info contains the meaning and reading of the current item, so in reviews it should only appear once the item info is fully expanded to avoid spoilers.

wkItemInfo.forType("kanji").under("reading").spoiling("meaning,reading").notifyWhenVisible(this.injectKeiseiSection.bind(this));
wkItemInfo.forType("radical").under("meaning").notifyWhenVisible(this.injectKeiseiSection.bind(this));
Niai userscript

This script inserts its info only for kanji. During lessons, the info should appear in the ā€œExamplesā€ tab, but on all other pages, it should appear below the reading section. The info contains the meaning and reading of the current item, so in reviews it should only appear once the item info is fully expanded to avoid spoilers.

wkItemInfo.on("itemPage,lessonQuiz,review").forType("kanji").under("reading").spoiling("meaning,reading").notify(this.injectNiaiSection.bind(this));
wkItemInfo.on("lesson").forType("kanji").under("examples").notify(this.injectNiaiSection.bind(this));
Advanced Context Sentence 2 userscript

This script does not insert a new section, but modifies the existing context sentence section of vocabulary items. WK Item Info Injector can notify the script whenever a new context sentence section appears on screen, so that the section can immediately be modified:

wkItemInfo.forType("vocabulary", "kanaVocabulary").under("examples").notifyWhenVisible(() => evolveContextSentence());
KanjiDamage Mnemonics 2 userscript

This script inserts additional meaning and reading mnemonics for some kanji. They are inserted as subsections for the existing meaning and reading sections.

wkItemInfo.forType("kanji").under("meaning").appendSubsection(meaningHeading, appendMeaningMnemonic);
wkItemInfo.forType("kanji").under("reading").appendSubsection(readingHeading, appendReadingMnemonic);
Old Mnemonics userscript

This script inserts meaning and reading mnemonics for some radicals and kanji. In contrast to the KanjiDamage Mnemonics userscript, for some reason I decided to place the two mnemonics not as subsections, but as two main sections after the reading section (so that the reading mnemonic comes right after the meaning mnemonic). I will show two versions how to achieve this: In the first, both the meaning and the reading mnemonic still belong to ā€œmeaningā€ and ā€œreadingā€, respectively, but the meaning mnemonic should not use the default location below the meaning section, but should be inserted below the reading section. This requires more control than offered by the append actions: Instead use the notify() action and append with the additional settings object {under: "reading"} (but only if the reading section exists on the current page ā€“ in lessons and/or for radicals, the mnemonic should be inserted below the meaning section).

wkItemInfo.forType("radical,kanji").under("meaning").notify(o => {
	let heading = o.type === "radical" ? "Old Name Mnemonic" : "Old Meaning Mnemonic";
	let body = oldMnemonicSection(o.id, "meaning");
	o.injector.append(header, body, {under: [...o.under, ...o.hiddenSpoiler].includes("reading") ? "reading" : "meaning"});
});
wkItemInfo.forType("kanji").under("reading").append("Old Reading Mnemonic", o => oldMnemonicSection(o.id, "reading"));

Alternative version with the ā€œmeaningā€ part split up into more cases to simplify the callback function (with added spaces to make the code easier to read):

wkItemInfo                                            .forType("radical").under("meaning")                    .append("Old Name Mnemonic"   , o => oldMnemonicSection(o.id, "meaning"));
wkItemInfo.on("lesson"                               ).forType("kanji"  ).under("meaning")                    .append("Old Meaning Mnemonic", o => oldMnemonicSection(o.id, "meaning"));
wkItemInfo.on("itemPage,lessonQuiz,review,extraStudy").forType("kanji"  ).under("reading").spoiling("meaning").append("Old Meaning Mnemonic", o => oldMnemonicSection(o.id, "meaning"));
wkItemInfo                                            .forType("kanji"  ).under("reading")                    .append("Old Reading Mnemonic", o => oldMnemonicSection(o.id, "reading"));

Security

WK Item Info Injector assumes that everything on the WaniKani page is trustworthy. This might not be the case if the user has installed a malicious userscript, a malicious browser extension, or a userscript with an XSS vulnerability. The script does not sanitize data read from the DOM tree or from jStorage and just passes it on to the callback functions. Furthermore, if a sandboxed script (anything with @grant other than none) @requires WK Item Info Injector, it might use unsafeWindow to install wkItemInfo into the global page context. For more details about this security concern, you can read my discussion with @est_fills_cando about this topic.

I am of the opinion that the risk of malicious code being executed on the WaniKani site is low enough to ignore this possibility, but if you have a different opinion let me know.

Used Version

WK Item Info Injector is placed in the global scope of the webpage, so usually all scripts use the same instance. This makes the order in which the additional sections are appended deterministic. An outdated instance in the global scope is however replaced if a newer version of WK Item Info Injector is executed.

Example: Script A @requires version 1.0 of WK Item Info Injector, and Script B @requires version 1.1. If Script A executes before Script B, then Script A will install version 1.0 into the global scope, but Script B will later replace it with version 1.1. If at that point, Script A has already registered a selector in version 1.0, then this selector will continue to be handled by version 1.0. Both version 1.0 and 1.1 will continue to run in any case.
If Script B executes before Script A, then Script B will install version 1.1, and Script A will also use the existing version 1.1, which should not cause a problem because all versions should be backwards compatible.

Consequentially, if the user has installed WK Item Info Injector directly in their script manager, it should automatically be kept up-to-date, and as long as it runs before all @require-ing scripts, all of them will use the same (the newest) version. WK Item Info Injector will by default run before most other scripts because it uses @run-at document-start, but if there are @require-ing scripts that also use @run-at document-start, WK Item Info Injector has to be placed above those other scripts in the script manager.

Features

  • Simplified injection of item info
  • Usually deterministic order of injected sections (depending on the order in which the actions were registered)
  • Adds links at the top of item pages that point to injected main sections
  • Still works on item pages if not logged into WaniKani
  • Still works even if the user has Tampermonkey disabled on page load and enables it later
  • Tries to minimize browser reflow by inserting sections in batches
  • Adds a side info bar to the meaning tab of radical lessons if required
  • Easily remove or recompute your inserted section, e.g. after the user changed a setting

Why?

Aside from hopefully being useful for other script authors, the main reason for the creation of this library script are the extensive changes of the WaniKani page structure caused by the ongoing transition to React. Two months ago, a change to the lesson page caused several scripts to break: Two of them were my own, but also popular scripts like Keisei and Niai were affected. Most of the broken scripts were unmaintained, so while I was updating my two scripts, I got the idea that I could instead move the injection functionality into a separate library script, which I could then use to quickly fix all the unmaintained scripts.

Feedback and Requests

If you need additional features, ask here in this thread and I will think about adding them. Iā€™m also interested to hear what I could have done better in the design of the interface (selector chain + action), but I probably wonā€™t change it because everything should stay backwards compatible. Also let me know if any part of the documentation is unclear, or if the script shows some unexpected behavior. Lastly, Iā€™m not a native English speaker, so my wording might be awkward or grammatically incorrect ā€“ feel free to also correct me on that.

22 Likes

Developing Anime Sentences was 1000 times easier thanks to this library. Thank you so much @Sinyaven!

3 Likes

Version 1.2:

Now works with the recent update to the lesson quiz

Recently, the additional information panel in the lesson quiz was converted to React. WK Item Info Injector should now work again during the lesson quiz (both with and without script compatibility mode).

In the React version, the item info is created when the user proceeds to a new item (in the old version, it was only created once the user opened the item info). I decided to match the new WK behavior and also inject the info at that point instead of waiting for the user to open the item info. This speeds up opening the (unexpanded) item info, but causes a lot of unnecessary injections (for every review instead of just on demand). Iā€™m not sure if this was the best solution or if I should change it in the future ā€“ especially once the review page is also converted to React.

Kanji lesson meaning side info column added on demand

During kanji lessons, the side info column is not rendered anymore if there are no meaning synonyms. WK Item Info Injector now adds that column on demand (as was already done in the case of radical lessons) so that it is still possible to inject item info in this location.

Minor bugfixes/improvements
  • In some cases, side info was injected at the wrong place on item pages.
  • WaniKani now displays side info in a darker text color (class="text-gray-700"). WK Item Info Injector now automatically adds this class to injected elements.
// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=985948

Can someone explain to me how userscript authors are supposed to make use of WaniKani.version? I was expecting the compatibility mode to be on an older version number, but both modes are on the same version. WaniKani.wanikani_compatibility_mode can be used to differentiate between compatibility mode on/off. But if the version number is always the latest, is the version number even useful in any way?

2 Likes

Version 1.3:

Added two new actions

The new actions appendAtBottom() and appendSideInfoAtBottom() are the counterparts to the established actions appendAtTop() and appendSideInfoAtTop() and can be used to place your injected section below all native WK sections.

Bugfix related to the relatively new lesson tab "Word Use"

Some vocabulary items have a fifth lesson tab ā€œWord Useā€. I have not added support for this tab yet ā€“ you cannot inject sections into this tab using WK Item Info Injector, and while the user is on this tab, wkItemInfo.currentState.under is [undefined]. But at least the fifth tab should not affect the established injection functionality anymore.

Bugfix for endless loop

During lessons, WaniKani does not remove the lesson quiz elements. If these elements are changed while the user is actually doing lessons (and not the lesson quiz), the MutationObserver in WK Item Info Injector would trigger while in an unexpected state and possibly execute registered callback functions (actions).

The registered action of the WaniKani Pitch Info script changes every reading element on the page ā€“ also the reading elements in the hidden lesson quiz. This change triggered the MutationObserver, which in turn called the WaniKani Pitch Info action again, creating an endless loop.

// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1013941

Version 1.4:

Now supporting extra study page

Today WaniKani has added a new functionality: extra study. This update adds support for this new page. The .on() selector now also accepts the keyword extraStudy.

// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1024045
1 Like

Version 1.5:

Now supporting the accordion UI in the lesson quiz

A while ago, WK updated the lesson quiz again ā€“ this time they changed the item info display into an accordion. It took me a while to decide where the sections should be injected: should append() insert the section into an existing collapsible section, or should it create a new collapsible section? In the end, I decided to follow the initially intended semantics of my interface so that append() creates a new, more or less independent section (therefore a new collapsible section), while appendSubsection() or appendSideInfo() extends one of the existing sections (therefore injected into an existing collapsible section).

In case the number of collapsed sections gets too high and it becomes annoying clicking each one to open them, it is possible to use my Spacebar Expand All script to ā€œrestoreā€ the old spacebar functionality of showing the entire info section.

The main sections are injected once the user opens the item info. The subsections and side info within vanilla WK sections are injected once the user opens the corresponding WK section.

An interesting detail I noticed while working on this: Until now, WK has put the side info Word Type/Parts of Speech into the Meaning section, but in the new lesson quiz layout, this side info is in the Context section.

IMPORTANT: change of behavior

I had to slightly change the interface provided by WK Item Info Injector to make it work with the accordion UI: In addition to the notify() action, there is now a notifyWhenVisible() action. While notify() calls the callback function as soon as the user shows the item info (so that the callback function can add more collapsible sections if needed), notifyWhenVisible() calls the callback function when the targeted section is expanded (so that the callback function can modify native WK information; e.g. Advanced Context Sentence 2 that modifies the WK context sentences).

Also, calling injector.appendSubsection() within the callback when the targeted section is collapsed warns in the console that the subsection could not be injected. But the subsection will be cached and injected once the targeted section gets expanded, so it should still work despite the warning. Maybe I will remove the warning in a future version. However, injector.appendSubsection(h, b, {injectImmediately: true}) will also not inject immediately, so use notifyWhenVisible() if you need injectImmediately.

Greasemonkey compatibility

Greasemonkey does not automatically provide access to variables stored in the pageā€™s window object ā€“ you would have to explicitly use unsafeWindow.WaniKani.wanikani_compatibility_mode to check if the page is in script compatibility mode, or unsafeWindow.$.jStorage to access the jStorage loaded by WaniKani. Instead of using unsafeWindow or loading a new instance of jStorage into the script context, I decided to instead try out the window.eval() method described in the Greasemonkey wiki.

// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1057854
1 Like

Nevermind, just didnā€™t understand it fully I donā€™t think

Is it possible to get a callback call for every section besides defining it for each section separately? So letā€™s say I want to get both reading and meaning, and instead of appending some section, I want to modify the contents of these, so I put in ā€œmeaning,readingā€ for ā€œunderā€, and then currently in reviews I either get a callback for ā€œreadingā€ or ā€œmeaningā€, whichever comes first, but is it possible to get first a callback for meaning, and then reading, in case first only reading was visible?
Currently this could be done with:
wkItemInfo.under(ā€œreadingā€).spoiling().notify(ā€¦);
wkItemInfo.under(ā€œmeaningā€).spoiling().notify(ā€¦);

I only assumed this would work, but it actually doesnā€™t, just calls the callback twice for one section then stops.

1 Like

No, for this you have to register it separately per section. Do you have a use case where this poses a problem?

If you use wkItemInfo.under("meaning,reading") in reviews, this means that it will target the reading section (because it is the last in the list), except if itā€™s a radical that doesnā€™t have a reading section, then it targets the meaning section. Such a selector is useful if you want to append your section below all vanilla WK mnemonics, but can not be used for your use case.

If you tell Item Info Injector that your section does not spoil anything then it wonā€™t delay the action until the targeted section is visible. So I think you should remove the spoiling() selector.


So if you want to modify the content of the vanilla WK meaning section in your callback you would use:

wkItemInfo.under("meaning").notifyWhenVisible(yourCallback);

notifyWhenVisible() and notify() are almost the same ā€“ they only differ during the new lesson quiz where the section headings are always immediately visible while the content might still be collapsed. Since you want to modify the content, you have to use notifyWhenVisible() for it to work during the lesson quiz.

When I tried to define it per section, for some reason it was like the first that gets called also called the other callback, if that makes sense? So if I had

wkItemInfo.under("reading").notify(...)
wkItemInfo.under("meanign").notify(...)

Both of them got called with state.under being [ā€œmeaningā€, ā€œcompositionā€] or [ā€œreadingā€, ā€œcompositionā€], whichever came first.
Also, not having spoiling made it so I needed to open all of the information to work I think

1 Like

Are you sure that this also happened when you did not include the spoiling() selector?

I assume you tried

wkItemInfo.under("meaning,reading").notify(yourCallback)

and yourCallback was only called once the user showed all information. The reason this happens is because if you ā€œhookā€ your action to meaning and reading, then Item Info Injector by default assumes your info spoils meaning and reading, so it delays the action until all info is viewed. I donā€™t know what exactly you want to achieve, but I think everything becomes less confusing if you do not use multiple values in under() (and donā€™t use spoiling() at all because you probably donā€™t need it). If you want, you can describe your use case and I can tell you how/if it can be done with Item Info Injector.

This one works, it seems the spoiling() was my issue, thanks a lot!

1 Like

Crashes for radical itemInfo page, e.g.

// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        https://www.wanikani.com/radicals/poop
// @icon         https://www.google.com/s2/favicons?sz=64&domain=wanikani.com
// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1057854
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

wkItemInfo
    .on('itemPage')
    .append('blahblaj', (state) => {
      return document.createElement('hr');
    });
})();

Brave Browser, Windows 11, Tampermonkey

image

1 Like

Thanks for letting me know that they have changed the Radical item pages. I will try to update my script as soon as possible.

I was hoping it would still work with compatibility mode enabled, but the change seems to be in both versions. Iā€™m wondering if this is a mistake or if they have abandoned this approach. Their webpage about the compatibility mode is also pretty outdated.

1 Like

Version 1.6:

  • Updated to support new React-based radical item info page
// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1101319
2 Likes

class="subject-section__text" is missing, so styling remains broken. I am not sure if class="subject-section__subsection" is also needed.

1 Like

Version 1.6 only added this class if the user only provided a string and not their own elements for the info body. I was not sure if I should also modify the elements provided by the user by automatically adding the class subject-section__text to them, but itā€™s probably useful to do this.


Version 1.7:

  • Automatically add class subject-section__text to info body elements on item pages
// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1101385
2 Likes

Version 1.8:

Reverted the change in v1.7 and instead copied CSS from WK to apply it to the parent element

I want to avoid tampering with the user-provided elements as much as possible to avoid unexpected behavior. With version 1.7, a user that wants to create a new section which also has its own subsections might write something like this:

let heading = document.createElement("h3");
heading.textContent = "Subsection Heading";
heading.classList.add("subject-section__subtitle");
let p = document.createElement("p");
p.textContent = "Subsection text";
wkItemInfo.appendAtTop("Test", [heading, p]);

but because version 1.7 automatically adds the class subject-section__text to every provided element ā€“ including the h3 element ā€“ the heading is not styled as a subsection heading but as regular text. Therefore, I reverted this behavior and instead added WaniKaniā€™s regular text styling to the CSS for the parent section.item-info-injector element which can easily be overridden by the user.

// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1102710
1 Like

Is it possible to either append below or append at top, depending on callback logic?

1 Like

For this, you have to use the notify() action. For example:

wkItemInfo.forType("radical").spoiling("nothing").notify(state => {
	if (state.meaning[0].startsWith("B")) {
		state.injector.appendAtTop("Heading", "Body");
	} else {
		state.injector.appendAtBottom("Heading", "Body");
	}
});

For radicals with a name starting with the letter B, the section is inserted at the top, and for other radicals it is inserted at the bottom.

1 Like

Version 1.9:

  • Updated to support new React-based Kanji item info page
// @require      https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1103761
3 Likes