hugo-toha/assets/scripts/features/embedpdf/index.js

171 lines
4.2 KiB
JavaScript

import { insertScript } from '../../core';
const PDFJS_BUNDLE = "https://cdn.jsdelivr.net/npm/pdfjs-dist@3.0.279/build/pdf.min.js";
const WORKER_BUNDLE = "https://cdn.jsdelivr.net/npm/pdfjs-dist@3.0.279/build/pdf.worker.min.js";
class PDFViewer {
constructor(el) {
const {
url,
hidePaginator,
hideLoader,
scale,
pageNum
} = el.dataset;
if(url == null) {
throw new Error('Cannot load PDF! Attribute `data-url` is not set.');
}
// props
this.url = url;
this.hidePaginator = hidePaginator !== 'false';
this.hideLoader = hideLoader !== 'false';
this.scale = scale || 3;
// initial state
this.pageNum = parseInt(pageNum ,10) || 1;
this.loaded = false;
this.pageRendering = false;
this.pageNumPending = null;
// DOM elements
this.$canvas = el.getElementsByClassName('pdf-canvas')[0];
if(this.$canvas == null) {
throw new Error('canvas element not found!');
};
this.$paginator = el.getElementsByClassName("paginator")[0];
this.$loadingWrapper = el.getElementsByClassName('loading-wrapper')[0];
this.$next = el.getElementsByClassName('next')[0];
this.$prev = el.getElementsByClassName('prev')[0];
this.$pageNum = el.getElementsByClassName('page-num')[0];
this.$pageCount = el.getElementsByClassName('page-count')[0];
// context
this.ctx = this.$canvas.getContext('2d');
// events
this.$next.addEventListener('click', this.handleNextPage.bind(this));
this.$prev.addEventListener('click', this.handlePrevPage.bind(this));
this.showPaginator();
this.showLoader();
this.loadPDF();
}
/**
* If we haven't disabled the loader, show loader and hide canvas
*/
showLoader() {
if(this.hideLoader) return;
this.$loadingWrapper.style.display = 'flex';
this.$canvas.style.display = 'none';
}
/**
* If we haven't disabled the paginator, show paginator
*/
showPaginator() {
if(this.hidePaginator) return;
this.$paginator.style.display = 'block';
}
/**
* Hides loader and shows canvas
*/
showContent() {
this.$loadingWrapper.style.display = 'none';
this.$canvas.style.display = 'block';
}
/**
* Asynchronously downloads PDF.
*/
async loadPDF() {
this.pdfDoc = await window.pdfjsLib.getDocument(this.url).promise;
this.$pageCount.textContent = this.pdfDoc.numPages;
// If the user passed in a number that is out of range, render the last page.
if(this.pageNum > this.pdfDoc.numPages) {
this.pageNum = this.pdfDoc.numPages
}
this.renderPage(this.pageNum);
}
/**
* Get page info from document, resize canvas accordingly, and render page.
* @param num Page number.
*/
async renderPage(num) {
this.pageRendering = true;
const page = await this.pdfDoc.getPage(num);
const viewport = page.getViewport({ scale: this.scale });
this.$canvas.height = viewport.height;
this.$canvas.width = viewport.width;
// Wait for rendering to finish
await page.render({
canvasContext: this.ctx,
viewport,
}).promise;
this.pageRendering = false;
this.showContent();
if (this.pageNumPending !== null) {
// New page rendering is pending
this.renderPage(this.pageNumPending);
this.pageNumPending = null;
}
// Update page counters
this.$pageNum.textContent = num;
}
/**
* If another page rendering in progress, waits until the rendering is
* finished. Otherwise, executes rendering immediately.
*/
queueRenderPage(num) {
if (this.pageRendering) {
this.pageNumPending = num;
} else {
this.renderPage(num);
}
}
/**
* Displays previous page.
*/
handlePrevPage() {
if (this.pageNum <= 1) {
return;
}
this.pageNum--;
this.queueRenderPage(this.pageNum);
}
/**
* Displays next page.
*/
handleNextPage() {
if (this.pageNum >= this.pdfDoc.numPages) {
return;
}
this.pageNum++;
this.queueRenderPage(this.pageNum);
}
}
insertScript('pdfjs', PDFJS_BUNDLE, () => {
window.pdfjsLib.GlobalWorkerOptions.workerSrc = WORKER_BUNDLE;
Array.from(document.getElementsByClassName('pdf-viewer')).forEach(el => {
console.log(el);
new PDFViewer(el);
});
});