1(function () { 2 function qs(root, selector) { 3 return root.querySelector(selector); 4 } 5 6 function buildUrl(feed, nextPage) { 7 var url = new URL(EVF_FEED.restUrl, window.location.origin); 8 url.searchParams.set('page', nextPage); 9 url.searchParams.set('per_page', feed.dataset.perPage || '20'); 10 url.searchParams.set('category', feed.dataset.category || ''); 11 url.searchParams.set('source', feed.dataset.source || ''); 12 url.searchParams.set('sort', feed.dataset.sort || 'newest'); 13 url.searchParams.set('show_ads', feed.dataset.showAds || '1'); 14 return url.toString(); 15 } 16 17 function escapeHtml(value) { 18 return String(value || '').replace(/[&<>"']/g, function (char) { 19 return {'&': '&', '<': '<', '>': '>', '"': '"', "'": '''}[char]; 20 }); 21 } 22 23 function renderVideo(item) { 24 var display = escapeHtml(item.display_type || 'auto'); 25 var thumb = item.thumbnail 26 ? '<img src="' + escapeHtml(item.thumbnail) + '" alt="' + escapeHtml(item.title) + '" loading="lazy">' 27 : '<div class="evf-video-thumb__fallback"></div>'; 28 var category = item.category ? '<span>' + escapeHtml(item.category) + '</span>' : ''; 29 return '<article class="evf-video-card evf-video-card--' + display + '" data-evf-video-card>' + 30 '<div class="evf-video-thumb" data-evf-player data-evf-embed-url="' + escapeHtml(item.embed_url) + '" data-evf-single-url="' + escapeHtml(item.url) + '">' + 31 thumb + 32 '<button class="evf-play-button" type="button" aria-label="Play video"><span></span></button>' + 33 '</div>' + 34 '<div class="evf-video-card__body">' + 35 '<h3 class="evf-video-card__title"><a href="' + escapeHtml(item.url) + '">' + escapeHtml(item.title) + '</a></h3>' + 36 '<div class="evf-video-card__meta">' + category + '</div>' + 37 '</div></article>'; 38 } 39 40 function renderItem(item) { 41 if (item.type === 'ad') { 42 return item.html || ''; 43 } 44 return renderVideo(item); 45 } 46 47 function loadMore(feed, button) { 48 var nextPage = Number(feed.dataset.page || '1') + 1; 49 button.disabled = true; 50 fetch(buildUrl(feed, nextPage), {credentials: 'same-origin'}) 51 .then(function (response) { return response.json(); }) 52 .then(function (data) { 53 var grid = qs(feed, '[data-evf-feed-grid]'); 54 grid.insertAdjacentHTML('beforeend', (data.items || []).map(renderItem).join('')); 55 initAdCards(grid); 56 feed.dataset.page = String(data.pagination.page); 57 if (!data.pagination.has_more) { 58 button.remove(); 59 } else { 60 button.disabled = false; 61 } 62 }) 63 .catch(function () { 64 button.disabled = false; 65 }); 66 } 67 68 document.addEventListener('click', function (event) { 69 var button = event.target.closest('[data-evf-load-more]'); 70 if (!button) { 71 return; 72 } 73 var feed = button.closest('[data-evf-feed]'); 74 if (feed) { 75 loadMore(feed, button); 76 } 77 }); 78 79 function adHasContent(card) { 80 if (!card) { 81 return false; 82 } 83 if (card.querySelector('iframe, img, object, embed')) { 84 return true; 85 } 86 return String(card.textContent || '').trim().length > 0; 87 } 88 89 function initAdCards(root) { 90 (root || document).querySelectorAll('.evf-ad-card').forEach(function (card) { 91 if (card.dataset.evfAdWatch) { 92 return; 93 } 94 card.dataset.evfAdWatch = '1'; 95 var update = function () { 96 card.classList.toggle('evf-ad-card--empty', !adHasContent(card)); 97 }; 98 var observer = new MutationObserver(update); 99 observer.observe(card, { childList: true, subtree: true, characterData: true, attributes: true }); 100 window.setTimeout(update, 12000); 101 window.setTimeout(update, 30000); 102 }); 103 } 104 105 document.addEventListener('DOMContentLoaded', function () { 106 initAdCards(document); 107 }); 108 109 if ('IntersectionObserver' in window) { 110 document.querySelectorAll('[data-evf-feed][data-mode="infinite_scroll"] [data-evf-load-more]').forEach(function (button) { 111 var observer = new IntersectionObserver(function (entries) {
112 entries.forEach(function (entry) { 113 if (entry.isIntersecting && !button.disabled) { 114 loadMore(button.closest('[data-evf-feed]'), button); 115 } 116 }); 117 }, {rootMargin: '300px'}); 118 observer.observe(button); 119 }); 120 } 121})();
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.