Scroll To Top (function () {
var SCOPE = '.upc-events';
var ITEM = '.mn-scroll-item';
var DATE = '.mn-scroll-date';
var MONTHS = { jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun: 5, jul: 6,
aug: 7, sep: 8, oct: 9, nov: 10, dec: 11 };
var LAST = Number.MAX_SAFE_INTEGER; // unparseable dates sink to the bottom
var DAY_MS = 864e5;
var GRACE_DAYS = 1; // a date this recently past still counts as "current year"
var HREF_DATE = /-(\d{2})-(\d{2})-(\d{4})-\d+(?:[\/?#]|$)/; // ...-09-16-2026-5284
var TEXT_DATE = /([A-Za-z]{3})[a-z]*\.?\s+(\d{1,2})(?:[^\d]+(\d{4}))?/; var wired = new WeakSet(); /* --- date resolution ------------------------------------------------- */ // "MMM d" carries no year, so roll forward when the day has already passed.
function inferYear(month, day) {
var now = new Date();
var d = new Date(now.getFullYear(), month, day);
if (d.getTime() < now.getTime() - GRACE_DAYS * DAY_MS) {
d.setFullYear(d.getFullYear() + 1);
}
return d.getTime();
} function getTime(item) {
// Detail URLs usually embed the real MM-DD-YYYY — trust that first.
var link = item.querySelector('a[href]');
var href = link && link.getAttribute('href').match(HREF_DATE);
if (href) return new Date(+href[3], +href[1] - 1, +href[2]).getTime(); var el = item.querySelector(DATE);
var text = (el ? el.textContent : '').match(TEXT_DATE);
if (!text) return LAST; var month = MONTHS[text[1].toLowerCase()];
if (month === undefined) return LAST; return text[3] ? new Date(+text[3], month, +text[2]).getTime()
: inferYear(month, +text[2]);
} /* --- sorting --------------------------------------------------------- */ function sort(scope) {
var groups = new Map(); scope.querySelectorAll(ITEM).forEach(function (item) {
var siblings = groups.get(item.parentNode) || [];
siblings.push(item);
groups.set(item.parentNode, siblings);
}); groups.forEach(function (items, parent) {
var sorted = items
.map(function (el, i) { return { el: el, time: getTime(el), i: i }; })
.sort(function (a, b) { return a.time - b.time || a.i - b.i; }); // Already chronological — leave the DOM alone.
if (sorted.every(function (o, i) { return o.i === i; })) return; var frag = document.createDocumentFragment();
sorted.forEach(function (o) { frag.appendChild(o.el); });
parent.appendChild(frag);
});
} /* --- init ------------------------------------------------------------ */ function watch(scope) {
if (wired.has(scope)) return;
wired.add(scope); var opts = { childList: true, subtree: true };
var queued = false; var observer = new MutationObserver(function () {
if (queued) return;
queued = true;
requestAnimationFrame(function () {
queued = false;
observer.disconnect(); // don't react to our own re-ordering
sort(scope);
observer.observe(scope, opts);
});
}); sort(scope); // in case the widget already rendered
observer.observe(scope, opts);
} function init() {
document.querySelectorAll(SCOPE).forEach(watch);
} if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();