2020-06-06 00:03:37 +02:00
|
|
|
"use strict";
|
2016-04-12 23:49:46 +02:00
|
|
|
|
2020-06-06 00:03:37 +02:00
|
|
|
const router = require("../router.js");
|
|
|
|
const views = require("../util/views.js");
|
2016-04-12 23:49:46 +02:00
|
|
|
|
2020-06-06 00:03:37 +02:00
|
|
|
const holderTemplate = views.getTemplate("endless-pager");
|
|
|
|
const pageTemplate = views.getTemplate("endless-pager-page");
|
2016-06-14 10:31:48 +02:00
|
|
|
|
2017-02-26 12:57:24 +01:00
|
|
|
function isScrolledIntoView(element) {
|
|
|
|
let top = 0;
|
|
|
|
do {
|
|
|
|
top += element.offsetTop || 0;
|
|
|
|
element = element.offsetParent;
|
2020-06-04 20:09:35 +02:00
|
|
|
} while (element);
|
2020-06-06 00:03:37 +02:00
|
|
|
return top >= window.scrollY && top <= window.scrollY + window.innerHeight;
|
2017-02-19 13:33:13 +01:00
|
|
|
}
|
|
|
|
|
2016-04-12 23:49:46 +02:00
|
|
|
class EndlessPageView {
|
2016-06-14 10:31:48 +02:00
|
|
|
constructor(ctx) {
|
2020-06-06 00:03:37 +02:00
|
|
|
this._hostNode = document.getElementById("content-holder");
|
2016-08-28 18:53:06 +02:00
|
|
|
views.replaceContent(this._hostNode, holderTemplate());
|
|
|
|
}
|
|
|
|
|
|
|
|
run(ctx) {
|
2017-02-19 13:33:13 +01:00
|
|
|
this._destroy();
|
|
|
|
|
2016-05-20 21:35:12 +02:00
|
|
|
this._active = true;
|
2017-02-19 13:33:13 +01:00
|
|
|
this._runningRequests = 0;
|
|
|
|
this._initialPageLoad = true;
|
2016-04-12 23:49:46 +02:00
|
|
|
|
2016-09-10 11:15:43 +02:00
|
|
|
this.clearMessages();
|
2016-08-28 18:53:06 +02:00
|
|
|
views.emptyContent(this._pagesHolderNode);
|
|
|
|
|
2017-02-09 00:48:06 +01:00
|
|
|
this.minOffsetShown = null;
|
|
|
|
this.maxOffsetShown = null;
|
|
|
|
this.totalRecords = null;
|
|
|
|
this.currentOffset = 0;
|
2017-02-19 13:33:13 +01:00
|
|
|
this.defaultLimit = parseInt(ctx.parameters.limit || ctx.defaultLimit);
|
2017-02-09 00:48:06 +01:00
|
|
|
|
2017-02-19 13:33:13 +01:00
|
|
|
const initialOffset = parseInt(ctx.parameters.offset || 0);
|
2020-06-06 00:03:37 +02:00
|
|
|
this._loadPage(ctx, initialOffset, this.defaultLimit, true).then(
|
|
|
|
(pageNode) => {
|
2017-02-19 13:33:13 +01:00
|
|
|
if (initialOffset !== 0) {
|
2017-02-09 00:48:06 +01:00
|
|
|
pageNode.scrollIntoView();
|
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
}
|
|
|
|
);
|
2017-02-19 13:33:13 +01:00
|
|
|
|
|
|
|
this._timeout = window.setInterval(() => {
|
|
|
|
window.requestAnimationFrame(() => {
|
|
|
|
this._probePageLoad(ctx);
|
|
|
|
this._syncUrl(ctx);
|
|
|
|
});
|
|
|
|
}, 250);
|
2016-08-24 10:55:31 +02:00
|
|
|
|
|
|
|
views.monitorNodeRemoval(this._pagesHolderNode, () => this._destroy());
|
|
|
|
}
|
|
|
|
|
2016-08-28 18:53:06 +02:00
|
|
|
get pageHeaderHolderNode() {
|
2020-06-06 00:03:37 +02:00
|
|
|
return this._hostNode.querySelector(".page-header-holder");
|
2016-08-28 18:53:06 +02:00
|
|
|
}
|
|
|
|
|
2017-02-19 13:33:13 +01:00
|
|
|
get topPageGuardNode() {
|
2020-06-06 00:03:37 +02:00
|
|
|
return this._hostNode.querySelector(".page-guard.top");
|
2017-02-19 13:33:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
get bottomPageGuardNode() {
|
2020-06-06 00:03:37 +02:00
|
|
|
return this._hostNode.querySelector(".page-guard.bottom");
|
2017-02-19 13:33:13 +01:00
|
|
|
}
|
|
|
|
|
2016-08-28 18:53:06 +02:00
|
|
|
get _pagesHolderNode() {
|
2020-06-06 00:03:37 +02:00
|
|
|
return this._hostNode.querySelector(".pages-holder");
|
2016-08-28 18:53:06 +02:00
|
|
|
}
|
|
|
|
|
2016-08-24 10:55:31 +02:00
|
|
|
_destroy() {
|
2017-02-19 13:33:13 +01:00
|
|
|
window.clearInterval(this._timeout);
|
2016-08-24 10:55:31 +02:00
|
|
|
this._active = false;
|
2016-05-09 11:17:04 +02:00
|
|
|
}
|
2016-04-12 23:49:46 +02:00
|
|
|
|
2017-02-19 13:33:13 +01:00
|
|
|
_syncUrl(ctx) {
|
2016-06-12 21:01:18 +02:00
|
|
|
let topPageNode = null;
|
|
|
|
let element = document.elementFromPoint(
|
|
|
|
window.innerWidth / 2,
|
2020-06-06 00:03:37 +02:00
|
|
|
window.innerHeight / 2
|
|
|
|
);
|
2016-06-12 21:01:18 +02:00
|
|
|
while (element.parentNode !== null) {
|
2020-06-06 00:03:37 +02:00
|
|
|
if (element.classList.contains("page")) {
|
2016-06-12 21:01:18 +02:00
|
|
|
topPageNode = element;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
element = element.parentNode;
|
|
|
|
}
|
|
|
|
if (!topPageNode) {
|
|
|
|
return;
|
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
let topOffset = parseInt(topPageNode.getAttribute("data-offset"));
|
|
|
|
let topLimit = parseInt(topPageNode.getAttribute("data-limit"));
|
2017-02-09 00:48:06 +01:00
|
|
|
if (topOffset !== this.currentOffset) {
|
2016-06-12 21:01:18 +02:00
|
|
|
router.replace(
|
2017-02-09 00:48:06 +01:00
|
|
|
ctx.getClientUrlForPage(
|
|
|
|
topOffset,
|
2020-06-06 00:03:37 +02:00
|
|
|
topLimit === ctx.defaultLimit ? null : topLimit
|
|
|
|
),
|
2016-07-08 00:46:48 +02:00
|
|
|
ctx.state,
|
2020-06-06 00:03:37 +02:00
|
|
|
false
|
|
|
|
);
|
2017-02-09 00:48:06 +01:00
|
|
|
this.currentOffset = topOffset;
|
2016-06-12 21:01:18 +02:00
|
|
|
}
|
2017-02-19 13:33:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_probePageLoad(ctx) {
|
|
|
|
if (!this._active || this._runningRequests) {
|
|
|
|
return;
|
|
|
|
}
|
2016-06-12 21:01:18 +02:00
|
|
|
|
2017-02-09 00:48:06 +01:00
|
|
|
if (this.totalRecords === null) {
|
2016-06-12 21:01:18 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-06 00:03:37 +02:00
|
|
|
if (
|
|
|
|
this.minOffsetShown > 0 &&
|
|
|
|
isScrolledIntoView(this.topPageGuardNode)
|
|
|
|
) {
|
2017-02-09 00:48:06 +01:00
|
|
|
this._loadPage(
|
2017-02-19 13:33:13 +01:00
|
|
|
ctx,
|
|
|
|
this.minOffsetShown - this.defaultLimit,
|
|
|
|
this.defaultLimit,
|
2020-06-06 00:03:37 +02:00
|
|
|
false
|
|
|
|
);
|
2017-02-19 13:33:13 +01:00
|
|
|
}
|
|
|
|
|
2020-06-06 00:03:37 +02:00
|
|
|
if (
|
|
|
|
this.maxOffsetShown < this.totalRecords &&
|
|
|
|
isScrolledIntoView(this.bottomPageGuardNode)
|
|
|
|
) {
|
|
|
|
this._loadPage(ctx, this.maxOffsetShown, this.defaultLimit, true);
|
2016-06-12 21:01:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-09 00:48:06 +01:00
|
|
|
_loadPage(ctx, offset, limit, append) {
|
2017-02-19 13:33:13 +01:00
|
|
|
this._runningRequests++;
|
2016-07-13 17:18:28 +02:00
|
|
|
return new Promise((resolve, reject) => {
|
2020-06-06 00:03:37 +02:00
|
|
|
ctx.requestPage(offset, limit).then(
|
|
|
|
(response) => {
|
|
|
|
if (!this._active) {
|
|
|
|
this._runningRequests--;
|
|
|
|
return Promise.reject();
|
|
|
|
}
|
|
|
|
window.requestAnimationFrame(() => {
|
|
|
|
let pageNode = this._renderPage(ctx, append, response);
|
|
|
|
this._runningRequests--;
|
|
|
|
resolve(pageNode);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
(error) => {
|
|
|
|
this.showError(error.message);
|
2017-02-19 13:33:13 +01:00
|
|
|
this._runningRequests--;
|
2020-06-06 00:03:37 +02:00
|
|
|
reject();
|
2016-07-13 17:18:28 +02:00
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
);
|
2016-04-12 23:49:46 +02:00
|
|
|
});
|
|
|
|
}
|
2016-06-12 21:01:18 +02:00
|
|
|
|
2017-02-09 00:48:06 +01:00
|
|
|
_renderPage(ctx, append, response) {
|
2016-07-13 17:18:28 +02:00
|
|
|
let pageNode = null;
|
|
|
|
|
2016-06-12 21:01:18 +02:00
|
|
|
if (response.total) {
|
2016-07-13 17:18:28 +02:00
|
|
|
pageNode = pageTemplate({
|
2017-02-09 00:48:06 +01:00
|
|
|
totalPages: Math.ceil(response.total / response.limit),
|
|
|
|
page: Math.ceil(
|
2020-06-06 00:03:37 +02:00
|
|
|
(response.offset + response.limit) / response.limit
|
|
|
|
),
|
2017-02-09 00:48:06 +01:00
|
|
|
});
|
2020-06-06 00:03:37 +02:00
|
|
|
pageNode.setAttribute("data-offset", response.offset);
|
|
|
|
pageNode.setAttribute("data-limit", response.limit);
|
2017-02-09 00:48:06 +01:00
|
|
|
|
|
|
|
ctx.pageRenderer({
|
|
|
|
parameters: ctx.parameters,
|
|
|
|
response: response,
|
2020-06-06 00:03:37 +02:00
|
|
|
hostNode: pageNode.querySelector(".page-content-holder"),
|
2016-06-12 21:01:18 +02:00
|
|
|
});
|
|
|
|
|
2017-02-09 00:48:06 +01:00
|
|
|
this.totalRecords = response.total;
|
2016-06-12 21:01:18 +02:00
|
|
|
|
2020-06-06 00:03:37 +02:00
|
|
|
if (
|
|
|
|
response.offset < this.minOffsetShown ||
|
|
|
|
this.minOffsetShown === null
|
|
|
|
) {
|
2017-02-09 00:48:06 +01:00
|
|
|
this.minOffsetShown = response.offset;
|
2016-06-12 21:01:18 +02:00
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
if (
|
|
|
|
response.offset + response.results.length >
|
|
|
|
this.maxOffsetShown ||
|
|
|
|
this.maxOffsetShown === null
|
|
|
|
) {
|
2017-02-09 00:48:06 +01:00
|
|
|
this.maxOffsetShown =
|
|
|
|
response.offset + response.results.length;
|
2016-06-12 21:01:18 +02:00
|
|
|
}
|
2020-06-06 00:03:37 +02:00
|
|
|
response.results.addEventListener("remove", (e) => {
|
2017-02-09 22:39:28 +01:00
|
|
|
this.maxOffsetShown--;
|
|
|
|
this.totalRecords--;
|
|
|
|
});
|
2016-06-12 21:01:18 +02:00
|
|
|
|
|
|
|
if (append) {
|
2016-06-14 10:31:48 +02:00
|
|
|
this._pagesHolderNode.appendChild(pageNode);
|
2017-02-19 13:33:13 +01:00
|
|
|
if (this._initialPageLoad && response.offset > 0) {
|
2016-06-12 21:01:18 +02:00
|
|
|
window.scroll(0, pageNode.getBoundingClientRect().top);
|
2016-06-14 10:31:48 +02:00
|
|
|
}
|
2016-06-12 21:01:18 +02:00
|
|
|
} else {
|
2016-06-14 10:31:48 +02:00
|
|
|
this._pagesHolderNode.prependChild(pageNode);
|
2016-06-12 21:01:18 +02:00
|
|
|
|
|
|
|
window.scroll(
|
|
|
|
window.scrollX,
|
2020-06-06 00:03:37 +02:00
|
|
|
window.scrollY + pageNode.offsetHeight
|
|
|
|
);
|
2016-06-12 21:01:18 +02:00
|
|
|
}
|
2017-02-09 00:48:06 +01:00
|
|
|
} else if (!response.results.length) {
|
2020-06-06 00:03:37 +02:00
|
|
|
this.showInfo("No data to show");
|
2016-06-12 21:01:18 +02:00
|
|
|
}
|
2016-07-13 17:18:28 +02:00
|
|
|
|
2017-02-19 13:33:13 +01:00
|
|
|
this._initialPageLoad = false;
|
2016-07-13 17:18:28 +02:00
|
|
|
return pageNode;
|
2016-06-12 21:01:18 +02:00
|
|
|
}
|
2016-06-14 10:31:48 +02:00
|
|
|
|
2016-09-10 11:15:43 +02:00
|
|
|
clearMessages() {
|
|
|
|
views.clearMessages(this._hostNode);
|
|
|
|
}
|
|
|
|
|
2016-06-14 10:31:48 +02:00
|
|
|
showSuccess(message) {
|
|
|
|
views.showSuccess(this._hostNode, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
showError(message) {
|
|
|
|
views.showError(this._hostNode, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
showInfo(message) {
|
|
|
|
views.showInfo(this._hostNode, message);
|
|
|
|
}
|
2016-04-12 23:49:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = EndlessPageView;
|