colorDate

"Set date color according to date 根据网页上日期的新旧程度, 给日期进行着色, 比如说已经是5年前的一个日期会成为红色, 以便提醒阅览者,注意信息可能过于陈旧。"

目前為 2018-04-05 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name     colorDate
// @namespace https://github.com/mika-cn/user-scripts
// @description "Set date color according to date 根据网页上日期的新旧程度, 给日期进行着色, 比如说已经是5年前的一个日期会成为红色, 以便提醒阅览者,注意信息可能过于陈旧。"
// @version  1.1.1
// @grant    none
// @include *
// @author   mika
// ==/UserScript==

(function(){
  'use strict';

  /*
   * 获取文本节点迭代器
   */
  function getTextNodeIterator(){
    return document.createNodeIterator(
      document.body,
      NodeFilter.SHOW_TEXT, function(node) {
        if(['script', 'style', 'datetext', 'datespan'].indexOf(node.parentNode.nodeName.toLowerCase()) > -1){
          return NodeFilter.FILTER_REJECT;
        }else{
          return NodeFilter.FILTER_ACCEPT;
        }
      }
   );
  }


  /*
   * 根据规则(判断是否是日期),获取满足的节点
   */
  function getMatchNode(){
    var nodes = [];
    var currentNode;
    var nodeIterator = getTextNodeIterator();
    while (currentNode = nodeIterator.nextNode()) {
      var v = currentNode.nodeValue;
      var rule = rules.some(function(rule){
        return !!v.match(rule.regExp);
      });

      if(rule){
        nodes.push(currentNode);
      }
    }
    return nodes;
  }

  /*
   * 时长阀值与色值
   */
  var day  = 24 * 60 * 60 * 1000;
  var year = 365 * day;
  var colors = [
    {color: "#ff0000", threshold: 5 * year},
    {color: "#ff5f00", threshold: 3 * year},
    {color: "#d700ff", threshold: 1 * year},
    {color: "#8700ff", threshold: 183 * day},
    {color: "#00af00", threshold: 7 * day},
    {color: "#00d700", threshold: 1 * day},
  ];

  var monthPart = (`
    Jan|January|
    Feb|February|
    Mar|March|
    Apr|April|
    May|
    Jun|June|
    Jul|July|
    Aug|Augest|
    Sep|Sept|September|
    Oct|October|
    Nov|November|
    Dec|December
  `).replace(/\n\s*/gm, '');

  /*
   * 判断规则
   */
  var rules = [
    {key: "01", regExp: /\d{4}-[01]?\d-[0-3]?\d/mg},
    {key: "01", regExp: /\d{4}\/[01]?\d\/[0-3]?\d/mg},
    {key: "02", regExp: /\d{4}-[01]{1}\d(?!-\d[0-3]?\d)/mg},
    {key: "02", regExp: /\d{4}\/[01]{1}\d(?!\/\d[0-3]?\d)/mg},
    {key: "03", regExp: /(?:\s|^)[01]{1}\d-[0-3]{1}\d/mg},
    {key: "01", regExp: new RegExp("(?:"+ monthPart +") [0-3]?\\d[,\\s]{1}\\s?\\d{4}", 'igm')},
    {key: "01", regExp: /\d{4}年[01]?\d月[0-3]?\d日/mg},
    {key: "02", regExp: /\d{4}年[01]?\d月(?!^[0-3]?\d日)/mg},
    {key: "03", regExp: /(?:\s|^)[01]?\d月[0-3]?\d日/mg},
    {key: "04", regExp: /\d+\s?天前/mg},
    {key: "04", regExp: /\d+\s?days?\sago/mg},
    {key: "05", regExp: /\d+\s?月前/mg},
    {key: "05", regExp: /\d+\s?months?\sago/mg},
    {key: "06", regExp: new RegExp("(?:"+ monthPart +")['\\s]{1}[0-3]{1}\\d(?![,\\s]{1}\\s?\\d{4})", 'igm')},
    {key: "07", regExp: /\d+\s?年前/mg},
    {key: "07", regExp: /\d+\s?years?\sago/mg},
  ];

  /*
   * 根据不同的正则,返回处理函数
   */
  function getHandler(key){
    switch(key){
      case "01" : return handler_01();
      case "02" : return handler_02();
      case "03" : return handler_03();
      case "04" : return handler_04();
      case "05" : return handler_05();
      case "06" : return handler_06();
      case "07" : return handler_07();
      default: return function(match){ return match;};
    }
  }

  function handler_01(){
    return function(match){
      var dateStr = match.replace(/年|月/g, '-').replace("日", '');
      return replace(match, dateStr);
    };
  }

  function handler_02(){
    return function(match){
      var dateStr = match.replace(/年|\//g, '-').replace("月", '') + "-01";
      return replace(match, dateStr);
    };
  }

  function handler_03(){
    return function(match){
      var dateStr = (new Date()).getFullYear().toString() + "-" + match.trim().replace(/月/g, '-').replace("日", '');
      return replace(match, dateStr);
    };
  }

  function handler_04(){
    return function(match){
      var n = parseInt(match.match(/\d+/)[0]);
      return replace(match, (new Date() - n * day));
    };
  }

  function handler_05(){
    return function(match){
      var n = parseInt(match.match(/\d+/)[0]);
      return replace(match, (new Date() - n * 30 * day));
    };
  }

  function handler_06(){
    return function(match){
      return replace(match, match.replace(/'/, ' ') + " " + (new Date()).getFullYear().toString());
    };
  }


  function handler_07(){
    return function(match){
      var n = parseInt(match.match(/\d+/)[0]);
      return replace(match, (new Date() - n * 365 * day));
    };
  }


  /*
   * 处理函数, 根据时长阀值着色
   */
  function replace(match, dateStr){
    try{
      var result = match;
      var diff = Date.now() - new Date(dateStr);
      colors.some(function(item){
        if(diff >= item.threshold){
          result = "<datespan style='color:"+ item.color +";'>" + match +"</datespan>";
          return true;
        }
        return false;
      });
      return result;
    }catch(e){
      return match;
    }
  }


  // 主函数
  function colorDate(){
    var nodes = getMatchNode();
    nodes.forEach(function(node){
      // replace child node
      var newNode = document.createElement("datetext");
      var html = node.nodeValue;
      rules.forEach(function(rule){
        var match = html.match(rule.regExp);
        if(match){
          html = html.replace(rule.regExp, getHandler(rule.key));
        }
      });
      newNode.innerHTML = html;
      node.parentNode.replaceChild(newNode, node);
    });
  }



  /*
   * 创建延迟调用对象
   * 用来防止短时间内被调用多次
   */
  var createDelayCall = function(fn, delay){
    var dc = {};
    dc.action = fn;
    dc.clearTimeout = function(){
      if(dc.timeoutId){
        clearTimeout(dc.timeoutId);
      }
    };
    dc.run = function(){
      dc.clearTimeout();
      dc.timeoutId = setTimeout(function(){
        dc.action();
        dc.clearTimeout();
      }, delay);
    };
    return dc;
  };

  /*
   * 判断变更是否来自脚本
   */
  function isColorDateMotation(mutationRecords){
    return mutationRecords.every(function(record){
      return record.type === "childList" && record.addedNodes.length > 0 && record.addedNodes[0].nodeName.toLowerCase() === "datetext";
    });
  }


  /*
   * 初始化 mutationObserver
   */
  function initMutationObserver(){
    var observer = new MutationObserver(function(mutationRecords){
      if(isColorDateMotation(mutationRecords)){
        // 本脚本产生的变更,不触发
        // console.log("Ignore motation")
      }else{
        delayColorDate.run();
      }
    });
    observer.observe(document, {
      attributes: false,
      childList: true,
      subtree: true
    });
    //console.log("init mutationObserver");
  }

  var delayColorDate = createDelayCall(colorDate, 400);
  // 监听变更,触发着色 (适用于动态网页,如: ajax加载内容后产生变更)
  initMutationObserver();
  // 静态网页加载过程中不会产生变更, 直接调用
  setTimeout(colorDate, 400);
  setTimeout(colorDate, 1000);
  setTimeout(colorDate, 4000);

})();