Infinite Scroll VOZ

Infinite Scroll VOZ - Lướt voz.vn nhanh gọn như lướt facebook. https://github.com/ReeganExE/voz-infinite-scroll

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         Infinite Scroll VOZ
// @namespace    https://voz.vn
// @version      3.3.1846240259
// @description  Infinite Scroll VOZ - Lướt voz.vn nhanh gọn như lướt facebook. https://github.com/ReeganExE/voz-infinite-scroll
// @author       Ninh Pham (ReeganExE), Nguyen Duy Tiep (green-leaves)
// @match        https://voz.vn/t/*
// @grant        GM_addStyle
// ==/UserScript==

GM_addStyle(`
.hide {display: none} .show{display: block}
.fixed-page {
    position: fixed;
    left: 13px;
    bottom: 20px;
}

.reply-form.hide .message-cell--main {
  display: none;
}
.reply-form {
    position: fixed;
    bottom: -800px;
    z-index: 100;
    border: solid 1px #616161;
    box-shadow: 1px 1px 10px 4px #585858;
    border-radius: 2px;
    -webkit-transition: bottom .2s ease-in-out;
    -moz-transition: bottom .2s ease-in-out;
    -o-transition: bottom .2s ease-in-out;
    transition: bottom .2s ease-in-out;
}

.reply-form .block-body {
  margin: 0;
  border: solid 1px #a5a5a5!important;
}

.reply-button {
  position: fixed;
  bottom: 30px;
  z-index: 100;
  right: 70px;
}
`);
(function () {
    const PAGE_WRAPPER_SELECTOR = '.pageNav-main';
    const POST_BODY_SELECTOR = '.js-replyNewMessageContainer';
    const parser = new DOMParser();
    const posts = document.querySelector(POST_BODY_SELECTOR);
    let currentPage = +getCurrentPage();
    let lastPage = +getLastPage();
    let isLoading = false;
    let threadId = '';
    const PAGE_REG = /Page \d+/;
    const BUFFER_HEIGHT = 400; // Magic number, to load next page before reach the end.
    const loadingSpinHTML = 
    '<div class="" style="width: 160px;margin: 0 auto;padding: 20px;text-align: center;color: white;">Loading... <img src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA=="/></div>';
    const loadingSpin = document.createElement('div');
    loadingSpin.innerHTML = loadingSpinHTML;
    loadingSpin.className = 'hide';
    main({
        getThreadId() {
            return location.pathname.match(/\/t\/.*\.(\d+)\/?/)[1];
        }
    });
    function main({ getThreadId }) {
        const pageNavWrappers = document.querySelectorAll(PAGE_WRAPPER_SELECTOR);
        if (pageNavWrappers.length) {
            pageNavWrappers[pageNavWrappers.length - 1].classList.add('fixed-page');
        }
        // Reply form
        handleReplyForm();
        function handleReplyForm() {
            const repyForm = document.querySelector('form.js-quickReply');
            if (repyForm) {
                repyForm.classList.add('reply-form');
                // Reply button
                const replyButton = htmlToElement(`
      <button type="button" class="button--primary button button--icon button--icon--reply reply-button">
        <span class="button-text">
          Ẩn/Hiện Reply
        </span>
      </button>
    `);
                const post = document.querySelector('.message.message--post.js-post.js-inlineModContainer');
                repyForm.style.width = `${post.clientWidth}px`;
                let show = false;
                function hideReplyForm() {
                    show = false;
                    repyForm.style.bottom = '-800px';
                }
                function showReplyForm() {
                    show = true;
                    repyForm.style.bottom = '40px';
                }
                repyForm.addEventListener('keydown', (e) => {
                    if (e.keyCode === 27) {
                        hideReplyForm();
                        show = false;
                    }
                });
                replyButton.addEventListener('click', () => {
                    show = !show;
                    if (show) {
                        showReplyForm();
                    }
                    else {
                        hideReplyForm();
                    }
                });
                document.body.appendChild(replyButton);
                setTimeout(() => {
                    jQuery(document).on('xf-click:before-click.XFQuoteClick', showReplyForm);
                }, 1000);
            }
        }
        if (posts) {
            const postsOffsetTop = getCoords(posts).top;
            threadId = getThreadId();
            insertAfter(posts, loadingSpin);
            window.addEventListener('scroll', () => {
                if (window.scrollY + window.innerHeight + BUFFER_HEIGHT >=
                    posts.offsetHeight + postsOffsetTop) {
                    if (isLoadable()) {
                        loadingSpin.className = 'show';
                        isLoading = true;
                        loadThreadPage(threadId, ++currentPage, (loadedDoc) => {
                            pushState(currentPage);
                            posts.innerHTML += loadedDoc.querySelector(POST_BODY_SELECTOR).innerHTML;
                            updatePageNavigator(loadedDoc);
                            lastPage = getLastPage(loadedDoc);
                            isLoading = false;
                            loadingSpin.className = 'hide';
                            // Reintialize the events handler for quotes, reply, report, reaction
                            XF.activate(posts);
                        });
                    }
                }
            });
        }
    }
    function pushState(currentPage) {
        let { title } = document;
        if (PAGE_REG.test(title)) {
            title = title.replace(PAGE_REG, `Page ${currentPage}`);
        }
        else {
            title = `${title} Page ${currentPage}`;
        }
        let [root] = location.href.split('/page-');
        if (!root.endsWith('/')) {
            root += '/';
        }
        root += `page-${currentPage}`;
        history.pushState({}, title, root + location.search);
        document.title = title;
    }
    function isLoadable() {
        return !isLoading && currentPage < lastPage;
    }
    function getCurrentPage() {
        const page = document.querySelector('.pageNav-main .pageNav-page--current');
        return page ? Number(page.textContent.trim()) : 1;
    }
    function getLastPage(doc) {
        if (!doc)
            doc = document;
        const page = doc.querySelector('.pageNav-page:last-child a');
        return page ? Number(page.textContent.trim()) : 1;
    }
    function updatePageNavigator(loadedDoc) {
        const pageNavs = document.querySelectorAll(PAGE_WRAPPER_SELECTOR);
        const newHtmlNav = loadedDoc.querySelector(PAGE_WRAPPER_SELECTOR).innerHTML;
        for (let i = 0; i < pageNavs.length; i++) {
            pageNavs[i].innerHTML = newHtmlNav;
        }
    }
    function getCoords(elem) {
        // crossbrowser version
        const box = elem.getBoundingClientRect();
        const { body } = document;
        const docEl = document.documentElement;
        const scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
        const scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
        const clientTop = docEl.clientTop || body.clientTop || 0;
        const clientLeft = docEl.clientLeft || body.clientLeft || 0;
        const top = box.top + scrollTop - clientTop;
        const left = box.left + scrollLeft - clientLeft;
        return { top: Math.round(top), left: Math.round(left) };
    }
    function loadThreadPage(threadId, pageNo, callback) {
        ajax('GET', `/t/ok.${threadId}/page-${pageNo}`, (xhr) => callback(parser.parseFromString(xhr.responseText, 'text/html')));
    }
    function ajax(method, url, callback) {
        const xhr = new XMLHttpRequest();
        xhr.open(method, url);
        xhr.send(null);
        xhr.onreadystatechange = function () {
            const DONE = 4; // readyState 4 means the request is done.
            const OK = 200; // status 200 is a successful return.
            if (xhr.readyState === DONE) {
                if (xhr.status === OK) {
                    callback(xhr);
                }
                else {
                    console.log(`Error: ${xhr.status}`); // An error occurred during the request.
                }
            }
        };
    }
    function insertAfter(referenceNode, newNode) {
        referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
    }
    /**
     * @param {String} HTML representing a single element
     * @return {Element}
     * @author https://stackoverflow.com/a/35385518/1099314
     */
    function htmlToElement(html) {
        const template = document.createElement('template');
        html = html.trim(); // Never return a text node of whitespace as the result
        template.innerHTML = html;
        return template.content.firstChild;
    }
})();