1function removeAllPreviewCards () { 2 document.querySelectorAll('.link-preview-card').forEach(card => card.remove()) 3} 4 5function initPreviewCardLinks () { 6 // Clean up any orphaned preview cards from previous navigation 7 removeAllPreviewCards() 8 9 if (window.innerWidth < 768) { return } // Don't init preview cards on mobile 10 11 const mainElement = document.querySelector('main') 12 if (!mainElement) { return } 13 14 // Only init for .preview-card-link 15 const anchors = mainElement.querySelectorAll('a.preview-card-link[href]') 16 const survivalDomains = ['survival.es', 'survival.it', 'survivalinternational.fr', 'survivalinternational.de', 'survivalinternational.org', 'survivalbrasil.org'] 17 const allowedSubdomains = ['', 'www', 'staging'] 18 19 anchors.forEach(anchor => { 20 if (anchor.hasAttribute('data-controller')) { return } 21 if (anchor.classList.contains('card')) { return } 22 if (anchor.classList.contains('btn')) { return } 23 if (!anchor.classList.contains('preview-card-link')) { return } 24 25 const subdomain = matchedSubdomain(anchor.href, survivalDomains) 26 if (subdomain === null || !allowedSubdomains.includes(subdomain)) { return } 27 28 // Add preview controller attributes 29 anchor.setAttribute('data-controller', 'numiko--link-preview-card') 30 anchor.setAttribute('data-action', 'mouseenter->numiko--link-preview-card#showPreview mouseleave->numiko--link-preview-card#hidePreview mousemove->link-preview-card--preview#trackCursor') 31 32 // staging. has no www. counterpart, so unifying it would produce www.staging. 33 if (subdomain !== 'staging') { unifyDomainLinks(anchor) } 34 }) 35} 36 37// Only content pages on the bare domain, www., or staging. should get a preview card - 38// this excludes asset/CDN subdomains (e.g. assets.) and other app subdomains (e.g. donate.). 39function matchedSubdomain (href, survivalDomains) { 40 const hostname = new URL(href).hostname 41 const matchedDomain = survivalDomains.find(domain => hostname === domain || hostname.endsWith(`.${domain}`)) 42 if (!matchedDomain) { return null } 43 44 return hostname === matchedDomain ? '' : hostname.slice(0, -(matchedDomain.length + 1)) 45} 46 47function unifyDomainLinks (anchor) { 48 const requiresWWW = window.location.hostname.includes('www.') 49 const anchorContainsWWW = anchor.href.includes('www.') 50 if (requiresWWW === anchorContainsWWW) { return } 51 if (requiresWWW && !anchorContainsWWW) { 52 anchor.href = anchor.href.replace('://', '://www.') 53 } else if (!requiresWWW && anchorContainsWWW) { 54 anchor.href = anchor.href.replace('://www.', '://') 55 } 56} 57 58function setupTurboCleanup () { 59 document.addEventListener('turbo:before-cache', () => { 60 removeAllPreviewCards() 61 }) 62 63 document.addEventListener('turbo:load', () => { 64 removeAllPreviewCards() 65 initPreviewCardLinks() 66 }) 67} 68 69export { initPreviewCardLinks, removeAllPreviewCards, setupTurboCleanup }
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.