/** * ZDDC — shared preview helpers * * Cross-tool helpers for previewing file types that need a decoder: * - TIFF (UTIF.js) — multi-page, browser-PDF-viewer-style toolbar * - ZIP listing (JSZip) — sortable file-list view * - DOCX (docx-preview) — Word-styled pages in a scroll container * - XLSX/XLS (SheetJS) — sheet-to-HTML table with a sheet tab bar * * Renderers operate on any document (parent window or popup window), so the * same code works for tools whose preview opens in a popup (classifier, * archive, transmittal) and tools that render inline (browse). * * The DOCX/XLSX renderers expect their vendor lib bundled by the calling * tool's build.sh (docx-preview.min.js → window.docx, xlsx.full.min.js → * window.XLSX); they degrade to a friendly message if it isn't present. * * Public API on window.zddc.preview: * loadLibrary(url) → Promise * renderTiff(doc, container, arrayBuffer, opts) → Promise * renderZipListing(doc, container, arrayBuffer, opts) → Promise * renderDocx(doc, container, arrayBuffer, opts) → Promise * renderXlsx(doc, container, arrayBuffer, opts) → Promise * TIFF_EXTENSIONS, IMAGE_EXTENSIONS, TEXT_EXTENSIONS, OFFICE_EXTENSIONS * isTiff(ext), isImage(ext), isText(ext), isZip(ext), isOffice(ext) * * Each tool keeps its own dispatcher; this lib only owns the heavy renderers. */ (function (root) { 'use strict'; var TIFF_EXTENSIONS = ['tif', 'tiff']; var IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'bmp', 'ico']; var TEXT_EXTENSIONS = [ 'txt', 'md', 'markdown', 'json', 'xml', 'csv', 'tsv', 'log', 'html', 'htm', 'css', 'js', 'mjs', 'ts', 'tsx', 'jsx', 'py', 'rb', 'sh', 'bash', 'zsh', 'bat', 'ps1', 'yaml', 'yml', 'ini', 'cfg', 'conf', 'toml', 'c', 'cc', 'cpp', 'h', 'hpp', 'go', 'rs', 'java', 'kt', 'sql', 'env' ]; var OFFICE_EXTENSIONS = ['docx', 'xlsx', 'xls']; function lowerExt(ext) { return (ext || '').toLowerCase(); } function isTiff(ext) { return TIFF_EXTENSIONS.indexOf(lowerExt(ext)) !== -1; } function isImage(ext) { return IMAGE_EXTENSIONS.indexOf(lowerExt(ext)) !== -1; } function isText(ext) { return TEXT_EXTENSIONS.indexOf(lowerExt(ext)) !== -1; } function isZip(ext) { return lowerExt(ext) === 'zip'; } function isOffice(ext) { return OFFICE_EXTENSIONS.indexOf(lowerExt(ext)) !== -1; } // ── CDN library loader (parent window cache) ───────────────────────────── var _libCache = new Map(); function loadLibrary(url) { if (_libCache.has(url)) return _libCache.get(url); var p = new Promise(function (resolve, reject) { var s = document.createElement('script'); s.src = url; s.onload = function () { resolve(); }; s.onerror = function () { reject(new Error('Failed to load: ' + url)); }; document.head.appendChild(s); }); _libCache.set(url, p); return p; } // ── Style injection (idempotent per-document) ──────────────────────────── function injectStyles(doc, id, css) { if (doc.getElementById(id)) return; var style = doc.createElement('style'); style.id = id; style.textContent = css; doc.head.appendChild(style); } // ── Helpers ────────────────────────────────────────────────────────────── function formatSize(bytes) { if (bytes == null) return ''; if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB'; } function formatDate(d) { if (!d) return ''; var pad = function (n) { return n < 10 ? '0' + n : '' + n; }; return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()); } function escapeHtml(s) { return String(s) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } // ── TIFF renderer ──────────────────────────────────────────────────────── var TIFF_CSS = '.tiff-toolbar{display:flex;align-items:center;gap:.4rem;padding:.4rem .6rem;' + 'background:#f5f5f5;border-bottom:1px solid #ddd;flex-wrap:wrap;font-size:.85rem;}' + '.tiff-toolbar .tiff-btn{padding:.25rem .55rem;border:1px solid #ccc;border-radius:3px;' + 'background:#fff;cursor:pointer;font-size:.85rem;line-height:1;min-width:1.8rem;}' + '.tiff-toolbar .tiff-btn:hover:not(:disabled){background:#e8e8e8;}' + '.tiff-toolbar .tiff-btn:disabled{opacity:.4;cursor:default;}' + '.tiff-toolbar .tiff-page-info{display:inline-flex;align-items:center;gap:.3rem;}' + '.tiff-toolbar .tiff-page-input{width:3.2rem;padding:.2rem .3rem;border:1px solid #ccc;' + 'border-radius:3px;text-align:center;font-size:.85rem;}' + '.tiff-toolbar .tiff-zoom-select{padding:.2rem .3rem;border:1px solid #ccc;border-radius:3px;' + 'background:#fff;font-size:.85rem;}' + '.tiff-toolbar .tiff-spacer{flex:1;}' + '.tiff-viewport{flex:1;overflow:auto;background:#525659;display:flex;align-items:flex-start;' + 'justify-content:center;padding:1rem;}' + '.tiff-canvas{background:#fff;box-shadow:0 2px 8px rgba(0,0,0,.4);display:block;' + 'image-rendering:auto;}' + '.tiff-error{flex:1;display:flex;align-items:center;justify-content:center;color:#900;' + 'padding:2rem;text-align:center;}'; function renderTiff(doc, container, arrayBuffer, opts) { opts = opts || {}; injectStyles(doc, 'zddc-tiff-styles', TIFF_CSS); // UTIF is bundled (shared/vendor/utif.min.js) — window.UTIF is // available synchronously. Promise.resolve() keeps the existing // .then() chain shape so callers don't need to change. return Promise.resolve().then(function () { var ifds; try { ifds = window.UTIF.decode(arrayBuffer); } catch (e) { container.innerHTML = '
Failed to parse TIFF: ' + escapeHtml(e.message || e) + '
'; return; } if (!ifds || !ifds.length) { container.innerHTML = '
No images found in TIFF.
'; return; } // Reset container to a flex column container.innerHTML = ''; container.style.display = 'flex'; container.style.flexDirection = 'column'; container.style.minHeight = '0'; container.style.height = '100%'; container.style.overflow = 'hidden'; // Toolbar var toolbar = doc.createElement('div'); toolbar.className = 'tiff-toolbar'; var btnPrev = doc.createElement('button'); btnPrev.className = 'tiff-btn'; btnPrev.type = 'button'; btnPrev.title = 'Previous page'; btnPrev.textContent = '◀'; var pageInfo = doc.createElement('span'); pageInfo.className = 'tiff-page-info'; var pageInput = doc.createElement('input'); pageInput.type = 'number'; pageInput.min = '1'; pageInput.value = '1'; pageInput.className = 'tiff-page-input'; var pageOf = doc.createElement('span'); pageOf.textContent = ' of ' + ifds.length; pageInfo.appendChild(doc.createTextNode('Page ')); pageInfo.appendChild(pageInput); pageInfo.appendChild(pageOf); var btnNext = doc.createElement('button'); btnNext.className = 'tiff-btn'; btnNext.type = 'button'; btnNext.title = 'Next page'; btnNext.textContent = '▶'; var spacer = doc.createElement('span'); spacer.className = 'tiff-spacer'; var btnZoomOut = doc.createElement('button'); btnZoomOut.className = 'tiff-btn'; btnZoomOut.type = 'button'; btnZoomOut.title = 'Zoom out'; btnZoomOut.textContent = '−'; var zoomSelect = doc.createElement('select'); zoomSelect.className = 'tiff-zoom-select'; var zoomOptions = [ ['fit-width', 'Fit width'], ['fit-page', 'Fit page'], ['0.5', '50%'], ['0.75', '75%'], ['1', '100%'], ['1.25', '125%'], ['1.5', '150%'], ['2', '200%'], ['3', '300%'], ['4', '400%'] ]; zoomOptions.forEach(function (z) { var o = doc.createElement('option'); o.value = z[0]; o.textContent = z[1]; zoomSelect.appendChild(o); }); zoomSelect.value = 'fit-width'; var btnZoomIn = doc.createElement('button'); btnZoomIn.className = 'tiff-btn'; btnZoomIn.type = 'button'; btnZoomIn.title = 'Zoom in'; btnZoomIn.textContent = '+'; toolbar.appendChild(btnPrev); toolbar.appendChild(pageInfo); toolbar.appendChild(btnNext); toolbar.appendChild(spacer); toolbar.appendChild(btnZoomOut); toolbar.appendChild(zoomSelect); toolbar.appendChild(btnZoomIn); // Viewport with canvas var viewport = doc.createElement('div'); viewport.className = 'tiff-viewport'; var canvas = doc.createElement('canvas'); canvas.className = 'tiff-canvas'; viewport.appendChild(canvas); container.appendChild(toolbar); container.appendChild(viewport); // Render state var currentPage = 0; var zoom = 1; var fitMode = 'width'; // 'width' | 'page' | null var decoded = new Array(ifds.length); function decodePage(i) { if (decoded[i]) return decoded[i]; var ifd = ifds[i]; window.UTIF.decodeImage(arrayBuffer, ifd); var rgba = window.UTIF.toRGBA8(ifd); decoded[i] = { rgba: rgba, w: ifd.width, h: ifd.height }; return decoded[i]; } function applyZoom() { var page = decoded[currentPage]; if (!page) return; var availW = viewport.clientWidth - 32; // padding var availH = viewport.clientHeight - 32; var scale; if (fitMode === 'width') { scale = availW / page.w; } else if (fitMode === 'page') { scale = Math.min(availW / page.w, availH / page.h); } else { scale = zoom; } if (!isFinite(scale) || scale <= 0) scale = 1; canvas.style.width = (page.w * scale) + 'px'; canvas.style.height = (page.h * scale) + 'px'; } function renderPage() { var page; try { page = decodePage(currentPage); } catch (e) { container.innerHTML = '
Failed to decode page ' + (currentPage + 1) + ': ' + escapeHtml(e.message || e) + '
'; return; } canvas.width = page.w; canvas.height = page.h; var ctx = canvas.getContext('2d'); var imgData = ctx.createImageData(page.w, page.h); imgData.data.set(page.rgba); ctx.putImageData(imgData, 0, 0); applyZoom(); pageInput.value = String(currentPage + 1); btnPrev.disabled = currentPage <= 0; btnNext.disabled = currentPage >= ifds.length - 1; } function setZoomFromSelect() { var v = zoomSelect.value; if (v === 'fit-width') { fitMode = 'width'; } else if (v === 'fit-page') { fitMode = 'page'; } else { fitMode = null; zoom = parseFloat(v) || 1; } applyZoom(); } function nudgeZoom(factor) { if (fitMode) { // capture current effective scale before leaving fit mode var page = decoded[currentPage]; if (page) { var availW = viewport.clientWidth - 32; var availH = viewport.clientHeight - 32; zoom = fitMode === 'width' ? availW / page.w : Math.min(availW / page.w, availH / page.h); } else { zoom = 1; } fitMode = null; } zoom = Math.max(0.1, Math.min(8, zoom * factor)); // Match select option if any are close, else show as percent var matched = false; for (var i = 0; i < zoomSelect.options.length; i++) { var ov = zoomSelect.options[i].value; if (ov !== 'fit-width' && ov !== 'fit-page' && Math.abs(parseFloat(ov) - zoom) < 0.001) { zoomSelect.value = ov; matched = true; break; } } if (!matched) { // Nearest standard step var best = '1', bestDiff = Infinity; for (var j = 0; j < zoomSelect.options.length; j++) { var v2 = zoomSelect.options[j].value; if (v2 === 'fit-width' || v2 === 'fit-page') continue; var diff = Math.abs(parseFloat(v2) - zoom); if (diff < bestDiff) { bestDiff = diff; best = v2; } } zoom = parseFloat(best); zoomSelect.value = best; } applyZoom(); } btnPrev.addEventListener('click', function () { if (currentPage > 0) { currentPage--; renderPage(); } }); btnNext.addEventListener('click', function () { if (currentPage < ifds.length - 1) { currentPage++; renderPage(); } }); pageInput.addEventListener('change', function () { var n = parseInt(pageInput.value, 10); if (!isNaN(n) && n >= 1 && n <= ifds.length) { currentPage = n - 1; renderPage(); } else { pageInput.value = String(currentPage + 1); } }); zoomSelect.addEventListener('change', setZoomFromSelect); btnZoomIn.addEventListener('click', function () { nudgeZoom(1.25); }); btnZoomOut.addEventListener('click', function () { nudgeZoom(1 / 1.25); }); // Keyboard nav (only when toolbar/viewport in focus path) container.tabIndex = 0; container.addEventListener('keydown', function (e) { if (e.target === pageInput) return; if (e.key === 'ArrowLeft' || e.key === 'PageUp') { if (currentPage > 0) { currentPage--; renderPage(); e.preventDefault(); } } else if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ') { if (currentPage < ifds.length - 1) { currentPage++; renderPage(); e.preventDefault(); } } }); // Re-fit on viewport resize if (typeof (doc.defaultView && doc.defaultView.ResizeObserver) === 'function') { var ro = new doc.defaultView.ResizeObserver(function () { applyZoom(); }); ro.observe(viewport); } else if (doc.defaultView) { doc.defaultView.addEventListener('resize', function () { applyZoom(); }); } renderPage(); }); } // ── ZIP listing renderer ───────────────────────────────────────────────── var ZIP_CSS = '.zip-header{padding:.4rem .8rem;background:#f5f5f5;border-bottom:1px solid #ddd;' + 'font-size:.85rem;color:#444;}' + '.zip-table-wrap{flex:1;overflow:auto;}' + '.zip-table{width:100%;border-collapse:collapse;font-size:.85rem;font-family:' + '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;}' + '.zip-table thead th{position:sticky;top:0;background:#f0f0f0;text-align:left;' + 'padding:.4rem .6rem;border-bottom:1px solid #ccc;cursor:pointer;user-select:none;' + 'font-weight:600;}' + '.zip-table thead th:hover{background:#e6e6e6;}' + '.zip-table thead th.zip-sort-asc::after{content:" ▲";font-size:.7rem;color:#888;}' + '.zip-table thead th.zip-sort-desc::after{content:" ▼";font-size:.7rem;color:#888;}' + '.zip-table tbody td{padding:.3rem .6rem;border-bottom:1px solid #eee;}' + '.zip-table tbody tr:hover{background:#f6faff;}' + '.zip-table .zip-folder{color:#888;}' + '.zip-table .zip-name{color:#222;}' + '.zip-table .zip-size,.zip-table .zip-date{font-variant-numeric:tabular-nums;' + 'white-space:nowrap;color:#555;}' + '.zip-table .zip-col-size,.zip-table .zip-col-date{text-align:right;}' + '.zip-empty{padding:2rem;text-align:center;color:#888;}'; function renderZipListing(doc, container, arrayBuffer, opts) { opts = opts || {}; injectStyles(doc, 'zddc-zip-styles', ZIP_CSS); // JSZip is bundled in every tool that uses preview-lib (each // tool's build.sh concatenates shared/vendor/jszip.min.js). return window.JSZip.loadAsync(arrayBuffer).then(function (zip) { var entries = []; zip.forEach(function (relativePath, zipEntry) { if (zipEntry.dir) return; var size = (zipEntry._data && zipEntry._data.uncompressedSize) || 0; entries.push({ path: relativePath, name: relativePath.split('/').pop(), size: size, modified: zipEntry.date instanceof Date ? zipEntry.date : null }); }); container.innerHTML = ''; container.style.display = 'flex'; container.style.flexDirection = 'column'; container.style.minHeight = '0'; container.style.height = '100%'; container.style.overflow = 'hidden'; var totalSize = entries.reduce(function (s, e) { return s + e.size; }, 0); var header = doc.createElement('div'); header.className = 'zip-header'; header.textContent = entries.length + ' file' + (entries.length === 1 ? '' : 's') + (totalSize ? ' · ' + formatSize(totalSize) + ' uncompressed' : ''); container.appendChild(header); if (!entries.length) { var empty = doc.createElement('div'); empty.className = 'zip-empty'; empty.textContent = '(empty archive)'; container.appendChild(empty); return; } var wrap = doc.createElement('div'); wrap.className = 'zip-table-wrap'; var table = doc.createElement('table'); table.className = 'zip-table'; var thead = doc.createElement('thead'); var trh = doc.createElement('tr'); var cols = [ { key: 'path', label: 'Name', cls: 'zip-col-name' }, { key: 'size', label: 'Size', cls: 'zip-col-size' }, { key: 'modified', label: 'Modified', cls: 'zip-col-date' } ]; cols.forEach(function (c) { var th = doc.createElement('th'); th.className = c.cls; th.dataset.key = c.key; th.textContent = c.label; trh.appendChild(th); }); thead.appendChild(trh); table.appendChild(thead); var tbody = doc.createElement('tbody'); table.appendChild(tbody); wrap.appendChild(table); container.appendChild(wrap); var sortKey = 'path'; var sortDir = 1; function render() { var sorted = entries.slice().sort(function (a, b) { var av, bv; if (sortKey === 'size') { av = a.size; bv = b.size; } else if (sortKey === 'modified') { av = a.modified ? a.modified.getTime() : 0; bv = b.modified ? b.modified.getTime() : 0; } else { av = a.path.toLowerCase(); bv = b.path.toLowerCase(); } if (av < bv) return -1 * sortDir; if (av > bv) return 1 * sortDir; return 0; }); tbody.innerHTML = ''; sorted.forEach(function (e) { var tr = doc.createElement('tr'); var td1 = doc.createElement('td'); var slash = e.path.lastIndexOf('/'); if (slash >= 0) { var folder = doc.createElement('span'); folder.className = 'zip-folder'; folder.textContent = e.path.substring(0, slash + 1); td1.appendChild(folder); } var name = doc.createElement('span'); name.className = 'zip-name'; name.textContent = e.name; td1.appendChild(name); var td2 = doc.createElement('td'); td2.className = 'zip-size'; td2.textContent = formatSize(e.size); var td3 = doc.createElement('td'); td3.className = 'zip-date'; td3.textContent = formatDate(e.modified); tr.appendChild(td1); tr.appendChild(td2); tr.appendChild(td3); tbody.appendChild(tr); }); // Update sort arrows var ths = thead.querySelectorAll('th'); for (var i = 0; i < ths.length; i++) { ths[i].classList.remove('zip-sort-asc', 'zip-sort-desc'); if (ths[i].dataset.key === sortKey) { ths[i].classList.add(sortDir > 0 ? 'zip-sort-asc' : 'zip-sort-desc'); } } } thead.querySelectorAll('th').forEach(function (th) { th.addEventListener('click', function () { var k = th.dataset.key; if (sortKey === k) sortDir = -sortDir; else { sortKey = k; sortDir = 1; } render(); }); }); render(); }).catch(function (err) { container.innerHTML = '
Failed to read ZIP: ' + escapeHtml(err.message || err) + '
'; }); } // ── DOCX (docx-preview) ──────────────────────────────────────────────── // // docx-preview renders Word-styled pages with an intrinsic page width, so // we wrap it in a scroll container: a wide document scrolls WITHIN the // preview pane rather than pushing the page wider. docx-preview is bundled // by the tools that opt in (each build.sh concatenates // shared/vendor/docx-preview.min.js → window.docx). var DOCX_CSS = '.zddc-docx{height:100%;min-width:0;overflow:auto;' + 'background:var(--bg-secondary,#eee);padding:1rem;box-sizing:border-box;}' + '.zddc-docx .docx-wrapper{background:transparent;padding:0;}'; function renderDocx(doc, container, arrayBuffer, opts) { opts = opts || {}; injectStyles(doc, 'zddc-docx-styles', DOCX_CSS); if (!window.docx || typeof window.docx.renderAsync !== 'function') { container.innerHTML = '
DOCX preview unavailable ' + '(renderer not bundled in this tool).
'; return Promise.resolve(); } container.innerHTML = ''; var scroll = doc.createElement('div'); scroll.className = 'zddc-docx'; container.appendChild(scroll); return Promise.resolve( window.docx.renderAsync(arrayBuffer, scroll, null, { inWrapper: true }) ).catch(function (err) { container.innerHTML = '
' + 'Failed to render DOCX: ' + escapeHtml(err.message || err) + '
'; }); } // ── XLSX / XLS (SheetJS) ─────────────────────────────────────────────── // // Reads the workbook and renders the active sheet to an HTML table; a tab // bar switches sheets when there's more than one. SheetJS is bundled by // the tools that opt in (shared/vendor/xlsx.full.min.js → window.XLSX). var XLSX_CSS = '.zddc-xlsx{display:flex;flex-direction:column;height:100%;min-width:0;' + 'min-height:0;overflow:hidden;}' + '.zddc-xlsx__tabs{display:flex;flex-wrap:wrap;gap:0.25rem;padding:0.4rem;' + 'border-bottom:1px solid var(--border,#ccc);flex:0 0 auto;}' + '.zddc-xlsx__tab{padding:0.2rem 0.6rem;border:1px solid var(--border,#ccc);' + 'border-radius:4px;background:var(--bg,#fff);color:var(--text,#222);' + 'cursor:pointer;font-size:0.85rem;}' + '.zddc-xlsx__tab.is-active{background:var(--primary,#2563eb);color:#fff;' + 'border-color:var(--primary,#2563eb);}' + '.zddc-xlsx__body{flex:1 1 auto;min-width:0;min-height:0;overflow:auto;}' + '.zddc-xlsx__body table{border-collapse:collapse;font-size:0.85rem;' + 'color:var(--text,#222);}' + '.zddc-xlsx__body td,.zddc-xlsx__body th{border:1px solid var(--border,#ddd);' + 'padding:0.2rem 0.45rem;white-space:nowrap;}'; function renderXlsx(doc, container, arrayBuffer, opts) { opts = opts || {}; injectStyles(doc, 'zddc-xlsx-styles', XLSX_CSS); if (!window.XLSX || typeof window.XLSX.read !== 'function') { container.innerHTML = '
Spreadsheet preview unavailable ' + '(renderer not bundled in this tool).
'; return Promise.resolve(); } try { var wb = window.XLSX.read(arrayBuffer, { type: 'array' }); container.innerHTML = ''; var rootEl = doc.createElement('div'); rootEl.className = 'zddc-xlsx'; var body = doc.createElement('div'); body.className = 'zddc-xlsx__body'; function showSheet(name) { var sheet = wb.Sheets[name]; body.innerHTML = sheet ? window.XLSX.utils.sheet_to_html(sheet, { editable: false }) : ''; } if (wb.SheetNames.length > 1) { var tabs = doc.createElement('div'); tabs.className = 'zddc-xlsx__tabs'; wb.SheetNames.forEach(function (name, i) { var btn = doc.createElement('button'); btn.className = 'zddc-xlsx__tab' + (i === 0 ? ' is-active' : ''); btn.textContent = name; btn.addEventListener('click', function () { var all = tabs.querySelectorAll('.zddc-xlsx__tab'); for (var j = 0; j < all.length; j++) all[j].classList.remove('is-active'); btn.classList.add('is-active'); showSheet(name); }); tabs.appendChild(btn); }); rootEl.appendChild(tabs); } rootEl.appendChild(body); container.appendChild(rootEl); showSheet(wb.SheetNames[0]); } catch (err) { container.innerHTML = '
' + 'Failed to render spreadsheet: ' + escapeHtml(err.message || err) + '
'; } return Promise.resolve(); } // ── Public API ─────────────────────────────────────────────────────────── if (!root.zddc) root.zddc = {}; root.zddc.preview = { TIFF_EXTENSIONS: TIFF_EXTENSIONS, IMAGE_EXTENSIONS: IMAGE_EXTENSIONS, TEXT_EXTENSIONS: TEXT_EXTENSIONS, OFFICE_EXTENSIONS: OFFICE_EXTENSIONS, isTiff: isTiff, isImage: isImage, isText: isText, isZip: isZip, isOffice: isOffice, loadLibrary: loadLibrary, renderTiff: renderTiff, renderZipListing: renderZipListing, renderDocx: renderDocx, renderXlsx: renderXlsx, formatSize: formatSize, formatDate: formatDate }; })(typeof window !== 'undefined' ? window : this);