trsrm / VK Hotkeys

// ==UserScript==
// @name        VK Hotkeys
// @version     1.1.0
// @namespace   https://github.com/trsrm/vk-scripts
// @author      Taras Romaniv (https://github.com/trsrm)
// @description Hotkeys for http://vk.com website
// @homepageURL https://github.com/trsrm/vk-scripts
// @match       *://vk.com/feed
// @run-at      document-end
// @grant       none
// ==/UserScript==

(function () {
    'use strict';

    var feed = {

        activePost: void 0,

        intervalScrollCheck: void 0,

        setActivePost: function (post) {
            post.style['box-shadow'] = '-15px 0 0 0 #fff, -16px 0 0 0 rgba(89, 125, 163, .66)';
            this.activePost = post;
            this.startScrollChecks();
        },

        unsetActivePost: function () {
            this.activePost.style['box-shadow'] = 'none';
            this.activePost = void 0;
            this.stopScrollChecks();
        },

        startScrollChecks: function () {
            var that = this;
            this.intervalScrollCheck = setInterval(function () {
                if (window.pageYOffset + 100 > that.activePost.offsetTop + that.activePost.offsetHeight ||
                    window.pageYOffset + window.innerHeight - 100 < that.activePost.offsetTop
                ) {
                    that.unsetActivePost();
                }
            }, 1000);
        },

        stopScrollChecks: function () {
            clearInterval(this.intervalScrollCheck);
        },

        scrollToPost: function (post) {
            this.unsetActivePost();
            window.scrollTo(0, post.offsetTop);
            this.setActivePost(post);
        },

        nextPost: function () {
            if (!this.activePost) {
                this.activePost = this.findVisiblePost();
            }
            if (Math.ceil(window.pageYOffset / 100) * 100 < this.activePost.offsetTop) {
                this.scrollToPost(this.activePost);
            } else {
                var next = this.activePost.nextElementSibling;
                if (next) {
                    this.scrollToPost(next);
                }
            }
        },

        prevPost: function () {
            this.activePost = this.activePost || this.findVisiblePost();
            var prev = this.activePost.previousElementSibling;
            if (prev) {
                this.scrollToPost(prev);
            }
        },

        clearGarbage: function () {
            var adsPlaceholder = document.querySelector('#ads_feed_placeholder');
            if (adsPlaceholder) {
                var adsParent = adsPlaceholder.parentNode;
                adsParent.parentNode.removeChild(adsParent);
            }
        },

        findVisiblePost: function () {
            feed.clearGarbage();
            var posts = document.querySelectorAll('#feed_rows > .feed_row');
            for (var i = 0; i < posts.length; i++) {
                if (window.pageYOffset < posts[i].offsetTop + posts[i].offsetHeight) {
                    return posts[i];
                }
            }
        }

    };

    window.addEventListener('keypress', function (event) {
        switch (event.keyCode) {
            case 106:
            case 1086:
                feed.nextPost();
                break;
            case 107:
            case 1083:
                feed.prevPost();
                break;
        }
    });

})();