class SystemSettings { static setBasePath(basePath) { SystemSettings._basePath = basePath; } static getBasePath() { return SystemSettings._basePath; } } SystemSettings.setBasePath("/"); class MenuAction { constructor(title, callback, showFor, order) { this.title = Helper.nonNull(title, null); this.callback = callback; this.showFor = Helper.nonNull(showFor, MenuAction.SHOW_FOR_MEDIUM); this.order = Helper.nonNull(order, 1000); this._menu = null; this._activated = true; this._visible = true; this.id = MenuAction.maxId++; this._icon = null; this._shouldTranslate = true; this._copies = []; } setTitle(title) { this.title = title; } setShouldTranslate(shouldTranslate) { this._shouldTranslate = shouldTranslate; } getTitle() { return this.title; } getShouldTranslate() { return this._shouldTranslate; } remove(removeCopies) { removeCopies = Helper.nonNull(removeCopies, false); if (Helper.isNotNull(this._menu)) { console.log(this._menu); this._menu.removeAction(this); this._menu = null; } if (removeCopies) { for (let i = 0, n = this._copies.length; i < n; i++) { this._copies[i].remove(); } } } getMenu() { return this._menu; } setMenu(value) { this._menu = value; } getVisible() { return this._visible; } setVisible(value) { if (value !== this._visible) { this._visible = value; this.redraw(); } } getActivated() { return this._activated; } getIcon() { return this._icon; } setIcon(value) { this._icon = value; } getId() { return this.id; } redraw() { if (Helper.isNotNull(this._menu)) { this._menu.updateAction(this); } } copy(instance){ let copy = Helper.nonNull(instance, new MenuAction()); copy.title = this.title; copy.callback = this.callback; copy.showFor = this.showFor; copy.order = this.order; copy._activated = this._activated; copy._visible = this._visible; copy._icon = this._icon; copy._shouldTranslate = this._shouldTranslate; copy._menu = null; copy.id = MenuAction.maxId++; this._copies.push(copy); return copy; } redrawMenu() { if (Helper.isNotNull(this._menu)) { this._menu.redraw(); } } } MenuAction.maxId = 0; MenuAction.SHOW_ALWAYS = "always"; MenuAction.SHOW_FOR_MEDIUM = "medium"; MenuAction.SHOW_FOR_LARGE = "large"; MenuAction.SHOW_NEVER = "never"; class OpenSubmenuAction extends MenuAction { constructor(title, menu, showFor, order) { super(title, function (action) { action.getSubmenu().toggle(); action.redraw(); }, showFor, order); this.submenu = menu; menu.setParentAction(this); } getSubmenu() { return this.submenu; } copy(instance) { instance = super.copy(Helper.nonNull(instance, new OpenSubmenuAction(null, this.submenu.copy()))); return instance; } } class Menu { constructor(parentElementSelector) { this.actions = []; this.submenus = []; if (typeof parentElementSelector === 'string') { this.parentElements = document.querySelectorAll(parentElementSelector); } else if (Array.isArray(parentElementSelector)) { this.parentElements = parentElementSelector; } else { this.parentElements = [parentElementSelector]; } } copy(instance) { instance = Helper.nonNull(instance, new Menu([])); instance.actions = []; for (let i = 0, n = this.actions.length; i < n; i++) { instance.actions.push(this.actions[i].copy()); } instance.submenus = []; for (let i = 0, n = this.submenus.length; i < n; i++) { instance.submenus.push(this.submenus[i].copy()); } return instance; } addAction(action) { if (Helper.includesNot(this.actions, action)) { this.actions.push(action); this.redraw(); action.setMenu(this); if (action instanceof OpenSubmenuAction) { this.submenus.push(action.getSubmenu()); } } } draw() { if (Helper.isNotNull(this.parentElements)) { this.sortActions(); let actionElements = []; for (let i = 0, n = this.actions.length; i < n; i++) { let element = this.renderAction(this.actions[i]); this.actions[i]._htmlElement = element; actionElements.push(element); } for (let i = 0, n = this.parentElements.length; i < n; i++) { this.parentElements[i].removeAllChildren(); for (let i2 = 0, n2 = actionElements.length; i2 < n2; i2++) { this.parentElements[i].appendChild(Helper.cloneNode(actionElements[i2])); } this.parentElements[i].onclick = this._getOnClickListener(); } } } _getOnClickListener() { let menu = this; return function (event) { let _element = event.target; if (_element.matches('.action') || _element.matches('.action *')) { // while (!_element.matches('.action > a')) { // _element = _element.parentNode; // } _element = _element.closest(".action"); let actionId = parseInt(_element.dataset["id"]); for (let i = 0, n = menu.actions.length; i < n; i++) { if (menu.actions[i].id === actionId) { if (typeof menu.actions[i].callback === 'function' && menu.actions[i].getActivated()) { menu.actions[i].callback(menu.actions[i], event); } return menu.actions[i]; } } for (let i = 0, n = menu.submenus.length; i < n; i++) { if (menu.submenus[i].click(actionId, event)) { return menu.submenus[i]; } } } return null; }; } /** @protected */ renderAction(action) { let aElement = document.createElement("a"); if (typeof action.callback === 'string') { aElement.href = action.callback; } if (Helper.isNotNull(action.getIcon())) { let iconElement = document.createElement("img"); iconElement.src = action.getIcon(); iconElement.classList.add('action-image'); if (action.getShouldTranslate()) { iconElement.dataset["translationTitle"] = action.title; } aElement.appendChild(iconElement); } let title = action.getTitle(); if (action.getShouldTranslate()) { title = Translator.makePersistentTranslation(title); } else { title = document.createTextNode(title); } aElement.appendChild(title); return this.renderLiElement(aElement, action) } /** @protected */ renderLiElement(aElement, action) { let liElement = document.createElement("li"); liElement.classList.add('action'); liElement.appendChild(aElement); liElement.dataset["id"] = action.id; if (Helper.isNotNull(action.getIcon())) { liElement.classList.add("img"); } if (!action.getVisible()) { liElement.classList.add("hidden"); } if (action instanceof OpenSubmenuAction) { action.getSubmenu().draw(); liElement.appendChild(action.getSubmenu().getParentElement()); liElement.classList.add("is-dropdown-submenu-parent"); liElement.classList.add("opens-right"); } return liElement; } /** @private */ sortActions() { this.actions = this.actions.sort(function (first, second) { return first.order - second.order; }); } _getElementsForAction(action){ let elements = []; for (let i = 0, n = this.parentElements.length; i < n; i++) { let elem = this.parentElements[i].querySelector("[data-id=\""+action.getId()+"\"]"); Helper.isNull(elem) || elements.push(elem); } return elements } updateAction(action) { let oldElements = this._getElementsForAction(action); if (oldElements.length === 0) { return; } let element = this.renderAction(action); action._htmlElement = element; for (let i = 0, n = oldElements.length; i < n; i++) { oldElements[i].replaceWith(Helper.cloneNode(element)); } } removeAction(action) { let index = this.actions.indexOf(action); if (index > 0) { this.actions.splice(index, 1); let oldElements = this._getElementsForAction(action); for (let i = 0, n = oldElements.length; i < n; i++) { oldElements[i].remove(); } if (action instanceof OpenSubmenuAction) { let index = this.submenus.indexOf(action.getSubmenu()); this.submenus.splice(index, 1); } } } redraw() { this.draw(); } } Menu.SHOW_ALWAYS = "always"; Menu.SHOW_FOR_MEDIUM = "medium"; Menu.SHOW_FOR_SMEDIUM = "smedium"; Menu.SHOW_FOR_LARGE = "large"; Menu.SHOW_NEVER = "never"; class Submenu extends Menu { constructor() { let menuElement = document.createElement("ul"); menuElement.classList.add("menu"); menuElement.classList.add("vertical"); menuElement.classList.add("submenu"); menuElement.classList.add("is-dropdown-submenu"); menuElement.classList.add("first-sub"); super(menuElement); this.parentAction = null; this.isOpen = false; } copy(instance) { instance = super.copy(Helper.nonNull(instance, new Submenu())); instance.parentElements = []; for (let i = 0, n = this.parentElements.length; i < n; i++) { instance.parentElements.push(Helper.cloneNode(this.parentElements[i])); } instance.parentAction = this.parentAction; instance.isOpen = this.isOpen; return instance; } setParentAction(action) { this.parentAction = action; } draw() { super.draw(); if (Helper.isNotNull(this.parentElements)) { let self = this; for (let i = 0, n = this.parentElements.length; i < n; i++) { let closeListener = document.createElement("div"); closeListener.classList.add("close-listener"); closeListener.onclick = function(e){ console.log(e); self.close(); }; this.parentElements[i].insertBefore(closeListener, this.parentElements[i].firstElementChild); } } } getParentElement() { return this.parentElements[0]; } _getOnClickListener() { return function () {}; } click(actionId, event) { for (let i = 0, n = this.actions.length; i < n; i++) { if (this.actions[i].id === actionId) { if (typeof this.actions[i].callback === 'function' && this.actions[i].getActivated()) { this.actions[i].callback(this.actions[i], event); } this.close(); return true; } } return false; } toggle() { if (this.isOpen) { this.close(); } else { this.open(); } } open() { this.isOpen = true; for (let i = 0, n = this.parentElements.length; i < n; i++) { this.parentElements[i].classList.add("js-dropdown-active"); } if (Helper.isNotNull(this.parentAction)) { this.parentAction.redraw(); } } close() { this.isOpen = false; for (let i = 0, n = this.parentElements.length; i < n; i++) { this.parentElements[i].classList.remove("js-dropdown-active"); } if (Helper.isNotNull(this.parentAction)) { this.parentAction.redraw(); } } } class TranslatorDB { constructor() { this._indexedDB = indexedDB || mozIndexedDB || webkitIndexedDB || msIndexedDB; this._version = 3; let self = this; this._dbPromise = new Promise(function (resolve, reject) { let request = self._indexedDB.open("Translator", self._version); request.onupgradeneeded = function (event) { let db = event.target.result; self._upgradeDb(db); }; request.onsuccess = function (event) { let db = event.target.result; resolve(db); }; request.onerror = function (event) { reject(event); }; }).catch(function(e){ console.error(e); }); } _upgradeDb(db) { try { db.deleteObjectStore("currentLang"); db.deleteObjectStore("translations"); } catch (e) { console.warn(e); } let currentLangObjectStore = db.createObjectStore("currentLang", {"keyPath": "id"}); let translationsObjectStore = db.createObjectStore("translations", {"keyPath": ["lang","key"]}); translationsObjectStore.createIndex("lang", "lang", {"unique": false}); } setLanguage(lang) { this._dbPromise.then(function (db) { let transaction = TranslatorDB._openTransaction(["currentLang"], "readwrite", db); let currentLangObjectStore = transaction.objectStore("currentLang"); currentLangObjectStore.put({"id": 1, "lang": lang}); }).catch(function(e){ console.error(e); }); } saveTranslationsForLang(lang, translations) { return this._dbPromise.then(function (db) { return new Promise(function (resolve) { let transaction = TranslatorDB._openTransaction(["translations"], "readwrite", db); let translationsObjectStore = transaction.objectStore("translations"); for (let k in translations) { translationsObjectStore.put({"lang": lang, "key": k, "translation": translations[k]}); } transaction.oncomplete = function () { resolve(); }; }); }).catch(function(e){ // console.error(e); }); } loadTranslationsForLang(lang) { return this._dbPromise.then(function (db) { return new Promise(function (resolve) { let transaction = TranslatorDB._openTransaction(["translations"], "readonly", db); let translationsObjectStore = transaction.objectStore("translations"); let index = translationsObjectStore.index("lang"); let request = index.openCursor(IDBKeyRange.only(lang)); let translations = {}; request.onsuccess = function (e) { let cursor = e.target.result; if (cursor) { let translation = cursor.value; translations[translation["key"]] = translation["translation"]; cursor.continue(); } }; transaction.oncomplete = function(){ resolve(translations); }; }); }).catch(function(e){ console.error(e); return {}; }); } getLanguage() { return this._dbPromise.then(function (db) { return new Promise(function (resolve) { let transaction = TranslatorDB._openTransaction(["currentLang"], "readonly", db); let currentLangObjectStore = transaction.objectStore("currentLang"); let req = currentLangObjectStore.get(1); req.onsuccess = function (e) { let data = e.currentTarget.result; if (data) { resolve(data["lang"]); } else { resolve(null); } }; req.onerror = function (e) { resolve(null); }; }); }).catch(function(e){ // console.error(e); }); } static _openTransaction(name, transactionMode, db) { let transaction = null; try { transaction = db.transaction(name, transactionMode); } catch (e) { console.warn(e); transaction = db.transaction(name); } return transaction; } } class Translator { constructor() { this._translations = []; this._db = new TranslatorDB(); this._currentLanguage = null; this._supportedLanguages = Translator.supportedLanguages; this._baseLanguage = Translator.baseLanguage; this._languageBasePath = Translator.languageBasePath; this._markUntranslatedTranslations = Translator.markUntranslatedTranslations; this._markTranslations = Translator.markTranslations; let self = this; this._initPromise = this.loadBaseLanguage().then(function () { return self.loadUserLanguage(); }); } _loadLanguage(language) { let self = this; return fetch(Helper.basePath(this._languageBasePath + language + ".json")).then(function (result) { return result.json(); }).then(function (res) { self._translations[language] = Object.assign(res, self._translations[language]); self._db.saveTranslationsForLang(language, self._translations[language]); }).catch(function (err) { console.error("could not load lang " + language + " because of error: ", err); }); } loadBaseLanguage() { let self = this; return this._loadLanguage(this._baseLanguage).then(function () { self._currentLanguage = self._baseLanguage; if (typeof document !== 'undefined') { document.getElementsByTagName("html")[0].setAttribute("lang", self._baseLanguage); } }); }; static setLanguage(language) { let instance = Translator.getInstance(); if (instance) { return instance.setLanguage(language); } } setLanguage(language) { if (this._currentLanguage === language) { this.updateTranslations(); return Promise.resolve(); } if (this._supportedLanguages.indexOf(language) === -1) { return Promise.resolve(); } this._currentLanguage = language; if (typeof localStorage !== 'undefined') { localStorage.setItem("language", language); } this._db.setLanguage(language); let self = this; return this._loadLanguage(language).then(function () { if (typeof document !== 'undefined') { document.getElementsByTagName("html")[0].setAttribute("lang", language); } self.updateTranslations(); }); } static translate(key, args) { let instance = Translator.getInstance(); if (instance) { return instance.translate(key, args); } return ""; } translate(key, args) { if (typeof key === 'object' && Helper.isNotNull(key)) { key = this.addDynamicTranslation(key); } let translation = null; if (Helper.isNotNull(this._translations[this._currentLanguage]) && Helper.isNotNull(this._translations[this._currentLanguage][key])) { translation = this._translations[this._currentLanguage][key]; } if (Helper.isNull(translation)) { if (Translator.logMissingTranslations) { console.warn("missing translation for language " + this._currentLanguage + " and key " + key); } if (Helper.isNotNull(this._translations[this._baseLanguage])) { translation = this._translations[this._baseLanguage][key]; } if (Helper.isNull(translation)) { if (Translator.logMissingTranslations) { console.error("missing base translation for key " + key + ". FIX IT"); } translation = key; } if (this._markUntranslatedTranslations) { translation = ">>" + translation + "<<"; } } if (this._markTranslations) { translation = "$" + translation + "$"; } if (args !== undefined) { translation = translation.format(args); } return translation; } static addDynamicTranslation(trans) { let instance = Translator.getInstance(); if (instance) { return instance.addDynamicTranslation(trans); } } addDynamicTranslation(trans) { let key = trans["key"]; delete trans["key"]; for (let lang in trans) { if (trans.hasOwnProperty(lang)) { if (Helper.isNull(this._translations[lang])) { this._translations[lang] = {}; } this._translations[lang][key] = trans[lang]; } } return key; } updateTranslations() { if (typeof document !== 'undefined') { let elements = document.querySelectorAll("[data-translation]"); for (let i = 0, max = elements.length; i < max; i++) { if (elements[i].dataset["translation"] != "") { try { elements[i].innerHTML = this.translate(elements[i].dataset["translation"], (elements[i].dataset["translationArgs"] !== undefined) ? JSON.parse(elements[i].dataset["translationArgs"]) : undefined); } catch (err) { console.error("wrong configured translation: " + err); } } for (let k in elements[i].dataset) { if (k.startsWith("translation") && !k.endsWith("Args")) { try { elements[i][k.substr(11).toLowerCase()] = this.translate(elements[i].dataset[k], (elements[i].dataset[k + "Args"] !== undefined) ? JSON.parse(elements[i].dataset[k + "Args"]) : undefined); } catch (err) { console.error("wrong configured translation: " + err); } } } } } } loadUserLanguage() { let userLanguage = localStorage.getItem("language"); if (Helper.isNull(userLanguage) || this._supportedLanguages.indexOf(userLanguage) === -1) { let userLanguages = []; if (Helper.isNotNull(navigator.languages)) { userLanguages = navigator.languages.slice(0); //.slice(0) klont das Array. Behebt einen Bug in Firefox } if (navigator.language !== undefined) { userLanguages.push(navigator.language); } //sicherstellen, dass überhaupt eine Sprache gefunden wird userLanguages.push(this._baseLanguage); if (userLanguages !== undefined) { for (let i = 0, numLanguages = userLanguages.length; i < numLanguages; i++) { if (this._supportedLanguages.indexOf(userLanguages[i]) !== -1) { userLanguage = userLanguages[i]; break; } } } } return this.setLanguage(userLanguage.toLowerCase()) } static makePersistentTranslation(key, args, tag) { tag = Helper.nonNull(tag, "span"); if (typeof key === 'object') { key = Translator.addDynamicTranslation(key); } if (typeof document !== 'undefined') { let htmlElem = document.createElement(tag); htmlElem.dataset["translation"] = key; if (args !== undefined) { htmlElem.dataset["translationArgs"] = JSON.stringify(args); } htmlElem.innerHTML = Translator.translate(key, args); return htmlElem; } } static generateChangeLanguageMenuAction() { let submenu = new Submenu(); submenu.addAction(new MenuAction("en", function () { Translator.getInstance().setLanguage("en"); })); submenu.addAction(new MenuAction("de", function () { Translator.getInstance().setLanguage("de"); })); return new OpenSubmenuAction("current-lang", submenu, Menu.SHOW_ALWAYS) } static init() { Translator.instance = new Translator(); // Translator.loadBaseLanguage().then(function () { // Translator.loadUserLanguage(); // }); } static getInstance() { return Translator.instance; } } Translator.logMissingTranslations = false; Translator.instance = null; Translator.baseLanguage = "en"; Translator.supportedLanguages = [ "de", "en" ]; Translator.markUntranslatedTranslations = true; Translator.markTranslations = false; Translator.languageBasePath = "js/lang/"; Translator.currentLanguage = null; Translator.translations = {}; class Helper { static init() { Helper.heightMmToPxFactor = null; Helper.widthMmToPxFactor = null; } static includesNot(array, value, fromIndex) { return -1 === array.indexOf(value, fromIndex); } static includes(array, value, fromIndex) { return !Helper.includesNot(array, value, fromIndex); } static isSet() { if (arguments.length > 0) { const object = arguments[0]; let keys = Array.prototype.slice.call(arguments, 1); return (Helper.isNotNull(object) && (keys.length === 0 || Helper.isSet.apply(null, [object[keys[0]]].concat(keys.slice(1))))); } return false; } static isNull(variable) { return (variable === null || variable === undefined); } static isNotNull(variable) { return !Helper.isNull(variable); } static nonNull(val1, val2) { if (Helper.isNotNull(val1)) { return val1; } return val2; } static notEmpty(value) { return !Helper.empty(value); } static buildQuery(values) { let queryStrings = []; for (let k in values) { queryStrings.push(encodeURIComponent(k) + "=" + encodeURIComponent(values[k])); } return "?" + queryStrings.join("&"); } static empty(value) { return (Helper.isNull(value) || (typeof value === 'string' && value.trim() === "")) } static inflateElementsFromString(string) { let template = document.createElement('template'); template.innerHTML = string; return template.content.childNodes; } static createLoadingSymbol() { let svgNS = "http://www.w3.org/2000/svg"; let loader = document.createElement("div"); loader.className = 'loader'; let svg = document.createElementNS(svgNS, "svg"); svg.setAttribute('viewBox', "0 0 32 32"); svg.setAttribute("widh", "32"); svg.setAttribute("height", "32"); let circle = document.createElementNS(svgNS, "circle"); circle.setAttribute("id", "spinner"); circle.setAttribute("cx", "16"); circle.setAttribute("cy", "16"); circle.setAttribute("r", "14"); circle.setAttribute("fill", "none"); svg.appendChild(circle); loader.appendChild(svg); return loader; } static basePath(url) { return SystemSettings.getBasePath() + url; } static isMobileApple() { return navigator.userAgent.match(/iPhone|iPad|iPod/i); } static isMobile() { return (navigator.userAgent.match(/Android|BlackBerry|Opera Mini|IEMobile/i) !== null || Helper.isMobileApple() || (typeof window.orientation !== "undefined" || window.orientation === false || window.orientation === null)); } static select(e) { let range = document.createRange(); range.selectNodeContents(e); let sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } static format(number, leadingZeros) { number = "" + number; while (number.length < leadingZeros) { number = "0" + number; } return number; } static cloneNode(srcNode) { let destNode = srcNode.cloneNode(true); destNode.onclick = srcNode.onclick; return destNode; } static encodeToBase(stringToEncode, base) { let encodedString = ""; let charlength = Math.floor(Math.log(265) / Math.log(base)); for (let i = 0, n = stringToEncode.length; i < n; i++) { let value = stringToEncode.charCodeAt(i).toString(base); let joinLength = value.length % charlength; if (joinLength > 0) { let joinArray = new Array(charlength + 1 - (joinLength)); //+1, da join nur zwischen elemente einfügt value = joinArray.join("0") + value; } encodedString += value; } return encodedString; } static decodeToBase(stringToDecode, base) { let charlength = Math.floor(Math.log(265) / Math.log(base)); let values = stringToDecode.match(new RegExp(".{1," + charlength + "}", "g")) || []; let encodedString = ""; for (let i = 0, n = values.length; i < n; i++) { encodedString += String.fromCharCode(parseInt(values[i], base)); } return encodedString; } static toggleVisibility(elem) { if (elem.style.display === "none") { elem.style.display = ""; return true; } else { elem.style.display = "none"; return false; } } static print(content) { let printContent = document.getElementById("print-content"); if (content instanceof Element) { printContent.removeAllChildren(); printContent.appendChild(content); } else { printContent.innerHTML = content; } window.print(); } static strftime(sFormat, date, useUTC) { if (!(date instanceof Date)) date = new Date(date); useUTC = Helper.nonNull(useUTC, false); let nDay = (useUTC) ? date.getUTCDay() : date.getDay(), nDate = (useUTC) ? date.getUTCDate() : date.getDate(), nMonth = (useUTC) ? date.getUTCMonth() : date.getMonth(), nYear = (useUTC) ? date.getUTCFullYear() : date.getFullYear(), nHour = (useUTC) ? date.getUTCHours() : date.getHours(), aDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], aMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], aDayCount = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], isLeapYear = function () { if ((nYear & 3) !== 0) return false; return nYear % 100 !== 0 || nYear % 400 === 0; }, getThursday = function () { let target = new Date(date); target.setDate(nDate - ((nDay + 6) % 7) + 3); return target; }, zeroPad = function (nNum, nPad) { return ('' + (Math.pow(10, nPad) + nNum)).slice(1); }; return sFormat.replace(/%[a-z]/gi, function (sMatch) { return { '%a': Translator.makePersistentTranslation(aDays[nDay].slice(0, 3)).outerHTML, '%A': Translator.makePersistentTranslation(aDays[nDay]).outerHTML, '%b': Translator.makePersistentTranslation(aMonths[nMonth].slice(0, 3)).outerHTML, '%B': Translator.makePersistentTranslation(aMonths[nMonth]).outerHTML, '%c': date.toUTCString(), '%C': Math.floor(nYear / 100), '%d': zeroPad(nDate, 2), '%e': nDate, '%f': zeroPad(date.getTime() % 1000, 4), '%F': date.toISOString().slice(0, 10), '%G': getThursday().getFullYear(), '%g': ('' + getThursday().getFullYear()).slice(2), '%H': zeroPad(nHour, 2), '%I': zeroPad((nHour + 11) % 12 + 1, 2), '%j': zeroPad(aDayCount[nMonth] + nDate + ((nMonth > 1 && isLeapYear()) ? 1 : 0), 3), '%k': '' + nHour, '%l': (nHour + 11) % 12 + 1, '%m': zeroPad(nMonth + 1, 2), '%M': zeroPad(date.getMinutes(), 2), '%p': (nHour < 12) ? 'AM' : 'PM', '%P': (nHour < 12) ? 'am' : 'pm', '%s': Math.round(date.getTime() / 1000), '%S': zeroPad(date.getSeconds(), 2), '%u': nDay || 7, '%V': (function () { let target = getThursday(), n1stThu = target.valueOf(); target.setMonth(0, 1); let nJan1 = target.getDay(); if (nJan1 !== 4) target.setMonth(0, 1 + ((4 - nJan1) + 7) % 7); return zeroPad(1 + Math.ceil((n1stThu - target) / 604800000), 2); })(), '%w': '' + nDay, '%x': date.toLocaleDateString(), '%X': date.toLocaleTimeString(), '%y': ('' + nYear).slice(2), '%Y': nYear, '%z': date.toTimeString().replace(/.+GMT([+-]\d+).+/, '$1'), '%Z': date.toTimeString().replace(/.+\((.+?)\)$/, '$1') }[sMatch] || sMatch; }); } static cloneJson(obj) { // https://stackoverflow.com/questions/4120475/how-to-create-and-clone-a-json-object/17502990#17502990 let i; // basic type deep copy if (Helper.isNull(obj) || typeof obj !== 'object') { return obj } // array deep copy if (obj instanceof Array) { let cloneA = []; for (i = 0; i < obj.length; ++i) { cloneA[i] = Helper.cloneJson(obj[i]); } return cloneA; } if (obj instanceof Date) { return new Date(obj.getTime()); } // object deep copy let cloneO = {}; for (i in obj) { cloneO[i] = Helper.cloneJson(obj[i]); } return cloneO; } static htmlspecialcharsDecode(text) { const map = { '&': '&', '&': "&", '<': '<', '>': '>', '"': '"', ''': "'", '’': "’", '‘': "‘", '–': "–", '—': "—", '…': "…", '”': '”' }; if (Helper.isNotNull(text) && typeof text.replace === "function") { return text.replace(/\&[\w\d\#]{2,5}\;/g, function (m) { return map[m]; }); } return text; } static formDataFromObject(obj) { let formData = new FormData(); for (let k in obj) { formData.set(k, obj[k]); } return formData; } static scaleContentRecursive(element, content) { let elementStyle = window.getComputedStyle(element); let contentStyle = window.getComputedStyle(content); if (contentStyle.height > elementStyle.height || contentStyle.width > elementStyle.width) { return Helper.scaleDownContentRecursive(element, content); } } static scaleDownContentRecursive(element, content) { Helper.convertChildrenToRelativeRecursive(element); let elementStyle = window.getComputedStyle(element); let contentStyle = window.getComputedStyle(content); let runs = 0; let fontSize = parseFloat(contentStyle.getPropertyValue("font-size")); let width = contentStyle.width; let height = contentStyle.height; while (contentStyle.height > elementStyle.height || contentStyle.width > elementStyle.width) { fontSize *= 0.95; if (height > elementStyle.height) { height *= 0.95; } if (width > contentStyle.width) { width *= 0.95; } content.style["font-size"] = fontSize + "px"; content.style["max-height"] = height + "px"; content.style["max-width"] = width + "px"; runs++; if (runs > 2000) { console.log("breaked"); break; } } Helper.convertToRelative(content); contentStyle = window.getComputedStyle(content); content.style["font-size"] = (parseFloat(contentStyle.getPropertyValue("font-size")) / parseFloat(document.documentElement.clientHeight) * 100) + "vh"; } static convertChildrenToRelativeRecursive(element) { let children = element.childNodes; for (let i = 0, n = children.length; i < n; i++) { if (children[i] instanceof Element) { Helper.convertToRelative(children[i]); Helper.convertChildrenToRelativeRecursive(children[i]); } } } static convertToRelative(element) { let hasTransitionClass = (element.classList.contains("no-transtition")); element.classList.add("no-transition"); let parent = element.parentNode; console.log(element); let elementStyle = window.getComputedStyle(element); let parentStyle = window.getComputedStyle(parent); let fontSize = parseFloat(elementStyle.getPropertyValue("font-size")) / parseFloat(parentStyle.getPropertyValue("font-size")); let maxHeight = elementStyle.height; let maxWidth = elementStyle.width; let pHeight = parentStyle.height; let pWidth = parentStyle.width; let relativeAttributes = element.style; relativeAttributes['max-height'] = Math.floor(maxHeight / pHeight * 100) + "%"; relativeAttributes['margin-left'] = Math.floor(parseFloat(elementStyle.getPropertyValue('margin-left')) / pWidth * 100) + "%"; relativeAttributes['margin-right'] = Math.floor(parseFloat(elementStyle.getPropertyValue('margin-right')) / pWidth * 100) + "%"; relativeAttributes['margin-top'] = Math.floor(parseFloat(elementStyle.getPropertyValue('margin-top')) / pHeight * 100) + "%"; relativeAttributes['margin-bottom'] = Math.floor(parseFloat(elementStyle.getPropertyValue('margin-bottom')) / pHeight * 100) + "%"; relativeAttributes['max-width'] = Math.floor(maxWidth / pWidth * 100) + "%"; relativeAttributes["font-size"] = fontSize + "em"; // console.log(relativeAttributes); // element.css(relativeAttributes); if (!hasTransitionClass) { element.classList.remove("no-transition"); } } static isChrome() { let isChromium = window.chrome, winNav = window.navigator, vendorName = winNav.vendor, isOpera = winNav.userAgent.indexOf("OPR") > -1, isIEedge = winNav.userAgent.indexOf("Edge") > -1, isIOSChrome = winNav.userAgent.match("CriOS"); if (isIOSChrome) { return true; } else { return isChromium !== null && typeof isChromium !== "undefined" && vendorName === "Google Inc." && isOpera === false && isIEedge === false; } } static getIndexedObject(array, keyValue) { let obj = {}; for (let i = 0, n = array.length; i < n; i++) { obj[array[i][keyValue]] = array[i]; } return obj; } static invertKeyValues(obj) { let new_obj = {}; for (let prop in obj) { if (obj.hasOwnProperty(prop)) { new_obj[obj[prop]] = prop; } } return new_obj; } static toArray(object) { let res = []; for (let k in object) { res.push(object[k]); } return res; } } Helper.init(); class ThemeManager { static init() { ThemeManager.loadCurrentTheme(); } static changeCurrentTheme(newTheme) { let theme = null; if (typeof newTheme === 'string') { let themes = ThemeManager.themes.filter(function (theme) { return theme._name === newTheme; }); if (themes.length > 0) { theme = themes[0]; } } else if (ThemeManager.themes.indexOf(newTheme) !== -1) { theme = newTheme; } if (Helper.isNotNull(theme)) { localStorage.setItem("currentTheme", theme._name); let themePromise = new Promise(function (resolve) { document.querySelector("nav.top-bar").addEventListener("transitionend", function(){ resolve(); }); }); document.body.className = theme._className; ThemeManager.currentTheme = theme; for (let i = 0, n = ThemeManager.changeListeners.length; i < n; i++) { ThemeManager.changeListeners[i](ThemeManager.currentTheme, themePromise); } } } static addTheme(theme) { ThemeManager.themes.push(theme); } static loadCurrentTheme() { ThemeManager.changeCurrentTheme(localStorage.getItem("currentTheme")); if (Helper.isNull(ThemeManager.currentTheme)) { let className = document.body.className; let themes = ThemeManager.themes.filter(function (theme) { return theme._className === className; }); if (themes.length > 0) { ThemeManager.changeCurrentTheme(themes[0]); } else if (ThemeManager.themes.length > 0) { ThemeManager.changeCurrentTheme(ThemeManager.themes[0]); } } } static generateChangeThemeMenuAction() { return new MenuAction(ThemeManager.currentTheme._name, function (action) { let currentThemeIndex = ThemeManager.themes.indexOf(ThemeManager.currentTheme); let nextIndex = (currentThemeIndex + 1) % ThemeManager.themes.length; ThemeManager.changeCurrentTheme(ThemeManager.themes[nextIndex]); action.title = ThemeManager.currentTheme._name; action._menu.redraw(); }, Menu.SHOW_ALWAYS) } static addChangeListener(listener) { ThemeManager.changeListeners.push(listener); } } ThemeManager.themes = []; ThemeManager.changeListeners = []; class CookieCompliance { constructor(cookieContainerId) { this.cookieContainerId = cookieContainerId; this.dropCookie = true; this.cookieDuration = 365 * 10; this.cookieName = 'complianceCookie'; this.cookieValue = 'true'; } showIfNeeded() { if (CookieCompliance.checkCookie(this.cookieName) !== this.cookieValue) { this.show(); } } removeMe() { this.createCookie(this.cookieName, this.cookieValue, this.cookieDuration); } createCookie(name, value, days) { let expires; if (Helper.isNotNull(days)) { const date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); expires = "; expires=" + date.toGMTString(); } else { expires = ""; } if (this.dropCookie) { document.cookie = name + "=" + value + expires + "; path=/"; } } eraseCookie(name) { this.createCookie(name, "", -1); } static checkCookie(name) { const nameEQ = name + "="; const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { let c = cookies[i]; while (c.charAt(0) === ' ') { c = c.substring(1, c.length); } if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); } } return null; } show() { let cookieCompliance = this; const cookieMessage = document.getElementById(this.cookieContainerId); cookieMessage.style.display='block'; cookieMessage.querySelector("#close-cookie-msg").onclick = function(){ cookieCompliance.removeMe(); cookieMessage.remove(); }; } } class ActionBarMenu extends Menu { static init() { function parseStyleToObject(str) { let styleObject = {}; if (typeof str !== 'string') { return styleObject; } str = str.trim().slice(1, -1); // browsers re-quote string style values if (!str) { return styleObject; } styleObject = str.split('&').reduce(function (ret, param) { const parts = param.replace(/\+/g, ' ').split('='); let key = parts[0]; let val = parts[1]; key = decodeURIComponent(key); // missing `=` should be `null`: // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters val = val === undefined ? null : decodeURIComponent(val); if (!ret.hasOwnProperty(key)) { ret[key] = val; } else if (Array.isArray(ret[key])) { ret[key].push(val); } else { ret[key] = [ret[key], val]; } return ret; }, {}); return styleObject; } let cssStyle = document.getElementsByClassName('foundation-mq'); if (cssStyle.length === 0) { return; } let queries = []; cssStyle = parseStyleToObject(window.getComputedStyle(cssStyle[0]).getPropertyValue('font-family')); for (let key in cssStyle) { if (cssStyle.hasOwnProperty(key)) { queries.push({ _name: key, value: 'only screen and (min-width: ' + cssStyle[key] + ')' }); } } window.addEventListener('resize', function () { if (Helper.isNotNull(ActionBarMenu.currentMenu)) { ActionBarMenu.currentMenu.updateToggleButton(); } }); let responsiveMenu = document.getElementById("responsive-menu"); document.getElementById("responsive-menu-toggle").onclick = function () { if (window.getComputedStyle(responsiveMenu).getPropertyValue('display') === 'none') { responsiveMenu.style.display = 'block'; } else if (Helper.isNotNull(ActionBarMenu.currentMenu)) { ActionBarMenu.currentMenu.close(); } }; responsiveMenu.firstElementChild.addEventListener("click", function (e) { if (e.target === responsiveMenu.firstElementChild && Helper.isNotNull(ActionBarMenu.currentMenu)) { ActionBarMenu.currentMenu.close(); } } ); ActionBarMenu.queries = queries; } static _getCurrentSize() { let matched; for (let i = 0, n = ActionBarMenu.queries.length; i < n; i++) { let query = ActionBarMenu.queries[i]; if (matchMedia(query.value).matches) { matched = query; } } if (typeof matched === 'object') { return matched._name; } else { return matched; } } renderLiElement(aElement, action) { let liElement = super.renderLiElement(aElement, action); liElement.classList.add(action.showFor); return liElement; } static filterVisibleElements(elements) { let visibleElements = []; for (let i = 0, n = elements.length; i < n; i++) { if (!elements[i].classList.contains("hidden")) { visibleElements.push(elements[i]); } } return visibleElements; } updateToggleButton() { let size = ActionBarMenu._getCurrentSize(); let firstParentElement = this.parentElements[0]; if (ActionBarMenu.filterVisibleElements(firstParentElement.getElementsByClassName(Menu.SHOW_FOR_LARGE)).length > 0 && (size === "medium" || size === "smedium" || size === "small") || ActionBarMenu.filterVisibleElements(firstParentElement.getElementsByClassName(Menu.SHOW_FOR_MEDIUM)).length > 0 && (size === "smedium" || size === "small") || ActionBarMenu.filterVisibleElements(firstParentElement.getElementsByClassName(Menu.SHOW_FOR_SMEDIUM)).length > 0 && (size === "small") || ActionBarMenu.filterVisibleElements(firstParentElement.getElementsByClassName(Menu.SHOW_NEVER)).length > 0) { document.getElementById("responsive-menu-toggle").style.display = 'block'; } else { document.getElementById("responsive-menu-toggle").style.display = 'none'; if (Helper.isNotNull(ActionBarMenu.currentMenu)) { ActionBarMenu.currentMenu.close(); } } } _getOnClickListener() { let superListener = super._getOnClickListener(); return function (event) { let action = superListener(event); if (!(action instanceof OpenSubmenuAction) && Helper.isNotNull(ActionBarMenu.currentMenu)) { ActionBarMenu.currentMenu.close(); } } } draw(parentElement) { let returnValue = super.draw(parentElement); this.updateToggleButton(); ActionBarMenu.currentMenu = this; return returnValue; } close() { document.getElementById("responsive-menu").style.display = 'none'; for (let i = 0, n = this.submenus.length; i < n; i++) { this.submenus[i].close(); } } removeAction(action) { let res = super.removeAction(action); this.updateToggleButton(); return res; } } ActionBarMenu.queries = []; ActionBarMenu.currentMenu = null; ActionBarMenu.init(); class ViewInflater { static inflate(viewUrl, parentUrls) { parentUrls = Helper.nonNull(parentUrls, []).slice(0); let resultPromise = Promise.resolve(); if (viewUrl instanceof Element) { resultPromise = Promise.resolve(viewUrl); } else { if (parentUrls.indexOf(viewUrl) !== -1) { return Promise.reject("views are in a circuit! cannot resolve view for url " + parentUrls[0] + "! url " + viewUrl + " is in stack before!"); } parentUrls.push(viewUrl); resultPromise = fetch(Helper.basePath(viewUrl), {credentials: "same-origin"}).then(function (result) { return result.text(); }).then(function (htmlText) { let doc = (new DOMParser()).parseFromString(htmlText, "text/html"); if (Helper.isNull(doc)) { doc = document.implementation.createHTMLDocument(''); doc.body.innerHTML = htmlText; } return doc.body.firstChild }); } return resultPromise.then(function (parentElement) { let promises = []; let childViews = parentElement.querySelectorAll("[data-view]"); for (let i = 0, n = childViews.length; i < n; i++) { promises.push(ViewInflater.inflate(childViews[i].dataset["view"], parentUrls).then(function (element) { childViews[i].replaceWith(element); })); } return Promise.all(promises).then(function () { return parentElement; }); }); } } class Context { constructor(view) { let self = this; this._siteContent = null; this.firstStart = true; this.inflatePromise = new Promise(function (resolver) { self.inflatePromiseResolver = resolver; }); this.fragments = {}; if (Helper.isNotNull(view)) { this.inflateView(view); } } onConstruct() { let results = []; for (let k in this.fragments) { results.push(this.fragments[k].onConstruct.apply(this.fragments[k], arguments)); results.push(this.fragments[k].inflatePromise); } return Promise.all(results); } onStart() { if (this.firstStart) { this.onFirstStart(); this.firstStart = false; } for (let k in this.fragments) { let fragment = this.fragments[k]; fragment.onStart.apply(this.fragments[k], arguments); this.fragments[k].inflatePromise.then(function (fragmentView) { if (fragment.isActive()) { fragmentView.classList.remove("hidden"); } else { fragmentView.classList.add("hidden"); } }); } } onFirstStart() { // for (let k in this.fragments) { // this.fragments[k].onFirstStart.apply(this.fragments[k], arguments); // } } onPause() { for (let k in this.fragments) { this.fragments[k].onPause.apply(this.fragments[k], arguments); } } onDestroy() { for (let k in this.fragments) { this.fragments[k].onDestroy.apply(this.fragments[k], arguments); } } addFragment(viewQuery, fragment) { this.fragments[viewQuery] = fragment; this.inflatePromise = this.inflatePromise.then(function (siteContent) { return fragment.inflatePromise.then(function (fragmentView) { siteContent.querySelector(viewQuery).appendChild(fragmentView); return siteContent; }); }); } /** @protected */ inflateView(link) { let self = this; this.inflatePromiseResolver(ViewInflater.inflate(link).then(function (siteContent) { self._siteContent = siteContent; return siteContent; })); return this.inflatePromise; } findBy(query, all, asPromise) { all = Helper.nonNull(all, false); asPromise = Helper.nonNull(asPromise, false); let getVal = function (root) { let res = null; if (all) { res = root.querySelectorAll(query); if (root.matches(query)) { res.push(root); } } else { if (root.matches(query)) { res = root; } else { res = root.querySelector(query); } } return res; }; if (asPromise) { return this.inflatePromise.then(function (rootView) { return getVal(rootView); }); } return getVal(this._siteContent); } } class AbstractSite$1 extends Context { constructor(siteManager, view, deepLink) { super(view); this.isVisible = false; this.siteManager = siteManager; this.isFinishing = false; this.actionMenu = null; this.url = ""; this.deepLink = deepLink; this.startArgs = {}; this.title = siteManager.getDefaultTitle(); } setTitle(titleElement, title) { if (typeof titleElement === "string") { title = titleElement; titleElement = document.createTextNode(titleElement); } this.title = { element: titleElement }; this.title["title"] = Helper.nonNull(title, this.title["title"]); if (this.isVisible) { this.siteManager.updateTitle(); } } startStartsite() { return this.startSite(this.siteManager.getStartSiteName()); } inflateView(link) { let self = this; return super.inflateView(link).then(function (res) { let promises = []; for (let i = 0, n = self.fragments.length; i < n; i++) { promises.push(self.fragments[i].inflatePromise); } return Promise.all(promises).then(function () { return res; }); }); } onConstruct(args) { this.startArgs = args; if (Helper.isNotNull(this.deepLink)) { this.setUrlFromParams(args); } return super.onConstruct(args); } onStart(args) { this.isVisible = true; let res = super.onStart(args); this.actionMenu.redraw(); return res; } onPause(args) { super.onPause(args); this.isVisible = false; } finish(result) { if (!this.isFinishing) { this.isFinishing = true; this.siteManager.endSite(this, result); } } startSite(siteName, args) { return this.siteManager.startSite(siteName, args); } toForeground() { this.siteManager.toForeground(this); } finishAndStartNext(siteName, startParams, finishResult) { this.startSite(siteName, startParams); this.finish(finishResult); } createActionBarMenu(menu) { let defaultActions = this.siteManager.getDefaultActions(); for (let i = 0, n = defaultActions.length; i < n; i++) { menu.addAction(defaultActions[i].copy()); } return menu; } setUrl(url) { this.url = url; this.siteManager.updateUrl(this); } setUrlFromParams(params) { this.setUrl(this.deepLink + Helper.buildQuery(params)); } updateUrlParams(params) { this.startArgs = Object.assign(this.startArgs, params); this.setUrlFromParams(this.startArgs); } getUrl() { return this.url; } getFullUrl() { return Helper.basePath(this.url); } onBackPressed() { } addListener(event, selector, listenerFunction) { this.siteManager.addListener(this, event, selector, listenerFunction); } addKeyListener(keycode, listenerFunction) { this.siteManager.addKeyListener(this, keycode, listenerFunction); } addKeyAndEventListener(keycode, event, selector, listenerFunction) { this.siteManager.addKeyAndEventListener(this, keycode, event, selector, listenerFunction); } } class SiteContainer { constructor(site, finishResolver) { this._site = site; this._siteContent = null; this._pauseParameters = {}; this._startParameters = {}; this._finishResolver = finishResolver; } getSite() { return this._site; } setSite(site) { if (site instanceof AbstractSite) { this._site = site; } } getSiteContent() { return this._siteContent; } setSiteContent(value) { this._siteContent = value; } getPauseParameters() { return this._pauseParameters; } setPauseParameters(value) { this._pauseParameters = value; } getStartParameters() { return this._startParameters; } setStartParameters(value) { this._startParameters = value; } getFinishResolver() { return this._finishResolver; } setFinishResolver(value) { this._finishResolver = value; } } class SiteManager { constructor(siteDivId, actionBarMenuSelector) { this.siteDiv = document.getElementById(siteDivId); this.siteContainerStack = []; this.currentSiteContainerToShow = null; this.actionBarMenuSelector = Helper.nonNull(actionBarMenuSelector, '.action-bar'); this.siteStartingPromise = Promise.resolve(); this.defaultActions = []; this.startSiteName = null; this.titleElement = document.querySelector(".top-bar-title"); const defaultTitleElem = document.createElement("span"); while(this.titleElement.childNodes.length > 0) { const child = this.titleElement.firstChild; child.remove(); defaultTitleElem.appendChild(child); } this.defaultTitle = { element: defaultTitleElem, title: document.title }; console.log(this.defaultTitle); let siteManager = this; window.onpopstate = function (e) { if (siteManager.siteContainerStack.length >= 1) { let site = siteManager.siteContainerStack[siteManager.siteContainerStack.length - 1].getSite(); if (site.onBackPressed() !== false) { siteManager.endSite(site); } } }; } getDefaultTitle() { return this.defaultTitle; } setStartSiteName(startSiteName) { this.startSiteName = startSiteName; } getStartSiteName() { return this.startSiteName; } addDefaultAction(action) { this.defaultActions.push(action); } getDefaultActions() { return this.defaultActions; } startSite(siteConstructor, paramsPromise) { if (!(siteConstructor.prototype instanceof AbstractSite$1)) { throw { "error": "wrong class given! Expected AbstractSite, given "+siteConstructor.name }; } let site = new siteConstructor(this); let resolver = {}; let finishPromise = new Promise(function (resolve, reject) { resolver.resolve = resolve; resolver.reject = reject; }); let siteContainer = new SiteContainer(site, resolver); this.siteDiv.removeAllChildren().appendChild(Helper.createLoadingSymbol()); let manager = this; this.siteStartingPromise = new Promise(function (resolve) { Promise.resolve(paramsPromise).then(function (params) { siteContainer.setStartParameters(params); return Promise.all([site.onConstruct(params), site.inflatePromise]); }).then(function () { site.actionMenu = site.createActionBarMenu(manager.buildActionBarMenu()); }).then(function () { resolve(manager.show(siteContainer)); }); }); return finishPromise; } endSite(site, result) { let manager = this; this.siteStartingPromise.then(function () { let index = manager.findContainerIndexBySite(site); let container = manager.siteContainerStack.splice(index, 1); container = container[0]; let showSiteContainer = null; if (container === manager.currentSiteContainerToShow) { manager.currentSiteContainerToShow.getSite().onPause(); manager.currentSiteContainerToShow = null; let newSiteContainerIndex = manager.siteContainerStack.length - 1; if (newSiteContainerIndex < 0) { manager.showAppEndedMessage(); manager.startSite(manager.startSiteName); return; } manager.siteDiv.removeAllChildren().appendChild(Helper.createLoadingSymbol()); showSiteContainer = manager.siteContainerStack[newSiteContainerIndex]; } container.getSite().onDestroy(); Promise.resolve(result).then(function (resValue) { container.getFinishResolver().resolve(resValue); if (Helper.isNotNull(showSiteContainer)) { manager.show(showSiteContainer); } }); }); } addListener(site, event, _selector, listener) { this.siteDiv.addEventListener(event, function (_event) { let _element = _event.target; if (site.isVisible && _element.matches(_selector)) { listener(_element, _event); } }); } addKeyAndEventListener(site, keycode, event, selector, listener) { this.addListener(site, event, selector, listener); this.addKeyListener(site, keycode, listener); } addKeyListener(site, keycode, listener) { window.addEventListener("keydown", function (e) { if (site.isVisible && e.which === keycode) { listener(this, e); } }); } toForeground(site) { let index = this.findContainerIndexBySite(site); let container = this.siteContainerStack.splice(index, 1); container = container[0]; this.show(container); } refreshCurrentSite() { return this.show(this.currentSiteContainerToShow); } /** @private */ show(siteContainer) { if (Helper.isNotNull(this.currentSiteContainerToShow)) { this.currentSiteContainerToShow.setPauseParameters(this.currentSiteContainerToShow.getSite().onPause()); this.currentSiteContainerToShow.setSiteContent(this.siteDiv.innerHTML); } this.siteDiv.removeAllChildren().appendChild(Helper.createLoadingSymbol()); let siteManager = this; this.currentSiteContainerToShow = siteContainer; if (-1 === this.siteContainerStack.indexOf(siteContainer)) { this.siteContainerStack.push(siteContainer); } return siteContainer.getSite().inflatePromise.then(function (data) { siteContainer.getSite().actionMenu.redraw(); siteManager.siteDiv.removeAllChildren().appendChild(data); siteManager.updateTitle(); Translator.getInstance().updateTranslations(); return data; }).then(function (data) { siteContainer.getSite().onStart(siteContainer.getPauseParameters()); history.pushState({ 'siteName': siteContainer.getSite().constructor.name, 'siteData': data.outerHTML, 'stackPosition': siteManager.siteContainerStack.length - 1 }, siteContainer.getSite().constructor.name, siteContainer.getSite().getFullUrl()); }); } updateUrl(site) { if (Helper.isNotNull(this.currentSiteContainerToShow) && this.currentSiteContainerToShow.getSite() === site) { let self = this; history.replaceState({ 'siteName': site.constructor.name, 'siteData': site._siteContent.outerHTML, 'stackPosition': self.siteContainerStack.length - 1 }, site.constructor.name, site.getFullUrl()); } } getCurrentSite() { if (this.currentSiteContainerToShow != null) return this.currentSiteContainerToShow.getSite(); } redrawCurrentActionBar() { if (this.currentSiteContainerToShow != null) this.currentSiteContainerToShow.getSite().actionMenu.redraw(); } updateTitle() { let title = this.getCurrentSite().title; this.titleElement.removeAllChildren().appendChild(title.element); document.title = Helper.nonNull(title.title, this.defaultTitle.title); } /** @private */ findContainerIndexBySite(site) { for (let i = 0, n = this.siteContainerStack.length; i < n; i++) { if (this.siteContainerStack[i].getSite() === site) { return i; } } return -1; } /** @private */ findContainerBySite(site) { let index = this.findContainerIndexBySite(site); if (index === -1) { return null; } return this.siteContainerStack[index]; } /** @private */ showAppEndedMessage() { this.siteDiv.removeAllChildren().appendChild(Translator.makePersistentTranslation("The app has ended! Please close the window.")); } /** @private */ buildActionBarMenu() { return new ActionBarMenu(this.actionBarMenuSelector); } } class PauseSite extends AbstractSite$1 { onConstruct(args) { let pausedElement = null; if (Helper.isSet(args, "url")) { pausedElement = args["url"]; } else { pausedElement = document.createElement("div"); pausedElement.innerHTML = "Paused..."; } this.inflateView(pausedElement); } } class App { constructor() { this._siteManager = null; this._actionBarMenuSelector = '.action-bar'; this._basePath = SystemSettings.getBasePath(); this._siteContentId = 'site-content'; this._deepLinks = new Map(); this._defaultActions = []; this._addThemeAction = false; this._showCookieCompliance = true; this._startSite = null; } getSiteManager() { return this._siteManager; } addDefaultAction(action) { this._defaultActions.push(action); } setAddThemeAction(addThemeAction) { this._addThemeAction = addThemeAction; } getSiteContentId() { return this._siteContentId; } setSiteContentId(value) { this._siteContentId = value; } getActionBarMenuSelector() { return this._actionBarMenuSelector; } setActionBarMenuSelector(value) { this._actionBarMenuSelector = value; } getBasePath() { return this._basePath; } setBasePath(value) { this._basePath = value; } addDeepLink(alias, site) { this._deepLinks.set(alias.toLowerCase(), site); } setShowCookieCompliance(cookieCompliance) { this._showCookieCompliance = cookieCompliance; } refreshCurrentSite() { this._siteManager.refreshCurrentSite(); } pause(elementToShow){ this.startSite(PauseSite, {"url": elementToShow}); } resume(){ const currentSite = this._siteManager.getCurrentSite(); if (currentSite instanceof PauseSite) { currentSite.finish(); } } _resolveDeepLink(deepLink) { deepLink = deepLink.toLowerCase(); if (this._deepLinks.has(deepLink)) { return this._deepLinks.get(deepLink); } return null; } _getDeepLink() { let deepLink = ""; if (window.location.pathname.search(this._basePath) === 0) { deepLink = window.location.pathname.substr(this._basePath.length).trim(); } if (deepLink.charAt(0) === '/') { deepLink = deepLink.substr(1).trim(); } if (deepLink.charAt(deepLink.length - 1) === '/') { deepLink = deepLink.substr(0, deepLink.length - 2).trim(); } if (deepLink.length === 0 && window.location.hash) { deepLink = window.location.hash.substr(1).trim(); } return this._resolveDeepLink(deepLink); } _addDeepLinksListener() { let app = this; let elements = document.getElementsByClassName("deep-link"); for (let i = 0, n = elements.length; i < n; i++) { elements[i].addEventListener("click", function (e) { e.preventDefault(); app._siteManager.startSite(Helper.nonNull(app._resolveDeepLink(this.dataset["siteName"]), app._startSite), App._extractParams(this.dataset["siteArgs"])); return true; }); } } removeDefaultAction(action) { let index = this._defaultActions.indexOf(action); if (index >= 0) { this._defaultActions[index].remove(true); this._defaultActions.splice(index, 1); } } startSite(site, parameter) { return this._siteManager.startSite(site, parameter); } start(fallbackStartSite) { SystemSettings.setBasePath(this._basePath); let startSite = Helper.nonNull(this._getDeepLink(), fallbackStartSite); let startParams = App._getStartParams(); this._startSite = fallbackStartSite; Translator.init(); ThemeManager.init(); if (this._addThemeAction) { this.addDefaultAction(ThemeManager.generateChangeThemeMenuAction()); } this._siteManager = new SiteManager(this._siteContentId, this._actionBarMenuSelector); this._siteManager.defaultActions = this._defaultActions; this._siteManager.setStartSiteName(fallbackStartSite); this._siteManager.startSite(startSite, startParams); this._addDeepLinksListener(); if (this._showCookieCompliance) { new CookieCompliance('cookie-compliance').showIfNeeded(); } } static _extractParams(paramString) { if (Helper.isNull(paramString)) { return null; } let result = {}, tmp = []; let items = paramString.split("&"); for (let index = 0; index < items.length; index++) { tmp = items[index].split("="); if (tmp[0].trim().length > 0) { result[tmp[0]] = decodeURIComponent(tmp[1]); } } return result; } static _getStartParams() { return App._extractParams(window.location.search.substr(1)); } } class InitPromise { static addPromise(promise) { if (typeof promise === 'function') { let func = promise; promise = InitPromise.mainPromise.then(function(app){ return (func(app)); }); } InitPromise.promises.push(promise); } static resolve(app) { InitPromise.mainResolver(app); return InitPromise.mainPromise.then(function(){ return Promise.all(InitPromise.promises); }); } } InitPromise.promises = []; InitPromise.mainPromise = new Promise(function(resolver){ InitPromise.mainResolver = resolver; }); class ShareButton { constructor(deviceType, icon, callback) { this._deviceType = deviceType; this._icon = icon; this._callback = callback; } shouldShowFor(deviceType) { return (deviceType === (deviceType & this._deviceType)) } getIcon() { return this._icon; } getCallback() { return this._callback; } } ShareButton.TYPE_DESKTOP = 1; ShareButton.TYPE_MOBILE_APPLE = 2; ShareButton.TYPE_MOBILE_LEFTOVER = 4; ShareButton.TYPE_MOBILE = ShareButton.TYPE_MOBILE_APPLE+ShareButton.TYPE_MOBILE_LEFTOVER; ShareButton.TYPE_ALL = ShareButton.TYPE_DESKTOP+ShareButton.TYPE_MOBILE; class ShareManager { static init() { ShareManager.shareButtons = []; } static addShareButton(shareButton) { ShareManager.shareButtons.push(shareButton); } static generateDefaultShareElement(shareUrl) { return ShareManager.generateShareElement(shareUrl, ShareManager.getDefaultGenerateCallback()); } static generateDefaultShareElementForButtons(shareUrl, buttons) { return ShareManager.generateShareElementForButtons(shareUrl, buttons, ShareManager.getDefaultGenerateCallback()); } static generateShareElement(shareUrl, generateCallback) { return ShareManager.generateShareElementForButtons(shareUrl, ShareManager.shareButtons, generateCallback); } static generateShareElementForButtons(shareUrl, buttons, generateCallback) { let shareButtonElement = document.createElement("div"); let currentDeviceType = ShareManager.getCurrentDeviceType(); for (let i = 0, n = buttons.length; i < n; i++) { if (buttons[i].shouldShowFor(currentDeviceType)) { let elem = generateCallback(buttons[i], shareUrl); elem.onclick = function(event){ buttons[i].getCallback()(shareUrl, this, event); }; shareButtonElement.appendChild(elem); } } return shareButtonElement; } static getCurrentDeviceType() { if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { return ShareButton.TYPE_MOBILE_APPLE; } else if ((navigator.userAgent.match(/Android|BlackBerry|Opera Mini|IEMobile/i) !== null || (typeof window.orientation !== "undefined"))) { return ShareButton.TYPE_MOBILE_LEFTOVER; } else { return ShareButton.TYPE_DESKTOP; } } static getDefaultGenerateCallback() { return function(button){ let linkElement = document.createElement("a"); let iconElement = document.createElement("img"); linkElement.appendChild(iconElement); iconElement.src = Helper.basePath(button.getIcon()); iconElement.classList.add("share-icon"); return linkElement; } } } ShareManager.init(); class SmsShareButton extends ShareButton { constructor(icon) { super(ShareButton.TYPE_MOBILE, icon, function (link) { let linkToOpen = ""; if (ShareManager.getCurrentDeviceType() === ShareButton.TYPE_MOBILE_APPLE) { linkToOpen = "sms:&body="+encodeURIComponent(link); } else { linkToOpen = "sms:?body=" + encodeURIComponent(link); } window.open(linkToOpen, '_blank'); }); } } class TelegramShareButton extends ShareButton { constructor(icon) { super(ShareButton.TYPE_ALL, icon, function (link) { let linkToOpen = "https://t.me/share/url?url="+encodeURIComponent(link); window.open(linkToOpen, '_blank'); }); } } class WhatsappShareButton extends ShareButton { constructor(icon) { super(ShareButton.TYPE_ALL, icon, function (link) { let linkToOpen = ""; if (ShareManager.getCurrentDeviceType() === ShareButton.TYPE_DESKTOP) { linkToOpen = "https://web.whatsapp.com/send?text="+encodeURIComponent(link); } else { linkToOpen = "whatsapp://send?text=" + encodeURIComponent(link); } window.open(linkToOpen, '_blank'); }); } } class Theme { constructor(name, className, icon) { this._name = name; this._className = className; this._icon = icon; } } function applyPolyfills(){ if (!String.prototype.format) { String.prototype.format = function (args) { return this.replace(/{(\d+)}/g, function (match, number) { return args[number] !== undefined ? args[number] : match ; }); }; } Object.assign = Helper.nonNull(Object.assign, function (base, obj) { base = Helper.nonNull(base, {}); if (obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj) return base; // if (obj instanceof Date) { // temp = new obj.constructor(); //or new Date(obj); // } // else { // temp = obj.constructor(); // } for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj['isActiveClone'] = null; base[key] = obj[key]; delete obj['isActiveClone']; } } return base; }); if (typeof window !== 'undefined') { if (Helper.isNotNull(window["Node"]) && !window["Node"]["prototype"]["removeAllChildren"]) { Node.prototype["removeAllChildren"] = function () { while (this.firstChild) { this.removeChild(this.firstChild); } return this; }; } if (HTMLElement) { HTMLElement.prototype["fadeOut"] = Helper.nonNull(HTMLElement.prototype["fadeOut"], function (time, effect, delay) { time = Helper.nonNull(time, 0.5); effect = Helper.nonNull(effect, "ease-in-out"); delay = Helper.nonNull(delay, 0); this.style.transition = "opacity " + time + "s " + effect + " " + delay + "s"; let elem = this; let animPromise = new Promise(function (resolve) { let transEndLis = function (e) { elem.removeEventListener("transitionend", transEndLis); elem.removeEventListener("transitioncancel", transCancelledLis); elem.style.opacity = null; elem.style.transition = null; resolve(true, e); }; let transCancelledLis = function (e) { elem.removeEventListener("transitionend", transEndLis); elem.removeEventListener("transitioncancel", transCancelledLis); elem.style.opacity = null; elem.style.transition = null; resolve(false, e); }; elem.addEventListener("transitionend", transEndLis); elem.addEventListener("transitioncancel", transCancelledLis); }); //Nach Seitenneuzeichnen, damit chrome das immer macht (und FF auch) requestAnimationFrame(function () { requestAnimationFrame(function () { elem.style.opacity = 0; }); }); return animPromise }); HTMLElement.prototype["fadeIn"] = Helper.nonNull(HTMLElement.prototype["fadeIn"], function (time, effect, delay) { time = Helper.nonNull(time, 0.5); effect = Helper.nonNull(effect, "ease-in-out"); delay = Helper.nonNull(delay, 0); this.style.transition = "opacity " + time + "s " + effect + " " + delay + "s"; let elem = this; let animPromise = new Promise(function (resolve) { let transEndLis = function (e) { elem.removeEventListener("transitionend", transEndLis); elem.removeEventListener("transitioncancel", transCancelledLis); elem.style.opacity = null; elem.style.transition = null; resolve(true, e); }; let transCancelledLis = function (e) { elem.removeEventListener("transitionend", transEndLis); elem.removeEventListener("transitioncancel", transCancelledLis); elem.style.opacity = null; elem.style.transition = null; resolve(false, e); }; elem.addEventListener("transitionend", transEndLis); elem.addEventListener("transitioncancel", transCancelledLis); }); //Nach Seitenneuzeichnen, damit chrome das immer macht (und FF auch) requestAnimationFrame(function () { requestAnimationFrame(function () { elem.style.opacity = 1; }); }); return animPromise; }); } if (Node) { Node.prototype["replaceWith"] = Helper.nonNull(Node.prototype["replaceWith"], function (elem) { this.parentElement.replaceChild(elem, this); }); Node.prototype["remove"] = Helper.nonNull(Node.prototype["remove"], function () { this.parentElement.removeChild(this); }); } if (Element) { Element.prototype.matches = Helper.nonNull(Element.prototype.matches, Helper.nonNull(Element.prototype["matchesSelector"], Element.prototype["webkitMatchesSelector"])); window["Element"]["prototype"]["closest"] = Helper.nonNull(window["Element"]["prototype"]["getAll"], function (s) { // if (!Element.prototype.matches) // Element.prototype.matches = Element.prototype.msMatchesSelector || // Element.prototype.webkitMatchesSelector; // // if (!Element.prototype.closest) // Element.prototype.closest = function(s) { let el = this; if (!document.documentElement.contains(el)) return null; do { if (el.matches(s)) return el; el = el.parentElement; } while (el !== null); return null; // }; }); } window["IDBObjectStore"]["prototype"]["getAll"] = Helper.nonNull(window["IDBObjectStore"]["prototype"]["getAll"], function () { let res = {}; let items = []; this.openCursor().onsuccess = function (e) { let cursor = e.target.result; if (Helper.isNotNull(cursor)) { items.push(cursor.value); cursor.continue(); } else if (Helper.isNotNull(res.onsuccess)) { res.onsuccess({currentTarget: {result: items}}); } }; return res; }); } String.prototype.startsWith = Helper.nonNull(String.prototype.startsWith, function (searchString, position) { position = position || 0; return this.indexOf(searchString, position) === position; }); String.prototype.endsWith = Helper.nonNull(String.prototype.endsWith, function (searchString, position) { var subjectString = this.toString(); if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; } position -= searchString.length; var lastIndex = subjectString.indexOf(searchString, position); return lastIndex !== -1 && lastIndex === position; }); var fetch = Helper.nonNull(fetch, function (url) { console.log("customFetch", url); let request = null; if (window.XMLHttpRequest) { // Mozilla, Safari, ... request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) { } } } let resultPromise = new Promise(function (resolve) { request.onload = function () { let data = this.responseText; let response = { json: function () { return Promise.resolve(JSON.parse(data)); }, text: function () { return Promise.resolve(data); } }; resolve(response); }; request.onerror = function (err) { resolve(Promise.reject(err)); }; }); request.open('get', url, true); request.send(); return resultPromise; }); } SystemSettings.setBasePath("/pwa/stories/public/"); Translator.supportedLanguages = ["de", "en"]; Translator.markTranslations = false; class DataManager { static load(url, isCachable, raw) { isCachable = Helper.nonNull(isCachable, false); raw = Helper.nonNull(raw, false); let fullUrl = (isCachable) ? Helper.basePath(DataManager.cachePath + url) : Helper.basePath(DataManager.dataPath + url); return fetch(fullUrl, {"credentials": "same-origin"}).then(function (res) { if (raw) { return res.text(); } return res.json(); }).catch(function (e) { console.error("error", e); if (!raw) { return { "success": false, "errors": [ "not-online" ] } } }); } static send(url, params) { let fullUrl = Helper.basePath(DataManager.dataPath + url); if (!(params instanceof FormData)) { let newParams = new FormData(); for (let k in params) { newParams.append(k, params[k]); } params = newParams; } return fetch(fullUrl, { "credentials": "same-origin", "method": "POST", "body": params }).then(function (res) { return res.json(); }).catch(function (e) { console.error("error", e); return { "success": false, "errors": [ "not-online" ] } }); } static buildQuery(values) { return Helper.buildQuery(values); } } DataManager.dataPath = "data/"; DataManager.cachePath = "cached/"; class SettingsSite extends AbstractSite$1 { constructor(siteManager) { super(siteManager, 'public/html/settings.html', "settings"); for (let k in SettingsSite.settingsFragments) { this.addSettingsFragment(k, new SettingsSite.settingsFragments[k](this)); } this.active = null; } addSettingsFragment(name, settingsFragment) { this.addFragment("#settings-fragments", settingsFragment); delete this.fragments["#settings-fragments"]; this.fragments[name] = settingsFragment; } onStart() { let res = super.onStart(); if (Helper.isNotNull(this.active) && !this.fragments[this.active].isActive()) { this.setActive(null); } this.buildList(); return res; } setActive(name) { if (Helper.isNotNull(this.active)) { this.fragments[this.active].inflatePromise.then(function (view) { view.classList.remove("active"); }); this.findBy("#show-fragment-" + this.active).classList.remove("active"); } this.active = name; if (Helper.isNotNull(this.active)) { this.fragments[this.active].inflatePromise.then(function (view) { view.classList.add("active"); }); this.findBy("#show-fragment-" + this.active).classList.add("active"); } } buildList() { let listNameElem = this.findBy("#settings-fragment-list"); listNameElem.removeAllChildren(); let self = this; for (let k in this.fragments) { if (this.fragments[k].isActive()) { let liElement = document.createElement("li"); liElement.id = "show-fragment-" + k; liElement.appendChild(Translator.makePersistentTranslation(k, null, "a")); liElement.addEventListener("click", function () { self.setActive(k); }); listNameElem.appendChild(liElement); if (Helper.isNull(this.active)) { this.setActive(k); } } } } static addSettingsFragment(name, settingsFragment) { SettingsSite.settingsFragments[name] = settingsFragment; } static setAddSettingsSite(addLink) { SettingsSite.shouldAddSettingsSite = addLink; } } SettingsSite.settingsFragments = {}; SettingsSite.shouldAddSettingsSite = true; InitPromise.addPromise(function (app) { if (SettingsSite.shouldAddSettingsSite) { app.addDeepLink("settings", SettingsSite.name); let settingsAction = new MenuAction("settings", function () { app.startSite(SettingsSite.name); }, MenuAction.SHOW_FOR_LARGE, 10000); settingsAction.setIcon("img/settings.png"); app.addDefaultAction(settingsAction); } }); class ClockSite extends AbstractSite$1 { constructor(siteManager) { super(siteManager, "html/application/clock.html"); } onConstruct(args) { this.setTitle("clock"); return super.onConstruct(args); } onFirstStart(){ DataManager.load("clock").then(function(data){ document.getElementById("current-time").innerText = data.result.date; }); } } applyPolyfills(); ThemeManager.addTheme(new Theme('red', '')); ThemeManager.addTheme(new Theme("blue", "blue")); ThemeManager.addTheme(new Theme("black", "black")); ThemeManager.addTheme(new Theme("green", "green")); ThemeManager.addTheme(new Theme("pink", "pink")); ShareManager.addShareButton(new WhatsappShareButton('img/whatsapp.svg')); ShareManager.addShareButton(new SmsShareButton('img/sms.svg')); ShareManager.addShareButton(new TelegramShareButton('img/telegram.svg')); // ShareManager.addShareButton(new CopyShareButton('img/copy.svg')); let app = new App(); // app.addDeepLink("policy", PrivatePolicySite.name); app.setAddThemeAction(true); // app.addDefaultAction(Translator.generateChangeLanguageMenuAction()); //bridge für Android // window["ThemeManager"] = ThemeManager; // window["ThemeManager"]["addChangeListener"] = ThemeManager.addChangeListener; // window["app"] = app; // window["app"]["refreshCurrentSite"] = app.refreshCurrentSite; // window["Translator"] = Translator; // window["Translator"]["setLanguage"] = Translator.setLanguage; InitPromise.resolve(app).then(function(){ app.start(ClockSite); Translator.setLanguage("de"); });