287 lines
12 KiB
JavaScript
287 lines
12 KiB
JavaScript
/*
|
|
* TablesServlet specific JavaScript
|
|
*
|
|
* TODO: add enum descriptions to schema and use them for converting states, and
|
|
* for calc'ing the "next" state
|
|
* TODO: improve performance when client is sitting on page while lengthy list is loaded into JMRI
|
|
* TODO I18N titles and headers
|
|
* TODO: debug JMRI handling for firing route, signalHead lit and held, signalMast lit and held
|
|
*/
|
|
|
|
var jmri = null;
|
|
//convert page parms into array to use for filtering rows
|
|
const urlSearchParams = new URLSearchParams(window.location.search);
|
|
const params = Object.fromEntries(urlSearchParams.entries());
|
|
|
|
//handle an error message returned via the websocket from the server
|
|
// parms: html error code, message is the message text
|
|
function showError(code, message) {
|
|
$("#activity-alert").removeClass("show").addClass("hidden");
|
|
$("table#jmri-data").removeClass("show").addClass("hidden");
|
|
$("#warning-no-data").removeClass("show").addClass("hidden");
|
|
$("#error-message").html("Error " + code + ":" + message);
|
|
$("#error-message").removeClass("hidden").addClass("show");
|
|
}
|
|
|
|
//parm is the array of items from which to build table
|
|
function rebuildTable(data) {
|
|
tableType = $("html").data("table-type");
|
|
if (data[0] && data[0].type !== tableType) {
|
|
if (tableType.startsWith(data[0].type)
|
|
|| (tableType=="memories" && data[0].type=="memory")
|
|
|| (tableType=="roster" && data[0].type=="rosterEntry")) { //server returns singular for plural request
|
|
window.location = "/tables/" + data[0].type; //redirect to singular page
|
|
}
|
|
jmri.log("incoming type '" + data[0].type + "' does not match current page '" + tableType + "', ignoring.");
|
|
return;
|
|
}
|
|
$("#activity-alert").removeClass("hidden").addClass("show");
|
|
$("table#jmri-data").removeClass("show").addClass("hidden");
|
|
$("#warning-no-data").removeClass("show").addClass("hidden");
|
|
$("#error-message").removeClass("show").addClass("hidden");
|
|
if (data.length) {
|
|
//build header row from first row of data
|
|
var thead = '<tr>';
|
|
$.each(Object.keys(data[0].data), function (index, value) {
|
|
thead += "<th class='" + value + "'>" + value + "</th>";
|
|
});
|
|
thead += '</tr>';
|
|
$("table#jmri-data thead").html(thead);
|
|
//build all data rows for table body
|
|
var tbody = '';
|
|
var rows = 0;
|
|
data.forEach(function (item) { //loop thru rows in json
|
|
var keep = true;
|
|
$.each(params, function (index, value) { //compare against filter parms, skipping unless all match
|
|
if (displayCellValue(item.type, index, item.data[index]).toLowerCase() != value.toLowerCase()) {
|
|
keep = false;
|
|
jmri.log("no match for " +index+"="+value+" != " + item.data[index])
|
|
return false;
|
|
}
|
|
});
|
|
if (keep) {
|
|
jmri.socket.send(item.type, { name: item.data.name }); //request updates from server
|
|
tbody += "<tr data-name='" + item.data.name + "'>";
|
|
tbody += buildRow(item.data) + '</tr>';
|
|
rows++;
|
|
}
|
|
});
|
|
if (rows) {
|
|
$("table#jmri-data tbody").html(tbody);
|
|
$("table#jmri-data").removeClass("hidden").addClass("show");
|
|
var newTableObject = document.getElementById("jmri-data");
|
|
sorttable.makeSortable(newTableObject);
|
|
hideEmptyColumns("table#jmri-data tr th");
|
|
sortByFirstColumn();
|
|
} else {
|
|
$("#warning-no-data").removeClass("hidden").addClass("show");
|
|
}
|
|
} else {
|
|
$("#warning-no-data").removeClass("hidden").addClass("show");
|
|
}
|
|
$("#activity-alert").removeClass("show").addClass("hidden");
|
|
|
|
//setup for clicking on certain columns to send state changes
|
|
$('table.idTag,table.light,table.route,table.sensor,table.turnout,table.car,table.engine,table.signalHead,table.signalMast')
|
|
.on('click', 'td.locationUnknown,td.outOfService,td.state,td.lit,td.held', function (e) {
|
|
rowName = $(this).parent('tr').data('name');
|
|
colName = e.target.className;
|
|
currValue = $(this).data('value').toString();
|
|
jmri.socket.send(tableType, { 'name': rowName, [colName]: getNextValue(currValue) }, 'post');
|
|
});
|
|
}
|
|
//handle the toggling of the next Value for clicks
|
|
var getNextValue = function(value){
|
|
switch (value) {
|
|
case '0': return '4';
|
|
case '1': return '4';
|
|
case '2': return '4';
|
|
case '4': return '2';
|
|
case 'true': return 'false';
|
|
case 'false': return 'true';
|
|
case 'yes': return 'no';
|
|
case 'no': return 'yes';
|
|
default: return value; //no match, leave it the same
|
|
}
|
|
};
|
|
|
|
//returns the html for a single row from that row's data object
|
|
function buildRow(data) {
|
|
var r = "";
|
|
tableType = $("html").data("table-type");
|
|
//note: syntax below required since some JMRI json objects have a "length" attribute equal 0
|
|
$.each(Object.keys(data), function (index, value) {
|
|
r += "<td class='" + value + "' data-value='" + data[value] + "'>"
|
|
+ displayCellValue(tableType, value, data[value]) + "</td>";
|
|
});
|
|
return r;
|
|
}
|
|
|
|
//find row by key and replace it with generated html for that row
|
|
function replaceRow(key, data) {
|
|
var row = $("table#jmri-data tr[data-name='" + key + "']");
|
|
if ($(row).length) {
|
|
row.html(buildRow(data));
|
|
hideEmptyColumns("table#jmri-data tr th");
|
|
} else {
|
|
jmri.log("row not found for name='" + key + "'");
|
|
}
|
|
}
|
|
|
|
/* convert each cell into more human-readable form */
|
|
function displayCellValue(type, colName, value) {
|
|
if (value == null) {
|
|
return ""; //return empty string for any null value
|
|
}
|
|
if (type == "type" && colName == "name"){ //if list is of json types,
|
|
return "<a href='/tables/" + value + "' >" + value + "</a>"; // create link to table for this type
|
|
}
|
|
if ($.isArray(value)) {
|
|
if (value.length == 0) {
|
|
return "";
|
|
}
|
|
if (typeof value[0] === "object") { //check first item to see if associative array
|
|
ret = "";
|
|
comma = "";
|
|
value.forEach(function(item) { //return list
|
|
if (item) {
|
|
if (item.name) {
|
|
ret += comma + item.name;
|
|
if (item.value) {
|
|
ret += "=" + item.value;
|
|
} else if (item.userName) {
|
|
ret += " " + item.userName;
|
|
} else if (item.label) {
|
|
ret += " " + item.label;
|
|
}
|
|
} else {
|
|
Object.keys(item).forEach(function(key) { //otherwise list the array of pairs
|
|
ret += comma + key + ":" + item[key];
|
|
});
|
|
}
|
|
comma = ", ";
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
ret = "";
|
|
comma = "";
|
|
value.forEach(function(item) { //otherwise build and return simple array list
|
|
ret += comma + item;
|
|
comma = ", ";
|
|
});
|
|
return ret;
|
|
}
|
|
if (typeof value === "object") {
|
|
if (value.type == "idTag"){
|
|
return value.data.userName // handle idTag object by displaying userName
|
|
} else if (value.userName) {
|
|
return value.userName; // return userName of object if it has one
|
|
} else if (value.name) {
|
|
return value.name; // return name of object if it has one
|
|
} else {
|
|
return "[obj]"; //placeholder
|
|
}
|
|
}
|
|
//convert known states to human-readable strings, if not known show as is
|
|
if ((colName == "state") || (colName == "occupiedSense")) {
|
|
switch (type) {
|
|
case "turnout":
|
|
switch (value) {
|
|
case jmri.UNKNOWN: return "unknown";
|
|
case jmri.CLOSED: return "closed";
|
|
case jmri.THROWN: return "thrown";
|
|
case jmri.INCONSISTENT: return "inconsistent";
|
|
default: return value;
|
|
}
|
|
case "route":
|
|
case "sensor":
|
|
case "layoutBlock":
|
|
switch (value) {
|
|
case jmri.UNKNOWN: return "unknown";
|
|
case jmri.ACTIVE: return "active";
|
|
case jmri.INACTIVE: return "inactive";
|
|
case jmri.INCONSISTENT: return "inconsistent";
|
|
default: return value;
|
|
}
|
|
case "block":
|
|
switch (value) {
|
|
case jmri.UNKNOWN: return "unknown";
|
|
case 2: return "occupied";
|
|
case 4: return "unoccupied";
|
|
default: return value;
|
|
}
|
|
case "light":
|
|
switch (value) {
|
|
case jmri.UNKNOWN: return "unknown";
|
|
case 2: return "on";
|
|
case 4: return "off";
|
|
default: return value;
|
|
}
|
|
default:
|
|
return value; //not special, just return the passed in value
|
|
}
|
|
}
|
|
return value; //otherwise return unchanged value
|
|
}
|
|
|
|
//replace some special chars with html equivalents
|
|
function htmlEncode(html) {
|
|
return document.createElement('a').appendChild(
|
|
document.createTextNode(html)).parentNode.innerHTML;
|
|
};
|
|
|
|
function hideEmptyColumns(selector) {
|
|
$(selector).each(function (index) {
|
|
//select all tds in this column
|
|
var tds = $(this).parents('table').find('tr td:nth-child(' + (index + 1) + ')');
|
|
//check if all the cells in this column are empty
|
|
if (tds.length === tds.filter(':empty').length) {
|
|
$(this).addClass("hidden"); //hide header
|
|
tds.addClass("hidden"); //hide cells
|
|
} else {
|
|
$(this).removeClass("hidden"); //show header
|
|
tds.removeClass("hidden"); //show cells
|
|
}
|
|
});
|
|
}
|
|
|
|
function sortByFirstColumn() {
|
|
var firstTH = document.getElementsByTagName("th")[0];
|
|
sorttable.innerSortFunction.apply(firstTH, []);
|
|
}
|
|
|
|
//-----------------------------------------javascript processing starts here (main) ---------------------------------------------
|
|
$(document).ready(function () {
|
|
|
|
// add table type to heading and title
|
|
$("#table-type").text($("html").data("table-type"));
|
|
document.title = $("h1.title").text();
|
|
|
|
//show filter if used
|
|
if (!$.isEmptyObject(params)) {
|
|
$('#filter-text').text("Filter: " + urlSearchParams.toString().replace("&",", "));
|
|
}
|
|
|
|
jmri = $.JMRI({
|
|
// when we get the hello message, send a websocket list request which
|
|
// returns the list and sets up change listeners
|
|
// note: the functions and parameter names must match exactly those in jquery.jmri.js
|
|
hello: function (data) {
|
|
jmri.getList($("html").data("table-type")); // request list and updates for the table-type
|
|
},
|
|
// everything calls console()
|
|
console: function (originalData) {
|
|
var data = JSON.parse(originalData);
|
|
// jmri.log("in console: data=" + JSON.stringify(data).substr(0, 180) + "...");
|
|
if ($.isArray(data)) { // if its an array,
|
|
rebuildTable(data); // replace the table with the array list
|
|
} else if ((data.type) && (data.type === "error")) {
|
|
showError(data.data.code, data.data.message); //display any errors returned
|
|
} else if ((data.type) && (!data.type.match("pong|hello|goodbye"))) { //skip control messages
|
|
replaceRow(data.data.name, data.data); // if single item, update the row
|
|
}
|
|
},
|
|
});
|
|
});
|