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._liClass = ""; 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; } getShowFor(){ return this.showFor; } 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; i < this._copies.length; 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._liClass = this._liClass; 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; i < this.actions.length; 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'); if (action._liClass.trim() !== "") { liElement.classList.add(action._liClass); } 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; i < this.parentElements.length; 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; i < oldElements.length; 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; i < this.parentElements.length; 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) { for (let i = 0; i < arguments.length; i++) { if (Helper.isNotNull(arguments[i])) { return arguments[i]; } } return null; } 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; i < stringToEncode.length; 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 SystemSettings { static setBasePath(basePath) { SystemSettings._basePath = basePath; } static getBasePath() { return SystemSettings._basePath; } static set(key, value) { SystemSettings._settings[key] = value; } static get(key, defaultValue) { return Helper.nonNull(SystemSettings._settings[key], defaultValue); } static has(key){ return Helper.nonNull(SystemSettings._settings[key]); } } SystemSettings.setBasePath("/"); SystemSettings._settings = {}; 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 { static async showIfNeeded(cookieContainer) { let cookieCompliance = new CookieCompliance(cookieContainer); return cookieCompliance.showIfNeeded(); } constructor(cookieContainerId) { this.cookieContainerId = cookieContainerId; this.dropCookie = true; this.cookieDuration = 365 * 10; this.cookieName = 'complianceCookie'; this.cookieValue = 'true'; } async showIfNeeded() { if (CookieCompliance.checkCookie(this.cookieName) !== this.cookieValue) { return this.show(); } return Promise.resolve(); } 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'; return new Promise(r => { cookieMessage.querySelector("#close-cookie-msg").onclick = function () { cookieCompliance.removeMe(); cookieMessage.remove(); r(); }; }); } } 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; i < ActionBarMenu.queries.length; i++) { let query = ActionBarMenu.queries[i]; if (matchMedia(query.value).matches) { matched = query; } } if (typeof matched === 'object') { return matched._name; } else { return matched; } } 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; } renderLiElement(aElement, action) { let liElement = super.renderLiElement(aElement, action); liElement.classList.add(action.getShowFor()); return liElement; } updateToggleButton() { let size = ActionBarMenu._getCurrentSize(); let firstParentElement = this.parentElements[0]; if ((size === "medium" || size === "smedium" || size === "small") && ActionBarMenu.filterVisibleElements(firstParentElement.getElementsByClassName(Menu.SHOW_FOR_LARGE)).length > 0 || (size === "smedium" || size === "small") && ActionBarMenu.filterVisibleElements(firstParentElement.getElementsByClassName(Menu.SHOW_FOR_MEDIUM)).length > 0 || (size === "small") && ActionBarMenu.filterVisibleElements(firstParentElement.getElementsByClassName(Menu.SHOW_FOR_SMEDIUM)).length > 0 || 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 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 }; 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; } async startSite(siteConstructor, paramsPromise) { if (!(siteConstructor.prototype instanceof AbstractSite)) { 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()); this.siteStartingPromise = Promise.resolve(paramsPromise).then(async (params) => { siteContainer.setStartParameters(params); await Promise.all([site.onConstruct(params), site.inflatePromise]); site.actionMenu = site.createActionBarMenu(this.buildActionBarMenu()); return this.show(siteContainer); }).catch((e) => { console.error("site start error:", e); }); 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 { 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) { this._cookieClosePromise = CookieCompliance.showIfNeeded('cookie-compliance'); } } 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 ChainAble{ constructor() { this.promise = Promise.resolve(); } _chain(func){ if (typeof func !== "function") { return this._chain(function(){ return Promise.resolve(func); }); } else { this.promise = this.promise.then(func); } return this; } } class Dialog { constructor(content, title) { this.resolver = null; this.content = null; this.backgroundElement = null; this.cancelable = true; this.title = Helper.nonNull(title, ""); this.translatable = true; this.additionalClasses = ""; this.buttons = []; this.result = null; if (Helper.isNotNull(content)) { this.setContent(content); } } setTitle(title) { this.title = title; return this; } setTranslatable(translatable) { this.translatable = translatable; } setAdditionalClasses(classes) { this.additionalClasses = classes; } getTitle() { return this.title; } setCancelable(cancelable) { this.cancelable = (cancelable === true); return this; } async setContent(content) { this.contentPromise = Promise.resolve(content); this.content = await this.contentPromise; return this; } addButton(elementOrText, listenerOrResult, shouldClose) { shouldClose = Helper.nonNull(shouldClose, true); let button = null; if (typeof elementOrText === "string") { button = document.createElement("button"); button.classList.add("button"); button.classList.add("right"); button.appendChild(Translator.makePersistentTranslation(elementOrText)); } else { button = elementOrText; } let self = this; if (typeof listenerOrResult !== "function") { let result = listenerOrResult; listenerOrResult = function () { self.result = result; }; } let callback = null; if (shouldClose) { callback = function (e) { if (Helper.isNotNull(listenerOrResult)) { listenerOrResult(e); } self.close(); }; } else { callback = listenerOrResult; } if (Helper.isNotNull(callback)) { button.addEventListener("click", callback); } this.buttons.push(button); } async show() { let titleElement = document.createElement("span"); titleElement.classList.add("title"); if (this.translatable && this.title !== "") { titleElement.appendChild(Translator.makePersistentTranslation(this.title)); } else { titleElement.innerHTML = this.title; } let titleBar = document.createElement("div"); titleBar.appendChild(titleElement); let contentContainer = document.createElement("div"); contentContainer.classList.add("content-container"); let modalDialog = document.createElement("div"); modalDialog.className = this.additionalClasses; modalDialog.classList.add("modal"); modalDialog.appendChild(titleBar); modalDialog.appendChild(contentContainer); let buttonBar = document.createElement("div"); buttonBar.classList.add("modal-button-container"); for (let i = 0, n = this.buttons.length; i < n; i++) { buttonBar.appendChild(this.buttons[i]); } await this.contentPromise; if (!(this.content instanceof Node)) { this.content = (this.translatable) ? Translator.makePersistentTranslation(this.content) : document.createTextNode(this.content); } contentContainer.appendChild(this.content); this.backgroundElement = document.createElement("div"); this.backgroundElement.classList.add("background"); this.backgroundElement.appendChild(modalDialog); this.backgroundElement.querySelector(".modal").appendChild(buttonBar); this.backgroundElement.style.display = "block"; let self = this; if (this.cancelable) { let closeButton = document.createElement("span"); closeButton.classList.add("close"); closeButton.innerHTML = "×"; titleBar.appendChild(closeButton); closeButton.addEventListener("click", function () { self.close(); }); window.addEventListener("click", function (e) { if (e.target === self.backgroundElement) { self.close(); } }); } document.body.appendChild(this.backgroundElement); Translator.getInstance().updateTranslations(); return new Promise(function (resolve) { self.resolver = resolve; }); } close() { if (Helper.isNotNull(this.backgroundElement)) { this.backgroundElement.style.display = "none"; this.backgroundElement.remove(); this.backgroundElement = null; } if (Helper.isNotNull(this.resolver)) { this.resolver(this.result); } } addDefaultButton(){ this.addButton("confirm-button"); } } class ConfirmDialog extends Dialog { constructor(content, title) { super(content, title); } async show() { this.addButton("confirm-button", true); this.addButton("cancel-button", false); return super.show(); } close() { if (Helper.isNull(this.result)) { this.result = false; } return super.close(); } } class FlashMessenger { static deleteMessage(_idNumber, _delayInMilliSeconds) { _delayInMilliSeconds = Helper.nonNull(_delayInMilliSeconds, 0); setTimeout(function () { let elem = document.getElementById("flashMessage" + _idNumber); elem.fadeOut(.2).then(function () { elem.remove(); }); }, _delayInMilliSeconds); } static addMessage(messageType, messageText, timeToShow, translate){ let translationArgs = null; if (Helper.isNull(messageText) || typeof messageText === "object") { translationArgs = messageText; messageText = messageType; messageType = FlashMessenger.MESSAGE_TYPE_SUCCESS; translate = true; } translate = Helper.nonNull(translate, false); let id = FlashMessenger.messageCount; let wrapper = document.createElement("div"); let _flashMessage = document.createElement("div"); _flashMessage.className = "flashMessage " + messageType; _flashMessage.id = "flashMessage" + id; _flashMessage.style.opacity = '0'; _flashMessage.addEventListener("click", function () { FlashMessenger.deleteMessage(id); }); _flashMessage.appendChild((translate) ? Translator.makePersistentTranslation(messageText, translationArgs, "span") : document.createTextNode(messageText)); wrapper.appendChild(_flashMessage); document.getElementById("flashMessageContainer").appendChild(wrapper); _flashMessage.fadeIn(); timeToShow = Helper.nonNull(timeToShow, FlashMessenger.defaultTimeToShow); if (timeToShow > 0) { FlashMessenger.deleteMessage(FlashMessenger.messageCount, timeToShow); } FlashMessenger.messageCount++; } } FlashMessenger.messageCount = 0; FlashMessenger.defaultTimeToShow = 3500; FlashMessenger.LENGTH_SHORT= 1000; FlashMessenger.MESSAGE_TYPE_SUCCESS = 'success'; FlashMessenger.MESSAGE_TYPE_ERROR = 'error'; FlashMessenger.MESSAGE_TYPE_DEFAULT = 'default'; FlashMessenger.MESSAGE_TYPE_INFO = 'info'; FlashMessenger.MESSAGE_TYPE_WARNING = 'warning'; class AbstractService{ constructor(gapiHandler) { this.gapiHandler = gapiHandler; } } class AbstractGapiResponse extends ChainAble{ constructor(gameService, params) { super(); this.gameService = gameService; if (Helper.isNotNull(params)) { this.setValues(params); } } setValues(params){} load(){} } class Achievement extends AbstractGapiResponse { setHiddenIconUrl(hiddenIconUrl) { this.hiddenIconUrl = hiddenIconUrl; } setValues(params) { let self = this; this._chain(params)._chain(function (res) { self.achievementType = res["achievementType"]; self.description = res["description"]; self.experiencePoints = res["experiencePoints"]; self.id = res["id"]; self.initialState = res["initialState"]; self.isRevealedIconUrlDefault = res["isRevealedIconUrlDefault"]; self.isUnlockedIconUrlDefault = res["isUnlockedIconUrlDefault"]; self.name = res["name"]; self.revealedIconUrl = res["revealedIconUrl"]; self.unlockedIconUrl = res["unlockedIconUrl"]; self.achievementState = res["achievementState"]; // self.experiencePoints = res["experiencePoints"]; //oben bereits gesetzt // self.id = res["id"]; //oben bereits gesetzt self.lastUpdatedTimestamp = res["lastUpdatedTimestamp"]; }); return this.promise; } show(elem) { let self = this; this._chain(function () { let achievementImg = elem.querySelector(".achievement-img"); let achievementTitle = elem.querySelector(".achievement-title"); let achievementDescription = elem.querySelector(".achievement-description"); let achievementLastTimestamp = elem.querySelector(".achievement-lastTimestamp"); let achievementXp = elem.querySelector(".achievement-xp"); if (self.achievementState === "UNLOCKED") { achievementImg.src = self.unlockedIconUrl; achievementLastTimestamp.innerText = Helper.strftime("%d %a %Y", parseInt(self.lastUpdatedTimestamp)); } else if (self.achievementState === "REVEALED") { achievementImg.src = self.revealedIconUrl; } else { achievementImg.src = self.hiddenIconUrl; achievementTitle.appendChild(Translator.makePersistentTranslation("achievement-hidden-title")); achievementDescription.appendChild(Translator.makePersistentTranslation("achievement-hidden-description")); return; } achievementTitle.innerText = self.name; achievementDescription.innerText = self.description; achievementXp.innerText = self.experiencePoints+" XP"; }); return this.promise; } } class AchievementList extends AbstractGapiResponse { constructor(gameService, params) { super(gameService, params); this.removedAchievementIds = []; } setHiddenImgLink(hiddenImgLink) { this.hiddenIconUrl = hiddenImgLink; } setRemovedAchievementIds(removedAchievementIds) { this.removedAchievementIds = removedAchievementIds; } load(maxResults, pageToken, language) { return this.setValues(Promise.all([this.gameService.getAchievements(maxResults, pageToken, null, language), this.gameService.getPlayerAchievements(null, maxResults, pageToken, null, language)]).then(function (res) { if (Helper.isNotNull(res[0]["items"]) && Helper.isNotNull(res[1]["items"])) { for (let i = 0, n = Math.min(res[0]["items"].length, res[1]["items"].length); i < n; i++) { for (let key in res[1]["items"][i]) { res[0]["items"][i][key] = res[1]["items"][i][key]; } } } return res[0]; }) ); } setValues(params) { let self = this; this._chain(params)._chain(function (res) { let promises = []; self.nextPageToken = res["nextPageToken"]; self.items = []; if (Helper.isNotNull(res["items"])) { for (let i = 0, n = res["items"].length; i < n; i++) { if (self.removedAchievementIds.indexOf(res["items"][i]["id"]) === -1) { let achievement = new Achievement(self.gameService, res["items"][i]); achievement.setHiddenIconUrl(self.hiddenIconUrl); self.items.push(achievement); promises.push(achievement.promise); } } } return Promise.all(promises); }); return this.promise; } show(elem) { let self = this; this._chain(function () { let nextButton = elem.querySelector("#achievement-list-next-button"); let achievementTemplate = elem.querySelector("#achievement-list-achievement-template"); let parentAchievementTemplate = achievementTemplate.parentElement; parentAchievementTemplate.removeAllChildren(); achievementTemplate.id = ""; let promises = []; for (let i = 0, n = self.items.length; i < n; i++) { let achievementElem = Helper.cloneNode(achievementTemplate); promises.push(self.items[i].show(achievementElem)); parentAchievementTemplate.appendChild(achievementElem); } return Promise.all(promises); }); return this.promise; } } class GameService extends AbstractService { getLeaderboards(maxResults, pageToken, language, consistencyToken) { language = Helper.nonNull(language, Translator.currentLanguage); return this.gapiHandler.promise.then(function () { return new Promise(function (resolve) { gapi["client"]["games"]["leaderboards"]["list"]({ "maxResults": maxResults, "consistencyToken": consistencyToken, "pageToken": pageToken, "language": language })["execute"](function (response) { resolve(response); }); }); }); } getScoresAroundPlayer(leaderboardId, collection, timeSpan, maxResults, pageToken, resultsAbove, returnTopIfAbsent, consistencyToken, language) { language = Helper.nonNull(language, Translator.currentLanguage); return this.gapiHandler.promise.then(function () { return new Promise(function (resolve) { gapi["client"]["games"]["scores"]["listWindow"]({ "collection": collection, "leaderboardId": leaderboardId, "timeSpan": timeSpan, "consistencyToken": consistencyToken, "language": language, "maxResults": maxResults, "pageToken": pageToken, "resultsAbove": resultsAbove, "returnTopIfAbsent": returnTopIfAbsent })["execute"](function (response) { resolve(response); }); }); }); } getAchievements(maxResults, pageToken, consistencyToken, language) { language = Helper.nonNull(language, Translator.currentLanguage); return this.gapiHandler.promise.then(function () { return new Promise(function (resolve) { gapi["client"]["games"]["achievementDefinitions"]["list"]({ "consistencyToken": consistencyToken, "language": language, "maxResults": maxResults, "pageToken": pageToken, })["execute"](function (response) { resolve(response); }); }); }); } getPlayerAchievements(playerId, maxResults, pageToken, consistencyToken, language) { playerId = Helper.nonNull(playerId, "me"); language = Helper.nonNull(language, Translator.currentLanguage); return this.gapiHandler.promise.then(function () { return new Promise(function (resolve) { gapi["client"]["games"]["achievements"]["list"]({ "playerId": playerId, "consistencyToken": consistencyToken, "language": language, "maxResults": maxResults, "pageToken": pageToken, })["execute"](function (response) { resolve(response); }); }); }); } submitScore(leaderboardId, score) { return this.gapiHandler.promise.then(function () { return new Promise(function (resolve) { gapi["client"]["games"]["scores"]["submit"]({ "leaderboardId": leaderboardId, "score": score })["execute"](function (response) { resolve(response); }); }); }); } revealAchievement(achievementId) { return this.gapiHandler.promise.then(function () { return new Promise(function (resolve) { gapi["client"]["games"]["achievements"]["reveal"]({ "achievementId": achievementId })["execute"](function (response) { resolve(response); }); }); }); } unlockAchievement(achievementId, showIsNewAchievement) { return this.gapiHandler.promise.then(function () { return new Promise(function (resolve) { gapi["client"]["games"]["achievements"]["unlock"]({ "achievementId": achievementId })["execute"](function (response) { resolve(response); }); }); }).then(function(response){ if (showIsNewAchievement && response["newlyUnlocked"]) { FlashMessenger.addMessage(FlashMessenger.MESSAGE_TYPE_SUCCESS, Translator.translate("new-unlocked-achievement")); } }); } } GameService.COLLECTION_PUBLIC = "PUBLIC"; GameService.COLLECTION_SOCIAL = "SOCIAL"; GameService.COLLECTION_SOCIAL_1P = "SOCIAL_1P"; GameService.TIME_SPAN_ALL_TIME = "ALL_TIME"; GameService.TIME_SPAN_DAILY = "DAILY"; GameService.TIME_SPAN_WEEKLY = "WEEKLY"; class GapiPlayer extends AbstractGapiResponse{ setValues(params) { let self = this; this._chain(params)._chain(function(res){ self.playerId = res["playerId"]; self.displayName = res["displayName"]; self.avatarImageUrl = res["avatarImageUrl"]; self.experienceInfo = res["experienceInfo"]; // self.bannerUrlPortrait = res["scoreValue"]; // self.formattedScore = res["formattedScore"]; // self.timeSpan = res["timeSpan"]; // self.writeTimestampMillis = res["writeTimestampMillis"]; // self.scoreTag = res["scoreTag"]; }); return this.promise; } } class Score extends AbstractGapiResponse { setValues(params) { let self = this; this._chain(params)._chain(function(res){ self.player = new GapiPlayer(self.gameService, res["player"]); self.scoreRank = res["scoreRank"]; self.formattedScoreRank = res["formattedScoreRank"]; self.scoreValue = res["scoreValue"]; self.formattedScore = res["formattedScore"]; self.timeSpan = res["timeSpan"]; self.writeTimestampMillis = res["writeTimestampMillis"]; self.scoreTag = res["scoreTag"]; return self.player.promise; }); return this.promise; } } class Leaderboard extends AbstractGapiResponse { setValues(params) { let self = this; this.promise = this.promise.then(function () { return Promise.resolve(params); }).then(function (res) { let promises = []; self.nextPageToken = res["nextPageToken"]; self.previousPageToken = res["prevPageToken"]; self.numScores = res["numScores"]; self.playerScore = new Score(self.gameService, res["playerScore"]); promises.push(self.playerScore.promise); self.items = []; if (Helper.isNotNull(res["items"])) { for (let i = 0, n = res["items"].length; i < n; i++) { self.items.push(new Score(self.gameService, res["items"][i])); promises.push(self.items[i].promise); } } return Promise.all(promises); }); return this.promise; } setLeaderboardId(leaderboardId) { this.leaderboardId = leaderboardId; } load(maxResults, pageToken, resultsAbove) { return this.setValues(this.gameService.getScoresAroundPlayer(this.leaderboardId, this.collection, this.timeSpan, maxResults, pageToken, resultsAbove)); } setCollection(collection) { this.collection = collection; } setTimeSpan(timeSpan) { this.timeSpan = timeSpan; } setDisplayName(name) { this.displayName = name; } show(elem, buildTimeSelect, buildCollectionSelect, isTime) { buildCollectionSelect = Helper.nonNull(buildCollectionSelect, true); buildTimeSelect = Helper.nonNull(buildTimeSelect, true); isTime = Helper.nonNull(isTime, false); let args = arguments; let self = this; this._chain(function () { let prevButton = elem.querySelector("#leaderboard-previous-button"); let nextButton = elem.querySelector("#leaderboard-next-button"); let scoreTemplate = elem.querySelector("#leaderboard-score-template"); let parentScoreElem = scoreTemplate.parentElement; parentScoreElem.removeAllChildren(); let timeElem = self._buildTime(buildTimeSelect); elem.querySelector("#leaderboard-time-span").removeAllChildren().appendChild(timeElem); let collectionElem = self._buildCollection(buildCollectionSelect); elem.querySelector("#leaderboard-collection").removeAllChildren().appendChild(collectionElem); timeElem.onchange = function () { self._updateWithDataFromElem(elem, parentScoreElem, scoreTemplate, args); }; collectionElem.onchange = function () { self._updateWithDataFromElem(elem, parentScoreElem, scoreTemplate, args); }; for (let i = 0, n = self.items.length; i < n; i++) { let score = self.items[i]; let scoreElem = Helper.cloneNode(scoreTemplate); scoreElem.querySelector(".leaderboard-rank").innerText = score.scoreRank; scoreElem.querySelector(".leaderboard-player-img").src = score.player.avatarImageUrl; scoreElem.querySelector(".leaderboard-player-name").innerText = score.player.displayName; scoreElem.querySelector(".leaderboard-points").innerText = (isTime)?Helper.strftime("%H:%M:%S", new Date(parseInt(score.scoreValue)), true):score.formattedScore; scoreElem.id = ""; if (Helper.isNotNull(self.playerScore) && Helper.isNotNull(self.playerScore.player) && score.player.playerId === self.playerScore.player.playerId) { scoreElem.classList.add("leaderboard-current-player"); } parentScoreElem.appendChild(scoreElem); } if (self.items.length === 0) { parentScoreElem.appendChild(Translator.makePersistentTranslation("no-scores-available")); } }); return this.promise; } _buildCollection(buildCollectionSelect) { if (buildCollectionSelect) { let select = document.createElement("select"); let publicOption = document.createElement("option"); publicOption.value = GameService.COLLECTION_PUBLIC; publicOption.innerHTML = Translator.translate(GameService.COLLECTION_PUBLIC); publicOption.dataset["translate"] = GameService.COLLECTION_PUBLIC; if (GameService.COLLECTION_PUBLIC === this.collection) { publicOption.selected = true; } let socialOption = document.createElement("option"); socialOption.value = GameService.COLLECTION_SOCIAL; socialOption.innerHTML = Translator.translate(GameService.COLLECTION_SOCIAL); socialOption.dataset["translate"] = GameService.COLLECTION_SOCIAL; if (GameService.COLLECTION_SOCIAL === this.collection) { socialOption.selected = true; } select.appendChild(socialOption); select.appendChild(publicOption); return select; } else { return Translator.makePersistentTranslation(this.collection); } } _updateWithDataFromElem(rootElem, scoreTable, scoreTemplate, args) { let loadingSymbol = Helper.createLoadingSymbol(); rootElem.replaceWith(loadingSymbol); scoreTable.removeAllChildren().appendChild(scoreTemplate); let collectionElem = rootElem.querySelector("#leaderboard-collection select"); if (Helper.isNotNull(collectionElem)) { this.collection = collectionElem.options[collectionElem.selectedIndex].value; } let timeSpanElem = rootElem.querySelector("#leaderboard-time-span select"); if (Helper.isNotNull(timeSpanElem)) { this.timeSpan = timeSpanElem.options[timeSpanElem.selectedIndex].value; } this.load(); this.show.apply(this, args).then(function () { loadingSymbol.replaceWith(rootElem); }); } _buildTime(buildTimeSelect) { if (buildTimeSelect) { let select = document.createElement("select"); let allTime = document.createElement("option"); allTime.value = GameService.TIME_SPAN_ALL_TIME; allTime.innerHTML = Translator.translate(GameService.TIME_SPAN_ALL_TIME); allTime.dataset["translate"] = GameService.TIME_SPAN_ALL_TIME; if (GameService.TIME_SPAN_ALL_TIME === this.timeSpan) { allTime.selected = true; } let weeklyOption = document.createElement("option"); weeklyOption.value = GameService.TIME_SPAN_WEEKLY; weeklyOption.innerHTML = Translator.translate(GameService.TIME_SPAN_WEEKLY); weeklyOption.dataset["translate"] = GameService.TIME_SPAN_WEEKLY; if (GameService.TIME_SPAN_WEEKLY === this.timeSpan) { weeklyOption.selected = true; } let dailyOption = document.createElement("option"); dailyOption.value = GameService.TIME_SPAN_DAILY; dailyOption.innerHTML = Translator.translate(GameService.TIME_SPAN_DAILY); dailyOption.dataset["translate"] = GameService.TIME_SPAN_DAILY; if (GameService.TIME_SPAN_DAILY === this.timeSpan) { dailyOption.selected = true; } select.appendChild(dailyOption); select.appendChild(weeklyOption); select.appendChild(allTime); return select; } else { return Translator.makePersistentTranslation(this.timeSpan); } } } class GapiHandler { constructor() { this.loginCallbacks = []; this.games = new GameService(this); } init() { this.promise = new Promise(function (resolver) { let scriptElem = document.createElement("script"); scriptElem.src = "https://apis.google.com/js/api.js"; scriptElem.async = true; scriptElem.defer = true; scriptElem.onload = resolver; scriptElem.onreadystatechange = function () { if (this.readyState === "complete") { resolver(); } }; document.body.appendChild(scriptElem); }).then(function () { window["gapi"] = gapi; }); return this.promise; } load(libs) { this.promise = this.promise.then(function () { return new Promise(function (resolver) { gapi["load"](libs, resolver); }); }); return this.promise; } initClient(apiKey, clientId, scope, discoveryDocs, fetchBasicProfile, uxMode) { scope = Helper.nonNull(scope, "profile"); fetchBasicProfile = Helper.nonNull(fetchBasicProfile, false); discoveryDocs = Helper.nonNull(discoveryDocs, []); uxMode = Helper.nonNull(uxMode,"popup"); let self = this; this.promise = this.promise.then(function () { return gapi["client"]["init"]({ "apiKey": apiKey, "discoveryDocs": discoveryDocs, "clientId": clientId, "scope": scope, "fetch_basic_profile":fetchBasicProfile, "ux_mode":uxMode }); }).then(function () { let authInstance = gapi["auth2"]["getAuthInstance"](); authInstance["isSignedIn"]["listen"](function (isLoggedIn) { for (let i = 0, n = self.loginCallbacks.length; i < n; i++) { self.loginCallbacks[i](isLoggedIn); } }); }); return this.promise; } loadClient(lib, version) { this.promise = this.promise.then(function () { return new Promise(function (resolver) { gapi["client"]["load"](lib, version, resolver); }); }); return this.promise; } addLoginCallback(callback) { this.loginCallbacks.push(callback); let authInstance = gapi["auth2"]["getAuthInstance"](); callback(authInstance["isSignedIn"]["get"]()); } addLoginAction(app, callback) { let self = this; callback = Helper.nonNull(callback, function(){ self.login(); }); this.loginAction = new MenuAction("login", callback, MenuAction.SHOW_NEVER, 10001); app.addDefaultAction(this.loginAction); ActionBarMenu.currentMenu.addAction(this.loginAction.copy()); } removeLoginAction(app) { app.removeDefaultAction(this.loginAction); } login() { this.promise = this.promise.then(function () { let authInstance = gapi["auth2"]["getAuthInstance"](); return authInstance["signIn"](); }); return this.promise; } logout() { this.promise = this.promise.then(function () { let authInstance = gapi["auth2"]["getAuthInstance"](); return authInstance["signOut"](); }); return this.promise; } isLoggedIn() { return this.promise.then(function () { let authInstance = gapi["auth2"]["getAuthInstance"](); return authInstance["isSignedIn"]["get"](); }) } } 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"; } return 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"; } return 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( (resolve, reject) => { 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 } }; }).catch(e => { console.warn(e); reject(e); }); }); } loadAll(objectStore, query, count) { let self = this; return new Promise((resolve, reject) => { 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 } }; }).catch(e => { console.warn(e); reject(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(); } }; }); }); } removeAll(objectStore){ return new Promise((resolve) => { this.openStore(objectStore, "readwrite", (store) => { let req = store.clear(); req.onerror = (e) => { throw { "type":"indexed-db-index-error", "event": e } }; req.onsuccess = resolve; }); }) } upgrade(db) { }; } class Prioritised { constructor(resultPromises, callback) { this.result = null; this.prioResult = -1; this.callback = null; this.hasResult = false; this.highestPrio = -1; let self = this; this.callbackCalledPromise = new Promise(function (resolver) { self.callbackCalledPromiseResolver = resolver; }); this.endPromise = new Promise(function (resolver) { self.endPromiseResolver = resolver; }); this.setCallback(callback); for (let k in resultPromises) { this.registerResultPromise(k, resultPromises[k]); } } async registerResultPromise(prio, promise) { this.highestPrio = Math.max(prio, this.highestPrio); let res = await Promise.resolve(promise); this._setResult(prio, res); } _setResult(prio, result) { if (this.prioResult <= prio || !this.hasResult) { this.hasResult = true; this.prioResult = prio; this.result = result; if (Helper.isNotNull(this.callback)) { this.callback(result, prio, this); this.callbackCalledPromiseResolver(); } if (prio == this.highestPrio) { this.endPromiseResolver(); } } } setCallback(callback) { this.callback = callback; if (this.hasResult && Helper.isNotNull(callback)) { callback(this.result, this.prioResult, this); this.callbackCalledPromiseResolver(); } } getCallbackCalledPromise() { return this.callbackCalledPromise; } getEndPromise() { return this.endPromise; } } class ScriptLoader { static loadScript(scriptSrc) { if (Helper.isNotNull(ScriptLoader.scriptPromises[scriptSrc])) { return ScriptLoader.scriptPromises[scriptSrc]; } else { let scriptPromise = new Promise(function (resolve) { let script = document.createElement("script"); script.src = Helper.basePath(scriptSrc); script.onload = resolve; document.body.appendChild(script); }); ScriptLoader.scriptPromises[scriptSrc] = scriptPromise; return scriptPromise; } } static loadCss(cssFile, media){ if (Helper.isNotNull(ScriptLoader.cssPromises[cssFile])) { return ScriptLoader.cssPromises[cssFile]; } else { media = Helper.nonNull(media, "all"); let cssPromise = new Promise(function (resolve) { let link = document.createElement("link"); link.rel='stylesheet'; link.type="text/css"; link.href = Helper.basePath(cssFile); link.media = media; link.onload = resolve; document.head.appendChild(link); }); ScriptLoader.cssPromises[cssFile] = cssPromise; return cssPromise; } } } ScriptLoader.scriptPromises = {}; ScriptLoader.cssPromises = {}; class ShareButton { constructor(deviceType, icon, callback, shouldLoadImg) { this._deviceType = deviceType; this._icon = icon; this._callback = callback; console.log("shouldLoad", Helper.nonNull(shouldLoadImg, false), shouldLoadImg); if (Helper.nonNull(shouldLoadImg, false)){ this._icon = ViewInflater.inflate(this._icon); } } 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 CopyShareButton extends ShareButton { constructor(icon, shouldLoadImg) { super(ShareButton.TYPE_ALL, icon, function (link) { try{ let elementToCopy = document.createElement("div"); elementToCopy.style.position = "fixed"; elementToCopy.style.top = 0; elementToCopy.style.left = 0; elementToCopy.style.width = "0em"; elementToCopy.style.height = "0em"; elementToCopy.style.padding = 0; elementToCopy.style.border = "none"; elementToCopy.style.outline = "none"; elementToCopy.style.boxShadow = "none"; elementToCopy.style.backgroundElement = "transparent"; elementToCopy.style.color = "transparent"; elementToCopy.innerText = link; document.body.appendChild(elementToCopy); Helper.select(elementToCopy); if (document.execCommand("copy")) { FlashMessenger.addMessage(FlashMessenger.MESSAGE_TYPE_SUCCESS, Translator.translate("copied"), 1800); } elementToCopy.remove(); } catch(e) { console.error(e); } }, shouldLoadImg); } } 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"); linkElement.classList.add("share-icon"); let iconUrl = button.getIcon(); if (typeof iconUrl === "string") { let iconElement = document.createElement("img"); iconElement.src = Helper.basePath(button.getIcon()); iconElement.classList.add("share-icon"); linkElement.appendChild(iconElement); } else { Promise.resolve(iconUrl).then(elem => { linkElement.appendChild(elem); }); } return linkElement; } } } ShareManager.init(); class SmsShareButton extends ShareButton { constructor(icon, shouldLoadImg) { 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'); }, shouldLoadImg); } } class TelegramShareButton extends ShareButton { constructor(icon, shouldLoadImg) { super(ShareButton.TYPE_ALL, icon, function (link) { let linkToOpen = "https://t.me/share/url?url="+encodeURIComponent(link); window.open(linkToOpen, '_blank'); }, shouldLoadImg); } } class WhatsappShareButton extends ShareButton { constructor(icon, shouldLoadImg) { 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'); }, shouldLoadImg); } } class SimpleWS{ static _initMe(ws, url, protocols) { ws._connectionPromise = new Promise(function (resolve) { ws._ws = new WebSocket(url, protocols); ws._ws.onopen = function () { resolve(); for (let i = 0, n = ws._openListeners.length; i < n; i++) { if (Helper.isNotNull(ws._openListeners[i])) { ws._openListeners[i](); } } }; ws._ws.onerror = function (err) { resolve(Promise.reject("WS-Error")); console.error(err); for (let i = 0, n = ws._errorListeners.length; i < n; i++) { if (Helper.isNotNull(ws._errorListeners[i])) { ws._errorListeners[i](err); } } }; ws._ws.onclose = function (e) { console.log("ws-close"); for (let i = 0, n = ws._closeListeners.length; i < n; i++) { if (Helper.isNotNull(ws._closeListeners[i])) { ws._closeListeners[i](e, ws._shouldReconnectAfterClose); } } if (ws._shouldReconnectAfterClose) { setTimeout(function () { SimpleWS._initMe(ws, url, protocols); }, 200); } }; ws._ws.onmessage = function (message) { if (message.data === "pong") { ws.ping(); return; } let jsonMessage = JSON.parse(message.data); for (let i = 0, n = ws._messageListeners.length; i < n; i++) { if (ws._messageListeners[i]) { ws._messageListeners[i](jsonMessage, message); } } }; }); } constructor(url, protocols, shouldReconnectAfterClose) { this._shouldReconnectAfterClose = Helper.nonNull(shouldReconnectAfterClose, true); this._ws = null; this._openListeners = []; this._errorListeners = []; this._messageListeners = []; this._closeListeners = []; this._connectionPromise = Promise.resolve(); this._pong = null; this._ping = null; SimpleWS._initMe(this, url, protocols); this.activatePingPong(); } ping(interval, answerTime) { interval = Helper.nonNull(interval, 5000); answerTime = Helper.nonNull(answerTime, 5000); if (this._pong !== null) { clearTimeout(this._pong); } if (this._ping !== null) { clearTimeout(this._ping); } let self = this; this._ping = setTimeout(function () { self.send({'action':"ping"}); self._pong = setTimeout(function () { self._ws.close(); }, answerTime); }, interval); } activatePingPong() { this.ping(); } send(jsonMessage) { let ws = this; return this._connectionPromise.then(function () { ws._ws.send(JSON.stringify(jsonMessage)); }); } connectedPromise() { let self = this; return new Promise(function(resolve){ self.addOpenListener(function(){ resolve(); }, true); }) } promiseSend(jsonMessage) { let self = this; return this.connectedPromise().then(function(){ return self.send(jsonMessage); }); } close() { this._shouldReconnectAfterClose = false; this._ws.close(); } addErrorListener(listener) { if (typeof listener === 'function') { return this._errorListeners.push(listener) - 1; } return -1; } removeErrorListener(listenerId) { if (listenerId < this._errorListeners.length && listenerId >= 0) { this._errorListeners[listenerId] = null; } } addCloseListener(listener, callNowIfClosed) { callNowIfClosed = Helper.nonNull(callNowIfClosed, false); if (typeof listener === 'function') { if (callNowIfClosed && this.status() !== WebSocket.OPEN) { listener(); } return this._closeListeners.push(listener) - 1; } return -1; } removeCloseListener(listenerId) { if (listenerId < this._closeListeners.length && listenerId >= 0) { this._closeListeners[listenerId] = null; } } addOpenListener(listener, callNowIfOpen) { callNowIfOpen = Helper.nonNull(callNowIfOpen, false); if (typeof listener === 'function') { if (callNowIfOpen && this.status() === WebSocket.OPEN) { listener(); } return this._openListeners.push(listener) - 1; } return -1; } removeOpenListener(listenerId) { if (listenerId < this._openListeners.length && listenerId >= 0) { this._openListeners[listenerId] = null; } } addMessageListener(listener) { if (typeof listener === 'function') { return this._messageListeners.push(listener) - 1; } return -1; } removeMessageListener(listenerId) { if (listenerId < this._messageListeners.length && listenerId >= 0) { this._messageListeners[listenerId] = null; } } status() { return this._ws.readyState; } } class Fragment extends Context { constructor(site, view) { super(view); this.site = site; this.active = true; } getSite() { return this.site; } isActive() { return this.active; } } class Theme { constructor(name, className, icon) { this._name = name; this._className = className; this._icon = icon; } } class DBTranslator extends Translator { _loadLanguage(language) { let self = this; return this._db.loadTranslationsForLang(language).then(function (res) { self._translations[language] = Object.assign(res, self._translations[language]); }).catch(function (err) { console.error("could not load lang " + language + " because of error: ", err); }); } loadUserLanguage() { this._currentLanguage = null; let self = this; return this._db.getLanguage().then(function (userLanguage) { if (Helper.isNull(userLanguage) || self._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(self._baseLanguage); if (userLanguages !== undefined) { for (let i = 0, numLanguages = userLanguages.length; i < numLanguages; i++) { if (self._supportedLanguages.indexOf(userLanguages[i]) !== -1) { userLanguage = userLanguages[i]; break; } } } } return self.setLanguage(userLanguage.toLowerCase()) }); } } 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); //Fallback setTimeout(() => { resolve(false); }, (time + delay) * 1000); }); //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); if (getComputedStyle(elem).getPropertyValue("opacity") === "1") { resolve(false); } //Fallback setTimeout(() => { resolve(false); }, (time + delay) * 1000); //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; }); window["fetch"] = Helper.nonNull(window["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; }); } export { App, ChainAble, CookieCompliance, ConfirmDialog, Dialog, FlashMessenger, AbstractService, AbstractGapiResponse, Achievement, AchievementList, GameService, GapiPlayer, Leaderboard, Score, GapiHandler, Helper, InitPromise, ActionBarMenu, Menu, MenuAction, OpenSubmenuAction, Submenu, MyDb, Prioritised, ScriptLoader, CopyShareButton, ShareButton, ShareManager, SmsShareButton, TelegramShareButton, WhatsappShareButton, SimpleWS, AbstractSite, Context, Fragment, PauseSite, SiteContainer, SiteManager, SystemSettings, Theme, ThemeManager, DBTranslator, Translator, TranslatorDB, ViewInflater, applyPolyfills };