// dtp-display-engine.js // Copyright (C) 2022 DTP Technologies, LLC // License: Apache-2.0 'use strict'; import UIkit from 'uikit'; import DtpLog from './dtp-log.js'; export default class DtpDisplayEngine { static get COMPONENT ( ) { return { logId: 'display-engine', index: 'displayEngine', className: 'DisplayEngine' }; } constructor (app) { this.app = app; this.processors = { }; this.log = new DtpLog(DtpDisplayEngine.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(); } }