msn-viewer/index.js

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));
});