// Constants
const regex = {
error: new RegExp('^[a-zA-Z]*Error:'),
};
// DOM elements
const elements = {
wrapper: document.querySelector('#wrapper'),
prompt: document.querySelector('#prompt'),
container: document.querySelector('#container'),
browser: document.querySelector('#browser'),
config: document.querySelector('#config'),
log: document.querySelector('#log'),
};
// Show prompt and hide log container
elements.prompt.hidden = false;
elements.container.hidden = true;
/**
* Escape HTML.
*/
function escapeHTML(text) {
const template = document.createElement('span');
template.innerText = text;
return template.innerHTML;
}
/**
* Create an element from HTML.
*/
function createElementFromHTML(html) {
const template = document.createElement('template');
template.innerHTML = html.trim();
return template.content.firstChild;
}
/**
* Format a record (message) value.
*/
function formatRecordValue(value) {
// Handle null
if (value === null) {
return `${escapeHTML(`${value}`)}`;
}
// Handle boolean
if (value.constructor === Boolean) {
return `${escapeHTML(value)}`;
}
// Handle number
if (value.constructor === Number) {
return `${escapeHTML(value)}`;
}
// Handle string, converted types (e.g. ArrayBuffer, Blob, ...)
// and errors (exceptions).
if (value.constructor === String) {
if (value.startsWith('[') && value.endsWith(']')) {
return `${escapeHTML(value)}`;
}
if (regex.error.test(value)) {
return `${escapeHTML(value)}`;
}
return `${escapeHTML(value)}`;
}
// Handle object
if (value.constructor === Object) {
const entries = Object.entries(value);
return `
Object(${entries.length})
${entries.map(([key, value]) => {
return `- ${escapeHTML(key)}: ${formatRecordValue(value)}
`;
}).join('\n')}
`;
}
// Handle array
if (value instanceof Array) {
return `
Array(${value.length})
${value.map((item, index) => {
return `- ${index}: ${formatRecordValue(item)}
`;
}).join('\n')}
`;
}
// Unknown
return `[${value.constructor}]`;
}
/**
* Show the log in the UI.
* @param data A log report in JSON notation.
*/
function showLog(data) {
// Decode as JSON
let container;
try {
container = JSON.parse(data);
} catch (error) {
return console.error('Could not parse pasted text to object:', error);
}
// Required keys to be available
if (!(container.config instanceof Object) ||
container.browser.constructor !== String ||
!(container.log instanceof Array)) {
return console.error('Not a valid container object');
}
// Hide prompt and show log container
elements.prompt.hidden = true;
elements.container.hidden = false;
// Display meta data
elements.browser.textContent = container.browser;
elements.config.textContent = JSON.stringify(container.config, null, 2);
// Display log records
elements.log.innerHTML = '';
let startTimestampMs;
for (let [timestampMs, type, tag, ...values] of container.log) {
// Determine start timestamp so we can display the offset in seconds
if (startTimestampMs === undefined) {
startTimestampMs = timestampMs;
}
// Get CSS style from tag (if any)
if (tag.startsWith('%c')) {
const style = escapeHTML(values.shift());
tag = `${escapeHTML(tag.substring(2))}`;
} else {
tag = escapeHTML(tag);
}
// Add element to log container
elements.log.appendChild(createElementFromHTML(`
${((timestampMs - startTimestampMs) / 1000).toFixed(3)} |
${tag} |
${values.map((value) => formatRecordValue(value)).join('\n')} |
`));
}
}
/**
* Listen for *paste* events.
*/
document.addEventListener('paste', (event) => {
// If no clipboard data is available, do nothing.
let text;
try {
text = event.clipboardData.getData('text');
} catch (error) {
return console.error('Could not retrieve pasted data as text:', error);
}
// Show log
showLog(text);
});
/**
* Listen for *drag* events.
*/
document.addEventListener('dragover', (event) => {
event.preventDefault();
});
document.addEventListener('dragenter', () => {
elements.wrapper.className = 'drag-over';
});
document.addEventListener('drop', (event) => {
event.preventDefault();
elements.wrapper.className = '';
// Read first file (if any)
const files = event.dataTransfer.files;
if (files.length === 0) {
console.error('No files in drop event');
return;
}
const reader = new FileReader();
reader.addEventListener('load', (event) => {
showLog(event.target.result);
});
reader.readAsText(files[0]);
});