| /** |
| * @licstart The following is the entire license notice for the |
| * Javascript code in this page |
| * |
| * Copyright 2020 Mozilla Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * @licend The above is the entire license notice for the |
| * Javascript code in this page |
| */ |
| "use strict"; |
| |
| Object.defineProperty(exports, "__esModule", { |
| value: true |
| }); |
| exports.isValidRotation = isValidRotation; |
| exports.isValidScrollMode = isValidScrollMode; |
| exports.isValidSpreadMode = isValidSpreadMode; |
| exports.isPortraitOrientation = isPortraitOrientation; |
| exports.clamp = clamp; |
| exports.getPDFFileNameFromURL = getPDFFileNameFromURL; |
| exports.noContextMenuHandler = noContextMenuHandler; |
| exports.parseQueryString = parseQueryString; |
| exports.backtrackBeforeAllVisibleElements = backtrackBeforeAllVisibleElements; |
| exports.getVisibleElements = getVisibleElements; |
| exports.roundToDivide = roundToDivide; |
| exports.getPageSizeInches = getPageSizeInches; |
| exports.approximateFraction = approximateFraction; |
| exports.getOutputScale = getOutputScale; |
| exports.scrollIntoView = scrollIntoView; |
| exports.watchScroll = watchScroll; |
| exports.binarySearchFirstItem = binarySearchFirstItem; |
| exports.normalizeWheelEventDirection = normalizeWheelEventDirection; |
| exports.normalizeWheelEventDelta = normalizeWheelEventDelta; |
| exports.waitOnEventOrTimeout = waitOnEventOrTimeout; |
| exports.moveToEndOfArray = moveToEndOfArray; |
| exports.WaitOnType = exports.animationStarted = exports.ProgressBar = exports.EventBus = exports.NullL10n = exports.SpreadMode = exports.ScrollMode = exports.TextLayerMode = exports.RendererType = exports.PresentationModeState = exports.VERTICAL_PADDING = exports.SCROLLBAR_PADDING = exports.MAX_AUTO_SCALE = exports.UNKNOWN_SCALE = exports.MAX_SCALE = exports.MIN_SCALE = exports.DEFAULT_SCALE = exports.DEFAULT_SCALE_VALUE = exports.CSS_UNITS = exports.AutoPrintRegExp = void 0; |
| const CSS_UNITS = 96.0 / 72.0; |
| exports.CSS_UNITS = CSS_UNITS; |
| const DEFAULT_SCALE_VALUE = "auto"; |
| exports.DEFAULT_SCALE_VALUE = DEFAULT_SCALE_VALUE; |
| const DEFAULT_SCALE = 1.0; |
| exports.DEFAULT_SCALE = DEFAULT_SCALE; |
| const MIN_SCALE = 0.1; |
| exports.MIN_SCALE = MIN_SCALE; |
| const MAX_SCALE = 10.0; |
| exports.MAX_SCALE = MAX_SCALE; |
| const UNKNOWN_SCALE = 0; |
| exports.UNKNOWN_SCALE = UNKNOWN_SCALE; |
| const MAX_AUTO_SCALE = 1.25; |
| exports.MAX_AUTO_SCALE = MAX_AUTO_SCALE; |
| const SCROLLBAR_PADDING = 40; |
| exports.SCROLLBAR_PADDING = SCROLLBAR_PADDING; |
| const VERTICAL_PADDING = 5; |
| exports.VERTICAL_PADDING = VERTICAL_PADDING; |
| const PresentationModeState = { |
| UNKNOWN: 0, |
| NORMAL: 1, |
| CHANGING: 2, |
| FULLSCREEN: 3 |
| }; |
| exports.PresentationModeState = PresentationModeState; |
| const RendererType = { |
| CANVAS: "canvas", |
| SVG: "svg" |
| }; |
| exports.RendererType = RendererType; |
| const TextLayerMode = { |
| DISABLE: 0, |
| ENABLE: 1, |
| ENABLE_ENHANCE: 2 |
| }; |
| exports.TextLayerMode = TextLayerMode; |
| const ScrollMode = { |
| UNKNOWN: -1, |
| VERTICAL: 0, |
| HORIZONTAL: 1, |
| WRAPPED: 2 |
| }; |
| exports.ScrollMode = ScrollMode; |
| const SpreadMode = { |
| UNKNOWN: -1, |
| NONE: 0, |
| ODD: 1, |
| EVEN: 2 |
| }; |
| exports.SpreadMode = SpreadMode; |
| const AutoPrintRegExp = /\bprint\s*\(/; |
| exports.AutoPrintRegExp = AutoPrintRegExp; |
| |
| function formatL10nValue(text, args) { |
| if (!args) { |
| return text; |
| } |
| |
| return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (all, name) => { |
| return name in args ? args[name] : "{{" + name + "}}"; |
| }); |
| } |
| |
| const NullL10n = { |
| async getLanguage() { |
| return "en-us"; |
| }, |
| |
| async getDirection() { |
| return "ltr"; |
| }, |
| |
| async get(property, args, fallback) { |
| return formatL10nValue(fallback, args); |
| }, |
| |
| async translate(element) {} |
| |
| }; |
| exports.NullL10n = NullL10n; |
| |
| function getOutputScale(ctx) { |
| const devicePixelRatio = window.devicePixelRatio || 1; |
| const backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; |
| const pixelRatio = devicePixelRatio / backingStoreRatio; |
| return { |
| sx: pixelRatio, |
| sy: pixelRatio, |
| scaled: pixelRatio !== 1 |
| }; |
| } |
| |
| function scrollIntoView(element, spot, skipOverflowHiddenElements = false) { |
| let parent = element.offsetParent; |
| |
| if (!parent) { |
| console.error("offsetParent is not set -- cannot scroll"); |
| return; |
| } |
| |
| let offsetY = element.offsetTop + element.clientTop; |
| let offsetX = element.offsetLeft + element.clientLeft; |
| |
| while (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth || skipOverflowHiddenElements && getComputedStyle(parent).overflow === "hidden") { |
| if (parent.dataset._scaleY) { |
| offsetY /= parent.dataset._scaleY; |
| offsetX /= parent.dataset._scaleX; |
| } |
| |
| offsetY += parent.offsetTop; |
| offsetX += parent.offsetLeft; |
| parent = parent.offsetParent; |
| |
| if (!parent) { |
| return; |
| } |
| } |
| |
| if (spot) { |
| if (spot.top !== undefined) { |
| offsetY += spot.top; |
| } |
| |
| if (spot.left !== undefined) { |
| offsetX += spot.left; |
| parent.scrollLeft = offsetX; |
| } |
| } |
| |
| parent.scrollTop = offsetY; |
| } |
| |
| function watchScroll(viewAreaElement, callback) { |
| const debounceScroll = function (evt) { |
| if (rAF) { |
| return; |
| } |
| |
| rAF = window.requestAnimationFrame(function viewAreaElementScrolled() { |
| rAF = null; |
| const currentX = viewAreaElement.scrollLeft; |
| const lastX = state.lastX; |
| |
| if (currentX !== lastX) { |
| state.right = currentX > lastX; |
| } |
| |
| state.lastX = currentX; |
| const currentY = viewAreaElement.scrollTop; |
| const lastY = state.lastY; |
| |
| if (currentY !== lastY) { |
| state.down = currentY > lastY; |
| } |
| |
| state.lastY = currentY; |
| callback(state); |
| }); |
| }; |
| |
| const state = { |
| right: true, |
| down: true, |
| lastX: viewAreaElement.scrollLeft, |
| lastY: viewAreaElement.scrollTop, |
| _eventHandler: debounceScroll |
| }; |
| let rAF = null; |
| viewAreaElement.addEventListener("scroll", debounceScroll, true); |
| return state; |
| } |
| |
| function parseQueryString(query) { |
| const parts = query.split("&"); |
| const params = Object.create(null); |
| |
| for (let i = 0, ii = parts.length; i < ii; ++i) { |
| const param = parts[i].split("="); |
| const key = param[0].toLowerCase(); |
| const value = param.length > 1 ? param[1] : null; |
| params[decodeURIComponent(key)] = decodeURIComponent(value); |
| } |
| |
| return params; |
| } |
| |
| function binarySearchFirstItem(items, condition) { |
| let minIndex = 0; |
| let maxIndex = items.length - 1; |
| |
| if (maxIndex < 0 || !condition(items[maxIndex])) { |
| return items.length; |
| } |
| |
| if (condition(items[minIndex])) { |
| return minIndex; |
| } |
| |
| while (minIndex < maxIndex) { |
| const currentIndex = minIndex + maxIndex >> 1; |
| const currentItem = items[currentIndex]; |
| |
| if (condition(currentItem)) { |
| maxIndex = currentIndex; |
| } else { |
| minIndex = currentIndex + 1; |
| } |
| } |
| |
| return minIndex; |
| } |
| |
| function approximateFraction(x) { |
| if (Math.floor(x) === x) { |
| return [x, 1]; |
| } |
| |
| const xinv = 1 / x; |
| const limit = 8; |
| |
| if (xinv > limit) { |
| return [1, limit]; |
| } else if (Math.floor(xinv) === xinv) { |
| return [1, xinv]; |
| } |
| |
| const x_ = x > 1 ? xinv : x; |
| let a = 0, |
| b = 1, |
| c = 1, |
| d = 1; |
| |
| while (true) { |
| const p = a + c, |
| q = b + d; |
| |
| if (q > limit) { |
| break; |
| } |
| |
| if (x_ <= p / q) { |
| c = p; |
| d = q; |
| } else { |
| a = p; |
| b = q; |
| } |
| } |
| |
| let result; |
| |
| if (x_ - a / b < c / d - x_) { |
| result = x_ === x ? [a, b] : [b, a]; |
| } else { |
| result = x_ === x ? [c, d] : [d, c]; |
| } |
| |
| return result; |
| } |
| |
| function roundToDivide(x, div) { |
| const r = x % div; |
| return r === 0 ? x : Math.round(x - r + div); |
| } |
| |
| function getPageSizeInches({ |
| view, |
| userUnit, |
| rotate |
| }) { |
| const [x1, y1, x2, y2] = view; |
| const changeOrientation = rotate % 180 !== 0; |
| const width = (x2 - x1) / 72 * userUnit; |
| const height = (y2 - y1) / 72 * userUnit; |
| return { |
| width: changeOrientation ? height : width, |
| height: changeOrientation ? width : height |
| }; |
| } |
| |
| function backtrackBeforeAllVisibleElements(index, views, top) { |
| if (index < 2) { |
| return index; |
| } |
| |
| let elt = views[index].div; |
| let pageTop = elt.offsetTop + elt.clientTop; |
| |
| if (pageTop >= top) { |
| elt = views[index - 1].div; |
| pageTop = elt.offsetTop + elt.clientTop; |
| } |
| |
| for (let i = index - 2; i >= 0; --i) { |
| elt = views[i].div; |
| |
| if (elt.offsetTop + elt.clientTop + elt.clientHeight <= pageTop) { |
| break; |
| } |
| |
| index = i; |
| } |
| |
| return index; |
| } |
| |
| function getVisibleElements(scrollEl, views, sortByVisibility = false, horizontal = false) { |
| const top = scrollEl.scrollTop, |
| bottom = top + scrollEl.clientHeight; |
| const left = scrollEl.scrollLeft, |
| right = left + scrollEl.clientWidth; |
| |
| function isElementBottomAfterViewTop(view) { |
| const element = view.div; |
| const elementBottom = element.offsetTop + element.clientTop + element.clientHeight; |
| return elementBottom > top; |
| } |
| |
| function isElementRightAfterViewLeft(view) { |
| const element = view.div; |
| const elementRight = element.offsetLeft + element.clientLeft + element.clientWidth; |
| return elementRight > left; |
| } |
| |
| const visible = [], |
| numViews = views.length; |
| let firstVisibleElementInd = numViews === 0 ? 0 : binarySearchFirstItem(views, horizontal ? isElementRightAfterViewLeft : isElementBottomAfterViewTop); |
| |
| if (firstVisibleElementInd > 0 && firstVisibleElementInd < numViews && !horizontal) { |
| firstVisibleElementInd = backtrackBeforeAllVisibleElements(firstVisibleElementInd, views, top); |
| } |
| |
| let lastEdge = horizontal ? right : -1; |
| |
| for (let i = firstVisibleElementInd; i < numViews; i++) { |
| const view = views[i], |
| element = view.div; |
| const currentWidth = element.offsetLeft + element.clientLeft; |
| const currentHeight = element.offsetTop + element.clientTop; |
| const viewWidth = element.clientWidth, |
| viewHeight = element.clientHeight; |
| const viewRight = currentWidth + viewWidth; |
| const viewBottom = currentHeight + viewHeight; |
| |
| if (lastEdge === -1) { |
| if (viewBottom >= bottom) { |
| lastEdge = viewBottom; |
| } |
| } else if ((horizontal ? currentWidth : currentHeight) > lastEdge) { |
| break; |
| } |
| |
| if (viewBottom <= top || currentHeight >= bottom || viewRight <= left || currentWidth >= right) { |
| continue; |
| } |
| |
| const hiddenHeight = Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom); |
| const hiddenWidth = Math.max(0, left - currentWidth) + Math.max(0, viewRight - right); |
| const percent = (viewHeight - hiddenHeight) * (viewWidth - hiddenWidth) * 100 / viewHeight / viewWidth | 0; |
| visible.push({ |
| id: view.id, |
| x: currentWidth, |
| y: currentHeight, |
| view, |
| percent |
| }); |
| } |
| |
| const first = visible[0], |
| last = visible[visible.length - 1]; |
| |
| if (sortByVisibility) { |
| visible.sort(function (a, b) { |
| const pc = a.percent - b.percent; |
| |
| if (Math.abs(pc) > 0.001) { |
| return -pc; |
| } |
| |
| return a.id - b.id; |
| }); |
| } |
| |
| return { |
| first, |
| last, |
| views: visible |
| }; |
| } |
| |
| function noContextMenuHandler(evt) { |
| evt.preventDefault(); |
| } |
| |
| function isDataSchema(url) { |
| let i = 0; |
| const ii = url.length; |
| |
| while (i < ii && url[i].trim() === "") { |
| i++; |
| } |
| |
| return url.substring(i, i + 5).toLowerCase() === "data:"; |
| } |
| |
| function getPDFFileNameFromURL(url, defaultFilename = "document.pdf") { |
| if (typeof url !== "string") { |
| return defaultFilename; |
| } |
| |
| if (isDataSchema(url)) { |
| console.warn("getPDFFileNameFromURL: " + 'ignoring "data:" URL for performance reasons.'); |
| return defaultFilename; |
| } |
| |
| const reURI = /^(?:(?:[^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/; |
| const reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i; |
| const splitURI = reURI.exec(url); |
| let suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]); |
| |
| if (suggestedFilename) { |
| suggestedFilename = suggestedFilename[0]; |
| |
| if (suggestedFilename.includes("%")) { |
| try { |
| suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0]; |
| } catch (ex) {} |
| } |
| } |
| |
| return suggestedFilename || defaultFilename; |
| } |
| |
| function normalizeWheelEventDirection(evt) { |
| let delta = Math.sqrt(evt.deltaX * evt.deltaX + evt.deltaY * evt.deltaY); |
| const angle = Math.atan2(evt.deltaY, evt.deltaX); |
| |
| if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) { |
| delta = -delta; |
| } |
| |
| return delta; |
| } |
| |
| function normalizeWheelEventDelta(evt) { |
| let delta = normalizeWheelEventDirection(evt); |
| const MOUSE_DOM_DELTA_PIXEL_MODE = 0; |
| const MOUSE_DOM_DELTA_LINE_MODE = 1; |
| const MOUSE_PIXELS_PER_LINE = 30; |
| const MOUSE_LINES_PER_PAGE = 30; |
| |
| if (evt.deltaMode === MOUSE_DOM_DELTA_PIXEL_MODE) { |
| delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE; |
| } else if (evt.deltaMode === MOUSE_DOM_DELTA_LINE_MODE) { |
| delta /= MOUSE_LINES_PER_PAGE; |
| } |
| |
| return delta; |
| } |
| |
| function isValidRotation(angle) { |
| return Number.isInteger(angle) && angle % 90 === 0; |
| } |
| |
| function isValidScrollMode(mode) { |
| return Number.isInteger(mode) && Object.values(ScrollMode).includes(mode) && mode !== ScrollMode.UNKNOWN; |
| } |
| |
| function isValidSpreadMode(mode) { |
| return Number.isInteger(mode) && Object.values(SpreadMode).includes(mode) && mode !== SpreadMode.UNKNOWN; |
| } |
| |
| function isPortraitOrientation(size) { |
| return size.width <= size.height; |
| } |
| |
| const WaitOnType = { |
| EVENT: "event", |
| TIMEOUT: "timeout" |
| }; |
| exports.WaitOnType = WaitOnType; |
| |
| function waitOnEventOrTimeout({ |
| target, |
| name, |
| delay = 0 |
| }) { |
| return new Promise(function (resolve, reject) { |
| if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) { |
| throw new Error("waitOnEventOrTimeout - invalid parameters."); |
| } |
| |
| function handler(type) { |
| if (target instanceof EventBus) { |
| target._off(name, eventHandler); |
| } else { |
| target.removeEventListener(name, eventHandler); |
| } |
| |
| if (timeout) { |
| clearTimeout(timeout); |
| } |
| |
| resolve(type); |
| } |
| |
| const eventHandler = handler.bind(null, WaitOnType.EVENT); |
| |
| if (target instanceof EventBus) { |
| target._on(name, eventHandler); |
| } else { |
| target.addEventListener(name, eventHandler); |
| } |
| |
| const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT); |
| const timeout = setTimeout(timeoutHandler, delay); |
| }); |
| } |
| |
| const animationStarted = new Promise(function (resolve) { |
| window.requestAnimationFrame(resolve); |
| }); |
| exports.animationStarted = animationStarted; |
| |
| function dispatchDOMEvent(eventName, args = null) { |
| throw new Error("Not implemented: dispatchDOMEvent"); |
| } |
| |
| class EventBus { |
| constructor(options) { |
| this._listeners = Object.create(null); |
| } |
| |
| on(eventName, listener) { |
| this._on(eventName, listener, { |
| external: true |
| }); |
| } |
| |
| off(eventName, listener) { |
| this._off(eventName, listener, { |
| external: true |
| }); |
| } |
| |
| dispatch(eventName) { |
| const eventListeners = this._listeners[eventName]; |
| |
| if (!eventListeners || eventListeners.length === 0) { |
| return; |
| } |
| |
| const args = Array.prototype.slice.call(arguments, 1); |
| let externalListeners; |
| eventListeners.slice(0).forEach(function ({ |
| listener, |
| external |
| }) { |
| if (external) { |
| if (!externalListeners) { |
| externalListeners = []; |
| } |
| |
| externalListeners.push(listener); |
| return; |
| } |
| |
| listener.apply(null, args); |
| }); |
| |
| if (externalListeners) { |
| externalListeners.forEach(function (listener) { |
| listener.apply(null, args); |
| }); |
| externalListeners = null; |
| } |
| } |
| |
| _on(eventName, listener, options = null) { |
| let eventListeners = this._listeners[eventName]; |
| |
| if (!eventListeners) { |
| this._listeners[eventName] = eventListeners = []; |
| } |
| |
| eventListeners.push({ |
| listener, |
| external: (options && options.external) === true |
| }); |
| } |
| |
| _off(eventName, listener, options = null) { |
| const eventListeners = this._listeners[eventName]; |
| |
| if (!eventListeners) { |
| return; |
| } |
| |
| for (let i = 0, ii = eventListeners.length; i < ii; i++) { |
| if (eventListeners[i].listener === listener) { |
| eventListeners.splice(i, 1); |
| return; |
| } |
| } |
| } |
| |
| } |
| |
| exports.EventBus = EventBus; |
| |
| function clamp(v, min, max) { |
| return Math.min(Math.max(v, min), max); |
| } |
| |
| class ProgressBar { |
| constructor(id, { |
| height, |
| width, |
| units |
| } = {}) { |
| this.visible = true; |
| this.div = document.querySelector(id + " .progress"); |
| this.bar = this.div.parentNode; |
| this.height = height || 100; |
| this.width = width || 100; |
| this.units = units || "%"; |
| this.div.style.height = this.height + this.units; |
| this.percent = 0; |
| } |
| |
| _updateBar() { |
| if (this._indeterminate) { |
| this.div.classList.add("indeterminate"); |
| this.div.style.width = this.width + this.units; |
| return; |
| } |
| |
| this.div.classList.remove("indeterminate"); |
| const progressSize = this.width * this._percent / 100; |
| this.div.style.width = progressSize + this.units; |
| } |
| |
| get percent() { |
| return this._percent; |
| } |
| |
| set percent(val) { |
| this._indeterminate = isNaN(val); |
| this._percent = clamp(val, 0, 100); |
| |
| this._updateBar(); |
| } |
| |
| setWidth(viewer) { |
| if (!viewer) { |
| return; |
| } |
| |
| const container = viewer.parentNode; |
| const scrollbarWidth = container.offsetWidth - viewer.offsetWidth; |
| |
| if (scrollbarWidth > 0) { |
| this.bar.style.width = `calc(100% - ${scrollbarWidth}px)`; |
| } |
| } |
| |
| hide() { |
| if (!this.visible) { |
| return; |
| } |
| |
| this.visible = false; |
| this.bar.classList.add("hidden"); |
| document.body.classList.remove("loadingInProgress"); |
| } |
| |
| show() { |
| if (this.visible) { |
| return; |
| } |
| |
| this.visible = true; |
| document.body.classList.add("loadingInProgress"); |
| this.bar.classList.remove("hidden"); |
| } |
| |
| } |
| |
| exports.ProgressBar = ProgressBar; |
| |
| function moveToEndOfArray(arr, condition) { |
| const moved = [], |
| len = arr.length; |
| let write = 0; |
| |
| for (let read = 0; read < len; ++read) { |
| if (condition(arr[read])) { |
| moved.push(arr[read]); |
| } else { |
| arr[write] = arr[read]; |
| ++write; |
| } |
| } |
| |
| for (let read = 0; write < len; ++read, ++write) { |
| arr[write] = moved[read]; |
| } |
| } |