627 lines
19 KiB
JavaScript
627 lines
19 KiB
JavaScript
/**
|
||
* SOES EEPROM generator
|
||
* UI behavior logic
|
||
|
||
* This tool serves as:
|
||
- EtherCAT Slave Information XML + EEPROM binary generator
|
||
- SOES code generator
|
||
|
||
* Victor Sluiter 2013-2018
|
||
* Kuba Buda 2020-2021
|
||
*/
|
||
'use strict'
|
||
|
||
// ####################### UI changes handlers ####################### //
|
||
|
||
function getForm() {
|
||
return document.getElementById("SlaveForm");
|
||
}
|
||
|
||
function getOutputForm() {
|
||
return document.getElementById("outCodeForm");
|
||
}
|
||
|
||
function onFormChanged() {
|
||
const form = getForm();
|
||
saveLocalBackup(form);
|
||
processForm(form);
|
||
}
|
||
|
||
/** Shortcuts:
|
||
* Ctrl + S to save project
|
||
* Ctrl + O to load save file
|
||
* Shortcuts start to work after user clicked on page
|
||
*/
|
||
document.onkeydown = function(e) {
|
||
const S_keyCode = 83;
|
||
const O_keyCode = 79;
|
||
if (e.ctrlKey){
|
||
if (e.keyCode === S_keyCode) {
|
||
event.preventDefault();
|
||
onSaveClick();
|
||
return false;
|
||
}
|
||
else if (e.keyCode == O_keyCode) {
|
||
event.preventDefault();
|
||
onRestoreClick();
|
||
return false;
|
||
}
|
||
}
|
||
};
|
||
|
||
// When the user clicks anywhere outside of the modal, close it
|
||
window.onclick = function(event) {
|
||
if (event.target == odModal) {
|
||
odModalClose();
|
||
}
|
||
}
|
||
|
||
window.onload = (event) => {
|
||
odModalSetup();
|
||
syncModalSetup();
|
||
const form = getForm();
|
||
setFormValues(form, getFormDefaultValues());
|
||
tryRestoreLocalBackup(form);
|
||
// for convinience during tool development, trigger codegen on page refresh
|
||
processForm(form);
|
||
|
||
const _isComputerFast = automaticCodegen;
|
||
|
||
if (_isComputerFast) {
|
||
// code is regenerated on every form change.
|
||
// no need to remember to generate before copying or downloading
|
||
// app is noticeably slower
|
||
|
||
processForm(form); // make sure displayed code is up to date at startup, e.g redo, if it came from backup
|
||
|
||
document.getElementById('GenerateFilesButton').style.display = 'none'; // 'generate' button no longer needed
|
||
form.addEventListener('change', function() {
|
||
onFormChanged();
|
||
});
|
||
}
|
||
setupDarkMode();
|
||
}
|
||
|
||
|
||
// ####################### dark mode logic ####################### //
|
||
function setupDarkMode() {
|
||
if (!localStorage.darkMode) {
|
||
localStorage.darkMode = 'dark'; // dark mode by default
|
||
}
|
||
document.documentElement.setAttribute("data-theme", localStorage.darkMode);
|
||
}
|
||
|
||
function toggleDarkMode() {
|
||
var newMode = (localStorage.darkMode == 'dark') ? "light" : "dark"
|
||
localStorage.darkMode = newMode;
|
||
document.documentElement.setAttribute("data-theme", localStorage.darkMode);
|
||
}
|
||
|
||
// ####################### code generation UI logic ####################### //
|
||
|
||
/** Code generation method, triggered by UI */
|
||
function processForm(form)
|
||
{
|
||
const od = buildObjectDictionary(form);
|
||
const indexes = getUsedIndexes(od);
|
||
var outputCtl = getOutputForm();
|
||
|
||
outputCtl.objectlist.value = objectlist_generator(form, od, indexes);
|
||
outputCtl.ecat_options.value = ecat_options_generator(form, od, indexes);
|
||
outputCtl.utypes.value = utypes_generator(form, od, indexes);
|
||
outputCtl.HEX.hexData = hex_generator(form);
|
||
outputCtl.HEX.value = toIntelHex(outputCtl.HEX.hexData);
|
||
outputCtl.ESI.value = esi_generator(form, od, indexes, _dc);
|
||
|
||
saveLocalBackup(form);
|
||
|
||
return outputCtl;
|
||
}
|
||
|
||
// ####################### Button handlers ####################### //
|
||
function getProjectName(form) {
|
||
return variableName(form.TextDeviceName.value);
|
||
}
|
||
|
||
function onGenerateDownloadClick()
|
||
{
|
||
const form = getForm();
|
||
var result = processForm(form);
|
||
downloadGeneratedFilesZipped(form, result);
|
||
|
||
function downloadGeneratedFilesZipped(form, result) {
|
||
var zip = new JSZip();
|
||
const projectName = getProjectName(form);
|
||
zip.file(`${projectName}.xml`, result.ESI.value);
|
||
zip.file('eeprom.hex', result.HEX.value);
|
||
zip.file('eeprom.bin', result.HEX.hexData);
|
||
zip.file('ecat_options.h', result.ecat_options.value);
|
||
zip.file('objectlist.c', result.objectlist.value);
|
||
zip.file('utypes.h', result.utypes.value);
|
||
zip.file('esi.json', prepareBackupFileContent(form));
|
||
|
||
zip.generateAsync({type:"blob"}).then(function (blob) { // generate the zip file
|
||
downloadFile(blob, "esi.zip", "application/zip"); // trigger the download
|
||
}, function (err) {
|
||
console.log(err);
|
||
});
|
||
}
|
||
|
||
function downloadGeneratedFiles(form, result) {
|
||
const projectName = getProjectName(form);
|
||
downloadFile(result.ESI.value, `${projectName}.xml`, 'text/html');
|
||
downloadFile(result.HEX.value, 'eeprom.hex', 'application/octet-stream');
|
||
downloadFile(result.ecat_options.value, 'ecat_options.h', 'text/plain');
|
||
downloadFile(result.objectlist.value, 'objectlist.c', 'text/plain');
|
||
downloadFile(result.utypes.value, 'utypes.h', 'text/plain');
|
||
downloadBackupFile(form);
|
||
|
||
}
|
||
}
|
||
|
||
function onGenerateClick() {
|
||
processForm(getForm());
|
||
}
|
||
|
||
function onSaveClick() {
|
||
const form = getForm();
|
||
downloadBackupFile(form);
|
||
saveLocalBackup(form);
|
||
}
|
||
|
||
function onRestoreClick() {
|
||
// trigger file input dialog window
|
||
document.getElementById('restoreFileInput').click();
|
||
}
|
||
|
||
function onRestoreComplete(fileContent) {
|
||
const form = getForm();
|
||
restoreBackup(fileContent, form);
|
||
processForm(form);
|
||
}
|
||
|
||
function onResetClick() {
|
||
if (confirm("Are you sure you want to reset project to default values?")){
|
||
resetLocalBackup();
|
||
location.reload(true);
|
||
}
|
||
}
|
||
|
||
function onDownloadEsiXmlClick() {
|
||
const form = getForm();
|
||
const projectName = getProjectName(form);
|
||
downloadFile(getOutputForm().ESI.value, `${projectName}.xml`, 'text/html');
|
||
}
|
||
|
||
function onDownloadBinClick() {
|
||
const record = getOutputForm().HEX.hexData;
|
||
if (!record) { alert("Generate code before you can download it"); return; }
|
||
downloadFile(record, 'eeprom.bin', 'application/octet-stream');
|
||
}
|
||
|
||
// ####################### Handle modal dialog ####################### //
|
||
|
||
var odModal = {};
|
||
|
||
function odModalSetup() {
|
||
// Get the modal
|
||
odModal = document.getElementById("editObjectModal");
|
||
if (odModal) {
|
||
odModal.form = document.getElementById('EditObjectForm');
|
||
}
|
||
else {
|
||
alert("Element required to edit Object Dictionary not found!");
|
||
}
|
||
}
|
||
|
||
// When the user clicks the button, open the modal
|
||
function odModalOpen() {
|
||
odModal.style.display = "block";
|
||
}
|
||
|
||
function odModalClose() {
|
||
odModal.style.display = "none";
|
||
}
|
||
|
||
/** update control values on OD modal */
|
||
function odModalUpdate(index, objd) {
|
||
odModal.form.Index.value = `0x${index}`;
|
||
odModal.form.ObjectName.value = objd.name;
|
||
odModal.form.DTYPE.value = objd.dtype || DTYPE.UNSIGNED8;
|
||
odModal.form.Access.value = objd.access || 'RO';
|
||
odModal.objd = objd;
|
||
}
|
||
|
||
function odModalHideControls() {
|
||
document.getElementById('dialogRowIndex').style.display = 'none';
|
||
document.getElementById('dialogRowDtype').style.display = 'none';
|
||
document.getElementById('dialogRowValue').style.display = 'none';
|
||
document.getElementById('dialogRowAccess').style.display = 'none';
|
||
}
|
||
|
||
// ####################### Modal dialogs for OD edition ####################### //
|
||
|
||
function editExistingOD_ObjectDialog(odSectionName, index, otype) {
|
||
const od = getObjDictSection(odSectionName);
|
||
var objd = od[index];
|
||
odModal.index_initial_value = index;
|
||
checkObjectType(otype, objd);
|
||
odModalUpdate(index, objd);
|
||
}
|
||
|
||
function addNewOD_ObjectDialog(odSectionName, otype) {
|
||
var objd = getNewObjd(odSectionName, otype);
|
||
var index = getFirstFreeIndex(odSectionName);
|
||
delete odModal.index_initial_value; // add new object, not replace edited one
|
||
odModalUpdate(index, objd);
|
||
}
|
||
|
||
function odModalOpenForObject(otype) {
|
||
odModalHideControls();
|
||
document.getElementById('dialogRowIndex').style.display = '';
|
||
document.getElementById('dialogRowAccess').style.display = '';
|
||
|
||
switch (otype) {
|
||
case OTYPE.VAR: {
|
||
document.getElementById('dialogRowDtype').style.display = '';
|
||
document.getElementById('dialogRowValue').style.display = '';
|
||
break;
|
||
}
|
||
case OTYPE.ARRAY: {
|
||
document.getElementById('dialogRowDtype').style.display = "";
|
||
break;
|
||
}
|
||
case OTYPE.RECORD: {
|
||
break;
|
||
}
|
||
default: {
|
||
alert(`Unknown object type ${otype}, cannot open modal for it!`);
|
||
return;
|
||
}
|
||
}
|
||
odModalOpen();
|
||
document.getElementById('modalInputIndex').focus();
|
||
}
|
||
|
||
function odModalSetTitle(message) {
|
||
document.getElementById('editObjectTitle').innerHTML = `<strong>${message}</strong>`;
|
||
}
|
||
|
||
// ####################### Edit Object Dictionary UI logic ####################### //
|
||
|
||
function editVAR_Click(odSectionName, indexValue = null) {
|
||
const otype = OTYPE.VAR;
|
||
const index = indexToString(indexValue);
|
||
var actionName = "Edit";
|
||
odModal.odSectionName = odSectionName;
|
||
|
||
if (objectExists(odSectionName, index)) {
|
||
editExistingOD_ObjectDialog(odSectionName, index, otype);
|
||
odModal.form.DTYPE.value = odModal.objd.dtype;
|
||
} else {
|
||
addNewOD_ObjectDialog(odSectionName, otype);
|
||
actionName = "Add"
|
||
}
|
||
odModalSetTitle(`${actionName} ${odSectionName.toUpperCase()} variable`);
|
||
odModalOpenForObject(otype);
|
||
}
|
||
|
||
function editARRAY_Click(odSectionName, indexValue = null) {
|
||
const otype = OTYPE.ARRAY;
|
||
const index = indexToString(indexValue);
|
||
var actionName = "Edit";
|
||
odModal.odSectionName = odSectionName;
|
||
odModal.form.Access
|
||
|
||
if (objectExists(odSectionName, index)) {
|
||
editExistingOD_ObjectDialog(odSectionName, index, otype);
|
||
odModal.form.DTYPE.value = odModal.objd.dtype;
|
||
} else {
|
||
addNewOD_ObjectDialog(odSectionName, otype);
|
||
actionName = "Add"
|
||
}
|
||
odModalSetTitle(`${actionName} ${odSectionName.toUpperCase()} array`);
|
||
odModalOpenForObject(otype);
|
||
}
|
||
|
||
function editRECORD_Click(odSectionName, indexValue = null) {
|
||
const otype = OTYPE.RECORD;
|
||
const index = indexToString(indexValue);
|
||
var actionName = "Edit";
|
||
odModal.odSectionName = odSectionName;
|
||
|
||
if (objectExists(odSectionName, index)) {
|
||
editExistingOD_ObjectDialog(odSectionName, index, otype);
|
||
} else {
|
||
addNewOD_ObjectDialog(odSectionName, otype);
|
||
actionName = "Add"
|
||
}
|
||
odModalSetTitle(`${actionName} ${odSectionName.toUpperCase()} record`);
|
||
odModalOpenForObject(otype);
|
||
}
|
||
|
||
function onEditObjectSubmit(modalform) {
|
||
if (odModal.subitem) {
|
||
onEditSubitemSubmit(odModal.subitem);
|
||
delete odModal.subitem;
|
||
return;
|
||
}
|
||
const objd = odModal.objd;
|
||
const objectType = objd.otype;
|
||
const index = indexToString(modalform.Index.value);
|
||
|
||
objd.name = modalform.ObjectName.value;
|
||
|
||
switch (objectType) {
|
||
case OTYPE.VAR:
|
||
objd.dtype = modalform.DTYPE.value;
|
||
|
||
if (objd.dtype == DTYPE.VISIBLE_STRING) {
|
||
objd.data = ''
|
||
} else {
|
||
objd.value = modalform.InitalValue.value;
|
||
}
|
||
break;
|
||
case OTYPE.ARRAY:
|
||
objd.dtype = modalform.DTYPE.value;
|
||
|
||
break;
|
||
case OTYPE.RECORD:
|
||
|
||
break;
|
||
default:
|
||
alert(`Unexpected type ${objectType} on object ${modalform.ObjectName} being edited!`);
|
||
break;
|
||
}
|
||
const odSection = getObjDictSection(odModal.odSectionName);
|
||
if (odModal.index_initial_value) {
|
||
removeObject(odSection, odModal.index_initial_value); // detach from OD, to avoid duplicate if index changed
|
||
}
|
||
addObject(odSection, objd, index); // attach updated object
|
||
odModalClose();
|
||
reloadOD_Section(odModal.odSectionName);
|
||
delete odModal.odSectionName;
|
||
odModal.objd = {};
|
||
|
||
onFormChanged();
|
||
}
|
||
|
||
function onRemoveClick(odSectionName, indexValue, subindex = null) {
|
||
const index = indexToString(indexValue);
|
||
const odSection = getObjDictSection(odSectionName);
|
||
const objd = odSection[index];
|
||
if(!objd) { alert(`${odSectionName.toUpperCase()} object ${index} does not exist!`); return; }
|
||
|
||
if(subindex) {
|
||
if(!objd.items) { alert(`Object 0x${index} "${objd.name}" does not have any items!`); return; }
|
||
if(objd.items.length < subindex) { alert(`Object 0x${index} "${objd.name}" does not have enough items!`); return; }
|
||
if(objd.items.length < 3) { // only max subindex and one more subitem
|
||
alert(`Object 0x${index} "${objd.name}" has only 1 subitem, it should not be empty. Remove entire object instead.`); return; }
|
||
}
|
||
|
||
if (confirm(getConfirmMessage(objd, index, subindex))) {
|
||
|
||
if (subindex) {
|
||
const subitemsToRemove = 1;
|
||
objd.items.splice(subindex, subitemsToRemove);
|
||
} else {
|
||
removeObject(odSection, index);
|
||
}
|
||
reloadOD_Section(odSectionName);
|
||
onFormChanged();
|
||
}
|
||
|
||
function getConfirmMessage(objd, index, subindex) {
|
||
if (subindex) {
|
||
return `Are you sure you want to remove subitem ${subindex} "${objd.items[subindex].name }" from object 0x${index} "${objd.name}"?`
|
||
}
|
||
return `Are you sure you want to remove object 0x${index} "${objd.name}"?`
|
||
}
|
||
}
|
||
|
||
function addSubitemClick(odSectionName, indexValue) {
|
||
const index = indexToString(indexValue);
|
||
const odSection = getObjDictSection(odSectionName);
|
||
const objd = odSection[index];
|
||
|
||
// we expect objd to have items array with at least [{ name: 'Max SubIndex' }]
|
||
if(!objd.items || !objd.items.length ) { alert(`Object ${index} "${objd.name}" has no subitems!`); return; }
|
||
|
||
switch(objd.otype) {
|
||
case OTYPE.ARRAY: {
|
||
addArraySubitem(objd);
|
||
break;
|
||
}
|
||
case OTYPE.RECORD: {
|
||
addRecordSubitem(objd);
|
||
break;
|
||
}
|
||
default: {
|
||
alert(`Object ${index} "${objd.name}" has OTYPE ${objd.otype} so cannot add subitems`);
|
||
}
|
||
}
|
||
const subindex = objd.items.length - 1; // subitem is added to end of items list
|
||
editSubitemClick(odSectionName, indexValue, subindex, "Add");
|
||
}
|
||
|
||
function editSubitemClick(odSectionName, indexValue, subindex, actionName = "Edit") {
|
||
const index = indexToString(indexValue);
|
||
const odSection = getObjDictSection(odSectionName);
|
||
const objd = odSection[index];
|
||
|
||
if(!objd.items || objd.items.length <= subindex ) { alert(`Object ${index} "${objd.name}" does not have ${subindex} subitems!`); return; }
|
||
|
||
odModalSetTitle(`${actionName} ${odSectionName.toUpperCase()} object 0x${index} "${objd.name}" subitem 0x${indexToString(subindex)}`);
|
||
|
||
const subitem = objd.items[subindex];
|
||
|
||
odModalHideControls();
|
||
|
||
document.getElementById('dialogRowValue').style.display = "";
|
||
odModal.form.InitalValue.value = subitem.value ?? 0;
|
||
|
||
if (objd.otype == OTYPE.RECORD) {
|
||
document.getElementById('dialogRowDtype').style.display = "";
|
||
odModal.form.DTYPE.value = subitem.dtype;
|
||
document.getElementById('dialogRowAccess').style.display = ''; // access for record subitems can differ
|
||
odModal.form.Access.value = subitem.access || 'RO';
|
||
}
|
||
odModal.form.ObjectName.value = subitem.name;
|
||
odModal.subitem = { odSectionName: odSectionName, index: index, subindex: subindex };
|
||
odModalOpen();
|
||
document.getElementById('modalInputObjectName').focus();
|
||
}
|
||
|
||
function onEditSubitemSubmit(modalSubitem) {
|
||
const odSection = getObjDictSection(modalSubitem.odSectionName);
|
||
const objd = odSection[modalSubitem.index];
|
||
const subitem = objd.items[modalSubitem.subindex];
|
||
|
||
subitem.name = odModal.form.ObjectName.value;
|
||
subitem.value = odModal.form.InitalValue.value;
|
||
if (objd.otype == OTYPE.RECORD) {
|
||
subitem.dtype = odModal.form.DTYPE.value;
|
||
subitem.access = odModal.form.Access.value;
|
||
}
|
||
odModalClose();
|
||
onFormChanged();
|
||
reloadOD_Section(modalSubitem.odSectionName);
|
||
}
|
||
|
||
// ####################### Display Object Dictionary state ####################### //
|
||
|
||
function reloadOD_Sections() {
|
||
reloadOD_Section(sdo);
|
||
reloadOD_Section(txpdo);
|
||
reloadOD_Section(rxpdo);
|
||
}
|
||
|
||
function reloadOD_Section(odSectionName) {
|
||
const odSection = getObjDictSection(odSectionName);
|
||
var indexes = getUsedIndexes(odSection);
|
||
var section = '';
|
||
indexes.forEach(index => {
|
||
const objd = odSection[index];
|
||
section += `<div class="odItem"><span class="odItemContent"><strong>0x${index}</strong> "${objd.name}" ${objd.otype} ${objd.dtype ?? ''}</span><span>`;
|
||
if (objd.otype == OTYPE.ARRAY || objd.otype == OTYPE.RECORD) {
|
||
section += `<button onClick='addSubitemClick(${odSectionName}, 0x${index})'> ➕ Add subitem </button>`;
|
||
}
|
||
section += `<button onClick='onRemoveClick(${odSectionName}, 0x${index})'> ❌ Remove </button>`;
|
||
section += `<button onClick='edit${objd.otype}_Click(${odSectionName}, 0x${index})'> 🛠️ Edit </button>`;
|
||
section += `</span></div>`;
|
||
if (objd.items) {
|
||
var subindex = 1; // skip Max Subindex
|
||
objd.items.slice(subindex).forEach(subitem => {
|
||
var subindexHex = subindex < 16 ? `0${subindex.toString(16)}` : subindex.toString(16);
|
||
section += `<div class="odSubitem"><span class="odSubitemContent"><strong>:0x${subindexHex}</strong> "${subitem.name}" ${subitem.dtype ?? ''}</span>`;
|
||
section += `<span><button onClick='onRemoveClick(${odSectionName}, 0x${index}, ${subindex})'> ❌ Remove </button>`;
|
||
section += `<button onClick='editSubitemClick(${odSectionName}, 0x${index}, ${subindex})'> 🛠️ Edit </button>`;
|
||
section += `</span></div>`;
|
||
++subindex;
|
||
});
|
||
}
|
||
});
|
||
const odSectionElement = document.getElementById(`tr_${odSectionName}`);
|
||
if (odSectionElement) {
|
||
odSectionElement.innerHTML = section;
|
||
}
|
||
}
|
||
|
||
// ####################### Synchronization settings UI ####################### //
|
||
|
||
var syncModal = {};
|
||
var _dc = []
|
||
|
||
function syncModalSetup() {
|
||
// Get the modal
|
||
syncModal = document.getElementById("syncModal");
|
||
if (syncModal) {
|
||
syncModal.form = document.getElementById('syncModalForm');
|
||
}
|
||
else {
|
||
alert("Element required to edit Object Dictionary not found!");
|
||
}
|
||
}
|
||
|
||
function syncModalClose() {
|
||
syncModal.style.display = "none";
|
||
delete syncModal.edited;
|
||
reloadSyncModes();
|
||
}
|
||
|
||
function syncModalOpen() {
|
||
syncModal.style.display = "block";
|
||
}
|
||
|
||
function syncModeEdit(sync) {
|
||
syncModal.edited = sync;
|
||
|
||
syncModal.form.Name.value = sync.Name;
|
||
syncModal.form.Description.value = sync.Description;
|
||
syncModal.form.AssignActivate.value = sync.AssignActivate;
|
||
syncModal.form.Sync0cycleTime.value = sync.Sync0cycleTime;
|
||
syncModal.form.Sync0shiftTime.value = sync.Sync0shiftTime;
|
||
syncModal.form.Sync1cycleTime.value = sync.Sync1cycleTime;
|
||
syncModal.form.Sync1shiftTime.value = sync.Sync1shiftTime;
|
||
|
||
syncModalOpen();
|
||
}
|
||
|
||
function onSyncSubmit(syncForm) {
|
||
|
||
syncModal.edited.Name = syncForm.Name.value;
|
||
syncModal.edited.Description = syncForm.Description.value;
|
||
syncModal.edited.AssignActivate = syncForm.AssignActivate.value;
|
||
syncModal.edited.Sync0cycleTime = syncForm.Sync0cycleTime.value;
|
||
syncModal.edited.Sync0shiftTime = syncForm.Sync0shiftTime.value;
|
||
syncModal.edited.Sync1cycleTime = syncForm.Sync1cycleTime.value;
|
||
syncModal.edited.Sync1shiftTime = syncForm.Sync1shiftTime.value;
|
||
|
||
syncModalClose();
|
||
onFormChanged();
|
||
}
|
||
|
||
// ####################### Synchronization settings button handlers ####################### //
|
||
|
||
function addSyncClick() {
|
||
const newSyncMode = {
|
||
Name: "DcOff",
|
||
Description: "DC unused",
|
||
AssignActivate: "#x000",
|
||
Sync0cycleTime: 0,
|
||
Sync0shiftTime: 0,
|
||
Sync1cycleTime: 0,
|
||
Sync1shiftTime: 0,
|
||
}
|
||
_dc.push(newSyncMode);
|
||
syncModeEdit(newSyncMode);
|
||
}
|
||
|
||
function onEditSyncClick(i) {
|
||
const syncMode = _dc[i];
|
||
syncModeEdit(syncMode);
|
||
}
|
||
|
||
function onRemoveSyncClick(i) {
|
||
_dc.splice(i, 1);
|
||
reloadSyncModes();
|
||
onFormChanged();
|
||
}
|
||
|
||
// ####################### Synchronization settings UI ####################### //
|
||
|
||
function reloadSyncModes() {
|
||
var section = '';
|
||
var i = 0;
|
||
_dc.forEach(sync => {
|
||
section += `<div class="odItem"><span class="odItemContent"><strong>${sync.Name}</strong> ${sync.Description} [${sync.AssignActivate}]</span><span>`;
|
||
section += `<button onClick='onRemoveSyncClick(${i})'> ❌ Remove </button>`;
|
||
section += `<button onClick='onEditSyncClick(${i})'> 🛠️ Edit </button>`;
|
||
section += `</span></div>`;
|
||
++i;
|
||
});
|
||
const sectionElement = document.getElementById(`dcSyncModes`);
|
||
if (sectionElement) {
|
||
sectionElement.innerHTML = section;
|
||
}
|
||
} |