Wanikani hanzi-writer addition

Replaces kanji in wanikani with hanzi writer. Licenses for kanji data are found at https://github.com/chanind/hanzi-writer-data-jp/

当前为 2024-08-11 提交的版本,查看 最新版本

// ==UserScript==
// @name        Wanikani hanzi-writer addition
// @namespace   https://declanfodor.com
// @description Replaces kanji in wanikani with hanzi writer. Licenses for kanji data are found at https://github.com/chanind/hanzi-writer-data-jp/
// @match       https://www.wanikani.com/*
// @version     0.0.2
// @author      Declan Fodor
// @resource    kanjiJSON https://raw.githubusercontent.com/chanind/hanzi-writer-data-jp/master/data/all.json
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/hanzi-writer.js
// @require     https://cdn.jsdelivr.net/npm/@violentmonkey/dom@2
// @license     MIT
// @grant       GM_addStyle
// @grant       GM_getResourceText
// ==/UserScript==

(function () {
'use strict';

const WK_PAGE = Object.freeze({
  REVIEW: Symbol("review_page"),
  LESSON: Symbol("lesson_page"),
  DASHBOARD: Symbol("dashboard_page"),
  LOADING: Symbol("other_page") // Or a page we haven't implemented behavior for yet
});
class PageStatus {
  constructor(previousStatus) {
    this.page = this.whichPage(unsafeWindow.location.href);
    this.switched = previousStatus ? previousStatus.page !== this.page : true;
  }
  whichPage(url) {
    switch (url) {
      case "https://www.wanikani.com/":
        return WK_PAGE.DASHBOARD;
      case "https://www.wanikani.com/subjects/review":
        return WK_PAGE.REVIEW;
      default:
        return WK_PAGE.LOADING;
    }
  }
}

class PageObserver {
  constructor(wk_page, onPage, offPage) {
    this.status = new PageStatus(null);
    this.observer = new MutationObserver(() => {
      if (this.status.switched && this.status.page === wk_page) {
        onPage();
      } else if (this.status.switched) {
        offPage();
      }
      this.status = new PageStatus(this.status);
    });
    this.observer.observe(document, {
      childList: true,
      subtree: true
    });
  }
}

let hanziWriterSettings = {
  drawingColor: '#000000',
  drawingWidth: 20,
  strokeColor: '#ff9900',
  quiz: true
};

let kanjiJSON = JSON.parse(GM_getResourceText("kanjiJSON"));
class ReviewPage {
  constructor() {
    this.kanjiElem = null;
    this.kanji = null;
    this.writer = null;
    this.containerdiv = null;
  }
  /**
   * Called whenever the kanji has switched. It creates the hanzi writer instance
   */
  drawHanziWriter() {
    if (!this.writer) {
      let characterheader = document.querySelector(".quiz .character-header");
      this.containerdiv = document.createElement("div");
      this.containerdiv.id = "wkhwa-container-div";
      GM_addStyle(`#wkhwa-container-div {
                position: relative;
                top: -32px;
                display: flex;
                align-items: center;
                justify-content: center;
            }`);
      this.writer = HanziWriter.create(this.containerdiv, this.kanji, {
        width: 200,
        height: 200,
        showCharacter: false,
        showHintAfterMisses: 3,
        padding: 5,
        drawingWidth: hanziWriterSettings.drawingWidth,
        strokeColor: hanziWriterSettings.strokeColor,
        drawingColor: hanziWriterSettings.drawingColor,
        charDataLoader: (char, onLoad) => {
          onLoad(kanjiJSON[char]);
        }
      });
      characterheader.append(this.containerdiv);
      {
        this.writer.quiz();
      }
    } else {
      this.writer.setCharacter(this.kanji);
    }
  }
  onReviewPage() {
    this.observer = new MutationObserver(() => {
      if (this.refreshKanjiState()) {
        this.drawHanziWriter();
      }
    });
    this.kanjiElem = document.querySelector(".quiz .character-header .character-header__characters");
    if (this.refreshKanjiState()) {
      this.drawHanziWriter();
    }
    this.observer.observe(this.kanjiElem, {
      childList: true,
      subtree: true
    });
  }
  showHanziWriter() {
    this.kanjiElem.hidden = true;
    if (this.containerdiv) {
      this.containerdiv.hidden = false;
    }
  }
  hideHanziWriter() {
    this.kanjiElem.hidden = false;
    if (this.containerdiv) {
      this.containerdiv.hidden = true;
    }
  }
  /**
   * Returns true if the kanji shown has switched. Returns false otherwise
   * This function also manages hiding and showing kanji 
   * in the event that the characters shown are either a radical or vocabulary
   */
  refreshKanjiState() {
    if (document.querySelector(".quiz-input__question-category").innerText.toLowerCase() === "kanji") {
      if (this.kanjiElem.innerText !== this.kanji) {
        // We have switched to a new kanji, mayhap away from vocabulary, so we need to set these to be shown
        this.showHanziWriter();
        this.kanji = this.kanjiElem.innerText;
        return true;
      } else {
        return false;
      }
    }
    // The character content has switched to vocabulary or a radical
    this.hideHanziWriter();
    return false;
  }
  /**
   * Cleans up various objects if we switch away from them.
   */
  offReviewPage() {
    this.kanji = null;
    this.writer = null;
    this.containerdiv = null;
    if (this.observer) {
      this.observer.disconnect();
    }
  }
}

let reviewpage = new ReviewPage();
new PageObserver(WK_PAGE.REVIEW, reviewpage.onReviewPage.bind(reviewpage), reviewpage.offReviewPage.bind(reviewpage));

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址