1/** 2 * Magical Posts Display - Theme Builder frontend scripts. 3 * 4 * Powers AJAX Load More buttons and Infinite Scroll sentinels rendered by 5 * the Archive Posts and Post Pagination theme widgets. Appends freshly 6 * rendered post items into the `[data-mgpd-archive]` grid on the page. 7 * 8 * @package Magical_Posts_Display 9 */ 10(function ($) { 11 'use strict'; 12 13 if (typeof mgpdThemeBuilder === 'undefined') { 14 return; 15 } 16 17 var config = mgpdThemeBuilder; 18 19 /** 20 * Fetch the next page of items for an archive container. 21 * 22 * @param {jQuery} $container Archive grid element ([data-mgpd-archive]). 23 * @param {number} page Page number to load. 24 * @param {Function} done Callback(success, data). 25 */ 26 function fetchPage($container, page, done) { 27 $.ajax({ 28 url: config.ajaxurl, 29 type: 'POST', 30 dataType: 'json', 31 data: { 32 action: 'mgpd_theme_load_more', 33 nonce: config.nonce, 34 page: page, 35 query_args: $container.attr('data-query-args') || '{}', 36 widget_settings: $container.attr('data-widget-settings') || '{}' 37 }, 38 success: function (response) { 39 if (response && response.success) { 40 done(true, response.data || {}); 41 } else { 42 done(false, response); 43 } 44 }, 45 error: function () { 46 done(false, null); 47 } 48 }); 49 } 50 51 /** 52 * Append rendered items with a soft entrance animation. 53 * 54 * @param {jQuery} $container Archive grid element. 55 * @param {string} html Item markup. 56 */ 57 function appendItems($container, html) { 58 var $items = $(html).filter(function () { 59 return this.nodeType === 1; 60 }); 61 62 $items.addClass('mgpd-item-enter'); 63 $container.append($items); 64 65 // Double rAF so the initial styles are committed before transitioning. 66 window.requestAnimationFrame(function () { 67 window.requestAnimationFrame(function () { 68 $items.removeClass('mgpd-item-enter'); 69 }); 70 }); 71 72 $(document).trigger('mgpd:posts-appended', [$items]); 73 } 74 75 /** 76 * Reveal the "end of list" message inside a pagination nav. 77 * 78 * @param {jQuery} $nav Pagination nav element. 79 */ 80 function showEndMessage($nav) { 81 var $end = $nav.find('.mgpd-end-message'); 82 if ($end.length) { 83 $end.removeAttr('hidden'); 84 } 85 } 86 87 /** 88 * Load the next page for a trigger element (button or sentinel). 89 * 90 * @param {jQuery} $trigger Element carrying page/max/target data. 91 */ 92 function loadNext($trigger) { 93 if ($trigger.data('mgpdBusy')) { 94 return; 95 } 96 97 var $nav = $trigger.closest('.mgpd-archive-pagination, .mgpd-theme-pagination'); 98 var $container = $($trigger.attr('data-target') || '[data-mgpd-archive]').first(); 99 100 // Post Pagination widget without an Archive Posts widget on the 101 // page: fall back to a regular page load so links still work. 102 if (!$container.length) { 103 var page = parseInt($trigger.attr('data-page'), 10) + 1; 104 if (page >= 2) { 105 var url = new URL(window.location.href); 106 url.searchParams.set('paged', page); 107 window.location.href = url.toString(); 108 } 109 return; 110 } 111 112 var page = parseInt($trigger.attr('data-page'), 10) || 1; 113 var max = parseInt($trigger.attr('data-max'), 10) || 1; 114 var next = page + 1; 115 116 if (next > max) { 117 $trigger.attr('data-exhausted', '1'); 118 if ($nav.length) { 119 showEndMessage($nav); 120 } 121 return; 122 } 123 124 $trigger.data('mgpdBusy', true); 125 126 if ($trigger.is('button')) { 127 $trigger.addClass('loading').prop('disabled', true); 128 } else { 129 $nav.find('.mgpd-infinite-loader').addClass('active'); 130 } 131 132 fetchPage($container, next, function (success, data) { 133 $trigger.data('mgpdBusy', false); 134 135 if ($trigger.is('button')) { 136 $trigger.removeClass('loading').prop('disabled', false); 137 } else { 138 $nav.find('.mgpd-infinite-loader').removeClass('active'); 139 } 140 141 if (!success) { 142 window.console && console.warn('MGPD: load more request failed.', data); 143 return; 144 } 145 146 if (data.html) { 147 appendItems($container, data.html); 148 } 149 150 $trigger.attr('data-page', next); 151 152 var hasMore = typeof data.has_more !== 'undefined' 153 ? !!data.has_more 154 : next < max; 155 156 if (!hasMore) { 157 $trigger.attr('data-exhausted', '1'); 158 if ($trigger.is('button')) { 159 $trigger[0].hidden = true; 160 } 161 if ($nav.length) { 162 showEndMessage($nav); 163 } 164 } 165 }); 166 } 167 168 // Load More buttons (Archive Posts widget + Post Pagination widget). 169 $(document).on('click', '[data-mgpd-loadmore]', function (e) { 170 e.preventDefault(); 171 loadNext($(this)); 172 }); 173 174 // Infinite Scroll sentinels. 175 if ('IntersectionObserver' in window) { 176 var observer = new IntersectionObserver(function (entries) {
177 entries.forEach(function (entry) { 178 if (!entry.isIntersecting) { 179 return; 180 } 181 182 var $trigger = $(entry.target); 183 if ($trigger.attr('data-exhausted')) { 184 observer.unobserve(entry.target); 185 return; 186 } 187 188 loadNext($trigger); 189 }); 190 }, { 191 rootMargin: '400px 0px', 192 threshold: 0 193 }); 194 195 function observeSentinels() { 196 $('[data-mgpd-infinite]').each(function () { 197 if (!this.dataset.mgpdObserved) { 198 this.dataset.mgpdObserved = '1'; 199 observer.observe(this); 200 } 201 }); 202 } 203 204 observeSentinels(); 205 $(document).on('mgpd:posts-appended', function () { 206 observeSentinels(); 207 208 // Stop observing exhausted sentinels. 209 $('[data-mgpd-infinite][data-exhausted]').each(function () { 210 observer.unobserve(this); 211 }); 212 }); 213 } else { 214 // Legacy fallback: treat infinite scroll as a load more button. 215 $(document).on('click', '[data-mgpd-infinite]', function (e) { 216 e.preventDefault(); 217 loadNext($(this)); 218 }); 219 } 220})(jQuery);
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.