1/* 2 * Shalamar Foods â visual + structural styling for /news/* article pages 3 * ---------------------------------------------------------------------- 4 * UpGates renders /news/<slug> articles in a basic .pg-txt > .text.text-site 5 * container with no typography hierarchy, no TOC, no callouts. This script 6 * raises the visual quality to ~aktin.cz level so the blog can carry SEO 7 * weight without looking amateur. 8 * 9 * Three layers, all client-side: 10 * 11 * 1. CSS injection â typography (17px / line-height 1.7), terracotta H2, 12 * list spacing, max-width 720px, lead paragraph emphasis, CTA box for 13 * /a/<slug>-recept internal links. 14 * 15 * 2. DOM enhancements (only on /news/<slug>, not on /news/ index): 16 * - Auto-TOC if article has 4+ H2 (collapsible on mobile, sticky desktop) 17 * - Reading time estimate (~N min) under H1 18 * - Article schema.org JSON-LD for SEO 19 * - Wrap /a/<slug>-recept anchors into a CTA card 20 * 21 * 3. Idempotent â safe to run multiple times. Watches for late-rendered 22 * content (UpGates can hydrate after DOMContentLoaded). 23 * 24 * Path guard: only fires on /news/<slug>, /en/news/<slug>, /ch/news/<slug>. 25 * The /news/ index, homepage, product pages etc. are no-ops. 26 * 27 * Reference: aktin.cz article layout (2026-05-05). 28 */ 29(function () { 30 'use strict'; 31 32 // -------- Path guards -------- 33 var ARTICLE_RE = /^\/(?:en\/|ch\/)?news\/[^\/]+\/?$/; 34 var LISTING_RE = /^\/(?:en\/|ch\/)?news\/?$/; 35 var IS_ARTICLE = ARTICLE_RE.test(location.pathname); 36 var IS_LISTING = LISTING_RE.test(location.pathname); 37 if (!IS_ARTICLE && !IS_LISTING) return; 38 39 var TERRACOTTA = '#7a2d1a'; 40 var TERRACOTTA_LIGHT = '#fff7ee'; 41 var TERRACOTTA_BORDER = '#D4571C'; 42 43 // -------- Hero images per article slug -------- 44 // Edit this map to add/swap top images. Stored on /shop-data/ via FTP root. 45 var HERO_IMAGES = { 46 'kicari-recept': { 47 src: '/shop-data/kicari-recept-1600.jpg', 48 alt: 'KiÄari z basmati rýže a žluté mung dal v keramické misce, navrch ghÃ, kmÃn a zelená chilli', 49 caption: 'KiÄari: basmati a žlutá mung dal z jednoho hrnce, navrch tadka z ghà a kmÃnu' 50 }, 51 'butter-chicken-recept': { 52 src: '/shop-data/shl-news-butter-chicken.jpg', 53 alt: 'Butter chicken murgh makhani s krémovou rajÄatovou omáÄkou', 54 caption: 'Butter chicken (murgh makhani) â krémová rajÄatová omáÄka s tandoori kuÅetem' 55 }, 56 'dal-tadka-recept': { 57 src: '/shop-data/shl-news-dal-tadka.jpg', 58 alt: 'Žlutá toor dal s tadkou kmÃnu, Äesneku a suÅ¡eného chilli', 59 caption: 'Dal tadka â žlutá toor ÄoÄka s temperovaným koÅenÃm v ghÃ' 60 }, 61 'pravy-pakistansky-biryani-pruvodce': { 62 src: '/shop-data/shl-news-biryani.jpg', 63 alt: 'Pakistánský chicken biryani s Å¡afránovou rýžà a suÅ¡enými Å¡vestkami aloo bukhara', 64 caption: 'Pákistánský chicken biryani â vrstvená basmati rýže, Å¡afrán a aloo bukhara' 65 }, 66 'jak-uvarit-basmati-ryzi': { 67 src: '/shop-data/shl-news-basmati.jpg?v=2', 68 alt: 'UvaÅená basmati rýže v misce s oddÄlenými dlouhými zrny', 69 caption: 'Basmati rýže â uvaÅená správným pomÄrem 1:1,5 s oddÄlenými zrny' 70 }, 71 'co-je-garam-masala': { 72 src: '/shop-data/shl-news-garam-masala.jpg', 73 alt: 'SmÄs koÅenà garam masala â kardamom, hÅebÃÄek, skoÅice, kmÃn', 74 caption: 'Garam masala â smÄs kardamomu, hÅebÃÄku, skoÅice, kmÃnu, koriandru a kardamomu' 75 }, 76 'masala-chai-recept': { 77 src: '/shop-data/shl-news-masala-chai.jpg', 78 alt: 'Masala chai v hrnku â indický koÅenÄný Äaj s mlékem', 79 caption: 'Masala chai â silný CTC Äaj vaÅený s mlékem, kardamomem, zázvorem a skoÅicÃ' 80 }, 81 'co-je-paneer-jak-ho-uvarit': { 82 src: '/shop-data/shl-news-paneer.jpg', 83 alt: 'Paneer â indický Äerstvý sýr v omáÄce s koriandrem', 84 caption: 'Paneer â Äerstvý sýr z plnotuÄného mléka a citronové šťávy' 85 }, 86 'jak-pouzivat-tamarind': { 87 src: '/shop-data/shl-news-tamarind.jpg', 88 alt: 'Tamarindová pasta a slisované bloky tamarindu', 89 caption: 'Tamarind v různých formách â pasta, slisované bloky, suÅ¡ené lusky' 90 }, 91 'biryani-vs-pulao': { 92 src: '/shop-data/shl-news-pulao.jpg',
93 alt: 'Pulao s hráškem v sklenÄné misce â rýžový pokrm vaÅený absorpÄnà metodou', 94 caption: 'Pea pulao â rýže s hráškem vaÅená absorpÄnà metodou, jednoduššà alternativa biryani' 95 }, 96 'tikka-masala-vs-tandoori-masala': { 97 src: '/shop-data/shl-news-tandoori.jpg', 98 alt: 'Tandoori kuÅe s limetkou na rustikálnÃm dÅevÄném prknÄ', 99 caption: 'Tandoori kuÅe â marinováno v jogurtu a koÅenÃ, peÄené v hlinÄné peci' 100 }, 101 '10-indickych-koreni-ktere-potrebujete': { 102 src: '/shop-data/shl-news-koreni.jpg', 103 alt: 'Sortiment indických koÅenà v tradiÄnà nádobÄ â kmÃn, kurkuma, kardamom, hÅebÃÄek', 104 caption: 'Indická koÅenà â základ každé indické kuchynÄ od kmÃnu po garam masalu' 105 }, 106 'halal-maso-pruvodce': { 107 src: '/shop-data/shl-news-halal.jpg', 108 alt: 'Äerstvé syrové maso na Åeznickém pultu', 109 caption: 'Halal maso â vyžaduje specifický způsob porážky a certifikaci ÅeznictvÃm' 110 }, 111 'aalu-methi-brambory-s-piskavici': { 112 src: '/shop-data/shl-news-aalu-methi.jpg', 113 alt: 'Aalu methi â brambory s pÃskavicÃ, severoindický pokrm', 114 caption: 'Aalu methi â brambory duÅ¡ené s listovou pÃskavicà (methi)' 115 }, 116 'zacatecnikuv-pruvodce-indickou-kuchyni': { 117 src: '/shop-data/shl-news-zacatecnik.jpg', 118 alt: 'Stůl plný indických pokrmů â průvodce pro zaÄáteÄnÃky', 119 caption: 'Indická kuchynÄ â průvodce pro zaÄáteÄnÃky se základnÃmi ingrediencemi' 120 }, 121 'eid-recepty-sladkosti': { 122 src: '/shop-data/shl-news-eid.jpg', 123 alt: 'Eid sladkosti na zlatÄ barveném talÃÅi', 124 caption: 'Eid sladkosti â sheer khurma, gulab jamun a dalšà tradiÄnà dezerty' 125 } 126 }; 127 128 // -------- 1. CSS -------- 129 function injectCSS() { 130 if (document.getElementById('shl-news-article-css')) return; 131 var css = [ 132 // Article body container â wider reading column 133 'body[class*="page-news_id-"] article.ContentPage .pg-txt .text.text-site{', 134 'font-size:17px;line-height:1.7;color:#1a1a1a;', 135 '}', 136 'body[class*="page-news_id-"] article.ContentPage .pg-txt .max{', 137 'max-width:760px;', 138 '}', 139 // Headings 140 'body[class*="page-news_id-"] article.ContentPage .pg-hdr h1.ph-main{', 141 'font-size:2.1rem;line-height:1.25;font-weight:700;color:#1a1a1a;', 142 '}', 143 'body[class*="page-news_id-"] article.ContentPage .text.text-site h2{', 144 'font-size:1.55rem;line-height:1.3;font-weight:700;color:' + TERRACOTTA + ';', 145 'margin:2.2em 0 0.7em;padding-top:0.3em;', 146 'border-top:1px solid #e9ecef;', 147 '}', 148 'body[class*="page-news_id-"] article.ContentPage .text.text-site h2:first-child{', 149 'border-top:none;margin-top:0;padding-top:0;', 150 '}', 151 'body[class*="page-news_id-"] article.ContentPage .text.text-site h3{', 152 'font-size:1.25rem;line-height:1.35;font-weight:600;color:#1a1a1a;', 153 'margin:1.8em 0 0.5em;', 154 '}', 155 // Paragraphs â vzduÅ¡nÄjšà 156 'body[class*="page-news_id-"] article.ContentPage .text.text-site p{', 157 'margin:0 0 1.3em;', 158 '}', 159 // Lead paragraph (first paragraph after intro section) 160 'body[class*="page-news_id-"] article.ContentPage .text.text-site > p:first-child,', 161 'body[class*="page-news_id-"] article.ContentPage .text.text-site > div:first-child > p:first-child{', 162 'font-size:1.12em;line-height:1.65;color:#3a3a3a;', 163 'border-left:3px solid ' + TERRACOTTA + ';', 164 'padding:0.4em 0 0.4em 1.1em;margin-bottom:1.6em;', 165 'background:#fafafa;', 166 '}', 167 // Lists 168 'body[class*="page-news_id-"] article.ContentPage .text.text-site ul,', 169 'body[class*="page-news_id-"] article.ContentPage .text.text-site ol{', 170 'margin:0 0 1.3em;padding-left:1.6em;', 171 '}', 172 'body[class*="page-news_id-"] article.ContentPage .text.text-site li{', 173 'margin-bottom:0.5em;line-height:1.6;', 174 '}', 175 'body[class*="page-news_id-"] article.ContentPage .text.text-site ul li::marker{', 176 'color:' + TERRACOTTA + ';', 177 '}', 178 // Strong as inline keyword 179 'body[class*="page-news_id-"] article.ContentPage .text.text-site strong{',
180 'color:#1a1a1a;font-weight:600;', 181 '}', 182 // CTA box â wraps /a/<slug>-recept anchors transformed by JS 183 '.shl-news-cta{', 184 'display:flex;gap:14px;align-items:center;flex-wrap:wrap;', 185 'margin:1.8em 0;padding:18px 22px;', 186 'background:linear-gradient(135deg,' + TERRACOTTA_LIGHT + ' 0%,#ffe9d5 100%);', 187 'border-radius:12px;border-left:4px solid ' + TERRACOTTA_BORDER + ';', 188 'text-decoration:none;color:inherit;', 189 'transition:transform .15s,box-shadow .15s;', 190 'box-shadow:0 1px 4px rgba(212,87,28,.08);', 191 '}', 192 '.shl-news-cta:hover{', 193 'transform:translateY(-1px);box-shadow:0 4px 12px rgba(212,87,28,.16);', 194 'text-decoration:none;color:inherit;', 195 '}', 196 '.shl-news-cta-icon{', 197 'flex:0 0 auto;width:42px;height:42px;border-radius:50%;', 198 'background:' + TERRACOTTA + ';color:#fff;', 199 'display:flex;align-items:center;justify-content:center;', 200 'font-size:20px;font-weight:700;', 201 '}', 202 '.shl-news-cta-text{flex:1 1 auto;
202min-width:200px;}', 203 '.shl-news-cta-label{', 204 'font-size:0.78em;text-transform:uppercase;letter-spacing:0.5px;', 205 'color:' + TERRACOTTA + ';font-weight:600;margin-bottom:2px;', 206 '}', 207 '.shl-news-cta-title{font-size:1.08em;font-weight:600;color:#1a1a1a;}', 208 '.shl-news-cta-arrow{flex:0 0 auto;font-size:1.6em;color:' + TERRACOTTA + ';}', 209 // Reading-time meta 210 '.shl-news-meta{', 211 'display:flex;gap:18px;align-items:center;flex-wrap:wrap;', 212 'font-size:0.9em;color:#6c757d;margin-top:0.7em;', 213 'padding-top:0.7em;border-top:1px solid #f0eae3;', 214 '}', 215 '.shl-news-meta-item{display:inline-flex;align-items:center;gap:7px;}', 216 '.shl-meta-icon{', 217 'width:14px;height:14px;flex:0 0 14px;', 218 'fill:currentColor;opacity:0.65;', 219 '}', 220 // Hero image (figure under H1) 221 '.shl-news-hero{', 222 'margin:0 0 2.4em;padding:0;', 223 'max-width:760px;', 224 '}', 225 '.shl-news-hero img{', 226 'width:100%;height:auto;aspect-ratio:16/9;', 227 'object-fit:cover;display:block;', 228 'border-radius:8px;', 229 'box-shadow:0 12px 32px -8px rgba(122,45,26,0.18),0 2px 6px rgba(0,0,0,0.04);', 230 '}', 231 '.shl-news-hero figcaption{', 232 'margin-top:0.85em;font-size:0.86em;line-height:1.55;', 233 'color:#7a7268;font-style:italic;text-align:center;', 234 'font-family:Georgia,"Iowan Old Style","Palatino Linotype",serif;', 235 '}', 236 '@media (max-width:640px){', 237 '.shl-news-hero{margin-bottom:1.6em;}', 238 '.shl-news-hero img{aspect-ratio:4/3;border-radius:6px;}', 239 '.shl-news-hero figcaption{font-size:0.82em;}', 240 '}', 241 // TOC 242 '.shl-toc{', 243 'background:#fafafa;border:1px solid #e9ecef;border-radius:8px;', 244 'padding:16px 20px;margin:0 0 1.8em;', 245 '}', 246 '.shl-toc-title{', 247 'font-size:0.82em;font-weight:600;text-transform:uppercase;', 248 'letter-spacing:0.6px;color:#666;margin-bottom:10px;', 249 '}', 250 '.shl-toc ol{margin:0;padding:0;list-style:none;counter-reset:shl-toc-i;}', 251 '.shl-toc li{', 252 'counter-increment:shl-toc-i;padding:5px 0;', 253 'border-bottom:1px solid #f0f0f0;', 254 '}', 255 '.shl-toc li:last-child{border-bottom:none;}', 256 '.shl-toc li::before{', 257 'content:counter(shl-toc-i, decimal-leading-zero) ".";', 258 'color:' + TERRACOTTA + ';font-weight:600;margin-right:10px;', 259 'font-feature-settings:"tnum";', 260 '}', 261 '.shl-toc a{', 262 'color:#1a1a1a;text-decoration:none;', 263 'transition:color .12s;', 264 '}', 265 '.shl-toc a:hover{color:' + TERRACOTTA + ';}', 266 // /news listing â Aktin-style 2-column card grid (larger cards) 267 'body.page-news .artcl-wrap{', 268 'display:grid!important;', 269 'grid-template-columns:repeat(2,1fr)!important;', 270 'gap:36px 32px!important;', 271 'flex-direction:initial!important;', 272 '}', 273 '@media (max-width:900px){', 274 'body.page-news .artcl-wrap{grid-template-columns:1fr!important;}', 275 '}', 276 'body.page-news .artcl-wrap > hr,', 277 'body.page-news .artcl-wrap > br{display:none!important;}', 278 'body.page-news article.SNIitem{', 279 'display:flex!important;flex-direction:column!important;', 280 'gap:0!important;margin:0!important;padding:0!important;', 281 'background:#fff!important;border-radius:10px!important;', 282 'overflow:hidden!important;', 283 'box-shadow:0 1px 3px rgba(0,0,0,0.06),0 4px 12px rgba(122,45,26,0.04)!important;', 284 'transition:transform .18s,box-shadow .18s!important;', 285 '}', 286 'body.page-news article.SNIitem:hover{', 287 'transform:translateY(-2px)!important;', 288 'box-shadow:0 4px 16px rgba(0,0,0,0.08),0 12px 28px rgba(122,45,26,0.10)!important;', 289 '}', 290 'body.page-news article.SNIitem > figure{', 291 'flex:0 0 auto!important;width:100%!important;max-width:none!important;', 292 'height:auto!important;aspect-ratio:3/2!important;', 293 'margin:0!important;overflow:hidden!important;', 294 'padding:0!important;border:0!important;background:#f4ebe3!important;', 295 'border-radius:0!important;', 296 '}', 297 'body.page-news article.SNIitem > figure img{', 298 'width:100%!important;height:100%!important;', 299 'object-fit:cover!important;object-position:center 45%!important;', 300 'display:block!important;', 301 'padding:0!important;border:0!important;background:transparent!important;', 302 'border-radius:0!important;max-width:none!important;', 303 '}', 304 'body.page-news article.SNIitem > .a-i-txt,', 305 'body.page-news article.SNIitem >
305 div:not(:has(> img)):not(figure){', 306 'flex:1 1 auto!important;display:flex!important;flex-direction:column!important;', 307 'padding:24px 28px 26px!important;gap:10px!important;', 308 '}', 309 'body.page-news article.SNIitem h2{', 310 'font-size:1.4rem!important;line-height:1.3!important;', 311 'margin:0 0 8px!important;font-weight:700!important;', 312 'display:-webkit-box!important;-webkit-line-clamp:2!important;', 313 '-webkit-box-orient:vertical!important;overflow:hidden!important;', 314 '}', 315 'body.page-news article.SNIitem h2 a{color:#1a1a1a!important;text-decoration:none!important;}', 316 'body.page-news article.SNIitem .text{', 317 'font-size:1rem!important;line-height:1.6!important;color:#555!important;', 318 'display:-webkit-box!important;-webkit-line-clamp:3!important;', 319 '-webkit-box-orient:vertical!important;overflow:hidden!important;', 320 'margin:0!important;', 321 '}', 322 'body.page-news article.SNIitem .text p{margin:0!important;}', 323 '@media (max-width:640px){', 324 'body.page-news .artcl-wrap{', 325 'grid-template-columns:1fr!important;gap:20px!important;', 326 '}', 327 'body.page-news article.SNIitem > figure{height:180px!important;}', 328 '}', 329 // Mobile tweaks 330 '@media (max-width:640px){', 331 'body[class*="page-news_id-"] article.ContentPage .pg-hdr h1.ph-main{font-size:1.65rem;}', 332 'body[class*="page-news_id-"] article.ContentPage .text.text-site{font-size:16px;}', 333 'body[class*="page-news_id-"] article.ContentPage .text.text-site h2{font-size:1.35rem;}', 334 '.shl-news-cta{padding:14px 16px;gap:10px;}', 335 '.shl-news-cta-icon{width:36px;height:36px;font-size:17px;}', 336 '}' 337 ].join(''); 338 var s = document.createElement('style'); 339 s.id = 'shl-news-article-css'; 340 s.textContent = css; 341 (document.head || document.documentElement).appendChild(s); 342 } 343 344 // -------- 2. Slugify (for anchor IDs) -------- 345 function slugify(s) { 346 return (s || '') 347 .toLowerCase() 348 .normalize('NFD').replace(/[Ì-ͯ]/g, '') 349 .replace(/[^a-z0-9]+/g, '-') 350 .replace(/^-+|-+$/g, ''); 351 } 352 353 // -------- 3. Reading time -------- 354 function injectReadingTime() { 355 if (document.querySelector('.shl-news-meta')) return; 356 var article = document.querySelector('article.ContentPage'); 357 if (!article) return; 358 var hdr = article.querySelector('.pg-hdr .max') || article.querySelector('.pg-hdr'); 359 if (!hdr) return; 360 var bodyText = (article.querySelector('.pg-txt') || article).innerText || ''; 361 var words = bodyText.trim().split(/\s+/).filter(Boolean).length; 362 if (words < 100) return; 363 var minutes = Math.max(1, Math.round(words / 200)); 364 365 var meta = document.createElement('div'); 366 meta.className = 'shl-news-meta'; 367 var iconClock = '<svg class="shl-meta-icon" aria-hidden="true" focusable="false"><use href="/images/icons/fa/solid.svg#clock"></use></svg>'; 368 var iconDoc = '<svg class="shl-meta-icon" aria-hidden="true" focusable="false"><use href="/images/icons/fa/solid.svg#file-lines"></use></svg>'; 369 meta.innerHTML = 370 '<span class="shl-news-meta-item">' + iconClock + minutes + ' min ÄtenÃ</span>' + 371 '<span class="shl-news-meta-item">' + iconDoc + words.toLocaleString('cs-CZ') + ' slov</span>'; 372 hdr.appendChild(meta); 373 } 374 375 // -------- Hero image injection -------- 376 function injectHero() { 377 if (document.querySelector('.shl-news-hero')) return; 378 var slug = location.pathname.replace(/\/$/, '').split('/').pop(); 379 var hero = HERO_IMAGES[slug]; 380 if (!hero) return; 381 var article = document.querySelector('article.ContentPage'); 382 if (!article) return; 383 var header = article.querySelector('.pg-hdr'); 384 var bodySection = article.querySelector('.pg-txt'); 385 if (!header || !bodySection) return; 386 387 var fig = document.createElement('figure'); 388 fig.className = 'shl-news-hero'; 389 var html = '<img src="' + hero.src + '" alt="' + (hero.alt || '') + '" loading="eager" fetchpriority="high">'; 390 if (hero.caption) html += '<figcaption>' + hero.caption + '</figcaption>'; 391 fig.innerHTML = html; 392 393 // Insert at the top of the body section, inside .max wrapper if present 394 var maxWrap = bodySection.querySelector('.max'); 395 if (maxWrap) { 396 maxWrap.insertBefore(fig, maxWrap.firstChild); 397 } else { 398 bodySection.insertBefore(fig, bodySection.firstChild); 399 } 400 } 401 402 // -------- 4. Auto TOC -------- 403 function injectTOC() { 404 if (document.querySelector('.shl-toc')) return; 405 var textBox = document.querySelector('article.ContentPage .pg-txt .text.text-site'); 406 if (!textBox) return; 407 var h2s = Array.from(textBox.querySelectorAll(':scope > h2, :scope > div > h2')); 408 if (h2s.length < 4) return; 409 410 var toc = document.createElement('nav'); 411 toc.className = 'shl-toc'; 412 toc.setAttribute('aria-label', 'Obsah Älánku'); 413 var html = '<div class="shl-toc-title">Obsah Älánku</div><ol>';
414 h2s.forEach(function (h, i) { 415 var slug = h.id || slugify(h.textContent); 416 if (!slug) slug = 'h2-' + i; 417 h.id = slug; 418 // Add scroll-margin so the anchor doesn't hide under sticky header 419 h.style.scrollMarginTop = '80px'; 420 html += '<li><a href="#' + slug + '">' + h.textContent.replace(/[<>]/g, '') + '</a></li>'; 421 }); 422 html += '</ol>'; 423 toc.innerHTML = html; 424 425 // Insert before the first child of text-site 426 textBox.insertBefore(toc, textBox.firstChild); 427 } 428 429 // -------- 5. /a/<slug>-recept link â CTA box -------- 430 function transformRecipeCTA() { 431 var textBox = document.querySelector('article.ContentPage .pg-txt .text.text-site'); 432 if (!textBox) return; 433 var anchors = textBox.querySelectorAll('a[href^="/a/"][href$="-recept"], a[href^="/en/a/"][href$="-recipe"], a[href^="/ch/a/"]'); 434 anchors.forEach(function (a) { 435 // Only transform if anchor's parent is a <p> and it's in the latter half of article 436 // Heuristic: skip if already wrapped or if it's inside a list 437 if (a.dataset.shlNewsCta === '1') return; 438 if (a.closest('.shl-news-cta')) return; 439 if (a.closest('li')) return; 440 var p = a.closest('p'); 441 if (!p) return; 442 443 // Build CTA replacement 444 var cta = document.createElement('a'); 445 cta.className = 'shl-news-cta'; 446 cta.href = a.getAttribute('href'); 447 cta.dataset.shlNewsCta = '1'; 448 cta.innerHTML = 449 '<span class="shl-news-cta-icon" aria-hidden="true">â</span>' + 450 '<span class="shl-news-cta-text">' + 451 '<div class="shl-news-cta-label">Krok-za-krokem recept</div>' + 452 '<div class="shl-news-cta-title">' + (a.textContent || 'OtevÅÃt recept') + '</div>' + 453 '</span>' + 454 '<span class="shl-news-cta-arrow" aria-hidden="true">âº</span>'; 455 456 // Replace the entire paragraph if it's just the link, else replace just the link 457 if ((p.textContent || '').trim() === (a.textContent || '').trim()) { 458 p.parentNode.replaceChild(cta, p); 459 } else { 460 // Wrap before the paragraph: drop the paragraph and insert CTA after, then keep paragraph minus the link 461 p.parentNode.insertBefore(cta, p.nextSibling); 462 a.remove(); 463 // Clean up if paragraph is now empty 464 if (!(p.textContent || '').trim()) p.remove(); 465 } 466 }); 467 } 468 469 // -------- 6. Article schema.org JSON-LD -------- 470 function injectArticleSchema() { 471 if (document.querySelector('script[data-shl-article-schema]')) return; 472 var h1 = document.querySelector('article.ContentPage h1'); 473 if (!h1) return; 474 var title = (h1.textContent || '').trim(); 475 if (!title) return; 476 var url = 'https://www.shalamar.eu' + location.pathname.replace(/\/$/, ''); 477 // Find first image in the article body for image 478 var img = document.querySelector('article.ContentPage img'); 479 var imgUrl = img ? img.src : 'https://www.shalamar.eu/_assets/img/logo.png'; 480 // Try to get publish date from any visible time/date element 481 var publishDate = null; 482 var timeEl = document.querySelector('article.ContentPage time[datetime]'); 483 if (timeEl) publishDate = timeEl.getAttribute('datetime'); 484 485 var data = { 486 '@context': 'https://schema.org', 487 '@type': 'Article', 488 'headline': title, 489 'image': [imgUrl], 490 'author': { '@type': 'Organization', 'name': 'Shalamar Foods' }, 491 'publisher': { 492 '@type': 'Organization', 493 'name': 'Shalamar Foods', 494 'logo': { '@type': 'ImageObject', 'url': 'https://www.shalamar.eu/_assets/img/logo.png' } 495 }, 496 'mainEntityOfPage': { '@type': 'WebPage', '@id': url } 497 }; 498 if (publishDate) data.datePublished = publishDate; 499 500 var s = document.createElement('script'); 501 s.type = 'application/ld+json'; 502 s.setAttribute('data-shl-article-schema', '1'); 503 s.textContent = JSON.stringify(data); 504 document.head.appendChild(s); 505 } 506 507 // -------- Run -------- 508 injectCSS(); 509 510 // -------- Listing thumbnail injection -------- 511 function injectListingThumbnails() { 512 if (!IS_LISTING) return; 513 var articles = document.querySelectorAll('article.SNIitem');
514 articles.forEach(function (art) { 515 // Skip if article already has a figure/img 516 if (art.querySelector('figure, img')) return; 517 // Find slug from any anchor inside 518 var a = art.querySelector('a[href*="/news/"]'); 519 if (!a) return; 520 var slug = (a.getAttribute('href') || '').replace(/\/$/, '').split('/').pop(); 521 var hero = HERO_IMAGES[slug]; 522 if (!hero) return; 523 524 // Build figure matching UpGates' structure 525 var fig = document.createElement('figure'); 526 fig.className = 'figure flex-shrink-0 cursor-pointer shl-news-listing-fig'; 527 fig.setAttribute('data-href', a.getAttribute('href')); 528 fig.innerHTML = '<img src="' + hero.src + '" alt="' + (hero.alt || '') + '" loading="lazy" style="width:100%;max-width:280px;height:auto;aspect-ratio:4/3;object-fit:cover;border-radius:6px;">'; 529 // Click forwarding 530 fig.addEventListener('click', function () { location.href = a.getAttribute('href'); }); 531 // Insert as first child of article (UpGates puts figure first, then text div) 532 art.insertBefore(fig, art.firstChild); 533 }); 534 } 535 536 function run() { 537 if (IS_ARTICLE) { 538 injectHero(); 539 injectReadingTime(); 540 injectTOC(); 541 transformRecipeCTA(); 542 injectArticleSchema(); 543 } 544 if (IS_LISTING) { 545 injectListingThumbnails(); 546 } 547 } 548 549 if (document.readyState === 'loading') { 550 document.addEventListener('DOMContentLoaded', run, { once: true }); 551 } else { 552 run(); 553 } 554 555 // Late-render fallback (UpGates sometimes hydrates after DOMContentLoaded) 556 setTimeout(run, 500); 557 setTimeout(run, 1500); 558})();
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.