您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add the ability to replay BOTH of the pitch accents for the word you're being tested on. Use this to get a better understanding of how the difference should sound, but please be sure to disable this before uploading screenshots of your progress.
// ==UserScript== // @name kotu.io Pitch Accent Replay // @namespace http://tampermonkey.net/ // @version 0.3 // @description Add the ability to replay BOTH of the pitch accents for the word you're being tested on. Use this to get a better understanding of how the difference should sound, but please be sure to disable this before uploading screenshots of your progress. // @author You // @match https://kotu.io/tests/pitchAccent/minimalPairs // @icon https://www.google.com/s2/favicons?domain=kotu.io // @grant none // ==/UserScript== (function() { 'use strict'; window.rerolls = 3; let isNakadaka = function(pitchAccent, moraCount) { let stringName = 0 == pitchAccent || pitchAccent == moraCount ? "Heiban / Odaka" : 1 == pitchAccent ? "Atamadaka" : "Nakadaka"; return stringName == "Nakadaka"; // Somewhat silly but I don't feel like untangling the ternary mess that the minified code has } let anyNakadaka = function(body) { let anyNakadaka = false; for(let i = 0; i < body.pairs.length; i++) { let pair = body.pairs[i]; if(isNakadaka(pair.pitchAccent, pair.entries[0].moraCount)){ anyNakadaka = true; break; } } return anyNakadaka } let app = document.querySelector("#app-container"); var callback = function(mutationsList) { let buttons = app.querySelectorAll(".btn.btn-primary"); if(window.lastUrls !== undefined) { console.log("Adding audio"); let added = false; for(let i = 0; i < buttons.length; i++) { if(buttons[i].parentNode.children.length == 1 && window.lastUrls.length == buttons.length) { let replay = document.createElement("div"); let baseUrl = "/api/media/nhk/audio/" replay.innerHTML = "<audio controls style=\"width:100%; height:25px;\" src=\""+baseUrl + window.lastUrls[i]+"\"></audio>"; buttons[i].parentNode.appendChild(replay); added = true; } } if(added) window.lastUrls = undefined; } let statisticsArr = [].slice.call(app.querySelectorAll(".text-center")).filter(x=>x.textContent.includes("Statistics")); if(statisticsArr.length > 0) { let statistics = statisticsArr[0]; if(!statistics.textContent.includes("CHEAT MODE")) { statistics.innerHTML = "<div>Statistics</div><div style=\"color:red;\">CHEAT MODE ENABLED</div><div style=\"font-size:12px;\">Disable cheat mode before testing yourself and posting screenshots of this</div>"; } } }; var observer = new MutationObserver(callback); observer.observe(app, { characterDataOldValue: true, subtree: true, childList: true, characterData: true }); window.lastUrls = undefined; const {fetch: origFetch} = window; window.fetch = async (...args) => { let rerollCount = 1; if(args[0] == "/api/tests/pitchAccent/minimalPairs/random") { rerollCount += window.rerolls; } let fetches = []; for(let i = 0; i < rerollCount; i++) { fetches.push(origFetch(...args)); } const responses = await Promise.all(fetches); let response = responses[0]; if(rerollCount > 1) { let jsoned = await Promise.all(responses.map(x=>x.clone().json())); for(let i = 0; i < jsoned.length; i++) { let body = jsoned[i]; if(anyNakadaka(body)){ response = responses[i]; // Replace response with one that is nakadaka to favor nakadaka since it shows up less often break; } } } response .clone() .json() .then(body => { if(args[0] == "/api/tests/pitchAccent/minimalPairs/random") { console.log("intercepted response:", body) window.lastUrls = body.pairs.map(x=>x.soundFile); callback(null); } }) .catch(err => console.error(err)); return response; }; console.log("Added modified fetch"); })();