Пятничный трекер

в 9:49, , рубрики: хабродвижок

Несколько дней назад, @vconst написал пост "Верни трекер! (С)" с воззваниями по поводу изменения функциональности трекера в новой версии Хабра.

У меня возникла идея, что используя только расширение браузера возможно реализовать трекер с фактически любой функциональностью.

В качестве Proof-of-Concept, я попробовал самую простую идею, добавить старую версию трекера в правый сайдбар. Всё получилось даже проще, чем казалось.

Исходный код скрипта
(function () {
    function injectTracker() {

        const MAX_TRACKS = 10;

        // To add the tracker as a first block on the right sidebar, change "false" to "true"
        const insertAsFirstBlock = false;

        // set "true" if you want to tract articles only with unseen comments
        const onlyWithUnseenComments = false;

        // styling
        const trackerBlockCss = `<style scoped>
        .post-info__title a { color: #444; }
        a[href*="#comments"] { color: #82a3b1; }
        a[href*="#first_unread"] { color: #cf0000; }
        span.tracker { position: absolute; right: 0; }
        span.remove-tracker { position: absolute; right: 0;  display: none; }
        li:hover > .post-info__title span.remove-tracker { display: inline-block; color: #888; }
        .post-info__title:hover span.remove-tracker:hover { color: #444; }
        .tracker-refresh-icon { cursor: pointer; }
        .icon-anim { animation-name: rotate; animation-duration: 1s; animation-iteration-count: infinite; animation-timing-function: linear; animation-play-state: running; } 
        </style>`;

        const deleteSvg = '<svg class="delete-icon"  xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" style="vertical-align: middle;" height="16" width="16"><path d="m16.5 33.6 7.5-7.5 7.5 7.5 2.1-2.1-7.5-7.5 7.5-7.5-2.1-2.1-7.5 7.5-7.5-7.5-2.1 2.1 7.5 7.5-7.5 7.5ZM24 44q-4.1 0-7.75-1.575-3.65-1.575-6.375-4.3-2.725-2.725-4.3-6.375Q4 28.1 4 24q0-4.15 1.575-7.8 1.575-3.65 4.3-6.35 2.725-2.7 6.375-4.275Q19.9 4 24 4q4.15 0 7.8 1.575 3.65 1.575 6.35 4.275 2.7 2.7 4.275 6.35Q44 19.85 44 24q0 4.1-1.575 7.75-1.575 3.65-4.275 6.375t-6.35 4.3Q28.15 44 24 44Zm0-3q7.1 0 12.05-4.975Q41 31.05 41 24q0-7.1-4.95-12.05Q31.1 7 24 7q-7.05 0-12.025 4.95Q7 16.9 7 24q0 7.05 4.975 12.025Q16.95 41 24 41Zm0-17Z"/></svg>';
        const refeshSvg = '<svg class="tracker-refresh-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" style="vertical-align: middle;" height="20" width="20" ><path d="M24 40q-6.65 0-11.325-4.675Q8 30.65 8 24q0-6.65 4.675-11.325Q17.35 8 24 8q4.25 0 7.45 1.725T37 14.45V8h3v12.7H27.3v-3h8.4q-1.9-3-4.85-4.85Q27.9 11 24 11q-5.45 0-9.225 3.775Q11 18.55 11 24q0 5.45 3.775 9.225Q18.55 37 24 37q4.15 0 7.6-2.375 3.45-2.375 4.8-6.275h3.1q-1.45 5.25-5.75 8.45Q29.45 40 24 40Z"/></svg>';

        const oldTrackerBlock = animateRefreshIcon();

        fetch('https://habr.com/ru/tracker/')
            .then(response => response.text())
            .then(response => processTracker(response));

        function processTracker(response) {
            const m = response.match(/<form[^>]+id="tracker_feed_form"[^>]*>(.*?)</form>/s);
            if (!m)
                return;

            const d = document.querySelector('.sidebar_right');
            if (!d)
                return;

            oldTrackerBlock?.parentNode.removeChild(oldTrackerBlock);

            const parseDiv = document.createElement("div");
            parseDiv.innerHTML = m[1];

            const newDiv = addNewElement(d, 'div', 'default-block default-block_sidebar block-tracker', trackerBlockCss, insertAsFirstBlock);

            const headerDiv = addNewElement(newDiv, 'div', 'default-block__header', '<h3 class="default-block__header-title"><a href="https://habr.com/ru/tracker/" target="_blank">Tracker</a> <span class="tracker-refresh-btn" >' + refeshSvg + '</span></h3> ');
            headerDiv.querySelector('.tracker-refresh-btn').addEventListener('click', injectTracker);

            const newContent = addNewElement(newDiv, 'div', 'default-block__content');
            const newUl = addNewElement(newContent, 'ul', 'content-list content-list_most-read');

            let currentTrack = 0;

            [...parseDiv.querySelectorAll('table.tracker-table tr')].filter(el => {
                if (currentTrack >= MAX_TRACKS)
                    return false;

                currentTrack += addTrack(el, newUl);
            });

            if (currentTrack === 0)
                addNewElement(newContent, 'div', '', onlyWithUnseenComments ? 'No articles with new comments' : '<a href="https://habr.com/ru/tracker/" target="_blank">No articles to track</a>');

            parseDiv.remove();
        }

        function addTrack(el, newUl) {
            let firstTd = el.querySelector('td:nth-child(1)');
            if (!firstTd)
                return 0;

            const trackerHtml = el.querySelector('td:nth-child(3)')?.innerHTML;

            if (onlyWithUnseenComments && !trackerHtml?.includes('+'))
                return 0;

            const newLi = addNewElement(newUl, 'li', 'content-list__item content-list__item_devided post-info');
            const newTopDiv = addNewElement(newLi, 'div', 'tracker-stats-info');

            addNewElement(newTopDiv, 'span', 'post_author', firstTd?.innerHTML.replaceAll('<a ', '<a target="_blank" '));
            addNewElement(newTopDiv, 'span', 'tracker', trackerHtml.replaceAll('<a ', '<a target="_blank" '));

            const newPost = addNewElement(newLi, 'div', 'post-info__title', el.querySelector('td:nth-child(2)')?.innerHTML.replaceAll('<a ', '<a target="_blank" ') + `<span class="remove-tracker" title="Remove from tracker">${deleteSvg}</span>`);
            newPost.querySelector('.remove-tracker').addEventListener('click', removeTracker);

            return 1;
        }

        function removeTracker(event) {

            const arctileEl = event.currentTarget.parentNode.querySelector('* > a');
            if (!arctileEl)
                return;

            const m = arctileEl.getAttribute('href')?.match(//([0-9]+)//);
            if (!m)
                return;

            const postId = m[1];

            event.currentTarget.classList.add('icon-anim');

            fetch('https://habr.com/json/tracker/feed/remove/',
                {
                    method: 'POST',
                    body: `post[${postId}]=on`,
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }
                }).then(() => injectTracker());
        }

        function addNewElement(parentElement, tageName, className, html, makeAsFirstChild) {
            const newEl = document.createElement(tageName);

            if (makeAsFirstChild)
                parentElement.insertBefore(newEl, parentElement.firstChild);
            else
                parentElement.appendChild(newEl);

            if (className)
                newEl.className = className;

            if (html)
                newEl.innerHTML = html;

            return newEl;
        }

        function animateRefreshIcon() {
            let oldTrackerBlock = document.querySelector('div.block-tracker');
            if (oldTrackerBlock)
                document.querySelector('.tracker-refresh-icon')?.classList.add('icon-anim');

            return oldTrackerBlock;
        }
    }

    injectTracker();
})();

Если вы используете Greasemonkey  или подобное расширение, то можете добавить этот скрипт для сайта хабра и на каждой странице у вас будет новая секция "tracker".

Я использую кастомизированный HabrSanitizer и если вы также его поклонник и хотели бы добавить функциональность трекера к нему, то надо загрузить исходники к себе на локальный компьютер, добавить его в качестве локального расширения (Load Unpacked) и вставить код скрипта перед строчкой (на сегодня это строка 397)

const isOnPersonalPage = onPersonalPage(window.location.href);

Можно протестировать функциональность и без установки расширения. На любой странице хабра нужно открыть DevTools и вставить код этого скрипта в Console. Но конечно это единовременное изменение страницы и при навигации со страницы, трэкер блок будет удалён и на новой странице необходимо будет повторить процедуру ещё раз.

Ещё раз - это скрипт работает только для старой версии интерфейса хабра.

Если всё сделано правильно то вы увидете под блоком "Читают сейчас" новый блок, "Tracker".

Что-то типа:

Пятничный трекер - 1

Следующий шаг будет попробовать что-то типа этого для новой версии.

Единственная проблема, это то что продётся читать не одну дополнительную страницу, а десять или более. Так что чтобы не генерировать избыточную нагрузку, может быть Хабру имеет смысл не бороться с пользователями, а прислушаться и мигрировать старый трекер на новый интерфейс. Это должно быть довольно просто.

Всем удачных выходных!

Автор:
hbn3

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js