// 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})
`; } // Handle array if (value instanceof Array) { return `
Array(${value.length})
    ${value.map((item, index) => { return `
  1. ${index}: ${formatRecordValue(item)}
  2. `; }).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]); });