您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
替换 GitHub issues 的默认过滤器
// ==UserScript== // @name GitHub Default Issues Filter // @name:zh-CN GitHub Issues 默认过滤器 // @description Replaces GitHub issue's default filter // @description:zh-CN 替换 GitHub issues 的默认过滤器 // @version 0.1 // @author guansss // @namespace https://github.com/guansss // @source https://github.com/guansss/userscripts // @supportURL https://github.com/guansss/userscripts/issues // @match *://github.com/* // @run-at document-start // @grant GM_info // @license MIT // @noframes // ==/UserScript== function _script_main() { 'use strict'; const ready = new Promise((resolve) => { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => resolve); } else { resolve(); } }); function hookMethod(object, method, callback) { const original = object[method]; object[method] = function () { callback.apply(this, arguments); return original.apply(this, arguments); }; } let log; setLogger(console.log); function setLogger(logger) { log = logger.bind(console, `[${GM_info.script.name}]`); } /** * Periodically calls given function until it returns true. */ function repeat(fn, interval = 200) { if (fn()) { return 0; } const id = setInterval(() => { try { fn() && clearInterval(id); } catch (e) { log(e); clearInterval(id); } }, interval); return id; } /** * Periodically calls given function until the return value is truthy. * @returns A CancelablePromise that resolves with the function's return value when truthy. */ function until(fn, interval = 0) { let cancelled = false; const promise = new Promise((resolve, reject) => repeat(() => { if (cancelled) { return true; } try { const result = fn(); if (result) { resolve(result); // break the repeat() loop return true; } } catch (e) { reject(e); return true; } }, interval) ); promise.cancel = () => (cancelled = true); return promise; } const urlRegex = /.*issues\/?$/; const defaultFilters = 'is:issue is:open '; const targetFilters = 'is:issue'; let currentTask; function cancelCurrentTask() { currentTask && currentTask.cancel(); } function check(url) { if (urlRegex.test(url)) { cancelCurrentTask(); currentTask = until(() => { const input = document.getElementById('js-issues-search'); if (input && input.parentElement && input.parentElement.tagName === 'FORM') { // when navigating from pull requests to issues, the input is persisted until navigation completes, // so we should ensure the input value is as expected if (input.value === defaultFilters) { input.value = targetFilters; input.parentElement.dispatchEvent(new SubmitEvent('submit', { bubbles: true })); return true; } // cancel current task whenever the input is changed by user, usually this won't happen // since the interval is very short, but just in case the user is Shining Finger input.addEventListener('input', cancelCurrentTask, { once: true }); } }, 100); } } const handleStateChange = (data, unused, url) => { if (url instanceof URL) { url = url.href; } if (url) { check(url); } }; hookMethod(history, 'pushState', handleStateChange); hookMethod(history, 'replaceState', handleStateChange); ready.then(() => check(location.href)); } _script_main();