var ActionEditor: ActionEditor = { elements: { up: { container: document.querySelector('.event.up'), selector: document.querySelector('.event.up').querySelector('.actionselector'), actions: document.querySelector('.event.up').querySelector('.actions') }, down: { container: document.querySelector('.event.down'), selector: document.querySelector('.event.down').querySelector('.actionselector'), actions: document.querySelector('.event.down').querySelector('.actions') }, latch: { container: document.querySelector('.event.toggle'), selector: document.querySelector('.event.toggle').querySelector('.tlatch').querySelector('.actionselector'), actions: document.querySelector('.event.toggle').querySelector('.tlatch').querySelector('.actions') }, unlatch: { container: document.querySelector('.event.toggle'), selector: document .querySelector('.event.toggle') .querySelector('.tunlatch') .querySelector('.actionselector'), actions: document.querySelector('.event.toggle').querySelector('.tunlatch').querySelector('.actions') } }, openEditors: { up: {}, down: {}, latch: {}, unlatch: {} }, listeners() { if (ActionSelector) { ActionSelector.register(ActionEditor.elements.up.selector, (integrationID: string, actionID: string) => handleNewAction(integrationID, actionID, 'up') ); ActionSelector.register(ActionEditor.elements.down.selector, (integrationID: string, actionID: string) => handleNewAction(integrationID, actionID, 'down') ); ActionSelector.register(ActionEditor.elements.latch.selector, (integrationID: string, actionID: string) => handleNewAction(integrationID, actionID, 'latch') ); ActionSelector.register(ActionEditor.elements.unlatch.selector, (integrationID: string, actionID: string) => handleNewAction(integrationID, actionID, 'unlatch') ); function handleNewAction( integrationID: string, actionID: string, type: 'up' | 'down' | 'latch' | 'unlatch' ) { socket.emit( 'actioneditor', 'create', PageHandler.currentPageID, Editor.currentX, Editor.currentY, type, integrationID, actionID, (actionInstance: Page_Key_Action) => { ActionEditor.openAction(actionInstance, type); } ); } } else setTimeout(ActionEditor.listeners, 100); }, open(actions: Page_Key_ActionTypes, x: string, y: string, key: Page_Key, pageID: string) { for (var type in actions) { ActionEditor.elements[type].actions.innerHTML = ''; for (var actionInstanceID in actions[type]) { ActionEditor.openAction(actions[type][actionInstanceID], type); } } }, openAction(actionInstance: Page_Key_Action, type: string) { var actionInstanceID = actionInstance.actionInstanceID; var container = ce('div', 'actioncontainer', { actionInstanceID }); var header = ce('div', 'header'); header.appendChild( ce('div', 'integration', null, ActionSelector.maps.integrationNames[actionInstance.integrationID]) ); header.appendChild(ce('div', 'action', null, ActionSelector.maps.actionNames[actionInstance.actionID])); var buttons = ce('div', 'buttons'); var actionLogs = ce( 'div', [ 'btn', 'logs' ], null, 'Logs' ); actionLogs.onclick = () => UndeckedNotification('Not implented yet', 'error'); var actionRemove = ce( 'div', [ 'btn', 'remove' ], null, 'Remove' ); actionRemove.onclick = () => { if (container.parentElement) container.parentElement.removeChild(container); if ( ActionEditor.openEditors[type] != undefined && ActionEditor.openEditors[type][actionInstanceID] != undefined ) { ActionEditor.openEditors[type][actionInstanceID].destroy(); } socket.emit( 'actioneditor', 'remove', PageHandler.currentPageID, Editor.currentX, Editor.currentY, actionInstanceID, type ); Editor.registerChange(); }; buttons.appendChild(actionLogs); buttons.appendChild(actionRemove); header.appendChild(buttons); container.appendChild(header); var fields = ce('div', 'fields'); container.appendChild(fields); ActionEditor.elements[type].actions.appendChild(container); ActionEditor.openEditors[type][actionInstanceID] = new Action( { integrationID: actionInstance.integrationID, actionID: actionInstance.actionID, actionType: type, keyX: Editor.currentX, keyY: Editor.currentY, actionInstanceID, pageID: PageHandler.currentPageID }, fields ); }, close() { for (var type in ActionEditor.openEditors) { for (var actionInstanceID in ActionEditor.openEditors[type]) { ActionEditor.openEditors[type][actionInstanceID].destroy(); } } ActionEditor.elements.up.actions.innerHTML = ''; ActionEditor.elements.down.actions.innerHTML = ''; } }; ActionEditor.listeners(); var Action = class Action { settings: Action_Settings; container: HTMLDivElement; actionEditorID: string; lastFields: EditorAPI_Field[]; constructor(settings: Action_Settings, container: HTMLDivElement) { this.settings = settings; this.container = container; socket.emit( 'actioneditor', 'start', this.settings, (error: string, actionEditorID: string, ready: () => void) => { if (error) return UndeckedNotification(error, 'error', 5000); this.actionEditorID = actionEditorID; this.listeners(); this.emit('ready'); } ); } emit(query: string, ...args: any[]) { socket.emit('actioneditor', 'instance', this.actionEditorID, query, ...args); } listeners() { socket.on(`AE_${this.actionEditorID}`, (query: string, ...args: any[]) => { switch (query) { case 'fields': var fields: EditorAPI_Field[] = args[0]; this.render(fields); break; } }); } destroy() { if (this.container && this.container.parentElement) this.container.parentElement.removeChild(this.container); this.emit('close'); } render(fields: EditorAPI_Field[]) { var valueNameMap = {}; var updateMulti = (selected: string[], input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement) => { var text: string = selected != undefined && selected.length > 0 ? selected .map((value) => { return valueNameMap[value]; }) .join(', ') : 'None'; input.innerHTML = ''; input.appendChild(ce('option', 'mutli', { value: selected.join(',') }, text)); }; var getMultiValues = (dropdowninner: HTMLDivElement): string[] => { var values: string[] = []; dropdowninner.querySelectorAll('.option').forEach((option: HTMLDivElement) => { var checkbox: HTMLInputElement = option.querySelector('input'); var optionID = option.getAttribute('optionID'); if (checkbox.checked) values.push(optionID); }); return values; }; fields.forEach((field) => { var fieldcontainer: HTMLDivElement = this.container.querySelector(`.field_${field.id}`); if (fieldcontainer == null) { fieldcontainer = ce('div', [ 'field', `field_${field.id}` ]); this.container.appendChild(fieldcontainer); } var label: HTMLDivElement = fieldcontainer.querySelector('.fieldlabel'); if (label == null) { label = ce('div', 'fieldlabel', null, `${field.name}${field.required ? ' *' : ''}`); fieldcontainer.appendChild(label); } else label.innerText = `${field.name}${field.required ? ' *' : ''}`; var input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement = fieldcontainer.querySelector( '.input' ); if (input == null) { switch (field.type) { case 'number': case 'text': case 'color': input = ce('input', 'input', { type: field.type, value: field.value }); fieldcontainer.appendChild(input); break; case 'select': case 'connection': if (field.multi != undefined && field.multi == true) { var multiSelect = ce('div', [ 'multiselect' ]); input = ce('select', [ 'input', 'multiinput' ]); input.onmouseup = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); dropdown.style.display = 'block'; input.classList.add('open'); var handleWindowClick = (e: MouseEvent) => { if (e.target) { var target = e.target; if ( !target.classList.contains('msdp') && !target.classList.contains('multiinput') ) { window.removeEventListener('click', handleWindowClick); dropdown.style.display = 'none'; input.classList.remove('open'); } } }; window.addEventListener('click', handleWindowClick); }; input.onmousedown = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); }; multiSelect.appendChild(input); var dropdown = ce('div', [ 'dropdown', 'msdp' ]); var dropdowninner = ce('div', [ 'inner', 'msdp' ]); dropdown.appendChild(dropdowninner); multiSelect.appendChild(dropdown); if (field.values != undefined && field.values.length > 0) for (let i = 0; i < field.values.length; i++) { ((value) => { valueNameMap[value.id] = field.values[i].text; var item = ce( 'div', [ 'option', 'msdp' ], { optionID: value.id } ); var checkbox = ce('input', 'msdp', { type: 'checkbox' }); checkbox.checked = field.value != undefined && field.value.includes(value.id); item.onclick = () => { checkbox.checked = !checkbox.checked; updateMulti(getMultiValues(dropdowninner), input); this.emit('fields', this.export()); }; item.appendChild(checkbox); item.appendChild( ce( 'div', [ 'text', 'msdp' ], null, value.text ) ); dropdowninner.appendChild(item); })(field.values[i]); } updateMulti(field.value, input); fieldcontainer.appendChild(multiSelect); } else { input = ce('select', 'input'); if (field.values != undefined && field.values.length > 0) for (let i = 0; i < field.values.length; i++) { var option = ce( 'option', null, { value: field.values[i].id }, field.values[i].text ); if (field.value == field.values[i].id) option.setAttribute('selected', ''); input.appendChild(option); } fieldcontainer.appendChild(input); } break; } if (input) input.oninput = () => this.emit('fields', this.export()); } else { input.value = field.value; if (field.type == 'select' || field.type == 'connection') { var parsedOptionValues = []; if (field.multi != undefined && field.multi == true) { var dropdown_inner = input.parentElement.querySelector('.inner'); if (field.values != undefined && field.values.length > 0) for (let i = 0; i < field.values.length; i++) { var selectValue = field.values[i]; var existingOption: HTMLDivElement = dropdown_inner.querySelector( `.option[optionid="${selectValue.id}"]` ); parsedOptionValues.push(selectValue.id); if (existingOption) { var text: HTMLDivElement = existingOption.querySelector('.text'); text.innerText = selectValue.text; } else { ((value) => { valueNameMap[value.id] = field.values[i].text; var item = ce( 'div', [ 'option', 'msdp' ], { optionID: value.id } ); var checkbox = ce('input', 'msdp', { type: 'checkbox' }); checkbox.checked = field.value != undefined && field.value.includes(value.id); item.onclick = () => { checkbox.checked = !checkbox.checked; updateMulti(getMultiValues(dropdown_inner), input); this.emit('fields', this.export()); }; item.appendChild(checkbox); item.appendChild( ce( 'div', [ 'text', 'msdp' ], null, value.text ) ); dropdown_inner.appendChild(item); })(selectValue); } } else dropdown_inner.innerHTML = ''; dropdown_inner.querySelectorAll('.option').forEach((option: HTMLOptionElement) => { var optionValue = option.getAttribute('optionID'); var checkbox: HTMLInputElement = option.querySelector('input'); if (!parsedOptionValues.includes(optionValue)) option.parentElement.removeChild(option); else checkbox.checked = field.value.includes(optionValue); }); } else { if (field.values != undefined && field.values.length > 0) for (let i = 0; i < field.values.length; i++) { var selectValue = field.values[i]; var existing: HTMLOptionElement = input.querySelector( `option[value="${selectValue.id}"]` ); if (existing) existing.innerText = selectValue.text; else { existing = ce( 'option', null, { value: selectValue.id }, selectValue.text ); input.appendChild(existing); } parsedOptionValues.push(selectValue.id); } else input.innerHTML = ''; input.querySelectorAll('option').forEach((option: HTMLOptionElement) => { var optionValue = option.getAttribute('value'); option.removeAttribute('selected'); if (!parsedOptionValues.includes(optionValue)) option.parentElement.removeChild(option); else if (field.value == option) option.setAttribute('selected', ''); }); } // if (field.value != undefined && input.value.length > 0) input.value = field.value; } } }); this.lastFields = fields; } export(): EditorAPI_Field[] { var copyOfLast = JSON.parse(JSON.stringify(this.lastFields)); for (let i = 0; i < copyOfLast.length; i++) { var field = copyOfLast[i]; var fieldcontainer: HTMLDivElement = this.container.querySelector(`.field_${field.id}`); if (fieldcontainer) { var input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement = fieldcontainer.querySelector( '.input' ); if (input) { if (!input.classList.contains('multiinput')) copyOfLast[i].value = input.value; else copyOfLast[i].value = input.value.split(','); } } } return copyOfLast; } }; interface Action_Settings { integrationID: string; actionID: string; actionInstanceID: string; pageID: string; actionType: 'up' | 'down'; keyX: number | string; keyY: number | string; } interface ActionEditor { elements: { up: { container: HTMLDivElement; selector: HTMLInputElement; actions: HTMLDivElement; }; down: { container: HTMLDivElement; selector: HTMLInputElement; actions: HTMLDivElement; }; latch: { container: HTMLDivElement; selector: HTMLInputElement; actions: HTMLDivElement; }; unlatch: { container: HTMLDivElement; selector: HTMLInputElement; actions: HTMLDivElement; }; }; openEditors: { up: { [actionInstanceID: string]: typeof Action }; down: { [actionInstanceID: string]: typeof Action }; latch: { [actionInstanceID: string]: typeof Action }; unlatch: { [actionInstanceID: string]: typeof Action }; }; listeners: () => void; open: (actions: Page_Key_ActionTypes, x: string, y: string, key: Page_Key, pageID: string) => void; openAction: (actionInstance: Page_Key_Action, type: string) => void; close: () => void; } interface Page_Key_ActionTypes { up: Page_Key_Actions; down: Page_Key_Actions; } interface Page_Key_Actions { [actionInstanceID: string]: Page_Key_Action; } interface Page_Key_Action { integrationID: string; actionID: string; actionInstanceID: string; properties: { [property: string]: any }; logs: { timestamp: number; type: 'error' | 'info' | 'warning'; text: string }[]; } interface EditorAPI_Field { id: string; name: string; type: 'text' | 'number' | 'select' | 'connection' | 'color'; value: any; values?: { id: string; text: string }[]; required?: boolean; multi?: boolean; }