(function () {
var CFG = {"uid":"6a8bee079f46b","title":"Freibad A","categories":[{"key":"16","label":"Bereich A","colorClass":"marker-cat-1"},{"key":"396","label":"Bereich B","colorClass":"marker-cat-2"},{"key":"417","label":"Bereich C","colorClass":"marker-cat-3"}],"eventsBase":"\/\/test.eaero.de\/api\/hoursExplorer\/events\/16,396,417","secret":"8070e66f923d45bbbe5c4480073b6c10","startIso":"2026-08-24","noCSS":false};
var MARKUP = '
';
var CSS = '/* * Öffnungszeiten-Ausgabe "Explorer" (ERP-2026-038) * Optik 1:1 aus doc/BRIEFINGS/BRIEFING-2026-038/objekt-zeitansichten-demo.html. * Alles unter .ea-explorer gescopt, damit weder Styles der Wirtsseite noch * unsere Styles sich gegenseitig beeinflussen. Pro Mandant überschreibbar via * Hours/{clientNo}/hoursExplorer.css (+ .additional.css). */ .ea-explorer { /* Fest heller Modus, damit die Ausgabe auf weißen Webseiten unabhängig vom OS-/Browser-Theme immer hell erscheint (nicht Dark-Mode-adaptiv). */ color-scheme: light; --background: rgb(255 255 255); --foreground: rgb(26 28 31); --card: color-mix(in oklab, var(--foreground) 5%, transparent); --card-foreground: var(--foreground); --popover: rgb(255 255 255); --popover-foreground: var(--foreground); --primary: rgb(51 156 255); --primary-foreground: rgb(255 255 255); --secondary: rgb(255 255 255 / 96%); --secondary-foreground: var(--foreground); --muted: color-mix(in srgb, var(--foreground) 10%, transparent); --muted-foreground: rgb(26 28 31 / 49.4%); --accent: rgb(229 242 255); --accent-foreground: var(--primary); --destructive: rgb(226 85 7); --border: rgb(26 28 31 / 8%); --input: rgb(26 28 31 / 11.8%); --ring: rgb(51 156 255); --viz-series-1: var(--primary); --viz-series-2: rgb(243 136 59); --viz-series-3: rgb(93 201 119); --viz-series-4: rgb(235 119 177); --viz-series-5: rgb(155 121 236); --viz-series-6: rgb(58 185 177); --font-sans: -apple-system, system-ui, "Segoe UI", sans-serif; --font-size-base: 14px; --font-size-normal: max(11px, var(--font-size-base)); --font-size-small: max(11px, calc(var(--font-size-base) - 2px)); --font-size-h2: calc(var(--font-size-normal) * 1.4285714286); --font-size-h3: calc(var(--font-size-normal) * 1.2857142857); --font-weight-normal: 430; --font-weight-medium: 500; --line-height-normal: calc(var(--font-size-normal) * 1.5); --line-height-small: calc(var(--font-size-small) + 4px); --radius: 12.5px; --radius-sm: calc(var(--radius) * 0.6); --radius-lg: var(--radius); --radius-2xl: calc(var(--radius) * 1.6); --radius-full: 9999px; --shadow-sm: 0 1px 2px -1px rgb(0 0 0 / 8%); --checkmark-image: url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 17 17\'%3E%3Cpath d=\'M12.8961 3.64101C13.1297 3.41418 13.4984 3.37523 13.7779 3.56581C14.0571 3.75635 14.1554 4.11331 14.0299 4.41347L13.9615 4.53847L7.71151 13.7045C7.59411 13.8767 7.4063 13.9877 7.19881 14.0072C6.99136 14.0267 6.78564 13.9533 6.63826 13.806L2.88826 10.056L2.79842 9.9457C2.6192 9.67407 2.64927 9.30496 2.88826 9.06581C3.12738 8.82669 3.49647 8.79676 3.76815 8.97597L3.8785 9.06581L7.03084 12.2182L12.8053 3.74941L12.8961 3.64101Z\'/%3E%3C/svg%3E"); color: var(--foreground); font-family: var(--font-sans); font-size: var(--font-size-base); font-weight: var(--font-weight-normal); line-height: var(--line-height-normal); width: 100%; } .ea-explorer *, .ea-explorer *::before, .ea-explorer *::after { box-sizing: border-box; } .ea-explorer h3 { margin: 0; font-size: var(--font-size-h3); font-weight: var(--font-weight-medium); line-height: 1.3; } /* ---- Basis-Komponenten (aus dem Demo, gescopt) ---- */ .ea-explorer .btn { appearance: button; display: inline-flex; inline-size: fit-content; max-inline-size: 100%; min-height: 28px; align-items: center; justify-content: center; gap: 4px; margin: 0; padding: 0 8px; border: 1px solid var(--input); border-radius: var(--radius-lg); color: var(--secondary-foreground); background: var(--secondary); cursor: pointer; font: inherit; text-align: center; text-decoration: none; white-space: nowrap; user-select: none; } .ea-explorer .btn:not(:disabled):hover { background: color-mix(in srgb, var(--foreground) 6%, var(--secondary)); } .ea-explorer .btn:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; } .ea-explorer .btn-ghost { border-color: transparent; color: var(--muted-foreground); background: transparent; } .ea-explorer .btn-ghost:not(:disabled):hover { color: var(--foreground); background: color-mix(in srgb, var(--foreground) 6%, var(--secondary)); } .ea-explorer .btn.viz-tile { inline-size: 100%; min-width: 0; overflow-wrap: anywhere; white-space: normal; } .ea-explorer .btn:not(.btn-primary, .viz-tile):is([aria-pressed="true"], [aria-selected="true"], .is-selected) { border-color: var(--primary); color: var(--primary-foreground); background: var(--primary); } .ea-explorer .btn.viz-tile:is([aria-pressed="true"], [aria-selected="true"], .is-selected) { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); } .ea-explorer .form-label { display: block; margin-bottom: 6px; color: var(--foreground); } .ea-explorer .form-select { appearance: none; display: block; width: 100%; min-height: 28px; margin: 0; padding: 0 32px 0 8px; outline: none; border: 1px solid var(--input); border-radius: var(--radius-lg); color: var(--foreground); background-color: var(--secondary); background-image: linear-gradient(45deg, transparent 50%, var(--muted-foreground) 50%), linear-gradient(135deg, var(--muted-foreground) 50%, transparent 50%); background-position: calc(100% - 14px) 50%, calc(100% - 10px) 50%; background-repeat: no-repeat; background-size: 4px 4px; cursor: pointer; font: inherit; } .ea-explorer .form-select:not(:disabled):hover { background-color: color-mix(in srgb, var(--foreground) 6%, var(--secondary)); } .ea-explorer .form-select:focus-visible { border-color: var(--ring); box-shadow: inset 0 0 0 1px var(--ring); } .ea-explorer small, .ea-explorer .text-small { font-size: var(--font-size-small); line-height: var(--line-height-small); } .ea-explorer .text-muted { color: var(--muted-foreground); } /* ---- Widget-spezifisch (Demo #clickable-calendar-demo → .ea-explorer) ---- */ .ea-explorer .toolbar, .ea-explorer .view-switch, .ea-explorer .period-navigation, .ea-explorer .category-key { align-items: center; display: flex; flex-wrap: wrap; gap: 0.75rem; } .ea-explorer .toolbar { justify-content: space-between; margin-bottom: 1rem; } .ea-explorer .view-switch { gap: 0.5rem; } .ea-explorer .category-field { min-width: min(100%, 16rem); } .ea-explorer .period-navigation { justify-content: center; margin-bottom: 0.75rem; } .ea-explorer .period-copy { min-width: min(100%, 19rem); text-align: center; } .ea-explorer .facility-name { font-weight: 500; margin: 0; } .ea-explorer .period-label { color: var(--muted-foreground); margin-top: 0.25rem; } .ea-explorer .explorer-loading { color: var(--muted-foreground); margin-bottom: 0.5rem; text-align: center; } .ea-explorer .category-key { border-top: 1px solid var(--border); margin-bottom: 0.75rem; padding-top: 0.65rem; } .ea-explorer .category-key[hidden] { display: none; } .ea-explorer .key-item, .ea-explorer .day-markers { align-items: center; display: inline-flex; gap: 0.35rem; } .ea-explorer .marker { border-radius: 50%; display: inline-block; flex: 0 0 auto; height: 0.65rem; width: 0.65rem; } .ea-explorer .marker-cat-1 { background: var(--viz-series-1); } .ea-explorer .marker-cat-2 { background: var(--viz-series-2); } .ea-explorer .marker-cat-3 { background: var(--viz-series-3); } .ea-explorer .marker-cat-4 { background: var(--viz-series-4); } .ea-explorer .marker-cat-5 { background: var(--viz-series-5); } .ea-explorer .marker-cat-6 { background: var(--viz-series-6); } .ea-explorer .marker-cat-7 { background: var(--viz-series-1); } .ea-explorer .marker-cat-8 { background: var(--viz-series-2); } .ea-explorer table { border-collapse: collapse; table-layout: fixed; width: 100%; } .ea-explorer th, .ea-explorer td { border: 1px solid var(--border); padding: 0.65rem; text-align: left; vertical-align: top; } .ea-explorer th { font-weight: 500; } .ea-explorer .week-table th:first-child, .ea-explorer .week-table td:first-child { width: 31%; } .ea-explorer .schedule-group { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.45rem; margin-bottom: 0.65rem; } .ea-explorer .schedule-group:last-child { margin-bottom: 0; } .ea-explorer .schedule-group .marker { margin-top: 0.3rem; } .ea-explorer .category-name, .ea-explorer .day-detail-heading, .ea-explorer .weekday-heading { font-weight: 500; } .ea-explorer .time-list { display: grid; gap: 0.15rem; margin-top: 0.15rem; } .ea-explorer .closed-copy { color: var(--muted-foreground); } .ea-explorer .month-grid { display: grid; gap: 0.35rem; grid-auto-rows: minmax(4.5rem, auto); grid-template-columns: repeat(7, minmax(0, 1fr)); } .ea-explorer .weekday-heading { align-self: center; text-align: center; } .ea-explorer .day-button-content { display: grid; gap: 0.35rem; justify-items: center; } .ea-explorer .day-number, .ea-explorer .closed-label { display: block; } .ea-explorer .day-markers { gap: 0.25rem; justify-content: center; min-height: 0.65rem; } .ea-explorer .outside-month { opacity: 0.58; } .ea-explorer .closed-day { background: color-mix(in srgb, var(--destructive) 9%, transparent); } .ea-explorer .day-detail { border-top: 1px solid var(--border); margin-top: 1rem; padding-top: 0.8rem; } .ea-explorer .day-detail-heading { margin-bottom: 0.75rem; } .ea-explorer .year-grid { display: grid; gap: 1.25rem; grid-template-columns: repeat(3, minmax(0, 1fr)); } .ea-explorer .mini-month { border-top: 1px solid var(--border); min-width: 0; padding-top: 0.5rem; } .ea-explorer .mini-month-header { margin-bottom: 0.35rem; } .ea-explorer .mini-month table th, .ea-explorer .mini-month table td { border: 0; padding: 0.12rem; text-align: center; } .ea-explorer .year-day { align-items: center; display: flex; justify-content: center; min-height: 1.35rem; width: 100%; } .ea-explorer .year-day-open { background: color-mix(in srgb, var(--accent) 55%, transparent); } .ea-explorer .year-day-closed { color: var(--muted-foreground); text-decoration: line-through; } .ea-explorer .empty-cell { color: var(--muted-foreground); } @media (max-width: 650px) { .ea-explorer .year-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (max-width: 520px) { .ea-explorer .toolbar, .ea-explorer .period-navigation { justify-content: center; } .ea-explorer .closed-label { display: none; } .ea-explorer .month-grid { gap: 0.2rem; grid-auto-rows: minmax(3.75rem, auto); } } @media (max-width: 420px) { .ea-explorer .year-grid { grid-template-columns: 1fr; } .ea-explorer th, .ea-explorer td { padding: 0.45rem; } } ';
// ---- CSS einmal pro Seite injizieren (klassenbasiert, mehrere Instanzen teilen es) ----
if (!CFG.noCSS && CSS && !document.getElementById('ea-explorer-style')) {
var st = document.createElement('style');
st.id = 'ea-explorer-style';
st.type = 'text/css';
st.appendChild(document.createTextNode(CSS));
(document.head || document.getElementsByTagName('head')[0] || document.documentElement).appendChild(st);
}
// ---- Markup einhängen: das ausführende Script-Tag durch den Widget-Container ersetzen ----
var container = document.createElement('div');
container.className = 'ea-explorer-mount';
container.innerHTML = MARKUP;
var self = document.currentScript;
if (!self) {
var cand = document.querySelectorAll('script[src*="/api/hoursExplorer/"]');
for (var k = cand.length - 1; k >= 0; k--) {
if (!cand[k].getAttribute('data-ea-done')) { self = cand[k]; break; }
}
}
if (self && self.parentNode) {
self.setAttribute('data-ea-done', '1');
self.parentNode.replaceChild(container, self);
} else {
(document.body || document.documentElement).appendChild(container);
}
var root = container.querySelector('.ea-explorer');
if (root) { initExplorer(root, CFG); }
function initExplorer(root, cfg) {
var content = root.querySelector('.explorer-content');
var periodLabel = root.querySelector('.explorer-period');
var categoryKey = root.querySelector('.explorer-key');
var categorySelect = root.querySelector('.explorer-category');
var facilityName = root.querySelector('.facility-name');
var loading = root.querySelector('.explorer-loading');
var viewButtons = toArray(root.querySelectorAll('[data-view]'));
if (facilityName) { facilityName.textContent = cfg.title || ''; }
// Kategorien aus der Konfiguration (eine ID = eine Kategorie)
var categories = {};
var categoryOrder = [];
(cfg.categories || []).forEach(function (c) {
categories[c.key] = { label: c.label, marker: c.colorClass };
categoryOrder.push(c.key);
});
// Dropdown befüllen: "Alle Bereiche" + je Kategorie
appendOption(categorySelect, 'all', 'Alle Bereiche');
categoryOrder.forEach(function (key) {
appendOption(categorySelect, key, categories[key].label);
});
var weekdaysLong = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'];
var weekdaysShort = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'];
var months = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'];
var startDate = parseIso(cfg.startIso) || todayDate();
var state = {
view: 'week',
category: 'all',
anchor: cloneDate(startDate),
selected: cloneDate(startDate)
};
var loadedDays = {}; // iso -> [ {category, start, end, cls, type, text} ]
var loadedSet = {}; // iso -> true (Tag wurde geladen, ggf. leer = geschlossen)
// ---- Datums-Helfer ----
function makeDate(y, m, d) { return new Date(y, m, d, 12, 0, 0, 0); }
function todayDate() { var n = new Date(); return makeDate(n.getFullYear(), n.getMonth(), n.getDate()); }
function cloneDate(dt) { return makeDate(dt.getFullYear(), dt.getMonth(), dt.getDate()); }
function addDays(dt, a) { var n = cloneDate(dt); n.setDate(n.getDate() + a); return n; }
function pad(n) { return (n < 10 ? '0' : '') + n; }
function iso(dt) { return dt.getFullYear() + '-' + pad(dt.getMonth() + 1) + '-' + pad(dt.getDate()); }
function formatDate(dt) { return pad(dt.getDate()) + '.' + pad(dt.getMonth() + 1) + '.' + dt.getFullYear(); }
function mondayOf(dt) { var day = dt.getDay(); return addDays(dt, day === 0 ? -6 : 1 - day); }
function calendarStart(y, m) { return mondayOf(makeDate(y, m, 1)); }
function parseIso(s) {
if (!s) { return null; }
var p = String(s).split('-');
if (p.length !== 3) { return null; }
return makeDate(+p[0], +p[1] - 1, +p[2]);
}
// ---- Daten ----
function slotsFor(date) { return loadedDays[iso(date)] || []; }
function visibleSlots(date) {
var slots = slotsFor(date);
if (state.category === 'all') { return slots; }
return slots.filter(function (s) { return s.category === state.category; });
}
function visibleCategories(date) {
var slots = visibleSlots(date);
return categoryOrder.filter(function (key) {
return slots.some(function (s) { return s.category === key; });
});
}
function markerHtml(key) {
var c = categories[key];
return '';
}
function categoryLabel() {
if (state.category === 'all') { return 'Alle Bereiche'; }
return categories[state.category] ? categories[state.category].label : '';
}
function scheduleHtml(date) {
var slots = visibleSlots(date);
if (!slots.length) { return 'geschlossen'; }
return categoryOrder.map(function (key) {
var items = slots.filter(function (s) { return s.category === key; });
if (!items.length) { return ''; }
var times = items.map(function (it) {
return '' + esc(it.start) + ' – ' + esc(it.end) + ' Uhr';
}).join('');
return '' + markerHtml(key) +
'
' + esc(categories[key].label) + '
' +
'
' + times + '
';
}).join('');
}
// ---- Nachladen ----
function rangeForView() {
if (state.view === 'week') {
var mon = mondayOf(state.anchor);
return [mon, addDays(mon, 6)];
}
if (state.view === 'month') {
var s = calendarStart(state.anchor.getFullYear(), state.anchor.getMonth());
return [s, addDays(s, 41)];
}
var y = state.anchor.getFullYear();
return [makeDate(y, 0, 1), makeDate(y, 11, 31)];
}
function ensureLoaded(startD, endD, cb) {
var missingStart = null, missingEnd = null, d;
for (d = cloneDate(startD); d <= endD; d = addDays(d, 1)) {
if (!loadedSet[iso(d)]) {
if (!missingStart) { missingStart = cloneDate(d); }
missingEnd = cloneDate(d);
}
}
if (!missingStart) { cb(); return; }
var url = cfg.eventsBase + '/' + iso(missingStart) + '/' + iso(missingEnd) + '/' + cfg.secret;
setLoading(true);
httpGetJson(url, function (data) {
var days = (data && data.days) || {};
for (var key in days) { if (Object.prototype.hasOwnProperty.call(days, key)) { loadedDays[key] = days[key]; } }
markLoaded(missingStart, missingEnd);
setLoading(false);
cb();
}, function () {
// Fehler: als geladen markieren, um Endlosnachladen zu vermeiden (Anzeige = geschlossen)
markLoaded(missingStart, missingEnd);
setLoading(false);
cb();
});
}
function markLoaded(startD, endD) {
for (var d = cloneDate(startD); d <= endD; d = addDays(d, 1)) { loadedSet[iso(d)] = true; }
}
function setLoading(on) { if (loading) { loading.hidden = !on; } }
// ---- Rendering ----
function render() {
viewButtons.forEach(function (b) {
b.setAttribute('aria-pressed', String(b.getAttribute('data-view') === state.view));
});
var r = rangeForView();
ensureLoaded(r[0], r[1], doRender);
}
function doRender() {
renderKey();
if (state.view === 'week') { renderWeek(); }
else if (state.view === 'month') { renderMonth(); }
else if (state.view === 'year') { renderYear(); }
}
function renderKey() {
if (state.view !== 'month' || state.category !== 'all' || !categoryOrder.length) {
categoryKey.hidden = true;
categoryKey.innerHTML = '';
return;
}
categoryKey.hidden = false;
categoryKey.innerHTML = categoryOrder.map(function (key) {
return '' + markerHtml(key) + esc(categories[key].label) + '';
}).join('');
}
function renderWeek() {
var monday = mondayOf(state.anchor);
var sunday = addDays(monday, 6);
state.anchor = monday;
periodLabel.textContent = categoryLabel() + ' · ' + formatDate(monday) + ' – ' + formatDate(sunday);
var rows = '';
for (var i = 0; i < 7; i++) {
var date = addDays(monday, i);
rows += '| ' + weekdaysLong[date.getDay()] + ' ' +
pad(date.getDate()) + '.' + pad(date.getMonth() + 1) + '. | ' +
scheduleHtml(date) + ' |
';
}
content.innerHTML = '';
}
function renderMonth() {
var year = state.anchor.getFullYear();
var month = state.anchor.getMonth();
var start = calendarStart(year, month);
periodLabel.textContent = categoryLabel() + ' · ' + months[month] + ' ' + year;
var headers = weekdaysShort.map(function (d) {
return '' + d + '
';
}).join('');
var days = '';
for (var index = 0; index < 42; index++) {
var date = addDays(start, index);
var keys = visibleCategories(date);
var outside = date.getMonth() !== month ? ' outside-month' : '';
var closed = keys.length ? '' : ' closed-day';
var isSelected = iso(date) === iso(state.selected);
var statusLabel = keys.length
? keys.map(function (k) { return categories[k].label; }).join(', ')
: 'geschlossen';
var availability = keys.length
? '' + keys.map(markerHtml).join('') + ''
: 'geschlossen';
days += '';
}
content.innerHTML = '' + headers + days +
'
' +
esc(weekdaysLong[state.selected.getDay()] + ', ' + formatDate(state.selected)) + '
' +
scheduleHtml(state.selected) + '
';
}
function renderMiniMonth(year, month) {
var start = calendarStart(year, month);
var header = weekdaysShort.map(function (d) {
return '' + d.slice(0, 1) + ' | ';
}).join('');
var rows = '';
for (var week = 0; week < 6; week++) {
var cells = '';
for (var day = 0; day < 7; day++) {
var date = addDays(start, week * 7 + day);
if (date.getMonth() !== month) {
cells += '· | ';
} else {
var isOpen = visibleSlots(date).length > 0;
var statusClass = isOpen ? 'year-day-open' : 'year-day-closed';
var statusLabel = isOpen ? 'geöffnet' : 'geschlossen';
cells += '' + date.getDate() + ' | ';
}
}
rows += '' + cells + '
';
}
return '' +
'' +
header + '
' + rows + '
';
}
function renderYear() {
var year = state.anchor.getFullYear();
periodLabel.textContent = categoryLabel() + ' · ' + year;
var grid = '';
for (var m = 0; m < 12; m++) { grid += renderMiniMonth(year, m); }
content.innerHTML = '' + grid + '
';
}
// ---- Navigation ----
function shiftPeriod(direction) {
if (state.view === 'week') {
state.anchor = addDays(state.anchor, direction * 7);
} else if (state.view === 'month') {
state.anchor = makeDate(state.anchor.getFullYear(), state.anchor.getMonth() + direction, 1);
state.selected = cloneDate(state.anchor);
} else if (state.view === 'year') {
state.anchor = makeDate(state.anchor.getFullYear() + direction, 0, 1);
}
render();
}
categorySelect.addEventListener('change', function () {
state.category = categorySelect.value;
render();
});
root.addEventListener('click', function (event) {
var viewButton = closest(event.target, 'data-view');
if (viewButton) {
state.view = viewButton.getAttribute('data-view');
if (state.view === 'month') {
state.anchor = makeDate(state.anchor.getFullYear(), state.anchor.getMonth(), 1);
state.selected = cloneDate(state.anchor);
}
render();
return;
}
var actionButton = closest(event.target, 'data-action');
if (actionButton) {
shiftPeriod(actionButton.getAttribute('data-action') === 'previous' ? -1 : 1);
return;
}
var dayButton = closest(event.target, 'data-date');
if (dayButton) {
var v = dayButton.getAttribute('data-date').split('-');
state.selected = makeDate(+v[0], +v[1] - 1, +v[2]);
if (state.selected.getMonth() !== state.anchor.getMonth() ||
state.selected.getFullYear() !== state.anchor.getFullYear()) {
state.anchor = makeDate(state.selected.getFullYear(), state.selected.getMonth(), 1);
}
render();
return;
}
var monthButton = closest(event.target, 'data-month');
if (monthButton) {
state.view = 'month';
state.anchor = makeDate(state.anchor.getFullYear(), +monthButton.getAttribute('data-month'), 1);
state.selected = cloneDate(state.anchor);
render();
}
});
render();
}
// ---- generische Helfer (nativ, keine Bibliothek) ----
function toArray(nodeList) { return Array.prototype.slice.call(nodeList); }
function appendOption(select, value, label) {
var o = document.createElement('option');
o.value = value;
o.textContent = label;
select.appendChild(o);
}
function closest(el, attr) {
while (el && el.nodeType === 1) {
if (el.hasAttribute(attr)) { return el; }
el = el.parentNode;
}
return null;
}
function esc(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
}
function httpGetJson(url, ok, err) {
if (window.fetch) {
window.fetch(url).then(function (r) { return r.json(); })
.then(function (j) { ok(j); })
.catch(function () { if (err) { err(); } });
return;
}
var x = new XMLHttpRequest();
x.open('GET', url, true);
x.onreadystatechange = function () {
if (x.readyState === 4) {
if (x.status >= 200 && x.status < 300) {
try { ok(JSON.parse(x.responseText)); } catch (e) { if (err) { err(); } }
} else if (err) { err(); }
}
};
x.send();
}
})();