Initial commit

This commit is contained in:
l_samenv
2020-12-04 09:05:06 +01:00
parent 172042e731
commit 9e1d3b4e07
54 changed files with 47695 additions and 0 deletions

View File

@ -0,0 +1,385 @@
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % COMMUNICATION
var timeoutID; // We need this ID to reset the timer every 30 seconds
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Server Sent Event
function buildUpdateConnection() {
// Establishes server-sent-event-connection, which is used for all sorts of
// updates and exists as long as the client is running.
// Executed at programstart (see also SEAWebClientMain.js).
var path = "http://" + hostPort + "/update";
if (debugCommunication) {
console.log("%cto server (SSE): " + path,
"color:white;background:lightblue");
}
try {
var src = new EventSource(path);
} catch (e) {
console.log(e)
alertify.prompt(
"NETWORK ERROR",
"Failed to establish connection to data-server at the given address!"
+ "Try to enter HOST and PORT of the data-server manually!",
hostPort, function(evt, value) {
hostPort = value;
buildUpdateConnection();
}, function() {
})
}
src.onmessage = function(e) {
var message = JSON.parse(e.data);
if (message) {
handleUpdateMessage(src, message);
}
};
src.onerror = function(e) {
console.log(e);
console.log('EVTSRC error')
alertify
.prompt(
"NETWORK ERROR",
"Failed to establish connection to data-server at the given address!"
+ "Try to enter HOST and PORT of the data-server manually!",
hostPort, function(evt, value) {
hostPort = value;
buildUpdateConnection();
}, function() {
})
src.close();
};
}
function handleUpdateMessage(src, message) {
// Handles incoming SSE-messages depending on type of message.
if (debugCommunication > 1) {
console.log("%cfrom server (SSE): " + message.type,
"color:white;background:lightgray", message);
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// <---------------------------------------------------------------------------------BUG!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
resetTimer(src);
switch (message.type) {
// id-message: Confirms establishment of SSE-connection and determines
// specific ID of the client
case "id":
for (var i = 0; i < swiper.length; i++) {
swiper[i].removeAllSlides();
}
clientID = message.id;
if ("device" in message) {
if (message.device == "_inst_select") {
clientTitle = "select instrument";
console.log('IDselect')
pushInitCommand("getblock?path=_inst_select&", "instrument selection");
menuMode = true;
sizeChange();
} else {
clientTitle = message.instrument + " " + message.device;
console.log('loadBlocks')
loadFirstBlocks();
}
document.title = "SEA "+clientTitle;
} else {
document.title = "SEA "+clientTitle + " " + message.title;
}
var header = document.getElementById("header");
header.setAttribute("style", "width: auto;");
header.innerHTML = clientTitle;
console.log('ID', initCommands);
nextInitCommand();
break;
// console-update-message: Confirms a command.
case "command":
var histories = document.getElementsByClassName("history");
for (var i = 0; i < histories.length; i++) {
var line = document.createElement('div');
line.innerHTML = htmlEscape(message.line);
line.style.color = "cornflowerblue";
if (message.origin == "self") {
line.style.fontWeight = "bold";
}
histories[i].appendChild(document.createElement('br'));
histories[i].appendChild(line);
histories[i].scrollTop = histories[i].scrollHeight;
}
var cmd = htmlEscape(message.line);
if (commandHistory.indexOf(cmd) === -1) {
commandHistory.unshift(cmd);
}
break;
// console-update-message: Confirms execution of command.
case "reply":
var histories = document.getElementsByClassName("history");
for (var i = 0; i < histories.length; i++) {
var line = document.createElement('div');
line.innerHTML = htmlEscape(message.line);
line.style.color = "black";
if (message.origin == "self") {
line.style.fontWeight = "bold";
} else if (message.origin == "async") {
line.style.color = "green";
if (!showAsync) continue;
}
histories[i].appendChild(line);
histories[i].scrollTop = histories[i].scrollHeight;
}
break;
// graph-message: Evokes redraw of graphics.
case "graph":
console.log("graph");
createCharts2(message.graph);
break;
// redraw.message: Communicates changes requiring reset of a whole group
case "redraw": // SHOULD BE TESTED
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// SPECIAL CASE: is message.title = "main possible? It should evoke
// redraw of client
// special meaning: s < 0: replace slides in all swiper instances
reqJSON(-1, "http://" + hostPort + "/getblock?path=" + message.path
+ "&id=" + clientID, successHandler, errorHandler);
break;
// graph-update-message:
case "graph-update":
//if (getUpdatesGraphics) {
//timeServer = message.time;
console.log("graph-update");
updateCharts2(message.graph);
//}
break;
// update-message: Communicates change of values.
case "update":
if (debugCommunication > 1) {
console.log(message);
}
updateValues(message, src);
break;
}
}
function htmlEscape(str) {
if (!str) return "";
return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g,
'&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function resetTimer(src) {
// Executed every time a heartbeat-message is obtained.
// If no heartbeat-messages are obtained for a certain amount of time,
// an error-message is thrown.
clearTimeout(timeoutID);
timeoutID = setTimeout(function(src) {
console.log("timeout");
alertify.error("connection lost");
if (src) {
src.close();
}
}, 60000);
}
function updateValues(message, src) {
// Handles changes of parameter-values
for (var i = 0; i < message.updates.length; i++) {
var component = message.updates[i];
var value = component.value;
var matches = document.getElementsByName(component.name);
for (var j = 0; j < matches.length; j++) {
var type = matches[j].getAttribute("__ctype__");
if (type == "rdonly") {
var text = htmlEscape(value);
if (text) {
matches[j].innerHTML = text;
}
} else if (type == "input") {
var row = matches[j].parentNode.parentNode.parentNode;
row.style.backgroundColor = "white";
var oldValue = matches[j].getAttribute("oldValue")
|| matches[j].value;
if (value != matches[j].value && value != oldValue) {
if (matches[j] == document.activeElement
|| oldValue != matches[j].value) {
row.style.backgroundColor = "orange";
} else {
matches[j].value = value;
}
}
matches[j].setAttribute("actualValue", value);
resizeTextfield(matches[j]);
} else if (type == "checkbox") {
var row = matches[j].parentNode.parentNode;
row.style.backgroundColor = "white";
matches[j].checked = value == 1 && value == "1";
} else if (type == "enum") {
matches[j].style.display = "block";
var row = matches[j].parentNode.parentNode;
row.style.backgroundColor = "white";
matches[j].value = value;
}
}
}
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// XMLHttpRequest
function reqJSON(s, url, successHandler, errorHandler) {
var xhr = typeof XMLHttpRequest != 'undefined' ? new XMLHttpRequest()
: new ActiveXObject('Microsoft.XMLHTTP');
if (debugCommunication) {
console.log("%cto server (reqJSON): " + url,
"color:white;background:lightgreen");
}
xhr.open('get', url, true);
xhr.onreadystatechange = function() {
// console.log(xhr)
var status;
var data;
// https://xhr.spec.whatwg.org/#dom-xmlhttprequest-readystate
if (xhr.readyState == 4) { // `DONE`
status = xhr.status;
if (status == 200) {
data = JSON.parse(xhr.responseText);
successHandler && successHandler(s, data);
} else {
errorHandler && errorHandler(status);
}
}
};
xhr.send();
}
function successHandler(s, message) {
// Handles incoming XMLHttp-messages depending on type of message.
// s: slide number or -1 for replacing slide in all slider instances
if (debugCommunication) {
console.log("%cfrom server (reqJSON): " + message.type,
"color:white;background:dimgray", message);
}
switch (message.type) {
// Response to a "getblock"-server-request.
case "draw":
if (debugCommunication) {
console.log(message);
}
if (message.path == "main") {
// Happens only initially or at device change.
for (var sLocal = 0; sLocal < MAXBLOCK; sLocal++) {
insertSlide(sLocal, message.title, "main", createContent(
sLocal, message));
}
} else {
if (s < 0) { // redraw: check for slides in all swiper instances
for (var isw = 0; isw < MAXBLOCK; isw ++) {
var slide = findSlide(isw, message.path);
if (slide) {
console.log("redraw", isw);
replaceSlideContent(slide, message.title,
createContent(isw, message));
}
}
} else {
insertSlide(s, message.title, message.path, createContent(s,
message));
}
}
nextInitCommand();
// Request for updates.
if (getUpdates) {
reqJSON(s, "http://" + hostPort + "/updateblock?path="
+ message.path + "&id=" + clientID, successHandler,
errorHandler);
}
break;
// Response to a "update-block"-server-request.
case "accept-block":
break;
// Response to a "console"-server-request.
case "accept-console":
// draw console, only on the first and the last swiper
insertSlide(0, "console", "console",
createContentConsole(sLocal));
insertSlide(3, "console", "console",
createContentConsole(sLocal));
nextInitCommand();
// send empty command in order to trigger getting history
reqJSON(0, "http://" + hostPort + "/sendcommand?command=&id=" + clientID, successHandler,
errorHandler);
break;
// Response to a "gettime"-server-request.
case "time":
timeRange = message.time;
/*createGraphics();
// By default mostleft swiper-instance shows graphics.
swiper[0].slideTo(0);
// Update time-selection. (see also SEAWebClientGraphics.js)
var select = document.getElementsByClassName("select-time")[0];
begin = timeRange[0] - timeRange[1];
select.value = begin;
// Server-request for variable-list.*/
reqJSON(0, "http://" + hostPort + "/getvars?time=" + timeRange + "&id="
+ clientID, successHandler, errorHandler);
break;
// Response to a "getvars"-server-request.
case "var_list":
//blocks = message.blocks;
/*var varlist = [];
for (var i = 0; i < blocks.length; i++) {
for (var j = 0; j < blocks[i].curves.length; j++) {
varlist.push(blocks[i].curves[j].name);
}
}
// Update graphics
if (varlist.length > 0) {
reqJSON(0, "http://" + hostPort + "/graph?time=" + timeRange
+ "&variables=" + varlist + "&id=" + clientID,
successHandler, errorHandler);
} else {
nextInitCommand();
}*/
graphs.receivedVars(message.blocks);
nextInitCommand();
break;
// Response to a "graph"-server-request.
case "graph-draw":
// obsolete?
if (debugCommunication) {
console.log("graph-draw", message);
}
createCharts2(message.graph);
nextInitCommand();
// Request for updates.
reqJSON(s, "http://" + hostPort + "/updategraph?id=" + clientID,
successHandler, errorHandler);
break;
// Response to a "updategraph"-server-request.
case "accept-graph":
break;
case "error":
console.log("%cError-Message received!", "color:white;background:red");
console.log(message);
break;
default:
break;
}
}
function errorHandler(status) {
if (debugCommunication) {
console.log("error", status);
}
}

View File

@ -0,0 +1,67 @@
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % CONSOLE
var commandHistory = []; // Stores commands executed by console.
var histIndex = -1; // Selected element of 'commandHistory'. (-1 = "")
function createContentConsole(s) {
// Creates input-textfield and texarea showing console-history.
var commandline = document.createElement('input');
commandline.setAttribute("type", "text");
commandline.setAttribute("class", "row commandline");
commandline.onkeydown = function (e) {
//console.log(histIndex);
if (e.which === 38 || e.key == "ArrowUp") {
if (histIndex + 1 < commandHistory.length) {
histIndex++;
this.value = commandHistory[histIndex];
}
var input = this;
window.setTimeout(
function () {
input.setSelectionRange(input.value.length,
input.value.length);
}, 0, input);
}
if (e.which === 40 || e.key == "ArrowDown") {
if (histIndex >= 0) {
histIndex--;
if (histIndex > -1) {
this.value = commandHistory[histIndex];
} else {
this.value = "";
}
}
}
};
commandline.setAttribute("autocomplete", "on");
var wrapper = document.createElement('form');
wrapper.setAttribute("class", "commandline-wrapper");
wrapper.onsubmit = function (e) {
e.preventDefault();
histIndex = -1;
// Request for command.
reqJSON(s, "http://" + hostPort + "/sendcommand?command="
+ commandline.value + "&id=" + clientID, successHandler,
errorHandler);
commandline.value = "";
};
wrapper.setAttribute("method", 'GET');
wrapper.appendChild(commandline);
var history = document.createElement('div');
history.setAttribute("class", "history");
var content = document.createElement('div');
content.setAttribute("class", "content-console");
content.appendChild(wrapper);
content.appendChild(history);
return content;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % GRAPHICS
/*
var swiperGraphics = []; // This array contains graphics-swiper-Instances.
var begin = -10; // Time to look back (in seconds).
var timeServer; // Every time a graph-update-mesage is received the
// server-timestamp is stored.
var timeRange; // Range of time to regard (unix-time in seconds).
var blocks; // array of graphic-blocks sorted by units. (as from var_list, but values added)
var MARGIN_TOP = 10;
var MARGIN_RIGHT = 10;
var MARGIN_BOTTOM = 20;
var MARGIN_LEFT = 60;
var width;
var height;
var margin;
var scaleX;
zoomSelection = false;
function createCharts(graph) {
createCharts2(graph);
return
}
function updateCharts(graph){
updateCharts2(graph)
}

View File

@ -0,0 +1,565 @@
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % GROUP
var writePermissionTimeout; // Sets writePermission to 'false, restarts by
// user-interaction.
var prompt = false // True while a prompt is opened.
function getGroup(s, name) {
var found = false;
if (name == "") {
swiper[s].slideTo(defaultSlidePos(s));
return;
}
for (var i = 0; i < swiper[s].slides.length; i++) {
var slideType = swiper[s].slides[i].getAttribute("slide-type");
if (slideType == name) {
found = true;
swiper[s].slideTo(i);
}
}
if (!found && name != "console" && name != "graphics") {
// Server-request for group.
reqJSON(s, "http://" + hostPort + "/getblock?path=" + name
+ "&id=" + clientID, successHandler, errorHandler);
}
}
function sendCommand(s, command) {
reqJSON(s, "http://" + hostPort + "/sendcommand?command=" + command
+ "&id=" + clientID, successHandler, errorHandler);
}
function createContent(s, message) {
// Depending on the message received from the server the content of the
// group
// is created dynamically. Handles draw-message.
var content = document.createElement('div');
content.setAttribute("class", "content");
// Process components of the message
for (var i = 0; i < message.components.length; i++) {
var component = message.components[i];
var name = component.name;
var type = component.type;
var title = name;
if ("title" in component)
title = component.title;
var info = component.info;
switch (type) {
case "group":
content.appendChild(createLink(s, name, title));
break;
case "rdonly":
if ("link" in component) {
content.appendChild(createReadOnlyInstrLink(s, name, title,
component.link, info));
} else {
content.appendChild(createReadOnly(s, name, title, info));
}
break;
case "rdlink":
content.appendChild(createReadOnlyGroupLink(s, name, title, info));
break;
case "input":
content.appendChild(createInput(s, name, title,
info));
break;
case "checkbox":
content.appendChild(createCheckbox(s, name, title,
info));
break;
case "enum":
content.appendChild(createSelection(s, name, title,
component.enum_names, info));
break;
case "pushbutton":
console.log(component);
console.log(component.info);
content.appendChild(createPushButton(s, name, title,
info));
break;
default:
break;
}
}
return content;
}
function gotoGroups(slideNames) {
slideNames = slideNames.split("%20");
var l = Math.min(MAXBLOCK,slideNames.length);
document.title = "SEA "+ clientTitle + " " + slideNames.join(" ");
for (var s=0; s<l; s++) {
getGroup(s, slideNames[s]);
}
}
function createLink(s, name, title) {
// Creates row-element containing link.
var row = document.createElement('row');
row.setAttribute("id", name);
row.setAttribute("name", title);
row.setAttribute("class", "interactive row link");
row.setAttribute("tabindex", 0);
row.onclick = function () {
var slideNames = getSlideNames();
slideNames[s] = name;
document.title = "SEA "+ clientTitle + " " + slideNames.join(" ");
history.pushState({func: "gotoGroups", funarg: slideNames.join("%20")}, document.title, "#" + slideNames.join("%20"));
getGroup(s, name);
}
if (title === "console" || title === "device config") {
row.setAttribute("class", "interactive row link link-static");
row.innerHTML = "console";
}
row.innerHTML = title;
return row;
}
function createReadOnly(s, name, title, info) {
// Creates row-element containing read-only-item.
var left = createTitle(title, info);
var right = document.createElement('span');
right.setAttribute("class", "col-right");
right.setAttribute("name", name);
right.setAttribute("__ctype__", "rdonly");
return appendToContent(info, left, right);
}
function createReadOnlyGroupLink(s, name, title, info) {
// Creates row-element containing link AND read-only-item.
// for secop
var left = createTitle(title, info);
left.setAttribute("id", name);
left.setAttribute("name", title);
left.setAttribute("class", "interactive link");
left.onclick = function () {
getGroup(s, title);
}
var right = document.createElement('span');
right.setAttribute("class", "col-right");
right.setAttribute("name", name);
right.setAttribute("__ctype__", "rdonly");
return appendToContent(info, left, right);
}
function createReadOnlyInstrLink(s, name, title, link, info) {
// Creates row-element containing link AND read-only-item.
// var left = createTitle(title, info);
var left = document.createElement('a');
left.setAttribute("class", "col-left");
left.innerHTML = title;
left.setAttribute("id", name);
left.setAttribute("name", title);
left.setAttribute("class", "interactive link");
var right = document.createElement('span');
right.setAttribute("class", "col-right");
right.setAttribute("name", name);
right.setAttribute("__ctype__", "rdonly");
row = appendToContent(info, left, right);
row.onclick = function () {
this.style.backgroundColor = "orangered";
left.click();
}
if (link.charAt(0) == ':') {
left.href = "http://" + location.hostname + link + "/";
} else {
left.href = link;
}
row.setAttribute("class", "row clickable");
return row;
}
function createPushButton(s, name, title, info) {
// Creates row-element containing a push button
var left = createTitle(title, info);
console.log(info);
left.setAttribute("id", name);
left.setAttribute("name", title);
var right = document.createElement('span');
right.setAttribute("class", "col-right clickable push-button");
right.setAttribute("name", name);
right.setAttribute("__ctype__", "rdonly");
row = appendToContent(info, left, right);
right.onclick = function () {
if (writePermission) {
var row = left.parentNode;
right.style.backgroundColor = "orangered";
// Request for command
sendCommand(s, name);
} else {
prompt = true;
alertify.confirm("", "You are connected with <b>" + clientTitle
+ "</b>. <br>"
+ "Are you sure you want to modify things here?",
function () {
// User decided to proceed.
writePermission = true;
writePermissionTimeout = setTimeout(function () {
writePermission = false;
}, 3600000);
var row = left.parentNode;
row.style.backgroundColor = "orangered";
// Request for command
sendCommand(s, name);
prompt = false;
}, function () {
// User decided to cancel
prompt = false;
});
}
}
row.setAttribute("class", "row");
return row;
}
function createInput(s, name, title, info) {
// Creates row-element containing input-item.
if (info) {
var infoBox = createInfo(title, info);
}
var left = createTitle(title, info);
var input = document.createElement('input');
input.setAttribute("type", "text");
input.setAttribute("class", "input-text");
input.setAttribute("name", name);
input.setAttribute("__ctype__", "input");
input.style.width = "100px";
input.onkeydown = function (e) {
if (e.which === 27 || e.key == "Escape") {
// User decided to cancel
input.value = input.getAttribute("oldValue");
resizeTextfield(input);
var row = left.parentNode;
row.style.backgroundColor = "white";
}
}
input.onfocus = function () {
input.setAttribute("oldValue", input.value);
if (isTouchDevice())
setTimeout(function () {
posTextfield(s, left);
}, 1);
}
input.onblur = function () {
if (prompt) {
return false;
}
var row = left.parentNode;
var value = input.value;
var oldValue = input.getAttribute("oldValue") || value;
if (!input.hasAttribute("actualValue")) {
input.setAttribute("actualValue", oldValue);
}
var actualValue = input.getAttribute("actualValue");
if (value === actualValue || value === oldValue) {
input.value = actualValue;
// nothing to do.
row.style.backgroundColor = "white";
return false;
}
// User changed value and moved focus to other object.
alertify.confirm("", "You changed a field without pressing the return key.<br>"
+ "Hint: press ESC for leaving a field unchanged.<b>"
+ "You are connected with <b>" + clientTitle + "</b>.<br>"
+ "Are you sure you want to change the value of<br><b>"
+ name + "</b> from <b>" + actualValue
+ "</b> to <b>" + value + "</b>?", function () {
// User decided to proceed.
writePermission = true;
writePermissionTimeout = setTimeout(function () {
writePermission = false;
}, 3600000);
row.style.backgroundColor = "orangered";
// Request for command
sendCommand(s, name + " " + value);
resizeTextfield(input);
prompt = false;
}, function () {
// User decided to cancel
input.value = input.getAttribute("actualValue");
resizeTextfield(input);
row.style.backgroundColor = "white";
prompt = false;
});
}
var form = document.createElement('form');
form.onsubmit = function (e) {
e.preventDefault();
// remove following check: sometimes we want to send a command even with an unchanged value
//if (input.value === input.getAttribute("oldValue")) {
// // nothing to do.
// return false;
//}
if (writePermission) {
var row = left.parentNode;
row.style.backgroundColor = "orangered";
// Request for command
sendCommand(s, name + " " + input.value);
} else {
var value = input.value
prompt = true;
alertify.confirm("", "You are connected with <b>" + clientTitle
+ "</b>. <br>"
+ "Are you sure you want to modify things here?",
function () {
// User decided to proceed.
writePermission = true;
writePermissionTimeout = setTimeout(function () {
writePermission = false;
}, 3600000);
var row = left.parentNode;
row.style.backgroundColor = "orangered";
// Request for command
sendCommand(s, name + " " + value);
resizeTextfield(input);
prompt = false;
}, function () {
// User decided to cancel
input.value = input.getAttribute("oldValue");
resizeTextfield(input);
prompt = false;
});
}
};
form.appendChild(input);
var right = document.createElement('span');
right.setAttribute("class", "col-right");
right.appendChild(form);
return appendToContent(info, left, right);
}
function posTextfield(s, left) {
var content = swiper[s].slides[swiper[s].activeIndex].childNodes[1];
var row = left.parentNode;
content.scrollTop = row.offsetTop - 30;
}
function resizeTextfield(input) {
if (input.value.length > input.size * 12 / 20) {
var str0 = window.getComputedStyle(input).fontSize;
var str1 = str0.substring(0, str0.length - 2);
if (input.value.length < 43) {
input.style.width = input.value.length * str1 * 12 / 20 + "px";
}
} else {
input.style.width = "100px";
}
}
function createCheckbox(s, name, title, info) {
// Creates row-element containing checkbox-item
var left = createTitle(title, info);
var input = document.createElement('input');
input.setAttribute("type", "checkbox");
input.setAttribute("name", name);
input.setAttribute("__ctype__", "checkbox");
input.setAttribute("class", "parameter-checkbox");
input.onkeyup = function (e) {
if (e.keyCode === 32) {
handleCheckbox();
}
}
var label = document.createElement('label');
label.setAttribute("for", input);
label.setAttribute("class", "parameter-label");
label.onclick = function () {
handleCheckbox();
}
function handleCheckbox() {
if (writePermission) {
var row = left.parentNode;
row.style.backgroundColor = "orangered";
if (input.checked) {
var value = "0";
input.checked = false;
} else {
var value = "1";
input.checked = true;
}
// Request for command
sendCommand(s, name + " " + value);
} else {
alertify.confirm("", "You are connected with <b>" + clientTitle
+ "</b>. <br>"
+ "Are you sure you want to modify things here?",
function () {
// User decided to proceed.
writePermission = true;
writePermissionTimeout = setTimeout(function () {
writePermission = false;
}, 3600000);
var row = left.parentNode;
row.style.backgroundColor = "orangered";
if (input.checked) {
var value = "0";
input.checked = false;
} else {
var value = "1";
input.checked = true;
}
// Request for command
sendCommand(s, name + " " + value);
}, function () {
// User decided to cancel
});
}
};
var right = document.createElement('span');
right.setAttribute("class", "col-right");
right.appendChild(input);
right.appendChild(label);
return appendToContent(info, left, right);
}
function createSelection(s, name, title, buttons, info) {
// Creates row-element containing dropdown-selection.
var left = createTitle(title, info);
var select = document.createElement('select');
select.setAttribute("name", name);
select.setAttribute("__ctype__", "enum");
select.setAttribute("class", "select-params");
select.onfocus = function () {
select.setAttribute("oldIndex", select.selectedIndex);
}
select.oninput = function () {
if (writePermission && title != "device config") {
var row = left.parentNode;
row.style.backgroundColor = "orangered";
// Request for command
sendCommand(s, name + " " + this.value);
} else {
alertify.confirm("", "You are connected with <b>" + clientTitle
+ "</b>. <br>"
+ "Are you sure you want to modify things here?",
function () {
// User decided to proceed.
writePermission = true;
writePermissionTimeout = setTimeout(function () {
writePermission = false;
}, 3600000);
var row = left.parentNode;
row.style.backgroundColor = "orangered";
// Request for command
sendCommand(s, name + " " + select.value);
}, function () {
// User decided to cancel
select.value = select.options[select
.getAttribute("oldIndex")].value;
});
}
};
for (var i = 0; i < buttons.length; i++) {
var option = document.createElement('option');
option.setAttribute("type", "enum");
option.setAttribute("class", "option-params");
option.setAttribute("value", buttons[i].value);
option.appendChild(document.createTextNode(buttons[i].title));
select.add(option);
}
select.style.display = "none";
var right = document.createElement('span');
right.setAttribute("class", "col-right");
right.appendChild(select);
return appendToContent(info, left, right);
}
function createTitle(title, info) {
// Creates left side of row-tag containing title. Title may hold additional
// information, which is shown, when title-tag is clicked.
var left = document.createElement('span');
if (info) {
left.setAttribute("class", "col-left event-toggle-info");
left.onclick = function () {
var infoBox = left.parentNode.childNodes[0];
if (infoBox.style.display == "none") {
infoBox.style.display = "block";
} else {
infoBox.style.display = "none";
}
}
left.innerHTML = title + "<sup><b>(i)</b></sup>";
} else {
left.setAttribute("class", "col-left");
left.innerHTML = title;
}
return left;
}
function createInfo(info) {
// Creates info-box, which isn't visible by default but can be displayed.
var infoBox = document.createElement('div');
infoBox.setAttribute("class", "info-box");
infoBox.onclick = function () {
infoBox.style.display = "none";
}
infoBox.innerHTML = info;
return infoBox;
}
function appendToContent(info, left, right) {
// Cretees row-tag containing infoBox (not visible by default), left side
// (span)
// and right side (span).
var row = document.createElement('div');
row.setAttribute("class", "row");
if (info) {
row.appendChild(createInfo(info));
}
for (var i = 1; i < arguments.length; i++)
if (arguments[i]) {
row.appendChild(arguments[i]);
}
return row;
}

View File

@ -0,0 +1,198 @@
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % INIT
var MAXBLOCK = 4; // max number of blocks
var elements = []; // grid elements
var swiper = []; // This array contains main-swiper-Instances.
var hostPort = ""; // Address and port of static html-file.
var clientID = ""; // ID given by server when SSE-connection is established.
var clientTitle = ""; // Contains name of instrument and device.
var getUpdates = true;
var getUpdatesGraphics = true;
var initCommands = [];
var loadingShown = true;
var writePermission = false;
var menuMode = false;
var panelOn = true;
var firstState = 0;
function Settings() {
// get key/value pairs from search part of the URL and fill into query
var qstr = location.search;
console.log(qstr);
if (qstr) {
var a = (qstr[0] === '?' ? qstr.substr(1) : qstr).split('&');
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
key = decodeURIComponent(b[0]);
value = decodeURIComponent(b.slice(1).join("=").replace(/\+/g, '%20'));
this[key] = value;
}
}
this.treat = function (variable, shortcut, convert, defaultValue) {
// the third argument should be:
// None or omitted for a string
// to_bool for converting to boolean
// Number for converting to a number
if (shortcut in this) {
value = this[shortcut];
if (convert) {
value = convert(value);
}
} else {
value = defaultValue;
}
//console.log(variable, value);
window[variable] = value;
return this;
}
this.add_init = function (shortcut, command, defaultValue) {
if (shortcut in this) {
value = to_bool(this[shortcut]);
console.log(shortcut, value);
} else {
value = defaultValue;
console.log(shortcut, value);
}
if (value) {
initCommands.push(command);
}
return this;
}
}
function to_bool(string) {
// everything else than false, 0 or an empty string is considered as true
return !/^(false|FALSE|False|0|)$/.test(string);
}
// example: localhost:5000/SEAWebClient.html?hp=ldmzolliker:5000&dc=1
// hostPort given and debugCommunication switched on
new Settings()
.treat("debugCommunication", "dc", 0, 0) // 1: debug synchronous comm. 2: debug syn and asyn comm
.treat("debugGraphics", "dg", to_bool, false)
.treat("hostPort", "hp", 0, location.hostname + ":" + location.port)
.treat("showMain", "sm", to_bool, true)
.treat("showConsole", "sc", to_bool, true)
.treat("showOverview", "so", to_bool, true)
.treat("showGraphics", "sg", to_bool, true)
.treat("showAsync", "sa", to_bool, false)
function loadFirstBlocks() {
if (showMain) pushInitCommand("getblock?path=main&", "main")
if (showConsole) pushInitCommand("console?", "console")
if (nColumns == 1) { // probably mobile phone}
if (showGraphics) pushInitCommand("gettime?time=-1800,0&", "graphics")
if (showOverview) pushInitCommand("getblock?path=_overview&", "overview")
} else {
if (showOverview) pushInitCommand("getblock?path=_overview&", "overview")
if (showGraphics) pushInitCommand("gettime?time=-1800,0&", "graphics")
// last is shown first
}
}
function nextInitCommand() {
// do the next init request
if (initCommands.length > 0) {
next = initCommands.shift();
cmd = next[0]
text = next[1]
var loadingSpan = document.getElementsByClassName("loading-span")[0];
loadingSpan.innerHTML = loadingSpan.innerHTML + "<br>loading " + htmlEscape(text) + " ...";
reqJSON(0, "http://" + hostPort + "/" + cmd + "id=" + clientID, successHandler, errorHandler);
} else if (loadingShown) {
var loadingScreen = document.getElementsByClassName("loading-div")[0];
loadingScreen.style.display = "none";
loadingShown = false;
if (location.hash) { // there was a #hash part
var slideNames = location.hash.substr(1);
gotoGroups(slideNames);
}
console.log("loading finished");
}
}
function pushInitCommand(cmd, text) {
initCommands.push([cmd, text]);
}
window.onload = function() {
// Executed when loading of page is completed.
// Create grid-elements. (see also at
// 'SEAWebClientResponsivity.js')
elements = createGrid();
// Number of shown columns 'm' and rows 'n' is determined depending on
// available viewport-size.
determineViewportSize();
// Determine size of grid-elements depending on number of columns 'm' and
// rows 'n'
adjustGrid();
// Create swiper-instances.
for (var s = 0; s < MAXBLOCK; s++) {
swiper[s] = insertSwiper(s);
}
var homeButton = document.getElementById("home-icon");
homeButton.onclick = function () {
window.location = "http://" + location.hostname + ":8800/";
};
buildUpdateConnection();
if (location.hash) {
console.log("hash in url", location.hash);
initSlides = location.hash.substring(1);
} else {
initSlides = "";
}
// Initialisation will be continued, when SSE-connection is established
// and id-message is obtained.
// (see also at SEAWebClientCommunication.js)
addEventListener("popstate", function (e) {
if (e.state) {
if (loadingShown) {
if (initSlides != e.state.funarg) {
console.log("hash mismatch", initSlides, e.state.funarg);
initSlides = e.state.funarg;
}
} else {
console.log("popstate", e.state.func, e.state.funarg);
window[e.state.func](e.state.funarg);
}
} else {
document.title = "SEA "+ clientTitle;
for (var s=0; s<MAXBLOCK; s++) {
swiper[s].slideTo(defaultSlidePos(s));
}
}
})
};
function toggleHeader() {
// Show and hide box showing name of the current device ('see also
// SEAWebClient.html')
var main_panel = document.getElementById("main-panel");
panelOn = !panelOn;
if (panelOn) {
header.innerHTML = clientTitle
/* header.setAttribute("style", "width: auto;"); */
main_panel.setAttribute("style", "display: block;");
} else {
/* header.setAttribute("style", "width: 30px;"); */
main_panel.setAttribute("style", "display: none;");
}
return true;
}

View File

@ -0,0 +1,151 @@
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % RESPONSIVITY
var nColumns = 1; // Viewport is subdivided in nColumns columns.
var nRows = 1; // Viewport is subdivided in nRows rows.
var gridCountGraphics = 2; // Number of displayed graphics-swipers.
var MINWIDTH = 400; // Minimal width of block.
var MINHEIGHT = 700; // Minimal height of block.
function createGrid() {
// Creates grid-elements. By default only the first one is shown
// and
// takes the whole viewport.
var elements = [];
for (var i = 0; i < 4; i++) {
var element = document.createElement('div');
element.setAttribute("class", "grid-element");
document.getElementById("center").appendChild(element);
elements.push(element);
}
return elements;
}
function determineViewportSize() {
// Number of columns 'nColumns' and rows 'nRows' is determined depending on available
// viewport-size.
var width = window.innerWidth || document.documentElement.clientWidth
|| document.body.clientWidth;
var height = window.innerHeight || document.documentElement.clientHeight
|| document.body.clientHeight;
nColumns = 1;
if (width > MINWIDTH * 1.8) {
nColumns = 2;
}
if (width > MINWIDTH * 2.5) {
nColumns = 3;
}
if (width > MINWIDTH * 3.5) {
nColumns = 4;
}
nRows = 1;
if (height > MINHEIGHT) {
nRows = 2;
}
if (menuMode) {
nRows = 1;
nColumns = 1;
}
}
function sizeChange() {
determineViewportSize();
adjustGrid();
}
function adjustGrid() {
// Determines size of grid-elements depending on number of columns 'nColumns' and
// rows 'nRows'
var width = window.innerWidth || document.documentElement.clientWidth
|| document.body.clientWidth;
var height = window.innerHeight || document.documentElement.clientHeight
|| document.body.clientHeight;
switch (nColumns) {
case 1:
if (menuMode) {
leftWidth = Math.min(100, MINWIDTH / width * 100);
style(0,leftWidth + "vw","100vh");
style(1); // hide
style(2); // hide
style(3); // hide
} else {
style(0,"100vw","100vh");
style(1); // hide
style(2); // hide
style(3); // hide
}
break;
case 2:
rightWidth = Math.min(50, MINWIDTH / width * 100);
leftWidth = 100 - rightWidth;
if (nRows == 1) {
style(0,leftWidth + "vw","100vh");
style(1,rightWidth + "vw","100vh");
style(2); // hide
style(3); // hide
} else {
style(0,leftWidth + "vw","100vh");
style(1,rightWidth + "vw","50vh");
style(2); // hide
style(3,rightWidth + "vw","50vh");
}
break;
case 3:
rightWidth = MINWIDTH / width * 100;
leftWidth = 100 - rightWidth;
if (nRows == 1) {
style(0,leftWidth + "vw","100vh");
style(1,rightWidth + "vw","100vh");
style(2); // hide
style(3); // hide
} else {
style(0,leftWidth + "vw","100vh");
style(1,rightWidth + "vw","50vh");
style(2); // hide
style(3,rightWidth + "vw","50vh");
}
break;
case 4:
rightWidth = MINWIDTH / width * 100;
leftWidth = 100 - 2 * rightWidth;
if (nRows == 1) {
style(0,leftWidth + "vw","100vh");
style(1,rightWidth + "vw","100vh");
style(2); // hide
style(3,rightWidth + "vw","100vh");
} else {
style(0,leftWidth + "vw","100vh");
style(1,rightWidth + "vw","50vh");
style(2,rightWidth + "vw","50vh");
style(3,(2 * rightWidth) + "vw","50vh");
}
break;
default:
break;
}
}
function style(s, width, height) {
if (width) {
elements[s].style.display = "inline-block";
elements[s].style.width = width;
} else {
elements[s].style.display = "none";
}
if (height) {
elements[s].style.height = height;
}
elements[s].style.float = "left";
}
function isTouchDevice() {
return !!('ontouchstart' in window)
|| !!('msmaxtouchpoints' in window.navigator);
};

View File

@ -0,0 +1,42 @@
var showSettings = false;
function toggleSettings() {
// Shows and hides settings.
console.log("toggle settings");
if (showSettings) {
document.getElementsByClassName("start-settings-checkboxes")[0].style.display = "none";
document.getElementsByClassName("start-settings-show-hide")[0].innerHTML = "show";
showSettings = false;
} else {
document.getElementsByClassName("start-settings-checkboxes")[0].style.display = "inline";
document.getElementsByClassName("start-settings-show-hide")[0].innerHTML = "hide";
showSettings = true;
}
}
function followLink(destination) {
// Build query string and load new page.
var checkboxes = document.getElementsByClassName("start-checkbox");
var query = "";
// Loop through settings-checkboxes.
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked != checkboxes[i].defaultChecked) {
if (query === "") {
query = "?";
} else {
query += "&";
}
query += checkboxes[i].name + "=";
if (checkboxes[i].checked) {
query += "1";
} else {
query += "0";
}
}
}
destination += query;
console.log(destination);
window.location = destination;
}

View File

@ -0,0 +1,174 @@
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % SWIPER
function insertSwiper(s) {
// Create an empty swiper-instance and append the swiper-container to
// 'grid-element' s.
var container = document.createElement('div');
container.setAttribute("class", "swiper-container swiper-container-main");
elements[s].appendChild(container);
var swiperwrapper = document.createElement('div');
swiperwrapper.setAttribute("class", "swiper-wrapper swiper-wrapper-main");
swiperwrapper.setAttribute("s", s);
container.appendChild(swiperwrapper);
var paginationWrapper = document.createElement('div');
paginationWrapper.setAttribute("class", "swiper-pagination");
container.appendChild(paginationWrapper);
var buttonPrev = document.createElement("div");
buttonPrev.setAttribute("class", "swiper-button-prev swiper-button-black");
var buttonNext = document.createElement("div");
buttonNext.setAttribute("class", "swiper-button-next swiper-button-black");
var swiper = new Swiper(container, {
direction : 'horizontal',
pagination: {
el: paginationWrapper,
clickable: true
},
spaceBetween : 0,
navigation:{
prevEl: buttonPrev,
nextEl: buttonNext
}
});
//console.log(swiper);
if (!isTouchDevice()) {
// Add swiper-button.
swiper.params.noSwipingClass = "swiper-slide-main";
container.appendChild(buttonPrev);
container.appendChild(buttonNext);
}
return swiper;
}
function findSlide(s, type) {
var i;
for (i = 0; i < swiper[s].slides.length; i++) {
if (swiper[s].slides[i].getAttribute("slide-type") === type) {
return swiper[s].slides[i];
}
}
return null;
}
function replaceSlideContent(slide, title, content) {
titlewrapper = slide.childNodes[0].childNodes[0];
titlewrapper.innerHTML = title;
slide.replaceChild(content, slide.childNodes[1])
}
function insertSlide(s, title, type, content) {
// Inserts new group to instance s of Swiper.
var slide = findSlide(s, type);
if (slide) { // slide already exists
replaceSlideContent(slide, title, content);
return
}
var panel = document.createElement('div');
panel.setAttribute("class", "panel");
titlewrapper = document.createElement('span');
titlewrapper.innerHTML = title;
panel.appendChild(titlewrapper);
if (type == "_overview" || type == "main") {
//panel.appendChild(createHomeButton(s));
} else if (type != "graphics" && type != "_inst_select" && type != "console") {
panel.appendChild(createCloseButton(s));
}
/*if (type === "graphics") {
panel.appendChild(createUpdateButton(s));
}*/
slide = document.createElement('div');
slide.setAttribute("class", "swiper-slide swiper-slide-main");
// Store type so it can be found easiely later.
slide.setAttribute("slide-type", type);
slide.appendChild(panel);
slide.appendChild(content);
// Graphics-slide is put at mostleft position.
if (type == "graphics" || type == "_overview") {
// Remove old graphics-slide.
/* see above
if(swiper[0].slides[0].getAttribute("slide-type") === "graphics"){
swiper[0].removeSlide(0);
}
*/
swiper[s].prependSlide(slide);
swiper[s].slideTo(0);
} else if (type == "console") {
swiper[s].appendSlide(slide);
if (s === 3) {
// Slide mostright swiper-instance to last position (console)
swiper[3].slideNext();
}
} else {
swiper[s].appendSlide(slide);
if (swiper[s].slides.length > 1) {
var consoleslide = swiper[s].slides[swiper[s].slides.length - 2];
if (consoleslide.getAttribute["slide-type"] == "console") {
// shift Console-slide to mostright position.
swiper[s].removeSlide(swiper[s].slides.length - 2);
swiper[s].appendSlide(consoleslide);
// Slide to position of new slide
swiper[s].slideTo(swiper[s].slides.length - 2);
} else {
swiper[s].slideTo(swiper[s].slides.length - 1);
}
}
}
}
function createCloseButton(s) {
// Creates 'span'-element containing close-button.
var wrapper = document.createElement('span');
wrapper.onclick = function () {
swiper[s].removeSlide(swiper[s].activeIndex);
swiper[s].slidePrev();
};
var closeButton = '<svg class="interactive icon slide-close-icon" fill="#000000" height="24" viewBox="0 0 24 24" width="24"><path d="M19 6.41L17.6 5 12 10.6 6.4 5 5 6.4 10.6 12 5 17.6 6.4 19 12 13.4 17.6 19 19 17.6 13.4 12z"/><path d="M0 0h24v24H0z" fill="none"/></svg>';
wrapper.innerHTML = closeButton;
return wrapper;
}
function createUpdateButton(s){
// Creates 'span'-element containing update-button (Should be removed later!)
var button = document.createElement('span');
button.setAttribute("class","interactive toggle-updates-graphics")
button.onclick = function () {
getUpdatesGraphics = ! getUpdatesGraphics;
button.innerHTML = "updates = "+getUpdatesGraphics;
};
button.innerHTML = "updates: "+getUpdatesGraphics;
return button;
}
function defaultSlidePos(s) {
return s < 3 ? 0 : swiper[s].slides.length-1;
}
function getSlideNames() {
var names = []
for (var s=0; s<MAXBLOCK; s++) {
var sw = swiper[s];
var name = "";
if (sw.activeIndex != defaultSlidePos(s)) {
name = sw.slides[sw.activeIndex].getAttribute("slide-type");
}
names.push();
}
for (var s=MAXBLOCK-1; s>=0; s--) {
if (names[s] != "") break;
names.pop();
}
return names;
}