您需要先安装一个扩展,例如 篡改猴、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.1 // @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 = () => { loadDropdown(dropdownButtonDetails, wordId, word, deckUri) .then(() => dropdownButtonDetails.removeEventListener("toggle", onToggle)); }; dropdownButtonDetails.addEventListener("toggle", onToggle); return dropdownButtonDetails; } function loadDropdown(details, wordId, word, deckUri) { const filteredDeckUri = `${deckUri}&q=${word}&show_only=new,locked,overdue`; return fetch(filteredDeckUri) .then((response) => response.text()) .then((text) => { const html = new DOMParser().parseFromString(text, "text/html"); const dropdownContent = html.querySelector(`.entry .vocabulary-spelling a[href^="/vocabulary/${wordId}"]`) ?.closest('.entry') ?.querySelector('.dropdown details .dropdown-content'); if (!dropdownContent) { debug && console.debug('Dropdown not found in the deck page'); return; } dropdownContent.style = "bottom: 0;" + dropdownContent.style; details.appendChild(dropdownContent); }) .catch((err) => { console.error('An error has occurred.', err); }); } })();