1/* 2 * brand-reveal.js -- the "Gapfy <Product>" title flourish. 3 * 4 * For every .brand-mark on the page: measure the "Gapfy " prefix, show the 5 * full mark briefly, then collapse the prefix right-to-left so only the 6 * product name remains. A click brings "Gapfy" back; clicking again hides it. 7 * 8 * The full mark always lives in the DOM, so this is purely cosmetic. Under 9 * prefers-reduced-motion we leave the full mark in place and never collapse. 10 * 11 * Blazor's enhanced navigation (menu links, no full reload) reuses the title 12 * node and strips the JS-added attributes, but it does NOT remove listeners 13 * attached with addEventListener. So binding click PER ELEMENT stacks a fresh 14 * listener on every navigation -- two toggles cancel out and the click looks 15 * dead. The click is therefore delegated to the document: one listener, bound 16 * once, reading the live state. The entrance re-scans on enhancedload (the 17 * enhanced-load bridge re-emits Blazor's event as a DOM event), matching the 18 * other motion scripts. 19 */ 20(function () { 21 var HOLD_MS = 650; 22 var ENTRANCE_FLAG = 'brandRevealEntered'; 23 24 function reduceMotion() { 25 return window.matchMedia && 26 window.matchMedia('(prefers-reduced-motion: reduce)').matches; 27 } 28 29 // scrollWidth returns the content width regardless of the CSS max-width 30 // cap, so this is correct whether the prefix is currently shown or 31 // collapsed. 32 function measure(mark) { 33 var prefix = mark.querySelector('.brand-mark__prefix'); 34 if (prefix) { 35 mark.style.setProperty('--brand-full', prefix.scrollWidth + 'px'); 36 } 37 } 38 39 function collapse(mark) { 40 measure(mark); 41 mark.setAttribute('data-state', 'collapsed'); 42 } 43 44 function expand(mark) { 45 measure(mark); 46 mark.removeAttribute('data-state'); 47 } 48 49 // Show the full mark, then collapse after a short hold. Runs once per 50 // element per page view; Blazor strips the flag on enhanced navigation, so 51 // it naturally replays when you move between pages. 52 function playEntrance(mark) { 53 measure(mark); 54 if (document.fonts && document.fonts.ready) { 55 document.fonts.ready.then(function () { measure(mark); }); 56 } 57 mark.classList.add('is-ready'); 58 if (reduceMotion()) { return; } 59 window.setTimeout(function () { collapse(mark); }, HOLD_MS); 60 } 61 62 function scan() { 63 var marks = document.querySelectorAll('.brand-mark'); 64 for (var i = 0; i < marks.length; i++) { 65 var mark = marks[i]; 66 if (mark.dataset[ENTRANCE_FLAG]) { continue; } 67 mark.dataset[ENTRANCE_FLAG] = '1'; 68 playEntrance(mark); 69 } 70 } 71 72 // Delegated click -- bound once, survives every DOM swap. Clicking while 73 // collapsed brings "Gapfy" back; clicking again hides it. User-initiated, 74 // so it works even under reduced motion. 75 if (!window.__brandRevealClickBound) { 76 window.__brandRevealClickBound = true; 77 document.addEventListener('click', function (e) { 78 var mark = e.target && e.target.closest ? e.target.closest('.brand-mark') : null; 79 if (!mark) { return; } 80 if (mark.getAttribute('data-state') === 'collapsed') { 81 expand(mark); 82 } else { 83 collapse(mark); 84 } 85 }); 86 } 87 88 if (document.readyState === 'loading') { 89 document.addEventListener('DOMContentLoaded', scan); 90 } else { 91 scan(); 92 } 93 94 document.addEventListener('enhancedload', scan); 95})();
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.