// ================================================================== // MARK: JS-Erweiterungen // ================================================================== //Element.insertAfter() Extension //Ergänzt die fehlende insertAfter-Funktion - funktioniert wie insertBefore, nur eben hintendran //see: https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore ; (function (prototype) { prototype.insertAfter = prototype.insertAfter || function (newNode, referenceNode) { return this.insertBefore(newNode, referenceNode.nextElementSibling); } })(Element.prototype, Document.prototype, DocumentFragment.prototype); // ================================================================== // MARK:SNIP // ================================================================== snip = { // MARK: discreteUnload discreteUnload: false, // wenn true, wird beim Unloadvorgang nicht abgeblendet (siehe /verwaltung/resources/js/main.js) // MARK: ready() ready: function (callback) { if (document.readyState === "complete" || (document.readyState !== "loading" && !document.documentElement.doScroll)) { snip.dispatch('snipready', window, { window: window }); if (window != top) snip.dispatch('snipready', top, { window: window }); callback(); } else document.addEventListener("DOMContentLoaded", function (e) { snip.dispatch('snipready', window, { window: window }); if (window != top) snip.dispatch('snipready', top, { window: window }); var boundCallback = callback.bind(e); boundCallback(); }); }, // MARK: _() // Selektor-Funktion. Schmeisst im Gegensatz zu querySelector() keinen Fehler bei falschen Selektoren // Kann ausserdem auch neue Nodes aus Strings erzeugen // Der zurückgegebene Node wird mit der Methode .on ausgestattet. // Sie funktioniert einerseits als Shorthand für snip.on(event, selektor) - der selektor kann dann weggelassen werden // Wird trotzdem ein selektor angegeben, wird capture auf true gesetzt, d.h. der Event wird delegiert // @param selector Ein oder mehrere DOM-Selektoren (kommasepariert) oder eine DOM-Node. // @param el Parent-Node, ab der gesucht werden soll. Wenn nicht angegeben, wird das ganze Dokument durchsucht // @param function Callback-Funktion, beim Aufruf wird das Schlüsselwort this auf das Element gemappt. // @param boolean returnempty: wenn true, wird ein -Objekt zurückgegeben, wenn der Selektor fehlschlägt -> vermeidet Fehlermeldungen bei Chaining-Operationen, wenn das Element nicht gefunden wird. // @return Node-Element oder Null, wenn es nicht gefunden wird und returnempty nicht gesetzt ist // Beispiele: // snip._('.bogus'); selektiert das erste Element mit der Klasse bogus // snip._('.bogus', el) selektiert das erste Unterelement von el mit der Klasse bogus // snip._('
Bogus
') erzeugt einen neuen DOM-Knoten und gibt ihn zurück // snip._('.bogus', el, function(){this.style.color='#FF0000'; this.remove();}) selektiert das erste Unterelement von el mit der Klasse bogus und führt die Callback-Funktion aus // snip._('.gibtsNicht', true); gibt eine -Node zurück, mit der ggf. direkt weitere Operationen vorgenommen werden können. _: function (selector) { if (typeof selector == 'undefined') return null; if (!selector) return null; else var nod = null, el = document, returnempty = false; for (i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'function') var callback = arguments[i]; else if (typeof arguments[i] == 'string') var el = snip._(arguments[i]); else if (typeof arguments[i] == 'object') var el = arguments[i]; else if (typeof arguments[i] == 'boolean') var returnempty = arguments[i]; } if (snip.node.test(selector)) nod = selector; else if (selector.trim().substring(0, 1) == '<') nod = snip.nodeFromString(selector); else { try { nod = el.querySelector(selector); } catch (error) { nod = null; } } if (!!nod) { var unbound; nod.css = function (style) { return snip.css(this, style); }; unbound = function (events, selectors, callback, options) { var item = this; if (typeof events == 'object') { snip.each(events, function () { item.on.apply(null, this); }); return; } if (typeof selectors == 'function') { options = callback; callback = selectors; selectors = this; } if (typeof options == 'undefined') options = {}; options.useCapture = true; snip.on(events, [item, selectors], callback, options); }; nod.on = unbound.bind(nod); unbound = function (path, param, callback, options) { if (typeof options == 'undefined') options = {}; options.element = nod; options.loaderId = snip.randomId('snipload'); if (typeof callback == 'function') options.callback = callback; callback = function (response) { snip._('#' + this.options.loaderId, function () { this.remove(); }); this.options.element.innerHTML = response; if (typeof (this.options.callback) == 'function') try { this.response = response; var boundCallback = this.options.callback.bind(this.options.element, this); boundCallback(); } catch (e) { console.error('snip._().load Callback Error', { arguments: this, error: e }); } } document.body.append(snip._('
')); snip._('#' + options.loaderId, function () { this.style.position = 'absolute'; this.style.top = nod.offsetTop + 'px'; this.style.left = nod.offsetLeft + 'px'; this.style.width = nod.offsetWidth + 'px'; this.style.height = nod.offsetHeight + 'px'; this.style.backgroundPosition = '-' + nod.offsetWidth + 'px 0'; }) snip.load.request(path, param, callback, options)(); }; nod.load = unbound.bind(nod); } else if (!!returnempty) { let elm = document.createElement('empty'); elm.on = function () { }; return elm; } if (typeof callback == 'function' && !!nod) { var boundCallback = callback.bind(nod); boundCallback(); } return nod; }, // MARK: get() // snip.get Gibt den Wert eines Elementes zurück oder einen default-Wert, ohne Fehler zu werfen, wenn das Element nicht gefunden wird oder keinen Wert hat. // Der Wert ist bei Formularelementen die Value-Eigenschaft, ansonsten die innerHTML-Eigenschaft // @param selector Ein DOM-Selektor oder eine DOM-Node oder ein Array [parent, child] -> das Element wird dann nur innerhalb des Parents gesucht // @param defaultVal Der Standardwert, der zurückgegeben wird, wenn das Element nicht existiert oder keinen hat. Default: null // Wenn der Standardwert numerisch ist, wird der Wert vor der Rückgabe in eine Zahl umgewandelt // @return mixed get: function (selector) { let retVal = arguments.length > 1 ? arguments[1] : null, element = Array.isArray(selector) ? snip._(selector[1], selector[0]) : snip._(selector); if (!!element) { if (element.hasAttribute('value')) retVal = typeof retVal == 'number' ? Number(element.value) : element.value; else if (element.nodeName == 'SELECT') retVal = typeof retVal == 'number' ? Number(element.value) : element.value; else if (element.nodeName == 'TEXTAREA') retVal = typeof retVal == 'number' ? Number(element.value) : element.value; else if (typeof element.innerHTML != 'undefined') retVal = typeof retVal == 'number' ? Number(element.innerHTML) : element.innerHTML; } return retVal; }, // MARK: set() // snip.set Setzt Eigenschaften eines Elementes // @param selector Ein DOM-Selektor oder eine DOM-Node oder ein Array [parent, child] -> das Element wird dann nur innerhalb des Parents gesucht // @param value entweder ein Objekt mit den jeweiligen Eigenschaften und Werten oder ein Einzelwert, dieser wird bei Formularelementen der Value-Eigenschaft, ansonsten der innerHTML-Eigenschaft zugeordnet. // @return mixed false, wenn das Element nicht gefunden wurde, sonst das element set: function (selector, value) { let elements = Array.isArray(selector) ? snip.__(selector[1], selector[0]) : snip.__(selector); if (elements.length == 0) return false; snip.each(elements, function (index, element) { if (typeof value == 'object') snip.each(value, function (property, val) { element[property] = val; if (val === false) element.removeAttribute(property); else element.setAttribute(property, val === true ? "" : val); }); else if (element.hasAttribute('value')) { element.value = value; element.setAttribute('value', value); }// setAttribute ggf. nötig, damit [value=XY]-Selektoren im CSS funktionieren else if (element.nodeName == 'SELECT') { element.value = value; } else if (typeof element.innerHTML != 'undefined') { element.innerHTML = value; } }); return elements; }, // MARK: css() // snip.css Setzt CSS-Eigenschaften eines Elementes oder gibt sie zurück // @param selector Ein DOM-Selektor oder eine DOM-Node oder ein Array [parent, child] -> das Element wird dann nur innerhalb des Parents gesucht // @param style entweder ein Objekt mit den jeweiligen Eigenschaften und Werten oder ein Einzelwert, letzterer wird dann zurückgegeben // @return mixed false, wenn das Element nicht gefunden wurde, sonst das element oder die Eigenschaft, wenn keine Werte übergeben wurden css: function (selector, style) { let elements = Array.isArray(selector) ? snip.__(selector[1], selector[0]) : snip.__(selector); if (elements.length == 0) return false; if (typeof style == 'object') { snip.each(elements, function (index, element) { snip.each(style, function (property, val) { element.style[property] = val; }); }); return elements; } else return window.getComputedStyle(elements[0])[style]; }, style: function (selector, style) { return snip.css(selector, style); }, // MARK: append() // snip.append Fügt Nodes oder Text an ein Element // @param selector Ein DOM-Selektor oder eine DOM-Node oder ein Array [parent, child] -> das Element wird dann nur innerhalb des Parents gesucht // @param insert Ein DOM-Selektor oder eine DOM-Node oder ein String // @return mixed false, wenn das Element nicht gefunden wurde, sonst das element oder die Eigenschaft, wenn keine Werte übergeben wurden append: function (selector, insert) { if (Array.isArray(selector)) snip.__(selector[1], selector[0], function () { this.append(snip.node.parse(insert, true)); }); else snip.__(selector, function () { this.append(snip.node.parse(insert, true)); }); }, // MARK: after() // snip.after Fügt Nodes oder Text an ein Element // @param selector Ein DOM-Selektor oder eine DOM-Node oder ein Array [parent, child] -> das Element wird dann nur innerhalb des Parents gesucht // @param insert Ein DOM-Selektor oder eine DOM-Node oder ein String // @return mixed false, wenn das Element nicht gefunden wurde, sonst das element oder die Eigenschaft, wenn keine Werte übergeben wurden after: function (selector, insert) { if (Array.isArray(selector)) snip.__(selector[1], selector[0], function () { this.parentNode.insertAfter(snip.node.parse(insert, true), this); }); else snip.__(selector, function () { this.parentNode.insertAfter(snip.node.parse(insert, true), this); }); }, // MARK: before() // snip.before Fügt Nodes oder Text an ein Element // @param selector Ein DOM-Selektor oder eine DOM-Node oder ein Array [parent, child] -> das Element wird dann nur innerhalb des Parents gesucht // @param insert Ein DOM-Selektor oder eine DOM-Node oder ein String // @return mixed false, wenn das Element nicht gefunden wurde, sonst das element oder die Eigenschaft, wenn keine Werte übergeben wurden before: function (selector, insert) { if (Array.isArray(selector)) snip.__(selector[1], selector[0], function () { this.parentNode.insertBefore(snip.node.parse(insert, true), this); }); else snip.__(selector, function () { this.parentNode.insertBefore(snip.node.parse(insert, true), this); }); }, // MARK: __() // snip.__ Selektor-Funktion. Schmeisst im Gegensatz zu querySelectorAll() keinen Fehler und gibt einen Array statt einer NodeList zurück // @param selector Ein oder mehrere DOM-Selektoren (kommasepariert) oder eine DOM-Node. // @param el Parent-Node, in der gesucht werden soll. Wenn nicht angegeben, wird das ganze Dokument durchsucht // @param index Index des Elements innerhalb des Arrays, das zurückgegeben werden soll. Wenn angegeben, wird diese Node direkt zurückgegeben, kein Array! // @param function Callback-Funktion, die für jedes Element der Iteration aufgerufen wird. // Sie wird jeweils mit drei Argumenten aufgerufen: 1. Der Index des Elements 1. Der Wert des Elements 3. Das Array oder Objekt, das durchlaufen wird // Das Schlüsselwort this enthält das jeweilige Element __: function (selector) { if (typeof selector == 'undefined') return null; else var nods = [], el = document; if (Array.isArray(selector)) { el = selector[0]; selector = selector[1]; } for (i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'function') var callback = arguments[i]; else if (typeof arguments[i] == 'string') el = snip._(arguments[i]); else if (typeof arguments[i] == 'object' && arguments[i] != null) el = arguments[i]; else if (typeof arguments[i] == 'number') var selectedIndex = arguments[i]; } if (snip.node.test(selector)) nods = [selector]; else { try { nods = Array.prototype.slice.call(el.querySelectorAll(selector)); } catch (error) { console.error('DOMQuery failed for ' + selector + ':' + error, el); nods = []; } } nods.forEach(function (item, index, objekt) { objekt[index].css = function (style) { return typeof style == 'object' ? snip.css(this, style) : ''; }; }); if (typeof callback == 'function') { nods.forEach(function (item, index, objekt) { if (typeof selectedIndex != 'number' || index == selectedIndex) { var boundCallback = callback.bind(item, index, item, objekt); boundCallback(); } }); } return typeof selectedIndex == 'number' ? nods[selectedIndex] : nods; }, // MARK: index() // Gibt den Index eines Elements in Relation zu einer Kollektion von Elementen zurück index: function (elm, coll) { elm = snip._(elm); coll = typeof coll == 'string' ? snip.__(coll) : coll; var index = null; if (typeof coll != 'object') return index; snip.each(coll, function (idx) { if (elm == this) index = idx; }); return index; }, // MARK: each() // Iteratorfunktion: führt für jedes Element eines Arrays, Objekts oder einer Nodelist die Callbackfunktion aus // @param string|array|object Element über das iteriert werden soll. Wenn es ein String ist, wird snip.__ ausgeführt und ggf. über die zurückgegebene Nodelist iteriert. // @param function Die Callback-Funktion, die für jedes Element der Iteration aufgerufen wird. // Sie wird jeweils mit drei Argumenten aufgerufen: 1. Der Index des Elements 1. Der Wert des Elements 3. Das Array oder Objekt, das durchlaufen wird // Das Schlüsselwort this enthält das jeweilige Element // Wenn die Callback-Funktion bool false zurückgibt, wird die Iteration abgebrochen each: function () { for (i = 0; i < arguments.length; i++) { if (typeof arguments[i] == 'string') var obj = snip.__(arguments[i]); if (typeof arguments[i] == 'object') var obj = arguments[i]; if (typeof arguments[i] == 'function') var callback = arguments[i]; } if (Array.isArray(obj)) { for (var i = 0; i < obj.length; i++) { var item = obj[i], boundCallback = callback.bind(item, i, item, obj), ret = boundCallback(); if (ret === false) break; } } else if (typeof obj == 'object') { for (key in obj) { if (obj.hasOwnProperty(key)) { var boundCallback = callback.bind(obj[key], key, obj[key], obj), ret = boundCallback(); if (ret === false) break; } } } }, // MARK: siblings() // Durchläuft die Geschwisterknoten eines Elements und gibt ggf. eine Liste dieser zurück (oder ggf. auch den ersten Geschwisterknoten, der gefunden wird) // @param string|node DOM-Selektor oder eine DOM-Node des Elements. // Optionale Parameter (in beliebiger Reihenfolge): // @param string[prev|next|all] (optional, default:'all') Wenn 'prev', werden alle vorhergehenden Geschwisterknoten zurückgegeben, wenn 'next' die nachfolgenden, ansonsten alle // @param string (optional) ein CSS-Selektor als Vorfilter (.d.h. nur die Knoten werden durchlaufen, die diesem entsprechen) // @param boolean (optional, default: false) Wenn true, werden Textknoten ebenfalls zurückgegeben, sonst nur Elementknoten // @param function (optional) Iteratorfunktion, wird für jedes gefundene Element aufgerufen. // Das erste Funktionsargument und auch das Schlüsselwort this enthalten beim Aufruf jeweils das aktuelle Element // Zusätzlich dient sie als Filterfunktion: nur Elemente, für welche die Funktion true zurückgibt, werden am Ende zurückgegeben // Wenn das Element selbst zurückgegeben wird, wird die Iteration abgebrochen und statt einer Liste nur dieser eine Knoten sofort zurückgegeben // @return array|node Array mit den gefundenen Geschwisterknoten oder den ersten gefundenen Geschwisterknoten (wenn die Filterfunktion das Element zurückgibt) siblings: function (elm) { let element = snip._(Array.isArray(elm) ? elm.shift() : elm), preFilter = null, type = null, filter = true, siblings = [], includeTextNodes = false; if (!element) return siblings; for (i = 0; i < arguments.length; i++) { if (typeof arguments[i] == 'boolean') includeTextNodes = arguments[i]; if (typeof arguments[i] == 'string') if (['prev', 'next', 'all'].includes(arguments[i]) && !type) type = arguments[i]; else preFilter = arguments[i]; if (typeof arguments[i] == 'function') filter = arguments[i]; } if (!type) type = 'all'; let el = (type == 'all') ? element.parentNode.firstChild : element; while (el = type == 'prev' ? (includeTextNodes ? el.previousSibling : el.previousElementSibling) : (includeTextNodes ? el.nextSibling : el.nextElementSibling)) { if (el != element) { if (!preFilter || el.matches(preFilter)) { if (typeof filter == 'function') var filterCallback = filter.bind(el, el), filterResult = filterCallback(el); else var filterResult = true; if (filterResult == el) return el; if (filterResult) siblings.push(el); } } } return siblings; }, // MARK: on() // snip.on Legt einen neuen EventListener an // @param events String Ein oder mehrere Event-Typen, leerzeichensepariert oder ein Array, der wiederum Arrays mit Parametern (events, selectors, callback, options) enthält // @param selectors String|Node|Array Ein oder mehrere DOM-SelektorStrings (kommasepariert) oder eine DOM-Node. // Für Event-Delegation kann auch ein Array[Parent, EventTarget] übergeben werden. // @param callback Function Callback-Funktion // @param options boolean|object Entweder Objekt mit EventListener-Optionen (siehe https://developer.mozilla.org/de/docs/Web/API/EventTarget/addEventListener) // oder bool-Wert: wenn true, wird der Capture-Mode verwendet on: function (events, selectors, callback, options) { if (typeof events == 'object') { snip.each(events, function () { snip.on.apply(null, this); }); return; } events = events.split(' '); if (events.length == 0) return; if (typeof options == 'undefined') options = {}; if (typeof options == 'boolean') options = { useCapture: options }; if (Array.isArray(selectors)) { options.target = selectors[1]; selectors = selectors[0]; } if (typeof selectors == 'string') selectors = selectors.split(','); else if (snip.node.test(selectors)) selectors = [selectors]; if (!selectors) selectors = [window]; if (typeof (selectors.forEach) != 'function') { console.error('snip.on-Error: Selector (type ' + typeof selectors + (snip.node.test(selectors) ? '/Node' : '') + ') invalid:', selectors); console.trace(); } else selectors.forEach(function (selector) { var items = snip.__(selector); if (items.length == 0) { return; } items.forEach(function (item, index) { events.forEach(function (event) { event = event.split('.'); var namespace = event.length > 1 ? event[1] : false; event = event[0]; var target = !!options.target ? options.target : selector; if (typeof item.snip_listeners == 'undefined') item.snip_listeners = {}; if (typeof item.snip_listeners[event] != 'undefined') { var eventItem = { callback: callback, namespace: namespace, target: target, options: options }; if (item.snip_listeners[event].callbacks.indexOf(eventItem) < 0) item.snip_listeners[event].callbacks.push(eventItem); } else { item.snip_listeners[event] = { selector: selector + '[' + index + ']', callbacks: [{ callback: callback, namespace: namespace, target: target, options: options }], listener: function (e) { var log = false; if (!!this.snip_listeners[e.type]) { this.snip_listeners[e.type].callbacks.forEach(function (callbackItem) { if (!!callbackItem.target) { element = (typeof e.target.closest == 'function' && typeof callbackItem.target == 'string') ? e.target.closest(callbackItem.target) : callbackItem.target; if (element != null && (typeof item.contains != 'function' || item.contains(element))) { switch (typeof callbackItem.options.log) { case 'function': log = callbackItem.options.log(e, this); break; case 'boolean': log = callbackItem.options.log; break; case 'string': log = callbackItem.options.log; break; default: break; } var boundCallback = callbackItem.callback.bind(element); if (typeof callbackItem.callback == 'function') boundCallback(e, element, item); } } }); } return; } } var eventOptions = { capture: !!options.useCapture, passive: typeof (options.passive) == 'undefined' ? true : !!options.passive, once: typeof (options.once) == 'undefined' ? false : !!options.once , trigger: typeof (options.trigger) == 'undefined' ? false : !!options.trigger }; item.addEventListener(event, item.snip_listeners[event].listener, eventOptions); if(eventOptions.trigger){ snip.trigger(event, item); console.log('trigger once '+event); } } }); }); }); }, // MARK: off() off: function (events, selectors, callback, options) { events = events.split(' '); if (events.length == 0) return; if (typeof options !== 'object') options = { log: options }; if (Array.isArray(selectors)) { options.target = selectors[1]; selectors = selectors[0]; } if (typeof selectors == 'string') selectors = selectors.split(','); else if (snip.node.test(selectors)) selectors = [selectors]; if (!selectors) selectors = [window]; selectors.forEach(function (selector) { var items = snip.__(selector); if (items.length > 0) { items.forEach(function (item, index) { events.forEach(function (event) { event = event.split('.'); var namespace = event.length > 1 ? event[1] : false; event = event[0]; if (typeof item.snip_listeners != 'undefined') { if (typeof item.snip_listeners[event] != 'undefined') { var cbCount = item.snip_listeners[event].callbacks.length; item.snip_listeners[event].callbacks = item.snip_listeners[event].callbacks.filter(function (ele) { return (!callback || ele.callback != callback) && (!namespace || ele.namespace != namespace) && ele.target != options.target; }); if (item.snip_listeners[event].callbacks.length == 0) { item.removeEventListener(event, item.snip_listeners[event].listener); delete item.snip_listeners[event]; } } } }); }); } }); }, // MARK: trigger() // snip.trigger Triggert einen Event für gewählte Elemente // @param type String Event-Typ // @param el String|Node DOM-SelektorString oder DOM-Node. // @todo dispatchEvent ist deprecated, austauschen!!! trigger: function (type, el) { var e = document.createEvent('HTMLEvents'), items = snip.__(el); if (items.length == 0) return; e.initEvent(type, false, true); items.forEach(function (item) { item.dispatchEvent(e); }); }, // MARK: dispatch() // snip.dispatch Erzeugt und triggert einen Custom-Event für gewählte Elemente // @param eventType String Event-Typ // @param el String|Node DOM-SelektorString oder DOM-Node. // @param detail mixed Daten die an die Eigenschaft detail des Custom-Events übergeben werden // @param params object Parameter für den Custom-Event: (bool) bubbles: Event steigt im DOM auf, (bool) cancelable: Event kann abgebrochen werden, (mixed) detail: zusätzliche Event-Informationen dispatch(eventType, el, detail, params) { var params = snip.object.extend({ bubbles: false, cancelable: false, detail: null }, params); params.detail = !!detail ? detail : null; snip.__(el, function () { this.dispatchEvent(new CustomEvent(eventType, params)); }); }, __defaultDisplay: { caption: 'table-caption', col: 'table-column', colgroup: 'table-column-group', img: 'inline-block', li: 'list-item', map: 'inline', output: 'inline', q: 'inline', table: 'table', tbody: 'table-row-group', td: 'table-cell', tfoot: 'table-footer-group', th: 'table-cell', thead: 'table-header-group', tr: 'table-row' }, // MARK: hide() // Element verstecken hide: function (selector, el) { snip.__(selector, el, function () { var display = window.getComputedStyle(this).display; if (display != 'none') this.snipDisplay = display; this.style.display = 'none'; }); }, // MARK: show() // (verstecktes) Element anzeigen show: function (selector, el) { snip.__(selector, el, function () { var display = window.getComputedStyle(this).display; if (display == 'none') { if (typeof this.snipDisplay == 'undefined') this.snipDisplay = !!snip.__defaultDisplay[this.nodeName.toLowerCase()] ? snip.__defaultDisplay[this.nodeName.toLowerCase()] : 'block'; this.style.display = this.snipDisplay; } }); }, // MARK: toggle() // Element-Sichtbarkeit an/-abstellen // @param string|node DOM-Selektor oder eine DOM-Node des Elements // @param string|node (optional) DOM-Selektor eines Vorfahren // @param bool (optional) Sichtbarkeitsstatus erzwingen. true: Element zeigen false: Element verstecken. Ansonsten werden sichtbare versteckt, versteckte sichtbar gemacht toggle: function (selector) { let el = null, setDisplay = null; for (i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'boolean') setDisplay = arguments[i]; else if (typeof arguments[i] == 'object') el = arguments[i]; else if (typeof arguments[i] == 'string') el = snip._(arguments[i]); } snip.__(selector, el, function () { display = window.getComputedStyle(this).display; if (!!setDisplay) { if (typeof this.snipDisplay == 'undefined' && display != 'none') this.snipDisplay = display; display = 'none'; } if (display == 'none' && setDisplay !== false) { if (typeof this.snipDisplay == 'undefined') this.snipDisplay = !!snip.__defaultDisplay[this.nodeName.toLowerCase()] ? snip.__defaultDisplay[this.nodeName.toLowerCase()] : 'block'; this.style.display = this.snipDisplay; } else { if (this.style.display != 'none') this.snipDisplay = display; this.style.display = 'none'; } }); }, // MARK: maxZ() maxZ: function (element, sub) { var maxZ = 0, sub = !!sub; if (typeof element == 'undefined') element = 'body'; element = snip._(element); if (!element) return maxZ; for (var i = 0; i < element.children.length; i++) { var item = element.children[i], positioned = ['absolute', 'relative', 'fixed', 'sticky'].includes(item.style.position), indexed = positioned && item.style.zIndex && item.style.zIndex != 'auto', stackingContext = indexed || (item.style.opacity && item.style.opacity < 1); if (stackingContext) maxZ = item.style.zIndex ? Math.max(maxZ, parseInt(item.style.zIndex)) : maxZ + 1; else if (!sub) { maxZ++; if (item.children.length > 0) maxZ = Math.max(maxZ, snip.maxZ(item, true)); } } return maxZ; }, // MARK: addStyle() addStyle: function (css) { var type = snip.string.isValidUrl(css) ? 'link' : 'style', id = null, container = document.getElementsByTagName('head')[0]; for (i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'string') id = arguments[i]; if (snip.node.test(arguments[i])) container = arguments[i]; } if (type == 'style') { var style = document.createElement('style'); style.type = 'text/css'; if (style.styleSheet) style.styleSheet.cssText = css;// IE else style.appendChild(document.createTextNode(css));// other browsers } else { var style = document.createElement('link'); style.type = 'text/css'; style.rel = 'stylesheet'; style.href = css; } if (!!id) { if (!!document.getElementById(id)) document.getElementById(id).remove(); style.id = id; } container.appendChild(style); return style; }, // MARK: addScript() addScript: function (js) { var type = snip.string.isValidUrl(js) ? 'ext' : 'int', id = null, callback = null, callbackdata = {}, script = document.createElement("script"), element = document; for (i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'string') id = arguments[i]; if (typeof arguments[i] == 'function') callback = arguments[i]; if (typeof arguments[i] == 'boolean') type = arguments[i] ? 'ext' : 'int'; if (snip.node.test(arguments[i])) { element = arguments[i]; } else if (typeof arguments[i] == 'object') callbackdata = arguments[i]; } script.type = "text/javascript"; if (type == 'int') script.text = js; else { if (!!callback) script.onload = callback; script.src = js; } if (!!id) { snip._('#' + id, function () { this.remove(); }); script.id = id; } element.getElementsByTagName('head')[0].appendChild(script); if (type == 'int' && !!callback) { callbackdata.scriptId = id; callback(callbackdata); } }, // MARK: _eventLog _eventLog: { active: false, name: 'eventLog', data: [], start: function () { snip._eventLog.active = true; snip._eventLog.data = []; sessionStorage.setItem(snip._eventLog.name, JSON.stringify(snip._eventLog.data)); window.addEventListener('unload', function (e) { //if(typeof (document.activeElement.href)=='undefined' || document.activeElement.href.indexOf('.uhrzeit.org') < 0) snip._eventLog.add('unload', document); snip._eventLog.flush(); }, false); }, add: function (event, element, data) { if (snip._eventLog.active !== true) return false; if (typeof element == 'object' && element.nodeType && element != document) { var parent = element.closest('.modBox'); if (!!parent) parent = parent.id; else if (element.closest('header')) parent = 'header'; else if (element.closest('footer')) parent = 'footer'; else parent = 'document'; if (element.snip_listeners && typeof element.snip_listeners[event] != 'undefined') element = element.snip_listeners[event].selector; else element = element.nodeName; } else { var parent = 'document'; element = typeof element == 'object' && element.nodeName ? element.nodeName : element; } snip._eventLog.data.push({ 'time': dayjs().format('YYYY-MM-DD HH:mm:ss'), 'event': event, 'path': location.pathname, 'container': parent, 'element': element, 'data': data }); sessionStorage.setItem(snip._eventLog.name, JSON.stringify(snip._eventLog.data)); }, flush: function () { if (snip._eventLog.active !== true) return false; var data = sessionStorage.getItem(snip._eventLog.name); if (!!data) { data = JSON.parse(data); data = snip.object.serialize({ logdata: data }); if (typeof navigator.sendBeacon == 'function') { navigator.sendBeacon('/sys/mod.ajax.php?mod=ajax-eventlog', data); } else { var xhr = new XMLHttpRequest(); xhr.open("POST", '/sys/mod.ajax.php?mod=ajax-eventlog', false); // synchronous to prevent being ignored at unload xhr.send(data); } } } }, // MARK: load() load: { // Führt einen Ajax-Request aus (eher für internen Gebrauch) // @param string die URL // @param object (optional) Ein Objekt mit den Parametern, die an das zu ladende Modul übergeben werden // @param function (optional) Callback-Funktion, wird mit der Response als Argument aufgerufen, this enthält ein Objekt mit den Eigenschaften path (url), param (übergebene parameter) und options (Optionen) // @param object (optional) Ein Objekt mit zusätzlichen Optionen (z.B. method:'POST', dataType:'json') request: function () { var path = arguments[0], param = arguments[1], callback = arguments[2], optionset = arguments[3], thisSet = { path: path, param: param, options: optionset }, httpRequest = new XMLHttpRequest(); return function () { if (typeof param == 'object') param = snip.object.serialize(param); else if (typeof param == 'string') param = param; else if (typeof param == 'function' && typeof callback != 'function') { if (typeof callback != 'undefined' && typeof optionset == 'undefined') optionset = callback; callback = param; param = ''; } optionset = typeof optionset == 'object' ? snip.object.extend({ method: 'GET' }, optionset) : { method: 'GET' }; if (!httpRequest) return false; httpRequest.onreadystatechange = function () { try { if (httpRequest.readyState === XMLHttpRequest.DONE) { if (httpRequest.status === 200) { var response = httpRequest.responseText; if (optionset.dataType == 'json') response = JSON.parse(response); if (typeof (callback) == 'function') try { var boundCallback = callback.bind(thisSet, response); boundCallback(); } catch (e) { console.error('snip.load Callback Error', { arguments: thisSet, request: httpRequest, options: optionset, error: e }); } } else { console.error('snip.load Ajax failed', { arguments: thisSet, request: httpRequest, options: optionset }); } } } catch (e) { console.error('snip.load caught Exception - ', e); } }; if (optionset.method.toUpperCase() == 'POST') { httpRequest.open(optionset.method, path); httpRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); httpRequest.send(param); } else { httpRequest.open(optionset.method, path + (path.indexOf('?') < 0 ? '?' : '&') + param); httpRequest.send(); } }; }, // MARK: load.file() file: function (path, param, callback, options) { snip.load.request(path, param, callback, options)(); }, // MARK: load.json() json: function () { for (i = 0; i < arguments.length; i++) { if (typeof arguments[i] == 'string') var path = arguments[i]; if (typeof arguments[i] == 'object') { if (typeof param == 'undefined') var param = arguments[i]; else var options = arguments[i]; } if (typeof arguments[i] == 'function') var callback = arguments[i]; } var type = path.indexOf('/') < 0 && path.indexOf('.') < 0 && path.substr(0, 1) != '?' ? 'mod' : 'file'; if (type == 'mod') { param = snip.object.extend(param, { mod: path }); path = '/snips/' + path; } options = snip.object.extend(options, { dataType: 'json' }); snip.load.request(path, param, callback, options)(); }, // MARK: load.mod() // Lädt ein Modul und integriert es ggf. in die Seite (d.h. auch interne und externe Styles und Scripte) // Parameter: (der erste Parameter ist immer die modId, alle anderen optional und in beliebiger Reihenfolge) // @param string modId Die ID des zu ladenden Mods // @param String|Node snipContainer DOM-SelektorString oder DOM-Node des Elements, in das das Mod-HTML geladen werden soll // Wenn der Container selbst die ID des zu ladenden Moduls trägt, wird nur das innere HTML des Moduls geladen (integrate ist dann true, s.u.) // @param object param Ein Objekt mit den Parametern, die an das zu ladende Modul übergeben werden // @param boolean integrate false : die ModulElemente (d.h. auch interne und externe Styles und Scripte) werden nicht in das DOM integriert, es erfolgt ggf. nur ein Callback // true : es werden alle Scripte und Styles und das innere Modul-HTML integriert // undefined: es werden alle Scripte und Styles und das äußere Modul-HTML integriert (also mit dem umgebenden Div mit der ModulId) // default : false, außer evtl. wenn ein snipContainer angegeben ist (s.o.) // @param function callback Callback-Funktion (wird mit den Parametern (string) Containerelement und (object) Moduldaten aufgerufen) mod: function (modId) { for (i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'string') var snipContainer = arguments[i]; if (typeof arguments[i] == 'object') var param = arguments[i]; if (typeof arguments[i] == 'boolean') var integrate = arguments[i]; if (typeof arguments[i] == 'function') var callback = arguments[i]; } if (typeof integrate == 'undefined' && !!snipContainer) { var boxId = 'box_' + modId.replace('@', '').replace('~', '_'), container = snip._(snipContainer); if (!!container && container.getAttribute('id') == boxId) integrate = true; } snip.load.request('/snips/' + modId, snip.object.extend(param, { mod: modId, integrate: integrate }), function (data) { var modId = this.param.mod.replace(/[^a-zA-Z]*/g, ''), domreadyCt = 0, domreadies = [], readyReg = /\$\(\s*(\s*document\s*\)\s*\.\s*ready\s*\()*function\s*\(\s*\)/, snipReadyReg = /snip\.ready\s*\(\s*function\s*\(\s*\)/; if (this.param.integrate !== false) { snip.addStyle(data.style, modId + 'StyleLoaded'); if (!!data.externalStyle) snip.addStyle(data.externalStyle, modId + 'extStyleLoaded'); while (data.script.match(readyReg)) { var domreadyFuncName = modId + String(domreadyCt); data.script = data.script.replace(readyReg, domreadyFuncName + " = (function()"); domreadies.push(domreadyFuncName); domreadyCt++; } while (data.script.match(snipReadyReg)) { var domreadyFuncName = modId + String(domreadyCt); data.script = data.script.replace(snipReadyReg, domreadyFuncName + " = (function()"); domreadies.push(domreadyFuncName); domreadyCt++; } snip.addScript(data.script, modId + 'ScriptLoaded'); if (!!this.options.snipContainer) var container = snip._(this.options.snipContainer); if (!!container) container.innerHTML = !!this.param.integrate && this.param.integrate != 'undefined' ? data.htm : data.html; if (!!data.externalScript) snip.addScript(data.externalScript); for (ct = 0; ct < domreadies.length; ct++) { window[domreadies[ct]](); } } if (this.options.snipCallback) this.options.snipCallback(this.options.snipContainer, data); }, { dataType: 'json', snipContainer: snipContainer, snipCallback: callback })(); } }, // MARK: offset() // snip.offset Gibt den Offset eines Elements relativ zum Dokument, zum Viewport oder zu einem anderen Element zurück // Akzeptiert als Element und Relationselement DomNodes oder Selektor-Strings, als Relationselement zusätzlich die JS-Objekte document (relativ zum Dokument) und window (relativ zum viewport). // Beispiele: // snip.offset('#Element'); relativ zum Parent (wie jQuery.position) // snip.offset('#Element', window); relativ zum Viewport // snip.offset('#Element', document); relativ zum Dokument (wie jQuery.offset) // snip.offset('#Element', '#relative');relativ zum Element mit der ID 'relative' offset: function (el, relativeTo) { if (typeof el == 'string') el = document.querySelector(el); if (typeof el.getBoundingClientRect != 'function') return false; relativeTo = arguments.length > 1 ? snip._(relativeTo) : false; if (!relativeTo) relativeTo = el.parentNode; var rect = el.getBoundingClientRect(), offset = { top: rect.top, left: rect.left, width: rect.width, height: rect.height }; if (relativeTo == document) { var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft, scrollTop = window.pageYOffset || document.documentElement.scrollTop; offset.top += scrollTop; offset.left += scrollLeft; } else if (relativeTo != window) { var parentRect = relativeTo.getBoundingClientRect(); offset.top -= parentRect.top; offset.left -= parentRect.left; } return offset; }, // MARK: inViewport() // Gibt zurück, ob das Element im Viewport liegt // @param node|string el Element-Objekt oder Element-Selektor-String // @param number Anteil des Elements, der im Viewport sichtbar ist (0-1, default 1) // @param string[top|bottom|left|right] (optional) Element-Seite, die im Viewport stehen soll. Offensichtlich nur sinnvoll in Verbindung wenn der sichtbare Anteil kleiner 1 ist // @param boolean wenn true, wird statt eines Bool-Wertes ein Objekt mit den sichtbaren Anteilen der einzelnen Seiten des Elements zurückgegeben inViewport: function (el) { for (i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'string') var edge = arguments[i]; if (typeof arguments[i] == 'number') var portion = arguments[i]; if (typeof arguments[i] == 'boolean') var returnData = arguments[i]; } if (typeof portion == 'undefined') var portion = 1; if (typeof edge == 'undefined') var edge = ''; if (typeof returnData == 'undefined') var returnData = false; if (typeof el == 'string') el = document.querySelector(el); var bounds = snip.offset(el, document), scrollLeft = window.pageXOffset || document.documentElement.scrollLeft, scrollTop = window.pageYOffset || document.documentElement.scrollTop, vpWidth = window.innerWidth, vpHeight = window.innerHeight, viewport = { top: scrollTop, right: scrollLeft + vpWidth, bottom: scrollTop + vpHeight, left: scrollLeft }; if (!bounds) return false; bounds.right = bounds.left + bounds.width; bounds.bottom = bounds.top + bounds.height; var visible = (!(viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom)); if (!visible && !returnData) return false; var deltas = { top: bounds.top < viewport.top ? 0 : Math.min(1, (viewport.bottom - bounds.top) / bounds.height), bottom: bounds.bottom > viewport.bottom ? 0 : Math.min(1, (bounds.bottom - viewport.top) / bounds.height), left: bounds.left < viewport.left ? 0 : Math.min(1, (viewport.right - bounds.left) / bounds.width), right: bounds.right > viewport.right ? 0 : Math.min(1, (bounds.right - viewport.left) / bounds.width) }; if (returnData) return deltas; if (edge == 'top') return deltas.top >= portion; if (edge == 'right') return deltas.right >= portion; if (edge == 'bottom') return deltas.bottom >= portion; if (edge == 'left') return deltas.left >= portion; return (deltas.left * deltas.right) >= portion && (deltas.top * deltas.bottom) >= portion; }, // MARK: observeY() // Gibt zurück, ob das Element über oder unter einer gegebenen Y-Position relativ zum Viewport liegt // @param node|string el Element-Objekt oder Element-Selektor-String // @param number (optional) Y-Position // @param function (optional) Callback-Funktion, die aufgerufen wird, wenn das Element die Y-Position passiert. Die Callback-Funktion wird mit einem bool-Argument aufgerufen, dass true ist, wenn die Y-Position des Elements kleiner als die Y-Position liegt, sonst true // @param boolean (optional) wenn false, wird der Observer deaktiviert // @param object (optional) Ein Objekt mit Optionen (noch nichts implementiert) observeY: function (element) { element = snip._(element); if (!element) return false; var enabled = true, offset = 0, options = {}, callback = null; for (i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'number') offset = arguments[i]; if (typeof arguments[i] == 'object') options = arguments[i]; if (typeof arguments[i] == 'boolean') enabled = arguments[i]; if (typeof arguments[i] == 'function') callback = arguments[i]; } offset += snip.offset(element, document).top; if (enabled) { element.dataset['snipFix'] = "on"; if (typeof callback == 'function') element.snipFixCallback = callback; for (var key in options) { var snipkey = 'snipFix' + key[0].toUpperCase() + key.substring(1).toLowerCase(); if (options.hasOwnProperty(key)) element.dataset[snipkey] = options[key]; } element.dataset['snipFixOffset'] = offset; if (typeof snipObserver == 'undefined') { snip.on('scroll resize', window, function (e) { snip.__('[data-snip-fix]').forEach(function (item) { var scrollTop = window.pageYOffset || document.documentElement.scrollTop; if (scrollTop > parseInt(item.dataset.snipFixOffset)) { if (!item.dataset['snipFixed']) { item.dataset.snipFixed = "1"; if (typeof item.snipFixCallback == 'function') item.snipFixCallback(true); } } else if (!!item.dataset['snipFixed']) { delete item.dataset['snipFixed']; item.snipFixCallback(false); } }); }); } } else { for (var key in element.dataset) { if (element.dataset.hasOwnProperty(key) && key.indexOf('snipFix') === 0) delete (element.dataset[key]); } } return element.dataset; }, // MARK: setSizes() // Setzt für Bilder und Hintergrundbilder ein Set aus URLs für verschiedene Darstellungsgrößen ein und triggert snip.setSize, um die Image-Source festzulegen // // @param node|string el Element-Objekt oder Element-Selektor-String // @param object sizes Objekt mit jeweils der Breite, ab der die URL zu verwenden ist als Schlüssel und der URL als Wert. // setSizes: function (elm, sizes) { var elm = snip._(elm); if (!!elm) { elm.classList.add('snipSizes'); elm.dataset.size = 0; var sizeSet = []; for (var size in sizes) { if (sizes.hasOwnProperty(size)) { elm.dataset[size] = sizes[size]; sizeSet.push(size); } } elm.dataset.sizes = sizeSet.join(','); snip.setSize(elm); window.addEventListener('resize', function () { snip.setSize(); }); return true; } else return false; }, // MARK: setSize() // Setzt die URL für ein Bild oder Hintergrundbild in Abhängigkeit von der Breite eines Elternelements und den im Dataset angegeben URLs für verschiedene Breiten (siehe snip.setSizes) // Im Gegensatz zu mediaQueries wird hier als Bezugsgröße nicht die Viewport-Width verwendet, sondern die Breite des nächsten Elternelements mit der Klasse .modBox // Wenn dieses nicht existiert, wird die Breite des Elements selbst herangezogen (wirklich sinnvoll nur für Hintergrundbilder) // über das Element-Attribut data-sizeparent="{CSS-Selektor}" kann ausserdem ein beliebiges Element als Bezugsgröße angegeben werden. // Bei Änderungen der URL wird das Event 'changedUrl' auf das betreffende Element gefeuert. Die Eventobjekt-Eigenschaft detail enthält ein Objekt mit den Schlüsseln 'width' (aktuelle Bezugsbreite) und 'src' (aktuelle Image-Url) // Wenn das Element die Klasse 'lazy' enthält, also über den LazyLoader gesteuert wird, wird die URL nicht direkt geändert, stattdessen wird das Datenattribut 'data-src' (Images) bzw. 'data-bg' (BackgroundImages) gesetzt. // // @param node|string el Element-Objekt oder Element-Selektor-String. Wenn leer, werden alle Elemente mit der Klasse 'snipSizes' durchlaufen setSize: function (item) { var snipz = item ? snip.__(item) : snip.__('.snipSizes'); snipz.forEach(function (item) { if (!!item.dataset && !!item.dataset.sizes) { var p = !!item.dataset.sizeparent ? snip._(item.dataset.sizeparent) : item.closest('.modBox'), w = !!p ? p.offsetWidth : item.offsetWidth, sizes = item.dataset.sizes.split(','), ct = 0; if (item.dataset.size < w) { for (var i = 0; i < sizes.length; i++) { ct = i; if (sizes[i] >= w) { item.dataset.size = sizes[i]; break; } } if (item.dataset.size == 0) item.dataset.size = sizes[ct]; if (item.classList.contains('lazy') && !item.classList.contains('loaded')) { if (item.nodeName == 'IMG') item.setAttribute('data-src', item.dataset[item.dataset.size]); else item.setAttribute('data-bg', 'url(' + item.dataset[item.dataset.size] + ')'); snip.setLazy(); } else { if (item.nodeName == 'IMG') item.src = item.dataset[item.dataset.size]; else item.style.backgroundImage = 'url(' + item.dataset[item.dataset.size] + ')'; } var event = new CustomEvent('changedUrl', { detail: { width: w, src: item.dataset[item.dataset.size] } }), cancelled = !item.dispatchEvent(event); } } }); if (typeof setSizeTo != 'undefined') delete setSizeTo; }, // MARK: mapScale() // Macht ImageMaps skalierbar, indem die area-Koordinaten neu berechnet werden, sobald das Bild geladen wird oder der window.resize-Event getriggert wird. // Einmal aufgerufen, wird es auf alle -Elemente angewendet, die die Klasse mapScale und ein usemap-Attribut haben // Zur korrekten Berechnungen bei skalierenden Bildern sollten das width- und height-Attribut auf die Größe festgelegt sein, die der ImageMap zugrundeliegt. mapScale: function () { var items = arguments.length > 0 && !arguments[0] instanceof Event ? arguments[0] : 'img.mapScale[usemap]', images2map = snip.__(items); if (images2map.length == 0) return; images2map.forEach(function (image2map, index) { var img = new Image(); img.onload = function () {// Since WebKit doesn't know the height until after the image has loaded, perform everything in an onload copy if (!img.complete) return false; if (img.naturalWidth === 0) return false; var w = image2map.getAttribute('width') || img.naturalWidth, h = image2map.getAttribute('height') || img.naturalHeight, wRatio = image2map.offsetWidth / w, hRatio = image2map.offsetHeight / h, mapName = image2map.getAttribute('usemap').replace('#', ''), map = snip._('map[name="' + mapName + '"]'); if (!!map) { snip.each('area', function () { var currentCoords = this.getAttribute('coords'), initialCoords = this.getAttribute('data-coords'); if (!initialCoords) { this.setAttribute('data-coords', currentCoords); initialCoords = currentCoords; } var coords = initialCoords.split(','), coordsPercent = new Array(coords.length); for (var i = 0; i < coordsPercent.length; ++i) { coordsPercent[i] = i % 2 === 0 ? parseInt(coords[i] * wRatio) : parseInt(coords[i] * hRatio); } this.setAttribute('coords', coordsPercent.toString()); }); snip.each('a.tag.' + mapName, function () { this.style.top = parseInt(((this.dataset.top / h) * 100) * hRatio) + 'px'; this.style.left = parseInt(((this.dataset.left / w) * 100) * wRatio) + 'px'; }); } }; img.src = image2map.src; }); window.addEventListener('resize', snip.mapScale, true); }, node: { // MARK: node.index() // Gibt den Index eines Elements in Relation zum Elternelement zurück index: function (el) { el = snip._(el); var i = -1; if (!!el) do { i++; } while (el = el.previousElementSibling); return i; }, // MARK: node.parse() // Erzeugt eine DOM-Node aus einem String // @param str string // @return Element // snip.node.parse('
bar
'); parse: function (str) { if (arguments.length > 1 && !!arguments[1]) { let template = document.createElement("template"); template.innerHTML = str; let node = template.content.cloneNode(true); return node; } return new DOMParser().parseFromString(str, 'text/html').body.childNodes[0]; }, // MARK: node.test() // Gibt zurück, ob der übergebene Wert ein DOM-Element oder das window-Objekt oder das document-Objekt ist // @param mixed element Der zu prüfende Wert // @param boolean strict Wenn true, wird nur geprüft, ob es eine DomNode ist, window und document liefern dann false. Default ist false. // @return boolean test: function (element, strict) { if (typeof strict != 'boolean') strict = false; if (typeof element == 'undefined') return false; if (!element) return false; return element instanceof Element // Element ist eine Node || ( !strict && ( (typeof HTMLDocument != 'undefined' && (element instanceof HTMLDocument || element instanceof top.HTMLDocument)) // Element ist ein Dokument || (typeof Document != 'undefined' && (element instanceof Document || element instanceof top.Document))) // Element ist ein Dokument || (element.document && element.location && element.alert && element.setInterval) // Element ist ein Window ); }, // MARK: node.sort() sort: function () { var parentNode = null, filter = null, sortFunc = function (a, b) { return a.innerHTML == b.innerHTML ? 0 : (a.innerHTML > b.innerHTML ? 1 : -1); }; for (i = 0; i < arguments.length; i++) { if (typeof arguments[i] == 'function') sortFunc = arguments[i]; else if (!!parentNode) filter = arguments[i]; else parentNode = arguments[i]; } var list = snip._(parentNode); if (!list) return false; var items = filter == null ? Array.prototype.slice.call(list.childNodes).filter(nod => nod.nodeType == 1) : snip.__(filter, list); if (items.length == 0) return false; items.sort(sortFunc); var lastItem = items.pop(); for (i = 0; i < items.length; ++i) list.insertBefore(items[i], lastItem); } }, // MARK: nodeFromString() nodeFromString: function (str) { return snip.node.parse(str); },// Alias für snip.node.parse(), kann bei Gelegenheit hoffentlich weg // MARK: isDOMElement() isDOMElement: function (element) { return snip.node.test(element); }, // Alias für snip.node.isNode, kann bei Gelegenheit hoffentlich weg form: { // MARK: form.serialize() // Gibt Formulardaten serialisiert zurück (z.B. zur Verwendung als QueryString) // @param mixed Formular, entweder als DomNode-Objekt oder SelektorString // @param string|node SelektorString oder DOMNode, wenn angegeben, werden nur Elemente übernommen, die innerhalb des Selektors liegen // @return string die serialisierten Formulardaten // snip.form.serialize(document.testForm); serialize: function (form) { var selector = null, booldummy = false; // Booldummy steht hier aus Legacygründen, keine Ahnung, wofür das sein soll... for (i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'boolean') booldummy = arguments[i]; else if (typeof arguments[i] == 'string') selector = arguments[i]; else if (typeof arguments[i] == 'object') selector = arguments[i]; } form = snip._(form); if (!form) return false; var serialized = []; for (var i = 0; i < form.elements.length; i++) { var field = form.elements[i]; if (typeof selector == 'string' && field.closest(selector) == null) continue; //else if(typeof selector=='object' && !selector.contains(field)) continue; if (!field.name || field.disabled || field.type === 'file' || field.type === 'reset' || field.type === 'submit' || field.type === 'button') continue; if (field.type === 'select-multiple') { for (var n = 0; n < field.options.length; n++) { if (!field.options[n].selected) continue; serialized.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(field.options[n].value)); } } else if ((field.type !== 'checkbox' && field.type !== 'radio') || field.checked) { serialized.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(field.value)); } } return serialized.join('&'); } }, array: { // MARK: array.shuffle() // Randomly shuffle an array // https://stackoverflow.com/a/2450976/1293256 // @param {Array} array The array to shuffle // @return {String} The first item in the shuffled array shuffle: function (array) { var currentIndex = array.length; var temporaryValue, randomIndex; while (0 !== currentIndex) { randomIndex = Math.floor(Math.random() * currentIndex); currentIndex -= 1; temporaryValue = array[currentIndex]; array[currentIndex] = array[randomIndex]; array[randomIndex] = temporaryValue; } return array; } }, object: { // MARK: object.extend() extend: function (out) { out = out || {}; for (var i = 1; i < arguments.length; i++) { var obj = arguments[i]; if (!obj) continue; for (var key in obj) { if (obj.hasOwnProperty(key)) { if (typeof obj[key] === 'object') { if (obj[key] instanceof Array == true) out[key] = obj[key].slice(0); else out[key] = snip.object.extend(out[key], obj[key]); } else out[key] = obj[key]; } } } return out; }, // MARK: object.serialize() serialize: function (obj, prefix) { var str = [], p; for (p in obj) { if (obj.hasOwnProperty(p)) { var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p]; str.push((v !== null && typeof v === "object") ? snip.object.serialize(v, k) : encodeURIComponent(k) + "=" + encodeURIComponent(v)); } } return str.join("&"); } }, string: { // MARK: string.isValidUrl() isValidUrl: function (str) { var pattern = new RegExp( '^(https?:\\/\\/)?' + // protocol '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string '(\\#[-a-z\\d_]*)?$', 'i' // fragment locator ); return !!pattern.test(str); } }, number: { // MARK: number.format() // Zahl formatiieren (nutzt Intl.NumberFormat) // @param {number} die zu formatierende Zahl // @param {string} (optional) Locale (default:'en-En') // @param {number} (optional) Anzahl der Nachkommastellen (default:2) // @param {boolean} (optional) Dezimalgruppierung (Tausender-Trennzeichen) (default:false) // @param {objekt} (optional) Weitere Optionen zur Formatierung, siehe https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options // @return {number} Die formatierte Zahl format: function (betrag) { let format = 'en-EN', options = { minimumFractionDigits: 2, useGrouping: false } for (var i = 1; i < arguments.length; i++) { if (typeof arguments[i] == 'string') format = arguments[i].toLowerCase() == 'de' ? 'de-DE' : arguments[i]; if (typeof arguments[i] == 'number') options.minimumFractionDigits = arguments[i]; if (typeof arguments[i] == 'boolean') options.useGrouping = arguments[i]; if (typeof arguments[i] == 'object') options = snip.object.extend(options, arguments[i]); } return new Intl.NumberFormat(format, options).format(betrag); } }, preis: { // MARK: preis.add() // Addiert die Parameter als Preise (2 Dezimalstellen), akzeptiert dabei Strings und alle Zahlentypen und vermeidet floating-point-precision-Fehler add: function () { let sum = 0; for (var i = 0; i < arguments.length; i++) sum += Math.round(parseFloat(arguments[i]) * 100); return sum / 100; } } }; // MARK: randomId() snip.randomId = (function () { var globalIdCounter = 0; return function (baseStr) { return (baseStr + globalIdCounter++); } })(); // MARK: swipedection() snip.swipedection = function (el, options) { let touchsurface = snip._(el), startX, startY, distX, distY, threshold = 150, //required min distance traveled to be considered swipe restraint = 100, // maximum distance allowed at the same time in perpendicular direction allowedTime = 300, // maximum time allowed to travel that distance elapsedTime, startTime; touchsurface.addEventListener('touchstart', function (e) { let touchobj = e.changedTouches[0]; distX = 0; distY = 0; startX = touchobj.pageX; startY = touchobj.pageY; startTime = new Date().getTime(); // record time when finger first makes contact with surface //e.preventDefault(); }, { capture: true, passive: false }) touchsurface.addEventListener('touchmove', function (e) { //e.preventDefault(); // prevent scrolling when inside DIV }, { capture: true, passive: false }) touchsurface.addEventListener('touchend', function (e) { let touchobj = e.changedTouches[0], swipedir = ''; // get time elapsed distX = touchobj.pageX - startX; // get horizontal dist traveled by finger while in contact with surface distY = touchobj.pageY - startY; // get vertical dist traveled by finger while in contact with surface elapsedTime = new Date().getTime() - startTime; if (elapsedTime <= allowedTime) { // first condition for awipe met if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint) { // 2nd condition for horizontal swipe met swipedir = (distX < 0) ? 'Left' : 'Right'; // if dist traveled is negative, it indicates left swipe } else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint) { // 2nd condition for vertical swipe met swipedir = (distY < 0) ? 'Up' : 'Down'; // if dist traveled is negative, it indicates up swipe } } if (swipedir != '') { console.log('swipe detected: ' + swipedir); snip.dispatch('swipe' + swipedir, touchsurface, { 'x': distX, 'y': distY, 'startX': startX, 'startY': startY }, { bubbles: true }); //e.preventDefault(); } }, { capture: true, passive: false }); } // snip.countryFlags // Polyfill für fehlenden Unicode-Flaggen-Support (z.B. Google Chrome) // Benötigt den TwemojiCountryFlags.woff2-Font (ggf. Link in fontUrl eintragen) // // @returns true if the web font was loaded (ie the browser does not support country flags) // siehe https://github.com/talkjs/country-flag-emoji-polyfill */ snip.countryFlags = function () { const fontName = "Twemoji Country Flags"; const fontUrl = "/img/mod/core/TwemojiCountryFlags.woff2"; const fontChecks = '"Twemoji Mozilla","Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji","EmojiOne Color","Android Emoji",sans-serif'; function makeCtx() { const canvas = document.createElement("canvas"); canvas.width = canvas.height = 1; const ctx = canvas.getContext("2d", { willReadFrequently: true }); ctx.textBaseline = "top"; ctx.font = `100px ${fontChecks}`; ctx.scale(0.01, 0.01); return ctx; } function getColor(ctx, text, color) { ctx.clearRect(0, 0, 100, 100); ctx.fillStyle = color; ctx.fillText(text, 0, 0); const bytes = ctx.getImageData(0, 0, 1, 1).data; return bytes.join(","); } function supportsEmoji(text) { const ctx = makeCtx(); const white = getColor(ctx, text, "#fff"); const black = getColor(ctx, text, "#000"); return black === white && !black.startsWith("0,0,0,"); } if (typeof window !== "undefined" && supportsEmoji("😊") && !supportsEmoji("🇨🇭")) { const style = document.createElement("style"); style.textContent = `@font-face { font-family: "${fontName}"; unicode-range: U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F; src: url('${fontUrl}') format('woff2'); font-display: swap; }`; document.head.appendChild(style); return true; } return false; } // MARK: print() // Druckt ein Seitenelement aus // @param string|node DOMNode oder Selektor des Elements, das gedruckt werden soll // @param string|node Beliebig viele Style-Elemente, die in das Druckdokument übernommen werden. Dies können CSS-Deklarationen, Stylesheet-URLs oder auch Style-Elementknoten sein. // @param function eine Callback-Funktion, die nach der Erstellung des iFrames, aber vor dem Druck ausgeführt wird und den Body des Frames als Schlüsselwort this übergibt - so kann das DOM vor dem Druck noch angepasst werden // Beispiel: snip.print('#printSection', 'https://www.uhrzeit.org/style.css', 'body{padding:20mm 10mm !important;}', document.getElementById('StyleTagId')); snip.print = function (element) { var element = snip._(element); if (!element) return false; snip._('#printFrame', function () { this.remove(); }) var style = '', headStyle = [], verbose = false; for (i = 1; i < arguments.length; i++) { if (snip.node.test(arguments[i])) style += ''; else if (snip.string.isValidUrl(arguments[i])) { var lnk = document.createElement('link'); lnk.type = 'text/css'; lnk.rel = 'stylesheet'; lnk.href = arguments[i]; headStyle.push(lnk); } else if (typeof arguments[i] == 'boolean') verbose = true; else if (typeof arguments[i] == 'function') beforePrint = arguments[i]; else style += ''; } var frame = document.createElement("iframe"); frame.id = "printFrame"; frame.name = "printFrame"; frame.height = 0; frame.onload = function () { for (var i = 0; i < headStyle.length; i++) { this.contentWindow.document.getElementsByTagName('head')[0].appendChild(headStyle[i]); } this.contentWindow.document.body.innerHTML = style + element.outerHTML; this.contentWindow.document.querySelector(':root').setAttribute('moznomarginboxes', ''); this.contentWindow.document.querySelector(':root').setAttribute('mozdisallowselectionprint', ''); if (typeof beforePrint == 'function') { beforePrint = beforePrint.bind(this.contentWindow.document.body); beforePrint(); } setTimeout(function () { snip._('#printFrame', function () { console.log(this, this.contentWindow.document.body.innerHTML); this.contentWindow.print() }); }, 1000); }; frame.src = 'about:blank'; document.body.append(frame); return; }; // MARK: download() // Erzeugt einen Download aus einem beliebigen Text // @param string Dateiname // @param string Der Dateiinhalt // @param type (optional) Mime-Type, Default: 'text/plain' // @param charset (optional) Zeichenkodierung, Default: 'utf-8' // Beispiel: snip.download('test.csv', 'dies;das;jenes', 'text/csv', 'utf-8'); snip.download = function (filename, text, type, charset) { if (!filename) return false; if (typeof type == 'undefined') type = 'text/plain'; if (typeof charset == 'undefined') charset = 'utf-8'; var element = document.createElement('a'); element.setAttribute('href', 'data:' + type + ';charset=' + charset + ',' + encodeURIComponent(text)); element.setAttribute('download', filename); element.style.display = 'none'; document.body.appendChild(element); element.click(); document.body.removeChild(element); } // MARK: disableSelection() snip.disableSelection = function (el) { snip.__(el, function () { if (!snip.node.test(this)) return; this.setAttribute('unselectable', 'on'); this.style.userSelect = 'none'; this.style.WebkitUserSelect = 'none'; this.style.KhtmlUserSelect = 'none'; this.style.MozUserSelect = 'none'; snip.on('selectstart', this, function (e) { e.preventDefault(); return false; }); }); } // MARK: invert() snip.invert = function (el) { snip.__(el, function () { var bg = window.getComputedStyle(this).backgroundColor, color = window.getComputedStyle(this).color; this.style.color = bg; this.style.backgroundColor = color; }); } snip.status = function(element, status) { snip._(element, function() { if (status == 'pending') element.classList.add('pending'); else{ element.classList.remove('pending'); if (status == 'success'){ element.classList.add('success'); setTimeout(function(){element.classList.remove('success');}, 2000); } else if (status == 'error') element.classList.add('error'); } }); } //================================================================== // MARK: day.js 1.8.16 //================================================================== //https://github.com/iamkun/dayjs // Plugins von unpkg.com, z.B. https://unpkg.com/dayjs-ext@2.2.0/plugin/customParseFormat.js !function (t, n) { "object" == typeof exports && "undefined" != typeof module ? module.exports = n() : "function" == typeof define && define.amd ? define(n) : t.dayjs = n() }(this, function () { "use strict"; var t = "millisecond", n = "second", e = "minute", r = "hour", i = "day", s = "week", u = "month", a = "quarter", o = "year", h = /^(\d{4})-?(\d{1,2})-?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d{1,3})?$/, f = /\[([^\]]+)]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, c = function (t, n, e) { var r = String(t); return !r || r.length >= n ? t : "" + Array(n + 1 - r.length).join(e) + t }, d = { s: c, z: function (t) { var n = -t.utcOffset(), e = Math.abs(n), r = Math.floor(e / 60), i = e % 60; return (n <= 0 ? "+" : "-") + c(r, 2, "0") + ":" + c(i, 2, "0") }, m: function (t, n) { var e = 12 * (n.year() - t.year()) + (n.month() - t.month()), r = t.clone().add(e, u), i = n - r < 0, s = t.clone().add(e + (i ? -1 : 1), u); return Number(-(e + (n - r) / (i ? r - s : s - r)) || 0) }, a: function (t) { return t < 0 ? Math.ceil(t) || 0 : Math.floor(t) }, p: function (h) { return { M: u, y: o, w: s, d: i, h: r, m: e, s: n, ms: t, Q: a }[h] || String(h || "").toLowerCase().replace(/s$/, "") }, u: function (t) { return void 0 === t } }, $ = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_") }, l = "en", m = {}; m[l] = $; var y = function (t) { return t instanceof v }, M = function (t, n, e) { var r; if (!t) return l; if ("string" == typeof t) m[t] && (r = t), n && (m[t] = n, r = t); else { var i = t.name; m[i] = t, r = i } return e || (l = r), r }, g = function (t, n, e) { if (y(t)) return t.clone(); var r = n ? "string" == typeof n ? { format: n, pl: e } : n : {}; return r.date = t, new v(r) }, D = d; D.l = M, D.i = y, D.w = function (t, n) { return g(t, { locale: n.$L, utc: n.$u }) }; var v = function () { function c(t) { this.$L = this.$L || M(t.locale, null, !0), this.parse(t) } var d = c.prototype; return d.parse = function (t) { this.$d = function (t) { var n = t.date, e = t.utc; if (null === n) return new Date(NaN); if (D.u(n)) return new Date; if (n instanceof Date) return new Date(n); if ("string" == typeof n && !/Z$/i.test(n)) { var r = n.match(h); if (r) return e ? new Date(Date.UTC(r[1], r[2] - 1, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, r[7] || 0)) : new Date(r[1], r[2] - 1, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, r[7] || 0) } return new Date(n) }(t), this.init() }, d.init = function () { var t = this.$d; this.$y = t.getFullYear(), this.$M = t.getMonth(), this.$D = t.getDate(), this.$W = t.getDay(), this.$H = t.getHours(), this.$m = t.getMinutes(), this.$s = t.getSeconds(), this.$ms = t.getMilliseconds() }, d.$utils = function () { return D }, d.isValid = function () { return !("Invalid Date" === this.$d.toString()) }, d.isSame = function (t, n) { var e = g(t); return this.startOf(n) <= e && e <= this.endOf(n) }, d.isAfter = function (t, n) { return g(t) < this.startOf(n) }, d.isBefore = function (t, n) { return this.endOf(n) < g(t) }, d.$g = function (t, n, e) { return D.u(t) ? this[n] : this.set(e, t) }, d.year = function (t) { return this.$g(t, "$y", o) }, d.month = function (t) { return this.$g(t, "$M", u) }, d.day = function (t) { return this.$g(t, "$W", i) }, d.date = function (t) { return this.$g(t, "$D", "date") }, d.hour = function (t) { return this.$g(t, "$H", r) }, d.minute = function (t) { return this.$g(t, "$m", e) }, d.second = function (t) { return this.$g(t, "$s", n) }, d.millisecond = function (n) { return this.$g(n, "$ms", t) }, d.unix = function () { return Math.floor(this.valueOf() / 1e3) }, d.valueOf = function () { return this.$d.getTime() }, d.startOf = function (t, a) { var h = this, f = !!D.u(a) || a, c = D.p(t), d = function (t, n) { var e = D.w(h.$u ? Date.UTC(h.$y, n, t) : new Date(h.$y, n, t), h); return f ? e : e.endOf(i) }, $ = function (t, n) { return D.w(h.toDate()[t].apply(h.toDate(), (f ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(n)), h) }, l = this.$W, m = this.$M, y = this.$D, M = "set" + (this.$u ? "UTC" : ""); switch (c) { case o: return f ? d(1, 0) : d(31, 11); case u: return f ? d(1, m) : d(0, m + 1); case s: var g = this.$locale().weekStart || 0, v = (l < g ? l + 7 : l) - g; return d(f ? y - v : y + (6 - v), m); case i: case "date": return $(M + "Hours", 0); case r: return $(M + "Minutes", 1); case e: return $(M + "Seconds", 2); case n: return $(M + "Milliseconds", 3); default: return this.clone() } }, d.endOf = function (t) { return this.startOf(t, !1) }, d.$set = function (s, a) { var h, f = D.p(s), c = "set" + (this.$u ? "UTC" : ""), d = (h = {}, h[i] = c + "Date", h.date = c + "Date", h[u] = c + "Month", h[o] = c + "FullYear", h[r] = c + "Hours", h[e] = c + "Minutes", h[n] = c + "Seconds", h[t] = c + "Milliseconds", h)[f], $ = f === i ? this.$D + (a - this.$W) : a; if (f === u || f === o) { var l = this.clone().set("date", 1); l.$d[d]($), l.init(), this.$d = l.set("date", Math.min(this.$D, l.daysInMonth())).toDate() } else d && this.$d[d]($); return this.init(), this }, d.set = function (t, n) { return this.clone().$set(t, n) }, d.get = function (t) { return this[D.p(t)]() }, d.add = function (t, a) { var h, f = this; t = Number(t); var c = D.p(a), d = function (n) { var e = g(f); return D.w(e.date(e.date() + Math.round(n * t)), f) }; if (c === u) return this.set(u, this.$M + t); if (c === o) return this.set(o, this.$y + t); if (c === i) return d(1); if (c === s) return d(7); var $ = (h = {}, h[e] = 6e4, h[r] = 36e5, h[n] = 1e3, h)[c] || 1, l = this.valueOf() + t * $; return D.w(l, this) }, d.subtract = function (t, n) { return this.add(-1 * t, n) }, d.format = function (t) { var n = this; if (!this.isValid()) return "Invalid Date"; var e = t || "YYYY-MM-DDTHH:mm:ssZ", r = D.z(this), i = this.$locale(), s = this.$H, u = this.$m, a = this.$M, o = i.weekdays, h = i.months, c = function (t, r, i, s) { return t && (t[r] || t(n, e)) || i[r].substr(0, s) }, d = function (t) { return D.s(s % 12 || 12, t, "0") }, $ = i.meridiem || function (t, n, e) { var r = t < 12 ? "AM" : "PM"; return e ? r.toLowerCase() : r }, l = { YY: String(this.$y).slice(-2), YYYY: this.$y, M: a + 1, MM: D.s(a + 1, 2, "0"), MMM: c(i.monthsShort, a, h, 3), MMMM: h[a] || h(this, e), D: this.$D, DD: D.s(this.$D, 2, "0"), d: String(this.$W), dd: c(i.weekdaysMin, this.$W, o, 2), ddd: c(i.weekdaysShort, this.$W, o, 3), dddd: o[this.$W], H: String(s), HH: D.s(s, 2, "0"), h: d(1), hh: d(2), a: $(s, u, !0), A: $(s, u, !1), m: String(u), mm: D.s(u, 2, "0"), s: String(this.$s), ss: D.s(this.$s, 2, "0"), SSS: D.s(this.$ms, 3, "0"), Z: r }; return e.replace(f, function (t, n) { return n || l[t] || r.replace(":", "") }) }, d.utcOffset = function () { return 15 * -Math.round(this.$d.getTimezoneOffset() / 15) }, d.diff = function (t, h, f) { var c, d = D.p(h), $ = g(t), l = 6e4 * ($.utcOffset() - this.utcOffset()), m = this - $, y = D.m(this, $); return y = (c = {}, c[o] = y / 12, c[u] = y, c[a] = y / 3, c[s] = (m - l) / 6048e5, c[i] = (m - l) / 864e5, c[r] = m / 36e5, c[e] = m / 6e4, c[n] = m / 1e3, c)[d] || m, f ? y : D.a(y) }, d.daysInMonth = function () { return this.endOf(u).$D }, d.$locale = function () { return m[this.$L] }, d.locale = function (t, n) { if (!t) return this.$L; var e = this.clone(); return e.$L = M(t, n, !0), e }, d.clone = function () { return D.w(this.toDate(), this) }, d.toDate = function () { return new Date(this.$d) }, d.toJSON = function () { return this.isValid() ? this.toISOString() : null }, d.toISOString = function () { return this.$d.toISOString() }, d.toString = function () { return this.$d.toUTCString() }, c }(); return g.prototype = v.prototype, g.extend = function (t, n) { return t(n, v, g), g }, g.locale = M, g.isDayjs = y, g.unix = function (t) { return g(1e3 * t) }, g.en = m[l], g.Ls = m, g }); //Plugin weekOfYear.js !function (e, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : e.dayjs_plugin_weekOfYear = t() }(this, function () { "use strict"; var e = "week", t = "year"; return function (i, n) { var r = n.prototype; r.week = function (i) { if (void 0 === i && (i = null), null !== i) return this.add(7 * (i - this.week()), "day"); var n = this.$locale().yearStart || 1; if (11 === this.month() && this.date() > 25) { var r = this.startOf(t).add(1, t).date(n), f = this.endOf(e); if (r.isBefore(f)) return 1 } var s = this.startOf(t).date(n).startOf(e).subtract(1, "millisecond"), a = this.diff(s, e, !0); return a < 0 ? this.startOf("week").week() : Math.ceil(a) }, r.weeks = function (e) { return void 0 === e && (e = null), this.week(e) } } }); dayjs.extend(dayjs_plugin_weekOfYear); //Plugin isoweek.js !function (e, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = "undefined" != typeof globalThis ? globalThis : e || self).dayjs_plugin_isoWeek = t() }(this, (function () { "use strict"; var e = "day"; return function (t, i, s) { var a = function (t) { return t.add(4 - t.isoWeekday(), e) }, d = i.prototype; d.isoWeekYear = function () { return a(this).year() }, d.isoWeek = function (t) { if (!this.$utils().u(t)) return this.add(7 * (t - this.isoWeek()), e); var i, d, n, o, r = a(this), u = (i = this.isoWeekYear(), d = this.$u, n = (d ? s.utc : s)().year(i).startOf("year"), o = 4 - n.isoWeekday(), n.isoWeekday() > 4 && (o += 7), n.add(o, e)); return r.diff(u, "week") + 1 }, d.isoWeekday = function (e) { return this.$utils().u(e) ? this.day() || 7 : this.day(this.day() % 7 ? e : e - 7) }; var n = d.startOf; d.startOf = function (e, t) { var i = this.$utils(), s = !!i.u(t) || t; return "isoweek" === i.p(e) ? s ? this.date(this.date() - (this.isoWeekday() - 1)).startOf("day") : this.date(this.date() - 1 - (this.isoWeekday() - 1) + 7).endOf("day") : n.bind(this)(e, t) } } })); dayjs.extend(dayjs_plugin_isoWeek); //Plugin plugin/advancedFormat.js !function (e, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : e.dayjs_plugin_advancedFormat = t() }(this, function () { "use strict"; return function (e, t, r) { var n = t.prototype, o = n.format; r.en.ordinal = function (e) { var t = ["th", "st", "nd", "rd"], r = e % 100; return "[" + e + (t[(r - 20) % 10] || t[r] || t[0]) + "]" }, n.format = function (e) { var t = this, r = this.$locale(), n = this.$utils(), i = (e || "YYYY-MM-DDTHH:mm:ssZ").replace(/\[([^\]]+)]|Q|wo|gggg|Do|X|x|k{1,2}|S/g, function (e) { switch (e) { case "Q": return Math.ceil((t.$M + 1) / 3); case "Do": return r.ordinal(t.$D); case "gggg": return t.weekYear(); case "wo": return r.ordinal(t.week(), "W"); case "k": case "kk": return n.s(String(0 === t.$H ? 24 : t.$H), "k" === e ? 1 : 2, "0"); case "X": return Math.floor(t.$d.getTime() / 1e3); case "x": return t.$d.getTime(); default: return e } }); return o.bind(this)(i) } } }); dayjs.extend(dayjs_plugin_advancedFormat); //Plugin plugin/localizedFormat.js !function (e, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : e.dayjs_plugin_localizedFormat = t() }(this, function () { "use strict"; return function (e, t, o) { var n = t.prototype, r = n.format, M = { LTS: "h:mm:ss A", LT: "h:mm A", L: "MM/DD/YYYY", LL: "MMMM D, YYYY", LLL: "MMMM D, YYYY h:mm A", LLLL: "dddd, MMMM D, YYYY h:mm A" }; o.en.formats = M; n.format = function (e) { void 0 === e && (e = "YYYY-MM-DDTHH:mm:ssZ"); var t = this.$locale().formats, o = void 0 === t ? {} : t, n = e.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g, function (e, t, n) { var r = n && n.toUpperCase(); return t || o[n] || M[n] || o[r].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g, function (e, t, o) { return t || o.slice(1) }) }); return r.call(this, n) } } }); dayjs.extend(dayjs_plugin_localizedFormat); //Plugin plugin/customParseFormat.js !function (n, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : n.dayjs_plugin_customParseFormat = t() }(this, function () { "use strict"; var i = /(\[[^[]*\])|([-:/.()\s]+)|(A|a|YYYY|YY?|MM?|DD?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g, n = /\d\d/, t = /\d\d?/, r = /[+-]\d\d:?\d\d/, a = {}, f = {}, y = {}, m = [31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; function w(n) { var c = n.match(i); if (!c) throw new Error('Invalid format: "' + n + '".'); for (var d = c.length, t = 0; t < d; t += 1) { var r = c[t], e = a[r], o = f[r]; c[t] = o ? { regex: e, parser: o } : r.replace(/^\[|\]$/g, "") } return function (n) { for (var t = {}, r = 0, e = 0; r < d; r += 1) { var o = c[r]; if ("string" == typeof o) { if (n.indexOf(o, e) !== e) { var i = n.substr(e, o.length); throw new Error('Expected "' + o + '" at character ' + e + ', found "' + i + '".') } e += o.length } else { var a = o.regex, f = o.parser, s = n.substr(e), u = a.exec(s); if (!u || 0 !== u.index) throw new Error('Matching "' + a + '" at character ' + e + ' failed with "' + s + '".'); var h = u[0]; f.call(t, h), e += h.length } } return function (n) { var t, r, e = n.day, o = n.month; if (t = 2 === o ? (r = n.year) % 4 == 0 && r % 100 != 0 || r % 400 == 0 ? 29 : 28 : m[o - 1], !(1 <= e && e <= t)) throw new Error('Invalid day: "' + e + '".') }(t), function (n) { var t = n.afternoon; if (void 0 !== t) { var r = n.hours; t ? r < 12 && (n.hours += 12) : 12 === r && (n.hours = 0), delete n.afternoon } }(t), t } } function e(n, t) { a[n] = t } function o(n, r, e) { var t; "string" == typeof n && (n = [n]), t = "string" == typeof r ? e ? function (n) { var t = +n; if (!e(t)) throw new Error("Invalid " + r + ': "' + n + '".'); this[r] = t } : function (n) { this[r] = +n } : r; for (var o = 0, i = n.length; o < i; o += 1)f[n[o]] = t } e("A", /[AP]M/), o(["A"], function (n) { this.afternoon = "PM" === n }), e("a", /[ap]m/), o(["a"], function (n) { this.afternoon = "pm" === n }), e("S", /\d/), e("SS", n), e("SSS", /\d{3}/); for (var s = function (n, t) { o(n, function (n) { this.milliseconds = +n * t }) }, u = "S", h = 100; 1 <= h; u += "S", h /= 10)s(u, h); e("s", t), e("ss", n), o(["s", "ss"], "seconds", function (n) { return n <= 59 }), e("m", t), e("mm", n), o(["m", "mm"], "minutes", function (n) { return n <= 59 }), e("H", t), e("h", t), e("HH", n), e("hh", n), o(["H", "HH"], "hours", function (n) { return n <= 23 }), o(["h", "hh"], "hours", function (n) { return 1 <= n && n <= 12 }), e("D", t), e("DD", n), o(["D", "DD"], "day"), e("M", t), e("MM", n), o(["M", "MM"], "month", function (n) { return 1 <= n && n <= 12 }), e("Y", /[+-]?\d+/), e("YY", n), e("YYYY", /\d{4}/), o(["Y", "YYYY"], "year"), o("YY", function (n) { n = +n, this.year = n + (68 < n ? 1900 : 2e3) }), e("z", /[A-Z]{3,4}/), o("z", function (n) { (this.zone || (this.zone = {})).abbreviation = n }), e("Z", r), e("ZZ", r), o(["Z", "ZZ"], function (n) { (this.zone || (this.zone = {})).offset = function (n) { var t = n.match(/([+-]|\d\d)/g), r = 60 * t[1] + +t[2], e = 0 === r ? 0 : "+" === t[0] ? -r : r; if (!(e % 15 == 0 && Math.abs(e) <= 765)) throw new Error('Invalid time zone offset: "' + n + '".'); return e }(n) }); return function (n, t) { var r = t.prototype, p = r.parse; r.parse = function (n) { var t, r, e, o = n.date, i = n.format; if (i) { try { var a, f = (t = o, (e = y[r = i]) || (e = w(r), y[r] = e), e(t)), s = f.year, u = f.month, h = f.day, c = f.hours, d = f.minutes, m = f.seconds, v = f.milliseconds, l = f.zone; if (l) { var Y = Date.UTC(s, u - 1, h, c || 0, d || 0, m || 0, v || 0) + 60 * l.offset * 1e3; a = new Date(Y) } else a = new Date(s, u - 1, h, c || 0, d || 0, m || 0, v || 0); this.$d = a } catch (n) { this.$d = new Date(Number.NaN) } this.init(n) } else p.call(this, n) } } }); dayjs.extend(dayjs_plugin_customParseFormat); //Plugin plugin/isSameOrBefore.js !function (e, i) { "object" == typeof exports && "undefined" != typeof module ? module.exports = i() : "function" == typeof define && define.amd ? define(i) : (e = "undefined" != typeof globalThis ? globalThis : e || self).dayjs_plugin_isSameOrBefore = i() }(this, (function () { "use strict"; return function (e, i) { i.prototype.isSameOrBefore = function (e, i) { return this.isSame(e, i) || this.isBefore(e, i) } } })); dayjs.extend(dayjs_plugin_isSameOrBefore); //Plugin plugin/isSameOrAfter.js !function (e, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = "undefined" != typeof globalThis ? globalThis : e || self).dayjs_plugin_isSameOrAfter = t() }(this, (function () { "use strict"; return function (e, t) { t.prototype.isSameOrAfter = function (e, t) { return this.isSame(e, t) || this.isAfter(e, t) } } })); dayjs.extend(dayjs_plugin_isSameOrAfter); //locale/en.js !function (e, n) { "object" == typeof exports && "undefined" != typeof module ? module.exports = n() : "function" == typeof define && define.amd ? define(n) : e.dayjs_locale_en = n() }(this, function () { "use strict"; return { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_") } }); //locale/de.js !function (e, n) { "object" == typeof exports && "undefined" != typeof module ? module.exports = n(require("dayjs")) : "function" == typeof define && define.amd ? define(["dayjs"], n) : e.dayjs_locale_de = n(e.dayjs) }(this, function (e) { "use strict"; e = e && e.hasOwnProperty("default") ? e.default : e; var n = { name: "de", weekdays: "Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"), weekdaysShort: "So._Mo._Di._Mi._Do._Fr._Sa.".split("_"), weekdaysMin: "So_Mo_Di_Mi_Do_Fr_Sa".split("_"), months: "Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"), monthsShort: "Jan_Feb_März_Apr_Mai_Juni_Juli_Aug_Sept_Okt_Nov_Dez".split("_"), ordinal: function (e) { return e + "." }, weekStart: 1, formats: { LTS: "HH:mm:ss", LT: "HH:mm", L: "DD.MM.YYYY", LL: "D. MMMM YYYY", LLL: "D. MMMM YYYY HH:mm", LLLL: "dddd, D. MMMM YYYY HH:mm" }, relativeTime: { future: "in %s", past: "vor %s", s: "wenigen Sekunden", m: "einer Minute", mm: "%d Minuten", h: "einer Stunde", hh: "%d Stunden", d: "einem Tag", dd: "%d Tagen", M: "einem Monat", MM: "%d Monaten", y: "einem Jahr", yy: "%d Jahren" } }; return e.locale(n, null, !0), n });