update ui for chrome 61.0.3163.100

This commit is contained in:
deepak1556 2017-12-01 13:08:13 +05:30
Родитель beb36874a6
Коммит 8fd9c2b363
42 изменённых файлов: 1840 добавлений и 1462 удалений

Просмотреть файл

@ -37,11 +37,13 @@ class BrowserApi {
* upon starting the plugin.
* @param {boolean} manageZoom Whether to manage zoom.
*/
constructor(streamInfo, defaultZoom, initialZoom, manageZoom) {
constructor(streamInfo, defaultZoom, initialZoom) {
this.streamInfo_ = streamInfo;
this.defaultZoom_ = defaultZoom;
this.initialZoom_ = initialZoom;
this.manageZoom_ = manageZoom;
this.zoomBehavior_ = streamInfo.embedded ?
BrowserApi.ZoomBehavior.PROPAGATE_PARENT :
BrowserApi.ZoomBehavior.MANAGE;
}
/**
@ -50,13 +52,13 @@ class BrowserApi {
* contained in the PDF.
* @param {boolean} manageZoom Whether to manage zoom.
*/
static create(streamInfo, manageZoom) {
static create(streamInfo) {
return Promise.all([
lookupDefaultZoom(),
lookupInitialZoom()
]).then(function(zoomFactors) {
return new BrowserApi(
streamInfo, zoomFactors[0], zoomFactors[1], manageZoom);
streamInfo, zoomFactors[0], zoomFactors[1]);
});
}
@ -75,7 +77,7 @@ class BrowserApi {
* has been updated.
*/
setZoom(zoom) {
if (!this.manageZoom_)
if (this.zoomBehavior_ != BrowserApi.ZoomBehavior.MANAGE)
return Promise.resolve();
return cr.sendWithPromise('setZoom', zoom);
}
@ -96,13 +98,21 @@ class BrowserApi {
return this.initialZoom_;
}
/**
* Returns how to manage the zoom.
* @return {BrowserApi.ZoomBehavior} How to manage zoom.
*/
getZoomBehavior() {
return this.zoomBehavior_;
}
/**
* Adds an event listener to be notified when the browser zoom changes.
* @param {function} listener The listener to be called with the new zoom
* factor.
*/
addZoomEventListener(listener) {
if (!this.manageZoom_)
if (this.zoomBehavior_ != BrowserApi.ZoomBehavior.MANAGE)
return;
cr.addWebUIListener('onZoomLevelChanged', function(newZoomFactor) {
@ -111,6 +121,16 @@ class BrowserApi {
}
};
/**
* Enumeration of ways to manage zoom changes.
* @enum {number}
*/
BrowserApi.ZoomBehavior = {
NONE: 0,
MANAGE: 1,
PROPAGATE_PARENT: 2
};
/**
* Creates a BrowserApi instance for an extension not running as a mime handler.
* @return {Promise<BrowserApi>} A promise to a BrowserApi instance constructed
@ -125,6 +145,6 @@ function createBrowserApi(opts) {
tabId: -1,
};
return new Promise(function(resolve, reject) {
resolve(BrowserApi.create(streamInfo, true));
resolve(BrowserApi.create(streamInfo));
});
}

16
elements/shared-vars.html Normal file
Просмотреть файл

@ -0,0 +1,16 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
<style is="custom-style">
html {
--primary-text-color: var(--paper-grey-900);
--iron-icon-height: 20px;
--iron-icon-width: 20px;
--paper-icon-button: {
height: 32px;
padding: 6px;
width: 32px;
};
--paper-icon-button-ink-color: rgb(189, 189, 189);
--viewer-icon-ink-color: rgb(189, 189, 189);
}
</style>

Просмотреть файл

@ -1,52 +0,0 @@
/* Copyright 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
#item {
@apply(--layout-center);
@apply(--layout-horizontal);
color: rgb(80, 80, 80);
cursor: pointer;
font-size: 77.8%;
height: 30px;
position: relative;
}
#item:hover {
background-color: rgb(237, 237, 237);
color: rgb(20, 20, 20);
}
paper-ripple {
/* Allowing the ripple to capture pointer events prevents a focus rectangle
* for showing up for clicks, while still allowing it with tab-navigation.
* This undoes a paper-ripple bugfix aimed at non-Chrome browsers.
* TODO(tsergeant): Improve focus in viewer-bookmark so this can be removed
* (https://crbug.com/5448190). */
pointer-events: auto;
}
#title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#expand {
--iron-icon-height: 16px;
--iron-icon-width: 16px;
--paper-icon-button-ink-color: var(--paper-grey-900);
height: 28px;
min-width: 28px;
padding: 6px;
transition: transform 150ms;
width: 28px;
}
:host-context([dir=rtl]) #expand {
transform: rotate(180deg);
}
:host([children-shown]) #expand {
transform: rotate(90deg);
}

Просмотреть файл

@ -1,11 +1,59 @@
<link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
<dom-module id="viewer-bookmark" attributes="bookmark">
<link rel="import" type="css" href="viewer-bookmark.css">
<template>
<style>
#item {
@apply(--layout-center);
@apply(--layout-horizontal);
cursor: pointer;
height: 30px;
position: relative;
}
#item:hover {
background-color: rgb(237, 237, 237);
}
paper-ripple {
/* Allowing the ripple to capture pointer events prevents a focus
* rectangle for showing up for clicks, while still allowing it with
* tab-navigation. This undoes a paper-ripple bug fix aimed at
* non-Chrome browsers. TODO(tsergeant): Improve focus in
* viewer-bookmark so this can be removed (https://crbug.com/5448190).
*/
pointer-events: auto;
}
#title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#expand {
--iron-icon-height: 16px;
--iron-icon-width: 16px;
--paper-icon-button-ink-color: var(--paper-grey-900);
height: 28px;
min-width: 28px;
padding: 6px;
transition: transform 150ms;
width: 28px;
}
:host-context([dir=rtl]) #expand {
transform: rotate(180deg);
}
:host([children-shown]) #expand {
transform: rotate(90deg);
}
</style>
<div id="item" on-click="onClick">
<paper-ripple></paper-ripple>
<paper-icon-button id="expand" icon="cr:chevron-right"
@ -15,12 +63,12 @@
</div>
<!-- dom-if will stamp the complex bookmark tree lazily as individual nodes
are opened. -->
<template is="dom-if" if="{{childrenShown}}" id="sub-bookmarks">
<template is="dom-repeat" items="{{bookmark.children}}">
<template is="dom-if" if="[[childrenShown]]" id="sub-bookmarks">
<template is="dom-repeat" items="[[bookmark.children]]">
<viewer-bookmark bookmark="{{item}}" depth="{{childDepth}}">
</viewer-bookmark>
</template>
</template>
</template>
<script src="viewer-bookmark.js"></script>
</dom-module>
<script src="viewer-bookmark.js"></script>

Просмотреть файл

@ -2,91 +2,91 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* The |title| is the text label displayed for the bookmark.
*
* The bookmark may point at a location in the PDF or a URI.
* If it points at a location, |page| indicates which 0-based page it leads to.
* Optionally, |y| is the y position in that page, in pixel coordinates.
* If it points at an URI, |uri| is the target for that bookmark.
*
* |children| is an array of the |Bookmark|s that are below this in a table of
* contents tree structure.
*/
var Bookmark;
(function() {
/** Amount that each level of bookmarks is indented by (px). */
var BOOKMARK_INDENT = 20;
/** Amount that each level of bookmarks is indented by (px). */
var BOOKMARK_INDENT = 20;
Polymer({
is: 'viewer-bookmark',
Polymer({
is: 'viewer-bookmark',
properties: {
/**
* A bookmark object, each containing a:
* - title
* - page (optional)
* - children (an array of bookmarks)
*/
bookmark: {
type: Object,
observer: 'bookmarkChanged_'
},
properties: {
/** @type {Bookmark} */
bookmark: {type: Object, observer: 'bookmarkChanged_'},
depth: {
type: Number,
observer: 'depthChanged'
},
depth: {type: Number, observer: 'depthChanged'},
childDepth: Number,
childDepth: Number,
childrenShown: {
type: Boolean,
reflectToAttribute: true,
value: false
},
childrenShown: {type: Boolean, reflectToAttribute: true, value: false},
keyEventTarget: {
type: Object,
value: function() {
return this.$.item;
}
keyEventTarget: {
type: Object,
value: function() {
return this.$.item;
}
},
behaviors: [
Polymer.IronA11yKeysBehavior
],
keyBindings: {
'enter': 'onEnter_',
'space': 'onSpace_'
},
bookmarkChanged_: function() {
this.$.expand.style.visibility =
this.bookmark.children.length > 0 ? 'visible' : 'hidden';
},
depthChanged: function() {
this.childDepth = this.depth + 1;
this.$.item.style.webkitPaddingStart =
(this.depth * BOOKMARK_INDENT) + 'px';
},
onClick: function() {
if (this.bookmark.hasOwnProperty('page'))
this.fire('change-page', {page: this.bookmark.page});
else if (this.bookmark.hasOwnProperty('uri'))
this.fire('navigate', {uri: this.bookmark.uri, newtab: true});
},
onEnter_: function(e) {
// Don't allow events which have propagated up from the expand button to
// trigger a click.
if (e.detail.keyboardEvent.target != this.$.expand)
this.onClick();
},
onSpace_: function(e) {
// paper-icon-button stops propagation of space events, so there's no need
// to check the event source here.
this.onClick();
// Prevent default space scroll behavior.
e.detail.keyboardEvent.preventDefault();
},
toggleChildren: function(e) {
this.childrenShown = !this.childrenShown;
e.stopPropagation(); // Prevent the above onClick handler from firing.
}
});
},
behaviors: [Polymer.IronA11yKeysBehavior],
keyBindings: {'enter': 'onEnter_', 'space': 'onSpace_'},
bookmarkChanged_: function() {
this.$.expand.style.visibility =
this.bookmark.children.length > 0 ? 'visible' : 'hidden';
},
depthChanged: function() {
this.childDepth = this.depth + 1;
this.$.item.style.webkitPaddingStart =
(this.depth * BOOKMARK_INDENT) + 'px';
},
onClick: function() {
if (this.bookmark.hasOwnProperty('page')) {
if (this.bookmark.hasOwnProperty('y')) {
this.fire(
'change-page-and-y',
{page: this.bookmark.page, y: this.bookmark.y});
} else {
this.fire('change-page', {page: this.bookmark.page});
}
} else if (this.bookmark.hasOwnProperty('uri')) {
this.fire('navigate', {uri: this.bookmark.uri, newtab: true});
}
},
onEnter_: function(e) {
// Don't allow events which have propagated up from the expand button to
// trigger a click.
if (e.detail.keyboardEvent.target != this.$.expand)
this.onClick();
},
onSpace_: function(e) {
// paper-icon-button stops propagation of space events, so there's no need
// to check the event source here.
this.onClick();
// Prevent default space scroll behavior.
e.detail.keyboardEvent.preventDefault();
},
toggleChildren: function(e) {
this.childrenShown = !this.childrenShown;
e.stopPropagation(); // Prevent the above onClick handler from firing.
}
});
})();

Просмотреть файл

@ -1,11 +1,12 @@
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="../viewer-bookmark/viewer-bookmark.html">
<dom-module id="viewer-bookmarks-content">
<template>
<template is="dom-repeat" items="{{bookmarks}}">
<template is="dom-repeat" items="[[bookmarks]]">
<viewer-bookmark bookmark="{{item}}" depth="0"></viewer-bookmark>
</template>
</template>
<script src="viewer-bookmarks-content.js"></script>
</dom-module>
<script src="viewer-bookmarks-content.js"></script>

Просмотреть файл

@ -2,6 +2,4 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
Polymer({
is: 'viewer-bookmarks-content'
});
Polymer({is: 'viewer-bookmarks-content'});

Просмотреть файл

@ -1,7 +0,0 @@
/* Copyright 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
.last-item {
margin-bottom: 24px;
}

Просмотреть файл

@ -1,22 +1,24 @@
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/fade-in-animation.html">
<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-dialog/paper-dialog.html">
<dom-module id="viewer-error-screen">
<link rel="import" type="css" href="viewer-error-screen.css">
<template>
<paper-dialog id="dialog" modal no-cancel-on-esc-key
entry-animation="fade-in-animation">
<div id="load-failed-message" class="last-item">
{{strings.pageLoadFailed}}
<style include="cr-shared-style"></style>
<dialog is="cr-dialog" id="dialog" no-cancel>
<div slot="title">
[[strings.errorDialogTitle]]
</div>
<div class="buttons" hidden$="{{!reloadFn}}">
<paper-button on-click="reload" autofocus>
{{strings.pageReload}}
<div slot="body">
[[strings.pageLoadFailed]]
</div>
<div slot="button-container" hidden$="[[!reloadFn]]">
<paper-button class="action-button" on-click="reload">
[[strings.pageReload]]
</paper-button>
</div>
</paper-dialog>
</dialog>
</template>
<script src="viewer-error-screen.js"></script>
</dom-module>
<script src="viewer-error-screen.js"></script>

Просмотреть файл

@ -5,27 +5,13 @@
Polymer({
is: 'viewer-error-screen',
properties: {
reloadFn: {
type: Object,
value: null,
observer: 'reloadFnChanged_'
},
reloadFn: Function,
strings: Object,
},
reloadFnChanged_: function() {
// The default margins in paper-dialog don't work well with hiding/showing
// the .buttons div. We need to manually manage the bottom margin to get
// around this.
if (this.reloadFn)
this.$['load-failed-message'].classList.remove('last-item');
else
this.$['load-failed-message'].classList.add('last-item');
},
show: function() {
this.$.dialog.open();
/** @type {!CrDialogElement} */ (this.$.dialog).showModal();
},
reload: function() {

Просмотреть файл

@ -1,35 +0,0 @@
/* Copyright 2013 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
:host {
-webkit-transition: opacity 400ms ease-in-out;
pointer-events: none;
position: fixed;
right: 0;
}
#text {
background-color: rgba(0, 0, 0, 0.5);
border-radius: 5px;
color: white;
float: left;
font-family: sans-serif;
font-size: 12px;
font-weight: bold;
line-height: 48px;
text-align: center;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
width: 62px;
}
#triangle-right {
border-bottom: 6px solid transparent;
border-left: 8px solid rgba(0, 0, 0, 0.5);
border-top: 6px solid transparent;
display: inline;
float: left;
height: 0;
margin-top: 18px;
width: 0;
}

Просмотреть файл

@ -1,10 +1,42 @@
<link rel="import" href="chrome://resources/html/polymer.html">
<dom-module id="viewer-page-indicator">
<link rel="import" type="css" href="viewer-page-indicator.css">
<template>
<style>
:host {
pointer-events: none;
position: fixed;
right: 0;
transition: opacity 400ms ease-in-out;
}
#text {
background-color: rgba(0, 0, 0, 0.5);
border-radius: 5px;
color: white;
float: left;
font-family: sans-serif;
font-size: 12px;
font-weight: bold;
line-height: 48px;
text-align: center;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
width: 62px;
}
#triangle-right {
border-bottom: 6px solid transparent;
border-left: 8px solid rgba(0, 0, 0, 0.5);
border-top: 6px solid transparent;
display: inline;
float: left;
height: 0;
margin-top: 18px;
width: 0;
}
</style>
<div id="text">{{label}}</div>
<div id="triangle-right"></div>
</template>
<script src="viewer-page-indicator.js"></script>
</dom-module>
<script src="viewer-page-indicator.js"></script>

Просмотреть файл

@ -6,25 +6,17 @@ Polymer({
is: 'viewer-page-indicator',
properties: {
label: {
type: String,
value: '1'
},
label: {type: String, value: '1'},
index: {
type: Number,
observer: 'indexChanged'
},
index: {type: Number, observer: 'indexChanged'},
pageLabels: {
type: Array,
value: null,
observer: 'pageLabelsChanged'
}
pageLabels: {type: Array, value: null, observer: 'pageLabelsChanged'}
},
/** @type {number|undefined} */
timerId: undefined,
/** @override */
ready: function() {
var callback = this.fadeIn.bind(this, 2000);
window.addEventListener('scroll', function() {
@ -36,27 +28,29 @@ Polymer({
this.fadeIn(6000);
},
/** @param {number} displayTime */
fadeIn: function(displayTime) {
var percent = window.scrollY /
(document.body.scrollHeight -
(document.scrollingElement.scrollHeight -
document.documentElement.clientHeight);
this.style.top = percent *
(document.documentElement.clientHeight - this.offsetHeight) + 'px';
<if expr="is_macosx">
this.style.top =
percent * (document.documentElement.clientHeight - this.offsetHeight) +
'px';
// <if expr="is_macosx">
// On the Mac, if overlay scrollbars are enabled, prevent them from
// overlapping the triangle.
if (window.innerWidth == document.body.scrollWidth)
if (window.innerWidth == document.scrollingElement.scrollWidth)
this.style.right = '16px';
else
this.style.right = '0px';
</if>
// </if>
this.style.opacity = 1;
clearTimeout(this.timerId);
this.timerId = setTimeout(function() {
this.timerId = setTimeout(() => {
this.style.opacity = 0;
this.timerId = undefined;
}.bind(this), displayTime);
}, displayTime);
},
pageLabelsChanged: function() {

Просмотреть файл

@ -1,53 +0,0 @@
/* Copyright 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
:host {
color: #fff;
font-size: 94.4%;
}
:host ::selection {
background: rgba(255, 255, 255, 0.3);
}
#pageselector {
--paper-input-container-underline: {
visibility: hidden;
};
--paper-input-container-underline-focus: {
visibility: hidden;
};
display: inline-block;
padding: 0;
width: 1ch;
}
#input {
-webkit-margin-start: -3px;
color: #fff;
line-height: 18px;
padding: 3px;
text-align: end;
vertical-align: baseline;
}
#input:focus,
#input:hover {
background-color: rgba(0, 0, 0, 0.5);
border-radius: 2px;
}
#slash {
padding: 0 3px;
}
#pagelength-spacer {
display: inline-block;
text-align: start;
}
#slash,
#pagelength {
font-size: 76.5%;
}

Просмотреть файл

@ -3,8 +3,56 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html">
<dom-module id="viewer-page-selector">
<link rel="import" type="css" href="viewer-page-selector.css">
<template>
<style>
:host {
color: #fff;
font-size: 1.23rem;
}
:host ::selection {
background: rgba(255, 255, 255, 0.3);
}
#pageselector {
--container: { visibility: hidden; };
--paper-input-container-underline: var(--container);
--paper-input-container-underline-focus: var(--container);
display: inline-block;
padding: 0;
width: 1ch;
}
#input {
-webkit-margin-start: -3px;
color: #fff;
line-height: 18px;
padding: 3px;
text-align: end;
vertical-align: baseline;
}
#input:focus,
#input:hover {
background-color: rgba(0, 0, 0, 0.5);
border-radius: 2px;
}
#slash {
padding: 0 3px;
}
#pagelength-spacer {
display: inline-block;
text-align: start;
}
#input,
#slash,
#pagelength {
font-size: 0.81rem;
}
</style>
<paper-input-container id="pageselector" no-label-float>
<input id="input" is="iron-input" value="{{pageNo}}"
prevent-invalid-input allowed-pattern="\d" on-mouseup="select"
@ -15,5 +63,5 @@
<span id="pagelength">{{docLength}}</span>
</span>
</template>
<script src="viewer-page-selector.js"></script>
</dom-module>
<script src="viewer-page-selector.js"></script>

Просмотреть файл

@ -9,27 +9,20 @@ Polymer({
/**
* The number of pages the document contains.
*/
docLength: {
type: Number,
value: 1,
observer: 'docLengthChanged'
},
docLength: {type: Number, value: 1, observer: 'docLengthChanged_'},
/**
* The current page being viewed (1-based). A change to pageNo is mirrored
* immediately to the input field. A change to the input field is not
* mirrored back until pageNoCommitted() is called and change-page is fired.
*/
pageNo: {
type: Number,
value: 1
},
pageNo: {type: Number, value: 1},
strings: Object,
},
pageNoCommitted: function() {
var page = parseInt(this.$.input.value);
var page = parseInt(this.$.input.value, 10);
if (!isNaN(page) && page <= this.docLength && page > 0)
this.fire('change-page', {page: page - 1});
@ -38,7 +31,8 @@ Polymer({
this.$.input.blur();
},
docLengthChanged: function() {
/** @private */
docLengthChanged_: function() {
var numDigits = this.docLength.toString().length;
this.$.pageselector.style.width = numDigits + 'ch';
// Set both sides of the slash to the same width, so that the layout is

Просмотреть файл

@ -1,30 +1,38 @@
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/fade-in-animation.html">
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/fade-out-animation.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-dialog/paper-dialog.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
<dom-module id="viewer-password-screen">
<template>
<style include="iron-flex iron-flex-alignment"></style>
<paper-dialog id="dialog" modal no-cancel-on-esc-key
entry-animation="fade-in-animation" exit-animation="fade-out-animation">
<div id="message">{{strings.passwordPrompt}}</div>
<div class="horizontal layout start">
<paper-input-container id="password-container" class="flex"
no-label-float invalid="[[invalid]]">
<input is="iron-input" id="password" type="password" size="20"
on-keypress="handleKey" autofocus>
</input>
<paper-input-error>{{strings.passwordInvalid}}</paper-input-error>
</paper-input-container>
<paper-button id="submit" on-click="submit">
{{strings.passwordSubmit}}
<style include="cr-shared-style">
#password {
--paper-input-container-focus-color: var(--google-blue-500);
--paper-input-container-input: {
font-size: inherit;
};
}
</style>
<dialog is="cr-dialog" id="dialog" no-cancel>
<div slot="title">[[strings.passwordDialogTitle]]</div>
<div slot="body">
<div id="message">[[strings.passwordPrompt]]</div>
<paper-input id="password"
type="password"
error-message="[[strings.passwordInvalid]]"
invalid="[[invalid]]"
no-label-float
autofocus>
</paper-input>
</div>
<div slot="button-container">
<paper-button id="submit" class="action-button" on-click="submit">
[[strings.passwordSubmit]]
</paper-button>
</div>
</paper-dialog>
</dialog>
</template>
<script src="viewer-password-screen.js"></script>
</dom-module>
<script src="viewer-password-screen.js"></script>

Просмотреть файл

@ -8,50 +8,37 @@ Polymer({
properties: {
invalid: Boolean,
active: {
type: Boolean,
value: false,
observer: 'activeChanged'
},
strings: Object,
},
ready: function() {
this.activeChanged();
get active() {
return this.$.dialog.open;
},
accept: function() {
this.active = false;
show: function() {
this.$.dialog.showModal();
},
close: function() {
if (this.active)
this.$.dialog.close();
},
deny: function() {
this.$.password.disabled = false;
var password = /** @type {!PaperInputElement} */ (this.$.password);
password.disabled = false;
this.$.submit.disabled = false;
this.invalid = true;
this.$.password.focus();
this.$.password.select();
},
handleKey: function(e) {
if (e.keyCode == 13)
this.submit();
password.focus();
password.inputElement.select();
},
submit: function() {
if (this.$.password.value.length == 0)
var password = /** @type {!PaperInputElement} */ (this.$.password);
if (password.value.length == 0)
return;
this.$.password.disabled = true;
password.disabled = true;
this.$.submit.disabled = true;
this.fire('password-submitted', {password: this.$.password.value});
this.fire('password-submitted', {password: password.value});
},
activeChanged: function() {
if (this.active) {
this.$.dialog.open();
this.$.password.focus();
} else {
this.$.dialog.close();
}
}
});

Просмотреть файл

@ -1,92 +0,0 @@
/* Copyright 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
:host ::selection {
background: rgba(255, 255, 255, 0.3);
}
/* We introduce a wrapper aligner element to help with laying out the main
* toolbar content without changing the bottom-aligned progress bar. */
#aligner {
@apply(--layout-horizontal);
@apply(--layout-center);
padding: 0 16px;
width: 100%;
}
#title {
@apply(--layout-flex-5);
font-size: 77.8%;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#pageselector-container {
@apply(--layout-flex-1);
text-align: center;
/* The container resizes according to the width of the toolbar. On small
* screens with large numbers of pages, overflow page numbers without
* wrapping. */
white-space: nowrap;
}
#buttons {
@apply(--layout-flex-5);
-webkit-user-select: none;
text-align: end;
}
paper-icon-button {
-webkit-margin-end: 12px;
}
viewer-toolbar-dropdown {
-webkit-margin-end: 4px;
}
paper-progress {
--paper-progress-active-color: var(--google-blue-300);
--paper-progress-container-color: transparent;
--paper-progress-height: 3px;
transition: opacity 150ms;
width: 100%;
}
paper-toolbar {
--paper-toolbar-background: rgb(50, 54, 57);
--paper-toolbar-height: 48px;
@apply(--shadow-elevation-2dp);
color: rgb(241, 241, 241);
font-size: 1.5em;
}
.invisible {
visibility: hidden;
}
@media(max-width: 675px) {
#bookmarks,
#rotate-left {
display: none;
}
#pageselector-container {
flex: 2;
}
}
@media(max-width: 450px) {
#rotate-right {
display: none;
}
}
@media(max-width: 400px) {
#buttons,
#pageselector-container {
display: none;
}
}

Просмотреть файл

@ -5,7 +5,6 @@
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animation-runner-behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-toolbar/paper-toolbar.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="../icons.html">
<link rel="import" href="../viewer-bookmarks-content/viewer-bookmarks-content.html">
@ -13,12 +12,110 @@
<link rel="import" href="../viewer-toolbar-dropdown/viewer-toolbar-dropdown.html">
<dom-module id="viewer-pdf-toolbar">
<link rel="import" type="css" href="../shared-icon-style.css">
<link rel="import" type="css" href="viewer-pdf-toolbar.css">
<template>
<style>
:host ::selection {
background: rgba(255, 255, 255, 0.3);
}
<paper-toolbar>
<div id="aligner" class="middle">
/* We introduce a wrapper aligner element to help with laying out the main
* toolbar content without changing the bottom-aligned progress bar. */
#aligner {
align-items: center;
display: flex;
padding: 0 8px;
width: 100%;
}
#title {
@apply(--layout-flex-5);
font-size: 0.87rem;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#pageselector-container {
@apply(--layout-flex-1);
text-align: center;
/* The container resizes according to the width of the toolbar. On small
* screens with large numbers of pages, overflow page numbers without
* wrapping. */
white-space: nowrap;
}
#buttons {
@apply(--layout-flex-5);
text-align: end;
user-select: none;
}
paper-icon-button {
-webkit-margin-end: 12px;
}
viewer-toolbar-dropdown {
-webkit-margin-end: 4px;
}
paper-progress {
--paper-progress-active-color: var(--google-blue-300);
--paper-progress-container-color: transparent;
--paper-progress-height: 3px;
transition: opacity 150ms;
width: 100%;
}
#toolbar {
@apply(--shadow-elevation-2dp);
background-color: rgb(50, 54, 57);
color: rgb(241, 241, 241);
display: flex;
height: 48px;
padding: 0 16px;
}
#progress-container {
bottom: 0;
left: 0;
margin: 0;
position: absolute;
right: 0;
top: auto;
width: auto;
}
.invisible {
visibility: hidden;
}
@media(max-width: 675px) {
#bookmarks,
#rotate-left {
display: none;
}
#pageselector-container {
flex: 2;
}
}
@media(max-width: 450px) {
#rotate-right {
display: none;
}
}
@media(max-width: 400px) {
#buttons,
#pageselector-container {
display: none;
}
}
</style>
<div id="toolbar">
<div id="aligner">
<span id="title" title="{{docTitle}}">
<span>{{docTitle}}</span>
</span>
@ -59,10 +156,10 @@
</viewer-toolbar-dropdown>
</div>
</div>
<div class="bottom fit">
<div id="progress-container">
<paper-progress id="progress" value="{{loadProgress}}"></paper-progress>
</div>
</paper-toolbar>
</div>
</template>
<script src="viewer-pdf-toolbar.js"></script>
</dom-module>
<script src="viewer-pdf-toolbar.js"></script>

Просмотреть файл

@ -2,145 +2,126 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
(function() {
Polymer({
is: 'viewer-pdf-toolbar',
Polymer({
is: 'viewer-pdf-toolbar',
behaviors: [
Polymer.NeonAnimationRunnerBehavior
],
behaviors: [Polymer.NeonAnimationRunnerBehavior],
properties: {
/**
* The current loading progress of the PDF document (0 - 100).
*/
loadProgress: {
type: Number,
observer: 'loadProgressChanged'
},
properties: {
/**
* The current loading progress of the PDF document (0 - 100).
*/
loadProgress: {type: Number, observer: 'loadProgressChanged'},
/**
* The title of the PDF document.
*/
docTitle: String,
/**
* The title of the PDF document.
*/
docTitle: String,
/**
* The number of the page being viewed (1-based).
*/
pageNo: Number,
/**
* The number of the page being viewed (1-based).
*/
pageNo: Number,
/**
* Tree of PDF bookmarks (or null if the document has no bookmarks).
*/
bookmarks: {
type: Object,
value: null
},
/**
* Tree of PDF bookmarks (or null if the document has no bookmarks).
*/
bookmarks: {type: Object, value: null},
/**
* The number of pages in the PDF document.
*/
docLength: Number,
/**
* The number of pages in the PDF document.
*/
docLength: Number,
/**
* Whether the toolbar is opened and visible.
*/
opened: {
type: Boolean,
value: true
},
/**
* Whether the toolbar is opened and visible.
*/
opened: {type: Boolean, value: true},
strings: Object,
strings: Object,
animationConfig: {
value: function() {
return {
'entry': {
name: 'transform-animation',
node: this,
transformFrom: 'translateY(-100%)',
transformTo: 'translateY(0%)',
timing: {
easing: 'cubic-bezier(0, 0, 0.2, 1)',
duration: 250
}
},
'exit': {
name: 'slide-up-animation',
node: this,
timing: {
easing: 'cubic-bezier(0.4, 0, 1, 1)',
duration: 250
}
}
};
}
animationConfig: {
value: function() {
return {
'entry': {
name: 'transform-animation',
node: this,
transformFrom: 'translateY(-100%)',
transformTo: 'translateY(0%)',
timing: {easing: 'cubic-bezier(0, 0, 0.2, 1)', duration: 250}
},
'exit': {
name: 'slide-up-animation',
node: this,
timing: {easing: 'cubic-bezier(0.4, 0, 1, 1)', duration: 250}
}
};
}
},
listeners: {
'neon-animation-finish': '_onAnimationFinished'
},
_onAnimationFinished: function() {
this.style.transform = this.opened ? 'none' : 'translateY(-100%)';
},
loadProgressChanged: function() {
if (this.loadProgress >= 100) {
this.$.pageselector.classList.toggle('invisible', false);
this.$.buttons.classList.toggle('invisible', false);
this.$.progress.style.opacity = 0;
}
},
hide: function() {
if (this.opened)
this.toggleVisibility();
},
show: function() {
if (!this.opened) {
this.toggleVisibility();
}
},
toggleVisibility: function() {
this.opened = !this.opened;
this.cancelAnimation();
this.playAnimation(this.opened ? 'entry' : 'exit');
},
selectPageNumber: function() {
this.$.pageselector.select();
},
shouldKeepOpen: function() {
return this.$.bookmarks.dropdownOpen || this.loadProgress < 100 ||
this.$.pageselector.isActive();
},
hideDropdowns: function() {
if (this.$.bookmarks.dropdownOpen) {
this.$.bookmarks.toggleDropdown();
return true;
}
return false;
},
setDropdownLowerBound: function(lowerBound) {
this.$.bookmarks.lowerBound = lowerBound;
},
rotateRight: function() {
this.fire('rotate-right');
},
download: function() {
this.fire('save');
},
print: function() {
this.fire('print');
}
});
},
listeners: {'neon-animation-finish': '_onAnimationFinished'},
_onAnimationFinished: function() {
this.style.transform = this.opened ? 'none' : 'translateY(-100%)';
},
loadProgressChanged: function() {
if (this.loadProgress >= 100) {
this.$.pageselector.classList.toggle('invisible', false);
this.$.buttons.classList.toggle('invisible', false);
this.$.progress.style.opacity = 0;
}
},
hide: function() {
if (this.opened)
this.toggleVisibility();
},
show: function() {
if (!this.opened) {
this.toggleVisibility();
}
},
toggleVisibility: function() {
this.opened = !this.opened;
this.cancelAnimation();
this.playAnimation(this.opened ? 'entry' : 'exit');
},
selectPageNumber: function() {
this.$.pageselector.select();
},
shouldKeepOpen: function() {
return this.$.bookmarks.dropdownOpen || this.loadProgress < 100 ||
this.$.pageselector.isActive();
},
hideDropdowns: function() {
if (this.$.bookmarks.dropdownOpen) {
this.$.bookmarks.toggleDropdown();
return true;
}
return false;
},
setDropdownLowerBound: function(lowerBound) {
this.$.bookmarks.lowerBound = lowerBound;
},
rotateRight: function() {
this.fire('rotate-right');
},
download: function() {
this.fire('save');
},
print: function() {
this.fire('print');
}
});
})();

Просмотреть файл

@ -1,59 +0,0 @@
/* Copyright 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
:host {
text-align: start;
}
#container {
position: absolute;
/* Controls the position of the dropdown relative to the right of the screen.
* Default is aligned with the right of the toolbar buttons.
* TODO(tsergeant): Change the layout of the dropdown so this is not required.
*/
right: var(--viewer-toolbar-dropdown-right-distance, 36px);
}
:host-context([dir=rtl]) #container {
left: var(--viewer-toolbar-dropdown-right-distance, 36px);
right: auto;
}
paper-material {
background-color: rgb(256, 256, 256);
border-radius: 4px;
overflow-y: hidden;
padding-bottom: 2px;
width: 260px;
}
#scroll-container {
max-height: 300px;
overflow-y: auto;
padding: 6px 0 4px 0;
}
#icon {
cursor: pointer;
display: inline-block;
}
:host([dropdown-open]) #icon {
background-color: rgb(25, 27, 29);
border-radius: 4px;
}
#arrow {
-webkit-margin-start: -12px;
-webkit-padding-end: 4px;
}
h1 {
border-bottom: 1px solid rgb(219, 219, 219);
color: rgb(33, 33, 33);
font-size: 77.8%;
font-weight: 500;
margin: 0;
padding: 14px 28px;
}

Просмотреть файл

@ -1,13 +1,69 @@
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/web-animations.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-material/paper-material.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
<dom-module id="viewer-toolbar-dropdown">
<link rel="import" type="css" href="../shared-icon-style.css">
<link rel="import" type="css" href="viewer-toolbar-dropdown.css">
<template>
<style>
:host {
text-align: start;
}
#container {
position: absolute;
/* Controls the position of the dropdown relative to the right of the
* screen. Default is aligned with the right of the toolbar buttons.
* TODO(tsergeant): Change the layout of the dropdown so this is not
* required.
*/
right: var(--viewer-toolbar-dropdown-right-distance, 28px);
}
:host-context([dir=rtl]) #container {
left: var(--viewer-toolbar-dropdown-right-distance, 28px);
right: auto;
}
#dropdown {
@apply(--shadow-elevation-2dp);
background-color: rgb(256, 256, 256);
border-radius: 4px;
color: var(--primary-text-color);
overflow-y: hidden;
padding-bottom: 2px;
width: 260px;
}
#scroll-container {
max-height: 300px;
overflow-y: auto;
padding: 6px 0 4px 0;
}
#icon {
cursor: pointer;
display: inline-block;
}
:host([dropdown-open]) #icon {
background-color: rgb(25, 27, 29);
border-radius: 4px;
}
#arrow {
-webkit-margin-start: -12px;
-webkit-padding-end: 4px;
}
h1 {
border-bottom: 1px solid rgb(219, 219, 219);
font-size: 0.87rem;
font-weight: 500;
margin: 0;
padding: 14px 28px;
}
</style>
<div on-click="toggleDropdown" id="icon">
<paper-icon-button id="main-icon" icon="[[dropdownIcon]]"
aria-label$="{{header}}" title$="{{header}}">
@ -16,14 +72,13 @@
</div>
<div id="container">
<paper-material id="dropdown" style="display: none">
<div id="dropdown" style="display: none">
<h1>{{header}}</h1>
<div id="scroll-container">
<content></content>
<slot></slot>
</div>
</paper-material>
</div>
</div>
</template>
<script src="viewer-toolbar-dropdown.js"></script>
</dom-module>
<script src="viewer-toolbar-dropdown.js"></script>

Просмотреть файл

@ -3,136 +3,132 @@
// found in the LICENSE file.
(function() {
/**
* Size of additional padding in the inner scrollable section of the dropdown.
*/
var DROPDOWN_INNER_PADDING = 12;
/**
* Size of additional padding in the inner scrollable section of the dropdown.
*/
var DROPDOWN_INNER_PADDING = 12;
/** Size of vertical padding on the outer #dropdown element. */
var DROPDOWN_OUTER_PADDING = 2;
/** Size of vertical padding on the outer #dropdown element. */
var DROPDOWN_OUTER_PADDING = 2;
/** Minimum height of toolbar dropdowns (px). */
var MIN_DROPDOWN_HEIGHT = 200;
/** Minimum height of toolbar dropdowns (px). */
var MIN_DROPDOWN_HEIGHT = 200;
Polymer({
is: 'viewer-toolbar-dropdown',
Polymer({
is: 'viewer-toolbar-dropdown',
properties: {
/** String to be displayed at the top of the dropdown. */
header: String,
properties: {
/** String to be displayed at the top of the dropdown. */
header: String,
/** Icon to display when the dropdown is closed. */
closedIcon: String,
/** Icon to display when the dropdown is closed. */
closedIcon: String,
/** Icon to display when the dropdown is open. */
openIcon: String,
/** Icon to display when the dropdown is open. */
openIcon: String,
/** True if the dropdown is currently open. */
dropdownOpen: {
type: Boolean,
reflectToAttribute: true,
value: false
},
/** True if the dropdown is currently open. */
dropdownOpen: {type: Boolean, reflectToAttribute: true, value: false},
/** Toolbar icon currently being displayed. */
dropdownIcon: {
type: String,
computed: 'computeIcon_(dropdownOpen, closedIcon, openIcon)'
},
/** Lowest vertical point that the dropdown should occupy (px). */
lowerBound: {
type: Number,
observer: 'lowerBoundChanged_'
},
/**
* True if the max-height CSS property for the dropdown scroll container
* is valid. If false, the height will be updated the next time the
* dropdown is visible.
*/
maxHeightValid_: false,
/** Current animation being played, or null if there is none. */
animation_: Object
/** Toolbar icon currently being displayed. */
dropdownIcon: {
type: String,
computed: 'computeIcon_(dropdownOpen, closedIcon, openIcon)'
},
computeIcon_: function(dropdownOpen, closedIcon, openIcon) {
return dropdownOpen ? openIcon : closedIcon;
},
lowerBoundChanged_: function() {
this.maxHeightValid_ = false;
if (this.dropdownOpen)
this.updateMaxHeight();
},
toggleDropdown: function() {
this.dropdownOpen = !this.dropdownOpen;
if (this.dropdownOpen) {
this.$.dropdown.style.display = 'block';
if (!this.maxHeightValid_)
this.updateMaxHeight();
}
this.cancelAnimation_();
this.playAnimation_(this.dropdownOpen);
},
updateMaxHeight: function() {
var scrollContainer = this.$['scroll-container'];
var height = this.lowerBound -
scrollContainer.getBoundingClientRect().top -
DROPDOWN_INNER_PADDING;
height = Math.max(height, MIN_DROPDOWN_HEIGHT);
scrollContainer.style.maxHeight = height + 'px';
this.maxHeightValid_ = true;
},
cancelAnimation_: function() {
if (this._animation)
this._animation.cancel();
},
/** Lowest vertical point that the dropdown should occupy (px). */
lowerBound: {type: Number, observer: 'lowerBoundChanged_'},
/**
* Start an animation on the dropdown.
* @param {boolean} isEntry True to play entry animation, false to play
* exit.
* @private
* True if the max-height CSS property for the dropdown scroll container
* is valid. If false, the height will be updated the next time the
* dropdown is visible.
*/
playAnimation_: function(isEntry) {
this.animation_ = isEntry ? this.animateEntry_() : this.animateExit_();
this.animation_.onfinish = function() {
this.animation_ = null;
if (!this.dropdownOpen)
this.$.dropdown.style.display = 'none';
}.bind(this);
},
maxHeightValid_: false,
animateEntry_: function() {
var maxHeight = this.$.dropdown.getBoundingClientRect().height -
DROPDOWN_OUTER_PADDING;
/** Current animation being played, or null if there is none. */
animation_: Object
},
if (maxHeight < 0)
maxHeight = 0;
computeIcon_: function(dropdownOpen, closedIcon, openIcon) {
return dropdownOpen ? openIcon : closedIcon;
},
var fade = new KeyframeEffect(this.$.dropdown, [
{opacity: 0},
{opacity: 1}
], {duration: 150, easing: 'cubic-bezier(0, 0, 0.2, 1)'});
var slide = new KeyframeEffect(this.$.dropdown, [
{height: '20px', transform: 'translateY(-10px)'},
{height: maxHeight + 'px', transform: 'translateY(0)'}
], {duration: 250, easing: 'cubic-bezier(0, 0, 0.2, 1)'});
lowerBoundChanged_: function() {
this.maxHeightValid_ = false;
if (this.dropdownOpen)
this.updateMaxHeight();
},
return document.timeline.play(new GroupEffect([fade, slide]));
},
animateExit_: function() {
return this.$.dropdown.animate([
{transform: 'translateY(0)', opacity: 1},
{transform: 'translateY(-5px)', opacity: 0}
], {duration: 100, easing: 'cubic-bezier(0.4, 0, 1, 1)'});
toggleDropdown: function() {
this.dropdownOpen = !this.dropdownOpen;
if (this.dropdownOpen) {
this.$.dropdown.style.display = 'block';
if (!this.maxHeightValid_)
this.updateMaxHeight();
}
});
this.cancelAnimation_();
this.playAnimation_(this.dropdownOpen);
},
updateMaxHeight: function() {
var scrollContainer = this.$['scroll-container'];
var height = this.lowerBound - scrollContainer.getBoundingClientRect().top -
DROPDOWN_INNER_PADDING;
height = Math.max(height, MIN_DROPDOWN_HEIGHT);
scrollContainer.style.maxHeight = height + 'px';
this.maxHeightValid_ = true;
},
cancelAnimation_: function() {
if (this._animation)
this._animation.cancel();
},
/**
* Start an animation on the dropdown.
* @param {boolean} isEntry True to play entry animation, false to play
* exit.
* @private
*/
playAnimation_: function(isEntry) {
this.animation_ = isEntry ? this.animateEntry_() : this.animateExit_();
this.animation_.onfinish = () => {
this.animation_ = null;
if (!this.dropdownOpen)
this.$.dropdown.style.display = 'none';
};
},
animateEntry_: function() {
var maxHeight =
this.$.dropdown.getBoundingClientRect().height - DROPDOWN_OUTER_PADDING;
if (maxHeight < 0)
maxHeight = 0;
var fade = new KeyframeEffect(
this.$.dropdown, [{opacity: 0}, {opacity: 1}],
{duration: 150, easing: 'cubic-bezier(0, 0, 0.2, 1)'});
var slide = new KeyframeEffect(
this.$.dropdown,
[
{height: '20px', transform: 'translateY(-10px)'},
{height: maxHeight + 'px', transform: 'translateY(0)'}
],
{duration: 250, easing: 'cubic-bezier(0, 0, 0.2, 1)'});
return document.timeline.play(new GroupEffect([fade, slide]));
},
animateExit_: function() {
return this.$.dropdown.animate(
[
{transform: 'translateY(0)', opacity: 1},
{transform: 'translateY(-5px)', opacity: 0}
],
{duration: 100, easing: 'cubic-bezier(0.4, 0, 1, 1)'});
}
});
})();

Просмотреть файл

@ -1,32 +0,0 @@
/* Copyright 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
#wrapper {
transition: transform 250ms;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
}
:host([closed]) #wrapper {
/* 132px roughly flips the location of the button across the right edge of the
* page. */
transform: translateX(132px);
transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
}
:host-context([dir=rtl]):host([closed]) #wrapper {
transform: translateX(-132px);
}
paper-fab {
--paper-fab-keyboard-focus-background: var(--viewer-icon-ink-color);
--paper-fab-mini: {
height: 36px;
padding: 8px;
width: 36px;
};
@apply(--shadow-elevation-4dp);
background-color: rgb(242, 242, 242);
color: rgb(96, 96, 96);
overflow: visible;
}

Просмотреть файл

@ -2,14 +2,42 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-fab/paper-fab.html">
<dom-module id="viewer-zoom-button">
<link rel="import" type="css" href="../shared-icon-style.css">
<link rel="import" type="css" href="viewer-zoom-button.css">
<template>
<style>
#wrapper {
transition: transform 250ms;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
}
:host([closed]) #wrapper {
/* 132px roughly flips the location of the button across the right edge
* of the page. */
transform: translateX(132px);
transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
}
:host-context([dir=rtl]):host([closed]) #wrapper {
transform: translateX(-132px);
}
paper-fab {
@apply(--shadow-elevation-4dp);
--paper-fab-keyboard-focus-background: var(--viewer-icon-ink-color);
--paper-fab-mini: {
height: 36px;
padding: 8px;
width: 36px;
};
background-color: rgb(242, 242, 242);
color: var(--paper-grey-700);
overflow: visible;
}
</style>
<div id="wrapper">
<paper-fab id="button" mini icon="[[visibleIcon_]]" on-click="fireClick"
aria-label$="[[visibleTooltip_]]" title="[[visibleTooltip_]]">
</paper-fab>
</div>
</template>
<script src="viewer-zoom-button.js"></script>
</dom-module>
<script src="viewer-zoom-button.js"></script>

Просмотреть файл

@ -18,41 +18,25 @@ Polymer({
* perform the conversion manually.
* @private
*/
icons_: {
type: Array,
value: [''],
computed: 'computeIconsArray_(icons)'
},
icons_: {type: Array, value: [''], computed: 'computeIconsArray_(icons)'},
tooltips: Array,
closed: {
type: Boolean,
reflectToAttribute: true,
value: false
},
closed: {type: Boolean, reflectToAttribute: true, value: false},
delay: {
type: Number,
observer: 'delayChanged_'
},
delay: {type: Number, observer: 'delayChanged_'},
/**
* Index of the icon currently being displayed.
*/
activeIndex: {
type: Number,
value: 0
},
activeIndex: {type: Number, value: 0},
/**
* Icon currently being displayed on the FAB.
* @private
*/
visibleIcon_: {
type: String,
computed: 'computeVisibleIcon_(icons_, activeIndex)'
},
visibleIcon_:
{type: String, computed: 'computeVisibleIcon_(icons_, activeIndex)'},
visibleTooltip_: {
type: String,

Просмотреть файл

@ -1,41 +0,0 @@
/* Copyright 2015 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
:host {
-webkit-user-select: none;
bottom: 0;
padding: 48px 0;
position: fixed;
right: 0;
z-index: 3;
}
:host-context([dir=rtl]) {
left: 0;
right: auto;
}
#zoom-buttons {
position: relative;
right: 48px;
}
:host-context([dir=rtl]) #zoom-buttons {
left: 48px;
right: auto;
}
viewer-zoom-button {
display: block;
}
/* A small gap between the zoom in/zoom out buttons. */
#zoom-out-button {
margin-top: 10px;
}
/* A larger gap between the fit button and bottom two buttons. */
#zoom-in-button {
margin-top: 24px;
}

Просмотреть файл

@ -4,9 +4,46 @@
<link rel="import" href="viewer-zoom-button.html">
<dom-module id="viewer-zoom-toolbar">
<link rel="import" type="css" href="viewer-zoom-toolbar.css">
<template>
<style>
:host {
bottom: 0;
padding: 48px 0;
position: fixed;
right: 0;
user-select: none;
z-index: 3;
}
:host-context([dir=rtl]) {
left: 0;
right: auto;
}
#zoom-buttons {
position: relative;
right: 48px;
}
:host-context([dir=rtl]) #zoom-buttons {
left: 48px;
right: auto;
}
viewer-zoom-button {
display: block;
}
/* A small gap between the zoom in/zoom out buttons. */
#zoom-out-button {
margin-top: 10px;
}
/* A larger gap between the fit button and bottom two buttons. */
#zoom-in-button {
margin-top: 24px;
}
</style>
<div id="zoom-buttons">
<viewer-zoom-button id="fit-button" on-fabclick="fitToggle" delay="100"
icons="pdf:fullscreen-exit cr:fullscreen">
@ -17,5 +54,5 @@
on-fabclick="zoomOut" delay="0"></viewer-zoom-button>
</div>
</template>
<script src="viewer-zoom-toolbar.js"></script>
</dom-module>
<script src="viewer-zoom-toolbar.js"></script>

Просмотреть файл

@ -4,106 +4,121 @@
(function() {
var FIT_TO_PAGE = 0;
var FIT_TO_WIDTH = 1;
var FIT_TO_PAGE_BUTTON_STATE = 0;
var FIT_TO_WIDTH_BUTTON_STATE = 1;
Polymer({
is: 'viewer-zoom-toolbar',
Polymer({
is: 'viewer-zoom-toolbar',
properties: {
strings: {
type: Object,
observer: 'updateTooltips_'
},
properties: {
strings: {type: Object, observer: 'updateTooltips_'},
visible_: {
type: Boolean,
value: true
}
},
visible_: {type: Boolean, value: true}
},
isVisible: function() {
return this.visible_;
},
isVisible: function() {
return this.visible_;
},
/**
* @private
* Change button tooltips to match any changes to localized strings.
*/
updateTooltips_: function() {
this.$['fit-button'].tooltips = [
this.strings.tooltipFitToPage,
this.strings.tooltipFitToWidth
];
this.$['zoom-in-button'].tooltips = [this.strings.tooltipZoomIn];
this.$['zoom-out-button'].tooltips = [this.strings.tooltipZoomOut];
},
/**
* @private
* Change button tooltips to match any changes to localized strings.
*/
updateTooltips_: function() {
this.$['fit-button'].tooltips =
[this.strings.tooltipFitToPage, this.strings.tooltipFitToWidth];
this.$['zoom-in-button'].tooltips = [this.strings.tooltipZoomIn];
this.$['zoom-out-button'].tooltips = [this.strings.tooltipZoomOut];
},
fitToPage: function() {
this.fire('fit-to-page');
this.$['fit-button'].activeIndex = FIT_TO_WIDTH;
},
fitToWidth: function() {
this.fire('fit-to-width');
this.$['fit-button'].activeIndex = FIT_TO_PAGE;
},
fitToPage: function() {
this.fireFitToChangedEvent_(FittingType.FIT_TO_PAGE);
this.$['fit-button'].activeIndex = FIT_TO_WIDTH_BUTTON_STATE;
},
/**
* Handle clicks of the fit-button.
*/
fitToggle: function() {
if (this.$['fit-button'].activeIndex == FIT_TO_WIDTH)
this.fire('fit-to-width');
else
this.fire('fit-to-page');
},
fitToWidth: function() {
this.fireFitToChangedEvent_(FittingType.FIT_TO_WIDTH);
this.$['fit-button'].activeIndex = FIT_TO_PAGE_BUTTON_STATE;
},
/**
* Handle the keyboard shortcut equivalent of fit-button clicks.
*/
fitToggleFromHotKey: function() {
this.fitToggle();
/**
* Handle clicks of the fit-button.
*/
fitToggle: function() {
this.fireFitToChangedEvent_(
this.$['fit-button'].activeIndex == FIT_TO_WIDTH_BUTTON_STATE ?
FittingType.FIT_TO_WIDTH :
FittingType.FIT_TO_PAGE);
},
// Toggle the button state since there was no mouse click.
var button = this.$['fit-button'];
if (button.activeIndex == FIT_TO_WIDTH)
button.activeIndex = FIT_TO_PAGE;
else
button.activeIndex = FIT_TO_WIDTH;
},
/**
* Handle the keyboard shortcut equivalent of fit-button clicks.
*/
fitToggleFromHotKey: function() {
this.fitToggle();
/**
* Handle clicks of the zoom-in-button.
*/
zoomIn: function() {
this.fire('zoom-in');
},
// Toggle the button state since there was no mouse click.
var button = this.$['fit-button'];
button.activeIndex =
(button.activeIndex == FIT_TO_WIDTH_BUTTON_STATE ?
FIT_TO_PAGE_BUTTON_STATE :
FIT_TO_WIDTH_BUTTON_STATE);
},
/**
* Handle clicks of the zoom-out-button.
*/
zoomOut: function() {
this.fire('zoom-out');
},
/**
* Handle forcing zoom via scripting to a fitting type.
* @param {FittingType} fittingType Page fitting type to force.
*/
forceFit: function(fittingType) {
this.fireFitToChangedEvent_(fittingType);
show: function() {
if (!this.visible_) {
this.visible_ = true;
this.$['fit-button'].show();
this.$['zoom-in-button'].show();
this.$['zoom-out-button'].show();
}
},
// Set the button state since there was no mouse click.
var nextButtonState =
(fittingType == FittingType.FIT_TO_WIDTH ? FIT_TO_PAGE_BUTTON_STATE :
FIT_TO_WIDTH_BUTTON_STATE);
this.$['fit-button'].activeIndex = nextButtonState;
},
hide: function() {
if (this.visible_) {
this.visible_ = false;
this.$['fit-button'].hide();
this.$['zoom-in-button'].hide();
this.$['zoom-out-button'].hide();
}
},
});
/**
* @private
* Fire a 'fit-to-changed' {CustomEvent} with the given FittingType as detail.
* @param {FittingType} fittingType to include as payload.
*/
fireFitToChangedEvent_: function(fittingType) {
this.fire('fit-to-changed', fittingType);
},
/**
* Handle clicks of the zoom-in-button.
*/
zoomIn: function() {
this.fire('zoom-in');
},
/**
* Handle clicks of the zoom-out-button.
*/
zoomOut: function() {
this.fire('zoom-out');
},
show: function() {
if (!this.visible_) {
this.visible_ = true;
this.$['fit-button'].show();
this.$['zoom-in-button'].show();
this.$['zoom-out-button'].show();
}
},
hide: function() {
if (this.visible_) {
this.visible_ = false;
this.$['fit-button'].hide();
this.$['zoom-in-button'].hide();
this.$['zoom-out-button'].hide();
}
},
});
})();

Просмотреть файл

@ -14,31 +14,54 @@ class GestureDetector {
* @param {!Element} element The element to monitor for touch gestures.
*/
constructor(element) {
/** @private {!Element} */
this.element_ = element;
this.element_.addEventListener(
'touchstart', this.onTouchStart_.bind(this), { passive: false });
'touchstart',
/** @type {function(!Event)} */ (this.onTouchStart_.bind(this)),
{passive: true});
let boundOnTouch =
/** @type {function(!Event)} */ (this.onTouch_.bind(this));
this.element_.addEventListener('touchmove', boundOnTouch, {passive: false});
this.element_.addEventListener('touchend', boundOnTouch, {passive: true});
this.element_.addEventListener(
'touchmove', this.onTouch_.bind(this), { passive: true });
'touchcancel', boundOnTouch, {passive: true});
this.element_.addEventListener(
'touchend', this.onTouch_.bind(this), { passive: true });
this.element_.addEventListener(
'touchcancel', this.onTouch_.bind(this), { passive: true });
'wheel',
/** @type {function(!Event)} */ (this.onWheel_.bind(this)),
{passive: false});
this.pinchStartEvent_ = null;
this.lastTouchTouchesCount_ = 0;
/** @private {?TouchEvent} */
this.lastEvent_ = null;
this.listeners_ = new Map([
['pinchstart', []],
['pinchupdate', []],
['pinchend', []]
]);
/**
* The scale relative to the start of the pinch when handling ctrl-wheels.
* null when there is no ongoing pinch.
* @private {?number}
*/
this.accumulatedWheelScale_ = null;
/**
* A timeout ID from setTimeout used for sending the pinchend event when
* handling ctrl-wheels.
* @private {?number}
*/
this.wheelEndTimeout_ = null;
/** @private {!Map<string, !Array<!Function>>} */
this.listeners_ =
new Map([['pinchstart', []], ['pinchupdate', []], ['pinchend', []]]);
}
/**
* Add a |listener| to be notified of |type| events.
* @param {string} type The event type to be notified for.
* @param {Function} listener The callback.
* @param {!Function} listener The callback.
*/
addEventListener(type, listener) {
if (this.listeners_.has(type)) {
@ -46,6 +69,14 @@ class GestureDetector {
}
}
/**
* Returns true if the last touch start was a two finger touch.
* @return {boolean} True if the last touch start was a two finger touch.
*/
wasTwoFingerTouch() {
return this.lastTouchTouchesCount_ == 2;
}
/**
* Call the relevant listeners with the given |pinchEvent|.
* @private
@ -64,17 +95,13 @@ class GestureDetector {
* @param {!TouchEvent} event Touch event on the element.
*/
onTouchStart_(event) {
// We must preventDefault if there is a two finger touch. By doing so
// native pinch-zoom does not interfere with our way of handling the event.
if (event.touches.length == 2) {
event.preventDefault();
this.pinchStartEvent_ = event;
this.lastEvent_ = event;
this.notify_({
type: 'pinchstart',
center: GestureDetector.center_(event)
});
}
this.lastTouchTouchesCount_ = event.touches.length;
if (!this.wasTwoFingerTouch())
return;
this.pinchStartEvent_ = event;
this.lastEvent_ = event;
this.notify_({type: 'pinchstart', center: GestureDetector.center_(event)});
}
/**
@ -86,12 +113,14 @@ class GestureDetector {
if (!this.pinchStartEvent_)
return;
let lastEvent = /** @type {!TouchEvent} */ (this.lastEvent_);
// Check if the pinch ends with the current event.
if (event.touches.length < 2 ||
this.lastEvent_.touches.length !== event.touches.length) {
let startScaleRatio = GestureDetector.pinchScaleRatio_(
this.lastEvent_, this.pinchStartEvent_);
let center = GestureDetector.center_(this.lastEvent_);
lastEvent.touches.length !== event.touches.length) {
let startScaleRatio =
GestureDetector.pinchScaleRatio_(lastEvent, this.pinchStartEvent_);
let center = GestureDetector.center_(lastEvent);
let endEvent = {
type: 'pinchend',
startScaleRatio: startScaleRatio,
@ -103,9 +132,13 @@ class GestureDetector {
return;
}
let scaleRatio = GestureDetector.pinchScaleRatio_(event, this.lastEvent_);
let startScaleRatio = GestureDetector.pinchScaleRatio_(
event, this.pinchStartEvent_);
// We must preventDefault two finger touchmoves. By doing so native
// pinch-zoom does not interfere with our way of handling the event.
event.preventDefault();
let scaleRatio = GestureDetector.pinchScaleRatio_(event, lastEvent);
let startScaleRatio =
GestureDetector.pinchScaleRatio_(event, this.pinchStartEvent_);
let center = GestureDetector.center_(event);
this.notify_({
type: 'pinchupdate',
@ -118,6 +151,63 @@ class GestureDetector {
this.lastEvent_ = event;
}
/**
* The callback for wheel events on the element.
* @private
* @param {!WheelEvent} event Wheel event on the element.
*/
onWheel_(event) {
// We handle ctrl-wheels to invoke our own pinch zoom. On Mac, synthetic
// ctrl-wheels are created from trackpad pinches. We handle these ourselves
// to prevent the browser's native pinch zoom. We also use our pinch
// zooming mechanism for handling non-synthetic ctrl-wheels. This allows us
// to anchor the zoom around the mouse position instead of the scroll
// position.
if (!event.ctrlKey)
return;
event.preventDefault();
let wheelScale = Math.exp(-event.deltaY / 100);
// Clamp scale changes from the wheel event as they can be
// quite dramatic for non-synthetic ctrl-wheels.
let scale = Math.min(1.25, Math.max(0.75, wheelScale));
let position = {x: event.clientX, y: event.clientY};
if (this.accumulatedWheelScale_ == null) {
this.accumulatedWheelScale_ = 1.0;
this.notify_({type: 'pinchstart', center: position});
}
this.accumulatedWheelScale_ *= scale;
this.notify_({
type: 'pinchupdate',
scaleRatio: scale,
direction: scale > 1.0 ? 'in' : 'out',
startScaleRatio: this.accumulatedWheelScale_,
center: position
});
// We don't get any phase information for the ctrl-wheels, so we don't know
// when the gesture ends. We'll just use a timeout to send the pinch end
// event a short time after the last ctrl-wheel we see.
if (this.wheelEndTimeout_ != null) {
window.clearTimeout(this.wheelEndTimeout_);
this.wheelEndTimeout_ = null;
}
let gestureEndDelayMs = 100;
let endEvent = {
type: 'pinchend',
startScaleRatio: this.accumulatedWheelScale_,
center: position
};
this.wheelEndTimeout_ = window.setTimeout(function(endEvent) {
this.notify_(endEvent);
this.wheelEndTimeout_ = null;
this.accumulatedWheelScale_ = null;
}.bind(this), gestureEndDelayMs, endEvent);
}
/**
* Computes the change in scale between this touch event
* and a previous one.
@ -161,4 +251,4 @@ class GestureDetector {
y: (touch1.clientY + touch2.clientY) / 2
};
}
};
}

Просмотреть файл

@ -9,9 +9,9 @@
<link rel="import" href="elements/viewer-password-screen/viewer-password-screen.html">
<link rel="import" href="elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html">
<link rel="import" href="elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html">
<link rel="import" href="elements/shared-vars.html">
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="stylesheet" href="chrome://resources/css/roboto.css">
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
<link rel="stylesheet" href="index.css">
</head>
<body>
@ -28,6 +28,7 @@
<viewer-error-screen id="error-screen"></viewer-error-screen>
</body>
<script src="pdf_fitting_type.js"></script>
<script src="toolbar_manager.js"></script>
<script src="viewport.js"></script>
<script src="open_pdf_params_parser.js"></script>

Просмотреть файл

@ -7,10 +7,12 @@
/**
* Creates a new NavigatorDelegate for calling browser-specific functions to
* do the actual navigating.
* @param {boolean} isInTab Indicates if the PDF viewer is displayed in a tab.
* @param {number} tabId The tab ID of the PDF viewer or -1 if the viewer is
* not displayed in a tab.
* @constructor
*/
function NavigatorDelegate(isInTab) {
this.isInTab_ = isInTab;
function NavigatorDelegate(tabId) {
this.tabId_ = tabId;
}
/**
@ -21,6 +23,7 @@ function NavigatorDelegate(isInTab) {
* @param {Object} navigatorDelegate The object with callback functions that
* get called when navigation happens in the current tab, a new tab,
* and a new window.
* @constructor
*/
function Navigator(originalUrl, viewport, paramsParser, navigatorDelegate) {
this.originalUrl_ = originalUrl;
@ -38,8 +41,8 @@ NavigatorDelegate.prototype = {
navigateInCurrentTab: function(url) {
// When the PDFviewer is inside a browser tab, prefer the tabs API because
// it can navigate from one file:// URL to another.
if (chrome.tabs && this.isInTab_)
chrome.tabs.update({url: url});
if (chrome.tabs && this.tabId_ != -1)
chrome.tabs.update(this.tabId_, {url: url});
else
window.location.href = url;
},
@ -90,7 +93,6 @@ Navigator.WindowOpenDisposition = {
Navigator.prototype = {
/**
* @private
* Function to navigate to the given URL. This might involve navigating
* within the PDF page or opening a new url (in the same tab or a new tab).
* @param {string} url The URL to navigate to.
@ -172,15 +174,13 @@ Navigator.prototype = {
/**
* @private
* Checks if the URL starts with a scheme and is not just a scheme.
* @param {string} The input URL
* @param {string} url The input URL
* @return {boolean} Whether the url is valid.
*/
isValidUrl_: function(url) {
// Make sure |url| starts with a valid scheme.
if (!url.startsWith('http://') &&
!url.startsWith('https://') &&
!url.startsWith('ftp://') &&
!url.startsWith('file://') &&
if (!url.startsWith('http://') && !url.startsWith('https://') &&
!url.startsWith('ftp://') && !url.startsWith('file://') &&
!url.startsWith('mailto:')) {
return false;
}
@ -191,11 +191,8 @@ Navigator.prototype = {
// Make sure |url| is not only a scheme.
if (url == 'http://' ||
url == 'https://' ||
url == 'ftp://' ||
url == 'file://' ||
url == 'mailto:') {
if (url == 'http://' || url == 'https://' || url == 'ftp://' ||
url == 'file://' || url == 'mailto:') {
return false;
}
@ -205,7 +202,7 @@ Navigator.prototype = {
/**
* @private
* Attempt to figure out what a URL is when there is no scheme.
* @param {string} The input URL
* @param {string} url The input URL
* @return {string} The URL with a scheme or the original URL if it is not
* possible to determine the scheme.
*/
@ -224,8 +221,8 @@ Navigator.prototype = {
var schemeEndIndex = this.originalUrl_.indexOf('://');
var firstSlash = this.originalUrl_.indexOf('/', schemeEndIndex + 3);
// e.g. http://www.foo.com/bar -> http://www.foo.com
var domain = firstSlash != -1 ?
this.originalUrl_.substr(0, firstSlash) : this.originalUrl_;
var domain = firstSlash != -1 ? this.originalUrl_.substr(0, firstSlash) :
this.originalUrl_;
return domain + url;
}
@ -243,7 +240,8 @@ Navigator.prototype = {
if (!isRelative) {
var domainSeparatorIndex = url.indexOf('/');
var domainName = domainSeparatorIndex == -1 ?
url : url.substr(0, domainSeparatorIndex);
url :
url.substr(0, domainSeparatorIndex);
var domainDotCount = (domainName.match(/\./g) || []).length;
if (domainDotCount < 2)
isRelative = true;
@ -251,8 +249,8 @@ Navigator.prototype = {
if (isRelative) {
var slashIndex = this.originalUrl_.lastIndexOf('/');
var path = slashIndex != -1 ?
this.originalUrl_.substr(0, slashIndex) : this.originalUrl_;
var path = slashIndex != -1 ? this.originalUrl_.substr(0, slashIndex) :
this.originalUrl_;
return path + '/' + url;
}

Просмотреть файл

@ -2,49 +2,89 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var OpenPDFParamsParser;
(function() {
'use strict';
/**
* Creates a new OpenPDFParamsParser. This parses the open pdf parameters
* passed in the url to set initial viewport settings for opening the pdf.
* @param {Object} getNamedDestinationsFunction The function called to fetch
* @param {!Function} getNamedDestinationsFunction The function called to fetch
* the page number for a named destination.
* @constructor
*/
function OpenPDFParamsParser(getNamedDestinationsFunction) {
OpenPDFParamsParser = function(getNamedDestinationsFunction) {
this.outstandingRequests_ = [];
this.getNamedDestinationsFunction_ = getNamedDestinationsFunction;
}
};
OpenPDFParamsParser.prototype = {
/**
* @private
* Parse zoom parameter of open PDF parameters. If this
* parameter is passed while opening PDF then PDF should be opened
* at the specified zoom level.
* @param {number} zoom value.
* @param {Object} viewportPosition to store zoom and position value.
* Parse zoom parameter of open PDF parameters. The PDF should be opened at
* the specified zoom level.
* @param {string} paramValue zoom value.
* @return {Object} Map with zoom parameters (zoom and position).
*/
parseZoomParam_: function(paramValue, viewportPosition) {
parseZoomParam_: function(paramValue) {
var paramValueSplit = paramValue.split(',');
if ((paramValueSplit.length != 1) && (paramValueSplit.length != 3))
return;
if (paramValueSplit.length != 1 && paramValueSplit.length != 3)
return {};
// User scale of 100 means zoom value of 100% i.e. zoom factor of 1.0.
var zoomFactor = parseFloat(paramValueSplit[0]) / 100;
if (isNaN(zoomFactor))
return;
return {};
// Handle #zoom=scale.
if (paramValueSplit.length == 1) {
viewportPosition['zoom'] = zoomFactor;
return;
return {'zoom': zoomFactor};
}
// Handle #zoom=scale,left,top.
var position = {x: parseFloat(paramValueSplit[1]),
y: parseFloat(paramValueSplit[2])};
viewportPosition['position'] = position;
viewportPosition['zoom'] = zoomFactor;
var position = {
x: parseFloat(paramValueSplit[1]),
y: parseFloat(paramValueSplit[2])
};
return {'position': position, 'zoom': zoomFactor};
},
/**
* @private
* Parse view parameter of open PDF parameters. The PDF should be opened at
* the specified fitting type mode and position.
* @param {string} paramValue view value.
* @return {Object} Map with view parameters (view and viewPosition).
*/
parseViewParam_: function(paramValue) {
var viewModeComponents = paramValue.toLowerCase().split(',');
if (viewModeComponents.length < 1)
return {};
var params = {};
var viewMode = viewModeComponents[0];
var acceptsPositionParam;
if (viewMode === 'fit') {
params['view'] = FittingType.FIT_TO_PAGE;
acceptsPositionParam = false;
} else if (viewMode === 'fith') {
params['view'] = FittingType.FIT_TO_WIDTH;
acceptsPositionParam = true;
} else if (viewMode === 'fitv') {
params['view'] = FittingType.FIT_TO_HEIGHT;
acceptsPositionParam = true;
}
if (!acceptsPositionParam || viewModeComponents.length < 2)
return params;
var position = parseFloat(viewModeComponents[1]);
if (!isNaN(position))
params['viewPosition'] = position;
return params;
},
/**
@ -107,33 +147,29 @@ OpenPDFParamsParser.prototype = {
* @param {Function} callback function to be called with viewport info.
*/
getViewportFromUrlParams: function(url, callback) {
var viewportPosition = {};
viewportPosition['url'] = url;
var params = {};
params['url'] = url;
var paramsDictionary = this.parseUrlParams_(url);
var urlParams = this.parseUrlParams_(url);
if ('page' in paramsDictionary) {
if ('page' in urlParams) {
// |pageNumber| is 1-based, but goToPage() take a zero-based page number.
var pageNumber = parseInt(paramsDictionary['page']);
var pageNumber = parseInt(urlParams['page'], 10);
if (!isNaN(pageNumber) && pageNumber > 0)
viewportPosition['page'] = pageNumber - 1;
params['page'] = pageNumber - 1;
}
if ('zoom' in paramsDictionary)
this.parseZoomParam_(paramsDictionary['zoom'], viewportPosition);
if ('view' in paramsDictionary)
viewportPosition['view'] = paramsDictionary['view'];
if ('view' in urlParams)
Object.assign(params, this.parseViewParam_(urlParams['view']));
if (viewportPosition.page === undefined &&
'nameddest' in paramsDictionary) {
this.outstandingRequests_.push({
callback: callback,
viewportPosition: viewportPosition
});
this.getNamedDestinationsFunction_(paramsDictionary['nameddest']);
if ('zoom' in urlParams)
Object.assign(params, this.parseZoomParam_(urlParams['zoom']));
if (params.page === undefined && 'nameddest' in urlParams) {
this.outstandingRequests_.push({callback: callback, params: params});
this.getNamedDestinationsFunction_(urlParams['nameddest']);
} else {
callback(viewportPosition);
callback(params);
}
},
@ -146,7 +182,9 @@ OpenPDFParamsParser.prototype = {
onNamedDestinationReceived: function(pageNumber) {
var outstandingRequest = this.outstandingRequests_.shift();
if (pageNumber != -1)
outstandingRequest.viewportPosition.page = pageNumber;
outstandingRequest.callback(outstandingRequest.viewportPosition);
outstandingRequest.params.page = pageNumber;
outstandingRequest.callback(outstandingRequest.params);
},
};
}());

316
pdf.js
Просмотреть файл

@ -4,16 +4,6 @@
'use strict';
/**
* An enum containing a value specifying whether the PDF is currently loading,
* has finished loading or failed to load.
*/
var LoadState = {
LOADING: 'loading',
SUCCESS: 'success',
FAILED: 'failed'
};
/**
* @return {number} Width of a scrollbar in pixels
*/
@ -61,9 +51,9 @@ function shouldIgnoreKeyEvents(activeElement) {
activeElement = activeElement.shadowRoot.activeElement;
}
return (activeElement.isContentEditable ||
activeElement.tagName == 'INPUT' ||
activeElement.tagName == 'TEXTAREA');
return (
activeElement.isContentEditable || activeElement.tagName == 'INPUT' ||
activeElement.tagName == 'TEXTAREA');
}
/**
@ -112,6 +102,8 @@ function PDFViewer(browserApi) {
this.delayedScriptingMessages_ = [];
this.isPrintPreview_ = location.origin === 'chrome://print';
this.isPrintPreviewLoaded_ = false;
this.isUserInitiatedEvent_ = true;
// Parse open pdf parameters.
this.paramsParser_ =
@ -142,14 +134,15 @@ function PDFViewer(browserApi) {
var topToolbarHeight =
(toolbarEnabled) ? PDFViewer.MATERIAL_TOOLBAR_HEIGHT : 0;
topToolbarHeight = (toolbarSpacerEnabled) ? topToolbarHeight : 0
this.viewport_ = new Viewport(window,
this.sizer_,
this.viewportChanged_.bind(this),
this.beforeZoom_.bind(this),
this.afterZoom_.bind(this),
getScrollbarWidth(),
this.browserApi_.getDefaultZoom(),
topToolbarHeight);
var defaultZoom =
this.browserApi_.getZoomBehavior() == BrowserApi.ZoomBehavior.MANAGE ?
this.browserApi_.getDefaultZoom() :
1.0;
this.viewport_ = new Viewport(
window, this.sizer_, this.viewportChanged_.bind(this),
this.beforeZoom_.bind(this), this.afterZoom_.bind(this),
this.setUserInitiated_.bind(this), getScrollbarWidth(), defaultZoom,
topToolbarHeight);
// Create the plugin object dynamically so we can set its src. The plugin
// element is sized to fill the entire window and is set to be fixed
@ -160,18 +153,18 @@ function PDFViewer(browserApi) {
// chrome/renderer/printing/print_web_view_helper.cc actually references it.
this.plugin_.id = 'plugin';
this.plugin_.type = 'application/x-google-chrome-pdf';
this.plugin_.addEventListener('message', this.handlePluginMessage_.bind(this),
false);
this.plugin_.addEventListener(
'message', this.handlePluginMessage_.bind(this), false);
// Handle scripting messages from outside the extension that wish to interact
// with it. We also send a message indicating that extension has loaded and
// is ready to receive messages.
window.addEventListener('message', this.handleScriptingMessage.bind(this),
false);
window.addEventListener(
'message', this.handleScriptingMessage.bind(this), false);
this.plugin_.setAttribute('src', this.originalUrl_);
this.plugin_.setAttribute('stream-url',
this.browserApi_.getStreamInfo().streamUrl);
this.plugin_.setAttribute(
'stream-url', this.browserApi_.getStreamInfo().streamUrl);
var headers = '';
for (var header in this.browserApi_.getStreamInfo().responseHeaders) {
headers += header + ': ' +
@ -184,8 +177,8 @@ function PDFViewer(browserApi) {
this.plugin_.setAttribute('top-toolbar-height', topToolbarHeight);
if (this.browserApi_.getStreamInfo().embedded) {
this.plugin_.setAttribute('top-level-url',
this.browserApi_.getStreamInfo().tabUrl);
this.plugin_.setAttribute(
'top-level-url', this.browserApi_.getStreamInfo().tabUrl);
} else {
this.plugin_.setAttribute('full-frame', '');
}
@ -193,18 +186,16 @@ function PDFViewer(browserApi) {
// Setup the button event listeners.
this.zoomToolbar_ = $('zoom-toolbar');
this.zoomToolbar_.addEventListener('fit-to-width',
this.viewport_.fitToWidth.bind(this.viewport_));
this.zoomToolbar_.addEventListener('fit-to-page',
this.fitToPage_.bind(this));
this.zoomToolbar_.addEventListener('zoom-in',
this.viewport_.zoomIn.bind(this.viewport_));
this.zoomToolbar_.addEventListener('zoom-out',
this.viewport_.zoomOut.bind(this.viewport_));
this.zoomToolbar_.addEventListener(
'fit-to-changed', this.fitToChanged_.bind(this));
this.zoomToolbar_.addEventListener(
'zoom-in', this.viewport_.zoomIn.bind(this.viewport_));
this.zoomToolbar_.addEventListener(
'zoom-out', this.viewport_.zoomOut.bind(this.viewport_));
this.gestureDetector_ = new GestureDetector(this.plugin_);
this.gestureDetector_.addEventListener(
'pinchstart', this.viewport_.pinchZoomStart.bind(this.viewport_));
'pinchstart', this.onPinchStart_.bind(this));
this.sentPinchEvent_ = false;
this.gestureDetector_.addEventListener(
'pinchupdate', this.onPinchUpdate_.bind(this));
@ -216,34 +207,40 @@ function PDFViewer(browserApi) {
this.toolbar_.hidden = false;
this.toolbar_.addEventListener('save', this.save_.bind(this));
this.toolbar_.addEventListener('print', this.print_.bind(this));
this.toolbar_.addEventListener('rotate-right',
this.rotateClockwise_.bind(this));
this.toolbar_.addEventListener(
'rotate-right', this.rotateClockwise_.bind(this));
// Must attach to mouseup on the plugin element, since it eats mousedown
// and click events.
this.plugin_.addEventListener('mouseup',
this.toolbar_.hideDropdowns.bind(this.toolbar_));
this.plugin_.addEventListener(
'mouseup', this.toolbar_.hideDropdowns.bind(this.toolbar_));
this.toolbar_.docTitle = getFilenameFromURL(this.originalUrl_);
}
document.body.addEventListener('change-page', function(e) {
document.body.addEventListener('change-page', e => {
this.viewport_.goToPage(e.detail.page);
}.bind(this));
});
document.body.addEventListener('navigate', function(e) {
var disposition =
e.detail.newtab ? Navigator.WindowOpenDisposition.NEW_BACKGROUND_TAB :
Navigator.WindowOpenDisposition.CURRENT_TAB;
document.body.addEventListener('change-page-and-y', e => {
this.viewport_.goToPageAndY(e.detail.page, e.detail.y);
});
document.body.addEventListener('navigate', e => {
var disposition = e.detail.newtab ?
Navigator.WindowOpenDisposition.NEW_BACKGROUND_TAB :
Navigator.WindowOpenDisposition.CURRENT_TAB;
this.navigator_.navigate(e.detail.uri, disposition);
}.bind(this));
});
this.toolbarManager_ =
new ToolbarManager(window, this.toolbar_, this.zoomToolbar_);
// Set up the ZoomManager.
this.zoomManager_ = new ZoomManager(
this.viewport_, this.browserApi_.setZoom.bind(this.browserApi_),
/// Set up the ZoomManager.
this.zoomManager_ = ZoomManager.create(
this.browserApi_.getZoomBehavior(), this.viewport_,
this.browserApi_.setZoom.bind(this.browserApi_),
this.browserApi_.getInitialZoom());
this.viewport_.zoomManager = this.zoomManager_;
this.browserApi_.addZoomEventListener(
this.zoomManager_.onBrowserZoomChange.bind(this.zoomManager_));
@ -252,6 +249,8 @@ function PDFViewer(browserApi) {
document.addEventListener('mousemove', this.handleMouseEvent_.bind(this));
document.addEventListener('mouseout', this.handleMouseEvent_.bind(this));
document.addEventListener('wheel', this.handleMouseEvent_.bind(this));
document.addEventListener(
'contextmenu', this.handleContextMenuEvent_.bind(this));
var tabId = this.browserApi_.getStreamInfo().tabId;
this.navigator_ = new Navigator(
@ -281,9 +280,9 @@ PDFViewer.prototype = {
this.toolbarManager_.hideToolbarsAfterTimeout(e);
var pageUpHandler = function() {
// Go to the previous page if we are fit-to-page.
if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE
var pageUpHandler = () => {
// Go to the previous page if we are fit-to-page or fit-to-height.
if (this.viewport_.isPagedMode()
|| (e.ctrlKey || e.metaKey)) {
this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
// Since we do the movement of the page.
@ -292,10 +291,10 @@ PDFViewer.prototype = {
position.y -= this.viewport.size.height;
this.viewport.position = position;
}
}.bind(this);
var pageDownHandler = function() {
// Go to the next page if we are fit-to-page.
if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE
};
var pageDownHandler = () => {
// Go to the next page if we are fit-to-page or fit-to-height.
if (this.viewport_.isPagedMode()
|| (e.ctrlKey || e.metaKey)) {
this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
// Since we do the movement of the page.
@ -304,7 +303,7 @@ PDFViewer.prototype = {
position.y += this.viewport.size.height;
this.viewport.position = position;
}
}.bind(this);
};
switch (e.keyCode) {
case 9: // Tab key.
@ -329,7 +328,7 @@ PDFViewer.prototype = {
pageDownHandler();
return;
case 37: // Left arrow key.
if (!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
if (!hasKeyModifiers(e)) {
// Go to the previous page if there are no horizontal scrollbars and
// no form field is focused.
if (!(this.viewport_.documentHasScrollbars().horizontal ||
@ -350,7 +349,7 @@ PDFViewer.prototype = {
}
return;
case 39: // Right arrow key.
if (!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
if (!hasKeyModifiers(e)) {
// Go to the next page if there are no horizontal scrollbars and no
// form field is focused.
if (!(this.viewport_.documentHasScrollbars().horizontal ||
@ -372,9 +371,7 @@ PDFViewer.prototype = {
return;
case 65: // 'a' key.
if (e.ctrlKey || e.metaKey) {
this.plugin_.postMessage({
type: 'selectAll'
});
this.plugin_.postMessage({type: 'selectAll'});
// Since we do selection ourselves.
e.preventDefault();
}
@ -387,12 +384,12 @@ PDFViewer.prototype = {
return;
case 80: // 'p' key
if ((e.ctrlKey || e.metaKey) && e.altKey) {
this.zoomToolbar_.fitToPage();
this.viewport_.fitToPage();
}
return;
case 87: // 'w' key
if ((e.ctrlKey || e.metaKey) && e.altKey) {
this.zoomToolbar_.fitToWidth();
this.viewport_.fitToWidth();
}
return;
case 219: // Left bracket key.
@ -411,10 +408,8 @@ PDFViewer.prototype = {
// Give print preview a chance to handle the key event.
if (!fromScriptingAPI && this.isPrintPreview_) {
this.sendScriptingMessage_({
type: 'sendKeyEvent',
keyEvent: SerializeKeyEvent(e)
});
this.sendScriptingMessage_(
{type: 'sendKeyEvent', keyEvent: SerializeKeyEvent(e)});
} else {
// Show toolbars as a fallback.
if (!(e.shiftKey || e.ctrlKey || e.altKey))
@ -436,14 +431,23 @@ PDFViewer.prototype = {
}
},
handleContextMenuEvent_: function(e) {
// Stop Chrome from popping up the context menu on long press. We need to
// make sure the start event did not have 2 touches because we don't want
// to block two finger tap opening the context menu. We check for
// firesTouchEvents in order to not block the context menu on right click.
if (e.sourceCapabilities.firesTouchEvents &&
!this.gestureDetector_.wasTwoFingerTouch()) {
e.preventDefault();
}
},
/**
* @private
* Rotate the plugin clockwise.
*/
rotateClockwise_: function() {
this.plugin_.postMessage({
type: 'rotateClockwise'
});
this.plugin_.postMessage({type: 'rotateClockwise'});
},
/**
@ -451,18 +455,24 @@ PDFViewer.prototype = {
* Rotate the plugin counter-clockwise.
*/
rotateCounterClockwise_: function() {
this.plugin_.postMessage({
type: 'rotateCounterclockwise'
});
this.plugin_.postMessage({type: 'rotateCounterclockwise'});
},
/**
* @private
* Set zoom to "fit to page".
* Request to change the viewport fitting type.
* @param {CustomEvent} e Event received with the new FittingType as detail.
*/
fitToPage_: function() {
this.viewport_.fitToPage();
this.toolbarManager_.forceHideTopToolbar();
fitToChanged_: function(e) {
if (e.detail == FittingType.FIT_TO_PAGE) {
this.viewport_.fitToPage();
this.toolbarManager_.forceHideTopToolbar();
} else if (e.detail == FittingType.FIT_TO_WIDTH) {
this.viewport_.fitToWidth();
} else if (e.detail == FittingType.FIT_TO_HEIGHT) {
this.viewport_.fitToHeight();
this.toolbarManager_.forceHideTopToolbar();
}
},
/**
@ -470,9 +480,7 @@ PDFViewer.prototype = {
* Notify the plugin to print.
*/
print_: function() {
this.plugin_.postMessage({
type: 'print'
});
this.plugin_.postMessage({type: 'print'});
},
/**
@ -480,9 +488,7 @@ PDFViewer.prototype = {
* Notify the plugin to save.
*/
save_: function() {
this.plugin_.postMessage({
type: 'save'
});
this.plugin_.postMessage({type: 'save'});
},
/**
@ -491,10 +497,8 @@ PDFViewer.prototype = {
* @param {string} name The namedDestination to fetch page number from plugin.
*/
getNamedDestination_: function(name) {
this.plugin_.postMessage({
type: 'getNamedDestination',
namedDestination: name
});
this.plugin_.postMessage(
{type: 'getNamedDestination', namedDestination: name});
},
/**
@ -506,11 +510,9 @@ PDFViewer.prototype = {
if (this.loadState_ == LoadState.LOADING)
return;
window.dispatchEvent(
new CustomEvent('pdf-loaded', { detail: this.loadState_ }))
this.sendScriptingMessage_({
type: 'documentLoaded',
load_state: this.loadState_
});
new CustomEvent('pdf-loaded', { detail: this.loadState_ }))
this.sendScriptingMessage_(
{type: 'documentLoaded', load_state: this.loadState_});
},
/**
@ -518,30 +520,36 @@ PDFViewer.prototype = {
* Handle open pdf parameters. This function updates the viewport as per
* the parameters mentioned in the url while opening pdf. The order is
* important as later actions can override the effects of previous actions.
* @param {Object} viewportPosition The initial position of the viewport to be
* displayed.
* @param {Object} params The open params passed in the URL.
*/
handleURLParams_: function(viewportPosition) {
if (viewportPosition.page != undefined)
this.viewport_.goToPage(viewportPosition.page);
if (viewportPosition.position) {
handleURLParams_: function(params) {
if (params.page != undefined)
this.viewport_.goToPage(params.page);
if (params.position) {
// Make sure we don't cancel effect of page parameter.
this.viewport_.position = {
x: this.viewport_.position.x + viewportPosition.position.x,
y: this.viewport_.position.y + viewportPosition.position.y
x: this.viewport_.position.x + params.position.x,
y: this.viewport_.position.y + params.position.y
};
}
if (viewportPosition.zoom)
this.viewport_.setZoom(viewportPosition.zoom);
if (viewportPosition.view) {
switch (viewportPosition.view.toLowerCase()) {
case 'fitw':
this.zoomToolbar_.fitToWidth();
break;
case 'fitp':
this.zoomToolbar_.fitToPage();
break;
if (params.zoom)
this.viewport_.setZoom(params.zoom);
if (params.view) {
this.isUserInitiatedEvent_ = false;
this.zoomToolbar_.forceFit(params.view);
if (params.viewPosition) {
var zoomedPositionShift = params.viewPosition * this.viewport_.zoom;
var currentViewportPosition = this.viewport_.position;
if (params.view == FittingType.FIT_TO_WIDTH)
currentViewportPosition.y += zoomedPositionShift;
else if (params.view == FittingType.FIT_TO_HEIGHT)
currentViewportPosition.x += zoomedPositionShift;
this.viewport_.position = currentViewportPosition;
}
this.isUserInitiatedEvent_ = true;
}
},
@ -561,7 +569,7 @@ PDFViewer.prototype = {
this.sizer_.style.display = 'none';
if (this.passwordScreen_.active) {
this.passwordScreen_.deny();
this.passwordScreen_.active = false;
this.passwordScreen_.close();
}
this.loadState_ = LoadState.FAILED;
this.sendDocumentLoadedMessage_();
@ -570,8 +578,7 @@ PDFViewer.prototype = {
if (this.lastViewportPosition_)
this.viewport_.position = this.lastViewportPosition_;
this.paramsParser_.getViewportFromUrlParams(
this.originalUrl_,
this.handleURLParams_.bind(this));
this.originalUrl_, this.handleURLParams_.bind(this));
this.loadState_ = LoadState.SUCCESS;
this.sendDocumentLoadedMessage_();
while (this.delayedScriptingMessages_.length > 0)
@ -604,10 +611,8 @@ PDFViewer.prototype = {
* @param {Object} event a password-submitted event.
*/
onPasswordSubmitted_: function(event) {
this.plugin_.postMessage({
type: 'getPasswordComplete',
password: event.detail.password
});
this.plugin_.postMessage(
{type: 'getPasswordComplete', password: event.detail.password});
},
/**
@ -619,11 +624,13 @@ PDFViewer.prototype = {
switch (message.data.type.toString()) {
case 'documentDimensions':
this.documentDimensions_ = message.data;
this.isUserInitiatedEvent_ = false;
this.viewport_.setDocumentDimensions(this.documentDimensions_);
this.isUserInitiatedEvent_ = true;
// If we received the document dimensions, the password was good so we
// can dismiss the password screen.
if (this.passwordScreen_.active)
this.passwordScreen_.accept();
this.passwordScreen_.close();
if (this.pageIndicator_)
this.pageIndicator_.initialFadeIn();
@ -643,7 +650,7 @@ PDFViewer.prototype = {
// If the password screen isn't up, put it up. Otherwise we're
// responding to an incorrect password so deny it.
if (!this.passwordScreen_.active)
this.passwordScreen_.active = true;
this.passwordScreen_.show();
else
this.passwordScreen_.deny();
break;
@ -666,6 +673,10 @@ PDFViewer.prototype = {
this.navigator_.navigate(message.data.url, message.data.disposition);
}
break;
case 'printPreviewLoaded':
this.isPrintPreviewLoaded_ = true;
this.sendDocumentLoadedMessage_();
break;
case 'setScrollPosition':
var position = this.viewport_.position;
if (message.data.x !== undefined)
@ -692,6 +703,13 @@ PDFViewer.prototype = {
case 'setIsSelecting':
this.viewportScroller_.setEnableScrolling(message.data.isSelecting);
break;
case 'setIsEditMode':
// TODO(hnakashima): Replace this with final visual indication from UX.
if (message.data.isEditMode)
this.toolbar_.docTitle = document.title + ' (edit mode)';
else
this.toolbar_.docTitle = document.title;
break;
case 'getNamedDestinationReply':
this.paramsParser_.onNamedDestinationReceived(message.data.pageNumber);
break;
@ -707,9 +725,7 @@ PDFViewer.prototype = {
* reacting to scroll events while zoom is taking place to avoid flickering.
*/
beforeZoom_: function() {
this.plugin_.postMessage({
type: 'stopScrolling'
});
this.plugin_.postMessage({type: 'stopScrolling'});
if (this.viewport_.pinchPhase == Viewport.PinchPhase.PINCH_START) {
var position = this.viewport_.position;
@ -717,6 +733,7 @@ PDFViewer.prototype = {
var pinchPhase = this.viewport_.pinchPhase;
this.plugin_.postMessage({
type: 'viewport',
userInitiated: true,
zoom: zoom,
xOffset: position.x,
yOffset: position.y,
@ -739,6 +756,7 @@ PDFViewer.prototype = {
this.plugin_.postMessage({
type: 'viewport',
userInitiated: this.isUserInitiatedEvent_,
zoom: zoom,
xOffset: position.x,
yOffset: position.y,
@ -751,6 +769,19 @@ PDFViewer.prototype = {
this.zoomManager_.onPdfZoomChange();
},
/**
* @param {boolean} userInitiated The value to set |isUserInitiatedEvent_|
* to.
* @private
* A callback that sets |isUserInitiatedEvent_| to |userInitiated|.
*/
setUserInitiated_: function(userInitiated) {
if (this.isUserInitiatedEvent_ == userInitiated) {
throw 'Trying to set user initiated to current value.';
}
this.isUserInitiatedEvent_ = userInitiated;
},
/**
* @private
* A callback that's called when an update to a pinch zoom is detected.
@ -760,10 +791,10 @@ PDFViewer.prototype = {
// Throttle number of pinch events to one per frame.
if (!this.sentPinchEvent_) {
this.sentPinchEvent_ = true;
window.requestAnimationFrame(function() {
window.requestAnimationFrame(() => {
this.sentPinchEvent_ = false;
this.viewport_.pinchZoom(e);
}.bind(this));
});
}
},
@ -775,9 +806,22 @@ PDFViewer.prototype = {
onPinchEnd_: function(e) {
// Using rAF for pinch end prevents pinch updates scheduled by rAF getting
// sent after the pinch end.
window.requestAnimationFrame(function() {
window.requestAnimationFrame(() => {
this.viewport_.pinchZoomEnd(e);
}.bind(this));
});
},
/**
* @private
* A callback that's called when the start of a pinch zoom is detected.
* @param {!Object} e the pinch event.
*/
onPinchStart_: function(e) {
// We also use rAF for pinch start, so that if there is a pinch end event
// scheduled by rAF, this pinch start will be sent after.
window.requestAnimationFrame(() => {
this.viewport_.pinchZoomStart(e);
});
},
/**
@ -802,8 +846,8 @@ PDFViewer.prototype = {
// than the spec. In RTL layout, the zoom toolbar is on the left side, but
// the scrollbar is still on the right, so this is not necessary.
if (!isRTL()) {
this.zoomToolbar_.style.right = -verticalScrollbarWidth +
(scrollbarWidth / 2) + 'px';
this.zoomToolbar_.style.right =
-verticalScrollbarWidth + (scrollbarWidth / 2) + 'px';
}
// Having a horizontal scrollbar is much rarer so we don't offset the
// toolbar from the bottom any more than what the spec says. This means
@ -892,7 +936,9 @@ PDFViewer.prototype = {
this.loadState_ = LoadState.LOADING;
if (!this.inPrintPreviewMode_) {
this.inPrintPreviewMode_ = true;
this.viewport_.fitToPage();
this.isUserInitiatedEvent_ = false;
this.zoomToolbar_.forceFit(FittingType.FIT_TO_PAGE);
this.isUserInitiatedEvent_ = true;
}
// Stash the scroll location so that it can be restored when the new
@ -915,8 +961,8 @@ PDFViewer.prototype = {
grayscale: message.data.grayscale,
// If the PDF isn't modifiable we send 0 as the page count so that no
// blank placeholder pages get appended to the PDF.
pageCount: (message.data.modifiable ?
message.data.pageNumbers.length : 0)
pageCount:
(message.data.modifiable ? message.data.pageNumbers.length : 0)
});
return true;
case 'sendKeyEvent':

16
pdf_fitting_type.js Normal file
Просмотреть файл

@ -0,0 +1,16 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* Enumeration of page fitting types.
* @enum {string}
*/
var FittingType = {
NONE: 'none',
FIT_TO_PAGE: 'fit-to-page',
FIT_TO_WIDTH: 'fit-to-width',
FIT_TO_HEIGHT: 'fit-to-height'
};

Просмотреть файл

@ -9,7 +9,7 @@
*/
function DeserializeKeyEvent(dict) {
var e = document.createEvent('Event');
e.initEvent('keydown');
e.initEvent('keydown', true, true);
e.keyCode = dict.keyCode;
e.shiftKey = dict.shiftKey;
e.ctrlKey = dict.ctrlKey;
@ -34,41 +34,59 @@ function SerializeKeyEvent(event) {
};
}
/**
* An enum containing a value specifying whether the PDF is currently loading,
* has finished loading or failed to load.
* @enum {string}
*/
var LoadState = {LOADING: 'loading', SUCCESS: 'success', FAILED: 'failed'};
/**
* Create a new PDFScriptingAPI. This provides a scripting interface to
* the PDF viewer so that it can be customized by things like print preview.
* @param {Window} window the window of the page containing the pdf viewer.
* @param {Object} plugin the plugin element containing the pdf viewer.
* @constructor
*/
function PDFScriptingAPI(window, plugin) {
this.loadState_ = LoadState.LOADING;
this.pendingScriptingMessages_ = [];
this.setPlugin(plugin);
window.addEventListener('message', function(event) {
window.addEventListener('message', event => {
if (event.origin != 'chrome://pdf-viewer' &&
event.origin != 'chrome://print') {
console.error('Received message that was not from the extension: ' +
event);
console.error(
'Received message that was not from the extension: ' + event);
return;
}
switch (event.data.type) {
case 'viewport':
/**
* @type {{
* pageX: number,
* pageY: number,
* pageWidth: number,
* viewportWidth: number,
* viewportHeight: number
* }}
*/
var viewportData = event.data;
if (this.viewportChangedCallback_)
this.viewportChangedCallback_(event.data.pageX,
event.data.pageY,
event.data.pageWidth,
event.data.viewportWidth,
event.data.viewportHeight);
this.viewportChangedCallback_(
viewportData.pageX, viewportData.pageY, viewportData.pageWidth,
viewportData.viewportWidth, viewportData.viewportHeight);
break;
case 'documentLoaded':
this.loadState_ = event.data.load_state;
var data = /** @type {{load_state: LoadState}} */ (event.data);
this.loadState_ = data.load_state;
if (this.loadCallback_)
this.loadCallback_(this.loadState_ == LoadState.SUCCESS);
break;
case 'getSelectedTextReply':
var data = /** @type {{selectedText: string}} */ (event.data);
if (this.selectedTextCallback_) {
this.selectedTextCallback_(event.data.selectedText);
this.selectedTextCallback_(data.selectedText);
this.selectedTextCallback_ = null;
}
break;
@ -77,7 +95,7 @@ function PDFScriptingAPI(window, plugin) {
this.keyEventCallback_(DeserializeKeyEvent(event.data.keyEvent));
break;
}
}.bind(this), false);
}, false);
}
PDFScriptingAPI.prototype = {
@ -95,20 +113,18 @@ PDFScriptingAPI.prototype = {
this.pendingScriptingMessages_.push(message);
},
/**
* Sets the plugin element containing the PDF viewer. The element will usually
* be passed into the PDFScriptingAPI constructor but may also be set later.
* @param {Object} plugin the plugin element containing the PDF viewer.
*/
/**
* Sets the plugin element containing the PDF viewer. The element will usually
* be passed into the PDFScriptingAPI constructor but may also be set later.
* @param {Object} plugin the plugin element containing the PDF viewer.
*/
setPlugin: function(plugin) {
this.plugin_ = plugin;
if (this.plugin_) {
// Send a message to ensure the postMessage channel is initialized which
// allows us to receive messages.
this.sendMessage_({
type: 'initialize'
});
this.sendMessage_({type: 'initialize'});
// Flush pending messages.
while (this.pendingScriptingMessages_.length > 0)
this.sendMessage_(this.pendingScriptingMessages_.shift());
@ -166,11 +182,7 @@ PDFScriptingAPI.prototype = {
* @param {number} index the index of the page to load.
*/
loadPreviewPage: function(url, index) {
this.sendMessage_({
type: 'loadPreviewPage',
url: url,
index: index
});
this.sendMessage_({type: 'loadPreviewPage', url: url, index: index});
},
/**
@ -178,9 +190,7 @@ PDFScriptingAPI.prototype = {
* load.
*/
selectAll: function() {
this.sendMessage_({
type: 'selectAll'
});
this.sendMessage_({type: 'selectAll'});
},
/**
@ -194,9 +204,7 @@ PDFScriptingAPI.prototype = {
if (this.selectedTextCallback_)
return false;
this.selectedTextCallback_ = callback;
this.sendMessage_({
type: 'getSelectedText'
});
this.sendMessage_({type: 'getSelectedText'});
return true;
},
@ -204,9 +212,7 @@ PDFScriptingAPI.prototype = {
* Print the document. May only be called after document load.
*/
print: function() {
this.sendMessage_({
type: 'print'
});
this.sendMessage_({type: 'print'});
},
/**
@ -214,10 +220,8 @@ PDFScriptingAPI.prototype = {
* @param {Event} keyEvent the key event to send to the extension.
*/
sendKeyEvent: function(keyEvent) {
this.sendMessage_({
type: 'sendKeyEvent',
keyEvent: SerializeKeyEvent(keyEvent)
});
this.sendMessage_(
{type: 'sendKeyEvent', keyEvent: SerializeKeyEvent(keyEvent)});
},
};
@ -230,8 +234,9 @@ PDFScriptingAPI.prototype = {
* @return {HTMLIFrameElement} the iframe element containing the PDF viewer.
*/
function PDFCreateOutOfProcessPlugin(src) {
var client = new PDFScriptingAPI(window);
var iframe = window.document.createElement('iframe');
var client = new PDFScriptingAPI(window, null);
var iframe = assertInstanceof(
window.document.createElement('iframe'), HTMLIFrameElement);
iframe.setAttribute('src', 'pdf_preview.html?' + src);
// Prevent the frame from being tab-focusable.
iframe.setAttribute('tabindex', '-1');

Просмотреть файл

@ -17,40 +17,33 @@
<include name="IDR_PDF_UI_MANAGER_JS" file="toolbar_manager.js" type="BINDATA" />
<include name="IDR_PDF_VIEWPORT_JS" file="viewport.js" type="BINDATA" />
<include name="IDR_PDF_OPEN_PDF_PARAMS_PARSER_JS" file="open_pdf_params_parser.js" type="BINDATA" />
<include name="IDR_PDF_PDF_FITTING_TYPE_JS" file="pdf_fitting_type.js" type="BINDATA" />
<include name="IDR_PDF_NAVIGATOR_JS" file="navigator.js" type="BINDATA" />
<include name="IDR_PDF_VIEWPORT_SCROLLER_JS" file="viewport_scroller.js" type="BINDATA" />
<include name="IDR_PDF_GESTURE_DETECTOR_JS" file="gesture_detector.js" type="BINDATA" />
<include name="IDR_PDF_ZOOM_MANAGER_JS" file="zoom_manager.js" type="BINDATA" />
<include name="IDR_PDF_BROWSER_API_JS" file="browser_api.js" type="BINDATA" />
<include name="IDR_PDF_SHARED_ICON_STYLE_CSS" file="elements/shared-icon-style.css" type="BINDATA" />
<include name="IDR_PDF_SHARED_VARS_HTML" file="elements/shared-vars.html" type="BINDATA" />
<include name="IDR_PDF_ICONS_HTML" file="elements/icons.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_BOOKMARK_CSS" file="elements/viewer-bookmark/viewer-bookmark.css" type="BINDATA" />
<include name="IDR_PDF_VIEWER_BOOKMARK_HTML" file="elements/viewer-bookmark/viewer-bookmark.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_BOOKMARK_JS" file="elements/viewer-bookmark/viewer-bookmark.js" type="BINDATA" />
<include name="IDR_PDF_VIEWER_BOOKMARKS_CONTENT_HTML" file="elements/viewer-bookmarks-content/viewer-bookmarks-content.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_BOOKMARKS_CONTENT_JS" file="elements/viewer-bookmarks-content/viewer-bookmarks-content.js" type="BINDATA" />
<include name="IDR_PDF_VIEWER_ERROR_SCREEN_CSS" file="elements/viewer-error-screen/viewer-error-screen.css" type="BINDATA" />
<include name="IDR_PDF_VIEWER_ERROR_SCREEN_HTML" file="elements/viewer-error-screen/viewer-error-screen.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_ERROR_SCREEN_JS" file="elements/viewer-error-screen/viewer-error-screen.js" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PAGE_INDICATOR_CSS" file="elements/viewer-page-indicator/viewer-page-indicator.css" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PAGE_INDICATOR_HTML" file="elements/viewer-page-indicator/viewer-page-indicator.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PAGE_INDICATOR_JS" file="elements/viewer-page-indicator/viewer-page-indicator.js" type="BINDATA" flattenhtml="true" />
<include name="IDR_PDF_VIEWER_PAGE_SELECTOR_CSS" file="elements/viewer-page-selector/viewer-page-selector.css" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PAGE_SELECTOR_HTML" file="elements/viewer-page-selector/viewer-page-selector.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PAGE_SELECTOR_JS" file="elements/viewer-page-selector/viewer-page-selector.js" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PASSWORD_SCREEN_HTML" file="elements/viewer-password-screen/viewer-password-screen.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PASSWORD_SCREEN_JS" file="elements/viewer-password-screen/viewer-password-screen.js" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PDF_TOOLBAR_CSS" file="elements/viewer-pdf-toolbar/viewer-pdf-toolbar.css" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PDF_TOOLBAR_HTML" file="elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_PDF_TOOLBAR_JS" file="elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js" type="BINDATA" />
<include name="IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_CSS" file="elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.css" type="BINDATA" />
<include name="IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_HTML" file="elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_JS" file="elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js" type="BINDATA" />
<include name="IDR_PDF_VIEWER_ZOOM_BUTTON_CSS" file="elements/viewer-zoom-toolbar/viewer-zoom-button.css" type="BINDATA" />
<include name="IDR_PDF_VIEWER_ZOOM_BUTTON_HTML" file="elements/viewer-zoom-toolbar/viewer-zoom-button.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_ZOOM_BUTTON_JS" file="elements/viewer-zoom-toolbar/viewer-zoom-button.js" type="BINDATA" />
<include name="IDR_PDF_VIEWER_ZOOM_SELECTOR_CSS" file="elements/viewer-zoom-toolbar/viewer-zoom-toolbar.css" type="BINDATA" />
<include name="IDR_PDF_VIEWER_ZOOM_SELECTOR_HTML" file="elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html" type="BINDATA" />
<include name="IDR_PDF_VIEWER_ZOOM_SELECTOR_JS" file="elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js" type="BINDATA" />
</includes>

Просмотреть файл

@ -136,7 +136,7 @@ ToolbarManager.prototype = {
/**
* Wrapper around Date.now() to make it easily replaceable for testing.
* @return {int}
* @return {number}
* @private
*/
getCurrentTimestamp_: function() {
@ -233,9 +233,9 @@ ToolbarManager.prototype = {
return;
this.toolbar_.hide();
this.sideToolbarAllowedOnly_ = true;
this.sideToolbarAllowedOnlyTimer_ = this.window_.setTimeout(function() {
this.sideToolbarAllowedOnlyTimer_ = this.window_.setTimeout(() => {
this.sideToolbarAllowedOnlyTimer_ = null;
}.bind(this), FORCE_HIDE_TIMEOUT);
}, FORCE_HIDE_TIMEOUT);
},
/**

Просмотреть файл

@ -9,18 +9,10 @@
* @return {number} the height of the intersection of the rects
*/
function getIntersectionHeight(rect1, rect2) {
return Math.max(0,
return Math.max(
0,
Math.min(rect1.y + rect1.height, rect2.y + rect2.height) -
Math.max(rect1.y, rect2.y));
}
/**
* Makes sure that the scale level doesn't get out of the limits.
* @param {number} scale The new scale level.
* @return {number} The scale clamped within the limits.
*/
function clampScale(scale) {
return Math.min(5, Math.max(0.25, scale));
Math.max(rect1.y, rect2.y));
}
/**
@ -30,10 +22,7 @@ function clampScale(scale) {
* @return {!Object} The vector.
*/
function vectorDelta(p1, p2) {
return {
x: p2.x - p1.x,
y: p2.y - p1.y
};
return {x: p2.x - p1.x, y: p2.y - p1.y};
}
function frameToPluginCoordinate(coordinateInFrame) {
@ -53,30 +42,30 @@ function frameToPluginCoordinate(coordinateInFrame) {
* @param {Function} viewportChangedCallback is run when the viewport changes
* @param {Function} beforeZoomCallback is run before a change in zoom
* @param {Function} afterZoomCallback is run after a change in zoom
* @param {Function} setUserInitiatedCallback is run to indicate whether a zoom
* event is user initiated.
* @param {number} scrollbarWidth the width of scrollbars on the page
* @param {number} defaultZoom The default zoom level.
* @param {number} topToolbarHeight The number of pixels that should initially
* be left blank above the document for the toolbar.
*/
function Viewport(window,
sizer,
viewportChangedCallback,
beforeZoomCallback,
afterZoomCallback,
scrollbarWidth,
defaultZoom,
topToolbarHeight) {
function Viewport(
window, sizer, viewportChangedCallback, beforeZoomCallback,
afterZoomCallback, setUserInitiatedCallback, scrollbarWidth, defaultZoom,
topToolbarHeight) {
this.window_ = window;
this.sizer_ = sizer;
this.viewportChangedCallback_ = viewportChangedCallback;
this.beforeZoomCallback_ = beforeZoomCallback;
this.afterZoomCallback_ = afterZoomCallback;
this.setUserInitiatedCallback_ = setUserInitiatedCallback;
this.allowedToChangeZoom_ = false;
this.zoom_ = 1;
this.internalZoom_ = 1;
this.zoomManager_ = new InactiveZoomManager(this, 1);
this.documentDimensions_ = null;
this.pageDimensions_ = [];
this.scrollbarWidth_ = scrollbarWidth;
this.fittingType_ = Viewport.FittingType.NONE;
this.fittingType_ = FittingType.NONE;
this.defaultZoom_ = defaultZoom;
this.topToolbarHeight_ = topToolbarHeight;
this.prevScale_ = 1;
@ -86,19 +75,9 @@ function Viewport(window,
this.firstPinchCenterInFrame_ = null;
window.addEventListener('scroll', this.updateViewport_.bind(this));
window.addEventListener('resize', this.resize_.bind(this));
window.addEventListener('resize', this.resizeWrapper_.bind(this));
}
/**
* Enumeration of page fitting types.
* @enum {string}
*/
Viewport.FittingType = {
NONE: 'none',
FIT_TO_PAGE: 'fit-to-page',
FIT_TO_WIDTH: 'fit-to-width'
};
/**
* Enumeration of pinch states.
* This should match PinchPhase enum in pdf/out_of_process_instance.h
@ -126,8 +105,10 @@ Viewport.SCROLL_INCREMENT = 40;
* components/ui/zoom/page_zoom_constants.h and
* chrome/browser/resources/settings/appearance_page/appearance_page.js
*/
Viewport.ZOOM_FACTORS = [0.25, 1 / 3, 0.5, 2 / 3, 0.75, 0.8, 0.9,
1, 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5];
Viewport.ZOOM_FACTORS = [
0.25, 1 / 3, 0.5, 2 / 3, 0.75, 0.8, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3,
4, 5
];
/**
* The minimum and maximum range to be used to clip zoom factor.
@ -137,10 +118,26 @@ Viewport.ZOOM_FACTOR_RANGE = {
max: Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1]
};
/**
* Clamps the zoom factor (or page scale factor) to be within the limits.
* @param {number} factor The zoom/scale factor.
* @return {number} The factor clamped within the limits.
*/
Viewport.clampZoom = function(factor) {
return Math.max(
Viewport.ZOOM_FACTOR_RANGE.min,
Math.min(factor, Viewport.ZOOM_FACTOR_RANGE.max));
};
/**
* The width of the page shadow around pages in pixels.
*/
Viewport.PAGE_SHADOW = {top: 3, bottom: 7, left: 5, right: 5};
Viewport.PAGE_SHADOW = {
top: 3,
bottom: 7,
left: 5,
right: 5
};
Viewport.prototype = {
/**
@ -172,10 +169,7 @@ Viewport.prototype = {
documentNeedsScrollbars_: function(zoom) {
var zoomedDimensions = this.getZoomedDocumentDimensions_(zoom);
if (!zoomedDimensions) {
return {
horizontal: false,
vertical: false
};
return {horizontal: false, vertical: false};
}
// If scrollbars are required for one direction, expand the document in the
@ -199,7 +193,7 @@ Viewport.prototype = {
* respectively.
*/
documentHasScrollbars: function() {
return this.documentNeedsScrollbars_(this.zoom_);
return this.documentNeedsScrollbars_(this.zoom);
},
/**
@ -207,11 +201,11 @@ Viewport.prototype = {
* Helper function called when the zoomed document size changes.
*/
contentSizeChanged_: function() {
var zoomedDimensions = this.getZoomedDocumentDimensions_(this.zoom_);
var zoomedDimensions = this.getZoomedDocumentDimensions_(this.zoom);
if (zoomedDimensions) {
this.sizer_.style.width = zoomedDimensions.width + 'px';
this.sizer_.style.height = zoomedDimensions.height +
this.topToolbarHeight_ + 'px';
this.sizer_.style.height =
zoomedDimensions.height + this.topToolbarHeight_ + 'px';
}
},
@ -223,15 +217,27 @@ Viewport.prototype = {
this.viewportChangedCallback_();
},
/**
* @private
* Called when the browser window size changes.
*/
resizeWrapper_: function() {
this.setUserInitiatedCallback_(false);
this.resize_();
this.setUserInitiatedCallback_(true);
},
/**
* @private
* Called when the viewport size changes.
*/
resize_: function() {
if (this.fittingType_ == Viewport.FittingType.FIT_TO_PAGE)
if (this.fittingType_ == FittingType.FIT_TO_PAGE)
this.fitToPageInternal_(false);
else if (this.fittingType_ == Viewport.FittingType.FIT_TO_WIDTH)
else if (this.fittingType_ == FittingType.FIT_TO_WIDTH)
this.fitToWidth();
else if (this.fittingType_ == FittingType.FIT_TO_HEIGHT)
this.fitToHeightInternal_(false);
else
this.updateViewport_();
},
@ -258,7 +264,7 @@ Viewport.prototype = {
* @type {Object} the size of the viewport excluding scrollbars.
*/
get size() {
var needsScrollbars = this.documentNeedsScrollbars_(this.zoom_);
var needsScrollbars = this.documentNeedsScrollbars_(this.zoom);
var scrollbarWidth = needsScrollbars.vertical ? this.scrollbarWidth_ : 0;
var scrollbarHeight = needsScrollbars.horizontal ? this.scrollbarWidth_ : 0;
return {
@ -271,7 +277,15 @@ Viewport.prototype = {
* @type {number} the zoom level of the viewport.
*/
get zoom() {
return this.zoom_;
return this.zoomManager_.applyBrowserZoom(this.internalZoom_);
},
/**
* Set the zoom manager.
* @type {ZoomManager} manager the zoom manager to set.
*/
set zoomManager(manager) {
this.zoomManager_ = manager;
},
/**
@ -299,6 +313,7 @@ Viewport.prototype = {
/**
* @private
* @param {function} f Function to wrap
* Used to wrap a function that might perform zooming on the viewport. This is
* required so that we can notify the plugin that zooming is in progress
* so that while zooming is taking place it can stop reacting to scroll events
@ -320,19 +335,19 @@ Viewport.prototype = {
setZoomInternal_: function(newZoom) {
if (!this.allowedToChangeZoom_) {
throw 'Called Viewport.setZoomInternal_ without calling ' +
'Viewport.mightZoom_.';
'Viewport.mightZoom_.';
}
// Record the scroll position (relative to the top-left of the window).
var currentScrollPos = {
x: this.position.x / this.zoom_,
y: this.position.y / this.zoom_
x: this.position.x / this.zoom,
y: this.position.y / this.zoom
};
this.zoom_ = newZoom;
this.internalZoom_ = newZoom;
this.contentSizeChanged_();
// Scroll to the scaled scroll position.
this.position = {
x: currentScrollPos.x * newZoom,
y: currentScrollPos.y * newZoom
x: currentScrollPos.x * this.zoom,
y: currentScrollPos.y * this.zoom
};
},
@ -344,10 +359,11 @@ Viewport.prototype = {
* @param {!Object} center The pinch center in content coordinates.
*/
setPinchZoomInternal_: function(scaleDelta, center) {
assert(this.allowedToChangeZoom_,
assert(
this.allowedToChangeZoom_,
'Called Viewport.setPinchZoomInternal_ without calling ' +
'Viewport.mightZoom_.');
this.zoom_ = clampScale(this.zoom_ * scaleDelta);
'Viewport.mightZoom_.');
this.internalZoom_ = Viewport.clampZoom(this.internalZoom_ * scaleDelta);
var newCenterInContent = this.frameToContent(center);
var delta = {
@ -357,16 +373,13 @@ Viewport.prototype = {
// Record the scroll position (relative to the pinch center).
var currentScrollPos = {
x: this.position.x - delta.x * this.zoom_,
y: this.position.y - delta.y * this.zoom_
x: this.position.x - delta.x * this.zoom,
y: this.position.y - delta.y * this.zoom
};
this.contentSizeChanged_();
// Scroll to the scaled scroll position.
this.position = {
x: currentScrollPos.x,
y: currentScrollPos.y
};
this.position = {x: currentScrollPos.x, y: currentScrollPos.y};
},
/**
@ -379,8 +392,8 @@ Viewport.prototype = {
// TODO(mcnee) Add a helper Point class to avoid duplicating operations
// on plain {x,y} objects.
return {
x: (framePoint.x + this.position.x) / this.zoom_,
y: (framePoint.y + this.position.y) / this.zoom_
x: (framePoint.x + this.position.x) / this.zoom,
y: (framePoint.y + this.position.y) / this.zoom
};
},
@ -389,13 +402,34 @@ Viewport.prototype = {
* @param {number} newZoom the zoom level to zoom to.
*/
setZoom: function(newZoom) {
this.fittingType_ = Viewport.FittingType.NONE;
newZoom = Math.max(Viewport.ZOOM_FACTOR_RANGE.min,
Math.min(newZoom, Viewport.ZOOM_FACTOR_RANGE.max));
this.mightZoom_(function() {
this.setZoomInternal_(newZoom);
this.fittingType_ = FittingType.NONE;
this.mightZoom_(() => {
this.setZoomInternal_(Viewport.clampZoom(newZoom));
this.updateViewport_();
}.bind(this));
});
},
/**
* Gets notified of the browser zoom changing seperately from the
* internal zoom.
* @param {number} oldBrowserZoom the previous value of the browser zoom.
*/
updateZoomFromBrowserChange: function(oldBrowserZoom) {
this.mightZoom_(() => {
// Record the scroll position (relative to the top-left of the window).
var oldZoom = oldBrowserZoom * this.internalZoom_;
var currentScrollPos = {
x: this.position.x / oldZoom,
y: this.position.y / oldZoom
};
this.contentSizeChanged_();
// Scroll to the scaled scroll position.
this.position = {
x: currentScrollPos.x * this.zoom,
y: currentScrollPos.y * this.zoom
};
this.updateViewport_();
});
},
/**
@ -406,7 +440,7 @@ Viewport.prototype = {
},
/**
* @type {Viewport.FittingType} the fitting type the viewport is currently in.
* @type {FittingType} the fitting type the viewport is currently in.
*/
get fittingType() {
return this.fittingType_;
@ -414,8 +448,8 @@ Viewport.prototype = {
/**
* @private
* @param {integer} y the y-coordinate to get the page at.
* @return {integer} the index of a page overlapping the given y-coordinate.
* @param {number} y the y-coordinate to get the page at.
* @return {number} the index of a page overlapping the given y-coordinate.
*/
getPageAtY_: function(y) {
var min = 0;
@ -429,12 +463,13 @@ Viewport.prototype = {
top = this.pageDimensions_[page - 1].y +
this.pageDimensions_[page - 1].height;
}
var bottom = this.pageDimensions_[page].y +
this.pageDimensions_[page].height;
var bottom =
this.pageDimensions_[page].y + this.pageDimensions_[page].height;
if (top <= y && bottom > y)
return page;
else if (top > y)
if (top > y)
max = page - 1;
else
min = page + 1;
@ -445,24 +480,26 @@ Viewport.prototype = {
/**
* Returns the page with the greatest proportion of its height in the current
* viewport.
* @return {int} the index of the most visible page.
* @return {number} the index of the most visible page.
*/
getMostVisiblePage: function() {
var firstVisiblePage = this.getPageAtY_(this.position.y / this.zoom_);
var firstVisiblePage = this.getPageAtY_(this.position.y / this.zoom);
if (firstVisiblePage == this.pageDimensions_.length - 1)
return firstVisiblePage;
var viewportRect = {
x: this.position.x / this.zoom_,
y: this.position.y / this.zoom_,
width: this.size.width / this.zoom_,
height: this.size.height / this.zoom_
x: this.position.x / this.zoom,
y: this.position.y / this.zoom,
width: this.size.width / this.zoom,
height: this.size.height / this.zoom
};
var firstVisiblePageVisibility = getIntersectionHeight(
this.pageDimensions_[firstVisiblePage], viewportRect) /
var firstVisiblePageVisibility =
getIntersectionHeight(
this.pageDimensions_[firstVisiblePage], viewportRect) /
this.pageDimensions_[firstVisiblePage].height;
var nextPageVisibility = getIntersectionHeight(
this.pageDimensions_[firstVisiblePage + 1], viewportRect) /
var nextPageVisibility =
getIntersectionHeight(
this.pageDimensions_[firstVisiblePage + 1], viewportRect) /
this.pageDimensions_[firstVisiblePage + 1].height;
if (nextPageVisibility > firstVisiblePageVisibility)
return firstVisiblePage + 1;
@ -471,25 +508,28 @@ Viewport.prototype = {
/**
* @private
* Compute the zoom level for fit-to-page or fit-to-width. |pageDimensions| is
* the dimensions for a given page and if |widthOnly| is true, it indicates
* that fit-to-page zoom should be computed rather than fit-to-page.
* @param {Object} pageDimensions the dimensions of a given page
* @param {boolean} widthOnly a bool indicating whether fit-to-page or
* fit-to-width should be computed.
* @return {number} the zoom to use
* Compute the zoom level for fit-to-page, fit-to-width or fit-to-height.
*
* At least one of {fitWidth, fitHeight} must be true.
*
* @param {Object} pageDimensions the dimensions of a given page in px.
* @param {boolean} fitWidth a bool indicating whether the whole width of the
* page needs to be in the viewport.
* @param {boolean} fitHeight a bool indicating whether the whole height of
* the page needs to be in the viewport.
* @return {number} the internal zoom to set
*/
computeFittingZoom_: function(pageDimensions, widthOnly) {
computeFittingZoom_: function(pageDimensions, fitWidth, fitHeight) {
assert(
fitWidth || fitHeight,
'Invalid parameters. At least one of fitWidth and fitHeight must be ' +
'true.');
// First compute the zoom without scrollbars.
var zoomWidth = this.window_.innerWidth / pageDimensions.width;
var zoom;
var zoomHeight;
if (widthOnly) {
zoom = zoomWidth;
} else {
zoomHeight = this.window_.innerHeight / pageDimensions.height;
zoom = Math.min(zoomWidth, zoomHeight);
}
var zoom = this.computeFittingZoomGivenDimensions_(
fitWidth, fitHeight, this.window_.innerWidth, this.window_.innerHeight,
pageDimensions.width, pageDimensions.height);
// Check if there needs to be any scrollbars.
var needsScrollbars = this.documentNeedsScrollbars_(zoom);
@ -521,43 +561,110 @@ Viewport.prototype = {
windowWithScrollbars.width -= scrollbarWidth;
// Recompute the zoom.
zoomWidth = windowWithScrollbars.width / pageDimensions.width;
if (widthOnly) {
zoom = zoomWidth;
} else {
zoomHeight = windowWithScrollbars.height / pageDimensions.height;
zoom = Math.min(zoomWidth, zoomHeight);
}
return zoom;
},
zoom = this.computeFittingZoomGivenDimensions_(
fitWidth, fitHeight, windowWithScrollbars.width,
windowWithScrollbars.height, pageDimensions.width,
pageDimensions.height);
/**
* Zoom the viewport so that the page-width consumes the entire viewport.
*/
fitToWidth: function() {
this.mightZoom_(function() {
this.fittingType_ = Viewport.FittingType.FIT_TO_WIDTH;
if (!this.documentDimensions_)
return;
// When computing fit-to-width, the maximum width of a page in the
// document is used, which is equal to the size of the document width.
this.setZoomInternal_(this.computeFittingZoom_(this.documentDimensions_,
true));
var page = this.getMostVisiblePage();
this.updateViewport_();
}.bind(this));
return this.zoomManager_.internalZoomComponent(zoom);
},
/**
* @private
* Zoom the viewport so that a page consumes the entire viewport.
* Compute a zoom level given the dimensions to fit and the actual numbers
* in those dimensions.
*
* @param {boolean} fitWidth make sure the page width is totally contained in
* the window.
* @param {boolean} fitHeight make sure the page height is totally contained
* in the window.
* @param {number} windowWidth the width of the window in px.
* @param {number} windowHeight the height of the window in px.
* @param {number} pageWidth the width of the page in px.
* @param {number} pageHeight the height of the page in px.
* @return {number} the internal zoom to set
*/
computeFittingZoomGivenDimensions_: function(
fitWidth, fitHeight, windowWidth, windowHeight, pageWidth, pageHeight) {
// Assumes at least one of {fitWidth, fitHeight} is set.
var zoomWidth;
var zoomHeight;
if (fitWidth)
zoomWidth = windowWidth / pageWidth;
if (fitHeight)
zoomHeight = windowHeight / pageHeight;
if (!fitWidth && fitHeight)
return zoomHeight;
if (fitWidth && !fitHeight)
return zoomWidth;
// Assume fitWidth && fitHeight
return Math.min(zoomWidth, zoomHeight);
},
/**
* Zoom the viewport so that the page width consumes the entire viewport.
*/
fitToWidth: function() {
this.mightZoom_(() => {
this.fittingType_ = FittingType.FIT_TO_WIDTH;
if (!this.documentDimensions_)
return;
// When computing fit-to-width, the maximum width of a page in the
// document is used, which is equal to the size of the document width.
this.setZoomInternal_(
this.computeFittingZoom_(this.documentDimensions_, true, false));
this.updateViewport_();
});
},
/**
* @private
* Zoom the viewport so that the page height consumes the entire viewport.
* @param {boolean} scrollToTopOfPage Set to true if the viewport should be
* scrolled to the top of the current page. Set to false if the viewport
* should remain at the current scroll position.
*/
fitToHeightInternal_: function(scrollToTopOfPage) {
this.mightZoom_(() => {
this.fittingType_ = FittingType.FIT_TO_HEIGHT;
if (!this.documentDimensions_)
return;
var page = this.getMostVisiblePage();
// When computing fit-to-height, the maximum height of the current page
// is used.
var dimensions = {
width: 0,
height: this.pageDimensions_[page].height,
};
this.setZoomInternal_(this.computeFittingZoom_(dimensions, false, true));
if (scrollToTopOfPage) {
this.position = {x: 0, y: this.pageDimensions_[page].y * this.zoom};
}
this.updateViewport_();
});
},
/**
* Zoom the viewport so that the page height consumes the entire viewport.
*/
fitToHeight: function() {
this.fitToHeightInternal_(true);
},
/**
* @private
* Zoom the viewport so that a page consumes as much as possible of the it.
* @param {boolean} scrollToTopOfPage Set to true if the viewport should be
* scrolled to the top of the current page. Set to false if the viewport
* should remain at the current scroll position.
*/
fitToPageInternal_: function(scrollToTopOfPage) {
this.mightZoom_(function() {
this.fittingType_ = Viewport.FittingType.FIT_TO_PAGE;
this.mightZoom_(() => {
this.fittingType_ = FittingType.FIT_TO_PAGE;
if (!this.documentDimensions_)
return;
var page = this.getMostVisiblePage();
@ -566,15 +673,12 @@ Viewport.prototype = {
width: this.documentDimensions_.width,
height: this.pageDimensions_[page].height,
};
this.setZoomInternal_(this.computeFittingZoom_(dimensions, false));
this.setZoomInternal_(this.computeFittingZoom_(dimensions, true, true));
if (scrollToTopOfPage) {
this.position = {
x: 0,
y: this.pageDimensions_[page].y * this.zoom_
};
this.position = {x: 0, y: this.pageDimensions_[page].y * this.zoom};
}
this.updateViewport_();
}.bind(this));
});
},
/**
@ -589,32 +693,32 @@ Viewport.prototype = {
* Zoom out to the next predefined zoom level.
*/
zoomOut: function() {
this.mightZoom_(function() {
this.fittingType_ = Viewport.FittingType.NONE;
this.mightZoom_(() => {
this.fittingType_ = FittingType.NONE;
var nextZoom = Viewport.ZOOM_FACTORS[0];
for (var i = 0; i < Viewport.ZOOM_FACTORS.length; i++) {
if (Viewport.ZOOM_FACTORS[i] < this.zoom_)
if (Viewport.ZOOM_FACTORS[i] < this.internalZoom_)
nextZoom = Viewport.ZOOM_FACTORS[i];
}
this.setZoomInternal_(nextZoom);
this.updateViewport_();
}.bind(this));
});
},
/**
* Zoom in to the next predefined zoom level.
*/
zoomIn: function() {
this.mightZoom_(function() {
this.fittingType_ = Viewport.FittingType.NONE;
this.mightZoom_(() => {
this.fittingType_ = FittingType.NONE;
var nextZoom = Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1];
for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) {
if (Viewport.ZOOM_FACTORS[i] > this.zoom_)
if (Viewport.ZOOM_FACTORS[i] > this.internalZoom_)
nextZoom = Viewport.ZOOM_FACTORS[i];
}
this.setZoomInternal_(nextZoom);
this.updateViewport_();
}.bind(this));
});
},
/**
@ -622,17 +726,18 @@ Viewport.prototype = {
* @param {!Object} e The pinch event.
*/
pinchZoom: function(e) {
this.mightZoom_(function() {
this.mightZoom_(() => {
this.pinchPhase_ = e.direction == 'out' ?
Viewport.PinchPhase.PINCH_UPDATE_ZOOM_OUT :
Viewport.PinchPhase.PINCH_UPDATE_ZOOM_IN;
Viewport.PinchPhase.PINCH_UPDATE_ZOOM_OUT :
Viewport.PinchPhase.PINCH_UPDATE_ZOOM_IN;
var scaleDelta = e.startScaleRatio / this.prevScale_;
this.pinchPanVector_ =
vectorDelta(e.center, this.firstPinchCenterInFrame_);
var needsScrollbars = this.documentNeedsScrollbars_(
clampScale(this.zoom_ * scaleDelta));
var needsScrollbars =
this.documentNeedsScrollbars_(this.zoomManager_.applyBrowserZoom(
Viewport.clampZoom(this.internalZoom_ * scaleDelta)));
this.pinchCenter_ = e.center;
@ -653,11 +758,10 @@ Viewport.prototype = {
this.keepContentCentered_ = false;
}
this.setPinchZoomInternal_(
scaleDelta, frameToPluginCoordinate(e.center));
this.setPinchZoomInternal_(scaleDelta, frameToPluginCoordinate(e.center));
this.updateViewport_();
this.prevScale_ = e.startScaleRatio;
}.bind(this));
});
},
pinchZoomStart: function(e) {
@ -666,7 +770,7 @@ Viewport.prototype = {
this.oldCenterInContent =
this.frameToContent(frameToPluginCoordinate(e.center));
var needsScrollbars = this.documentNeedsScrollbars_(this.zoom_);
var needsScrollbars = this.documentNeedsScrollbars_(this.zoom);
this.keepContentCentered_ = !needsScrollbars.horizontal;
// We keep track of begining of the pinch.
// By doing so we will be able to compute the pan distance.
@ -674,15 +778,14 @@ Viewport.prototype = {
},
pinchZoomEnd: function(e) {
this.mightZoom_(function() {
this.mightZoom_(() => {
this.pinchPhase_ = Viewport.PinchPhase.PINCH_END;
var scaleDelta = e.startScaleRatio / this.prevScale_;
this.pinchCenter_ = e.center;
this.setPinchZoomInternal_(
scaleDelta, frameToPluginCoordinate(e.center));
this.setPinchZoomInternal_(scaleDelta, frameToPluginCoordinate(e.center));
this.updateViewport_();
}.bind(this));
});
this.pinchPhase_ = Viewport.PinchPhase.PINCH_NONE;
this.pinchPanVector_ = null;
@ -695,7 +798,16 @@ Viewport.prototype = {
* @param {number} page the index of the page to go to. zero-based.
*/
goToPage: function(page) {
this.mightZoom_(function() {
this.goToPageAndY(page, 0);
},
/**
* Go to the given y position in the given page index.
* @param {number} page the index of the page to go to. zero-based.
* @param {number} y the y position in the page to go to.
*/
goToPageAndY: function(page, y) {
this.mightZoom_(() => {
if (this.pageDimensions_.length === 0)
return;
if (page < 0)
@ -704,17 +816,17 @@ Viewport.prototype = {
page = this.pageDimensions_.length - 1;
var dimensions = this.pageDimensions_[page];
var toolbarOffset = 0;
// Unless we're in fit to page mode, scroll above the page by
// |this.topToolbarHeight_| so that the toolbar isn't covering it
// Unless we're in fit to page or fit to height mode, scroll above the
// page by |this.topToolbarHeight_| so that the toolbar isn't covering it
// initially.
if (this.fittingType_ != Viewport.FittingType.FIT_TO_PAGE)
if (!this.isPagedMode())
toolbarOffset = this.topToolbarHeight_;
this.position = {
x: dimensions.x * this.zoom_,
y: dimensions.y * this.zoom_ - toolbarOffset
x: dimensions.x * this.zoom,
y: (dimensions.y + y) * this.zoom - toolbarOffset
};
this.updateViewport_();
}.bind(this));
});
},
/**
@ -722,22 +834,19 @@ Viewport.prototype = {
* @param {Object} documentDimensions the dimensions of the document
*/
setDocumentDimensions: function(documentDimensions) {
this.mightZoom_(function() {
this.mightZoom_(() => {
var initialDimensions = !this.documentDimensions_;
this.documentDimensions_ = documentDimensions;
this.pageDimensions_ = this.documentDimensions_.pageDimensions;
if (initialDimensions) {
this.setZoomInternal_(
Math.min(this.defaultZoom_,
this.computeFittingZoom_(this.documentDimensions_, true)));
this.position = {
x: 0,
y: -this.topToolbarHeight_
};
this.setZoomInternal_(Math.min(
this.defaultZoom_,
this.computeFittingZoom_(this.documentDimensions_, true, false)));
this.position = {x: 0, y: -this.topToolbarHeight_};
}
this.contentSizeChanged_();
this.resize_();
}.bind(this));
});
},
/**
@ -748,12 +857,7 @@ Viewport.prototype = {
*/
getPageScreenRect: function(page) {
if (!this.documentDimensions_) {
return {
x: 0,
y: 0,
width: 0,
height: 0
};
return {x: 0, y: 0, width: 0, height: 0};
}
if (page >= this.pageDimensions_.length)
page = this.pageDimensions_.length - 1;
@ -777,15 +881,29 @@ Viewport.prototype = {
Viewport.PAGE_SHADOW.left;
// Compute the space on the left of the document if the document fits
// completely in the screen.
var spaceOnLeft = (this.size.width -
this.documentDimensions_.width * this.zoom_) / 2;
var spaceOnLeft =
(this.size.width - this.documentDimensions_.width * this.zoom) / 2;
spaceOnLeft = Math.max(spaceOnLeft, 0);
return {
x: x * this.zoom_ + spaceOnLeft - this.window_.pageXOffset,
y: insetDimensions.y * this.zoom_ - this.window_.pageYOffset,
width: insetDimensions.width * this.zoom_,
height: insetDimensions.height * this.zoom_
x: x * this.zoom + spaceOnLeft - this.window_.pageXOffset,
y: insetDimensions.y * this.zoom - this.window_.pageYOffset,
width: insetDimensions.width * this.zoom,
height: insetDimensions.height * this.zoom
};
},
/**
* Check if the current fitting type is a paged mode.
*
* In a paged mode, page up and page down scroll to the top of the
* previous/next page and part of the page is under the toolbar.
*
* @return {boolean} Whether the current fitting type is a paged mode.
*/
isPagedMode: function(page) {
return (
this.fittingType_ == FittingType.FIT_TO_PAGE ||
this.fittingType_ == FittingType.FIT_TO_HEIGHT);
}
};

Просмотреть файл

@ -24,6 +24,7 @@ ViewportScroller.MAX_DRAG_SCROLL_DISTANCE_ = 100;
* @param {Object} viewport The viewport info of the page.
* @param {Object} plugin The PDF plugin element.
* @param {Object} window The window containing the viewer.
* @constructor
*/
function ViewportScroller(viewport, plugin, window) {
this.viewport_ = viewport;
@ -43,9 +44,9 @@ ViewportScroller.prototype = {
*/
startDragScrollTimer_: function() {
if (this.timerId_ === null) {
this.timerId_ =
this.window_.setInterval(this.dragScrollPage_.bind(this),
ViewportScroller.DRAG_TIMER_INTERVAL_MS_);
this.timerId_ = this.window_.setInterval(
this.dragScrollPage_.bind(this),
ViewportScroller.DRAG_TIMER_INTERVAL_MS_);
this.lastFrameTime_ = Date.now();
}
},
@ -70,7 +71,7 @@ ViewportScroller.prototype = {
var position = this.viewport_.position;
var currentFrameTime = Date.now();
var timeAdjustment = (currentFrameTime - this.lastFrameTime_) /
ViewportScroller.DRAG_TIMER_INTERVAL_MS_;
ViewportScroller.DRAG_TIMER_INTERVAL_MS_;
position.y += (this.scrollVelocity_.y * timeAdjustment);
position.x += (this.scrollVelocity_.x * timeAdjustment);
this.viewport_.position = position;
@ -85,18 +86,19 @@ ViewportScroller.prototype = {
* @return {Object} Object with x and y direction scroll velocity.
*/
calculateVelocity_: function(event) {
var x = Math.min(Math.max(-event.offsetX,
event.offsetX - this.plugin_.offsetWidth, 0),
ViewportScroller.MAX_DRAG_SCROLL_DISTANCE_) *
Math.sign(event.offsetX);
var y = Math.min(Math.max(-event.offsetY,
event.offsetY - this.plugin_.offsetHeight, 0),
ViewportScroller.MAX_DRAG_SCROLL_DISTANCE_) *
Math.sign(event.offsetY);
return {
x: x,
y: y
};
var x =
Math.min(
Math.max(
-event.offsetX, event.offsetX - this.plugin_.offsetWidth, 0),
ViewportScroller.MAX_DRAG_SCROLL_DISTANCE_) *
Math.sign(event.offsetX);
var y =
Math.min(
Math.max(
-event.offsetY, event.offsetY - this.plugin_.offsetHeight, 0),
ViewportScroller.MAX_DRAG_SCROLL_DISTANCE_) *
Math.sign(event.offsetY);
return {x: x, y: y};
},
/**
@ -122,13 +124,13 @@ ViewportScroller.prototype = {
if (isSelecting) {
if (!this.mousemoveCallback_)
this.mousemoveCallback_ = this.onMousemove_.bind(this);
this.plugin_.addEventListener('mousemove', this.mousemoveCallback_,
false);
this.plugin_.addEventListener(
'mousemove', this.mousemoveCallback_, false);
} else {
this.stopDragScrollTimer_();
if (this.mousemoveCallback_) {
this.plugin_.removeEventListener('mousemove', this.mousemoveCallback_,
false);
this.plugin_.removeEventListener(
'mousemove', this.mousemoveCallback_, false);
}
}
}

Просмотреть файл

@ -5,20 +5,109 @@
'use strict';
/**
* A class that manages updating the browser with zoom changes.
* Abstract parent of classes that manage updating the browser
* with zoom changes and/or updating the viewer's zoom when
* the browser zoom changes.
*/
class ZoomManager {
/**
* Constructs a ZoomManager
* Constructs a ZoomManager.
* @param {!Viewport} viewport A Viewport for which to manage zoom.
* @param {number} initialZoom The initial browser zoom level.
*/
constructor(viewport, initialZoom) {
if (this.constructor === ZoomManager) {
throw new TypeError('Instantiated abstract class: ZoomManager');
}
this.viewport_ = viewport;
this.browserZoom_ = initialZoom;
}
/**
* Creates the appropriate kind of zoom manager given the zoom behavior.
* @param {BrowserApi.ZoomBehavior} zoomBehavior How to manage zoom.
* @param {!Viewport} viewport A Viewport for which to manage zoom.
* @param {Function} setBrowserZoomFunction A function that sets the browser
* zoom to the provided value.
* @param {number} initialZoom The initial browser zoom level.
*/
static create(zoomBehavior, viewport, setBrowserZoomFunction, initialZoom) {
switch (zoomBehavior) {
case BrowserApi.ZoomBehavior.MANAGE:
return new ActiveZoomManager(
viewport, setBrowserZoomFunction, initialZoom);
case BrowserApi.ZoomBehavior.PROPAGATE_PARENT:
return new EmbeddedZoomManager(viewport, initialZoom);
default:
return new InactiveZoomManager(viewport, initialZoom);
}
}
/**
* Invoked when a browser-initiated zoom-level change occurs.
* @param {number} newZoom the zoom level to zoom to.
*/
onBrowserZoomChange(newZoom) {}
/**
* Invoked when an extension-initiated zoom-level change occurs.
*/
onPdfZoomChange() {}
/**
* Combines the internal pdf zoom and the browser zoom to
* produce the total zoom level for the viewer.
* @param {number} internalZoom the zoom level internal to the viewer.
* @return {number} the total zoom level.
*/
applyBrowserZoom(internalZoom) {
return this.browserZoom_ * internalZoom;
}
/**
* Given a zoom level, return the internal zoom level needed to
* produce that zoom level.
* @param {number} totalZoom the total zoom level.
* @return {number} the zoom level internal to the viewer.
*/
internalZoomComponent(totalZoom) {
return totalZoom / this.browserZoom_;
}
/**
* Returns whether two numbers are approximately equal.
* @param {number} a The first number.
* @param {number} b The second number.
*/
floatingPointEquals(a, b) {
let MIN_ZOOM_DELTA = 0.01;
// If the zoom level is close enough to the current zoom level, don't
// change it. This avoids us getting into an infinite loop of zoom changes
// due to floating point error.
return Math.abs(a - b) <= MIN_ZOOM_DELTA;
}
}
/**
* InactiveZoomManager has no control over the browser's zoom
* and does not respond to browser zoom changes.
*/
class InactiveZoomManager extends ZoomManager {}
/**
* ActiveZoomManager controls the browser's zoom.
*/
class ActiveZoomManager extends ZoomManager {
/**
* Constructs a ActiveZoomManager.
* @param {!Viewport} viewport A Viewport for which to manage zoom.
* @param {Function} setBrowserZoomFunction A function that sets the browser
* zoom to the provided value.
* @param {number} initialZoom The initial browser zoom level.
*/
constructor(viewport, setBrowserZoomFunction, initialZoom) {
this.viewport_ = viewport;
super(viewport, initialZoom);
this.setBrowserZoomFunction_ = setBrowserZoomFunction;
this.browserZoom_ = initialZoom;
this.changingBrowserZoom_ = null;
}
@ -56,28 +145,54 @@ class ZoomManager {
if (this.floatingPointEquals(this.browserZoom_, zoom))
return;
this.changingBrowserZoom_ = this.setBrowserZoomFunction_(zoom).then(
function() {
this.changingBrowserZoom_ = this.setBrowserZoomFunction_(zoom).then(() => {
this.browserZoom_ = zoom;
this.changingBrowserZoom_ = null;
// The extension's zoom level may have changed while the browser zoom
// change was in progress. We call back into onPdfZoomChange to ensure the
// browser zoom is up to date.
// change was in progress. We call back into onPdfZoomChange to ensure
// the browser zoom is up to date.
this.onPdfZoomChange();
}.bind(this));
});
}
/**
* Returns whether two numbers are approximately equal.
* @param {number} a The first number.
* @param {number} b The second number.
* Combines the internal pdf zoom and the browser zoom to
* produce the total zoom level for the viewer.
* @param {number} internalZoom the zoom level internal to the viewer.
* @return {number} the total zoom level.
*/
floatingPointEquals(a, b) {
let MIN_ZOOM_DELTA = 0.01;
// If the zoom level is close enough to the current zoom level, don't
// change it. This avoids us getting into an infinite loop of zoom changes
// due to floating point error.
return Math.abs(a - b) <= MIN_ZOOM_DELTA;
applyBrowserZoom(internalZoom) {
// The internal zoom and browser zoom are changed together, so the
// browser zoom is already applied.
return internalZoom;
}
};
/**
* Given a zoom level, return the internal zoom level needed to
* produce that zoom level.
* @param {number} totalZoom the total zoom level.
* @return {number} the zoom level internal to the viewer.
*/
internalZoomComponent(totalZoom) {
// The internal zoom and browser zoom are changed together, so the
// internal zoom is the total zoom.
return totalZoom;
}
}
/**
* This EmbeddedZoomManager responds to changes in the browser zoom,
* but does not control the browser zoom.
*/
class EmbeddedZoomManager extends ZoomManager {
/**
* Invoked when a browser-initiated zoom-level change occurs.
* @param {number} newZoom the new browser zoom level.
*/
onBrowserZoomChange(newZoom) {
let oldZoom = this.browserZoom_;
this.browserZoom_ = newZoom;
this.viewport_.updateZoomFromBrowserChange(oldZoom);
}
}