您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Based on Linkify Plus. Turn plain text URLs into links.
当前为
// ==UserScript== // @name Linkify Plus Plus // @version 8.2.1 // @description Based on Linkify Plus. Turn plain text URLs into links. // @license BSD-3-Clause // @homepageURL https://github.com/eight04/linkify-plus-plus // @supportURL https://github.com/eight04/linkify-plus-plus/issues // @namespace eight04.blogspot.com // @include * // @exclude https://www.google.*/search* // @exclude https://www.google.*/webhp* // @exclude https://music.google.com/* // @exclude https://mail.google.com/* // @exclude https://docs.google.com/* // @exclude https://encrypted.google.com/* // @exclude http://mxr.mozilla.org/* // @exclude http://w3c*.github.io/* // @require https://gf.qytechs.cn/scripts/7212-gm-config-eight-s-version/code/GM_config%20(eight's%20version).js?version=156587 // @require https://gf.qytechs.cn/scripts/27630-linkify-plus-plus-core/code/linkify-plus-plus-core.js?version=213494 // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @grant unsafeWindow // @compatible firefox Tampermonkey latest // @compatible chrome Tampermonkey latest // ==/UserScript== /* globals GM_config */ (function(){ // Limit contentType to "text/plain" or "text/html" if (document.contentType != undefined && document.contentType != "text/plain" && document.contentType != "text/html") { return; } var {linkify, UrlMatcher, INVALID_TAGS} = window.linkifyPlusPlusCore; const BUFFER_SIZE = 100; // Valid root node before linkifing function validRoot(node, validator) { // Cache valid state in node.VALID if (node.VALID !== undefined) { return node.VALID; } // Loop through ancestor var cache = [], isValid; while (node != document.documentElement) { cache.push(node); // It is invalid if it has invalid ancestor if (!validator(node) || INVALID_TAGS[node.nodeName]) { isValid = false; break; } // The node was removed from DOM tree if (!node.parentNode) { return false; } node = node.parentNode; if (node.VALID !== undefined) { isValid = node.VALID; break; } } // All ancestors are fine if (isValid === undefined) { isValid = true; } // Cache the result var i; for (i = 0; i < cache.length; i++) { cache[i].VALID = isValid; } return isValid; } function createValidator({selector, skipSelector}) { return function(node) { if (node.isContentEditable) { return false; } if (selector && node.matches && node.matches(selector)) { return true; } if (skipSelector && node.matches && node.matches(skipSelector)) { return false; } return true; }; } function selectorTest(selector) { try { document.documentElement.matches(selector); } catch (err) { alert(`Invalid selector: ${selector}`); return false; } return true; } function createList(text) { text = text.trim(); if (!text) { return null; } return text.split("\n"); } function createBuffer(size) { const set = new Set; const buff = Array(size); const eventBus = document.createElement("span"); let start = 0; let end = 0; return {push, eventBus, shift}; function push(item) { if (set.has(item)) { return; } if (set.size && start === end) { // overflow eventBus.dispatchEvent(new CustomEvent("overflow")); set.clear(); return; } set.add(item); buff[end] = item; end = (end + 1) % size; eventBus.dispatchEvent(new CustomEvent("add")); } function shift() { if (!set.size) { return; } const item = buff[start]; set.delete(item); buff[start] = null; start = (start + 1) % size; return item; } } function createLinkifyProcess(options) { const buffer = createBuffer(BUFFER_SIZE); let overflowed = false; let started = false; buffer.eventBus.addEventListener("add", start); buffer.eventBus.addEventListener("overflow", () => overflowed = true); return {process}; function process(root) { if (overflowed) { return false } if (validRoot(root, options.validator)) { buffer.push(root); } return true; } function start() { if (started) { return; } started = true; deque(); } function deque() { let root; if (overflowed) { root = document.body; overflowed = false; } else { root = buffer.shift(); } if (!root) { started = false; return; } linkify(root, options) .then(() => { var p = Promise.resolve(); if (options.selector) { for (var node of root.querySelectorAll(options.selector)) { p = p.then(linkify.bind(null, node, options)); } } return p; }) .catch(err => { console.error(err); }) .then(deque); } } function createOptions() { const options = {}; setup(); return options; function setup() { GM_config.setup({ ip: { label: "Match 4 digits IP", type: "checkbox", default: true }, image: { label: "Embed images", type: "checkbox", default: true }, imageSkipSelector: { label: "Don't embed images under following elements", type: "textarea", default: ".hljs, .highlight, .brush\\:" }, unicode: { label: "Allow non-ascii character", type: "checkbox", default: false }, newTab: { label: "Open link in new tab", type: "checkbox", default: false }, standalone: { label: "URL must be surrounded by whitespace", type: "checkbox", default: false }, boundaryLeft: { label: "Boundary characters between whitespace and URL (left)", type: "text", default: "{[(\"'" }, boundaryRight: { label: "Boundary characters between whitespace and URL (right)", type: "text", default: "'\")]},.;?!" }, skipSelector: { label: "Do not linkify these elements. (CSS selector)", type: "textarea", default: ".highlight, .editbox, .brush\\:, .bdsug, .spreadsheetinfo" }, selector: { label: "Always linkify these elements, override above. (CSS selector)", type: "textarea", default: "" }, timeout: { label: "Max execution time (ms).", type: "number", default: 10000 }, maxRunTime: { label: "Max script run time (ms). If the script is freezing your browser, try to decrease this value.", type: "number", default: 100 }, customRules: { label: "Custom rules. One pattern per line. (RegExp)", type: "textarea", default: "" } }, function() { Object.assign(options, GM_config.get()); if (options.selector && !selectorTest(options.selector)) { options.selector = null; } if (options.skipSelector && !selectorTest(options.skipSelector)) { options.skipSelector = null; } if (options.customRules) { options.customRules = createList(options.customRules); } options.validator = createValidator(options); options.fuzzyIp = options.ip; options.ignoreMustache = unsafeWindow.angular || unsafeWindow.Vue; options.embedImage = options.image; options.matcher = new UrlMatcher(options); options.onlink = options.imageSkipSelector ? onlink : null; }); } function onlink({link, range, content}) { if (link.childNodes[0].nodeName != "IMG") return; var parent = range.startContainer; // it might be a text node if (!parent.closest) { parent = parent.parentNode; } if (!parent.closest(options.imageSkipSelector)) return; // remove image link.innerHTML = ""; link.appendChild(content); } } // Program init GM_addStyle(".linkifyplus img { max-width: 90%; }"); const linkifyProcess = createLinkifyProcess(createOptions()); const observer = new MutationObserver(function(mutations){ // Filter out mutations generated by LPP var lastRecord = mutations[mutations.length - 1], nodes = lastRecord.addedNodes, i; if (nodes.length >= 2) { for (i = 0; i < 2; i++) { if (nodes[i].className == "linkifyplus") { return; } } } for (var record of mutations) { if (record.addedNodes.length) { if (!linkifyProcess.process(record.target)) { // it's full break; } } } }); function init() { observer.observe(document.body, { childList: true, subtree: true }); linkifyProcess.process(document.body); } function prepare() { // wait till everything is ready return Promise.all([prepareApp(), prepareBody()]); function prepareApp() { const appRoot = document.querySelector("[data-server-rendered]"); if (!appRoot) { return; } return new Promise(resolve => { const onChange = () => { if (!appRoot.hasAttribute("data-server-rendered")) { resolve(); observer.disconnect(); } }; const observer = new MutationObserver(onChange); observer.observe(appRoot, {attributes: true}); }); } function prepareBody() { if (document.body) { return; } return new Promise(resolve => { // https://github.com/Tampermonkey/tampermonkey/issues/485 document.addEventListener("DOMContentLoaded", resolve, {once: true}); }); } } prepare().then(init); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址