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.
233 lines
7.1 KiB
233 lines
7.1 KiB
// dtp-display-engine.js
|
|
// Copyright (C) 2022 DTP Technologies, LLC
|
|
// License: Apache-2.0
|
|
|
|
'use strict';
|
|
|
|
const DTP_COMPONENT = { name: 'Display Engine', slug: 'display-engine' };
|
|
|
|
import UIkit from 'uikit';
|
|
import DtpLog from './dtp-log.js';
|
|
|
|
export default class DtpDisplayEngine {
|
|
|
|
constructor (app) {
|
|
this.app = app;
|
|
this.processors = { };
|
|
this.log = new DtpLog(DTP_COMPONENT);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
processor[command.action](displayList, command);
|
|
});
|
|
} 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) {
|
|
console.debug('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.error('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.error('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 element = document.querySelector(command.selector);
|
|
if (!element) {
|
|
console.debug('displayList.replaceElement has failed to find requested element', { command });
|
|
return;
|
|
}
|
|
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) {
|
|
console.debug('displayList.removeElement has failed', { command });
|
|
return;
|
|
}
|
|
elements.forEach((element) => {
|
|
element.parentElement.removeChild(element);
|
|
});
|
|
}
|
|
|
|
/*
|
|
* 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: 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) {
|
|
this.app.currentModal = UIkit.modal.dialog(command.params.html);
|
|
}
|
|
|
|
async closeModal ( ) {
|
|
if (!this.app.currentModal) {
|
|
return;
|
|
}
|
|
this.app.currentModal.hide();
|
|
delete this.app.currentModal;
|
|
}
|
|
|
|
async navigateTo (displayList, command) {
|
|
window.location = command.params.href;
|
|
}
|
|
|
|
async reload ( ) {
|
|
window.location.reload();
|
|
}
|
|
}
|