1const toggle = document.querySelector(".menu-toggle"); 2const nav = document.querySelector(".site-nav"); 3 4toggle?.addEventListener("click", () => { 5 const open = nav.classList.toggle("open"); 6 toggle.setAttribute("aria-expanded", open); 7}); 8 9document.querySelectorAll(".site-nav a").forEach((a) => 10 a.addEventListener("click", () => { 11 nav.classList.remove("open"); 12 toggle?.setAttribute("aria-expanded", "false"); 13 }), 14); 15 16const year = document.getElementById("year"); 17if (year) year.textContent = new Date().getFullYear(); 18 19// The newsletter footer is repeated across the main site and legacy archive. 20// Add the consent control to older archived copies that pre-date the new markup. 21document.querySelectorAll('form[name="newsletter"]').forEach((form) => { 22 if (form.querySelector('[name="marketing-consent"]')) return; 23 const label = document.createElement("label"); 24 label.className = "newsletter-consent"; 25 const checkbox = document.createElement("input"); 26 checkbox.type = "checkbox"; 27 checkbox.name = "marketing-consent"; 28 checkbox.value = "yes"; 29 checkbox.required = true; 30 const text = document.createElement("span"); 31 text.textContent = 32 "I consent to GLCT using my email address to send occasional news and event updates."; 33 label.append(checkbox, text); 34 form.append(label); 35}); 36 37(function () { 38 const select = document.querySelector('select[name="enquiry-type"]'); 39 if (!select) return; 40 const type = new URLSearchParams(window.location.search).get("type"); 41 const map = { 42 support: "Requesting support", 43 volunteer: "Volunteering", 44 fundraising: "Fundraising", 45 business: "Corporate partnership", 46 policy: "Policy / governance request", 47 }; 48 if (type && map[type]) select.value = map[type]; 49})(); 50 51// Keep the public events diary aligned with the trustee-managed diary. 52(async function () { 53 const cards = [...document.querySelectorAll("[data-events-diary] [data-diary-id]")]; 54 if (!cards.length) return; 55 const formatDate = (start, end) => { 56 const options = { day: "numeric", month: "long", year: "numeric" }; 57 const first = new Date(`${start}T12:00:00`).toLocaleDateString("en-GB", options); 58 if (!end) return first; 59 const last = new Date(`${end}T12:00:00`); 60 const startDate = new Date(`${start}T12:00:00`); 61 if (startDate.getMonth() === last.getMonth() && startDate.getFullYear() === last.getFullYear()) { 62 return `${startDate.getDate()}â${last.toLocaleDateString("en-GB", options)}`; 63 } 64 return `${first}â${last.toLocaleDateString("en-GB", options)}`; 65 }; 66 try { 67 const response = await fetch("/api/events-live", { headers: { Accept: "application/json" } }); 68 if (!response.ok) return; 69 const data = await response.json(); 70 const byId = new Map((data.items || []).map((item) => [item.id, item])); 71 for (const card of cards) { 72 const item = byId.get(card.dataset.diaryId); 73 if (!item) continue; 74 card.dataset.diaryActive = String(item.active !== false); 75 card.hidden = 76 card.dataset.diaryActive === "false" || 77 card.dataset.projectActive === "false"; 78 const date = card.querySelector("[data-diary-date]"); 79 const name = card.querySelector("[data-diary-name]"); 80 const location = card.querySelector("[data-diary-location]"); 81 const description = card.querySelector("[data-diary-description]"); 82 if (date) { 83 date.textContent = formatDate(item.startDate, item.endDate); 84 date.dateTime = item.startDate; 85 } 86 if (name) { 87 let displayName = item.name; 88 if (name.dataset.diaryShortName === "true" && item.location) { 89 displayName = displayName.replace(/\s+at\s+.+$/i, ""); 90 } 91 name.textContent = displayName; 92 } 93 if (location) { 94 location.textContent = item.location || ""; 95 location.hidden = !item.location; 96 } 97 if (description) description.textContent = item.description; 98 } 99 } catch (_error) { 100 // Static diary copy remains available if the API is unavailable. 101 } 102})(); 103 104// Keep the three signature-project cards current without turning them into a
104news feed. 105(async function () { 106 const cards = [ 107 ...document.querySelectorAll("[data-signature-projects] [data-project-id]"), 108 ]; 109 if (!cards.length) return; 110 try { 111 const res = await fetch("/api/projects-live", { 112 headers: { Accept: "application/json" }, 113 }); 114 if (!res.ok) return; 115 const data = await res.json(); 116 const items = Array.isArray(data.items) ? data.items : []; 117 const byId = new Map(items.map((item) => [item.id, item])); 118 for (const card of cards) { 119 const item = byId.get(card.dataset.projectId); 120 if (!item) continue; 121 card.dataset.projectActive = String(item.active !== false); 122 card.hidden = 123 card.dataset.diaryActive === "false" || 124 card.dataset.projectActive === "false"; 125 const timing = card.querySelector("[data-project-timing]"); 126 const title = card.querySelector("[data-project-title]"); 127 const description = card.querySelector("[data-project-description]"); 128 if (timing && item.timing) timing.textContent = item.timing; 129 if (title && item.title) title.textContent = item.title; 130 if (description && item.description) 131 description.textContent = item.description; 132 } 133 } catch (_err) { 134 // The static copy in index.html remains visible if the API is unavailable. 135 } 136})();
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.