DTP Base provides a scalable and secure Node.js application development harness ready for production service.
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.
 
 
 
 

301 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);
this.log.debug('constructor', 'DTP Display Engine instance created');
}
/**
* 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();
}
}