您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds helpful methods for dealing with Turbo Events to WaniKani Open Framework
当前为
// ==UserScript== // @name Wanikani Open Framework Turbo Events // @namespace https://gf.qytechs.cn/en/users/11878 // @description Adds helpful methods for dealing with Turbo Events to WaniKani Open Framework // @version 1.0.0 // @match https://www.wanikani.com/* // @match https://preview.wanikani.com/* // @author Inserio // @copyright 2024, Brian Shenk // @license MIT; http://opensource.org/licenses/MIT // @run-at document-start // @grant none // ==/UserScript== /* global wkof */ /* jshint esversion: 11 */ (function() { 'use strict'; // https://turbo.hotwired.dev/reference/events const turboEvents = Object.freeze({ click: {source: 'document', name: 'turbo:click'}, before_visit: {source: 'document', name: 'turbo:before-visit'}, visit: {source: 'document', name: 'turbo:visit'}, before_cache: {source: 'document', name: 'turbo:before-cache'}, before_render: {source: 'document', name: 'turbo:before-render'}, render: {source: 'document', name: 'turbo:render'}, load: {source: 'document', name: 'turbo:load'}, morph: {source: 'pageRefresh', name: 'turbo:morph'}, before_morph_element: {source: 'pageRefresh', name: 'turbo:before-morph-element'}, before_morph_attribute: {source: 'pageRefresh', name: 'turbo:before-morph-attribute'}, morph_element: {source: 'pageRefresh', name: 'turbo:morph-element'}, submit_start: {source: 'forms', name: 'turbo:submit-start'}, submit_end: {source: 'forms', name: 'turbo:submit-end'}, before_frame_render: {source: 'frames', name: 'turbo:before-frame-render'}, frame_render: {source: 'frames', name: 'turbo:frame-render'}, frame_load: {source: 'frames', name: 'turbo:frame-load'}, frame_missing: {source: 'frames', name: 'turbo:frame-missing'}, before_stream_render: {source: 'streams', name: 'turbo:before-stream-render'}, before_fetch_request: {source: 'httpRequests', name: 'turbo:before-fetch-request'}, before_fetch_response: {source: 'httpRequests', name: 'turbo:before-fetch-response'}, before_prefetch: {source: 'httpRequests', name: 'turbo:before-prefetch'}, fetch_request_error: {source: 'httpRequests', name: 'turbo:fetch-request-error'}, }); const eventMap = Object.freeze({ click: function on_click(callback) { return onEvent(turboEvents.click.name, callback); }, before_visit: function on_before_visit(callback) { return onEvent(turboEvents.before_visit.name, callback); }, visit: function on_visit(callback) { return onEvent(turboEvents.visit.name, callback); }, before_cache: function on_before_cache(callback) { return onEvent(turboEvents.before_cache.name, callback); }, before_render: function on_before_render(callback) { return onEvent(turboEvents.before_render.name, callback); }, render: function on_render(callback) { return onEvent(turboEvents.render.name, callback); }, load: function on_load(callback) { return onEvent(turboEvents.load.name, callback); }, morph: function on_morph(callback) { return onEvent(turboEvents.morph.name, callback); }, before_morph_element: function on_before_morph_element(callback) { return onEvent(turboEvents.before_morph_element.name, callback); }, before_morph_attribute: function on_before_morph_attribute(callback) { return onEvent(turboEvents.before_morph_attribute.name, callback); }, morph_element: function on_morph_element(callback) { return onEvent(turboEvents.morph_element.name, callback); }, submit_start: function on_submit_start(callback) { return onEvent(turboEvents.submit_start.name, callback); }, submit_end: function on_submit_end(callback) { return onEvent(turboEvents.submit_end.name, callback); }, before_frame_render: function on_before_frame_render(callback) { return onEvent(turboEvents.before_frame_render.name, callback); }, frame_render: function on_frame_render(callback) { return onEvent(turboEvents.frame_render.name, callback); }, frame_load: function on_frame_load(callback) { return onEvent(turboEvents.frame_load.name, callback); }, frame_missing: function on_frame_missing(callback) { return onEvent(turboEvents.frame_missing.name, callback); }, before_stream_render: function on_before_stream_render(callback) { return onEvent(turboEvents.before_stream_render.name, callback); }, before_fetch_request: function on_before_fetch_request(callback) { return onEvent(turboEvents.before_fetch_request.name, callback); }, before_fetch_response: function on_before_fetch_response(callback) { return onEvent(turboEvents.before_fetch_response.name, callback); }, before_prefetch: function on_before_prefetch(callback) { return onEvent(turboEvents.before_prefetch.name, callback); }, fetch_request_error: function on_fetch_request_error(callback) { return onEvent(turboEvents.fetch_request_error.name, callback); }, }); const publishedInterface= { on_page_event: onPageEvent, remove_event_handler: removeEventHandler, on: eventMap, events: turboEvents, }; let lastUrlLoaded = document.URL; //------------------------------ // Add handlers for all events. //------------------------------ let event_handlers = {}; function onEvent(eventName, handler) { if (!event_handlers[eventName]) event_handlers[eventName] = new Set(); if (event_handlers[eventName].size === 0) { const eventListener = event => handleEvent(eventName, event); document.documentElement.addEventListener(eventName, eventListener); event_handlers[eventName].add(eventListener); } event_handlers[eventName].add(handler); return handler; } function removeEventHandler(eventName, handler) { const eventHandlers = event_handlers[eventName]; if (eventHandlers) { eventHandlers.delete(handler); if (eventHandlers.size === 1) { document.documentElement.removeEventListener(eventName, eventHandlers[0]); eventHandlers.clear(); } } } //------------------------------ // Call event handlers. //------------------------------ function handleEvent(eventName, event) { const handlers = event_handlers[eventName]; let firstElement = true; for (const handler of handlers) { if (firstElement) { firstElement = false; continue; } if (typeof handler === 'function') handler(event); } } //------------------------------ // Add handlers for page events for a list of URLs. //------------------------------ let page_handlers = []; function onPageEvent(handler) { if (!Array.isArray(handler.urls)) handler.urls = [handler.urls]; if (!Array.isArray(handler.events)) handler.events = [handler.events]; handler.urls = handler.urls.map((url) => { if (url instanceof RegExp) return url; if (typeof url !== 'string') return null; return new RegExp(url.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replaceAll('*','.*')); }).filter(url => url !== null); handler.events = handler.events.map((event) => { if (typeof event === 'string') return event; if (typeof event === 'object' && event.name) return event.name; return null; }).filter(event => event !== null); page_handlers.push(handler); if (handler.events.includes('load')) { handler.urls.forEach(url => { if (!url.test(lastUrlLoaded)) return; handler.callback(); }); } } //------------------------------ // Call page event handlers. //------------------------------ async function handlePageEvents(event_name, event, new_page_url) { page_handlers.forEach(handler => { if (!handler.urls.find(url => url.test(new_page_url))) return; if ((handler.events.length === 0 || handler.events.includes(event_name)) && typeof handler.callback === 'function') handler.callback(event); }); } function addTurboEvents() { wkof.turbo = publishedInterface; document.documentElement.addEventListener(turboEvents.click.name, async event => { lastUrlLoaded = event.detail.url; await handlePageEvents(turboEvents.click.name, event, lastUrlLoaded); }); document.documentElement.addEventListener(turboEvents.before_visit.name, async event => { lastUrlLoaded = event.detail.url; await handlePageEvents(turboEvents.before_visit.name, event, lastUrlLoaded); }); document.documentElement.addEventListener(turboEvents.visit.name, async event => { lastUrlLoaded = event.detail.url; await handlePageEvents(turboEvents.visit.name, event, lastUrlLoaded); }); document.documentElement.addEventListener(turboEvents.before_cache.name, async event => { lastUrlLoaded = document.URL; await handlePageEvents(turboEvents.visit.name, event, lastUrlLoaded); }); document.documentElement.addEventListener(turboEvents.before_render.name, async event => { lastUrlLoaded = document.URL; let observer = new MutationObserver(async m => { if (relevantRootElementChildren(m[0].target).length > 0) return; observer.disconnect(); observer = null; await handlePageEvents(turboEvents.before_render.name, event, lastUrlLoaded); }); observer.observe(event.detail.newBody, {childList: true}); }); document.documentElement.addEventListener(turboEvents.load.name, async event => { lastUrlLoaded = event.detail.url; await handlePageEvents(turboEvents.load.name, event, lastUrlLoaded); }); } // it seems like Turbo does not move the SVG element into document.body, so let's ignore it function relevantRootElementChildren(rootElement) { return [...rootElement?.children ?? []].filter(c => c.tagName !== `svg`); } function startup() { if (!window.wkof) { const response = confirm('WaniKani Open Framework Additional Filters requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.'); if (response) window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549'; return; } wkof.ready('wkof') .then(addTurboEvents) .then(turboEventsReady); } function turboEventsReady() { wkof.set_state('wkof.TurboEvents', 'ready'); } startup(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址