PageSourceSearch

https://andrewsag.com/assets/brand-ticker.js?v=20261007-2

js andrewsag.com collected 2026-10-08 21:34:41 UTC 3,157 bytes, 55 lines download raw bytes

1(() => {
2  const esc = value => String(value ?? '').replace(/[&<>"']/g, ch => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[ch]));
3  const localLogo = brand => {
4    const configured = String(brand.logo || brand.logoUrl || '').trim();
5    if (!configured) return '';
6    try {
7      const url = new URL(configured, window.location.href);
8      return url.origin === window.location.origin ? url.href : '';
9    } catch (_) {
10      return '';
11    }
12  };
13  const identity = (brand, context = 'card') => {
14    const logo = localLogo(brand);
15    const logoOnly = context === 'marquee' && brand.marqueeLogoOnly === true && !!logo;
16    const plate = ['dark', 'light'].includes(brand.logoPlate) ? ` logo-plate-${brand.logoPlate}` : '';
17    const image = logo ? `<img class="brand-logo-image brand-logo-${context}${plate}" src="${esc(logo)}" alt="" loading="lazy" onerror="this.style.display='none'">` : '';
18    return `<span class="brand-identity${logoOnly ? ' brand-identity-logo-only' : ''}">${image}${logoOnly ? '' : `<span class="brand-name">${esc(brand.name)}</span>`}</span>`;
19  };
20  const marquee = brands => {
21    const links = brands.filter(brand => brand.showInBanner !== false).map(brand => `<a href="${esc(brand.url)}" target="_blank" rel="noopener" aria-label="Visit ${esc(brand.name)} website">${identity(brand, 'marquee')}</a>`).join('');
22    const clones = links.replace(/<a /g, '<a tabindex="-1" ');
23    return `<div class="brand-marquee"><div class="brand-marquee-set">${links}</div><div class="brand-marquee-set" aria-hidden="true">${clones}</div></div>`;
24  };
25  let brandsPromise;
26  const load = () => brandsPromise ||= fetch('data/brands.json', { cache: 'no-store' })
27    .then(response => response.ok ? response.json() : [])
28    .then(data => (Array.isArray(data) ? data : []).filter(brand => brand && brand.published !== false).sort((a, b) => (Number(a.sortOrder) || 999) - (Number(b.sortOrder) || 999)));
29
30  window.ANDREWS_AG_BRANDS = { identity, marquee, load };
31  // Keyboard users need a still, scrollable row with the active link in view.
32  // Pointer activation keeps the current position so moving a link cannot cancel a click.
33  document.addEventListener('focusin', event => {
34    const link = event.target.closest?.('.brand-marquee a');
35    const wrapper = link?.closest('.brand-marquee-wrap');
36    if (!wrapper || !link.matches(':focus-visible')) return;
37    wrapper.classList.add('is-keyboard-active');
38    link.scrollIntoView({ block: 'nearest', inline: 'nearest', behavior: 'instant' });
39  });
40  document.addEventListener('focusout', event => {
41    const wrapper = event.target.closest?.('.brand-marquee-wrap');
42    if (!wrapper) return;
43    requestAnimationFrame(() => {
44      if (wrapper.contains(document.activeElement)) return;
45      wrapper.scrollLeft = 0;
46      wrapper.classList.remove('is-keyboard-active');
47    });
48  });
49  const topBanner = document.getElementById('home-top-brand-marquee');
50  if (topBanner) load().then(brands => {
51    if (!brands.some(brand => brand.showInBanner !== false)) return;
52    topBanner.innerHTML = marquee(brands);
53    topBanner.hidden = false;
54  }).catch(() => {});
55})();

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.