App/res/main.js

944 lines
34 KiB
JavaScript
Executable File

function toggleVisibility(idImg, idDiv) {
var div = document.getElementById(idDiv);
var img = document.getElementById(idImg);
if (div.style.display === "none") {
div.style.display = "flex";
img.src = "res/dark/hide.png";
} else {
div.style.display = "none";
img.src = "res/dark/show.png";
}
}
class APIScanner
{
static api;
static settings;
static scanned;
constructor(parent) {
APIScanner.api = parent;
this.qr = null;
this.cameras = [];
}
Start(id, subroute, parser) {
APIScanner.settings = {
subroute: subroute,
parser: parser,
id: id
};
APIScanner.scanned = {primary: {}, secondary: {}};
for (let i in parser) {
if (i!=subroute) {
APIScanner.scanned.secondary[parser[i]] = null;
} }
// Find already parsed IDs
const subData = APIScanner.api.activeRoute.resourcesIndex[id].data.SUB[parser[subroute]];
for (let i in subData) {
APIScanner.scanned.primary[subData[i].ID] = true;
}
this.qr = new QrScanner(
document.getElementById("qrscanner_video"),
APIScanner.parse,
{returnDetailedScanResult: true, highlightScanRegion: true, highlightCodeOutline: true}
);
if (QrScanner.hasCamera()) {
let _this = this;
QrScanner.listCameras(true)
.then(result => _this.cameras = result)
.catch(error => alert(error || 'Camera detection failed.'));
}
this.cameraIndex = 0;
document.getElementById("qrscanner_text").innerHTML = "";
this.qr.start();
}
Stop() {
this.qr.stop();
this.qr.destroy();
this.qr = null;
}
SwitchCamera() {
if (this.qr!==null && QrScanner.hasCamera()) {
++this.cameraIndex;
if (this.cameraIndex>=this.cameras.length) {
this.cameraIndex = 0;
}
this.qr.setCamera(this.cameras[this.cameraIndex].id);
}
}
static parse(result) {
let scan = result.data.split("/");
if (scan.length==3) {
let route = scan[1];
let subid = scan[2];
// Is it a route we should parse?
if (typeof(APIScanner.settings.parser[route])!="undefined") {
const marker = APIScanner.settings.parser[route];
// Is it the primary route and if yes, has this ID been parsed before?
if (route==APIScanner.settings.subroute && typeof(APIScanner.scanned.primary[subid])=="undefined") {
APIScanner.scanned.primary[subid] = true;
let index = -1;
const options = APIScanner.api.activeRoute.options[marker];
for (let i in options) {
if (options[i].ID==subid) {
index = i;
break;
} }
// Has the ID been found?
if (index>=0) {
document.getElementById("qrscanner_text").innerHTML += options[index].NAME + "<br />";
APIScanner.api.Request("POST", APIScanner.scanned.secondary, APIScanner.settings.id, APIScanner.settings.subroute, subid);
}
} else if(route!=APIScanner.settings.subroute) { // Is it a secondary field?
APIScanner.scanned.secondary[marker] = subid;
} } } }
}
class APIResource // This holds ONE table record
{
constructor(route, group, data) {
this.route = route;
this.group = group;
this.ID = data.ID;
group.resourcesIndex[this.ID] = this;
route.resourcesIndex[this.ID] = this;
this.data = data;
this.show = false;
// TODO: This SHOULD be determined by the client through comparing of data with privileges and not be preprocessed by server. Should it?
this.localAdmin = data.admin;
}
Render(includingBox = false) {
var data = this.data;
data.SHOWBUTTON = this.show;
data.SHOWCONTENT = this.show;
data.SELECTBOX = "<input type=checkbox onChange='SelectChange("+this.ID+", "+(this.route.selected.includes(this.ID) ? "false);' checked" : "true);'")+" />";
var ret = "";
return (includingBox ? "<div id='LIST/" + this.ID + "' class='list_entry'>" : "")
+ this.route.tpl.Render(data, "LIST", (this.route.admin || this.localAdmin ? ":ADMIN" : ""))
+ (includingBox ? "</div>" : "");
}
}
class APIGroup
{
constructor(route, id, resources) {
this.route = route;
this.ID = id;
this.resources = []; // List of APIResources
this.resourcesIndex = {}; // resources indexed by ID
this.show = true;
this.route.groupsIndex[id] = this;
for (var i in resources) {
this.Add(resources[i]);
}
}
Add(resource) {
this.resources.push(new APIResource(this.route, this, resource));
}
Delete(id) {
var index = this.route.findIndexInEntries(id, this.resources);
delete this.resources[index];
this.resources.splice(index, 1);
}
Clear() {
for (var i in this.resources) {
delete this.resources[i];
}
this.resources = [];
this.resourcesIndex = {};
}
Render() {
var ret = "<article id='GROUP/" + this.ID + "'>" + this.route.tpl.Render({"GROUP": this.ID}, "GROUP_BEGIN", "");
for (var k in this.resources) {
ret+= this.resources[k].Render(true);
}
ret+= this.route.tpl.Render({"GROUP": this.ID}, "GROUP_END", "") + "</article>";
return ret;
}
}
class APIFilter // This holds ONE filter element
{
constructor(data, type) {
this.Set(data, type);
}
Get() {
return {and: this.and, field: this.field, op: this.op, value: this.value};
}
Set(data, type) {
this.and = typeof(data.and)!="undefined" ? data.and : "and";
this.field = typeof(data.field)!="undefined" ? data.field : "none";
this.op = typeof(data.op)!="undefined" ? data.op : "";
this.value = typeof(data.value)!="undefined" ? data.value : ("Multiple"==type ? [] : "");
}
}
class APIRoute // This holds a list of resources
{
constructor(parent, route, info) {
this.parent = parent;
this.route = route;
this.isPage = info.isPage;
this.mainFields = info.mainFields;
this.filter = [];
this.tplFile = info.template;
this.tpl = null;
this.image = typeof(info.image)!="undefined" ? info.image : null;
this.name = info.name;
this.admin = false;
this.options = [];
this.selected = [];
this.dropzones = {};
this.groups = []; // List of APIResourceGroups
this.groupsIndex = {};
this.resourcesIndex = {}; // Index of APIResources by ID linking to entries inside their respective group
}
Add() {
// Get JSON
var send = {};
for (var i in this.mainFields) {
if (this.mainFields[i].add) {
var e = document.getElementById("ADD/"+i);
if (e!=null) {
if (this.mainFields[i].type=="Multiple") {
// Multiple values can be selected
send[i] = Array.from(e.selectedOptions).map(el=>el.value);
} else if (this.mainFields[i].type=="Checkbox") {
send[i] = e.checked;
} else if (e.value=="__NULL__") {
// Null is selected
send[i] = null;
} else {
// Only one value can be selected/input
send[i] = e.value;
}
} else if (this.mainFields[i].type=="DateTime") {
var eDate = document.getElementById("ADD/"+i+"/DATE");
var eTime = document.getElementById("ADD/"+i+"/TIME");
if (null!=eDate && null!=eTime) {
send[i] = eDate.value + " " + eTime.value;
} } } }
this.parent.Request("POST", send);
}
Save(id) {
// Get JSON
var json = this.findEntry(id).data.MAIN;
var send = {};
var element = document.getElementById("LIST/"+id);
element.className = "list_entry";
for (var i in this.mainFields) {
if (this.mainFields[i].edit) {
var e = document.getElementById("LIST/"+id+"/"+i);
if (e!=null) {
if (this.mainFields[i].type=="Multiple") {
// Multiple values can be selected
send[i] = Array.from(e.selectedOptions).map(el=>el.value);
} else if (this.mainFields[i].type=="Checkbox") {
send[i] = e.checked;
} else if (e.value=="__NULL__") {
// Null is selected
send[i] = null;
} else {
// Only one value can be selected/input
send[i] = e.value;
}
} else if (this.mainFields[i].type=="DateTime") {
var eDate = document.getElementById("LIST/"+id+"/"+i+"/DATE");
var eTime = document.getElementById("LIST/"+id+"/"+i+"/TIME");
if (null!=eDate && null!=eTime) {
send[i] = eDate.value + " " + eTime.value;
} } } }
this.parent.Request("PATCH", send, id);
}
Edit(id) {
var _this = this;
var entry = this.findEntry(id);
var element = document.getElementById("LIST/"+id);
element.innerHTML = this.tpl.Render(entry.data, "LIST", ":EDIT");
element.className = "list_entry list_edit";
this.renderHelper(id);
}
Cancel(id) {
var entry = this.findEntry(id);
var element = document.getElementById("LIST/"+id);
element.innerHTML = this.tpl.Render(entry.data, "LIST", ":ADMIN");
element.className = "list_entry";
}
SubShowAdd(id, markers) {
var entry = this.findEntry(id);
let marker;
var data = {"ID": id};
if (Array.isArray(markers)) {
marker = markers[0];
for (let i=1; i<markers.length; ++i) {
data[markers[i]] = this.options[markers[i]];
}
} else {
marker = markers;
}
data[marker] = [];
for (var i in this.options[marker]) {
if (this.findIndexInEntries(this.options[marker][i].ID, entry.data.SUB[marker])==null) {
data[marker].push(this.options[marker][i]);
} }
var element = document.getElementById("LIST/"+id+"/SUB/"+marker+"/ADD");
element.innerHTML = this.tpl.Render(data, "ADD.SUB."+marker, "");
}
SubShowScan(id, subroute, parsers) {
document.getElementById("qrscanner").style.display = "flex";
this.parent.scanner.Start(id, subroute, parsers);
}
SubAdd(id, markers, route) {
let marker;
let json = {};
if (Array.isArray(markers)) {
marker = markers[0];
for (let i=1; i<markers.length; ++i) {
json[markers[i]] = document.getElementById("ADD/"+id+"/"+markers[i]).value;
if (json[markers[i]]=="__NULL__") {
json[markers[i]] = null;
}
}
} else {
marker = markers;
}
const subid = document.getElementById("ADD/"+id+"/"+marker).value;
this.parent.Request("POST", json, id, route, subid);
}
ToggleEntryView(id) {
var entry = this.findEntry(id);
entry.show = !entry.show;
var element = document.getElementById("LIST/"+id);
element.innerHTML = this.renderEntry(entry);
}
FilterAdd() {
var length = this.filter.push(new APIFilter({}, "none"));
this.parent.Request("GET");
}
FilterDelete(id) {
delete this.filter[id];
this.filter.splice(id, 1);
this.parent.Request("GET");
}
FilterChange(id) {
var data = {
and: document.getElementById("FILTER/"+id+"/AND").value,
field: document.getElementById("FILTER/"+id+"/FIELD").value
};
if ("none"!=data.field) {
var eValue = document.getElementById("FILTER/"+id+"/VALUE");
var eOp = document.getElementById("FILTER/"+id+"/OP");
if (null!=eOp) {
data.op = eOp.value;
}
if (null==eValue) {
if (this.mainFields[data.field].type=="DateTime") {
var eDate = document.getElementById("FILTER/"+id+"/VALUE/DATE");
var eTime = document.getElementById("FILTER/"+id+"/VALUE/TIME");
if (null!=eDate && null!=eTime) {
data.value = eDate.value + " " + eTime.value;
} }
} else {
if (this.mainFields[data.field].type=="Multiple") {
// Multiple values can be selected
data.value = Array.from(eValue.selectedOptions).map(el=>el.value);
} else if (this.mainFields[data.field].type=="Checkbox") {
data.value = eValue.checked;
} else if (eValue.value=="__NULL__") {
// Null is selected
data.value = null;
} else {
// Only one value can be selected/input
data.value = eValue.value;
} } }
if ("undefined"!=typeof(this.filter[id])) {
this.filter[id].Set(data, "none"!=data.field ? this.mainFields[data.field].type : "none");
}
this.parent.Request("GET");
}
SelectChange(id, state) {
var selectedIndex = this.selected.indexOf(id);
if (state && 0>selectedIndex) {
this.selected.push(id);
} else if (!state && -1<selectedIndex) {
this.selected.splice(selectedIndex, 1);
}
var html = this.renderEntry(this.findEntry(id));
document.getElementById("LIST/"+id).innerHTML = html;
if (state) {
document.getElementById("LIST/"+id).classList.add("list_selected");
} else {
document.getElementById("LIST/"+id).classList.remove("list_selected");
}
}
/* "private" functions */
parseContent(responseJson, method, id, sub) {
this.meta = responseJson.meta;
this.prints = responseJson.prints;
if (id!=null) {
// Find id and …
var element = document.getElementById("LIST/"+id);
if (id in this.resourcesIndex) {
if (method=="DELETE" && sub==null) {
// … delete it
var group = this.resourcesIndex[id].group;
group.Delete(id);
element.parentNode.removeChild(element);
} else {
// … replace it
this.resourcesIndex[id].data = responseJson.content;
element.innerHTML = this.renderEntry(this.resourcesIndex[id]);
} }
} else if (method=="POST") {
// Insert the newly created into entries
if (typeof(this.groupsIndex[responseJson.content.GROUP])=="undefined") {
this.groups.push(new APIGroup(this, responseJson.content.GROUP, [responseJson.content]));
} else {
this.groupsIndex[responseJson.content.GROUP].Add(responseJson.content);
}
var entryHtml = "<div id='LIST/" + responseJson.content.ID + "' class='list_entry'>" + this.renderEntry(this.resourcesIndex[responseJson.content.ID]) + "</div>";
var elList = document.getElementById("GROUP/" + responseJson.content.GROUP);
elList.insertAdjacentHTML("beforeend", entryHtml);
} else if (method=="GET") {
// Update everything
this.admin = responseJson.admin;
this.options = responseJson.options;
for (var k in this.filter) {
delete this.filter[k];
}
this.selected = [];
if ("undefined"!=typeof(responseJson.selected)) {
this.selected = responseJson.selected;
}
this.filter = [];
for (var l in responseJson.filter) {
this.filter.push(new APIFilter(responseJson.filter[l], "none"!=responseJson.filter[l].field ? this.mainFields[responseJson.filter[l].field].type : "none"));
}
for (var iG in this.groups) {
this.groups[iG].Clear();
delete this.groups[iG];
}
this.groups = [];
this.groupsIndex = {};
for (var groupID in responseJson.content) {
this.groups.push(new APIGroup(this, responseJson.content[groupID].ID, responseJson.content[groupID].ENTRIES));
}
if (this.tplFile!=null) {
this.reloadTemplate(this.tplFile);
} } }
findIndexInEntries(id, data) {
for (var i in data) {
if (data[i].ID==id) {
return i;
} }
return null;
}
findEntry(id) {
if (id in this.resourcesIndex) {
return this.resourcesIndex[id];
}
return null;
}
reloadTemplate() {
var _this = this;
var request = new XMLHttpRequest();
request.open("GET", this.tplFile, true);
request.setRequestHeader("Accept", "text/html");
request.onreadystatechange = function() {
if (request.readyState != 4 || request.status != 200) return;
_this.tpl = new TemplateEngine(request.responseText, _this.options);
_this.render();
};
request.send();
}
renderEntry(entry) {
var data = entry.data;
data.SHOWBUTTON = entry.show;
data.SHOWCONTENT = entry.show;
data.SELECTBOX = "<input type=checkbox onChange='SelectChange("+entry.ID+", "+(this.selected.includes(entry.ID) ? "false);' checked" : "true);'")+" />";
return this.tpl.Render(entry.data, "LIST", (this.admin || entry.localAdmin ? ":ADMIN" : ""));
}
getFilter() {
var filter = [];
for (var i in this.filter) {
filter.push(this.filter[i].Get());
}
return filter;
}
renderFilterEntry(id) {
var html = "";
var data = this.filter[id].Get();
// Render and/or combiner
if (id>0) {
html+= "<select id='FILTER/"+id+"/AND' onChange='FilterChange("+id+");'><option value='and'"+("and"==data.and ? " selected" : "")+">Und</option>"
+"<option value='or'"+("or"==data.and ? " selected" : "")+">Oder</option></select>";
} else {
html+= "<input type=hidden id='FILTER/"+id+"/AND' value='and' />";
}
// Render Field selector
html+= "<select id='FILTER/"+id+"/FIELD' onChange='FilterChange("+id+");'><option value='none'"+("none"==data.field ? " selected" : "")+">Ungefiltert</option>";
for (var i in this.mainFields) {
if (this.mainFields[i].filter) {
html+= "<option value='"+i+"'"+(i==data.field ? " selected" : "")+">"+this.mainFields[i].label+"</option>";
} }
html+= "</select>";
if ("undefined"!=typeof(this.mainFields[data.field])) {
// Render operator
// Render value input
switch (this.mainFields[data.field].type) {
case "Checkbox":
html+= "ist <input type=hidden id='FILTER/"+id+"/OP' value='=' />";
html+= "<input type='checkbox' id='FILTER/"+id+"/VALUE' "+(data.value ? "checked " : "")+"onChange='FilterChange("+id+");' />";
break;
case "Multiple":
html+= "<select id='FILTER/"+id+"/OP' onChange='FilterChange("+id+");'>"
+"<option value='like'"+("like"==data.op ? " selected" : "")+">enthält</option>"
+"<option value='not like'"+("not like"==data.op ? " selected" : "")+">enthält nicht</option></select>"
+"<select id='FILTER/"+id+"/VALUE' onChange='FilterChange("+id+");' multiple>";
if ("undefined"!=typeof(this.options[data.field])) {
for (var j in this.options[data.field]) {
html+= "<option value='"+this.options[data.field][j].ID+"'"+(data.value.includes(this.options[data.field][j].ID) ? " selected" : "")+">"
+this.options[data.field][j].NAME+"</option>";
} }
html+= "</select>";
break;
case "Select":
html+= "<select id='FILTER/"+id+"/OP' onChange='FilterChange("+id+");'>"
+"<option value='='"+("="==data.op ? " selected" : "")+">=</option>"
+"<option value='<>'"+("<>"==data.op ? " selected" : "")+">!=</option></select>"
+"<select id='FILTER/"+id+"/VALUE' onChange='FilterChange("+id+");'>";
if ("undefined"!=typeof(this.options[data.field])) {
for (var k in this.options[data.field]) {
html+= "<option value='"+this.options[data.field][k].ID+"'"+(data.value==this.options[data.field][k].ID ? " selected" : "")+">"
+this.options[data.field][k].NAME+"</option>";
} }
html+= "</select>";
break;
case "String":
html+= "<select id='FILTER/"+id+"/OP' onChange='FilterChange("+id+");'><option value='like'"+("like"==data.op ? " selected" : "")+">enthält</option>"
+"<option value='not like'"+("not like"==data.op ? " selected" : "")+">enthält nicht</option></select>"
+"<input id='FILTER/"+id+"/VALUE' onChange='FilterChange("+id+");' value='"+data.value+"' style='width: 4em;'/>";
break;
case "Integer":
html+= "<select id='FILTER/"+id+"/OP' onChange='FilterChange("+id+");'><option value='='"+("="==data.op ? " selected" : "")+">=</option>"
+"<option value='<>'"+("<>"==data.op ? " selected" : "")+">!=</option>"
+"<option value='<='"+("<="==data.op ? " selected" : "")+"><=</option>"
+"<option value='>='"+(">="==data.op ? " selected" : "")+">>=</option></select>"
+"<input type='number' id='FILTER/"+id+"/VALUE' onChange='FilterChange("+id+");' value='"+data.value+"' />";
break;
case "DateTime":
html+= "<select id='FILTER/"+id+"/OP' onChange='FilterChange("+id+");'>"
+"<option value='<='"+("<="==data.op ? " selected" : "")+"><=</option>"
+"<option value='>='"+(">="==data.op ? " selected" : "")+">>=</option></select>";
var datetimesplit = data.value.split(" ");
html+= "<input type='date' id='FILTER/"+id+"/VALUE/DATE' onChange='FilterChange("+id+");' value='"+datetimesplit[0]+"' />";
html+= "<input type='time' id='FILTER/"+id+"/VALUE/TIME' onChange='FilterChange("+id+");' value='"+datetimesplit[1]+"' />";
break;
} }
// Render deletor
html+= "<a href='javascript:FilterDelete("+id+");'><img src='/res/dark/minus.png' /></a>";
return html;
}
renderFilter() {
var ret = "<ul><li>Filter:</li>";
for (var i in this.filter) {
ret+= "<li id='FILTER/"+i+"'>"+this.renderFilterEntry(i)+"</li>";
}
ret+= "<a href='javascript:FilterAdd();'><li><img src='/res/dark/add.png' /></li></a>";
if (this.filter.length>1) {
ret+= "<a href='javascript:FilterDelete('ALL');'><li><img src='/res/dark/delete.png' /></li></a>";
}
return ret+"</ul>";
}
render() {
var metaHtml = "<article id='meta'>" + this.tpl.Render(this.meta, "META", "") + "</article>";
var entriesHtml = "<article id='LIST/HEADER'><h1>" + this.name + "<span id='loadImage'></span></h1><ul class='inline'>";
for (var i in this.prints) {
entriesHtml+= "<a href='javascript:Print(\"" + this.prints[i].ID + "\", \"" + this.prints[i].Bezeichnung + "\");'><li>"
+ this.prints[i].Bezeichnung + "<img src='/res/dark/print.png' /></li></a>";
}
entriesHtml+= "</ul>";
entriesHtml+= "<div id='FILTER' class='list_entry_title'>" + this.renderFilter() + "</div></article>";
for (var j in this.groups) {
entriesHtml+= this.groups[j].Render();
}
var addHtml = (this.admin ? "<article id='LIST/ADD'>" + this.tpl.Render(this.options, "ADD.MAIN", "") + "</article>" : "");
document.getElementById("content").innerHTML = metaHtml + addHtml + entriesHtml;
//this.renderHelper();
}
renderHelper(id = null) {
var _this = this;
if (null!==id && !!document.getElementById("dropzone_"+id)) {
this.dropzones[id] = new Dropzone("form#dropzone_"+id, {
url: document.getElementById("dropzone_"+id).action,
//url: this.route + "/" + id + "/Bilder",
createImageThumbnails: false,
headers: {
"Authorization": "Bearer " + _this.parent.jwt,
"Accept": "application/json"
},
dictDefaultMessage: "Datei Drop "
}).on("sending", function(file, xhr, formData) {
_this.Cancel(id);
_this.parent.showMessages(["Datei wird hochgeladen! Dies kann je nach Größe eine Weile dauern."]);
formData.append("secToken", _this.parent.secToken);
}).on("complete", function(file) {
if (file.xhr.status==201) {
_this.parent.showMessages(["Datei erfolgreich hochgeladen!"]);
}
var json = _this.parent.parseResponse(file.xhr.responseText);
_this.parent.parseJson(json, file.xhr.status);
_this.parseContent(json, "GET", id);
});
} }
}
class APIManager
{
constructor() {
this.jwt = this.getCookie("jwt");
this.emulate = null;
this.secToken = this.getCookie("secToken");
this.loggedIn = null;
this.root = "https://app.fw-innenstadt.de";
this.scanner = new APIScanner(this);
this.routes = {"/index.php/": new APIRoute(this, "/index.php/", {
link: "/index.php/", name: "Start", image: "/wappen48.png",
template: "/res/main.html", isPage: true, marker: "START",
mainFields: []})};
this.path = this.getCookie("path");
if (this.path=="") {
this.path = "/index.php/";
this.activeRoute = this.routes[this.path];
}
this.Request("GET");
}
Request(method, json = {}, id = null, sub = null, subid = null) {
var _this = this;
var request = new XMLHttpRequest();
var path = this.path + (id!=null ? "/" + id + (sub!=null ? "/" + sub + (subid!=null ? "/"+subid : "") : "") : "");
var accept = "application/json";
var loadElement = document.getElementById("loadImage");
if (null!=loadElement) {
loadElement.innerHTML = "<img src='/res/loading.gif' />";
}
json.secToken = typeof(this.secToken)!="undefined" ? this.secToken : "";
if ("undefined"!=typeof(this.activeRoute)) {
var hasParam = false;
// Append filter if exists
if (this.activeRoute.filter.length>0) {
path += "?filter="+encodeURI(JSON.stringify(this.activeRoute.getFilter()));
hasParam = true;
}
// Append selection
if (this.activeRoute.selected.length>0) {
path += (hasParam ? "&" : "?")+"selected="+encodeURI(JSON.stringify(this.activeRoute.selected));
hasParam = true;
}
// Append print request
if (typeof(json.Print)!="undefined") {
path += (hasParam ? "&" : "?")+"print="+json.Print;
accept = "text/html";
hasParam = true;
}
// Append user emulation
if (this.emulate!=null) {
path += (hasParam ? "&" : "?")+"emulate="+this.emulate;
hasParam = true;
}
}
request.open(method, path, true);
if (this.jwt!="") {
request.setRequestHeader("Authorization", "Bearer " + this.jwt);
}
if (method!="GET") {
request.setRequestHeader("Content-Type", "application/json");
}
request.setRequestHeader("Accept", accept);
request.onreadystatechange = function() {
if (request.readyState != 4) return;
var loadElement = document.getElementById("loadImage");
if (null!=loadElement) {
loadElement.innerHTML = "";
}
if ("text/html"==accept) {
if (200==request.status) {
var windowUrl = 'about:blank';
var uniqueName = new Date();
var windowName = 'Print' + uniqueName.getTime();
var printWindow = window.open(windowUrl, windowName, 'left=50000,top=50000,width=0,height=0');
printWindow.document.write("<html><body style='margin: 0; font-family: Calibri, Carlito, Arial Narrow, Liberation Sans Narrow, Arial, Liberation Sans; font-size: 12px;'>");
printWindow.document.write(request.responseText);
printWindow.document.write("</body></html>");
printWindow.document.close();
printWindow.focus();
} else {
_this.showMessages(["Es konnte kein Ausdruck abgerufen werden!"]);
}
} else if ("application/json"==accept) {
var responseJson = _this.parseResponse(request.responseText);
_this.parseJson(responseJson, request.status);
if (request.status>=200 && request.status<300) {
if (typeof(_this.activeRoute)!="undefined") {
_this.activeRoute.parseContent(responseJson, method, id, sub);
} } } };
request.send(JSON.stringify(json));
}
Open(path) {
if (typeof(this.routes[path])!="undefined") {
this.path = path;
this.setCookie("path", path);
this.activeRoute = this.routes[path];
this.Request("GET");
} else if ("/index.php/Logout"==path) {
this.path = path;
this.setCookie("path", "/index.php/");
this.activeRoute = this.routes["/index.php/"];
this.Request("GET");
}
}
Login() {
var button = document.getElementById("loginButton");
button.disabled = true;
button.className = "login disabled";
var json = {
"login": document.getElementById("loginUser").value,
"password": document.getElementById("loginPassword").value
};
this.path = "/index.php/";
this.activeRoute = this.routes[this.path];
this.setCookie("path", this.path);
this.Request("POST", json);
}
/* "Private" functions */
registerRoutes(routes) {
for (var i in routes) {
if (typeof(this.routes[routes[i].link])=="undefined") {
this.routes[routes[i].link] = new APIRoute(this, routes[i].link, routes[i]);
} }
if (typeof(this.activeRoute)=="undefined") {
this.activeRoute = this.routes[this.path];
}
this.renderNavigation();
}
renderNavigation() {
let nav = document.getElementById("navigation");
let selectnav = document.getElementById("navigation_select");
nav.innerHTML = "";
selectnav.innerHTML = "";
for (let i in this.routes) {
if (this.routes[i].isPage) {
let selected = this.routes[i]==this.activeRoute ? " selected" : "";
selectnav.innerHTML+= "<option value='" + this.routes[i].route + "'" + selected + ">☰ " + this.routes[i].name + "</option>";
let cssClass = this.routes[i]==this.activeRoute ? " class='current'" : "";
if (this.routes[i].image!=null) {
nav.innerHTML+= "<a href=\"javascript:Open('" + this.routes[i].route + "');\"" + cssClass + "><li><img src='"
+ this.routes[i].image + "' title='" + this.routes[i].name + "' /></li></a>";
} else {
nav.innerHTML+= "<a href=\"javascript:Open('" + this.routes[i].route + "');\"" + cssClass + "><li>" + this.routes[i].name + "</li></a>";
} } }
nav.innerHTML+= "<a href='javascript:Logout();'><li><img src='/res/dark/logout.png' title='Ausloggen' /></li></a>";
selectnav.innerHTML+= "<option value='_LOGOUT_'>Logout</option>";
}
parseResponse(responseText) {
var json = {};
try {
json = JSON.parse(responseText);
} catch (SyntaxError) {
this.showMessages(["Parsen der Antwort gescheitert. Es folgt die Antwort!", responseText]);
}
return json;
}
parseJson(json, status) {
if (typeof(json.status.newjwt)!="undefined" && json.status.newjwt!=null) {
this.setCookie("jwt", json.status.newjwt);
this.jwt = json.status.newjwt;
}
if (typeof(json.status.secToken)!="undefined" && json.status.secToken!=null) {
this.setCookie("secToken", json.status.secToken);
this.secToken = json.status.secToken;
}
if (typeof(json.status.loggedIn)!="undefined" && json.status.loggedIn) {
if (json.status.loggedIn!=this.loggedIn) {
this.changeLoginScreen(true);
}
if (status>=300) {
this.showMessages(["Die Anfrage wurde mit Code " + status + " abgelehnt.<br />(" + this.translateHttpStatus(status) + ")"]);
}
} else {
this.changeLoginScreen(false);
}
if (typeof(json.messages)!="undefined") {
this.showMessages(json.messages);
}
if (typeof(json.pages)!="undefined") {
this.registerRoutes(json.pages);
}
}
translateHttpStatus(status) {
switch (status) {
case 200: return "OK";
case 201: return "Erstellt";
case 400: return "Fehlerhafte Anfrage";
case 401: return "Anmeldung erforderlich";
case 403: return "Keine Berechtigung";
case 404: return "Nicht gefunden";
case 500: return "Fehler auf Server";
default: return "" + status + " = Unbekannt";
}
}
changeLoginScreen(newStatus) {
var s = document.getElementById('header').style;
if (this.loggedIn==null) {
if (!newStatus) {
s.display = "block";
s.opacity = 1;
}
} else if (newStatus) {
(function fadeOut(){(s.opacity-=0.1)<0?s.display="none":setTimeout(fadeOut,40)})();
} else {
var button = document.getElementById("loginButton");
button.disabled = false;
button.className = "login";
s.display = "block";
(function fadeIn(){(s.opacity-=-0.1)>1?s.opacity=1:setTimeout(fadeIn,40)})();
}
this.loggedIn = newStatus;
document.getElementById("navigation").className = (this.loggedIn ? "" : "hidden");
document.getElementById("content").className = (this.loggedIn ? "" : "hidden");
}
showMessages(messages) {
for (var i in messages) {
Toastify({
text: messages[i],
duration: 3000,
close: true,
gravity: "top", // `top` or `bottom`
position: "right", // `left`, `center` or `right`
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
stopOnFocus: true, // Prevents dismissing of toast on hover
}).showToast();
} }
setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+ d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/;SameSite=Strict;Secure";
}
getCookie(cname){
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
} }
return "";
}
}
var api = new APIManager();
function ToggleEntryView(id) {api.activeRoute.ToggleEntryView(id);}
function Open(path) {api.Open(path);}
function OpenSelected() {
let s = document.getElementById("navigation_select").value;
if (s=="_LOGOUT_") {Logout();} else {api.Open(s);}
}
function Logout() {api.Open("/index.php/Logout");}
function FilterAdd() {api.activeRoute.FilterAdd();}
function FilterDelete(id) {api.activeRoute.FilterDelete(id);}
function FilterChange(id) {api.activeRoute.FilterChange(id);}
function Add() {api.activeRoute.Add();}
function Delete(id, data = {}) {api.Request("DELETE", data, id);}
function Edit(id) {api.activeRoute.Edit(id);}
function Cancel(id) {api.activeRoute.Cancel(id);}
function Save(id) {api.activeRoute.Save(id);}
function SubShowAdd(id, markers) {api.activeRoute.SubShowAdd(id, markers);}
function SubShowScan(id, subroute, parsers) {api.activeRoute.SubShowScan(id, subroute, parsers);}
function HideScanner() {api.scanner.Stop(); document.getElementById("qrscanner").style.display = "none";}
function SwitchCamera() {api.scanner.SwitchCamera();}
function SubAdd(id, markers, subroute) {api.activeRoute.SubAdd(id, markers, subroute);}
function SubDelete(id, subroute, subid = null) {api.Request("DELETE", {}, id, subroute, subid);}
function Request(method, data, id = null, subroute = null, subid = null) {api.Request(method, data, id, subroute, subid);}
function Print(printID, printName) {api.Request("GET", {Print: printID, PrintName: printName});}
function SelectChange(id, state) {api.activeRoute.SelectChange(id, state);}