Hacker News - Highlight and navigate original poster's comments

Highlight original poster comments on Hackers News and navigate them with the keyboard

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Hacker News - Highlight and navigate original poster's comments
// @description Highlight original poster comments on Hackers News and navigate them with the keyboard
// @namespace   valacar
// @author      valacar
// @include     https://news.ycombinator.com/item?id=*
// @version     0.3
// @grant       none
// @noframes
// @license     MIT
// @compatible  firefox Firefox
// @compatible  chrome Chrome
// ==/UserScript==

(function() {
  'use strict';

  const ENABLE_KEYBOARD_NAVIGATION = true;
  const VERTICAL_CENTER_PERCENT = 0.25; // percentage from the top

  function appendStyle(cssString) {
    const parent = document.head || document.documentElement;
    if (parent) {
      const style = document.createElement("style");
      style.setAttribute("type", "text/css");
      style.textContent = cssString;
      parent.appendChild(style);
    }
  }

  appendStyle(
`
.originalPoster,
.opPostCountInfo
{
  background: #ffc;
}
.currentHighlight
{
  background: linear-gradient(to right, #ffc 66%, transparent);
}
`);

  const posterUserName = document.querySelector('.subtext a').textContent;
  let opPostCount = 0;

  if (posterUserName) {
    const commentUserLinks = document.querySelectorAll('.comhead a');

    if (commentUserLinks) {
      for (let i = 0; i < commentUserLinks.length; i++) {
        let commentUserName = commentUserLinks[i].textContent;
        if (posterUserName === commentUserName) {
          opPostCount++;
          commentUserLinks[i].classList.add('originalPoster');
          commentUserLinks[i].id = 'op-post-' + opPostCount;
        }
      }
    }
  }

  if (opPostCount > 0) {
    const newSpan = document.createElement('span');
    newSpan.textContent = ' (' + opPostCount + ' by original poster)';
    newSpan.className = 'opPostCountInfo';

    if (ENABLE_KEYBOARD_NAVIGATION) {
      newSpan.setAttribute('title', "Use the left/right arrow keys\n(or n/N, or n/p) to scroll to OP comments.");
      newSpan.setAttribute('style', "cursor: help;");
    }

    const subText = document.querySelectorAll('.subtext a[href^="item?id="')[1];
    subText.appendChild(newSpan);
  }

  // http://stackoverflow.com/questions/8922107/javascript-scrollintoview-middle-alignment
  Element.prototype.documentOffsetTop = function () {
    return this.offsetTop + (this.offsetParent ? this.offsetParent.documentOffsetTop() : 0);
  };

  function ScrollToID(id) {
    const target = document.getElementById(id);
    const top = target.documentOffsetTop() - (window.innerHeight * VERTICAL_CENTER_PERCENT);
    window.scrollTo(0, top);
    const oldCurrent = document.querySelector(".currentHighlight");
    if (oldCurrent) {
      oldCurrent.classList.remove("currentHighlight");
    }
    // XXX: not the best idea to blindly go back two parents
    target.parentNode.parentNode.classList.add("currentHighlight");
  }

  if (ENABLE_KEYBOARD_NAVIGATION) {
    let num = 0;

    window.addEventListener("keydown", function (event) {
      // Should do nothing if the key event was already consumed.
      if (event.defaultPrevented) {
        return;
      }

      // don't break alt-left and alt-right history navigation
      // and exit if textarea is in focus
      if (event.altKey || /(input|textarea)/i.test(document.activeElement.nodeName)) {
        return;
      }

      switch (event.key) {
        case "n":
        case "ArrowRight":
        case "Right":
          if (num < opPostCount) {
            num++;
          }
          break;

        case "N":
        case "p":
        case "ArrowLeft":
        case "Left":
          if (num > 1) {
            num--;
          }
          break;

        default:
          return; // Quit when this doesn't handle the key event.
      }

      // Consume the event for suppressing "double action".
      event.preventDefault();

      //console.log('num = ' + num + ' focus = ' + document.activeElement.tagName);
      ScrollToID("op-post-" + num);

    }, true);
  }

})();