add pdf webui as submodule
This commit is contained in:
Родитель
3c90fabab7
Коммит
c4dd2f4f56
|
@ -25,3 +25,6 @@
|
|||
[submodule "vendor/grit"]
|
||||
path = vendor/grit
|
||||
url = https://chromium.googlesource.com/chromium/src/tools/grit.git
|
||||
[submodule "vendor/pdf_viewer"]
|
||||
path = vendor/pdf_viewer
|
||||
url = https://github.com/deepak1556/pdf-viewer.git
|
||||
|
|
|
@ -1,130 +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.
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Returns a promise that will resolve to the default zoom factor.
|
||||
* @return {Promise<number>} A promise that will resolve to the default zoom
|
||||
* factor.
|
||||
*/
|
||||
function lookupDefaultZoom() {
|
||||
return cr.sendWithPromise('getDefaultZoom');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will resolve to the initial zoom factor
|
||||
* upon starting the plugin. This may differ from the default zoom
|
||||
* if, for example, the page is zoomed before the plugin is run.
|
||||
* @return {Promise<number>} A promise that will resolve to the initial zoom
|
||||
* factor.
|
||||
*/
|
||||
function lookupInitialZoom() {
|
||||
return cr.sendWithPromise('getInitialZoom');
|
||||
}
|
||||
|
||||
/**
|
||||
* A class providing an interface to the browser.
|
||||
*/
|
||||
class BrowserApi {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {!Object} streamInfo The stream object which points to the data
|
||||
* contained in the PDF.
|
||||
* @param {number} defaultZoom The default browser zoom.
|
||||
* @param {number} initialZoom The initial browser zoom
|
||||
* upon starting the plugin.
|
||||
* @param {boolean} manageZoom Whether to manage zoom.
|
||||
*/
|
||||
constructor(streamInfo, defaultZoom, initialZoom, manageZoom) {
|
||||
this.streamInfo_ = streamInfo;
|
||||
this.defaultZoom_ = defaultZoom;
|
||||
this.initialZoom_ = initialZoom;
|
||||
this.manageZoom_ = manageZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise to a BrowserApi.
|
||||
* @param {!Object} streamInfo The stream object pointing to the data
|
||||
* contained in the PDF.
|
||||
* @param {boolean} manageZoom Whether to manage zoom.
|
||||
*/
|
||||
static create(streamInfo, manageZoom) {
|
||||
return Promise.all([
|
||||
lookupDefaultZoom(),
|
||||
lookupInitialZoom()
|
||||
]).then(function(zoomFactors) {
|
||||
return new BrowserApi(
|
||||
streamInfo, zoomFactors[0], zoomFactors[1], manageZoom);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stream info pointing to the data contained in the PDF.
|
||||
* @return {Object} The stream info object.
|
||||
*/
|
||||
getStreamInfo() {
|
||||
return this.streamInfo_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the browser zoom.
|
||||
* @param {number} zoom The zoom factor to send to the browser.
|
||||
* @return {Promise} A promise that will be resolved when the browser zoom
|
||||
* has been updated.
|
||||
*/
|
||||
setZoom(zoom) {
|
||||
if (!this.manageZoom_)
|
||||
return Promise.resolve();
|
||||
return cr.sendWithPromise('setZoom', zoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default browser zoom factor.
|
||||
* @return {number} The default browser zoom factor.
|
||||
*/
|
||||
getDefaultZoom() {
|
||||
return this.defaultZoom_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initial browser zoom factor.
|
||||
* @return {number} The initial browser zoom factor.
|
||||
*/
|
||||
getInitialZoom() {
|
||||
return this.initialZoom_;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_)
|
||||
return;
|
||||
|
||||
cr.addWebUIListener('onZoomLevelChanged', function(newZoomFactor) {
|
||||
listener(newZoomFactor);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a BrowserApi instance for an extension not running as a mime handler.
|
||||
* @return {Promise<BrowserApi>} A promise to a BrowserApi instance constructed
|
||||
* from the URL.
|
||||
*/
|
||||
function createBrowserApi(opts) {
|
||||
let streamInfo = {
|
||||
streamUrl: opts.originalURL,
|
||||
originalUrl: opts.originalURL,
|
||||
responseHeaders: {},
|
||||
embedded: window.parent != window,
|
||||
tabId: -1,
|
||||
};
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolve(BrowserApi.create(streamInfo, true));
|
||||
});
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<link rel="import" href="chrome://resources/html/polymer.html">
|
||||
<link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
|
||||
|
||||
<iron-iconset-svg size="24" name="pdf">
|
||||
<svg>
|
||||
<defs>
|
||||
<!--
|
||||
These icons are copied from Polymer's iron-icons and kept in sorted order.
|
||||
See http://goo.gl/Y1OdAq for instructions on adding additional icons.
|
||||
-->
|
||||
<g id="add"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path></g>
|
||||
<g id="bookmark"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"></path></g>
|
||||
<g id="bookmark-border"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"></path></g>
|
||||
<g id="fullscreen-exit"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"></path></g>
|
||||
<g id="remove"><path d="M19 13H5v-2h14v2z"></path></g>
|
||||
<g id="rotate-right"><path d="M15.55 5.55L11 1v3.07C7.06 4.56 4 7.92 4 12s3.05 7.44 7 7.93v-2.02c-2.84-.48-5-2.94-5-5.91s2.16-5.43 5-5.91V10l4.55-4.45zM19.93 11c-.17-1.39-.72-2.73-1.62-3.89l-1.42 1.42c.54.75.88 1.6 1.02 2.47h2.02zM13 17.9v2.02c1.39-.17 2.74-.71 3.9-1.61l-1.44-1.44c-.75.54-1.59.89-2.46 1.03zm3.89-2.42l1.42 1.41c.9-1.16 1.45-2.5 1.62-3.89h-2.02c-.14.87-.48 1.72-1.02 2.48z"></path></g>
|
||||
</defs>
|
||||
</svg>
|
||||
</iron-iconset-svg>
|
|
@ -1,15 +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. */
|
||||
|
||||
:root {
|
||||
--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);
|
||||
}
|
|
@ -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,26 +0,0 @@
|
|||
<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/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>
|
||||
<div id="item" on-click="onClick">
|
||||
<paper-ripple></paper-ripple>
|
||||
<paper-icon-button id="expand" icon="cr:chevron-right"
|
||||
on-click="toggleChildren">
|
||||
</paper-icon-button>
|
||||
<span id="title" tabindex="0">{{bookmark.title}}</span>
|
||||
</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}}">
|
||||
<viewer-bookmark bookmark="{{item}}" depth="{{childDepth}}">
|
||||
</viewer-bookmark>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script src="viewer-bookmark.js"></script>
|
|
@ -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.
|
||||
|
||||
(function() {
|
||||
/** Amount that each level of bookmarks is indented by (px). */
|
||||
var BOOKMARK_INDENT = 20;
|
||||
|
||||
Polymer({
|
||||
is: 'viewer-bookmark',
|
||||
|
||||
properties: {
|
||||
/**
|
||||
* A bookmark object, each containing a:
|
||||
* - title
|
||||
* - page (optional)
|
||||
* - children (an array of bookmarks)
|
||||
*/
|
||||
bookmark: {
|
||||
type: Object,
|
||||
observer: 'bookmarkChanged_'
|
||||
},
|
||||
|
||||
depth: {
|
||||
type: Number,
|
||||
observer: 'depthChanged'
|
||||
},
|
||||
|
||||
childDepth: Number,
|
||||
|
||||
childrenShown: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true,
|
||||
value: false
|
||||
},
|
||||
|
||||
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.
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -1,11 +0,0 @@
|
|||
<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}}">
|
||||
<viewer-bookmark bookmark="{{item}}" depth="0"></viewer-bookmark>
|
||||
</template>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script src="viewer-bookmarks-content.js"></script>
|
|
@ -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.
|
||||
|
||||
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 +0,0 @@
|
|||
<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/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}}
|
||||
</div>
|
||||
<div class="buttons" hidden$="{{!reloadFn}}">
|
||||
<paper-button on-click="reload" autofocus>
|
||||
{{strings.pageReload}}
|
||||
</paper-button>
|
||||
</div>
|
||||
</paper-dialog>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script src="viewer-error-screen.js"></script>
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
Polymer({
|
||||
is: 'viewer-error-screen',
|
||||
properties: {
|
||||
reloadFn: {
|
||||
type: Object,
|
||||
value: null,
|
||||
observer: 'reloadFnChanged_'
|
||||
},
|
||||
|
||||
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();
|
||||
},
|
||||
|
||||
reload: function() {
|
||||
if (this.reloadFn)
|
||||
this.reloadFn();
|
||||
}
|
||||
});
|
|
@ -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 +0,0 @@
|
|||
<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>
|
||||
<div id="text">{{label}}</div>
|
||||
<div id="triangle-right"></div>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script src="viewer-page-indicator.js"></script>
|
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
Polymer({
|
||||
is: 'viewer-page-indicator',
|
||||
|
||||
properties: {
|
||||
label: {
|
||||
type: String,
|
||||
value: '1'
|
||||
},
|
||||
|
||||
index: {
|
||||
type: Number,
|
||||
observer: 'indexChanged'
|
||||
},
|
||||
|
||||
pageLabels: {
|
||||
type: Array,
|
||||
value: null,
|
||||
observer: 'pageLabelsChanged'
|
||||
}
|
||||
},
|
||||
|
||||
timerId: undefined,
|
||||
|
||||
ready: function() {
|
||||
var callback = this.fadeIn.bind(this, 2000);
|
||||
window.addEventListener('scroll', function() {
|
||||
requestAnimationFrame(callback);
|
||||
});
|
||||
},
|
||||
|
||||
initialFadeIn: function() {
|
||||
this.fadeIn(6000);
|
||||
},
|
||||
|
||||
fadeIn: function(displayTime) {
|
||||
var percent = window.scrollY /
|
||||
(document.body.scrollHeight -
|
||||
document.documentElement.clientHeight);
|
||||
this.style.top = percent *
|
||||
(document.documentElement.clientHeight - this.offsetHeight) + 'px';
|
||||
this.style.opacity = 1;
|
||||
clearTimeout(this.timerId);
|
||||
|
||||
this.timerId = setTimeout(function() {
|
||||
this.style.opacity = 0;
|
||||
this.timerId = undefined;
|
||||
}.bind(this), displayTime);
|
||||
},
|
||||
|
||||
pageLabelsChanged: function() {
|
||||
this.indexChanged();
|
||||
},
|
||||
|
||||
indexChanged: function() {
|
||||
if (this.pageLabels)
|
||||
this.label = this.pageLabels[this.index];
|
||||
else
|
||||
this.label = String(this.index + 1);
|
||||
}
|
||||
});
|
|
@ -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. */
|
||||
|
||||
: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;
|
||||
}
|
||||
|
||||
#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%;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<link rel="import" href="chrome://resources/html/polymer.html">
|
||||
<link rel="import" href="chrome://resources/polymer/v1_0/iron-input/iron-input.html">
|
||||
<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>
|
||||
<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"
|
||||
on-change="pageNoCommitted" aria-label$="{{strings.labelPageNumber}}">
|
||||
</paper-input-container>
|
||||
<span id="slash"> / </span>
|
||||
<span id="pagelength-spacer">
|
||||
<span id="pagelength">{{docLength}}</span>
|
||||
</span>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script src="viewer-page-selector.js"></script>
|
|
@ -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.
|
||||
|
||||
Polymer({
|
||||
is: 'viewer-page-selector',
|
||||
|
||||
properties: {
|
||||
/**
|
||||
* The number of pages the document contains.
|
||||
*/
|
||||
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
|
||||
},
|
||||
|
||||
strings: Object,
|
||||
},
|
||||
|
||||
pageNoCommitted: function() {
|
||||
var page = parseInt(this.$.input.value);
|
||||
|
||||
if (!isNaN(page) && page <= this.docLength && page > 0)
|
||||
this.fire('change-page', {page: page - 1});
|
||||
else
|
||||
this.$.input.value = this.pageNo;
|
||||
this.$.input.blur();
|
||||
},
|
||||
|
||||
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
|
||||
// exactly centered.
|
||||
this.$['pagelength-spacer'].style.width = numDigits + 'ch';
|
||||
},
|
||||
|
||||
select: function() {
|
||||
this.$.input.select();
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {boolean} True if the selector input field is currently focused.
|
||||
*/
|
||||
isActive: function() {
|
||||
return this.shadowRoot.activeElement == this.$.input;
|
||||
}
|
||||
});
|
|
@ -1,30 +0,0 @@
|
|||
<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">
|
||||
|
||||
<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}}
|
||||
</paper-button>
|
||||
</div>
|
||||
</paper-dialog>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script src="viewer-password-screen.js"></script>
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
Polymer({
|
||||
is: 'viewer-password-screen',
|
||||
|
||||
properties: {
|
||||
invalid: Boolean,
|
||||
|
||||
active: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
observer: 'activeChanged'
|
||||
},
|
||||
|
||||
strings: Object,
|
||||
},
|
||||
|
||||
ready: function() {
|
||||
this.activeChanged();
|
||||
},
|
||||
|
||||
accept: function() {
|
||||
this.active = false;
|
||||
},
|
||||
|
||||
deny: function() {
|
||||
this.$.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();
|
||||
},
|
||||
|
||||
submit: function() {
|
||||
if (this.$.password.value.length == 0)
|
||||
return;
|
||||
this.$.password.disabled = true;
|
||||
this.$.submit.disabled = true;
|
||||
this.fire('password-submitted', {password: this.$.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;
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
<link rel="import" href="chrome://resources/html/polymer.html">
|
||||
<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html">
|
||||
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/slide-up-animation.html">
|
||||
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/transform-animation.html">
|
||||
<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">
|
||||
<link rel="import" href="../viewer-page-selector/viewer-page-selector.html">
|
||||
<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>
|
||||
|
||||
<paper-toolbar>
|
||||
<div id="aligner" class="middle">
|
||||
<span id="title" title="{{docTitle}}">
|
||||
<span>{{docTitle}}</span>
|
||||
</span>
|
||||
|
||||
<div id="pageselector-container">
|
||||
<viewer-page-selector id="pageselector" class="invisible"
|
||||
doc-length="{{docLength}}" page-no="{{pageNo}}"
|
||||
strings="{{strings}}">
|
||||
</viewer-page-selector>
|
||||
</div>
|
||||
|
||||
<div id="buttons" class="invisible">
|
||||
<paper-icon-button id="rotate-right" icon="pdf:rotate-right"
|
||||
on-click="rotateRight"
|
||||
aria-label$="{{strings.tooltipRotateCW}}"
|
||||
title$="{{strings.tooltipRotateCW}}">
|
||||
</paper-icon-button>
|
||||
|
||||
<paper-icon-button id="download" icon="cr:file-download"
|
||||
on-click="download"
|
||||
aria-label$="{{strings.tooltipDownload}}"
|
||||
title$="{{strings.tooltipDownload}}">
|
||||
</paper-icon-button>
|
||||
|
||||
<paper-icon-button id="print" icon="cr:print"
|
||||
on-click="print"
|
||||
aria-label$="{{strings.tooltipPrint}}"
|
||||
title$="{{strings.tooltipPrint}}">
|
||||
</paper-icon-button>
|
||||
|
||||
<viewer-toolbar-dropdown id="bookmarks"
|
||||
hidden$="[[!bookmarks.length]]"
|
||||
open-icon="pdf:bookmark"
|
||||
closed-icon="pdf:bookmark-border"
|
||||
header="{{strings.bookmarks}}">
|
||||
<viewer-bookmarks-content bookmarks="{{bookmarks}}">
|
||||
</viewer-bookmarks-content>
|
||||
</viewer-toolbar-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom fit">
|
||||
<paper-progress id="progress" value="{{loadProgress}}"></paper-progress>
|
||||
</div>
|
||||
</paper-toolbar>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script src="viewer-pdf-toolbar.js"></script>
|
|
@ -1,146 +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.
|
||||
(function() {
|
||||
Polymer({
|
||||
is: 'viewer-pdf-toolbar',
|
||||
|
||||
behaviors: [
|
||||
Polymer.NeonAnimationRunnerBehavior
|
||||
],
|
||||
|
||||
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 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
|
||||
},
|
||||
|
||||
/**
|
||||
* The number of pages in the PDF document.
|
||||
*/
|
||||
docLength: Number,
|
||||
|
||||
/**
|
||||
* Whether the toolbar is opened and visible.
|
||||
*/
|
||||
opened: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
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,29 +0,0 @@
|
|||
<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>
|
||||
<div on-click="toggleDropdown" id="icon">
|
||||
<paper-icon-button id="main-icon" icon="[[dropdownIcon]]"
|
||||
aria-label$="{{header}}" title$="{{header}}">
|
||||
</paper-icon-button>
|
||||
<iron-icon icon="cr:arrow-drop-down" id="arrow"></iron-icon>
|
||||
</div>
|
||||
|
||||
<div id="container">
|
||||
<paper-material id="dropdown" style="display: none">
|
||||
<h1>{{header}}</h1>
|
||||
<div id="scroll-container">
|
||||
<content></content>
|
||||
</div>
|
||||
</paper-material>
|
||||
</div>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script src="viewer-toolbar-dropdown.js"></script>
|
|
@ -1,138 +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.
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/** Minimum height of toolbar dropdowns (px). */
|
||||
var MIN_DROPDOWN_HEIGHT = 200;
|
||||
|
||||
Polymer({
|
||||
is: 'viewer-toolbar-dropdown',
|
||||
|
||||
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 open. */
|
||||
openIcon: String,
|
||||
|
||||
/** 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
|
||||
},
|
||||
|
||||
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();
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 = function() {
|
||||
this.animation_ = null;
|
||||
if (!this.dropdownOpen)
|
||||
this.$.dropdown.style.display = 'none';
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<link rel="import" href="chrome://resources/html/polymer.html">
|
||||
<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>
|
||||
<div id="wrapper">
|
||||
<paper-fab id="button" mini icon="[[visibleIcon_]]" on-click="fireClick"
|
||||
aria-label$="[[visibleTooltip_]]" title="[[visibleTooltip_]]">
|
||||
</paper-fab>
|
||||
</div>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script src="viewer-zoom-button.js"></script>
|
|
@ -1,95 +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.
|
||||
|
||||
Polymer({
|
||||
is: 'viewer-zoom-button',
|
||||
|
||||
properties: {
|
||||
/**
|
||||
* Icons to be displayed on the FAB. Multiple icons should be separated with
|
||||
* spaces, and will be cycled through every time the FAB is clicked.
|
||||
*/
|
||||
icons: String,
|
||||
|
||||
/**
|
||||
* Array version of the list of icons. Polymer does not allow array
|
||||
* properties to be set from HTML, so we must use a string property and
|
||||
* perform the conversion manually.
|
||||
* @private
|
||||
*/
|
||||
icons_: {
|
||||
type: Array,
|
||||
value: [''],
|
||||
computed: 'computeIconsArray_(icons)'
|
||||
},
|
||||
|
||||
tooltips: Array,
|
||||
|
||||
closed: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true,
|
||||
value: false
|
||||
},
|
||||
|
||||
delay: {
|
||||
type: Number,
|
||||
observer: 'delayChanged_'
|
||||
},
|
||||
|
||||
/**
|
||||
* Index of the icon currently being displayed.
|
||||
*/
|
||||
activeIndex: {
|
||||
type: Number,
|
||||
value: 0
|
||||
},
|
||||
|
||||
/**
|
||||
* Icon currently being displayed on the FAB.
|
||||
* @private
|
||||
*/
|
||||
visibleIcon_: {
|
||||
type: String,
|
||||
computed: 'computeVisibleIcon_(icons_, activeIndex)'
|
||||
},
|
||||
|
||||
visibleTooltip_: {
|
||||
type: String,
|
||||
computed: 'computeVisibleTooltip_(tooltips, activeIndex)'
|
||||
}
|
||||
},
|
||||
|
||||
computeIconsArray_: function(icons) {
|
||||
return icons.split(' ');
|
||||
},
|
||||
|
||||
computeVisibleIcon_: function(icons, activeIndex) {
|
||||
return icons[activeIndex];
|
||||
},
|
||||
|
||||
computeVisibleTooltip_: function(tooltips, activeIndex) {
|
||||
return tooltips[activeIndex];
|
||||
},
|
||||
|
||||
delayChanged_: function() {
|
||||
this.$.wrapper.style.transitionDelay = this.delay + 'ms';
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this.closed = false;
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.closed = true;
|
||||
},
|
||||
|
||||
fireClick: function() {
|
||||
// We cannot attach an on-click to the entire viewer-zoom-button, as this
|
||||
// will include clicks on the margins. Instead, proxy clicks on the FAB
|
||||
// through.
|
||||
this.fire('fabclick');
|
||||
|
||||
this.activeIndex = (this.activeIndex + 1) % this.icons_.length;
|
||||
}
|
||||
});
|
|
@ -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;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
<link rel="import" href="chrome://resources/html/polymer.html">
|
||||
<link rel="import" href="chrome://resources/cr_elements/icons.html">
|
||||
<link rel="import" href="../icons.html">
|
||||
<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>
|
||||
|
||||
<div id="zoom-buttons">
|
||||
<viewer-zoom-button id="fit-button" on-fabclick="fitToggle" delay="100"
|
||||
icons="pdf:fullscreen-exit cr:fullscreen">
|
||||
</viewer-zoom-button>
|
||||
<viewer-zoom-button id="zoom-in-button" icons="pdf:add"
|
||||
on-fabclick="zoomIn" delay="50"></viewer-zoom-button>
|
||||
<viewer-zoom-button id="zoom-out-button" icons="pdf:remove"
|
||||
on-fabclick="zoomOut" delay="0"></viewer-zoom-button>
|
||||
</div>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script src="viewer-zoom-toolbar.js"></script>
|
|
@ -1,75 +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.
|
||||
|
||||
(function() {
|
||||
|
||||
var FIT_TO_PAGE = 0;
|
||||
var FIT_TO_WIDTH = 1;
|
||||
|
||||
Polymer({
|
||||
is: 'viewer-zoom-toolbar',
|
||||
|
||||
properties: {
|
||||
strings: {
|
||||
type: Object,
|
||||
observer: 'updateTooltips_'
|
||||
},
|
||||
|
||||
visible_: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
}
|
||||
},
|
||||
|
||||
isVisible: function() {
|
||||
return this.visible_;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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];
|
||||
},
|
||||
|
||||
fitToggle: function() {
|
||||
if (this.$['fit-button'].activeIndex == FIT_TO_WIDTH)
|
||||
this.fire('fit-to-width');
|
||||
else
|
||||
this.fire('fit-to-page');
|
||||
},
|
||||
|
||||
zoomIn: function() {
|
||||
this.fire('zoom-in');
|
||||
},
|
||||
|
||||
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();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
})();
|
|
@ -1,50 +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. */
|
||||
|
||||
body {
|
||||
background-color: rgb(82, 86, 89);
|
||||
font-family: 'Roboto', 'Noto', sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
viewer-page-indicator {
|
||||
visibility: hidden;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
viewer-pdf-toolbar {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
#plugin {
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#sizer {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
@media(max-height: 250px) {
|
||||
viewer-pdf-toolbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-height: 200px) {
|
||||
viewer-zoom-toolbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 300px) {
|
||||
viewer-zoom-toolbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="chrome://pdf-viewer/">
|
||||
<meta charset="utf-8">
|
||||
<link rel="import" href="elements/viewer-error-screen/viewer-error-screen.html">
|
||||
<link rel="import" href="elements/viewer-page-indicator/viewer-page-indicator.html">
|
||||
<link rel="import" href="elements/viewer-page-selector/viewer-page-selector.html">
|
||||
<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="stylesheet" href="chrome://resources/css/text_defaults.css">
|
||||
<link rel="stylesheet" href="chrome://resources/css/roboto.css">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
<script src="chrome://resources/js/promise_resolver.js"></script>
|
||||
<script src="chrome://resources/js/cr.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<viewer-pdf-toolbar id="toolbar" hidden></viewer-pdf-toolbar>
|
||||
|
||||
<div id="sizer"></div>
|
||||
<viewer-password-screen id="password-screen"></viewer-password-screen>
|
||||
|
||||
<viewer-zoom-toolbar id="zoom-toolbar"></viewer-zoom-toolbar>
|
||||
|
||||
<viewer-page-indicator id="page-indicator"></viewer-page-indicator>
|
||||
|
||||
<viewer-error-screen id="error-screen"></viewer-error-screen>
|
||||
|
||||
</body>
|
||||
<script src="toolbar_manager.js"></script>
|
||||
<script src="viewport.js"></script>
|
||||
<script src="open_pdf_params_parser.js"></script>
|
||||
<script src="navigator.js"></script>
|
||||
<script src="viewport_scroller.js"></script>
|
||||
<script src="zoom_manager.js"></script>
|
||||
<script src="pdf_scripting_api.js"></script>
|
||||
<script src="chrome://resources/js/util.js"></script>
|
||||
<script src="browser_api.js"></script>
|
||||
<script src="pdf.js"></script>
|
||||
<script src="main.js"></script>
|
||||
</html>
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright 2014 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';
|
||||
|
||||
/**
|
||||
* Global PDFViewer object, accessible for testing.
|
||||
* @type Object
|
||||
*/
|
||||
var viewer;
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* Stores any pending messages received which should be passed to the
|
||||
* PDFViewer when it is created.
|
||||
* @type Array
|
||||
*/
|
||||
var pendingMessages = [];
|
||||
|
||||
/**
|
||||
* Handles events that are received prior to the PDFViewer being created.
|
||||
* @param {Object} message A message event received.
|
||||
*/
|
||||
function handleScriptingMessage(message) {
|
||||
pendingMessages.push(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the global PDFViewer and pass any outstanding messages to it.
|
||||
* @param {Object} browserApi An object providing an API to the browser.
|
||||
*/
|
||||
function initViewer(browserApi) {
|
||||
// PDFViewer will handle any messages after it is created.
|
||||
window.removeEventListener('message', handleScriptingMessage, false);
|
||||
viewer = new PDFViewer(browserApi);
|
||||
while (pendingMessages.length > 0)
|
||||
viewer.handleScriptingMessage(pendingMessages.shift());
|
||||
}
|
||||
|
||||
/**
|
||||
* Entrypoint for starting the PDF viewer. This function obtains the browser
|
||||
* API for the PDF and constructs a PDFViewer object with it.
|
||||
*/
|
||||
cr.sendWithPromise('initialize').then(function(opts) {
|
||||
// Set up an event listener to catch scripting messages which are sent prior
|
||||
// to the PDFViewer being created.
|
||||
window.addEventListener('message', handleScriptingMessage, false);
|
||||
|
||||
createBrowserApi(opts).then(initViewer);
|
||||
});
|
||||
})()
|
|
@ -1,211 +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.
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Creates a new Navigator for navigating to links inside or outside the PDF.
|
||||
* @param {string} originalUrl The original page URL.
|
||||
* @param {Object} viewport The viewport info of the page.
|
||||
* @param {Object} paramsParser The object for URL parsing.
|
||||
* @param {Function} navigateInCurrentTabCallback The Callback function that
|
||||
* gets called when navigation happens in the current tab.
|
||||
* @param {Function} navigateInNewTabCallback The Callback function
|
||||
* that gets called when navigation happens in the new tab.
|
||||
*/
|
||||
function Navigator(originalUrl,
|
||||
viewport,
|
||||
paramsParser,
|
||||
navigateInCurrentTabCallback,
|
||||
navigateInNewTabCallback) {
|
||||
this.originalUrl_ = originalUrl;
|
||||
this.viewport_ = viewport;
|
||||
this.paramsParser_ = paramsParser;
|
||||
this.navigateInCurrentTabCallback_ = navigateInCurrentTabCallback;
|
||||
this.navigateInNewTabCallback_ = navigateInNewTabCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents options when navigating to a new url. C++ counterpart of
|
||||
* the enum is in ui/base/window_open_disposition.h. This enum represents
|
||||
* the only values that are passed from Plugin.
|
||||
* @enum {number}
|
||||
*/
|
||||
Navigator.WindowOpenDisposition = {
|
||||
CURRENT_TAB: 1,
|
||||
NEW_FOREGROUND_TAB: 3,
|
||||
NEW_BACKGROUND_TAB: 4,
|
||||
NEW_WINDOW: 6,
|
||||
SAVE_TO_DISK: 7
|
||||
};
|
||||
|
||||
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.
|
||||
* @param {number} disposition The window open disposition when
|
||||
* navigating to the new URL.
|
||||
*/
|
||||
navigate: function(url, disposition) {
|
||||
if (url.length == 0)
|
||||
return;
|
||||
|
||||
// If |urlFragment| starts with '#', then it's for the same URL with a
|
||||
// different URL fragment.
|
||||
if (url.charAt(0) == '#') {
|
||||
// if '#' is already present in |originalUrl| then remove old fragment
|
||||
// and add new url fragment.
|
||||
var hashIndex = this.originalUrl_.search('#');
|
||||
if (hashIndex != -1)
|
||||
url = this.originalUrl_.substring(0, hashIndex) + url;
|
||||
else
|
||||
url = this.originalUrl_ + url;
|
||||
}
|
||||
|
||||
// If there's no scheme, then take a guess at the scheme.
|
||||
if (url.indexOf('://') == -1 && url.indexOf('mailto:') == -1)
|
||||
url = this.guessUrlWithoutScheme_(url);
|
||||
|
||||
if (!this.isValidUrl_(url))
|
||||
return;
|
||||
|
||||
switch (disposition) {
|
||||
case Navigator.WindowOpenDisposition.CURRENT_TAB:
|
||||
this.paramsParser_.getViewportFromUrlParams(
|
||||
url, this.onViewportReceived_.bind(this));
|
||||
break;
|
||||
case Navigator.WindowOpenDisposition.NEW_BACKGROUND_TAB:
|
||||
this.navigateInNewTabCallback_(url, false);
|
||||
break;
|
||||
case Navigator.WindowOpenDisposition.NEW_FOREGROUND_TAB:
|
||||
this.navigateInNewTabCallback_(url, true);
|
||||
break;
|
||||
case Navigator.WindowOpenDisposition.NEW_WINDOW:
|
||||
// TODO(jaepark): Shift + left clicking a link in PDF should open the
|
||||
// link in a new window. See http://crbug.com/628057.
|
||||
this.paramsParser_.getViewportFromUrlParams(
|
||||
url, this.onViewportReceived_.bind(this));
|
||||
break;
|
||||
case Navigator.WindowOpenDisposition.SAVE_TO_DISK:
|
||||
// TODO(jaepark): Alt + left clicking a link in PDF should
|
||||
// download the link.
|
||||
this.paramsParser_.getViewportFromUrlParams(
|
||||
url, this.onViewportReceived_.bind(this));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Called when the viewport position is received.
|
||||
* @param {Object} viewportPosition Dictionary containing the viewport
|
||||
* position.
|
||||
*/
|
||||
onViewportReceived_: function(viewportPosition) {
|
||||
var originalUrl = this.originalUrl_;
|
||||
var hashIndex = originalUrl.search('#');
|
||||
if (hashIndex != -1)
|
||||
originalUrl = originalUrl.substring(0, hashIndex);
|
||||
|
||||
var newUrl = viewportPosition.url;
|
||||
hashIndex = newUrl.search('#');
|
||||
if (hashIndex != -1)
|
||||
newUrl = newUrl.substring(0, hashIndex);
|
||||
|
||||
var pageNumber = viewportPosition.page;
|
||||
if (pageNumber != undefined && originalUrl == newUrl)
|
||||
this.viewport_.goToPage(pageNumber);
|
||||
else
|
||||
this.navigateInCurrentTabCallback_(viewportPosition.url);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Checks if the URL starts with a scheme and s not just a scheme.
|
||||
* @param {string} The input URL
|
||||
* @return {boolean} Whether the url is valid.
|
||||
*/
|
||||
isValidUrl_: function(url) {
|
||||
// Make sure |url| starts with a valid scheme.
|
||||
if (url.indexOf('http://') != 0 &&
|
||||
url.indexOf('https://') != 0 &&
|
||||
url.indexOf('ftp://') != 0 &&
|
||||
url.indexOf('file://') != 0 &&
|
||||
url.indexOf('mailto:') != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure |url| is not only a scheme.
|
||||
if (url == 'http://' ||
|
||||
url == 'https://' ||
|
||||
url == 'ftp://' ||
|
||||
url == 'file://' ||
|
||||
url == 'mailto:') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Attempt to figure out what a URL is when there is no scheme.
|
||||
* @param {string} The input URL
|
||||
* @return {string} The URL with a scheme or the original URL if it is not
|
||||
* possible to determine the scheme.
|
||||
*/
|
||||
guessUrlWithoutScheme_: function(url) {
|
||||
// If the original URL is mailto:, that does not make sense to start with,
|
||||
// and neither does adding |url| to it.
|
||||
// If the original URL is not a valid URL, this cannot make a valid URL.
|
||||
// In both cases, just bail out.
|
||||
if (this.originalUrl_.startsWith('mailto:') ||
|
||||
!this.isValidUrl_(this.originalUrl_)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Check for absolute paths.
|
||||
if (url.startsWith('/')) {
|
||||
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_;
|
||||
return domain + url;
|
||||
}
|
||||
|
||||
// Check for obvious relative paths.
|
||||
var isRelative = false;
|
||||
if (url.startsWith('.') || url.startsWith('\\'))
|
||||
isRelative = true;
|
||||
|
||||
// In Adobe Acrobat Reader XI, it looks as though links with less than
|
||||
// 2 dot separators in the domain are considered relative links, and
|
||||
// those with 2 of more are considered http URLs. e.g.
|
||||
//
|
||||
// www.foo.com/bar -> http
|
||||
// foo.com/bar -> relative link
|
||||
if (!isRelative) {
|
||||
var domainSeparatorIndex = url.indexOf('/');
|
||||
var domainName = domainSeparatorIndex == -1 ?
|
||||
url : url.substr(0, domainSeparatorIndex);
|
||||
var domainDotCount = (domainName.match(/\./g) || []).length;
|
||||
if (domainDotCount < 2)
|
||||
isRelative = true;
|
||||
}
|
||||
|
||||
if (isRelative) {
|
||||
var slashIndex = this.originalUrl_.lastIndexOf('/');
|
||||
var path = slashIndex != -1 ?
|
||||
this.originalUrl_.substr(0, slashIndex) : this.originalUrl_;
|
||||
return path + '/' + url;
|
||||
}
|
||||
|
||||
return 'http://' + url;
|
||||
}
|
||||
};
|
|
@ -1,147 +0,0 @@
|
|||
// Copyright 2014 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';
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the page number for a named destination.
|
||||
*/
|
||||
function OpenPDFParamsParser(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.
|
||||
*/
|
||||
parseZoomParam_: function(paramValue, viewportPosition) {
|
||||
var paramValueSplit = paramValue.split(',');
|
||||
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;
|
||||
|
||||
// Handle #zoom=scale.
|
||||
if (paramValueSplit.length == 1) {
|
||||
viewportPosition['zoom'] = zoomFactor;
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle #zoom=scale,left,top.
|
||||
var position = {x: parseFloat(paramValueSplit[1]),
|
||||
y: parseFloat(paramValueSplit[2])};
|
||||
viewportPosition['position'] = position;
|
||||
viewportPosition['zoom'] = zoomFactor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the parameters encoded in the fragment of a URL into a dictionary.
|
||||
* @private
|
||||
* @param {string} url to parse
|
||||
* @return {Object} Key-value pairs of URL parameters
|
||||
*/
|
||||
parseUrlParams_: function(url) {
|
||||
var params = {};
|
||||
|
||||
var paramIndex = url.search('#');
|
||||
if (paramIndex == -1)
|
||||
return params;
|
||||
|
||||
var paramTokens = url.substring(paramIndex + 1).split('&');
|
||||
if ((paramTokens.length == 1) && (paramTokens[0].search('=') == -1)) {
|
||||
// Handle the case of http://foo.com/bar#NAMEDDEST. This is not
|
||||
// explicitly mentioned except by example in the Adobe
|
||||
// "PDF Open Parameters" document.
|
||||
params['nameddest'] = paramTokens[0];
|
||||
return params;
|
||||
}
|
||||
|
||||
for (var i = 0; i < paramTokens.length; ++i) {
|
||||
var keyValueSplit = paramTokens[i].split('=');
|
||||
if (keyValueSplit.length != 2)
|
||||
continue;
|
||||
params[keyValueSplit[0]] = keyValueSplit[1];
|
||||
}
|
||||
|
||||
return params;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse PDF url parameters used for controlling the state of UI. These need
|
||||
* to be available when the UI is being initialized, rather than when the PDF
|
||||
* is finished loading.
|
||||
* @param {string} url that needs to be parsed.
|
||||
* @return {Object} parsed url parameters.
|
||||
*/
|
||||
getUiUrlParams: function(url) {
|
||||
var params = this.parseUrlParams_(url);
|
||||
var uiParams = {toolbar: true};
|
||||
|
||||
if ('toolbar' in params && params['toolbar'] == 0)
|
||||
uiParams.toolbar = false;
|
||||
|
||||
return uiParams;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse PDF url parameters. These parameters are mentioned in the url
|
||||
* and specify actions to be performed when opening pdf files.
|
||||
* See http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/
|
||||
* pdfs/pdf_open_parameters.pdf for details.
|
||||
* @param {string} url that needs to be parsed.
|
||||
* @param {Function} callback function to be called with viewport info.
|
||||
*/
|
||||
getViewportFromUrlParams: function(url, callback) {
|
||||
var viewportPosition = {};
|
||||
viewportPosition['url'] = url;
|
||||
|
||||
var paramsDictionary = this.parseUrlParams_(url);
|
||||
|
||||
if ('page' in paramsDictionary) {
|
||||
// |pageNumber| is 1-based, but goToPage() take a zero-based page number.
|
||||
var pageNumber = parseInt(paramsDictionary['page']);
|
||||
if (!isNaN(pageNumber) && pageNumber > 0)
|
||||
viewportPosition['page'] = pageNumber - 1;
|
||||
}
|
||||
|
||||
if ('zoom' in paramsDictionary)
|
||||
this.parseZoomParam_(paramsDictionary['zoom'], viewportPosition);
|
||||
|
||||
if (viewportPosition.page === undefined &&
|
||||
'nameddest' in paramsDictionary) {
|
||||
this.outstandingRequests_.push({
|
||||
callback: callback,
|
||||
viewportPosition: viewportPosition
|
||||
});
|
||||
this.getNamedDestinationsFunction_(paramsDictionary['nameddest']);
|
||||
} else {
|
||||
callback(viewportPosition);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This is called when a named destination is received and the page number
|
||||
* corresponding to the request for which a named destination is passed.
|
||||
* @param {number} pageNumber The page corresponding to the named destination
|
||||
* requested.
|
||||
*/
|
||||
onNamedDestinationReceived: function(pageNumber) {
|
||||
var outstandingRequest = this.outstandingRequests_.shift();
|
||||
if (pageNumber != -1)
|
||||
outstandingRequest.viewportPosition.page = pageNumber;
|
||||
outstandingRequest.callback(outstandingRequest.viewportPosition);
|
||||
},
|
||||
};
|
|
@ -1,871 +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.
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @return {number} Width of a scrollbar in pixels
|
||||
*/
|
||||
function getScrollbarWidth() {
|
||||
var div = document.createElement('div');
|
||||
div.style.visibility = 'hidden';
|
||||
div.style.overflow = 'scroll';
|
||||
div.style.width = '50px';
|
||||
div.style.height = '50px';
|
||||
div.style.position = 'absolute';
|
||||
document.body.appendChild(div);
|
||||
var result = div.offsetWidth - div.clientWidth;
|
||||
div.parentNode.removeChild(div);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filename component of a URL, percent decoded if possible.
|
||||
* @param {string} url The URL to get the filename from.
|
||||
* @return {string} The filename component.
|
||||
*/
|
||||
function getFilenameFromURL(url) {
|
||||
// Ignore the query and fragment.
|
||||
var mainUrl = url.split(/#|\?/)[0];
|
||||
var components = mainUrl.split(/\/|\\/);
|
||||
var filename = components[components.length - 1];
|
||||
try {
|
||||
return decodeURIComponent(filename);
|
||||
} catch (e) {
|
||||
if (e instanceof URIError)
|
||||
return filename;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when navigation happens in the current tab.
|
||||
* @param {string} url The url to be opened in the current tab.
|
||||
*/
|
||||
function onNavigateInCurrentTab(url) {
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when navigation happens in the new tab.
|
||||
* @param {string} url The url to be opened in the new tab.
|
||||
*/
|
||||
function onNavigateInNewTab(url) {
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether keydown events should currently be ignored. Events are ignored when
|
||||
* an editable element has focus, to allow for proper editing controls.
|
||||
* @param {HTMLElement} activeElement The currently selected DOM node.
|
||||
* @return {boolean} True if keydown events should be ignored.
|
||||
*/
|
||||
function shouldIgnoreKeyEvents(activeElement) {
|
||||
while (activeElement.shadowRoot != null &&
|
||||
activeElement.shadowRoot.activeElement != null) {
|
||||
activeElement = activeElement.shadowRoot.activeElement;
|
||||
}
|
||||
|
||||
return (activeElement.isContentEditable ||
|
||||
activeElement.tagName == 'INPUT' ||
|
||||
activeElement.tagName == 'TEXTAREA');
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum number of pixels to offset the toolbar by from the bottom and
|
||||
* right side of the screen.
|
||||
*/
|
||||
PDFViewer.MIN_TOOLBAR_OFFSET = 15;
|
||||
|
||||
/**
|
||||
* The height of the toolbar along the top of the page. The document will be
|
||||
* shifted down by this much in the viewport.
|
||||
*/
|
||||
PDFViewer.MATERIAL_TOOLBAR_HEIGHT = 56;
|
||||
|
||||
/**
|
||||
* Minimum height for the material toolbar to show (px). Should match the media
|
||||
* query in index-material.css. If the window is smaller than this at load,
|
||||
* leave no space for the toolbar.
|
||||
*/
|
||||
PDFViewer.TOOLBAR_WINDOW_MIN_HEIGHT = 250;
|
||||
|
||||
/**
|
||||
* The light-gray background color used for print preview.
|
||||
*/
|
||||
PDFViewer.LIGHT_BACKGROUND_COLOR = '0xFFCCCCCC';
|
||||
|
||||
/**
|
||||
* The dark-gray background color used for the regular viewer.
|
||||
*/
|
||||
PDFViewer.DARK_BACKGROUND_COLOR = '0xFF525659';
|
||||
|
||||
/**
|
||||
* Creates a new PDFViewer. There should only be one of these objects per
|
||||
* document.
|
||||
* @constructor
|
||||
* @param {!BrowserApi} browserApi An object providing an API to the browser.
|
||||
*/
|
||||
function PDFViewer(browserApi) {
|
||||
this.browserApi_ = browserApi;
|
||||
this.originalUrl_ = this.browserApi_.getStreamInfo().originalUrl;
|
||||
this.loadState_ = LoadState.LOADING;
|
||||
this.parentWindow_ = null;
|
||||
this.parentOrigin_ = null;
|
||||
this.isFormFieldFocused_ = false;
|
||||
|
||||
this.delayedScriptingMessages_ = [];
|
||||
|
||||
this.isPrintPreview_ = this.originalUrl_.indexOf('chrome://print') == 0;
|
||||
|
||||
// Parse open pdf parameters.
|
||||
this.paramsParser_ =
|
||||
new OpenPDFParamsParser(this.getNamedDestination_.bind(this));
|
||||
var toolbarEnabled =
|
||||
this.paramsParser_.getUiUrlParams(this.originalUrl_).toolbar &&
|
||||
!this.isPrintPreview_;
|
||||
|
||||
// The sizer element is placed behind the plugin element to cause scrollbars
|
||||
// to be displayed in the window. It is sized according to the document size
|
||||
// of the pdf and zoom level.
|
||||
this.sizer_ = $('sizer');
|
||||
if (this.isPrintPreview_)
|
||||
this.pageIndicator_ = $('page-indicator');
|
||||
this.passwordScreen_ = $('password-screen');
|
||||
this.passwordScreen_.addEventListener('password-submitted',
|
||||
this.onPasswordSubmitted_.bind(this));
|
||||
this.errorScreen_ = $('error-screen');
|
||||
// Can only reload if we are in a normal tab.
|
||||
if (!this.browserApi_.getStreamInfo().embedded) {
|
||||
this.errorScreen_.reloadFn = function() {
|
||||
chrome.send('reload');
|
||||
};
|
||||
}
|
||||
|
||||
// Create the viewport.
|
||||
var shortWindow = window.innerHeight < PDFViewer.TOOLBAR_WINDOW_MIN_HEIGHT;
|
||||
var topToolbarHeight =
|
||||
(toolbarEnabled) ? PDFViewer.MATERIAL_TOOLBAR_HEIGHT : 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);
|
||||
|
||||
// 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
|
||||
// positioning, acting as a viewport. The plugin renders into this viewport
|
||||
// according to the scroll position of the window.
|
||||
this.plugin_ = document.createElement('embed');
|
||||
// NOTE: The plugin's 'id' field must be set to 'plugin' since
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
this.plugin_.setAttribute('src', this.originalUrl_);
|
||||
this.plugin_.setAttribute('stream-url',
|
||||
this.browserApi_.getStreamInfo().streamUrl);
|
||||
var headers = '';
|
||||
for (var header in this.browserApi_.getStreamInfo().responseHeaders) {
|
||||
headers += header + ': ' +
|
||||
this.browserApi_.getStreamInfo().responseHeaders[header] + '\n';
|
||||
}
|
||||
this.plugin_.setAttribute('headers', headers);
|
||||
|
||||
var backgroundColor = PDFViewer.DARK_BACKGROUND_COLOR;
|
||||
this.plugin_.setAttribute('background-color', backgroundColor);
|
||||
this.plugin_.setAttribute('top-toolbar-height', topToolbarHeight);
|
||||
|
||||
if (!this.browserApi_.getStreamInfo().embedded)
|
||||
this.plugin_.setAttribute('full-frame', '');
|
||||
this.sizer_.appendChild(this.plugin_);
|
||||
|
||||
// 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_));
|
||||
|
||||
if (toolbarEnabled) {
|
||||
this.toolbar_ = $('toolbar');
|
||||
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));
|
||||
// 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.toolbar_.docTitle = getFilenameFromURL(this.originalUrl_);
|
||||
}
|
||||
|
||||
document.body.addEventListener('change-page', function(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;
|
||||
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_),
|
||||
this.browserApi_.getInitialZoom());
|
||||
this.browserApi_.addZoomEventListener(
|
||||
this.zoomManager_.onBrowserZoomChange.bind(this.zoomManager_));
|
||||
|
||||
// Setup the keyboard event listener.
|
||||
document.addEventListener('keydown', this.handleKeyEvent_.bind(this));
|
||||
document.addEventListener('mousemove', this.handleMouseEvent_.bind(this));
|
||||
document.addEventListener('mouseout', this.handleMouseEvent_.bind(this));
|
||||
|
||||
this.navigator_ = new Navigator(this.originalUrl_,
|
||||
this.viewport_, this.paramsParser_,
|
||||
onNavigateInCurrentTab,
|
||||
onNavigateInNewTab);
|
||||
this.viewportScroller_ =
|
||||
new ViewportScroller(this.viewport_, this.plugin_, window);
|
||||
|
||||
// Request translated strings.
|
||||
//chrome.resourcesPrivate.getStrings('pdf', this.handleStrings_.bind(this));
|
||||
}
|
||||
|
||||
PDFViewer.prototype = {
|
||||
/**
|
||||
* @private
|
||||
* Handle key events. These may come from the user directly or via the
|
||||
* scripting API.
|
||||
* @param {KeyboardEvent} e the event to handle.
|
||||
*/
|
||||
handleKeyEvent_: function(e) {
|
||||
var position = this.viewport_.position;
|
||||
// Certain scroll events may be sent from outside of the extension.
|
||||
var fromScriptingAPI = e.fromScriptingAPI;
|
||||
|
||||
if (shouldIgnoreKeyEvents(document.activeElement) || e.defaultPrevented)
|
||||
return;
|
||||
|
||||
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) {
|
||||
this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
|
||||
// Since we do the movement of the page.
|
||||
e.preventDefault();
|
||||
} else if (fromScriptingAPI) {
|
||||
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) {
|
||||
this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
|
||||
// Since we do the movement of the page.
|
||||
e.preventDefault();
|
||||
} else if (fromScriptingAPI) {
|
||||
position.y += this.viewport.size.height;
|
||||
this.viewport.position = position;
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 9: // Tab key.
|
||||
this.toolbarManager_.showToolbarsForKeyboardNavigation();
|
||||
return;
|
||||
case 27: // Escape key.
|
||||
if (!this.isPrintPreview_) {
|
||||
this.toolbarManager_.hideSingleToolbarLayer();
|
||||
return;
|
||||
}
|
||||
break; // Ensure escape falls through to the print-preview handler.
|
||||
case 32: // Space key.
|
||||
if (e.shiftKey)
|
||||
pageUpHandler();
|
||||
else
|
||||
pageDownHandler();
|
||||
return;
|
||||
case 33: // Page up key.
|
||||
pageUpHandler();
|
||||
return;
|
||||
case 34: // Page down key.
|
||||
pageDownHandler();
|
||||
return;
|
||||
case 37: // Left arrow key.
|
||||
if (!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
|
||||
// Go to the previous page if there are no horizontal scrollbars and
|
||||
// no form field is focused.
|
||||
if (!(this.viewport_.documentHasScrollbars().horizontal ||
|
||||
this.isFormFieldFocused_)) {
|
||||
this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
|
||||
// Since we do the movement of the page.
|
||||
e.preventDefault();
|
||||
} else if (fromScriptingAPI) {
|
||||
position.x -= Viewport.SCROLL_INCREMENT;
|
||||
this.viewport.position = position;
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 38: // Up arrow key.
|
||||
if (fromScriptingAPI) {
|
||||
position.y -= Viewport.SCROLL_INCREMENT;
|
||||
this.viewport.position = position;
|
||||
}
|
||||
return;
|
||||
case 39: // Right arrow key.
|
||||
if (!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
|
||||
// Go to the next page if there are no horizontal scrollbars and no
|
||||
// form field is focused.
|
||||
if (!(this.viewport_.documentHasScrollbars().horizontal ||
|
||||
this.isFormFieldFocused_)) {
|
||||
this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
|
||||
// Since we do the movement of the page.
|
||||
e.preventDefault();
|
||||
} else if (fromScriptingAPI) {
|
||||
position.x += Viewport.SCROLL_INCREMENT;
|
||||
this.viewport.position = position;
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 40: // Down arrow key.
|
||||
if (fromScriptingAPI) {
|
||||
position.y += Viewport.SCROLL_INCREMENT;
|
||||
this.viewport.position = position;
|
||||
}
|
||||
return;
|
||||
case 65: // a key.
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
this.plugin_.postMessage({
|
||||
type: 'selectAll'
|
||||
});
|
||||
// Since we do selection ourselves.
|
||||
e.preventDefault();
|
||||
}
|
||||
return;
|
||||
case 71: // g key.
|
||||
if (this.toolbar_ && (e.ctrlKey || e.metaKey) && e.altKey) {
|
||||
this.toolbarManager_.showToolbars();
|
||||
this.toolbar_.selectPageNumber();
|
||||
}
|
||||
return;
|
||||
case 219: // left bracket.
|
||||
if (e.ctrlKey)
|
||||
this.rotateCounterClockwise_();
|
||||
return;
|
||||
case 221: // right bracket.
|
||||
if (e.ctrlKey)
|
||||
this.rotateClockwise_();
|
||||
return;
|
||||
}
|
||||
|
||||
// Give print preview a chance to handle the key event.
|
||||
if (!fromScriptingAPI && this.isPrintPreview_) {
|
||||
this.sendScriptingMessage_({
|
||||
type: 'sendKeyEvent',
|
||||
keyEvent: SerializeKeyEvent(e)
|
||||
});
|
||||
} else {
|
||||
// Show toolbars as a fallback.
|
||||
if (!(e.shiftKey || e.ctrlKey || e.altKey))
|
||||
this.toolbarManager_.showToolbars();
|
||||
}
|
||||
},
|
||||
|
||||
handleMouseEvent_: function(e) {
|
||||
if (e.type == 'mousemove')
|
||||
this.toolbarManager_.handleMouseMove(e);
|
||||
else if (e.type == 'mouseout')
|
||||
this.toolbarManager_.hideToolbarsForMouseOut();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Rotate the plugin clockwise.
|
||||
*/
|
||||
rotateClockwise_: function() {
|
||||
this.plugin_.postMessage({
|
||||
type: 'rotateClockwise'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Rotate the plugin counter-clockwise.
|
||||
*/
|
||||
rotateCounterClockwise_: function() {
|
||||
this.plugin_.postMessage({
|
||||
type: 'rotateCounterclockwise'
|
||||
});
|
||||
},
|
||||
|
||||
fitToPage_: function() {
|
||||
this.viewport_.fitToPage();
|
||||
this.toolbarManager_.forceHideTopToolbar();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Notify the plugin to print.
|
||||
*/
|
||||
print_: function() {
|
||||
this.plugin_.postMessage({
|
||||
type: 'print'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Notify the plugin to save.
|
||||
*/
|
||||
save_: function() {
|
||||
this.plugin_.postMessage({
|
||||
type: 'save'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches the page number corresponding to the given named destination from
|
||||
* the plugin.
|
||||
* @param {string} name The namedDestination to fetch page number from plugin.
|
||||
*/
|
||||
getNamedDestination_: function(name) {
|
||||
this.plugin_.postMessage({
|
||||
type: 'getNamedDestination',
|
||||
namedDestination: name
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Sends a 'documentLoaded' message to the PDFScriptingAPI if the document has
|
||||
* finished loading.
|
||||
*/
|
||||
sendDocumentLoadedMessage_: function() {
|
||||
if (this.loadState_ == LoadState.LOADING)
|
||||
return;
|
||||
this.sendScriptingMessage_({
|
||||
type: 'documentLoaded',
|
||||
load_state: this.loadState_
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* 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.
|
||||
*/
|
||||
handleURLParams_: function(viewportPosition) {
|
||||
if (viewportPosition.page != undefined)
|
||||
this.viewport_.goToPage(viewportPosition.page);
|
||||
if (viewportPosition.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
|
||||
};
|
||||
}
|
||||
if (viewportPosition.zoom)
|
||||
this.viewport_.setZoom(viewportPosition.zoom);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Update the loading progress of the document in response to a progress
|
||||
* message being received from the plugin.
|
||||
* @param {number} progress the progress as a percentage.
|
||||
*/
|
||||
updateProgress_: function(progress) {
|
||||
if (this.toolbar_)
|
||||
this.toolbar_.loadProgress = progress;
|
||||
|
||||
if (progress == -1) {
|
||||
// Document load failed.
|
||||
this.errorScreen_.show();
|
||||
this.sizer_.style.display = 'none';
|
||||
if (this.passwordScreen_.active) {
|
||||
this.passwordScreen_.deny();
|
||||
this.passwordScreen_.active = false;
|
||||
}
|
||||
this.loadState_ = LoadState.FAILED;
|
||||
this.sendDocumentLoadedMessage_();
|
||||
} else if (progress == 100) {
|
||||
// Document load complete.
|
||||
if (this.lastViewportPosition_)
|
||||
this.viewport_.position = this.lastViewportPosition_;
|
||||
this.paramsParser_.getViewportFromUrlParams(
|
||||
this.originalUrl_,
|
||||
this.handleURLParams_.bind(this));
|
||||
this.loadState_ = LoadState.SUCCESS;
|
||||
this.sendDocumentLoadedMessage_();
|
||||
while (this.delayedScriptingMessages_.length > 0)
|
||||
this.handleScriptingMessage(this.delayedScriptingMessages_.shift());
|
||||
|
||||
this.toolbarManager_.hideToolbarsAfterTimeout();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Load a dictionary of translated strings into the UI. Used as a callback for
|
||||
* chrome.resourcesPrivate.
|
||||
* @param {Object} strings Dictionary of translated strings
|
||||
*/
|
||||
handleStrings_: function(strings) {
|
||||
document.documentElement.dir = strings.textdirection;
|
||||
document.documentElement.lang = strings.language;
|
||||
|
||||
$('toolbar').strings = strings;
|
||||
$('zoom-toolbar').strings = strings;
|
||||
$('password-screen').strings = strings;
|
||||
$('error-screen').strings = strings;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* An event handler for handling password-submitted events. These are fired
|
||||
* when an event is entered into the password screen.
|
||||
* @param {Object} event a password-submitted event.
|
||||
*/
|
||||
onPasswordSubmitted_: function(event) {
|
||||
this.plugin_.postMessage({
|
||||
type: 'getPasswordComplete',
|
||||
password: event.detail.password
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* An event handler for handling message events received from the plugin.
|
||||
* @param {MessageObject} message a message event.
|
||||
*/
|
||||
handlePluginMessage_: function(message) {
|
||||
switch (message.data.type.toString()) {
|
||||
case 'documentDimensions':
|
||||
this.documentDimensions_ = message.data;
|
||||
this.viewport_.setDocumentDimensions(this.documentDimensions_);
|
||||
// If we received the document dimensions, the password was good so we
|
||||
// can dismiss the password screen.
|
||||
if (this.passwordScreen_.active)
|
||||
this.passwordScreen_.accept();
|
||||
|
||||
if (this.pageIndicator_)
|
||||
this.pageIndicator_.initialFadeIn();
|
||||
|
||||
if (this.toolbar_) {
|
||||
this.toolbar_.docLength =
|
||||
this.documentDimensions_.pageDimensions.length;
|
||||
}
|
||||
break;
|
||||
case 'email':
|
||||
var href = 'mailto:' + message.data.to + '?cc=' + message.data.cc +
|
||||
'&bcc=' + message.data.bcc + '&subject=' + message.data.subject +
|
||||
'&body=' + message.data.body;
|
||||
window.location.href = href;
|
||||
break;
|
||||
case 'getPassword':
|
||||
// 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;
|
||||
else
|
||||
this.passwordScreen_.deny();
|
||||
break;
|
||||
case 'getSelectedTextReply':
|
||||
this.sendScriptingMessage_(message.data);
|
||||
break;
|
||||
case 'goToPage':
|
||||
this.viewport_.goToPage(message.data.page);
|
||||
break;
|
||||
case 'loadProgress':
|
||||
this.updateProgress_(message.data.progress);
|
||||
break;
|
||||
case 'navigate':
|
||||
// If in print preview, always open a new tab.
|
||||
if (this.isPrintPreview_) {
|
||||
this.navigator_.navigate(
|
||||
message.data.url,
|
||||
Navigator.WindowOpenDisposition.NEW_BACKGROUND_TAB);
|
||||
} else {
|
||||
this.navigator_.navigate(message.data.url, message.data.disposition);
|
||||
}
|
||||
break;
|
||||
case 'setScrollPosition':
|
||||
var position = this.viewport_.position;
|
||||
if (message.data.x !== undefined)
|
||||
position.x = message.data.x;
|
||||
if (message.data.y !== undefined)
|
||||
position.y = message.data.y;
|
||||
this.viewport_.position = position;
|
||||
break;
|
||||
case 'cancelStreamUrl':
|
||||
//chrome.mimeHandlerPrivate.abortStream();
|
||||
break;
|
||||
case 'metadata':
|
||||
if (message.data.title) {
|
||||
document.title = message.data.title;
|
||||
} else {
|
||||
document.title = getFilenameFromURL(this.originalUrl_);
|
||||
}
|
||||
this.bookmarks_ = message.data.bookmarks;
|
||||
if (this.toolbar_) {
|
||||
this.toolbar_.docTitle = document.title;
|
||||
this.toolbar_.bookmarks = this.bookmarks;
|
||||
}
|
||||
break;
|
||||
case 'setIsSelecting':
|
||||
this.viewportScroller_.setEnableScrolling(message.data.isSelecting);
|
||||
break;
|
||||
case 'getNamedDestinationReply':
|
||||
this.paramsParser_.onNamedDestinationReceived(message.data.pageNumber);
|
||||
break;
|
||||
case 'formFocusChange':
|
||||
this.isFormFieldFocused_ = message.data.focused;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* A callback that's called before the zoom changes. Notify the plugin to stop
|
||||
* reacting to scroll events while zoom is taking place to avoid flickering.
|
||||
*/
|
||||
beforeZoom_: function() {
|
||||
this.plugin_.postMessage({
|
||||
type: 'stopScrolling'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* A callback that's called after the zoom changes. Notify the plugin of the
|
||||
* zoom change and to continue reacting to scroll events.
|
||||
*/
|
||||
afterZoom_: function() {
|
||||
var position = this.viewport_.position;
|
||||
var zoom = this.viewport_.zoom;
|
||||
this.plugin_.postMessage({
|
||||
type: 'viewport',
|
||||
zoom: zoom,
|
||||
xOffset: position.x,
|
||||
yOffset: position.y
|
||||
});
|
||||
this.zoomManager_.onPdfZoomChange();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* A callback that's called after the viewport changes.
|
||||
*/
|
||||
viewportChanged_: function() {
|
||||
if (!this.documentDimensions_)
|
||||
return;
|
||||
|
||||
// Offset the toolbar position so that it doesn't move if scrollbars appear.
|
||||
var hasScrollbars = this.viewport_.documentHasScrollbars();
|
||||
var scrollbarWidth = this.viewport_.scrollbarWidth;
|
||||
var verticalScrollbarWidth = hasScrollbars.vertical ? scrollbarWidth : 0;
|
||||
var horizontalScrollbarWidth =
|
||||
hasScrollbars.horizontal ? scrollbarWidth : 0;
|
||||
|
||||
// Shift the zoom toolbar to the left by half a scrollbar width. This
|
||||
// gives a compromise: if there is no scrollbar visible then the toolbar
|
||||
// will be half a scrollbar width further left than the spec but if there
|
||||
// is a scrollbar visible it will be half a scrollbar width further right
|
||||
// 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';
|
||||
}
|
||||
// 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
|
||||
// that when there is a scrollbar visible, it will be a full scrollbar
|
||||
// width closer to the bottom of the screen than usual, but this is ok.
|
||||
this.zoomToolbar_.style.bottom = -horizontalScrollbarWidth + 'px';
|
||||
|
||||
// Update the page indicator.
|
||||
var visiblePage = this.viewport_.getMostVisiblePage();
|
||||
|
||||
if (this.toolbar_)
|
||||
this.toolbar_.pageNo = visiblePage + 1;
|
||||
|
||||
// TODO(raymes): Give pageIndicator_ the same API as toolbar_.
|
||||
if (this.pageIndicator_) {
|
||||
this.pageIndicator_.index = visiblePage;
|
||||
if (this.documentDimensions_.pageDimensions.length > 1 &&
|
||||
hasScrollbars.vertical) {
|
||||
this.pageIndicator_.style.visibility = 'visible';
|
||||
} else {
|
||||
this.pageIndicator_.style.visibility = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
var visiblePageDimensions = this.viewport_.getPageScreenRect(visiblePage);
|
||||
var size = this.viewport_.size;
|
||||
this.sendScriptingMessage_({
|
||||
type: 'viewport',
|
||||
pageX: visiblePageDimensions.x,
|
||||
pageY: visiblePageDimensions.y,
|
||||
pageWidth: visiblePageDimensions.width,
|
||||
viewportWidth: size.width,
|
||||
viewportHeight: size.height
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a scripting message from outside the extension (typically sent by
|
||||
* PDFScriptingAPI in a page containing the extension) to interact with the
|
||||
* plugin.
|
||||
* @param {MessageObject} message the message to handle.
|
||||
*/
|
||||
handleScriptingMessage: function(message) {
|
||||
if (this.parentWindow_ != message.source) {
|
||||
this.parentWindow_ = message.source;
|
||||
this.parentOrigin_ = message.origin;
|
||||
// Ensure that we notify the embedder if the document is loaded.
|
||||
if (this.loadState_ != LoadState.LOADING)
|
||||
this.sendDocumentLoadedMessage_();
|
||||
}
|
||||
|
||||
if (this.handlePrintPreviewScriptingMessage_(message))
|
||||
return;
|
||||
|
||||
// Delay scripting messages from users of the scripting API until the
|
||||
// document is loaded. This simplifies use of the APIs.
|
||||
if (this.loadState_ != LoadState.SUCCESS) {
|
||||
this.delayedScriptingMessages_.push(message);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.data.type.toString()) {
|
||||
case 'getSelectedText':
|
||||
case 'print':
|
||||
case 'selectAll':
|
||||
this.plugin_.postMessage(message.data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Handle scripting messages specific to print preview.
|
||||
* @param {MessageObject} message the message to handle.
|
||||
* @return {boolean} true if the message was handled, false otherwise.
|
||||
*/
|
||||
handlePrintPreviewScriptingMessage_: function(message) {
|
||||
if (!this.isPrintPreview_)
|
||||
return false;
|
||||
|
||||
switch (message.data.type.toString()) {
|
||||
case 'loadPreviewPage':
|
||||
this.plugin_.postMessage(message.data);
|
||||
return true;
|
||||
case 'resetPrintPreviewMode':
|
||||
this.loadState_ = LoadState.LOADING;
|
||||
if (!this.inPrintPreviewMode_) {
|
||||
this.inPrintPreviewMode_ = true;
|
||||
this.viewport_.fitToPage();
|
||||
}
|
||||
|
||||
// Stash the scroll location so that it can be restored when the new
|
||||
// document is loaded.
|
||||
this.lastViewportPosition_ = this.viewport_.position;
|
||||
|
||||
// TODO(raymes): Disable these properly in the plugin.
|
||||
var printButton = $('print-button');
|
||||
if (printButton)
|
||||
printButton.parentNode.removeChild(printButton);
|
||||
var saveButton = $('save-button');
|
||||
if (saveButton)
|
||||
saveButton.parentNode.removeChild(saveButton);
|
||||
|
||||
this.pageIndicator_.pageLabels = message.data.pageNumbers;
|
||||
|
||||
this.plugin_.postMessage({
|
||||
type: 'resetPrintPreviewMode',
|
||||
url: message.data.url,
|
||||
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)
|
||||
});
|
||||
return true;
|
||||
case 'sendKeyEvent':
|
||||
this.handleKeyEvent_(DeserializeKeyEvent(message.data.keyEvent));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Send a scripting message outside the extension (typically to
|
||||
* PDFScriptingAPI in a page containing the extension).
|
||||
* @param {Object} message the message to send.
|
||||
*/
|
||||
sendScriptingMessage_: function(message) {
|
||||
if (this.parentWindow_ && this.parentOrigin_) {
|
||||
var targetOrigin;
|
||||
// Only send data back to the embedder if it is from the same origin,
|
||||
// unless we're sending it to ourselves (which could happen in the case
|
||||
// of tests). We also allow documentLoaded messages through as this won't
|
||||
// leak important information.
|
||||
if (this.parentOrigin_ == window.location.origin)
|
||||
targetOrigin = this.parentOrigin_;
|
||||
else if (message.type == 'documentLoaded')
|
||||
targetOrigin = '*';
|
||||
else
|
||||
targetOrigin = this.originalUrl_;
|
||||
this.parentWindow_.postMessage(message, targetOrigin);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {Viewport} the viewport of the PDF viewer.
|
||||
*/
|
||||
get viewport() {
|
||||
return this.viewport_;
|
||||
},
|
||||
|
||||
/**
|
||||
* Each bookmark is an Object containing a:
|
||||
* - title
|
||||
* - page (optional)
|
||||
* - array of children (themselves bookmarks)
|
||||
* @type {Array} the top-level bookmarks of the PDF.
|
||||
*/
|
||||
get bookmarks() {
|
||||
return this.bookmarks_;
|
||||
}
|
||||
};
|
|
@ -1,262 +0,0 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
/**
|
||||
* Turn a dictionary received from postMessage into a key event.
|
||||
* @param {Object} dict A dictionary representing the key event.
|
||||
* @return {Event} A key event.
|
||||
*/
|
||||
function DeserializeKeyEvent(dict) {
|
||||
var e = document.createEvent('Event');
|
||||
e.initEvent('keydown');
|
||||
e.keyCode = dict.keyCode;
|
||||
e.shiftKey = dict.shiftKey;
|
||||
e.ctrlKey = dict.ctrlKey;
|
||||
e.altKey = dict.altKey;
|
||||
e.metaKey = dict.metaKey;
|
||||
e.fromScriptingAPI = true;
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a key event into a dictionary which can be sent over postMessage.
|
||||
* @param {Event} event A key event.
|
||||
* @return {Object} A dictionary representing the key event.
|
||||
*/
|
||||
function SerializeKeyEvent(event) {
|
||||
return {
|
||||
keyCode: event.keyCode,
|
||||
shiftKey: event.shiftKey,
|
||||
ctrlKey: event.ctrlKey,
|
||||
altKey: event.altKey,
|
||||
metaKey: event.metaKey
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function PDFScriptingAPI(window, plugin) {
|
||||
this.loadState_ = LoadState.LOADING;
|
||||
this.pendingScriptingMessages_ = [];
|
||||
this.setPlugin(plugin);
|
||||
|
||||
window.addEventListener('message', function(event) {
|
||||
if (event.origin != 'chrome://pdf-viewer' &&
|
||||
event.origin != 'chrome://print') {
|
||||
console.error('Received message that was not from the extension: ' +
|
||||
event);
|
||||
return;
|
||||
}
|
||||
switch (event.data.type) {
|
||||
case 'viewport':
|
||||
if (this.viewportChangedCallback_)
|
||||
this.viewportChangedCallback_(event.data.pageX,
|
||||
event.data.pageY,
|
||||
event.data.pageWidth,
|
||||
event.data.viewportWidth,
|
||||
event.data.viewportHeight);
|
||||
break;
|
||||
case 'documentLoaded':
|
||||
this.loadState_ = event.data.load_state;
|
||||
if (this.loadCallback_)
|
||||
this.loadCallback_(this.loadState_ == LoadState.SUCCESS);
|
||||
break;
|
||||
case 'getSelectedTextReply':
|
||||
if (this.selectedTextCallback_) {
|
||||
this.selectedTextCallback_(event.data.selectedText);
|
||||
this.selectedTextCallback_ = null;
|
||||
}
|
||||
break;
|
||||
case 'sendKeyEvent':
|
||||
if (this.keyEventCallback_)
|
||||
this.keyEventCallback_(DeserializeKeyEvent(event.data.keyEvent));
|
||||
break;
|
||||
}
|
||||
}.bind(this), false);
|
||||
}
|
||||
|
||||
PDFScriptingAPI.prototype = {
|
||||
/**
|
||||
* @private
|
||||
* Send a message to the extension. If messages try to get sent before there
|
||||
* is a plugin element set, then we queue them up and send them later (this
|
||||
* can happen in print preview).
|
||||
* @param {Object} message The message to send.
|
||||
*/
|
||||
sendMessage_: function(message) {
|
||||
if (this.plugin_)
|
||||
this.plugin_.postMessage(message, '*');
|
||||
else
|
||||
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.
|
||||
*/
|
||||
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'
|
||||
});
|
||||
// Flush pending messages.
|
||||
while (this.pendingScriptingMessages_.length > 0)
|
||||
this.sendMessage_(this.pendingScriptingMessages_.shift());
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the callback which will be run when the PDF viewport changes.
|
||||
* @param {Function} callback the callback to be called.
|
||||
*/
|
||||
setViewportChangedCallback: function(callback) {
|
||||
this.viewportChangedCallback_ = callback;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the callback which will be run when the PDF document has finished
|
||||
* loading. If the document is already loaded, it will be run immediately.
|
||||
* @param {Function} callback the callback to be called.
|
||||
*/
|
||||
setLoadCallback: function(callback) {
|
||||
this.loadCallback_ = callback;
|
||||
if (this.loadState_ != LoadState.LOADING && this.loadCallback_)
|
||||
this.loadCallback_(this.loadState_ == LoadState.SUCCESS);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a callback that gets run when a key event is fired in the PDF viewer.
|
||||
* @param {Function} callback the callback to be called with a key event.
|
||||
*/
|
||||
setKeyEventCallback: function(callback) {
|
||||
this.keyEventCallback_ = callback;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the PDF viewer into print preview mode.
|
||||
* @param {string} url the url of the PDF to load.
|
||||
* @param {boolean} grayscale whether or not to display the PDF in grayscale.
|
||||
* @param {Array<number>} pageNumbers an array of the page numbers.
|
||||
* @param {boolean} modifiable whether or not the document is modifiable.
|
||||
*/
|
||||
resetPrintPreviewMode: function(url, grayscale, pageNumbers, modifiable) {
|
||||
this.loadState_ = LoadState.LOADING;
|
||||
this.sendMessage_({
|
||||
type: 'resetPrintPreviewMode',
|
||||
url: url,
|
||||
grayscale: grayscale,
|
||||
pageNumbers: pageNumbers,
|
||||
modifiable: modifiable
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a page into the document while in print preview mode.
|
||||
* @param {string} url the url of the pdf page to load.
|
||||
* @param {number} index the index of the page to load.
|
||||
*/
|
||||
loadPreviewPage: function(url, index) {
|
||||
this.sendMessage_({
|
||||
type: 'loadPreviewPage',
|
||||
url: url,
|
||||
index: index
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Select all the text in the document. May only be called after document
|
||||
* load.
|
||||
*/
|
||||
selectAll: function() {
|
||||
this.sendMessage_({
|
||||
type: 'selectAll'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the selected text in the document. The callback will be called with the
|
||||
* text that is selected. May only be called after document load.
|
||||
* @param {Function} callback a callback to be called with the selected text.
|
||||
* @return {boolean} true if the function is successful, false if there is an
|
||||
* outstanding request for selected text that has not been answered.
|
||||
*/
|
||||
getSelectedText: function(callback) {
|
||||
if (this.selectedTextCallback_)
|
||||
return false;
|
||||
this.selectedTextCallback_ = callback;
|
||||
this.sendMessage_({
|
||||
type: 'getSelectedText'
|
||||
});
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Print the document. May only be called after document load.
|
||||
*/
|
||||
print: function() {
|
||||
this.sendMessage_({
|
||||
type: 'print'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a key event to the extension.
|
||||
* @param {Event} keyEvent the key event to send to the extension.
|
||||
*/
|
||||
sendKeyEvent: function(keyEvent) {
|
||||
this.sendMessage_({
|
||||
type: 'sendKeyEvent',
|
||||
keyEvent: SerializeKeyEvent(keyEvent)
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a PDF viewer with a scripting interface. This is basically 1) an
|
||||
* iframe which is navigated to the PDF viewer extension and 2) a scripting
|
||||
* interface which provides access to various features of the viewer for use
|
||||
* by print preview and accessibility.
|
||||
* @param {string} src the source URL of the PDF to load initially.
|
||||
* @return {HTMLIFrameElement} the iframe element containing the PDF viewer.
|
||||
*/
|
||||
function PDFCreateOutOfProcessPlugin(src) {
|
||||
var client = new PDFScriptingAPI(window);
|
||||
var iframe = window.document.createElement('iframe');
|
||||
iframe.setAttribute('src', 'pdf_preview.html?' + src);
|
||||
// Prevent the frame from being tab-focusable.
|
||||
iframe.setAttribute('tabindex', '-1');
|
||||
|
||||
iframe.onload = function() {
|
||||
client.setPlugin(iframe.contentWindow);
|
||||
};
|
||||
|
||||
// Add the functions to the iframe so that they can be called directly.
|
||||
iframe.setViewportChangedCallback =
|
||||
client.setViewportChangedCallback.bind(client);
|
||||
iframe.setLoadCallback = client.setLoadCallback.bind(client);
|
||||
iframe.setKeyEventCallback = client.setKeyEventCallback.bind(client);
|
||||
iframe.resetPrintPreviewMode = client.resetPrintPreviewMode.bind(client);
|
||||
iframe.loadPreviewPage = client.loadPreviewPage.bind(client);
|
||||
iframe.sendKeyEvent = client.sendKeyEvent.bind(client);
|
||||
return iframe;
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<grit latest_public_release="0" current_release="1">
|
||||
<outputs>
|
||||
<output filename="pdf_viewer_resources.h" type="rc_header">
|
||||
<emit emit_type='prepend'></emit>
|
||||
</output>
|
||||
<output filename="pdf_viewer_resources_map.cc" type="resource_file_map_source" />
|
||||
<output filename="pdf_viewer_resources_map.h" type="resource_map_header" />
|
||||
<output filename="../../pdf_viewer_resources.pak" type="data_package" />
|
||||
</outputs>
|
||||
<release seq="1">
|
||||
<includes>
|
||||
<include name="IDR_PDF_INDEX_CSS" file="index.css" allowexternalscript="true" type="BINDATA" />
|
||||
<include name="IDR_PDF_INDEX_HTML" file="index.html" allowexternalscript="true" type="BINDATA" />
|
||||
<include name="IDR_PDF_MAIN_JS" file="main.js" type="BINDATA" />
|
||||
<include name="IDR_PDF_PDF_JS" file="pdf.js" type="BINDATA" />
|
||||
<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_NAVIGATOR_JS" file="navigator.js" type="BINDATA" />
|
||||
<include name="IDR_PDF_VIEWPORT_SCROLLER_JS" file="viewport_scroller.js" type="BINDATA" />
|
||||
<include name="IDR_PDF_PDF_SCRIPTING_API_JS" file="pdf_scripting_api.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_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>
|
||||
</release>
|
||||
</grit>
|
|
@ -1,252 +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.
|
||||
|
||||
'use strict';
|
||||
|
||||
/** Idle time in ms before the UI is hidden. */
|
||||
var HIDE_TIMEOUT = 2000;
|
||||
/** Time in ms after force hide before toolbar is shown again. */
|
||||
var FORCE_HIDE_TIMEOUT = 1000;
|
||||
/**
|
||||
* Velocity required in a mousemove to reveal the UI (pixels/ms). This is
|
||||
* intended to be high enough that a fast flick of the mouse is required to
|
||||
* reach it.
|
||||
*/
|
||||
var SHOW_VELOCITY = 10;
|
||||
/** Distance from the top of the screen required to reveal the toolbars. */
|
||||
var TOP_TOOLBAR_REVEAL_DISTANCE = 100;
|
||||
/** Distance from the bottom-right of the screen required to reveal toolbars. */
|
||||
var SIDE_TOOLBAR_REVEAL_DISTANCE_RIGHT = 150;
|
||||
var SIDE_TOOLBAR_REVEAL_DISTANCE_BOTTOM = 250;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param {MouseEvent} e Event to test.
|
||||
* @return {boolean} True if the mouse is close to the top of the screen.
|
||||
*/
|
||||
function isMouseNearTopToolbar(e) {
|
||||
return e.y < TOP_TOOLBAR_REVEAL_DISTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MouseEvent} e Event to test.
|
||||
* @param {Window} window Window to test against.
|
||||
* @return {boolean} True if the mouse is close to the bottom-right of the
|
||||
* screen.
|
||||
*/
|
||||
function isMouseNearSideToolbar(e, window) {
|
||||
var atSide = e.x > window.innerWidth - SIDE_TOOLBAR_REVEAL_DISTANCE_RIGHT;
|
||||
if (isRTL())
|
||||
atSide = e.x < SIDE_TOOLBAR_REVEAL_DISTANCE_RIGHT;
|
||||
var atBottom = e.y > window.innerHeight - SIDE_TOOLBAR_REVEAL_DISTANCE_BOTTOM;
|
||||
return atSide && atBottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Toolbar Manager, responsible for co-ordinating between multiple
|
||||
* toolbar elements.
|
||||
* @constructor
|
||||
* @param {Object} window The window containing the UI.
|
||||
* @param {Object} toolbar The top toolbar element.
|
||||
* @param {Object} zoomToolbar The zoom toolbar element.
|
||||
*/
|
||||
function ToolbarManager(window, toolbar, zoomToolbar) {
|
||||
this.window_ = window;
|
||||
this.toolbar_ = toolbar;
|
||||
this.zoomToolbar_ = zoomToolbar;
|
||||
|
||||
this.toolbarTimeout_ = null;
|
||||
this.isMouseNearTopToolbar_ = false;
|
||||
this.isMouseNearSideToolbar_ = false;
|
||||
|
||||
this.sideToolbarAllowedOnly_ = false;
|
||||
this.sideToolbarAllowedOnlyTimer_ = null;
|
||||
|
||||
this.keyboardNavigationActive = false;
|
||||
|
||||
this.lastMovementTimestamp = null;
|
||||
|
||||
this.window_.addEventListener('resize', this.resizeDropdowns_.bind(this));
|
||||
this.resizeDropdowns_();
|
||||
}
|
||||
|
||||
ToolbarManager.prototype = {
|
||||
|
||||
handleMouseMove: function(e) {
|
||||
this.isMouseNearTopToolbar_ = this.toolbar_ && isMouseNearTopToolbar(e);
|
||||
this.isMouseNearSideToolbar_ = isMouseNearSideToolbar(e, this.window_);
|
||||
|
||||
this.keyboardNavigationActive = false;
|
||||
var touchInteractionActive =
|
||||
(e.sourceCapabilities && e.sourceCapabilities.firesTouchEvents);
|
||||
|
||||
// Allow the top toolbar to be shown if the mouse moves away from the side
|
||||
// toolbar (as long as the timeout has elapsed).
|
||||
if (!this.isMouseNearSideToolbar_ && !this.sideToolbarAllowedOnlyTimer_)
|
||||
this.sideToolbarAllowedOnly_ = false;
|
||||
|
||||
// Allow the top toolbar to be shown if the mouse moves to the top edge.
|
||||
if (this.isMouseNearTopToolbar_)
|
||||
this.sideToolbarAllowedOnly_ = false;
|
||||
|
||||
// Tapping the screen with toolbars open tries to close them.
|
||||
if (touchInteractionActive && this.zoomToolbar_.isVisible()) {
|
||||
this.hideToolbarsIfAllowed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the toolbars if the mouse is near the top or bottom-right of the
|
||||
// screen, if the mouse moved fast, or if the touchscreen was tapped.
|
||||
if (this.isMouseNearTopToolbar_ || this.isMouseNearSideToolbar_ ||
|
||||
this.isHighVelocityMouseMove_(e) || touchInteractionActive) {
|
||||
if (this.sideToolbarAllowedOnly_)
|
||||
this.zoomToolbar_.show();
|
||||
else
|
||||
this.showToolbars();
|
||||
}
|
||||
this.hideToolbarsAfterTimeout();
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether a mousemove event is high enough velocity to reveal the toolbars.
|
||||
* @param {MouseEvent} e Event to test.
|
||||
* @return {boolean} true if the event is a high velocity mousemove, false
|
||||
* otherwise.
|
||||
* @private
|
||||
*/
|
||||
isHighVelocityMouseMove_: function(e) {
|
||||
if (e.type == 'mousemove') {
|
||||
if (this.lastMovementTimestamp == null) {
|
||||
this.lastMovementTimestamp = this.getCurrentTimestamp_();
|
||||
} else {
|
||||
var movement =
|
||||
Math.sqrt(e.movementX * e.movementX + e.movementY * e.movementY);
|
||||
var newTime = this.getCurrentTimestamp_();
|
||||
var interval = newTime - this.lastMovementTimestamp;
|
||||
this.lastMovementTimestamp = newTime;
|
||||
|
||||
if (interval != 0)
|
||||
return movement / interval > SHOW_VELOCITY;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Wrapper around Date.now() to make it easily replaceable for testing.
|
||||
* @return {int}
|
||||
* @private
|
||||
*/
|
||||
getCurrentTimestamp_: function() {
|
||||
return Date.now();
|
||||
},
|
||||
|
||||
/**
|
||||
* Display both UI toolbars.
|
||||
*/
|
||||
showToolbars: function() {
|
||||
if (this.toolbar_)
|
||||
this.toolbar_.show();
|
||||
this.zoomToolbar_.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show toolbars and mark that navigation is being performed with
|
||||
* tab/shift-tab. This disables toolbar hiding until the mouse is moved or
|
||||
* escape is pressed.
|
||||
*/
|
||||
showToolbarsForKeyboardNavigation: function() {
|
||||
this.keyboardNavigationActive = true;
|
||||
this.showToolbars();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide toolbars after a delay, regardless of the position of the mouse.
|
||||
* Intended to be called when the mouse has moved out of the parent window.
|
||||
*/
|
||||
hideToolbarsForMouseOut: function() {
|
||||
this.isMouseNearTopToolbar_ = false;
|
||||
this.isMouseNearSideToolbar_ = false;
|
||||
this.hideToolbarsAfterTimeout();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the toolbars are able to be closed, and close them if they are.
|
||||
* Toolbars may be kept open based on mouse/keyboard activity and active
|
||||
* elements.
|
||||
*/
|
||||
hideToolbarsIfAllowed: function() {
|
||||
if (this.isMouseNearSideToolbar_ || this.isMouseNearTopToolbar_)
|
||||
return;
|
||||
|
||||
if (this.toolbar_ && this.toolbar_.shouldKeepOpen())
|
||||
return;
|
||||
|
||||
if (this.keyboardNavigationActive)
|
||||
return;
|
||||
|
||||
// Remove focus to make any visible tooltips disappear -- otherwise they'll
|
||||
// still be visible on screen when the toolbar is off screen.
|
||||
if ((this.toolbar_ && document.activeElement == this.toolbar_) ||
|
||||
document.activeElement == this.zoomToolbar_) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
|
||||
if (this.toolbar_)
|
||||
this.toolbar_.hide();
|
||||
this.zoomToolbar_.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the toolbar after the HIDE_TIMEOUT has elapsed.
|
||||
*/
|
||||
hideToolbarsAfterTimeout: function() {
|
||||
if (this.toolbarTimeout_)
|
||||
this.window_.clearTimeout(this.toolbarTimeout_);
|
||||
this.toolbarTimeout_ = this.window_.setTimeout(
|
||||
this.hideToolbarsIfAllowed.bind(this), HIDE_TIMEOUT);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the 'topmost' layer of toolbars. Hides any dropdowns that are open, or
|
||||
* hides the basic toolbars otherwise.
|
||||
*/
|
||||
hideSingleToolbarLayer: function() {
|
||||
if (!this.toolbar_ || !this.toolbar_.hideDropdowns()) {
|
||||
this.keyboardNavigationActive = false;
|
||||
this.hideToolbarsIfAllowed();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the top toolbar and keep it hidden until both:
|
||||
* - The mouse is moved away from the right side of the screen
|
||||
* - 1 second has passed.
|
||||
*
|
||||
* The top toolbar can be immediately re-opened by moving the mouse to the top
|
||||
* of the screen.
|
||||
*/
|
||||
forceHideTopToolbar: function() {
|
||||
if (!this.toolbar_)
|
||||
return;
|
||||
this.toolbar_.hide();
|
||||
this.sideToolbarAllowedOnly_ = true;
|
||||
this.sideToolbarAllowedOnlyTimer_ = this.window_.setTimeout(function() {
|
||||
this.sideToolbarAllowedOnlyTimer_ = null;
|
||||
}.bind(this), FORCE_HIDE_TIMEOUT);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the size of toolbar dropdowns based on the positions of the rest of
|
||||
* the UI.
|
||||
* @private
|
||||
*/
|
||||
resizeDropdowns_: function() {
|
||||
if (!this.toolbar_)
|
||||
return;
|
||||
var lowerBound = this.window_.innerHeight - this.zoomToolbar_.clientHeight;
|
||||
this.toolbar_.setDropdownLowerBound(lowerBound);
|
||||
}
|
||||
};
|
|
@ -1,599 +0,0 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
/**
|
||||
* Returns the height of the intersection of two rectangles.
|
||||
* @param {Object} rect1 the first rect
|
||||
* @param {Object} rect2 the second rect
|
||||
* @return {number} the height of the intersection of the rects
|
||||
*/
|
||||
function getIntersectionHeight(rect1, rect2) {
|
||||
return Math.max(0,
|
||||
Math.min(rect1.y + rect1.height, rect2.y + rect2.height) -
|
||||
Math.max(rect1.y, rect2.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new viewport.
|
||||
* @constructor
|
||||
* @param {Window} window the window
|
||||
* @param {Object} sizer is the element which represents the size of the
|
||||
* document in the viewport
|
||||
* @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 {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) {
|
||||
this.window_ = window;
|
||||
this.sizer_ = sizer;
|
||||
this.viewportChangedCallback_ = viewportChangedCallback;
|
||||
this.beforeZoomCallback_ = beforeZoomCallback;
|
||||
this.afterZoomCallback_ = afterZoomCallback;
|
||||
this.allowedToChangeZoom_ = false;
|
||||
this.zoom_ = 1;
|
||||
this.documentDimensions_ = null;
|
||||
this.pageDimensions_ = [];
|
||||
this.scrollbarWidth_ = scrollbarWidth;
|
||||
this.fittingType_ = Viewport.FittingType.NONE;
|
||||
this.defaultZoom_ = defaultZoom;
|
||||
this.topToolbarHeight_ = topToolbarHeight;
|
||||
|
||||
window.addEventListener('scroll', this.updateViewport_.bind(this));
|
||||
window.addEventListener('resize', this.resize_.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'
|
||||
};
|
||||
|
||||
/**
|
||||
* The increment to scroll a page by in pixels when up/down/left/right arrow
|
||||
* keys are pressed. Usually we just let the browser handle scrolling on the
|
||||
* window when these keys are pressed but in certain cases we need to simulate
|
||||
* these events.
|
||||
*/
|
||||
Viewport.SCROLL_INCREMENT = 40;
|
||||
|
||||
/**
|
||||
* Predefined zoom factors to be used when zooming in/out. These are in
|
||||
* ascending order. This should match the lists in
|
||||
* 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];
|
||||
|
||||
/**
|
||||
* The minimum and maximum range to be used to clip zoom factor.
|
||||
*/
|
||||
Viewport.ZOOM_FACTOR_RANGE = {
|
||||
min: Viewport.ZOOM_FACTORS[0],
|
||||
max: Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1]
|
||||
};
|
||||
|
||||
/**
|
||||
* The width of the page shadow around pages in pixels.
|
||||
*/
|
||||
Viewport.PAGE_SHADOW = {top: 3, bottom: 7, left: 5, right: 5};
|
||||
|
||||
Viewport.prototype = {
|
||||
/**
|
||||
* Returns the zoomed and rounded document dimensions for the given zoom.
|
||||
* Rounding is necessary when interacting with the renderer which tends to
|
||||
* operate in integral values (for example for determining if scrollbars
|
||||
* should be shown).
|
||||
* @param {number} zoom The zoom to use to compute the scaled dimensions.
|
||||
* @return {Object} A dictionary with scaled 'width'/'height' of the document.
|
||||
* @private
|
||||
*/
|
||||
getZoomedDocumentDimensions_: function(zoom) {
|
||||
if (!this.documentDimensions_)
|
||||
return null;
|
||||
return {
|
||||
width: Math.round(this.documentDimensions_.width * zoom),
|
||||
height: Math.round(this.documentDimensions_.height * zoom)
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Returns true if the document needs scrollbars at the given zoom level.
|
||||
* @param {number} zoom compute whether scrollbars are needed at this zoom
|
||||
* @return {Object} with 'horizontal' and 'vertical' keys which map to bool
|
||||
* values indicating if the horizontal and vertical scrollbars are needed
|
||||
* respectively.
|
||||
*/
|
||||
documentNeedsScrollbars_: function(zoom) {
|
||||
var zoomedDimensions = this.getZoomedDocumentDimensions_(zoom);
|
||||
if (!zoomedDimensions) {
|
||||
return {
|
||||
horizontal: false,
|
||||
vertical: false
|
||||
};
|
||||
}
|
||||
|
||||
// If scrollbars are required for one direction, expand the document in the
|
||||
// other direction to take the width of the scrollbars into account when
|
||||
// deciding whether the other direction needs scrollbars.
|
||||
if (zoomedDimensions.width > this.window_.innerWidth)
|
||||
zoomedDimensions.height += this.scrollbarWidth_;
|
||||
else if (zoomedDimensions.height > this.window_.innerHeight)
|
||||
zoomedDimensions.width += this.scrollbarWidth_;
|
||||
return {
|
||||
horizontal: zoomedDimensions.width > this.window_.innerWidth,
|
||||
vertical: zoomedDimensions.height + this.topToolbarHeight_ >
|
||||
this.window_.innerHeight
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the document needs scrollbars at the current zoom level.
|
||||
* @return {Object} with 'x' and 'y' keys which map to bool values
|
||||
* indicating if the horizontal and vertical scrollbars are needed
|
||||
* respectively.
|
||||
*/
|
||||
documentHasScrollbars: function() {
|
||||
return this.documentNeedsScrollbars_(this.zoom_);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Helper function called when the zoomed document size changes.
|
||||
*/
|
||||
contentSizeChanged_: function() {
|
||||
var zoomedDimensions = this.getZoomedDocumentDimensions_(this.zoom_);
|
||||
if (zoomedDimensions) {
|
||||
this.sizer_.style.width = zoomedDimensions.width + 'px';
|
||||
this.sizer_.style.height = zoomedDimensions.height +
|
||||
this.topToolbarHeight_ + 'px';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Called when the viewport should be updated.
|
||||
*/
|
||||
updateViewport_: function() {
|
||||
this.viewportChangedCallback_();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Called when the viewport size changes.
|
||||
*/
|
||||
resize_: function() {
|
||||
if (this.fittingType_ == Viewport.FittingType.FIT_TO_PAGE)
|
||||
this.fitToPageInternal_(false);
|
||||
else if (this.fittingType_ == Viewport.FittingType.FIT_TO_WIDTH)
|
||||
this.fitToWidth();
|
||||
else
|
||||
this.updateViewport_();
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {Object} the scroll position of the viewport.
|
||||
*/
|
||||
get position() {
|
||||
return {
|
||||
x: this.window_.pageXOffset,
|
||||
y: this.window_.pageYOffset - this.topToolbarHeight_
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Scroll the viewport to the specified position.
|
||||
* @type {Object} position the position to scroll to.
|
||||
*/
|
||||
set position(position) {
|
||||
this.window_.scrollTo(position.x, position.y + this.topToolbarHeight_);
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {Object} the size of the viewport excluding scrollbars.
|
||||
*/
|
||||
get size() {
|
||||
var needsScrollbars = this.documentNeedsScrollbars_(this.zoom_);
|
||||
var scrollbarWidth = needsScrollbars.vertical ? this.scrollbarWidth_ : 0;
|
||||
var scrollbarHeight = needsScrollbars.horizontal ? this.scrollbarWidth_ : 0;
|
||||
return {
|
||||
width: this.window_.innerWidth - scrollbarWidth,
|
||||
height: this.window_.innerHeight - scrollbarHeight
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {number} the zoom level of the viewport.
|
||||
*/
|
||||
get zoom() {
|
||||
return this.zoom_;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* 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
|
||||
* from the viewport. This is to avoid flickering.
|
||||
*/
|
||||
mightZoom_: function(f) {
|
||||
this.beforeZoomCallback_();
|
||||
this.allowedToChangeZoom_ = true;
|
||||
f();
|
||||
this.allowedToChangeZoom_ = false;
|
||||
this.afterZoomCallback_();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Sets the zoom of the viewport.
|
||||
* @param {number} newZoom the zoom level to zoom to.
|
||||
*/
|
||||
setZoomInternal_: function(newZoom) {
|
||||
if (!this.allowedToChangeZoom_) {
|
||||
throw 'Called Viewport.setZoomInternal_ without calling ' +
|
||||
'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_
|
||||
};
|
||||
this.zoom_ = newZoom;
|
||||
this.contentSizeChanged_();
|
||||
// Scroll to the scaled scroll position.
|
||||
this.position = {
|
||||
x: currentScrollPos.x * newZoom,
|
||||
y: currentScrollPos.y * newZoom
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the zoom to the given zoom level.
|
||||
* @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.updateViewport_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {number} the width of scrollbars in the viewport in pixels.
|
||||
*/
|
||||
get scrollbarWidth() {
|
||||
return this.scrollbarWidth_;
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {Viewport.FittingType} the fitting type the viewport is currently in.
|
||||
*/
|
||||
get fittingType() {
|
||||
return this.fittingType_;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {integer} y the y-coordinate to get the page at.
|
||||
* @return {integer} the index of a page overlapping the given y-coordinate.
|
||||
*/
|
||||
getPageAtY_: function(y) {
|
||||
var min = 0;
|
||||
var max = this.pageDimensions_.length - 1;
|
||||
while (max >= min) {
|
||||
var page = Math.floor(min + ((max - min) / 2));
|
||||
// There might be a gap between the pages, in which case use the bottom
|
||||
// of the previous page as the top for finding the page.
|
||||
var top = 0;
|
||||
if (page > 0) {
|
||||
top = this.pageDimensions_[page - 1].y +
|
||||
this.pageDimensions_[page - 1].height;
|
||||
}
|
||||
var bottom = this.pageDimensions_[page].y +
|
||||
this.pageDimensions_[page].height;
|
||||
|
||||
if (top <= y && bottom > y)
|
||||
return page;
|
||||
else if (top > y)
|
||||
max = page - 1;
|
||||
else
|
||||
min = page + 1;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the page with the greatest proportion of its height in the current
|
||||
* viewport.
|
||||
* @return {int} the index of the most visible page.
|
||||
*/
|
||||
getMostVisiblePage: function() {
|
||||
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_
|
||||
};
|
||||
var firstVisiblePageVisibility = getIntersectionHeight(
|
||||
this.pageDimensions_[firstVisiblePage], viewportRect) /
|
||||
this.pageDimensions_[firstVisiblePage].height;
|
||||
var nextPageVisibility = getIntersectionHeight(
|
||||
this.pageDimensions_[firstVisiblePage + 1], viewportRect) /
|
||||
this.pageDimensions_[firstVisiblePage + 1].height;
|
||||
if (nextPageVisibility > firstVisiblePageVisibility)
|
||||
return firstVisiblePage + 1;
|
||||
return firstVisiblePage;
|
||||
},
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
computeFittingZoom_: function(pageDimensions, widthOnly) {
|
||||
// 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);
|
||||
}
|
||||
// Check if there needs to be any scrollbars.
|
||||
var needsScrollbars = this.documentNeedsScrollbars_(zoom);
|
||||
|
||||
// If the document fits, just return the zoom.
|
||||
if (!needsScrollbars.horizontal && !needsScrollbars.vertical)
|
||||
return zoom;
|
||||
|
||||
var zoomedDimensions = this.getZoomedDocumentDimensions_(zoom);
|
||||
|
||||
// Check if adding a scrollbar will result in needing the other scrollbar.
|
||||
var scrollbarWidth = this.scrollbarWidth_;
|
||||
if (needsScrollbars.horizontal &&
|
||||
zoomedDimensions.height > this.window_.innerHeight - scrollbarWidth) {
|
||||
needsScrollbars.vertical = true;
|
||||
}
|
||||
if (needsScrollbars.vertical &&
|
||||
zoomedDimensions.width > this.window_.innerWidth - scrollbarWidth) {
|
||||
needsScrollbars.horizontal = true;
|
||||
}
|
||||
|
||||
// Compute available window space.
|
||||
var windowWithScrollbars = {
|
||||
width: this.window_.innerWidth,
|
||||
height: this.window_.innerHeight
|
||||
};
|
||||
if (needsScrollbars.horizontal)
|
||||
windowWithScrollbars.height -= scrollbarWidth;
|
||||
if (needsScrollbars.vertical)
|
||||
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 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));
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Zoom the viewport so that a page 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.
|
||||
*/
|
||||
fitToPageInternal_: function(scrollToTopOfPage) {
|
||||
this.mightZoom_(function() {
|
||||
this.fittingType_ = Viewport.FittingType.FIT_TO_PAGE;
|
||||
if (!this.documentDimensions_)
|
||||
return;
|
||||
var page = this.getMostVisiblePage();
|
||||
// Fit to the current page's height and the widest page's width.
|
||||
var dimensions = {
|
||||
width: this.documentDimensions_.width,
|
||||
height: this.pageDimensions_[page].height,
|
||||
};
|
||||
this.setZoomInternal_(this.computeFittingZoom_(dimensions, false));
|
||||
if (scrollToTopOfPage) {
|
||||
this.position = {
|
||||
x: 0,
|
||||
y: this.pageDimensions_[page].y * this.zoom_
|
||||
};
|
||||
}
|
||||
this.updateViewport_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Zoom the viewport so that a page consumes the entire viewport. Also scrolls
|
||||
* the viewport to the top of the current page.
|
||||
*/
|
||||
fitToPage: function() {
|
||||
this.fitToPageInternal_(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Zoom out to the next predefined zoom level.
|
||||
*/
|
||||
zoomOut: function() {
|
||||
this.mightZoom_(function() {
|
||||
this.fittingType_ = Viewport.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_)
|
||||
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;
|
||||
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_)
|
||||
nextZoom = Viewport.ZOOM_FACTORS[i];
|
||||
}
|
||||
this.setZoomInternal_(nextZoom);
|
||||
this.updateViewport_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Go to the given page index.
|
||||
* @param {number} page the index of the page to go to. zero-based.
|
||||
*/
|
||||
goToPage: function(page) {
|
||||
this.mightZoom_(function() {
|
||||
if (this.pageDimensions_.length === 0)
|
||||
return;
|
||||
if (page < 0)
|
||||
page = 0;
|
||||
if (page >= this.pageDimensions_.length)
|
||||
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
|
||||
// initially.
|
||||
if (this.fittingType_ != Viewport.FittingType.FIT_TO_PAGE)
|
||||
toolbarOffset = this.topToolbarHeight_;
|
||||
this.position = {
|
||||
x: dimensions.x * this.zoom_,
|
||||
y: dimensions.y * this.zoom_ - toolbarOffset
|
||||
};
|
||||
this.updateViewport_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the dimensions of the document.
|
||||
* @param {Object} documentDimensions the dimensions of the document
|
||||
*/
|
||||
setDocumentDimensions: function(documentDimensions) {
|
||||
this.mightZoom_(function() {
|
||||
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.contentSizeChanged_();
|
||||
this.resize_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the coordinates of the page contents (excluding the page shadow)
|
||||
* relative to the screen.
|
||||
* @param {number} page the index of the page to get the rect for.
|
||||
* @return {Object} a rect representing the page in screen coordinates.
|
||||
*/
|
||||
getPageScreenRect: function(page) {
|
||||
if (!this.documentDimensions_) {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
}
|
||||
if (page >= this.pageDimensions_.length)
|
||||
page = this.pageDimensions_.length - 1;
|
||||
|
||||
var pageDimensions = this.pageDimensions_[page];
|
||||
|
||||
// Compute the page dimensions minus the shadows.
|
||||
var insetDimensions = {
|
||||
x: pageDimensions.x + Viewport.PAGE_SHADOW.left,
|
||||
y: pageDimensions.y + Viewport.PAGE_SHADOW.top,
|
||||
width: pageDimensions.width - Viewport.PAGE_SHADOW.left -
|
||||
Viewport.PAGE_SHADOW.right,
|
||||
height: pageDimensions.height - Viewport.PAGE_SHADOW.top -
|
||||
Viewport.PAGE_SHADOW.bottom
|
||||
};
|
||||
|
||||
// Compute the x-coordinate of the page within the document.
|
||||
// TODO(raymes): This should really be set when the PDF plugin passes the
|
||||
// page coordinates, but it isn't yet.
|
||||
var x = (this.documentDimensions_.width - pageDimensions.width) / 2 +
|
||||
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;
|
||||
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_
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,135 +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.
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The period of time in milliseconds to wait between updating the viewport
|
||||
* position by the scroll velocity.
|
||||
*/
|
||||
ViewportScroller.DRAG_TIMER_INTERVAL_MS_ = 100;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The maximum drag scroll distance per DRAG_TIMER_INTERVAL in pixels.
|
||||
*/
|
||||
ViewportScroller.MAX_DRAG_SCROLL_DISTANCE_ = 100;
|
||||
|
||||
/**
|
||||
* Creates a new ViewportScroller.
|
||||
* A ViewportScroller scrolls the page in response to drag selection with the
|
||||
* mouse.
|
||||
* @param {Object} viewport The viewport info of the page.
|
||||
* @param {Object} plugin The PDF plugin element.
|
||||
* @param {Object} window The window containing the viewer.
|
||||
*/
|
||||
function ViewportScroller(viewport, plugin, window) {
|
||||
this.viewport_ = viewport;
|
||||
this.plugin_ = plugin;
|
||||
this.window_ = window;
|
||||
this.mousemoveCallback_ = null;
|
||||
this.timerId_ = null;
|
||||
this.scrollVelocity_ = null;
|
||||
this.lastFrameTime_ = 0;
|
||||
}
|
||||
|
||||
ViewportScroller.prototype = {
|
||||
/**
|
||||
* @private
|
||||
* Start scrolling the page by |scrollVelocity_| every
|
||||
* |DRAG_TIMER_INTERVAL_MS_|.
|
||||
*/
|
||||
startDragScrollTimer_: function() {
|
||||
if (this.timerId_ === null) {
|
||||
this.timerId_ =
|
||||
this.window_.setInterval(this.dragScrollPage_.bind(this),
|
||||
ViewportScroller.DRAG_TIMER_INTERVAL_MS_);
|
||||
this.lastFrameTime_ = Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Stops the drag scroll timer if it is active.
|
||||
*/
|
||||
stopDragScrollTimer_: function() {
|
||||
if (this.timerId_ !== null) {
|
||||
this.window_.clearInterval(this.timerId_);
|
||||
this.timerId_ = null;
|
||||
this.lastFrameTime_ = 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Scrolls the viewport by the current scroll velocity.
|
||||
*/
|
||||
dragScrollPage_: function() {
|
||||
var position = this.viewport_.position;
|
||||
var currentFrameTime = Date.now();
|
||||
var timeAdjustment = (currentFrameTime - this.lastFrameTime_) /
|
||||
ViewportScroller.DRAG_TIMER_INTERVAL_MS_;
|
||||
position.y += (this.scrollVelocity_.y * timeAdjustment);
|
||||
position.x += (this.scrollVelocity_.x * timeAdjustment);
|
||||
this.viewport_.position = position;
|
||||
this.lastFrameTime_ = currentFrameTime;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Calculate the velocity to scroll while dragging using the distance of the
|
||||
* cursor outside the viewport.
|
||||
* @param {Object} event The mousemove event.
|
||||
* @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
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Handles mousemove events. It updates the scroll velocity and starts and
|
||||
* stops timer based on scroll velocity.
|
||||
* @param {Object} event The mousemove event.
|
||||
*/
|
||||
onMousemove_: function(event) {
|
||||
this.scrollVelocity_ = this.calculateVelocity_(event);
|
||||
if (!this.scrollVelocity_.x && !this.scrollVelocity_.y)
|
||||
this.stopDragScrollTimer_();
|
||||
else if (!this.timerId_)
|
||||
this.startDragScrollTimer_();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets whether to scroll the viewport when the mouse is outside the
|
||||
* viewport.
|
||||
* @param {boolean} isSelecting Represents selection status.
|
||||
*/
|
||||
setEnableScrolling: function(isSelecting) {
|
||||
if (isSelecting) {
|
||||
if (!this.mousemoveCallback_)
|
||||
this.mousemoveCallback_ = this.onMousemove_.bind(this);
|
||||
this.plugin_.addEventListener('mousemove', this.mousemoveCallback_,
|
||||
false);
|
||||
} else {
|
||||
this.stopDragScrollTimer_();
|
||||
if (this.mousemoveCallback_) {
|
||||
this.plugin_.removeEventListener('mousemove', this.mousemoveCallback_,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,83 +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.
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* A class that manages updating the browser with zoom changes.
|
||||
*/
|
||||
class ZoomManager {
|
||||
/**
|
||||
* Constructs a ZoomManager
|
||||
* @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;
|
||||
this.setBrowserZoomFunction_ = setBrowserZoomFunction;
|
||||
this.browserZoom_ = initialZoom;
|
||||
this.changingBrowserZoom_ = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a browser-initiated zoom-level change occurs.
|
||||
* @param {number} newZoom the zoom level to zoom to.
|
||||
*/
|
||||
onBrowserZoomChange(newZoom) {
|
||||
// If we are changing the browser zoom level, ignore any browser zoom level
|
||||
// change events. Either, the change occurred before our update and will be
|
||||
// overwritten, or the change being reported is the change we are making,
|
||||
// which we have already handled.
|
||||
if (this.changingBrowserZoom_)
|
||||
return;
|
||||
|
||||
if (this.floatingPointEquals(this.browserZoom_, newZoom))
|
||||
return;
|
||||
|
||||
this.browserZoom_ = newZoom;
|
||||
this.viewport_.setZoom(newZoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when an extension-initiated zoom-level change occurs.
|
||||
*/
|
||||
onPdfZoomChange() {
|
||||
// If we are already changing the browser zoom level in response to a
|
||||
// previous extension-initiated zoom-level change, ignore this zoom change.
|
||||
// Once the browser zoom level is changed, we check whether the extension's
|
||||
// zoom level matches the most recently sent zoom level.
|
||||
if (this.changingBrowserZoom_)
|
||||
return;
|
||||
|
||||
let zoom = this.viewport_.zoom;
|
||||
if (this.floatingPointEquals(this.browserZoom_, zoom))
|
||||
return;
|
||||
|
||||
this.changingBrowserZoom_ = this.setBrowserZoomFunction_(zoom).then(
|
||||
function() {
|
||||
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.
|
||||
this.onPdfZoomChange();
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
Subproject commit c99278fc5a4c0cf9e225b401cbec65a176c991fc
|
Загрузка…
Ссылка в новой задаче