auto-run-script-for-every-task

try to take over the world!

// ==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)
  }
})();