183 lines
5.4 KiB
JavaScript
183 lines
5.4 KiB
JavaScript
|
function showApplication(toggle) {
|
||
|
if (toggle) {
|
||
|
document.getElementById('real-body').classList.remove('d-none');
|
||
|
} else {
|
||
|
document.getElementById('real-body').classList.add('d-none');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function showLoadingIndicator(toggle) {
|
||
|
if (toggle) {
|
||
|
document.getElementById('loading').classList.remove('d-none');
|
||
|
} else {
|
||
|
document.getElementById('loading').classList.add('d-none');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function displayError(querySelector, category, ex) {
|
||
|
console.error(ex);
|
||
|
const errorDiv = document.querySelector(querySelector);
|
||
|
const errorHeading = errorDiv.querySelector('.alert-heading');
|
||
|
|
||
|
let message = ex.message;
|
||
|
|
||
|
if (ex.hasOwnProperty('cause')) {
|
||
|
if (ex.cause.hasOwnProperty('message')) {
|
||
|
message += ': ' + ex.cause.message;
|
||
|
} else {
|
||
|
message += ex.cause;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//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 = message;
|
||
|
errorDiv.appendChild(errorSpan);
|
||
|
|
||
|
errorHeading.innerText = category;
|
||
|
errorDiv.classList.remove('d-none');
|
||
|
}
|
||
|
|
||
|
function displayFatalError(category, ex) {
|
||
|
showApplication(false);
|
||
|
showLoadingIndicator(false);
|
||
|
displayError('#fatal-error', category, ex);
|
||
|
}
|
||
|
|
||
|
function displayNonFatalError(category, ex) {
|
||
|
showApplication(true);
|
||
|
showLoadingIndicator(false);
|
||
|
displayError('#display-backup-error', category, ex);
|
||
|
}
|
||
|
|
||
|
function hideErrors() {
|
||
|
document
|
||
|
.querySelectorAll('#fatal-error, #display-backup-error')
|
||
|
.forEach(el => el.classList.add('d-none'));
|
||
|
}
|
||
|
|
||
|
function importStylesheet(xsl) {
|
||
|
//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', { cause });
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async function createXSLTProcessor() {
|
||
|
return fetch("/MessageLog.xsl")
|
||
|
.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) {
|
||
|
while (element.hasChildNodes()) {
|
||
|
element.removeChild(element.lastChild);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function checkOverflow(elem) {
|
||
|
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)) {
|
||
|
div.parentElement.classList.add('overflow-icon');
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function parseXML(xmlText) {
|
||
|
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
||
|
}
|
||
|
|
||
|
function displayBackup(xsltProcessor, file) {
|
||
|
hideErrors();
|
||
|
|
||
|
//document.getElementById('intro-card').classList.add('d-none');
|
||
|
|
||
|
const chatDisplay = document.getElementById("chat-display");
|
||
|
removeChildren(chatDisplay);
|
||
|
|
||
|
file.text().then(xmlText => {
|
||
|
document.getElementById('currently-viewing').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);
|
||
|
chatDisplay.appendChild(fragment);
|
||
|
processFragment();
|
||
|
}
|
||
|
catch (e) {
|
||
|
displayNonFatalError('Could not load backup', e);
|
||
|
}
|
||
|
} else {
|
||
|
const err = new Error(errorNode.firstChild.nodeValue);
|
||
|
displayNonFatalError('Could not load backup', err);
|
||
|
}
|
||
|
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function initEvents(xsltProcessor) {
|
||
|
const selectFileButtons = document.querySelectorAll("#backup-file-button, #backup-file-button-card");
|
||
|
const fileSelector = document.getElementById("backup-file");
|
||
|
|
||
|
selectFileButtons.forEach(button => button.addEventListener("click", e => {
|
||
|
fileSelector.click();
|
||
|
e.preventDefault();
|
||
|
}));
|
||
|
|
||
|
fileSelector.addEventListener("change", async (e) => {
|
||
|
const file = e.currentTarget.files[0];
|
||
|
displayBackup(xsltProcessor, file);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
window.addEventListener('resize', () => {
|
||
|
if (window.innerWidth <= 500) {
|
||
|
document.querySelector('#chat-display table').classList.add('table-sm');
|
||
|
} else {
|
||
|
document.querySelector('#chat-display table').classList.remove('table-sm');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
window.addEventListener('DOMContentLoaded', async () => {
|
||
|
createXSLTProcessor()
|
||
|
.then(initEvents)
|
||
|
.then(_ => {
|
||
|
showLoadingIndicator(false);
|
||
|
showApplication(true);
|
||
|
})
|
||
|
.catch(ex => displayFatalError('Initialiation Failed', ex));
|
||
|
});
|