您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a progress bar during jpdb reviews
// ==UserScript== // @name Jpdb percentage bar // @namespace http://tampermonkey.net/ // @version 0.4.2 // @description Adds a progress bar during jpdb reviews // @author Calonca // @match https://jpdb.io/review // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @require http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @license GPLv2 // @namespace https://gf.qytechs.cn/users/956173-calonca // ==/UserScript== let lightStyleSheet = ` .innerBar { width: 0%; height: 35px; background-color: #7baee9; text-align: center; /* To center it horizontally (if you want) */ line-height: 35px; /* To center it vertically */ color: white; } .outerBar { width: 100%; font-family: "Nunito Sans","Extra Sans JP","Noto Sans Symbols2","Segoe UI","Noto Sans JP","Noto Sans CJK JP","Hiragino Sans GB","Meiryo",sans-serif; /*width: 100vw; position: relative; left: calc(-50vw + 50%);*/ background-color: #a2a2a2; text-align: center; /* To center it horizontally (if you want) */ } /* Modal Content */ .modal-content { background-color: #ffffff; margin: auto; padding: 20px; border: 1px solid #888; width: 80%; } `; let darkStyleSheet = ` .innerBar { width: 0%; height: 35px; background-color: #3a76bf;/*#3266bf;*/ text-align: center; /* To center it horizontally (if you want) */ line-height: 35px; /* To center it vertically */ color: white; } .outerBar { width: 100%; font-family: "Nunito Sans","Extra Sans JP","Noto Sans Symbols2","Segoe UI","Noto Sans JP","Noto Sans CJK JP","Hiragino Sans GB","Meiryo",sans-serif; /*width: 100vw; position: relative; left: calc(-50vw + 50%);*/ background-color: #515151;/*#858585;*/ text-align: center; /* To center it horizontally (if you want) */ } /* Modal Content */ .modal-content { background-color: #181818; margin: auto; padding: 20px; border: 1px solid #888; width: 80%; } `; const styleSheet = ` /* The Close Button */ .close { color: #aaaaaa; float: right; font-size: 35px; font-weight: bold; } .close:hover, .close:focus { color: #000; text-decoration: none; cursor: pointer; } .modal { display: none; /* Hidden by default */ position: fixed; /* Stay in place */ z-index: 1; /* Sit on top */ padding-top: 100px; /* Location of the box */ left: 0; top: 0; width: 100%; /* Full width */ height: 100%; /* Full height */ overflow: auto; /* Enable scroll if needed */ background-color: rgb(0,0,0); /* Fallback color */ background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ } ` const modalText = ` <div id="myModal" class="modal"> <!-- Modal content --> <div class="modal-content"> <span id="close-settings" class="close">×</span> <h4 style="margin-top: 0;">Progress bar Options</h4> <h6 style="margin-top: 0;">Bar position</h6> <div style="margin-left: 1rem;"> <div class="checkbox"><input type="radio" id="top-radio" name="pos" value="top"/> <label for="top-radio">Top</label></div> <div class="checkbox"><input type="radio" id="middle-radio" name="pos" value="middle" /> <label for="middle-radio">Middle</label></div> <div class="checkbox"><input type="radio" id="bottom-radio" name="pos" value="bottom" /> <label for="bottom-radio">Bottom</label></div> </div> <h6 style="margin-top: 0;">Reset progress bar</h6> <button type="button" id="reset-btn" style="margin-left: 1rem;margin-bottom: 1rem;">Reset</button> </br> <div class="modal-footer" style=" border-top: 1px solid var(--answer-box-color); display: flex; justify-content: center; padding-top: 1.4rem;"> <button type="button" id="save-btn">Save changes</button> </div> </div> </div>`; const doneNumKey = "revsDone"; const posKey = "position"; let style = document.createElement("style"); style.type = "text/css"; //Get theme if (document.getElementsByClassName("dark-mode")[0]){ style.innerHTML = styleSheet + darkStyleSheet; } else{ style.innerHTML = styleSheet + lightStyleSheet; } (document.head || document.documentElement).appendChild(style); const debug = false; (function() { 'use strict'; if (GM_getValue(doneNumKey)==null){ //Set values in first script usage GM_setValue(doneNumKey,0) } if (GM_getValue(posKey)==null){ //Set values in first script usage GM_setValue(posKey,"middle") } function getPercentage(done, remaining){ if (debug) return "50%"; return (100*(done/(Number(done)+Number(remaining)))).toFixed(2)+"%"; } function getDoneAndRemainingString(done, remaining){ return " done: "+done+" | "+"remaining: "+remaining; } //Progress bar let outerBar = document.createElement("div"); outerBar.className = "outerBar"; let innerBar = document.createElement("div"); innerBar.className = "innerBar"; outerBar.appendChild(innerBar); let learnNavItem = document.querySelectorAll(".nav-item")[0] let span = learnNavItem.childNodes[1]; let revNum = span.textContent; outerBar.onclick = onShowOptions; let showAnswerButton = document.getElementById("show-answer"); if (showAnswerButton){ waitForKeyElements ( "#grade-3" , afterShowingGradeButtons ); } //Closes the modal if the user clicks outside of it window.onclick = function(event) { if (event.target == modal) { modal.style.display = "none"; } } //Add elements to the document updateBar(); if (document.getElementsByClassName("main-row")[0]){//Doing review addBar(); var modal = htmlToElement(modalText); document.getElementsByClassName("main-row")[0].appendChild(modal); }else {//Continue or finish screen let parent = document.getElementsByClassName("container bugfix")[0]; let importantText = document.getElementsByTagName('h5')[0] if (importantText.innerHTML == "Good job! You've finished all of your due cards!"){//Finish screen let resetText = document.createElement("h5"); resetText.innerHTML = "Progress bar has been reset"; parent.insertBefore(resetText,parent.childNodes[1]); GM_setValue(doneNumKey,0); } } function updateBar(){ let percentage = getPercentage(GM_getValue(doneNumKey),revNum); innerBar.style.width = percentage; innerBar.innerHTML = getDoneAndRemainingString(GM_getValue(doneNumKey),revNum)+" | "+percentage; } //Reset reviews count function reset(){ modal.style.display = "none"; GM_setValue(doneNumKey,0); updateBar(); } //Saves the bar position and moves it function saveSettings (){ let barPos = document.querySelector('input[name="pos"]:checked').value; modal.style.display = "none"; if (barPos == null || barPos!=GM_getValue(posKey)){ GM_setValue(posKey,barPos); outerBar.parentNode.removeChild(outerBar); addBar(); } } /** * @param {String} HTML representing a single element * @return {Element} */ function htmlToElement(html) { var template = document.createElement('template'); html = html.trim(); // Never return a text node of whitespace as the result template.innerHTML = html; return template.content.firstChild; } //Shows the modal function onShowOptions() { let el = document.getElementById(GM_getValue(posKey)+"-radio") el.checked=true; modal.style.display = "block"; document.getElementById('save-btn').onclick = saveSettings; document.getElementById("reset-btn").onclick = reset; document.getElementById("close-settings").onclick = ()=>{modal.style.display = "none"}; } //Increase done review count on button click function increaseDone() { GM_setValue(doneNumKey,GM_getValue(doneNumKey)+1); } //Add behaviour to grading buttons function afterShowingGradeButtons(){ outerBar.onclick = null; document.getElementById("grade-3").addEventListener ("click", increaseDone , false); document.getElementById("grade-4").addEventListener ("click", increaseDone , false); document.getElementById("grade-5").addEventListener ("click", increaseDone , false); } function addBar(){ let pos = GM_getValue(posKey); if (pos=='middle' || pos==null){ let elementAfter = document.getElementsByClassName("main-row")[0]; elementAfter.parentElement.insertBefore(outerBar,elementAfter); } else if (pos=='top'){ let elementAfter = document.getElementsByClassName("nav minimal")[0]; //let elementAfter = document.getElementsByClassName("container bugfix")[0]; elementAfter.parentElement.insertBefore(outerBar,elementAfter); } else if (pos=='bottom'){ let mainRow = document.getElementsByClassName("main-row")[0]; mainRow.parentElement.appendChild(outerBar); } } })(); //The following function is used by the script. //I Put it here instead of requires due to GreasyFolk rules, the original can be found at https://gist.github.com/BrockA/2625891 /*--- waitForKeyElements(): A utility function, for Greasemonkey scripts, that detects and handles AJAXed content. Usage example: waitForKeyElements ( "div.comments" , commentCallbackFunction ); //--- Page-specific function to do what we want when the node is found. function commentCallbackFunction (jNode) { jNode.text ("This comment changed by waitForKeyElements()."); } IMPORTANT: This function requires your script to have loaded jQuery. */ function waitForKeyElements ( selectorTxt, /* Required: The jQuery selector string that specifies the desired element(s). */ actionFunction, /* Required: The code to run when elements are found. It is passed a jNode to the matched element. */ bWaitOnce, /* Optional: If false, will continue to scan for new elements even after the first match is found. */ iframeSelector /* Optional: If set, identifies the iframe to search. */ ) { var targetNodes, btargetsFound; if (typeof iframeSelector == "undefined") targetNodes = $(selectorTxt); else targetNodes = $(iframeSelector).contents () .find (selectorTxt); if (targetNodes && targetNodes.length > 0) { btargetsFound = true; /*--- Found target node(s). Go through each and act if they are new. */ targetNodes.each ( function () { var jThis = $(this); var alreadyFound = jThis.data ('alreadyFound') || false; if (!alreadyFound) { //--- Call the payload function. var cancelFound = actionFunction (jThis); if (cancelFound) btargetsFound = false; else jThis.data ('alreadyFound', true); } } ); } else { btargetsFound = false; } //--- Get the timer-control variable for this selector. var controlObj = waitForKeyElements.controlObj || {}; var controlKey = selectorTxt.replace (/[^\w]/g, "_"); var timeControl = controlObj [controlKey]; //--- Now set or clear the timer as appropriate. if (btargetsFound && bWaitOnce && timeControl) { //--- The only condition where we need to clear the timer. clearInterval (timeControl); delete controlObj [controlKey] } else { //--- Set a timer, if needed. if ( ! timeControl) { timeControl = setInterval ( function () { waitForKeyElements ( selectorTxt, actionFunction, bWaitOnce, iframeSelector ); }, 300 ); controlObj [controlKey] = timeControl; } } waitForKeyElements.controlObj = controlObj; }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址