import { ScriptLoader } from './script-loader.js'; import { ViewerApi } from './viewer-api.js'; import { ViewerProgressBar } from './viewer-progress-bar.js'; export class FileViewer { constructor() { const params = new URLSearchParams(window.location.search); this.id = params.get('id'); this.name = params.get('name') || 'Dokumen'; this.type = (params.get('type') || 'pdf').toLowerCase(); this.titleEl = document.getElementById('viewer-title'); this.typeBadgeEl = document.getElementById('viewer-type'); this.downloadEl = document.getElementById('viewer-download'); this.contentEl = document.getElementById('viewer-content'); this.assetUrl = this.id ? ViewerApi.assetUrl(this.id) : null; } async init() { if (!this.id) { this._renderError('Parameter berkas tidak ditemukan di URL.'); return; } document.title = `${this.name} — Pratinjau Dokumen`; this.titleEl.textContent = this.name; this.typeBadgeEl.textContent = this.type.toUpperCase(); this._setupDownload(); try { if (this.type === 'docx') { await this._renderDocx(); } else { this._renderPdf(); } } catch (err) { console.error(err); this._renderError('Gagal memuat pratinjau berkas. Silakan unduh berkas untuk membuka secara lokal.'); } } _setupDownload() { const ext = `.${this.type}`; const filename = this.name.toLowerCase().endsWith(ext) ? this.name : `${this.name}${ext}`; this.downloadEl.removeAttribute('href'); this.downloadEl.style.cursor = 'pointer'; this.downloadEl.addEventListener('click', async event => { event.preventDefault(); const originalHtml = this.downloadEl.innerHTML; this.downloadEl.innerHTML = ' Mengunduh…'; this.downloadEl.style.pointerEvents = 'none'; try { const blob = await ViewerApi.fetchBlob(this.assetUrl); const blobUrl = URL.createObjectURL(blob); const link = document.createElement('a'); link.style.display = 'none'; link.href = blobUrl; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(blobUrl); } catch (err) { console.warn('Blob download failed, falling back:', err); window.open(`${this.assetUrl}?download`, '_blank'); } finally { this.downloadEl.innerHTML = originalHtml; this.downloadEl.style.pointerEvents = ''; } }); } _renderPdf() { const iframe = document.createElement('iframe'); iframe.className = 'viewer-pdf-frame'; iframe.title = this.name; iframe.src = this.assetUrl; this.contentEl.replaceChildren(iframe); } async _renderDocx() { const progressBar = new ViewerProgressBar(this.contentEl); const [blob] = await Promise.all([ ViewerApi.fetchBlob(this.assetUrl, ratio => progressBar.update(ratio)), this._loadDocxLibraries(), ]); progressBar.finish(); if (!window.docx || typeof window.docx.renderAsync !== 'function') { throw new Error('Library docx-preview belum dimuat.'); } const container = document.createElement('div'); container.className = 'viewer-docx-content'; this.contentEl.replaceChildren(container); await window.docx.renderAsync(blob, container, null, { inWrapper: true, ignoreWidth: false, ignoreHeight: false, }); } async _loadDocxLibraries() { if (window.docx) return; await ScriptLoader.load('https://unpkg.com/jszip@3.10.1/dist/jszip.min.js'); await ScriptLoader.load('https://unpkg.com/docx-preview@0.3.5/dist/docx-preview.js'); } _renderError(message) { const wrapper = document.createElement('div'); wrapper.className = 'viewer-error'; const icon = document.createElement('i'); icon.className = 'fas fa-exclamation-triangle'; const span = document.createElement('span'); span.textContent = message; const back = document.createElement('a'); back.href = 'index.html#repository'; back.className = 'btn-back-mini'; back.textContent = 'Kembali ke Repositori'; wrapper.append(icon, span, back); this.contentEl.replaceChildren(wrapper); } }