WK Extra study mover

Moves the WaniKani extra study box below the level progress

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

You will need to install an extension such as Tampermonkey to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         WK Extra study mover
// @namespace    http://tampermonkey.net/
// @version      0.8.7
// @description  Moves the WaniKani extra study box below the level progress
// @author       Gorbit99
// @include      /^https?:\/\/(www\.)?(preview\.)?wanikani\.com\/?(dashboard)?$/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=wanikani.com
// @grant        none
// @license      MIT
// @require      https://greasyfork.org/scripts/441792-cidwwa/code/CIDWWA.js?version=1270274
// @require      https://greasyfork.org/scripts/489759-wk-custom-icons/code/CustomIcons.js?version=1344498
// @run-at       document-end
// ==/UserScript==

(() => {
    "use strict";

    const headerObserverConfig = {
        childList: true,
    };

    const headerObserver = new MutationObserver((mutationList) => {
        for (const mutation of mutationList) {
            for (const added of mutation.addedNodes) {
                if (added.id === "LnRHeader") {
                    const progress = document.querySelector(".wk-panel--level-progress");
                    const recentMistakes = document.querySelector(".wk-panel--recent-mistakes");
                    const extraStudy = document.querySelector(".wk-panel--extra-study");
                    const dashboard = document.querySelector(".progress-and-forecast");
                    dashboard.style.gridTemplateRows = "auto auto auto auto";
                    switch (settings.position) {
                        case "none":
                            progress.style.gridRow = settings.goodMojo == "yes" ? "1/5" : "2/5";
                            recentMistakes.style.gridRow = "1";
                            break;
                        case "top":
                            recentMistakes.style.gridRow = "3";
                            extraStudy.style.setProperty("grid-row", settings.goodMojo == "yes" ? "1/4" : "1/3", "important");
                            break;
                        case "above-rm":
                            recentMistakes.style.gridRow = "3";
                            extraStudy.style.setProperty("grid-row", settings.goodMojo == "yes" ? "1/4" : "1/3", "important");
                            break;
                        case "above-lp":
                            recentMistakes.style.gridRow = "1";
                            extraStudy.style.setProperty("grid-row", settings.goodMojo == "yes" ? "1/4" : "2/4", "important");
                            break;
                        case "below-lp":
                            progress.style.gridRow = settings.goodMojo == "yes" ? "1/4" : "2 / 4";
                            recentMistakes.style.gridRow = "1";
                            break;
                        case "above-ib":
                            progress.style.gridRow = settings.goodMojo == "yes" ? "1/4" : "2 / 4";
                            recentMistakes.style.gridRow = "1";
                            break;
                        case "below-ib":
                            progress.style.gridRow = settings.goodMojo == "yes" ? "1/4" : "2 / 4";
                            recentMistakes.style.gridRow = "1";
                            break;
                        case "above-rf":
                            progress.style.gridRow = settings.goodMojo == "yes" ? "1/4" : "3/4";
                            recentMistakes.style.gridRow = "1/3";
                            break;
                        case "below-rf":
                            progress.style.gridRow = settings.goodMojo == "yes" ? "1/5" : "2/5";
                            recentMistakes.style.gridRow = "1";
                            break;
                        case "header":
                            progress.style.gridRow = settings.goodMojo == "yes" ? "1/4" : "2 / 4";
                            recentMistakes.style.gridRow = "1";
                            break;
                    }
                    headerObserver.disconnect();
                }
            }
        }
    });

    const gearIcon = `
    <svg id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M424.5,216.5h-15.2c-12.4,0-22.8-10.7-22.8-23.4c0-6.4,2.7-12.2,7.5-16.5l9.8-9.6c9.7-9.6,9.7-25.3,0-34.9l-22.3-22.1  c-4.4-4.4-10.9-7-17.5-7c-6.6,0-13,2.6-17.5,7l-9.4,9.4c-4.5,5-10.5,7.7-17,7.7c-12.8,0-23.5-10.4-23.5-22.7V89.1  c0-13.5-10.9-25.1-24.5-25.1h-30.4c-13.6,0-24.4,11.5-24.4,25.1v15.2c0,12.3-10.7,22.7-23.5,22.7c-6.4,0-12.3-2.7-16.6-7.4l-9.7-9.6  c-4.4-4.5-10.9-7-17.5-7s-13,2.6-17.5,7L110,132c-9.6,9.6-9.6,25.3,0,34.8l9.4,9.4c5,4.5,7.8,10.5,7.8,16.9  c0,12.8-10.4,23.4-22.8,23.4H89.2c-13.7,0-25.2,10.7-25.2,24.3V256v15.2c0,13.5,11.5,24.3,25.2,24.3h15.2  c12.4,0,22.8,10.7,22.8,23.4c0,6.4-2.8,12.4-7.8,16.9l-9.4,9.3c-9.6,9.6-9.6,25.3,0,34.8l22.3,22.2c4.4,4.5,10.9,7,17.5,7  c6.6,0,13-2.6,17.5-7l9.7-9.6c4.2-4.7,10.2-7.4,16.6-7.4c12.8,0,23.5,10.4,23.5,22.7v15.2c0,13.5,10.8,25.1,24.5,25.1h30.4  c13.6,0,24.4-11.5,24.4-25.1v-15.2c0-12.3,10.7-22.7,23.5-22.7c6.4,0,12.4,2.8,17,7.7l9.4,9.4c4.5,4.4,10.9,7,17.5,7  c6.6,0,13-2.6,17.5-7l22.3-22.2c9.6-9.6,9.6-25.3,0-34.9l-9.8-9.6c-4.8-4.3-7.5-10.2-7.5-16.5c0-12.8,10.4-23.4,22.8-23.4h15.2  c13.6,0,23.3-10.7,23.3-24.3V256v-15.2C447.8,227.2,438.1,216.5,424.5,216.5z M336.8,256L336.8,256c0,44.1-35.7,80-80,80  c-44.3,0-80-35.9-80-80l0,0l0,0c0-44.1,35.7-80,80-80C301.1,176,336.8,211.9,336.8,256L336.8,256z"/></svg>
    `;

    headerObserver.observe(document.head, headerObserverConfig);

    let settings = {
        position: "below-lp",
        style: "normal",
        questionmarks: "yes",
        orientation: "horizontal",
        goodMojo: "no",
    };

    const modal = window.createModal({
        title: "Extra Study Mover Settings",
    });

    const iconStyle = document.createElement("style");
    document.head.append(iconStyle);
    iconStyle.innerHTML = `
      .es-mover-settings__gear svg {
        width: 1.5em;
      }
    `;

    function loadSettings() {
        const storedSettings = window.localStorage.getItem("esMover.settings");
        if (storedSettings) {
            settings = JSON.parse(storedSettings);
            settings.questionmarks ??= "yes";
            settings.orientation ??= "horizontal";
            settings.goodMojo ??= "no";
        }
    }

    function saveSettings() {
        window.localStorage.setItem("esMover.settings", JSON.stringify(settings));
    }

    function injectChanges() {
        if (document.querySelector(".wk-panel--extra-study").childElementCount === 0) {
            requestAnimationFrame(injectChanges);
            return;
        }
        changeOrientation();
        changePosition();
        createSettingsButton();
        populateSettingsModal();
        changeStyling();

        if (settings.questionmarks === "no") {
            removeQuestionmarks();
        }
    }

    function changeStyling() {
        const extraStudy = document.querySelector(".wk-panel--extra-study");
        if (settings.position !== "header") {
            const title = extraStudy.querySelector("h2");
            switch (settings.style) {
                case "normal":
                    break;
                case "only-text":
                    extraStudy.querySelector("img").parentNode.remove();
                    extraStudy.querySelector(".extra-study__info").style.flexBasis = "100%";
                    break;
                case "only-image":
                    extraStudy.querySelector(".extra-study__info p").remove();
                    break;
                case "reduced":
                    extraStudy.querySelector(".extra-study__info p").remove();
                    extraStudy.querySelector("img").parentNode.remove();
                    extraStudy.querySelector(".extra-study__info").style.flexBasis = "100%";
                    break;
                case "minimal": {
                    const items = extraStudy.querySelector(".extra-study__buttons");
                    title.after(items);
                    items.classList.remove("w-full");
                    extraStudy.querySelector(".extra-study").remove();
                    title.parentNode.style.display = "flex";
                    title.parentNode.style.flexDirection = "row";
                    title.parentNode.style.alignItems = "center";
                    title.style.marginBottom = "0";
                    break;
                }
            }
        }
        document.querySelector(".wk-panel--extra-study")
            .classList.add(`es-mover-${settings.style}`);
    }

    function changeOrientation() {
        if (settings.orientation === "horizontal") {
            const extraStudyList = document.querySelector(".extra-study__buttons");
            extraStudyList.style.flexDirection = "row";
            extraStudyList.style.display = "flex";

            const orientationStyleElement = document.createElement("style");
            document.head.append(orientationStyleElement);
            orientationStyleElement.innerHTML = `
         .extra-study__buttons > div{
          margin-bottom: 0 !important;
          margin-top: 0 !important;
          min-width: 0;
          flex: 1 1 0;
          position: relative;
        }

        .extra-study-button__link {
          width: 100%;
        }

        .extra-study__info {
          width: 100%;
        }

        .extra-study-button__tooltip-button {
          position: absolute;
          right: -0.95em;
          bottom: 0.05em;
        }
        `;
            }
        }

    function changePosition() {
        const container = document.querySelector(".progress-and-forecast");
        const extraStudy = document.querySelector(".wk-panel--extra-study");
        const dashboardProgress = document.querySelector(".wk-panel--level-progress");
        const lAndR = document.querySelector(".lessons-and-reviews");
        const srsProgress = document.querySelector(".srs-progress");
        const reviewForecast = document.querySelector(".wk-panel--review-forecast");
        const recentMistakes = document.querySelector(".wk-panel--recent-mistakes");
        extraStudy.classList.add("es-mover");

        if (settings.goodMojo == "yes") {
            recentMistakes.style.display = "none";
        }

        switch (settings.position) {
            case "top":
                container.style.gridTemplateRows = "auto auto auto";
                extraStudy.style.setProperty("grid-row", "1", "important");
                lAndR.style.gridRow = settings.goodMojo == "yes" ? "2/4" : "2/3";
                lAndR.style.marginBottom = "0";
                recentMistakes.style.gridRow = 3;
                break;
            case "above-rm":
                extraStudy.style.setProperty("grid-row", settings.goodMojo == "yes" ? "2/4" : "2", "important");
                recentMistakes.style.gridRow = 3;
                break;
            case "above-lp":
                extraStudy.style.setProperty("grid-row", settings.goodMojo == "yes" ? "2/4" : "3", "important");
                break;
            case "below-lp":
                container.style.marginBottom = "30px";
                dashboardProgress.style.gridRow = settings.goodMojo == "yes" ? "2/4" : "3";
                dashboardProgress.style.marginBottom = "0";
                extraStudy.style.setProperty("grid-row", "4", "important");
                extraStudy.style.marginBottom = "0";
                reviewForecast.style.marginBottom = "0";
                break;
            case "above-ib":
                container.style.gridTemplateRows = "auto auto";
                dashboardProgress.style.gridRow = settings.goodMojo == "yes" ? "2/4" : "3";
                dashboardProgress.style.marginBottom = "0";
                extraStudy.style.marginBottom = "30px";
                lAndR.style.gridRow = "1";
                lAndR.style.marginBottom = "0";
                srsProgress.before(extraStudy);
                break;
            case "below-ib":
                container.style.gridTemplateRows = "auto auto";
                dashboardProgress.style.gridRow = settings.goodMojo == "yes" ? "2/4" : "3";
                dashboardProgress.style.marginBottom = "0";
                extraStudy.style.marginBottom = "30px";
                lAndR.style.gridRow = "1";
                lAndR.style.marginBottom = "0";
                srsProgress.after(extraStudy);
                break;
            case "above-rf":
                container.style.gridTemplateRows = "auto auto auto";
                dashboardProgress.style.gridRow = settings.goodMojo == "yes" ? "2/4" : "3";
                dashboardProgress.style.marginBottom = "0";
                extraStudy.style.gridColumn = "5/span 2";
                extraStudy.style.setProperty("grid-row", "1", "important");
                extraStudy.style.marginBottom = "0";
                lAndR.style.gridRow = "1";
                lAndR.style.marginBottom = "0";
                reviewForecast.style.gridRow = "2/5";
                break;
            case "below-rf":
                container.style.gridTemplateRows = "auto auto";
                dashboardProgress.style.gridRow = settings.goodMojo == "yes" ? "2/5" : "3/5";
                extraStudy.style.gridColumn = "5/span 2";
                extraStudy.style.setProperty("grid-row", "4", "important");
                extraStudy.style.marginBottom = "30px";
                lAndR.style.gridRow = "1";
                lAndR.style.marginBottom = "0";
                reviewForecast.style.gridRow = "1/4";
                reviewForecast.style.marginBottom = "0";
                break;
            case "none": {
                container.style.gridTemplateRows = "auto auto";
                dashboardProgress.style.gridRow = settings.goodMojo == "yes" ? "2/5" : "3/5";
                extraStudy.style.display = "none";
                lAndR.style.gridRow = "1";
                lAndR.style.marginBottom = "0";
                const menus = document.querySelectorAll(".sitemap__section--subsection");
                const settingsMenu = menus[menus.length - 1];
                settingsMenu.querySelector(".sitemap__pages").innerHTML += `
          <li class="sitemap__page" style="cursor: pointer">
            <a class="es-mover-settings">Extra Study Position</a>
          </li>
        `;
                break;
            }
            case "header": {
                const button = window.createButton({
                    englishText: "Extra Study",
                    japaneseText: "エクストラ",
                    color: "#ffb13d",
                    hoverColor: "#ffbf5e",
                    withDropdown: {
                        bgColor: "#f4f4f4",
                    },
                });

                const buttonDropdownStyle = `
          position: relative;
          color: black;
          overflow: visible;
        `;

                const settingsGearStyle = `
          position: absolute;
          top: 2px;
          right: 10px;
          color: var(--color-text);
        `;

                const buttonsContainerStyle = `
          display: flex;
          flex-direction: column;
          padding: 26px 12px 12px;
          gap: 12px;
          margin: 0;
          color: white;
          list-style-type: none;
        `;

                button.setDropdownContent(`
            <div
              class="wk-panel--extra-study es-mover es-mover-header"
              style="${buttonDropdownStyle}"
            >
              <span
                class="es-mover-settings cursor-pointer"
                style="${settingsGearStyle}"
              >
                <span class="es-mover-settings__gear">
                  ${gearIcon}
                </span>
              </span>
              <div style="${buttonsContainerStyle}" class="extra-study-buttons">
              </div>
            </div>
          `);
                const buttonContainer = document.querySelector(".extra-study-buttons");
                [...document.querySelectorAll(".extra-study__buttons > div")]
                    .forEach((button) => {
                    buttonContainer.append(button);
                });

                buttonContainer.classList.add("extra-study__buttons");

                const audioQuizSwitch = document.querySelector("#audio-quiz-switch");
                if (audioQuizSwitch) {
                    buttonContainer.after(
                        document.querySelector("[for=audio-quiz-switch]")
                    );
                    buttonContainer.after(audioQuizSwitch);
                }

                const style = document.createElement("style");
                style.innerHTML = ".wk-panel--extra-study ul > div {margin: 0;}";
                document.head.append(style);

                extraStudy.remove();
                dashboardProgress.style.gridRow = settings.goodMojo == "yes" ? "2/4" : "3";
                dashboardProgress.style.marginBottom = "0";

                break;
            }
        }

        document.querySelector(".wk-panel--extra-study")
            .classList.add(`es-mover-${settings.position}`);
    }

    function createSettingsButton() {
        if (settings.position !== "header" && settings.position !== "none") {
            const title = document.querySelector(".wk-panel--extra-study h2");
            title.style.marginBottom = "0";

            title.outerHTML = `
      <div style="display: flex; justify-content: space-between; align-items: center; width: 100%; margin-bottom: var(--spacing-tight)">
        ${title.outerHTML}
        <div class="es-mover-settings" style="cursor: pointer">
          <span class="es-mover-settings__gear">${gearIcon}</span>
        </div>
      </div>
    `;
        }

        const settingsButton = document.querySelector(".es-mover-settings");
        settingsButton.addEventListener("click", () => modal.toggle());
    }

    function populateSettingsModal() {
        const positionOptions = [
            { value: "top", label: "Top" },
            { value: "above-rm", label: "Above Recent Mistakes" },
            { value: "above-lp", label: "Above Level Progress" },
            { value: "below-lp", label: "Below Level Progress" },
            { value: "above-ib", label: "Above Item Breakdown" },
            { value: "below-ib", label: "Below Item Breakdown" },
            { value: "above-rf", label: "Above Review Forecast" },
            { value: "below-rf", label: "Below Review Forecast" },
            { value: "header", label: "In the header" },
            { value: "none", label: "None" },
        ];

        const positionOptionsHTML = positionOptions.map(option =>
                                                        `<option
      value="${option.value}" 
      ${option.value == settings.position ? "selected" : ""}
    >
      ${option.label}
    </option>`)
        .join("");

        const styleOptions = [
            { value: "normal", label: "Normal" },
            { value: "only-text", label: "Only Text" },
            { value: "only-image", label: "Only Image" },
            { value: "reduced", label: "Reduced" },
            { value: "minimal", label: "Minimal" },
        ];

        const styleOptionsHTML = styleOptions.map(option =>
                                                  `<option
      value="${option.value}" 
      ${option.value == settings.style ? "selected" : ""}
    >
      ${option.label}
    </option>`)
      .join("");

        const questionmarkOptions = [
            { value: "yes", label: "Yes" },
            { value: "no", label: "No" },
        ];

        const questionmarkOptionsHTML = questionmarkOptions.map(option =>
                                                                `<option
      value="${option.value}" 
      ${option.value == settings.questionmarks ? "selected" : ""}
    >
      ${option.label}
    </option>`)
      .join("");

        const orientationOptions = [
            { value: "horizontal", label: "Horizontal" },
            { value: "vertical", label: "Vertical" },
        ];

        const orientationOptionsHTML = orientationOptions.map(option =>
                                                              `<option
      value="${option.value}" 
      ${option.value == settings.orientation ? "selected" : ""}
    >
      ${option.label}
    </option>`)
      .join("");

        const goodMojoOptions = [
            { value: "no", label: "No" },
            { value: "yes", label: "Yes" },
        ];

        const goodMojoOptionsHTML = goodMojoOptions.map(option =>
                                                              `<option
      value="${option.value}"
      ${option.value == settings.goodMojo ? "selected" : ""}
    >
      ${option.label}
    </option>`)
      .join("");

        const buttonsStyle = `
    margin-top: 20px;
    display: flex;
    justify-content: center;
    gap: 3em;
  `;

        modal.setContent(`
    <div class="es-mover-settings-items">
      <div class="control-group flag">
        <label class="flag__item">Extra study panel position</label>
        <select 
          class="flag__asset position-select" 
          value="${settings.position}"
        >
          ${positionOptionsHTML}
        </select>
      </div>
      <div class="control-group flag">
        <label class="flag__item">Extra study style</label>
        <select class="flag__asset style-select" value="${settings.style}">
          ${styleOptionsHTML}
        </select>
      </div>
      <div class="control-group flag">
        <label class="flag__item">Include questionmarks</label>
        <select 
          class="flag__asset questionmark-select" 
          value="${settings.questionmarks}"
        >
          ${questionmarkOptionsHTML}
        </select>
      </div>
      <div class="control-group flag">
        <label class="flag__item">Button orientation</label>
        <select 
          class="flag__asset orientation-select" 
          value="${settings.orientation}"
        >
          ${orientationOptionsHTML}
        </select>
      </div>
      <div class="control-group flag">
        <label class="flag__item">Good Mojo</label>
        <select
          class="flag__asset good-mojo-select"
          value="${settings.goodMojo}"
        >
          ${goodMojoOptionsHTML}
        </select>
      </div>
      <div class="buttons" style="${buttonsStyle}">
        <button class="btn btn-cancel">Cancel</button>
        <button class="btn btn-ok">Ok</button>
      </div>
    </div>
  `);
        const settingsItems = document.querySelector(".es-mover-settings-items");
        const buttons = settingsItems.querySelector(".buttons");

        const positionSelect = settingsItems.querySelector(".position-select");
        const styleSelect = settingsItems.querySelector(".style-select");
        const questionmarkSelect =
              settingsItems.querySelector(".questionmark-select");
        const orientationSelect =
              settingsItems.querySelector(".orientation-select");
        const goodMojoSelect =
              settingsItems.querySelector(".good-mojo-select");

        const okButton = buttons.querySelector(".btn-ok");
        const cancelButton = buttons.querySelector(".btn-cancel");

        okButton.addEventListener("click", () => {
            settings.position = positionSelect.value;
            settings.style = styleSelect.value;
            settings.questionmarks = questionmarkSelect.value;
            settings.orientation = orientationSelect.value;
            settings.goodMojo = goodMojoSelect.value;
            saveSettings();
            location.reload();
        });

        cancelButton.addEventListener("click", () => {
            positionSelect.value = settings.position;
            styleSelect.value = settings.style;
            questionmarkSelect.value = settings.questionmarks;
            orientationSelect.value = settings.orientation;
            goodMojoSelect.value = settings.goodMojo;
            modal.close();
        });
    }

    function removeQuestionmarks() {
        [...document.querySelectorAll(".extra-study-button__tooltip-button")]
            .forEach((button) => {
            button.style.display = "none";
            //button.querySelector(":scope > div").remove();
            //button.querySelector("a, button").style.paddingRight = "12px";
        });
    }

    loadSettings();
    injectChanges();
})();