2018-07-02 18:47:21 +02:00

3847 lines
120 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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