1// One collection drives topic filters, full-archive search, and explicit paging. 2(function () { 3 var root = document.querySelector('[data-blog-journal]'); 4 if (!root) return; 5 var BATCH_SIZE = 12; 6 var input = root.querySelector('#journalSearch'); 7 var picker = root.querySelector('[data-journal-picker]'); 8 var toggle = root.querySelector('[data-journal-toggle]'); 9 var panel = root.querySelector('[data-journal-panel]'); 10 var pickerLabel = root.querySelector('[data-journal-picker-label]'); 11 var options = Array.from(panel.querySelectorAll('[data-journal-category]')); 12 var chips = Array.from(root.querySelectorAll('[data-journal-category]')); 13 var featured = root.querySelector('[data-journal-featured]'); 14 var more = root.querySelector('[data-journal-more]'); 15 var pagination = root.querySelector('[data-journal-pagination]'); 16 var empty = root.querySelector('[data-journal-empty]'); 17 var count = root.querySelector('[data-journal-count]'); 18 var heading = root.querySelector('#journalArchiveTitle'); 19 var clear = root.querySelector('[data-journal-clear]'); 20 var category = ''; 21 var limit = BATCH_SIZE; 22 23 function normalize(value) { 24 return value.toLocaleLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').trim(); 25 } 26 var records = Array.from(root.querySelectorAll('[data-journal-card]')).map(function (card) { 27 return { element: card, text: normalize(card.dataset.search), category: card.dataset.category, technology: card.dataset.technology === 'true', featured: card.dataset.featured === 'true' }; 28 }); 29 var matching = []; 30 root.classList.add('journal-enhanced'); 31 32 function render() { 33 var query = normalize(input.value); 34 var terms = query.split(/\s+/).filter(Boolean); 35 var filtering = Boolean(query || category); 36 matching = records.filter(function (record) { 37 return (filtering || !record.featured) && (!category || (category === 'ai-technology' ? record.technology : record.category === category)) && terms.every(function (term) { return record.text.includes(term); }); 38 }); 39 records.forEach(function (record) { record.element.hidden = true; }); 40 matching.slice(0, limit).forEach(function (record) { record.element.hidden = false; }); 41 if (featured) featured.hidden = filtering; 42 var selected = chips.find(function (chip) { return chip.dataset.journalCategory === category; }); 43 var secondary = selected && panel.contains(selected); 44 pickerLabel.textContent = secondary ? selected.textContent.trim() : root.dataset.moreLabel; 45 toggle.classList.toggle('is-selected', Boolean(secondary)); 46 chips.forEach(function (chip) { chip.setAttribute('aria-pressed', String(chip.dataset.journalCategory === category)); }); 47 clear.hidden = !input.value; 48 empty.hidden = matching.length !== 0; 49 pagination.hidden = limit >= matching.length; 50 heading.textContent = query ? root.dataset.resultsLabel : (category && selected ? selected.textContent.trim() : root.dataset.latestLabel); 51 count.textContent = root.dataset.countTemplate.replace('{shown}', String(Math.min(limit, matching.length))).replace('{total}', String(matching.length)); 52 } 53 function resetPage() { limit = BATCH_SIZE; render(); } 54 chips.forEach(function (chip) { 55 chip.addEventListener('click', function () { 56 category = chip.dataset.journalCategory; 57 closePicker(panel.contains(chip)); 58 resetPage(); 59 }); 60 }); 61 // Disclosure buttons retain normal Tab navigation, with optional arrow-key navigation. 62 function closePicker(restoreFocus) { 63 panel.hidden = true; 64 toggle.setAttribute('aria-expanded', 'false'); 65 if (restoreFocus) toggle.focus(); 66 } 67 function openPicker(focusOption, last) { 68 panel.hidden = false; 69 toggle.setAttribute('aria-expanded', 'true'); 70 if (focusOption && options.length) { 71 var selected = options.find(function (option) { return option.dataset.journalCategory === category; }); 72 (last ? options[options.length - 1] : selected || options[0]).focus(); 73 } 74 } 75 toggle.addEventListener('click', function () { 76 if (panel.hidden) openPicker(false); else closePicker(false); 77 }); 78 toggle.addEventListener('keydown', function (event) { 79 if (event.key === 'ArrowDown' || event.key === 'ArrowUp') { 80 event.preventDefault(); openPicker(true, event.key === 'ArrowUp'); 81 } 82 }); 83 picker.addEventListener('keydown', function (event) { 84 if (event.key === 'Escape' && !panel.hidden) { event.preventDefault(); closePicker(true); } 85 }); 86 panel.addEventListener('keydown', function (event) { 87 var index = options.indexOf(document.activeElement); 88 var next; 89 if (event.key === 'ArrowDown') next = (index + 1) % options.length; 90 if (event.key === 'ArrowUp') next = (index + options.length - 1) % options.length; 91 if (event.key === 'Home') next = 0; 92 if (event.key === 'End') next = options.length - 1; 93 if (next !== undefined) { event.preventDefault(); options[next].focus(); } 94 }); 95 document.addEventListener('pointerdown', function (event) { 96 if (!picker.contains(event.target)) closePicker(false); 97 }); 98 document.addEventListener('focusin', function (event) { 99 if (!picker.contains(event.target)) closePicker(false); 100 }); 101 input.addEventListener('input', resetPage); 102 clear.addEventListener('click', function () { input.value = ''; resetPage(); input.focus(); }); 103 root.querySelector('[data-journal-reset]').addEventListener('click', function () { 104 input.value = ''; category = ''; resetPage(); input.focus(); 105 }); 106 more.addEventListener('click', function () { 107 var firstNew = matching[limit]; 108 limit += BATCH_SIZE; 109 render(); 110 if (firstNew) firstNew.element.focus({ preventScroll: true }); 111 }); 112 render(); 113})(); 114 115(function () { 116 var form = document.querySelector('.blog-newsletter-form'); 117 if (!form) return; 118 119 var PORTAL_ID = '4235311'; 120 var FORM_IDS = { 121 en: '462da299-9a1f-4d61-a630-dcc3d474ec39', 122 es: '6f61fc1f-80a1-4927-ab70-5e84e79e70fc', 123 fr: '52323858-459b-4012-8472-841227a29632' 124 }; 125 var SUCCESS_TEXT = { en: 'Thanks â', es: '¡Gracias! â', fr: 'Merci â' }; 126 var lang = document.documentElement.lang || 'en'; 127 var formId = FORM_IDS[lang] || FORM_IDS.en; 128 129 form.addEventListener('submit', function (e) { 130 e.preventDefault(); 131 var input = form.querySelector('input[type="email"]'); 132 var button = form.querySelector('button'); 133 var email = input ? input.value.trim() : ''; 134 if (!email) return; 135 136 fetch('https://api.hsforms.com/submissions/v3/integration/submit/'
136+ PORTAL_ID + '/' + formId, { 137 method: 'POST', 138 headers: { 'Content-Type': 'application/json' }, 139 body: JSON.stringify({ 140 fields: [{ objectTypeId: '0-1', name: 'email', value: email }], 141 context: { pageUri: window.location.href, pageName: document.title } 142 }) 143 }).then(function () { 144 if (input) input.value = ''; 145 if (button) button.textContent = SUCCESS_TEXT[lang] || SUCCESS_TEXT.en; 146 }).catch(function () { 147 if (button) button.textContent = SUCCESS_TEXT[lang] || SUCCESS_TEXT.en; 148 }); 149 }); 150})();
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.