1(() => { 2 'use strict'; 3 4 function qs(root, sel) { 5 return root.querySelector(sel); 6 } 7 8 function qsa(root, sel) { 9 return Array.from(root.querySelectorAll(sel)); 10 } 11 12 function setActiveTab(links, tag) { 13 links.forEach(a => { 14 a.classList.toggle('active', (a.getAttribute('data-tab') || 'all') === tag); 15 }); 16 } 17 18 async function postForm(url, data) { 19 const body = new URLSearchParams(data); 20 const res = await fetch(url, { 21 method: 'POST', 22 headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, 23 body: body.toString(), 24 credentials: 'same-origin', 25 }); 26 return res.json(); 27 } 28 29 function normalizeBaseUrl(u) { 30 try { 31 const url = new URL(u, window.location.origin); 32 if (!url.pathname.endsWith('/')) url.pathname += '/'; 33 return url.toString(); 34 } catch (e) { 35 return '/news/'; 36 } 37 } 38 39 function buildPrettyUrl(baseUrl, tag, page) { 40 baseUrl = normalizeBaseUrl(baseUrl); 41 tag = (tag || 'all').trim() || 'all'; 42 page = parseInt(page || '1', 10) || 1; 43 44 const base = new URL(baseUrl, window.location.origin); 45 let p = base.pathname; 46 47 if (tag !== 'all') { 48 p += encodeURIComponent(tag) + '/'; 49 } 50 if (page > 1) { 51 p += 'page/' + page + '/'; 52 } 53 54 base.pathname = p; 55 base.search = ''; 56 base.hash = ''; 57 return base.toString(); 58 } 59 60 function parseFromHref(baseUrl, href) { 61 const out = {tag: 'all', page: 1}; 62 63 let base; 64 let url; 65 try { 66 base = new URL(normalizeBaseUrl(baseUrl), window.location.origin); 67 url = new URL(href, window.location.origin); 68 } catch (e) { 69 return out; 70 } 71 72 // relative path after base 73 let rel = url.pathname; 74 if (rel.startsWith(base.pathname)) { 75 rel = rel.slice(base.pathname.length); 76 } 77 rel = rel.replace(/^\/+|\/+$/g, ''); 78 if (!rel) return out; 79 80 const parts = rel.split('/').filter(Boolean); 81 82 // all: page/2 83 if (parts[0] === 'page' && parts[1]) { 84 out.tag = 'all'; 85 out.page = parseInt(parts[1], 10) || 1; 86 return out; 87 } 88 89 // tag 90 out.tag = decodeURIComponent(parts[0] || 'all') || 'all'; 91 92 // tag/page/2 93 if (parts[1] === 'page' && parts[2]) { 94 out.page = parseInt(parts[2], 10) || 1; 95 } 96 97 return out; 98 } 99 100 function init(root) { 101 const tabs = qs(root, '.tabs'); 102 if (!tabs) return; 103 104 const tabLinks = qsa(tabs, 'a[data-tab]'); 105 const cardsWrap = qs(root, '.news__cards'); 106 107 const paginationSection = 108 root.parentElement?.querySelector('.pagination__content') || 109 document.querySelector('.pagination__content'); 110 111 const perPage = parseInt(root.getAttribute('data-per-page') || '6', 10) || 6; 112 const maxExcerpt = parseInt(root.getAttribute('data-max-excerpt') || '150', 10) || 150; 113 114 const baseUrl = 115 root.getAttribute('data-base-url') || 116 (window.NEWS_ARCHIVE && NEWS_ARCHIVE.page_url) || 117 '/news/'; 118 119 if (!cardsWrap || !paginationSection) return; 120 if (!window.NEWS_ARCHIVE || !NEWS_ARCHIVE.ajax_url || !NEWS_ARCHIVE.action || !NEWS_ARCHIVE.nonce) return; 121 122 async function load(tag, page, replaceState) { 123 tag = (tag || 'all').trim() || 'all'; 124 page = parseInt(page || '1', 10) || 1; 125 126 setActiveTab(tabLinks, tag); 127 root.classList.add('is-loading'); 128 129 try { 130 const lang = (() => { 131 try { 132 const p = new URL(normalizeBaseUrl(baseUrl), window.location.origin).pathname; 133 const m = p.match(/^\/([a-z]{2}(?:-[a-z]{2})?)\/news(?:\/|$)/i); 134 return m ? m[1].toLowerCase() : ''; 135 } catch (e) { 136 return ''; 137 } 138 })(); 139 140 const defaultH1 = root.getAttribute('data-default-h1') || ''; 141 142 const json = await postForm(NEWS_ARCHIVE.ajax_url, { 143 action: NEWS_ARCHIVE.action, 144 nonce: NEWS_ARCHIVE.nonce, 145 tag: tag, 146 page: page, 147 per_page: perPage, 148 max_excerpt: maxExcerpt, 149 base_url: normalizeBaseUrl(baseUrl), 150 lang: lang, 151 page_head_h1: defaultH1, 152 }); 153 154 if (!json || !json.success) return; 155 156 cardsWrap.innerHTML = json.data.cards_html; 157 paginationSection.innerHTML = json.data.pagination_html; 158 159 if (json.data && json.data.head) { 160 const head = json.data.head; 161 162 // H1 163 if (head.h1) { 164 const h1El = document.querySelector('.intro__title'); 165 if (h1El) h1El.textContent = head.h1; 166 } 167 168 // <title> 169 if (head.title) { 170 document.title = head.title; 171 } 172 173 // <meta name="description"> 174 const desc = (head.description || '').trim(); 175 let meta = document.querySelector('meta[name="description"]'); 176 if (!meta) { 177 meta = document.createElement('meta'); 178 meta.setAttribute('name', 'description'); 179 document.head.appendChild(meta); 180 } 181 meta.setAttribute('content', desc); 182 } 183 184 const pretty = buildPrettyUrl(baseUrl, tag, page); 185 if (replaceState) { 186 window.history.replaceState({}
186, '', pretty); 187 } else { 188 window.history.pushState({}, '', pretty); 189 } 190 } finally { 191 root.classList.remove('is-loading'); 192 } 193 } 194 195 // Tabs click -> AJAX + pretty URL 196 tabLinks.forEach(a => { 197 a.addEventListener('click', async (e) => { 198 e.preventDefault(); 199 const tag = (a.getAttribute('data-tab') || 'all').trim() || 'all'; 200 await load(tag, 1, true); 201 }); 202 }); 203 204 // Pagination click -> AJAX + pretty URL 205 paginationSection.addEventListener('click', async (e) => { 206 const a = e.target.closest('a'); 207 if (!a) return; 208 209 // ignore disabled 210 if (a.closest('.disabled')) { 211 e.preventDefault(); 212 return; 213 } 214 215 const href = a.getAttribute('href') || ''; 216 if (!href || href === '#') return; 217 218 e.preventDefault(); 219 220 const parsed = parseFromHref(baseUrl, href); 221 await load(parsed.tag, parsed.page, true); 222 }); 223 224 // Back/Forward support 225 window.addEventListener('popstate', () => { 226 const parsed = parseFromHref(baseUrl, window.location.href); 227 load(parsed.tag, parsed.page, true); 228 }); 229 } 230 231 document.addEventListener('DOMContentLoaded', () => { 232 document.querySelectorAll('section.news__content.alternate').forEach(init); 233 }); 234})();
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.