6 border-bottom-left-radius: 0;
7 border-bottom-right-radius: 0;
8 border-right: 1px solid white;
12 border-right: 1px solid white;
21 text-align-last:center;
24 background-color: transparent;
29 border-left: 2px solid #808080;
30 border-right: 2px solid #808080;
31 border-top: 2px solid #808080;
32 border-radius: 5px 5px 0 0;
36let estyle = document.createElement('style');
37estyle.textContent = eqtable_style;
38document.head.appendChild(estyle);
40<!-- Select file dialog -->
42<div class="dlgTitlebar">Select file</div>
45 <div style="margin: auto;width: 70%">
46 <input type="file" id="fileSelector" accept=".json">
49 <button class="dlgButton" onclick="importFileFromSelector(this.parentNode.parentNode);dlgHide('dlgFileSelect');">Load</button>
50 <button class="dlgButton" onclick="dlgHide('dlgFileSelect')">Cancel</button>
64function check_frontend() {
66 let fename = equipment.frontend_name;
67 mjsonrpc_call("cm_exist", {"name": fename}).then( function(rpc) {
68 if (rpc.result.status === 1)
69 document.getElementById("fewarning").style.display = "none";
71 document.getElementById("fewarning").style.display = "";
72 document.getElementById("fewarning").firstChild.innerHTML =
73 "<a href='?cmd=Programs'>" + fename + "</a> not running";
75 window.setTimeout(check_frontend, 5000);
76 }).catch(function(error){mjsonrpc_error_alert(error);});
80function valueChanged(t) {
82 let td = t.parentElement;
84 // don't make extra display yellow
85 if (td.parentElement &&
86 td.parentElement.parentElement &&
87 td.parentElement.parentElement.parentElement &&
88 td.parentElement.parentElement.parentElement.parentElement &&
89 td.parentElement.parentElement.parentElement.parentElement.id === "displayExtra")
92 // check if value really changed
93 if (t.oldValue === undefined) {
94 t.oldValue = t.innerText;
98 if (t.innerText === t.oldValue)
100 t.oldValue = t.innerText;
102 td.style.backgroundColor = 'var(--myellow)';
103 td.style.setProperty("-webkit-transition", "", "");
104 td.style.setProperty("transition", "", "");
106 window.setTimeout(() => {
107 td.style.setProperty("-webkit-transition", "background-color 1s", "");
108 td.style.setProperty("transition", "background-color 1s", "");
109 td.style.backgroundColor = "";
113function redirectEq(div, eqName) {
114 // update URL manually
115 let url = window.location.href;
116 if (url.search("&eq=") !== -1) {
117 url = url.slice(0, url.search("&eq="));
118 url += "&eq=" + eqName;
119 if (url !== window.location.href)
120 window.history.replaceState({}, "Equipment", url);
123 // delete lower part of old table
124 let table = document.getElementById(equipment.tid);
135 eqtable_init(div, eqName, true, true);
140function validateData(value, elem) {
141 // avoid double call of validateData
142 // - once by direct call
143 // - once by ODBFinishInlineEdit if focus is lost
144 if (confirmFlag === 1)
149 equipment.table.forEach( e => {
157 dlgConfirm("You have selected " + nSel + " values. Do you want to change all of them to " + value + " ?",
158 setAll, { "value" : value, "elem": elem });
164function setAll(flag, param) {
167 let tr = param.elem.parentNode.parentNode;
169 for (let i=0 ; i<tr.childNodes.length ; i++)
170 if (tr.childNodes[i] === param.elem.parentNode) {
174 equipment.table.forEach(e => {
175 if (index && e.selected) {
176 let m = e.tr.childNodes[index].firstChild;
177 m.setValue(param.value);
184 window.location.href = "?cmd=odb&odb_path=/Equipment/" + equipment.name;
187function selectGroup(t) {
188 for (let g of equipment.group) {
189 let trGroup = document.getElementsByName(g);
191 for (let tr of trGroup)
192 tr.style.display = (g === t.value || t.value === "All") ? "table-row" : "none";
196function enable_row(row, enabled, index) {
197 let disabled = false;
198 if (enabled !== undefined) {
199 if (Array.isArray(enabled)) {
202 } else if (enabled === false)
206 row.disabled = disabled;
209 row.style.backgroundColor = "#B0B0B0";
211 row.style.backgroundColor = "";
213 // go through row elements and disable input and select elements
214 for (let child of row.children) {
215 if (child.childNodes[0] && child.childNodes[0].dataset && child.childNodes[0].dataset.odbEditable !== undefined)
216 child.childNodes[0].dataset.odbEditable = disabled ? "0" : "1";
217 if (child.childNodes[0] && child.childNodes[0].tagName === "SELECT")
218 child.childNodes[0].disabled = disabled;
222function pageSel(page) {
224 let ps = document.getElementsByClassName('pageSelector');
228 // change color of tabs
230 for (let i= 0 ; i<ps.length ; i++) {
231 if (ps[i].children[0].innerText === page) {
233 ps[i].style.backgroundColor = "#8cbdff";
234 ps[i].children[0].style.color = "#f8f8f8";
236 ps[i].style.backgroundColor = "#dddddd";
237 ps[i].children[0].style.color = "#0000ff";
241 // hide/unhide table rows
242 if (page === "All") {
243 for (let i=0 ; i<equipment.table.length ; i++)
244 equipment.table[i].tr.style.display = "";
246 for (let i=0 ; i<equipment.table.length ; i++) {
247 if (Math.floor(i / equipment.channelsPerDevice) === pageIndex)
248 equipment.table[i].tr.style.display = "";
250 equipment.table[i].tr.style.display = "none";
254 // modify extra values
255 let extra = document.getElementById('displayExtra');
257 extra = extra.children[0].children[0].children[0];
258 for (let i=0 ; i<extra.children.length ; i++) {
259 let d = extra.children[i].children[0];
260 if (d.className === "modbselect" || d.className ==="modbvalue") {
262 let p = d.dataset.odbPath;
264 if (page === "All") {
266 p = p.replace(/\[.*?\]/g, "[*]");
271 p = p.replace(/\[.*?\]/g, "[" + pageIndex + "]");
273 p = p + "[" + pageIndex + "]";
276 d.dataset.odbPath = p;
281 equipment.selectedPage = pageIndex;
286function eqtable_init(div, eqName, bButtons, bEqSelect) {
288 if (eqName === undefined)
289 equipment.name = new URLSearchParams(window.location.search).get('eq');
291 equipment.name = eqName;
293 if (equipment.name === undefined || equipment.name === null) {
294 dlgAlert("Please specify equipment name in URL with \"eq=<name>\"");
298 // add equipment table to <div>
299 if (div === undefined) {
300 dlgAlert("Please specify <div> element when calling eqtable_init()");
304 equipment.tid = 'eqTable' + equipment.name.replace(/\s+/g, ''); // remove all spaces from eq
306 let html = '<table id="' + equipment.tid + '" class="mtable" style="visibility: hidden">';
311 ' <td id="tableHeader" class="mtableheader">\n' +
312 ' <select class="mthselect" id="eqSelect" onchange="redirectEq(document.getElementById(\'' + div.id + '\'), this.value)">\n' +
317 html += '<tr><td id="fewarning" width="100%" style="text-align: center; display: none">' +
318 '<span style="font-size: 2em; padding: 5px; display: block; background-color: var(--mred);">' +
319 '<a href="?cmd=Programs">Frontend</a> not running</span>' +
325 <td id="menuButtons" style="border-radius: 12px;">
326 <button class="dlgButton" id="btnSave" title="Save current setting" onclick="saveFilePicker();">Save</button>
327 <button class="dlgButton" id="btnLoad" title="Load setting from file" style="display: none" onclick="loadFilePicker();">Load</button>
328 <button class="dlgButton" id="btnExport" title="Export current setting" onclick="exportFileQuery();">Export</button>
329 <button class="dlgButton" id="btnImport" title="Import current setting" style="display: none" onclick="importFile();">Import</button>
330 <button class="dlgButton" id="btnODB" title="Go to ODB" style="float: right" onclick="goToOdb();">ODB</button>
336 div.innerHTML = html;
338 mjsonrpc_db_get_value('/Equipment/' + equipment.name).then(
340 let eq = rpc.result.data[0];
342 dlgAlert('Equipment \"' + equipment.name + '\" does not exist in ODB',
343 () => window.location.href = ".");
347 if (eq.variables === null) {
348 dlgAlert('No valid \"/Equipment/' + equipment.name + '/Variables\" in ODB',
349 () => window.location.href = ".");
353 let table = document.getElementById(equipment.tid);
356 let gridDisplay = true;
358 equipment.frontend_name = eq.common["frontend name"];
360 if (document.getElementById('eqSelect'))
361 document.getElementById('eqSelect').innerHTML = equipment.name;
362 if (document.getElementById('tableHeader'))
363 document.getElementById('tableHeader').style.width = "300px";
365 // obtain lengths of elements under /Variables
367 for (const e in eq.variables) {
372 let v = eq.variables[e];
373 let len = v.length ? v.length : 1;
385 // overwrite gridDisplay flag from ODB if present
386 if (eq.settings && eq.settings["grid display"] === undefined) {
387 eq.settings["grid display"] = gridDisplay;
389 let p = "/Equipment/" + equipment.name + "/Settings/Grid display";
390 mjsonrpc_db_create([{"path": p, "type": TID_BOOL}]).then(function (rpc) {
391 mjsonrpc_db_paste([p], [gridDisplay]).then(function (rpc) {
392 }).catch(function (error) {
393 mjsonrpc_error_alert(error);
395 }).catch(function (error) {
396 mjsonrpc_error_alert(error);
401 gridDisplay = eq.settings["grid display"];
408 cpd = eq.settings["channels per device"];
409 if (cpd && cpd > 0) {
411 let tr = table.insertRow();
412 let td = tr.insertCell();
413 td.id = "pageSelector";
414 td.style.padding = "0";
415 td.style.borderBottom = "2px solid #808080";
417 let pageSel = "<table><tr>";
418 pageSel += "<td class=\"pageSelector\"><a href=\"#\" onclick=\"pageSel('All')\">All</a></td>";
419 eq.devNames = eq.settings["device names"];
420 if (eq.devNames !== undefined && !Array.isArray(eq.devNames))
421 eq.devNames = new Array(eq.devNames);
423 if (eq.devNames === undefined) {
425 for (let i = 0; i < nRows / cpd; i++)
426 eq.devNames[i] = String(i);
429 for (let i = 0; i < nRows / cpd; i++) {
430 pageSel += "<td class=\"pageSelector\"><a href=\"#\" onclick=\"pageSel('" + eq.devNames[i] +
431 "')\">" + eq.devNames[i] + "</a></td>";
433 pageSel += "</tr></table>";
434 td.innerHTML = pageSel;
435 equipment.channelsPerDevice = cpd;
438 // extra display at the top
439 if (eq.settings && eq.settings["display extra"]) {
440 let tr = table.insertRow();
441 let td = tr.insertCell();
442 td.id = "displayExtra";
443 td.style.padding = "0";
445 let extraTable = "<table><tr>";
446 let displayExtra = eq.settings["display extra"].split(',').map(item => item.trim());
447 for (const vName of displayExtra) {
448 td.style.height = "22px";
450 let subdir = "Variables";
451 if (eq.variables[vName.toLowerCase()] === undefined)
453 let editable = (eq.settings &&
454 eq.settings.editable && eq.settings.editable.includes(vName));
457 if (eq.variables[vName.toLowerCase()] === undefined) {
458 if (Array.isArray(eq.settings[vName.toLowerCase()]))
459 bool = (typeof eq.settings[vName.toLowerCase()][0] === 'boolean');
461 bool = (typeof eq.settings[vName.toLowerCase()] === 'boolean');
463 if (Array.isArray(eq.variables[vName.toLowerCase()]))
464 bool = (typeof eq.variables[vName.toLowerCase()][0] === 'boolean');
466 bool = (typeof eq.variables[vName.toLowerCase()] === 'boolean');
473 format = eq.settings['format ' + vName.toLowerCase()];
477 unit = eq.settings['unit ' + vName.toLowerCase()];
483 vDiv = "<select class=\"modbselect\" " +
484 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
485 "><option value='false'>No</option>" +
486 "<option value='true'>Yes</option>" +
487 "<option value='...'>...</option>" +
488 "onchange='valueChanged(this);' " +
491 vDiv = "<span class=\"modbvalue\" style=\"border-right: 1px black;\" " +
492 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
493 "data-odb-editable='1' " +
495 "data-format=\"" + format + "\" " +
496 "onchange='valueChanged(this);' " +
499 vDiv = "<span class=\"modbvalue\" " +
500 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
501 "data-odb-editable='1' " +
503 "onchange='valueChanged(this);' " +
508 vDiv = "<span class=\"modbvalue\" " +
509 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
510 "data-format=\"" + format + "\" " +
511 "onchange='valueChanged(this);' " +
514 vDiv = "<span class=\"modbvalue\" " +
515 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
516 "onchange='valueChanged(this);' " +
523 extraTable += "<td>";
524 extraTable += vName + ": " + vDiv + " ";
525 extraTable += "</td>";
528 extraTable += "</tr></table>";
529 td.innerHTML = extraTable;
532 if (eq.settings && eq.settings.names !== undefined && !Array.isArray(eq.settings.names))
533 eq.settings.names = [eq.settings.names];
536 equipment.enabled = eq.settings.enabled;
538 equipment.enabled = true;
540 let groupDisplay = false;
542 if (eq.settings && eq.settings.names !== undefined)
543 groupDisplay = eq.settings.names.every(e => e.includes('%'));
546 let tr = table.insertRow();
547 let td = tr.insertCell();
548 td.id = "groupSelector";
549 let h = "Group: ";
551 eq.settings.names.forEach(n => {
552 let g = n.substring(0, n.indexOf('%'));
553 if (!groupList.includes(g))
557 h += "<select onchange='selectGroup(this)'>";
558 groupList.forEach(g => {
559 h += "<option value=\"" + g + "\">" + g + "</option>";
561 h += "<option value=\"All\">- All -</option>";
565 equipment.group = groupList;
570 // assemble display list
573 if (eq.settings && eq.settings.display) {
574 display = eq.settings.display.split(',').map(item => item.trim());
577 display.push("Names");
578 let v = Object.entries(eq.variables)
579 .filter(([key, value]) => key.includes('/name'))
580 .map(([key, value]) => value);
581 display = display.concat(v);
584 let tr = table.insertRow();
586 for (const title of display) {
587 let th = document.createElement('th');
588 th.innerHTML = (title === "Names" ? "Name" : title);
589 th.className = "mtableheader mvarheader";
592 if (eq.variables[title.toLowerCase()] !== undefined)
593 subdir = "/Variables/";
594 else if (eq.settings && eq.settings[title.toLowerCase()] !== undefined)
595 subdir = "/Settings/";
597 if (eq.settings && eq.settings['unit ' + title.toLowerCase()]) {
605 let editable = (eq.settings &&
606 eq.settings.editable &&
607 eq.settings.editable.toLowerCase().includes(title.toLowerCase()));
609 // enable load/import if we have editable variables
610 if (editable && bButtons) {
611 document.getElementById('btnLoad').style.display = "inline";
612 document.getElementById('btnImport').style.display = "inline";
615 equipment.vars.push({
617 'editable': editable,
622 for (let i = 0; i < nRows; i++) {
623 if (equipment.table.length <= i)
624 equipment.table.push({});
626 equipment.table[i].selected = false;
627 equipment.table[i].lastSelected = false;
629 // create new row, install listener and set style
630 let tr = table.insertRow();
634 tr.classList.add("p" + Math.floor(i / cpd));
636 tr.addEventListener('mousedown', mouseEvent);
637 tr.addEventListener('mousemove', mouseEvent);
638 tr.addEventListener('mouseout', mouseEvent);
640 // catch all mouseup events to stop dragging
641 document.addEventListener('mouseup', mouseEvent);
643 tr.style.userSelect = 'none';
646 let group = eq.settings.names[i];
647 group = group.substring(0, group.indexOf('%'));
648 tr.setAttribute('name', group);
649 if (groupList.length > 0 && group !== groupList[0])
650 tr.style.display = "none";
653 equipment.table[i].tr = tr;
655 // iterate over all variables in equipment
656 for (let v of equipment.vars) {
658 let td = tr.insertCell();
659 td.style.textAlign = "right";
661 if (eq.settings && eq.settings.group)
662 if (eq.settings.group[i])
663 td.style.borderTop = "2px solid #808080";
665 if (v.name === "#") {
666 td.innerHTML = i.toString();
667 td.style.width = "10px";
668 } else if (v.name === "Names") {
669 td.style.textAlign = "center";
670 if (eq.settings && eq.settings.names) {
672 td.innerHTML = eq.settings.names[i].substring(eq.settings.names[i].indexOf('%') + 1);
674 td.innerHTML = eq.settings.names[i];
677 let format = eq.settings && eq.settings['format ' + v.name.toLowerCase()];
678 if (format === undefined || format === null)
679 format = "%f2"; // default format
680 else if (Array.isArray(format))
686 if (v.subdir.includes("Variables")) {
687 b = typeof eq.variables[v.name.toLowerCase()] === 'boolean' ||
688 (typeof eq.variables[v.name.toLowerCase()] === 'object' &&
689 typeof eq.variables[v.name.toLowerCase()][0] === 'boolean');
690 } else if (v.subdir.includes("Settings")) {
691 b = typeof eq.settings[v.name.toLowerCase()] === 'boolean' ||
692 (typeof eq.settings[v.name.toLowerCase()] === 'object' &&
693 typeof eq.settings[v.name.toLowerCase()][0] === 'boolean');
697 td.innerHTML = "<select class=\"modbselect\" " +
698 "data-odb-path=\"/Equipment/" + equipment.name + v.subdir + v.name + "[" + i + "]\" " +
699 "data-validate='validateData' " +
700 "><option value='false'>No</option>" +
701 "<option value='true'>Yes</option>" +
704 td.style.width = "90px";
705 td.style.height = "22px";
706 td.innerHTML = "<div class=\"modbvalue\" " +
707 "data-odb-path=\"/Equipment/" + equipment.name + v.subdir + v.name + "[" + i + "]\" " +
708 "data-odb-editable='1' " +
710 "data-format=\"" + format + "\" " +
711 "data-validate='validateData' " +
712 "onchange='valueChanged(this);' " +
716 td.innerHTML = "<div class=\"modbvalue\" " +
717 "data-odb-path=\"/Equipment/" + equipment.name + v.subdir + v.name + "[" + i + "]\" " +
718 "data-format=\"" + format + "\" " +
719 "onchange='valueChanged(this);' " +
723 if (eq.settings && eq.settings['unit ' + v.name.toLowerCase()]) {
725 let unit = eq.settings['unit ' + v.name.toLowerCase()];
726 if (Array.isArray(unit))
729 td = tr.insertCell();
730 td.style.width = "10px";
732 if (eq.settings && eq.settings.group)
733 if (eq.settings.group[i])
734 td.style.borderTop = "2px solid #808080";
738 td.style.textAlign = 'center';
743 document.getElementById(equipment.tid).style.visibility = '';
745 if (cpd && cpd > 0) {
746 enable_row(tr, eq.settings.enabled, Math.floor(i / cpd));
748 if (eq.settings === undefined)
749 enable_row(tr, true, i);
751 if (Array.isArray(eq.settings.enabled))
752 enable_row(tr, eq.settings.enabled[i], i);
754 enable_row(tr, eq.settings.enabled, i);
760 pageSel(eq.devNames[0]);
762 } else { // ------------ non-grid display
766 for (const e in eq.variables) {
767 if (!e.includes('/name'))
770 let vName = eq.variables[e];
771 let vArr = eq.variables[vName.toLowerCase()];
773 if (eq.settings && eq.settings.editable) {
774 if (Array.isArray(eq.settings.editable))
775 editable = eq.settings.editable;
777 editable = eq.settings.editable.toLowerCase().includes(vName.toLowerCase());
780 // skip subdirectories
781 if (typeof vArr === 'object' && !Array.isArray(vArr))
785 if (Array.isArray(editable))
786 flag = editable.includes(true);
788 equipment.vars.push({
793 // enable load/import if we have editable variables
794 if (flag && bButtons) {
795 document.getElementById('btnLoad').style.display = "inline";
796 document.getElementById('btnImport').style.display = "inline";
799 let tr = table.insertRow();
802 let th = document.createElement('th');
803 th.className = "mtableheader mvarheader";
809 if (eq.settings && eq.settings['names ' + vName.toLowerCase()] !== undefined) {
810 let th = document.createElement('th');
811 th.className = "mtableheader mvarheader";
812 th.innerHTML = "Name";
817 th = document.createElement('th');
818 th.innerHTML = vName;
819 th.className = "mtableheader mvarheader";
825 if (!Array.isArray(vArr))
827 for (let i = 0; i < vArr.length; i++) {
829 equipment.table.push({});
831 equipment.table[n].selected = false;
832 equipment.table[n].lastSelected = false;
834 // create new row, install listener and set style
835 let tr = table.insertRow();
838 tr.addEventListener('mousedown', mouseEvent);
839 tr.addEventListener('mousemove', mouseEvent);
840 tr.addEventListener('mouseout', mouseEvent);
842 // catch all mouseup events to stop dragging
843 document.addEventListener('mouseup', mouseEvent);
845 tr.style.userSelect = 'none';
848 let group = eq.settings.names[i];
849 group = group.substring(0, group.indexOf('%'));
850 tr.setAttribute('name', group);
851 if (groupList.length > 0 && group !== groupList[0])
852 tr.style.display = "none";
855 equipment.table[n].tr = tr;
858 if (eq.settings && eq.settings['names ' + vName.toLowerCase()] !== undefined) {
859 td = tr.insertCell();
860 if (eq.settings && eq.settings.group)
861 if (eq.settings.group[i])
862 td.style.borderTop = "2px solid #808080";
866 td = tr.insertCell();
867 if (eq.settings && eq.settings.group)
868 if (eq.settings.group[i])
869 td.style.borderTop = "2px solid #808080";
871 if (Array.isArray(eq.settings['names ' + vName.toLowerCase()])) {
872 td.innerHTML = eq.settings['names ' + vName.toLowerCase()][i]
874 td.innerHTML = eq.settings['names ' + vName.toLowerCase()];
877 td = tr.insertCell();
879 if (eq.settings && eq.settings.group)
880 if (eq.settings.group[i])
881 td.style.borderTop = "2px solid #808080";
883 td.innerHTML = vName + '[' + i + ']';
888 if (eq.settings && Array.isArray(eq.settings['unit ' + vName.toLowerCase()]))
889 unit = eq.settings['unit ' + vName.toLowerCase()];
890 else if (eq.settings && eq.settings['unit ' + vName.toLowerCase()])
891 unit = new Array(vArr.length).fill(eq.settings['unit ' + vName.toLowerCase()]);
894 td = tr.insertCell();
896 if (eq.settings && eq.settings.group)
897 if (eq.settings.group[i])
898 td.style.borderTop = "2px solid #808080";
902 format = eq.settings['format ' + vName.toLowerCase()];
903 if (format === undefined || format === null)
904 format = new Array(vArr.length).fill("%f2"); // default format
907 if (Array.isArray(flag))
911 td.style.width = "90px";
912 td.style.height = "22px";
913 td.style.textAlign = "right";
914 td.innerHTML = "<div class=\"modbvalue\" " +
915 "data-odb-path=\"/Equipment/" + equipment.name + "/Variables/" + vName + "[" + i + "]\" " +
916 "data-odb-editable='1' " +
918 "data-format=\"" + format[i] + "\" " +
919 "onchange='valueChanged(this);' " +
922 td.style.textAlign = "right";
923 td.innerHTML = "<div class=\"modbvalue\" " +
924 "data-odb-path=\"/Equipment/" + equipment.name + "/Variables/" + vName + "[" + i + "]\" " +
925 "data-format=\"" + format[i] + "\" " +
926 "onchange='valueChanged(this);' " +
930 td = tr.insertCell();
931 td.style.width = "10px";
933 if (eq.settings && eq.settings.group)
934 if (eq.settings.group[i])
935 td.style.borderTop = "2px solid #808080";
938 td.innerHTML = unit[i];
946 document.getElementById(equipment.tid).style.visibility = '';
949 if (document.getElementById('fewarning'))
950 document.getElementById('fewarning').colSpan = columns;
951 if (document.getElementById('tableHeader'))
952 document.getElementById('tableHeader').colSpan = columns;
953 if (document.getElementById('menuButtons'))
954 document.getElementById('menuButtons').colSpan = columns;
956 document.getElementById('groupSelector').colSpan = columns;
957 if (document.getElementById('displayExtra'))
958 document.getElementById('displayExtra').colSpan = columns;
959 if (document.getElementById('pageSelector'))
960 document.getElementById('pageSelector').colSpan = columns;
962 // populate equipment table
964 mjsonrpc_db_ls(["/Equipment"]).then(
966 let eqList = rpc.result.data[0];
967 let sel = document.getElementById('eqSelect');
969 let o = document.createElement('option');
973 sel.value = equipment.name;
977 let d = document.getElementById(equipment.tid);
979 d = d.parentNode.parentNode.parentNode;
980 if (d.tagName === 'DIV')
986 document.addEventListener('mousedown', removeSelection);
991let allowIncDec = false;
993function saveFilePicker() {
994 file_picker("equipment", "*.json", saveFile, true, equipment.name, true);
997function saveFile(filename) {
998 mjsonrpc_db_copy(['/Equipment/' + equipment.name + '/Variables']).then(rpc => {
1000 if (filename.indexOf('.json') === -1)
1001 filename += '.json';
1004 "/MIDAS version": "2.1",
1005 "/filename": filename,
1006 "/ODB path": "/Equipment/" + equipment.name + "/Variables"
1008 header = JSON.stringify(header, null, ' ');
1009 header = header.substring(0, header.length-2) + ','; // strip trailing '}'
1011 let odbJson = JSON.stringify(rpc.result.data[0], null, ' ');
1012 if (odbJson.indexOf('{') === 0)
1013 odbJson = odbJson.substring(1); // strip leading '{'
1015 odbJson = header + odbJson;
1017 file_save_ascii(filename, odbJson, "Equipment \"" + equipment.name +
1018 "\" saved to file \"" + filename + "\"");
1020 }).catch(error => mjsonrpc_error_alert(error));
1023function exportFileQuery() {
1024 dlgQuery("Enter filename: ", "default.json", exportFile);
1027function exportFile(filename) {
1028 if (filename === false)
1030 mjsonrpc_db_copy(['/Equipment/' + equipment.name + '/Variables']).then(rpc => {
1032 if (filename.indexOf('.json') === -1)
1033 filename += '.json';
1036 "/MIDAS version": "2.1",
1037 "/filename": filename,
1038 "/ODB path": "/Equipment/" + equipment.name + "/Variables"
1040 header = JSON.stringify(header, null, ' ');
1041 header = header.substring(0, header.length-2) + ','; // strip trailing '}'
1043 let odbJson = JSON.stringify(rpc.result.data[0], null, ' ');
1044 if (odbJson.indexOf('{') === 0)
1045 odbJson = odbJson.substring(1); // strip leading '{'
1047 odbJson = header + odbJson;
1049 // use trick from FileSaver.js
1050 let a = document.getElementById('downloadHook');
1052 a = document.createElement("a");
1053 a.style.display = "none";
1054 a.id = "downloadHook";
1055 document.body.appendChild(a);
1058 let blob = new Blob([odbJson], {type: "text/json"});
1059 let url = window.URL.createObjectURL(blob);
1062 a.download = filename;
1064 window.URL.revokeObjectURL(url);
1065 dlgAlert("Equipment \"" + equipment.name +
1066 "\" downloaded to file \"" + filename + "\"");
1068 }).catch(error => mjsonrpc_error_alert(error));
1071function loadFilePicker() {
1072 file_picker("equipment", "*.json", loadFile);
1075function loadFile(filename) {
1076 file_load_ascii(filename, (text) => fileInterprete(filename, text) );
1079async function importFile() {
1080 // Chrome has file picker, others might have not
1083 [fileHandle] = await window.showOpenFilePicker();
1084 const file = await fileHandle.getFile();
1085 const text = await file.text();
1086 fileInterprete(file.name, text);
1088 if (error.name !== 'AbortError') {
1089 // fall-back to old method
1090 if (document.getElementById('dlgFileSelect') === null) {
1091 let fsDiv = document.createElement('div');
1092 fsDiv.id = "dlgFileSelect";
1093 fsDiv.className = "dlgFrame";
1094 fsDiv.innerHTML = dlgFileSelect;
1095 document.body.appendChild(fsDiv);
1097 dlgShow('dlgFileSelect', true);
1102function importFileFromSelector() {
1104 let input = document.getElementById('fileSelector');
1105 let file = input.files[0];
1106 if (file !== undefined) {
1107 let reader = new FileReader();
1108 reader.readAsText(file);
1110 reader.onerror = function () {
1111 dlgAlert('File read error: ' + reader.error);
1114 reader.onload = function () {
1115 fileInterprete(file.name, reader.result);
1120function fileInterprete(filename, text) {
1122 let j = JSON.parse(text);
1124 // check for correct equipment
1125 let path = j["/ODB path"].split('/');
1126 if (path[2] !== equipment.name) {
1127 dlgAlert("File contains settings for equipment \"" + path[2] + "\"<br />"+
1128 "and therefore cannot be loaded into equipment \"" + equipment.name +"\"");
1132 mhttpd_refresh_pause(true);
1134 for (let v of equipment.vars)
1136 modbset("/Equipment/" + equipment.name + "/Variables/" + v.name, j[v.name]);
1138 mhttpd_refresh_pause(false);
1140 window.setTimeout(mhttpd_refresh, 10);
1142 mjsonrpc_cm_msg("Values loaded from file \"" + filename + "\"");
1144 dlgAlert("File \"" + filename + "\" is not a valid JSON file:<br /><br />" + error);
1148function renderSelection() {
1149 // change color according to selection
1150 for (let i=0 ; i<equipment.table.length ; i++) {
1151 if (equipment.table[i].selected) {
1152 equipment.table[i].tr.style.backgroundColor = '#004CBD';
1153 equipment.table[i].tr.style.color = '#FFFFFF';
1156 if (equipment.table[i].tr.getElementsByTagName('a').length > 0)
1157 equipment.table[i].tr.getElementsByTagName('a')[0].style.color = '#FFFFFF';
1162 if (equipment.channelsPerDevice)
1163 index = Math.floor(i / equipment.channelsPerDevice);
1164 enable_row(equipment.table[i].tr, equipment.enabled, index);
1166 equipment.table[i].tr.style.color = ''; // text is black again
1169 let a = equipment.table[i].tr.getElementsByTagName('a');
1178let mouseDragged = false;
1180// function gets called from document
1181function removeSelection(e) {
1182 // don't de-select if we click on "->" or on drop-down box
1183 if (e.target.tagName === 'BUTTON' || e.target.tagName === 'SELECT')
1186 for (let i = 0; i < equipment.table.length; i++) {
1187 equipment.table[i].selected = false;
1188 equipment.table[i].lastSelected = false;
1193function getSelectedElements() {
1195 for (let i = 0; i < equipment.table.length; i++)
1196 if (equipment.table[i].selected)
1202function mouseEvent(e) {
1204 if (e.type === 'mouseup')
1205 mouseDragged = false;
1207 // keep off secondary mouse buttons
1211 // keep off drop down boxes
1212 if (e.target.tagName === 'SELECT')
1216 e.stopPropagation();
1220 for (index = 0; index < equipment.table.length; index++)
1221 if (equipment.table[index].tr === e.target ||
1222 equipment.table[index].tr.contains(e.target))
1224 if (index === equipment.table.length)
1227 // ignore clicks to <button>'a and <a>'s
1228 if (e.target.tagName === 'A' || e.target.tagName === 'BUTTON' || e.target.tagName === 'IMG')
1231 if (e.type === 'mousedown') {
1232 mouseDragged = true;
1236 // search last selected element
1238 for (i1 = 0 ; i1 < equipment.table.length ; i1++)
1239 if (equipment.table[i1].lastSelected)
1241 if (i1 === equipment.table.length)
1242 i1 = 0; // none selected, so use first one
1245 [i1, index] = [index, i1];
1247 for (let i=i1 ; i<= index ; i++)
1248 if (!equipment.table[i].tr.disabled)
1249 equipment.table[i].selected = true;
1251 } else if (e.metaKey || e.ctrlKey) {
1253 console.log("index = " + index);
1255 // just toggle current selection
1256 if (!equipment.table[index].tr.disabled)
1257 equipment.table[index].selected = !equipment.table[index].selected;
1259 // remember which row was last selected
1260 for (let i=0 ; i<equipment.table.length ; i++)
1261 equipment.table[i].lastSelected = false;
1262 if (equipment.table[index].selected)
1263 equipment.table[index].lastSelected = true;
1266 // no key pressed -> un-select all but current
1267 for (let i = 0; i < equipment.table.length; i++) {
1268 equipment.table[i].selected = false;
1269 equipment.table[i].lastSelected = false;
1272 if (!equipment.table[index].tr.disabled) {
1273 equipment.table[index].selected = true;
1274 equipment.table[index].lastSelected = true;
1281 if (e.type === 'mousemove' && mouseDragged) {
1283 // don't do dragging if shift or ctrl key pressed
1284 if (!e.shiftKey && !e.metaKey && !e.ctrlKey) {
1286 for (let i = 0; i < equipment.table.length; i++)
1287 equipment.table[i].selected = false;
1291 for (i2 = 0; i2 < equipment.table.length; i2++)
1292 if (equipment.table[i2].tr === e.target ||
1293 equipment.table[i2].tr.contains(e.target))
1296 if (i2 < equipment.table.length) {
1297 // search last selected element
1299 for (i1 = 0; i1 < equipment.table.length; i1++)
1300 if (equipment.table[i1].lastSelected)
1302 if (i1 === equipment.table.length)
1303 i1 = 0; // none selected, so use first one
1306 [i1, i2] = [i2, i1];
1308 for (let i = i1; i <= i2; i++)
1309 if (!equipment.table[i].tr.disabled)
1310 equipment.table[i].selected = true;
1318function dlgEquipment(eqName, bButtons = false, bEqSelect = false) {
1320 // check if equipment exists in ODB
1321 mjsonrpc_db_ls(['/Equipment/' + eqName]).then(
1323 let eq = rpc.result.data[0];
1325 dlgAlert('dlgEquipment: Equipment \"' + eqName + '\" does not exist in ODB');
1329 if (eq.variables === null) {
1330 dlgAlert('dlgEquipment: No valid \"/Equipment/' + eqName + '/Variables\" in ODB');
1334 let d = document.getElementById('dlgEqTitle');
1336 // If dialog is already there, reuse it.
1338 d = document.createElement("div");
1339 d.className = "dlgFrame";
1340 d.style.zIndex = "30";
1341 d.shouldDestroy = true;
1343 d.innerHTML = "<div class=\"dlgTitlebar\" id=\"dlgEqTitle\">" + eqName + "</div>" +
1344 "<div class=\"dlgPanel\" style=\"padding: 4px;\">" + "<div id=\"dlgEquipment\">" + "</div></div>";
1345 document.body.appendChild(d);
1349 eqtable_init(document.getElementById('dlgEquipment'), eqName, bButtons, bEqSelect);
1352 let eqSelect = document.getElementById('eqSelect');
1353 eqSelect.onchange = (event) => {
1354 redirectEq(document.getElementById('dlgEquipment'), event.target.value);