wordRotator/src/js/lib/pwa-lib.js
2018-10-01 22:41:59 +02:00

4323 lines
141 KiB
JavaScript
Executable File
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 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 = "&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) {
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 = {
'&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 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 = "&times;";
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)
{
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 CopyShareButton extends ShareButton
{
constructor(icon) {
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);
}
});
}
}
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 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 };