1/** 2 * Old North Church - Theme Scripts 3 * 4 * Main JavaScript file for theme functionality. 5 * 6 * @package Old_North 7 * @since 1.0.0 8 */ 9 10(function() { 11 'use strict'; 12 13 // ========================================================================= 14 // Shared Height Animation Utilities 15 // CSS handles easing/duration, JS sets heights and toggles `is-animating` 16 // ========================================================================= 17 const running = new WeakMap(); // panel -> cleanup 18 19 const naturalHeight = (panel) => { 20 const prevHeight = panel.style.height; 21 const prevDisplay = panel.style.display; 22 23 const wasHidden = getComputedStyle(panel).display === "none"; 24 if (wasHidden) { 25 panel.style.display = "block"; 26 panel.style.height = "auto"; 27 } else { 28 panel.style.height = "auto"; 29 } 30 31 const h = panel.scrollHeight; 32 33 panel.style.height = prevHeight; 34 panel.style.display = prevDisplay; 35 36 return h; 37 }; 38 39 const stopRunning = (panel) => { 40 const cleanup = running.get(panel); 41 if (cleanup) cleanup(); 42 }; 43 44 function animateOpen(panel) { 45 stopRunning(panel); 46 47 // Measure target height before starting 48 const target = naturalHeight(panel); 49 50 // Set initial state 51 panel.style.display = "block"; 52 panel.style.height = "0px"; 53 54 // Force layout so 0px is committed 55 panel.getBoundingClientRect(); 56 57 // Add transition class BEFORE changing height 58 panel.classList.add("is-animating"); 59 60 // Force layout again so transition class is applied 61 panel.getBoundingClientRect(); 62 63 const onEnd = (e) => { 64 if (e.target !== panel || e.propertyName !== "height") return; 65 panel.removeEventListener("transitionend", onEnd); 66 67 panel.classList.remove("is-animating"); 68 panel.style.height = "auto"; 69 70 running.delete(panel); 71 }; 72 73 const cleanup = () => { 74 panel.removeEventListener("transitionend", onEnd); 75 panel.classList.remove("is-animating"); 76 panel.style.height = "auto"; 77 running.delete(panel); 78 }; 79 80 running.set(panel, cleanup); 81 panel.addEventListener("transitionend", onEnd); 82 83 // Trigger the transition by setting explicit px height 84 panel.style.height = `${target}px`; 85 } 86 87 function animateClose(panel) { 88 stopRunning(panel); 89 90 const start = panel.getBoundingClientRect().height || naturalHeight(panel); 91 panel.style.height = `${start}px`; 92 93 // Force layout so start height is committed before collapsing 94 panel.getBoundingClientRect(); 95 96 panel.classList.add("is-animating"); 97 98 const onEnd = (e) => { 99 if (e.target !== panel || e.propertyName !== "height") return; 100 panel.removeEventListener("transitionend", onEnd); 101 102 panel.classList.remove("is-animating"); 103 panel.style.height = "0px"; 104 105 running.delete(panel); 106 }; 107 108 const cleanup = () => { 109 panel.removeEventListener("transitionend", onEnd); 110 panel.classList.remove("is-animating"); 111 panel.style.height = "0px"; 112 running.delete(panel); 113 }; 114 115 running.set(panel, cleanup); 116 panel.addEventListener("transitionend", onEnd); 117 118 // Trigger the transition to 0 119 panel.style.height = "0px"; 120 } 121 122 // Export utilities to window for use by other scripts (e.g., oni-menu) 123 window.OniAnimations = { naturalHeight, animateOpen, animateClose }; 124 125 // ========================================================================= 126 // Accordion Panel Animation 127 // ========================================================================= 128 const TOGGLE_SEL = ".wp-block-accordion-heading__toggle[aria-controls]"; 129 130 const getPanel = (btn) => { 131 const id = btn.getAttribute("aria-controls"); 132 return id ? document.getElementById(id) : null; 133 }; 134 135 function syncInitial(panel, open) { 136 panel.style.display = "block"; 137 panel.style.height = open ? "auto" : "0px"; 138 panel.classList.remove("is-animating"); 139 } 140 141 function initAccordionToggle(btn) { 142 const panel = getPanel(btn); 143 if (!panel) return; 144 145 syncInitial(panel, btn.getAttribute("aria-expanded") === "true"); 146 147 // WordPress core toggles aria-expanded; observe that 148 const obs = new MutationObserver(() => { 149 const open = btn.getAttribute("aria-expanded") === "true"; 150 if (open) animateOpen(panel); 151 else animateClose(panel); 152 }); 153 154 obs.observe(btn, { attributes: true, attributeFilter: ["aria-expanded"] }); 155 } 156 157 function initAccordions() {
158 document.querySelectorAll(TOGGLE_SEL).forEach(initAccordionToggle); 159 } 160 161 // ========================================================================= 162 // Initialize on DOM Ready 163 // ========================================================================= 164 if (document.readyState === "loading") { 165 document.addEventListener("DOMContentLoaded", initAccordions); 166 } else { 167 initAccordions(); 168 } 169 170})();
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.