您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a new word's context menu from its top deck in JPDB reviews
- // ==UserScript==
- // @name JPDB add context menu in reviews
- // @namespace jpdb.io
- // @version 0.1.2
- // @description Adds a new word's context menu from its top deck in JPDB reviews
- // @author daruko
- // @match https://jpdb.io/review*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=jpdb.io
- // @grant none
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- const debug = false;
- let menuDetails = init();
- const observer = new MutationObserver(handleMutation);
- observer.observe(document.body, { childList: true, subtree: true });
- function handleMutation(mutations) {
- if (!menuDetails || !document.body.contains(menuDetails)) {
- menuDetails = init();
- }
- }
- function init() {
- const wordUri = document.querySelector('.review-reveal .answer-box a[href^="/vocabulary"]')?.href;
- const deckUri = document.querySelector('.review-reveal a[href^="/deck"]')?.href;
- if (!wordUri || !deckUri) {
- debug && console.debug('URI not found');
- return;
- }
- const [,, wordId, word] = new URL(wordUri).pathname.split('/', 4);
- if (!word) {
- debug && console.debug('Word URI not recognized');
- return;
- }
- return createDropdownButton(wordId, word, deckUri);
- }
- function createDropdownButton(wordId, word, deckUri) {
- const sideButton = document.querySelector('.review-button-group .side-button');
- if (!sideButton) {
- debug && console.debug('Side button not found');
- return;
- }
- const buttonWrapper = document.createElement("div");
- buttonWrapper.style = "display: flex; flex-direction: column;";
- sideButton.style = "flex-grow: 1;" + sideButton.style;
- sideButton.parentNode.insertBefore(buttonWrapper, sideButton);
- buttonWrapper.appendChild(sideButton);
- const dropdownButtonLabel = document.createElement("label");
- dropdownButtonLabel.className = "side-button";
- dropdownButtonLabel.style = "margin-top: 0;";
- const dropdownButtonDiv = document.createElement("div");
- dropdownButtonDiv.className = "dropdown right-aligned";
- dropdownButtonLabel.appendChild(dropdownButtonDiv);
- const dropdownButtonDetails = document.createElement("details");
- dropdownButtonDiv.appendChild(dropdownButtonDetails);
- const dropdownButtonSummary = document.createElement("summary");
- dropdownButtonSummary.style = "padding: 0; border: none;";
- dropdownButtonSummary.appendChild(document.createTextNode("⋮"));
- dropdownButtonDetails.appendChild(dropdownButtonSummary);
- buttonWrapper.appendChild(dropdownButtonLabel);
- const onToggle = () => {
- useDropdown(dropdownButtonDetails, wordId, word, deckUri)
- .then(() => dropdownButtonDetails.removeEventListener("toggle", onToggle));
- };
- dropdownButtonDetails.addEventListener("toggle", onToggle);
- return dropdownButtonDetails;
- }
- async function useDropdown(details, wordId, word, deckUri) {
- const filteredDeckUri = `${deckUri}&q=${word}`;
- const fallbackDeckUri = `${deckUri.replace(/id=[0-9]+/, 'id=global')}&q=${word}`;
- let dropdownContent = await loadDropdown(filteredDeckUri, wordId);
- if (!dropdownContent) {
- debug && console.debug('Dropdown not found in the primary deck page');
- dropdownContent = await loadDropdown(fallbackDeckUri, wordId);
- if (!dropdownContent) {
- debug && console.debug('Dropdown not found in the global deck page');
- return;
- }
- }
- details.appendChild(dropdownContent);
- const offset = Math.min(details.getBoundingClientRect().left - dropdownContent.offsetWidth,
- details.parentElement.offsetWidth);
- dropdownContent.style = `bottom: 0; right: ${offset}px;` + dropdownContent.style;
- return dropdownContent;
- }
- function loadDropdown(uri, wordId) {
- return fetch(uri)
- .then((response) => response.text())
- .then((text) => {
- const html = new DOMParser().parseFromString(text, "text/html");
- return html.querySelector(`.entry .vocabulary-spelling a[href^="/vocabulary/${wordId}"]`)
- ?.closest('.entry')
- ?.querySelector('.dropdown details .dropdown-content');
- })
- .catch((err) => {
- console.error('An error has occurred.', err);
- });
- }
- })();