// ==UserScript== // @name Infinite scroller // @namespace Violentmonkey Scripts // @match https://*.arca.live/* // @grant none // @version 1.0 // @author awk // @description 2021. 1. 6. 오전 11:17:30 // @downloadURL none // ==/UserScript== // bounce-back element size const bounceBackBuffer = 96; // last-checked page let scrolledPage = 1; let isLoading = false; const bounceBack = () => { if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { scrollBy({ 'top': -bounceBackBuffer, 'left': 0, 'behavior': 'smooth' }); } }; const getArticleId = elem => ~~elem.href.match(/\/b\/[^/]+\/(\d+)/)[1]; const loadMore = async () => { if(isLoading) return; isLoading = true; // true if scrolledPage value is reached to unread page let reached = false; const currentLastArticle = document.querySelector('.vrow:last-child'); const currentLastArticleId = getArticleId(currentLastArticle); // determine if current page doesn't include any article if(!currentLastArticleId) { isLoading = false; return; } while(!reached) { ++scrolledPage; // get next-page url const nextPage = (() => { // determine if current url includes page information if(location.href.match(/[&?]p=(\d+)/)) { return location.href.replace(/[&?]p=\d+/, pageString => { return pageString.replace(/\d+/, scrolledPage); }); } // determine if current url includes query string else if(location.href.match(/\?.+?=.+?/)){ return `${location.href}&p=${scrolledPage}`; } // it might be first-page of the board else { return `${location.href}?p=${scrolledPage}`; } })(); console.log(nextPage); // fetch and parse next-page DOM const nextPageDOM = await fetch(nextPage) .then(res => res.text()) .then(pageText => new DOMParser().parseFromString(pageText, 'text/html')); const nextPageLastArticle = nextPageDOM.querySelector('.vrow:last-child'); // determine if reached to end if(!nextPageLastArticle || nextPageLastArticle.classList.length != 1) { // reached to end bounceBack(); break; } // determine if reached to unread page const nextPageLastArticleId = getArticleId(nextPageLastArticle); if(currentLastArticleId > nextPageLastArticleId) { // filter unread articles const unreadArticles = [...nextPageDOM.querySelectorAll('.vrow')].filter(article => { // filter out notices if(article.classList.length != 1) return false; return getArticleId(article) < currentLastArticleId; }); // attach unread articles const articleList = document.querySelector('.list-table'); unreadArticles.forEach(article => articleList.appendChild(article)); break; } } isLoading = false; return; } window.addEventListener('load', e => { // load current page const pageMatch = location.href.match(/[&?]p=(\d+)/); console.log(location.href); scrolledPage = pageMatch ? ~~pageMatch[1] : 1; // attach bounce-back element const bounceBackElement = document.createElement('div'); bounceBackElement.style.width = 'inherit'; bounceBackElement.style.height = `${bounceBackBuffer}px`; // center-align text bounceBackElement.style.textAlign = 'center'; bounceBackElement.style.lineHeight = `${bounceBackBuffer}px`; // element color bounceBackElement.style.backgroundColor = 'grey'; bounceBackElement.style.color = 'white'; // element text bounceBackElement.innerText = 'Loading next page...'; bounceBackElement.fontWeight = 'bold'; document.body.appendChild(bounceBackElement); // apply smooth-scroll for bounce-back document.body.style.scrollBehavior = 'smooth'; }); window.addEventListener('scroll', e => { if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { loadMore(); } });