From 69a0fde476196a20e1e27f2c173582f637535a84 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Tue, 1 Sep 2026 22:03:19 +0600 Subject: [PATCH 1/3] feat(pdoPage): remove jQuery dependency (#315) Replace dual jQuery frontends with one vanilla pdopage.js using fetch, URLSearchParams, CSS sticky, and IntersectionObserver. Drop sticky plugin and jquery.pdopage.js; emit CustomEvent pdopage:load. --- Gruntfile.js | 14 +- _build/build.config.php | 2 +- assets/components/pdotools/css/pdopage.css | 23 +- .../components/pdotools/css/pdopage.min.css | 2 +- .../components/pdotools/js/jquery.pdopage.js | 381 ------- .../pdotools/js/jquery.pdopage.min.js | 1 - .../pdotools/js/lib/jquery.sticky.js | 170 ---- .../pdotools/js/lib/jquery.sticky.min.js | 1 - assets/components/pdotools/js/pdopage.js | 661 +++++++------ assets/components/pdotools/js/pdopage.min.js | 2 +- core/components/pdotools/docs/changelog.txt | 6 + .../pdotools/lexicon/de/properties.inc.php | 10 +- .../pdotools/lexicon/en/properties.inc.php | 10 +- .../pdotools/lexicon/ru/properties.inc.php | 10 +- package-lock.json | 934 +----------------- package.json | 2 +- 16 files changed, 453 insertions(+), 1776 deletions(-) delete mode 100644 assets/components/pdotools/js/jquery.pdopage.js delete mode 100644 assets/components/pdotools/js/jquery.pdopage.min.js delete mode 100644 assets/components/pdotools/js/lib/jquery.sticky.js delete mode 100644 assets/components/pdotools/js/lib/jquery.sticky.min.js diff --git a/Gruntfile.js b/Gruntfile.js index 513cb8f..d716e61 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -7,18 +7,6 @@ module.exports = function (grunt) { 'assets/components/pdotools/js/pdopage.js' ], dest: 'assets/components/pdotools/js/pdopage.min.js' - }, - jquery_pdopage: { - src: [ - 'assets/components/pdotools/js/jquery.pdopage.js' - ], - dest: 'assets/components/pdotools/js/jquery.pdopage.min.js' - }, - jquery_sticky: { - src: [ - 'assets/components/pdotools/js/lib/jquery.sticky.js' - ], - dest: 'assets/components/pdotools/js/lib/jquery.sticky.min.js' } }, cssmin: { @@ -49,4 +37,4 @@ module.exports = function (grunt) { //register the task grunt.registerTask('default', ['uglify', 'cssmin']); -}; \ No newline at end of file +}; diff --git a/_build/build.config.php b/_build/build.config.php index d61f5b5..9037f0b 100644 --- a/_build/build.config.php +++ b/_build/build.config.php @@ -3,7 +3,7 @@ const PKG_NAME = 'pdoTools'; const PKG_NAME_LOWER = 'pdotools'; -const PKG_VERSION = '3.0.3'; +const PKG_VERSION = '3.1.0'; const PKG_RELEASE = 'pl'; const PKG_AUTO_INSTALL = false; diff --git a/assets/components/pdotools/css/pdopage.css b/assets/components/pdotools/css/pdopage.css index 18b8ec8..1fa7f84 100644 --- a/assets/components/pdotools/css/pdopage.css +++ b/assets/components/pdotools/css/pdopage.css @@ -1,14 +1,29 @@ #pdopage .pagination { margin: 0; } -.sticky-pagination.is-sticky { - opacity: .5; +#pdopage .pagination.pdopage-sticky, +.pdopage-sticky { + position: sticky; + top: 2px; + z-index: 2; } -.sticky-pagination.is-sticky:hover { - opacity: 1; +#pdopage.loading { + opacity: .3; + pointer-events: none; +} +.pdopage-sentinel { + width: 100%; + height: 1px; + margin: 0; + padding: 0; + border: 0; + overflow: hidden; } .btn-more { width: 150px; display: block; margin: auto; } +.btn-more[hidden] { + display: none !important; +} diff --git a/assets/components/pdotools/css/pdopage.min.css b/assets/components/pdotools/css/pdopage.min.css index a6dc2b9..c3dbe46 100644 --- a/assets/components/pdotools/css/pdopage.min.css +++ b/assets/components/pdotools/css/pdopage.min.css @@ -1 +1 @@ -#pdopage .pagination{margin:0}.sticky-pagination.is-sticky{opacity:.5}.sticky-pagination.is-sticky:hover{opacity:1}.btn-more{width:150px;display:block;margin:auto} \ No newline at end of file +#pdopage .pagination{margin:0}#pdopage .pagination.pdopage-sticky,.pdopage-sticky{position:sticky;top:2px;z-index:2}#pdopage.loading{opacity:.3;pointer-events:none}.pdopage-sentinel{width:100%;height:1px;margin:0;padding:0;border:0;overflow:hidden}.btn-more{width:150px;display:block;margin:auto}.btn-more[hidden]{display:none!important} \ No newline at end of file diff --git a/assets/components/pdotools/js/jquery.pdopage.js b/assets/components/pdotools/js/jquery.pdopage.js deleted file mode 100644 index 4da7b46..0000000 --- a/assets/components/pdotools/js/jquery.pdopage.js +++ /dev/null @@ -1,381 +0,0 @@ -;(function ($, window, document, undefined) { - - 'use strict'; - - var pluginName = 'pdoPage', - defaults = { - wrapper: '#pdopage', - rows: '#pdopage .row', - pagination: '#pdopage .pagination', - link: '#pdopage .pagination a', - more: '#pdopage .btn-more', - pdoTitle: '#pdopage .title', - moreTpl: '', - waitAnimation: '
', - mode: 'scroll', - pageVarKey: 'page', - pageLimit: 12, - assetsUrl: '/assets/components/pdotools/', - scrollTop: true - }; - - function Plugin(element, options) { - this.element = element; - this.settings = $.extend({}, defaults, options); - this._defaults = defaults; - this._name = pluginName; - this.key = this.settings.pageVarKey; - this.wrapper = $(this.settings.wrapper); - this.mode = this.settings.mode; - this.reached = false; - this.history = this.settings.history; - this.oldBrowser = !(window.history && history.pushState); - this.init(); - } - - $.extend(Plugin.prototype, { - init: function () { - var _this = this; - if (this.page == undefined) { - var params = this.hashGet(); - var page = params[this.key] == undefined ? 1 : params[this.key]; - this.page = Number(page); - } - switch (this.mode) { - case 'default': - this.initDefault(); - break; - case 'scroll': - case 'button': - if (this.history) { - if (typeof(jQuery().sticky) == 'undefined') { - $.getScript(this.settings.assetsUrl + 'js/lib/jquery.sticky.js', function () { - _this.init(_this.settings); - }); - return; - } - this.stickyPagination(); - } else { - $(this.settings.pagination).hide(); - } - if (this.mode == 'button') { - this.initButton(); - } else { - this.initScroll(); - } - break; - } - }, - initDefault: function () { - // Default pagination - var _this = this; - $(document).on('click', this.settings.link, function (e) { - e.preventDefault(); - var href = $(this).prop('href'); - var match = href.match(new RegExp(_this.key + '=(\\d+)')); - var page = !match ? 1 : match[1]; - if (_this.page != page) { - if (_this.history) { - _this.hashAdd(_this.key, page); - } - _this.loadPage(href); - } - }); - if (this.history) { - $(window).on('popstate', function (e) { - if (e.originalEvent.state && e.originalEvent.state.pdoPage) { - _this.loadPage(e.originalEvent.state.pdoPage); - } - }); - history.replaceState({pdoPage: window.location.href}, ''); - } - }, - initButton: function () { - // More button pagination - var _this = this; - $(this.settings.rows).after(this.settings.moreTpl); - var has_results = false; - $(this.settings.link).each(function () { - var href = $(this).prop('href'); - var match = href.match(new RegExp(_this.key + '=(\\d+)')); - var page = !match ? 1 : match[1]; - if (page > _this.page) { - has_results = true; - return false; - } - }); - if (!has_results) { - $(this.settings.more).hide(); - } - $(document).on('click', this.settings.more, function (e) { - e.preventDefault(); - _this.addPage() - }); - }, - initScroll: function () { - // Scroll pagination - var _this = this; - var $window = $(window); - $window.on('scroll', function () { - if (!_this.reached && $window.scrollTop() > _this.wrapper.height() - $window.height()) { - _this.reached = true; - _this.addPage(); - } - }); - }, - addPage: function () { - var _this = this; - var params = this.hashGet(); - var current = params[this.key] || _this.page || 1; - $(this.settings.link).each(function () { - var href = $(this).prop('href'); - var match = href.match(new RegExp(_this.key + '=(\\d+)')); - var page = !match ? 1 : Number(match[1]); - if (page > current) { - if (_this.history) { - _this.hashAdd(_this.key, page); - } - _this.page = current; - _this.loadPage(href, 'append'); - return false; - } - }); - }, - loadPage: function (href, mode) { - var _this = this; - var rows = $(this.settings.rows); - var pagination = $(this.settings.pagination); - var match = href.match(new RegExp(this.key + '=(\\d+)')); - var page = !match ? 1 : Number(match[1]); - if (!mode) { - mode = 'replace'; - } - if (this.page == page && mode != 'force') { - return; - } - this.wrapper.trigger('beforeLoad', [this, this.settings]); - if (this.mode != 'scroll') { - this.wrapper.css({opacity: .3}); - } - this.page = page; - var waitAnimation = $(this.settings.waitAnimation); - if (mode == 'append') { - this.wrapper.find(rows).append(waitAnimation); - } else { - this.wrapper.find(rows).empty().append(waitAnimation); - } - var params = this.getUrlParameters(href); - params[this.key] = this.page; - $.get(window.location.pathname, params, function (response) { - if (response) { - _this.wrapper.find(pagination).replaceWith(response.pagination); - if (!_this.history) { - $(_this.settings.pagination).hide(); - } - if (mode === 'append') { - _this.wrapper.find(rows).append(response.output); - if (_this.mode == 'button') { - if (response.pages == response.page || response.pages == 0) { - $(_this.settings.more).hide(); - } else { - $(_this.settings.more).show(); - } - } else if (_this.mode == 'scroll') { - _this.reached = false; - } - waitAnimation.remove(); - } else { - _this.wrapper.find(rows).html(response.output); - if (mode === 'force') { - _this.page = response.page || 1; - if (_this.settings.mode == 'button') { - if (response.pages == response.page || response.pages == 0) { - $(_this.settings.more).hide(); - } else { - $(_this.settings.more).show(); - } - } - if (_this.history) { - _this.hashSet(params); - } - } - } - _this.wrapper.trigger('afterLoad', [_this, _this.settings, response]); - if (_this.mode != 'scroll') { - _this.wrapper.css({opacity: 1}); - if (_this.mode == 'default' && _this.settings.scrollTop) { - $('html, body').animate({ - scrollTop: _this.wrapper.position().top - 50 || 0 - }, 0); - } - } - _this.updateTitle(response); - } - }, 'json'); - }, - stickyPagination: function () { - var pagination = $(this.settings.pagination); - if (pagination.is(':visible')) { - pagination.sticky({ - wrapperClassName: 'sticky-pagination', - getWidthFrom: this.settings.pagination, - responsiveWidth: true - }); - this.wrapper.trigger('scroll'); - } - }, - updateTitle: function (response) { - if (typeof(pdoTitle) == 'undefined') { - return; - } - var title = $('title'); - var separator = pdoTitle.separator || ' / '; - var tpl = pdoTitle.tpl; - var parts = []; - var items = title.text().split(separator); - var pcre = new RegExp('^' + tpl.split(' ')[0] + ' '); - for (var i = 0; i < items.length; i++) { - if (i === 1 && response.page && response.page > 1) { - parts.push(tpl.replace('{page}', response.page).replace('{pageCount}', response.pages)); - } - if (!items[i].match(pcre)) { - parts.push(items[i]); - } - } - title.text(parts.join(separator)); - }, - getUrlParameters: function (url) { - var result = {}; - var searchIndex = url.indexOf("?"); - if (searchIndex !== -1) { - result = this.deparam(url.substring(searchIndex + 1)); - } - return result; - }, - deparam: function (params) { - // source: https://github.com/jupiterjs/jquerymx/blob/master/lang/string/deparam/deparam.js - var digitTest = /^\d+$/, - keyBreaker = /([^\[\]]+)|(\[\])/g, - paramTest = /([^?#]*)(#.*)?$/, - prep = function (str) { - return decodeURIComponent(str.replace(/\+/g, ' ')); - }; - var data = {}, pairs, lastPart; - if (params && paramTest.test(params)) { - pairs = params.split('&'); - $.each(pairs, function (index, pair) { - var parts = pair.split('='), - key = prep(parts.shift()), - value = prep(parts.join('=')), - current = data; - if (key) { - parts = key.match(keyBreaker); - for (var j = 0, l = parts.length - 1; j < l; j++) { - if (!current[parts[j]]) { - // If what we are pointing to looks like an `array` - current[parts[j]] = digitTest.test(parts[j + 1]) || parts[j + 1] === '[]' ? [] : {}; - } - current = current[parts[j]]; - } - lastPart = parts.pop(); - if (lastPart === '[]') { - current.push(value); - } else { - current[lastPart] = value; - } - } - }); - } - return data; - }, - hashGet: function () { - var vars = {}, hash, hashes; - if (!this.oldBrowser) { - var pos = window.location.href.indexOf('?'); - hashes = (pos != -1) ? decodeURIComponent(window.location.href.substr(pos + 1)) : ''; - vars = this.deparam(hashes); - } else { - hashes = decodeURIComponent(window.location.hash.substr(1)); - if (hashes.length) { - hashes = hashes.split('/'); - for (var i in hashes) { - if (hashes.hasOwnProperty(i)) { - hash = hashes[i].split('='); - if (typeof hash[1] == 'undefined') { - vars.anchor = hash[0]; - } else { - vars[hash[0]] = hash[1]; - } - } - } - } - } - return vars; - }, - hashSet: function (vars) { - var hash = ''; - for (var i in vars) { - if (vars.hasOwnProperty(i)) { - if (typeof vars[i] != 'object') { - hash += '&' + i + '=' + vars[i]; - } else { - for (var j in vars[i]) { - if (vars[i].hasOwnProperty(j)) { - if (!isNaN(parseFloat(j)) && isFinite(parseFloat(j))) { - hash += '&' + i + '[' + ']=' + vars[i][j]; - } else { - hash += '&' + i + '[' + j + ']=' + vars[i][j]; - } - } - } - } - } - } - if (!this.oldBrowser) { - if (hash.length !== 0) { - hash = '?' + hash.substr(1); - } - window.history.pushState({pdoPage: window.location.pathname + hash}, '', window.location.pathname + hash); - } else { - window.location.hash = hash.substr(1); - } - }, - hashAdd: function (key, val) { - var hash = this.hashGet(); - hash[key] = val; - this.hashSet(hash); - }, - hashRemove: function (key) { - var hash = this.hashGet(); - delete hash[key]; - this.hashSet(hash); - }, - hashClear: function () { - this.hashSet({}); - } - }); - - $.fn[pluginName] = function (options) { - var args = arguments; - if (options === undefined || typeof options === 'object') { - return this.each(function () { - if (!$.data(this, 'plugin_' + pluginName)) { - $.data(this, 'plugin_' + pluginName, new Plugin(this, options)); - } - }); - } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { - var returns; - this.each(function () { - var instance = $.data(this, 'plugin_' + pluginName); - if (instance instanceof Plugin && typeof instance[options] === 'function') { - returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1)); - } - if (options === 'destroy') { - $.data(this, 'plugin_' + pluginName, null); - } - }); - return returns !== undefined ? returns : this; - } - }; - -})(jQuery, window, document); diff --git a/assets/components/pdotools/js/jquery.pdopage.min.js b/assets/components/pdotools/js/jquery.pdopage.min.js deleted file mode 100644 index 1a313ae..0000000 --- a/assets/components/pdotools/js/jquery.pdopage.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(a,b,c,d){"use strict";function e(c,d){this.element=c,this.settings=a.extend({},g,d),this._defaults=g,this._name=f,this.key=this.settings.pageVarKey,this.wrapper=a(this.settings.wrapper),this.mode=this.settings.mode,this.reached=!1,this.history=this.settings.history,this.oldBrowser=!(b.history&&history.pushState),this.init()}var f="pdoPage",g={wrapper:"#pdopage",rows:"#pdopage .row",pagination:"#pdopage .pagination",link:"#pdopage .pagination a",more:"#pdopage .btn-more",pdoTitle:"#pdopage .title",moreTpl:'',waitAnimation:'
',mode:"scroll",pageVarKey:"page",pageLimit:12,assetsUrl:"/assets/components/pdotools/",scrollTop:!0};a.extend(e.prototype,{init:function(){var b=this;if(void 0==this.page){var c=this.hashGet(),d=void 0==c[this.key]?1:c[this.key];this.page=Number(d)}switch(this.mode){case"default":this.initDefault();break;case"scroll":case"button":if(this.history){if(void 0===jQuery().sticky)return void a.getScript(this.settings.assetsUrl+"js/lib/jquery.sticky.js",function(){b.init(b.settings)});this.stickyPagination()}else a(this.settings.pagination).hide();"button"==this.mode?this.initButton():this.initScroll()}},initDefault:function(){var d=this;a(c).on("click",this.settings.link,function(b){b.preventDefault();var c=a(this).prop("href"),e=c.match(new RegExp(d.key+"=(\\d+)")),f=e?e[1]:1;d.page!=f&&(d.history&&d.hashAdd(d.key,f),d.loadPage(c))}),this.history&&(a(b).on("popstate",function(a){a.originalEvent.state&&a.originalEvent.state.pdoPage&&d.loadPage(a.originalEvent.state.pdoPage)}),history.replaceState({pdoPage:b.location.href},""))},initButton:function(){var b=this;a(this.settings.rows).after(this.settings.moreTpl);var d=!1;a(this.settings.link).each(function(){var c=a(this).prop("href"),e=c.match(new RegExp(b.key+"=(\\d+)"));if((e?e[1]:1)>b.page)return d=!0,!1}),d||a(this.settings.more).hide(),a(c).on("click",this.settings.more,function(a){a.preventDefault(),b.addPage()})},initScroll:function(){var c=this,d=a(b);d.on("scroll",function(){!c.reached&&d.scrollTop()>c.wrapper.height()-d.height()&&(c.reached=!0,c.addPage())})},addPage:function(){var b=this,c=this.hashGet(),d=c[this.key]||b.page||1;a(this.settings.link).each(function(){var c=a(this).prop("href"),e=c.match(new RegExp(b.key+"=(\\d+)")),f=e?Number(e[1]):1;if(f>d)return b.history&&b.hashAdd(b.key,f),b.page=d,b.loadPage(c,"append"),!1})},loadPage:function(c,d){var e=this,f=a(this.settings.rows),g=a(this.settings.pagination),h=c.match(new RegExp(this.key+"=(\\d+)")),i=h?Number(h[1]):1;if(d||(d="replace"),this.page!=i||"force"==d){this.wrapper.trigger("beforeLoad",[this,this.settings]),"scroll"!=this.mode&&this.wrapper.css({opacity:.3}),this.page=i;var j=a(this.settings.waitAnimation);"append"==d?this.wrapper.find(f).append(j):this.wrapper.find(f).empty().append(j);var k=this.getUrlParameters(c);k[this.key]=this.page,a.get(b.location.pathname,k,function(b){b&&(e.wrapper.find(g).replaceWith(b.pagination),e.history||a(e.settings.pagination).hide(),"append"===d?(e.wrapper.find(f).append(b.output),"button"==e.mode?b.pages==b.page||0==b.pages?a(e.settings.more).hide():a(e.settings.more).show():"scroll"==e.mode&&(e.reached=!1),j.remove()):(e.wrapper.find(f).html(b.output),"force"===d&&(e.page=b.page||1,"button"==e.settings.mode&&(b.pages==b.page||0==b.pages?a(e.settings.more).hide():a(e.settings.more).show()),e.history&&e.hashSet(k))),e.wrapper.trigger("afterLoad",[e,e.settings,b]),"scroll"!=e.mode&&(e.wrapper.css({opacity:1}),"default"==e.mode&&e.settings.scrollTop&&a("html, body").animate({scrollTop:e.wrapper.position().top-50||0},0)),e.updateTitle(b))},"json")}},stickyPagination:function(){var b=a(this.settings.pagination);b.is(":visible")&&(b.sticky({wrapperClassName:"sticky-pagination",getWidthFrom:this.settings.pagination,responsiveWidth:!0}),this.wrapper.trigger("scroll"))},updateTitle:function(b){if("undefined"!=typeof pdoTitle){for(var c=a("title"),d=pdoTitle.separator||" / ",e=pdoTitle.tpl,f=[],g=c.text().split(d),h=new RegExp("^"+e.split(" ")[0]+" "),i=0;i1&&f.push(e.replace("{page}",b.page).replace("{pageCount}",b.pages)),g[i].match(h)||f.push(g[i]);c.text(f.join(d))}},getUrlParameters:function(a){var b={},c=a.indexOf("?");return-1!==c&&(b=this.deparam(a.substring(c+1))),b},deparam:function(b){var c,d,e=/^\d+$/,f=/([^\[\]]+)|(\[\])/g,g=/([^?#]*)(#.*)?$/,h=function(a){return decodeURIComponent(a.replace(/\+/g," "))},i={};return b&&g.test(b)&&(c=b.split("&"),a.each(c,function(a,b){var c=b.split("="),g=h(c.shift()),j=h(c.join("=")),k=i;if(g){c=g.match(f);for(var l=0,m=c.length-1;l dwh) ? dwh - scrollTop : 0; - - for (var i = 0; i < sticked.length; i++) { - var s = sticked[i], - elementTop = s.stickyWrapper.offset().top, - etse = elementTop - s.topSpacing - extra; - - if (scrollTop <= etse) { - if (s.currentTop !== null) { - s.stickyElement - .css('position', '') - .css('top', ''); - s.stickyElement.trigger('sticky-end', [s]).parent().removeClass(s.className); - s.currentTop = null; - } - } - else { - var newTop = documentHeight - s.stickyElement.outerHeight() - - s.topSpacing - s.bottomSpacing - scrollTop - extra; - if (newTop < 0) { - newTop = newTop + s.topSpacing; - } else { - newTop = s.topSpacing; - } - if (s.currentTop != newTop) { - s.stickyElement - .css('position', 'fixed') - .css('top', newTop); - - if (typeof s.getWidthFrom !== 'undefined') { - s.stickyElement.css('width', $(s.getWidthFrom).width()); - } - - s.stickyElement.trigger('sticky-start', [s]).parent().addClass(s.className); - s.currentTop = newTop; - } - } - } - }, - resizer = function() { - windowHeight = $window.height(); - - for (var i = 0; i < sticked.length; i++) { - var s = sticked[i]; - if (typeof s.getWidthFrom !== 'undefined' && s.responsiveWidth === true) { - s.stickyElement.css('width', $(s.getWidthFrom).width()); - } - } - }, - methods = { - init: function(options) { - var o = $.extend({}, defaults, options); - return this.each(function() { - var stickyElement = $(this); - - var stickyId = stickyElement.attr('id'); - var wrapperId = stickyId ? stickyId + '-' + defaults.wrapperClassName : defaults.wrapperClassName - var wrapper = $('
') - .attr('id', stickyId + '-sticky-wrapper') - .addClass(o.wrapperClassName); - stickyElement.wrapAll(wrapper); - - if (o.center) { - stickyElement.parent().css({width:stickyElement.outerWidth(),marginLeft:"auto",marginRight:"auto"}); - } - - if (stickyElement.css("float") == "right") { - stickyElement.css({"float":"none"}).parent().css({"float":"right"}); - } - - var stickyWrapper = stickyElement.parent(); - stickyWrapper.css('height', stickyElement.outerHeight()); - sticked.push({ - topSpacing: o.topSpacing, - bottomSpacing: o.bottomSpacing, - stickyElement: stickyElement, - currentTop: null, - stickyWrapper: stickyWrapper, - className: o.className, - getWidthFrom: o.getWidthFrom, - responsiveWidth: o.responsiveWidth - }); - }); - }, - update: scroller, - unstick: function(options) { - return this.each(function() { - var unstickyElement = $(this); - - var removeIdx = -1; - for (var i = 0; i < sticked.length; i++) - { - if (sticked[i].stickyElement.get(0) == unstickyElement.get(0)) - { - removeIdx = i; - } - } - if(removeIdx != -1) - { - sticked.splice(removeIdx,1); - unstickyElement.unwrap(); - unstickyElement.removeAttr('style'); - } - }); - } - }; - - // should be more efficient than using $window.scroll(scroller) and $window.resize(resizer): - if (window.addEventListener) { - window.addEventListener('scroll', scroller, false); - window.addEventListener('resize', resizer, false); - } else if (window.attachEvent) { - window.attachEvent('onscroll', scroller); - window.attachEvent('onresize', resizer); - } - - $.fn.sticky = function(method) { - if (methods[method]) { - return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); - } else if (typeof method === 'object' || !method ) { - return methods.init.apply( this, arguments ); - } else { - $.error('Method ' + method + ' does not exist on jQuery.sticky'); - } - }; - - $.fn.unstick = function(method) { - if (methods[method]) { - return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); - } else if (typeof method === 'object' || !method ) { - return methods.unstick.apply( this, arguments ); - } else { - $.error('Method ' + method + ' does not exist on jQuery.sticky'); - } - - }; - $(function() { - setTimeout(scroller, 0); - }); -})(jQuery); \ No newline at end of file diff --git a/assets/components/pdotools/js/lib/jquery.sticky.min.js b/assets/components/pdotools/js/lib/jquery.sticky.min.js deleted file mode 100644 index 4a95213..0000000 --- a/assets/components/pdotools/js/lib/jquery.sticky.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(a){var b={topSpacing:0,bottomSpacing:0,className:"is-sticky",wrapperClassName:"sticky-wrapper",center:!1,getWidthFrom:"",responsiveWidth:!1},c=a(window),d=a(document),e=[],f=c.height(),g=function(){for(var b=c.scrollTop(),g=d.height(),h=g-f,i=b>h?h-b:0,j=0;j").attr("id",c+"-sticky-wrapper").addClass(d.wrapperClassName);b.wrapAll(f),d.center&&b.parent().css({width:b.outerWidth(),marginLeft:"auto",marginRight:"auto"}),"right"==b.css("float")&&b.css({float:"none"}).parent().css({float:"right"});var g=b.parent();g.css("height",b.outerHeight()),e.push({topSpacing:d.topSpacing,bottomSpacing:d.bottomSpacing,stickyElement:b,currentTop:null,stickyWrapper:g,className:d.className,getWidthFrom:d.getWidthFrom,responsiveWidth:d.responsiveWidth})})},update:g,unstick:function(b){return this.each(function(){for(var b=a(this),c=-1,d=0;d pdoPage.keys[key]) { - has_results = true; - return false; + case 'scroll': + case 'button': + if (config.history) { + const pagination = this.getPagination(); + if (pagination) { + pagination.classList.add('pdopage-sticky'); + } + } else { + const hiddenPagination = this.getPagination(); + if (hiddenPagination) { + hiddenPagination.hidden = true; } - }); - if (!has_results) { - $(config['more']).hide(); } - $(document).on('click', config['more'], function (e) { - e.preventDefault(); - pdoPage.addPage(config) - }); - } - else { - // Scroll pagination - var wrapper = $(config['wrapper']); - var $window = $(window); - $window.on('load scroll', function () { - if (!pdoPage.Reached && $window.scrollTop() > wrapper.height() - $window.height()) { - pdoPage.Reached = true; - pdoPage.addPage(config); + if (config.mode === 'button') { + const rows = this.getRows(); + if (rows && config.moreTpl) { + rows.insertAdjacentHTML('afterend', config.moreTpl); } - }); - } - break; - } -}; - -pdoPage.addPage = function (config) { - var key = config['pageVarKey']; - var current = pdoPage.keys[key] || 1; - $(config['link']).each(function () { - var href = $(this).prop('href'); - var match = href.match(new RegExp(key + '=(\\d+)')); - var page = !match ? 1 : Number(match[1]); - if (page > current) { - if (config.history) { - if (page == 1) { - pdoPage.Hash.remove(key); + const hasMore = qsa(config.link).some(function (link) { + return pageFromHref(link.href, key) > Number(pdoPage.keys[key] || 1); + }); + const more = qs(config.more); + if (more && !hasMore) { + more.hidden = true; + } + document.addEventListener('click', function (e) { + const btn = isMoreTarget(e.target, config.more); + if (!btn) { + return; + } + e.preventDefault(); + self.addPage(); + }); } else { - pdoPage.Hash.add(key, page); + this.bindScroll(); } - } - pdoPage.loadPage(href, config, 'append'); - return false; + break; } - }); -}; - -pdoPage.loadPage = function (href, config, mode) { - var wrapper = $(config['wrapper']); - var rows = $(config['rows']); - var pagination = $(config['pagination']); - var key = config['pageVarKey']; - var match = href.match(new RegExp(key + '=(\\d+)')); - var page = !match ? 1 : Number(match[1]); - if (!mode) { - mode = 'replace'; - } + }; - if (pdoPage.keys[key] == page) { - return; - } - if (pdoPage.callbacks['before'] && typeof(pdoPage.callbacks['before']) == 'function') { - pdoPage.callbacks['before'].apply(this, [config]); - } - else { - if (config['mode'] != 'scroll') { - wrapper.css({opacity: .3}); + Controller.prototype.bindScroll = function () { + const self = this; + const rows = this.getRows(); + if (!rows) { + return; } - wrapper.addClass('loading'); - } - var params = pdoPage.Hash.get(); - for (var i in params) { - if (params.hasOwnProperty(i) && pdoPage.keys[i] && i != key) { - delete(params[i]); - } - } - params[key] = pdoPage.keys[key] = page; - params['pageId'] = config['pageId']; - params['hash'] = config['hash']; - - $.post(config['connectorUrl'], params, function (response) { - if (response && response['total']) { - wrapper.find(pagination).html(response['pagination']); - if (mode == 'append') { - wrapper.find(rows).append(response['output']); - if (config['mode'] == 'button') { - if (response['pages'] == response['page']) { - $(config['more']).hide(); - } - else { - $(config['more']).show(); - } - } - else if (config['mode'] == 'scroll') { - pdoPage.Reached = false; - var $window = $(window); - if ($window.scrollTop() > wrapper.height() - $window.height()) { - pdoPage.Reached = true; - pdoPage.addPage(config); + this.sentinel = document.createElement('div'); + this.sentinel.className = 'pdopage-sentinel'; + this.sentinel.setAttribute('aria-hidden', 'true'); + rows.parentNode.insertBefore(this.sentinel, rows.nextSibling); + + if ('IntersectionObserver' in window) { + this.observer = new IntersectionObserver(function (entries) { + entries.forEach(function (entry) { + if (entry.isIntersecting && !self.busy && !self.reached) { + self.reached = true; + self.addPage(); } - } + }); + }, {root: null, rootMargin: '0px', threshold: 0}); + this.observer.observe(this.sentinel); + return; + } + + const onScroll = function () { + if (self.busy || self.reached) { + return; + } + const wrapper = self.getWrapper(); + if (!wrapper) { + return; } - else { - wrapper.find(rows).html(response['output']); + if (window.scrollY > wrapper.offsetHeight - window.innerHeight) { + self.reached = true; + self.addPage(); } + }; + window.addEventListener('scroll', onScroll, {passive: true}); + window.addEventListener('load', onScroll); + }; - if (pdoPage.callbacks['after'] && typeof(pdoPage.callbacks['after']) == 'function') { - pdoPage.callbacks['after'].apply(this, [config, response]); + Controller.prototype.addPage = function () { + const key = this.key; + const current = Number(pdoPage.keys[key] || 1); + let next = null; + const links = qsa(this.config.link); + for (let i = 0; i < links.length; i++) { + if (pageFromHref(links[i].href, key) > current) { + next = links[i]; + break; } - else { - wrapper.removeClass('loading'); - if (config['mode'] != 'scroll') { - wrapper.css({opacity: 1}); - if (config['mode'] == 'default' && config['scrollTop'] !== false) { - $('html, body').animate({scrollTop: wrapper.position().top - 50 || 0}, 0); - } - } + } + if (!next) { + this.reached = true; + if (this.observer && this.sentinel) { + this.observer.unobserve(this.sentinel); } - pdoPage.updateTitle(config, response); - $(document).trigger('pdopage_load', [config, response]); + return; } - }, 'json'); -}; - -pdoPage.stickyPagination = function (config) { - var pagination = $(config['pagination']); - if (pagination.is(':visible')) { - pagination.sticky({ - wrapperClassName: 'sticky-pagination', - getWidthFrom: config['wrapper'], - responsiveWidth: true, - topSpacing: 2 - }); - $(config['wrapper']).trigger('scroll'); - } -}; + const page = pageFromHref(next.href, key); + if (this.config.history) { + setHistory(key, page); + } + this.load(next.href, 'append'); + }; -pdoPage.updateTitle = function (config, response) { - if (typeof(pdoTitle) == 'undefined') { - return; - } - var $title = $('title'); - var separator = pdoTitle.separator || ' / '; - var tpl = pdoTitle.tpl; - - var title = []; - var items = $title.text().split(separator); - var pcre = new RegExp('^' + tpl.split(' ')[0] + ' '); - for (var i = 0; i < items.length; i++) { - if (i === 1 && response.page && response.page > 1) { - title.push(tpl.replace('{page}', response.page).replace('{pageCount}', response.pages)); + Controller.prototype.setHtml = function (el, html) { + if (!el) { + return; } - if (!items[i].match(pcre)) { - title.push(items[i]); + while (el.firstChild) { + el.removeChild(el.firstChild); } - } - $title.text(title.join(separator)); -}; - -pdoPage.Hash = { - get: function () { - var vars = {}, hash, splitter, hashes; - if (!this.oldbrowser()) { - var pos = window.location.href.indexOf('?'); - hashes = (pos != -1) ? decodeURIComponent(window.location.href.substr(pos + 1)).replace('+', ' ') : ''; - splitter = '&'; + if (!html) { + return; } - else { - hashes = decodeURIComponent(window.location.hash.substr(1)).replace('+', ' '); - splitter = '/'; + const template = document.createElement('template'); + template.innerHTML = html; + el.appendChild(template.content); + }; + + Controller.prototype.appendHtml = function (el, html) { + if (!el || !html) { + return; } + const template = document.createElement('template'); + template.innerHTML = html; + el.appendChild(template.content); + }; + + Controller.prototype.load = function (href, mode) { + const self = this; + const config = this.config; + const key = this.key; + const wrapper = this.getWrapper(); + const rows = this.getRows(); + const pagination = this.getPagination(); + const page = pageFromHref(href, key); + mode = mode || 'replace'; - if (hashes.length == 0) { - return vars; + if (Number(pdoPage.keys[key]) === page && mode !== 'force') { + return; } - else { - hashes = hashes.split(splitter); + if (this.busy) { + return; } - var matches, key; - for (var i in hashes) { - if (hashes.hasOwnProperty(i)) { - hash = hashes[i].split('='); - if (typeof hash[1] == 'undefined') { - vars['anchor'] = hash[0]; - } - else { - matches = hash[0].match(/\[(.*?|)\]$/); - if (matches) { - key = hash[0].replace(matches[0], ''); - if (!vars.hasOwnProperty(key)) { - // Array - if (matches[1] == '') { - vars[key] = []; - } - // Object - else { - vars[key] = {}; - } - } - if (vars[key] instanceof Array) { - vars[key].push(hash[1]); - } - else { - vars[key][matches[1]] = hash[1]; - } - } - // String or numeric - else { - vars[hash[0]] = hash[1]; - } - } + this.busy = true; + pdoPage.keys[key] = page; + + if (typeof pdoPage.callbacks.before === 'function') { + pdoPage.callbacks.before.apply(this, [config]); + } else if (wrapper && config.mode !== 'scroll') { + wrapper.classList.add('loading'); + } + + const params = new URLSearchParams(window.location.search); + Object.keys(pdoPage.keys).forEach(function (otherKey) { + if (otherKey !== key) { + params.delete(otherKey); } + }); + params.set(key, String(page)); + params.set('pageId', String(config.pageId)); + params.set('hash', String(config.hash)); + + if (this.abortController) { + this.abortController.abort(); } - return vars; - }, - - set: function (vars) { - var hash = ''; - for (var i in vars) { - if (vars.hasOwnProperty(i)) { - if (typeof vars[i] == 'object') { - for (var j in vars[i]) { - if (vars[i].hasOwnProperty(j)) { - // Array - if (vars[i] instanceof Array) { - hash += '&' + i + '[]=' + vars[i][j]; - } - // Object - else { - hash += '&' + i + '[' + j + ']=' + vars[i][j]; - } + this.abortController = new AbortController(); + + fetch(config.connectorUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + 'X-Requested-With': 'XMLHttpRequest' + }, + body: params.toString(), + credentials: 'same-origin', + signal: this.abortController.signal + }).then(function (response) { + if (!response.ok) { + throw new Error('pdoPage request failed'); + } + return response.json(); + }).then(function (data) { + if (!data || !data.total) { + return; + } + self.setHtml(pagination, data.pagination || ''); + if (mode === 'append') { + self.appendHtml(rows, data.output || ''); + if (config.mode === 'button') { + const more = qs(config.more); + if (more) { + more.hidden = Number(data.pages) === Number(data.page) || Number(data.pages) === 0; + } + } else if (config.mode === 'scroll') { + if (Number(data.pages) === Number(data.page) || Number(data.pages) === 0) { + self.reached = true; + if (self.observer && self.sentinel) { + self.observer.unobserve(self.sentinel); } + } else { + self.reached = false; } } - // String or numeric - else { - hash += '&' + i + '=' + vars[i]; + } else { + self.setHtml(rows, data.output || ''); + } + + if (typeof pdoPage.callbacks.after === 'function') { + pdoPage.callbacks.after.apply(self, [config, data]); + } else if (wrapper) { + wrapper.classList.remove('loading'); + if (config.mode === 'default' && config.scrollTop !== false) { + const top = wrapper.getBoundingClientRect().top + window.scrollY - 50; + window.scrollTo(0, top > 0 ? top : 0); } } - } - if (!this.oldbrowser()) { - if (hash.length != 0) { - hash = '?' + hash.substr(1); + pdoPage.updateTitle(config, data); + document.dispatchEvent(new CustomEvent('pdopage:load', { + detail: {config: config, response: data} + })); + }).catch(function (error) { + if (error && error.name === 'AbortError') { + return; } - window.history.pushState({pdoPage: document.location.pathname + hash}, '', document.location.pathname + hash); + if (wrapper) { + wrapper.classList.remove('loading'); + } + self.reached = false; + }).then(function () { + self.busy = false; + }); + }; + + pdoPage.initialize = function (config) { + const key = config.pageVarKey; + if (pdoPage.instances[key]) { + return pdoPage.instances[key]; } - else { - window.location.hash = hash.substr(1); + const params = new URLSearchParams(window.location.search); + const current = params.get(key); + pdoPage.keys[key] = current ? Number(current) : 1; + pdoPage.configs[key] = config; + const controller = new Controller(config); + pdoPage.instances[key] = controller; + controller.bind(); + return controller; + }; + + pdoPage.addPage = function (config) { + const controller = pdoPage.instances[config.pageVarKey]; + if (controller) { + controller.addPage(); } - }, - - add: function (key, val) { - var hash = this.get(); - hash[key] = val; - this.set(hash); - }, - - remove: function (key) { - var hash = this.get(); - delete hash[key]; - this.set(hash); - }, - - clear: function () { - this.set({}); - }, - - oldbrowser: function () { - return !(window.history && history.pushState); - } -}; + }; -if (typeof(jQuery) == 'undefined') { - console.log("You must load jQuery for using ajax mode in pdoPage."); -} + pdoPage.loadPage = function (href, config, mode) { + const controller = pdoPage.instances[config.pageVarKey]; + if (controller) { + controller.load(href, mode || 'replace'); + } + }; + + pdoPage.updateTitle = function (config, response) { + if (typeof window.pdoTitle === 'undefined') { + return; + } + const separator = pdoTitle.separator || ' / '; + const tpl = pdoTitle.tpl; + const title = []; + const items = document.title.split(separator); + const pcre = new RegExp('^' + tpl.split(' ')[0] + ' '); + for (let i = 0; i < items.length; i++) { + if (i === 1 && response.page && response.page > 1) { + title.push(tpl.replace('{page}', response.page).replace('{pageCount}', response.pages)); + } + if (!items[i].match(pcre)) { + title.push(items[i]); + } + } + document.title = title.join(separator); + }; +})(window, document); diff --git a/assets/components/pdotools/js/pdopage.min.js b/assets/components/pdotools/js/pdopage.min.js index 34fd532..fc50bf3 100644 --- a/assets/components/pdotools/js/pdopage.min.js +++ b/assets/components/pdotools/js/pdopage.min.js @@ -1 +1 @@ -"undefined"==typeof pdoPage&&(pdoPage={callbacks:{},keys:{},configs:{}}),pdoPage.Reached=!1,pdoPage.initialize=function(a){if(void 0==pdoPage.keys[a.pageVarKey]){var b=a.pageVarKey,c=pdoPage.Hash.get(),d=void 0==c[b]?1:c[b];pdoPage.keys[b]=Number(d),pdoPage.configs[b]=a}var e=this;switch(a.mode){case"default":$(document).on("click",a.link,function(b){b.preventDefault();var c=$(this).prop("href"),d=a.pageVarKey,f=c.match(new RegExp(d+"=(\\d+)")),g=f?f[1]:1;pdoPage.keys[d]!=g&&(a.history&&(1==g?pdoPage.Hash.remove(d):pdoPage.Hash.add(d,g)),e.loadPage(c,a))}),a.history&&($(window).on("popstate",function(b){b.originalEvent.state&&b.originalEvent.state.pdoPage&&e.loadPage(b.originalEvent.state.pdoPage,a)}),history.replaceState({pdoPage:window.location.href},""));break;case"scroll":case"button":if(a.history){if(void 0===jQuery().sticky)return void $.getScript(a.assetsUrl+"js/lib/jquery.sticky.min.js",function(){pdoPage.initialize(a)});pdoPage.stickyPagination(a)}else $(a.pagination).hide();var f=a.pageVarKey;if("button"==a.mode){$(a.rows).after(a.moreTpl);var g=!1;$(a.link).each(function(){var a=$(this).prop("href"),b=a.match(new RegExp(f+"=(\\d+)"));if((b?b[1]:1)>pdoPage.keys[f])return g=!0,!1}),g||$(a.more).hide(),$(document).on("click",a.more,function(b){b.preventDefault(),pdoPage.addPage(a)})}else{var h=$(a.wrapper),i=$(window);i.on("load scroll",function(){!pdoPage.Reached&&i.scrollTop()>h.height()-i.height()&&(pdoPage.Reached=!0,pdoPage.addPage(a))})}}},pdoPage.addPage=function(a){var b=a.pageVarKey,c=pdoPage.keys[b]||1;$(a.link).each(function(){var d=$(this).prop("href"),e=d.match(new RegExp(b+"=(\\d+)")),f=e?Number(e[1]):1;if(f>c)return a.history&&(1==f?pdoPage.Hash.remove(b):pdoPage.Hash.add(b,f)),pdoPage.loadPage(d,a,"append"),!1})},pdoPage.loadPage=function(a,b,c){var d=$(b.wrapper),e=$(b.rows),f=$(b.pagination),g=b.pageVarKey,h=a.match(new RegExp(g+"=(\\d+)")),i=h?Number(h[1]):1;if(c||(c="replace"),pdoPage.keys[g]!=i){pdoPage.callbacks.before&&"function"==typeof pdoPage.callbacks.before?pdoPage.callbacks.before.apply(this,[b]):("scroll"!=b.mode&&d.css({opacity:.3}),d.addClass("loading"));var j=pdoPage.Hash.get();for(var k in j)j.hasOwnProperty(k)&&pdoPage.keys[k]&&k!=g&&delete j[k];j[g]=pdoPage.keys[g]=i,j.pageId=b.pageId,j.hash=b.hash,$.post(b.connectorUrl,j,function(a){if(a&&a.total){if(d.find(f).html(a.pagination),"append"==c){if(d.find(e).append(a.output),"button"==b.mode)a.pages==a.page?$(b.more).hide():$(b.more).show();else if("scroll"==b.mode){pdoPage.Reached=!1;var g=$(window);g.scrollTop()>d.height()-g.height()&&(pdoPage.Reached=!0,pdoPage.addPage(b))}}else d.find(e).html(a.output);pdoPage.callbacks.after&&"function"==typeof pdoPage.callbacks.after?pdoPage.callbacks.after.apply(this,[b,a]):(d.removeClass("loading"),"scroll"!=b.mode&&(d.css({opacity:1}),"default"==b.mode&&!1!==b.scrollTop&&$("html, body").animate({scrollTop:d.position().top-50||0},0))),pdoPage.updateTitle(b,a),$(document).trigger("pdopage_load",[b,a])}},"json")}},pdoPage.stickyPagination=function(a){var b=$(a.pagination);b.is(":visible")&&(b.sticky({wrapperClassName:"sticky-pagination",getWidthFrom:a.wrapper,responsiveWidth:!0,topSpacing:2}),$(a.wrapper).trigger("scroll"))},pdoPage.updateTitle=function(a,b){if("undefined"!=typeof pdoTitle){for(var c=$("title"),d=pdoTitle.separator||" / ",e=pdoTitle.tpl,f=[],g=c.text().split(d),h=new RegExp("^"+e.split(" ")[0]+" "),i=0;i1&&f.push(e.replace("{page}",b.page).replace("{pageCount}",b.pages)),g[i].match(h)||f.push(g[i]);c.text(f.join(d))}},pdoPage.Hash={get:function(){var a,b,c,d={};if(this.oldbrowser())c=decodeURIComponent(window.location.hash.substr(1)).replace("+"," "),b="/";else{var e=window.location.href.indexOf("?");c=-1!=e?decodeURIComponent(window.location.href.substr(e+1)).replace("+"," "):"",b="&"}if(0==c.length)return d;c=c.split(b);var f,g;for(var h in c)c.hasOwnProperty(h)&&(a=c[h].split("="),void 0===a[1]?d.anchor=a[0]:(f=a[0].match(/\[(.*?|)\]$/),f?(g=a[0].replace(f[0],""),d.hasOwnProperty(g)||(""==f[1]?d[g]=[]:d[g]={}),d[g]instanceof Array?d[g].push(a[1]):d[g][f[1]]=a[1]):d[a[0]]=a[1]));return d},set:function(a){var b="";for(var c in a)if(a.hasOwnProperty(c))if("object"==typeof a[c])for(var d in a[c])a[c].hasOwnProperty(d)&&(a[c]instanceof Array?b+="&"+c+"[]="+a[c][d]:b+="&"+c+"["+d+"]="+a[c][d]);else b+="&"+c+"="+a[c];this.oldbrowser()?window.location.hash=b.substr(1):(0!=b.length&&(b="?"+b.substr(1)),window.history.pushState({pdoPage:document.location.pathname+b},"",document.location.pathname+b))},add:function(a,b){var c=this.get();c[a]=b,this.set(c)},remove:function(a){var b=this.get();delete b[a],this.set(b)},clear:function(){this.set({})},oldbrowser:function(){return!(window.history&&history.pushState)}},"undefined"==typeof jQuery&&console.log("You must load jQuery for using ajax mode in pdoPage."); \ No newline at end of file +((c,d)=>{function p(e,t){return(t||d).querySelector(e)}function i(e,t){return Array.from((t||d).querySelectorAll(e))}function h(e,t){try{var n=new URL(e,c.location.origin).searchParams.get(t);return n?Number(n):1}catch(e){return 1}}function s(e,t){var n=new URLSearchParams(c.location.search),e=(1===Number(t)?n.delete(e):n.set(e,String(t)),n.toString()),t=c.location.pathname+(e?"?"+e:"");c.history.pushState({pdoPage:t},"",t)}function o(e){this.config=e,this.key=e.pageVarKey,this.busy=!1,this.reached=!1,this.abortController=null,this.observer=null,this.sentinel=null,this.bound=!1}void 0===c.pdoPage&&(c.pdoPage={callbacks:{},keys:{},configs:{},instances:{}}),o.prototype.getWrapper=function(){return p(this.config.wrapper)},o.prototype.getRows=function(){return p(this.config.rows)},o.prototype.getPagination=function(){return p(this.config.pagination)},o.prototype.bind=function(){if(!this.bound){this.bound=!0;let o=this,a=this.config,r=this.key;switch(a.mode){case"default":d.addEventListener("click",function(e){var t,n=e.target.closest("a");n&&(t=n,i(a.link).includes(t))&&(e.preventDefault(),t=h(n.href,r),Number(pdoPage.keys[r])!==t&&(a.history&&s(r,t),o.load(n.href,"replace")))}),a.history&&(c.addEventListener("popstate",function(e){e.state&&e.state.pdoPage&&o.load(e.state.pdoPage,"replace")}),history.replaceState({pdoPage:c.location.href},""));break;case"scroll":case"button":var e,t;a.history?(e=this.getPagination())&&e.classList.add("pdopage-sticky"):(e=this.getPagination())&&(e.hidden=!0),"button"===a.mode?((e=this.getRows())&&a.moreTpl&&e.insertAdjacentHTML("afterend",a.moreTpl),e=i(a.link).some(function(e){return h(e.href,r)>Number(pdoPage.keys[r]||1)}),(t=p(a.more))&&!e&&(t.hidden=!0),d.addEventListener("click",function(e){var t,n;t=e.target,((n=p(n=a.more))&&(t===n||n.contains(t))?n:null)&&(e.preventDefault(),o.addPage())})):this.bindScroll()}}},o.prototype.bindScroll=function(){let t=this;var e=this.getRows();e&&(this.sentinel=d.createElement("div"),this.sentinel.className="pdopage-sentinel",this.sentinel.setAttribute("aria-hidden","true"),e.parentNode.insertBefore(this.sentinel,e.nextSibling),"IntersectionObserver"in c?(this.observer=new IntersectionObserver(function(e){e.forEach(function(e){!e.isIntersecting||t.busy||t.reached||(t.reached=!0,t.addPage())})},{root:null,rootMargin:"0px",threshold:0}),this.observer.observe(this.sentinel)):(c.addEventListener("scroll",e=function(){var e;t.busy||t.reached||(e=t.getWrapper())&&c.scrollY>e.offsetHeight-c.innerHeight&&(t.reached=!0,t.addPage())},{passive:!0}),c.addEventListener("load",e)))},o.prototype.addPage=function(){var t=this.key,n=Number(pdoPage.keys[t]||1);let o=null;var e,a=i(this.config.link);for(let e=0;en){o=a[e];break}o?(e=h(o.href,t),this.config.history&&s(t,e),this.load(o.href,"append")):(this.reached=!0,this.observer&&this.sentinel&&this.observer.unobserve(this.sentinel))},o.prototype.setHtml=function(e,t){if(e){for(;e.firstChild;)e.removeChild(e.firstChild);var n;t&&((n=d.createElement("template")).innerHTML=t,e.appendChild(n.content))}},o.prototype.appendHtml=function(e,t){var n;e&&t&&((n=d.createElement("template")).innerHTML=t,e.appendChild(n.content))},o.prototype.load=function(e,n){let o=this,a=this.config,r=this.key,i=this.getWrapper(),s=this.getRows(),l=this.getPagination();e=h(e,r);if(n=n||"replace",(Number(pdoPage.keys[r])!==e||"force"===n)&&!this.busy){this.busy=!0,pdoPage.keys[r]=e,"function"==typeof pdoPage.callbacks.before?pdoPage.callbacks.before.apply(this,[a]):i&&"scroll"!==a.mode&&i.classList.add("loading");let t=new URLSearchParams(c.location.search);Object.keys(pdoPage.keys).forEach(function(e){e!==r&&t.delete(e)}),t.set(r,String(e)),t.set("pageId",String(a.pageId)),t.set("hash",String(a.hash)),this.abortController&&this.abortController.abort(),this.abortController=new AbortController,fetch(a.connectorUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","X-Requested-With":"XMLHttpRequest"},body:t.toString(),credentials:"same-origin",signal:this.abortController.signal}).then(function(e){if(e.ok)return e.json();throw new Error("pdoPage request failed")}).then(function(e){var t;e&&e.total&&(o.setHtml(l,e.pagination||""),"append"===n?(o.appendHtml(s,e.output||""),"button"===a.mode?(t=p(a.more))&&(t.hidden=Number(e.pages)===Number(e.page)||0===Number(e.pages)):"scroll"===a.mode&&(Number(e.pages)===Number(e.page)||0===Number(e.pages)?(o.reached=!0,o.observer&&o.sentinel&&o.observer.unobserve(o.sentinel)):o.reached=!1)):o.setHtml(s,e.output||""),"function"==typeof pdoPage.callbacks.after?pdoPage.callbacks.after.apply(o,[a,e]):i&&(i.classList.remove("loading"),"default"===a.mode)&&!1!==a.scrollTop&&(t=i.getBoundingClientRect().top+c.scrollY-50,c.scrollTo(0,0=0.10.0" - } - }, - "node_modules/align-text/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -93,15 +67,6 @@ "node": ">=0.10.0" } }, - "node_modules/array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", @@ -157,21 +122,6 @@ "node": ">=8" } }, - "node_modules/browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", - "dev": true, - "dependencies": { - "pako": "~0.2.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, "node_modules/bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", @@ -191,41 +141,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "dependencies": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "dependencies": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -255,17 +170,6 @@ "node": ">= 10.0" } }, - "node_modules/cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "dependencies": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -299,45 +203,12 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "node_modules/continuable-cache": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", "dev": true }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "dependencies": { - "array-find-index": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -356,27 +227,6 @@ "ms": "^2.1.1" } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -402,15 +252,6 @@ "string-template": "~0.2.1" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -479,16 +320,19 @@ } }, "node_modules/figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, + "license": "MIT", "dependencies": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "escape-string-regexp": "^1.0.5" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/fill-range": { @@ -503,19 +347,6 @@ "node": ">=8" } }, - "node_modules/find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "dependencies": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/findup-sync": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", @@ -629,15 +460,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/getobject": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz", @@ -712,12 +534,6 @@ "node": ">= 0.10" } }, - "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, "node_modules/grunt": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.4.1.tgz", @@ -811,126 +627,20 @@ "node": ">=14.0" } }, - "node_modules/grunt-contrib-cssmin/node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/grunt-contrib-cssmin/node_modules/gzip-size": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", - "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "duplexer": "^0.1.1", - "pify": "^4.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/grunt-contrib-cssmin/node_modules/maxmin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-3.0.0.tgz", - "integrity": "sha512-wcahMInmGtg/7c6a75fr21Ch/Ks1Tb+Jtoan5Ft4bAI0ZvJqyOw8kkM7e7p8hDSzY805vmxwHT50KcjGwKyJ0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "figures": "^3.2.0", - "gzip-size": "^5.1.1", - "pretty-bytes": "^5.3.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/grunt-contrib-cssmin/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/grunt-contrib-cssmin/node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/grunt-contrib-uglify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-2.3.0.tgz", - "integrity": "sha1-s9AmDr3WzvoS/y+Onh4ln33kIW8=", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-5.2.2.tgz", + "integrity": "sha512-ITxiWxrjjP+RZu/aJ5GLvdele+sxlznh+6fK9Qckio5ma8f7Iv8woZjRkGfafvpuygxNefOJNc+hfjjBayRn2Q==", "dev": true, + "license": "MIT", "dependencies": { - "chalk": "^1.0.0", - "maxmin": "^1.1.0", - "object.assign": "^4.0.4", - "uglify-js": "~2.8.21", + "chalk": "^4.1.2", + "maxmin": "^3.0.0", + "uglify-js": "^3.16.1", "uri-path": "^1.0.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-contrib-uglify/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-contrib-uglify/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-contrib-uglify/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "engines": { - "node": ">=0.8.0" + "node": ">=12" } }, "node_modules/grunt-contrib-watch": { @@ -1060,19 +770,17 @@ } }, "node_modules/gzip-size": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz", - "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", "dev": true, + "license": "MIT", "dependencies": { - "browserify-zlib": "^0.1.4", - "concat-stream": "^1.4.1" - }, - "bin": { - "gzip-size": "cli.js" + "duplexer": "^0.1.1", + "pify": "^4.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, "node_modules/has": { @@ -1141,12 +849,6 @@ "node": "*" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, "node_modules/http-parser-js": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", @@ -1165,18 +867,6 @@ "node": ">=0.10.0" } }, - "node_modules/indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "dependencies": { - "repeating": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1219,18 +909,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "node_modules/is-core-module": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", @@ -1252,18 +930,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1321,12 +987,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -1336,12 +996,6 @@ "node": ">=0.10.0" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1379,15 +1033,6 @@ "node": ">=0.10.0" } }, - "node_modules/lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/liftup": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz", @@ -1428,50 +1073,12 @@ "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", "dev": true }, - "node_modules/load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "dependencies": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", @@ -1493,83 +1100,23 @@ "node": ">=0.10.0" } }, - "node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/maxmin": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz", - "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=", - "dev": true, - "dependencies": { - "chalk": "^1.0.0", - "figures": "^1.0.1", - "gzip-size": "^1.0.0", - "pretty-bytes": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/maxmin/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/maxmin/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-3.0.0.tgz", + "integrity": "sha512-wcahMInmGtg/7c6a75fr21Ch/Ks1Tb+Jtoan5Ft4bAI0ZvJqyOw8kkM7e7p8hDSzY805vmxwHT50KcjGwKyJ0g==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "chalk": "^4.1.0", + "figures": "^3.2.0", + "gzip-size": "^5.1.1", + "pretty-bytes": "^5.3.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/maxmin/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "dependencies": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" + "node": ">=10" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/micromatch": { @@ -1597,12 +1144,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -1633,18 +1174,6 @@ "nopt": "bin/nopt.js" } }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1663,33 +1192,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", @@ -1768,12 +1270,6 @@ "os-tmpdir": "^1.0.0" } }, - "node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", - "dev": true - }, "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -1788,18 +1284,6 @@ "node": ">=0.8" } }, - "node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -1809,18 +1293,6 @@ "node": ">=0.10.0" } }, - "node_modules/path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "dependencies": { - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1857,20 +1329,6 @@ "node": ">=0.10.0" } }, - "node_modules/path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -1884,57 +1342,28 @@ } }, "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, "node_modules/pretty-bytes": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", - "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true, - "dependencies": { - "get-stdin": "^4.0.1", - "meow": "^3.1.0" - }, - "bin": { - "pretty-bytes": "cli.js" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, "node_modules/qs": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz", @@ -1970,48 +1399,6 @@ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, - "node_modules/read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "dependencies": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "dependencies": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "node_modules/rechoir": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", @@ -2024,40 +1411,6 @@ "node": ">= 0.10" } }, - "node_modules/redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "dependencies": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "dependencies": { - "is-finite": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -2084,18 +1437,6 @@ "node": ">=0.10.0" } }, - "node_modules/right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "dependencies": { - "align-text": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2130,15 +1471,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -2153,12 +1485,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", - "dev": true - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -2169,53 +1495,12 @@ "node": ">=0.10.0" } }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, "node_modules/sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "dev": true }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/string-template": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", @@ -2234,33 +1519,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "dependencies": { - "is-utf8": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "dependencies": { - "get-stdin": "^4.0.1" - }, - "bin": { - "strip-indent": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2299,56 +1557,19 @@ "node": ">=8.0" } }, - "node_modules/trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, "node_modules/uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, - "dependencies": { - "source-map": "~0.5.1", - "yargs": "~3.10.0" - }, + "license": "BSD-2-Clause", "bin": { "uglifyjs": "bin/uglifyjs" }, "engines": { "node": ">=0.8.0" - }, - "optionalDependencies": { - "uglify-to-browserify": "~1.0.0" } }, - "node_modules/uglify-js/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -2398,16 +1619,6 @@ "node": ">= 0.10" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -2443,50 +1654,11 @@ "which": "bin/which" } }, - "node_modules/window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true - }, - "node_modules/yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "dependencies": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - }, - "node_modules/yargs/node_modules/camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } } } } diff --git a/package.json b/package.json index e170e45..b0de1c6 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "grunt": "^1.0.1", "grunt-banner": "^0.6.0", "grunt-contrib-cssmin": "^5.0.0", - "grunt-contrib-uglify": "^2.0.0", + "grunt-contrib-uglify": "^5.2.2", "grunt-contrib-watch": "^1.0.0" } } From 7fbb13a9c1b862d4edd78debf939469824e9ddc1 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Tue, 1 Sep 2026 22:06:25 +0600 Subject: [PATCH 2/3] docs(pdoPage): add English JSDoc for vanilla frontend Document public hooks, connector contract, and Controller methods. --- assets/components/pdotools/js/pdopage.js | 136 ++++++++++++++++++- assets/components/pdotools/js/pdopage.min.js | 2 +- 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/assets/components/pdotools/js/pdopage.js b/assets/components/pdotools/js/pdopage.js index e50119f..81656f8 100644 --- a/assets/components/pdotools/js/pdopage.js +++ b/assets/components/pdotools/js/pdopage.js @@ -1,3 +1,19 @@ +/** + * Vanilla frontend for the pdoPage snippet (ajax pagination). + * + * PHP registers this file and calls pdoPage.initialize(config). + * Requests go to connector.php with hash + pageId. The snippet only + * treats the call as ajax when X-Requested-With is XMLHttpRequest. + * + * Modes: default (click pager links), button (load more), scroll + * (IntersectionObserver, with a window scroll fallback). + * + * Optional hooks: pdoPage.callbacks.before / after. + * After a successful load the script fires CustomEvent 'pdopage:load' + * on document (detail: {config, response}). + * + * @file + */ (function (window, document) { 'use strict'; @@ -5,14 +21,32 @@ window.pdoPage = {callbacks: {}, keys: {}, configs: {}, instances: {}}; } + /** + * @param {string} selector + * @param {ParentNode} [root] + * @returns {Element|null} + */ function qs(selector, root) { return (root || document).querySelector(selector); } + /** + * @param {string} selector + * @param {ParentNode} [root] + * @returns {Element[]} + */ function qsa(selector, root) { return Array.from((root || document).querySelectorAll(selector)); } + /** + * Read the page number from a pagination href. + * Missing or invalid values become 1. + * + * @param {string} href + * @param {string} key Query param name (usually pageVarKey). + * @returns {number} + */ function pageFromHref(href, key) { try { const value = new URL(href, window.location.origin).searchParams.get(key); @@ -22,13 +56,29 @@ } } + /** + * True when el is one of the nodes matched by selector. + * Needed because closest() does not accept compound selectors + * like "#pdopage .pagination a". + * + * @param {Element} el + * @param {string} selector + * @returns {boolean} + */ function matchesSelector(el, selector) { return qsa(selector).includes(el); } + /** + * Resolve a click target to the "load more" element, if any. + * + * @param {EventTarget|null} target + * @param {string} moreSelector + * @returns {Element|null} + */ function isMoreTarget(target, moreSelector) { const more = qs(moreSelector); - if (!more) { + if (!more || !(target instanceof Node)) { return null; } if (target === more || more.contains(target)) { @@ -37,6 +87,13 @@ return null; } + /** + * Push the current page into the query string. Page 1 removes the key. + * + * @param {string} key + * @param {number|string} page + * @returns {void} + */ function setHistory(key, page) { const params = new URLSearchParams(window.location.search); if (Number(page) === 1) { @@ -49,6 +106,12 @@ window.history.pushState({pdoPage: url}, '', url); } + /** + * One pdoPage block on the page (keyed by pageVarKey). + * + * @param {Object} config Config from Paginator::loadJsCss(). + * @constructor + */ function Controller(config) { this.config = config; this.key = config.pageVarKey; @@ -60,18 +123,25 @@ this.bound = false; } + /** @returns {Element|null} */ Controller.prototype.getWrapper = function () { return qs(this.config.wrapper); }; + /** @returns {Element|null} */ Controller.prototype.getRows = function () { return qs(this.config.rows); }; + /** @returns {Element|null} */ Controller.prototype.getPagination = function () { return qs(this.config.pagination); }; + /** + * Attach listeners once per instance. + * @returns {void} + */ Controller.prototype.bind = function () { if (this.bound) { return; @@ -112,6 +182,7 @@ case 'scroll': case 'button': + // history off: hide the pager; history on: stick it with CSS if (config.history) { const pagination = this.getPagination(); if (pagination) { @@ -151,6 +222,10 @@ } }; + /** + * Watch a sentinel under the rows list and load the next page. + * @returns {void} + */ Controller.prototype.bindScroll = function () { const self = this; const rows = this.getRows(); @@ -176,6 +251,7 @@ return; } + // Older browsers without IntersectionObserver const onScroll = function () { if (self.busy || self.reached) { return; @@ -193,6 +269,10 @@ window.addEventListener('load', onScroll); }; + /** + * Find the next pager link and append that page. + * @returns {void} + */ Controller.prototype.addPage = function () { const key = this.key; const current = Number(pdoPage.keys[key] || 1); @@ -205,6 +285,7 @@ } } if (!next) { + // Nothing left: stop observing so we do not spin this.reached = true; if (this.observer && this.sentinel) { this.observer.unobserve(this.sentinel); @@ -218,6 +299,15 @@ this.load(next.href, 'append'); }; + /** + * Replace element contents with HTML from the connector response. + * Same trust model as the old jQuery .html() path: markup is + * rendered by MODX chunks on the server. + * + * @param {Element|null} el + * @param {string} html + * @returns {void} + */ Controller.prototype.setHtml = function (el, html) { if (!el) { return; @@ -233,6 +323,13 @@ el.appendChild(template.content); }; + /** + * Append HTML to an element (scroll / button modes). + * + * @param {Element|null} el + * @param {string} html + * @returns {void} + */ Controller.prototype.appendHtml = function (el, html) { if (!el || !html) { return; @@ -242,6 +339,13 @@ el.appendChild(template.content); }; + /** + * POST to connector.php and update the DOM. + * + * @param {string} href Pager link used to derive the page number. + * @param {'replace'|'append'|'force'} [mode='replace'] + * @returns {void} + */ Controller.prototype.load = function (href, mode) { const self = this; const config = this.config; @@ -268,6 +372,7 @@ wrapper.classList.add('loading'); } + // Keep other query params; drop sibling pdoPage keys from this page const params = new URLSearchParams(window.location.search); Object.keys(pdoPage.keys).forEach(function (otherKey) { if (otherKey !== key) { @@ -298,6 +403,7 @@ } return response.json(); }).then(function (data) { + // Snippet ajax branch always includes total when there is work to show if (!data || !data.total) { return; } @@ -350,6 +456,13 @@ }); }; + /** + * Entry point used by PHP frontend_init_js. + * One Controller per pageVarKey. + * + * @param {Object} config + * @returns {Controller} + */ pdoPage.initialize = function (config) { const key = config.pageVarKey; if (pdoPage.instances[key]) { @@ -365,6 +478,12 @@ return controller; }; + /** + * Public helper: load the next page for an existing instance. + * + * @param {Object} config + * @returns {void} + */ pdoPage.addPage = function (config) { const controller = pdoPage.instances[config.pageVarKey]; if (controller) { @@ -372,6 +491,14 @@ } }; + /** + * Public helper: load a specific href for an existing instance. + * + * @param {string} href + * @param {Object} config + * @param {'replace'|'append'|'force'} [mode] + * @returns {void} + */ pdoPage.loadPage = function (href, config, mode) { const controller = pdoPage.instances[config.pageVarKey]; if (controller) { @@ -379,6 +506,13 @@ } }; + /** + * Sync document.title with the pdoTitle snippet config, if present. + * + * @param {Object} config + * @param {{page?: number, pages?: number}} response + * @returns {void} + */ pdoPage.updateTitle = function (config, response) { if (typeof window.pdoTitle === 'undefined') { return; diff --git a/assets/components/pdotools/js/pdopage.min.js b/assets/components/pdotools/js/pdopage.min.js index fc50bf3..f56d736 100644 --- a/assets/components/pdotools/js/pdopage.min.js +++ b/assets/components/pdotools/js/pdopage.min.js @@ -1 +1 @@ -((c,d)=>{function p(e,t){return(t||d).querySelector(e)}function i(e,t){return Array.from((t||d).querySelectorAll(e))}function h(e,t){try{var n=new URL(e,c.location.origin).searchParams.get(t);return n?Number(n):1}catch(e){return 1}}function s(e,t){var n=new URLSearchParams(c.location.search),e=(1===Number(t)?n.delete(e):n.set(e,String(t)),n.toString()),t=c.location.pathname+(e?"?"+e:"");c.history.pushState({pdoPage:t},"",t)}function o(e){this.config=e,this.key=e.pageVarKey,this.busy=!1,this.reached=!1,this.abortController=null,this.observer=null,this.sentinel=null,this.bound=!1}void 0===c.pdoPage&&(c.pdoPage={callbacks:{},keys:{},configs:{},instances:{}}),o.prototype.getWrapper=function(){return p(this.config.wrapper)},o.prototype.getRows=function(){return p(this.config.rows)},o.prototype.getPagination=function(){return p(this.config.pagination)},o.prototype.bind=function(){if(!this.bound){this.bound=!0;let o=this,a=this.config,r=this.key;switch(a.mode){case"default":d.addEventListener("click",function(e){var t,n=e.target.closest("a");n&&(t=n,i(a.link).includes(t))&&(e.preventDefault(),t=h(n.href,r),Number(pdoPage.keys[r])!==t&&(a.history&&s(r,t),o.load(n.href,"replace")))}),a.history&&(c.addEventListener("popstate",function(e){e.state&&e.state.pdoPage&&o.load(e.state.pdoPage,"replace")}),history.replaceState({pdoPage:c.location.href},""));break;case"scroll":case"button":var e,t;a.history?(e=this.getPagination())&&e.classList.add("pdopage-sticky"):(e=this.getPagination())&&(e.hidden=!0),"button"===a.mode?((e=this.getRows())&&a.moreTpl&&e.insertAdjacentHTML("afterend",a.moreTpl),e=i(a.link).some(function(e){return h(e.href,r)>Number(pdoPage.keys[r]||1)}),(t=p(a.more))&&!e&&(t.hidden=!0),d.addEventListener("click",function(e){var t,n;t=e.target,((n=p(n=a.more))&&(t===n||n.contains(t))?n:null)&&(e.preventDefault(),o.addPage())})):this.bindScroll()}}},o.prototype.bindScroll=function(){let t=this;var e=this.getRows();e&&(this.sentinel=d.createElement("div"),this.sentinel.className="pdopage-sentinel",this.sentinel.setAttribute("aria-hidden","true"),e.parentNode.insertBefore(this.sentinel,e.nextSibling),"IntersectionObserver"in c?(this.observer=new IntersectionObserver(function(e){e.forEach(function(e){!e.isIntersecting||t.busy||t.reached||(t.reached=!0,t.addPage())})},{root:null,rootMargin:"0px",threshold:0}),this.observer.observe(this.sentinel)):(c.addEventListener("scroll",e=function(){var e;t.busy||t.reached||(e=t.getWrapper())&&c.scrollY>e.offsetHeight-c.innerHeight&&(t.reached=!0,t.addPage())},{passive:!0}),c.addEventListener("load",e)))},o.prototype.addPage=function(){var t=this.key,n=Number(pdoPage.keys[t]||1);let o=null;var e,a=i(this.config.link);for(let e=0;en){o=a[e];break}o?(e=h(o.href,t),this.config.history&&s(t,e),this.load(o.href,"append")):(this.reached=!0,this.observer&&this.sentinel&&this.observer.unobserve(this.sentinel))},o.prototype.setHtml=function(e,t){if(e){for(;e.firstChild;)e.removeChild(e.firstChild);var n;t&&((n=d.createElement("template")).innerHTML=t,e.appendChild(n.content))}},o.prototype.appendHtml=function(e,t){var n;e&&t&&((n=d.createElement("template")).innerHTML=t,e.appendChild(n.content))},o.prototype.load=function(e,n){let o=this,a=this.config,r=this.key,i=this.getWrapper(),s=this.getRows(),l=this.getPagination();e=h(e,r);if(n=n||"replace",(Number(pdoPage.keys[r])!==e||"force"===n)&&!this.busy){this.busy=!0,pdoPage.keys[r]=e,"function"==typeof pdoPage.callbacks.before?pdoPage.callbacks.before.apply(this,[a]):i&&"scroll"!==a.mode&&i.classList.add("loading");let t=new URLSearchParams(c.location.search);Object.keys(pdoPage.keys).forEach(function(e){e!==r&&t.delete(e)}),t.set(r,String(e)),t.set("pageId",String(a.pageId)),t.set("hash",String(a.hash)),this.abortController&&this.abortController.abort(),this.abortController=new AbortController,fetch(a.connectorUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","X-Requested-With":"XMLHttpRequest"},body:t.toString(),credentials:"same-origin",signal:this.abortController.signal}).then(function(e){if(e.ok)return e.json();throw new Error("pdoPage request failed")}).then(function(e){var t;e&&e.total&&(o.setHtml(l,e.pagination||""),"append"===n?(o.appendHtml(s,e.output||""),"button"===a.mode?(t=p(a.more))&&(t.hidden=Number(e.pages)===Number(e.page)||0===Number(e.pages)):"scroll"===a.mode&&(Number(e.pages)===Number(e.page)||0===Number(e.pages)?(o.reached=!0,o.observer&&o.sentinel&&o.observer.unobserve(o.sentinel)):o.reached=!1)):o.setHtml(s,e.output||""),"function"==typeof pdoPage.callbacks.after?pdoPage.callbacks.after.apply(o,[a,e]):i&&(i.classList.remove("loading"),"default"===a.mode)&&!1!==a.scrollTop&&(t=i.getBoundingClientRect().top+c.scrollY-50,c.scrollTo(0,0{function p(e,t){return(t||d).querySelector(e)}function i(e,t){return Array.from((t||d).querySelectorAll(e))}function h(e,t){try{var n=new URL(e,c.location.origin).searchParams.get(t);return n?Number(n):1}catch(e){return 1}}function s(e,t){var n=new URLSearchParams(c.location.search),e=(1===Number(t)?n.delete(e):n.set(e,String(t)),n.toString()),t=c.location.pathname+(e?"?"+e:"");c.history.pushState({pdoPage:t},"",t)}function o(e){this.config=e,this.key=e.pageVarKey,this.busy=!1,this.reached=!1,this.abortController=null,this.observer=null,this.sentinel=null,this.bound=!1}void 0===c.pdoPage&&(c.pdoPage={callbacks:{},keys:{},configs:{},instances:{}}),o.prototype.getWrapper=function(){return p(this.config.wrapper)},o.prototype.getRows=function(){return p(this.config.rows)},o.prototype.getPagination=function(){return p(this.config.pagination)},o.prototype.bind=function(){if(!this.bound){this.bound=!0;let o=this,a=this.config,r=this.key;switch(a.mode){case"default":d.addEventListener("click",function(e){var t,n=e.target.closest("a");n&&(t=n,i(a.link).includes(t))&&(e.preventDefault(),t=h(n.href,r),Number(pdoPage.keys[r])!==t&&(a.history&&s(r,t),o.load(n.href,"replace")))}),a.history&&(c.addEventListener("popstate",function(e){e.state&&e.state.pdoPage&&o.load(e.state.pdoPage,"replace")}),history.replaceState({pdoPage:c.location.href},""));break;case"scroll":case"button":var e,t;a.history?(e=this.getPagination())&&e.classList.add("pdopage-sticky"):(e=this.getPagination())&&(e.hidden=!0),"button"===a.mode?((e=this.getRows())&&a.moreTpl&&e.insertAdjacentHTML("afterend",a.moreTpl),e=i(a.link).some(function(e){return h(e.href,r)>Number(pdoPage.keys[r]||1)}),(t=p(a.more))&&!e&&(t.hidden=!0),d.addEventListener("click",function(e){var t,n;t=e.target,((n=p(n=a.more))&&t instanceof Node&&(t===n||n.contains(t))?n:null)&&(e.preventDefault(),o.addPage())})):this.bindScroll()}}},o.prototype.bindScroll=function(){let t=this;var e=this.getRows();e&&(this.sentinel=d.createElement("div"),this.sentinel.className="pdopage-sentinel",this.sentinel.setAttribute("aria-hidden","true"),e.parentNode.insertBefore(this.sentinel,e.nextSibling),"IntersectionObserver"in c?(this.observer=new IntersectionObserver(function(e){e.forEach(function(e){!e.isIntersecting||t.busy||t.reached||(t.reached=!0,t.addPage())})},{root:null,rootMargin:"0px",threshold:0}),this.observer.observe(this.sentinel)):(c.addEventListener("scroll",e=function(){var e;t.busy||t.reached||(e=t.getWrapper())&&c.scrollY>e.offsetHeight-c.innerHeight&&(t.reached=!0,t.addPage())},{passive:!0}),c.addEventListener("load",e)))},o.prototype.addPage=function(){var t=this.key,n=Number(pdoPage.keys[t]||1);let o=null;var e,a=i(this.config.link);for(let e=0;en){o=a[e];break}o?(e=h(o.href,t),this.config.history&&s(t,e),this.load(o.href,"append")):(this.reached=!0,this.observer&&this.sentinel&&this.observer.unobserve(this.sentinel))},o.prototype.setHtml=function(e,t){if(e){for(;e.firstChild;)e.removeChild(e.firstChild);var n;t&&((n=d.createElement("template")).innerHTML=t,e.appendChild(n.content))}},o.prototype.appendHtml=function(e,t){var n;e&&t&&((n=d.createElement("template")).innerHTML=t,e.appendChild(n.content))},o.prototype.load=function(e,n){let o=this,a=this.config,r=this.key,i=this.getWrapper(),s=this.getRows(),l=this.getPagination();e=h(e,r);if(n=n||"replace",(Number(pdoPage.keys[r])!==e||"force"===n)&&!this.busy){this.busy=!0,pdoPage.keys[r]=e,"function"==typeof pdoPage.callbacks.before?pdoPage.callbacks.before.apply(this,[a]):i&&"scroll"!==a.mode&&i.classList.add("loading");let t=new URLSearchParams(c.location.search);Object.keys(pdoPage.keys).forEach(function(e){e!==r&&t.delete(e)}),t.set(r,String(e)),t.set("pageId",String(a.pageId)),t.set("hash",String(a.hash)),this.abortController&&this.abortController.abort(),this.abortController=new AbortController,fetch(a.connectorUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","X-Requested-With":"XMLHttpRequest"},body:t.toString(),credentials:"same-origin",signal:this.abortController.signal}).then(function(e){if(e.ok)return e.json();throw new Error("pdoPage request failed")}).then(function(e){var t;e&&e.total&&(o.setHtml(l,e.pagination||""),"append"===n?(o.appendHtml(s,e.output||""),"button"===a.mode?(t=p(a.more))&&(t.hidden=Number(e.pages)===Number(e.page)||0===Number(e.pages)):"scroll"===a.mode&&(Number(e.pages)===Number(e.page)||0===Number(e.pages)?(o.reached=!0,o.observer&&o.sentinel&&o.observer.unobserve(o.sentinel)):o.reached=!1)):o.setHtml(s,e.output||""),"function"==typeof pdoPage.callbacks.after?pdoPage.callbacks.after.apply(o,[a,e]):i&&(i.classList.remove("loading"),"default"===a.mode)&&!1!==a.scrollTop&&(t=i.getBoundingClientRect().top+c.scrollY-50,c.scrollTo(0,0 Date: Tue, 1 Sep 2026 22:15:42 +0600 Subject: [PATCH 3/3] fix(pdoPage): address thermo-nuclear review blockers Normalize instances when PHP already created pdoPage, restore Hash helpers, abort for latest-wins loads, continue scroll while the sentinel stays visible, and commit/rollback page state on empty or failed responses. --- assets/components/pdotools/js/pdopage.js | 584 +++++++++++------- assets/components/pdotools/js/pdopage.min.js | 2 +- core/components/pdotools/docs/changelog.txt | 2 + .../pdotools/src/Support/Paginator.php | 2 +- 4 files changed, 368 insertions(+), 222 deletions(-) diff --git a/assets/components/pdotools/js/pdopage.js b/assets/components/pdotools/js/pdopage.js index 81656f8..8a7baa8 100644 --- a/assets/components/pdotools/js/pdopage.js +++ b/assets/components/pdotools/js/pdopage.js @@ -8,7 +8,7 @@ * Modes: default (click pager links), button (load more), scroll * (IntersectionObserver, with a window scroll fallback). * - * Optional hooks: pdoPage.callbacks.before / after. + * Optional hooks: pdoPage.callbacks.before / after (this === pdoPage). * After a successful load the script fires CustomEvent 'pdopage:load' * on document (detail: {config, response}). * @@ -17,34 +17,17 @@ (function (window, document) { 'use strict'; - if (typeof window.pdoPage === 'undefined') { - window.pdoPage = {callbacks: {}, keys: {}, configs: {}, instances: {}}; - } + // PHP startup may create pdoPage without instances. Always normalize. + const pdoPage = window.pdoPage || {}; + pdoPage.callbacks = pdoPage.callbacks || {}; + pdoPage.keys = pdoPage.keys || {}; + pdoPage.configs = pdoPage.configs || {}; + pdoPage.instances = pdoPage.instances || {}; + window.pdoPage = pdoPage; /** - * @param {string} selector - * @param {ParentNode} [root] - * @returns {Element|null} - */ - function qs(selector, root) { - return (root || document).querySelector(selector); - } - - /** - * @param {string} selector - * @param {ParentNode} [root] - * @returns {Element[]} - */ - function qsa(selector, root) { - return Array.from((root || document).querySelectorAll(selector)); - } - - /** - * Read the page number from a pagination href. - * Missing or invalid values become 1. - * * @param {string} href - * @param {string} key Query param name (usually pageVarKey). + * @param {string} key * @returns {number} */ function pageFromHref(href, key) { @@ -57,53 +40,147 @@ } /** - * True when el is one of the nodes matched by selector. - * Needed because closest() does not accept compound selectors - * like "#pdopage .pagination a". - * - * @param {Element} el - * @param {string} selector - * @returns {boolean} + * @param {Element|null} el + * @param {string} html + * @param {boolean} replace + * @returns {void} */ - function matchesSelector(el, selector) { - return qsa(selector).includes(el); + function applyHtml(el, html, replace) { + if (!el) { + return; + } + if (replace) { + el.innerHTML = html || ''; + return; + } + if (!html) { + return; + } + el.insertAdjacentHTML('beforeend', html); } /** - * Resolve a click target to the "load more" element, if any. - * - * @param {EventTarget|null} target - * @param {string} moreSelector - * @returns {Element|null} + * Public URL helpers used by custom frontend_init_js / filters. + * Same shape as the old jQuery-era Hash API (query string via pushState). */ - function isMoreTarget(target, moreSelector) { - const more = qs(moreSelector); - if (!more || !(target instanceof Node)) { - return null; - } - if (target === more || more.contains(target)) { - return more; + pdoPage.Hash = { + /** + * @returns {Object} + */ + get: function () { + const vars = {}; + let raw; + let splitter; + if (!this.oldbrowser()) { + const pos = window.location.href.indexOf('?'); + raw = pos !== -1 + ? decodeURIComponent(window.location.href.substr(pos + 1)).replace(/\+/g, ' ') + : ''; + splitter = '&'; + } else { + raw = decodeURIComponent(window.location.hash.substr(1)).replace(/\+/g, ' '); + splitter = '/'; + } + if (!raw.length) { + return vars; + } + const hashes = raw.split(splitter); + for (let i = 0; i < hashes.length; i++) { + const pair = hashes[i].split('='); + if (typeof pair[1] === 'undefined') { + vars.anchor = pair[0]; + continue; + } + const matches = pair[0].match(/\[(.*?|)\]$/); + if (matches) { + const key = pair[0].replace(matches[0], ''); + if (!Object.prototype.hasOwnProperty.call(vars, key)) { + vars[key] = matches[1] === '' ? [] : {}; + } + if (vars[key] instanceof Array) { + vars[key].push(pair[1]); + } else { + vars[key][matches[1]] = pair[1]; + } + } else { + vars[pair[0]] = pair[1]; + } + } + return vars; + }, + + /** + * @param {Object} vars + * @returns {void} + */ + set: function (vars) { + let hash = ''; + Object.keys(vars).forEach(function (i) { + const value = vars[i]; + if (value && typeof value === 'object') { + Object.keys(value).forEach(function (j) { + if (value instanceof Array) { + hash += '&' + i + '[]=' + value[j]; + } else { + hash += '&' + i + '[' + j + ']=' + value[j]; + } + }); + } else { + hash += '&' + i + '=' + value; + } + }); + if (!this.oldbrowser()) { + const query = hash.length ? '?' + hash.substr(1) : ''; + const url = document.location.pathname + query; + window.history.pushState({pdoPage: url}, '', url); + } else { + window.location.hash = hash.substr(1); + } + }, + + /** + * @param {string} key + * @param {*} val + * @returns {void} + */ + add: function (key, val) { + const hash = this.get(); + hash[key] = val; + this.set(hash); + }, + + /** + * @param {string} key + * @returns {void} + */ + remove: function (key) { + const hash = this.get(); + delete hash[key]; + this.set(hash); + }, + + /** @returns {void} */ + clear: function () { + this.set({}); + }, + + /** @returns {boolean} */ + oldbrowser: function () { + return !(window.history && history.pushState); } - return null; - } + }; /** - * Push the current page into the query string. Page 1 removes the key. - * * @param {string} key * @param {number|string} page * @returns {void} */ function setHistory(key, page) { - const params = new URLSearchParams(window.location.search); if (Number(page) === 1) { - params.delete(key); + pdoPage.Hash.remove(key); } else { - params.set(key, String(page)); + pdoPage.Hash.add(key, page); } - const query = params.toString(); - const url = window.location.pathname + (query ? '?' + query : ''); - window.history.pushState({pdoPage: url}, '', url); } /** @@ -118,28 +195,29 @@ this.busy = false; this.reached = false; this.abortController = null; + this.requestId = 0; this.observer = null; this.sentinel = null; this.bound = false; + this.previousPage = null; } /** @returns {Element|null} */ Controller.prototype.getWrapper = function () { - return qs(this.config.wrapper); + return document.querySelector(this.config.wrapper); }; /** @returns {Element|null} */ Controller.prototype.getRows = function () { - return qs(this.config.rows); + return document.querySelector(this.config.rows); }; /** @returns {Element|null} */ Controller.prototype.getPagination = function () { - return qs(this.config.pagination); + return document.querySelector(this.config.pagination); }; /** - * Attach listeners once per instance. * @returns {void} */ Controller.prototype.bind = function () { @@ -148,88 +226,110 @@ } this.bound = true; + switch (this.config.mode) { + case 'default': + this.bindDefault(); + break; + case 'button': + this.bindPagerChrome(); + this.bindButton(); + break; + case 'scroll': + this.bindPagerChrome(); + this.bindScroll(); + break; + } + }; + + /** + * Clickable pager + optional history/popstate. + * @returns {void} + */ + Controller.prototype.bindDefault = function () { const self = this; const config = this.config; const key = this.key; - switch (config.mode) { - case 'default': - document.addEventListener('click', function (e) { - const link = e.target.closest('a'); - if (!link || !matchesSelector(link, config.link)) { - return; - } - e.preventDefault(); - const page = pageFromHref(link.href, key); - if (Number(pdoPage.keys[key]) === page) { - return; - } - if (config.history) { - setHistory(key, page); - } - self.load(link.href, 'replace'); - }); + document.addEventListener('click', function (e) { + const link = e.target.closest('a'); + if (!link || !link.matches(config.link)) { + return; + } + e.preventDefault(); + const page = pageFromHref(link.href, key); + if (Number(pdoPage.keys[key]) === page) { + return; + } + if (config.history) { + setHistory(key, page); + } + self.load(link.href, 'replace'); + }); - if (config.history) { - window.addEventListener('popstate', function (e) { - if (e.state && e.state.pdoPage) { - self.load(e.state.pdoPage, 'replace'); - } - }); - history.replaceState({pdoPage: window.location.href}, ''); - } - break; + if (!config.history) { + return; + } + window.addEventListener('popstate', function (e) { + if (e.state && e.state.pdoPage) { + self.load(e.state.pdoPage, 'replace'); + } + }); + history.replaceState({pdoPage: window.location.href}, ''); + }; - case 'scroll': - case 'button': - // history off: hide the pager; history on: stick it with CSS - if (config.history) { - const pagination = this.getPagination(); - if (pagination) { - pagination.classList.add('pdopage-sticky'); - } - } else { - const hiddenPagination = this.getPagination(); - if (hiddenPagination) { - hiddenPagination.hidden = true; - } - } + /** + * Sticky pager when history is on; hide pager when history is off. + * @returns {void} + */ + Controller.prototype.bindPagerChrome = function () { + const pagination = this.getPagination(); + if (!pagination) { + return; + } + if (this.config.history) { + pagination.classList.add('pdopage-sticky'); + } else { + pagination.hidden = true; + } + }; - if (config.mode === 'button') { - const rows = this.getRows(); - if (rows && config.moreTpl) { - rows.insertAdjacentHTML('afterend', config.moreTpl); - } - const hasMore = qsa(config.link).some(function (link) { - return pageFromHref(link.href, key) > Number(pdoPage.keys[key] || 1); - }); - const more = qs(config.more); - if (more && !hasMore) { - more.hidden = true; - } - document.addEventListener('click', function (e) { - const btn = isMoreTarget(e.target, config.more); - if (!btn) { - return; - } - e.preventDefault(); - self.addPage(); - }); - } else { - this.bindScroll(); - } - break; + /** + * @returns {void} + */ + Controller.prototype.bindButton = function () { + const self = this; + const config = this.config; + const key = this.key; + const rows = this.getRows(); + if (rows && config.moreTpl) { + rows.insertAdjacentHTML('afterend', config.moreTpl); + } + const current = Number(pdoPage.keys[key] || 1); + const hasMore = Array.from(document.querySelectorAll(config.link)).some(function (link) { + return pageFromHref(link.href, key) > current; + }); + const more = document.querySelector(config.more); + if (more && !hasMore) { + more.hidden = true; } + document.addEventListener('click', function (e) { + const btn = e.target.closest(config.more); + if (!btn) { + return; + } + e.preventDefault(); + self.addPage(); + }); }; /** - * Watch a sentinel under the rows list and load the next page. + * Sentinel under the rows list triggers the next page. * @returns {void} */ Controller.prototype.bindScroll = function () { const self = this; const rows = this.getRows(); - if (!rows) { + if (!rows || !rows.parentNode) { return; } @@ -251,7 +351,6 @@ return; } - // Older browsers without IntersectionObserver const onScroll = function () { if (self.busy || self.reached) { return; @@ -269,110 +368,180 @@ window.addEventListener('load', onScroll); }; + /** @returns {void} */ + Controller.prototype.stopScroll = function () { + this.reached = true; + if (this.observer && this.sentinel) { + this.observer.unobserve(this.sentinel); + } + }; + /** - * Find the next pager link and append that page. + * True when the scroll sentinel is still in the viewport. + * @returns {boolean} + */ + Controller.prototype.sentinelVisible = function () { + if (!this.sentinel) { + return false; + } + const rect = this.sentinel.getBoundingClientRect(); + return rect.top < window.innerHeight && rect.bottom > 0; + }; + + /** + * After append: either stop, or keep loading while the sentinel stays visible. + * + * @param {{page?: number, pages?: number}} data * @returns {void} */ - Controller.prototype.addPage = function () { + Controller.prototype.continueScrollIfNeeded = function (data) { + if (Number(data.pages) === Number(data.page) || Number(data.pages) === 0) { + this.stopScroll(); + return; + } + this.reached = false; + if (this.sentinelVisible()) { + this.reached = true; + this.addPage(); + } + }; + + /** + * @returns {Element|null} + */ + Controller.prototype.findNextLink = function () { const key = this.key; const current = Number(pdoPage.keys[key] || 1); - let next = null; - const links = qsa(this.config.link); + const links = document.querySelectorAll(this.config.link); for (let i = 0; i < links.length; i++) { if (pageFromHref(links[i].href, key) > current) { - next = links[i]; - break; + return links[i]; } } + return null; + }; + + /** + * @returns {void} + */ + Controller.prototype.addPage = function () { + const next = this.findNextLink(); if (!next) { - // Nothing left: stop observing so we do not spin - this.reached = true; - if (this.observer && this.sentinel) { - this.observer.unobserve(this.sentinel); - } + this.stopScroll(); return; } - const page = pageFromHref(next.href, key); + const page = pageFromHref(next.href, this.key); if (this.config.history) { - setHistory(key, page); + setHistory(this.key, page); } this.load(next.href, 'append'); }; /** - * Replace element contents with HTML from the connector response. - * Same trust model as the old jQuery .html() path: markup is - * rendered by MODX chunks on the server. + * Mode-specific work after the DOM has been updated. * - * @param {Element|null} el - * @param {string} html + * @param {Object} data + * @param {'replace'|'append'|'force'} applyMode * @returns {void} */ - Controller.prototype.setHtml = function (el, html) { - if (!el) { + Controller.prototype.afterApply = function (data, applyMode) { + const config = this.config; + + if (applyMode === 'force') { + const page = Number(data.page) || 1; + pdoPage.keys[this.key] = page; + if (config.history) { + setHistory(this.key, page); + } + } + + if (config.mode === 'button' && (applyMode === 'append' || applyMode === 'force')) { + const more = document.querySelector(config.more); + if (more) { + more.hidden = Number(data.pages) === Number(data.page) || Number(data.pages) === 0; + } return; } - while (el.firstChild) { - el.removeChild(el.firstChild); + + if (config.mode === 'scroll' && applyMode === 'append') { + this.continueScrollIfNeeded(data); } - if (!html) { - return; + }; + + /** + * Roll back optimistic page state after abort-safe failure. + * @returns {void} + */ + Controller.prototype.rollback = function () { + if (this.previousPage !== null) { + pdoPage.keys[this.key] = this.previousPage; + if (this.config.history) { + setHistory(this.key, this.previousPage); + } + this.previousPage = null; + } + const wrapper = this.getWrapper(); + if (wrapper) { + wrapper.classList.remove('loading'); } - const template = document.createElement('template'); - template.innerHTML = html; - el.appendChild(template.content); + this.reached = false; }; /** - * Append HTML to an element (scroll / button modes). + * Apply connector JSON to the DOM. * - * @param {Element|null} el - * @param {string} html + * @param {Object} data + * @param {'replace'|'append'|'force'} applyMode * @returns {void} */ - Controller.prototype.appendHtml = function (el, html) { - if (!el || !html) { - return; + Controller.prototype.applyResponse = function (data, applyMode) { + const rows = this.getRows(); + const pagination = this.getPagination(); + applyHtml(pagination, data.pagination || '', true); + if (applyMode === 'append') { + applyHtml(rows, data.output || '', false); + } else { + applyHtml(rows, data.output || '', true); } - const template = document.createElement('template'); - template.innerHTML = html; - el.appendChild(template.content); + this.afterApply(data, applyMode); }; /** * POST to connector.php and update the DOM. + * Latest request wins: a new load() aborts the previous fetch. * - * @param {string} href Pager link used to derive the page number. - * @param {'replace'|'append'|'force'} [mode='replace'] + * @param {string} href + * @param {'replace'|'append'|'force'} [applyMode='replace'] * @returns {void} */ - Controller.prototype.load = function (href, mode) { + Controller.prototype.load = function (href, applyMode) { const self = this; const config = this.config; const key = this.key; const wrapper = this.getWrapper(); - const rows = this.getRows(); - const pagination = this.getPagination(); const page = pageFromHref(href, key); - mode = mode || 'replace'; + applyMode = applyMode || 'replace'; - if (Number(pdoPage.keys[key]) === page && mode !== 'force') { + if (Number(pdoPage.keys[key]) === page && applyMode !== 'force') { return; } - if (this.busy) { - return; + + if (this.abortController) { + this.abortController.abort(); } + this.abortController = new AbortController(); + const requestId = ++this.requestId; this.busy = true; + this.previousPage = Number(pdoPage.keys[key] || 1); pdoPage.keys[key] = page; if (typeof pdoPage.callbacks.before === 'function') { - pdoPage.callbacks.before.apply(this, [config]); + pdoPage.callbacks.before.apply(pdoPage, [config]); } else if (wrapper && config.mode !== 'scroll') { wrapper.classList.add('loading'); } - // Keep other query params; drop sibling pdoPage keys from this page const params = new URLSearchParams(window.location.search); Object.keys(pdoPage.keys).forEach(function (otherKey) { if (otherKey !== key) { @@ -383,11 +552,6 @@ params.set('pageId', String(config.pageId)); params.set('hash', String(config.hash)); - if (this.abortController) { - this.abortController.abort(); - } - this.abortController = new AbortController(); - fetch(config.connectorUrl, { method: 'POST', headers: { @@ -403,34 +567,19 @@ } return response.json(); }).then(function (data) { - // Snippet ajax branch always includes total when there is work to show - if (!data || !data.total) { + if (requestId !== self.requestId) { return; } - self.setHtml(pagination, data.pagination || ''); - if (mode === 'append') { - self.appendHtml(rows, data.output || ''); - if (config.mode === 'button') { - const more = qs(config.more); - if (more) { - more.hidden = Number(data.pages) === Number(data.page) || Number(data.pages) === 0; - } - } else if (config.mode === 'scroll') { - if (Number(data.pages) === Number(data.page) || Number(data.pages) === 0) { - self.reached = true; - if (self.observer && self.sentinel) { - self.observer.unobserve(self.sentinel); - } - } else { - self.reached = false; - } - } - } else { - self.setHtml(rows, data.output || ''); + if (!data || typeof data !== 'object' || !Object.prototype.hasOwnProperty.call(data, 'total')) { + self.rollback(); + return; } + // total may be 0 on an empty result set; still apply markup + self.previousPage = null; + self.applyResponse(data, applyMode); if (typeof pdoPage.callbacks.after === 'function') { - pdoPage.callbacks.after.apply(self, [config, data]); + pdoPage.callbacks.after.apply(pdoPage, [config, data]); } else if (wrapper) { wrapper.classList.remove('loading'); if (config.mode === 'default' && config.scrollTop !== false) { @@ -444,22 +593,21 @@ detail: {config: config, response: data} })); }).catch(function (error) { - if (error && error.name === 'AbortError') { + if (requestId !== self.requestId) { return; } - if (wrapper) { - wrapper.classList.remove('loading'); + if (error && error.name === 'AbortError') { + return; } - self.reached = false; + self.rollback(); }).then(function () { - self.busy = false; + if (requestId === self.requestId) { + self.busy = false; + } }); }; /** - * Entry point used by PHP frontend_init_js. - * One Controller per pageVarKey. - * * @param {Object} config * @returns {Controller} */ @@ -468,9 +616,11 @@ if (pdoPage.instances[key]) { return pdoPage.instances[key]; } - const params = new URLSearchParams(window.location.search); - const current = params.get(key); - pdoPage.keys[key] = current ? Number(current) : 1; + const params = pdoPage.Hash.get(); + const current = params[key]; + pdoPage.keys[key] = current !== undefined && current !== null && current !== '' + ? Number(current) + : 1; pdoPage.configs[key] = config; const controller = new Controller(config); pdoPage.instances[key] = controller; @@ -479,8 +629,6 @@ }; /** - * Public helper: load the next page for an existing instance. - * * @param {Object} config * @returns {void} */ @@ -492,8 +640,6 @@ }; /** - * Public helper: load a specific href for an existing instance. - * * @param {string} href * @param {Object} config * @param {'replace'|'append'|'force'} [mode] @@ -507,8 +653,6 @@ }; /** - * Sync document.title with the pdoTitle snippet config, if present. - * * @param {Object} config * @param {{page?: number, pages?: number}} response * @returns {void} diff --git a/assets/components/pdotools/js/pdopage.min.js b/assets/components/pdotools/js/pdopage.min.js index f56d736..fe43e81 100644 --- a/assets/components/pdotools/js/pdopage.min.js +++ b/assets/components/pdotools/js/pdopage.min.js @@ -1 +1 @@ -((c,d)=>{function p(e,t){return(t||d).querySelector(e)}function i(e,t){return Array.from((t||d).querySelectorAll(e))}function h(e,t){try{var n=new URL(e,c.location.origin).searchParams.get(t);return n?Number(n):1}catch(e){return 1}}function s(e,t){var n=new URLSearchParams(c.location.search),e=(1===Number(t)?n.delete(e):n.set(e,String(t)),n.toString()),t=c.location.pathname+(e?"?"+e:"");c.history.pushState({pdoPage:t},"",t)}function o(e){this.config=e,this.key=e.pageVarKey,this.busy=!1,this.reached=!1,this.abortController=null,this.observer=null,this.sentinel=null,this.bound=!1}void 0===c.pdoPage&&(c.pdoPage={callbacks:{},keys:{},configs:{},instances:{}}),o.prototype.getWrapper=function(){return p(this.config.wrapper)},o.prototype.getRows=function(){return p(this.config.rows)},o.prototype.getPagination=function(){return p(this.config.pagination)},o.prototype.bind=function(){if(!this.bound){this.bound=!0;let o=this,a=this.config,r=this.key;switch(a.mode){case"default":d.addEventListener("click",function(e){var t,n=e.target.closest("a");n&&(t=n,i(a.link).includes(t))&&(e.preventDefault(),t=h(n.href,r),Number(pdoPage.keys[r])!==t&&(a.history&&s(r,t),o.load(n.href,"replace")))}),a.history&&(c.addEventListener("popstate",function(e){e.state&&e.state.pdoPage&&o.load(e.state.pdoPage,"replace")}),history.replaceState({pdoPage:c.location.href},""));break;case"scroll":case"button":var e,t;a.history?(e=this.getPagination())&&e.classList.add("pdopage-sticky"):(e=this.getPagination())&&(e.hidden=!0),"button"===a.mode?((e=this.getRows())&&a.moreTpl&&e.insertAdjacentHTML("afterend",a.moreTpl),e=i(a.link).some(function(e){return h(e.href,r)>Number(pdoPage.keys[r]||1)}),(t=p(a.more))&&!e&&(t.hidden=!0),d.addEventListener("click",function(e){var t,n;t=e.target,((n=p(n=a.more))&&t instanceof Node&&(t===n||n.contains(t))?n:null)&&(e.preventDefault(),o.addPage())})):this.bindScroll()}}},o.prototype.bindScroll=function(){let t=this;var e=this.getRows();e&&(this.sentinel=d.createElement("div"),this.sentinel.className="pdopage-sentinel",this.sentinel.setAttribute("aria-hidden","true"),e.parentNode.insertBefore(this.sentinel,e.nextSibling),"IntersectionObserver"in c?(this.observer=new IntersectionObserver(function(e){e.forEach(function(e){!e.isIntersecting||t.busy||t.reached||(t.reached=!0,t.addPage())})},{root:null,rootMargin:"0px",threshold:0}),this.observer.observe(this.sentinel)):(c.addEventListener("scroll",e=function(){var e;t.busy||t.reached||(e=t.getWrapper())&&c.scrollY>e.offsetHeight-c.innerHeight&&(t.reached=!0,t.addPage())},{passive:!0}),c.addEventListener("load",e)))},o.prototype.addPage=function(){var t=this.key,n=Number(pdoPage.keys[t]||1);let o=null;var e,a=i(this.config.link);for(let e=0;en){o=a[e];break}o?(e=h(o.href,t),this.config.history&&s(t,e),this.load(o.href,"append")):(this.reached=!0,this.observer&&this.sentinel&&this.observer.unobserve(this.sentinel))},o.prototype.setHtml=function(e,t){if(e){for(;e.firstChild;)e.removeChild(e.firstChild);var n;t&&((n=d.createElement("template")).innerHTML=t,e.appendChild(n.content))}},o.prototype.appendHtml=function(e,t){var n;e&&t&&((n=d.createElement("template")).innerHTML=t,e.appendChild(n.content))},o.prototype.load=function(e,n){let o=this,a=this.config,r=this.key,i=this.getWrapper(),s=this.getRows(),l=this.getPagination();e=h(e,r);if(n=n||"replace",(Number(pdoPage.keys[r])!==e||"force"===n)&&!this.busy){this.busy=!0,pdoPage.keys[r]=e,"function"==typeof pdoPage.callbacks.before?pdoPage.callbacks.before.apply(this,[a]):i&&"scroll"!==a.mode&&i.classList.add("loading");let t=new URLSearchParams(c.location.search);Object.keys(pdoPage.keys).forEach(function(e){e!==r&&t.delete(e)}),t.set(r,String(e)),t.set("pageId",String(a.pageId)),t.set("hash",String(a.hash)),this.abortController&&this.abortController.abort(),this.abortController=new AbortController,fetch(a.connectorUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","X-Requested-With":"XMLHttpRequest"},body:t.toString(),credentials:"same-origin",signal:this.abortController.signal}).then(function(e){if(e.ok)return e.json();throw new Error("pdoPage request failed")}).then(function(e){var t;e&&e.total&&(o.setHtml(l,e.pagination||""),"append"===n?(o.appendHtml(s,e.output||""),"button"===a.mode?(t=p(a.more))&&(t.hidden=Number(e.pages)===Number(e.page)||0===Number(e.pages)):"scroll"===a.mode&&(Number(e.pages)===Number(e.page)||0===Number(e.pages)?(o.reached=!0,o.observer&&o.sentinel&&o.observer.unobserve(o.sentinel)):o.reached=!1)):o.setHtml(s,e.output||""),"function"==typeof pdoPage.callbacks.after?pdoPage.callbacks.after.apply(o,[a,e]):i&&(i.classList.remove("loading"),"default"===a.mode)&&!1!==a.scrollTop&&(t=i.getBoundingClientRect().top+c.scrollY-50,c.scrollTo(0,0{let h=l.pdoPage||{};function p(e,t){try{var o=new URL(e,l.location.origin).searchParams.get(t);return o?Number(o):1}catch(e){return 1}}function n(e,t,o){e&&(o?e.innerHTML=t||"":t&&e.insertAdjacentHTML("beforeend",t))}function i(e,t){1===Number(t)?h.Hash.remove(e):h.Hash.add(e,t)}function r(e){this.config=e,this.key=e.pageVarKey,this.busy=!1,this.reached=!1,this.abortController=null,this.requestId=0,this.observer=null,this.sentinel=null,this.bound=!1,this.previousPage=null}h.callbacks=h.callbacks||{},h.keys=h.keys||{},h.configs=h.configs||{},h.instances=h.instances||{},(l.pdoPage=h).Hash={get:function(){var e,t={};let o,n;if(n=this.oldbrowser()?(o=decodeURIComponent(l.location.hash.substr(1)).replace(/\+/g," "),"/"):(e=l.location.href.indexOf("?"),o=-1!==e?decodeURIComponent(l.location.href.substr(e+1)).replace(/\+/g," "):"","&"),o.length){var r=o.split(n);for(let e=0;er}),i=c.querySelector(o.more);i&&!e&&(i.hidden=!0),c.addEventListener("click",function(e){e.target.closest(o.more)&&(e.preventDefault(),t.addPage())})},r.prototype.bindScroll=function(){let t=this;var e=this.getRows();e&&e.parentNode&&(this.sentinel=c.createElement("div"),this.sentinel.className="pdopage-sentinel",this.sentinel.setAttribute("aria-hidden","true"),e.parentNode.insertBefore(this.sentinel,e.nextSibling),"IntersectionObserver"in l?(this.observer=new IntersectionObserver(function(e){e.forEach(function(e){!e.isIntersecting||t.busy||t.reached||(t.reached=!0,t.addPage())})},{root:null,rootMargin:"0px",threshold:0}),this.observer.observe(this.sentinel)):(l.addEventListener("scroll",e=function(){var e;t.busy||t.reached||(e=t.getWrapper())&&l.scrollY>e.offsetHeight-l.innerHeight&&(t.reached=!0,t.addPage())},{passive:!0}),l.addEventListener("load",e)))},r.prototype.stopScroll=function(){this.reached=!0,this.observer&&this.sentinel&&this.observer.unobserve(this.sentinel)},r.prototype.sentinelVisible=function(){var e;return!!this.sentinel&&(e=this.sentinel.getBoundingClientRect()).topo)return n[e];return null},r.prototype.addPage=function(){var e,t=this.findNextLink();t?(e=p(t.href,this.key),this.config.history&&i(this.key,e),this.load(t.href,"append")):this.stopScroll()},r.prototype.afterApply=function(e,t){var o,n=this.config;"force"===t&&(o=Number(e.page)||1,h.keys[this.key]=o,n.history)&&i(this.key,o),"button"!==n.mode||"append"!==t&&"force"!==t?"scroll"===n.mode&&"append"===t&&this.continueScrollIfNeeded(e):(o=c.querySelector(n.more))&&(o.hidden=Number(e.pages)===Number(e.page)||0===Number(e.pages))},r.prototype.rollback=function(){null!==this.previousPage&&(h.keys[this.key]=this.previousPage,this.config.history&&i(this.key,this.previousPage),this.previousPage=null);var e=this.getWrapper();e&&e.classList.remove("loading"),this.reached=!1},r.prototype.applyResponse=function(e,t){var o=this.getRows();n(this.getPagination(),e.pagination||"",!0),n(o,e.output||"","append"!==t),this.afterApply(e,t)},r.prototype.load=function(e,n){let r=this,i=this.config,s=this.key,a=this.getWrapper();e=p(e,s);if(n=n||"replace",Number(h.keys[s])!==e||"force"===n){this.abortController&&this.abortController.abort(),this.abortController=new AbortController;let o=++this.requestId,t=(this.busy=!0,this.previousPage=Number(h.keys[s]||1),h.keys[s]=e,"function"==typeof h.callbacks.before?h.callbacks.before.apply(h,[i]):a&&"scroll"!==i.mode&&a.classList.add("loading"),new URLSearchParams(l.location.search));Object.keys(h.keys).forEach(function(e){e!==s&&t.delete(e)}),t.set(s,String(e)),t.set("pageId",String(i.pageId)),t.set("hash",String(i.hash)),fetch(i.connectorUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","X-Requested-With":"XMLHttpRequest"},body:t.toString(),credentials:"same-origin",signal:this.abortController.signal}).then(function(e){if(e.ok)return e.json();throw new Error("pdoPage request failed")}).then(function(e){var t;o===r.requestId&&(e&&"object"==typeof e&&Object.prototype.hasOwnProperty.call(e,"total")?(r.previousPage=null,r.applyResponse(e,n),"function"==typeof h.callbacks.after?h.callbacks.after.apply(h,[i,e]):a&&(a.classList.remove("loading"),"default"===i.mode)&&!1!==i.scrollTop&&(t=a.getBoundingClientRect().top+l.scrollY-50,l.scrollTo(0,0pdoTools->config('frontend_startup_js'))) { $this->modx->regClientStartupScript( - '', + '', true ); } else {