PageSourceSearch

https://dasatraining.org/site.js

js dasatraining.org collected 2026-10-02 01:06:29 UTC 9,486 bytes, 152 lines download raw bytes

1/* Shared header, footer, and settings-driven content. Edit site-config.js, not this file. */
2(function () {
3  var C = window.DASA || {};
4  var page = document.body.getAttribute('data-page');
5
6  var SHIELD = '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#F2B134" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l7 3v5c0 5-3.5 8.5-7 10-3.5-1.5-7-5-7-10V6l7-3z"/><path d="M9 12l2 2 4-4"/></svg>';
7  var links = [
8    ['home', '/', 'Home'],
9    ['schedule', '/schedule', 'Schedule &amp; Register'],
10    ['self-paced', '/self-paced', 'Self-Paced'],
11    ['faqs', '/faqs', 'FAQs'],
12    ['resources', '/resources', 'Resources'],
13    ['contact', '/contact', 'Contact'],
14  ];
15
16  function esc(s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); }
17
18  // Header
19  var header = document.getElementById('site-header');
20  if (header) {
21    header.className = 'site-header';
22    header.innerHTML =
23      '<div class="wrap">' +
24        '<a class="brand" href="/"><span class="mark">' + SHIELD + '</span><span><b>NYS DASA Training</b><small>at Daemen University</small></span></a>' +
25        '<button class="menu-btn" type="button" aria-expanded="false" aria-controls="site-nav">Menu</button>' +
26        '<nav class="nav" id="site-nav" aria-label="Main">' +
27          links.map(function (l) { return '<a href="' + l[1] + '"' + (l[0] === page ? ' aria-current="page"' : '') + '>' + l[2] + '</a>'; }).join('') +
28          '<a class="btn primary" href="/schedule#register">Register</a>' +
29        '</nav>' +
30      '</div>';
31    var btn = header.querySelector('.menu-btn');
32    btn.addEventListener('click', function () {
33      var open = header.classList.toggle('open');
34      btn.setAttribute('aria-expanded', open ? 'true' : 'false');
35    });
36  }
37
38  // Footer
39  var footer = document.getElementById('site-footer');
40  if (footer) {
41    footer.className = 'site-footer';
42    footer.innerHTML =
43      '<div class="wrap">' +
44        '<div><b class="name">NYS DASA Training at Daemen University</b>' + esc(C.address) + '</div>' +
45        '<div><span class="lbl">CONTACT</span><a href="mailto:' + esc(C.email) + '">' + esc(C.email) + '</a><br>' + esc(C.phone) + '</div>' +
46        '<div><span class="lbl">FOLLOW</span>' + (C.facebook ? '<a href="' + esc(C.facebook) + '" target="_blank" rel="noopener">Like our page on Facebook</a>' : 'Facebook page coming soon') + '</div>' +
47      '</div>';
48  }
49
50  // Links from settings
51  document.querySelectorAll('[data-link]').forEach(function (a) {
52    var url = C[a.getAttribute('data-link')];
53    if (url) { a.href = url; if (/^https?:/.test(url)) { a.target = '_blank'; a.rel = 'noopener'; } }
54    else { a.setAttribute('aria-disabled', 'true'); }
55  });
56  document.querySelectorAll('[data-text]').forEach(function (el) { el.textContent = C[el.getAttribute('data-text')] || ''; });
57
58  // Schedule
59  var MONTHS = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'];
60  var DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
61  function parse(d) { var p = String(d).split('-'); return new Date(+p[0], +p[1] - 1, +p[2]); }
62  function longDate(d) { return DAYS[d.getDay()] + ', ' + ['January','February','March','April','May','June','July','August','September','October','November','December'][d.getMonth()] + ' ' + d.getDate(); }
63  var today = new Date(); today.setHours(0, 0, 0, 0);
64  var upcoming = (C.classes || []).filter(function (c) { return c && c.date && parse(c.date) >= today; })
65    .sort(function (a, b) { return parse(a.date) - parse(b.date); });
66
67  document.querySelectorAll('[data-schedule]').forEach(function (box) {
68    var limit = +box.getAttribute('data-schedule') || 99;
69    var items = upcoming.slice(0, limit);
70    if (!items.length) {
71      box.innerHTML = '<div class="empty">New class dates will be posted soon. Email <a href="mailto:' + esc(C.email) + '">' + esc(C.email) + '</a> to ask about the next class, or start with the self-paced option.</div>';
72      return;
73    }
74    box.innerHTML = items.map(function (c) {
75      var d = parse(c.date), wk = c.type === 'weeknight';
76      var title = wk ? 'Weeknight class · ' + longDate(d) + (c.date2 ? ' &amp; ' + longDate(parse(c.date2)) : '') : 'Saturday class · ' + longDate(d);
77      var time = wk ? C.weeknightTime : C.saturdayTime;
78      var st = c.status || 'open';
79      var pill = st === 'full' ? '<span class="pill full">Full</span>' : st === 'few' ? '<span class="pill few">Few seats</span>' : '<span class="pill open">Open</span>';
80      var reg = st === 'full' ? '' : '<a class="btn primary" href="' + esc(C.registrationForm) + '" target="_blank" rel="noopener">Register</a>';
81      return '<div class="class"><div class="date"><span>' + MONTHS[d.getMonth()] + '</span><b>' + d.getDate() + '</b></div>' +
82        '<div class="info"><b>' + title + '</b><span>' + esc(time) + ' · Zoom</span></div>' + pill + reg + '</div>';
83    }).join('');
84  });
85
86  // Hybrid sessions
87  var hybrid = (C.hybridSessions || []).filter(function (c) { return c && c.date && parse(c.date) >= today; })
88    .sort(function (a, b) { return parse(a.date) - parse(b.date); });
89  document.querySelectorAll('[data-hybrid]').forEach(function (box) {
90    if (!hybrid.length) {
91      box.innerHTML = '<div class="empty">New hybrid session dates will be posted soon. Email <a href="mailto:' + esc(C.email) + '">' + esc(C.email) + '</a> to ask about the next one.</div>';
92      return;
93    }
94    box.innerHTML = hybrid.map(function (c) {
95      var d = parse(c.date), st = c.status || 'open';
96      var pill = st === 'full' ? '<span class="pill full">Full</span>' : st === 'few' ? '<span class="pill few">Few seats</span>' : '<span class="pill open">Open</span>';
97      var reg = st === 'full' ? '' : '<a class="btn teal" href="' + esc(C.registrationForm) + '" target="_blank" rel="noopener">Register</a>';
98      return '<div class="class"><div class="date teal"><span>' + MONTHS[d.getMonth()] + '</span><b>' + d.getDate() + '</b></div>' +
99        '<div class="info"><b>Live Zoom session · ' + longDate(d) + '</b><span>' + ((c.time || C.hybridTime) ? esc(c.time || C.hybridTime) + ' · ' : '') + 'Finish the self-paced modules before this date</span></div>' + pill + reg + '</div>';
100    }).join('');
101  });
102  document.querySelectorAll('[data-hybrid-dates]').forEach(function (el) {
103    el.textContent = hybrid.length ? hybrid.slice(0, 3).map(function (c) { var d = parse(c.date); return DAYS[d.getDay()].slice(0, 3) + ', ' + ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][d.getMonth()] + ' ' + d.getDate(); }).join(' · ') : 'Dates coming soon';
104  });
105
106  // Next class pill (home hero)
107  var all = upcoming.map(function (c) { return { d: parse(c.date), label: c.type === 'weeknight' ? '6-hour weeknight class' : '6-hour Saturday class' }; })
108    .concat(hybrid.map(function (c) { return { d: parse(c.date), label: 'Self-paced + Zoom session' }; }))
109    .sort(function (a, b) { return a.d - b.d; });
110  document.querySelectorAll('[data-next-class]').forEach(function (el) {
111    if (!all.length) return;
112    var n = all[0], days = Math.round((n.d - today) / 864e5);
113    var when = days === 0 ? 'today' : days === 1 ? 'tomorrow' : 'in ' + days + ' days';
114    el.lastElementChild.innerHTML = '<b>Next class ' + when + '</b> · ' + esc(n.label) + ', ' + ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][n.d.getMonth()] + ' ' + n.d.getDate();
115  });
116  document.querySelectorAll('[data-count-classes]').forEach(function (el) { el.setAttribute('data-count', all.length); el.textContent = all.length; });
117
118  // Motion: count-up numbers and reveal on scroll (skipped if the visitor prefers reduced motion)
119  var still = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
120  if (!still && 'IntersectionObserver' in window) {
121    document.documentElement.classList.add('motion');
122    var io = new IntersectionObserver(function (entries) {
123      entries.forEach(function (e) {
124        if (!e.isIntersecting) return;
125        io.unobserve(e.target);
126        e.target.classList.add('in');
127        if (e.target.hasAttribute('data-count')) {
128          var el = e.target, to = +el.getAttribute('data-count'), pre = el.getAttribute('data-prefix') || '', suf = el.getAttribute('data-suffix') || '', t0 = null;
129          var step = function (t) { if (!t0) t0 = t; var p = Math.min(1, (t - t0) / 1200); el.textContent = pre + Math.round(to * (1 - Math.pow(1 - p, 3))) + suf; if (p < 1) requestAnimationFrame(step); };
130          requestAnimationFrame(step);
131        }
132      });
133    }, { threshold: 0.15 });
134    document.querySelectorAll('.reveal, [data-count]').forEach(function (el, i) {
135      if (el.classList.contains('reveal')) el.style.transitionDelay = ((i % 4) * 80) + 'ms';
136      io.observe(el);
137    });
138  }
139
140  // Resource lists
141  function list(id, items) {
142    var ul = document.getElementById(id);
143    if (!ul) return;
144    ul.innerHTML = (items || []).map(function (it) {
145      return it.url
146        ? '<li><a href="' + esc(it.url) + '" target="_blank" rel="noopener"><span>' + esc(it.name) + '</span><small>Open ↗</small></a></li>'
147        : '<li><span class="item"><span>' + esc(it.name) + '</span><small>Link coming soon</small></span></li>';
148    }).join('');
149  }
150  list('materials', C.materials);
151  list('videos', C.videos);
152})();

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.