From f6369fc87c7211cc0324f2774edf31441aac4937 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 28 Jun 2017 12:34:12 +0200 Subject: [PATCH] ES6-ify the code in `web/ui_utils.js` These changes consists mainly of replacing `var` with `let`/`const`, adding a couple of default parameters to function signatures, and finally converting `EventBus`/`ProgressBar` to proper classes. --- web/app.js | 2 +- web/ui_utils.js | 330 ++++++++++++++++++++++++------------------------ 2 files changed, 163 insertions(+), 169 deletions(-) diff --git a/web/app.js b/web/app.js index af8317e3d..27931fcb7 100644 --- a/web/app.js +++ b/web/app.js @@ -507,7 +507,7 @@ var PDFViewerApplication = { }, get loadingBar() { - var bar = new ProgressBar('#loadingBar', {}); + var bar = new ProgressBar('#loadingBar'); return shadow(this, 'loadingBar', bar); }, diff --git a/web/ui_utils.js b/web/ui_utils.js index 7ecf5efc5..df6702ca4 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -15,17 +15,17 @@ import { PDFJS } from 'pdfjs-lib'; -var CSS_UNITS = 96.0 / 72.0; -var DEFAULT_SCALE_VALUE = 'auto'; -var DEFAULT_SCALE = 1.0; -var MIN_SCALE = 0.25; -var MAX_SCALE = 10.0; -var UNKNOWN_SCALE = 0; -var MAX_AUTO_SCALE = 1.25; -var SCROLLBAR_PADDING = 40; -var VERTICAL_PADDING = 5; +const CSS_UNITS = 96.0 / 72.0; +const DEFAULT_SCALE_VALUE = 'auto'; +const DEFAULT_SCALE = 1.0; +const MIN_SCALE = 0.25; +const MAX_SCALE = 10.0; +const UNKNOWN_SCALE = 0; +const MAX_AUTO_SCALE = 1.25; +const SCROLLBAR_PADDING = 40; +const VERTICAL_PADDING = 5; -var RendererType = { +const RendererType = { CANVAS: 'canvas', SVG: 'svg', }; @@ -44,7 +44,7 @@ function formatL10nValue(text, args) { * No-op implemetation of the localization service. * @implements {IL10n} */ -var NullL10n = { +let NullL10n = { get(property, args, fallback) { return Promise.resolve(formatL10nValue(fallback, args)); }, @@ -115,13 +115,13 @@ if (typeof PDFJSDev === 'undefined' || not required, true otherwise. */ function getOutputScale(ctx) { - var devicePixelRatio = window.devicePixelRatio || 1; - var backingStoreRatio = ctx.webkitBackingStorePixelRatio || + let devicePixelRatio = window.devicePixelRatio || 1; + let backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; - var pixelRatio = devicePixelRatio / backingStoreRatio; + let pixelRatio = devicePixelRatio / backingStoreRatio; return { sx: pixelRatio, sy: pixelRatio, @@ -137,20 +137,20 @@ function getOutputScale(ctx) { * @param {boolean} skipOverflowHiddenElements - Ignore elements that have * the CSS rule `overflow: hidden;` set. The default is false. */ -function scrollIntoView(element, spot, skipOverflowHiddenElements) { +function scrollIntoView(element, spot, skipOverflowHiddenElements = false) { // Assuming offsetParent is available (it's not available when viewer is in // hidden iframe or object). We have to scroll: if the offsetParent is not set // producing the error. See also animationStarted. - var parent = element.offsetParent; + let parent = element.offsetParent; if (!parent) { console.error('offsetParent is not set -- cannot scroll'); return; } - var checkOverflow = skipOverflowHiddenElements || false; - var offsetY = element.offsetTop + element.clientTop; - var offsetX = element.offsetLeft + element.clientLeft; + let offsetY = element.offsetTop + element.clientTop; + let offsetX = element.offsetLeft + element.clientLeft; while (parent.clientHeight === parent.scrollHeight || - (checkOverflow && getComputedStyle(parent).overflow === 'hidden')) { + (skipOverflowHiddenElements && + getComputedStyle(parent).overflow === 'hidden')) { if (parent.dataset._scaleY) { offsetY /= parent.dataset._scaleY; offsetX /= parent.dataset._scaleX; @@ -179,7 +179,7 @@ function scrollIntoView(element, spot, skipOverflowHiddenElements) { * PDF.js friendly one: with scroll debounce and scroll direction. */ function watchScroll(viewAreaElement, callback) { - var debounceScroll = function debounceScroll(evt) { + let debounceScroll = function(evt) { if (rAF) { return; } @@ -187,8 +187,8 @@ function watchScroll(viewAreaElement, callback) { rAF = window.requestAnimationFrame(function viewAreaElementScrolled() { rAF = null; - var currentY = viewAreaElement.scrollTop; - var lastY = state.lastY; + let currentY = viewAreaElement.scrollTop; + let lastY = state.lastY; if (currentY !== lastY) { state.down = currentY > lastY; } @@ -197,13 +197,13 @@ function watchScroll(viewAreaElement, callback) { }); }; - var state = { + let state = { down: true, lastY: viewAreaElement.scrollTop, _eventHandler: debounceScroll, }; - var rAF = null; + let rAF = null; viewAreaElement.addEventListener('scroll', debounceScroll, true); return state; } @@ -212,12 +212,12 @@ function watchScroll(viewAreaElement, callback) { * Helper function to parse query string (e.g. ?param1=value&parm2=...). */ function parseQueryString(query) { - var parts = query.split('&'); - var params = {}; - for (var i = 0, ii = parts.length; i < ii; ++i) { - var param = parts[i].split('='); - var key = param[0].toLowerCase(); - var value = param.length > 1 ? param[1] : null; + let parts = query.split('&'); + let params = Object.create(null); + for (let i = 0, ii = parts.length; i < ii; ++i) { + let param = parts[i].split('='); + let key = param[0].toLowerCase(); + let value = param.length > 1 ? param[1] : null; params[decodeURIComponent(key)] = decodeURIComponent(value); } return params; @@ -233,8 +233,8 @@ function parseQueryString(query) { * or |items.length| if no such element exists. */ function binarySearchFirstItem(items, condition) { - var minIndex = 0; - var maxIndex = items.length - 1; + let minIndex = 0; + let maxIndex = items.length - 1; if (items.length === 0 || !condition(items[maxIndex])) { return items.length; @@ -244,8 +244,8 @@ function binarySearchFirstItem(items, condition) { } while (minIndex < maxIndex) { - var currentIndex = (minIndex + maxIndex) >> 1; - var currentItem = items[currentIndex]; + let currentIndex = (minIndex + maxIndex) >> 1; + let currentItem = items[currentIndex]; if (condition(currentItem)) { maxIndex = currentIndex; } else { @@ -267,21 +267,21 @@ function approximateFraction(x) { if (Math.floor(x) === x) { return [x, 1]; } - var xinv = 1 / x; - var limit = 8; + let xinv = 1 / x; + let limit = 8; if (xinv > limit) { return [1, limit]; } else if (Math.floor(xinv) === xinv) { return [1, xinv]; } - var x_ = x > 1 ? xinv : x; + let x_ = x > 1 ? xinv : x; // a/b and c/d are neighbours in Farey sequence. - var a = 0, b = 1, c = 1, d = 1; + let a = 0, b = 1, c = 1, d = 1; // Limiting search to order 8. while (true) { // Generating next term in sequence (order of q). - var p = a + c, q = b + d; + let p = a + c, q = b + d; if (q > limit) { break; } @@ -291,7 +291,7 @@ function approximateFraction(x) { a = p; b = q; } } - var result; + let result; // Select closest of the neighbours to x. if (x_ - a / b < c / d - x_) { result = x_ === x ? [a, b] : [b, a]; @@ -302,31 +302,31 @@ function approximateFraction(x) { } function roundToDivide(x, div) { - var r = x % div; + let r = x % div; return r === 0 ? x : Math.round(x - r + div); } /** * Generic helper to find out what elements are visible within a scroll pane. */ -function getVisibleElements(scrollEl, views, sortByVisibility) { - var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight; - var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth; +function getVisibleElements(scrollEl, views, sortByVisibility = false) { + let top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight; + let left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth; function isElementBottomBelowViewTop(view) { - var element = view.div; - var elementBottom = + let element = view.div; + let elementBottom = element.offsetTop + element.clientTop + element.clientHeight; return elementBottom > top; } - var visible = [], view, element; - var currentHeight, viewHeight, hiddenHeight, percentHeight; - var currentWidth, viewWidth; - var firstVisibleElementInd = (views.length === 0) ? 0 : + let visible = [], view, element; + let currentHeight, viewHeight, hiddenHeight, percentHeight; + let currentWidth, viewWidth; + let firstVisibleElementInd = views.length === 0 ? 0 : binarySearchFirstItem(views, isElementBottomBelowViewTop); - for (var i = firstVisibleElementInd, ii = views.length; i < ii; i++) { + for (let i = firstVisibleElementInd, ii = views.length; i < ii; i++) { view = views[i]; element = view.div; currentHeight = element.offsetTop + element.clientTop; @@ -354,12 +354,12 @@ function getVisibleElements(scrollEl, views, sortByVisibility) { }); } - var first = visible[0]; - var last = visible[visible.length - 1]; + let first = visible[0]; + let last = visible[visible.length - 1]; if (sortByVisibility) { visible.sort(function(a, b) { - var pc = a.percent - b.percent; + let pc = a.percent - b.percent; if (Math.abs(pc) > 0.001) { return -pc; } @@ -372,12 +372,12 @@ function getVisibleElements(scrollEl, views, sortByVisibility) { /** * Event handler to suppress context menu. */ -function noContextMenuHandler(e) { - e.preventDefault(); +function noContextMenuHandler(evt) { + evt.preventDefault(); } function isDataSchema(url) { - var i = 0, ii = url.length; + let i = 0, ii = url.length; while (i < ii && url[i].trim() === '') { i++; } @@ -397,12 +397,12 @@ function getPDFFileNameFromURL(url, defaultFilename = 'document.pdf') { 'ignoring "data:" URL for performance reasons.'); return defaultFilename; } - var reURI = /^(?:(?:[^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/; + const reURI = /^(?:(?:[^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/; // SCHEME HOST 1.PATH 2.QUERY 3.REF // Pattern to get last matching NAME.pdf - var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i; - var splitURI = reURI.exec(url); - var suggestedFilename = reFilename.exec(splitURI[1]) || + const reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i; + let splitURI = reURI.exec(url); + let suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]); if (suggestedFilename) { @@ -412,7 +412,7 @@ function getPDFFileNameFromURL(url, defaultFilename = 'document.pdf') { try { suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0]; - } catch (e) { // Possible (extremely rare) errors: + } catch (ex) { // Possible (extremely rare) errors: // URIError "Malformed URI", e.g. for "%AA.pdf" // TypeError "null has no properties", e.g. for "%2F.pdf" } @@ -422,17 +422,17 @@ function getPDFFileNameFromURL(url, defaultFilename = 'document.pdf') { } function normalizeWheelEventDelta(evt) { - var delta = Math.sqrt(evt.deltaX * evt.deltaX + evt.deltaY * evt.deltaY); - var angle = Math.atan2(evt.deltaY, evt.deltaX); + let delta = Math.sqrt(evt.deltaX * evt.deltaX + evt.deltaY * evt.deltaY); + let angle = Math.atan2(evt.deltaY, evt.deltaX); if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) { // All that is left-up oriented has to change the sign. delta = -delta; } - var MOUSE_DOM_DELTA_PIXEL_MODE = 0; - var MOUSE_DOM_DELTA_LINE_MODE = 1; - var MOUSE_PIXELS_PER_LINE = 30; - var MOUSE_LINES_PER_PAGE = 30; + 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; // Converts delta to per-page units if (evt.deltaMode === MOUSE_DOM_DELTA_PIXEL_MODE) { @@ -444,8 +444,8 @@ function normalizeWheelEventDelta(evt) { } function cloneObj(obj) { - var result = {}; - for (var i in obj) { + let result = Object.create(null); + for (let i in obj) { if (Object.prototype.hasOwnProperty.call(obj, i)) { result[i] = obj[i]; } @@ -456,144 +456,138 @@ function cloneObj(obj) { /** * Promise that is resolved when DOM window becomes visible. */ -var animationStarted = new Promise(function (resolve) { +let animationStarted = new Promise(function (resolve) { window.requestAnimationFrame(resolve); }); /** * (deprecated) External localization service. */ -var mozL10n; +let mozL10n; /** * (deprecated) Promise that is resolved when UI localization is finished. */ -var localized = Promise.resolve(); +let localized = Promise.resolve(); /** * Simple event bus for an application. Listeners are attached using the * `on` and `off` methods. To raise an event, the `dispatch` method shall be * used. */ -var EventBus = (function EventBusClosure() { - function EventBus() { +class EventBus { + constructor() { this._listeners = Object.create(null); } - EventBus.prototype = { - on: function EventBus_on(eventName, listener) { - var eventListeners = this._listeners[eventName]; - if (!eventListeners) { - eventListeners = []; - this._listeners[eventName] = eventListeners; - } - eventListeners.push(listener); - }, - off: function EventBus_on(eventName, listener) { - var eventListeners = this._listeners[eventName]; - var i; - if (!eventListeners || ((i = eventListeners.indexOf(listener)) < 0)) { - return; - } - eventListeners.splice(i, 1); - }, - dispatch: function EventBus_dispath(eventName) { - var eventListeners = this._listeners[eventName]; - if (!eventListeners || eventListeners.length === 0) { - return; - } - // Passing all arguments after the eventName to the listeners. - var args = Array.prototype.slice.call(arguments, 1); - // Making copy of the listeners array in case if it will be modified - // during dispatch. - eventListeners.slice(0).forEach(function (listener) { - listener.apply(null, args); - }); - }, - }; - return EventBus; -})(); -var ProgressBar = (function ProgressBarClosure() { - - function clamp(v, min, max) { - return Math.min(Math.max(v, min), max); + on(eventName, listener) { + let eventListeners = this._listeners[eventName]; + if (!eventListeners) { + eventListeners = []; + this._listeners[eventName] = eventListeners; + } + eventListeners.push(listener); } - function ProgressBar(id, opts) { + off(eventName, listener) { + let eventListeners = this._listeners[eventName]; + let i; + if (!eventListeners || ((i = eventListeners.indexOf(listener)) < 0)) { + return; + } + eventListeners.splice(i, 1); + } + + dispatch(eventName) { + let eventListeners = this._listeners[eventName]; + if (!eventListeners || eventListeners.length === 0) { + return; + } + // Passing all arguments after the eventName to the listeners. + let args = Array.prototype.slice.call(arguments, 1); + // Making copy of the listeners array in case if it will be modified + // during dispatch. + eventListeners.slice(0).forEach(function (listener) { + listener.apply(null, args); + }); + } +} + +function clamp(v, min, max) { + return Math.min(Math.max(v, min), max); +} + +class ProgressBar { + constructor(id, { height, width, units, } = {}) { this.visible = true; // Fetch the sub-elements for later. this.div = document.querySelector(id + ' .progress'); - // Get the loading bar element, so it can be resized to fit the viewer. this.bar = this.div.parentNode; // Get options, with sensible defaults. - this.height = opts.height || 100; - this.width = opts.width || 100; - this.units = opts.units || '%'; + this.height = height || 100; + this.width = width || 100; + this.units = units || '%'; // Initialize heights. this.div.style.height = this.height + this.units; this.percent = 0; } - ProgressBar.prototype = { + _updateBar() { + if (this._indeterminate) { + this.div.classList.add('indeterminate'); + this.div.style.width = this.width + this.units; + return; + } - updateBar: function ProgressBar_updateBar() { - if (this._indeterminate) { - this.div.classList.add('indeterminate'); - this.div.style.width = this.width + this.units; - return; - } + this.div.classList.remove('indeterminate'); + let progressSize = this.width * this._percent / 100; + this.div.style.width = progressSize + this.units; + } - this.div.classList.remove('indeterminate'); - var progressSize = this.width * this._percent / 100; - this.div.style.width = progressSize + this.units; - }, + get percent() { + return this._percent; + } - get percent() { - return this._percent; - }, + set percent(val) { + this._indeterminate = isNaN(val); + this._percent = clamp(val, 0, 100); + this._updateBar(); + } - set percent(val) { - this._indeterminate = isNaN(val); - this._percent = clamp(val, 0, 100); - this.updateBar(); - }, + setWidth(viewer) { + if (!viewer) { + return; + } + let container = viewer.parentNode; + let scrollbarWidth = container.offsetWidth - viewer.offsetWidth; + if (scrollbarWidth > 0) { + this.bar.setAttribute('style', 'width: calc(100% - ' + + scrollbarWidth + 'px);'); + } + } - setWidth: function ProgressBar_setWidth(viewer) { - if (viewer) { - var container = viewer.parentNode; - var scrollbarWidth = container.offsetWidth - viewer.offsetWidth; - if (scrollbarWidth > 0) { - this.bar.setAttribute('style', 'width: calc(100% - ' + - scrollbarWidth + 'px);'); - } - } - }, + hide() { + if (!this.visible) { + return; + } + this.visible = false; + this.bar.classList.add('hidden'); + document.body.classList.remove('loadingInProgress'); + } - hide: function ProgressBar_hide() { - if (!this.visible) { - return; - } - this.visible = false; - this.bar.classList.add('hidden'); - document.body.classList.remove('loadingInProgress'); - }, - - show: function ProgressBar_show() { - if (this.visible) { - return; - } - this.visible = true; - document.body.classList.add('loadingInProgress'); - this.bar.classList.remove('hidden'); - }, - }; - - return ProgressBar; -})(); + show() { + if (this.visible) { + return; + } + this.visible = true; + document.body.classList.add('loadingInProgress'); + this.bar.classList.remove('hidden'); + } +} export { CSS_UNITS,