PageSourceSearch

https://becomingordinary.org/main.js

js becomingordinary.org collected 2026-10-10 00:07:33 UTC 4,764 bytes, 128 lines download raw bytes

1// Mobile nav toggle
2const toggle = document.querySelector('.nav-toggle');
3const links = document.querySelector('.nav-links');
4if (toggle && links) {
5  toggle.addEventListener('click', () => links.classList.toggle('open'));
6
7  // Close mobile nav on link click
8  document.querySelectorAll('.nav-links a').forEach(link => {
9    link.addEventListener('click', () => links.classList.remove('open'));
10  });
11}
12
13// Enso: opens mobile menu on narrow screens, scrolls to About on desktop
14const enso = document.querySelector('.enso-container');
15if (enso && links) {
16  enso.addEventListener('click', () => {
17    if (window.innerWidth <= 640) {
18      links.classList.toggle('open');
19    } else {
20      const about = document.getElementById('about');
21      if (about) about.scrollIntoView({ behavior: 'smooth' });
22    }
23  });
24}
25
26// Support section toggle (scoped to the Gift section)
27const supportToggle = document.querySelector('#support .support-toggle');
28const supportExpanded = document.getElementById('support-expanded');
29
30function openSupport() {
31  if (supportExpanded && !supportExpanded.classList.contains('open')) {
32    supportExpanded.classList.add('open');
33    supportToggle.setAttribute('aria-expanded', 'true');
34    supportToggle.innerHTML = 'Hide &uarr;';
35  }
36}
37
38if (supportToggle && supportExpanded) {
39  supportExpanded.removeAttribute('hidden');
40  supportToggle.addEventListener('click', () => {
41    const isOpen = supportExpanded.classList.toggle('open');
42    supportToggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
43    supportToggle.innerHTML = isOpen ? 'Hide &uarr;' : 'Offer a gift &rarr;';
44  });
45
46  // Auto-expand when someone clicks a Support nav link
47  document.querySelectorAll('a[href="#support"]').forEach(link => {
48    link.addEventListener('click', () => openSupport());
49  });
50
51  // Auto-expand if the page is loaded with #support in the URL
52  if (window.location.hash === '#support') {
53    openSupport();
54  }
55}
56
57// Fees toggle (Private Practice Guidance)
58const feesToggle = document.getElementById('fees-toggle');
59const feesExpanded = document.getElementById('fees-expanded');
60if (feesToggle && feesExpanded) {
61  feesExpanded.removeAttribute('hidden');
62  feesToggle.addEventListener('click', () => {
63    const isOpen = feesExpanded.classList.toggle('open');
64    feesToggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
65    feesToggle.innerHTML = isOpen ? 'Hide &uarr;' : 'View fees &rarr;';
66  });
67}
68
69// Dana frequency toggle (one-time vs monthly)
70const danaToggleOptions = document.querySelectorAll('.dana-toggle-option');
71const danaOnceWrap = document.querySelector('.dana-tiers-once');
72const danaMonthlyWrap = document.querySelector('.dana-tiers-monthly');
73if (danaOnceWrap && danaMonthlyWrap) {
74  danaToggleOptions.forEach(option => {
75    option.addEventListener('click', () => {
76      const frequency = option.dataset.frequency;
77      danaToggleOptions.forEach(o => {
78        o.classList.toggle('active', o === option);
79        o.setAttribute('aria-selected', o === option ? 'true' : 'false');
80      });
81      if (frequency === 'once') {
82        danaOnceWrap.removeAttribute('hidden');
83        danaMonthlyWrap.setAttribute('hidden', '');
84      } else {
85        danaOnceWrap.setAttribute('hidden', '');
86        danaMonthlyWrap.removeAttribute('hidden');
87      }
88    });
89  });
90}
91
92// Contact form — AJAX submit with inline success message (falls back to normal POST if fetch fails)
93const contactForm = document.querySelector('.contact-form');
94if (contactForm) {
95  contactForm.addEventListener('submit', (e) => {
96    e.preventDefault();
97    const button = contactForm.querySelector('.contact-button');
98    const success = contactForm.querySelector('.contact-success');
99    button.disabled = true;
100    button.textContent = 'Sending…';
101    fetch('/', {
102      method: 'POST',
103      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
104      body: new URLSearchParams(new FormData(contactForm)).toString()
105    })
106      .then((res) => {
107        if (!res.ok) throw new Error('Submit failed');
108        contactForm.querySelectorAll('.contact-form-row, .contact-textarea, .contact-button').forEach(el => el.setAttribute('hidden', ''));
109        success.removeAttribute('hidden');
110      })
111      .catch(() => {
112        // Fall back to a standard form POST (Netlify's default success page).
113        // form.submit() does not re-fire the submit event, so no loop.
114        contactForm.submit();
115      });
116  });
117}
118
119// Scroll fade-in
120const observer = new IntersectionObserver((entries) => {
121  entries.forEach(entry => {
122    if (entry.isIntersecting) {
123      entry.target.classList.add('visible');
124    }
125  });
126}, { threshold: 0.15 });
127
128document.querySelectorAll('.fade-in-section').forEach(el => observer.observe(el));

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.