/* eslint-disable prettier/prettier */
import { MIN_X_OFFSET, MIN_Y_OFFSET, TW_DATA_TAG } from "./util.js";
import { ViewerType } from "./base_viewer.js";
/**
* Zoom service object
*/
class ZoomService {
constructor(app, initialZoom, isZoomFitToPage) {
this._app = app;
this._app.viewer.isZoomFitToPage = isZoomFitToPage || false;
this._initialZoomValue = initialZoom || 0;
this.zoomDropDownOpened = false;
this._bindEvents();
/**
* Available zoom levels for selected document and viewer dimensions.
*/
this.zoomLevels = [];
}
// #region private functions
_bindEvents() {
const me = this;
this._app.elements.tw2018elem_zoomDropDownValue.addEventListener(
"keydown",
function (e) {
switch (e.which || e.keyCode) {
case 13: {
let zoomValue = 0;
try {
zoomValue = parseFloat(
this.app.elements.tw2018elem_zoomDropDownValue.value
.replace("%", "")
.trim()
);
} catch (err) {}
if (
isNaN(zoomValue) ||
zoomValue < 10 ||
zoomValue > this._zoomService.getMaxZoomValue()
) {
me._app.elements.tw2018elem_zoomDropDownValue.value =
this.app.viewer.validZoomValue;
return;
}
me._app.viewer.isZoomFitToPage = false;
me.setZoomValue(zoomValue);
me.hideZoomDropDown();
}
}
}
);
this._app.elements.tw2018elem_zoomValuesDropDownClose.addEventListener(
"click",
function () {
me.hideZoomDropDown();
}
);
this._app.elements.tw2018elem_zoomDropDownArrow.addEventListener(
"click",
function (event) {
if (this.disabled) {
event.preventDefault();
return;
}
if (me.zoomDropDownOpened) {
me.hideZoomDropDown();
} else {
me.showZoomDropDown();
}
event.preventDefault();
}
);
me._app.elements.tw2018elem_zoominBtn.addEventListener(
"click",
function (event) {
if (!event.defaultPrevented) {
me.zoomIn();
}
}
);
me._app.elements.tw2018elem_zoomoutBtn.addEventListener(
"click",
function (event) {
if (!event.defaultPrevented) {
me.zoomOut();
}
}
);
}
hideZoomDropDown() {
this._app.elements.tw2018elem_zoomValuesDropDown.classList.remove(
"tw2018css_show"
);
this._app.elements.tw2018elem_zoomDropDownArrow.setAttribute(
"aria-expanded",
"false"
);
this.zoomDropDownOpened = false;
}
showZoomDropDown() {
const rectToolbar =
this._app.elements.tw2018elem_toolbar.getBoundingClientRect();
const rect =
this._app.elements.tw2018elem_zoomDropDownValue.getBoundingClientRect();
// const y = rectToolbar.height;
// const y = rect.bottom - 1;
const y = rect.top - rectToolbar.top + rect.height + 8;
const x = rect.left - rectToolbar.left - 1;
// this._app.elements.tw2018elem_zoomValues.style.top = y + "px";
// this._app.elements.tw2018elem_zoomValues.style.left = x + "px";
this._app.elements.tw2018elem_zoomValues.classList.add("tw2018css_show");
this._app.elements.tw2018elem_zoomValuesDropDown.style.top = y + "px";
this._app.elements.tw2018elem_zoomValuesDropDown.style.left = x + "px";
this._app.elements.tw2018elem_zoomValuesDropDown.classList.add(
"tw2018css_show"
);
this._app.elements.tw2018elem_zoomDropDownArrow.setAttribute(
"aria-expanded",
"true"
);
this.zoomDropDownOpened = true;
}
_updateToolbarZoomValue() {
this._app.elements.tw2018elem_zoominBtn.disabled =
this._app.viewer.currentZoomLevel === this.zoomLevels.length - 1;
this._app.elements.tw2018elem_zoomoutBtn.disabled =
this._app.viewer.currentZoomLevel === 0;
this._selectToolbarZoomDropDownValue();
}
_getToolbarZoomLi(scale, text) {
const li = document.createElement("li");
li.setAttribute("scale", scale);
const btn = document.createElement("button");
btn.innerText = text;
li.appendChild(btn);
// eslint-disable-next-line no-unsanitized/property
if (scale === this._app.viewer.scale) {
li.classList.add("tw2018css_selected");
li.setAttribute(TW_DATA_TAG, "");
li.setAttribute("aria-selected", "true");
}
const me = this;
me._app.elements.tw2018elem_zoomValues.appendChild(li);
btn.addEventListener("click", function () {
me._app.elements.tw2018elem_zoomDropDownValue.value = this.innerHTML;
const scaleVal = this.parentElement.getAttribute("scale");
me._app.viewer.scale =
scaleVal === "fit" ? me._app.viewer.fitToPage : parseFloat(scaleVal);
me._app.viewer.isZoomFitToPage =
me._app.viewer.scale === me._app.viewer.fitToPage;
me._app.viewer.currentZoomLevel = me.zoomLevels.indexOf(
me._app.viewer.scale
);
me.executeZoom();
me.hideZoomDropDown();
});
return li;
}
_fillToolbarZoomDropDownValues() {
const val = Math.round(this._app.viewer.scale * 100) + "%";
this._app.elements.tw2018elem_zoomDropDownValue.value = val;
this._app.viewer.validZoomValue = val;
this._app.elements.tw2018elem_zoomValues.innerHTML = "";
for (let i = 0; i < this.zoomLevels.length; i++) {
this._app.elements.tw2018elem_zoomValues.appendChild(
this._getToolbarZoomLi(
this.zoomLevels[i],
Math.round(this.zoomLevels[i] * 100) + "%"
)
);
}
const horizontalSeparator = document.createElement("hr");
horizontalSeparator.setAttribute(TW_DATA_TAG, "");
horizontalSeparator.classList.add("tw2018css_horizontalSeparator");
this._app.elements.tw2018elem_zoomValues.appendChild(horizontalSeparator);
this._app.elements.tw2018elem_zoomValues.appendChild(
this._getToolbarZoomLi(1, this._app.texts.zoom_actual_size)
);
const fitSize = this._getToolbarZoomLi(
"fit",
this._app.texts.zoom_fit_to_page
);
if (this._app.viewer.isZoomFitToPage) {
fitSize.classList.add("tw2018css_selected");
fitSize.setAttribute(TW_DATA_TAG, "");
}
this._app.elements.tw2018elem_zoomValues.appendChild(fitSize);
}
// In this method it isn't important what will be zoom level, we are just
// looking for the page with the biggest size
_getNumberOfPageWithBiggestSize() {
let numberOfBiggestPage = 0;
let minZoomFactor = 999999;
for (let i = 0; i < this._app.pdfDocument.pages.length; i++) {
const page = this._app.pdfDocument.pages[i];
const zoomFactorWidth = this._app.visibleAreaWidth / page.originalWidth;
const zoomFactorHeight =
this._app.visibleAreaHeight / page.originalHeight;
const pageZoom =
zoomFactorWidth < zoomFactorHeight ? zoomFactorWidth : zoomFactorHeight;
if (pageZoom <= minZoomFactor) {
minZoomFactor = pageZoom;
numberOfBiggestPage = i + 1;
}
}
return numberOfBiggestPage;
}
_getDisplayRatio() {
return 96 / 72;
}
_getOriginalDisplayTotalHeightAndWidthWithoutScrollBars() {
const pageSizesInfo = {
maxWidth: 0,
totalHeight: 0,
};
for (let i = 0; i < this._app.pdfDocument.pages.length; i++) {
const page = this._app.pdfDocument.pages[i];
pageSizesInfo.totalHeight += MIN_Y_OFFSET;
pageSizesInfo.totalHeight += page.originalHeight;
if (page.originalWidth > pageSizesInfo.maxWidth) {
pageSizesInfo.maxWidth = page.originalWidth;
}
}
pageSizesInfo.totalHeight += MIN_Y_OFFSET;
return pageSizesInfo;
}
_selectToolbarZoomDropDownValue() {
const val = Math.round(this._app.viewer.scale * 100) + "%";
this._app.elements.tw2018elem_zoomDropDownValue.value = val;
this._app.viewer.validZoomValue = val;
for (
let i = 0;
i < this._app.elements.tw2018elem_zoomValues.childElementCount;
i++
) {
const elem = this._app.elements.tw2018elem_zoomValues.children[i];
const scaleAttr = elem.getAttribute("scale");
if (
(scaleAttr === "fit" && this._app.viewer.isZoomFitToPage) ||
scaleAttr === this._app.viewer.scale
) {
elem.classList.add("tw2018css_selected");
} else {
elem.classList.remove("tw2018css_selected");
}
}
}
_checkIfVerticalScrollIsRequired(totalHeight, isHorizontalBarVisible) {
const horizontalScrollBarHeight = isHorizontalBarVisible ? 20 : 0;
return (
totalHeight + horizontalScrollBarHeight > this._app.visibleAreaHeight
);
}
_getFitToPageZoomLevel(totalHeight = -1) {
const numberOfPageWithBiggestSize = this._getNumberOfPageWithBiggestSize();
const biggestPage =
this._app.pdfDocument.pages[numberOfPageWithBiggestSize - 1];
const isVerticalScrollRequired = this._checkIfVerticalScrollIsRequired(
totalHeight,
false
); // horizontalScrollRequired is false because we are searching FitToPage zoom so there will not be horizontalScroll
const verticalScrollWidth = isVerticalScrollRequired ? 20 : 0;
const controlHeight = this._app.visibleAreaHeight - MIN_Y_OFFSET * 2;
const controlWidth =
this._app.visibleAreaWidth - verticalScrollWidth - MIN_X_OFFSET * 2;
const zoomFactorWidth = controlWidth / biggestPage.originalWidth;
const zoomFactorHeight = controlHeight / biggestPage.originalHeight;
return Math.min(zoomFactorWidth, zoomFactorHeight);
}
_setMaxZoomLevel() {
const numberOfPageWithBiggestSize = this._getNumberOfPageWithBiggestSize();
const maxPageWidth =
this._app.pdfDocument.pages[numberOfPageWithBiggestSize - 1]
.originalWidth;
const maxPageHeight =
this._app.pdfDocument.pages[numberOfPageWithBiggestSize - 1]
.originalHeight;
const maxBitmapSizeInBytes = 100 * 1024 * 1024;
const bytesPerPixel = 4; // 32 bit
const displayRatio = this._getDisplayRatio();
this._app.viewer.maxZoomLevel = Math.sqrt(
maxBitmapSizeInBytes /
bytesPerPixel /
(maxPageWidth * displayRatio * maxPageHeight * displayRatio)
);
}
// #endregion
// #region internal functions
useInitialZoomValueIfSet() {
if (!this._initialZoomValue) {
return false;
}
this.calculateZoom();
this.setZoomValue(this._initialZoomValue);
this._initialZoomValue = 0;
return true;
}
mouseWheelHandler(e) {
// cross-browser wheel data
const delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail));
if (
delta > 0 &&
this._app.viewer.currentZoomLevel < this.zoomLevels.length - 1
) {
// const oldValue = this.zoomLevels[this.viewer.currentZoomLevel];
this._app.viewer.currentZoomLevel++;
this.executeZoom();
}
if (delta < 0 && this._app.viewer.currentZoomLevel > 0) {
// const oldValue = this.zoomLevels[this.viewer.currentZoomLevel];
this._app.viewer.currentZoomLevel--;
this.executeZoom();
}
}
executeZoom(reloadDocument = true) {
this._app.viewer.tempPageNumber = this._app.viewer.currentPageNumber;
this._app.viewer.scale = this.zoomLevels[this._app.viewer.currentZoomLevel];
this._app.viewer.isZoomFitToPage =
this._app.viewer.scale === this._app.viewer.fitToPage;
this._updateToolbarZoomValue();
if (reloadDocument) {
this._app.renderPdfDocInViewer();
}
}
setZoomLevels(fitToPage, customZoom, maxZoomLevel) {
this.zoomLevels = [];
const customZoom1 = Math.min(fitToPage, customZoom);
const customZoom2 = Math.max(fitToPage, customZoom);
let customZoom1Added = false;
let customZoom2Added = false;
if (customZoom1 === customZoom2) {
customZoom1Added = true;
}
maxZoomLevel = (parseInt((maxZoomLevel * 100) / 50) * 50) / 100; // round max value to nearest 50
const defaultZoomLevels = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 4, 8, 16];
for (let i = 0; i < defaultZoomLevels.length; i++) {
const zoomLevel = defaultZoomLevels[i];
if (maxZoomLevel < zoomLevel) {
break;
}
if (!customZoom1Added && customZoom1 < zoomLevel) {
customZoom1Added = true;
if (customZoom1 > 0 && !this.zoomLevels.includes(customZoom1)) {
this.zoomLevels.push(customZoom1);
}
}
if (!customZoom2Added && customZoom2 < zoomLevel) {
customZoom2Added = true;
if (customZoom2 > 0 && !this.zoomLevels.includes(customZoom2)) {
this.zoomLevels.push(customZoom2);
}
}
if (!this.zoomLevels.includes(zoomLevel)) {
this.zoomLevels.push(zoomLevel);
}
}
if (
!customZoom1Added &&
customZoom1 < maxZoomLevel &&
customZoom1 > 0 &&
!this.zoomLevels.includes(customZoom1)
) {
this.zoomLevels.push(customZoom1);
}
if (
!customZoom2Added &&
customZoom2 < maxZoomLevel &&
customZoom2 > 0 &&
!this.zoomLevels.includes(customZoom2)
) {
this.zoomLevels.push(customZoom2);
}
if (!this.zoomLevels.includes(maxZoomLevel)) {
this.zoomLevels.push(maxZoomLevel);
}
}
fitToPageIfNeeded() {
this._app.viewer.fitToPage = this._getFitToPageZoomLevel();
if (this._app.viewer.isZoomFitToPage) {
this._app.viewer.scale = this._app.viewer.fitToPage;
this.setZoomLevels(
this._app.viewer.fitToPage,
this._app.viewer.scale,
this._app.viewer.maxZoomLevel
);
this._fillToolbarZoomDropDownValues();
this._app.viewer.currentZoomLevel = this.zoomLevels.indexOf(
this._app.viewer.scale
);
this.executeZoom();
}
}
calculateZoom() {
const heightArea =
this._app.viewer.type === ViewerType.MULTI_VIEW
? this._getOriginalDisplayTotalHeightAndWidthWithoutScrollBars()
: this._getNumberOfPageWithBiggestSize();
this._app.viewer.fitToPage = this._getFitToPageZoomLevel(
heightArea.totalHeight
);
this.setZoomLevels(
this._app.viewer.fitToPage,
this._app.viewer.scale,
this._app.viewer.maxZoomLevel
);
if (this._app.viewer.isZoomFitToPage) {
this._app.viewer.scale = this._app.viewer.fitToPage;
this._app.viewer.currentZoomLevel = this.zoomLevels.indexOf(
this._app.viewer.fitToPage
);
}
this._setMaxZoomLevel();
this.setZoomLevels(
this._app.viewer.fitToPage,
this._app.viewer.scale,
this._app.viewer.maxZoomLevel
);
this._fillToolbarZoomDropDownValues();
}
// #endregion
// #region public functions
/** Magnify displayed content size. */
zoomIn() {
if (this._app.viewer.currentZoomLevel === this.zoomLevels.length - 1) {
return;
}
// const oldValue = this.zoomLevels[this.viewer.currentZoomLevel];
this._app.viewer.currentZoomLevel++;
this.executeZoom();
}
//* Reduce displayed content size. */
zoomOut() {
if (this._app.viewer.currentZoomLevel === 0) {
return;
}
// const oldValue = this.zoomLevels[this.viewer.currentZoomLevel];
this._app.viewer.currentZoomLevel--;
this.executeZoom();
}
/** Sets and applies *zoomValue*.
* @param {number} zoomValue - Zoom value to set.
*/
setZoomValue(zoomValue) {
this._app.viewer.scale = zoomValue / 100;
if (this._app.viewer.isZoomFitToPage) {
this._app.viewer.scale = this._app.viewer.fitToPage;
}
this._app.viewer.currentZoomLevel = this.zoomLevels.indexOf(
this._app.viewer.scale
);
if (this._app.viewer.currentZoomLevel === -1) {
this.setZoomLevels(
this._app.viewer.fitToPage,
this._app.viewer.scale,
this._app.viewer.maxZoomLevel
);
this._app.viewer.currentZoomLevel = this.zoomLevels.indexOf(
this._app.viewer.scale
);
this._fillToolbarZoomDropDownValues();
}
this.executeZoom();
}
/** Returns max zoom value for loaded document and
* current viewer dimension size. */
getMaxZoomValue() {
return this.zoomLevels[this.zoomLevels.length - 1] * 100;
}
// #endregion
}
export { ZoomService };