Greasy Fork

Greasy Fork is available in English.

我帮你加上空格嗷~

在英文和其他文字之间,添加一个空格,让排版变得更舒适,也更利于阅读,

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        我帮你加上空格嗷~
// @namespace   Maybe you forgot a space.
// @match       *://*/*
// @grant       none
// @author      稻米鼠
// @version     0.0.5
// @author      -
// @description 在英文和其他文字之间,添加一个空格,让排版变得更舒适,也更利于阅读,
// ==/UserScript==

window.addEventListener('load', async function(){
  /**
   * observer 配置
   * @property { boolean } childList - 观察目标子节点的变化,比如添加或者删除目标子节点,不包括修改子节点以及子节点后代的变化
   * @property { boolean } subtree - 目标以及目标的后代改变都会观察
   * @property { boolean } attributes - 观察目标属性的改变
   * @property { boolean } characterData - 观察目标数据的改变
   * @property { boolean } attributeOldValue - 默认为 true,表示需要记录改变前的目标属性值,设置了 attributeOldValue 可以省略 attributes 设置
   * @property { boolean } characterDataOldValue - 默认为 true,表示需要记录改变前的目标数据值,设置了 characterDataOldValue 可以省略 characterData 设置
   * @property { array } attributeFilter - 如果不是所有的属性改变都需要被观察,并且 attributes 设置为 true 或者被忽略,那么设置一个需要观察的属性本地名称(不需要命名空间)的列表
   */
  const config = {
    attributeOldValue: false,
    characterData: true,
    childList: true,
    subtree: true,
  };
  /**
   * 遍历元素的全部祖先元素,判断是否应该对此元素进行进一步处理
   * @param {*} el 要测试段元素
   */
  const nodeTester = el=>{
    if(/^body$/i.test(el.tagName)){
      return true
    }else if(
      /^(style|script|pre|code)$/i.test(el.tagName)
      || /^true$/i.test(el.contentEditable)
    ){
      return false
    }else{
      return nodeTester(el.parentNode)
    }
  }
  /**
   * 递归处理元素和子元素
   *
   * @param { object } el 页面元素
   * @returns
   */
  const addSpace = async el=>{
    // 如果 这是一个文本节点,则对内容进行替换
    // [英文 和 标点]与其他内容之间,加上空格
    if( el.nodeType === Node.TEXT_NODE ){
      el.textContent = el.textContent
        .replace(/([^\s\w\p{P}])([\w\p{P}]+)/gu, (match, p1, p2) =>
          /[a-zA-Z0-9]/.test(p2) ? p1 + ' ' + p2 : match
        )
        .replace(/([\w\p{P}]+)([^\s\w\p{P}])/gu, (match, p1, p2) =>
          /[a-zA-Z0-9]/.test(p1) ? p1 + ' ' + p2 : match
        );
    }else if(
      el.nodeType === Node.ELEMENT_NODE
      && nodeTester(el)
      ){
      // 如果这是一个元素节点,并且不是特殊元素,则对它的子元素进行遍历
      for await (e of el.childNodes){
        await addSpace(e)
      }
    }
    return Promise.resolve()
  }
  // 页面加载完成后处理整个页面
  await addSpace(document.body)
  // 声明用以监控页面的 observer 对象
  let observer
  /**
   * 当页面发生变化时,处理记录元素变化的数组
   *
   * @param { array } record MutationRecord
   * @returns
   */
  const pageChangeWatcher = async record =>{
    // 先停止对页面变化的监控,避免陷入死循环
    observer.disconnect()
    // 发生变化的元素合集
    const els = record
      // 改变的类型为 characterData,并且不是 body 元素的话
      .filter(
        (e) =>
          /^characterData$/i.test(e.type) && !/^body$/i.test(e.target.tagName)
      )
      .map((e) => e.target) // 把发生改变的元素放入合集
      // 改变的类型为 childList,则把新增的元素放入合集
      record.filter( e=>/^childList$/i.test(e.type)).forEach(e=>{
        e.addedNodes.forEach(node=>els.push(node))
      })
      // 遍历合集中所有元素
      for await (e of els){
        if(nodeTester(e)) await addSpace(e)
      }
      // 页面处理完成之后重新监控页面变化
      if(observer) observer.observe(document.body, config);
  }
  // 监控页面变化
  observer = new MutationObserver(pageChangeWatcher);
  observer.observe(document.body, config);
})