Greasy Fork

Greasy Fork is available in English.

rZhihu

为知乎首页添加类似 Google Reader 的快捷键。

目前为 2017-05-26 提交的版本。查看 最新版本

// ==UserScript==
// @name            rZhihu
// @author          nonoroazoro
// @description     为知乎首页添加类似 Google Reader 的快捷键。
// @description:en  Add keyboard shortcuts for Zhihu homepage.
// @homepageURL     https://github.com/nonoroazoro/rZhihu
// @namespace       http://greasyfork.icu/zh-CN/scripts/30036-rzhihu
// @grant           none
// @version         1.0.1
// @run-at          document-end
// @include         https://www.zhihu.com/
// ==/UserScript==

let started = false;
let stories = [];
let storyContainer = null;
let current = 0;
let maxIndex = -1;
const ignoreList = ["INPUT", "DIV", "TEXTAREA"];

function start()
{
    storyContainer = document.querySelector(".TopstoryMain");
    observe(storyContainer, update);
    document.body.addEventListener("keydown", _keydownHandler);
}

function _keydownHandler(e)
{
    if (!started || ignoreList.indexOf(e.target.nodeName) != -1 || e.altKey || e.shiftKey || e.ctrlKey || e.metaKey)
    {
        return;
    }

    if (e.keyCode === 74)
    {
        // press "j"
        _next();
    }
    else if (e.keyCode === 75)
    {
        // press "k"
        _prev();
    }
    else if (e.keyCode === 79 || e.keyCode === 13)
    {
        // press "o/enter"
        _toggle();
    }
    else if (e.keyCode === 85)
    {
        // press "u"
        _unlike();
    }
}

/**
 * flip to next story.
 */
function _next()
{
    _flip(current + 1);
}

/**
 * flip to previous story.
 */
function _prev()
{
    _flip(current - 1);
}

/**
 * flip to story.
 */
function _flip(index)
{
    current = index;
    if (current < 0)
    {
        current = 0;
    }

    if (current > maxIndex)
    {
        current = maxIndex;
    }

    const offsetTop = stories[current].offsetTop;
    if (window.scrollY !== offsetTop)
    {
        window.scrollTo(0, offsetTop);
    }
}

/**
 * toggle story expand/collapse.
 */
function _toggle()
{
    const story = stories[current];
    const expand = story.querySelector(".is-collapsed .RichContent-inner");
    if (expand)
    {
        expand.click();
    }
    else
    {
        const collapse = story.querySelector(".ContentItem-actions > button:last-child");
        collapse.click();
    }
}

/**
 * toggle story unlike.
 */
function _unlike()
{
    const story = stories[current];
    const unlike = story.querySelector("button:first-child");
    unlike.click();
}

/**
 * update after the original zhihu story list is loaded.
 */
function update(mutations)
{
    if (mutations.length > 0)
    {
        try
        {
            const index = JSON.parse(storyContainer.dataset["zaModuleInfo"])["list"]["list_size"] - 1;
            if (index !== maxIndex)
            {
                maxIndex = index;
                stories = document.querySelectorAll(".Card.TopstoryItem");
            }
        }
        catch (e)
        {
            const index = storyContainer.children.length - 1;
            if (index !== maxIndex)
            {
                maxIndex = index;
                stories = storyContainer.children;
            }
        }

        // record script status.
        if (!started)
        {
            started = true;
        }
    }
}

function observe(element, callback)
{
    if (element && typeof callback === "function")
    {
        (new window.MutationObserver(debounce(callback))).observe(element, {
            attributes: true,
            attributeFilter: ["data-za-module-info"]
        });
    }
}

function debounce(callback, delay = 500)
{
    let timer = null;
    return function (...args)
    {
        const context = this;
        window.clearTimeout(timer);
        timer = window.setTimeout(() => callback.apply(context, args), delay);
    };
}

document.addEventListener("DOMContentLoaded", () => start());