// dtp-app.js
// Copyright (C) 2024 DTP Technologies, LLC
// All Rights Reserved
'use strict';
import DtpDisplayEngine from './dtp-display-engine.js';
import DtpSocket from './dtp-socket.js';
import DtpLog from './dtp-log.js';
import UIkit from 'uikit';
import numeral from 'numeral';
export default class DtpApp {
constructor (appName, user) {
this.user = user;
this.name = appName;
this.log = new DtpLog(appName);
this.displayEngine = new DtpDisplayEngine();
this.domParser = new DOMParser();
this.properties = { };
}
async onDtpLoad ( ) {
}
async connect (options) {
try {
this.log.debug('connect', 'creating WebSocket interface');
this.socket = new DtpSocket();
this.log.debug('connect', 'connecting WebSocket platform');
await this.socket.connect(options);
this.log.info('connect', 'DTP framework online');
} catch (error) {
this.log.error('connect', 'failed to connect', { error });
UIkit.modal.alert(`Failed to connect to server: ${error.message}`);
}
}
async confirmNavigation (event) {
const target = event.currentTarget || event.target;
event.preventDefault();
event.stopPropagation();
const href = target.getAttribute('href');
const hrefTarget = target.getAttribute('target');
const text = target.textContent;
const whitelist = [
'digitaltelepresence.com',
'www.digitaltelepresence.com',
'sites.digitaltelepresence.com',
'ourvoice.stream',
'www.ourvoice.stream',
];
try {
const url = new URL(href);
if (!whitelist.includes(url.hostname)) {
await UIkit.modal.confirm(`
You are navigating to ${href}
, a link or button that was displayed as:
${text}
Please only open links to destinations you trust and want to visit.
`);
}
window.open(href, hrefTarget);
} catch (error) {
this.log.info('confirmNavigation', 'navigation canceled', { error });
}
return true;
}
async showForm (event, formUrl, formId, initRoutine) {
event.preventDefault();
event.stopPropagation();
try {
const response = await fetch(formUrl, { method: 'GET' });
const html = await response.text();
this.currentModal = UIkit.modal.dialog(html);
if (formId) {
this.currentModal.$el.id = formId;
}
if (initRoutine) {
this[initRoutine]();
}
} catch (error) {
UIkit.modal.alert(`Failed to display form: ${error.message}`);
}
return true;
}
async submitForm (event, userAction) {
event.preventDefault();
event.stopPropagation();
const formElement = event.currentTarget || event.target;
const submitButton = formElement.querySelector('button[type="submit"]');
try {
const form = new FormData(formElement);
if (submitButton) {
submitButton.setAttribute('disabled', '');
}
// include the submitter if we have one and it presents all required data
if (event.submitter && event.submitter.name && event.submitter.value) {
form.append(event.submitter.name, event.submitter.value);
}
this.log.info('submitForm', userAction, { event, action: formElement.action });
const response = await fetch(formElement.action, {
method: formElement.method,
body: form,
});
if (!response.ok) {
let json;
try {
json = await response.json();
} catch (error) {
throw new Error('Server error');
}
throw new Error(json.message || 'Server error');
}
await this.processResponse(response);
} catch (error) {
UIkit.modal.alert(`Failed to ${userAction}: ${error.message}`);
} finally {
if (this.currentModal) {
this.currentModal.hide();
delete this.currentModal;
}
if (submitButton) {
submitButton.removeAttribute('disabled');
}
}
return;
}
async checkResponse (response) {
if (!response.ok) {
let json;
try {
json = await response.json();
} catch (error) {
throw new Error('Server error');
}
throw new Error(json.message);
}
}
async processResponse (response) {
const json = await response.json();
return this.processResponseJSON(json);
}
async processResponseJSON (json) {
if (!json.success) {
this.log.error('processResponseJSON', json.message);
throw new Error(json.message);
}
if (json.displayList) {
this.displayEngine.executeDisplayList(json.displayList);
}
}
async validateTextElement (element, elementName, options) {
const DEFAULT_MAX_CHARS = 1000;
const DEFAULT_OPTIONS = {
maxLength: DEFAULT_MAX_CHARS,
minLength: 1,
warnLengths: [80, 150],
feedback: {
textEmpty: `The ${elementName} can't be empty`,
textTooShort: "Keep going...",
textValid: "You're off to a great start!",
textLengthWarn: [
"This is getting a little crazy",
`Only ${Math.max(0, options.maxLength - element.value.length)} characters left`,
],
textMaxLength: `The ${elementName} is as long as it can be.`,
textTooLong: `${elementName} is too long by ${element.value.length - options.maxLength} characters`,
}
};
options = Object.assign(DEFAULT_OPTIONS, options);
if (options.prompt) {
options.prompt.textContent = `${element.value.length} of ${options.maxLength} max`;
}
let isValid = true;
if (element.value.length === 0 || element.value.length > options.maxLength) {
element.classList.remove('uk-form-success');
element.classList.remove('uk-form-warning');
element.classList.add('uk-form-danger');
isValid = false;
} else if (element.value.length > options.warnLengths[1]) {
element.classList.remove('uk-form-success');
element.classList.remove('uk-form-danger');
element.classList.add('uk-form-warning');
} else {
element.classList.remove('uk-form-danger');
element.classList.remove('uk-form-warning');
element.classList.remove('uk-form-success');
}
if (element.value.length === 0) {
window.dtp.submit.prompt.textContent = options.feedback.textEmpty;
isValid = false;
} else if (options.minLength && (element.value.length < options.minLength)) {
window.dtp.submit.prompt.textContent = options.feedback.textTooShort;
isValid = false;
} else if (element.value.length < options.warnLengths[0]) {
window.dtp.submit.prompt.textContent = options.feedback.textValid;
} else if (element.value.length < options.warnLengths[1]) {
window.dtp.submit.prompt.textContent = options.feedback.textLengthWarn[0];
} else if (element.value.length < options.maxLength) {
window.dtp.submit.prompt.textContent = options.feedback.textLengthWarn[1];
window.dtp.submit.prompt.classList.add('uk-text-bold');
} else if (element.value.length === options.maxLength) {
window.dtp.submit.prompt.textContent = options.feedback.textMaxLength;
window.dtp.submit.prompt.classList.add('uk-text-bold');
} else {
window.dtp.submit.prompt.textContent = options.feedback.textTooLong;
window.dtp.submit.prompt.classList.add('uk-text-bold');
isValid = false;
}
return isValid;
}
async parseHtml (html) {
return this.domParser.parseFromString(html, 'text/html');
}
async copyToClipboard (event) {
const target = event.currentTarget || event.target;
const text = target.getAttribute('data-text');
const message = target.getAttribute('data-message');
try {
await navigator.clipboard.writeText(text);
UIkit.modal.alert(message);
} catch (error) {
UIkit.modal.alert(`Failed to copy to clipboard: ${error.message}`);
}
}
formatCount (value) {
return numeral(value).format((value > 1000) ? '0,0.0a' : '0,0');
}
randomString ( ) {
return Math.random().toString().slice(2);
}
}