auto-run-script-for-every-task

try to take over the world!

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         auto-run-script-for-every-task
// @namespace    https://x181.cn
// @version      0.1
// @description  try to take over the world!
// @author       tt
// @match        https://*/*
// @match        http://*/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
  'use strict';

  // next: 'once'
  const scripts = [
    {
      match: /https:\/\/mp\.weixin\.qq\.com\/s.*/,
      script: weixinScriptTask,
      once: stopImmediatePropagation,
    },
    {
      match: 'http://uz.yurixu.com/',
      script: yurixuTask,
      interval: 0.2
    },
    {
      match: 'https://www.flickr.com/photos/',
      script: removeFlickerZoomLayerTask,
    },
    {
      match: 'https://helloacm.com/',
      script: removeRemovedAdBlockerModal
    },
    {
      match: 'link.zhihu.com',
      script: zhihuAutoRedirectTask,
      interval: 'once'
    },
    {
      match: /^https?:\/\/.*?\.zhihu\.com/,
      script: zhihuConvertLinkTask,
    },
    {
      match: /t\.cn/i,
      script: weiboLinkTask,
    }
  ]

  function weiboLinkTask() {
    let node = document.querySelector('.link')
    let text = node.innerText.trim()
    if (/https?:\/\//i.test(text)) {
      location.href = text
      return;
    }
  }

  function zhihuConvertLinkTask() {
    let links = document.querySelectorAll('a[href^="https://link.zhihu.com/"], a[href^="http://link.zhihu.com/"]')
    Array.from(links).forEach(node => {
      let href = node.href
      let url = new URL(href)
      let search = new URLSearchParams(url.search)
      let target = search.get('target')
      node.href = decodeURIComponent(target)
    })
  }

  function zhihuAutoRedirectTask() {
    if (location.host === 'link.zhihu.com') {
      let search = new URLSearchParams(location.search)
      let target = search.get('target')
      location.href = decodeURIComponent(target)
    }
  }

  function removeRemovedAdBlockerModal() {
    let nodes = document.querySelectorAll('cloudflare-app')
    Array.from(nodes).forEach(node => {
      let style = window.getComputedStyle(node, null)
      let zIndex = style.getPropertyValue('z-index') // style.zIndex
      if (zIndex >= 400) {
        node.style.display = 'none'
      }
    })
  }

  function removeFlickerZoomLayerTask() {
    let selectors = [
      '.facade-of-protection-zoom'
    ]
    selectors.forEach(selector => {
      let nodes = document.querySelectorAll(selector)
      nodes.forEach(node => {
        node.style.display = 'none';
      })
    })
  }

  // 微信公众号文章支持超链接
  function weixinScriptTask() {
    if (document.body == null) {
      return sleep(0.2).then(() => {
        return nextTick(weixinScriptTask)
      })
    }
    let walker = document.createTreeWalker(
      document.body,
      NodeFilter.SHOW_TEXT,
      {
        acceptNode: node => {
          let p = node.parentNode
          while(p && (['A', 'SCRIPT', 'STYLE'].indexOf(p.nodeName) === -1)) {
            p = p.parentNode
          }
          if (p) return NodeFilter.FILTER_SKIP
          let text = node.data
          if (/https?:\/\//i.test(text)) {
            return NodeFilter.FILTER_ACCEPT
          }
        }
      },
      true
    )

    let links = []
    while(walker.nextNode()) {
      let node = walker.currentNode
      let text = node.data.toLowerCase()
      let offset = text.indexOf('http')
      let linkTextNode = node.splitText(offset)
      let spaceOffset = linkTextNode.nodeValue.search(/\s/)
      let linkNode = linkTextNode
      if (spaceOffset > -1) {
        linkNode =linkTextNode.splitText(spaceOffset).previousSibling
      }
      let a = document.createElement('a')
      a.href = linkNode.nodeValue
      a.setAttribute('target', '_blank')
      linkNode.parentNode.insertBefore(a, linkNode)
      a.appendChild(linkNode)
      links.push(a)
    }
    if (links.length) {
      suptolink(links)
    }
  }

  function suptolink(links) {
    let sups = document.querySelectorAll('sup');
    let lastFindedNode = null;
    [...sups].reverse().forEach(sup => {
      let text = sup.innerText.trim()
      if (text === '') return
      // Todo. 判定是 [1] 形式.
      let link = findLinkByText(text, lastFindedNode || links[links.length - 1])
      if (link == null) return
      console.log('find.end:', link)
      lastFindedNode = link
      let a = document.createElement('a')
      a.href = link.href
      a.setAttribute('target', '_blank')
      sup.parentNode.insertBefore(a, sup)
      a.appendChild(sup)
    })
  }

  function findLinkByText(text, link) {
    let find
    let node = link.previousSibling || link.parentNode
    while(node && (node.nodeType == 1 || node.nodeType === 3)) {
      let t = node.innerText || node.nodeValue || ''
      if (node.nodeType === 1) {
        if (node.nodeName === 'A') link = node
        else {
          let a = node.querySelector('a')
          if (a) link = a
        }
      }
      if (t.indexOf(text) > -1) {
        find = link
        break
      }
      node = node.previousSibling || node.parentNode
    }
    return find
  }

  function stopImmediatePropagation() {
    ['click'].forEach(name => {
      document.addEventListener(name, function (e) {
        e.stopImmediatePropagation()
      }, true)
    })
  }

  // 租房
  function yurixuTask() {
    var links = document.querySelectorAll('a');
    [...links].forEach(link => {
      if (link.protocol === 'http:' && link.host === 'www.newsmth.net') {
        link.href = link.href.replace(/^http:/i, 'https:')
      }
    })
  }

  async function run(task, n = 0) {
    console.log('start run task = ', task.name || 'an')
    if ('function' === typeof task.once) {
      task.once(task)
      task.once = null
    }
    if ('function' === typeof task.prescript) task.prescript(task)
    if ('function' === typeof task.script) task.script(task)
    if ('function' === typeof task.postscript) task.postscript(task)

    let interval = task.interval
    if (interval === 'once') return
    // 默认策略
    if (interval === 'exponential' || interval == null) {
      await sleep(Math.pow(2, n))
      run(task, ++n)
      return
    }
    if (typeof interval === 'number') {
      await sleep(interval)
      run(task, ++n)
      return
    }
    if (typeof interval === 'function') {
      interval(() => run(task))
      return
    }
  }

  function init() {
    let url = location.href
    let tasks = scripts.filter(task => match(task.match, url))
    for (let i = 0; i < tasks.length; ++i) {
      run(tasks[i])
    }
  }

  init()

  function sleep(n) {
    return new Promise(function (resolve) {
      setTimeout(resolve, n * 1000)
    })
  }

  function match(rule, url) {
    if (typeof rule === 'boolean') return rule
    if (typeof rule === 'string') return url.includes(rule)
    if (rule.test) return rule.test(url)
    if (rule.sort) return rule.every(m => match(m, url))
    if (typeof rule === 'function') return rule(url)
    return false
  }

  function nextTick(fn) {
    let p = Promise.resolve()
    p.then(fn)
  }
})();