Greasy Fork is available in English.

不要翻译github上的代码

避免google网页翻译github站点中的代码

// ==UserScript==
// @name         不要翻译github上的代码
// @namespace    http://floatsyi.com/
// @version      0.5.5
// @description  避免google网页翻译github站点中的代码
// @author       floatsyi
// @license      MIT
// @include      *://github.com*
// @include      *://gist.github.com*
// @match        *://github.com*
// @match        *://gist.github.com*
// ==/UserScript==
/* jshint esversion: 6 */
;(function () {
  'use strict'
  // debug log
  const isDev = false
  const log = (...args) => {
    isDev && console.log(args)
  }
  const style = 'font-size: inherit;padding: 0;'

  const hasCodeEleChild = ele => !!ele.querySelector('code')
  const hasCodeEleParent = ele => ele.parentNode.nodeName === 'CODE'
  const hasPreEleChild = ele => !!ele.querySelector('code')

  function addCodeEle (ele) {
    ele.innerHTML = `<code class="doNotTranslate" style= "${style}">${ele.innerHTML}</code>`
  }

  function addCodeEleParent (ele) {
    ele.parentNode.innerHTML = `<code class="doNotTranslate" style= "${style}">${ele.parentNode.innerHTML}</code>`
  }

  function addPreEle (ele, styles = '') {
    ele.innerHTML = `<pre style= ${style}"><code class="doNotTranslate" style="${style + styles}">${
      ele.innerHTML
    }</code></pre>`
  }


  const _ = {}
  _.debounce = function (func, wait) {
    var lastCallTime
    var lastThis
    var lastArgs
    var timerId

    function startTimer (timerExpired, wait) {
      return setTimeout(timerExpired, wait)
    }

    function remainingWait (time) {
      const timeSinceLastCall = time - lastCallTime
      const timeWaiting = wait - timeSinceLastCall
      return timeWaiting
    }

    function shoudInvoking (time) {
      return lastCallTime !== undefined && time - lastCallTime >= wait
    }

    function timerExpired () {
      const time = Date.now()
      if (shoudInvoking(time)) {
        return invokeFunc()
      }
      timerId = startTimer(timerExpired, remainingWait(time))
    }

    function invokeFunc () {
      timerId = undefined
      const args = lastArgs
      const thisArg = lastThis
      let result = func.apply(thisArg, args)
      lastArgs = lastThis = undefined
      return result
    }

    function debounced (...args) {
      let time = Date.now()
      lastThis = this
      lastArgs = args
      lastCallTime = time
      if (timerId === undefined) {
        timerId = startTimer(timerExpired, wait)
      }
    }

    return debounced
  }

  function doNotTranslateGist () {
   if (window.location.href.match('/gist.github.com/') !== null) {
     const gist = document.querySelectorAll('.file')
     const pres = document.querySelectorAll('pre')
     ;[...gist, ...pres].forEach(it => {
        if (!hasCodeEleChild(it)) addCodeEle(it)
     })
   }
  }

  function doNotTranslateFilenamesAndDirectories () {
    const fileAndDirectory = document.querySelectorAll(
        '.files.js-navigation-container.js-active-navigation-container .content'
    )
    ;[...fileAndDirectory].forEach(item => {
      if (!hasPreEleChild(item)) {
        addPreEle(item, 'display: table')
      }
    })
  }

  function doNotTranslateCodeContentPages () {
    const isCodeContentPages = window.location.href.match('/blob/') !== null
    const isNotMD = window.location.href.match('.md') === null
    if (isCodeContentPages && isNotMD) {
      const main = document.querySelector('main')

      if (!hasCodeEleParent(main)) addCodeEleParent(main)
    }
  }

  function doNotTranslateReferenceCode () {
      // github 会自动给 .sg-mounted 的子节点 code 加 .sg-mounted class 这里要 加个 div 排除出去
    const referenceCodes = document.querySelectorAll('div.border.rounded-1.my-2')
    for (const referenceCode of [...referenceCodes]) {
        if (!hasCodeEleChild(referenceCode)) addCodeEle(referenceCode)
    }
  }

  function doNotTranslateTitle () {
    ;['1', '2', '3', '4', '5', '6'].forEach(item => {
      const itemEle = document.querySelectorAll(`h${item}`)
      // 没找到就退出
      if (!itemEle) return false
      ;[...itemEle].forEach(el => {
        // 如果是 issues 标题就退出
        if (el.classList.contains('gh-header-title')) return false
        if (!hasCodeEleChild(el)) addCodeEle(el)
      })
    })
  }

  function doNotTranslateCode () {
    const files = document.querySelectorAll('.file')
    const pres = document.querySelectorAll('pre')

    if (files.length > 0) {
      if (window.location.href.search(/.md/i) !== -1) {
        if (pres.length > 0) {
          pres.forEach(function (pre) {
            if (!hasCodeEleChild(pre)) addCodeEle(pre)
          })
        }
      } else {
        files.forEach(function (file) {
          if (!hasCodeEleChild(file)) addCodeEle(file)
        })
      }
    } else if (pres.length > 0) {
      pres.forEach(function (pre) {
        if (!hasCodeEleChild(pre)) addCodeEle(pre)
      })
    }
  }

  // 监听DOM变更
  const githubTV = document.querySelector('body')
  const isGitHub =
    window.location.href.search(/github.com/i) !== -1 && !!githubTV

  const option = {
    childList: true,
    subtree: true
  }

  let time = 0

  function doNotTranslate (mutations, observer) {
    // 处于过于频繁的 DOM 变更时, 暂停监听 50ms, 并放弃累积的未处理的变更事件
    if (time >= 20) {
      observer.disconnect()
      observer.takeRecords()
      time = 0
      setTimeout(function () {
        isGitHub && observer.observe(githubTV, option)
      }, 50)
    }
    // 如果是编辑页就退出
    if (window.location.href.match('/edit/') !== null) return false

    // 不要翻译引用代码
    doNotTranslateReferenceCode()
    // 不要翻译文件名与目录
    doNotTranslateFilenamesAndDirectories()
    // 不要翻译代码内容页
    doNotTranslateCodeContentPages()
    // 不要翻译代码
    doNotTranslateCode()
    // 不要翻译标题
    doNotTranslateTitle()
    // 不要翻译 gist 页面的代码
    doNotTranslateGist()
    time++
    log(`第${time}次执行: doNotTranslate`)
  }

  const MutationObserver =
    window.MutationObserver ||
    window.WebKitMutationObserver ||
    window.MozMutationObserver
  const mo = new MutationObserver(_.debounce(doNotTranslate, 50))
  isGitHub && mo.observe(githubTV, option)

})()