Greasy Fork

来自缓存

Greasy Fork is available in English.

yc-直接跳转文本链接

识别并直接跳转普通文本链接

当前为 2024-07-10 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         yc-直接跳转文本链接
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  识别并直接跳转普通文本链接
// @author       wcbblll
// @match        *://*/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
  'use strict';

  // 移除重复项
  function removeDuplicates(arr) {
    return arr.filter((item, index, self) => self.indexOf(item) === index);
  }

  // 从后往前遍历数组

  function traverseArrayBackward(arr, callback) {
    for (let i = arr.length - 1; i >= 0; i--) {
      callback(arr[i], i, i === 0);
    }
  }

  // 文本全局匹配链接返回数组,数组里面是字符串
  function getTextLinks(text) {
    const linkRegex = /http[s]?:\/\/[^\s\/$.?#].[^\s]*/g;
    const links = text.match(linkRegex);
    return links || [];
  }

  // 文本全局匹配链接返回数组,数组里面是对象
  function getTextLinksList(text) {
    const linkRegex = /http[s]?:\/\/[^\s\/$.?#].[^\s]*/g;
    const matches = text.matchAll(linkRegex);
    const matchArr = []
    for (const match of matches) {
      matchArr.push(match)
    }
    return matchArr
  }

  /**
   * Splits a given text into an array of objects, where each object represents a segment of the text.
   *
   * @param {string} text - The text to be split.
   * @param {Array<Object>} arr - An array of objects, where each object contains a link and its index in the text.
   * @return {Array<Object>} An array of objects, where each object represents a segment of the text.
   */
  function splitText(text, arr) {
    if (!arr.length) return [{ text, type: 'text' }]
    let lastIndex = 0
    let returnArr = []
    arr.forEach((item, i) => {
      const link = item[0]
      const textObj = { text: text.slice(lastIndex, item.index), type: 'text' }
      const linkObj = { text: link, type: 'link' }
      returnArr.push(textObj, linkObj)
      lastIndex = item.index + link.length
      if (i === arr.length - 1 && lastIndex < text.length) {
        returnArr.push({ text: text.slice(lastIndex), type: 'text' })
      }
    })
    return returnArr
  }

  // 根据链接地址创建a标签
  function createLink(link) {
    const a = document.createElement("a");
    a.href = link;
    a.textContent = link;
    a.target = "_blank"; // 可选:在新标签页打开链接
    a.rel = 'noopener noreferrer nofollow'
    return a;
  }

  // 根据文字创建文本标签
  function createTextNode(text) {
    const textNode = document.createTextNode(text);
    return textNode;
  }

  // 根据类型返回不同的创建标签方法
  function createNode(type, text) {
    switch (type) {
      case 'link':
        return createLink(text);
      case 'text':
        return createTextNode(text);
      default:
        throw new Error('Invalid type');
    }
  }

  /*
{
  match: {
      node:[]
  }
}
*/
  function createTextNodeTree(matchObj = {}, node, parent) {
    if (node.nodeType === 3 && parent.nodeName !== "A") {
      const textContent = node.textContent;
      let matches = getTextLinks(textContent)
      matches.forEach((match) => {
        if (matchObj[match] && matchObj[match].length > 0) {
          matchObj[match].push(node)
        } else {
          matchObj[match] = [node]
        }
      });
    }

    for (let child of node.childNodes) {
      createTextNodeTree(matchObj, child, node)
    }

    return matchObj;
  }
  function callback(mutationsList, observer) {
    if (lastExecutionTime + delay < Date.now()) {
      const matchObj = createTextNodeTree({}, document.body, null);
      for (const match in matchObj) {
        if (Object.hasOwnProperty.call(matchObj, match)) {
          const nodeList = removeDuplicates(matchObj[match]);
          nodeList.forEach((node) => {
            const generateNodeList = splitText(node.textContent, getTextLinksList(node.textContent));
            traverseArrayBackward(generateNodeList, ({ type, text }, i, isLast) => {
              try {
                isLast ? node.parentNode.replaceChild(createNode(type, text), node) :
                  node.nextSibling ?
                    node.parentNode.insertBefore(createNode(type, text), node.nextSibling) :
                    node.parentNode.appendChild(createNode(type, text));
              } catch (error) { }
            })
          })
        }
      }
      lastExecutionTime = Date.now();
    }
  }

  let observer = new MutationObserver(callback);

  let delay = 500; // 间隔时间,单位毫秒
  let lastExecutionTime = 0;

  observer.observe(document.body, { childList: true, attributes: true });
})();