import * as bootstrap from 'bootstrap'; import xsl from 'bundle-text:./MessageLog.xsl'; import * as ui from './ui'; function showApplication(toggle: boolean) { if (toggle) { ui.realBody.classList.remove('d-none'); } else { ui.realBody.classList.add('d-none'); } } function showLoadingIndicator(toggle: boolean) { if (toggle) { ui.loadingIndicator.classList.remove('d-none'); } else { ui.loadingIndicator.classList.add('d-none'); } } function displayError(errorDiv: HTMLElement, category: string, ex: Error) { console.error(ex); const errorHeading = errorDiv.querySelector('.alert-heading') as HTMLElement | null; //clear all error text, but not heading const errorMessages = errorDiv.querySelectorAll('span'); errorMessages.forEach(message => errorDiv.removeChild(message)); //add new message and show the error alert const errorSpan = document.createElement('span'); errorSpan.innerText = ex.message; errorDiv.appendChild(errorSpan); if (errorHeading) { errorHeading.innerText = category; } errorDiv.classList.remove('d-none'); } function displayFatalError(category: string, ex: Error) { showApplication(false); showLoadingIndicator(false); displayError(ui.fatalError, category, ex); } function displayNonFatalError(category: string, ex: Error) { showApplication(true); showLoadingIndicator(false); displayError(ui.nonFatalError, category, ex); } function hideErrors() { document .querySelectorAll('#fatal-error, #display-backup-error') .forEach(el => el.classList.add('d-none')); } function importStylesheet(xsl: Node) { //Firefox does not seem to report XML parsing errors into the //exception tree. So we catch here, log, and return some friendly //error. try { const xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(xsl); return xsltProcessor; } catch (cause) { console.error(cause); throw new Error('Stylesheet parsing failed'); } } async function createXSLTProcessor() { const doc = new DOMParser().parseFromString(xsl, "text/xml"); return Promise.resolve(importStylesheet(doc)); // return fetch(new URL("./MessageLog.xsl", import.meta.url)) // .then(resp => { // if (resp.ok) // return resp.text(); // else // throw new Error(resp.status + ' ' + resp.statusText); // }) // .then(str => new DOMParser().parseFromString(str, "text/xml")) // .then(importStylesheet) // .catch(cause => { // throw new Error('Could not load XSL stylesheet', { cause }); // }); } function removeChildren(element: HTMLElement) { while (element.hasChildNodes() && element.lastChild != null) { element.removeChild(element.lastChild); } } function checkOverflow(elem: HTMLElement) { const elemHeight = elem.scrollHeight; const parentHeight = elem.offsetHeight; return elemHeight > parentHeight; } function processFragment() { document .querySelectorAll('[data-bs-toggle="popover"]') .forEach(popover => new bootstrap.Popover(popover)); document.querySelectorAll('.message-content div').forEach(div => { if (checkOverflow(div as HTMLElement)) { div.parentElement?.classList.add('overflow-icon'); } }); } function displayBackup(xsltProcessor: XSLTProcessor, file: File) { hideErrors(); removeChildren(ui.chatDisplay); file.text().then(xmlText => { ui.currentlyViewing.innerText = file.name; const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlText, "text/xml"); const errorNode = xmlDoc.querySelector('parsererror'); if (!errorNode) { try { const fragment = xsltProcessor.transformToFragment(xmlDoc, document); ui.chatDisplay.appendChild(fragment); processFragment(); } catch (e) { if (e instanceof Error) { displayNonFatalError('Could not load backup', e); } } } else { const err = new Error(errorNode.firstChild?.nodeValue || ''); displayNonFatalError('Could not load backup', err); } }); } function initEvents(xsltProcessor: XSLTProcessor) { ui.fileSelector.addEventListener("change", async (e) => { const target = e.currentTarget as HTMLInputElement | null; const file = target?.files?.[0]; if (file) { displayBackup(xsltProcessor, file); } else { displayNonFatalError('Could not load file', new Error('No file found.')); } }); } window.addEventListener('DOMContentLoaded', async () => { window.addEventListener('resize', () => { if (window.innerWidth <= 500) { ui.chatDisplay.querySelector('table')?.classList.add('table-sm'); } else { ui.chatDisplay.querySelector('table')?.classList.remove('table-sm'); } }); createXSLTProcessor() .then(initEvents) .then(_ => { showLoadingIndicator(false); showApplication(true); }) .catch(ex => displayFatalError('Initialiation Failed', ex)); });