'use strict'; const page = require('page'); const events = require('../events.js'); const views = require('../util/views.js'); class EndlessPageView { constructor() { this.holderTemplate = views.getTemplate('endless-pager'); this.pageTemplate = views.getTemplate('endless-pager-page'); } render(ctx) { const target = document.getElementById('content-holder'); const source = this.holderTemplate(); const pageHeaderHolder = source.querySelector('.page-header-holder'); const pagesHolder = source.querySelector('.pages-holder'); views.listenToMessages(target); views.showView(target, source); let headerRendererCtx = ctx; headerRendererCtx.target = pageHeaderHolder; ctx.headerRenderer.render(headerRendererCtx); const threshold = window.innerHeight / 3; if (ctx.state && ctx.state.html) { this.minPageShown = ctx.state.minPageShown; this.maxPageShown = ctx.state.maxPageShown; this.totalPages = ctx.state.totalPages; this.currentPage = ctx.state.currentPage; } else { this.minPageShown = null; this.maxPageShown = null; this.totalPages = null; this.currentPage = null; } this.fetching = false; this.updater = () => { let topPage = null; let allPageNodes = pagesHolder.querySelectorAll('.page'); for (let pageNode of allPageNodes) { if (pageNode.getBoundingClientRect().bottom >= 0) { topPage = parseInt(pageNode.getAttribute('data-page')); break; } } if (topPage !== this.currentPage) { page.replace( ctx.clientUrl.format({page: topPage}), { minPageShown: this.minPageShown, maxPageShown: this.maxPageShown, totalPages: this.totalPages, currentPage: this.currentPage, html: pagesHolder.innerHTML, scrollX: window.scrollX, scrollY: window.scrollY, }, false, false); this.currentPage = topPage; } if (this.fetching || this.totalPages === null) { return; } let scrollHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight; if (this.minPageShown > 1 && window.scrollY - threshold < 0) { this.loadPage(pagesHolder, ctx, this.minPageShown - 1, false); } else if (this.maxPageShown < this.totalPages && window.scrollY + threshold > scrollHeight) { this.loadPage(pagesHolder, ctx, this.maxPageShown + 1, true); } }; if (ctx.state && ctx.state.html) { pagesHolder.innerHTML = ctx.state.html; window.scroll(ctx.state.scrollX, ctx.state.scrollY); this.updater(); } else { this.loadPage(pagesHolder, ctx, ctx.searchQuery.page, true); } window.addEventListener('scroll', this.updater, true); } unrender() { window.removeEventListener('scroll', this.updater, true); } loadPage(pagesHolder, ctx, pageNumber, append) { this.fetching = true; if (pageNumber < this.minPageShown || this.minPageShown === null) { this.minPageShown = pageNumber; } if (pageNumber > this.maxPageShown || this.maxPageShown === null) { this.maxPageShown = pageNumber; } ctx.requestPage(pageNumber).then(response => { this.totalPages = Math.ceil(response.total / response.pageSize); if (response.total) { const pageNode = this.pageTemplate({ page: pageNumber, totalPages: this.totalPages, }); pageNode.setAttribute('data-page', pageNumber); let pageRendererCtx = response; pageRendererCtx.target = pageNode.querySelector( '.page-content-holder'); ctx.pageRenderer.render(pageRendererCtx); if (append) { pagesHolder.appendChild(pageNode); } else { pagesHolder.prependChild(pageNode); // note: with probability of 75%, if the user has scrolled // with a mouse wheel, chrome 49 doesn't give a slightest // fuck about this and loads all the way to page 1 at once window.scroll( window.scrollX, window.scrollY + pageNode.offsetHeight); } } this.fetching = false; this.updater(); if (response.total <= (pageNumber - 1) * response.pageSize) { events.notify(events.Info, 'No data to show'); } }, response => { events.notify(events.Error, response.description); }); } } module.exports = EndlessPageView;