1(() => { 2 'use strict'; 3 4 const mobile = window.matchMedia('(max-width: 959px)'); 5 const selector = 'h1:not([data-no-heading-fit]), h2:not([data-no-heading-fit]), h3:not([data-no-heading-fit])'; 6 const originalSizes = new WeakMap(); 7 const measuredWidths = new WeakMap(); 8 let frame = 0; 9 10 const restore = (heading) => { 11 if (!originalSizes.has(heading)) { 12 originalSizes.set(heading, heading.style.fontSize); 13 } 14 heading.style.fontSize = originalSizes.get(heading); 15 heading.classList.remove('bp-heading-fitted'); 16 }; 17 18 const fit = (heading, force = false) => { 19 const width = heading.clientWidth; 20 if (!width || (!force && measuredWidths.get(heading) === width)) { 21 return; 22 } 23 24 restore(heading); 25 measuredWidths.set(heading, width); 26 27 if (!mobile.matches) { 28 return; 29 } 30 31 const initialSize = parseFloat(getComputedStyle(heading).fontSize); 32 const minimumSize = parseFloat(getComputedStyle(document.documentElement).fontSize) * 1.25; 33 const widthRatios = { H1: 0.2, H2: 0.16, H3: 0.12 }; 34 const responsiveCap = Math.max(minimumSize, width * widthRatios[heading.tagName]); 35 const maximumSize = Math.min(initialSize, responsiveCap); 36 const defaultLines = 'H3' === heading.tagName ? 2 : 3; 37 const requestedLines = parseInt(heading.dataset.headingLines || '', 10); 38 const maximumLines = Number.isFinite(requestedLines) ? requestedLines : Math.max(defaultLines, heading.querySelectorAll('br').length + 1); 39 const hasInlineMedia = Boolean(heading.querySelector('img, picture, svg, video')); 40 41 const fits = (size) => { 42 heading.style.fontSize = `${size.toFixed(2)}px`; 43 const lineHeight = parseFloat(getComputedStyle(heading).lineHeight); 44 const lines = lineHeight ? Math.ceil((heading.scrollHeight - 0.5) / lineHeight) : 1; 45 return heading.scrollWidth <= width + 1 && (hasInlineMedia || lines <= maximumLines); 46 }; 47 48 let fittedSize = maximumSize; 49 if (!fits(fittedSize)) { 50 let low = minimumSize; 51 let high = fittedSize; 52 for (let pass = 0; pass < 8; pass += 1) { 53 const candidate = (low + high) / 2; 54 if (fits(candidate)) { 55 low = candidate; 56 } else { 57 high = candidate; 58 } 59 } 60 fittedSize = low; 61 } 62 63 if (fittedSize < initialSize - 0.5) { 64 heading.style.fontSize = `${fittedSize.toFixed(2)}px`; 65 heading.classList.add('bp-heading-fitted'); 66 } else { 67 restore(heading); 68 } 69 }; 70 71 const fitAll = (force = false) => { 72 document.querySelectorAll(selector).forEach((heading) => fit(heading, force)); 73 }; 74 75 const schedule = (force = false) => { 76 window.cancelAnimationFrame(frame); 77 frame = window.requestAnimationFrame(() => fitAll(force)); 78 }; 79 80 const observer = new ResizeObserver((entries) => { 81 entries.forEach(({ target, contentRect }) => { 82 if (measuredWidths.get(target) !== contentRect.width) { 83 fit(target); 84 } 85 }); 86 }); 87 88 document.querySelectorAll(selector).forEach((heading) => observer.observe(heading)); 89 mobile.addEventListener('change', () => schedule(true)); 90 window.addEventListener('orientationchange', () => schedule(true), { passive: true }); 91 92 if (document.fonts && document.fonts.ready) { 93 document.fonts.ready.then(() => schedule(true)); 94 } else { 95 schedule(true); 96 } 97})();
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.