/*! Fiesta 1.1.0 | MPL-2.0 | https://fiesta.rastrillo.org */ (function () { "use strict"; var COMBINING = /[\u0300-\u036f]/g; var LETTERISH = /[a-z\u00c0-\u024f\u0370-\u03ff\u0400-\u04ff\u0530-\u058f\u0590-\u05ff\u0600-\u06ff\u0900-\u097f\u0980-\u09ff\u0b80-\u0bff\u0e00-\u0e7f]/; var BREAKS = /[\s,;\u060c\u061b\uff0c\u3001\u3002]/; function pad(n) { return (n < 10 ? "0" : "") + n; } function midnight(d) { return new Date(d.getFullYear(), d.getMonth(), d.getDate()); } function shiftDays(d, n) { var x = midnight(d); x.setDate(x.getDate() + n); return x; } function comingWeekday(now, target) { var delta = (target - now.getDay() + 7) % 7; return shiftDays(now, delta === 0 ? 7 : delta); } function lastWeekday(now, target) { var delta = (now.getDay() - target + 7) % 7; return shiftDays(now, -(delta === 0 ? 7 : delta)); } function inferYear(now, mo, d) { var y = now.getFullYear(); return new Date(y, mo, d) < midnight(now) ? y + 1 : y; } function makeDate(y, mo, d, h, mi) { if (y < 1000) return null; if (mo < 0 || mo > 11 || d < 1 || d > 31) return null; if (h < 0 || h > 23 || mi < 0 || mi > 59) return null; var dt = new Date(y, mo, d, h, mi, 0, 0); if (dt.getMonth() !== mo || dt.getDate() !== d) return null; return dt; } var NUMBERING = ["arab", "arabext", "beng", "deva"]; function foldDigits(nf, map) { var i, parts, k, digit, add = {}, differs = false; for (i = 0; i <= 9; i++) { parts = nf.formatToParts(i); digit = ""; for (k = 0; k < parts.length; k++) if (parts[k].type === "integer") digit = parts[k].value; if (digit.length !== 1) return false; if (add[digit] !== undefined) return false; if (map[digit] !== undefined && map[digit] !== String(i)) return false; if (digit !== String(i)) differs = true; add[digit] = String(i); } for (digit in add) if (Object.prototype.hasOwnProperty.call(add, digit)) map[digit] = add[digit]; return differs; } function digitFolder(locale) { var map = {}, differs = false, base, root, i; try { base = new Intl.NumberFormat(locale, { useGrouping: false }); } catch (e) { return null; } if (foldDigits(base, map)) differs = true; try { root = base.resolvedOptions().locale.split("-u-")[0]; } catch (e) { root = ""; } for (i = 0; root && i < NUMBERING.length; i++) { try { if (foldDigits(new Intl.NumberFormat(root + "-u-nu-" + NUMBERING[i], { useGrouping: false }), map)) differs = true; } catch (e) { } } if (!differs) return null; return function (s) { var out = "", j; for (j = 0; j < s.length; j++) out += map[s.charAt(j)] !== undefined ? map[s.charAt(j)] : s.charAt(j); return out; }; } function makeFold(digits) { return function (s) { var out = String(s === null || s === undefined ? "" : s); if (out.normalize) out = out.normalize("NFD").replace(COMBINING, ""); out = out.toLowerCase(); if (digits) out = digits(out); return out.replace(/\s+/g, " ").trim(); }; } function nameTable(locale, type, dates, fold) { var widths = ["long", "short"], out = [], i, c, w, opts, fmt, parts, k, value; var contexts = type === "month" ? [null, "numeric"] : [null]; for (i = 0; i < dates.length; i++) out.push([]); for (c = 0; c < contexts.length; c++) for (w = 0; w < widths.length; w++) { opts = { timeZone: "UTC" }; opts[type] = widths[w]; if (contexts[c]) opts.day = contexts[c]; try { fmt = new Intl.DateTimeFormat(locale, opts); } catch (e) { continue; } for (i = 0; i < dates.length; i++) { parts = fmt.formatToParts(dates[i]); value = ""; for (k = 0; k < parts.length; k++) if (parts[k].type === type) value = parts[k].value; value = fold(value); if (!value) continue; if (!/[^\d\s.,\/\-]/.test(value)) continue; if (out[i].indexOf(value) === -1) out[i].push(value); if (value.charAt(value.length - 1) === "." && value.length > 1) { value = value.slice(0, -1); if (out[i].indexOf(value) === -1) out[i].push(value); } } } return out; } function displayOptions(kind) { var opts = kind === "time" ? { hour: "numeric", minute: "2-digit" } : { weekday: "short", day: "numeric", month: "short", year: "numeric" }; if (kind === "datetime") { opts.hour = "numeric"; opts.minute = "2-digit"; } return opts; } function displayTokens(locale, fold) { var probes = [displayOptions("date"), displayOptions("datetime"), displayOptions("time"), { day: "numeric", month: "long" }, { day: "numeric", month: "short" }]; var when = [new Date(Date.UTC(2026, 11, 25, 15, 30)), new Date(Date.UTC(2026, 2, 3, 9, 5))]; var out = { literal: {}, am: {}, pm: {} }, p, i, fmt, parts, k, v, c, run; function keep(bag, s) { s = fold(s); if (s) bag[s] = true; } for (p = 0; p < probes.length; p++) { probes[p].timeZone = "UTC"; try { fmt = new Intl.DateTimeFormat(locale, probes[p]); } catch (e) { continue; } for (i = 0; i < when.length; i++) { parts = fmt.formatToParts(when[i]); for (k = 0; k < parts.length; k++) { v = parts[k].value; if (parts[k].type === "dayPeriod") { keep(when[i].getUTCHours() < 12 ? out.am : out.pm, v); continue; } if (parts[k].type !== "literal") continue; for (c = 0, run = ""; c <= v.length; c++) { if (c < v.length && letterish(v.charAt(c))) { run += v.charAt(c); continue; } keep(out.literal, run); run = ""; if (c < v.length && !BREAKS.test(v.charAt(c))) keep(out.literal, v.charAt(c)); } } } } return out; } var TABLES = {}; function tables(locale) { var key = locale || ""; if (TABLES[key]) return TABLES[key]; var loc = locale || undefined; var fold = makeFold(digitFolder(loc)); var monthDates = [], weekDates = [], i; for (i = 0; i < 12; i++) monthDates.push(new Date(Date.UTC(2026, i, 15))); for (i = 0; i < 7; i++) weekDates.push(new Date(Date.UTC(2026, 1, 1 + i))); var t = { locale: loc, fold: fold, months: nameTable(loc, "month", monthDates, fold), weekdays: nameTable(loc, "weekday", weekDates, fold), display: displayTokens(loc, fold) }; t.namedMonths = false; for (i = 0; i < t.months.length; i++) if (t.months[i].length) t.namedMonths = true; TABLES[key] = t; return t; } function index(vocab, tbl) { var list = [], name, alts, i, m, k; for (name in vocab) { if (!Object.prototype.hasOwnProperty.call(vocab, name)) continue; alts = String(vocab[name]).split("|"); for (i = 0; i < alts.length; i++) { var text = tbl.fold(alts[i]); if (text) list.push({ text: text, role: "word", kind: name, at: -1 }); } } var seen = ["literal", "am", "pm"], kind; for (i = 0; i < seen.length; i++) { kind = seen[i] === "literal" ? "ignore" : seen[i]; for (name in tbl.display[seen[i]]) if (Object.prototype.hasOwnProperty.call(tbl.display[seen[i]], name)) list.push({ text: name, role: "word", kind: kind, at: -1 }); } for (m = 0; m < tbl.months.length; m++) for (k = 0; k < tbl.months[m].length; k++) list.push({ text: tbl.months[m][k], role: "month", kind: "", at: m }); for (m = 0; m < tbl.weekdays.length; m++) for (k = 0; k < tbl.weekdays[m].length; k++) list.push({ text: tbl.weekdays[m][k], role: "weekday", kind: "", at: m }); list.sort(function (a, b) { return b.text.length - a.text.length; }); return list; } function indexFor(vocab, tbl) { var key = "", name; for (name in vocab) if (Object.prototype.hasOwnProperty.call(vocab, name)) key += name + "=" + vocab[name] + "\u0000"; if (tbl.indexKey !== key) { tbl.index = index(vocab, tbl); tbl.indexKey = key; } return tbl.index; } function letterish(c) { return !!c && !BREAKS.test(c) && LETTERISH.test(c); } function boundaryOK(s, from, to) { if (from > 0 && letterish(s.charAt(from - 1)) && letterish(s.charAt(from))) return false; if (to < s.length && letterish(s.charAt(to)) && letterish(s.charAt(to - 1))) return false; return true; } function matchAt(s, pos, idx) { var best = -1, tok = null, i, e; for (i = 0; i < idx.length; i++) { e = idx[i]; if (best >= 0 && e.text.length < best) break; if (s.slice(pos, pos + e.text.length) !== e.text) continue; if (!boundaryOK(s, pos, pos + e.text.length)) continue; if (best < 0) { best = e.text.length; tok = { t: "word", kinds: {}, month: -1, weekday: -1 }; } if (e.role === "word") tok.kinds[e.kind] = true; else if (e.role === "month") tok.month = e.at; else tok.weekday = e.at; } return tok ? { tok: tok, len: best } : null; } function scan(s, idx) { var toks = [], pos = 0, n = s.length, m, hit, start, glued = false; function push(tok, from, to) { tok.glued = glued; tok.from = from; tok.to = to; toks.push(tok); glued = true; } while (pos < n) { if (BREAKS.test(s.charAt(pos))) { pos++; glued = false; continue; } m = /^(\d{1,2}):(\d{2})/.exec(s.slice(pos)); if (m) { push({ t: "clock", h: +m[1], mi: +m[2] }, pos, pos + m[0].length); pos += m[0].length; continue; } m = /^(\d{1,2})[\/.\-](\d{1,2})[\/.\-](\d{4})(?!\d)/.exec(s.slice(pos)); if (m) { push({ t: "ndate", d: +m[1], mo: +m[2], y: +m[3] }, pos, pos + m[0].length); pos += m[0].length; continue; } hit = matchAt(s, pos, idx); if (hit) { push(hit.tok, pos, pos + hit.len); pos += hit.len; continue; } m = /^\d+/.exec(s.slice(pos)); if (m) { push({ t: "num", n: +m[0], digits: m[0].length }, pos, pos + m[0].length); pos += m[0].length; continue; } start = pos; while (pos < n && !BREAKS.test(s.charAt(pos)) && !/\d/.test(s.charAt(pos))) { if (pos > start && matchAt(s, pos, idx)) break; pos++; } if (pos === start) pos++; push({ t: "other", raw: s.slice(start, pos) }, start, pos); } return toks; } function parse(text, vocab, tbl, now, ctx) { ctx = ctx || {}; var raw = tbl.fold(text); if (!raw) return null; var idx = indexFor(vocab, tbl); var prev = ctx.prev || null; var base = ctx.base || null; var anchor = midnight(base || prev || now); var own = midnight(prev || base || now); var clockH = prev ? prev.getHours() : 9; var clockM = prev ? prev.getMinutes() : 0; var m, d; m = /^(\d{4})-(\d{1,2})-(\d{1,2})(?:[t ](\d{1,2}):(\d{2})(?::\d{2})?)?$/.exec(raw); if (m) { d = makeDate(+m[1], +m[2] - 1, +m[3], m[4] === undefined ? clockH : +m[4], m[4] === undefined ? clockM : +m[5]); return d ? { date: d, hasDate: true, hasTime: m[4] !== undefined } : null; } m = /^(\d{4})[\/.](\d{1,2})[\/.](\d{1,2})$/.exec(raw); if (m) { d = makeDate(+m[1], +m[2] - 1, +m[3], clockH, clockM); return d ? { date: d, hasDate: true, hasTime: false } : null; } function dmy(day, mon, year) { if (mon > 12 && day <= 12) { var swap = day; day = mon; mon = swap; } return makeDate(year === undefined ? inferYear(now, mon - 1, day) : year < 100 ? 2000 + year : year, mon - 1, day, clockH, clockM); } m = /^(\d{1,2})[\/.\-](\d{1,2})(?:[\/.\-](\d{2}|\d{4}))?$/.exec(raw); if (m) { d = dmy(+m[1], +m[2], m[3] === undefined ? undefined : +m[3]); return d ? { date: d, hasDate: true, hasTime: false } : null; } var toks = scan(raw, idx), i; if (!toks.length) return null; for (i = 0; i < toks.length; i++) if (toks[i].t === "other") return null; var used = []; function find(kind) { for (var k = 0; k < toks.length; k++) if (!used[k] && toks[k].t === "word" && toks[k].kinds[kind]) return k; return -1; } function findRole(role) { for (var k = 0; k < toks.length; k++) { if (used[k] || toks[k].t !== "word" || toks[k][role] < 0) continue; if (role === "weekday" && k > 0 && toks[k].glued && isCounter(k, k - 1)) continue; return k; } return -1; } function findKind(t) { for (var k = 0; k < toks.length; k++) if (!used[k] && toks[k].t === t) return k; return -1; } function after(k) { return k + 1 < toks.length && !used[k + 1] ? k + 1 : -1; } function before(k) { return k > 0 && !used[k - 1] ? k - 1 : -1; } function unit() { var names = ["minute", "hour", "day", "week", "month"], k, at; for (k = 0; k < names.length; k++) { at = find(names[k]); if (at >= 0) return { name: names[k], at: at }; } return null; } function isCounter(k, p) { if (toks[p].t === "num") return true; if (toks[p].t !== "word") return false; if (!toks[p].kinds.next && !toks[p].kinds.last) return false; return !!(toks[k].kinds.day || toks[k].kinds.week || toks[k].kinds.month); } function isWord(k, kind) { return k >= 0 && toks[k].t === "word" && !!toks[k].kinds[kind]; } function claimCounter(at, kind) { var nx = at >= 0 ? after(at) : -1; if (nx < 0 || toks[nx].t !== "word" || !toks[nx].glued) return false; if (!toks[nx].kinds[kind]) return false; used[nx] = true; return true; } function counterMonth() { if (tbl.namedMonths) return -1; for (var k = 0; k < toks.length; k++) { if (used[k] || toks[k].t !== "num" || toks[k].digits > 2) continue; var nx = after(k); if (nx >= 0 && toks[nx].t === "word" && toks[nx].glued && toks[nx].kinds.month) return k; } return -1; } var date = null, hasDate = false, hasTime = false, h = 0, mi = 0; var quantified = findKind("num") >= 0; var into = find("in"), back = find("ago"); if (into >= 0 && back >= 0 && into !== back) return null; if (quantified && (into >= 0 || back >= 0)) { var u = unit(); if (u) { var qi = findKind("num"); var qty = toks[qi].n, sign = back >= 0 ? -1 : 1; used[qi] = true; used[u.at] = true; if (into >= 0) used[into] = true; if (back >= 0) used[back] = true; if (u.name === "minute" || u.name === "hour") { date = new Date(now.getTime() + sign * qty * (u.name === "hour" ? 3600000 : 60000)); hasDate = true; hasTime = true; h = date.getHours(); mi = date.getMinutes(); } else { date = midnight(now); if (u.name === "day") date.setDate(date.getDate() + sign * qty); else if (u.name === "week") date.setDate(date.getDate() + sign * qty * 7); else date.setMonth(date.getMonth() + sign * qty); hasDate = true; } } } if (!hasTime) { var noon = find("noon"), mid = find("midnight"); if (noon >= 0) { h = 12; mi = 0; hasTime = true; used[noon] = true; } else if (mid >= 0) { h = 0; mi = 0; hasTime = true; used[mid] = true; } else { var ci = findKind("clock"); if (ci >= 0) { h = toks[ci].h; mi = toks[ci].mi; used[ci] = true; hasTime = true; } else { for (i = 0; i < toks.length && !hasTime; i++) { if (used[i] || toks[i].t !== "num") continue; var nx = after(i), pv = before(i); if (isWord(nx, "am") || isWord(nx, "pm") || isWord(pv, "am") || isWord(pv, "pm")) { h = toks[i].n; mi = 0; used[i] = true; hasTime = true; if (isWord(nx, "hour")) { used[nx] = true; var mnx = after(nx); if (mnx >= 0 && toks[mnx].t === "num" && (after(mnx) < 0 || isWord(after(mnx), "minute"))) { mi = toks[mnx].n; used[mnx] = true; if (after(mnx) >= 0) used[after(mnx)] = true; } } } else if (isWord(nx, "hour")) { h = toks[i].n; mi = 0; used[i] = true; used[nx] = true; hasTime = true; var mn = after(nx); if (mn >= 0 && toks[mn].t === "num" && (after(mn) < 0 || isWord(after(mn), "minute"))) { mi = toks[mn].n; used[mn] = true; if (after(mn) >= 0) used[after(mn)] = true; } } else if (isWord(pv, "at") || isWord(nx, "at")) { h = toks[i].n; mi = 0; used[i] = true; used[isWord(pv, "at") ? pv : nx] = true; hasTime = true; } } } if (hasTime) { var pm = find("pm"), am = find("am"); if (pm >= 0 || am >= 0) { if (h < 0 || h > 12) return null; if (pm >= 0) { used[pm] = true; h = (h % 12) + 12; } else { used[am] = true; h = h % 12; } } } } if (hasTime && (h > 23 || mi > 59)) return null; } var nd = findKind("ndate"); if (!hasDate && nd >= 0) { used[nd] = true; if (findRole("weekday") >= 0) used[findRole("weekday")] = true; date = dmy(toks[nd].d, toks[nd].mo, toks[nd].y); if (!date) return null; hasDate = true; } if (!hasDate) { var tod = find("today"), tom = find("tomorrow"), yes = find("yesterday"); if (tom >= 0) { date = shiftDays(now, 1); used[tom] = true; hasDate = true; } else if (yes >= 0) { date = shiftDays(now, -1); used[yes] = true; hasDate = true; } else if (tod >= 0) { date = midnight(now); used[tod] = true; hasDate = true; } } if (!hasDate) { var next = find("next"), last = find("last"), dup; var cm = counterMonth(); var wi = findRole("weekday"); var mo = findRole("month"); if (wi >= 0 && mo >= 0) { if (wi === mo) { for (dup = wi + 1; dup < toks.length; dup++) if (!used[dup] && toks[dup].t === "word" && toks[dup].month >= 0) { mo = dup; break; } if (mo !== wi) { used[wi] = true; wi = -1; } else if (findKind("num") >= 0) wi = -1; else mo = -1; } else { used[wi] = true; wi = -1; } } else if (wi >= 0 && cm >= 0) { used[wi] = true; wi = -1; } if (wi >= 0) { used[wi] = true; if (last >= 0) { used[last] = true; date = lastWeekday(now, toks[wi].weekday); } else { if (next >= 0) used[next] = true; date = comingWeekday(now, toks[wi].weekday); } hasDate = true; } else { if (mo >= 0) { used[mo] = true; var dnum = -1, ynum = -1, dat = -1, k; for (k = 0; k < toks.length; k++) { if (used[k] || toks[k].t !== "num") continue; if (toks[k].digits === 4) { if (ynum >= 0) break; ynum = toks[k].n; used[k] = true; } else if (dnum < 0) { dnum = toks[k].n; dat = k; used[k] = true; } else if (ynum < 0) { ynum = toks[k].n < 100 ? 2000 + toks[k].n : toks[k].n; used[k] = true; } else break; } if (dnum < 0) return null; claimCounter(dat, "day"); date = makeDate(ynum >= 0 ? ynum : inferYear(now, toks[mo].month, dnum), toks[mo].month, dnum, 0, 0); if (!date) return null; hasDate = true; } else if (cm >= 0) { var cmon = toks[cm].n - 1, cday = -1, cyear = -1, ck; used[cm] = true; used[cm + 1] = true; ck = after(cm + 1); if (ck >= 0 && toks[ck].t === "num" && toks[ck].digits <= 2) { cday = toks[ck].n; used[ck] = true; claimCounter(ck, "day"); } if (cday < 0) return null; for (ck = 0; ck < toks.length && cyear < 0; ck++) { if (used[ck] || toks[ck].t !== "num" || toks[ck].digits !== 4) continue; cyear = toks[ck].n; used[ck] = true; } date = makeDate(cyear >= 0 ? cyear : inferYear(now, cmon, cday), cmon, cday, 0, 0); if (!date) return null; hasDate = true; } else if (next >= 0 || last >= 0) { var u2 = unit(); if (u2 && (u2.name === "day" || u2.name === "week" || u2.name === "month")) { used[u2.at] = true; var dir = last >= 0 ? -1 : 1; used[last >= 0 ? last : next] = true; date = midnight(now); if (u2.name === "day") date.setDate(date.getDate() + dir); else if (u2.name === "week") date.setDate(date.getDate() + 7 * dir); else date.setMonth(date.getMonth() + dir); hasDate = true; } } } } if (!hasDate) { var only = -1, count = 0; for (i = 0; i < toks.length; i++) { if (used[i] || toks[i].t !== "num") continue; count++; if (toks[i].digits === 4) only = i; } if (count === 1 && only >= 0 && toks[only].n >= 1000) { used[only] = true; date = new Date(own.getFullYear(), own.getMonth(), own.getDate()); date.setFullYear(toks[only].n); hasDate = true; } } for (i = 0; i < toks.length; i++) { if (used[i]) continue; if (toks[i].t !== "word") return null; if (toks[i].month >= 0 || toks[i].weekday >= 0) return null; for (var kind in toks[i].kinds) if (kind !== "at" && kind !== "ignore") return null; } if (!hasDate && !hasTime) return null; var on = hasDate ? date : anchor; var out = makeDate(on.getFullYear(), on.getMonth(), on.getDate(), hasTime ? h : clockH, hasTime ? mi : clockM); return out ? { date: out, hasDate: hasDate, hasTime: hasTime } : null; } var MAX_COMPLETIONS = 6; function completionsFor(run, idx) { var out = [], seen = {}, i, e; if (!run) return out; for (i = 0; i < idx.length; i++) { e = idx[i]; if (e.role === "word" && (e.kind === "ignore" || e.kind === "at")) continue; if (e.text.length <= run.length) continue; if (e.text.slice(0, run.length) !== run) continue; if (seen[e.text]) continue; seen[e.text] = true; out.push(e.text); } return out; } function parseAll(text, vocab, tbl, now, ctx) { var exact = parse(text, vocab, tbl, now, ctx); if (exact) return [exact]; var raw = tbl.fold(text); if (!raw) return []; var idx = indexFor(vocab, tbl); var toks = scan(raw, idx), at = -1, i; for (i = 0; i < toks.length; i++) { if (toks[i].t !== "other") continue; if (at >= 0) return []; at = i; } if (at < 0) return []; var names = completionsFor(toks[at].raw, idx), out = [], seen = {}, read, key; for (i = 0; i < names.length; i++) { read = parse(raw.slice(0, toks[at].from) + " " + names[i] + " " + raw.slice(toks[at].to), vocab, tbl, now, ctx); if (!read) continue; key = String(read.date.getTime()) + (read.hasTime ? "t" : ""); if (seen[key]) continue; seen[key] = true; out.push(read); } out.sort(function (a, b) { return a.date - b.date; }); return out.slice(0, MAX_COMPLETIONS); } var ENGLISH_WORDS = { ago: "ago", am: "am|a.m.", at: "at", day: "day|days", hour: "hour|hours|h", in: "in", last: "last", midnight: "midnight", minute: "minute|minutes|min|mins|m", month: "month|months", next: "next", noon: "noon|midday", pm: "pm|p.m.", today: "today", tomorrow: "tomorrow", week: "week|weeks", yesterday: "yesterday" }; var NO_ATTRIBUTE = { getAttribute: function () { return null; } }; function vocabulary(native) { var words = fiestaWord(native, "date-words", ENGLISH_WORDS); if (typeof words === "string") { try { words = JSON.parse(words); } catch (e) { words = fiestaWord(NO_ATTRIBUTE, "date-words", ENGLISH_WORDS); } } return words || ENGLISH_WORDS; } if (typeof document !== "undefined") { var resetsPending = new WeakMap(), resetsAccepted = new WeakMap(); function tally(map, form) { return (form && map.get(form)) || 0; } function count(map, form, by) { if (form) map.set(form, tally(map, form) + by); } document.addEventListener("reset", function (e) { var form = e.target; count(resetsPending, form, 1); setTimeout(function () { count(resetsPending, form, -1); if (!e.defaultPrevented) count(resetsAccepted, form, 1); }, 0); }); var CAL_SVG = ''; var CLOCK_SVG = ''; var seq = 0; function toWire(kind, d) { var day = d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()); var clock = pad(d.getHours()) + ":" + pad(d.getMinutes()); if (kind === "date") return day; if (kind === "time") return clock; return day + "T" + clock; } function fromWire(kind, value, now) { var s = String(value || ""), m; if (kind === "time") { m = /^(\d{2}):(\d{2})/.exec(s); return m ? new Date(now.getFullYear(), now.getMonth(), now.getDate(), +m[1], +m[2]) : null; } m = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2}))?/.exec(s); if (!m) return null; return new Date(+m[1], +m[2] - 1, +m[3], m[4] ? +m[4] : 0, m[5] ? +m[5] : 0); } function formatter(locale, kind) { try { return new Intl.DateTimeFormat(locale, displayOptions(kind)); } catch (e) { return null; } } function canPick(el) { return !!el.showPicker; } function enhance(native) { if (native.dataset.rstEnhanced) return null; native.dataset.rstEnhanced = "true"; var kind = native.type === "time" ? "time" : (native.type === "date" ? "date" : "datetime"); var locale = document.documentElement.getAttribute("lang") || undefined; var tbl = tables(locale); var fmt = formatter(locale, kind); var vocab = vocabulary(native); var id = native.id || "rst-dtp-" + ++seq; var listId = id + "-listbox"; var setLabel = fiestaWord(native, "date-set", "Set"); var hintText = fiestaWord(native, "date-hint", ""); var pickLabel = fiestaWord(native, "date-pick", "Open the calendar"); var clockLabel = fiestaWord(native, "date-pick-time", "Open the clock"); var calLabel = fiestaWord(native, "date-calendar", "Calendar"); var prevLabel = fiestaWord(native, "date-prev-month", "Previous month"); var nextLabel = fiestaWord(native, "date-next-month", "Next month"); var manyFmt = fiestaWord(native, "date-results", "{n} suggestions"); var oneFmt = fiestaWord(native, "date-result-one", "1 suggestion"); var quick = { today: fiestaWord(native, "date-quick-today", "Today"), tomorrow: fiestaWord(native, "date-quick-tomorrow", "Tomorrow"), nextWeek: fiestaWord(native, "date-quick-next-week", "In a week"), plus1h: fiestaWord(native, "date-quick-plus-1h", "An hour later"), plus2h: fiestaWord(native, "date-quick-plus-2h", "Two hours later"), endOfDay: fiestaWord(native, "date-quick-end-of-day", "End of that day"), nextDay: fiestaWord(native, "date-quick-next-day", "Same time next day") }; var wrap = document.createElement("div"); wrap.setAttribute("rst-dtp", ""); var input = document.createElement("input"); input.type = "text"; input.setAttribute("rst-input", ""); input.setAttribute("rst-dtp-input", ""); input.id = id + "-combo"; input.autocomplete = "off"; input.spellcheck = false; input.setAttribute("role", "combobox"); input.setAttribute("aria-expanded", "false"); input.setAttribute("aria-controls", listId); input.setAttribute("aria-autocomplete", "list"); var list = document.createElement("ul"); list.setAttribute("rst-dtp-list", ""); list.id = listId; list.hidden = true; list.setAttribute("role", "listbox"); var status = document.createElement("p"); status.className = "rst-sr-only"; status.setAttribute("role", "status"); status.setAttribute("aria-live", "polite"); native.parentNode.insertBefore(wrap, native); wrap.appendChild(input); wrap.appendChild(list); wrap.appendChild(status); wrap.appendChild(native); var label = native.id ? document.querySelector('label[for="' + native.id + '"]') : null; if (label) label.setAttribute("for", input.id); else if (native.getAttribute("aria-label")) input.setAttribute("aria-label", native.getAttribute("aria-label")); if (native.getAttribute("aria-describedby")) input.setAttribute("aria-describedby", native.getAttribute("aria-describedby")); native.removeAttribute("rst-input"); native.classList.add("rst-sr-only"); native.setAttribute("tabindex", "-1"); native.setAttribute("aria-hidden", "true"); var rows = []; var active = -1; var baseOf = null; var onCommit = null; var isEnd = false; var mine = false; function current() { return fromWire(kind, native.value, new Date()); } function show(d) { return fmt ? fmt.format(d) : toWire(kind, d); } function paint() { var d = current(); input.value = d ? show(d) : ""; } function setActive(i) { active = i; for (var k = 0; k < rows.length; k++) rows[k].li.classList.toggle("is-active", k === i); if (i >= 0) input.setAttribute("aria-activedescendant", rows[i].li.id); else input.removeAttribute("aria-activedescendant"); } function addRow(label, meta, date, hasTime, variant, extra) { var li = document.createElement("li"); li.setAttribute("rst-dtp-row", variant || ""); if (extra) li.setAttribute(extra, ""); li.id = listId + "-" + rows.length; li.setAttribute("role", "option"); li.setAttribute("aria-selected", toWire(kind, date) === native.value ? "true" : "false"); var main = document.createElement("span"); main.setAttribute("rst-dtp-label", ""); main.textContent = label; li.appendChild(main); if (meta) { var m = document.createElement("span"); m.setAttribute("rst-dtp-set", ""); m.textContent = meta; li.appendChild(m); } li.addEventListener("mousedown", function (e) { e.preventDefault(); commit(date, hasTime); }); list.appendChild(li); rows.push({ li: li, date: date, hasTime: hasTime }); } function picks(now) { var start = baseOf ? baseOf() : null; var out = [], at; if (isEnd && start && kind !== "date") { return [ { label: quick.plus1h, date: new Date(start.getTime() + 3600000) }, { label: quick.plus2h, date: new Date(start.getTime() + 7200000) }, { label: quick.endOfDay, date: new Date(start.getFullYear(), start.getMonth(), start.getDate(), 23, 59) }, { label: quick.nextDay, date: new Date(start.getTime() + 86400000) } ]; } if (kind === "time") { var from = current() || now; return [ { label: quick.plus1h, date: new Date(from.getTime() + 3600000) }, { label: quick.plus2h, date: new Date(from.getTime() + 7200000) }, { label: quick.endOfDay, date: new Date(from.getFullYear(), from.getMonth(), from.getDate(), 23, 59) } ]; } at = function (d) { return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 9, 0); }; out.push({ label: quick.today, date: at(now) }); out.push({ label: quick.tomorrow, date: at(shiftDays(now, 1)) }); out.push({ label: quick.nextWeek, date: at(shiftDays(now, 7)) }); return out; } function ctx() { return { prev: current(), base: baseOf ? baseOf() : null }; } function rebuild(query) { var now = new Date(); list.textContent = ""; rows = []; var text = String(query || "").trim(); var reads = text ? parseAll(text, vocab, tbl, now, ctx()) : []; var i, skip = {}, wire; for (i = 0; i < reads.length; i++) { addRow(show(reads[i].date), i === 0 ? setLabel : "", reads[i].date, reads[i].hasTime, i === 0 ? "set" : "alt"); skip[toWire(kind, reads[i].date)] = true; } var quickPicks = picks(now); for (i = 0; i < quickPicks.length; i++) { wire = toWire(kind, quickPicks[i].date); if (skip[wire]) continue; addRow(quickPicks[i].label, show(quickPicks[i].date), quickPicks[i].date, kind !== "date", "", "rst-dtp-quick"); } if (hintText) { var hint = document.createElement("li"); hint.setAttribute("rst-dtp-hint", ""); hint.setAttribute("aria-hidden", "true"); hint.textContent = hintText.replace("{example}", show(new Date(now.getFullYear(), now.getMonth(), now.getDate(), 9, 0))); list.appendChild(hint); } status.textContent = rows.length === 1 ? oneFmt : manyFmt.replace("{n}", String(rows.length)); setActive(rows.length && (reads.length || !text) ? 0 : -1); } function bounds() { var when = new Date(); var lo = fromWire(kind, native.getAttribute("min"), when); var hi = fromWire(kind, native.getAttribute("max"), when); var start = baseOf ? baseOf() : null; if (isEnd && start && (!lo || start > lo)) lo = midnight(start); return { min: lo, max: hi }; } function previewOf(text) { var t = String(text || "").trim(); if (!t) return null; var reads = parseAll(t, vocab, tbl, new Date(), ctx()); return reads.length ? reads[0].date : null; } var makeCal = (typeof window !== "undefined" && window.fiestaCalendar) || null; var cal = null; if (makeCal && kind !== "time") { cal = makeCal({ id: id, locale: locale, labels: { calendar: calLabel, prev: prevLabel, next: nextLabel }, value: current, bounds: bounds, onPick: function (day) { var had = current(); commit(new Date(day.getFullYear(), day.getMonth(), day.getDate(), had ? had.getHours() : 9, had ? had.getMinutes() : 0), kind !== "date"); }, onDismiss: function () { closeAll(); input.focus(); } }); cal.el.id = id + "-cal"; wrap.appendChild(cal.el); } var pick = null; var ownsPopup = !!cal || kind === "time"; function pickExpanded(open) { if (pick && ownsPopup) pick.setAttribute("aria-expanded", open ? "true" : "false"); } if (cal || kind === "time" || canPick(native)) { pick = document.createElement("button"); pick.type = "button"; pick.setAttribute("rst-dtp-pick", ""); pick.setAttribute("aria-label", kind === "time" ? clockLabel : pickLabel); if (ownsPopup) { pick.setAttribute("aria-expanded", "false"); pick.setAttribute("aria-haspopup", cal ? "dialog" : "listbox"); } pick.innerHTML = kind === "time" ? CLOCK_SVG : CAL_SVG; wrap.insertBefore(pick, list); } var mode = ""; function closeAll() { mode = ""; list.hidden = true; active = -1; input.setAttribute("aria-expanded", "false"); input.setAttribute("aria-controls", listId); input.removeAttribute("aria-activedescendant"); if (cal) cal.close(); pickExpanded(false); wrap.classList.remove("is-above"); } function place() { var panel = mode === "cal" && cal ? cal.el : list; wrap.classList.remove("is-above"); if (!panel || panel.hidden) return; var box = wrap.getBoundingClientRect(); var need = panel.getBoundingClientRect().height; if (need > window.innerHeight - box.bottom && box.top > need) { wrap.classList.add("is-above"); } } function openList() { if (locked()) return; if (cal) cal.close(); mode = "list"; list.hidden = false; input.setAttribute("aria-expanded", "true"); input.setAttribute("aria-controls", listId); pickExpanded(false); rebuild(input.value); place(); } function openCal() { if (locked()) return; list.hidden = true; active = -1; input.removeAttribute("aria-activedescendant"); mode = "cal"; cal.open(previewOf(input.value)); input.setAttribute("aria-expanded", "true"); input.setAttribute("aria-controls", cal.el.id); pickExpanded(true); place(); } function openSlots() { if (locked()) return; var base = current() || new Date(); var day = midnight(base); var want = base.getHours() * 60 + base.getMinutes(); var i, at, best = -1, near = 0, gap; mode = "slots"; list.hidden = false; list.textContent = ""; rows = []; for (i = 0; i < 48; i++) { at = new Date(day.getFullYear(), day.getMonth(), day.getDate(), Math.floor(i / 2), (i % 2) * 30); gap = Math.abs(at.getHours() * 60 + at.getMinutes() - want); if (best < 0 || gap < best) { best = gap; near = rows.length; } addRow(show(at), "", at, true, "", "rst-dtp-quick"); } input.setAttribute("aria-expanded", "true"); input.setAttribute("aria-controls", listId); pickExpanded(true); status.textContent = manyFmt.replace("{n}", String(rows.length)); setActive(near); place(); if (rows[near]) { list.scrollTop = rows[near].li.offsetTop - Math.round(list.clientHeight / 2); } } function locked() { return native.readOnly || native.matches(":disabled"); } function follow() { input.disabled = native.disabled; input.readOnly = native.readOnly; if (pick) pick.disabled = native.disabled || native.readOnly; if (locked()) closeAll(); } new MutationObserver(follow).observe(native, { attributes: true, attributeFilter: ["disabled", "readonly"] }); follow(); function commit(date, hasTime) { if (locked()) { paint(); closeAll(); return; } mine = true; native.value = toWire(kind, date); native.dispatchEvent(new Event("change", { bubbles: true })); mine = false; input.value = show(date); closeAll(); if (onCommit) onCommit(date, hasTime); } function commitText() { if (locked()) { paint(); return; } var text = input.value.trim(); if (!text) { input.value = ""; if (native.value) { mine = true; native.value = ""; native.dispatchEvent(new Event("change", { bubbles: true })); mine = false; } return; } var reads = parseAll(text, vocab, tbl, new Date(), ctx()); if (reads.length) commit(reads[0].date, reads[0].hasTime); else paint(); } input.addEventListener("focus", function () { input.select(); if (!mode) openList(); }); input.addEventListener("click", function () { if (!mode) openList(); }); input.addEventListener("input", function () { if (mode === "cal" && cal) return cal.show(previewOf(input.value)); openList(); }); input.addEventListener("keydown", function (e) { switch (e.key) { case "ArrowDown": e.preventDefault(); if (mode === "cal" && cal) { cal.focusGrid(); break; } if (!mode) { openList(); break; } if (rows.length) setActive(Math.min(active + 1, rows.length - 1)); break; case "ArrowUp": e.preventDefault(); if (mode === "cal" && cal) { cal.focusGrid(); break; } if (!mode) { openList(); break; } if (rows.length) setActive(Math.max(active - 1, 0)); break; case "Enter": e.preventDefault(); if (mode !== "cal" && !list.hidden && active >= 0) commit(rows[active].date, rows[active].hasTime); else { commitText(); closeAll(); } break; case "Escape": e.preventDefault(); paint(); closeAll(); break; } }); input.addEventListener("keypress", function (e) { if (e.key === "Enter") e.preventDefault(); }); wrap.addEventListener("focusout", function () { var form = native.form, at = tally(resetsAccepted, form); var settle = function () { if (wrap.contains(document.activeElement)) return; if (tally(resetsPending, form) > 0) { setTimeout(settle, 0); return; } if (tally(resetsAccepted, form) !== at) return; commitText(); closeAll(); }; setTimeout(settle, 0); }); if (pick) { pick.addEventListener("mousedown", function (e) { e.preventDefault(); }); pick.addEventListener("click", function () { if (mode === "cal" || mode === "slots") { closeAll(); input.focus(); return; } input.focus(); if (cal) return openCal(); if (kind === "time") return openSlots(); closeAll(); try { native.showPicker(); } catch (err) { } }); } native.addEventListener("change", function () { if (mine) return; if (mode && document.activeElement === input) return; paint(); }); document.addEventListener("reset", function (e) { if (e.target !== native.form) return; setTimeout(function () { if (e.defaultPrevented) return; closeAll(); paint(); }, 0); }); paint(); return { get value() { return current(); }, commit: commit, follow: function (fn) { baseOf = fn; isEnd = true; }, committed: function (fn) { onCommit = fn; } }; } function wireRange(wrapper, start, end) { end.follow(function () { return start.value; }); if (wrapper.getAttribute("data-rst-range") !== "session") return; start.committed(function (d) { var have = end.value; if (!have || have <= d) end.commit(new Date(d.getTime() + 3600000), true); }); } function scanPage(root) { var armed = root.querySelectorAll("input[data-rst-date], input[data-rst-time]"); var made = [], i, k, wrappers, inside, pair; for (i = 0; i < armed.length; i++) made.push([armed[i], enhance(armed[i])]); wrappers = root.querySelectorAll("[data-rst-range]"); for (i = 0; i < wrappers.length; i++) { inside = wrappers[i].querySelectorAll("input[data-rst-date], input[data-rst-time]"); pair = []; for (k = 0; k < inside.length && pair.length < 2; k++) { for (var j = 0; j < made.length; j++) if (made[j][0] === inside[k] && made[j][1]) pair.push(made[j][1]); } if (pair.length === 2) wireRange(wrappers[i], pair[0], pair[1]); } } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", function () { scanPage(document); }); } else { scanPage(document); } } function fiestaPickLang(tag, registry) { var want = String(tag || "").toLowerCase(); if (!want) return null; var keys = Object.keys(registry || {}), i; for (i = 0; i < keys.length; i++) { if (keys[i].toLowerCase() === want) return keys[i]; } var primary = want.split("-")[0]; for (i = 0; i < keys.length; i++) { if (keys[i].toLowerCase().split("-")[0] === primary) return keys[i]; } return null; } function fiestaWord(el, key, fallback) { var v = el.getAttribute("data-rst-" + key); if (v !== null && v !== "") return v; var registry = (typeof self !== "undefined" && self.fiestaStrings) || {}; var lang = fiestaPickLang(document.documentElement.lang, registry); var table = lang ? registry[lang] : null; return table && table[key] !== undefined && table[key] !== "" ? table[key] : fallback; } if (typeof module !== "undefined" && module && module.exports) { module.exports = { parse: parse, parseAll: parseAll, tables: tables, displayOptions: displayOptions, vocabulary: vocabulary, fiestaPickLang: fiestaPickLang, fiestaWord: fiestaWord }; } })();