584 lines
16 KiB
TypeScript
584 lines
16 KiB
TypeScript
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 = <HTMLDivElement>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 = <HTMLDivElement>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: <any>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 = <HTMLDivElement>ce('div', [
|
|
'field',
|
|
`field_${field.id}`
|
|
]);
|
|
this.container.appendChild(fieldcontainer);
|
|
}
|
|
|
|
var label: HTMLDivElement = fieldcontainer.querySelector('.fieldlabel');
|
|
if (label == null) {
|
|
label = <HTMLDivElement>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 = <HTMLInputElement>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 = <HTMLInputElement>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 = <HTMLElement>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 = <HTMLDivElement>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 = <HTMLInputElement>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 = <HTMLInputElement>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 = <HTMLDivElement>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 = <HTMLInputElement>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 = <HTMLOptionElement>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;
|
|
}
|