/********************************************************************************** * Copyright by Safevia.net 2020 **********************************************************************************/ var App = window.App = window.App || {}; App.crypto = window.crypto; App.onInit = []; (function() { function constant(obj, propName, propValue) { Object.defineProperty( obj, propName, { value: propValue, writable: false, enumerable: true, configurable: true }); } var baseWebURL = location.href.includes("pl") ? location.href.split("pl")[0] : location.href.split("en")[0]; var baseServiceURL = 'https://messages.safevia.net/'; constant(App, 'BASE_WEB_URL', baseWebURL); constant(App, 'BASE_SERVICE_URL', baseServiceURL); constant(App, 'FAIL_NOT_SUPPORTED', 'NOT SUPPORTED'); constant(App, 'FAIL_NEED_PERMISSION', 'PERMISSION NEEDED'); constant(App, 'FAIL_NO_PERMISSION', 'NO PERMISSION'); })(); App.addInitListener = function(callback) { App.onInit.push(callback); } App.acceptPrivacy = function() { App.acceptPolicy('privacy', 1, 'stickbarPolicy'); } App.acceptPolicy = function(policyType, versionNumber, stickbarID) { var request = { service: "cookie", parameters: { "key": policyType, "cookieValue": "Version" + versionNumber + "_" + Date.now()}, success: function(response) { if (response && response.status == 'OK') { App.GUI.hide(stickbarID); } else { App.GUI.handleAcceptPolicyError(policyType, response, 'Exception while policy accepting. Response: ' . response); } }, failure: function(response) { App.GUI.handleAcceptPolicyError(policyType, response, 'Server failure while accepting policy: ' . response); } }; App.callService(request); } /** * request { * method: "GET", * url: "", * headers: {}, * parameters: {}, * body: null, * responseType: "json", // "arraybuffer" || "blob" || "document" || "json" || "text" * success: function, * failure: function * } */ App.ajax = function(request) { var client = new XMLHttpRequest(); var url = request.url; var responseType = request.responseType || "json"; var parameters = request.parameters; if (typeof(parameters) === "object") { var count = 0; for (var paramName in parameters) { if (parameters.hasOwnProperty(paramName)) { var paramValue = parameters[paramName]; url += (count == 0) ? "?" : "&"; url += encodeURIComponent(paramName) + "=" + encodeURIComponent(paramValue); count++; } } } else if (typeof(parameters) === "string") { url += "?" + parameters; } var successCallback = request.success; var failureCallback = request.failure; client.open(request.method || "GET", url, true); client.onreadystatechange = function() { if (client.readyState === XMLHttpRequest.DONE) { if (client.status === 200 || client.status === 204) { if (successCallback !== undefined) { var response = client.response; if (responseType == "json" && (client.responseType == "string" || typeof(response) == "string")) { response = JSON.parse(response); } successCallback(response); } } else { if (failureCallback !== undefined) { failureCallback(client); } } } } var headers = request.headers; if (typeof(headers) === "object") { for (var headerName in headers) { if (headers.hasOwnProperty(headerName)) { var headerValue = headers[headerName]; client.setRequestHeader(headerName, headerValue); } } } client.responseType = responseType; //https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials //XMLHttpRequest responses from a different domain cannot set cookie values for their own domain unless withCredentials is set to true before making the request, regardless of Access-Control- header values. client.withCredentials = true; if (request.body) { var payload = request.body; if (payload.toString() != "[object Blob]" && payload.toString() != "[object File]") { payload = JSON.stringify(payload); } client.send(payload); } else { client.send(); } } /** * request { * service: "", * parameters: {}, * body: null, * responseType: "", // "arraybuffer" || "blob" || "document" || "json" || "text" * success: function, * failure: function * } */ App.callService = function (request) { request.method = request.method || "POST"; request.url = request.url || App.BASE_SERVICE_URL + request.service; return App.ajax(request); } App.fail = function (e) { console.log(e); var msg = ""; var stack = null; if (e) { if (typeof(e) === "string") { msg = e; } else if (e.toString() == "[object XMLHttpRequest]" || e.status) { msg = "HTTP " + e.status + " " + e.responseURL; } else { if (e.stack) { stack = e.stack; } msg = e.toString(); } } App.callService({ service: "error-report", body: {"msg": msg, "stack": stack} }); } App.runHandleExceptions = function(func) { try { return func(); } catch (e) { App.fail(e); return null; } } App.wrapHandleExceptions = function(func) { return function() { return App.runHandleExceptions(func); } } App.runNext = function(func) { setTimeout(App.wrapHandleExceptions(func), 1); return func; } App.deriveKeyFromPassword = function(password, passwordAlgo, encryptionAlgo, salt) { return window.crypto.subtle.importKey("raw", App.util.encodeUTF8(password), {"name": passwordAlgo.name}, false, ["deriveKey"]). then(function(baseKey){ var algo = App.util.clone(passwordAlgo); algo.salt = salt; return window.crypto.subtle.deriveKey(algo, baseKey, encryptionAlgo, true, ["encrypt", "decrypt"]); }); } App.util = App.util || {}; App.util.clone = function(o) { return JSON.parse(JSON.stringify(o)); } App.util.encodeUTF8 = function(string) { if (window.TextEncoder) { return new TextEncoder('utf-8').encode(string); } var inputLen = string.length; var inputPos = 0; var outputPos = 0; var output = new Uint8Array(inputLen * 4); while (inputPos < inputLen) { var char = string.charCodeAt(inputPos++); if (char > 0xD7FF && char < 0xDC00) { if (inputPos < inputLen) { var charNext = string.charCodeAt(inputPos); if ((charNext & 0xFC00) === 0xDC00) { inputPos++; char = ((char & 0x3FF) << 10) + (charNext & 0x3FF) + 0x10000; } } if (char > 0xD7FF && char < 0xDC00) { continue; } } if ((char & 0xFFFFFF80) === 0) { output[outputPos++] = char; continue; } else if ((char & 0xFFFFF800) === 0) { output[outputPos++] = ((char >>> 6) & 0x1f) | 0xC0; } else if ((char & 0xFFFF0000) === 0) { output[outputPos++] = ((char >>> 12) & 0x0f) | 0xE0; output[outputPos++] = ((char >>> 6) & 0x3f) | 0x80; } else if ((char & 0xFFE00000) === 0) { output[outputPos++] = ((char >>> 18) & 0x07) | 0xF0; output[outputPos++] = ((char >>> 12) & 0x3F) | 0x80; output[outputPos++] = ((char >>> 6) & 0x3F) | 0x80; } else { continue; } output[outputPos++] = (char & 0x3F) | 0x80; } return output.slice(0, outputPos); } App.util.decodeUTF8 = function(data, callback) { if (window.TextDecoder) { var decoded = new TextDecoder('utf-8').decode(data); callback(decoded); return; } var blob = new Blob([data], {type: 'text/plain; charset=UTF-8'}); var reader = new FileReader(); reader.onload = function() { callback(reader.result); }; reader.readAsText(blob); } App.util.encodeBase64URL = function(input) { var bytes = input; if (bytes.byteLength) { //support ArrayBuffer bytes = new Uint8Array(bytes); } var strRaw = ''; for (var i = 0; i < bytes.length; i++) { strRaw += String.fromCharCode(bytes[i]); } var strBase64 = btoa(strRaw); var strBase64URL = ''; for (var i = 0; i < strBase64.length; i++) { var c = strBase64.charAt(i); if (c == '+') { strBase64URL += '-'; } else if (c == '/') { strBase64URL += '_'; } else if (c != '=') { strBase64URL += c; } } return strBase64URL; } App.util.decodeBase64URL = function(strBase64URL) { var strBase64 = ''; for (var i = 0; i < strBase64URL.length; i++) { var c = strBase64URL.charAt(i); if (c == '-') { strBase64 += '+'; } else if (c == '_') { strBase64 += '/'; } else { strBase64 += c; } } var strRaw = atob(strBase64); var bytes = new Uint8Array(strRaw.length); for (var i = 0; i < bytes.length; i++) { bytes[i] = strRaw.charCodeAt(i); } return bytes; } App.util.isCookieAccepted = function(cookieName) { var cookie = App.util.readCookie(cookieName); if (cookie != "" && cookie != null) { var ver = cookie.split('_'); if (ver.length > 0) { var v = parseInt(ver[0].replace("Version", "")); if (!isNaN(v)) { return true; } } } return false; } App.util.setCookie = function(key, value) { var cookieString = encodeURIComponent(key) + "=" + encodeURIComponent(value) + ";Expires=Fri, 31 Dec 9999 23:59:59 GMT;SameSite=Lax;"; if (window.location.protocol == "https:") { cookieString += ";Secure"; } cookieString += ";path=/"; document.cookie = cookieString; } App.util.readCookie = function(name) { var encodedNameWithEQ = encodeURIComponent(name) + "="; var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(encodedNameWithEQ) == 0) { var val = c.substring(encodedNameWithEQ.length); return decodeURIComponent(val); } } return null; } App.util.copyToClipboard = function(text, successCallback, failureCallback) { //failureCallback: FAIL_NOT_SUPPORTED, FAIL_NO_PERMISSION, FAIL_PERMISSION_NEEDED var doStandardClipboardHandling = function() { try { var writeTextResult = navigator.clipboard.writeText(text); if (!writeTextResult) { if (failureCallback) failureCallback(App.FAIL_NOT_SUPPORTED); } else { writeTextResult.then(function() { if (successCallback) successCallback(); }, function() { if (failureCallback) failureCallback(App.FAIL_NO_PERMISSION); }); } } catch (e) { if (failureCallback) failureCallback(App.FAIL_NOT_SUPPORTED); } } var doFallbackClipboardHandling = function() { var textArea; var bSuccessful = false; try { var textArea = document.createElement("textarea"); textArea.value = text; // Avoid scrolling to bottom textArea.style.top = "0"; textArea.style.left = "0"; textArea.style.position = "fixed"; document.body.appendChild(textArea); textArea.focus(); textArea.select(); bSuccessful = document.execCommand('copy'); } catch (err) { } if (textArea) document.body.removeChild(textArea); if (bSuccessful) { if (successCallback) successCallback(); } else { if (failureCallback) failureCallback(App.FAIL_NOT_SUPPORTED); } } var handlePermission = function (permissionStatus) { if (!permissionStatus) permissionStatus = this; //permissionStatus.state: 'granted', 'denied', 'prompt', 'default' if (permissionStatus.state == 'granted') { doStandardClipboardHandling(); return; } else if (permissionStatus.state == 'denied') { if (failureCallback) failureCallback(App.FAIL_NO_PERMISSION); } else { if (failureCallback) failureCallback(App.FAIL_NEED_PERMISSION); } } if (!navigator || !navigator.clipboard) { doFallbackClipboardHandling(); } else { var bPermissionSupported = false; try { var permissionHandling = function(permissionStatus) { permissionStatus.onchange = handlePermission; handlePermission(permissionStatus); }.catch(function() {}); //From documentation Firefox supports on query call - not request. if (navigator.permissions.request) { navigator.permissions.request({name:'clipboard-write'}).then(permissionHandling); bPermissionSupported = true; } else if (navigator.permissions.query) { navigator.permissions.query({name:'clipboard-write'}).then(permissionHandling); bPermissionSupported = true; } } catch (ex) { //permissions not handled or permissions don't have clipboard-write name (eg Firefox) } if (!bPermissionSupported) { doStandardClipboardHandling(); return; } } } App.URL = App.URL || {}; App.URL.parseQueryParameters = function(input, find) { var s = input || document.location.search || document.location.href; if (!s) { return {}; } var query = s; if (find !== false) { var idx = s.indexOf('?'); if (idx === -1) { return {}; } query = s.substr(idx + 1); } var params = {}; var rawParams = query.split("&"); if (rawParams && rawParams.length > 0) { for (var i = 0; i < rawParams.length; i++) { var rawParam = rawParams[i]; var idxKV = rawParam.indexOf('='); var rawParamName = idxKV >= 0 ? rawParam.substr(0, idxKV) : rawParam; var rawParamValue = idxKV >= 0 ? rawParam.substr(idxKV + 1) : null; params[decodeURIComponent(rawParamName)] = (rawParamValue != null ? decodeURIComponent(rawParamValue) : null); } } return params; } App.URL.parseHashParameters = function(input) { var s = input || window.location.hash; if (s) { if (s.substr(0, 1) == '#') { s = s.substr(1); } return App.URL.parseQueryParameters(s, false); } return {}; } App.GUI = App.GUI || {}; App.GUI.handleAcceptPolicyError = function(logEvent, consoleLog, msg) { App.fail(logEvent); }; App.GUI.element = function (target) { if (typeof(target) === "string") { var domElement = document.getElementById(target); if (!domElement) { throw Error("Could not find element: " + target); } return domElement; } return target; } App.GUI.exist = function(target) { var domElement = target; if (typeof(target) === "string") { domElement = document.getElementById(target); } return domElement ? true : false; } App.GUI.show = function (target) { var domElement = App.GUI.element(target); domElement.classList.remove("hidden"); return domElement; } App.GUI.hideAll = function (classes) { var domElements = document.getElementsByClassName(classes); for (i = 0; i < domElements.length; i++) { var domElement = domElements[i]; App.GUI.hide(domElement); } } App.GUI.hide = function (target) { var domElement = App.GUI.element(target); domElement.classList.add("hidden"); return domElement; } App.GUI.shown = function (target) { var domElement = App.GUI.element(target); domElement = document.getElementById(target); if (domElement) { return !domElement.classList.contains("hidden"); } else { return false; } } App.GUI.click = function (target, func) { var domElement = App.GUI.element(target); if (func) { domElement.addEventListener('click', function(event) { try { func(event); } catch (e) { App.fail(e); } event.preventDefault(); }); if (domElement.tagName === 'A') { domElement.setAttribute('href', '#'); } } else { domElement.click(); } } App.GUI.ondrop = function (target, func) { var domElement = App.GUI.element(target); if (func) { domElement.addEventListener('drop', function(event) { try { func(event); } catch (e) { App.fail(e); } event.preventDefault(); }); } } App.GUI.ondragover = function (target, func) { var domElement = App.GUI.element(target); if (func) { domElement.addEventListener('dragover', function(event) { try { func(event); } catch (e) { App.fail(e); } event.preventDefault(); }); } } App.GUI.ondragend = function (target, func) { var domElement = App.GUI.element(target); if (func) { var f = function(event) { try { func(event); } catch (e) { App.fail(e); } event.preventDefault(); }; domElement.addEventListener('dragleave', f); domElement.addEventListener('dragexit', f); domElement.addEventListener('dragend', f); } } App.GUI.ondragstart = function (target, func) { var domElement = App.GUI.element(target); if (func) { var f = function(event) { try { func(event); } catch (e) { App.fail(e); } event.preventDefault(); }; domElement.addEventListener('dragenter', f); domElement.addEventListener('dragstart', f); } } App.GUI.setFunction = function (functionName, target, func) { var domElement = App.GUI.element(target); domElement.addEventListener(functionName, function(event) { try { func(event); } catch (e) { App.fail(e); } event.preventDefault(); }); } App.GUI.screen = function (screen) { var domScreens = document.getElementsByClassName('screen'); for (i = 0; i < domScreens.length; i++) { var domScreen = domScreens[i]; App.GUI.hide(domScreen); domScreen.classList.remove("fadein"); } var domScreen = App.GUI.element(screen); if (domScreen) { domScreen.classList.add("fadein"); App.GUI.show(domScreen); } else { console.log("Could not find screen: " + domScreen); } } App.convert = {} App.convert.Uint8ArrayToHexString = function (input) { var output = ""; var tab = '0123456789ABCDEF'; for (var i = 0; i < input.length; i++) { var v = input[i]; output += tab[v >> 4] + tab[v & 15]; } return output; } App.convert.ArrayBufferToHexString = function (input) { return App.util.convert.Uint8ArrayToHexString(new Uint8Array(input)); } App.convert.HexStringToUint8Array = function (input) { var output = new Uint8Array(input.length / 2); var j = 0; for (var i = 0; i < input.length; i += 2) { output[j++] = parseInt( input.substr(i, 2), 16 ); } return output; }; App.duplicateHTML = function(original, targetParent, idToAssign) { var assignID = function(parentElement) { var children = parentElement.children; for (var i = 0; i < children.length; i++) { var childElement = children[i]; if (childElement.id) { childElement.id = childElement.id + idToAssign; } assignID(childElement); } } var clone = original.cloneNode(true); clone.id = original.id + idToAssign; targetParent.appendChild(clone); assignID(clone); return clone; } //propagate URL hash part in the language switch App.addInitListener(function() { var propagateHashToLanguageSwitch = function() { var el = document.getElementById('language-switch'); if (!el) return; var links = el.getElementsByTagName('a'); for (var i = 0; i < links.length; i++) { var link = links[i]; var href = link.getAttribute('href'); if (!href) continue; var idx = href.indexOf('#'); if (idx >= 0) { href = href.substr(0, idx); } href += window.location.hash; link.setAttribute('href', href); } }; window.onhashchange = propagateHashToLanguageSwitch; propagateHashToLanguageSwitch(); if (App.GUI.exist("btn-privacy")) { App.GUI.click("btn-privacy", App.acceptPrivacy); if (App.util.isCookieAccepted("privacy")) { App.GUI.hide('stickbarPolicy'); } } }); if (self === top) { window.onload = function() { try { document.body.classList.remove('antiClickjack'); if (typeof(App.crypto) !== 'undefined') { document.getElementById('browser-problem-banner').style.display = 'none'; } for (var i = 0; i < App.onInit.length; i++) { App.onInit[i](this); } } catch (e) { App.fail(e); alert(e); } }.bind(this); } else { top.location = self.location; }