1/** 2 * Load more on the news overview. 3 * 4 * Appends the next batch of cards after the ones already rendered. The month state travels 5 * with the request: the endpoint runs in its own PHP process and would otherwise repeat the 6 * heading of a month that is already open at the batch boundary. 7 * 8 * @copyright Copyright (C) 2026 onblur.media GmbH. All rights reserved. 9 * @license GNU General Public License version 2 or later; see LICENSE.txt 10 */ 11(function () { 12 'use strict'; 13 14 const button = document.getElementById('button-loadmore'); 15 const errorBox = document.getElementById('loadmore-error'); 16 17 if (!button) { 18 return; 19 } 20 21 const total = parseInt(button.dataset.total, 10) || 0; 22 const limitEnd = parseInt(button.dataset.limitend, 10) || 25; 23 const label = button.innerHTML; 24 let busy = false; 25 26 /** 27 * Toggle the loading state of the button. 28 * 29 * @param {boolean} loading Whether a request is in flight 30 * 31 * @returns {void} 32 */ 33 const setLoading = function (loading) { 34 busy = loading; 35 button.disabled = loading; 36 button.setAttribute('aria-busy', loading ? 'true' : 'false'); 37 button.innerHTML = loading 38 ? '<i class="fas fa-circle-notch fa-spin" aria-hidden="true"></i> ' + button.dataset.labelLoading 39 : label; 40 }; 41 42 button.addEventListener('click', function () { 43 if (busy) { 44 return; 45 } 46 47 const limitStart = (parseInt(button.dataset.limitstart, 10) || 0) + limitEnd; 48 const container = document.getElementById('container-news'); 49 50 if (!container) { 51 return; 52 } 53 54 const params = new URLSearchParams({ 55 option: 'com_ontemplate', 56 view: 'ajax', 57 layout: 'items.news', 58 limitstart: String(limitStart), 59 limitend: String(limitEnd), 60 catid: button.dataset.catid || '0', 61 lastmonth: button.dataset.lastmonth || '', 62 ordering: button.dataset.ordering || 'rdate', 63 }); 64 65 if (errorBox) { 66 errorBox.hidden = true; 67 } 68 69 setLoading(true); 70 71 fetch(button.dataset.endpoint + '?' + params.toString(), { 72 credentials: 'same-origin', 73 headers: { 'X-Requested-With': 'XMLHttpRequest' }, 74 }) 75 .then(function (response) { 76 if (!response.ok) { 77 throw new Error('HTTP ' + response.status); 78 } 79 80 return response.text(); 81 }) 82 .then(function (html) { 83 container.outerHTML = html; 84 button.dataset.limitstart = String(limitStart); 85 86 // The replacement carries the month that was open when the batch ended 87 const fresh = document.getElementById('container-news'); 88 89 if (fresh) { 90 button.dataset.lastmonth = fresh.dataset.lastmonth || ''; 91 } 92 93 if (limitStart + limitEnd >= total) { 94 button.hidden = true; 95 } 96 97 if (window._paq) { 98 window._paq.push(['trackEvent', 'News', 'LoadMore', String(limitStart)]); 99 } 100 }) 101 .catch(function () { 102 // A dialog would block every following interaction, so the message stays in the page 103 if (errorBox) { 104 errorBox.hidden = false; 105 } 106 }) 107 .finally(function () { 108 setLoading(false); 109 }); 110 }); 111}());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.