Pikabu Enhancement Suite

Новый новый дизайн пикабу. Не забудь установить юзерстиль.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Pikabu Enhancement Suite
// @version      1.4.8
// @license      CC-BY-SA-4.0
// @description  Новый новый дизайн пикабу. Не забудь установить юзерстиль.
// @author       NeverLoved
// @match        https://pikabu.ru/*
// @grant        none
// @namespace https://greasyfork.org/users/175168
// ==/UserScript==

(function() {
    'use strict';

    // Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/prepend()/prepend().md
    (function (arr) {
        arr.forEach(function (item) {
            if (item.hasOwnProperty('prepend')) {
                return;
            }
            Object.defineProperty(item, 'prepend', {
                configurable: true,
                enumerable: true,
                writable: true,
                value: function prepend() {
                    var argArr = Array.prototype.slice.call(arguments),
                        docFrag = document.createDocumentFragment();

                    argArr.forEach(function (argItem) {
                        var isNode = argItem instanceof Node;
                        docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
                    });

                    this.insertBefore(docFrag, this.firstChild);
                }
            });
        });
    })([Element.prototype, Document.prototype, DocumentFragment.prototype]);

    let _c;
    var defconfig = {
        "top_big_searchbar":false,
        "top_no_avatar":true,
        "porn_stealth":true,
        "restore_f":true,
        "great_comment_links":true,
        "post_header_collapse":true,
        "sidebar_icons":true,
        "sidebar_feed_link":false,
        "enable_nano_nav":true,
        "oldschool_favicon": false,

    };
    try {
        _c = JSON.parse(window.localStorage.getItem('enhancements'));
        if(!Object.keys(_c).length) {
            throw new Error('Empty');
        }
    } catch(e) {
        console.log('Config is empty, using defconfig');
        _c = defconfig;
    }

    var config = {
        values:_c,
        set:function(param, value) {
            this.values[param] = value;
            window.localStorage.setItem('enhancements', JSON.stringify(this.values));
        },
        get:function(param) {
            return this.values[param];
        }
    };

    function findParentByTag(el, tag) {
        while (el.parentNode) {
            el = el.parentNode;
            if (el.tagName === tag)
                return el;
        }
        return null;
    }

    function nodeIndex(node) {
        var children = node.parentNode.children;
        var num = 0;
        for (var i=0; i<children.length; i++) {
            if (children[i]==node) return num;
            if (children[i].nodeType==1) num++;
        }
        return -1;
    }

    var scrollTo = function(elem) {
        if(elem) {
            try {
                elem.scrollIntoView({behavior:'smooth', 'block':'start'});
            } catch(e) {
                console.log('Не удалось проскроллить к элементу', elem, e);
            }
        }
    };

    var scrollInstant = function(elem) {
        if(elem) {
            try {
                elem.scrollIntoView({behavior:'instant', 'block':'start'});
            } catch(e) {
                console.log('Не удалось проскроллить к элементу', elem, e);
            }
        }
    };


    if(window.location.pathname == '/settings') {
        var onchange = function(e) {
            let checkbox = e.target;
            config.set(checkbox.id, checkbox.checked);
        };

        var create_config_item = function(caption, id, value) {
            let checkbox = document.createElement('input');
            checkbox.setAttribute('id', id);
            checkbox.setAttribute('type', 'checkbox');
            if(value) {
                checkbox.checked = true;
            }
            checkbox.onchange = onchange;
            let label = document.createElement('label');
            label.innerText = caption;
            label.setAttribute('for', id);

            let tr = document.createElement('tr');
            tr.append(document.createElement('td'));
            tr.append(document.createElement('td'));
            tr.children[0].append(label);
            tr.children[1].append(checkbox);
            return tr;
        };

        var sett_table = document.createElement('table');
        sett_table.className = "enhanced_settings";
        var pew = document.createElement('style');
        pew.innerText = ".enhanced_settings [type=checkbox] { visibility: visible!important; }";
        document.body.append(pew);
        console.log(pew);
        [
            create_config_item("Иконки в меню сайдбара", 'sidebar_icons', config.get('sidebar_icons')),
            create_config_item('"Моя Лента" в правом меню', 'sidebar_feed_link', config.get('sidebar_feed_link')),
            create_config_item("Скрывать пост нажатием на пустое место в заголовке", 'post_header_collapse', config.get('post_header_collapse')),
            create_config_item("Скрытие просмотренных постов по F", 'restore_f', config.get('restore_f')),
            create_config_item("Включить плавающую навигацию справа", 'enable_nano_nav', config.get('enable_nano_nav')),
            create_config_item("Добавить нормальные ссылки на комментарии [#]", 'great_comment_links', config.get('great_comment_links')),
            create_config_item("Строка поиска всегда развёрнута", 'top_big_searchbar', config.get('top_big_searchbar')),
            create_config_item("Убрать быстрое меню в шапке", 'top_no_avatar', config.get('top_no_avatar')),
            create_config_item("Скрытная иконка клубнички", 'porn_stealth', config.get('porn_stealth')),
            create_config_item("Старая иконка сайта", 'oldschool_favicon', config.get('oldschool_favicon')),
        ].forEach(function(tr) { sett_table.append(tr); });
        document.querySelector('form[name="general"] section').append(sett_table);
        //document.querySelector('form[name="general"] .page-settings__table tbody').append();
    }

    var sidebar = document.querySelector('.sidebar aside');
    var top_menu = document.querySelector('header.header');
    var profile_info = sidebar.querySelector('.profile-info');
    var answers_link = sidebar.querySelector('.menu a.menu__item[href="/answers"]');
    var main_menu = answers_link.parentNode;
    var rating_container = profile_info.children[0];
    var subscribers_container = profile_info.children[1];

    var rating_val = 0;
    try {
        rating_val = JSON.parse(document.querySelector('[data-entry=initParams]').innerText).userKarma;
    } catch(e) {
        if((rating_val = rating_container.getAttribute('aria-label')) == null) {
            rating_val = rating_container.children[0].innerText;
            rating_val = parseFloat(rating_val.replace(/[^0-9.]/g, ''));
        }
    }

    var subscribers_val = 0;
    if((subscribers_val = subscribers_container.getAttribute('aria-label')) == null) {
        subscribers_val = subscribers_container.children[0].innerText;
    }
    subscribers_val = parseInt(subscribers_val.replace(/[^0-9]/g, ''));

    var rating = document.createElement('span');
    rating.innerText = "Рейтинг: "+rating_val;
    rating.style['padding-left'] = getComputedStyle(answers_link)['padding-left'];
    rating.className = "rating";

    var subscribers = document.createElement('span');
    subscribers.innerText = "Подписчиков: "+subscribers_val;
    subscribers.style['padding-left'] = getComputedStyle(answers_link)['padding-left'];
    subscribers.className = "subscribers";

    if(config.get('sidebar_feed_link')) {
        var feed_link_top = top_menu.querySelector('a[href="/subs"]');
        var bell = feed_link_top.querySelector('.bell');
        if(bell) {
            bell.className = "bell bell_profile-menu";
        }
        var clss = "menu__item";
        if(feed_link_top.parentNode.className.indexOf("header-menu__item_current")+1) {
            clss += " menu__item_current";
        }
        feed_link_top.className += clss;
        main_menu.prepend(feed_link_top);
        feed_link_top.style.display = 'block';
    }

    if(config.get('oldschool_favicon')) {
        document.getElementById("favicon").setAttribute("href", "https://cs.pikabu.ru/favicon2x.ico");
    }

    main_menu.prepend(subscribers);
    main_menu.prepend(rating);

    var edits = sidebar.querySelector('.menu a.menu__item[href="/edits"]');
    if(edits) {
        var unneeded_menu = edits.parentNode;
        main_menu.append(edits);
        unneeded_menu.remove();
    }
    profile_info.remove();

    try {
        var styluses = [].slice.call(document.querySelectorAll('.stylus'));
        var stylus;
        for(var i in styluses) {
            stylus = styluses[i];
            stylus.innerHTML = stylus.innerHTML.replace('.sidebar-block__header {', '.'+sidebar.querySelector(".sidebar-block__content").previousElementSibling.className+' {');
        }
    } catch(e) {
        console.log("Не установлен юзерстиль или нет доступа к блоку контента в сайдбаре.");
    }


    if(config.get('sidebar_icons')) {
        var menu_items = [].slice.call(main_menu.children); //Конвертируем HTMLCollection в массив
        menu_items.forEach(function(item) {
            let icon_container = document.createElement('span');

            icon_container.className = "icon_container";
            let i = document.createElement('i');
            let space = document.createTextNode(' ');
            let icon = 'circle-o';
            switch(item.getAttribute('href')) {
                case '/answers':
                    if(item.children.length) {
                        icon = "envelope-o";
                    } else {
                        icon = "envelope-open-o";
                    }
                    break;
                case '/comments':
                    icon = "comments-o";
                    break;
                case '/liked':
                    icon = "thumbs-o-up";
                    break;
                case '/saved-stories':
                    icon = 'floppy-o';
                    break;
                case '/subs-list':
                    icon = 'list';
                    break;
                case '/ignore-list':
                    icon = 'times';
                    break;
                case '/notes':
                    icon = 'sticky-note-o';
                    break;
                case '/edits':
                    icon = 'pencil';
                    break;
                case '/subs':
                    icon = "file-text-o";
                    break;
                default:
                    if(item.className == "rating") {
                        icon = "bar-chart";
                    } else if(item.className == "subscribers") {
                        icon = "users";
                    }
                    break;
            }
            i.className = 'fa fa-'+icon;
            icon_container.prepend(i);
            item.prepend(space);
            item.prepend(icon_container);
        });
    }
    var right_menu = top_menu.querySelector('.header-right-menu');
    var search_bar = right_menu.querySelector('.header-right-menu__search');

    if(config.get('top_big_searchbar')) {
        search_bar.style.width = "250px";
        search_bar.style['background-color'] = "#fff";
        search_bar.style['border-radius'] = "2px";
        search_bar.querySelector('button').style.color = "#8ac858";
    }
    if(config.get('top_no_avatar')) {
        right_menu.querySelector('.avatar').remove();
    }
    var notify_btn = right_menu.querySelector('.header-right-menu__notification');
    notify_btn.innerHTML = '<i class="fa fa-bell fa-bigger" style="font-size:1.3em"></i>';
    notify_btn.style['line-height'] = '1px';
    notify_btn.style['margin-right'] = '0px';


    var user_info = sidebar.querySelector('.user');
    var settings = user_info.querySelector('a[href="/settings"]');
    var exit = user_info.querySelector('.user__exit');
    var nick_container = user_info.querySelector('.user__nick_big').parentNode;

    try {
        var straw = sidebar.querySelector('[data-role="switch-adult"]');
        if(config.get('porn_stealth')) {
            straw.children[0].innerHTML = '<i class="fa fa-bigger fa-user-secret"></i>';
        } else {
            straw.children[0].innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" class="icon icon--ui__strawberry"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon--ui__strawberry"></use></svg>';
        }
        var qs = straw.parentNode.remove();
    } catch(err) {
        console.log('Настройка клубнички выключена.');
        var straw = document.createTextNode('');
    }

    try {
        var displaymode = sidebar.querySelector('[data-role="display-mode"]');
        displaymode.previousElementSibling.remove();
        var quickset = displaymode.parentNode.parentNode.parentNode;
        displaymode.style.float = "none";
        displaymode.style.display = "inline-block";
        var dtxt = displaymode.children[0];
        if(dtxt.innerText.indexOf('скрыто')+1) {
            dtxt.innerText += ' просмотренных постов';
        } else {
            dtxt.innerText += ' просмотренные посты';
        }
        var panel = document.querySelector('main section:first-child');
        //panel.insertBefore(displaymode, panel.lastChild);
        panel.insertBefore(displaymode, panel.lastElementChild);
        quickset.remove();

    } catch(err) {
        //var straw = document.createTextNode('');
    }

    settings.innerHTML = '<i class="fa fa-cog fa-bigger"></i>';
    settings.style.color = '#757575';

    exit.style.display = "inline-block";
    exit.innerHTML = '<i class="fa fa-sign-out fa-2x"></i>';


    var btn_container = exit.parentNode;
    btn_container.className += " btn_container";

    btn_container.append(settings);
    btn_container.append(document.createTextNode(' '));

    btn_container.append(notify_btn);
    btn_container.append(document.createTextNode(' '));
    btn_container.append(straw);

    user_info.append(exit);
    if(config.get('post_header_collapse')) {
        document.querySelector('main').addEventListener('click', function(e) {
            if(e.target.nodeName == "HEADER") {
                e.target.parentNode.parentNode.querySelector('.collapse-button').click();
            }
        }, false);
    }

    var remove_visited = function(shortcut) {
        document.querySelectorAll('article.story a.story__title-link_visited').forEach(function(item) {
            var story = item.parentNode.parentNode.parentNode.parentNode;
            story.setAttribute('data-visited', "true");
        });
        var curr = current_story();
        var scroll_next = true;
        var is_video = false;
        [].slice.call(document.querySelectorAll('article.story[data-visited="true"]')).forEach(function(item) {
            if(item == curr) {
                var yt = item.querySelector('iframe');
                if(yt) {
                    console.log('Нельзя прятать текущий пост если в нём есть iframe плеера. TODO: получать статус воспроизведения через Youtube Api. Или не TODO, ну его нафиг.');
                    scroll_next = false;
                    if(shortcut) { //Если remove_visited вызвано нажатием клавиши то пытаемся развернуть плеер в фуллскрин
                        var requestFullscreen = yt.requestFullScreen || yt.mozRequestFullScreen || yt.webkitRequestFullScreen || yt.msRequestFullscreen;
                        if(requestFullscreen) {
                            scrollInstant(item); //Скроллим к посту т.к скролл уезжает при удалении предыдущих постов
                            requestFullscreen.call(yt);
                        }
                    }
                    return;
                }
            }
            item.style.display = "none"; //для обратной совместимости с теми, кто не обновит юзерцсс
            item.setAttribute('data-hidden', "true");
        });
        if(scroll_next) {
            var next_story = document.querySelector('article.story[data-visited="false"]');
            scrollInstant(next_story);
        }
    };

    var current_story = function(){
        /* Множитель зума нужен только для хрома, в фаерфоксе там будет ~1 */
        var zoom = parseFloat((window.outerWidth/parseInt(getComputedStyle(document.documentElement,null).width)).toFixed(2));
        var el = document.elementFromPoint((window.outerWidth/zoom)/2, (window.outerHeight/zoom)/4);
        if(el.className.indexOf('stories-feed__container')+1) {
            console.log('Попали в margin.');
            window.scroll(0, window.scrollY-15); //margin-top поста 15px, сдвинув скролл на 15 пикселей мы куда-нибудь да попадём :)
            el = document.elementFromPoint((window.outerWidth/zoom)/2, (window.outerHeight/zoom)/4);
        }
        var story = findParentByTag(el, 'ARTICLE');
        return story;
    };
    var next_story = function() {
        try {
            var cs = current_story();
            if(!cs) {
                console.log('Текущей истории нет, переходим к первой видимой.');
                return document.querySelector('article.story:not([data-hidden="true"])');
            }
            var ni = nodeIndex(cs)+1;
            var ns = [].slice.call(document.querySelectorAll('article.story:nth-child(n+'+ni+'):not(:nth-child('+ni+')):not([data-hidden="true"])'));
            var next = ns.shift();

            var prev = prev_story(true);
            if(prev && prev.getClientRects()[0].top > 0) {
                console.log('Ох уж эти короткие посты.');
                next = cs; //следующий пост на самом деле тот, что мы уже считаем текущим
            }

            if(!next) {
                throw('');
            }
            return next;
        } catch(e) {
            console.log('Нет следующей истории.');
        }
    };
    var prev_story = function(simple) {
        try {
            var cs = current_story();
            var ni = nodeIndex(cs)+1;
            var ps = [].slice.call(document.querySelectorAll('article.story:nth-child(-n+'+ni+'):not(:nth-child('+ni+')):not([data-hidden="true"])'));
            var prev = ps.pop();
            if(!simple) {
                if(prev.getClientRects()[0].top > 0) { //пост слишком маленький, и это он на самом деле является current_story
                    console.log('Ох уж эти короткие посты.');
                    prev = ps.pop(); //достаём предыдущий пост
                }
            }
            if(!prev) {
                throw('');
            }
            return prev;
        } catch(e) {
            if(!simple) {
                console.log('Нет предыдущей стории.');
            }
            //return null;
        }
    };

    if(config.get('restore_f')) {
        if(document.querySelectorAll('article.story').length > 1) {
            /**
             * UPD: mutationObserver больше не нужен для целей скрытия,
             * в последнем обновлении посты сами помечаются прочитанными.
             * Используем это:
            */

            window.addEventListener('keyup', function(e) {
                if(e.key == "f" || e.key == "F" || e.key == "а" || e.key == "А") {
                    remove_visited(true);
                }
            });

            try {
                var fkey = document.querySelector('.hotkeys').parentNode.querySelector('[aria-label="Показать полностью"]');
                fkey.setAttribute('aria-label', 'Скрыть прочитанное');
                fkey.children[1].innerHTML = "<i class='fa fa-eye-slash'></i>";
            } catch(e) {
                //console.log('хоткеи не найдены');
            }
        }
    }

    if(config.get('enable_nano_nav')) {
        if(document.querySelectorAll('article.story').length > 1) {
            var nano_nav = document.createElement('div');
            nano_nav.id = "nano_nav";
            nano_nav.className = "sidebar-block sidebar-block_border";

            var separator = document.createElement('span');
            separator.className = "separator";

            var separator_empty = document.createElement('span');
            separator_empty.className = "separator empty";

            var prev = document.createElement('span');
            var next = document.createElement('span');
            var hide = document.createElement('span');

            prev.className = "link";
            next.className = "link";
            hide.className = "link";
            prev.innerHTML = '<i class="fa fa-chevron-left fa-2x hint" aria-label="Назад (или нажмите кнопку A)"></i>';
            next.innerHTML = '<i class="fa fa-chevron-right fa-2x hint" aria-label="Вперед (или нажмите кнопку D)"></i>';
            hide.innerHTML = '<i class="fa fa-eye-slash fa-2x hint" aria-label="Скрыть просмотренные (или нажмите кнопку F)"></i>';
            prev.onclick = function() { scrollTo(prev_story()); };
            next.onclick = function() { scrollTo(next_story()); };
            hide.onclick = function() { remove_visited(); };
            nano_nav.append(prev);
            nano_nav.append(document.createTextNode(' '));
            nano_nav.append(separator);
            nano_nav.append(document.createTextNode(' '));
            nano_nav.append(next);
            nano_nav.append(document.createTextNode(' '));
            nano_nav.append(separator_empty);
            nano_nav.append(document.createTextNode(' '));
            nano_nav.append(hide);
            document.body.append(nano_nav);
        }
    }

    if(config.get('great_comment_links')) {

        var makeCommentGreatAgain = function(element) {
            let comment_tools = element.querySelector('.comment__tools');
            let branch_link = comment_tools.querySelector('[data-role="link"]');
            let branch_link_href = branch_link.getAttribute('href');
            branch_link.setAttribute('aria-label', 'Ссылка на ветку комментариев');

            let comment_link = document.createElement('a');
            comment_link.className = 'comment__tool hint';
            comment_link.setAttribute('data-role', 'comment_link');
            comment_link.setAttribute('aria-label', 'Ссылка на комментарий');
            comment_link.setAttribute('href', branch_link_href.replace('?cid=', '#comment_'));
            comment_link.innerText = '#';
            comment_tools.insertBefore(comment_link, branch_link);
            element.setAttribute('data-great', "true");
        };
        var makeCommentsGreatAgain = function() {
            document.querySelectorAll('.comment:not([data-great])').forEach(function(item) {
                makeCommentGreatAgain(item);
            });
        };
        makeCommentsGreatAgain();
        var jumpto = function() {
            if(window.location.hash.indexOf('comment_')+1) {
                var comment = document.querySelector(window.location.hash);
                if(comment) {
                    comment.scrollIntoView({behavior:'smooth', 'block':'start'});
                } else { //В списке комментариев нет загруженного комментария
                    var more_comments = document.querySelector('button.comments__more-button');
                    if(more_comments) {
                        more_comments.scrollIntoView({behavior:'smooth', 'block':'start'});
                        more_comments.className = more_comments.className += " button_yellow";
                        (function() { setTimeout(function() { console.log('release');more_comments.className = more_comments.className.replace(' button_yellow', ''); }, 2500); })();
                    }
                }
            }
        };


        if(window.location.href.indexOf('/story/')+1) {
            jumpto();
            var is_comment_already = null;
            if(window.location.hash.indexOf('comment_')+1) {
                 is_comment_already = document.querySelector(window.location.hash);
            }
            var more_comments_btn = document.querySelector('button.comments__more-button');
            if(more_comments_btn) {
                more_comments_btn.addEventListener('click', function() {
                    var emitted = false;
                    var e = document.createEvent('Event');
                    e.initEvent('comments.loaded', true, true);
                    var comment_observer = new MutationObserver(function(records) {
                        records.some(function(record) {
                            if(record.target.className.indexOf('comment')+1) {
                                document.dispatchEvent(e);
                                comment_observer.disconnect();
                                return true;
                            } else {
                                return false;
                            }
                        });
                    });
                    comment_observer.observe(document.querySelector('section.comments'),  {'childList':true, 'subtree':true});

                });
            }



            document.addEventListener('comments.loaded', function() {
                console.log('comments loaded emitted');
                makeCommentsGreatAgain();
                if(!is_comment_already) {
                    jumpto();
                }
            });
        }
    }

})();