blob: 379671767297e294844cec27443290ccbb47770d [file] [log] [blame]
[email protected]3528d6302014-02-19 08:13:071// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
dpapad17f738f2019-10-22 21:03:025import {assert} from 'chrome://resources/js/assert.m.js';
6import {EventTracker} from 'chrome://resources/js/event_tracker.m.js';
7import {$} from 'chrome://resources/js/util.m.js';
8
Hui Yingst8a3f57f2020-02-20 15:12:529import {FittingType} from './constants.js';
dpapad17f738f2019-10-22 21:03:0210import {InactiveZoomManager, ZoomManager} from './zoom_manager.js';
11
rbpotter84b2076a2019-08-23 01:31:1512/**
13 * @typedef {{
14 * width: number,
15 * height: number,
K Moonaa70882f2019-09-26 21:02:4916 * layoutOptions: (!LayoutOptions|undefined),
rbpotter84b2076a2019-08-23 01:31:1517 * pageDimensions: Array<ViewportRect>,
18 * }}
19 */
20let DocumentDimensions;
21
Hui Yingst73ba19a2020-03-10 19:45:2122/**
23 * @typedef {{
24 * defaultPageOrientation: number,
25 * twoUpViewEnabled: boolean,
26 * }}
27 */
dpapad17f738f2019-10-22 21:03:0228export let LayoutOptions;
K Moonaa70882f2019-09-26 21:02:4929
rbpotter84b2076a2019-08-23 01:31:1530/** @typedef {{x: number, y: number}} */
dpapad17f738f2019-10-22 21:03:0231export let Point;
rbpotter84b2076a2019-08-23 01:31:1532
rbpotterde9429d2019-09-12 00:40:0833/** @typedef {{x: (number|undefined), y: (number|undefined)}} */
dpapad17f738f2019-10-22 21:03:0234export let PartialPoint;
rbpotterde9429d2019-09-12 00:40:0835
rbpotter84b2076a2019-08-23 01:31:1536/** @typedef {{width: number, height: number}} */
37let Size;
38
39/** @typedef {{x: number, y: number, width: number, height: number}} */
40let ViewportRect;
Henrique Nakashima585c7af02018-03-27 04:55:2141
42/**
rbpotter84b2076a2019-08-23 01:31:1543 * @param {!ViewportRect} rect1
44 * @param {!ViewportRect} rect2
45 * @return {number} The area of the intersection of the rects
[email protected]3528d6302014-02-19 08:13:0746 */
Jeremy Chinsen54f601822019-08-15 02:15:3747function getIntersectionArea(rect1, rect2) {
48 const left = Math.max(rect1.x, rect2.x);
49 const top = Math.max(rect1.y, rect2.y);
50 const right = Math.min(rect1.x + rect1.width, rect2.x + rect2.width);
51 const bottom = Math.min(rect1.y + rect1.height, rect2.y + rect2.height);
52
53 if (left >= right || top >= bottom) {
54 return 0;
55 }
56
57 return (right - left) * (bottom - top);
[email protected]3528d6302014-02-19 08:13:0758}
59
60/**
rbpotter84b2076a2019-08-23 01:31:1561 * @param {!Point} p1
62 * @param {!Point} p2
63 * @return {!Point} The vector between the two points.
mcnee6e1abbf2016-11-09 18:05:3164 */
65function vectorDelta(p1, p2) {
dbeam70db0fb2017-06-19 17:09:2766 return {x: p2.x - p1.x, y: p2.y - p1.y};
mcnee6e1abbf2016-11-09 18:05:3167}
68
rbpotter84b2076a2019-08-23 01:31:1569/**
70 * @param {!Point} coordinateInFrame
71 * @return {!Point} Coordinate converted to plugin coordinates.
72 */
mcnee6e1abbf2016-11-09 18:05:3173function frameToPluginCoordinate(coordinateInFrame) {
dstockwellafba4e42018-12-02 22:58:0674 const container = $('plugin');
mcnee6e1abbf2016-11-09 18:05:3175 return {
76 x: coordinateInFrame.x - container.getBoundingClientRect().left,
77 y: coordinateInFrame.y - container.getBoundingClientRect().top
78 };
79}
80
dpapad17f738f2019-10-22 21:03:0281export class Viewport {
dstockwella685a702019-01-29 02:21:5482 /**
rbpotter84b2076a2019-08-23 01:31:1583 * @param {!Window} window
84 * @param {!HTMLDivElement} sizer The element which represents the size of the
dstockwella685a702019-01-29 02:21:5485 * document in the viewport
rbpotter84b2076a2019-08-23 01:31:1586 * @param {number} scrollbarWidth The width of scrollbars on the page
dstockwella685a702019-01-29 02:21:5487 * @param {number} defaultZoom The default zoom level.
88 * @param {number} topToolbarHeight The number of pixels that should initially
89 * be left blank above the document for the toolbar.
90 */
rbpotter84b2076a2019-08-23 01:31:1591 constructor(window, sizer, scrollbarWidth, defaultZoom, topToolbarHeight) {
92 /** @private {!Window} */
dstockwella685a702019-01-29 02:21:5493 this.window_ = window;
rbpotter84b2076a2019-08-23 01:31:1594
95 /** @private {!HTMLDivElement} */
dstockwella685a702019-01-29 02:21:5496 this.sizer_ = sizer;
rbpotter84b2076a2019-08-23 01:31:1597
98 /** @private {number} */
99 this.scrollbarWidth_ = scrollbarWidth;
100
101 /** @private {number} */
102 this.defaultZoom_ = defaultZoom;
103
104 /** @private {number} */
105 this.topToolbarHeight_ = topToolbarHeight;
106
107 /** @private {function():void} */
108 this.viewportChangedCallback_ = function() {};
109
110 /** @private {function():void} */
111 this.beforeZoomCallback_ = function() {};
112
113 /** @private {function():void} */
114 this.afterZoomCallback_ = function() {};
115
116 /** @private {function(boolean):void} */
117 this.userInitiatedCallback_ = function() {};
118
119 /** @private {boolean} */
dstockwella685a702019-01-29 02:21:54120 this.allowedToChangeZoom_ = false;
rbpotter84b2076a2019-08-23 01:31:15121
122 /** @private {number} */
dstockwella685a702019-01-29 02:21:54123 this.internalZoom_ = 1;
rbpotter84b2076a2019-08-23 01:31:15124
Brian Clifton08b57c02019-12-18 01:29:36125 /**
126 * Predefined zoom factors to be used when zooming in/out. These are in
127 * ascending order.
128 * @private {!Array<number>}
129 */
130 this.presetZoomFactors_ = [];
131
rbpotter84b2076a2019-08-23 01:31:15132 /** @private {?ZoomManager} */
133 this.zoomManager_ = null;
134
dstockwella685a702019-01-29 02:21:54135 /** @private {?DocumentDimensions} */
136 this.documentDimensions_ = null;
rbpotter84b2076a2019-08-23 01:31:15137
dstockwella685a702019-01-29 02:21:54138 /** @private {Array<ViewportRect>} */
139 this.pageDimensions_ = [];
rbpotter84b2076a2019-08-23 01:31:15140
141 /** @private {!FittingType} */
dstockwella685a702019-01-29 02:21:54142 this.fittingType_ = FittingType.NONE;
rbpotter84b2076a2019-08-23 01:31:15143
rbpotter84b2076a2019-08-23 01:31:15144 /** @private {number} */
dstockwella685a702019-01-29 02:21:54145 this.prevScale_ = 1;
rbpotter84b2076a2019-08-23 01:31:15146
147 /** @private {!Viewport.PinchPhase} */
dstockwella685a702019-01-29 02:21:54148 this.pinchPhase_ = Viewport.PinchPhase.PINCH_NONE;
rbpotter84b2076a2019-08-23 01:31:15149
150 /** @private {?Point} */
dstockwella685a702019-01-29 02:21:54151 this.pinchPanVector_ = null;
rbpotter84b2076a2019-08-23 01:31:15152
153 /** @private {?Point} */
dstockwella685a702019-01-29 02:21:54154 this.pinchCenter_ = null;
rbpotter84b2076a2019-08-23 01:31:15155
dstockwella685a702019-01-29 02:21:54156 /** @private {?Point} */
157 this.firstPinchCenterInFrame_ = null;
rbpotter84b2076a2019-08-23 01:31:15158
159 /** @private {number} */
dstockwella685a702019-01-29 02:21:54160 this.rotations_ = 0;
rbpotter84b2076a2019-08-23 01:31:15161
162 /** @private {?Point} */
163 this.oldCenterInContent_ = null;
164
165 /** @private {boolean} */
166 this.keepContentCentered_ = false;
167
168 /** @private {!EventTracker} */
169 this.tracker_ = new EventTracker();
170
171 // Set to a default zoom manager - used in tests.
172 this.setZoomManager(new InactiveZoomManager(this.getZoom.bind(this), 1));
[email protected]3528d6302014-02-19 08:13:07173
dstockwella685a702019-01-29 02:21:54174 window.addEventListener('scroll', this.updateViewport_.bind(this));
175 window.addEventListener('resize', this.resizeWrapper_.bind(this));
176 }
Douglas Stockwell1752f7762018-11-28 23:41:36177
rbpotter84b2076a2019-08-23 01:31:15178 /** @param {function():void} viewportChangedCallback */
179 setViewportChangedCallback(viewportChangedCallback) {
180 this.viewportChangedCallback_ = viewportChangedCallback;
181 }
182
183 /** @param {function():void} beforeZoomCallback */
184 setBeforeZoomCallback(beforeZoomCallback) {
185 this.beforeZoomCallback_ = beforeZoomCallback;
186 }
187
188 /** @param {function():void} afterZoomCallback */
189 setAfterZoomCallback(afterZoomCallback) {
190 this.afterZoomCallback_ = afterZoomCallback;
191 }
192
193 /** @param {function(boolean):void} userInitiatedCallback */
194 setUserInitiatedCallback(userInitiatedCallback) {
195 this.userInitiatedCallback_ = userInitiatedCallback;
196 }
197
Lei Zhangc6c4ec32019-10-31 21:14:28198 rotateClockwise() {
199 this.rotateBySteps_(1);
200 }
201
202 rotateCounterclockwise() {
203 this.rotateBySteps_(3);
204 }
205
Douglas Stockwell1752f7762018-11-28 23:41:36206 /**
rbpotter84b2076a2019-08-23 01:31:15207 * @param {number} n The number of clockwise 90-degree rotations to increment
208 * by.
Douglas Stockwell1752f7762018-11-28 23:41:36209 */
Lei Zhangc6c4ec32019-10-31 21:14:28210 rotateBySteps_(n) {
Douglas Stockwell1752f7762018-11-28 23:41:36211 this.rotations_ = (this.rotations_ + n) % 4;
dstockwella685a702019-01-29 02:21:54212 }
Douglas Stockwell1752f7762018-11-28 23:41:36213
214 /**
rbpotter84b2076a2019-08-23 01:31:15215 * @return {number} The number of clockwise 90-degree rotations that have been
Douglas Stockwell1752f7762018-11-28 23:41:36216 * applied.
217 */
dstockwella685a702019-01-29 02:21:54218 getClockwiseRotations() {
Douglas Stockwell1752f7762018-11-28 23:41:36219 return this.rotations_;
dstockwella685a702019-01-29 02:21:54220 }
Douglas Stockwell1752f7762018-11-28 23:41:36221
Hui Yingst73ba19a2020-03-10 19:45:21222 /** @return {boolean} Whether viewport is in two-up view mode. */
223 twoUpViewEnabled() {
224 const options = this.getLayoutOptions();
225 if (options === undefined) {
226 return false;
227 }
228 return options.twoUpViewEnabled;
Jeremy Chinsenfb6768e2019-08-17 01:09:07229 }
230
231 /**
Brian Clifton08b57c02019-12-18 01:29:36232 * Clamps the zoom factor (or page scale factor) to be within the limits.
233 * @param {number} factor The zoom/scale factor.
234 * @return {number} The factor clamped within the limits.
235 * @private
236 */
237 clampZoom_(factor) {
238 return Math.max(
239 this.presetZoomFactors_[0],
240 Math.min(
241 factor,
242 this.presetZoomFactors_[this.presetZoomFactors_.length - 1]));
243 }
244
245 /**
246 * @param {!Array<number>} factors Array containing zoom/scale factors.
247 */
248 setZoomFactorRange(factors) {
249 assert(factors.length !== 0);
250 this.presetZoomFactors_ = factors;
251 }
252
253 /**
Douglas Stockwell1752f7762018-11-28 23:41:36254 * Converts a page position (e.g. the location of a bookmark) to a screen
255 * position.
Douglas Stockwell1752f7762018-11-28 23:41:36256 * @param {number} page
rbpotter84b2076a2019-08-23 01:31:15257 * @param {!Point} point The position on `page`.
258 * @return {!Point} The screen position.
Douglas Stockwell1752f7762018-11-28 23:41:36259 */
dstockwella685a702019-01-29 02:21:54260 convertPageToScreen(page, point) {
Douglas Stockwell1752f7762018-11-28 23:41:36261 const dimensions = this.getPageInsetDimensions(page);
262
263 // width & height are already rotated.
264 const height = dimensions.height;
265 const width = dimensions.width;
266
267 const matrix = new DOMMatrix();
268
269 const rotation = this.rotations_ * 90;
270 // Set origin for rotation.
rbpotterb43063eb2020-02-20 01:23:10271 if (rotation === 90) {
Douglas Stockwell1752f7762018-11-28 23:41:36272 matrix.translateSelf(width, 0);
rbpotterb43063eb2020-02-20 01:23:10273 } else if (rotation === 180) {
Douglas Stockwell1752f7762018-11-28 23:41:36274 matrix.translateSelf(width, height);
rbpotterb43063eb2020-02-20 01:23:10275 } else if (rotation === 270) {
Douglas Stockwell1752f7762018-11-28 23:41:36276 matrix.translateSelf(0, height);
277 }
278 matrix.rotateSelf(0, 0, rotation);
279
280 // Invert Y position with respect to height as page coordinates are
281 // measured from the bottom left.
282 matrix.translateSelf(0, height);
283 matrix.scaleSelf(1, -1);
284
285 const pointsToPixels = 96 / 72;
286 const result = matrix.transformPoint({
287 x: point.x * pointsToPixels,
288 y: point.y * pointsToPixels,
289 });
290 return {
291 x: result.x + Viewport.PAGE_SHADOW.left,
292 y: result.y + Viewport.PAGE_SHADOW.top,
293 };
dstockwella685a702019-01-29 02:21:54294 }
Douglas Stockwell1752f7762018-11-28 23:41:36295
296
[email protected]3528d6302014-02-19 08:13:07297 /**
raymese6e90c62015-08-10 06:21:40298 * Returns the zoomed and rounded document dimensions for the given zoom.
299 * Rounding is necessary when interacting with the renderer which tends to
300 * operate in integral values (for example for determining if scrollbars
301 * should be shown).
302 * @param {number} zoom The zoom to use to compute the scaled dimensions.
rbpotter84b2076a2019-08-23 01:31:15303 * @return {?Size} Scaled 'width' and 'height' of the document.
raymese6e90c62015-08-10 06:21:40304 * @private
305 */
dstockwella685a702019-01-29 02:21:54306 getZoomedDocumentDimensions_(zoom) {
Dan Beamd1cca6e2019-01-03 02:46:27307 if (!this.documentDimensions_) {
raymese6e90c62015-08-10 06:21:40308 return null;
Dan Beamd1cca6e2019-01-03 02:46:27309 }
raymese6e90c62015-08-10 06:21:40310 return {
311 width: Math.round(this.documentDimensions_.width * zoom),
312 height: Math.round(this.documentDimensions_.height * zoom)
313 };
dstockwella685a702019-01-29 02:21:54314 }
raymese6e90c62015-08-10 06:21:40315
rbpotter84b2076a2019-08-23 01:31:15316 /** @return {!Size} A dictionary with the 'width'/'height' of the document. */
dstockwella685a702019-01-29 02:21:54317 getDocumentDimensions() {
dstockwell118c5362019-01-03 03:01:34318 return {
319 width: this.documentDimensions_.width,
320 height: this.documentDimensions_.height
321 };
dstockwella685a702019-01-29 02:21:54322 }
dstockwell118c5362019-01-03 03:01:34323
324 /**
K Moonaa70882f2019-09-26 21:02:49325 * @return {!LayoutOptions|undefined} A dictionary carrying layout options
326 * from the plugin.
327 */
328 getLayoutOptions() {
329 return this.documentDimensions_ ? this.documentDimensions_.layoutOptions :
330 undefined;
331 }
332
333 /**
rbpotter84b2076a2019-08-23 01:31:15334 * @return {!ViewportRect} ViewportRect for the viewport given current zoom.
Jeremy Chinsenfb6768e2019-08-17 01:09:07335 * @private
336 */
337 getViewportRect_() {
rbpotter84b2076a2019-08-23 01:31:15338 const zoom = this.getZoom();
Jeremy Chinsenfb6768e2019-08-17 01:09:07339 return {
rbpotter84b2076a2019-08-23 01:31:15340 x: this.position.x / zoom,
341 y: this.position.y / zoom,
342 width: this.size.width / zoom,
343 height: this.size.height / zoom
Jeremy Chinsenfb6768e2019-08-17 01:09:07344 };
345 }
346
347 /**
rbpotter84b2076a2019-08-23 01:31:15348 * @param {number} zoom Zoom to compute scrollbars for
349 * @return {{horizontal: boolean, vertical: boolean}} Whether horizontal or
dstockwella685a702019-01-29 02:21:54350 * vertical scrollbars are needed.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42351 * @private
[email protected]3528d6302014-02-19 08:13:07352 */
dstockwella685a702019-01-29 02:21:54353 documentNeedsScrollbars_(zoom) {
dstockwellafba4e42018-12-02 22:58:06354 const zoomedDimensions = this.getZoomedDocumentDimensions_(zoom);
raymese6e90c62015-08-10 06:21:40355 if (!zoomedDimensions) {
dbeam70db0fb2017-06-19 17:09:27356 return {horizontal: false, vertical: false};
[email protected]499e9562014-06-26 05:45:27357 }
sammc63334fed2014-11-10 03:07:35358
359 // If scrollbars are required for one direction, expand the document in the
360 // other direction to take the width of the scrollbars into account when
361 // deciding whether the other direction needs scrollbars.
Dan Beamd1cca6e2019-01-03 02:46:27362 if (zoomedDimensions.width > this.window_.innerWidth) {
raymese6e90c62015-08-10 06:21:40363 zoomedDimensions.height += this.scrollbarWidth_;
Dan Beamd1cca6e2019-01-03 02:46:27364 } else if (zoomedDimensions.height > this.window_.innerHeight) {
raymese6e90c62015-08-10 06:21:40365 zoomedDimensions.width += this.scrollbarWidth_;
Dan Beamd1cca6e2019-01-03 02:46:27366 }
[email protected]3528d6302014-02-19 08:13:07367 return {
raymese6e90c62015-08-10 06:21:40368 horizontal: zoomedDimensions.width > this.window_.innerWidth,
raymes051fb2c2015-09-21 04:56:41369 vertical: zoomedDimensions.height + this.topToolbarHeight_ >
370 this.window_.innerHeight
[email protected]3528d6302014-02-19 08:13:07371 };
dstockwella685a702019-01-29 02:21:54372 }
[email protected]3528d6302014-02-19 08:13:07373
374 /**
rbpotter84b2076a2019-08-23 01:31:15375 * @return {!{horizontal: boolean, vertical: boolean}} Whether horizontal and
376 * vertical scrollbars are needed.
[email protected]3528d6302014-02-19 08:13:07377 */
dstockwella685a702019-01-29 02:21:54378 documentHasScrollbars() {
rbpotter84b2076a2019-08-23 01:31:15379 return this.documentNeedsScrollbars_(this.getZoom());
dstockwella685a702019-01-29 02:21:54380 }
[email protected]3528d6302014-02-19 08:13:07381
382 /**
rbpotter84b2076a2019-08-23 01:31:15383 * Helper function called when the zoomed document size changes. Updates the
384 * sizer's width and height.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42385 * @private
[email protected]3528d6302014-02-19 08:13:07386 */
dstockwella685a702019-01-29 02:21:54387 contentSizeChanged_() {
rbpotter84b2076a2019-08-23 01:31:15388 const zoomedDimensions = this.getZoomedDocumentDimensions_(this.getZoom());
raymese6e90c62015-08-10 06:21:40389 if (zoomedDimensions) {
390 this.sizer_.style.width = zoomedDimensions.width + 'px';
dbeam70db0fb2017-06-19 17:09:27391 this.sizer_.style.height =
392 zoomedDimensions.height + this.topToolbarHeight_ + 'px';
[email protected]345e7c62014-05-02 09:52:58393 }
dstockwella685a702019-01-29 02:21:54394 }
[email protected]3528d6302014-02-19 08:13:07395
396 /**
[email protected]312112c72014-04-14 01:45:43397 * Called when the viewport should be updated.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42398 * @private
[email protected]3528d6302014-02-19 08:13:07399 */
dstockwella685a702019-01-29 02:21:54400 updateViewport_() {
[email protected]312112c72014-04-14 01:45:43401 this.viewportChangedCallback_();
dstockwella685a702019-01-29 02:21:54402 }
[email protected]312112c72014-04-14 01:45:43403
404 /**
rbpottera82dacc2017-09-08 19:39:39405 * Called when the browser window size changes.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42406 * @private
rbpottera82dacc2017-09-08 19:39:39407 */
dstockwella685a702019-01-29 02:21:54408 resizeWrapper_() {
rbpotter84b2076a2019-08-23 01:31:15409 this.userInitiatedCallback_(false);
rbpottera82dacc2017-09-08 19:39:39410 this.resize_();
rbpotter84b2076a2019-08-23 01:31:15411 this.userInitiatedCallback_(true);
dstockwella685a702019-01-29 02:21:54412 }
rbpottera82dacc2017-09-08 19:39:39413
414 /**
[email protected]312112c72014-04-14 01:45:43415 * Called when the viewport size changes.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42416 * @private
[email protected]312112c72014-04-14 01:45:43417 */
dstockwella685a702019-01-29 02:21:54418 resize_() {
rbpotterb43063eb2020-02-20 01:23:10419 if (this.fittingType_ === FittingType.FIT_TO_PAGE) {
raymes161514f2015-02-17 05:09:51420 this.fitToPageInternal_(false);
rbpotterb43063eb2020-02-20 01:23:10421 } else if (this.fittingType_ === FittingType.FIT_TO_WIDTH) {
[email protected]312112c72014-04-14 01:45:43422 this.fitToWidth();
rbpotterb43063eb2020-02-20 01:23:10423 } else if (this.fittingType_ === FittingType.FIT_TO_HEIGHT) {
Henrique Nakashima50b18e02017-11-21 23:29:57424 this.fitToHeightInternal_(false);
rbpotterb43063eb2020-02-20 01:23:10425 } else if (this.internalZoom_ === 0) {
Henrique Nakashima4cf9ed42018-09-07 21:02:03426 this.fitToNone();
Dan Beamd1cca6e2019-01-03 02:46:27427 } else {
[email protected]312112c72014-04-14 01:45:43428 this.updateViewport_();
Dan Beamd1cca6e2019-01-03 02:46:27429 }
dstockwella685a702019-01-29 02:21:54430 }
[email protected]312112c72014-04-14 01:45:43431
rbpotter84b2076a2019-08-23 01:31:15432 /** @return {!Point} The scroll position of the viewport. */
[email protected]312112c72014-04-14 01:45:43433 get position() {
434 return {
435 x: this.window_.pageXOffset,
raymesbd82a942015-08-05 07:05:18436 y: this.window_.pageYOffset - this.topToolbarHeight_
[email protected]312112c72014-04-14 01:45:43437 };
dstockwella685a702019-01-29 02:21:54438 }
[email protected]312112c72014-04-14 01:45:43439
440 /**
441 * Scroll the viewport to the specified position.
rbpotter84b2076a2019-08-23 01:31:15442 * @param {!Point} position The position to scroll to.
[email protected]312112c72014-04-14 01:45:43443 */
444 set position(position) {
raymesbd82a942015-08-05 07:05:18445 this.window_.scrollTo(position.x, position.y + this.topToolbarHeight_);
dstockwella685a702019-01-29 02:21:54446 }
[email protected]312112c72014-04-14 01:45:43447
rbpotter84b2076a2019-08-23 01:31:15448 /** @return {!Size} the size of the viewport excluding scrollbars. */
[email protected]312112c72014-04-14 01:45:43449 get size() {
rbpotter84b2076a2019-08-23 01:31:15450 const needsScrollbars = this.documentNeedsScrollbars_(this.getZoom());
dstockwellafba4e42018-12-02 22:58:06451 const scrollbarWidth = needsScrollbars.vertical ? this.scrollbarWidth_ : 0;
452 const scrollbarHeight =
453 needsScrollbars.horizontal ? this.scrollbarWidth_ : 0;
[email protected]312112c72014-04-14 01:45:43454 return {
455 width: this.window_.innerWidth - scrollbarWidth,
456 height: this.window_.innerHeight - scrollbarHeight
457 };
dstockwella685a702019-01-29 02:21:54458 }
[email protected]312112c72014-04-14 01:45:43459
rbpotter84b2076a2019-08-23 01:31:15460 /** @return {number} The current zoom. */
461 getZoom() {
mcnee2999413a2016-12-06 20:29:25462 return this.zoomManager_.applyBrowserZoom(this.internalZoom_);
dstockwella685a702019-01-29 02:21:54463 }
mcnee2999413a2016-12-06 20:29:25464
rbpotter84b2076a2019-08-23 01:31:15465 /** @param {!ZoomManager} manager */
466 setZoomManager(manager) {
467 this.resetTracker();
mcnee2999413a2016-12-06 20:29:25468 this.zoomManager_ = manager;
rbpotter84b2076a2019-08-23 01:31:15469 this.tracker_.add(
470 this.zoomManager_.getEventTarget(), 'set-zoom',
471 e => this.setZoom(e.detail));
472 this.tracker_.add(
473 this.zoomManager_.getEventTarget(), 'update-zoom-from-browser',
474 this.updateZoomFromBrowserChange_.bind(this));
dstockwella685a702019-01-29 02:21:54475 }
[email protected]312112c72014-04-14 01:45:43476
477 /**
rbpotter84b2076a2019-08-23 01:31:15478 * @return {!Viewport.PinchPhase} The phase of the current pinch gesture for
mcnee6e1abbf2016-11-09 18:05:31479 * the viewport.
480 */
481 get pinchPhase() {
482 return this.pinchPhase_;
dstockwella685a702019-01-29 02:21:54483 }
mcnee6e1abbf2016-11-09 18:05:31484
485 /**
rbpotter84b2076a2019-08-23 01:31:15486 * @return {?Point} The panning caused by the current pinch gesture (as
mcnee6e1abbf2016-11-09 18:05:31487 * the deltas of the x and y coordinates).
488 */
489 get pinchPanVector() {
490 return this.pinchPanVector_;
dstockwella685a702019-01-29 02:21:54491 }
mcnee6e1abbf2016-11-09 18:05:31492
493 /**
rbpotter84b2076a2019-08-23 01:31:15494 * @return {?Point} The coordinates of the center of the current pinch
dstockwella685a702019-01-29 02:21:54495 * gesture.
mcnee6e1abbf2016-11-09 18:05:31496 */
497 get pinchCenter() {
498 return this.pinchCenter_;
dstockwella685a702019-01-29 02:21:54499 }
mcnee6e1abbf2016-11-09 18:05:31500
501 /**
[email protected]499e9562014-06-26 05:45:27502 * Used to wrap a function that might perform zooming on the viewport. This is
503 * required so that we can notify the plugin that zooming is in progress
504 * so that while zooming is taking place it can stop reacting to scroll events
505 * from the viewport. This is to avoid flickering.
rbpotter84b2076a2019-08-23 01:31:15506 * @param {function():void} f Function to wrap
Henrique Nakashimad5de6d0d2018-04-13 18:02:42507 * @private
[email protected]499e9562014-06-26 05:45:27508 */
dstockwella685a702019-01-29 02:21:54509 mightZoom_(f) {
[email protected]499e9562014-06-26 05:45:27510 this.beforeZoomCallback_();
511 this.allowedToChangeZoom_ = true;
512 f();
513 this.allowedToChangeZoom_ = false;
514 this.afterZoomCallback_();
rbpotter84b2076a2019-08-23 01:31:15515 this.zoomManager_.onPdfZoomChange();
dstockwella685a702019-01-29 02:21:54516 }
[email protected]499e9562014-06-26 05:45:27517
518 /**
rbpotter84b2076a2019-08-23 01:31:15519 * @param {number} newZoom The zoom level to set.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42520 * @private
[email protected]312112c72014-04-14 01:45:43521 */
dstockwella685a702019-01-29 02:21:54522 setZoomInternal_(newZoom) {
dstockwell154f9c42019-01-01 23:36:34523 assert(
524 this.allowedToChangeZoom_,
525 'Called Viewport.setZoomInternal_ without calling ' +
526 'Viewport.mightZoom_.');
sammc5c33b7e2014-11-07 04:03:09527 // Record the scroll position (relative to the top-left of the window).
rbpotter84b2076a2019-08-23 01:31:15528 let zoom = this.getZoom();
dstockwellafba4e42018-12-02 22:58:06529 const currentScrollPos = {
rbpotter84b2076a2019-08-23 01:31:15530 x: this.position.x / zoom,
531 y: this.position.y / zoom
raymesbd82a942015-08-05 07:05:18532 };
Henrique Nakashima4cf9ed42018-09-07 21:02:03533
mcnee2999413a2016-12-06 20:29:25534 this.internalZoom_ = newZoom;
[email protected]3528d6302014-02-19 08:13:07535 this.contentSizeChanged_();
536 // Scroll to the scaled scroll position.
rbpotter84b2076a2019-08-23 01:31:15537 zoom = this.getZoom();
raymesbd82a942015-08-05 07:05:18538 this.position = {
rbpotter84b2076a2019-08-23 01:31:15539 x: currentScrollPos.x * zoom,
540 y: currentScrollPos.y * zoom
raymesbd82a942015-08-05 07:05:18541 };
dstockwella685a702019-01-29 02:21:54542 }
[email protected]3528d6302014-02-19 08:13:07543
544 /**
mcnee6e1abbf2016-11-09 18:05:31545 * Sets the zoom of the viewport.
546 * Same as setZoomInternal_ but for pinch zoom we have some more operations.
547 * @param {number} scaleDelta The zoom delta.
rbpotter84b2076a2019-08-23 01:31:15548 * @param {!Point} center The pinch center in content coordinates.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42549 * @private
mcnee6e1abbf2016-11-09 18:05:31550 */
dstockwella685a702019-01-29 02:21:54551 setPinchZoomInternal_(scaleDelta, center) {
dbeam70db0fb2017-06-19 17:09:27552 assert(
553 this.allowedToChangeZoom_,
mcnee6e1abbf2016-11-09 18:05:31554 'Called Viewport.setPinchZoomInternal_ without calling ' +
dbeam70db0fb2017-06-19 17:09:27555 'Viewport.mightZoom_.');
Brian Clifton08b57c02019-12-18 01:29:36556 this.internalZoom_ = this.clampZoom_(this.internalZoom_ * scaleDelta);
mcnee6e1abbf2016-11-09 18:05:31557
rbpotter84b2076a2019-08-23 01:31:15558 const newCenterInContent = this.frameToContent_(center);
dstockwellafba4e42018-12-02 22:58:06559 const delta = {
rbpotter84b2076a2019-08-23 01:31:15560 x: (newCenterInContent.x - this.oldCenterInContent_.x),
561 y: (newCenterInContent.y - this.oldCenterInContent_.y)
mcnee6e1abbf2016-11-09 18:05:31562 };
563
564 // Record the scroll position (relative to the pinch center).
rbpotter84b2076a2019-08-23 01:31:15565 const zoom = this.getZoom();
dstockwellafba4e42018-12-02 22:58:06566 const currentScrollPos = {
rbpotter84b2076a2019-08-23 01:31:15567 x: this.position.x - delta.x * zoom,
568 y: this.position.y - delta.y * zoom
mcnee6e1abbf2016-11-09 18:05:31569 };
570
571 this.contentSizeChanged_();
572 // Scroll to the scaled scroll position.
dbeam70db0fb2017-06-19 17:09:27573 this.position = {x: currentScrollPos.x, y: currentScrollPos.y};
dstockwella685a702019-01-29 02:21:54574 }
mcnee6e1abbf2016-11-09 18:05:31575
576 /**
mcnee6e1abbf2016-11-09 18:05:31577 * Converts a point from frame to content coordinates.
rbpotter84b2076a2019-08-23 01:31:15578 * @param {!Point} framePoint The frame coordinates.
579 * @return {!Point} The content coordinates.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42580 * @private
mcnee6e1abbf2016-11-09 18:05:31581 */
rbpotter84b2076a2019-08-23 01:31:15582 frameToContent_(framePoint) {
mcnee6e1abbf2016-11-09 18:05:31583 // TODO(mcnee) Add a helper Point class to avoid duplicating operations
584 // on plain {x,y} objects.
rbpotter84b2076a2019-08-23 01:31:15585 const zoom = this.getZoom();
mcnee6e1abbf2016-11-09 18:05:31586 return {
rbpotter84b2076a2019-08-23 01:31:15587 x: (framePoint.x + this.position.x) / zoom,
588 y: (framePoint.y + this.position.y) / zoom
mcnee6e1abbf2016-11-09 18:05:31589 };
dstockwella685a702019-01-29 02:21:54590 }
mcnee6e1abbf2016-11-09 18:05:31591
592 /**
rbpotter84b2076a2019-08-23 01:31:15593 * @param {number} newZoom The zoom level to zoom to.
[email protected]499e9562014-06-26 05:45:27594 */
dstockwella685a702019-01-29 02:21:54595 setZoom(newZoom) {
Henrique Nakashima50b18e02017-11-21 23:29:57596 this.fittingType_ = FittingType.NONE;
dpapad9afc2802017-08-09 22:01:43597 this.mightZoom_(() => {
Brian Clifton08b57c02019-12-18 01:29:36598 this.setZoomInternal_(this.clampZoom_(newZoom));
[email protected]fbad5bb2014-07-18 07:20:36599 this.updateViewport_();
dpapad9afc2802017-08-09 22:01:43600 });
dstockwella685a702019-01-29 02:21:54601 }
[email protected]499e9562014-06-26 05:45:27602
rbpotter84b2076a2019-08-23 01:31:15603 /**
604 * @param {!CustomEvent<number>} e Event containing the old browser zoom.
605 * @private
606 */
607 updateZoomFromBrowserChange_(e) {
608 const oldBrowserZoom = e.detail;
dpapad9afc2802017-08-09 22:01:43609 this.mightZoom_(() => {
mcnee2999413a2016-12-06 20:29:25610 // Record the scroll position (relative to the top-left of the window).
dstockwellafba4e42018-12-02 22:58:06611 const oldZoom = oldBrowserZoom * this.internalZoom_;
612 const currentScrollPos = {
mcnee2999413a2016-12-06 20:29:25613 x: this.position.x / oldZoom,
614 y: this.position.y / oldZoom
615 };
616 this.contentSizeChanged_();
rbpotter84b2076a2019-08-23 01:31:15617 const newZoom = this.getZoom();
mcnee2999413a2016-12-06 20:29:25618 // Scroll to the scaled scroll position.
619 this.position = {
rbpotter84b2076a2019-08-23 01:31:15620 x: currentScrollPos.x * newZoom,
621 y: currentScrollPos.y * newZoom
mcnee2999413a2016-12-06 20:29:25622 };
623 this.updateViewport_();
dpapad9afc2802017-08-09 22:01:43624 });
dstockwella685a702019-01-29 02:21:54625 }
mcnee2999413a2016-12-06 20:29:25626
rbpotter84b2076a2019-08-23 01:31:15627 /** @return {number} The width of scrollbars in the viewport in pixels. */
[email protected]312112c72014-04-14 01:45:43628 get scrollbarWidth() {
629 return this.scrollbarWidth_;
dstockwella685a702019-01-29 02:21:54630 }
[email protected]3528d6302014-02-19 08:13:07631
rbpotter84b2076a2019-08-23 01:31:15632 /** @return {FittingType} The fitting type the viewport is currently in. */
[email protected]312112c72014-04-14 01:45:43633 get fittingType() {
634 return this.fittingType_;
dstockwella685a702019-01-29 02:21:54635 }
[email protected]3528d6302014-02-19 08:13:07636
637 /**
Jeremy Chinsen54f601822019-08-15 02:15:37638 * Get the page at a given y position. If there are multiple pages
639 * overlapping the given y-coordinate, return the page with the smallest
640 * index.
rbpotter84b2076a2019-08-23 01:31:15641 * @param {number} y The y-coordinate to get the page at.
642 * @return {number} The index of a page overlapping the given y-coordinate.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42643 * @private
[email protected]56b7e3c2014-02-20 04:31:24644 */
dstockwella685a702019-01-29 02:21:54645 getPageAtY_(y) {
dstockwellafba4e42018-12-02 22:58:06646 let min = 0;
647 let max = this.pageDimensions_.length - 1;
[email protected]56b7e3c2014-02-20 04:31:24648 while (max >= min) {
dstockwellafba4e42018-12-02 22:58:06649 const page = Math.floor(min + ((max - min) / 2));
[email protected]13df2a42014-02-27 03:50:41650 // There might be a gap between the pages, in which case use the bottom
651 // of the previous page as the top for finding the page.
dstockwellafba4e42018-12-02 22:58:06652 let top = 0;
[email protected]13df2a42014-02-27 03:50:41653 if (page > 0) {
654 top = this.pageDimensions_[page - 1].y +
655 this.pageDimensions_[page - 1].height;
656 }
dstockwellafba4e42018-12-02 22:58:06657 const bottom =
dbeam70db0fb2017-06-19 17:09:27658 this.pageDimensions_[page].y + this.pageDimensions_[page].height;
[email protected]13df2a42014-02-27 03:50:41659
rbpotter0a1022f2019-10-10 18:13:50660 if (top <= y && y <= bottom) {
[email protected]56b7e3c2014-02-20 04:31:24661 return page;
Dan Beamd1cca6e2019-01-03 02:46:27662 }
Lei Zhangddc0ef032017-11-22 02:14:24663
Dan Beamd1cca6e2019-01-03 02:46:27664 if (top > y) {
[email protected]56b7e3c2014-02-20 04:31:24665 max = page - 1;
Dan Beamd1cca6e2019-01-03 02:46:27666 } else {
[email protected]56b7e3c2014-02-20 04:31:24667 min = page + 1;
Dan Beamd1cca6e2019-01-03 02:46:27668 }
[email protected]56b7e3c2014-02-20 04:31:24669 }
670 return 0;
dstockwella685a702019-01-29 02:21:54671 }
[email protected]56b7e3c2014-02-20 04:31:24672
Jeremy Chinsen54f601822019-08-15 02:15:37673 /**
Jeremy Chinsenfb6768e2019-08-17 01:09:07674 * Return the last page visible in the viewport. Returns the last index of the
675 * document if the viewport is below the document.
rbpotter84b2076a2019-08-23 01:31:15676 * @param {!ViewportRect} viewportRect
677 * @return {number} The highest index of the pages visible in the viewport.
Jeremy Chinsen54f601822019-08-15 02:15:37678 * @private
679 */
rbpotter84b2076a2019-08-23 01:31:15680 getLastPageInViewport_(viewportRect) {
Jeremy Chinsenfb6768e2019-08-17 01:09:07681 const pageAtY = this.getPageAtY_(viewportRect.y + viewportRect.height);
Jeremy Chinsen54f601822019-08-15 02:15:37682
Hui Yingst73ba19a2020-03-10 19:45:21683 if (!this.twoUpViewEnabled() || pageAtY % 2 === 1 ||
Jeremy Chinsenfb6768e2019-08-17 01:09:07684 pageAtY + 1 >= this.pageDimensions_.length) {
685 return pageAtY;
Jeremy Chinsen54f601822019-08-15 02:15:37686 }
687
Jeremy Chinsenfb6768e2019-08-17 01:09:07688 const nextPage = this.pageDimensions_[pageAtY + 1];
689 return getIntersectionArea(viewportRect, nextPage) > 0 ? pageAtY + 1 :
690 pageAtY;
Jeremy Chinsen54f601822019-08-15 02:15:37691 }
692
rbpotter84b2076a2019-08-23 01:31:15693 /**
694 * @param {!Point} point
695 * @return {boolean} Whether |point| (in screen coordinates) is inside a page
696 */
dstockwell09815ba2019-01-16 06:44:24697 isPointInsidePage(point) {
rbpotter84b2076a2019-08-23 01:31:15698 const zoom = this.getZoom();
dstockwell09815ba2019-01-16 06:44:24699 const size = this.size;
700 const position = this.position;
701 const page = this.getPageAtY_((position.y + point.y) / zoom);
702 const pageWidth = this.pageDimensions_[page].width * zoom;
703 const documentWidth = this.getDocumentDimensions().width * zoom;
704
705 const outerWidth = Math.max(size.width, documentWidth);
706
707 if (pageWidth >= outerWidth) {
708 return true;
709 }
710
711 const x = point.x + position.x;
712
713 const minX = (outerWidth - pageWidth) / 2;
714 const maxX = outerWidth - minX;
715 return x >= minX && x <= maxX;
dstockwella685a702019-01-29 02:21:54716 }
dstockwell09815ba2019-01-16 06:44:24717
718 /**
rbpotter84b2076a2019-08-23 01:31:15719 * @return {number} The index of the page with the greatest proportion of its
720 * area in the current viewport.
[email protected]3528d6302014-02-19 08:13:07721 */
dstockwella685a702019-01-29 02:21:54722 getMostVisiblePage() {
Jeremy Chinsenfb6768e2019-08-17 01:09:07723 const viewportRect = this.getViewportRect_();
Jeremy Chinsen54f601822019-08-15 02:15:37724
725 const firstVisiblePage = this.getPageAtY_(viewportRect.y);
rbpotter84b2076a2019-08-23 01:31:15726 const lastPossibleVisiblePage = this.getLastPageInViewport_(viewportRect);
Jeremy Chinsen54f601822019-08-15 02:15:37727 if (firstVisiblePage === lastPossibleVisiblePage) {
728 return firstVisiblePage;
Dan Beamd1cca6e2019-01-03 02:46:27729 }
Jeremy Chinsen54f601822019-08-15 02:15:37730
731 let mostVisiblePage = firstVisiblePage;
732 let largestIntersection = 0;
733
734 for (let i = firstVisiblePage; i < lastPossibleVisiblePage + 1; i++) {
735 const pageArea =
736 this.pageDimensions_[i].width * this.pageDimensions_[i].height;
737
738 // TODO(thestig): check whether we can remove this check.
739 if (pageArea <= 0) {
740 continue;
741 }
742
743 const pageIntersectionArea =
744 getIntersectionArea(this.pageDimensions_[i], viewportRect) / pageArea;
745
746 if (pageIntersectionArea > largestIntersection) {
747 mostVisiblePage = i;
748 largestIntersection = pageIntersectionArea;
749 }
750 }
751
752 return mostVisiblePage;
dstockwella685a702019-01-29 02:21:54753 }
[email protected]3528d6302014-02-19 08:13:07754
755 /**
Henrique Nakashima50b18e02017-11-21 23:29:57756 * Compute the zoom level for fit-to-page, fit-to-width or fit-to-height.
Henrique Nakashima50b18e02017-11-21 23:29:57757 * At least one of {fitWidth, fitHeight} must be true.
rbpotter84b2076a2019-08-23 01:31:15758 * @param {!Size} pageDimensions The dimensions of a given page in px.
759 * @param {boolean} fitWidth Whether the whole width of the page needs to be
760 * in the viewport.
761 * @param {boolean} fitHeight Whether the whole height of the page needs to be
762 * in the viewport.
763 * @return {number} The internal zoom to set
Henrique Nakashimad5de6d0d2018-04-13 18:02:42764 * @private
[email protected]3528d6302014-02-19 08:13:07765 */
dstockwella685a702019-01-29 02:21:54766 computeFittingZoom_(pageDimensions, fitWidth, fitHeight) {
Henrique Nakashima50b18e02017-11-21 23:29:57767 assert(
768 fitWidth || fitHeight,
769 'Invalid parameters. At least one of fitWidth and fitHeight must be ' +
770 'true.');
771
[email protected]3528d6302014-02-19 08:13:07772 // First compute the zoom without scrollbars.
dstockwellafba4e42018-12-02 22:58:06773 let zoom = this.computeFittingZoomGivenDimensions_(
Henrique Nakashima50b18e02017-11-21 23:29:57774 fitWidth, fitHeight, this.window_.innerWidth, this.window_.innerHeight,
775 pageDimensions.width, pageDimensions.height);
776
[email protected]3528d6302014-02-19 08:13:07777 // Check if there needs to be any scrollbars.
dstockwellafba4e42018-12-02 22:58:06778 const needsScrollbars = this.documentNeedsScrollbars_(zoom);
[email protected]3528d6302014-02-19 08:13:07779
780 // If the document fits, just return the zoom.
Dan Beamd1cca6e2019-01-03 02:46:27781 if (!needsScrollbars.horizontal && !needsScrollbars.vertical) {
[email protected]3528d6302014-02-19 08:13:07782 return zoom;
Dan Beamd1cca6e2019-01-03 02:46:27783 }
[email protected]3528d6302014-02-19 08:13:07784
dstockwellafba4e42018-12-02 22:58:06785 const zoomedDimensions = this.getZoomedDocumentDimensions_(zoom);
[email protected]3528d6302014-02-19 08:13:07786
787 // Check if adding a scrollbar will result in needing the other scrollbar.
dstockwellafba4e42018-12-02 22:58:06788 const scrollbarWidth = this.scrollbarWidth_;
[email protected]312112c72014-04-14 01:45:43789 if (needsScrollbars.horizontal &&
[email protected]3528d6302014-02-19 08:13:07790 zoomedDimensions.height > this.window_.innerHeight - scrollbarWidth) {
[email protected]312112c72014-04-14 01:45:43791 needsScrollbars.vertical = true;
[email protected]3528d6302014-02-19 08:13:07792 }
[email protected]312112c72014-04-14 01:45:43793 if (needsScrollbars.vertical &&
[email protected]3528d6302014-02-19 08:13:07794 zoomedDimensions.width > this.window_.innerWidth - scrollbarWidth) {
[email protected]312112c72014-04-14 01:45:43795 needsScrollbars.horizontal = true;
[email protected]3528d6302014-02-19 08:13:07796 }
797
798 // Compute available window space.
dstockwellafba4e42018-12-02 22:58:06799 const windowWithScrollbars = {
[email protected]3528d6302014-02-19 08:13:07800 width: this.window_.innerWidth,
801 height: this.window_.innerHeight
802 };
Dan Beamd1cca6e2019-01-03 02:46:27803 if (needsScrollbars.horizontal) {
[email protected]3528d6302014-02-19 08:13:07804 windowWithScrollbars.height -= scrollbarWidth;
Dan Beamd1cca6e2019-01-03 02:46:27805 }
806 if (needsScrollbars.vertical) {
[email protected]3528d6302014-02-19 08:13:07807 windowWithScrollbars.width -= scrollbarWidth;
Dan Beamd1cca6e2019-01-03 02:46:27808 }
[email protected]3528d6302014-02-19 08:13:07809
810 // Recompute the zoom.
Henrique Nakashima50b18e02017-11-21 23:29:57811 zoom = this.computeFittingZoomGivenDimensions_(
812 fitWidth, fitHeight, windowWithScrollbars.width,
813 windowWithScrollbars.height, pageDimensions.width,
814 pageDimensions.height);
815
mcnee2999413a2016-12-06 20:29:25816 return this.zoomManager_.internalZoomComponent(zoom);
dstockwella685a702019-01-29 02:21:54817 }
[email protected]3528d6302014-02-19 08:13:07818
819 /**
Henrique Nakashima50b18e02017-11-21 23:29:57820 * Compute a zoom level given the dimensions to fit and the actual numbers
821 * in those dimensions.
rbpotter84b2076a2019-08-23 01:31:15822 * @param {boolean} fitWidth Whether to constrain the page width to the
823 * window.
824 * @param {boolean} fitHeight Whether to constrain the page height to the
825 * window.
826 * @param {number} windowWidth Width of the window in px.
827 * @param {number} windowHeight Height of the window in px.
828 * @param {number} pageWidth Width of the page in px.
829 * @param {number} pageHeight Height of the page in px.
830 * @return {number} The internal zoom to set
Henrique Nakashimad5de6d0d2018-04-13 18:02:42831 * @private
Henrique Nakashima50b18e02017-11-21 23:29:57832 */
dstockwella685a702019-01-29 02:21:54833 computeFittingZoomGivenDimensions_(
Henrique Nakashima50b18e02017-11-21 23:29:57834 fitWidth, fitHeight, windowWidth, windowHeight, pageWidth, pageHeight) {
835 // Assumes at least one of {fitWidth, fitHeight} is set.
dstockwellafba4e42018-12-02 22:58:06836 let zoomWidth;
837 let zoomHeight;
Henrique Nakashima50b18e02017-11-21 23:29:57838
Dan Beamd1cca6e2019-01-03 02:46:27839 if (fitWidth) {
Henrique Nakashima50b18e02017-11-21 23:29:57840 zoomWidth = windowWidth / pageWidth;
Dan Beamd1cca6e2019-01-03 02:46:27841 }
Henrique Nakashima50b18e02017-11-21 23:29:57842
Dan Beamd1cca6e2019-01-03 02:46:27843 if (fitHeight) {
Henrique Nakashima50b18e02017-11-21 23:29:57844 zoomHeight = windowHeight / pageHeight;
Dan Beamd1cca6e2019-01-03 02:46:27845 }
Henrique Nakashima50b18e02017-11-21 23:29:57846
dstockwellafba4e42018-12-02 22:58:06847 let zoom;
Henrique Nakashima4cf9ed42018-09-07 21:02:03848 if (!fitWidth && fitHeight) {
849 zoom = zoomHeight;
850 } else if (fitWidth && !fitHeight) {
851 zoom = zoomWidth;
852 } else {
853 // Assume fitWidth && fitHeight
854 zoom = Math.min(zoomWidth, zoomHeight);
855 }
Henrique Nakashima50b18e02017-11-21 23:29:57856
Henrique Nakashima4cf9ed42018-09-07 21:02:03857 return Math.max(zoom, 0);
dstockwella685a702019-01-29 02:21:54858 }
Henrique Nakashima50b18e02017-11-21 23:29:57859
rbpotter84b2076a2019-08-23 01:31:15860 /** Zoom the viewport so that the page width consumes the entire viewport. */
dstockwella685a702019-01-29 02:21:54861 fitToWidth() {
dpapad9afc2802017-08-09 22:01:43862 this.mightZoom_(() => {
Henrique Nakashima50b18e02017-11-21 23:29:57863 this.fittingType_ = FittingType.FIT_TO_WIDTH;
Dan Beamd1cca6e2019-01-03 02:46:27864 if (!this.documentDimensions_) {
[email protected]499e9562014-06-26 05:45:27865 return;
Dan Beamd1cca6e2019-01-03 02:46:27866 }
[email protected]499e9562014-06-26 05:45:27867 // When computing fit-to-width, the maximum width of a page in the
868 // document is used, which is equal to the size of the document width.
dbeam70db0fb2017-06-19 17:09:27869 this.setZoomInternal_(
Henrique Nakashima50b18e02017-11-21 23:29:57870 this.computeFittingZoom_(this.documentDimensions_, true, false));
[email protected]499e9562014-06-26 05:45:27871 this.updateViewport_();
dpapad9afc2802017-08-09 22:01:43872 });
dstockwella685a702019-01-29 02:21:54873 }
[email protected]3528d6302014-02-19 08:13:07874
875 /**
Henrique Nakashima50b18e02017-11-21 23:29:57876 * Zoom the viewport so that the page height consumes the entire viewport.
877 * @param {boolean} scrollToTopOfPage Set to true if the viewport should be
878 * scrolled to the top of the current page. Set to false if the viewport
879 * should remain at the current scroll position.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42880 * @private
Henrique Nakashima50b18e02017-11-21 23:29:57881 */
dstockwella685a702019-01-29 02:21:54882 fitToHeightInternal_(scrollToTopOfPage) {
Henrique Nakashima50b18e02017-11-21 23:29:57883 this.mightZoom_(() => {
884 this.fittingType_ = FittingType.FIT_TO_HEIGHT;
Dan Beamd1cca6e2019-01-03 02:46:27885 if (!this.documentDimensions_) {
Henrique Nakashima50b18e02017-11-21 23:29:57886 return;
Dan Beamd1cca6e2019-01-03 02:46:27887 }
dstockwellafba4e42018-12-02 22:58:06888 const page = this.getMostVisiblePage();
Henrique Nakashima50b18e02017-11-21 23:29:57889 // When computing fit-to-height, the maximum height of the current page
890 // is used.
dstockwellafba4e42018-12-02 22:58:06891 const dimensions = {
Henrique Nakashima50b18e02017-11-21 23:29:57892 width: 0,
893 height: this.pageDimensions_[page].height,
894 };
895 this.setZoomInternal_(this.computeFittingZoom_(dimensions, false, true));
896 if (scrollToTopOfPage) {
rbpotter84b2076a2019-08-23 01:31:15897 this.position = {
898 x: 0,
899 y: this.pageDimensions_[page].y * this.getZoom()
900 };
Henrique Nakashima50b18e02017-11-21 23:29:57901 }
902 this.updateViewport_();
903 });
dstockwella685a702019-01-29 02:21:54904 }
Henrique Nakashima50b18e02017-11-21 23:29:57905
rbpotter84b2076a2019-08-23 01:31:15906 /** Zoom the viewport so that the page height consumes the entire viewport. */
dstockwella685a702019-01-29 02:21:54907 fitToHeight() {
Henrique Nakashima50b18e02017-11-21 23:29:57908 this.fitToHeightInternal_(true);
dstockwella685a702019-01-29 02:21:54909 }
Henrique Nakashima50b18e02017-11-21 23:29:57910
911 /**
Henrique Nakashima50b18e02017-11-21 23:29:57912 * Zoom the viewport so that a page consumes as much as possible of the it.
rbpotter84b2076a2019-08-23 01:31:15913 * @param {boolean} scrollToTopOfPage Whether the viewport should be scrolled
914 * to the top of the current page. If false, the viewport will remain at
915 * the current scroll position.
Henrique Nakashimad5de6d0d2018-04-13 18:02:42916 * @private
[email protected]3528d6302014-02-19 08:13:07917 */
dstockwella685a702019-01-29 02:21:54918 fitToPageInternal_(scrollToTopOfPage) {
dpapad9afc2802017-08-09 22:01:43919 this.mightZoom_(() => {
Henrique Nakashima50b18e02017-11-21 23:29:57920 this.fittingType_ = FittingType.FIT_TO_PAGE;
Dan Beamd1cca6e2019-01-03 02:46:27921 if (!this.documentDimensions_) {
[email protected]499e9562014-06-26 05:45:27922 return;
Dan Beamd1cca6e2019-01-03 02:46:27923 }
dstockwellafba4e42018-12-02 22:58:06924 const page = this.getMostVisiblePage();
sammc07f53aa2014-11-06 06:57:46925 // Fit to the current page's height and the widest page's width.
dstockwellafba4e42018-12-02 22:58:06926 const dimensions = {
sammc07f53aa2014-11-06 06:57:46927 width: this.documentDimensions_.width,
928 height: this.pageDimensions_[page].height,
929 };
Henrique Nakashima50b18e02017-11-21 23:29:57930 this.setZoomInternal_(this.computeFittingZoom_(dimensions, true, true));
raymesbd82a942015-08-05 07:05:18931 if (scrollToTopOfPage) {
rbpotter84b2076a2019-08-23 01:31:15932 this.position = {
933 x: 0,
934 y: this.pageDimensions_[page].y * this.getZoom()
935 };
raymesbd82a942015-08-05 07:05:18936 }
[email protected]499e9562014-06-26 05:45:27937 this.updateViewport_();
dpapad9afc2802017-08-09 22:01:43938 });
dstockwella685a702019-01-29 02:21:54939 }
[email protected]3528d6302014-02-19 08:13:07940
941 /**
raymes161514f2015-02-17 05:09:51942 * Zoom the viewport so that a page consumes the entire viewport. Also scrolls
943 * the viewport to the top of the current page.
944 */
dstockwella685a702019-01-29 02:21:54945 fitToPage() {
raymes161514f2015-02-17 05:09:51946 this.fitToPageInternal_(true);
dstockwella685a702019-01-29 02:21:54947 }
raymes161514f2015-02-17 05:09:51948
rbpotter84b2076a2019-08-23 01:31:15949 /** Zoom the viewport to the default zoom. */
dstockwella685a702019-01-29 02:21:54950 fitToNone() {
Henrique Nakashima4cf9ed42018-09-07 21:02:03951 this.mightZoom_(() => {
952 this.fittingType_ = FittingType.NONE;
Dan Beamd1cca6e2019-01-03 02:46:27953 if (!this.documentDimensions_) {
Henrique Nakashima4cf9ed42018-09-07 21:02:03954 return;
Dan Beamd1cca6e2019-01-03 02:46:27955 }
Henrique Nakashima4cf9ed42018-09-07 21:02:03956 this.setZoomInternal_(Math.min(
957 this.defaultZoom_,
958 this.computeFittingZoom_(this.documentDimensions_, true, false)));
959 this.updateViewport_();
960 });
dstockwella685a702019-01-29 02:21:54961 }
Henrique Nakashima4cf9ed42018-09-07 21:02:03962
rbpotter84b2076a2019-08-23 01:31:15963 /** Zoom out to the next predefined zoom level. */
dstockwella685a702019-01-29 02:21:54964 zoomOut() {
dpapad9afc2802017-08-09 22:01:43965 this.mightZoom_(() => {
Henrique Nakashima50b18e02017-11-21 23:29:57966 this.fittingType_ = FittingType.NONE;
Brian Clifton08b57c02019-12-18 01:29:36967 let nextZoom = this.presetZoomFactors_[0];
968 for (let i = 0; i < this.presetZoomFactors_.length; i++) {
969 if (this.presetZoomFactors_[i] < this.internalZoom_) {
970 nextZoom = this.presetZoomFactors_[i];
Dan Beamd1cca6e2019-01-03 02:46:27971 }
[email protected]499e9562014-06-26 05:45:27972 }
[email protected]fbad5bb2014-07-18 07:20:36973 this.setZoomInternal_(nextZoom);
[email protected]499e9562014-06-26 05:45:27974 this.updateViewport_();
dpapad9afc2802017-08-09 22:01:43975 });
dstockwella685a702019-01-29 02:21:54976 }
[email protected]3528d6302014-02-19 08:13:07977
rbpotter84b2076a2019-08-23 01:31:15978 /** Zoom in to the next predefined zoom level. */
dstockwella685a702019-01-29 02:21:54979 zoomIn() {
dpapad9afc2802017-08-09 22:01:43980 this.mightZoom_(() => {
Henrique Nakashima50b18e02017-11-21 23:29:57981 this.fittingType_ = FittingType.NONE;
Brian Clifton08b57c02019-12-18 01:29:36982 const maxZoomIndex = this.presetZoomFactors_.length - 1;
983 let nextZoom = this.presetZoomFactors_[maxZoomIndex];
984 for (let i = maxZoomIndex; i >= 0; i--) {
985 if (this.presetZoomFactors_[i] > this.internalZoom_) {
986 nextZoom = this.presetZoomFactors_[i];
Dan Beamd1cca6e2019-01-03 02:46:27987 }
[email protected]499e9562014-06-26 05:45:27988 }
[email protected]fbad5bb2014-07-18 07:20:36989 this.setZoomInternal_(nextZoom);
[email protected]499e9562014-06-26 05:45:27990 this.updateViewport_();
dpapad9afc2802017-08-09 22:01:43991 });
dstockwella685a702019-01-29 02:21:54992 }
[email protected]3528d6302014-02-19 08:13:07993
994 /**
mcnee6e1abbf2016-11-09 18:05:31995 * Pinch zoom event handler.
996 * @param {!Object} e The pinch event.
997 */
dstockwella685a702019-01-29 02:21:54998 pinchZoom(e) {
dpapad9afc2802017-08-09 22:01:43999 this.mightZoom_(() => {
rbpotterb43063eb2020-02-20 01:23:101000 this.pinchPhase_ = e.direction === 'out' ?
dbeam70db0fb2017-06-19 17:09:271001 Viewport.PinchPhase.PINCH_UPDATE_ZOOM_OUT :
1002 Viewport.PinchPhase.PINCH_UPDATE_ZOOM_IN;
mcnee6e1abbf2016-11-09 18:05:311003
dstockwellafba4e42018-12-02 22:58:061004 const scaleDelta = e.startScaleRatio / this.prevScale_;
dstockwella685a702019-01-29 02:21:541005 if (this.firstPinchCenterInFrame_ != null) {
1006 this.pinchPanVector_ =
1007 vectorDelta(e.center, this.firstPinchCenterInFrame_);
1008 }
mcnee6e1abbf2016-11-09 18:05:311009
dstockwellafba4e42018-12-02 22:58:061010 const needsScrollbars =
dbeam70db0fb2017-06-19 17:09:271011 this.documentNeedsScrollbars_(this.zoomManager_.applyBrowserZoom(
Brian Clifton08b57c02019-12-18 01:29:361012 this.clampZoom_(this.internalZoom_ * scaleDelta)));
mcnee6e1abbf2016-11-09 18:05:311013
1014 this.pinchCenter_ = e.center;
1015
1016 // If there's no horizontal scrolling, keep the content centered so the
1017 // user can't zoom in on the non-content area.
1018 // TODO(mcnee) Investigate other ways of scaling when we don't have
1019 // horizontal scrolling. We want to keep the document centered,
1020 // but this causes a potentially awkward transition when we start
1021 // using the gesture center.
1022 if (!needsScrollbars.horizontal) {
1023 this.pinchCenter_ = {
1024 x: this.window_.innerWidth / 2,
1025 y: this.window_.innerHeight / 2
1026 };
1027 } else if (this.keepContentCentered_) {
rbpotter84b2076a2019-08-23 01:31:151028 this.oldCenterInContent_ =
1029 this.frameToContent_(frameToPluginCoordinate(e.center));
mcnee6e1abbf2016-11-09 18:05:311030 this.keepContentCentered_ = false;
1031 }
1032
dbeam70db0fb2017-06-19 17:09:271033 this.setPinchZoomInternal_(scaleDelta, frameToPluginCoordinate(e.center));
mcnee6e1abbf2016-11-09 18:05:311034 this.updateViewport_();
1035 this.prevScale_ = e.startScaleRatio;
dpapad9afc2802017-08-09 22:01:431036 });
dstockwella685a702019-01-29 02:21:541037 }
mcnee6e1abbf2016-11-09 18:05:311038
dstockwella685a702019-01-29 02:21:541039 /** @param {!Object} e The pinch event. */
1040 pinchZoomStart(e) {
mcnee6e1abbf2016-11-09 18:05:311041 this.pinchPhase_ = Viewport.PinchPhase.PINCH_START;
1042 this.prevScale_ = 1;
rbpotter84b2076a2019-08-23 01:31:151043 this.oldCenterInContent_ =
1044 this.frameToContent_(frameToPluginCoordinate(e.center));
mcnee6e1abbf2016-11-09 18:05:311045
rbpotter84b2076a2019-08-23 01:31:151046 const needsScrollbars = this.documentNeedsScrollbars_(this.getZoom());
mcnee6e1abbf2016-11-09 18:05:311047 this.keepContentCentered_ = !needsScrollbars.horizontal;
1048 // We keep track of begining of the pinch.
1049 // By doing so we will be able to compute the pan distance.
1050 this.firstPinchCenterInFrame_ = e.center;
dstockwella685a702019-01-29 02:21:541051 }
mcnee6e1abbf2016-11-09 18:05:311052
dstockwella685a702019-01-29 02:21:541053 /** @param {!Object} e The pinch event. */
1054 pinchZoomEnd(e) {
dpapad9afc2802017-08-09 22:01:431055 this.mightZoom_(() => {
mcnee6e1abbf2016-11-09 18:05:311056 this.pinchPhase_ = Viewport.PinchPhase.PINCH_END;
dstockwellafba4e42018-12-02 22:58:061057 const scaleDelta = e.startScaleRatio / this.prevScale_;
rbpotter84b2076a2019-08-23 01:31:151058 this.pinchCenter_ = /** @type {!Point} */ (e.center);
mcnee6e1abbf2016-11-09 18:05:311059
dbeam70db0fb2017-06-19 17:09:271060 this.setPinchZoomInternal_(scaleDelta, frameToPluginCoordinate(e.center));
mcnee6e1abbf2016-11-09 18:05:311061 this.updateViewport_();
dpapad9afc2802017-08-09 22:01:431062 });
mcnee6e1abbf2016-11-09 18:05:311063
1064 this.pinchPhase_ = Viewport.PinchPhase.PINCH_NONE;
1065 this.pinchPanVector_ = null;
1066 this.pinchCenter_ = null;
1067 this.firstPinchCenterInFrame_ = null;
dstockwella685a702019-01-29 02:21:541068 }
mcnee6e1abbf2016-11-09 18:05:311069
1070 /**
Jeremy Chinsenfb6768e2019-08-17 01:09:071071 * Go to the next page. If the document is in two-up view, go to the left page
1072 * of the next row.
Jeremy Chinsen168926a92019-08-16 16:22:151073 */
1074 goToNextPage() {
Jeremy Chinsenfb6768e2019-08-17 01:09:071075 const currentPage = this.getMostVisiblePage();
Hui Yingst73ba19a2020-03-10 19:45:211076 const nextPageOffset =
1077 (this.twoUpViewEnabled() && currentPage % 2 === 0) ? 2 : 1;
Jeremy Chinsenfb6768e2019-08-17 01:09:071078 this.goToPage(currentPage + nextPageOffset);
Jeremy Chinsen168926a92019-08-16 16:22:151079 }
1080
1081 /**
Jeremy Chinsenfb6768e2019-08-17 01:09:071082 * Go to the previous page. If the document is in two-up view, go to the left
1083 * page of the previous row.
Jeremy Chinsen168926a92019-08-16 16:22:151084 */
1085 goToPreviousPage() {
Jeremy Chinsenfb6768e2019-08-17 01:09:071086 const currentPage = this.getMostVisiblePage();
1087 let previousPageOffset = -1;
1088
Hui Yingst73ba19a2020-03-10 19:45:211089 if (this.twoUpViewEnabled()) {
rbpotterb43063eb2020-02-20 01:23:101090 previousPageOffset = (currentPage % 2 === 0) ? -2 : -3;
Jeremy Chinsenfb6768e2019-08-17 01:09:071091 }
1092
1093 this.goToPage(currentPage + previousPageOffset);
Jeremy Chinsen168926a92019-08-16 16:22:151094 }
1095
1096 /**
[email protected]3528d6302014-02-19 08:13:071097 * Go to the given page index.
[email protected]f90b9a42014-08-20 05:37:341098 * @param {number} page the index of the page to go to. zero-based.
[email protected]3528d6302014-02-19 08:13:071099 */
dstockwella685a702019-01-29 02:21:541100 goToPage(page) {
Henrique Nakashima85fc58b32017-12-11 21:53:421101 this.goToPageAndXY(page, 0, 0);
dstockwella685a702019-01-29 02:21:541102 }
Henrique Nakashima9d9e0632017-10-06 21:38:181103
1104 /**
1105 * Go to the given y position in the given page index.
1106 * @param {number} page the index of the page to go to. zero-based.
Henrique Nakashima85fc58b32017-12-11 21:53:421107 * @param {number} x the x position in the page to go to.
Henrique Nakashima9d9e0632017-10-06 21:38:181108 * @param {number} y the y position in the page to go to.
1109 */
dstockwella685a702019-01-29 02:21:541110 goToPageAndXY(page, x, y) {
dpapad9afc2802017-08-09 22:01:431111 this.mightZoom_(() => {
Dan Beamd1cca6e2019-01-03 02:46:271112 if (this.pageDimensions_.length === 0) {
[email protected]499e9562014-06-26 05:45:271113 return;
Dan Beamd1cca6e2019-01-03 02:46:271114 }
1115 if (page < 0) {
[email protected]499e9562014-06-26 05:45:271116 page = 0;
Dan Beamd1cca6e2019-01-03 02:46:271117 }
1118 if (page >= this.pageDimensions_.length) {
[email protected]499e9562014-06-26 05:45:271119 page = this.pageDimensions_.length - 1;
Dan Beamd1cca6e2019-01-03 02:46:271120 }
dstockwellafba4e42018-12-02 22:58:061121 const dimensions = this.pageDimensions_[page];
1122 let toolbarOffset = 0;
Henrique Nakashima50b18e02017-11-21 23:29:571123 // Unless we're in fit to page or fit to height mode, scroll above the
1124 // page by |this.topToolbarHeight_| so that the toolbar isn't covering it
raymesbd82a942015-08-05 07:05:181125 // initially.
Dan Beamd1cca6e2019-01-03 02:46:271126 if (!this.isPagedMode()) {
raymesbd82a942015-08-05 07:05:181127 toolbarOffset = this.topToolbarHeight_;
Dan Beamd1cca6e2019-01-03 02:46:271128 }
raymesbd82a942015-08-05 07:05:181129 this.position = {
rbpotter84b2076a2019-08-23 01:31:151130 x: (dimensions.x + x) * this.getZoom(),
1131 y: (dimensions.y + y) * this.getZoom() - toolbarOffset
raymesbd82a942015-08-05 07:05:181132 };
[email protected]499e9562014-06-26 05:45:271133 this.updateViewport_();
dpapad9afc2802017-08-09 22:01:431134 });
dstockwella685a702019-01-29 02:21:541135 }
[email protected]3528d6302014-02-19 08:13:071136
1137 /**
rbpotter84b2076a2019-08-23 01:31:151138 * @param {DocumentDimensions} documentDimensions The dimensions of the
dstockwella685a702019-01-29 02:21:541139 * document
[email protected]3528d6302014-02-19 08:13:071140 */
dstockwella685a702019-01-29 02:21:541141 setDocumentDimensions(documentDimensions) {
dpapad9afc2802017-08-09 22:01:431142 this.mightZoom_(() => {
dstockwellafba4e42018-12-02 22:58:061143 const initialDimensions = !this.documentDimensions_;
[email protected]499e9562014-06-26 05:45:271144 this.documentDimensions_ = documentDimensions;
1145 this.pageDimensions_ = this.documentDimensions_.pageDimensions;
1146 if (initialDimensions) {
dbeam70db0fb2017-06-19 17:09:271147 this.setZoomInternal_(Math.min(
1148 this.defaultZoom_,
Henrique Nakashima50b18e02017-11-21 23:29:571149 this.computeFittingZoom_(this.documentDimensions_, true, false)));
dbeam70db0fb2017-06-19 17:09:271150 this.position = {x: 0, y: -this.topToolbarHeight_};
[email protected]499e9562014-06-26 05:45:271151 }
1152 this.contentSizeChanged_();
1153 this.resize_();
dpapad9afc2802017-08-09 22:01:431154 });
dstockwella685a702019-01-29 02:21:541155 }
[email protected]312112c72014-04-14 01:45:431156
1157 /**
Douglas Stockwell1752f7762018-11-28 23:41:361158 * @param {number} page
dstockwella685a702019-01-29 02:21:541159 * @return {ViewportRect} The bounds for page `page` minus the shadows.
Douglas Stockwell1752f7762018-11-28 23:41:361160 */
dstockwella685a702019-01-29 02:21:541161 getPageInsetDimensions(page) {
Douglas Stockwell1752f7762018-11-28 23:41:361162 const pageDimensions = this.pageDimensions_[page];
1163 const shadow = Viewport.PAGE_SHADOW;
1164 return {
1165 x: pageDimensions.x + shadow.left,
1166 y: pageDimensions.y + shadow.top,
1167 width: pageDimensions.width - shadow.left - shadow.right,
1168 height: pageDimensions.height - shadow.top - shadow.bottom,
1169 };
dstockwella685a702019-01-29 02:21:541170 }
Douglas Stockwell1752f7762018-11-28 23:41:361171
1172 /**
[email protected]312112c72014-04-14 01:45:431173 * Get the coordinates of the page contents (excluding the page shadow)
1174 * relative to the screen.
rbpotter84b2076a2019-08-23 01:31:151175 * @param {number} page The index of the page to get the rect for.
1176 * @return {!ViewportRect} A rect representing the page in screen coordinates.
[email protected]312112c72014-04-14 01:45:431177 */
dstockwella685a702019-01-29 02:21:541178 getPageScreenRect(page) {
[email protected]499e9562014-06-26 05:45:271179 if (!this.documentDimensions_) {
dbeam70db0fb2017-06-19 17:09:271180 return {x: 0, y: 0, width: 0, height: 0};
[email protected]499e9562014-06-26 05:45:271181 }
Dan Beamd1cca6e2019-01-03 02:46:271182 if (page >= this.pageDimensions_.length) {
[email protected]312112c72014-04-14 01:45:431183 page = this.pageDimensions_.length - 1;
Dan Beamd1cca6e2019-01-03 02:46:271184 }
[email protected]312112c72014-04-14 01:45:431185
dstockwellafba4e42018-12-02 22:58:061186 const pageDimensions = this.pageDimensions_[page];
[email protected]312112c72014-04-14 01:45:431187
1188 // Compute the page dimensions minus the shadows.
dstockwellafba4e42018-12-02 22:58:061189 const insetDimensions = this.getPageInsetDimensions(page);
[email protected]312112c72014-04-14 01:45:431190
[email protected]345e7c62014-05-02 09:52:581191 // Compute the x-coordinate of the page within the document.
1192 // TODO(raymes): This should really be set when the PDF plugin passes the
1193 // page coordinates, but it isn't yet.
dstockwellafba4e42018-12-02 22:58:061194 const x = (this.documentDimensions_.width - pageDimensions.width) / 2 +
[email protected]345e7c62014-05-02 09:52:581195 Viewport.PAGE_SHADOW.left;
1196 // Compute the space on the left of the document if the document fits
1197 // completely in the screen.
rbpotter84b2076a2019-08-23 01:31:151198 const zoom = this.getZoom();
dstockwellafba4e42018-12-02 22:58:061199 let spaceOnLeft =
rbpotter84b2076a2019-08-23 01:31:151200 (this.size.width - this.documentDimensions_.width * zoom) / 2;
[email protected]345e7c62014-05-02 09:52:581201 spaceOnLeft = Math.max(spaceOnLeft, 0);
[email protected]312112c72014-04-14 01:45:431202
1203 return {
rbpotter84b2076a2019-08-23 01:31:151204 x: x * zoom + spaceOnLeft - this.window_.pageXOffset,
1205 y: insetDimensions.y * zoom - this.window_.pageYOffset,
1206 width: insetDimensions.width * zoom,
1207 height: insetDimensions.height * zoom
[email protected]312112c72014-04-14 01:45:431208 };
dstockwella685a702019-01-29 02:21:541209 }
Henrique Nakashima50b18e02017-11-21 23:29:571210
1211 /**
1212 * Check if the current fitting type is a paged mode.
Henrique Nakashima50b18e02017-11-21 23:29:571213 * In a paged mode, page up and page down scroll to the top of the
1214 * previous/next page and part of the page is under the toolbar.
Henrique Nakashima50b18e02017-11-21 23:29:571215 * @return {boolean} Whether the current fitting type is a paged mode.
1216 */
dstockwella685a702019-01-29 02:21:541217 isPagedMode() {
Henrique Nakashima50b18e02017-11-21 23:29:571218 return (
rbpotterb43063eb2020-02-20 01:23:101219 this.fittingType_ === FittingType.FIT_TO_PAGE ||
1220 this.fittingType_ === FittingType.FIT_TO_HEIGHT);
dstockwella685a702019-01-29 02:21:541221 }
Henrique Nakashima585c7af02018-03-27 04:55:211222
rbpotterde9429d2019-09-12 00:40:081223 /**
1224 * @param {!PartialPoint} point The position to which to scroll the viewport.
1225 */
dstockwella685a702019-01-29 02:21:541226 scrollTo(point) {
Henrique Nakashima585c7af02018-03-27 04:55:211227 let changed = false;
1228 const newPosition = this.position;
rbpotterb43063eb2020-02-20 01:23:101229 if (point.x !== undefined && point.x !== newPosition.x) {
Henrique Nakashima585c7af02018-03-27 04:55:211230 newPosition.x = point.x;
1231 changed = true;
1232 }
rbpotterb43063eb2020-02-20 01:23:101233 if (point.y !== undefined && point.y !== newPosition.y) {
Henrique Nakashima585c7af02018-03-27 04:55:211234 newPosition.y = point.y;
1235 changed = true;
1236 }
1237
Dan Beamd1cca6e2019-01-03 02:46:271238 if (changed) {
Henrique Nakashima585c7af02018-03-27 04:55:211239 this.position = newPosition;
Dan Beamd1cca6e2019-01-03 02:46:271240 }
dstockwella685a702019-01-29 02:21:541241 }
Henrique Nakashima585c7af02018-03-27 04:55:211242
rbpotter84b2076a2019-08-23 01:31:151243 /** @param {!Point} delta The delta by which to scroll the viewport. */
dstockwella685a702019-01-29 02:21:541244 scrollBy(delta) {
Henrique Nakashima585c7af02018-03-27 04:55:211245 const newPosition = this.position;
1246 newPosition.x += delta.x;
1247 newPosition.y += delta.y;
1248 this.scrollTo(newPosition);
[email protected]3528d6302014-02-19 08:13:071249 }
rbpotter84b2076a2019-08-23 01:31:151250
1251 /** Removes all events being tracked from the tracker. */
1252 resetTracker() {
1253 if (this.tracker_) {
1254 this.tracker_.removeAll();
1255 }
1256 }
dstockwella685a702019-01-29 02:21:541257}
rbpotter84b2076a2019-08-23 01:31:151258
1259/**
1260 * Enumeration of pinch states.
1261 * This should match PinchPhase enum in pdf/out_of_process_instance.h
1262 * @enum {number}
1263 */
1264Viewport.PinchPhase = {
1265 PINCH_NONE: 0,
1266 PINCH_START: 1,
1267 PINCH_UPDATE_ZOOM_OUT: 2,
1268 PINCH_UPDATE_ZOOM_IN: 3,
1269 PINCH_END: 4
1270};
1271
1272/**
1273 * The increment to scroll a page by in pixels when up/down/left/right arrow
1274 * keys are pressed. Usually we just let the browser handle scrolling on the
1275 * window when these keys are pressed but in certain cases we need to simulate
1276 * these events.
1277 */
1278Viewport.SCROLL_INCREMENT = 40;
1279
rbpotter84b2076a2019-08-23 01:31:151280/** The width of the page shadow around pages in pixels. */
1281Viewport.PAGE_SHADOW = {
1282 top: 3,
1283 bottom: 7,
1284 left: 5,
1285 right: 5
1286};