| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Returns the area of the intersection of two rectangles. | 6 * Returns the area of the intersection of two rectangles. |
| 7 * @param {Object} rect1 the first rect | 7 * @param {Object} rect1 the first rect |
| 8 * @param {Object} rect2 the second rect | 8 * @param {Object} rect2 the second rect |
| 9 * @return {number} the area of the intersection of the rects | 9 * @return {number} the area of the intersection of the rects |
| 10 */ | 10 */ |
| 11 function getIntersectionArea(rect1, rect2) { | 11 function getIntersectionArea(rect1, rect2) { |
| 12 var xOverlap = Math.max(0, | 12 var xOverlap = Math.max(0, |
| 13 Math.min(rect1.x + rect1.width, rect2.x + rect2.width) - | 13 Math.min(rect1.x + rect1.width, rect2.x + rect2.width) - |
| 14 Math.max(rect1.x, rect2.x)); | 14 Math.max(rect1.x, rect2.x)); |
| 15 var yOverlap = Math.max(0, | 15 var yOverlap = Math.max(0, |
| 16 Math.min(rect1.y + rect1.height, rect2.y + rect2.height) - | 16 Math.min(rect1.y + rect1.height, rect2.y + rect2.height) - |
| 17 Math.max(rect1.y, rect2.y)); | 17 Math.max(rect1.y, rect2.y)); |
| 18 return xOverlap * yOverlap; | 18 return xOverlap * yOverlap; |
| 19 } | 19 } |
| 20 | 20 |
| 21 /** | 21 /** |
| 22 * Create a new viewport. | 22 * Create a new viewport. |
| 23 * @param {Window} window the window | 23 * @param {Window} window the window |
| 24 * @param {Object} sizer is the element which represents the size of the | 24 * @param {Object} sizer is the element which represents the size of the |
| 25 * document in the viewport | 25 * document in the viewport |
| 26 * @param {Function} viewportChangedCallback is run when the viewport changes | 26 * @param {Function} viewportChangedCallback is run when the viewport changes |
| 27 * @param {Function} beforeZoomCallback is run before a change in zoom |
| 28 * @param {Function} afterZoomCallback is run after a change in zoom |
| 27 * @param {number} scrollbarWidth the width of scrollbars on the page | 29 * @param {number} scrollbarWidth the width of scrollbars on the page |
| 28 */ | 30 */ |
| 29 function Viewport(window, | 31 function Viewport(window, |
| 30 sizer, | 32 sizer, |
| 31 viewportChangedCallback, | 33 viewportChangedCallback, |
| 34 beforeZoomCallback, |
| 35 afterZoomCallback, |
| 32 scrollbarWidth) { | 36 scrollbarWidth) { |
| 33 this.window_ = window; | 37 this.window_ = window; |
| 34 this.sizer_ = sizer; | 38 this.sizer_ = sizer; |
| 35 this.viewportChangedCallback_ = viewportChangedCallback; | 39 this.viewportChangedCallback_ = viewportChangedCallback; |
| 40 this.beforeZoomCallback_ = beforeZoomCallback; |
| 41 this.afterZoomCallback_ = afterZoomCallback; |
| 42 this.allowedToChangeZoom_ = false; |
| 36 this.zoom_ = 1; | 43 this.zoom_ = 1; |
| 37 this.documentDimensions_ = null; | 44 this.documentDimensions_ = null; |
| 38 this.pageDimensions_ = []; | 45 this.pageDimensions_ = []; |
| 39 this.scrollbarWidth_ = scrollbarWidth; | 46 this.scrollbarWidth_ = scrollbarWidth; |
| 40 this.fittingType_ = Viewport.FittingType.NONE; | 47 this.fittingType_ = Viewport.FittingType.NONE; |
| 41 | 48 |
| 42 window.addEventListener('scroll', this.updateViewport_.bind(this)); | 49 window.addEventListener('scroll', this.updateViewport_.bind(this)); |
| 43 window.addEventListener('resize', this.resize_.bind(this)); | 50 window.addEventListener('resize', this.resize_.bind(this)); |
| 44 } | 51 } |
| 45 | 52 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 76 Viewport.prototype = { | 83 Viewport.prototype = { |
| 77 /** | 84 /** |
| 78 * @private | 85 * @private |
| 79 * Returns true if the document needs scrollbars at the given zoom level. | 86 * Returns true if the document needs scrollbars at the given zoom level. |
| 80 * @param {number} zoom compute whether scrollbars are needed at this zoom | 87 * @param {number} zoom compute whether scrollbars are needed at this zoom |
| 81 * @return {Object} with 'horizontal' and 'vertical' keys which map to bool | 88 * @return {Object} with 'horizontal' and 'vertical' keys which map to bool |
| 82 * values indicating if the horizontal and vertical scrollbars are needed | 89 * values indicating if the horizontal and vertical scrollbars are needed |
| 83 * respectively. | 90 * respectively. |
| 84 */ | 91 */ |
| 85 documentNeedsScrollbars_: function(zoom) { | 92 documentNeedsScrollbars_: function(zoom) { |
| 93 if (!this.documentDimensions_) { |
| 94 return { |
| 95 horizontal: false, |
| 96 vertical: false |
| 97 }; |
| 98 } |
| 86 var documentWidth = this.documentDimensions_.width * zoom; | 99 var documentWidth = this.documentDimensions_.width * zoom; |
| 87 var documentHeight = this.documentDimensions_.height * zoom; | 100 var documentHeight = this.documentDimensions_.height * zoom; |
| 88 return { | 101 return { |
| 89 horizontal: documentWidth > this.window_.innerWidth, | 102 horizontal: documentWidth > this.window_.innerWidth, |
| 90 vertical: documentHeight > this.window_.innerHeight | 103 vertical: documentHeight > this.window_.innerHeight |
| 91 }; | 104 }; |
| 92 }, | 105 }, |
| 93 | 106 |
| 94 /** | 107 /** |
| 95 * Returns true if the document needs scrollbars at the current zoom level. | 108 * Returns true if the document needs scrollbars at the current zoom level. |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 | 181 |
| 169 /** | 182 /** |
| 170 * @type {number} the zoom level of the viewport. | 183 * @type {number} the zoom level of the viewport. |
| 171 */ | 184 */ |
| 172 get zoom() { | 185 get zoom() { |
| 173 return this.zoom_; | 186 return this.zoom_; |
| 174 }, | 187 }, |
| 175 | 188 |
| 176 /** | 189 /** |
| 177 * @private | 190 * @private |
| 191 * Used to wrap a function that might perform zooming on the viewport. This is |
| 192 * required so that we can notify the plugin that zooming is in progress |
| 193 * so that while zooming is taking place it can stop reacting to scroll events |
| 194 * from the viewport. This is to avoid flickering. |
| 195 */ |
| 196 mightZoom_: function(f) { |
| 197 this.beforeZoomCallback_(); |
| 198 this.allowedToChangeZoom_ = true; |
| 199 f(); |
| 200 this.allowedToChangeZoom_ = false; |
| 201 this.afterZoomCallback_(); |
| 202 }, |
| 203 |
| 204 /** |
| 205 * @private |
| 178 * Sets the zoom of the viewport. | 206 * Sets the zoom of the viewport. |
| 179 * @param {number} newZoom the zoom level to zoom to. | 207 * @param {number} newZoom the zoom level to zoom to. |
| 180 */ | 208 */ |
| 181 setZoom_: function(newZoom) { | 209 setZoom_: function(newZoom) { |
| 210 if (!this.allowedToChangeZoom_) |
| 211 throw 'Called Viewport.setZoom_ without calling Viewport.mightZoom_.'; |
| 182 var oldZoom = this.zoom_; | 212 var oldZoom = this.zoom_; |
| 183 this.zoom_ = newZoom; | 213 this.zoom_ = newZoom; |
| 184 // Record the scroll position (relative to the middle of the window). | 214 // Record the scroll position (relative to the middle of the window). |
| 185 var currentScrollPos = [ | 215 var currentScrollPos = [ |
| 186 (this.window_.pageXOffset + this.window_.innerWidth / 2) / oldZoom, | 216 (this.window_.pageXOffset + this.window_.innerWidth / 2) / oldZoom, |
| 187 (this.window_.pageYOffset + this.window_.innerHeight / 2) / oldZoom | 217 (this.window_.pageYOffset + this.window_.innerHeight / 2) / oldZoom |
| 188 ]; | 218 ]; |
| 189 this.contentSizeChanged_(); | 219 this.contentSizeChanged_(); |
| 190 // Scroll to the scaled scroll position. | 220 // Scroll to the scaled scroll position. |
| 191 this.window_.scrollTo( | 221 this.window_.scrollTo( |
| 192 currentScrollPos[0] * newZoom - this.window_.innerWidth / 2, | 222 currentScrollPos[0] * newZoom - this.window_.innerWidth / 2, |
| 193 currentScrollPos[1] * newZoom - this.window_.innerHeight / 2); | 223 currentScrollPos[1] * newZoom - this.window_.innerHeight / 2); |
| 194 }, | 224 }, |
| 195 | 225 |
| 196 /** | 226 /** |
| 227 * @private |
| 228 * Sets the zoom for testing purposes. |
| 229 */ |
| 230 setZoomForTest_: function(newZoom) { |
| 231 this.mightZoom_(function() { |
| 232 this.setZoom_(newZoom); |
| 233 }.bind(this)); |
| 234 }, |
| 235 |
| 236 /** |
| 197 * @type {number} the width of scrollbars in the viewport in pixels. | 237 * @type {number} the width of scrollbars in the viewport in pixels. |
| 198 */ | 238 */ |
| 199 get scrollbarWidth() { | 239 get scrollbarWidth() { |
| 200 return this.scrollbarWidth_; | 240 return this.scrollbarWidth_; |
| 201 }, | 241 }, |
| 202 | 242 |
| 203 /** | 243 /** |
| 204 * @type {Viewport.FittingType} the fitting type the viewport is currently in. | 244 * @type {Viewport.FittingType} the fitting type the viewport is currently in. |
| 205 */ | 245 */ |
| 206 get fittingType() { | 246 get fittingType() { |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 326 var zoomHeight = windowWithScrollbars.height / pageDimensions.height; | 366 var zoomHeight = windowWithScrollbars.height / pageDimensions.height; |
| 327 zoom = Math.min(zoomWidth, zoomHeight); | 367 zoom = Math.min(zoomWidth, zoomHeight); |
| 328 } | 368 } |
| 329 return zoom; | 369 return zoom; |
| 330 }, | 370 }, |
| 331 | 371 |
| 332 /** | 372 /** |
| 333 * Zoom the viewport so that the page-width consumes the entire viewport. | 373 * Zoom the viewport so that the page-width consumes the entire viewport. |
| 334 */ | 374 */ |
| 335 fitToWidth: function() { | 375 fitToWidth: function() { |
| 336 this.fittingType_ = Viewport.FittingType.FIT_TO_WIDTH; | 376 this.mightZoom_(function() { |
| 337 if (!this.documentDimensions_) | 377 this.fittingType_ = Viewport.FittingType.FIT_TO_WIDTH; |
| 338 return; | 378 if (!this.documentDimensions_) |
| 339 // Track the last y-position so we stay at the same position after zooming. | 379 return; |
| 340 var oldY = this.window_.pageYOffset / this.zoom_; | 380 // Track the last y-position to stay at the same position after zooming. |
| 341 // When computing fit-to-width, the maximum width of a page in the document | 381 var oldY = this.window_.pageYOffset / this.zoom_; |
| 342 // is used, which is equal to the size of the document width. | 382 // When computing fit-to-width, the maximum width of a page in the |
| 343 this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true)); | 383 // document is used, which is equal to the size of the document width. |
| 344 var page = this.getMostVisiblePage(); | 384 this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true)); |
| 345 this.window_.scrollTo(0, oldY * this.zoom_); | 385 var page = this.getMostVisiblePage(); |
| 346 this.updateViewport_(); | 386 this.window_.scrollTo(0, oldY * this.zoom_); |
| 387 this.updateViewport_(); |
| 388 }.bind(this)); |
| 347 }, | 389 }, |
| 348 | 390 |
| 349 /** | 391 /** |
| 350 * Zoom the viewport so that a page consumes the entire viewport. Also scrolls | 392 * Zoom the viewport so that a page consumes the entire viewport. Also scrolls |
| 351 * to the top of the most visible page. | 393 * to the top of the most visible page. |
| 352 */ | 394 */ |
| 353 fitToPage: function() { | 395 fitToPage: function() { |
| 354 this.fittingType_ = Viewport.FittingType.FIT_TO_PAGE; | 396 this.mightZoom_(function() { |
| 355 if (!this.documentDimensions_) | 397 this.fittingType_ = Viewport.FittingType.FIT_TO_PAGE; |
| 356 return; | 398 if (!this.documentDimensions_) |
| 357 var page = this.getMostVisiblePage(); | 399 return; |
| 358 this.setZoom_(this.computeFittingZoom_(this.pageDimensions_[page], false)); | 400 var page = this.getMostVisiblePage(); |
| 359 // Center the document in the page by scrolling by the amount of empty | 401 this.setZoom_(this.computeFittingZoom_( |
| 360 // space to the left of the document. | 402 this.pageDimensions_[page], false)); |
| 361 var xOffset = | 403 // Center the document in the page by scrolling by the amount of empty |
| 362 (this.documentDimensions_.width - this.pageDimensions_[page].width) * | 404 // space to the left of the document. |
| 363 this.zoom_ / 2; | 405 var xOffset = |
| 364 this.window_.scrollTo(xOffset, | 406 (this.documentDimensions_.width - this.pageDimensions_[page].width) * |
| 365 this.pageDimensions_[page].y * this.zoom_); | 407 this.zoom_ / 2; |
| 366 this.updateViewport_(); | 408 this.window_.scrollTo(xOffset, |
| 409 this.pageDimensions_[page].y * this.zoom_); |
| 410 this.updateViewport_(); |
| 411 }.bind(this)); |
| 367 }, | 412 }, |
| 368 | 413 |
| 369 /** | 414 /** |
| 370 * Zoom out to the next predefined zoom level. | 415 * Zoom out to the next predefined zoom level. |
| 371 */ | 416 */ |
| 372 zoomOut: function() { | 417 zoomOut: function() { |
| 373 this.fittingType_ = Viewport.FittingType.NONE; | 418 this.mightZoom_(function() { |
| 374 var nextZoom = Viewport.ZOOM_FACTORS[0]; | 419 this.fittingType_ = Viewport.FittingType.NONE; |
| 375 for (var i = 0; i < Viewport.ZOOM_FACTORS.length; i++) { | 420 var nextZoom = Viewport.ZOOM_FACTORS[0]; |
| 376 if (Viewport.ZOOM_FACTORS[i] < this.zoom_) | 421 for (var i = 0; i < Viewport.ZOOM_FACTORS.length; i++) { |
| 377 nextZoom = Viewport.ZOOM_FACTORS[i]; | 422 if (Viewport.ZOOM_FACTORS[i] < this.zoom_) |
| 378 } | 423 nextZoom = Viewport.ZOOM_FACTORS[i]; |
| 379 this.setZoom_(nextZoom); | 424 } |
| 380 this.updateViewport_(); | 425 this.setZoom_(nextZoom); |
| 426 this.updateViewport_(); |
| 427 }.bind(this)); |
| 381 }, | 428 }, |
| 382 | 429 |
| 383 /** | 430 /** |
| 384 * Zoom in to the next predefined zoom level. | 431 * Zoom in to the next predefined zoom level. |
| 385 */ | 432 */ |
| 386 zoomIn: function() { | 433 zoomIn: function() { |
| 387 this.fittingType_ = Viewport.FittingType.NONE; | 434 this.mightZoom_(function() { |
| 388 var nextZoom = Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1]; | 435 this.fittingType_ = Viewport.FittingType.NONE; |
| 389 for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) { | 436 var nextZoom = Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1]; |
| 390 if (Viewport.ZOOM_FACTORS[i] > this.zoom_) | 437 for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) { |
| 391 nextZoom = Viewport.ZOOM_FACTORS[i]; | 438 if (Viewport.ZOOM_FACTORS[i] > this.zoom_) |
| 392 } | 439 nextZoom = Viewport.ZOOM_FACTORS[i]; |
| 393 this.setZoom_(nextZoom); | 440 } |
| 394 this.updateViewport_(); | 441 this.setZoom_(nextZoom); |
| 442 this.updateViewport_(); |
| 443 }.bind(this)); |
| 395 }, | 444 }, |
| 396 | 445 |
| 397 /** | 446 /** |
| 398 * Go to the given page index. | 447 * Go to the given page index. |
| 399 * @param {number} page the index of the page to go to. | 448 * @param {number} page the index of the page to go to. |
| 400 */ | 449 */ |
| 401 goToPage: function(page) { | 450 goToPage: function(page) { |
| 402 if (this.pageDimensions_.length == 0) | 451 this.mightZoom_(function() { |
| 403 return; | 452 if (this.pageDimensions_.length == 0) |
| 404 if (page < 0) | 453 return; |
| 405 page = 0; | 454 if (page < 0) |
| 406 if (page >= this.pageDimensions_.length) | 455 page = 0; |
| 407 page = this.pageDimensions_.length - 1; | 456 if (page >= this.pageDimensions_.length) |
| 408 var dimensions = this.pageDimensions_[page]; | 457 page = this.pageDimensions_.length - 1; |
| 409 this.window_.scrollTo(dimensions.x * this.zoom_, dimensions.y * this.zoom_); | 458 var dimensions = this.pageDimensions_[page]; |
| 459 this.window_.scrollTo(dimensions.x * this.zoom_, |
| 460 dimensions.y * this.zoom_); |
| 461 this.updateViewport_(); |
| 462 }.bind(this)); |
| 410 }, | 463 }, |
| 411 | 464 |
| 412 /** | 465 /** |
| 413 * Set the dimensions of the document. | 466 * Set the dimensions of the document. |
| 414 * @param {Object} documentDimensions the dimensions of the document | 467 * @param {Object} documentDimensions the dimensions of the document |
| 415 */ | 468 */ |
| 416 setDocumentDimensions: function(documentDimensions) { | 469 setDocumentDimensions: function(documentDimensions) { |
| 417 var initialDimensions = !this.documentDimensions_; | 470 this.mightZoom_(function() { |
| 418 this.documentDimensions_ = documentDimensions; | 471 var initialDimensions = !this.documentDimensions_; |
| 419 this.pageDimensions_ = this.documentDimensions_.pageDimensions; | 472 this.documentDimensions_ = documentDimensions; |
| 420 if (initialDimensions) { | 473 this.pageDimensions_ = this.documentDimensions_.pageDimensions; |
| 421 this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true)); | 474 if (initialDimensions) { |
| 422 if (this.zoom_ > 1) | 475 this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true)); |
| 423 this.setZoom_(1); | 476 if (this.zoom_ > 1) |
| 424 this.window_.scrollTo(0, 0); | 477 this.setZoom_(1); |
| 425 } | 478 this.window_.scrollTo(0, 0); |
| 426 this.contentSizeChanged_(); | 479 } |
| 427 this.resize_(); | 480 this.contentSizeChanged_(); |
| 481 this.resize_(); |
| 482 }.bind(this)); |
| 428 }, | 483 }, |
| 429 | 484 |
| 430 /** | 485 /** |
| 431 * Get the coordinates of the page contents (excluding the page shadow) | 486 * Get the coordinates of the page contents (excluding the page shadow) |
| 432 * relative to the screen. | 487 * relative to the screen. |
| 433 * @param {number} page the index of the page to get the rect for. | 488 * @param {number} page the index of the page to get the rect for. |
| 434 * @return {Object} a rect representing the page in screen coordinates. | 489 * @return {Object} a rect representing the page in screen coordinates. |
| 435 */ | 490 */ |
| 436 getPageScreenRect: function(page) { | 491 getPageScreenRect: function(page) { |
| 492 if (!this.documentDimensions_) { |
| 493 return { |
| 494 x: 0, |
| 495 y: 0, |
| 496 width: 0, |
| 497 height: 0 |
| 498 }; |
| 499 } |
| 437 if (page >= this.pageDimensions_.length) | 500 if (page >= this.pageDimensions_.length) |
| 438 page = this.pageDimensions_.length - 1; | 501 page = this.pageDimensions_.length - 1; |
| 439 | 502 |
| 440 var pageDimensions = this.pageDimensions_[page]; | 503 var pageDimensions = this.pageDimensions_[page]; |
| 441 | 504 |
| 442 // Compute the page dimensions minus the shadows. | 505 // Compute the page dimensions minus the shadows. |
| 443 var insetDimensions = { | 506 var insetDimensions = { |
| 444 x: pageDimensions.x + Viewport.PAGE_SHADOW.left, | 507 x: pageDimensions.x + Viewport.PAGE_SHADOW.left, |
| 445 y: pageDimensions.y + Viewport.PAGE_SHADOW.top, | 508 y: pageDimensions.y + Viewport.PAGE_SHADOW.top, |
| 446 width: pageDimensions.width - Viewport.PAGE_SHADOW.left - | 509 width: pageDimensions.width - Viewport.PAGE_SHADOW.left - |
| (...skipping 14 matching lines...) Expand all Loading... |
| 461 spaceOnLeft = Math.max(spaceOnLeft, 0); | 524 spaceOnLeft = Math.max(spaceOnLeft, 0); |
| 462 | 525 |
| 463 return { | 526 return { |
| 464 x: x * this.zoom_ + spaceOnLeft - this.window_.pageXOffset, | 527 x: x * this.zoom_ + spaceOnLeft - this.window_.pageXOffset, |
| 465 y: insetDimensions.y * this.zoom_ - this.window_.pageYOffset, | 528 y: insetDimensions.y * this.zoom_ - this.window_.pageYOffset, |
| 466 width: insetDimensions.width * this.zoom_, | 529 width: insetDimensions.width * this.zoom_, |
| 467 height: insetDimensions.height * this.zoom_ | 530 height: insetDimensions.height * this.zoom_ |
| 468 }; | 531 }; |
| 469 } | 532 } |
| 470 }; | 533 }; |
| OLD | NEW |