You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
9.4 KiB
300 lines
9.4 KiB
// dtp-display-engine.js
|
|
// Copyright (C) 2022,2023 DTP Technologies, LLC
|
|
// All Rights Reserved
|
|
|
|
'use strict';
|
|
|
|
const DTP_COMPONENT_NAME = 'DtpDisplayEngine';
|
|
import DtpLog from './dtp-log.js';
|
|
const dtp = window.dtp = window.dtp || { };
|
|
|
|
export default class DtpDisplayEngine {
|
|
|
|
constructor ( ) {
|
|
this.processors = { };
|
|
this.log = new DtpLog(DTP_COMPONENT_NAME);
|
|
}
|
|
|
|
/**
|
|
* Register a named Display List processor plugin. This lets client objects
|
|
* define their own Display List processor classes and resiter them. Server
|
|
* code can then execute actions on named processors in the client.
|
|
* @param {*} name The name of the DL processor being registered.
|
|
* @param {*} processor The custom DL processor being registered.
|
|
*/
|
|
registerProcessor (name, processor) {
|
|
this.processors[name] = processor;
|
|
}
|
|
|
|
/**
|
|
* Unregisters a named Display List processing plugin.
|
|
* @param {String} name The name of the DL processor to be unregistered.
|
|
*/
|
|
unregisterProcessor (name) {
|
|
if (!this.processors[name]) {
|
|
return;
|
|
}
|
|
delete this.processors[name];
|
|
}
|
|
|
|
/**
|
|
* Executes a Display List to implement view changes requested by the server.
|
|
* These can arrive from an HTTP request, via socket.io, or generated in code
|
|
* and passed in.
|
|
* @param {DtpDisplayList} displayList
|
|
*/
|
|
async executeDisplayList (displayList) {
|
|
try {
|
|
displayList.commands.forEach((command) => {
|
|
const processor = command.processor ? this.processors[command.processor] : this;
|
|
this.log.debug(command.action, 'action', command);
|
|
try {
|
|
processor[command.action](displayList, command);
|
|
} catch (error) {
|
|
this.log.error('executeDisplayList', 'failed to execute DisplayEngine command', { error });
|
|
}
|
|
});
|
|
} catch (error) {
|
|
this.log.error('executeDisplayList', 'failed to apply DisplayEngine updates', { error });
|
|
UIkit.modal.alert(`Failed to apply updates: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* action: addElement
|
|
* selector: Specifies the container element to insertAdjacentHTML
|
|
* where: 'beforebegin', 'afterbegin', 'beforeend', 'afterend'
|
|
* html: the HTML content to insert at the container as specified
|
|
*/
|
|
async addElement (displayList, command) {
|
|
const container = document.querySelector(command.selector);
|
|
if (!container) {
|
|
this.log.debug('addElement', 'displayList.addElement has failed', { command });
|
|
return;
|
|
}
|
|
container.insertAdjacentHTML(command.params.where, command.params.html);
|
|
}
|
|
|
|
async setTextContent (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || (elements.length === 0)) {
|
|
this.log.debug('setTextContent', 'failed to find target elements', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.textContent = command.params.text;
|
|
});
|
|
}
|
|
|
|
async setInputValue (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || (elements.length === 0)) {
|
|
this.log.debug('setInputValue', 'failed to find target elements', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.value = command.params.value;
|
|
});
|
|
}
|
|
|
|
/*
|
|
* action: replaceElement
|
|
* selector: Specifies the element to be replaced
|
|
* html: replaces the whole specified element
|
|
*/
|
|
async replaceElement (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || (elements.length === 0)) {
|
|
this.log.debug('replaceElement', 'displayList.replaceElement has failed to find requested element', { command });
|
|
return;
|
|
}
|
|
for (const element of elements) {
|
|
element.outerHTML = command.params.html;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* action: removeElement
|
|
* selector: Specifies the element(s) to be removed
|
|
*/
|
|
async removeElement (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || !elements.length) {
|
|
this.log.debug('removeElement', 'displayList.removeElement has failed', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.parentElement.removeChild(element);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Removes all child elements from the selected element(s).
|
|
* @param {DisplayList} displayList The display list being executed.
|
|
* @param {Command} command The command being executed.
|
|
*/
|
|
async removeChildren (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || !elements.length) {
|
|
this.log.debug('removeChildren', 'displayList.removeChildren has failed', { command });
|
|
return;
|
|
}
|
|
for (const e of elements) {
|
|
while (e.firstChild) {
|
|
e.removeChild(e.firstChild);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* action: setAttribute
|
|
* selector: Specifies the element(s) for which an attribute's value should be set
|
|
* name: the name of the attribute to be set
|
|
* value: the value to be set on the named attribute
|
|
*/
|
|
async setAttribute (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || !elements.length) {
|
|
console.debug('displayList.setAttribute has failed', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.setAttribute(command.params.name, command.params.value);
|
|
});
|
|
}
|
|
|
|
/*
|
|
* action: removeAttribute
|
|
* selector: specifies the element(s) from which an attribute is to be removed
|
|
* name: the name of the attribute to be removed
|
|
*/
|
|
async removeAttribute (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || !elements.length) {
|
|
console.debug('displayList.removeAttribute has failed', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.removeAttribute(command.params.name);
|
|
});
|
|
}
|
|
|
|
/*
|
|
* action: toggleAttribute
|
|
* selector: specifies the element(s) from which an attribute is to be removed
|
|
* name: the name of the attribute to be added or removed
|
|
* force: true to force the addition of the attribute, false to force the
|
|
* removal of the attribute, leave undefined to simply toggle.
|
|
*/
|
|
async toggleAttribute (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || !elements.length) {
|
|
console.debug('displayList.toggleAttribute has failed', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.toggleAttribute(command.params.name, command.params.force);
|
|
});
|
|
}
|
|
|
|
/*
|
|
* action: addClass
|
|
* selector: Specifies the element(s) for which style class(es) should be added
|
|
* name: the class name to add to the classList
|
|
*/
|
|
async addClass (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || !elements.length) {
|
|
console.debug('displayList.addClass has failed', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.classList.add(command.params.add);
|
|
});
|
|
}
|
|
|
|
/*
|
|
* action: removeClass
|
|
* selector: Specifies the element(s) for which style class(es) should be removed
|
|
* name: the class name to remove from the classList
|
|
*/
|
|
async removeClass (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || !elements.length) {
|
|
console.debug('displayList.removeClass has failed', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.classList.remove(command.params.remove);
|
|
});
|
|
}
|
|
|
|
/*
|
|
* action: replaceClass
|
|
* selector: Specifies the elements for which class replacement should occur
|
|
* remove: the class name to remove from the classList
|
|
* add: the class name to add to the classList
|
|
*/
|
|
async replaceClass (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || !elements.length) {
|
|
console.debug('displayList.replaceClass has failed', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.classList.remove(command.params.remove);
|
|
element.classList.add(command.params.add);
|
|
});
|
|
}
|
|
|
|
async showNotification (displayList, command) {
|
|
UIkit.notification(command.params);
|
|
}
|
|
|
|
async showModal (displayList, command) {
|
|
UIkit.modal.dialog(command.params.html);
|
|
}
|
|
|
|
async playNotificationSound (displayList, command) {
|
|
const { action } = command.params;
|
|
if (!dtp.app || !dtp.app.audio) {
|
|
return;
|
|
}
|
|
dtp.app.playNotificationSound(action);
|
|
}
|
|
|
|
async playSound (displayList, command) {
|
|
const { soundId } = command.params;
|
|
if (!dtp.app || !dtp.app.audio && !dtp.app.audio.hasSound(soundId)) {
|
|
return;
|
|
}
|
|
dtp.app.audio.playSound(soundId);
|
|
}
|
|
|
|
async setValue (displayList, command) {
|
|
const elements = document.querySelectorAll(command.selector);
|
|
if (!elements || !elements.length) {
|
|
console.debug('displayList.setValue (no elements found)', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.value = command.params.value;
|
|
});
|
|
}
|
|
|
|
async navigateTo (displayList, command) {
|
|
window.location = command.params.href;
|
|
}
|
|
|
|
async navigateBack ( ) {
|
|
if (window.history.length === 0) {
|
|
window.location = '/';
|
|
return;
|
|
}
|
|
window.history.back();
|
|
}
|
|
|
|
async reloadView ( ) {
|
|
window.location.reload();
|
|
}
|
|
}
|