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 MyDb { constructor(dbName, version) { let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB; this._conn = indexedDB.open(dbName, version); let myDB = this; this._conn.onupgradeneeded = function (upgradeEvent) { myDB.upgrade(myDB._conn.result, upgradeEvent.oldVersion, upgradeEvent.newVersion, upgradeEvent); }; this.queryPromise = new Promise(function (resolve) { myDB._conn.onsuccess = function (e) { myDB._db = myDB._conn.result; resolve(e); }; }); } openTransaction(name, transactionMode, callback) { let myDb = this; if (typeof transactionMode === 'function' && Helper.isNull(callback)) { callback = transactionMode; transactionMode = "read"; } this.queryPromise.then(function () { let res = null; try { res = myDb._conn.result.transaction(name, transactionMode); } catch (e) { console.warn(e); res = myDb._conn.result.transaction(name); } callback(res); }); } openStore(name, transactionMode, callback) { if (typeof transactionMode === 'function' && Helper.isNull(callback)) { callback = transactionMode; transactionMode = "readonly"; } this.openTransaction(name, transactionMode, function (t) { callback(t.objectStore(name)); }); } saveObj(obj, objectStore) { let self = this; return new Promise(function (resolve) { self.openStore(objectStore, "readwrite", function (store) { let request = store.put(obj); request.onsuccess = resolve; request.onerror = function (e) { throw { "type": "indexed-db-error", "event": e } }; }); }); } saveMany(manyObj, objectStore) { let self = this; return new Promise(function (resolve) { self.openStore(objectStore, "readwrite", function (store) { let promises = []; for (let i = 0, n = manyObj.length; i < n; i++) { promises.push(new Promise(function (resolveInner) { let request = store.put(manyObj[i]); request.onsuccess = resolveInner; request.onerror = function (e) { throw { "type": "indexed-db-error", "event": e } }; })); } resolve(Promise.all(promises)); }); }); } load(key, objectStore) { let self = this; return new Promise(function (resolve) { self.openStore(objectStore, function (store) { let request = store.get(key); request.onsuccess = function (e) { resolve(e.currentTarget.result); }; request.onerror = function (e) { console.warn(e); throw { "type": "indexed-db-load-error", "event": e } }; }); }); } loadAll(objectStore, query, count) { let self = this; return new Promise(function (resolve) { self.openStore(objectStore, function (store) { let request = store.getAll(query, count); request.onsuccess = function (e) { resolve(e.currentTarget.result); }; request.onerror = function (e) { console.warn(e); throw { "type": "indexed-db-load-error", "event": e } }; }); }); } loadMany(index, value, objectStore, limit, direction) { let self = this; return new Promise(function (resolve) { self.openStore(objectStore, function (store) { let indexRequest = store.index(index); indexRequest.onerror = function (e) { throw { "type": "indexed-db-index-error", "event": e } }; let request = indexRequest.openCursor(value, direction); request.onerror = function (e) { throw { "type": "indexed-db-index-error", "event": e } }; let objects = []; let numberResults = 0; request.onsuccess = function (e) { let cursor = e.target.result; if (cursor) { objects.push(cursor.value); numberResults++; if (Helper.isNull(limit) || numberResults < limit) { cursor.continue(); return; } } resolve(objects); }; }); }); } remove(id, objectStore) { let self = this; return new Promise(function (resolve) { self.openStore(objectStore, "readwrite", function (store) { let deleteRequest = store.delete(id); deleteRequest.onerror = function (e) { throw { "type": "indexed-db-delete-error", "event": e } }; deleteRequest.onsuccess = function (e) { resolve(); }; }); }); } removeMany(ids, objectStore) { let self = this; return new Promise(function (resolve) { self.openStore(objectStore, "readwrite", function (store) { let promises = []; for (let i = 0, n = ids.length; i < n; i++) { let deleteRequest = store.delete(ids[i]); deleteRequest.onerror = function (e) { throw { "type": "indexed-db-delete-error", "event": e } }; promises.push(new Promise(function (resolve) { deleteRequest.onsuccess = function () { resolve(); }; })); } resolve(Promise.all(promises)); }); }); } removeWithIndex(index, value, objectStore) { let self = this; return new Promise(function (resolve) { self.openStore(objectStore, "readwrite", function (store) { let indexRequest = store.index(index); indexRequest.onerror = function (e) { throw { "type": "indexed-db-index-error", "event": e } }; let request = indexRequest.openCursor(value); request.onerror = function (e) { throw { "type": "indexed-db-index-error", "event": e } }; request.onsuccess = function (e) { let cursor = e.target.result; if (cursor) { cursor.delete(); cursor.continue(); } else { resolve(); } }; }); }); } upgrade(db) { }; } 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/wordRotator/public/"); Translator.supportedLanguages = ["de", "en"]; Translator.markTranslations = false; class Segment{ constructor(element){ this.rotation = 0; this.element = element; this.parent = null; } setParent(parent) { this.parent = parent; } getLevel() { if (this.parent!==null) { return this.parent.getLevel(); } } isSolved(){ return (this.rotation === 0); } async rotate(){ return Promise.resolve(); }; _updateElement(){}; applyRotations(rotations){ return rotations; } getElement() { return this.element; } } class ScaleHelper { scaleTo(scale, fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight) { margin = Helper.nonNull(margin, 10); ignoreHeight = Helper.nonNull(ignoreHeight, false); ignoreWidth = Helper.nonNull(ignoreWidth, false); fontWeight = Helper.nonNull(fontWeight, fontElement.innerHTML.length); let hasNoTransitionClass = container.classList.contains("no-transition"); container.classList.add("no-transition"); let currentFontSize = 1; let diff = 0; let widthDiff = 0; let heightDiff = 0; let containerWidth = 0; let containerHeight = 0; do { currentFontSize += diff / (fontWeight + 1); fontElement.style.fontSize = currentFontSize + 'px'; let containerStyle = window.getComputedStyle(container); containerWidth = containerStyle.getPropertyValue("width").replace('px', ''); containerHeight = containerStyle.getPropertyValue("height").replace('px', ''); widthDiff = containerWidth - fontElement.offsetWidth; heightDiff = containerHeight - fontElement.offsetHeight; let newDiff = (ignoreWidth ? heightDiff : (ignoreHeight ? widthDiff : Math.min(widthDiff, heightDiff))); if (newDiff === diff) { break; } diff = newDiff; } while ((widthDiff > (1 - scale) * containerWidth || ignoreWidth) && (heightDiff > (1 - scale) * containerHeight || ignoreHeight)); fontElement.style.fontSize = (currentFontSize - margin) + 'px'; if (!hasNoTransitionClass) { container.classList.remove("no-transition"); } let self = this; window.addEventListener("resize", function () { setTimeout(() => { self.scaleTo(scale, fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight); }, 255); }); } scaleToFull(fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight) { return this.scaleTo(1, fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight); } } class ParentSegment extends Segment { constructor(element) { super(element); this.children = []; this.class = "rotate-0"; } async rotate() { if (!this.getLevel().getHasWon()) { this.rotation += 90; this.rotation %= 360; this._updateRotationClass(); this.getLevel().checkHasWon(new Promise((resolve, reject)=>{ this.element.addEventListener("animationend", resolve); })); // return new DelayPromise(250); return new Promise(function(resolve) { setTimeout(resolve, 250); }); } } applyRotations(rotations) { // debugger; this.rotation = rotations[0]; rotations.splice(0, 1); for (let i = 0, n = this.children.length; i < n; i++) { rotations = this.children[i].applyRotations(rotations); } return rotations; } isSolved() { for (let i = 0, n = this.children.length; i < n; i++) { if (!this.children[i].isSolved()) { return false; } } return super.isSolved(); } setChildren(children) { this.children = []; for (let i = 0, n = children.length; i < n; i++) { this.addChild(children[i]); } } addChild(child) { this.children.push(child); child.setParent(this); this._updateElement(); } _updateRotationClass() { // this.style.transform = "rotate("+this.rotation+"deg)"; this.element.classList.remove(this.class); this.class = "rotate-" + this.rotation; if (this.class === "rotate-0") { this.class = "rotate-360"; } this.element.classList.add(this.class); } _updateElement() { const childContainer = this.element.querySelector(".child-container"); childContainer.removeAllChildren(); this._updateRotationClass(); const self = this; this.element.onclick = function () { self.rotate(); }; for (let i = 0, n = this.children.length; i < n; i++) { this.children[i]._updateElement(); childContainer.appendChild(this.children[i].getElement()); } } } class LeafSegment extends Segment{ constructor(element, leaf) { super(element); this.leaf = 'A'; if (Helper.isNotNull(leaf)) { this.setLeaf(leaf); } } setLeaf(leaf) { this.leaf = leaf; } _updateElement() { this.element.querySelector(".leaf-element").removeAllChildren().appendChild(document.createTextNode(this.leaf)); } } class TemplateContainer{ constructor(leafTemplate, parentTemplate, rowTemplate){ this.leafTemplate = leafTemplate; this.parentTemplate = parentTemplate; this.rowTemplate = rowTemplate; } copyLeafTemplate() { return Helper.cloneNode(this.leafTemplate); } copyParentTemplate() { return Helper.cloneNode(this.parentTemplate); } copyRowTemplate() { return Helper.cloneNode(this.rowTemplate); } } class Level { constructor(templateContainer) { this.rootSegment = null; this.words = []; this.startRotations = []; this.templateContainer = templateContainer; this.hasWon = false; this.id = null; this.wonResolver = null; this.giveUpResolver = null; const self = this; this.wonPromise = new Promise((resolve, reject) => { self.wonResolver = resolve; self.giveUpResolver = reject; }); } setId(id) { this.id = id; } getId() { return this.id; } getLevel() { return this; } setRootSegment(rootSegment) { this.rootSegment = rootSegment; this.rootSegment.setParent(this); if (this.startRotations) { this.applyRotations(); } } setWords(words) { this.words = []; for (let i = 0, n = words.length; i < n; i++) { this.words.push(words[i].toUpperCase()); } } setStartRotations(rotations) { this.startRotations = rotations; } applyRotations(rotations) { if (this.rootSegment) { rotations = Helper.nonNull(rotations, this.startRotations); this.rootSegment.applyRotations(rotations); } } getHasWon() { return this.hasWon; } checkHasWon(delayPromise) { if (this.rootSegment.isSolved()){ this.hasWon = true; const self = this; delayPromise.then(()=>{ self.wonResolver(true); }); return true; } return false; } getWonPromise(){ return this.wonPromise; } getRootSegment(){ return this.rootSegment; } createSegments() {}; static _createLeafsForWord(word, leafSegmentTemplate) { let leafSegments = []; for (let i = 0, n = word.length; i < n; i++) { leafSegments.push(new LeafSegment(Helper.cloneNode(leafSegmentTemplate), word.charAt(i))); } return leafSegments; } } class RowSegment extends ParentSegment{ rotate() {} applyRotations(rotations) { for (let i = 0, n = this.children.length; i < n; i++) { rotations = this.children[i].applyRotations(rotations); } return rotations; } } class RowLevel extends Level { constructor(container, wordLength) { super(container); this.wordLength = wordLength; } createSegments() { if (this.words.length >= 2 && this.words[0].length >= this.wordLength && this.words[1].length >= this.wordLength) { let leafsWordOne = Level._createLeafsForWord(this.words[0], this.templateContainer.copyLeafTemplate()); let leafsWordTwo = Level._createLeafsForWord(this.words[1], this.templateContainer.copyLeafTemplate()); let rootSegment = new RowSegment(this.templateContainer.copyRowTemplate()); for (let i = 0, n = this.wordLength / 2; i < n; i++) { let parent = new ParentSegment(this.templateContainer.copyParentTemplate()); parent.addChild(leafsWordOne[2 * i]); parent.addChild(leafsWordOne[2 * i + 1]); parent.addChild(leafsWordTwo[2 * i]); parent.addChild(leafsWordTwo[2 * i + 1]); rootSegment.addChild(parent); } // rootSegment.applyRotations(this.startRotations); this.setRootSegment(rootSegment); } } } class LevelHelper { static setLevelType(typeId, level) { LevelHelper.types[typeId] = level; } static getLevelClass(type) { return LevelHelper.types[type]; } static inflateLevel(jsonLevel, templateContainer) { let level = new (LevelHelper.types[jsonLevel["rendererType"]])(templateContainer); level.setWords(jsonLevel["words"]); level.setId(jsonLevel["id"]); for (let i = 0, n = jsonLevel["rotations"].length; i < n; i++) { if (jsonLevel["rotations"][i] <= 4) { jsonLevel["rotations"][i] = 90 * jsonLevel["rotations"][i]; } } level.setStartRotations(jsonLevel["rotations"]); return level; } } LevelHelper.types = {}; class SimpleLevel extends RowLevel{ constructor(container) { super(container, 6); } } LevelHelper.setLevelType(20, SimpleLevel); class WordRotatorDb extends MyDb { static getInstance() { if (Helper.isNull(WordRotatorDb.instance)) { WordRotatorDb.instance = new WordRotatorDb(); } return WordRotatorDb.instance; } constructor() { super("wordRotator", 3); } upgrade(db, oldVersion, newVersion, e) { if (Helper.isNull(oldVersion) || oldVersion < 1 && newVersion >= 1) { let levelObjectStore = db.createObjectStore(WordRotatorDb.OBJECT_STORE.LEVEL, {"keyPath": "id"}); } if (Helper.isNull(oldVersion) || oldVersion < 2 && newVersion >= 2) { let levelObjectStore = e.target.transaction.objectStore(WordRotatorDb.OBJECT_STORE.LEVEL); levelObjectStore.createIndex("played", ["deleted", "played", "difficulty", "id"], {"unique": false}); } if (Helper.isNull(oldVersion) || oldVersion < 3 && newVersion >= 3) { let levelObjectStore = e.target.transaction.objectStore(WordRotatorDb.OBJECT_STORE.LEVEL); levelObjectStore.createIndex("difficulty", "difficulty", {"unique": false}); } }; async saveManyLevels(levels) { return this.saveMany(levels, WordRotatorDb.OBJECT_STORE.LEVEL); } async loadLevel(levelId) { return this.load(levelId, WordRotatorDb.OBJECT_STORE.LEVEL); } async loadNextLevel(rendererTypes) { const levels = await this.loadMany("difficulty", IDBKeyRange.lowerBound(0), WordRotatorDb.OBJECT_STORE.LEVEL); let newLevels = []; for (let i = 0, n = levels.length; i < n; i++) { if (!levels[i]["deleted"] && !levels[i]["played"] && rendererTypes.indexOf(levels[i]["rendererType"]) !== -1) { newLevels.push(levels[i]); } } if (newLevels.length === 0) { return null; } return newLevels[Math.round(Math.random() * newLevels.length) % newLevels.length]; } async saveLevelPlayed(levelId) { const level = await this.loadLevel(levelId); level.played = true; return await this.saveObj(level, WordRotatorDb.OBJECT_STORE.LEVEL); } } WordRotatorDb.OBJECT_STORE = { LEVEL: "level", }; WordRotatorDb.instance = null; class LevelSite extends AbstractSite$1 { constructor(siteManager) { super(siteManager, "html/application/level.html", "level"); } createActionBarMenu(menu) { menu = super.createActionBarMenu(menu); this.levelCounterAction = new MenuAction("", function(){}, Menu.SHOW_ALWAYS, 0); this.levelCounterAction.setShouldTranslate(false); menu.addAction(this.levelCounterAction); return menu; } onConstruct(args) { this.setTitle("Level"); this.levelCounter = Helper.nonNull(localStorage.getItem("levelCounter"), 1); return super.onConstruct(args); } onFirstStart() { super.onFirstStart(); let leafSegmentTemplate = this.findBy("#segment-leaf-template"); let parentSegmentTemplate = this.findBy("#segment-parent-template"); let rowSegmentTemplate = this.findBy("#segment-row-template"); leafSegmentTemplate.id = null; parentSegmentTemplate.id = null; rowSegmentTemplate.id = null; leafSegmentTemplate.remove(); parentSegmentTemplate.remove(); rowSegmentTemplate.remove(); let self = this; let continueButton = this.findBy("#continue-button"); continueButton.addEventListener("click", ()=> { self.nextLevel(); }); let wonText = this.findBy("#won-text"); let scaleHelper = new ScaleHelper(); scaleHelper.scaleToFull(continueButton, continueButton.parentElement); scaleHelper.scaleToFull(wonText, wonText.parentElement); this.levelCounterAction.setTitle(this.levelCounter); this.templateContainer = new TemplateContainer(leafSegmentTemplate, parentSegmentTemplate, rowSegmentTemplate); this.nextLevel(); } async nextLevel() { try { this._siteContent.classList.remove('won'); const db = WordRotatorDb.getInstance(); const nextLevelJson = await db.loadNextLevel([20]); const level = LevelHelper.inflateLevel(nextLevelJson, this.templateContainer); const self = this; level.getWonPromise().then(() => { self.levelWon(level); }); level.createSegments(); level.getRootSegment()._updateElement(); let levelSegment = this.findBy("#level"); levelSegment.removeAllChildren().appendChild(level.getRootSegment().getElement()); let scaleHelper = new ScaleHelper(); scaleHelper.scaleToFull(levelSegment, levelSegment.parentElement, false, false, 2,level.words[0].length * 2); this.level = level; } catch (e) { console.error(e); } } async levelWon(level){ const db = WordRotatorDb.getInstance(); // db.saveLevelPlayed(level); this.levelCounter++; localStorage.setItem("levelCounter", this.levelCounter); this.levelCounterAction.setTitle(this.levelCounter); this.levelCounterAction.redraw(); this._siteContent.classList.add('won'); } } 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 SynchronizeSite extends AbstractSite$1 { constructor(siteManager) { super(siteManager, "html/application/sync.html"); } async onConstruct(args) { let res = await super.onConstruct(args); await this.loadLevels(); return res; } onFirstStart() { super.onFirstStart(); this.startSite(LevelSite); } async loadLevels() { const dateLastSync = Helper.nonNull(localStorage.getItem("date-last-sync"), 0); const db = WordRotatorDb.getInstance(); let newLastSync = null; let maxRuns = 1; let levelPromises = []; for (let run = 0; run < maxRuns; run++) { let res = await DataManager.load("wordRotator/levels" + DataManager.buildQuery({ "currentRun": run, "dateLastSync": dateLastSync })); if (!res["success"]) { break; } res = res["result"]; newLastSync = Helper.nonNull(newLastSync, res["currentSyncDate"]); maxRuns = res["maxRuns"]; let levels = res["levels"]; for (let i = 0, n = levels.length; i < n; i++) { let currentLevel = levels[i]; levelPromises.push(db.loadLevel(levels[i]["id"]).then(level => { currentLevel["played"] = (Helper.nonNull(Helper.nonNull(level, {}).played, false)); return currentLevel; })); } } let levels = await Promise.all(levelPromises); await db.saveManyLevels(levels); localStorage.setItem("date-last-sync", newLastSync); } } 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(SynchronizeSite); Translator.setLanguage("de"); });