1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-22 16:18:08 +02:00

Enable auto-formatting of the entire code-base using Prettier (issue 11444)

Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).

Prettier is being used for a couple of reasons:

 - To be consistent with `mozilla-central`, where Prettier is already in use across the tree.

 - To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.

Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.

*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.

(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
This commit is contained in:
Jonas Jenwald 2019-12-25 15:59:37 +01:00
parent 8ec1dfde49
commit de36b2aaba
205 changed files with 40024 additions and 31859 deletions

View file

@ -13,9 +13,9 @@
* limitations under the License.
*/
import { AnnotationLayer } from 'pdfjs-lib';
import { NullL10n } from './ui_utils';
import { SimpleLinkService } from './pdf_link_service';
import { AnnotationLayer } from "pdfjs-lib";
import { NullL10n } from "./ui_utils";
import { SimpleLinkService } from "./pdf_link_service";
/**
* @typedef {Object} AnnotationLayerBuilderOptions
@ -33,9 +33,15 @@ class AnnotationLayerBuilder {
/**
* @param {AnnotationLayerBuilderOptions} options
*/
constructor({ pageDiv, pdfPage, linkService, downloadManager,
imageResourcesPath = '', renderInteractiveForms = false,
l10n = NullL10n, }) {
constructor({
pageDiv,
pdfPage,
linkService,
downloadManager,
imageResourcesPath = "",
renderInteractiveForms = false,
l10n = NullL10n,
}) {
this.pageDiv = pageDiv;
this.pdfPage = pdfPage;
this.linkService = linkService;
@ -52,14 +58,14 @@ class AnnotationLayerBuilder {
* @param {PageViewport} viewport
* @param {string} intent (default value is 'display')
*/
render(viewport, intent = 'display') {
this.pdfPage.getAnnotations({ intent, }).then((annotations) => {
render(viewport, intent = "display") {
this.pdfPage.getAnnotations({ intent }).then(annotations => {
if (this._cancelled) {
return;
}
let parameters = {
viewport: viewport.clone({ dontFlip: true, }),
viewport: viewport.clone({ dontFlip: true }),
div: this.div,
annotations,
page: this.pdfPage,
@ -79,8 +85,8 @@ class AnnotationLayerBuilder {
if (annotations.length === 0) {
return;
}
this.div = document.createElement('div');
this.div.className = 'annotationLayer';
this.div = document.createElement("div");
this.div.className = "annotationLayer";
this.pageDiv.appendChild(this.div);
parameters.div = this.div;
@ -98,7 +104,7 @@ class AnnotationLayerBuilder {
if (!this.div) {
return;
}
this.div.setAttribute('hidden', 'true');
this.div.setAttribute("hidden", "true");
}
}
@ -115,9 +121,13 @@ class DefaultAnnotationLayerFactory {
* @param {IL10n} l10n
* @returns {AnnotationLayerBuilder}
*/
createAnnotationLayerBuilder(pageDiv, pdfPage, imageResourcesPath = '',
renderInteractiveForms = false,
l10n = NullL10n) {
createAnnotationLayerBuilder(
pageDiv,
pdfPage,
imageResourcesPath = "",
renderInteractiveForms = false,
l10n = NullL10n
) {
return new AnnotationLayerBuilder({
pageDiv,
pdfPage,
@ -129,7 +139,4 @@ class DefaultAnnotationLayerFactory {
}
}
export {
AnnotationLayerBuilder,
DefaultAnnotationLayerFactory,
};
export { AnnotationLayerBuilder, DefaultAnnotationLayerFactory };

1776
web/app.js

File diff suppressed because it is too large Load diff

View file

@ -13,8 +13,8 @@
* limitations under the License.
*/
import { apiCompatibilityParams } from 'pdfjs-lib';
import { viewerCompatibilityParams } from './viewer_compatibility';
import { apiCompatibilityParams } from "pdfjs-lib";
import { viewerCompatibilityParams } from "./viewer_compatibility";
const OptionKind = {
VIEWER: 0x02,
@ -35,12 +35,12 @@ const defaultOptions = {
},
defaultUrl: {
/** @type {string} */
value: 'compressed.tracemonkey-pldi-09.pdf',
value: "compressed.tracemonkey-pldi-09.pdf",
kind: OptionKind.VIEWER,
},
defaultZoomValue: {
/** @type {string} */
value: '',
value: "",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
disableHistory: {
@ -73,7 +73,7 @@ const defaultOptions = {
},
externalLinkRel: {
/** @type {string} */
value: 'noopener noreferrer nofollow',
value: "noopener noreferrer nofollow",
kind: OptionKind.VIEWER,
},
externalLinkTarget: {
@ -88,7 +88,7 @@ const defaultOptions = {
},
imageResourcesPath: {
/** @type {string} */
value: './images/',
value: "./images/",
kind: OptionKind.VIEWER,
},
/**
@ -110,7 +110,7 @@ const defaultOptions = {
*/
renderer: {
/** @type {string} */
value: 'canvas',
value: "canvas",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
renderInteractiveForms: {
@ -156,8 +156,10 @@ const defaultOptions = {
},
cMapUrl: {
/** @type {string} */
value: (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION') ?
'../external/bcmaps/' : '../web/cmaps/'),
value:
(typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
? "../external/bcmaps/"
: "../web/cmaps/"),
kind: OptionKind.API,
},
disableAutoFetch: {
@ -188,7 +190,7 @@ const defaultOptions = {
},
docBaseUrl: {
/** @type {string} */
value: '',
value: "",
kind: OptionKind.API,
},
isEvalSupported: {
@ -219,13 +221,17 @@ const defaultOptions = {
},
workerSrc: {
/** @type {string} */
value: (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION') ?
'../src/worker_loader.js' : '../build/pdf.worker.js'),
value:
(typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
? "../src/worker_loader.js"
: "../build/pdf.worker.js"),
kind: OptionKind.WORKER,
},
};
if (typeof PDFJSDev === 'undefined' ||
PDFJSDev.test('!PRODUCTION || (GENERIC && !LIB)')) {
if (
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("!PRODUCTION || (GENERIC && !LIB)")
) {
defaultOptions.disablePreferences = {
/** @type {boolean} */
value: false,
@ -233,7 +239,7 @@ if (typeof PDFJSDev === 'undefined' ||
};
defaultOptions.locale = {
/** @type {string} */
value: (typeof navigator !== 'undefined' ? navigator.language : 'en-US'),
value: (typeof navigator !== "undefined" ? navigator.language : "en-US"),
kind: OptionKind.VIEWER,
};
defaultOptions.printResolution = {
@ -247,7 +253,7 @@ const userOptions = Object.create(null);
class AppOptions {
constructor() {
throw new Error('Cannot initialize AppOptions.');
throw new Error("Cannot initialize AppOptions.");
}
static get(name) {
@ -257,7 +263,7 @@ class AppOptions {
}
const defaultOption = defaultOptions[name];
if (defaultOption !== undefined) {
return (defaultOption.compatibility || defaultOption.value);
return defaultOption.compatibility || defaultOption.value;
}
return undefined;
}
@ -271,10 +277,14 @@ class AppOptions {
continue;
}
if (kind === OptionKind.PREFERENCE) {
const value = defaultOption.value, valueType = typeof value;
const value = defaultOption.value,
valueType = typeof value;
if (valueType === 'boolean' || valueType === 'string' ||
(valueType === 'number' && Number.isInteger(value))) {
if (
valueType === "boolean" ||
valueType === "string" ||
(valueType === "number" && Number.isInteger(value))
) {
options[name] = value;
continue;
}
@ -282,8 +292,10 @@ class AppOptions {
}
}
const userOption = userOptions[name];
options[name] = (userOption !== undefined ? userOption :
(defaultOption.compatibility || defaultOption.value));
options[name] =
userOption !== undefined
? userOption
: defaultOption.compatibility || defaultOption.value;
}
return options;
}
@ -297,7 +309,4 @@ class AppOptions {
}
}
export {
AppOptions,
OptionKind,
};
export { AppOptions, OptionKind };

View file

@ -14,19 +14,35 @@
*/
import {
CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, getGlobalEventBus,
getVisibleElements, isPortraitOrientation, isValidRotation, isValidScrollMode,
isValidSpreadMode, MAX_AUTO_SCALE, moveToEndOfArray, NullL10n,
PresentationModeState, RendererType, SCROLLBAR_PADDING, scrollIntoView,
ScrollMode, SpreadMode, TextLayerMode, UNKNOWN_SCALE, VERTICAL_PADDING,
watchScroll
} from './ui_utils';
import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue';
import { AnnotationLayerBuilder } from './annotation_layer_builder';
import { createPromiseCapability } from 'pdfjs-lib';
import { PDFPageView } from './pdf_page_view';
import { SimpleLinkService } from './pdf_link_service';
import { TextLayerBuilder } from './text_layer_builder';
CSS_UNITS,
DEFAULT_SCALE,
DEFAULT_SCALE_VALUE,
getGlobalEventBus,
getVisibleElements,
isPortraitOrientation,
isValidRotation,
isValidScrollMode,
isValidSpreadMode,
MAX_AUTO_SCALE,
moveToEndOfArray,
NullL10n,
PresentationModeState,
RendererType,
SCROLLBAR_PADDING,
scrollIntoView,
ScrollMode,
SpreadMode,
TextLayerMode,
UNKNOWN_SCALE,
VERTICAL_PADDING,
watchScroll,
} from "./ui_utils";
import { PDFRenderingQueue, RenderingStates } from "./pdf_rendering_queue";
import { AnnotationLayerBuilder } from "./annotation_layer_builder";
import { createPromiseCapability } from "pdfjs-lib";
import { PDFPageView } from "./pdf_page_view";
import { SimpleLinkService } from "./pdf_link_service";
import { TextLayerBuilder } from "./text_layer_builder";
const DEFAULT_CACHE_SIZE = 10;
@ -123,7 +139,7 @@ class BaseViewer {
*/
constructor(options) {
if (this.constructor === BaseViewer) {
throw new Error('Cannot initialize BaseViewer.');
throw new Error("Cannot initialize BaseViewer.");
}
this._name = this.constructor.name;
@ -134,9 +150,10 @@ class BaseViewer {
this.downloadManager = options.downloadManager || null;
this.findController = options.findController || null;
this.removePageBorders = options.removePageBorders || false;
this.textLayerMode = Number.isInteger(options.textLayerMode) ?
options.textLayerMode : TextLayerMode.ENABLE;
this.imageResourcesPath = options.imageResourcesPath || '';
this.textLayerMode = Number.isInteger(options.textLayerMode)
? options.textLayerMode
: TextLayerMode.ENABLE;
this.imageResourcesPath = options.imageResourcesPath || "";
this.renderInteractiveForms = options.renderInteractiveForms || false;
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
this.renderer = options.renderer || RendererType.CANVAS;
@ -160,12 +177,12 @@ class BaseViewer {
this._resetView();
if (this.removePageBorders) {
this.viewer.classList.add('removePageBorders');
this.viewer.classList.add("removePageBorders");
}
// Defer the dispatching of this event, to give other viewer components
// time to initialize *and* register 'baseviewerinit' event listeners.
Promise.resolve().then(() => {
this.eventBus.dispatch('baseviewerinit', { source: this, });
this.eventBus.dispatch("baseviewerinit", { source: this });
});
}
@ -203,7 +220,7 @@ class BaseViewer {
*/
set currentPageNumber(val) {
if (!Number.isInteger(val)) {
throw new Error('Invalid page number.');
throw new Error("Invalid page number.");
}
if (!this.pdfDocument) {
return;
@ -211,7 +228,8 @@ class BaseViewer {
// The intent can be to just reset a scroll position and/or scale.
if (!this._setCurrentPageNumber(val, /* resetCurrentPageView = */ true)) {
console.error(
`${this._name}.currentPageNumber: "${val}" is not a valid page.`);
`${this._name}.currentPageNumber: "${val}" is not a valid page.`
);
}
}
@ -232,7 +250,7 @@ class BaseViewer {
}
this._currentPageNumber = val;
this.eventBus.dispatch('pagechanging', {
this.eventBus.dispatch("pagechanging", {
source: this,
pageNumber: val,
pageLabel: this._pageLabels && this._pageLabels[val - 1],
@ -269,7 +287,8 @@ class BaseViewer {
// The intent can be to just reset a scroll position and/or scale.
if (!this._setCurrentPageNumber(page, /* resetCurrentPageView = */ true)) {
console.error(
`${this._name}.currentPageLabel: "${val}" is not a valid page.`);
`${this._name}.currentPageLabel: "${val}" is not a valid page.`
);
}
}
@ -277,8 +296,9 @@ class BaseViewer {
* @type {number}
*/
get currentScale() {
return this._currentScale !== UNKNOWN_SCALE ? this._currentScale :
DEFAULT_SCALE;
return this._currentScale !== UNKNOWN_SCALE
? this._currentScale
: DEFAULT_SCALE;
}
/**
@ -286,7 +306,7 @@ class BaseViewer {
*/
set currentScale(val) {
if (isNaN(val)) {
throw new Error('Invalid numeric scale.');
throw new Error("Invalid numeric scale.");
}
if (!this.pdfDocument) {
return;
@ -323,7 +343,7 @@ class BaseViewer {
*/
set pagesRotation(rotation) {
if (!isValidRotation(rotation)) {
throw new Error('Invalid pages rotation angle.');
throw new Error("Invalid pages rotation angle.");
}
if (!this.pdfDocument) {
return;
@ -345,7 +365,7 @@ class BaseViewer {
this._setScale(this._currentScaleValue, true);
}
this.eventBus.dispatch('rotationchanging', {
this.eventBus.dispatch("rotationchanging", {
source: this,
pagesRotation: rotation,
pageNumber,
@ -358,7 +378,7 @@ class BaseViewer {
get _setDocumentViewerElement() {
// In most viewers, e.g. `PDFViewer`, this should return `this.viewer`.
throw new Error('Not implemented: _setDocumentViewerElement');
throw new Error("Not implemented: _setDocumentViewerElement");
}
/**
@ -385,7 +405,7 @@ class BaseViewer {
pagesCapability.promise.then(() => {
this._pageViewsReady = true;
this.eventBus.dispatch('pagesloaded', {
this.eventBus.dispatch("pagesloaded", {
source: this,
pagesCount,
});
@ -397,7 +417,7 @@ class BaseViewer {
const firstPagePromise = pdfDocument.getPage(1);
this.firstPagePromise = firstPagePromise;
this._onBeforeDraw = (evt) => {
this._onBeforeDraw = evt => {
const pageView = this._pages[evt.pageNumber - 1];
if (!pageView) {
return;
@ -406,111 +426,120 @@ class BaseViewer {
// evicted from the buffer and destroyed even if we pause its rendering.
this._buffer.push(pageView);
};
this.eventBus.on('pagerender', this._onBeforeDraw);
this.eventBus.on("pagerender", this._onBeforeDraw);
this._onAfterDraw = (evt) => {
this._onAfterDraw = evt => {
if (evt.cssTransform || onePageRenderedCapability.settled) {
return;
}
onePageRenderedCapability.resolve();
this.eventBus.off('pagerendered', this._onAfterDraw);
this.eventBus.off("pagerendered", this._onAfterDraw);
this._onAfterDraw = null;
};
this.eventBus.on('pagerendered', this._onAfterDraw);
this.eventBus.on("pagerendered", this._onAfterDraw);
// Fetch a single page so we can get a viewport that will be the default
// viewport for all pages
firstPagePromise.then((firstPdfPage) => {
let scale = this.currentScale;
const viewport = firstPdfPage.getViewport({ scale: scale * CSS_UNITS, });
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
let textLayerFactory = null;
if (this.textLayerMode !== TextLayerMode.DISABLE) {
textLayerFactory = this;
}
let pageView = new PDFPageView({
container: this._setDocumentViewerElement,
eventBus: this.eventBus,
id: pageNum,
scale,
defaultViewport: viewport.clone(),
renderingQueue: this.renderingQueue,
textLayerFactory,
textLayerMode: this.textLayerMode,
annotationLayerFactory: this,
imageResourcesPath: this.imageResourcesPath,
renderInteractiveForms: this.renderInteractiveForms,
renderer: this.renderer,
enableWebGL: this.enableWebGL,
useOnlyCssZoom: this.useOnlyCssZoom,
maxCanvasPixels: this.maxCanvasPixels,
l10n: this.l10n,
});
this._pages.push(pageView);
}
// Set the first `pdfPage` immediately, since it's already loaded,
// rather than having to repeat the `PDFDocumentProxy.getPage` call in
// the `this._ensurePdfPageLoaded` method before rendering can start.
const firstPageView = this._pages[0];
if (firstPageView) {
firstPageView.setPdfPage(firstPdfPage);
this.linkService.cachePageRef(1, firstPdfPage.ref);
}
if (this._spreadMode !== SpreadMode.NONE) {
this._updateSpreadMode();
}
// Fetch all the pages since the viewport is needed before printing
// starts to create the correct size canvas. Wait until one page is
// rendered so we don't tie up too many resources early on.
onePageRenderedCapability.promise.then(() => {
if (this.findController) {
this.findController.setDocument(pdfDocument); // Enable searching.
}
// In addition to 'disableAutoFetch' being set, also attempt to reduce
// resource usage when loading *very* long/large documents.
if (pdfDocument.loadingParams['disableAutoFetch'] ||
pagesCount > 7500) {
// XXX: Printing is semi-broken with auto fetch disabled.
pagesCapability.resolve();
return;
}
let getPagesLeft = pagesCount - 1; // The first page was already loaded.
if (getPagesLeft <= 0) {
pagesCapability.resolve();
return;
}
for (let pageNum = 2; pageNum <= pagesCount; ++pageNum) {
pdfDocument.getPage(pageNum).then((pdfPage) => {
let pageView = this._pages[pageNum - 1];
if (!pageView.pdfPage) {
pageView.setPdfPage(pdfPage);
}
this.linkService.cachePageRef(pageNum, pdfPage.ref);
if (--getPagesLeft === 0) {
pagesCapability.resolve();
}
}, (reason) => {
console.error(`Unable to get page ${pageNum} to initialize viewer`,
reason);
if (--getPagesLeft === 0) {
pagesCapability.resolve();
}
firstPagePromise
.then(firstPdfPage => {
let scale = this.currentScale;
const viewport = firstPdfPage.getViewport({ scale: scale * CSS_UNITS });
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
let textLayerFactory = null;
if (this.textLayerMode !== TextLayerMode.DISABLE) {
textLayerFactory = this;
}
let pageView = new PDFPageView({
container: this._setDocumentViewerElement,
eventBus: this.eventBus,
id: pageNum,
scale,
defaultViewport: viewport.clone(),
renderingQueue: this.renderingQueue,
textLayerFactory,
textLayerMode: this.textLayerMode,
annotationLayerFactory: this,
imageResourcesPath: this.imageResourcesPath,
renderInteractiveForms: this.renderInteractiveForms,
renderer: this.renderer,
enableWebGL: this.enableWebGL,
useOnlyCssZoom: this.useOnlyCssZoom,
maxCanvasPixels: this.maxCanvasPixels,
l10n: this.l10n,
});
this._pages.push(pageView);
}
// Set the first `pdfPage` immediately, since it's already loaded,
// rather than having to repeat the `PDFDocumentProxy.getPage` call in
// the `this._ensurePdfPageLoaded` method before rendering can start.
const firstPageView = this._pages[0];
if (firstPageView) {
firstPageView.setPdfPage(firstPdfPage);
this.linkService.cachePageRef(1, firstPdfPage.ref);
}
if (this._spreadMode !== SpreadMode.NONE) {
this._updateSpreadMode();
}
// Fetch all the pages since the viewport is needed before printing
// starts to create the correct size canvas. Wait until one page is
// rendered so we don't tie up too many resources early on.
onePageRenderedCapability.promise.then(() => {
if (this.findController) {
this.findController.setDocument(pdfDocument); // Enable searching.
}
// In addition to 'disableAutoFetch' being set, also attempt to reduce
// resource usage when loading *very* long/large documents.
if (
pdfDocument.loadingParams["disableAutoFetch"] ||
pagesCount > 7500
) {
// XXX: Printing is semi-broken with auto fetch disabled.
pagesCapability.resolve();
return;
}
let getPagesLeft = pagesCount - 1; // The first page was already loaded.
if (getPagesLeft <= 0) {
pagesCapability.resolve();
return;
}
for (let pageNum = 2; pageNum <= pagesCount; ++pageNum) {
pdfDocument.getPage(pageNum).then(
pdfPage => {
let pageView = this._pages[pageNum - 1];
if (!pageView.pdfPage) {
pageView.setPdfPage(pdfPage);
}
this.linkService.cachePageRef(pageNum, pdfPage.ref);
if (--getPagesLeft === 0) {
pagesCapability.resolve();
}
},
reason => {
console.error(
`Unable to get page ${pageNum} to initialize viewer`,
reason
);
if (--getPagesLeft === 0) {
pagesCapability.resolve();
}
}
);
}
});
this.eventBus.dispatch("pagesinit", { source: this });
if (this.defaultRenderingQueue) {
this.update();
}
})
.catch(reason => {
console.error("Unable to initialize viewer", reason);
});
this.eventBus.dispatch('pagesinit', { source: this, });
if (this.defaultRenderingQueue) {
this.update();
}
}).catch((reason) => {
console.error('Unable to initialize viewer', reason);
});
}
/**
@ -522,8 +551,9 @@ class BaseViewer {
}
if (!labels) {
this._pageLabels = null;
} else if (!(Array.isArray(labels) &&
this.pdfDocument.numPages === labels.length)) {
} else if (
!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)
) {
this._pageLabels = null;
console.error(`${this._name}.setPageLabels: Invalid page labels.`);
} else {
@ -552,15 +582,15 @@ class BaseViewer {
this._spreadMode = SpreadMode.NONE;
if (this._onBeforeDraw) {
this.eventBus.off('pagerender', this._onBeforeDraw);
this.eventBus.off("pagerender", this._onBeforeDraw);
this._onBeforeDraw = null;
}
if (this._onAfterDraw) {
this.eventBus.off('pagerendered', this._onAfterDraw);
this.eventBus.off("pagerendered", this._onAfterDraw);
this._onAfterDraw = null;
}
// Remove the pages from the DOM...
this.viewer.textContent = '';
this.viewer.textContent = "";
// ... and reset the Scroll mode CSS class(es) afterwards.
this._updateScrollMode();
}
@ -572,7 +602,7 @@ class BaseViewer {
this.update();
}
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null, }) {
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null }) {
scrollIntoView(pageDiv, pageSpot);
}
@ -581,7 +611,7 @@ class BaseViewer {
if (isSameScale(this._currentScale, newScale)) {
if (preset) {
this.eventBus.dispatch('scalechanging', {
this.eventBus.dispatch("scalechanging", {
source: this,
scale: newScale,
presetValue: newValue,
@ -596,12 +626,20 @@ class BaseViewer {
this._currentScale = newScale;
if (!noScroll) {
let page = this._currentPageNumber, dest;
if (this._location &&
!(this.isInPresentationMode || this.isChangingPresentationMode)) {
let page = this._currentPageNumber,
dest;
if (
this._location &&
!(this.isInPresentationMode || this.isChangingPresentationMode)
) {
page = this._location.pageNumber;
dest = [null, { name: 'XYZ', }, this._location.left,
this._location.top, null];
dest = [
null,
{ name: "XYZ" },
this._location.left,
this._location.top,
null,
];
}
this.scrollPageIntoView({
pageNumber: page,
@ -610,7 +648,7 @@ class BaseViewer {
});
}
this.eventBus.dispatch('scalechanging', {
this.eventBus.dispatch("scalechanging", {
source: this,
scale: newScale,
presetValue: preset ? newValue : undefined,
@ -631,40 +669,44 @@ class BaseViewer {
if (!currentPage) {
return;
}
const noPadding = (this.isInPresentationMode || this.removePageBorders);
const noPadding = this.isInPresentationMode || this.removePageBorders;
let hPadding = noPadding ? 0 : SCROLLBAR_PADDING;
let vPadding = noPadding ? 0 : VERTICAL_PADDING;
if (!noPadding && this._isScrollModeHorizontal) {
[hPadding, vPadding] = [vPadding, hPadding]; // Swap the padding values.
}
let pageWidthScale = (this.container.clientWidth - hPadding) /
currentPage.width * currentPage.scale;
let pageHeightScale = (this.container.clientHeight - vPadding) /
currentPage.height * currentPage.scale;
let pageWidthScale =
((this.container.clientWidth - hPadding) / currentPage.width) *
currentPage.scale;
let pageHeightScale =
((this.container.clientHeight - vPadding) / currentPage.height) *
currentPage.scale;
switch (value) {
case 'page-actual':
case "page-actual":
scale = 1;
break;
case 'page-width':
case "page-width":
scale = pageWidthScale;
break;
case 'page-height':
case "page-height":
scale = pageHeightScale;
break;
case 'page-fit':
case "page-fit":
scale = Math.min(pageWidthScale, pageHeightScale);
break;
case 'auto':
case "auto":
// For pages in landscape mode, fit the page height to the viewer
// *unless* the page would thus become too wide to fit horizontally.
let horizontalScale = isPortraitOrientation(currentPage) ?
pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
let horizontalScale = isPortraitOrientation(currentPage)
? pageWidthScale
: Math.min(pageHeightScale, pageWidthScale);
scale = Math.min(MAX_AUTO_SCALE, horizontalScale);
break;
default:
console.error(
`${this._name}._setScale: "${value}" is an unknown zoom value.`);
`${this._name}._setScale: "${value}" is an unknown zoom value.`
);
return;
}
this._setScaleUpdatePages(scale, value, noScroll, /* preset = */ true);
@ -682,7 +724,7 @@ class BaseViewer {
}
let pageView = this._pages[this._currentPageNumber - 1];
this._scrollIntoView({ pageDiv: pageView.div, });
this._scrollIntoView({ pageDiv: pageView.div });
}
/**
@ -698,16 +740,21 @@ class BaseViewer {
* Scrolls page into view.
* @param {ScrollPageIntoViewParameters} params
*/
scrollPageIntoView({ pageNumber, destArray = null,
allowNegativeOffset = false, }) {
scrollPageIntoView({
pageNumber,
destArray = null,
allowNegativeOffset = false,
}) {
if (!this.pdfDocument) {
return;
}
const pageView = (Number.isInteger(pageNumber) &&
this._pages[pageNumber - 1]);
const pageView =
Number.isInteger(pageNumber) && this._pages[pageNumber - 1];
if (!pageView) {
console.error(`${this._name}.scrollPageIntoView: ` +
`"${pageNumber}" is not a valid pageNumber parameter.`);
console.error(
`${this._name}.scrollPageIntoView: ` +
`"${pageNumber}" is not a valid pageNumber parameter.`
);
return;
}
@ -715,16 +762,24 @@ class BaseViewer {
this._setCurrentPageNumber(pageNumber, /* resetCurrentPageView = */ true);
return;
}
let x = 0, y = 0;
let width = 0, height = 0, widthScale, heightScale;
let changeOrientation = (pageView.rotation % 180 === 0 ? false : true);
let pageWidth = (changeOrientation ? pageView.height : pageView.width) /
pageView.scale / CSS_UNITS;
let pageHeight = (changeOrientation ? pageView.width : pageView.height) /
pageView.scale / CSS_UNITS;
let x = 0,
y = 0;
let width = 0,
height = 0,
widthScale,
heightScale;
let changeOrientation = pageView.rotation % 180 === 0 ? false : true;
let pageWidth =
(changeOrientation ? pageView.height : pageView.width) /
pageView.scale /
CSS_UNITS;
let pageHeight =
(changeOrientation ? pageView.width : pageView.height) /
pageView.scale /
CSS_UNITS;
let scale = 0;
switch (destArray[1].name) {
case 'XYZ':
case "XYZ":
x = destArray[2];
y = destArray[3];
scale = destArray[4];
@ -735,14 +790,14 @@ class BaseViewer {
x = x !== null ? x : 0;
y = y !== null ? y : pageHeight;
break;
case 'Fit':
case 'FitB':
scale = 'page-fit';
case "Fit":
case "FitB":
scale = "page-fit";
break;
case 'FitH':
case 'FitBH':
case "FitH":
case "FitBH":
y = destArray[2];
scale = 'page-width';
scale = "page-width";
// According to the PDF spec, section 12.3.2.2, a `null` value in the
// parameter should maintain the position relative to the new page.
if (y === null && this._location) {
@ -750,14 +805,14 @@ class BaseViewer {
y = this._location.top;
}
break;
case 'FitV':
case 'FitBV':
case "FitV":
case "FitBV":
x = destArray[2];
width = pageWidth;
height = pageHeight;
scale = 'page-height';
scale = "page-height";
break;
case 'FitR':
case "FitR":
x = destArray[2];
y = destArray[3];
width = destArray[4] - x;
@ -765,15 +820,17 @@ class BaseViewer {
let hPadding = this.removePageBorders ? 0 : SCROLLBAR_PADDING;
let vPadding = this.removePageBorders ? 0 : VERTICAL_PADDING;
widthScale = (this.container.clientWidth - hPadding) /
width / CSS_UNITS;
heightScale = (this.container.clientHeight - vPadding) /
height / CSS_UNITS;
widthScale =
(this.container.clientWidth - hPadding) / width / CSS_UNITS;
heightScale =
(this.container.clientHeight - vPadding) / height / CSS_UNITS;
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
break;
default:
console.error(`${this._name}.scrollPageIntoView: ` +
`"${destArray[1].name}" is not a valid destination type.`);
console.error(
`${this._name}.scrollPageIntoView: ` +
`"${destArray[1].name}" is not a valid destination type.`
);
return;
}
@ -783,7 +840,7 @@ class BaseViewer {
this.currentScaleValue = DEFAULT_SCALE_VALUE;
}
if (scale === 'page-fit' && !destArray[4]) {
if (scale === "page-fit" && !destArray[4]) {
this._scrollIntoView({
pageDiv: pageView.div,
pageNumber,
@ -793,7 +850,7 @@ class BaseViewer {
let boundingRect = [
pageView.viewport.convertToViewportPoint(x, y),
pageView.viewport.convertToViewportPoint(x + width, y + height)
pageView.viewport.convertToViewportPoint(x + width, y + height),
];
let left = Math.min(boundingRect[0][0], boundingRect[1][0]);
let top = Math.min(boundingRect[0][1], boundingRect[1][1]);
@ -807,7 +864,7 @@ class BaseViewer {
}
this._scrollIntoView({
pageDiv: pageView.div,
pageSpot: { left, top, },
pageSpot: { left, top },
pageNumber,
});
}
@ -816,20 +873,22 @@ class BaseViewer {
let currentScale = this._currentScale;
let currentScaleValue = this._currentScaleValue;
let normalizedScaleValue =
parseFloat(currentScaleValue) === currentScale ?
Math.round(currentScale * 10000) / 100 : currentScaleValue;
parseFloat(currentScaleValue) === currentScale
? Math.round(currentScale * 10000) / 100
: currentScaleValue;
let pageNumber = firstPage.id;
let pdfOpenParams = '#page=' + pageNumber;
pdfOpenParams += '&zoom=' + normalizedScaleValue;
let pdfOpenParams = "#page=" + pageNumber;
pdfOpenParams += "&zoom=" + normalizedScaleValue;
let currentPageView = this._pages[pageNumber - 1];
let container = this.container;
let topLeft = currentPageView.getPagePoint(
(container.scrollLeft - firstPage.x),
(container.scrollTop - firstPage.y));
container.scrollLeft - firstPage.x,
container.scrollTop - firstPage.y
);
let intLeft = Math.round(topLeft[0]);
let intTop = Math.round(topLeft[1]);
pdfOpenParams += ',' + intLeft + ',' + intTop;
pdfOpenParams += "," + intLeft + "," + intTop;
this._location = {
pageNumber,
@ -842,12 +901,13 @@ class BaseViewer {
}
_updateHelper(visiblePages) {
throw new Error('Not implemented: _updateHelper');
throw new Error("Not implemented: _updateHelper");
}
update() {
const visible = this._getVisiblePages();
const visiblePages = visible.views, numVisiblePages = visiblePages.length;
const visiblePages = visible.views,
numVisiblePages = visiblePages.length;
if (numVisiblePages === 0) {
return;
@ -860,7 +920,7 @@ class BaseViewer {
this._updateHelper(visiblePages); // Run any class-specific update code.
this._updateLocation(visible.first);
this.eventBus.dispatch('updateviewarea', {
this.eventBus.dispatch("updateviewarea", {
source: this,
location: this._location,
});
@ -877,8 +937,9 @@ class BaseViewer {
get _isScrollModeHorizontal() {
// Used to ensure that pre-rendering of the next/previous page works
// correctly, since Scroll/Spread modes are ignored in Presentation Mode.
return (this.isInPresentationMode ?
false : this._scrollMode === ScrollMode.HORIZONTAL);
return this.isInPresentationMode
? false
: this._scrollMode === ScrollMode.HORIZONTAL;
}
get isInPresentationMode() {
@ -890,13 +951,15 @@ class BaseViewer {
}
get isHorizontalScrollbarEnabled() {
return (this.isInPresentationMode ?
false : (this.container.scrollWidth > this.container.clientWidth));
return this.isInPresentationMode
? false
: this.container.scrollWidth > this.container.clientWidth;
}
get isVerticalScrollbarEnabled() {
return (this.isInPresentationMode ?
false : (this.container.scrollHeight > this.container.clientHeight));
return this.isInPresentationMode
? false
: this.container.scrollHeight > this.container.clientHeight;
}
/**
@ -907,7 +970,7 @@ class BaseViewer {
*/
_getCurrentVisiblePage() {
if (!this.pagesCount) {
return { views: [], };
return { views: [] };
}
const pageView = this._pages[this._currentPageNumber - 1];
// NOTE: Compute the `x` and `y` properties of the current view,
@ -920,12 +983,16 @@ class BaseViewer {
y: element.offsetTop + element.clientTop,
view: pageView,
};
return { first: view, last: view, views: [view], };
return { first: view, last: view, views: [view] };
}
_getVisiblePages() {
return getVisibleElements(this.container, this._pages, true,
this._isScrollModeHorizontal);
return getVisibleElements(
this.container,
this._pages,
true,
this._isScrollModeHorizontal
);
}
/**
@ -937,18 +1004,21 @@ class BaseViewer {
}
if (pageNumber < 1 || pageNumber > this.pagesCount) {
console.error(
`${this._name}.isPageVisible: "${pageNumber}" is out of bounds.`);
`${this._name}.isPageVisible: "${pageNumber}" is out of bounds.`
);
return false;
}
return this._getVisiblePages().views.some(function(view) {
return (view.id === pageNumber);
return view.id === pageNumber;
});
}
cleanup() {
for (let i = 0, ii = this._pages.length; i < ii; i++) {
if (this._pages[i] &&
this._pages[i].renderingState !== RenderingStates.FINISHED) {
if (
this._pages[i] &&
this._pages[i].renderingState !== RenderingStates.FINISHED
) {
this._pages[i].reset();
}
}
@ -977,28 +1047,34 @@ class BaseViewer {
if (this._pagesRequests.has(pageView)) {
return this._pagesRequests.get(pageView);
}
const promise = this.pdfDocument.getPage(pageView.id).then((pdfPage) => {
if (!pageView.pdfPage) {
pageView.setPdfPage(pdfPage);
}
this._pagesRequests.delete(pageView);
return pdfPage;
}).catch((reason) => {
console.error('Unable to get page for page view', reason);
// Page error -- there is nothing that can be done.
this._pagesRequests.delete(pageView);
});
const promise = this.pdfDocument
.getPage(pageView.id)
.then(pdfPage => {
if (!pageView.pdfPage) {
pageView.setPdfPage(pdfPage);
}
this._pagesRequests.delete(pageView);
return pdfPage;
})
.catch(reason => {
console.error("Unable to get page for page view", reason);
// Page error -- there is nothing that can be done.
this._pagesRequests.delete(pageView);
});
this._pagesRequests.set(pageView, promise);
return promise;
}
forceRendering(currentlyVisiblePages) {
let visiblePages = currentlyVisiblePages || this._getVisiblePages();
let scrollAhead = (this._isScrollModeHorizontal ?
this.scroll.right : this.scroll.down);
let pageView = this.renderingQueue.getHighestPriority(visiblePages,
this._pages,
scrollAhead);
let scrollAhead = this._isScrollModeHorizontal
? this.scroll.right
: this.scroll.down;
let pageView = this.renderingQueue.getHighestPriority(
visiblePages,
this._pages,
scrollAhead
);
if (pageView) {
this._ensurePdfPageLoaded(pageView).then(() => {
this.renderingQueue.renderView(pageView);
@ -1014,16 +1090,21 @@ class BaseViewer {
* @param {PageViewport} viewport
* @returns {TextLayerBuilder}
*/
createTextLayerBuilder(textLayerDiv, pageIndex, viewport,
enhanceTextSelection = false) {
createTextLayerBuilder(
textLayerDiv,
pageIndex,
viewport,
enhanceTextSelection = false
) {
return new TextLayerBuilder({
textLayerDiv,
eventBus: this.eventBus,
pageIndex,
viewport,
findController: this.isInPresentationMode ? null : this.findController,
enhanceTextSelection: this.isInPresentationMode ? false :
enhanceTextSelection,
enhanceTextSelection: this.isInPresentationMode
? false
: enhanceTextSelection,
});
}
@ -1036,9 +1117,13 @@ class BaseViewer {
* @param {IL10n} l10n
* @returns {AnnotationLayerBuilder}
*/
createAnnotationLayerBuilder(pageDiv, pdfPage, imageResourcesPath = '',
renderInteractiveForms = false,
l10n = NullL10n) {
createAnnotationLayerBuilder(
pageDiv,
pdfPage,
imageResourcesPath = "",
renderInteractiveForms = false,
l10n = NullL10n
) {
return new AnnotationLayerBuilder({
pageDiv,
pdfPage,
@ -1058,8 +1143,10 @@ class BaseViewer {
let firstPageView = this._pages[0];
for (let i = 1, ii = this._pages.length; i < ii; ++i) {
let pageView = this._pages[i];
if (pageView.width !== firstPageView.width ||
pageView.height !== firstPageView.height) {
if (
pageView.width !== firstPageView.width ||
pageView.height !== firstPageView.height
) {
return false;
}
}
@ -1072,7 +1159,7 @@ class BaseViewer {
*/
getPagesOverview() {
let pagesOverview = this._pages.map(function(pageView) {
let viewport = pageView.pdfPage.getViewport({ scale: 1, });
let viewport = pageView.pdfPage.getViewport({ scale: 1 });
return {
width: viewport.width,
height: viewport.height,
@ -1083,7 +1170,7 @@ class BaseViewer {
return pagesOverview;
}
let isFirstPagePortrait = isPortraitOrientation(pagesOverview[0]);
return pagesOverview.map(function (size) {
return pagesOverview.map(function(size) {
if (isFirstPagePortrait === isPortraitOrientation(size)) {
return size;
}
@ -1115,18 +1202,20 @@ class BaseViewer {
throw new Error(`Invalid scroll mode: ${mode}`);
}
this._scrollMode = mode;
this.eventBus.dispatch('scrollmodechanged', { source: this, mode, });
this.eventBus.dispatch("scrollmodechanged", { source: this, mode });
this._updateScrollMode(/* pageNumber = */ this._currentPageNumber);
}
_updateScrollMode(pageNumber = null) {
const scrollMode = this._scrollMode, viewer = this.viewer;
const scrollMode = this._scrollMode,
viewer = this.viewer;
viewer.classList.toggle('scrollHorizontal',
scrollMode === ScrollMode.HORIZONTAL);
viewer.classList.toggle('scrollWrapped',
scrollMode === ScrollMode.WRAPPED);
viewer.classList.toggle(
"scrollHorizontal",
scrollMode === ScrollMode.HORIZONTAL
);
viewer.classList.toggle("scrollWrapped", scrollMode === ScrollMode.WRAPPED);
if (!this.pdfDocument || !pageNumber) {
return;
@ -1161,7 +1250,7 @@ class BaseViewer {
throw new Error(`Invalid spread mode: ${mode}`);
}
this._spreadMode = mode;
this.eventBus.dispatch('spreadmodechanged', { source: this, mode, });
this.eventBus.dispatch("spreadmodechanged", { source: this, mode });
this._updateSpreadMode(/* pageNumber = */ this._currentPageNumber);
}
@ -1170,9 +1259,10 @@ class BaseViewer {
if (!this.pdfDocument) {
return;
}
const viewer = this.viewer, pages = this._pages;
const viewer = this.viewer,
pages = this._pages;
// Temporarily remove all the pages from the DOM.
viewer.textContent = '';
viewer.textContent = "";
if (this._spreadMode === SpreadMode.NONE) {
for (let i = 0, iMax = pages.length; i < iMax; ++i) {
@ -1183,8 +1273,8 @@ class BaseViewer {
let spread = null;
for (let i = 0, iMax = pages.length; i < iMax; ++i) {
if (spread === null) {
spread = document.createElement('div');
spread.className = 'spread';
spread = document.createElement("div");
spread.className = "spread";
viewer.appendChild(spread);
} else if (i % 2 === parity) {
spread = spread.cloneNode(false);
@ -1202,6 +1292,4 @@ class BaseViewer {
}
}
export {
BaseViewer,
};
export { BaseViewer };

View file

@ -14,15 +14,16 @@
*/
/* globals chrome */
import { DefaultExternalServices, PDFViewerApplication } from './app';
import { AppOptions } from './app_options';
import { BasePreferences } from './preferences';
import { DownloadManager } from './download_manager';
import { GenericL10n } from './genericl10n';
import { DefaultExternalServices, PDFViewerApplication } from "./app";
import { AppOptions } from "./app_options";
import { BasePreferences } from "./preferences";
import { DownloadManager } from "./download_manager";
import { GenericL10n } from "./genericl10n";
if (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('CHROME')) {
throw new Error('Module "pdfjs-web/chromecom" shall not be used outside ' +
'CHROME build.');
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("CHROME")) {
throw new Error(
'Module "pdfjs-web/chromecom" shall not be used outside ' + "CHROME build."
);
}
let ChromeCom = {
@ -42,7 +43,7 @@ let ChromeCom = {
data,
};
if (!chrome.runtime) {
console.error('chrome.runtime is undefined.');
console.error("chrome.runtime is undefined.");
if (callback) {
callback();
}
@ -62,8 +63,10 @@ let ChromeCom = {
*/
resolvePDFFile(file, overlayManager, callback) {
// Expand drive:-URLs to filesystem URLs (Chrome OS)
file = file.replace(/^drive:/i,
'filesystem:' + location.origin + '/external/');
file = file.replace(
/^drive:/i,
"filesystem:" + location.origin + "/external/"
);
if (/^https?:/.test(file)) {
// Assumption: The file being opened is the file that was requested.
@ -82,9 +85,14 @@ let ChromeCom = {
// Even without this check, the file load in frames is still blocked,
// but this may change in the future (https://crbug.com/550151).
if (origin && !/^file:|^chrome-extension:/.test(origin)) {
PDFViewerApplication.error('Blocked ' + origin + ' from loading ' +
file + '. Refused to load a local file in a non-local page ' +
'for security reasons.');
PDFViewerApplication.error(
"Blocked " +
origin +
" from loading " +
file +
". Refused to load a local file in a non-local page " +
"for security reasons."
);
return;
}
isAllowedFileSchemeAccess(function(isAllowedAccess) {
@ -103,7 +111,7 @@ let ChromeCom = {
function getEmbedderOrigin(callback) {
let origin = window === top ? location.origin : location.ancestorOrigins[0];
if (origin === 'null') {
if (origin === "null") {
// file:-URLs, data-URLs, sandboxed frames, etc.
getParentOrigin(callback);
} else {
@ -112,11 +120,11 @@ function getEmbedderOrigin(callback) {
}
function getParentOrigin(callback) {
ChromeCom.request('getParentOrigin', null, callback);
ChromeCom.request("getParentOrigin", null, callback);
}
function isAllowedFileSchemeAccess(callback) {
ChromeCom.request('isAllowedFileSchemeAccess', null, callback);
ChromeCom.request("isAllowedFileSchemeAccess", null, callback);
}
function isRuntimeAvailable() {
@ -147,40 +155,43 @@ function requestAccessToLocalFile(fileUrl, overlayManager, callback) {
// Top-level frames are closed by Chrome upon reload, so there is no need
// for detecting unload of the top-level frame. Should this ever change
// (crbug.com/511670), then the user can just reload the tab.
window.addEventListener('focus', reloadIfRuntimeIsUnavailable);
window.addEventListener("focus", reloadIfRuntimeIsUnavailable);
onCloseOverlay = function() {
window.removeEventListener('focus', reloadIfRuntimeIsUnavailable);
window.removeEventListener("focus", reloadIfRuntimeIsUnavailable);
reloadIfRuntimeIsUnavailable();
overlayManager.close('chromeFileAccessOverlay');
overlayManager.close("chromeFileAccessOverlay");
};
}
if (!chromeFileAccessOverlayPromise) {
chromeFileAccessOverlayPromise = overlayManager.register(
'chromeFileAccessOverlay',
document.getElementById('chromeFileAccessOverlay'),
onCloseOverlay, true);
"chromeFileAccessOverlay",
document.getElementById("chromeFileAccessOverlay"),
onCloseOverlay,
true
);
}
chromeFileAccessOverlayPromise.then(function() {
let iconPath = chrome.runtime.getManifest().icons[48];
document.getElementById('chrome-pdfjs-logo-bg').style.backgroundImage =
'url(' + chrome.runtime.getURL(iconPath) + ')';
document.getElementById("chrome-pdfjs-logo-bg").style.backgroundImage =
"url(" + chrome.runtime.getURL(iconPath) + ")";
// Use Chrome's definition of UI language instead of PDF.js's #lang=...,
// because the shown string should match the UI at chrome://extensions.
// These strings are from chrome/app/resources/generated_resources_*.xtb.
/* eslint-disable no-unexpected-multiline */
let i18nFileAccessLabel =
PDFJSDev.json('$ROOT/web/chrome-i18n-allow-access-to-file-urls.json')
[chrome.i18n.getUILanguage && chrome.i18n.getUILanguage()];
let i18nFileAccessLabel = PDFJSDev.json(
"$ROOT/web/chrome-i18n-allow-access-to-file-urls.json"
)[chrome.i18n.getUILanguage && chrome.i18n.getUILanguage()];
/* eslint-enable no-unexpected-multiline */
if (i18nFileAccessLabel) {
document.getElementById('chrome-file-access-label').textContent =
i18nFileAccessLabel;
document.getElementById(
"chrome-file-access-label"
).textContent = i18nFileAccessLabel;
}
let link = document.getElementById('chrome-link-to-extensions-page');
link.href = 'chrome://extensions/?id=' + chrome.runtime.id;
let link = document.getElementById("chrome-link-to-extensions-page");
link.href = "chrome://extensions/?id=" + chrome.runtime.id;
link.onclick = function(e) {
// Direct navigation to chrome:// URLs is blocked by Chrome, so we
// have to ask the background page to open chrome://extensions/?id=...
@ -188,39 +199,42 @@ function requestAccessToLocalFile(fileUrl, overlayManager, callback) {
// Open in the current tab by default, because toggling the file access
// checkbox causes the extension to reload, and Chrome will close all
// tabs upon reload.
ChromeCom.request('openExtensionsPageForFileAccess', {
ChromeCom.request("openExtensionsPageForFileAccess", {
newTab: e.ctrlKey || e.metaKey || e.button === 1 || window !== top,
});
};
// Show which file is being opened to help the user with understanding
// why this permission request is shown.
document.getElementById('chrome-url-of-local-file').textContent = fileUrl;
document.getElementById("chrome-url-of-local-file").textContent = fileUrl;
document.getElementById('chrome-file-fallback').onchange = function() {
document.getElementById("chrome-file-fallback").onchange = function() {
let file = this.files[0];
if (file) {
let originalFilename = decodeURIComponent(fileUrl.split('/').pop());
let originalFilename = decodeURIComponent(fileUrl.split("/").pop());
let originalUrl = fileUrl;
if (originalFilename !== file.name) {
let msg = 'The selected file does not match the original file.' +
'\nOriginal: ' + originalFilename +
'\nSelected: ' + file.name +
'\nDo you want to open the selected file?';
let msg =
"The selected file does not match the original file." +
"\nOriginal: " +
originalFilename +
"\nSelected: " +
file.name +
"\nDo you want to open the selected file?";
if (!confirm(msg)) {
this.value = '';
this.value = "";
return;
}
// There is no way to retrieve the original URL from the File object.
// So just generate a fake path.
originalUrl = 'file:///fakepath/to/' + encodeURIComponent(file.name);
originalUrl = "file:///fakepath/to/" + encodeURIComponent(file.name);
}
callback(URL.createObjectURL(file), file.size, originalUrl);
overlayManager.close('chromeFileAccessOverlay');
overlayManager.close("chromeFileAccessOverlay");
}
};
overlayManager.open('chromeFileAccessOverlay');
overlayManager.open("chromeFileAccessOverlay");
});
}
@ -230,13 +244,14 @@ if (window === top) {
// localStorage and restored by extension-router.js.
// Unfortunately, the window and tab index are not restored. And if it was
// the only tab in an incognito window, then the tab is not restored either.
addEventListener('unload', function() {
addEventListener("unload", function() {
// If the runtime is still available, the unload is most likely a normal
// tab closure. Otherwise it is most likely an extension reload.
if (!isRuntimeAvailable()) {
localStorage.setItem(
'unload-' + Date.now() + '-' + document.hidden + '-' + location.href,
JSON.stringify(history.state));
"unload-" + Date.now() + "-" + document.hidden + "-" + location.href,
JSON.stringify(history.state)
);
}
});
}
@ -258,7 +273,7 @@ function setReferer(url, callback) {
if (!port) {
// The background page will accept the port, and keep adding the Referer
// request header to requests to |url| until the port is disconnected.
port = chrome.runtime.connect({ name: 'chromecom-referrer', });
port = chrome.runtime.connect({ name: "chromecom-referrer" });
}
port.onDisconnect.addListener(onDisconnect);
port.onMessage.addListener(onMessage);
@ -277,7 +292,7 @@ function setReferer(url, callback) {
// history.state, which is preserved across reloads/navigations.
let state = window.history.state || {};
state.chromecomState = referer;
window.history.replaceState(state, '');
window.history.replaceState(state, "");
}
onCompleted();
}
@ -300,7 +315,7 @@ let storageArea = chrome.storage.sync || chrome.storage.local;
class ChromePreferences extends BasePreferences {
async _writeToStorage(prefObj) {
return new Promise((resolve) => {
return new Promise(resolve => {
if (prefObj === this.defaults) {
let keysToRemove = Object.keys(this.defaults);
// If the storage is reset, remove the keys so that the values from
@ -317,8 +332,8 @@ class ChromePreferences extends BasePreferences {
}
async _readFromStorage(prefObj) {
return new Promise((resolve) => {
let getPreferences = (defaultPrefs) => {
return new Promise(resolve => {
let getPreferences = defaultPrefs => {
if (chrome.runtime.lastError) {
// Managed storage not supported, e.g. in Opera.
defaultPrefs = this.defaults;
@ -336,13 +351,16 @@ class ChromePreferences extends BasePreferences {
// Deprecated preferences are removed from web/default_preferences.json,
// but kept in extensions/chromium/preferences_schema.json for backwards
// compatibility with managed preferences.
let defaultManagedPrefs = Object.assign({
enableHandToolOnLoad: false,
disableTextLayer: false,
enhanceTextSelection: false,
showPreviousViewOnLoad: true,
disablePageMode: false,
}, this.defaults);
let defaultManagedPrefs = Object.assign(
{
enableHandToolOnLoad: false,
disableTextLayer: false,
enhanceTextSelection: false,
showPreviousViewOnLoad: true,
disablePageMode: false,
},
this.defaults
);
chrome.storage.managed.get(defaultManagedPrefs, function(items) {
items = items || defaultManagedPrefs;
@ -390,12 +408,15 @@ class ChromePreferences extends BasePreferences {
let ChromeExternalServices = Object.create(DefaultExternalServices);
ChromeExternalServices.initPassiveLoading = function(callbacks) {
let { overlayManager, } = PDFViewerApplication;
let { overlayManager } = PDFViewerApplication;
// defaultUrl is set in viewer.js
ChromeCom.resolvePDFFile(AppOptions.get('defaultUrl'), overlayManager,
function(url, length, originalUrl) {
callbacks.onOpenWithURL(url, length, originalUrl);
});
ChromeCom.resolvePDFFile(
AppOptions.get("defaultUrl"),
overlayManager,
function(url, length, originalUrl) {
callbacks.onOpenWithURL(url, length, originalUrl);
}
);
};
ChromeExternalServices.createDownloadManager = function(options) {
return new DownloadManager(options);
@ -408,6 +429,4 @@ ChromeExternalServices.createL10n = function(options) {
};
PDFViewerApplication.externalServices = ChromeExternalServices;
export {
ChromeCom,
};
export { ChromeCom };

View file

@ -14,37 +14,39 @@
*/
/* eslint-disable no-var */
'use strict';
"use strict";
var FontInspector = (function FontInspectorClosure() {
var fonts, createObjectURL;
var active = false;
var fontAttribute = 'data-font-name';
var fontAttribute = "data-font-name";
function removeSelection() {
let divs = document.querySelectorAll(`span[${fontAttribute}]`);
for (let div of divs) {
div.className = '';
div.className = "";
}
}
function resetSelection() {
let divs = document.querySelectorAll(`span[${fontAttribute}]`);
for (let div of divs) {
div.className = 'debuggerHideText';
div.className = "debuggerHideText";
}
}
function selectFont(fontName, show) {
let divs = document.querySelectorAll(`span[${fontAttribute}=${fontName}]`);
for (let div of divs) {
div.className = show ? 'debuggerShowText' : 'debuggerHideText';
div.className = show ? "debuggerShowText" : "debuggerHideText";
}
}
function textLayerClick(e) {
if (!e.target.dataset.fontName ||
e.target.tagName.toUpperCase() !== 'SPAN') {
if (
!e.target.dataset.fontName ||
e.target.tagName.toUpperCase() !== "SPAN"
) {
return;
}
var fontName = e.target.dataset.fontName;
var selects = document.getElementsByTagName('input');
var selects = document.getElementsByTagName("input");
for (var i = 0; i < selects.length; ++i) {
var select = selects[i];
if (select.dataset.fontName !== fontName) {
@ -57,25 +59,25 @@ var FontInspector = (function FontInspectorClosure() {
}
return {
// Properties/functions needed by PDFBug.
id: 'FontInspector',
name: 'Font Inspector',
id: "FontInspector",
name: "Font Inspector",
panel: null,
manager: null,
init: function init(pdfjsLib) {
var panel = this.panel;
panel.setAttribute('style', 'padding: 5px;');
var tmp = document.createElement('button');
tmp.addEventListener('click', resetSelection);
tmp.textContent = 'Refresh';
panel.setAttribute("style", "padding: 5px;");
var tmp = document.createElement("button");
tmp.addEventListener("click", resetSelection);
tmp.textContent = "Refresh";
panel.appendChild(tmp);
fonts = document.createElement('div');
fonts = document.createElement("div");
panel.appendChild(fonts);
createObjectURL = pdfjsLib.createObjectURL;
},
cleanup: function cleanup() {
fonts.textContent = '';
fonts.textContent = "";
},
enabled: false,
get active() {
@ -84,62 +86,65 @@ var FontInspector = (function FontInspectorClosure() {
set active(value) {
active = value;
if (active) {
document.body.addEventListener('click', textLayerClick, true);
document.body.addEventListener("click", textLayerClick, true);
resetSelection();
} else {
document.body.removeEventListener('click', textLayerClick, true);
document.body.removeEventListener("click", textLayerClick, true);
removeSelection();
}
},
// FontInspector specific functions.
fontAdded: function fontAdded(fontObj, url) {
function properties(obj, list) {
var moreInfo = document.createElement('table');
var moreInfo = document.createElement("table");
for (var i = 0; i < list.length; i++) {
var tr = document.createElement('tr');
var td1 = document.createElement('td');
var tr = document.createElement("tr");
var td1 = document.createElement("td");
td1.textContent = list[i];
tr.appendChild(td1);
var td2 = document.createElement('td');
var td2 = document.createElement("td");
td2.textContent = obj[list[i]].toString();
tr.appendChild(td2);
moreInfo.appendChild(tr);
}
return moreInfo;
}
var moreInfo = properties(fontObj, ['name', 'type']);
var moreInfo = properties(fontObj, ["name", "type"]);
var fontName = fontObj.loadedName;
var font = document.createElement('div');
var name = document.createElement('span');
var font = document.createElement("div");
var name = document.createElement("span");
name.textContent = fontName;
var download = document.createElement('a');
var download = document.createElement("a");
if (url) {
url = /url\(['"]?([^\)"']+)/.exec(url);
download.href = url[1];
} else if (fontObj.data) {
download.href = createObjectURL(fontObj.data, fontObj.mimeType);
}
download.textContent = 'Download';
var logIt = document.createElement('a');
logIt.href = '';
logIt.textContent = 'Log';
logIt.addEventListener('click', function(event) {
download.textContent = "Download";
var logIt = document.createElement("a");
logIt.href = "";
logIt.textContent = "Log";
logIt.addEventListener("click", function(event) {
event.preventDefault();
console.log(fontObj);
});
var select = document.createElement('input');
select.setAttribute('type', 'checkbox');
var select = document.createElement("input");
select.setAttribute("type", "checkbox");
select.dataset.fontName = fontName;
select.addEventListener('click', (function(select, fontName) {
return (function() {
selectFont(fontName, select.checked);
});
})(select, fontName));
select.addEventListener(
"click",
(function(select, fontName) {
return function() {
selectFont(fontName, select.checked);
};
})(select, fontName)
);
font.appendChild(select);
font.appendChild(name);
font.appendChild(document.createTextNode(' '));
font.appendChild(document.createTextNode(" "));
font.appendChild(download);
font.appendChild(document.createTextNode(' '));
font.appendChild(document.createTextNode(" "));
font.appendChild(logIt);
font.appendChild(moreInfo);
fonts.appendChild(font);
@ -165,24 +170,24 @@ var StepperManager = (function StepperManagerClosure() {
var breakPoints = Object.create(null);
return {
// Properties/functions needed by PDFBug.
id: 'Stepper',
name: 'Stepper',
id: "Stepper",
name: "Stepper",
panel: null,
manager: null,
init: function init(pdfjsLib) {
var self = this;
this.panel.setAttribute('style', 'padding: 5px;');
stepperControls = document.createElement('div');
stepperChooser = document.createElement('select');
stepperChooser.addEventListener('change', function(event) {
this.panel.setAttribute("style", "padding: 5px;");
stepperControls = document.createElement("div");
stepperChooser = document.createElement("select");
stepperChooser.addEventListener("change", function(event) {
self.selectStepper(this.value);
});
stepperControls.appendChild(stepperChooser);
stepperDiv = document.createElement('div');
stepperDiv = document.createElement("div");
this.panel.appendChild(stepperControls);
this.panel.appendChild(stepperDiv);
if (sessionStorage.getItem('pdfjsBreakPoints')) {
breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints'));
if (sessionStorage.getItem("pdfjsBreakPoints")) {
breakPoints = JSON.parse(sessionStorage.getItem("pdfjsBreakPoints"));
}
opMap = Object.create(null);
@ -191,21 +196,21 @@ var StepperManager = (function StepperManagerClosure() {
}
},
cleanup: function cleanup() {
stepperChooser.textContent = '';
stepperDiv.textContent = '';
stepperChooser.textContent = "";
stepperDiv.textContent = "";
steppers = [];
},
enabled: false,
active: false,
// Stepper specific functions.
create: function create(pageIndex) {
var debug = document.createElement('div');
debug.id = 'stepper' + pageIndex;
debug.setAttribute('hidden', true);
debug.className = 'stepper';
var debug = document.createElement("div");
debug.id = "stepper" + pageIndex;
debug.setAttribute("hidden", true);
debug.className = "stepper";
stepperDiv.appendChild(debug);
var b = document.createElement('option');
b.textContent = 'Page ' + (pageIndex + 1);
var b = document.createElement("option");
b.textContent = "Page " + (pageIndex + 1);
b.value = pageIndex;
stepperChooser.appendChild(b);
var initBreakPoints = breakPoints[pageIndex] || [];
@ -225,9 +230,9 @@ var StepperManager = (function StepperManagerClosure() {
for (i = 0; i < steppers.length; ++i) {
var stepper = steppers[i];
if (stepper.pageIndex === pageIndex) {
stepper.panel.removeAttribute('hidden');
stepper.panel.removeAttribute("hidden");
} else {
stepper.panel.setAttribute('hidden', true);
stepper.panel.setAttribute("hidden", true);
}
}
var options = stepperChooser.options;
@ -238,7 +243,7 @@ var StepperManager = (function StepperManagerClosure() {
},
saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
breakPoints[pageIndex] = bps;
sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
sessionStorage.setItem("pdfjsBreakPoints", JSON.stringify(breakPoints));
},
};
})();
@ -255,22 +260,26 @@ var Stepper = (function StepperClosure() {
}
function simplifyArgs(args) {
if (typeof args === 'string') {
if (typeof args === "string") {
var MAX_STRING_LENGTH = 75;
return args.length <= MAX_STRING_LENGTH ? args :
args.substring(0, MAX_STRING_LENGTH) + '...';
return args.length <= MAX_STRING_LENGTH
? args
: args.substring(0, MAX_STRING_LENGTH) + "...";
}
if (typeof args !== 'object' || args === null) {
if (typeof args !== "object" || args === null) {
return args;
}
if ('length' in args) { // array
var simpleArgs = [], i, ii;
if ("length" in args) {
// array
var simpleArgs = [],
i,
ii;
var MAX_ITEMS = 10;
for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) {
simpleArgs.push(simplifyArgs(args[i]));
}
if (i < args.length) {
simpleArgs.push('...');
simpleArgs.push("...");
}
return simpleArgs;
}
@ -293,16 +302,16 @@ var Stepper = (function StepperClosure() {
Stepper.prototype = {
init: function init(operatorList) {
var panel = this.panel;
var content = c('div', 'c=continue, s=step');
var table = c('table');
var content = c("div", "c=continue, s=step");
var table = c("table");
content.appendChild(table);
table.cellSpacing = 0;
var headerRow = c('tr');
var headerRow = c("tr");
table.appendChild(headerRow);
headerRow.appendChild(c('th', 'Break'));
headerRow.appendChild(c('th', 'Idx'));
headerRow.appendChild(c('th', 'fn'));
headerRow.appendChild(c('th', 'args'));
headerRow.appendChild(c("th", "Break"));
headerRow.appendChild(c("th", "Idx"));
headerRow.appendChild(c("th", "fn"));
headerRow.appendChild(c("th", "args"));
panel.appendChild(content);
this.table = table;
this.updateOperatorList(operatorList);
@ -326,56 +335,58 @@ var Stepper = (function StepperClosure() {
}
var chunk = document.createDocumentFragment();
var operatorsToDisplay = Math.min(MAX_OPERATORS_COUNT,
operatorList.fnArray.length);
var operatorsToDisplay = Math.min(
MAX_OPERATORS_COUNT,
operatorList.fnArray.length
);
for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) {
var line = c('tr');
line.className = 'line';
var line = c("tr");
line.className = "line";
line.dataset.idx = i;
chunk.appendChild(line);
var checked = this.breakPoints.includes(i);
var args = operatorList.argsArray[i] || [];
var breakCell = c('td');
var cbox = c('input');
cbox.type = 'checkbox';
cbox.className = 'points';
var breakCell = c("td");
var cbox = c("input");
cbox.type = "checkbox";
cbox.className = "points";
cbox.checked = checked;
cbox.dataset.idx = i;
cbox.onclick = cboxOnClick;
breakCell.appendChild(cbox);
line.appendChild(breakCell);
line.appendChild(c('td', i.toString()));
line.appendChild(c("td", i.toString()));
var fn = opMap[operatorList.fnArray[i]];
var decArgs = args;
if (fn === 'showText') {
if (fn === "showText") {
var glyphs = args[0];
var newArgs = [];
var str = [];
for (var j = 0; j < glyphs.length; j++) {
var glyph = glyphs[j];
if (typeof glyph === 'object' && glyph !== null) {
if (typeof glyph === "object" && glyph !== null) {
str.push(glyph.fontChar);
} else {
if (str.length > 0) {
newArgs.push(str.join(''));
newArgs.push(str.join(""));
str = [];
}
newArgs.push(glyph); // null or number
}
}
if (str.length > 0) {
newArgs.push(str.join(''));
newArgs.push(str.join(""));
}
decArgs = [newArgs];
}
line.appendChild(c('td', fn));
line.appendChild(c('td', JSON.stringify(simplifyArgs(decArgs))));
line.appendChild(c("td", fn));
line.appendChild(c("td", JSON.stringify(simplifyArgs(decArgs))));
}
if (operatorsToDisplay < operatorList.fnArray.length) {
line = c('tr');
var lastCell = c('td', '...');
line = c("tr");
var lastCell = c("td", "...");
lastCell.colspan = 4;
chunk.appendChild(lastCell);
}
@ -401,13 +412,13 @@ var Stepper = (function StepperClosure() {
var listener = function(e) {
switch (e.keyCode) {
case 83: // step
dom.removeEventListener('keydown', listener);
dom.removeEventListener("keydown", listener);
self.nextBreakPoint = self.currentIdx + 1;
self.goTo(-1);
callback();
break;
case 67: // continue
dom.removeEventListener('keydown', listener);
dom.removeEventListener("keydown", listener);
var breakPoint = self.getNextBreakPoint();
self.nextBreakPoint = breakPoint;
self.goTo(-1);
@ -415,15 +426,15 @@ var Stepper = (function StepperClosure() {
break;
}
};
dom.addEventListener('keydown', listener);
dom.addEventListener("keydown", listener);
self.goTo(idx);
},
goTo: function goTo(idx) {
var allRows = this.panel.getElementsByClassName('line');
var allRows = this.panel.getElementsByClassName("line");
for (var x = 0, xx = allRows.length; x < xx; ++x) {
var row = allRows[x];
if ((row.dataset.idx | 0) === idx) {
row.style.backgroundColor = 'rgb(251,250,207)';
row.style.backgroundColor = "rgb(251,250,207)";
row.scrollIntoView();
} else {
row.style.backgroundColor = null;
@ -451,12 +462,12 @@ var Stats = (function Stats() {
}
return {
// Properties/functions needed by PDFBug.
id: 'Stats',
name: 'Stats',
id: "Stats",
name: "Stats",
panel: null,
manager: null,
init(pdfjsLib) {
this.panel.setAttribute('style', 'padding: 5px;');
this.panel.setAttribute("style", "padding: 5px;");
},
enabled: false,
active: false,
@ -471,16 +482,16 @@ var Stats = (function Stats() {
this.panel.removeChild(b.div);
stats.splice(statsIndex, 1);
}
var wrapper = document.createElement('div');
wrapper.className = 'stats';
var title = document.createElement('div');
title.className = 'title';
title.textContent = 'Page: ' + pageNumber;
var statsDiv = document.createElement('div');
var wrapper = document.createElement("div");
wrapper.className = "stats";
var title = document.createElement("div");
title.className = "title";
title.textContent = "Page: " + pageNumber;
var statsDiv = document.createElement("div");
statsDiv.textContent = stat.toString();
wrapper.appendChild(title);
wrapper.appendChild(statsDiv);
stats.push({ pageNumber, div: wrapper, });
stats.push({ pageNumber, div: wrapper });
stats.sort(function(a, b) {
return a.pageNumber - b.pageNumber;
});
@ -503,14 +514,11 @@ window.PDFBug = (function PDFBugClosure() {
var activePanel = null;
return {
tools: [
FontInspector,
StepperManager,
Stats
],
tools: [FontInspector, StepperManager, Stats],
enable(ids) {
var all = false, tools = this.tools;
if (ids.length === 1 && ids[0] === 'all') {
var all = false,
tools = this.tools;
if (ids.length === 1 && ids[0] === "all") {
all = true;
}
for (var i = 0; i < tools.length; ++i) {
@ -540,34 +548,37 @@ window.PDFBug = (function PDFBugClosure() {
* Panel
* ...
*/
var ui = document.createElement('div');
ui.id = 'PDFBug';
var ui = document.createElement("div");
ui.id = "PDFBug";
var controls = document.createElement('div');
controls.setAttribute('class', 'controls');
var controls = document.createElement("div");
controls.setAttribute("class", "controls");
ui.appendChild(controls);
var panels = document.createElement('div');
panels.setAttribute('class', 'panels');
var panels = document.createElement("div");
panels.setAttribute("class", "panels");
ui.appendChild(panels);
container.appendChild(ui);
container.style.right = panelWidth + 'px';
container.style.right = panelWidth + "px";
// Initialize all the debugging tools.
var tools = this.tools;
var self = this;
for (var i = 0; i < tools.length; ++i) {
var tool = tools[i];
var panel = document.createElement('div');
var panelButton = document.createElement('button');
var panel = document.createElement("div");
var panelButton = document.createElement("button");
panelButton.textContent = tool.name;
panelButton.addEventListener('click', (function(selected) {
return function(event) {
event.preventDefault();
self.selectPanel(selected);
};
})(i));
panelButton.addEventListener(
"click",
(function(selected) {
return function(event) {
event.preventDefault();
self.selectPanel(selected);
};
})(i)
);
controls.appendChild(panelButton);
panels.appendChild(panel);
tool.panel = panel;
@ -575,9 +586,13 @@ window.PDFBug = (function PDFBugClosure() {
if (tool.enabled) {
tool.init(pdfjsLib);
} else {
panel.textContent = tool.name + ' is disabled. To enable add ' +
' "' + tool.id + '" to the pdfBug parameter ' +
'and refresh (separate multiple by commas).';
panel.textContent =
tool.name +
" is disabled. To enable add " +
' "' +
tool.id +
'" to the pdfBug parameter ' +
"and refresh (separate multiple by commas).";
}
buttons.push(panelButton);
}
@ -591,7 +606,7 @@ window.PDFBug = (function PDFBugClosure() {
}
},
selectPanel(index) {
if (typeof index !== 'number') {
if (typeof index !== "number") {
index = this.tools.indexOf(index);
}
if (index === activePanel) {
@ -601,13 +616,13 @@ window.PDFBug = (function PDFBugClosure() {
var tools = this.tools;
for (var j = 0; j < tools.length; ++j) {
if (j === index) {
buttons[j].setAttribute('class', 'active');
buttons[j].setAttribute("class", "active");
tools[j].active = true;
tools[j].panel.removeAttribute('hidden');
tools[j].panel.removeAttribute("hidden");
} else {
buttons[j].setAttribute('class', '');
buttons[j].setAttribute("class", "");
tools[j].active = false;
tools[j].panel.setAttribute('hidden', 'true');
tools[j].panel.setAttribute("hidden", "true");
}
}
},

View file

@ -14,27 +14,31 @@
*/
import {
apiCompatibilityParams, createObjectURL, createValidAbsoluteUrl
} from 'pdfjs-lib';
apiCompatibilityParams,
createObjectURL,
createValidAbsoluteUrl,
} from "pdfjs-lib";
if (typeof PDFJSDev !== 'undefined' && !PDFJSDev.test('CHROME || GENERIC')) {
throw new Error('Module "pdfjs-web/download_manager" shall not be used ' +
'outside CHROME and GENERIC builds.');
if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("CHROME || GENERIC")) {
throw new Error(
'Module "pdfjs-web/download_manager" shall not be used ' +
"outside CHROME and GENERIC builds."
);
}
const DISABLE_CREATE_OBJECT_URL =
apiCompatibilityParams.disableCreateObjectURL || false;
function download(blobUrl, filename) {
let a = document.createElement('a');
let a = document.createElement("a");
if (!a.click) {
throw new Error('DownloadManager: "a.click()" is not supported.');
}
a.href = blobUrl;
a.target = '_parent';
a.target = "_parent";
// Use a.download if available. This increases the likelihood that
// the file is downloaded instead of opened by another PDF plugin.
if ('download' in a) {
if ("download" in a) {
a.download = filename;
}
// <a> must be in the document for IE and recent Firefox versions,
@ -45,24 +49,28 @@ function download(blobUrl, filename) {
}
class DownloadManager {
constructor({ disableCreateObjectURL = DISABLE_CREATE_OBJECT_URL, }) {
constructor({ disableCreateObjectURL = DISABLE_CREATE_OBJECT_URL }) {
this.disableCreateObjectURL = disableCreateObjectURL;
}
downloadUrl(url, filename) {
if (!createValidAbsoluteUrl(url, 'http://example.com')) {
if (!createValidAbsoluteUrl(url, "http://example.com")) {
return; // restricted/invalid URL
}
download(url + '#pdfjs.action=download', filename);
download(url + "#pdfjs.action=download", filename);
}
downloadData(data, filename, contentType) {
if (navigator.msSaveBlob) { // IE10 and above
navigator.msSaveBlob(new Blob([data], { type: contentType, }), filename);
if (navigator.msSaveBlob) {
// IE10 and above
navigator.msSaveBlob(new Blob([data], { type: contentType }), filename);
return;
}
let blobUrl = createObjectURL(data, contentType,
this.disableCreateObjectURL);
let blobUrl = createObjectURL(
data,
contentType,
this.disableCreateObjectURL
);
download(blobUrl, filename);
}
@ -86,6 +94,4 @@ class DownloadManager {
}
}
export {
DownloadManager,
};
export { DownloadManager };

View file

@ -13,26 +13,26 @@
* limitations under the License.
*/
import { AppOptions } from './app_options';
import { CSS_UNITS } from './ui_utils';
import { PDFPrintServiceFactory } from './app';
import { shadow } from 'pdfjs-lib';
import { AppOptions } from "./app_options";
import { CSS_UNITS } from "./ui_utils";
import { PDFPrintServiceFactory } from "./app";
import { shadow } from "pdfjs-lib";
// Creates a placeholder with div and canvas with right size for the page.
function composePage(pdfDocument, pageNumber, size, printContainer) {
let canvas = document.createElement('canvas');
let canvas = document.createElement("canvas");
// The size of the canvas in pixels for printing.
const PRINT_RESOLUTION = AppOptions.get('printResolution') || 150;
const PRINT_RESOLUTION = AppOptions.get("printResolution") || 150;
const PRINT_UNITS = PRINT_RESOLUTION / 72.0;
canvas.width = Math.floor(size.width * PRINT_UNITS);
canvas.height = Math.floor(size.height * PRINT_UNITS);
// The physical size of the canvas as specified by the PDF document.
canvas.style.width = Math.floor(size.width * CSS_UNITS) + 'px';
canvas.style.height = Math.floor(size.height * CSS_UNITS) + 'px';
canvas.style.width = Math.floor(size.width * CSS_UNITS) + "px";
canvas.style.height = Math.floor(size.height * CSS_UNITS) + "px";
let canvasWrapper = document.createElement('div');
let canvasWrapper = document.createElement("div");
canvasWrapper.appendChild(canvas);
printContainer.appendChild(canvasWrapper);
@ -41,31 +41,37 @@ function composePage(pdfDocument, pageNumber, size, printContainer) {
let ctx = obj.context;
ctx.save();
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
pdfDocument.getPage(pageNumber).then(function(pdfPage) {
let renderContext = {
canvasContext: ctx,
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
viewport: pdfPage.getViewport({ scale: 1, rotation: size.rotation, }),
intent: 'print',
};
return pdfPage.render(renderContext).promise;
}).then(function() {
// Tell the printEngine that rendering this canvas/page has finished.
obj.done();
}, function(error) {
console.error(error);
// Tell the printEngine that rendering this canvas/page has failed.
// This will make the print process stop.
if ('abort' in obj) {
obj.abort();
} else {
obj.done();
}
});
pdfDocument
.getPage(pageNumber)
.then(function(pdfPage) {
let renderContext = {
canvasContext: ctx,
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
viewport: pdfPage.getViewport({ scale: 1, rotation: size.rotation }),
intent: "print",
};
return pdfPage.render(renderContext).promise;
})
.then(
function() {
// Tell the printEngine that rendering this canvas/page has finished.
obj.done();
},
function(error) {
console.error(error);
// Tell the printEngine that rendering this canvas/page has failed.
// This will make the print process stop.
if ("abort" in obj) {
obj.abort();
} else {
obj.done();
}
}
);
};
}
@ -77,10 +83,10 @@ function FirefoxPrintService(pdfDocument, pagesOverview, printContainer) {
FirefoxPrintService.prototype = {
layout() {
const { pdfDocument, pagesOverview, printContainer, } = this;
const { pdfDocument, pagesOverview, printContainer } = this;
const body = document.querySelector('body');
body.setAttribute('data-pdfjsprinting', true);
const body = document.querySelector("body");
body.setAttribute("data-pdfjsprinting", true);
for (let i = 0, ii = pagesOverview.length; i < ii; ++i) {
composePage(pdfDocument, i + 1, pagesOverview[i], printContainer);
@ -88,19 +94,19 @@ FirefoxPrintService.prototype = {
},
destroy() {
this.printContainer.textContent = '';
this.printContainer.textContent = "";
const body = document.querySelector('body');
body.removeAttribute('data-pdfjsprinting');
const body = document.querySelector("body");
body.removeAttribute("data-pdfjsprinting");
},
};
PDFPrintServiceFactory.instance = {
get supportsPrinting() {
let canvas = document.createElement('canvas');
let value = 'mozPrintCallback' in canvas;
let canvas = document.createElement("canvas");
let value = "mozPrintCallback" in canvas;
return shadow(this, 'supportsPrinting', value);
return shadow(this, "supportsPrinting", value);
},
createPrintService(pdfDocument, pagesOverview, printContainer) {
@ -108,6 +114,4 @@ PDFPrintServiceFactory.instance = {
},
};
export {
FirefoxPrintService,
};
export { FirefoxPrintService };

View file

@ -13,16 +13,20 @@
* limitations under the License.
*/
import '../extensions/firefox/tools/l10n';
import { createObjectURL, PDFDataRangeTransport, shadow } from 'pdfjs-lib';
import { BasePreferences } from './preferences';
import { DEFAULT_SCALE_VALUE } from './ui_utils';
import { PDFViewerApplication } from './app';
import "../extensions/firefox/tools/l10n";
import { createObjectURL, PDFDataRangeTransport, shadow } from "pdfjs-lib";
import { BasePreferences } from "./preferences";
import { DEFAULT_SCALE_VALUE } from "./ui_utils";
import { PDFViewerApplication } from "./app";
if (typeof PDFJSDev === 'undefined' ||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
throw new Error('Module "pdfjs-web/firefoxcom" shall not be used outside ' +
'FIREFOX and MOZCENTRAL builds.');
if (
typeof PDFJSDev === "undefined" ||
!PDFJSDev.test("FIREFOX || MOZCENTRAL")
) {
throw new Error(
'Module "pdfjs-web/firefoxcom" shall not be used outside ' +
"FIREFOX and MOZCENTRAL builds."
);
}
let FirefoxCom = (function FirefoxComClosure() {
@ -37,12 +41,15 @@ let FirefoxCom = (function FirefoxComClosure() {
* @returns {*} The response.
*/
requestSync(action, data) {
let request = document.createTextNode('');
let request = document.createTextNode("");
document.documentElement.appendChild(request);
let sender = document.createEvent('CustomEvent');
sender.initCustomEvent('pdf.js.message', true, false,
{ action, data, sync: true, });
let sender = document.createEvent("CustomEvent");
sender.initCustomEvent("pdf.js.message", true, false, {
action,
data,
sync: true,
});
request.dispatchEvent(sender);
let response = sender.detail.response;
document.documentElement.removeChild(request);
@ -58,22 +65,22 @@ let FirefoxCom = (function FirefoxComClosure() {
* with one data argument.
*/
request(action, data, callback) {
let request = document.createTextNode('');
let request = document.createTextNode("");
if (callback) {
document.addEventListener('pdf.js.response', function listener(event) {
document.addEventListener("pdf.js.response", function listener(event) {
let node = event.target;
let response = event.detail.response;
document.documentElement.removeChild(node);
document.removeEventListener('pdf.js.response', listener);
document.removeEventListener("pdf.js.response", listener);
return callback(response);
});
}
document.documentElement.appendChild(request);
let sender = document.createEvent('CustomEvent');
sender.initCustomEvent('pdf.js.message', true, false, {
let sender = document.createEvent("CustomEvent");
sender.initCustomEvent("pdf.js.message", true, false, {
action,
data,
sync: false,
@ -90,7 +97,7 @@ class DownloadManager {
}
downloadUrl(url, filename) {
FirefoxCom.request('download', {
FirefoxCom.request("download", {
originalUrl: url,
filename,
});
@ -99,7 +106,7 @@ class DownloadManager {
downloadData(data, filename, contentType) {
let blobUrl = createObjectURL(data, contentType);
FirefoxCom.request('download', {
FirefoxCom.request("download", {
blobUrl,
originalUrl: blobUrl,
filename,
@ -109,31 +116,35 @@ class DownloadManager {
download(blob, url, filename) {
let blobUrl = URL.createObjectURL(blob);
let onResponse = (err) => {
let onResponse = err => {
if (err && this.onerror) {
this.onerror(err);
}
URL.revokeObjectURL(blobUrl);
};
FirefoxCom.request('download', {
blobUrl,
originalUrl: url,
filename,
}, onResponse);
FirefoxCom.request(
"download",
{
blobUrl,
originalUrl: url,
filename,
},
onResponse
);
}
}
class FirefoxPreferences extends BasePreferences {
async _writeToStorage(prefObj) {
return new Promise(function(resolve) {
FirefoxCom.request('setPreferences', prefObj, resolve);
FirefoxCom.request("setPreferences", prefObj, resolve);
});
}
async _readFromStorage(prefObj) {
return new Promise(function(resolve) {
FirefoxCom.request('getPreferences', prefObj, function(prefStr) {
FirefoxCom.request("getPreferences", prefObj, function(prefStr) {
let readPrefs = JSON.parse(prefStr);
resolve(readPrefs);
});
@ -165,24 +176,24 @@ class MozL10n {
(function listenFindEvents() {
const events = [
'find',
'findagain',
'findhighlightallchange',
'findcasesensitivitychange',
'findentirewordchange',
'findbarclose',
"find",
"findagain",
"findhighlightallchange",
"findcasesensitivitychange",
"findentirewordchange",
"findbarclose",
];
const handleEvent = function({ type, detail, }) {
const handleEvent = function({ type, detail }) {
if (!PDFViewerApplication.initialized) {
return;
}
if (type === 'findbarclose') {
PDFViewerApplication.eventBus.dispatch(type, { source: window, });
if (type === "findbarclose") {
PDFViewerApplication.eventBus.dispatch(type, { source: window });
return;
}
PDFViewerApplication.eventBus.dispatch('find', {
PDFViewerApplication.eventBus.dispatch("find", {
source: window,
type: type.substring('find'.length),
type: type.substring("find".length),
query: detail.query,
phraseSearch: true,
caseSensitive: !!detail.caseSensitive,
@ -198,22 +209,20 @@ class MozL10n {
})();
(function listenZoomEvents() {
const events = [
'zoomin',
'zoomout',
'zoomreset',
];
const handleEvent = function({ type, detail, }) {
const events = ["zoomin", "zoomout", "zoomreset"];
const handleEvent = function({ type, detail }) {
if (!PDFViewerApplication.initialized) {
return;
}
// Avoid attempting to needlessly reset the zoom level *twice* in a row,
// when using the `Ctrl + 0` keyboard shortcut.
if (type === 'zoomreset' && // eslint-disable-next-line max-len
PDFViewerApplication.pdfViewer.currentScaleValue === DEFAULT_SCALE_VALUE) {
if (
type === "zoomreset" && // eslint-disable-next-line max-len
PDFViewerApplication.pdfViewer.currentScaleValue === DEFAULT_SCALE_VALUE
) {
return;
}
PDFViewerApplication.eventBus.dispatch(type, { source: window, });
PDFViewerApplication.eventBus.dispatch(type, { source: window });
};
for (const event of events) {
@ -223,68 +232,74 @@ class MozL10n {
class FirefoxComDataRangeTransport extends PDFDataRangeTransport {
requestDataRange(begin, end) {
FirefoxCom.request('requestDataRange', { begin, end, });
FirefoxCom.request("requestDataRange", { begin, end });
}
abort() {
// Sync call to ensure abort is really started.
FirefoxCom.requestSync('abortLoading', null);
FirefoxCom.requestSync("abortLoading", null);
}
}
PDFViewerApplication.externalServices = {
updateFindControlState(data) {
FirefoxCom.request('updateFindControlState', data);
FirefoxCom.request("updateFindControlState", data);
},
updateFindMatchesCount(data) {
FirefoxCom.request('updateFindMatchesCount', data);
FirefoxCom.request("updateFindMatchesCount", data);
},
initPassiveLoading(callbacks) {
let pdfDataRangeTransport;
window.addEventListener('message', function windowMessage(e) {
window.addEventListener("message", function windowMessage(e) {
if (e.source !== null) {
// The message MUST originate from Chrome code.
console.warn('Rejected untrusted message from ' + e.origin);
console.warn("Rejected untrusted message from " + e.origin);
return;
}
let args = e.data;
if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) {
if (typeof args !== "object" || !("pdfjsLoadAction" in args)) {
return;
}
switch (args.pdfjsLoadAction) {
case 'supportsRangedLoading':
pdfDataRangeTransport =
new FirefoxComDataRangeTransport(args.length, args.data, args.done);
case "supportsRangedLoading":
pdfDataRangeTransport = new FirefoxComDataRangeTransport(
args.length,
args.data,
args.done
);
callbacks.onOpenWithTransport(args.pdfUrl, args.length,
pdfDataRangeTransport);
callbacks.onOpenWithTransport(
args.pdfUrl,
args.length,
pdfDataRangeTransport
);
break;
case 'range':
case "range":
pdfDataRangeTransport.onDataRange(args.begin, args.chunk);
break;
case 'rangeProgress':
case "rangeProgress":
pdfDataRangeTransport.onDataProgress(args.loaded);
break;
case 'progressiveRead':
case "progressiveRead":
pdfDataRangeTransport.onDataProgressiveRead(args.chunk);
// Don't forget to report loading progress as well, since otherwise
// the loadingBar won't update when `disableRange=true` is set.
pdfDataRangeTransport.onDataProgress(args.loaded, args.total);
break;
case 'progressiveDone':
case "progressiveDone":
if (pdfDataRangeTransport) {
pdfDataRangeTransport.onDataProgressiveDone();
}
break;
case 'progress':
case "progress":
callbacks.onProgress(args.loaded, args.total);
break;
case 'complete':
case "complete":
if (!args.data) {
callbacks.onError(args.errorCode);
break;
@ -293,15 +308,15 @@ PDFViewerApplication.externalServices = {
break;
}
});
FirefoxCom.requestSync('initPassiveLoading', null);
FirefoxCom.requestSync("initPassiveLoading", null);
},
fallback(data, callback) {
FirefoxCom.request('fallback', data, callback);
FirefoxCom.request("fallback", data, callback);
},
reportTelemetry(data) {
FirefoxCom.request('reportTelemetry', JSON.stringify(data));
FirefoxCom.request("reportTelemetry", JSON.stringify(data));
},
createDownloadManager(options) {
@ -319,38 +334,35 @@ PDFViewerApplication.externalServices = {
},
get supportsIntegratedFind() {
let support = FirefoxCom.requestSync('supportsIntegratedFind');
return shadow(this, 'supportsIntegratedFind', support);
let support = FirefoxCom.requestSync("supportsIntegratedFind");
return shadow(this, "supportsIntegratedFind", support);
},
get supportsDocumentFonts() {
let support = FirefoxCom.requestSync('supportsDocumentFonts');
return shadow(this, 'supportsDocumentFonts', support);
let support = FirefoxCom.requestSync("supportsDocumentFonts");
return shadow(this, "supportsDocumentFonts", support);
},
get supportsDocumentColors() {
let support = FirefoxCom.requestSync('supportsDocumentColors');
return shadow(this, 'supportsDocumentColors', support);
let support = FirefoxCom.requestSync("supportsDocumentColors");
return shadow(this, "supportsDocumentColors", support);
},
get supportedMouseWheelZoomModifierKeys() {
let support = FirefoxCom.requestSync('supportedMouseWheelZoomModifierKeys');
return shadow(this, 'supportedMouseWheelZoomModifierKeys', support);
let support = FirefoxCom.requestSync("supportedMouseWheelZoomModifierKeys");
return shadow(this, "supportedMouseWheelZoomModifierKeys", support);
},
};
// l10n.js for Firefox extension expects services to be set.
document.mozL10n.setExternalLocalizerServices({
getLocale() {
return FirefoxCom.requestSync('getLocale', null);
return FirefoxCom.requestSync("getLocale", null);
},
getStrings(key) {
return FirefoxCom.requestSync('getStrings', key);
return FirefoxCom.requestSync("getStrings", key);
},
});
export {
DownloadManager,
FirefoxCom,
};
export { DownloadManager, FirefoxCom };

View file

@ -13,25 +13,27 @@
* limitations under the License.
*/
import { DefaultExternalServices, PDFViewerApplication } from './app';
import { BasePreferences } from './preferences';
import { DownloadManager } from './download_manager';
import { GenericL10n } from './genericl10n';
import { DefaultExternalServices, PDFViewerApplication } from "./app";
import { BasePreferences } from "./preferences";
import { DownloadManager } from "./download_manager";
import { GenericL10n } from "./genericl10n";
if (typeof PDFJSDev !== 'undefined' && !PDFJSDev.test('GENERIC')) {
throw new Error('Module "pdfjs-web/genericcom" shall not be used outside ' +
'GENERIC build.');
if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("GENERIC")) {
throw new Error(
'Module "pdfjs-web/genericcom" shall not be used outside ' +
"GENERIC build."
);
}
let GenericCom = {};
class GenericPreferences extends BasePreferences {
async _writeToStorage(prefObj) {
localStorage.setItem('pdfjs.preferences', JSON.stringify(prefObj));
localStorage.setItem("pdfjs.preferences", JSON.stringify(prefObj));
}
async _readFromStorage(prefObj) {
return JSON.parse(localStorage.getItem('pdfjs.preferences'));
return JSON.parse(localStorage.getItem("pdfjs.preferences"));
}
}
@ -42,11 +44,9 @@ GenericExternalServices.createDownloadManager = function(options) {
GenericExternalServices.createPreferences = function() {
return new GenericPreferences();
};
GenericExternalServices.createL10n = function({ locale = 'en-US', }) {
GenericExternalServices.createL10n = function({ locale = "en-US" }) {
return new GenericL10n(locale);
};
PDFViewerApplication.externalServices = GenericExternalServices;
export {
GenericCom,
};
export { GenericCom };

View file

@ -13,7 +13,7 @@
* limitations under the License.
*/
import '../external/webL10n/l10n';
import "../external/webL10n/l10n";
let webL10n = document.webL10n;
@ -48,6 +48,4 @@ class GenericL10n {
}
}
export {
GenericL10n,
};
export { GenericL10n };

View file

@ -26,7 +26,7 @@
function GrabToPan(options) {
this.element = options.element;
this.document = options.element.ownerDocument;
if (typeof options.ignoreTarget === 'function') {
if (typeof options.ignoreTarget === "function") {
this.ignoreTarget = options.ignoreTarget;
}
this.onActiveChanged = options.onActiveChanged;
@ -42,14 +42,14 @@ function GrabToPan(options) {
// This overlay will be inserted in the document when the mouse moves during
// a grab operation, to ensure that the cursor has the desired appearance.
var overlay = this.overlay = document.createElement('div');
overlay.className = 'grab-to-pan-grabbing';
var overlay = (this.overlay = document.createElement("div"));
overlay.className = "grab-to-pan-grabbing";
}
GrabToPan.prototype = {
/**
* Class name of element which can be grabbed
*/
CSS_CLASS_GRAB: 'grab-to-pan-grab',
CSS_CLASS_GRAB: "grab-to-pan-grab",
/**
* Bind a mousedown event to the element to enable grab-detection.
@ -57,7 +57,7 @@ GrabToPan.prototype = {
activate: function GrabToPan_activate() {
if (!this.active) {
this.active = true;
this.element.addEventListener('mousedown', this._onmousedown, true);
this.element.addEventListener("mousedown", this._onmousedown, true);
this.element.classList.add(this.CSS_CLASS_GRAB);
if (this.onActiveChanged) {
this.onActiveChanged(true);
@ -71,7 +71,7 @@ GrabToPan.prototype = {
deactivate: function GrabToPan_deactivate() {
if (this.active) {
this.active = false;
this.element.removeEventListener('mousedown', this._onmousedown, true);
this.element.removeEventListener("mousedown", this._onmousedown, true);
this._endPan();
this.element.classList.remove(this.CSS_CLASS_GRAB);
if (this.onActiveChanged) {
@ -99,7 +99,7 @@ GrabToPan.prototype = {
// Use matchesSelector to check whether the clicked element
// is (a child of) an input element / link
return node[matchesSelector](
'a[href], a[href] *, input, textarea, button, button *, select, option'
"a[href], a[href] *, input, textarea, button, button *, select, option"
);
},
@ -124,12 +124,12 @@ GrabToPan.prototype = {
this.scrollTopStart = this.element.scrollTop;
this.clientXStart = event.clientX;
this.clientYStart = event.clientY;
this.document.addEventListener('mousemove', this._onmousemove, true);
this.document.addEventListener('mouseup', this._endPan, true);
this.document.addEventListener("mousemove", this._onmousemove, true);
this.document.addEventListener("mouseup", this._endPan, true);
// When a scroll event occurs before a mousemove, assume that the user
// dragged a scrollbar (necessary for Opera Presto, Safari and IE)
// (not needed for Chrome/Firefox)
this.element.addEventListener('scroll', this._endPan, true);
this.element.addEventListener("scroll", this._endPan, true);
event.preventDefault();
event.stopPropagation();
@ -143,7 +143,7 @@ GrabToPan.prototype = {
* @private
*/
_onmousemove: function GrabToPan__onmousemove(event) {
this.element.removeEventListener('scroll', this._endPan, true);
this.element.removeEventListener("scroll", this._endPan, true);
if (isLeftMouseReleased(event)) {
this._endPan();
return;
@ -156,7 +156,7 @@ GrabToPan.prototype = {
this.element.scrollTo({
top: scrollTop,
left: scrollLeft,
behavior: 'instant',
behavior: "instant",
});
} else {
this.element.scrollTop = scrollTop;
@ -171,9 +171,9 @@ GrabToPan.prototype = {
* @private
*/
_endPan: function GrabToPan__endPan() {
this.element.removeEventListener('scroll', this._endPan, true);
this.document.removeEventListener('mousemove', this._onmousemove, true);
this.document.removeEventListener('mouseup', this._endPan, true);
this.element.removeEventListener("scroll", this._endPan, true);
this.document.removeEventListener("mousemove", this._onmousemove, true);
this.document.removeEventListener("mouseup", this._endPan, true);
// Note: ChildNode.remove doesn't throw if the parentNode is undefined.
this.overlay.remove();
},
@ -181,12 +181,12 @@ GrabToPan.prototype = {
// Get the correct (vendor-prefixed) name of the matches method.
var matchesSelector;
['webkitM', 'mozM', 'msM', 'oM', 'm'].some(function(prefix) {
var name = prefix + 'atches';
["webkitM", "mozM", "msM", "oM", "m"].some(function(prefix) {
var name = prefix + "atches";
if (name in document.documentElement) {
matchesSelector = name;
}
name += 'Selector';
name += "Selector";
if (name in document.documentElement) {
matchesSelector = name;
}
@ -199,8 +199,9 @@ var isNotIEorIsIE10plus = !document.documentMode || document.documentMode > 9;
var chrome = window.chrome;
var isChrome15OrOpera15plus = chrome && (chrome.webstore || chrome.app);
// ^ Chrome 15+ ^ Opera 15+
var isSafari6plus = /Apple/.test(navigator.vendor) &&
/Version\/([6-9]\d*|[1-5]\d+)/.test(navigator.userAgent);
var isSafari6plus =
/Apple/.test(navigator.vendor) &&
/Version\/([6-9]\d*|[1-5]\d+)/.test(navigator.userAgent);
/**
* Whether the left mouse is not pressed.
@ -209,7 +210,7 @@ var isSafari6plus = /Apple/.test(navigator.vendor) &&
* False if unsure or if the left mouse button is pressed.
*/
function isLeftMouseReleased(event) {
if ('buttons' in event && isNotIEorIsIE10plus) {
if ("buttons" in event && isNotIEorIsIE10plus) {
// http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-buttons
// Firefox 15+
// Internet Explorer 10+
@ -224,6 +225,4 @@ function isLeftMouseReleased(event) {
return false;
}
export {
GrabToPan,
};
export { GrabToPan };

View file

@ -99,14 +99,14 @@ class IPDFHistory {
/**
* @param {Object} params
*/
initialize({ fingerprint, resetHistory = false, updateUrl = false, }) {}
initialize({ fingerprint, resetHistory = false, updateUrl = false }) {}
reset() {}
/**
* @param {Object} params
*/
push({ namedDest = null, explicitDest, pageNumber, }) {}
push({ namedDest = null, explicitDest, pageNumber }) {}
pushCurrentPosition() {}
@ -148,8 +148,12 @@ class IPDFTextLayerFactory {
* @param {boolean} enhanceTextSelection
* @returns {TextLayerBuilder}
*/
createTextLayerBuilder(textLayerDiv, pageIndex, viewport,
enhanceTextSelection = false) {}
createTextLayerBuilder(
textLayerDiv,
pageIndex,
viewport,
enhanceTextSelection = false
) {}
}
/**
@ -165,9 +169,13 @@ class IPDFAnnotationLayerFactory {
* @param {IL10n} l10n
* @returns {AnnotationLayerBuilder}
*/
createAnnotationLayerBuilder(pageDiv, pdfPage, imageResourcesPath = '',
renderInteractiveForms = false,
l10n = undefined) {}
createAnnotationLayerBuilder(
pageDiv,
pdfPage,
imageResourcesPath = "",
renderInteractiveForms = false,
l10n = undefined
) {}
}
/**
@ -193,14 +201,14 @@ class IL10n {
* @param {string} fallback
* @returns {Promise<string>}
*/
async get(key, args, fallback) { }
async get(key, args, fallback) {}
/**
* Translates HTML element.
* @param {HTMLElement} element
* @returns {Promise<void>}
*/
async translate(element) { }
async translate(element) {}
}
export {

View file

@ -37,13 +37,17 @@ class OverlayManager {
* @returns {Promise} A promise that is resolved when the overlay has been
* registered.
*/
async register(name, element, callerCloseMethod = null,
canForceClose = false) {
async register(
name,
element,
callerCloseMethod = null,
canForceClose = false
) {
let container;
if (!name || !element || !(container = element.parentNode)) {
throw new Error('Not enough parameters.');
throw new Error("Not enough parameters.");
} else if (this._overlays[name]) {
throw new Error('The overlay is already registered.');
throw new Error("The overlay is already registered.");
}
this._overlays[name] = {
element,
@ -60,9 +64,9 @@ class OverlayManager {
*/
async unregister(name) {
if (!this._overlays[name]) {
throw new Error('The overlay does not exist.');
throw new Error("The overlay does not exist.");
} else if (this._active === name) {
throw new Error('The overlay cannot be removed while it is active.');
throw new Error("The overlay cannot be removed while it is active.");
}
delete this._overlays[name];
}
@ -74,21 +78,21 @@ class OverlayManager {
*/
async open(name) {
if (!this._overlays[name]) {
throw new Error('The overlay does not exist.');
throw new Error("The overlay does not exist.");
} else if (this._active) {
if (this._overlays[name].canForceClose) {
this._closeThroughCaller();
} else if (this._active === name) {
throw new Error('The overlay is already active.');
throw new Error("The overlay is already active.");
} else {
throw new Error('Another overlay is currently active.');
throw new Error("Another overlay is currently active.");
}
}
this._active = name;
this._overlays[this._active].element.classList.remove('hidden');
this._overlays[this._active].container.classList.remove('hidden');
this._overlays[this._active].element.classList.remove("hidden");
this._overlays[this._active].container.classList.remove("hidden");
window.addEventListener('keydown', this._keyDownBound);
window.addEventListener("keydown", this._keyDownBound);
}
/**
@ -98,24 +102,25 @@ class OverlayManager {
*/
async close(name) {
if (!this._overlays[name]) {
throw new Error('The overlay does not exist.');
throw new Error("The overlay does not exist.");
} else if (!this._active) {
throw new Error('The overlay is currently not active.');
throw new Error("The overlay is currently not active.");
} else if (this._active !== name) {
throw new Error('Another overlay is currently active.');
throw new Error("Another overlay is currently active.");
}
this._overlays[this._active].container.classList.add('hidden');
this._overlays[this._active].element.classList.add('hidden');
this._overlays[this._active].container.classList.add("hidden");
this._overlays[this._active].element.classList.add("hidden");
this._active = null;
window.removeEventListener('keydown', this._keyDownBound);
window.removeEventListener("keydown", this._keyDownBound);
}
/**
* @private
*/
_keyDown(evt) {
if (this._active && evt.keyCode === 27) { // Esc key.
if (this._active && evt.keyCode === 27) {
// Esc key.
this._closeThroughCaller();
evt.preventDefault();
}
@ -134,6 +139,4 @@ class OverlayManager {
}
}
export {
OverlayManager,
};
export { OverlayManager };

View file

@ -13,8 +13,8 @@
* limitations under the License.
*/
import { NullL10n } from './ui_utils';
import { PasswordResponses } from 'pdfjs-lib';
import { NullL10n } from "./ui_utils";
import { PasswordResponses } from "pdfjs-lib";
/**
* @typedef {Object} PasswordPromptOptions
@ -49,16 +49,21 @@ class PasswordPrompt {
this.reason = null;
// Attach the event listeners.
this.submitButton.addEventListener('click', this.verify.bind(this));
this.cancelButton.addEventListener('click', this.close.bind(this));
this.input.addEventListener('keydown', (e) => {
if (e.keyCode === 13) { // Enter key
this.submitButton.addEventListener("click", this.verify.bind(this));
this.cancelButton.addEventListener("click", this.close.bind(this));
this.input.addEventListener("keydown", e => {
if (e.keyCode === 13) {
// Enter key
this.verify();
}
});
this.overlayManager.register(this.overlayName, this.container,
this.close.bind(this), true);
this.overlayManager.register(
this.overlayName,
this.container,
this.close.bind(this),
true
);
}
open() {
@ -67,14 +72,20 @@ class PasswordPrompt {
let promptString;
if (this.reason === PasswordResponses.INCORRECT_PASSWORD) {
promptString = this.l10n.get('password_invalid', null,
'Invalid password. Please try again.');
promptString = this.l10n.get(
"password_invalid",
null,
"Invalid password. Please try again."
);
} else {
promptString = this.l10n.get('password_label', null,
'Enter the password to open this PDF file.');
promptString = this.l10n.get(
"password_label",
null,
"Enter the password to open this PDF file."
);
}
promptString.then((msg) => {
promptString.then(msg => {
this.label.textContent = msg;
});
});
@ -82,7 +93,7 @@ class PasswordPrompt {
close() {
this.overlayManager.close(this.overlayName).then(() => {
this.input.value = '';
this.input.value = "";
});
}
@ -100,6 +111,4 @@ class PasswordPrompt {
}
}
export {
PasswordPrompt,
};
export { PasswordPrompt };

View file

@ -14,9 +14,11 @@
*/
import {
createObjectURL, createPromiseCapability, getFilenameFromUrl,
removeNullCharacters
} from 'pdfjs-lib';
createObjectURL,
createPromiseCapability,
getFilenameFromUrl,
removeNullCharacters,
} from "pdfjs-lib";
/**
* @typedef {Object} PDFAttachmentViewerOptions
@ -34,22 +36,24 @@ class PDFAttachmentViewer {
/**
* @param {PDFAttachmentViewerOptions} options
*/
constructor({ container, eventBus, downloadManager, }) {
constructor({ container, eventBus, downloadManager }) {
this.container = container;
this.eventBus = eventBus;
this.downloadManager = downloadManager;
this.reset();
this.eventBus.on('fileattachmentannotation',
this._appendAttachment.bind(this));
this.eventBus.on(
"fileattachmentannotation",
this._appendAttachment.bind(this)
);
}
reset(keepRenderedCapability = false) {
this.attachments = null;
// Remove the attachments from the DOM.
this.container.textContent = '';
this.container.textContent = "";
if (!keepRenderedCapability) {
// NOTE: The *only* situation in which the `_renderedCapability` should
@ -64,7 +68,7 @@ class PDFAttachmentViewer {
_dispatchEvent(attachmentsCount) {
this._renderedCapability.resolve();
this.eventBus.dispatch('attachmentsloaded', {
this.eventBus.dispatch("attachmentsloaded", {
source: this,
attachmentsCount,
});
@ -76,26 +80,29 @@ class PDFAttachmentViewer {
_bindPdfLink(button, content, filename) {
if (this.downloadManager.disableCreateObjectURL) {
throw new Error(
'bindPdfLink: Unsupported "disableCreateObjectURL" value.');
'bindPdfLink: Unsupported "disableCreateObjectURL" value.'
);
}
let blobUrl;
button.onclick = function() {
if (!blobUrl) {
blobUrl = createObjectURL(content, 'application/pdf');
blobUrl = createObjectURL(content, "application/pdf");
}
let viewerUrl;
if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
// The current URL is the viewer, let's use it and append the file.
viewerUrl = '?file=' + encodeURIComponent(blobUrl + '#' + filename);
} else if (PDFJSDev.test('CHROME')) {
viewerUrl = "?file=" + encodeURIComponent(blobUrl + "#" + filename);
} else if (PDFJSDev.test("CHROME")) {
// In the Chrome extension, the URL is rewritten using the history API
// in viewer.js, so an absolute URL must be generated.
// eslint-disable-next-line no-undef
viewerUrl = chrome.runtime.getURL('/content/web/viewer.html') +
'?file=' + encodeURIComponent(blobUrl + '#' + filename);
} else if (PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
viewerUrl =
chrome.runtime.getURL("/content/web/viewer.html") +
"?file=" +
encodeURIComponent(blobUrl + "#" + filename);
} else if (PDFJSDev.test("FIREFOX || MOZCENTRAL")) {
// Let Firefox's content handler catch the URL and display the PDF.
viewerUrl = blobUrl + '?' + encodeURIComponent(filename);
viewerUrl = blobUrl + "?" + encodeURIComponent(filename);
}
window.open(viewerUrl);
return false;
@ -107,7 +114,7 @@ class PDFAttachmentViewer {
*/
_bindLink(button, content, filename) {
button.onclick = () => {
this.downloadManager.downloadData(content, filename, '');
this.downloadManager.downloadData(content, filename, "");
return false;
};
}
@ -115,7 +122,7 @@ class PDFAttachmentViewer {
/**
* @param {PDFAttachmentViewerRenderParameters} params
*/
render({ attachments, keepRenderedCapability = false, }) {
render({ attachments, keepRenderedCapability = false }) {
let attachmentsCount = 0;
if (this.attachments) {
@ -137,12 +144,14 @@ class PDFAttachmentViewer {
let item = attachments[names[i]];
let filename = removeNullCharacters(getFilenameFromUrl(item.filename));
let div = document.createElement('div');
div.className = 'attachmentsItem';
let button = document.createElement('button');
let div = document.createElement("div");
div.className = "attachmentsItem";
let button = document.createElement("button");
button.textContent = filename;
if (/\.pdf$/i.test(filename) &&
!this.downloadManager.disableCreateObjectURL) {
if (
/\.pdf$/i.test(filename) &&
!this.downloadManager.disableCreateObjectURL
) {
this._bindPdfLink(button, item.content, filename);
} else {
this._bindLink(button, item.content, filename);
@ -159,7 +168,7 @@ class PDFAttachmentViewer {
* Used to append FileAttachment annotations to the sidebar.
* @private
*/
_appendAttachment({ id, filename, content, }) {
_appendAttachment({ id, filename, content }) {
this._renderedCapability.promise.then(() => {
let attachments = this.attachments;
@ -184,6 +193,4 @@ class PDFAttachmentViewer {
}
}
export {
PDFAttachmentViewer,
};
export { PDFAttachmentViewer };

View file

@ -13,7 +13,7 @@
* limitations under the License.
*/
import { GrabToPan } from './grab_to_pan';
import { GrabToPan } from "./grab_to_pan";
const CursorTool = {
SELECT: 0, // The default value.
@ -34,7 +34,7 @@ class PDFCursorTools {
/**
* @param {PDFCursorToolsOptions} options
*/
constructor({ container, eventBus, cursorToolOnLoad = CursorTool.SELECT, }) {
constructor({ container, eventBus, cursorToolOnLoad = CursorTool.SELECT }) {
this.container = container;
this.eventBus = eventBus;
@ -82,11 +82,13 @@ class PDFCursorTools {
this.handTool.deactivate();
break;
case CursorTool.ZOOM:
/* falls through */
/* falls through */
}
};
switch (tool) { // Enable the new cursor tool.
switch (
tool // Enable the new cursor tool.
) {
case CursorTool.SELECT:
disableActiveTool();
break;
@ -95,7 +97,7 @@ class PDFCursorTools {
this.handTool.activate();
break;
case CursorTool.ZOOM:
/* falls through */
/* falls through */
default:
console.error(`switchTool: "${tool}" is an unsupported value.`);
return;
@ -111,7 +113,7 @@ class PDFCursorTools {
* @private
*/
_dispatchEvent() {
this.eventBus.dispatch('cursortoolchanged', {
this.eventBus.dispatch("cursortoolchanged", {
source: this,
tool: this.active,
});
@ -121,11 +123,11 @@ class PDFCursorTools {
* @private
*/
_addEventListeners() {
this.eventBus.on('switchcursortool', (evt) => {
this.eventBus.on("switchcursortool", evt => {
this.switchTool(evt.tool);
});
this.eventBus.on('presentationmodechanged', (evt) => {
this.eventBus.on("presentationmodechanged", evt => {
if (evt.switchInProgress) {
return;
}
@ -146,7 +148,4 @@ class PDFCursorTools {
}
}
export {
CursorTool,
PDFCursorTools,
};
export { CursorTool, PDFCursorTools };

View file

@ -13,30 +13,33 @@
* limitations under the License.
*/
import { createPromiseCapability, PDFDateString } from 'pdfjs-lib';
import { createPromiseCapability, PDFDateString } from "pdfjs-lib";
import {
getPageSizeInches, getPDFFileNameFromURL, isPortraitOrientation, NullL10n
} from './ui_utils';
getPageSizeInches,
getPDFFileNameFromURL,
isPortraitOrientation,
NullL10n,
} from "./ui_utils";
const DEFAULT_FIELD_CONTENT = '-';
const DEFAULT_FIELD_CONTENT = "-";
// See https://en.wikibooks.org/wiki/Lentis/Conversion_to_the_Metric_Standard_in_the_United_States
const NON_METRIC_LOCALES = ['en-us', 'en-lr', 'my'];
const NON_METRIC_LOCALES = ["en-us", "en-lr", "my"];
// Should use the format: `width x height`, in portrait orientation.
// See https://en.wikipedia.org/wiki/Paper_size
const US_PAGE_NAMES = {
'8.5x11': 'Letter',
'8.5x14': 'Legal',
"8.5x11": "Letter",
"8.5x14": "Legal",
};
const METRIC_PAGE_NAMES = {
'297x420': 'A3',
'210x297': 'A4',
"297x420": "A3",
"210x297": "A4",
};
function getPageName(size, isPortrait, pageNames) {
const width = (isPortrait ? size.width : size.height);
const height = (isPortrait ? size.height : size.width);
const width = isPortrait ? size.width : size.height;
const height = isPortrait ? size.height : size.width;
return pageNames[`${width}x${height}`];
}
@ -56,8 +59,12 @@ class PDFDocumentProperties {
* @param {EventBus} eventBus - The application event bus.
* @param {IL10n} l10n - Localization service.
*/
constructor({ overlayName, fields, container, closeButton, },
overlayManager, eventBus, l10n = NullL10n) {
constructor(
{ overlayName, fields, container, closeButton },
overlayManager,
eventBus,
l10n = NullL10n
) {
this.overlayName = overlayName;
this.fields = fields;
this.container = container;
@ -66,23 +73,27 @@ class PDFDocumentProperties {
this._reset();
if (closeButton) { // Bind the event listener for the Close button.
closeButton.addEventListener('click', this.close.bind(this));
if (closeButton) {
// Bind the event listener for the Close button.
closeButton.addEventListener("click", this.close.bind(this));
}
this.overlayManager.register(this.overlayName, this.container,
this.close.bind(this));
this.overlayManager.register(
this.overlayName,
this.container,
this.close.bind(this)
);
if (eventBus) {
eventBus.on('pagechanging', (evt) => {
eventBus.on("pagechanging", evt => {
this._currentPageNumber = evt.pageNumber;
});
eventBus.on('rotationchanging', (evt) => {
eventBus.on("rotationchanging", evt => {
this._pagesRotation = evt.pagesRotation;
});
}
this._isNonMetricLocale = true; // The default viewer locale is 'en-us'.
l10n.getLanguage().then((locale) => {
l10n.getLanguage().then(locale => {
this._isNonMetricLocale = NON_METRIC_LOCALES.includes(locale);
});
}
@ -91,8 +102,8 @@ class PDFDocumentProperties {
* Open the document properties overlay.
*/
open() {
let freezeFieldData = (data) => {
Object.defineProperty(this, 'fieldData', {
let freezeFieldData = data => {
Object.defineProperty(this, "fieldData", {
value: Object.freeze(data),
writable: false,
enumerable: true,
@ -100,74 +111,94 @@ class PDFDocumentProperties {
});
};
Promise.all([this.overlayManager.open(this.overlayName),
this._dataAvailableCapability.promise]).then(() => {
Promise.all([
this.overlayManager.open(this.overlayName),
this._dataAvailableCapability.promise,
]).then(() => {
const currentPageNumber = this._currentPageNumber;
const pagesRotation = this._pagesRotation;
// If the document properties were previously fetched (for this PDF file),
// just update the dialog immediately to avoid redundant lookups.
if (this.fieldData &&
currentPageNumber === this.fieldData['_currentPageNumber'] &&
pagesRotation === this.fieldData['_pagesRotation']) {
if (
this.fieldData &&
currentPageNumber === this.fieldData["_currentPageNumber"] &&
pagesRotation === this.fieldData["_pagesRotation"]
) {
this._updateUI();
return;
}
// Get the document properties.
this.pdfDocument.getMetadata().then(
({ info, metadata, contentDispositionFilename, }) => {
return Promise.all([
info,
metadata,
contentDispositionFilename || getPDFFileNameFromURL(this.url || ''),
this._parseFileSize(this.maybeFileSize),
this._parseDate(info.CreationDate),
this._parseDate(info.ModDate),
this.pdfDocument.getPage(currentPageNumber).then((pdfPage) => {
return this._parsePageSize(getPageSizeInches(pdfPage),
pagesRotation);
}),
this._parseLinearization(info.IsLinearized),
]);
}).then(([info, metadata, fileName, fileSize, creationDate, modDate,
pageSize, isLinearized]) => {
freezeFieldData({
'fileName': fileName,
'fileSize': fileSize,
'title': info.Title,
'author': info.Author,
'subject': info.Subject,
'keywords': info.Keywords,
'creationDate': creationDate,
'modificationDate': modDate,
'creator': info.Creator,
'producer': info.Producer,
'version': info.PDFFormatVersion,
'pageCount': this.pdfDocument.numPages,
'pageSize': pageSize,
'linearized': isLinearized,
'_currentPageNumber': currentPageNumber,
'_pagesRotation': pagesRotation,
this.pdfDocument
.getMetadata()
.then(({ info, metadata, contentDispositionFilename }) => {
return Promise.all([
info,
metadata,
contentDispositionFilename || getPDFFileNameFromURL(this.url || ""),
this._parseFileSize(this.maybeFileSize),
this._parseDate(info.CreationDate),
this._parseDate(info.ModDate),
this.pdfDocument.getPage(currentPageNumber).then(pdfPage => {
return this._parsePageSize(
getPageSizeInches(pdfPage),
pagesRotation
);
}),
this._parseLinearization(info.IsLinearized),
]);
})
.then(
([
info,
metadata,
fileName,
fileSize,
creationDate,
modDate,
pageSize,
isLinearized,
]) => {
freezeFieldData({
fileName,
fileSize,
title: info.Title,
author: info.Author,
subject: info.Subject,
keywords: info.Keywords,
creationDate,
modificationDate: modDate,
creator: info.Creator,
producer: info.Producer,
version: info.PDFFormatVersion,
pageCount: this.pdfDocument.numPages,
pageSize,
linearized: isLinearized,
_currentPageNumber: currentPageNumber,
_pagesRotation: pagesRotation,
});
this._updateUI();
// Get the correct fileSize, since it may not have been set (if
// `this.setFileSize` wasn't called) or may be incorrectly set.
return this.pdfDocument.getDownloadInfo();
}
)
.then(({ length }) => {
this.maybeFileSize = length;
return this._parseFileSize(length);
})
.then(fileSize => {
if (fileSize === this.fieldData["fileSize"]) {
return; // The fileSize has already been correctly set.
}
let data = Object.assign(Object.create(null), this.fieldData);
data["fileSize"] = fileSize;
freezeFieldData(data);
this._updateUI();
});
this._updateUI();
// Get the correct fileSize, since it may not have been set (if
// `this.setFileSize` wasn't called) or may be incorrectly set.
return this.pdfDocument.getDownloadInfo();
}).then(({ length, }) => {
this.maybeFileSize = length;
return this._parseFileSize(length);
}).then((fileSize) => {
if (fileSize === this.fieldData['fileSize']) {
return; // The fileSize has already been correctly set.
}
let data = Object.assign(Object.create(null), this.fieldData);
data['fileSize'] = fileSize;
freezeFieldData(data);
this._updateUI();
});
});
}
@ -248,8 +279,8 @@ class PDFDocumentProperties {
}
for (let id in this.fields) {
let content = this.fieldData[id];
this.fields[id].textContent = (content || content === 0) ?
content : DEFAULT_FIELD_CONTENT;
this.fields[id].textContent =
content || content === 0 ? content : DEFAULT_FIELD_CONTENT;
}
}
@ -261,15 +292,23 @@ class PDFDocumentProperties {
if (!kb) {
return undefined;
} else if (kb < 1024) {
return this.l10n.get('document_properties_kb', {
size_kb: (+kb.toPrecision(3)).toLocaleString(),
size_b: fileSize.toLocaleString(),
}, '{{size_kb}} KB ({{size_b}} bytes)');
return this.l10n.get(
"document_properties_kb",
{
size_kb: (+kb.toPrecision(3)).toLocaleString(),
size_b: fileSize.toLocaleString(),
},
"{{size_kb}} KB ({{size_b}} bytes)"
);
}
return this.l10n.get('document_properties_mb', {
size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(),
size_b: fileSize.toLocaleString(),
}, '{{size_mb}} MB ({{size_b}} bytes)');
return this.l10n.get(
"document_properties_mb",
{
size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(),
size_b: fileSize.toLocaleString(),
},
"{{size_mb}} MB ({{size_b}} bytes)"
);
}
/**
@ -299,11 +338,17 @@ class PDFDocumentProperties {
};
let pageName = null;
let name = getPageName(sizeInches, isPortrait, US_PAGE_NAMES) ||
getPageName(sizeMillimeters, isPortrait, METRIC_PAGE_NAMES);
let name =
getPageName(sizeInches, isPortrait, US_PAGE_NAMES) ||
getPageName(sizeMillimeters, isPortrait, METRIC_PAGE_NAMES);
if (!name && !(Number.isInteger(sizeMillimeters.width) &&
Number.isInteger(sizeMillimeters.height))) {
if (
!name &&
!(
Number.isInteger(sizeMillimeters.width) &&
Number.isInteger(sizeMillimeters.height)
)
) {
// Attempt to improve the page name detection by falling back to fuzzy
// matching of the metric dimensions, to account for e.g. rounding errors
// and/or PDF files that define the page sizes in an imprecise manner.
@ -317,45 +362,61 @@ class PDFDocumentProperties {
};
// Try to avoid false positives, by only considering "small" differences.
if (Math.abs(exactMillimeters.width - intMillimeters.width) < 0.1 &&
Math.abs(exactMillimeters.height - intMillimeters.height) < 0.1) {
if (
Math.abs(exactMillimeters.width - intMillimeters.width) < 0.1 &&
Math.abs(exactMillimeters.height - intMillimeters.height) < 0.1
) {
name = getPageName(intMillimeters, isPortrait, METRIC_PAGE_NAMES);
if (name) {
// Update *both* sizes, computed above, to ensure that the displayed
// dimensions always correspond to the detected page name.
sizeInches = {
width: Math.round(intMillimeters.width / 25.4 * 100) / 100,
height: Math.round(intMillimeters.height / 25.4 * 100) / 100,
width: Math.round((intMillimeters.width / 25.4) * 100) / 100,
height: Math.round((intMillimeters.height / 25.4) * 100) / 100,
};
sizeMillimeters = intMillimeters;
}
}
}
if (name) {
pageName = this.l10n.get('document_properties_page_size_name_' +
name.toLowerCase(), null, name);
pageName = this.l10n.get(
"document_properties_page_size_name_" + name.toLowerCase(),
null,
name
);
}
return Promise.all([
(this._isNonMetricLocale ? sizeInches : sizeMillimeters),
this.l10n.get('document_properties_page_size_unit_' +
(this._isNonMetricLocale ? 'inches' : 'millimeters'), null,
this._isNonMetricLocale ? 'in' : 'mm'),
this._isNonMetricLocale ? sizeInches : sizeMillimeters,
this.l10n.get(
"document_properties_page_size_unit_" +
(this._isNonMetricLocale ? "inches" : "millimeters"),
null,
this._isNonMetricLocale ? "in" : "mm"
),
pageName,
this.l10n.get('document_properties_page_size_orientation_' +
(isPortrait ? 'portrait' : 'landscape'), null,
isPortrait ? 'portrait' : 'landscape'),
]).then(([{ width, height, }, unit, name, orientation]) => {
return this.l10n.get('document_properties_page_size_dimension_' +
(name ? 'name_' : '') + 'string', {
this.l10n.get(
"document_properties_page_size_orientation_" +
(isPortrait ? "portrait" : "landscape"),
null,
isPortrait ? "portrait" : "landscape"
),
]).then(([{ width, height }, unit, name, orientation]) => {
return this.l10n.get(
"document_properties_page_size_dimension_" +
(name ? "name_" : "") +
"string",
{
width: width.toLocaleString(),
height: height.toLocaleString(),
unit,
name,
orientation,
}, '{{width}} × {{height}} {{unit}} (' +
(name ? '{{name}}, ' : '') + '{{orientation}})');
},
"{{width}} × {{height}} {{unit}} (" +
(name ? "{{name}}, " : "") +
"{{orientation}})"
);
});
}
@ -367,22 +428,26 @@ class PDFDocumentProperties {
if (!dateObject) {
return undefined;
}
return this.l10n.get('document_properties_date_string', {
return this.l10n.get(
"document_properties_date_string",
{
date: dateObject.toLocaleDateString(),
time: dateObject.toLocaleTimeString(),
}, '{{date}}, {{time}}');
},
"{{date}}, {{time}}"
);
}
/**
* @private
*/
_parseLinearization(isLinearized) {
return this.l10n.get('document_properties_linearized_' +
(isLinearized ? 'yes' : 'no'), null,
(isLinearized ? 'Yes' : 'No'));
return this.l10n.get(
"document_properties_linearized_" + (isLinearized ? "yes" : "no"),
null,
isLinearized ? "Yes" : "No"
);
}
}
export {
PDFDocumentProperties,
};
export { PDFDocumentProperties };

View file

@ -13,8 +13,8 @@
* limitations under the License.
*/
import { getGlobalEventBus, NullL10n } from './ui_utils';
import { FindState } from './pdf_find_controller';
import { getGlobalEventBus, NullL10n } from "./ui_utils";
import { FindState } from "./pdf_find_controller";
const MATCHES_COUNT_LIMIT = 1000;
@ -42,19 +42,19 @@ class PDFFindBar {
this.l10n = l10n;
// Add event listeners to the DOM elements.
this.toggleButton.addEventListener('click', () => {
this.toggleButton.addEventListener("click", () => {
this.toggle();
});
this.findField.addEventListener('input', () => {
this.dispatchEvent('');
this.findField.addEventListener("input", () => {
this.dispatchEvent("");
});
this.bar.addEventListener('keydown', (e) => {
this.bar.addEventListener("keydown", e => {
switch (e.keyCode) {
case 13: // Enter
if (e.target === this.findField) {
this.dispatchEvent('again', e.shiftKey);
this.dispatchEvent("again", e.shiftKey);
}
break;
case 27: // Escape
@ -63,27 +63,27 @@ class PDFFindBar {
}
});
this.findPreviousButton.addEventListener('click', () => {
this.dispatchEvent('again', true);
this.findPreviousButton.addEventListener("click", () => {
this.dispatchEvent("again", true);
});
this.findNextButton.addEventListener('click', () => {
this.dispatchEvent('again', false);
this.findNextButton.addEventListener("click", () => {
this.dispatchEvent("again", false);
});
this.highlightAll.addEventListener('click', () => {
this.dispatchEvent('highlightallchange');
this.highlightAll.addEventListener("click", () => {
this.dispatchEvent("highlightallchange");
});
this.caseSensitive.addEventListener('click', () => {
this.dispatchEvent('casesensitivitychange');
this.caseSensitive.addEventListener("click", () => {
this.dispatchEvent("casesensitivitychange");
});
this.entireWord.addEventListener('click', () => {
this.dispatchEvent('entirewordchange');
this.entireWord.addEventListener("click", () => {
this.dispatchEvent("entirewordchange");
});
this.eventBus.on('resize', this._adjustWidth.bind(this));
this.eventBus.on("resize", this._adjustWidth.bind(this));
}
reset() {
@ -91,7 +91,7 @@ class PDFFindBar {
}
dispatchEvent(type, findPrev) {
this.eventBus.dispatch('find', {
this.eventBus.dispatch("find", {
source: this,
type,
query: this.findField.value,
@ -105,37 +105,43 @@ class PDFFindBar {
updateUIState(state, previous, matchesCount) {
let notFound = false;
let findMsg = '';
let status = '';
let findMsg = "";
let status = "";
switch (state) {
case FindState.FOUND:
break;
case FindState.PENDING:
status = 'pending';
status = "pending";
break;
case FindState.NOT_FOUND:
findMsg = this.l10n.get('find_not_found', null, 'Phrase not found');
findMsg = this.l10n.get("find_not_found", null, "Phrase not found");
notFound = true;
break;
case FindState.WRAPPED:
if (previous) {
findMsg = this.l10n.get('find_reached_top', null,
'Reached top of document, continued from bottom');
findMsg = this.l10n.get(
"find_reached_top",
null,
"Reached top of document, continued from bottom"
);
} else {
findMsg = this.l10n.get('find_reached_bottom', null,
'Reached end of document, continued from top');
findMsg = this.l10n.get(
"find_reached_bottom",
null,
"Reached end of document, continued from top"
);
}
break;
}
this.findField.classList.toggle('notFound', notFound);
this.findField.setAttribute('data-status', status);
this.findField.classList.toggle("notFound", notFound);
this.findField.setAttribute("data-status", status);
Promise.resolve(findMsg).then((msg) => {
Promise.resolve(findMsg).then(msg => {
this.findMsg.textContent = msg;
this._adjustWidth();
});
@ -143,44 +149,61 @@ class PDFFindBar {
this.updateResultsCount(matchesCount);
}
updateResultsCount({ current = 0, total = 0, } = {}) {
updateResultsCount({ current = 0, total = 0 } = {}) {
if (!this.findResultsCount) {
return; // No UI control is provided.
}
let matchesCountMsg = '', limit = MATCHES_COUNT_LIMIT;
let matchesCountMsg = "",
limit = MATCHES_COUNT_LIMIT;
if (total > 0) {
if (total > limit) {
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('MOZCENTRAL')) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
// TODO: Remove this hard-coded `[other]` form once plural support has
// been implemented in the mozilla-central specific `l10n.js` file.
matchesCountMsg = this.l10n.get('find_match_count_limit[other]', {
limit,
}, 'More than {{limit}} matches');
matchesCountMsg = this.l10n.get(
"find_match_count_limit[other]",
{
limit,
},
"More than {{limit}} matches"
);
} else {
matchesCountMsg = this.l10n.get('find_match_count_limit', {
limit,
}, 'More than {{limit}} match' + (limit !== 1 ? 'es' : ''));
matchesCountMsg = this.l10n.get(
"find_match_count_limit",
{
limit,
},
"More than {{limit}} match" + (limit !== 1 ? "es" : "")
);
}
} else {
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('MOZCENTRAL')) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
// TODO: Remove this hard-coded `[other]` form once plural support has
// been implemented in the mozilla-central specific `l10n.js` file.
matchesCountMsg = this.l10n.get('find_match_count[other]', {
current,
total,
}, '{{current}} of {{total}} matches');
matchesCountMsg = this.l10n.get(
"find_match_count[other]",
{
current,
total,
},
"{{current}} of {{total}} matches"
);
} else {
matchesCountMsg = this.l10n.get('find_match_count', {
current,
total,
}, '{{current}} of {{total}} match' + (total !== 1 ? 'es' : ''));
matchesCountMsg = this.l10n.get(
"find_match_count",
{
current,
total,
},
"{{current}} of {{total}} match" + (total !== 1 ? "es" : "")
);
}
}
}
Promise.resolve(matchesCountMsg).then((msg) => {
Promise.resolve(matchesCountMsg).then(msg => {
this.findResultsCount.textContent = msg;
this.findResultsCount.classList.toggle('hidden', !total);
this.findResultsCount.classList.toggle("hidden", !total);
// Since `updateResultsCount` may be called from `PDFFindController`,
// ensure that the width of the findbar is always updated correctly.
this._adjustWidth();
@ -190,8 +213,8 @@ class PDFFindBar {
open() {
if (!this.opened) {
this.opened = true;
this.toggleButton.classList.add('toggled');
this.bar.classList.remove('hidden');
this.toggleButton.classList.add("toggled");
this.bar.classList.remove("hidden");
}
this.findField.select();
this.findField.focus();
@ -204,10 +227,10 @@ class PDFFindBar {
return;
}
this.opened = false;
this.toggleButton.classList.remove('toggled');
this.bar.classList.add('hidden');
this.toggleButton.classList.remove("toggled");
this.bar.classList.add("hidden");
this.eventBus.dispatch('findbarclose', { source: this, });
this.eventBus.dispatch("findbarclose", { source: this });
}
toggle() {
@ -230,7 +253,7 @@ class PDFFindBar {
// its width to the maximum possible width once the find bar does not fit
// entirely within the window anymore (and its elements are automatically
// wrapped). Here we detect and fix that.
this.bar.classList.remove('wrapContainers');
this.bar.classList.remove("wrapContainers");
let findbarHeight = this.bar.clientHeight;
let inputContainerHeight = this.bar.firstElementChild.clientHeight;
@ -239,11 +262,9 @@ class PDFFindBar {
// The findbar is taller than the input container, which means that
// the browser wrapped some of the elements. For a consistent look,
// wrap all of them to adjust the width of the find bar.
this.bar.classList.add('wrapContainers');
this.bar.classList.add("wrapContainers");
}
}
}
export {
PDFFindBar,
};
export { PDFFindBar };

View file

@ -13,9 +13,9 @@
* limitations under the License.
*/
import { getGlobalEventBus, scrollIntoView } from './ui_utils';
import { createPromiseCapability } from 'pdfjs-lib';
import { getCharacterType } from './pdf_find_utils';
import { getGlobalEventBus, scrollIntoView } from "./ui_utils";
import { createPromiseCapability } from "pdfjs-lib";
import { getCharacterType } from "./pdf_find_utils";
const FindState = {
FOUND: 0,
@ -29,25 +29,25 @@ const MATCH_SCROLL_OFFSET_TOP = -50; // px
const MATCH_SCROLL_OFFSET_LEFT = -400; // px
const CHARACTERS_TO_NORMALIZE = {
'\u2018': '\'', // Left single quotation mark
'\u2019': '\'', // Right single quotation mark
'\u201A': '\'', // Single low-9 quotation mark
'\u201B': '\'', // Single high-reversed-9 quotation mark
'\u201C': '"', // Left double quotation mark
'\u201D': '"', // Right double quotation mark
'\u201E': '"', // Double low-9 quotation mark
'\u201F': '"', // Double high-reversed-9 quotation mark
'\u00BC': '1/4', // Vulgar fraction one quarter
'\u00BD': '1/2', // Vulgar fraction one half
'\u00BE': '3/4', // Vulgar fraction three quarters
"\u2018": "'", // Left single quotation mark
"\u2019": "'", // Right single quotation mark
"\u201A": "'", // Single low-9 quotation mark
"\u201B": "'", // Single high-reversed-9 quotation mark
"\u201C": '"', // Left double quotation mark
"\u201D": '"', // Right double quotation mark
"\u201E": '"', // Double low-9 quotation mark
"\u201F": '"', // Double high-reversed-9 quotation mark
"\u00BC": "1/4", // Vulgar fraction one quarter
"\u00BD": "1/2", // Vulgar fraction one half
"\u00BE": "3/4", // Vulgar fraction three quarters
};
let normalizationRegex = null;
function normalize(text) {
if (!normalizationRegex) {
// Compile the regular expression for text normalization once.
const replace = Object.keys(CHARACTERS_TO_NORMALIZE).join('');
normalizationRegex = new RegExp(`[${replace}]`, 'g');
const replace = Object.keys(CHARACTERS_TO_NORMALIZE).join("");
normalizationRegex = new RegExp(`[${replace}]`, "g");
}
return text.replace(normalizationRegex, function(ch) {
return CHARACTERS_TO_NORMALIZE[ch];
@ -67,12 +67,12 @@ class PDFFindController {
/**
* @param {PDFFindControllerOptions} options
*/
constructor({ linkService, eventBus = getGlobalEventBus(), }) {
constructor({ linkService, eventBus = getGlobalEventBus() }) {
this._linkService = linkService;
this._eventBus = eventBus;
this._reset();
eventBus.on('findbarclose', this._onFindBarClose.bind(this));
eventBus.on("findbarclose", this._onFindBarClose.bind(this));
}
get highlightMatches() {
@ -122,15 +122,17 @@ class PDFFindController {
this._dirtyMatch = true;
}
this._state = state;
if (cmd !== 'findhighlightallchange') {
if (cmd !== "findhighlightallchange") {
this._updateUIState(FindState.PENDING);
}
this._firstPageCapability.promise.then(() => {
// If the document was closed before searching began, or if the search
// operation was relevant for a previously opened document, do nothing.
if (!this._pdfDocument ||
(pdfDocument && this._pdfDocument !== pdfDocument)) {
if (
!this._pdfDocument ||
(pdfDocument && this._pdfDocument !== pdfDocument)
) {
return;
}
this._extractText();
@ -142,7 +144,7 @@ class PDFFindController {
clearTimeout(this._findTimeout);
this._findTimeout = null;
}
if (cmd === 'find') {
if (cmd === "find") {
// Trigger the find action with a small delay to avoid starting the
// search when the user is still typing (saving resources).
this._findTimeout = setTimeout(() => {
@ -153,7 +155,7 @@ class PDFFindController {
// Immediately trigger searching for non-'find' operations, when the
// current state needs to be reset and matches re-calculated.
this._nextMatch();
} else if (cmd === 'findagain') {
} else if (cmd === "findagain") {
this._nextMatch();
// When the findbar was previously closed, and `highlightAll` is set,
@ -161,7 +163,7 @@ class PDFFindController {
if (findbarClosed && this._state.highlightAll) {
this._updateAllPages();
}
} else if (cmd === 'findhighlightallchange') {
} else if (cmd === "findhighlightallchange") {
// If there was a pending search operation, synchronously trigger a new
// search *first* to ensure that the correct matches are highlighted.
if (pendingTimeout) {
@ -176,7 +178,7 @@ class PDFFindController {
});
}
scrollMatchIntoView({ element = null, pageIndex = -1, matchIndex = -1, }) {
scrollMatchIntoView({ element = null, pageIndex = -1, matchIndex = -1 }) {
if (!this._scrollMatches || !element) {
return;
} else if (matchIndex === -1 || matchIndex !== this._selected.matchIdx) {
@ -200,11 +202,13 @@ class PDFFindController {
this._pageMatches = [];
this._pageMatchesLength = [];
this._state = null;
this._selected = { // Currently selected match.
this._selected = {
// Currently selected match.
pageIdx: -1,
matchIdx: -1,
};
this._offset = { // Where the find algorithm currently is in the document.
this._offset = {
// Where the find algorithm currently is in the document.
pageIdx: null,
matchIdx: null,
wrapped: false,
@ -240,7 +244,7 @@ class PDFFindController {
return true;
}
switch (cmd) {
case 'findagain':
case "findagain":
const pageNumber = this._selected.pageIdx + 1;
const linkService = this._linkService;
// Only treat a 'findagain' event as a new search operation when it's
@ -251,13 +255,16 @@ class PDFFindController {
// there's a risk that consecutive 'findagain' operations could "skip"
// over matches at the top/bottom of pages thus making them completely
// inaccessible when there's multiple pages visible in the viewer.
if (pageNumber >= 1 && pageNumber <= linkService.pagesCount &&
pageNumber !== linkService.page &&
!linkService.isPageVisible(pageNumber)) {
if (
pageNumber >= 1 &&
pageNumber <= linkService.pagesCount &&
pageNumber !== linkService.page &&
!linkService.isPageVisible(pageNumber)
) {
return true;
}
return false;
case 'findhighlightallchange':
case "findhighlightallchange":
return false;
}
return true;
@ -275,8 +282,10 @@ class PDFFindController {
const nextElem = matchesWithLength[currentIndex + 1];
// Check for cases like "TAMEd TAME".
if (currentIndex < matchesWithLength.length - 1 &&
currentElem.match === nextElem.match) {
if (
currentIndex < matchesWithLength.length - 1 &&
currentElem.match === nextElem.match
) {
currentElem.skipped = true;
return true;
}
@ -290,8 +299,10 @@ class PDFFindController {
if (prevElem.match + prevElem.matchLength < currentElem.match) {
break;
}
if (prevElem.match + prevElem.matchLength >=
currentElem.match + currentElem.matchLength) {
if (
prevElem.match + prevElem.matchLength >=
currentElem.match + currentElem.matchLength
) {
currentElem.skipped = true;
return true;
}
@ -302,8 +313,9 @@ class PDFFindController {
// Sort the array of `{ match: <match>, matchLength: <matchLength> }`
// objects on increasing index first and on the length otherwise.
matchesWithLength.sort(function(a, b) {
return a.match === b.match ? a.matchLength - b.matchLength :
a.match - b.match;
return a.match === b.match
? a.matchLength - b.matchLength
: a.match - b.match;
});
for (let i = 0, len = matchesWithLength.length; i < len; i++) {
if (isSubTerm(matchesWithLength, i)) {
@ -326,8 +338,8 @@ class PDFFindController {
return false;
}
}
const endIdx = (startIdx + length - 1);
if (endIdx < (content.length - 1)) {
const endIdx = startIdx + length - 1;
if (endIdx < content.length - 1) {
const last = content.charCodeAt(endIdx);
const limit = content.charCodeAt(endIdx + 1);
if (getCharacterType(last) === getCharacterType(limit)) {
@ -370,8 +382,10 @@ class PDFFindController {
if (matchIdx === -1) {
break;
}
if (entireWord &&
!this._isEntireWord(pageContent, matchIdx, subqueryLen)) {
if (
entireWord &&
!this._isEntireWord(pageContent, matchIdx, subqueryLen)
) {
continue;
}
// Other searches do not, so we store the length.
@ -389,14 +403,17 @@ class PDFFindController {
// Sort `matchesWithLength`, remove intersecting terms and put the result
// into the two arrays.
this._prepareMatches(matchesWithLength, this._pageMatches[pageIndex],
this._pageMatchesLength[pageIndex]);
this._prepareMatches(
matchesWithLength,
this._pageMatches[pageIndex],
this._pageMatchesLength[pageIndex]
);
}
_calculateMatch(pageIndex) {
let pageContent = this._pageContents[pageIndex];
let query = this._query;
const { caseSensitive, entireWord, phraseSearch, } = this._state;
const { caseSensitive, entireWord, phraseSearch } = this._state;
if (query.length === 0) {
// Do nothing: the matches should be wiped out already.
@ -444,27 +461,36 @@ class PDFFindController {
this._extractTextPromises[i] = extractTextCapability.promise;
promise = promise.then(() => {
return this._pdfDocument.getPage(i + 1).then((pdfPage) => {
return pdfPage.getTextContent({
normalizeWhitespace: true,
});
}).then((textContent) => {
const textItems = textContent.items;
const strBuf = [];
return this._pdfDocument
.getPage(i + 1)
.then(pdfPage => {
return pdfPage.getTextContent({
normalizeWhitespace: true,
});
})
.then(
textContent => {
const textItems = textContent.items;
const strBuf = [];
for (let j = 0, jj = textItems.length; j < jj; j++) {
strBuf.push(textItems[j].str);
}
for (let j = 0, jj = textItems.length; j < jj; j++) {
strBuf.push(textItems[j].str);
}
// Store the normalized page content (text items) as one string.
this._pageContents[i] = normalize(strBuf.join(''));
extractTextCapability.resolve(i);
}, (reason) => {
console.error(`Unable to get text content for page ${i + 1}`, reason);
// Page error -- assuming no text content.
this._pageContents[i] = '';
extractTextCapability.resolve(i);
});
// Store the normalized page content (text items) as one string.
this._pageContents[i] = normalize(strBuf.join(""));
extractTextCapability.resolve(i);
},
reason => {
console.error(
`Unable to get text content for page ${i + 1}`,
reason
);
// Page error -- assuming no text content.
this._pageContents[i] = "";
extractTextCapability.resolve(i);
}
);
});
}
}
@ -477,14 +503,14 @@ class PDFFindController {
this._linkService.page = index + 1;
}
this._eventBus.dispatch('updatetextlayermatches', {
this._eventBus.dispatch("updatetextlayermatches", {
source: this,
pageIndex: index,
});
}
_updateAllPages() {
this._eventBus.dispatch('updatetextlayermatches', {
this._eventBus.dispatch("updatetextlayermatches", {
source: this,
pageIndex: -1,
});
@ -517,7 +543,7 @@ class PDFFindController {
continue;
}
this._pendingFindMatches[i] = true;
this._extractTextPromises[i].then((pageIdx) => {
this._extractTextPromises[i].then(pageIdx => {
delete this._pendingFindMatches[pageIdx];
this._calculateMatch(pageIdx);
});
@ -525,7 +551,7 @@ class PDFFindController {
}
// If there's no query there's no point in searching.
if (this._query === '') {
if (this._query === "") {
this._updateUIState(FindState.FOUND);
return;
}
@ -541,12 +567,13 @@ class PDFFindController {
// page's matches.
if (offset.matchIdx !== null) {
const numPageMatches = this._pageMatches[offset.pageIdx].length;
if ((!previous && offset.matchIdx + 1 < numPageMatches) ||
(previous && offset.matchIdx > 0)) {
if (
(!previous && offset.matchIdx + 1 < numPageMatches) ||
(previous && offset.matchIdx > 0)
) {
// The simple case; we just have advance the matchIdx to select
// the next match on the page.
offset.matchIdx = (previous ? offset.matchIdx - 1 :
offset.matchIdx + 1);
offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1;
this._updateMatch(/* found = */ true);
return;
}
@ -565,7 +592,7 @@ class PDFFindController {
if (numMatches) {
// There were matches for the page, so initialize `matchIdx`.
offset.matchIdx = (previous ? numMatches - 1 : 0);
offset.matchIdx = previous ? numMatches - 1 : 0;
this._updateMatch(/* found = */ true);
return true;
}
@ -587,7 +614,7 @@ class PDFFindController {
_nextPageMatch() {
if (this._resumePageIdx !== null) {
console.error('There can only be one pending page.');
console.error("There can only be one pending page.");
}
let matches = null;
@ -606,13 +633,13 @@ class PDFFindController {
_advanceOffsetPage(previous) {
const offset = this._offset;
const numPages = this._linkService.pagesCount;
offset.pageIdx = (previous ? offset.pageIdx - 1 : offset.pageIdx + 1);
offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1;
offset.matchIdx = null;
this._pagesToSearch--;
if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
offset.pageIdx = (previous ? numPages - 1 : 0);
offset.pageIdx = previous ? numPages - 1 : 0;
offset.wrapped = true;
}
}
@ -626,7 +653,7 @@ class PDFFindController {
const previousPage = this._selected.pageIdx;
this._selected.pageIdx = this._offset.pageIdx;
this._selected.matchIdx = this._offset.matchIdx;
state = (wrapped ? FindState.WRAPPED : FindState.FOUND);
state = wrapped ? FindState.WRAPPED : FindState.FOUND;
// Update the currently selected page to wipe out any selected matches.
if (previousPage !== -1 && previousPage !== this._selected.pageIdx) {
@ -650,8 +677,10 @@ class PDFFindController {
// events will always be dispatched in the expected order.
this._firstPageCapability.promise.then(() => {
// Only update the UI if the document is open, and is the current one.
if (!this._pdfDocument ||
(pdfDocument && this._pdfDocument !== pdfDocument)) {
if (
!this._pdfDocument ||
(pdfDocument && this._pdfDocument !== pdfDocument)
) {
return;
}
// Ensure that a pending, not yet started, search operation is aborted.
@ -676,8 +705,9 @@ class PDFFindController {
}
_requestMatchesCount() {
const { pageIdx, matchIdx, } = this._selected;
let current = 0, total = this._matchesCountTotal;
const { pageIdx, matchIdx } = this._selected;
let current = 0,
total = this._matchesCountTotal;
if (matchIdx !== -1) {
for (let i = 0; i < pageIdx; i++) {
current += (this._pageMatches[i] && this._pageMatches[i].length) || 0;
@ -690,18 +720,18 @@ class PDFFindController {
if (current < 1 || current > total) {
current = total = 0;
}
return { current, total, };
return { current, total };
}
_updateUIResultsCount() {
this._eventBus.dispatch('updatefindmatchescount', {
this._eventBus.dispatch("updatefindmatchescount", {
source: this,
matchesCount: this._requestMatchesCount(),
});
}
_updateUIState(state, previous) {
this._eventBus.dispatch('updatefindcontrolstate', {
this._eventBus.dispatch("updatefindcontrolstate", {
source: this,
state,
previous,
@ -710,7 +740,4 @@ class PDFFindController {
}
}
export {
FindState,
PDFFindController,
};
export { FindState, PDFFindController };

View file

@ -25,46 +25,54 @@ const CharacterType = {
};
function isAlphabeticalScript(charCode) {
return charCode < 0x2E80;
return charCode < 0x2e80;
}
function isAscii(charCode) {
return (charCode & 0xFF80) === 0;
return (charCode & 0xff80) === 0;
}
function isAsciiAlpha(charCode) {
return (charCode >= /* a = */ 0x61 && charCode <= /* z = */ 0x7A) ||
(charCode >= /* A = */ 0x41 && charCode <= /* Z = */ 0x5A);
return (
(charCode >= /* a = */ 0x61 && charCode <= /* z = */ 0x7a) ||
(charCode >= /* A = */ 0x41 && charCode <= /* Z = */ 0x5a)
);
}
function isAsciiDigit(charCode) {
return (charCode >= /* 0 = */ 0x30 && charCode <= /* 9 = */ 0x39);
return charCode >= /* 0 = */ 0x30 && charCode <= /* 9 = */ 0x39;
}
function isAsciiSpace(charCode) {
return (charCode === /* SPACE = */ 0x20 || charCode === /* TAB = */ 0x09 ||
charCode === /* CR = */ 0x0D || charCode === /* LF = */ 0x0A);
return (
charCode === /* SPACE = */ 0x20 ||
charCode === /* TAB = */ 0x09 ||
charCode === /* CR = */ 0x0d ||
charCode === /* LF = */ 0x0a
);
}
function isHan(charCode) {
return (charCode >= 0x3400 && charCode <= 0x9FFF) ||
(charCode >= 0xF900 && charCode <= 0xFAFF);
return (
(charCode >= 0x3400 && charCode <= 0x9fff) ||
(charCode >= 0xf900 && charCode <= 0xfaff)
);
}
function isKatakana(charCode) {
return (charCode >= 0x30A0 && charCode <= 0x30FF);
return charCode >= 0x30a0 && charCode <= 0x30ff;
}
function isHiragana(charCode) {
return (charCode >= 0x3040 && charCode <= 0x309F);
return charCode >= 0x3040 && charCode <= 0x309f;
}
function isHalfwidthKatakana(charCode) {
return (charCode >= 0xFF60 && charCode <= 0xFF9F);
return charCode >= 0xff60 && charCode <= 0xff9f;
}
function isThai(charCode) {
return (charCode & 0xFF80) === 0x0E00;
return (charCode & 0xff80) === 0x0e00;
}
/**
@ -76,14 +84,17 @@ function getCharacterType(charCode) {
if (isAscii(charCode)) {
if (isAsciiSpace(charCode)) {
return CharacterType.SPACE;
} else if (isAsciiAlpha(charCode) || isAsciiDigit(charCode) ||
charCode === /* UNDERSCORE = */ 0x5F) {
} else if (
isAsciiAlpha(charCode) ||
isAsciiDigit(charCode) ||
charCode === /* UNDERSCORE = */ 0x5f
) {
return CharacterType.ALPHA_LETTER;
}
return CharacterType.PUNCT;
} else if (isThai(charCode)) {
return CharacterType.THAI_LETTER;
} else if (charCode === /* NBSP = */ 0xA0) {
} else if (charCode === /* NBSP = */ 0xa0) {
return CharacterType.SPACE;
}
return CharacterType.ALPHA_LETTER;
@ -101,7 +112,4 @@ function getCharacterType(charCode) {
return CharacterType.ALPHA_LETTER;
}
export {
CharacterType,
getCharacterType,
};
export { CharacterType, getCharacterType };

View file

@ -14,8 +14,11 @@
*/
import {
getGlobalEventBus, isValidRotation, parseQueryString, waitOnEventOrTimeout
} from './ui_utils';
getGlobalEventBus,
isValidRotation,
parseQueryString,
waitOnEventOrTimeout,
} from "./ui_utils";
// Heuristic value used when force-resetting `this._blockHashChange`.
const HASH_CHANGE_TIMEOUT = 1000; // milliseconds
@ -54,29 +57,29 @@ class PDFHistory {
/**
* @param {PDFHistoryOptions} options
*/
constructor({ linkService, eventBus, }) {
constructor({ linkService, eventBus }) {
this.linkService = linkService;
this.eventBus = eventBus || getGlobalEventBus();
this._initialized = false;
this._fingerprint = '';
this._fingerprint = "";
this.reset();
this._boundEvents = null;
this._isViewerInPresentationMode = false;
// Ensure that we don't miss either a 'presentationmodechanged' or a
// 'pagesinit' event, by registering the listeners immediately.
this.eventBus.on('presentationmodechanged', (evt) => {
this.eventBus.on("presentationmodechanged", evt => {
this._isViewerInPresentationMode = evt.active || evt.switchInProgress;
});
this.eventBus.on('pagesinit', () => {
this.eventBus.on("pagesinit", () => {
this._isPagesLoaded = false;
const onPagesLoaded = (evt) => {
this.eventBus.off('pagesloaded', onPagesLoaded);
const onPagesLoaded = evt => {
this.eventBus.off("pagesloaded", onPagesLoaded);
this._isPagesLoaded = !!evt.pagesCount;
};
this.eventBus.on('pagesloaded', onPagesLoaded);
this.eventBus.on("pagesloaded", onPagesLoaded);
});
}
@ -85,20 +88,21 @@ class PDFHistory {
* browser history entry or the document hash, whichever is present.
* @param {InitializeParameters} params
*/
initialize({ fingerprint, resetHistory = false, updateUrl = false, }) {
if (!fingerprint || typeof fingerprint !== 'string') {
initialize({ fingerprint, resetHistory = false, updateUrl = false }) {
if (!fingerprint || typeof fingerprint !== "string") {
console.error(
'PDFHistory.initialize: The "fingerprint" must be a non-empty string.');
'PDFHistory.initialize: The "fingerprint" must be a non-empty string.'
);
return;
}
// Ensure that any old state is always reset upon initialization.
if (this._initialized) {
this.reset();
}
const reInitialized = (this._fingerprint !== '' &&
this._fingerprint !== fingerprint);
const reInitialized =
this._fingerprint !== "" && this._fingerprint !== fingerprint;
this._fingerprint = fingerprint;
this._updateUrl = (updateUrl === true);
this._updateUrl = updateUrl === true;
this._initialized = true;
this._bindEvents();
@ -114,7 +118,7 @@ class PDFHistory {
this._position = null;
if (!this._isValidState(state, /* checkReload = */ true) || resetHistory) {
const { hash, page, rotation, } = this._parseCurrentHash();
const { hash, page, rotation } = this._parseCurrentHash();
if (!hash || reInitialized || resetHistory) {
// Ensure that the browser history is reset on PDF document load.
@ -123,16 +127,21 @@ class PDFHistory {
}
// Ensure that the browser history is initialized correctly when
// the document hash is present on PDF document load.
this._pushOrReplaceState({ hash, page, rotation, },
/* forceReplace = */ true);
this._pushOrReplaceState(
{ hash, page, rotation },
/* forceReplace = */ true
);
return;
}
// The browser history contains a valid entry, ensure that the history is
// initialized correctly on PDF document load.
let destination = state.destination;
this._updateInternalState(destination, state.uid,
/* removeTemporary = */ true);
this._updateInternalState(
destination,
state.uid,
/* removeTemporary = */ true
);
if (this._uid > this._maxUid) {
this._maxUid = this._uid;
}
@ -178,25 +187,36 @@ class PDFHistory {
* Push an internal destination to the browser history.
* @param {PushParameters}
*/
push({ namedDest = null, explicitDest, pageNumber, }) {
push({ namedDest = null, explicitDest, pageNumber }) {
if (!this._initialized) {
return;
}
if (namedDest && typeof namedDest !== 'string') {
console.error('PDFHistory.push: ' +
`"${namedDest}" is not a valid namedDest parameter.`);
if (namedDest && typeof namedDest !== "string") {
console.error(
"PDFHistory.push: " +
`"${namedDest}" is not a valid namedDest parameter.`
);
return;
} else if (!Array.isArray(explicitDest)) {
console.error('PDFHistory.push: ' +
`"${explicitDest}" is not a valid explicitDest parameter.`);
console.error(
"PDFHistory.push: " +
`"${explicitDest}" is not a valid explicitDest parameter.`
);
return;
} else if (!(Number.isInteger(pageNumber) &&
pageNumber > 0 && pageNumber <= this.linkService.pagesCount)) {
} else if (
!(
Number.isInteger(pageNumber) &&
pageNumber > 0 &&
pageNumber <= this.linkService.pagesCount
)
) {
// Allow an unset `pageNumber` if and only if the history is still empty;
// please refer to the `this._destination.page = null;` comment above.
if (pageNumber !== null || this._destination) {
console.error('PDFHistory.push: ' +
`"${pageNumber}" is not a valid pageNumber parameter.`);
console.error(
"PDFHistory.push: " +
`"${pageNumber}" is not a valid pageNumber parameter.`
);
return;
}
}
@ -209,9 +229,11 @@ class PDFHistory {
}
let forceReplace = false;
if (this._destination &&
(isDestHashesEqual(this._destination.hash, hash) ||
isDestArraysEqual(this._destination.dest, explicitDest))) {
if (
this._destination &&
(isDestHashesEqual(this._destination.hash, hash) ||
isDestArraysEqual(this._destination.dest, explicitDest))
) {
// When the new destination is identical to `this._destination`, and
// its `page` is undefined, replace the current browser history entry.
// NOTE: This can only occur if `this._destination` was set either:
@ -226,12 +248,15 @@ class PDFHistory {
return;
}
this._pushOrReplaceState({
dest: explicitDest,
hash,
page: pageNumber,
rotation: this.linkService.rotation,
}, forceReplace);
this._pushOrReplaceState(
{
dest: explicitDest,
hash,
page: pageNumber,
rotation: this.linkService.rotation,
},
forceReplace
);
if (!this._popStateInProgress) {
// Prevent the browser history from updating while the new destination is
@ -288,8 +313,10 @@ class PDFHistory {
* browser history, useful e.g. for skipping the next 'hashchange' event.
*/
get popStateInProgress() {
return this._initialized &&
(this._popStateInProgress || this._blockHashChange > 0);
return (
this._initialized &&
(this._popStateInProgress || this._blockHashChange > 0)
);
}
get initialBookmark() {
@ -307,12 +334,16 @@ class PDFHistory {
let shouldReplace = forceReplace || !this._destination;
let newState = {
fingerprint: this._fingerprint,
uid: shouldReplace ? this._uid : (this._uid + 1),
uid: shouldReplace ? this._uid : this._uid + 1,
destination,
};
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME') &&
window.history.state && window.history.state.chromecomState) {
if (
typeof PDFJSDev !== "undefined" &&
PDFJSDev.test("CHROME") &&
window.history.state &&
window.history.state.chromecomState
) {
// history.state.chromecomState is managed by chromecom.js.
newState.chromecomState = window.history.state.chromecomState;
}
@ -320,22 +351,26 @@ class PDFHistory {
let newUrl;
if (this._updateUrl && destination && destination.hash) {
const baseUrl = document.location.href.split('#')[0];
if (!baseUrl.startsWith('file://')) { // Prevent errors in Firefox.
const baseUrl = document.location.href.split("#")[0];
if (!baseUrl.startsWith("file://")) {
// Prevent errors in Firefox.
newUrl = `${baseUrl}#${destination.hash}`;
}
}
if (shouldReplace) {
window.history.replaceState(newState, '', newUrl);
window.history.replaceState(newState, "", newUrl);
} else {
this._maxUid = this._uid;
window.history.pushState(newState, '', newUrl);
window.history.pushState(newState, "", newUrl);
}
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME') &&
top === window) {
if (
typeof PDFJSDev !== "undefined" &&
PDFJSDev.test("CHROME") &&
top === window
) {
// eslint-disable-next-line no-undef
chrome.runtime.sendMessage('showPageAction');
chrome.runtime.sendMessage("showPageAction");
}
}
@ -364,9 +399,11 @@ class PDFHistory {
if (this._destination.hash === position.hash) {
return; // The current document position has not changed.
}
if (!this._destination.page &&
(POSITION_UPDATED_THRESHOLD <= 0 ||
this._numPositionUpdates <= POSITION_UPDATED_THRESHOLD)) {
if (
!this._destination.page &&
(POSITION_UPDATED_THRESHOLD <= 0 ||
this._numPositionUpdates <= POSITION_UPDATED_THRESHOLD)
) {
// `this._destination` was set through the user changing the hash of
// the document. Do not add `this._position` to the browser history,
// to avoid "flooding" it with lots of (nearly) identical entries,
@ -375,8 +412,10 @@ class PDFHistory {
}
let forceReplace = false;
if (this._destination.page >= position.first &&
this._destination.page <= position.page) {
if (
this._destination.page >= position.first &&
this._destination.page <= position.page
) {
// When the `page` of `this._destination` is still visible, do not
// update the browsing history when `this._destination` either:
// - contains an internal destination, since in this case we
@ -402,12 +441,14 @@ class PDFHistory {
if (checkReload) {
// Potentially accept the history entry, even if the fingerprints don't
// match, when the viewer was reloaded (see issue 6847).
if (typeof state.fingerprint !== 'string' ||
state.fingerprint.length !== this._fingerprint.length) {
if (
typeof state.fingerprint !== "string" ||
state.fingerprint.length !== this._fingerprint.length
) {
return false;
}
const [perfEntry] = performance.getEntriesByType('navigation');
if (!perfEntry || perfEntry.type !== 'reload') {
const [perfEntry] = performance.getEntriesByType("navigation");
if (!perfEntry || perfEntry.type !== "reload") {
return false;
}
} else {
@ -419,7 +460,7 @@ class PDFHistory {
if (!Number.isInteger(state.uid) || state.uid < 0) {
return false;
}
if (state.destination === null || typeof state.destination !== 'object') {
if (state.destination === null || typeof state.destination !== "object") {
return false;
}
return true;
@ -454,25 +495,31 @@ class PDFHistory {
const hash = unescape(getCurrentHash()).substring(1);
let page = parseQueryString(hash).page | 0;
if (!(Number.isInteger(page) &&
page > 0 && page <= this.linkService.pagesCount)) {
if (
!(
Number.isInteger(page) &&
page > 0 &&
page <= this.linkService.pagesCount
)
) {
page = null;
}
return { hash, page, rotation: this.linkService.rotation, };
return { hash, page, rotation: this.linkService.rotation };
}
/**
* @private
*/
_updateViewarea({ location, }) {
_updateViewarea({ location }) {
if (this._updateViewareaTimeout) {
clearTimeout(this._updateViewareaTimeout);
this._updateViewareaTimeout = null;
}
this._position = {
hash: this._isViewerInPresentationMode ?
`page=${location.pageNumber}` : location.pdfOpenParams.substring(1),
hash: this._isViewerInPresentationMode
? `page=${location.pageNumber}`
: location.pdfOpenParams.substring(1),
page: this.linkService.page,
first: location.pageNumber,
rotation: location.rotation,
@ -482,8 +529,12 @@ class PDFHistory {
return;
}
if (POSITION_UPDATED_THRESHOLD > 0 && this._isPagesLoaded &&
this._destination && !this._destination.page) {
if (
POSITION_UPDATED_THRESHOLD > 0 &&
this._isPagesLoaded &&
this._destination &&
!this._destination.page
) {
// If the current destination was set through the user changing the hash
// of the document, we will usually not try to push the current position
// to the browser history; see `this._tryPushCurrentPosition()`.
@ -523,19 +574,27 @@ class PDFHistory {
/**
* @private
*/
_popState({ state, }) {
let newHash = getCurrentHash(), hashChanged = this._currentHash !== newHash;
_popState({ state }) {
let newHash = getCurrentHash(),
hashChanged = this._currentHash !== newHash;
this._currentHash = newHash;
if ((typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME') &&
state && state.chromecomState && !this._isValidState(state)) ||
!state) {
if (
(typeof PDFJSDev !== "undefined" &&
PDFJSDev.test("CHROME") &&
state &&
state.chromecomState &&
!this._isValidState(state)) ||
!state
) {
// This case corresponds to the user changing the hash of the document.
this._uid++;
const { hash, page, rotation, } = this._parseCurrentHash();
this._pushOrReplaceState({ hash, page, rotation, },
/* forceReplace = */ true);
const { hash, page, rotation } = this._parseCurrentHash();
this._pushOrReplaceState(
{ hash, page, rotation },
/* forceReplace = */ true
);
return;
}
if (!this._isValidState(state)) {
@ -561,7 +620,7 @@ class PDFHistory {
this._blockHashChange++;
waitOnEventOrTimeout({
target: window,
name: 'hashchange',
name: "hashchange",
delay: HASH_CHANGE_TIMEOUT,
}).then(() => {
this._blockHashChange--;
@ -570,8 +629,11 @@ class PDFHistory {
// Navigate to the new destination.
let destination = state.destination;
this._updateInternalState(destination, state.uid,
/* removeTemporary = */ true);
this._updateInternalState(
destination,
state.uid,
/* removeTemporary = */ true
);
if (this._uid > this._maxUid) {
this._maxUid = this._uid;
}
@ -622,9 +684,9 @@ class PDFHistory {
pageHide: this._pageHide.bind(this),
};
this.eventBus.on('updateviewarea', this._boundEvents.updateViewarea);
window.addEventListener('popstate', this._boundEvents.popState);
window.addEventListener('pagehide', this._boundEvents.pageHide);
this.eventBus.on("updateviewarea", this._boundEvents.updateViewarea);
window.addEventListener("popstate", this._boundEvents.popState);
window.addEventListener("pagehide", this._boundEvents.pageHide);
}
/**
@ -634,22 +696,22 @@ class PDFHistory {
if (!this._boundEvents) {
return; // The event listeners were already removed.
}
this.eventBus.off('updateviewarea', this._boundEvents.updateViewarea);
window.removeEventListener('popstate', this._boundEvents.popState);
window.removeEventListener('pagehide', this._boundEvents.pageHide);
this.eventBus.off("updateviewarea", this._boundEvents.updateViewarea);
window.removeEventListener("popstate", this._boundEvents.popState);
window.removeEventListener("pagehide", this._boundEvents.pageHide);
this._boundEvents = null;
}
}
function isDestHashesEqual(destHash, pushHash) {
if (typeof destHash !== 'string' || typeof pushHash !== 'string') {
if (typeof destHash !== "string" || typeof pushHash !== "string") {
return false;
}
if (destHash === pushHash) {
return true;
}
let { nameddest, } = parseQueryString(destHash);
let { nameddest } = parseQueryString(destHash);
if (nameddest === pushHash) {
return true;
}
@ -664,7 +726,7 @@ function isDestArraysEqual(firstDest, secondDest) {
if (Array.isArray(first) || Array.isArray(second)) {
return false;
}
if (first !== null && typeof first === 'object' && second !== null) {
if (first !== null && typeof first === "object" && second !== null) {
if (Object.keys(first).length !== Object.keys(second).length) {
return false;
}
@ -692,8 +754,4 @@ function isDestArraysEqual(firstDest, secondDest) {
return true;
}
export {
PDFHistory,
isDestHashesEqual,
isDestArraysEqual,
};
export { PDFHistory, isDestHashesEqual, isDestArraysEqual };

View file

@ -13,7 +13,7 @@
* limitations under the License.
*/
import { getGlobalEventBus, parseQueryString } from './ui_utils';
import { getGlobalEventBus, parseQueryString } from "./ui_utils";
/**
* @typedef {Object} PDFLinkServiceOptions
@ -34,8 +34,12 @@ class PDFLinkService {
/**
* @param {PDFLinkServiceOptions} options
*/
constructor({ eventBus, externalLinkTarget = null,
externalLinkRel = null, externalLinkEnabled = true, } = {}) {
constructor({
eventBus,
externalLinkTarget = null,
externalLinkRel = null,
externalLinkEnabled = true,
} = {}) {
this.eventBus = eventBus || getGlobalEventBus();
this.externalLinkTarget = externalLinkTarget;
this.externalLinkRel = externalLinkRel;
@ -102,9 +106,10 @@ class PDFLinkService {
* @param {string|Array} dest - The named, or explicit, PDF destination.
*/
navigateTo(dest) {
let goToDestination = ({ namedDest, explicitDest, }) => {
let goToDestination = ({ namedDest, explicitDest }) => {
// Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
let destRef = explicitDest[0], pageNumber;
let destRef = explicitDest[0],
pageNumber;
if (destRef instanceof Object) {
pageNumber = this._cachedPageNumber(destRef);
@ -112,25 +117,34 @@ class PDFLinkService {
if (pageNumber === null) {
// Fetch the page reference if it's not yet available. This could
// only occur during loading, before all pages have been resolved.
this.pdfDocument.getPageIndex(destRef).then((pageIndex) => {
this.cachePageRef(pageIndex + 1, destRef);
goToDestination({ namedDest, explicitDest, });
}).catch(() => {
console.error(`PDFLinkService.navigateTo: "${destRef}" is not ` +
`a valid page reference, for dest="${dest}".`);
});
this.pdfDocument
.getPageIndex(destRef)
.then(pageIndex => {
this.cachePageRef(pageIndex + 1, destRef);
goToDestination({ namedDest, explicitDest });
})
.catch(() => {
console.error(
`PDFLinkService.navigateTo: "${destRef}" is not ` +
`a valid page reference, for dest="${dest}".`
);
});
return;
}
} else if (Number.isInteger(destRef)) {
pageNumber = destRef + 1;
} else {
console.error(`PDFLinkService.navigateTo: "${destRef}" is not ` +
`a valid destination reference, for dest="${dest}".`);
console.error(
`PDFLinkService.navigateTo: "${destRef}" is not ` +
`a valid destination reference, for dest="${dest}".`
);
return;
}
if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
console.error(`PDFLinkService.navigateTo: "${pageNumber}" is not ` +
`a valid page number, for dest="${dest}".`);
console.error(
`PDFLinkService.navigateTo: "${pageNumber}" is not ` +
`a valid page number, for dest="${dest}".`
);
return;
}
@ -138,7 +152,7 @@ class PDFLinkService {
// Update the browser history before scrolling the new destination into
// view, to be able to accurately capture the current document position.
this.pdfHistory.pushCurrentPosition();
this.pdfHistory.push({ namedDest, explicitDest, pageNumber, });
this.pdfHistory.push({ namedDest, explicitDest, pageNumber });
}
this.pdfViewer.scrollPageIntoView({
@ -148,8 +162,8 @@ class PDFLinkService {
};
new Promise((resolve, reject) => {
if (typeof dest === 'string') {
this.pdfDocument.getDestination(dest).then((destArray) => {
if (typeof dest === "string") {
this.pdfDocument.getDestination(dest).then(destArray => {
resolve({
namedDest: dest,
explicitDest: destArray,
@ -158,13 +172,15 @@ class PDFLinkService {
return;
}
resolve({
namedDest: '',
namedDest: "",
explicitDest: dest,
});
}).then((data) => {
}).then(data => {
if (!Array.isArray(data.explicitDest)) {
console.error(`PDFLinkService.navigateTo: "${data.explicitDest}" is` +
` not a valid destination array, for dest="${dest}".`);
console.error(
`PDFLinkService.navigateTo: "${data.explicitDest}" is` +
` not a valid destination array, for dest="${dest}".`
);
return;
}
goToDestination(data);
@ -176,14 +192,14 @@ class PDFLinkService {
* @returns {string} The hyperlink to the PDF object.
*/
getDestinationHash(dest) {
if (typeof dest === 'string') {
return this.getAnchorUrl('#' + escape(dest));
if (typeof dest === "string") {
return this.getAnchorUrl("#" + escape(dest));
}
if (Array.isArray(dest)) {
let str = JSON.stringify(dest);
return this.getAnchorUrl('#' + escape(str));
return this.getAnchorUrl("#" + escape(str));
}
return this.getAnchorUrl('');
return this.getAnchorUrl("");
}
/**
@ -193,7 +209,7 @@ class PDFLinkService {
* @returns {string} The hyperlink to the PDF object.
*/
getAnchorUrl(anchor) {
return (this.baseUrl || '') + anchor;
return (this.baseUrl || "") + anchor;
}
/**
@ -201,55 +217,73 @@ class PDFLinkService {
*/
setHash(hash) {
let pageNumber, dest;
if (hash.includes('=')) {
if (hash.includes("=")) {
let params = parseQueryString(hash);
if ('search' in params) {
this.eventBus.dispatch('findfromurlhash', {
if ("search" in params) {
this.eventBus.dispatch("findfromurlhash", {
source: this,
query: params['search'].replace(/"/g, ''),
phraseSearch: (params['phrase'] === 'true'),
query: params["search"].replace(/"/g, ""),
phraseSearch: params["phrase"] === "true",
});
}
// borrowing syntax from "Parameters for Opening PDF Files"
if ('nameddest' in params) {
if ("nameddest" in params) {
this.navigateTo(params.nameddest);
return;
}
if ('page' in params) {
pageNumber = (params.page | 0) || 1;
if ("page" in params) {
pageNumber = params.page | 0 || 1;
}
if ('zoom' in params) {
if ("zoom" in params) {
// Build the destination array.
let zoomArgs = params.zoom.split(','); // scale,left,top
let zoomArgs = params.zoom.split(","); // scale,left,top
let zoomArg = zoomArgs[0];
let zoomArgNumber = parseFloat(zoomArg);
if (!zoomArg.includes('Fit')) {
if (!zoomArg.includes("Fit")) {
// If the zoomArg is a number, it has to get divided by 100. If it's
// a string, it should stay as it is.
dest = [null, { name: 'XYZ', },
zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
(zoomArgNumber ? zoomArgNumber / 100 : zoomArg)];
dest = [
null,
{ name: "XYZ" },
zoomArgs.length > 1 ? zoomArgs[1] | 0 : null,
zoomArgs.length > 2 ? zoomArgs[2] | 0 : null,
zoomArgNumber ? zoomArgNumber / 100 : zoomArg,
];
} else {
if (zoomArg === 'Fit' || zoomArg === 'FitB') {
dest = [null, { name: zoomArg, }];
} else if ((zoomArg === 'FitH' || zoomArg === 'FitBH') ||
(zoomArg === 'FitV' || zoomArg === 'FitBV')) {
dest = [null, { name: zoomArg, },
zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null];
} else if (zoomArg === 'FitR') {
if (zoomArg === "Fit" || zoomArg === "FitB") {
dest = [null, { name: zoomArg }];
} else if (
zoomArg === "FitH" ||
zoomArg === "FitBH" ||
zoomArg === "FitV" ||
zoomArg === "FitBV"
) {
dest = [
null,
{ name: zoomArg },
zoomArgs.length > 1 ? zoomArgs[1] | 0 : null,
];
} else if (zoomArg === "FitR") {
if (zoomArgs.length !== 5) {
console.error(
'PDFLinkService.setHash: Not enough parameters for "FitR".');
'PDFLinkService.setHash: Not enough parameters for "FitR".'
);
} else {
dest = [null, { name: zoomArg, },
(zoomArgs[1] | 0), (zoomArgs[2] | 0),
(zoomArgs[3] | 0), (zoomArgs[4] | 0)];
dest = [
null,
{ name: zoomArg },
zoomArgs[1] | 0,
zoomArgs[2] | 0,
zoomArgs[3] | 0,
zoomArgs[4] | 0,
];
}
} else {
console.error(`PDFLinkService.setHash: "${zoomArg}" is not ` +
'a valid zoom value.');
console.error(
`PDFLinkService.setHash: "${zoomArg}" is not ` +
"a valid zoom value."
);
}
}
}
@ -262,13 +296,14 @@ class PDFLinkService {
} else if (pageNumber) {
this.page = pageNumber; // simple page
}
if ('pagemode' in params) {
this.eventBus.dispatch('pagemode', {
if ("pagemode" in params) {
this.eventBus.dispatch("pagemode", {
source: this,
mode: params.pagemode,
});
}
} else { // Named (or explicit) destination.
} else {
// Named (or explicit) destination.
dest = unescape(hash);
try {
dest = JSON.parse(dest);
@ -280,12 +315,14 @@ class PDFLinkService {
}
} catch (ex) {}
if (typeof dest === 'string' || isValidExplicitDestination(dest)) {
if (typeof dest === "string" || isValidExplicitDestination(dest)) {
this.navigateTo(dest);
return;
}
console.error(`PDFLinkService.setHash: "${unescape(hash)}" is not ` +
'a valid destination.');
console.error(
`PDFLinkService.setHash: "${unescape(hash)}" is not ` +
"a valid destination."
);
}
}
@ -295,35 +332,35 @@ class PDFLinkService {
executeNamedAction(action) {
// See PDF reference, table 8.45 - Named action
switch (action) {
case 'GoBack':
case "GoBack":
if (this.pdfHistory) {
this.pdfHistory.back();
}
break;
case 'GoForward':
case "GoForward":
if (this.pdfHistory) {
this.pdfHistory.forward();
}
break;
case 'NextPage':
case "NextPage":
if (this.page < this.pagesCount) {
this.page++;
}
break;
case 'PrevPage':
case "PrevPage":
if (this.page > 1) {
this.page--;
}
break;
case 'LastPage':
case "LastPage":
this.page = this.pagesCount;
break;
case 'FirstPage':
case "FirstPage":
this.page = 1;
break;
@ -331,7 +368,7 @@ class PDFLinkService {
break; // No action according to spec
}
this.eventBus.dispatch('namedaction', {
this.eventBus.dispatch("namedaction", {
source: this,
action,
});
@ -345,14 +382,14 @@ class PDFLinkService {
if (!pageRef) {
return;
}
const refStr = pageRef.gen === 0 ? `${pageRef.num}R` :
`${pageRef.num}R${pageRef.gen}`;
const refStr =
pageRef.gen === 0 ? `${pageRef.num}R` : `${pageRef.num}R${pageRef.gen}`;
this._pagesRefCache[refStr] = pageNum;
}
_cachedPageNumber(pageRef) {
const refStr = pageRef.gen === 0 ? `${pageRef.num}R` :
`${pageRef.num}R${pageRef.gen}`;
const refStr =
pageRef.gen === 0 ? `${pageRef.num}R` : `${pageRef.num}R${pageRef.gen}`;
return (this._pagesRefCache && this._pagesRefCache[refStr]) || null;
}
@ -368,38 +405,44 @@ function isValidExplicitDestination(dest) {
if (!Array.isArray(dest)) {
return false;
}
let destLength = dest.length, allowNull = true;
let destLength = dest.length,
allowNull = true;
if (destLength < 2) {
return false;
}
let page = dest[0];
if (!(typeof page === 'object' &&
Number.isInteger(page.num) && Number.isInteger(page.gen)) &&
!(Number.isInteger(page) && page >= 0)) {
if (
!(
typeof page === "object" &&
Number.isInteger(page.num) &&
Number.isInteger(page.gen)
) &&
!(Number.isInteger(page) && page >= 0)
) {
return false;
}
let zoom = dest[1];
if (!(typeof zoom === 'object' && typeof zoom.name === 'string')) {
if (!(typeof zoom === "object" && typeof zoom.name === "string")) {
return false;
}
switch (zoom.name) {
case 'XYZ':
case "XYZ":
if (destLength !== 5) {
return false;
}
break;
case 'Fit':
case 'FitB':
case "Fit":
case "FitB":
return destLength === 2;
case 'FitH':
case 'FitBH':
case 'FitV':
case 'FitBV':
case "FitH":
case "FitBH":
case "FitV":
case "FitBV":
if (destLength !== 3) {
return false;
}
break;
case 'FitR':
case "FitR":
if (destLength !== 6) {
return false;
}
@ -410,7 +453,7 @@ function isValidExplicitDestination(dest) {
}
for (let i = 2; i < destLength; i++) {
let param = dest[i];
if (!(typeof param === 'number' || (allowNull && param === null))) {
if (!(typeof param === "number" || (allowNull && param === null))) {
return false;
}
}
@ -468,7 +511,7 @@ class SimpleLinkService {
* @returns {string} The hyperlink to the PDF object.
*/
getDestinationHash(dest) {
return '#';
return "#";
}
/**
@ -476,7 +519,7 @@ class SimpleLinkService {
* @returns {string} The hyperlink to the PDF object.
*/
getAnchorUrl(hash) {
return '#';
return "#";
}
/**
@ -503,7 +546,4 @@ class SimpleLinkService {
}
}
export {
PDFLinkService,
SimpleLinkService,
};
export { PDFLinkService, SimpleLinkService };

View file

@ -13,9 +13,9 @@
* limitations under the License.
*/
import { addLinkAttributes, LinkTarget, removeNullCharacters } from 'pdfjs-lib';
import { addLinkAttributes, LinkTarget, removeNullCharacters } from "pdfjs-lib";
const DEFAULT_TITLE = '\u2013';
const DEFAULT_TITLE = "\u2013";
/**
* @typedef {Object} PDFOutlineViewerOptions
@ -33,14 +33,14 @@ class PDFOutlineViewer {
/**
* @param {PDFOutlineViewerOptions} options
*/
constructor({ container, linkService, eventBus, }) {
constructor({ container, linkService, eventBus }) {
this.container = container;
this.linkService = linkService;
this.eventBus = eventBus;
this.reset();
eventBus.on('toggleoutlinetree', this.toggleOutlineTree.bind(this));
eventBus.on("toggleoutlinetree", this.toggleOutlineTree.bind(this));
}
reset() {
@ -48,18 +48,18 @@ class PDFOutlineViewer {
this.lastToggleIsShow = true;
// Remove the outline from the DOM.
this.container.textContent = '';
this.container.textContent = "";
// Ensure that the left (right in RTL locales) margin is always reset,
// to prevent incorrect outline alignment if a new document is opened.
this.container.classList.remove('outlineWithDeepNesting');
this.container.classList.remove("outlineWithDeepNesting");
}
/**
* @private
*/
_dispatchEvent(outlineCount) {
this.eventBus.dispatch('outlineloaded', {
this.eventBus.dispatch("outlineloaded", {
source: this,
outlineCount,
});
@ -68,13 +68,13 @@ class PDFOutlineViewer {
/**
* @private
*/
_bindLink(element, { url, newWindow, dest, }) {
let { linkService, } = this;
_bindLink(element, { url, newWindow, dest }) {
let { linkService } = this;
if (url) {
addLinkAttributes(element, {
url,
target: (newWindow ? LinkTarget.BLANK : linkService.externalLinkTarget),
target: newWindow ? LinkTarget.BLANK : linkService.externalLinkTarget,
rel: linkService.externalLinkRel,
enabled: linkService.externalLinkEnabled,
});
@ -93,17 +93,17 @@ class PDFOutlineViewer {
/**
* @private
*/
_setStyles(element, { bold, italic, }) {
let styleStr = '';
_setStyles(element, { bold, italic }) {
let styleStr = "";
if (bold) {
styleStr += 'font-weight: bold;';
styleStr += "font-weight: bold;";
}
if (italic) {
styleStr += 'font-style: italic;';
styleStr += "font-style: italic;";
}
if (styleStr) {
element.setAttribute('style', styleStr);
element.setAttribute("style", styleStr);
}
}
@ -113,18 +113,18 @@ class PDFOutlineViewer {
*
* @private
*/
_addToggleButton(div, { count, items, }) {
let toggler = document.createElement('div');
toggler.className = 'outlineItemToggler';
_addToggleButton(div, { count, items }) {
let toggler = document.createElement("div");
toggler.className = "outlineItemToggler";
if (count < 0 && Math.abs(count) === items.length) {
toggler.classList.add('outlineItemsHidden');
toggler.classList.add("outlineItemsHidden");
}
toggler.onclick = (evt) => {
toggler.onclick = evt => {
evt.stopPropagation();
toggler.classList.toggle('outlineItemsHidden');
toggler.classList.toggle("outlineItemsHidden");
if (evt.shiftKey) {
let shouldShowAll = !toggler.classList.contains('outlineItemsHidden');
let shouldShowAll = !toggler.classList.contains("outlineItemsHidden");
this._toggleOutlineItem(div, shouldShowAll);
}
};
@ -142,8 +142,8 @@ class PDFOutlineViewer {
*/
_toggleOutlineItem(root, show = false) {
this.lastToggleIsShow = show;
for (const toggler of root.querySelectorAll('.outlineItemToggler')) {
toggler.classList.toggle('outlineItemsHidden', !show);
for (const toggler of root.querySelectorAll(".outlineItemToggler")) {
toggler.classList.toggle("outlineItemsHidden", !show);
}
}
@ -160,7 +160,7 @@ class PDFOutlineViewer {
/**
* @param {PDFOutlineViewerRenderParameters} params
*/
render({ outline, }) {
render({ outline }) {
let outlineCount = 0;
if (this.outline) {
@ -174,19 +174,18 @@ class PDFOutlineViewer {
}
let fragment = document.createDocumentFragment();
let queue = [{ parent: fragment, items: this.outline, }];
let queue = [{ parent: fragment, items: this.outline }];
let hasAnyNesting = false;
while (queue.length > 0) {
const levelData = queue.shift();
for (const item of levelData.items) {
let div = document.createElement('div');
div.className = 'outlineItem';
let div = document.createElement("div");
div.className = "outlineItem";
let element = document.createElement('a');
let element = document.createElement("a");
this._bindLink(element, item);
this._setStyles(element, item);
element.textContent =
removeNullCharacters(item.title) || DEFAULT_TITLE;
element.textContent = removeNullCharacters(item.title) || DEFAULT_TITLE;
div.appendChild(element);
@ -194,10 +193,10 @@ class PDFOutlineViewer {
hasAnyNesting = true;
this._addToggleButton(div, item);
let itemsDiv = document.createElement('div');
itemsDiv.className = 'outlineItems';
let itemsDiv = document.createElement("div");
itemsDiv.className = "outlineItems";
div.appendChild(itemsDiv);
queue.push({ parent: itemsDiv, items: item.items, });
queue.push({ parent: itemsDiv, items: item.items });
}
levelData.parent.appendChild(div);
@ -205,10 +204,10 @@ class PDFOutlineViewer {
}
}
if (hasAnyNesting) {
this.container.classList.add('outlineWithDeepNesting');
this.container.classList.add("outlineWithDeepNesting");
this.lastToggleIsShow =
(fragment.querySelectorAll('.outlineItemsHidden').length === 0);
fragment.querySelectorAll(".outlineItemsHidden").length === 0;
}
this.container.appendChild(fragment);
@ -217,6 +216,4 @@ class PDFOutlineViewer {
}
}
export {
PDFOutlineViewer,
};
export { PDFOutlineViewer };

View file

@ -14,14 +14,23 @@
*/
import {
approximateFraction, CSS_UNITS, DEFAULT_SCALE, getGlobalEventBus,
getOutputScale, NullL10n, RendererType, roundToDivide, TextLayerMode
} from './ui_utils';
approximateFraction,
CSS_UNITS,
DEFAULT_SCALE,
getGlobalEventBus,
getOutputScale,
NullL10n,
RendererType,
roundToDivide,
TextLayerMode,
} from "./ui_utils";
import {
createPromiseCapability, RenderingCancelledException, SVGGraphics
} from 'pdfjs-lib';
import { RenderingStates } from './pdf_rendering_queue';
import { viewerCompatibilityParams } from './viewer_compatibility';
createPromiseCapability,
RenderingCancelledException,
SVGGraphics,
} from "pdfjs-lib";
import { RenderingStates } from "./pdf_rendering_queue";
import { viewerCompatibilityParams } from "./viewer_compatibility";
/**
* @typedef {Object} PDFPageViewOptions
@ -66,7 +75,7 @@ class PDFPageView {
let defaultViewport = options.defaultViewport;
this.id = options.id;
this.renderingId = 'page' + this.id;
this.renderingId = "page" + this.id;
this.pdfPage = null;
this.pageLabel = null;
@ -75,9 +84,10 @@ class PDFPageView {
this.viewport = defaultViewport;
this.pdfPageRotate = defaultViewport.rotation;
this.hasRestrictedScaling = false;
this.textLayerMode = Number.isInteger(options.textLayerMode) ?
options.textLayerMode : TextLayerMode.ENABLE;
this.imageResourcesPath = options.imageResourcesPath || '';
this.textLayerMode = Number.isInteger(options.textLayerMode)
? options.textLayerMode
: TextLayerMode.ENABLE;
this.imageResourcesPath = options.imageResourcesPath || "";
this.renderInteractiveForms = options.renderInteractiveForms || false;
this.useOnlyCssZoom = options.useOnlyCssZoom || false;
this.maxCanvasPixels = options.maxCanvasPixels || MAX_CANVAS_PIXELS;
@ -100,11 +110,11 @@ class PDFPageView {
this.textLayer = null;
this.zoomLayer = null;
let div = document.createElement('div');
div.className = 'page';
div.style.width = Math.floor(this.viewport.width) + 'px';
div.style.height = Math.floor(this.viewport.height) + 'px';
div.setAttribute('data-page-number', this.id);
let div = document.createElement("div");
div.className = "page";
div.style.width = Math.floor(this.viewport.width) + "px";
div.style.height = Math.floor(this.viewport.height) + "px";
div.setAttribute("data-page-number", this.id);
this.div = div;
container.appendChild(div);
@ -115,8 +125,10 @@ class PDFPageView {
this.pdfPageRotate = pdfPage.rotate;
let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = pdfPage.getViewport({ scale: this.scale * CSS_UNITS,
rotation: totalRotation, });
this.viewport = pdfPage.getViewport({
scale: this.scale * CSS_UNITS,
rotation: totalRotation,
});
this.stats = pdfPage.stats;
this.reset();
}
@ -154,13 +166,14 @@ class PDFPageView {
this.renderingState = RenderingStates.INITIAL;
let div = this.div;
div.style.width = Math.floor(this.viewport.width) + 'px';
div.style.height = Math.floor(this.viewport.height) + 'px';
div.style.width = Math.floor(this.viewport.width) + "px";
div.style.height = Math.floor(this.viewport.height) + "px";
let childNodes = div.childNodes;
let currentZoomLayerNode = (keepZoomLayer && this.zoomLayer) || null;
let currentAnnotationNode = (keepAnnotations && this.annotationLayer &&
this.annotationLayer.div) || null;
let currentAnnotationNode =
(keepAnnotations && this.annotationLayer && this.annotationLayer.div) ||
null;
for (let i = childNodes.length - 1; i >= 0; i--) {
let node = childNodes[i];
if (currentZoomLayerNode === node || currentAnnotationNode === node) {
@ -168,7 +181,7 @@ class PDFPageView {
}
div.removeChild(node);
}
div.removeAttribute('data-loaded');
div.removeAttribute("data-loaded");
if (currentAnnotationNode) {
// Hide the annotation layer until all elements are resized
@ -195,14 +208,15 @@ class PDFPageView {
delete this.svg;
}
this.loadingIconDiv = document.createElement('div');
this.loadingIconDiv.className = 'loadingIcon';
this.loadingIconDiv = document.createElement("div");
this.loadingIconDiv.className = "loadingIcon";
div.appendChild(this.loadingIconDiv);
}
update(scale, rotation) {
this.scale = scale || this.scale;
if (typeof rotation !== 'undefined') { // The rotation may be zero.
if (typeof rotation !== "undefined") {
// The rotation may be zero.
this.rotation = rotation;
}
@ -215,7 +229,7 @@ class PDFPageView {
if (this.svg) {
this.cssTransform(this.svg, true);
this.eventBus.dispatch('pagerendered', {
this.eventBus.dispatch("pagerendered", {
source: this,
pageNumber: this.id,
cssTransform: true,
@ -227,19 +241,23 @@ class PDFPageView {
let isScalingRestricted = false;
if (this.canvas && this.maxCanvasPixels > 0) {
let outputScale = this.outputScale;
if (((Math.floor(this.viewport.width) * outputScale.sx) | 0) *
if (
((Math.floor(this.viewport.width) * outputScale.sx) | 0) *
((Math.floor(this.viewport.height) * outputScale.sy) | 0) >
this.maxCanvasPixels) {
this.maxCanvasPixels
) {
isScalingRestricted = true;
}
}
if (this.canvas) {
if (this.useOnlyCssZoom ||
(this.hasRestrictedScaling && isScalingRestricted)) {
if (
this.useOnlyCssZoom ||
(this.hasRestrictedScaling && isScalingRestricted)
) {
this.cssTransform(this.canvas, true);
this.eventBus.dispatch('pagerendered', {
this.eventBus.dispatch("pagerendered", {
source: this,
pageNumber: this.id,
cssTransform: true,
@ -247,9 +265,9 @@ class PDFPageView {
});
return;
}
if (!this.zoomLayer && !this.canvas.hasAttribute('hidden')) {
if (!this.zoomLayer && !this.canvas.hasAttribute("hidden")) {
this.zoomLayer = this.canvas.parentNode;
this.zoomLayer.style.position = 'absolute';
this.zoomLayer.style.position = "absolute";
}
}
if (this.zoomLayer) {
@ -285,21 +303,29 @@ class PDFPageView {
let height = this.viewport.height;
let div = this.div;
target.style.width = target.parentNode.style.width = div.style.width =
Math.floor(width) + 'px';
Math.floor(width) + "px";
target.style.height = target.parentNode.style.height = div.style.height =
Math.floor(height) + 'px';
Math.floor(height) + "px";
// The canvas may have been originally rotated; rotate relative to that.
let relativeRotation = this.viewport.rotation -
this.paintedViewportMap.get(target).rotation;
let relativeRotation =
this.viewport.rotation - this.paintedViewportMap.get(target).rotation;
let absRotation = Math.abs(relativeRotation);
let scaleX = 1, scaleY = 1;
let scaleX = 1,
scaleY = 1;
if (absRotation === 90 || absRotation === 270) {
// Scale x and y because of the rotation.
scaleX = height / width;
scaleY = width / height;
}
let cssTransform = 'rotate(' + relativeRotation + 'deg) ' +
'scale(' + scaleX + ',' + scaleY + ')';
let cssTransform =
"rotate(" +
relativeRotation +
"deg) " +
"scale(" +
scaleX +
"," +
scaleY +
")";
target.style.transform = cssTransform;
if (this.textLayer) {
@ -308,8 +334,8 @@ class PDFPageView {
// TODO: This could probably be simplified by drawing the text layer in
// one orientation and then rotating overall.
let textLayerViewport = this.textLayer.viewport;
let textRelativeRotation = this.viewport.rotation -
textLayerViewport.rotation;
let textRelativeRotation =
this.viewport.rotation - textLayerViewport.rotation;
let textAbsRotation = Math.abs(textRelativeRotation);
let scale = width / textLayerViewport.width;
if (textAbsRotation === 90 || textAbsRotation === 270) {
@ -323,30 +349,40 @@ class PDFPageView {
break;
case 90:
transX = 0;
transY = '-' + textLayerDiv.style.height;
transY = "-" + textLayerDiv.style.height;
break;
case 180:
transX = '-' + textLayerDiv.style.width;
transY = '-' + textLayerDiv.style.height;
transX = "-" + textLayerDiv.style.width;
transY = "-" + textLayerDiv.style.height;
break;
case 270:
transX = '-' + textLayerDiv.style.width;
transX = "-" + textLayerDiv.style.width;
transY = 0;
break;
default:
console.error('Bad rotation value.');
console.error("Bad rotation value.");
break;
}
textLayerDiv.style.transform =
'rotate(' + textAbsRotation + 'deg) ' +
'scale(' + scale + ', ' + scale + ') ' +
'translate(' + transX + ', ' + transY + ')';
textLayerDiv.style.transformOrigin = '0% 0%';
"rotate(" +
textAbsRotation +
"deg) " +
"scale(" +
scale +
", " +
scale +
") " +
"translate(" +
transX +
", " +
transY +
")";
textLayerDiv.style.transformOrigin = "0% 0%";
}
if (redrawAnnotations && this.annotationLayer) {
this.annotationLayer.render(this.viewport, 'display');
this.annotationLayer.render(this.viewport, "display");
}
}
@ -364,13 +400,13 @@ class PDFPageView {
draw() {
if (this.renderingState !== RenderingStates.INITIAL) {
console.error('Must be in new state before drawing');
console.error("Must be in new state before drawing");
this.reset(); // Ensure that we reset all state to prevent issues.
}
if (!this.pdfPage) {
this.renderingState = RenderingStates.FINISHED;
return Promise.reject(new Error('Page is not loaded'));
return Promise.reject(new Error("Page is not loaded"));
}
this.renderingState = RenderingStates.RUNNING;
@ -379,10 +415,10 @@ class PDFPageView {
let div = this.div;
// Wrap the canvas so that if it has a CSS transform for high DPI the
// overflow will be hidden in Firefox.
let canvasWrapper = document.createElement('div');
let canvasWrapper = document.createElement("div");
canvasWrapper.style.width = div.style.width;
canvasWrapper.style.height = div.style.height;
canvasWrapper.classList.add('canvasWrapper');
canvasWrapper.classList.add("canvasWrapper");
if (this.annotationLayer && this.annotationLayer.div) {
// The annotation layer needs to stay on top.
@ -393,8 +429,8 @@ class PDFPageView {
let textLayer = null;
if (this.textLayerMode !== TextLayerMode.DISABLE && this.textLayerFactory) {
let textLayerDiv = document.createElement('div');
textLayerDiv.className = 'textLayer';
let textLayerDiv = document.createElement("div");
textLayerDiv.className = "textLayer";
textLayerDiv.style.width = canvasWrapper.style.width;
textLayerDiv.style.height = canvasWrapper.style.height;
if (this.annotationLayer && this.annotationLayer.div) {
@ -404,15 +440,18 @@ class PDFPageView {
div.appendChild(textLayerDiv);
}
textLayer = this.textLayerFactory.
createTextLayerBuilder(textLayerDiv, this.id - 1, this.viewport,
this.textLayerMode === TextLayerMode.ENABLE_ENHANCE);
textLayer = this.textLayerFactory.createTextLayerBuilder(
textLayerDiv,
this.id - 1,
this.viewport,
this.textLayerMode === TextLayerMode.ENABLE_ENHANCE
);
}
this.textLayer = textLayer;
let renderContinueCallback = null;
if (this.renderingQueue) {
renderContinueCallback = (cont) => {
renderContinueCallback = cont => {
if (!this.renderingQueue.isHighestPriority(this)) {
this.renderingState = RenderingStates.PAUSED;
this.resume = () => {
@ -425,7 +464,7 @@ class PDFPageView {
};
}
const finishPaintTask = async (error) => {
const finishPaintTask = async error => {
// The paintTask may have been replaced by a new one, so only remove
// the reference to the paintTask if it matches the one that is
// triggering this callback.
@ -449,7 +488,7 @@ class PDFPageView {
this.error = error;
this.stats = pdfPage.stats;
this.eventBus.dispatch('pagerendered', {
this.eventBus.dispatch("pagerendered", {
source: this,
pageNumber: this.id,
cssTransform: false,
@ -461,37 +500,45 @@ class PDFPageView {
}
};
let paintTask = this.renderer === RendererType.SVG ?
this.paintOnSvg(canvasWrapper) :
this.paintOnCanvas(canvasWrapper);
let paintTask =
this.renderer === RendererType.SVG
? this.paintOnSvg(canvasWrapper)
: this.paintOnCanvas(canvasWrapper);
paintTask.onRenderContinue = renderContinueCallback;
this.paintTask = paintTask;
let resultPromise = paintTask.promise.then(function() {
return finishPaintTask(null).then(function () {
if (textLayer) {
let readableStream = pdfPage.streamTextContent({
normalizeWhitespace: true,
});
textLayer.setTextContentStream(readableStream);
textLayer.render();
}
});
}, function(reason) {
return finishPaintTask(reason);
});
let resultPromise = paintTask.promise.then(
function() {
return finishPaintTask(null).then(function() {
if (textLayer) {
let readableStream = pdfPage.streamTextContent({
normalizeWhitespace: true,
});
textLayer.setTextContentStream(readableStream);
textLayer.render();
}
});
},
function(reason) {
return finishPaintTask(reason);
}
);
if (this.annotationLayerFactory) {
if (!this.annotationLayer) {
this.annotationLayer = this.annotationLayerFactory.
createAnnotationLayerBuilder(div, pdfPage, this.imageResourcesPath,
this.renderInteractiveForms, this.l10n);
this.annotationLayer = this.annotationLayerFactory.createAnnotationLayerBuilder(
div,
pdfPage,
this.imageResourcesPath,
this.renderInteractiveForms,
this.l10n
);
}
this.annotationLayer.render(this.viewport, 'display');
this.annotationLayer.render(this.viewport, "display");
}
div.setAttribute('data-loaded', true);
div.setAttribute("data-loaded", true);
this.eventBus.dispatch('pagerender', {
this.eventBus.dispatch("pagerender", {
source: this,
pageNumber: this.id,
});
@ -511,16 +558,16 @@ class PDFPageView {
};
let viewport = this.viewport;
let canvas = document.createElement('canvas');
let canvas = document.createElement("canvas");
canvas.id = this.renderingId;
// Keep the canvas hidden until the first draw callback, or until drawing
// is complete when `!this.renderingQueue`, to prevent black flickering.
canvas.setAttribute('hidden', 'hidden');
canvas.setAttribute("hidden", "hidden");
let isCanvasHidden = true;
let showCanvas = function () {
let showCanvas = function() {
if (isCanvasHidden) {
canvas.removeAttribute('hidden');
canvas.removeAttribute("hidden");
isCanvasHidden = false;
}
};
@ -528,17 +575,19 @@ class PDFPageView {
canvasWrapper.appendChild(canvas);
this.canvas = canvas;
if (typeof PDFJSDev === 'undefined' ||
PDFJSDev.test('MOZCENTRAL || FIREFOX || GENERIC')) {
if (
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("MOZCENTRAL || FIREFOX || GENERIC")
) {
canvas.mozOpaque = true;
}
let ctx = canvas.getContext('2d', { alpha: false, });
let ctx = canvas.getContext("2d", { alpha: false });
let outputScale = getOutputScale(ctx);
this.outputScale = outputScale;
if (this.useOnlyCssZoom) {
let actualSizeViewport = viewport.clone({ scale: CSS_UNITS, });
let actualSizeViewport = viewport.clone({ scale: CSS_UNITS });
// Use a scale that makes the canvas have the originally intended size
// of the page.
outputScale.sx *= actualSizeViewport.width / viewport.width;
@ -563,14 +612,15 @@ class PDFPageView {
let sfy = approximateFraction(outputScale.sy);
canvas.width = roundToDivide(viewport.width * outputScale.sx, sfx[0]);
canvas.height = roundToDivide(viewport.height * outputScale.sy, sfy[0]);
canvas.style.width = roundToDivide(viewport.width, sfx[1]) + 'px';
canvas.style.height = roundToDivide(viewport.height, sfy[1]) + 'px';
canvas.style.width = roundToDivide(viewport.width, sfx[1]) + "px";
canvas.style.height = roundToDivide(viewport.height, sfy[1]) + "px";
// Add the viewport so it's known what it was originally drawn with.
this.paintedViewportMap.set(canvas, viewport);
// Rendering area
let transform = !outputScale.scaled ? null :
[outputScale.sx, 0, 0, outputScale.sy, 0, 0];
let transform = !outputScale.scaled
? null
: [outputScale.sx, 0, 0, outputScale.sy, 0, 0];
let renderContext = {
canvasContext: ctx,
transform,
@ -579,7 +629,7 @@ class PDFPageView {
renderInteractiveForms: this.renderInteractiveForms,
};
let renderTask = this.pdfPage.render(renderContext);
renderTask.onContinue = function (cont) {
renderTask.onContinue = function(cont) {
showCanvas();
if (result.onRenderContinue) {
result.onRenderContinue(cont);
@ -588,25 +638,30 @@ class PDFPageView {
}
};
renderTask.promise.then(function() {
showCanvas();
renderCapability.resolve(undefined);
}, function(error) {
showCanvas();
renderCapability.reject(error);
});
renderTask.promise.then(
function() {
showCanvas();
renderCapability.resolve(undefined);
},
function(error) {
showCanvas();
renderCapability.reject(error);
}
);
return result;
}
paintOnSvg(wrapper) {
if (typeof PDFJSDev !== 'undefined' &&
PDFJSDev.test('FIREFOX || MOZCENTRAL || CHROME')) {
if (
typeof PDFJSDev !== "undefined" &&
PDFJSDev.test("FIREFOX || MOZCENTRAL || CHROME")
) {
// Return a mock object, to prevent errors such as e.g.
// "TypeError: paintTask.promise is undefined".
return {
promise: Promise.reject(new Error('SVG rendering is not supported.')),
onRenderContinue(cont) { },
cancel() { },
promise: Promise.reject(new Error("SVG rendering is not supported.")),
onRenderContinue(cont) {},
cancel() {},
};
}
@ -614,16 +669,18 @@ class PDFPageView {
let ensureNotCancelled = () => {
if (cancelled) {
throw new RenderingCancelledException(
'Rendering cancelled, page ' + this.id, 'svg');
"Rendering cancelled, page " + this.id,
"svg"
);
}
};
let pdfPage = this.pdfPage;
let actualSizeViewport = this.viewport.clone({ scale: CSS_UNITS, });
let promise = pdfPage.getOperatorList().then((opList) => {
let actualSizeViewport = this.viewport.clone({ scale: CSS_UNITS });
let promise = pdfPage.getOperatorList().then(opList => {
ensureNotCancelled();
let svgGfx = new SVGGraphics(pdfPage.commonObjs, pdfPage.objs);
return svgGfx.getSVG(opList, actualSizeViewport).then((svg) => {
return svgGfx.getSVG(opList, actualSizeViewport).then(svg => {
ensureNotCancelled();
this.svg = svg;
this.paintedViewportMap.set(svg, actualSizeViewport);
@ -650,16 +707,14 @@ class PDFPageView {
* @param {string|null} label
*/
setPageLabel(label) {
this.pageLabel = (typeof label === 'string' ? label : null);
this.pageLabel = typeof label === "string" ? label : null;
if (this.pageLabel !== null) {
this.div.setAttribute('data-page-label', this.pageLabel);
this.div.setAttribute("data-page-label", this.pageLabel);
} else {
this.div.removeAttribute('data-page-label');
this.div.removeAttribute("data-page-label");
}
}
}
export {
PDFPageView,
};
export { PDFPageView };

View file

@ -13,12 +13,12 @@
* limitations under the License.
*/
import { normalizeWheelEventDelta } from './ui_utils';
import { normalizeWheelEventDelta } from "./ui_utils";
const DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500; // in ms
const DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms
const ACTIVE_SELECTOR = 'pdfPresentationMode';
const CONTROLS_SELECTOR = 'pdfPresentationModeControls';
const ACTIVE_SELECTOR = "pdfPresentationMode";
const CONTROLS_SELECTOR = "pdfPresentationModeControls";
const MOUSE_SCROLL_COOLDOWN_TIME = 50; // in ms
const PAGE_SWITCH_THRESHOLD = 0.1;
@ -42,7 +42,7 @@ class PDFPresentationMode {
/**
* @param {PDFPresentationModeOptions} options
*/
constructor({ container, pdfViewer, eventBus, contextMenuItems = null, }) {
constructor({ container, pdfViewer, eventBus, contextMenuItems = null }) {
this.container = container;
this.pdfViewer = pdfViewer;
this.eventBus = eventBus;
@ -55,21 +55,21 @@ class PDFPresentationMode {
this.touchSwipeState = null;
if (contextMenuItems) {
contextMenuItems.contextFirstPage.addEventListener('click', () => {
contextMenuItems.contextFirstPage.addEventListener("click", () => {
this.contextMenuOpen = false;
this.eventBus.dispatch('firstpage', { source: this, });
this.eventBus.dispatch("firstpage", { source: this });
});
contextMenuItems.contextLastPage.addEventListener('click', () => {
contextMenuItems.contextLastPage.addEventListener("click", () => {
this.contextMenuOpen = false;
this.eventBus.dispatch('lastpage', { source: this, });
this.eventBus.dispatch("lastpage", { source: this });
});
contextMenuItems.contextPageRotateCw.addEventListener('click', () => {
contextMenuItems.contextPageRotateCw.addEventListener("click", () => {
this.contextMenuOpen = false;
this.eventBus.dispatch('rotatecw', { source: this, });
this.eventBus.dispatch("rotatecw", { source: this });
});
contextMenuItems.contextPageRotateCcw.addEventListener('click', () => {
contextMenuItems.contextPageRotateCcw.addEventListener("click", () => {
this.contextMenuOpen = false;
this.eventBus.dispatch('rotateccw', { source: this, });
this.eventBus.dispatch("rotateccw", { source: this });
});
}
}
@ -117,17 +117,21 @@ class PDFPresentationMode {
evt.preventDefault();
let delta = normalizeWheelEventDelta(evt);
let currentTime = (new Date()).getTime();
let currentTime = new Date().getTime();
let storedTime = this.mouseScrollTimeStamp;
// If we've already switched page, avoid accidentally switching again.
if (currentTime > storedTime &&
currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
if (
currentTime > storedTime &&
currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME
) {
return;
}
// If the scroll direction changed, reset the accumulated scroll delta.
if ((this.mouseScrollDelta > 0 && delta < 0) ||
(this.mouseScrollDelta < 0 && delta > 0)) {
if (
(this.mouseScrollDelta > 0 && delta < 0) ||
(this.mouseScrollDelta < 0 && delta > 0)
) {
this._resetMouseScrollState();
}
this.mouseScrollDelta += delta;
@ -135,8 +139,8 @@ class PDFPresentationMode {
if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) {
let totalDelta = this.mouseScrollDelta;
this._resetMouseScrollState();
let success = totalDelta > 0 ? this._goToPreviousPage()
: this._goToNextPage();
let success =
totalDelta > 0 ? this._goToPreviousPage() : this._goToNextPage();
if (success) {
this.mouseScrollTimeStamp = currentTime;
}
@ -144,8 +148,12 @@ class PDFPresentationMode {
}
get isFullscreen() {
return !!(document.fullscreenElement || document.mozFullScreen ||
document.webkitIsFullScreen || document.msFullscreenElement);
return !!(
document.fullscreenElement ||
document.mozFullScreen ||
document.webkitIsFullScreen ||
document.msFullscreenElement
);
}
/**
@ -157,7 +165,7 @@ class PDFPresentationMode {
if (page <= 1) {
return false;
}
this.pdfViewer.currentPageNumber = (page - 1);
this.pdfViewer.currentPageNumber = page - 1;
return true;
}
@ -170,7 +178,7 @@ class PDFPresentationMode {
if (page >= this.pdfViewer.pagesCount) {
return false;
}
this.pdfViewer.currentPageNumber = (page + 1);
this.pdfViewer.currentPageNumber = page + 1;
return true;
}
@ -178,7 +186,7 @@ class PDFPresentationMode {
* @private
*/
_notifyStateChange() {
this.eventBus.dispatch('presentationmodechanged', {
this.eventBus.dispatch("presentationmodechanged", {
source: this,
active: this.active,
switchInProgress: !!this.switchInProgress,
@ -228,13 +236,13 @@ class PDFPresentationMode {
// Presentation Mode, by waiting until fullscreen mode in enabled.
setTimeout(() => {
this.pdfViewer.currentPageNumber = this.args.page;
this.pdfViewer.currentScaleValue = 'page-fit';
this.pdfViewer.currentScaleValue = "page-fit";
}, 0);
this._addWindowListeners();
this._showControls();
this.contextMenuOpen = false;
this.container.setAttribute('contextmenu', 'viewerContextMenu');
this.container.setAttribute("contextmenu", "viewerContextMenu");
// Text selection is disabled in Presentation Mode, thus it's not possible
// for the user to deselect text that is selected (e.g. with "Select all")
@ -264,7 +272,7 @@ class PDFPresentationMode {
this._removeWindowListeners();
this._hideControls();
this._resetMouseScrollState();
this.container.removeAttribute('contextmenu');
this.container.removeAttribute("contextmenu");
this.contextMenuOpen = false;
}
@ -280,8 +288,8 @@ class PDFPresentationMode {
if (evt.button === 0) {
// Enable clicking of links in presentation mode. Note: only links
// pointing to destinations in the current PDF document work.
let isInternalLink = (evt.target.href &&
evt.target.classList.contains('internalLink'));
let isInternalLink =
evt.target.href && evt.target.classList.contains("internalLink");
if (!isInternalLink) {
// Unless an internal link was clicked, advance one page.
evt.preventDefault();
@ -353,7 +361,7 @@ class PDFPresentationMode {
}
switch (evt.type) {
case 'touchstart':
case "touchstart":
this.touchSwipeState = {
startX: evt.touches[0].pageX,
startY: evt.touches[0].pageY,
@ -361,7 +369,7 @@ class PDFPresentationMode {
endY: evt.touches[0].pageY,
};
break;
case 'touchmove':
case "touchmove":
if (this.touchSwipeState === null) {
return;
}
@ -371,7 +379,7 @@ class PDFPresentationMode {
// particular has some sort of swipe gesture in fullscreen mode).
evt.preventDefault();
break;
case 'touchend':
case "touchend":
if (this.touchSwipeState === null) {
return;
}
@ -379,13 +387,17 @@ class PDFPresentationMode {
let dx = this.touchSwipeState.endX - this.touchSwipeState.startX;
let dy = this.touchSwipeState.endY - this.touchSwipeState.startY;
let absAngle = Math.abs(Math.atan2(dy, dx));
if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD &&
(absAngle <= SWIPE_ANGLE_THRESHOLD ||
absAngle >= (Math.PI - SWIPE_ANGLE_THRESHOLD))) {
if (
Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD &&
(absAngle <= SWIPE_ANGLE_THRESHOLD ||
absAngle >= Math.PI - SWIPE_ANGLE_THRESHOLD)
) {
// Horizontal swipe.
delta = dx;
} else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD &&
Math.abs(absAngle - (Math.PI / 2)) <= SWIPE_ANGLE_THRESHOLD) {
} else if (
Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD &&
Math.abs(absAngle - Math.PI / 2) <= SWIPE_ANGLE_THRESHOLD
) {
// Vertical swipe.
delta = dy;
}
@ -409,28 +421,28 @@ class PDFPresentationMode {
this.contextMenuBind = this._contextMenu.bind(this);
this.touchSwipeBind = this._touchSwipe.bind(this);
window.addEventListener('mousemove', this.showControlsBind);
window.addEventListener('mousedown', this.mouseDownBind);
window.addEventListener('wheel', this.mouseWheelBind);
window.addEventListener('keydown', this.resetMouseScrollStateBind);
window.addEventListener('contextmenu', this.contextMenuBind);
window.addEventListener('touchstart', this.touchSwipeBind);
window.addEventListener('touchmove', this.touchSwipeBind);
window.addEventListener('touchend', this.touchSwipeBind);
window.addEventListener("mousemove", this.showControlsBind);
window.addEventListener("mousedown", this.mouseDownBind);
window.addEventListener("wheel", this.mouseWheelBind);
window.addEventListener("keydown", this.resetMouseScrollStateBind);
window.addEventListener("contextmenu", this.contextMenuBind);
window.addEventListener("touchstart", this.touchSwipeBind);
window.addEventListener("touchmove", this.touchSwipeBind);
window.addEventListener("touchend", this.touchSwipeBind);
}
/**
* @private
*/
_removeWindowListeners() {
window.removeEventListener('mousemove', this.showControlsBind);
window.removeEventListener('mousedown', this.mouseDownBind);
window.removeEventListener('wheel', this.mouseWheelBind);
window.removeEventListener('keydown', this.resetMouseScrollStateBind);
window.removeEventListener('contextmenu', this.contextMenuBind);
window.removeEventListener('touchstart', this.touchSwipeBind);
window.removeEventListener('touchmove', this.touchSwipeBind);
window.removeEventListener('touchend', this.touchSwipeBind);
window.removeEventListener("mousemove", this.showControlsBind);
window.removeEventListener("mousedown", this.mouseDownBind);
window.removeEventListener("wheel", this.mouseWheelBind);
window.removeEventListener("keydown", this.resetMouseScrollStateBind);
window.removeEventListener("contextmenu", this.contextMenuBind);
window.removeEventListener("touchstart", this.touchSwipeBind);
window.removeEventListener("touchmove", this.touchSwipeBind);
window.removeEventListener("touchend", this.touchSwipeBind);
delete this.showControlsBind;
delete this.mouseDownBind;
@ -457,14 +469,17 @@ class PDFPresentationMode {
_addFullscreenChangeListeners() {
this.fullscreenChangeBind = this._fullscreenChange.bind(this);
window.addEventListener('fullscreenchange', this.fullscreenChangeBind);
window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind);
if (typeof PDFJSDev === 'undefined' ||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
window.addEventListener('webkitfullscreenchange',
this.fullscreenChangeBind);
window.addEventListener('MSFullscreenChange',
this.fullscreenChangeBind);
window.addEventListener("fullscreenchange", this.fullscreenChangeBind);
window.addEventListener("mozfullscreenchange", this.fullscreenChangeBind);
if (
typeof PDFJSDev === "undefined" ||
!PDFJSDev.test("FIREFOX || MOZCENTRAL")
) {
window.addEventListener(
"webkitfullscreenchange",
this.fullscreenChangeBind
);
window.addEventListener("MSFullscreenChange", this.fullscreenChangeBind);
}
}
@ -472,21 +487,27 @@ class PDFPresentationMode {
* @private
*/
_removeFullscreenChangeListeners() {
window.removeEventListener('fullscreenchange', this.fullscreenChangeBind);
window.removeEventListener('mozfullscreenchange',
this.fullscreenChangeBind);
if (typeof PDFJSDev === 'undefined' ||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
window.removeEventListener('webkitfullscreenchange',
this.fullscreenChangeBind);
window.removeEventListener('MSFullscreenChange',
this.fullscreenChangeBind);
window.removeEventListener("fullscreenchange", this.fullscreenChangeBind);
window.removeEventListener(
"mozfullscreenchange",
this.fullscreenChangeBind
);
if (
typeof PDFJSDev === "undefined" ||
!PDFJSDev.test("FIREFOX || MOZCENTRAL")
) {
window.removeEventListener(
"webkitfullscreenchange",
this.fullscreenChangeBind
);
window.removeEventListener(
"MSFullscreenChange",
this.fullscreenChangeBind
);
}
delete this.fullscreenChangeBind;
}
}
export {
PDFPresentationMode,
};
export { PDFPresentationMode };

View file

@ -13,9 +13,9 @@
* limitations under the License.
*/
import { CSS_UNITS, NullL10n } from './ui_utils';
import { PDFPrintServiceFactory, PDFViewerApplication } from './app';
import { AppOptions } from './app_options';
import { CSS_UNITS, NullL10n } from "./ui_utils";
import { PDFPrintServiceFactory, PDFViewerApplication } from "./app";
import { AppOptions } from "./app_options";
let activeService = null;
let overlayManager = null;
@ -26,35 +26,38 @@ function renderPage(activeServiceOnEntry, pdfDocument, pageNumber, size) {
let scratchCanvas = activeService.scratchCanvas;
// The size of the canvas in pixels for printing.
const PRINT_RESOLUTION = AppOptions.get('printResolution') || 150;
const PRINT_RESOLUTION = AppOptions.get("printResolution") || 150;
const PRINT_UNITS = PRINT_RESOLUTION / 72.0;
scratchCanvas.width = Math.floor(size.width * PRINT_UNITS);
scratchCanvas.height = Math.floor(size.height * PRINT_UNITS);
// The physical size of the img as specified by the PDF document.
let width = Math.floor(size.width * CSS_UNITS) + 'px';
let height = Math.floor(size.height * CSS_UNITS) + 'px';
let width = Math.floor(size.width * CSS_UNITS) + "px";
let height = Math.floor(size.height * CSS_UNITS) + "px";
let ctx = scratchCanvas.getContext('2d');
let ctx = scratchCanvas.getContext("2d");
ctx.save();
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillRect(0, 0, scratchCanvas.width, scratchCanvas.height);
ctx.restore();
return pdfDocument.getPage(pageNumber).then(function(pdfPage) {
let renderContext = {
canvasContext: ctx,
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
viewport: pdfPage.getViewport({ scale: 1, rotation: size.rotation, }),
intent: 'print',
};
return pdfPage.render(renderContext).promise;
}).then(function() {
return {
width,
height,
};
});
return pdfDocument
.getPage(pageNumber)
.then(function(pdfPage) {
let renderContext = {
canvasContext: ctx,
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
viewport: pdfPage.getViewport({ scale: 1, rotation: size.rotation }),
intent: "print",
};
return pdfPage.render(renderContext).promise;
})
.then(function() {
return {
width,
height,
};
});
}
function PDFPrintService(pdfDocument, pagesOverview, printContainer, l10n) {
@ -63,26 +66,30 @@ function PDFPrintService(pdfDocument, pagesOverview, printContainer, l10n) {
this.printContainer = printContainer;
this.l10n = l10n || NullL10n;
this.disableCreateObjectURL =
pdfDocument.loadingParams['disableCreateObjectURL'];
pdfDocument.loadingParams["disableCreateObjectURL"];
this.currentPage = -1;
// The temporary canvas where renderPage paints one page at a time.
this.scratchCanvas = document.createElement('canvas');
this.scratchCanvas = document.createElement("canvas");
}
PDFPrintService.prototype = {
layout() {
this.throwIfInactive();
const body = document.querySelector('body');
body.setAttribute('data-pdfjsprinting', true);
const body = document.querySelector("body");
body.setAttribute("data-pdfjsprinting", true);
let hasEqualPageSizes = this.pagesOverview.every(function(size) {
return size.width === this.pagesOverview[0].width &&
size.height === this.pagesOverview[0].height;
return (
size.width === this.pagesOverview[0].width &&
size.height === this.pagesOverview[0].height
);
}, this);
if (!hasEqualPageSizes) {
console.warn('Not all pages have the same size. The printed ' +
'result may be incorrect!');
console.warn(
"Not all pages have the same size. The printed " +
"result may be incorrect!"
);
}
// Insert a @page + size rule to make sure that the page size is correctly
@ -94,14 +101,18 @@ PDFPrintService.prototype = {
// In browsers where @page + size is not supported (such as Firefox,
// https://bugzil.la/851441), the next stylesheet will be ignored and the
// user has to select the correct paper size in the UI if wanted.
this.pageStyleSheet = document.createElement('style');
this.pageStyleSheet = document.createElement("style");
let pageSize = this.pagesOverview[0];
this.pageStyleSheet.textContent =
// "size:<width> <height>" is what we need. But also add "A4" because
// Firefox incorrectly reports support for the other value.
'@supports ((size:A4) and (size:1pt 1pt)) {' +
'@page { size: ' + pageSize.width + 'pt ' + pageSize.height + 'pt;}' +
'}';
"@supports ((size:A4) and (size:1pt 1pt)) {" +
"@page { size: " +
pageSize.width +
"pt " +
pageSize.height +
"pt;}" +
"}";
body.appendChild(this.pageStyleSheet);
},
@ -112,10 +123,10 @@ PDFPrintService.prototype = {
// us.
return;
}
this.printContainer.textContent = '';
this.printContainer.textContent = "";
const body = document.querySelector('body');
body.removeAttribute('data-pdfjsprinting');
const body = document.querySelector("body");
body.removeAttribute("data-pdfjsprinting");
if (this.pageStyleSheet) {
this.pageStyleSheet.remove();
@ -125,10 +136,10 @@ PDFPrintService.prototype = {
this.scratchCanvas = null;
activeService = null;
ensureOverlay().then(function() {
if (overlayManager.active !== 'printServiceOverlay') {
if (overlayManager.active !== "printServiceOverlay") {
return; // overlay was already closed
}
overlayManager.close('printServiceOverlay');
overlayManager.close("printServiceOverlay");
});
},
@ -154,12 +165,12 @@ PDFPrintService.prototype = {
useRenderedPage(printItem) {
this.throwIfInactive();
let img = document.createElement('img');
let img = document.createElement("img");
img.style.width = printItem.width;
img.style.height = printItem.height;
let scratchCanvas = this.scratchCanvas;
if (('toBlob' in scratchCanvas) && !this.disableCreateObjectURL) {
if ("toBlob" in scratchCanvas && !this.disableCreateObjectURL) {
scratchCanvas.toBlob(function(blob) {
img.src = URL.createObjectURL(blob);
});
@ -167,7 +178,7 @@ PDFPrintService.prototype = {
img.src = scratchCanvas.toDataURL();
}
let wrapper = document.createElement('div');
let wrapper = document.createElement("div");
wrapper.appendChild(img);
this.printContainer.appendChild(wrapper);
@ -179,7 +190,7 @@ PDFPrintService.prototype = {
performPrint() {
this.throwIfInactive();
return new Promise((resolve) => {
return new Promise(resolve => {
// Push window.print in the macrotask queue to avoid being affected by
// the deprecation of running print() code in a microtask, see
// https://github.com/mozilla/pdf.js/issues/7547.
@ -190,7 +201,7 @@ PDFPrintService.prototype = {
}
print.call(window);
// Delay promise resolution in case print() was not synchronous.
setTimeout(resolve, 20); // Tidy-up.
setTimeout(resolve, 20); // Tidy-up.
}, 0);
});
},
@ -201,7 +212,7 @@ PDFPrintService.prototype = {
throwIfInactive() {
if (!this.active) {
throw new Error('This print request was cancelled or completed.');
throw new Error("This print request was cancelled or completed.");
}
},
};
@ -209,97 +220,108 @@ PDFPrintService.prototype = {
let print = window.print;
window.print = function print() {
if (activeService) {
console.warn('Ignored window.print() because of a pending print job.');
console.warn("Ignored window.print() because of a pending print job.");
return;
}
ensureOverlay().then(function() {
if (activeService) {
overlayManager.open('printServiceOverlay');
overlayManager.open("printServiceOverlay");
}
});
try {
dispatchEvent('beforeprint');
dispatchEvent("beforeprint");
} finally {
if (!activeService) {
console.error('Expected print service to be initialized.');
console.error("Expected print service to be initialized.");
ensureOverlay().then(function() {
if (overlayManager.active === 'printServiceOverlay') {
overlayManager.close('printServiceOverlay');
if (overlayManager.active === "printServiceOverlay") {
overlayManager.close("printServiceOverlay");
}
});
return; // eslint-disable-line no-unsafe-finally
}
let activeServiceOnEntry = activeService;
activeService.renderPages().then(function() {
return activeServiceOnEntry.performPrint();
}).catch(function() {
// Ignore any error messages.
}).then(function() {
// aborts acts on the "active" print request, so we need to check
// whether the print request (activeServiceOnEntry) is still active.
// Without the check, an unrelated print request (created after aborting
// this print request while the pages were being generated) would be
// aborted.
if (activeServiceOnEntry.active) {
abort();
}
});
activeService
.renderPages()
.then(function() {
return activeServiceOnEntry.performPrint();
})
.catch(function() {
// Ignore any error messages.
})
.then(function() {
// aborts acts on the "active" print request, so we need to check
// whether the print request (activeServiceOnEntry) is still active.
// Without the check, an unrelated print request (created after aborting
// this print request while the pages were being generated) would be
// aborted.
if (activeServiceOnEntry.active) {
abort();
}
});
}
};
function dispatchEvent(eventType) {
let event = document.createEvent('CustomEvent');
event.initCustomEvent(eventType, false, false, 'custom');
let event = document.createEvent("CustomEvent");
event.initCustomEvent(eventType, false, false, "custom");
window.dispatchEvent(event);
}
function abort() {
if (activeService) {
activeService.destroy();
dispatchEvent('afterprint');
dispatchEvent("afterprint");
}
}
function renderProgress(index, total, l10n) {
let progressContainer = document.getElementById('printServiceOverlay');
let progress = Math.round(100 * index / total);
let progressBar = progressContainer.querySelector('progress');
let progressPerc = progressContainer.querySelector('.relative-progress');
let progressContainer = document.getElementById("printServiceOverlay");
let progress = Math.round((100 * index) / total);
let progressBar = progressContainer.querySelector("progress");
let progressPerc = progressContainer.querySelector(".relative-progress");
progressBar.value = progress;
l10n.get('print_progress_percent', { progress, }, progress + '%').
then((msg) => {
l10n.get("print_progress_percent", { progress }, progress + "%").then(msg => {
progressPerc.textContent = msg;
});
}
window.addEventListener('keydown', function(event) {
// Intercept Cmd/Ctrl + P in all browsers.
// Also intercept Cmd/Ctrl + Shift + P in Chrome and Opera
if (event.keyCode === /* P= */ 80 && (event.ctrlKey || event.metaKey) &&
!event.altKey && (!event.shiftKey || window.chrome || window.opera)) {
window.print();
window.addEventListener(
"keydown",
function(event) {
// Intercept Cmd/Ctrl + P in all browsers.
// Also intercept Cmd/Ctrl + Shift + P in Chrome and Opera
if (
event.keyCode === /* P= */ 80 &&
(event.ctrlKey || event.metaKey) &&
!event.altKey &&
(!event.shiftKey || window.chrome || window.opera)
) {
window.print();
// The (browser) print dialog cannot be prevented from being shown in IE11.
event.preventDefault();
if (event.stopImmediatePropagation) {
event.stopImmediatePropagation();
} else {
event.stopPropagation();
// The (browser) print dialog cannot be prevented from being shown in IE11.
event.preventDefault();
if (event.stopImmediatePropagation) {
event.stopImmediatePropagation();
} else {
event.stopPropagation();
}
}
}
}, true);
},
true
);
if ('onbeforeprint' in window) {
if ("onbeforeprint" in window) {
// Do not propagate before/afterprint events when they are not triggered
// from within this polyfill. (FF /IE / Chrome 63+).
let stopPropagationIfNeeded = function(event) {
if (event.detail !== 'custom' && event.stopImmediatePropagation) {
if (event.detail !== "custom" && event.stopImmediatePropagation) {
event.stopImmediatePropagation();
}
};
window.addEventListener('beforeprint', stopPropagationIfNeeded);
window.addEventListener('afterprint', stopPropagationIfNeeded);
window.addEventListener("beforeprint", stopPropagationIfNeeded);
window.addEventListener("afterprint", stopPropagationIfNeeded);
}
let overlayPromise;
@ -307,12 +329,16 @@ function ensureOverlay() {
if (!overlayPromise) {
overlayManager = PDFViewerApplication.overlayManager;
if (!overlayManager) {
throw new Error('The overlay manager has not yet been initialized.');
throw new Error("The overlay manager has not yet been initialized.");
}
overlayPromise = overlayManager.register('printServiceOverlay',
document.getElementById('printServiceOverlay'), abort, true);
document.getElementById('printCancel').onclick = abort;
overlayPromise = overlayManager.register(
"printServiceOverlay",
document.getElementById("printServiceOverlay"),
abort,
true
);
document.getElementById("printCancel").onclick = abort;
}
return overlayPromise;
}
@ -322,14 +348,16 @@ PDFPrintServiceFactory.instance = {
createPrintService(pdfDocument, pagesOverview, printContainer, l10n) {
if (activeService) {
throw new Error('The print service is created and active.');
throw new Error("The print service is created and active.");
}
activeService = new PDFPrintService(pdfDocument, pagesOverview,
printContainer, l10n);
activeService = new PDFPrintService(
pdfDocument,
pagesOverview,
printContainer,
l10n
);
return activeService;
},
};
export {
PDFPrintService,
};
export { PDFPrintService };

View file

@ -125,8 +125,10 @@ class PDFRenderingQueue {
}
} else {
let previousPageIndex = visible.first.id - 2;
if (views[previousPageIndex] &&
!this.isViewFinished(views[previousPageIndex])) {
if (
views[previousPageIndex] &&
!this.isViewFinished(views[previousPageIndex])
) {
return views[previousPageIndex];
}
}
@ -171,7 +173,4 @@ class PDFRenderingQueue {
}
}
export {
RenderingStates,
PDFRenderingQueue,
};
export { RenderingStates, PDFRenderingQueue };

View file

@ -13,10 +13,10 @@
* limitations under the License.
*/
import { NullL10n } from './ui_utils';
import { RenderingStates } from './pdf_rendering_queue';
import { NullL10n } from "./ui_utils";
import { RenderingStates } from "./pdf_rendering_queue";
const UI_NOTIFICATION_CLASS = 'pdfSidebarNotification';
const UI_NOTIFICATION_CLASS = "pdfSidebarNotification";
const SidebarView = {
UNKNOWN: -1,
@ -64,8 +64,14 @@ class PDFSidebar {
/**
* @param {PDFSidebarOptions} options
*/
constructor({ elements, pdfViewer, pdfThumbnailViewer, eventBus,
l10n = NullL10n, disableNotification = false, }) {
constructor({
elements,
pdfViewer,
pdfThumbnailViewer,
eventBus,
l10n = NullL10n,
disableNotification = false,
}) {
this.isOpen = false;
this.active = SidebarView.THUMBS;
this.isInitialViewSet = false;
@ -112,19 +118,19 @@ class PDFSidebar {
* @type {number} One of the values in {SidebarView}.
*/
get visibleView() {
return (this.isOpen ? this.active : SidebarView.NONE);
return this.isOpen ? this.active : SidebarView.NONE;
}
get isThumbnailViewVisible() {
return (this.isOpen && this.active === SidebarView.THUMBS);
return this.isOpen && this.active === SidebarView.THUMBS;
}
get isOutlineViewVisible() {
return (this.isOpen && this.active === SidebarView.OUTLINE);
return this.isOpen && this.active === SidebarView.OUTLINE;
}
get isAttachmentsViewVisible() {
return (this.isOpen && this.active === SidebarView.ATTACHMENTS);
return this.isOpen && this.active === SidebarView.ATTACHMENTS;
}
/**
@ -165,7 +171,7 @@ class PDFSidebar {
* @private
*/
_switchView(view, forceOpen = false) {
const isViewChanged = (view !== this.active);
const isViewChanged = view !== this.active;
let shouldForceRendering = false;
switch (view) {
@ -199,17 +205,25 @@ class PDFSidebar {
this.active = view;
// Update the CSS classes, for all buttons...
this.thumbnailButton.classList.toggle('toggled',
view === SidebarView.THUMBS);
this.outlineButton.classList.toggle('toggled',
view === SidebarView.OUTLINE);
this.attachmentsButton.classList.toggle('toggled',
view === SidebarView.ATTACHMENTS);
this.thumbnailButton.classList.toggle(
"toggled",
view === SidebarView.THUMBS
);
this.outlineButton.classList.toggle(
"toggled",
view === SidebarView.OUTLINE
);
this.attachmentsButton.classList.toggle(
"toggled",
view === SidebarView.ATTACHMENTS
);
// ... and for all views.
this.thumbnailView.classList.toggle('hidden', view !== SidebarView.THUMBS);
this.outlineView.classList.toggle('hidden', view !== SidebarView.OUTLINE);
this.attachmentsView.classList.toggle('hidden',
view !== SidebarView.ATTACHMENTS);
this.thumbnailView.classList.toggle("hidden", view !== SidebarView.THUMBS);
this.outlineView.classList.toggle("hidden", view !== SidebarView.OUTLINE);
this.attachmentsView.classList.toggle(
"hidden",
view !== SidebarView.ATTACHMENTS
);
if (forceOpen && !this.isOpen) {
this.open();
@ -231,9 +245,9 @@ class PDFSidebar {
return;
}
this.isOpen = true;
this.toggleButton.classList.add('toggled');
this.toggleButton.classList.add("toggled");
this.outerContainer.classList.add('sidebarMoving', 'sidebarOpen');
this.outerContainer.classList.add("sidebarMoving", "sidebarOpen");
if (this.active === SidebarView.THUMBS) {
this._updateThumbnailViewer();
@ -249,10 +263,10 @@ class PDFSidebar {
return;
}
this.isOpen = false;
this.toggleButton.classList.remove('toggled');
this.toggleButton.classList.remove("toggled");
this.outerContainer.classList.add('sidebarMoving');
this.outerContainer.classList.remove('sidebarOpen');
this.outerContainer.classList.add("sidebarMoving");
this.outerContainer.classList.remove("sidebarOpen");
this._forceRendering();
this._dispatchEvent();
@ -270,7 +284,7 @@ class PDFSidebar {
* @private
*/
_dispatchEvent() {
this.eventBus.dispatch('sidebarviewchanged', {
this.eventBus.dispatch("sidebarviewchanged", {
source: this,
view: this.visibleView,
});
@ -282,7 +296,8 @@ class PDFSidebar {
_forceRendering() {
if (this.onToggled) {
this.onToggled();
} else { // Fallback
} else {
// Fallback
this.pdfViewer.forceRendering();
this.pdfThumbnailViewer.forceRendering();
}
@ -292,7 +307,7 @@ class PDFSidebar {
* @private
*/
_updateThumbnailViewer() {
let { pdfViewer, pdfThumbnailViewer, } = this;
let { pdfViewer, pdfThumbnailViewer } = this;
// Use the rendered pages to set the corresponding thumbnail images.
let pagesCount = pdfViewer.pagesCount;
@ -314,11 +329,15 @@ class PDFSidebar {
return;
}
this.l10n.get('toggle_sidebar_notification.title', null,
'Toggle Sidebar (document contains outline/attachments)').
then((msg) => {
this.toggleButton.title = msg;
});
this.l10n
.get(
"toggle_sidebar_notification.title",
null,
"Toggle Sidebar (document contains outline/attachments)"
)
.then(msg => {
this.toggleButton.title = msg;
});
if (!this.isOpen) {
// Only show the notification on the `toggleButton` if the sidebar is
@ -348,7 +367,7 @@ class PDFSidebar {
return;
}
let removeNotification = (view) => {
let removeNotification = view => {
switch (view) {
case SidebarView.OUTLINE:
this.outlineButton.classList.remove(UI_NOTIFICATION_CLASS);
@ -370,12 +389,12 @@ class PDFSidebar {
removeNotification(view);
return;
}
for (view in SidebarView) { // Remove all sidebar notifications on reset.
for (view in SidebarView) {
// Remove all sidebar notifications on reset.
removeNotification(SidebarView[view]);
}
this.l10n.get('toggle_sidebar.title', null, 'Toggle Sidebar').
then((msg) => {
this.l10n.get("toggle_sidebar.title", null, "Toggle Sidebar").then(msg => {
this.toggleButton.title = msg;
});
}
@ -384,34 +403,34 @@ class PDFSidebar {
* @private
*/
_addEventListeners() {
this.viewerContainer.addEventListener('transitionend', (evt) => {
this.viewerContainer.addEventListener("transitionend", evt => {
if (evt.target === this.viewerContainer) {
this.outerContainer.classList.remove('sidebarMoving');
this.outerContainer.classList.remove("sidebarMoving");
}
});
this.toggleButton.addEventListener('click', () => {
this.toggleButton.addEventListener("click", () => {
this.toggle();
});
// Buttons for switching views.
this.thumbnailButton.addEventListener('click', () => {
this.thumbnailButton.addEventListener("click", () => {
this.switchView(SidebarView.THUMBS);
});
this.outlineButton.addEventListener('click', () => {
this.outlineButton.addEventListener("click", () => {
this.switchView(SidebarView.OUTLINE);
});
this.outlineButton.addEventListener('dblclick', () => {
this.eventBus.dispatch('toggleoutlinetree', { source: this, });
this.outlineButton.addEventListener("dblclick", () => {
this.eventBus.dispatch("toggleoutlinetree", { source: this });
});
this.attachmentsButton.addEventListener('click', () => {
this.attachmentsButton.addEventListener("click", () => {
this.switchView(SidebarView.ATTACHMENTS);
});
// Disable/enable views.
this.eventBus.on('outlineloaded', (evt) => {
this.eventBus.on("outlineloaded", evt => {
let outlineCount = evt.outlineCount;
this.outlineButton.disabled = !outlineCount;
@ -425,7 +444,7 @@ class PDFSidebar {
}
});
this.eventBus.on('attachmentsloaded', (evt) => {
this.eventBus.on("attachmentsloaded", evt => {
if (evt.attachmentsCount) {
this.attachmentsButton.disabled = false;
@ -454,7 +473,7 @@ class PDFSidebar {
});
// Update the thumbnailViewer, if visible, when exiting presentation mode.
this.eventBus.on('presentationmodechanged', (evt) => {
this.eventBus.on("presentationmodechanged", evt => {
if (!evt.active && !evt.switchInProgress && this.isThumbnailViewVisible) {
this._updateThumbnailViewer();
}
@ -462,7 +481,4 @@ class PDFSidebar {
}
}
export {
SidebarView,
PDFSidebar,
};
export { SidebarView, PDFSidebar };

View file

@ -13,11 +13,11 @@
* limitations under the License.
*/
import { clamp, NullL10n } from './ui_utils';
import { clamp, NullL10n } from "./ui_utils";
const SIDEBAR_WIDTH_VAR = '--sidebar-width';
const SIDEBAR_WIDTH_VAR = "--sidebar-width";
const SIDEBAR_MIN_WIDTH = 200; // pixels
const SIDEBAR_RESIZING_CLASS = 'sidebarResizing';
const SIDEBAR_RESIZING_CLASS = "sidebarResizing";
/**
* @typedef {Object} PDFSidebarResizerOptions
@ -47,19 +47,23 @@ class PDFSidebarResizer {
this.eventBus = eventBus;
this.l10n = l10n;
if ((typeof PDFJSDev === 'undefined' || !PDFJSDev.test('MOZCENTRAL')) &&
(typeof CSS === 'undefined' || typeof CSS.supports !== 'function' ||
!CSS.supports(SIDEBAR_WIDTH_VAR,
`calc(-1 * ${SIDEBAR_MIN_WIDTH}px)`))) {
console.warn('PDFSidebarResizer: ' +
'The browser does not support resizing of the sidebar.');
if (
(typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) &&
(typeof CSS === "undefined" ||
typeof CSS.supports !== "function" ||
!CSS.supports(SIDEBAR_WIDTH_VAR, `calc(-1 * ${SIDEBAR_MIN_WIDTH}px)`))
) {
console.warn(
"PDFSidebarResizer: " +
"The browser does not support resizing of the sidebar."
);
return;
}
this.enabled = true;
this.resizer.classList.remove('hidden'); // Show the resizer DOM element.
this.resizer.classList.remove("hidden"); // Show the resizer DOM element.
this.l10n.getDirection().then((dir) => {
this.isRTL = (dir === 'rtl');
this.l10n.getDirection().then(dir => {
this.isRTL = dir === "rtl";
});
this._addEventListeners();
}
@ -84,8 +88,11 @@ class PDFSidebarResizer {
}
// Prevent the sidebar from becoming too narrow, or from occupying more
// than half of the available viewer width.
const newWidth = clamp(width, SIDEBAR_MIN_WIDTH,
Math.floor(this.outerContainerWidth / 2));
const newWidth = clamp(
width,
SIDEBAR_MIN_WIDTH,
Math.floor(this.outerContainerWidth / 2)
);
// Only update the UI when the sidebar width did in fact change.
if (newWidth === this._width) {
return false;
@ -114,11 +121,11 @@ class PDFSidebarResizer {
// Re-enable the `transition-duration` rules when sidebar resizing ends...
this.outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS);
// ... and ensure that rendering will always be triggered.
this.eventBus.dispatch('resize', { source: this, });
this.eventBus.dispatch("resize", { source: this });
let _boundEvents = this._boundEvents;
window.removeEventListener('mousemove', _boundEvents.mouseMove);
window.removeEventListener('mouseup', _boundEvents.mouseUp);
window.removeEventListener("mousemove", _boundEvents.mouseMove);
window.removeEventListener("mouseup", _boundEvents.mouseUp);
}
/**
@ -132,7 +139,7 @@ class PDFSidebarResizer {
_boundEvents.mouseMove = this._mouseMove.bind(this);
_boundEvents.mouseUp = this._mouseUp.bind(this);
this.resizer.addEventListener('mousedown', (evt) => {
this.resizer.addEventListener("mousedown", evt => {
if (evt.button !== 0) {
return;
}
@ -140,15 +147,15 @@ class PDFSidebarResizer {
// in order to improve responsiveness and to avoid visual glitches.
this.outerContainer.classList.add(SIDEBAR_RESIZING_CLASS);
window.addEventListener('mousemove', _boundEvents.mouseMove);
window.addEventListener('mouseup', _boundEvents.mouseUp);
window.addEventListener("mousemove", _boundEvents.mouseMove);
window.addEventListener("mouseup", _boundEvents.mouseUp);
});
this.eventBus.on('sidebarviewchanged', (evt) => {
this.eventBus.on("sidebarviewchanged", evt => {
this.sidebarOpen = !!(evt && evt.view);
});
this.eventBus.on('resize', (evt) => {
this.eventBus.on("resize", evt => {
// When the *entire* viewer is resized, such that it becomes narrower,
// ensure that the sidebar doesn't end up being too wide.
if (!evt || evt.source !== window) {
@ -175,13 +182,11 @@ class PDFSidebarResizer {
// Trigger rendering if the sidebar width changed, to avoid
// depending on the order in which 'resize' events are handled.
if (updated) {
this.eventBus.dispatch('resize', { source: this, });
this.eventBus.dispatch("resize", { source: this });
}
});
});
}
}
export {
PDFSidebarResizer,
};
export { PDFSidebarResizer };

View file

@ -13,14 +13,14 @@
* limitations under the License.
*/
import { BaseViewer } from './base_viewer';
import { shadow } from 'pdfjs-lib';
import { BaseViewer } from "./base_viewer";
import { shadow } from "pdfjs-lib";
class PDFSinglePageViewer extends BaseViewer {
constructor(options) {
super(options);
this.eventBus.on('pagesinit', (evt) => {
this.eventBus.on("pagesinit", evt => {
// Since the pages are placed in a `DocumentFragment`, make sure that
// the current page becomes visible upon loading of the document.
this._ensurePageViewVisible();
@ -32,7 +32,7 @@ class PDFSinglePageViewer extends BaseViewer {
// `PDFSinglePageViewer`, we cannot append them to the `viewer` DOM element.
// Instead, they are placed in a `DocumentFragment`, and only the current
// page is displayed in the viewer (refer to `this._ensurePageViewVisible`).
return shadow(this, '_setDocumentViewerElement', this._shadowViewer);
return shadow(this, "_setDocumentViewerElement", this._shadowViewer);
}
_resetView() {
@ -54,7 +54,8 @@ class PDFSinglePageViewer extends BaseViewer {
case 1: // The normal page-switching case.
if (viewerNodes[0] !== previousPageView.div) {
throw new Error(
'_ensurePageViewVisible: Unexpected previously visible page.');
"_ensurePageViewVisible: Unexpected previously visible page."
);
}
if (pageView === previousPageView) {
break; // The correct page is already visible.
@ -67,7 +68,8 @@ class PDFSinglePageViewer extends BaseViewer {
break;
default:
throw new Error(
'_ensurePageViewVisible: Only one page should be visible at a time.');
"_ensurePageViewVisible: Only one page should be visible at a time."
);
}
this._previousPageNumber = this._currentPageNumber;
}
@ -79,8 +81,9 @@ class PDFSinglePageViewer extends BaseViewer {
super._scrollUpdate();
}
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null, }) {
if (pageNumber) { // Ensure that `this._currentPageNumber` is correct.
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null }) {
if (pageNumber) {
// Ensure that `this._currentPageNumber` is correct.
this._setCurrentPageNumber(pageNumber);
}
const scrolledDown = this._currentPageNumber >= this._previousPageNumber;
@ -90,7 +93,7 @@ class PDFSinglePageViewer extends BaseViewer {
// even if the current position doesn't change when the page is scrolled.
this.update();
super._scrollIntoView({ pageDiv, pageSpot, pageNumber, });
super._scrollIntoView({ pageDiv, pageSpot, pageNumber });
// Since scrolling is tracked using `requestAnimationFrame`, update the
// scroll direction during the next `this._scrollUpdate` invocation.
@ -104,18 +107,16 @@ class PDFSinglePageViewer extends BaseViewer {
return this._getCurrentVisiblePage();
}
_updateHelper(visiblePages) { }
_updateHelper(visiblePages) {}
get _isScrollModeHorizontal() {
// The Scroll/Spread modes are never used in `PDFSinglePageViewer`.
return shadow(this, '_isScrollModeHorizontal', false);
return shadow(this, "_isScrollModeHorizontal", false);
}
_updateScrollMode() { }
_updateScrollMode() {}
_updateSpreadMode() { }
_updateSpreadMode() {}
}
export {
PDFSinglePageViewer,
};
export { PDFSinglePageViewer };

View file

@ -14,10 +14,11 @@
*/
import {
createPromiseCapability, RenderingCancelledException
} from 'pdfjs-lib';
import { getOutputScale, NullL10n } from './ui_utils';
import { RenderingStates } from './pdf_rendering_queue';
createPromiseCapability,
RenderingCancelledException,
} from "pdfjs-lib";
import { getOutputScale, NullL10n } from "./ui_utils";
import { RenderingStates } from "./pdf_rendering_queue";
const MAX_NUM_SCALING_STEPS = 3;
const THUMBNAIL_CANVAS_BORDER_WIDTH = 1; // px
@ -43,7 +44,7 @@ const TempImageFactory = (function TempImageFactoryClosure() {
getCanvas(width, height) {
let tempCanvas = tempCanvasCache;
if (!tempCanvas) {
tempCanvas = document.createElement('canvas');
tempCanvas = document.createElement("canvas");
tempCanvasCache = tempCanvas;
}
tempCanvas.width = width;
@ -51,14 +52,16 @@ const TempImageFactory = (function TempImageFactoryClosure() {
// Since this is a temporary canvas, we need to fill it with a white
// background ourselves. `_getPageDrawContext` uses CSS rules for this.
if (typeof PDFJSDev === 'undefined' ||
PDFJSDev.test('MOZCENTRAL || FIREFOX || GENERIC')) {
if (
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("MOZCENTRAL || FIREFOX || GENERIC")
) {
tempCanvas.mozOpaque = true;
}
let ctx = tempCanvas.getContext('2d', { alpha: false, });
let ctx = tempCanvas.getContext("2d", { alpha: false });
ctx.save();
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillRect(0, 0, width, height);
ctx.restore();
return tempCanvas;
@ -84,10 +87,17 @@ class PDFThumbnailView {
/**
* @param {PDFThumbnailViewOptions} options
*/
constructor({ container, id, defaultViewport, linkService, renderingQueue,
disableCanvasToImageConversion = false, l10n = NullL10n, }) {
constructor({
container,
id,
defaultViewport,
linkService,
renderingQueue,
disableCanvasToImageConversion = false,
l10n = NullL10n,
}) {
this.id = id;
this.renderingId = 'thumbnail' + id;
this.renderingId = "thumbnail" + id;
this.pageLabel = null;
this.pdfPage = null;
@ -113,28 +123,29 @@ class PDFThumbnailView {
this.l10n = l10n;
let anchor = document.createElement('a');
anchor.href = linkService.getAnchorUrl('#page=' + id);
this.l10n.get('thumb_page_title', { page: id, }, 'Page {{page}}').
then((msg) => {
anchor.title = msg;
});
let anchor = document.createElement("a");
anchor.href = linkService.getAnchorUrl("#page=" + id);
this.l10n
.get("thumb_page_title", { page: id }, "Page {{page}}")
.then(msg => {
anchor.title = msg;
});
anchor.onclick = function() {
linkService.page = id;
return false;
};
this.anchor = anchor;
let div = document.createElement('div');
div.className = 'thumbnail';
div.setAttribute('data-page-number', this.id);
let div = document.createElement("div");
div.className = "thumbnail";
div.setAttribute("data-page-number", this.id);
this.div = div;
let ring = document.createElement('div');
ring.className = 'thumbnailSelectionRing';
let ring = document.createElement("div");
ring.className = "thumbnailSelectionRing";
let borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
ring.style.width = this.canvasWidth + borderAdjustment + 'px';
ring.style.height = this.canvasHeight + borderAdjustment + 'px';
ring.style.width = this.canvasWidth + borderAdjustment + "px";
ring.style.height = this.canvasHeight + borderAdjustment + "px";
this.ring = ring;
div.appendChild(ring);
@ -146,7 +157,7 @@ class PDFThumbnailView {
this.pdfPage = pdfPage;
this.pdfPageRotate = pdfPage.rotate;
let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = pdfPage.getViewport({ scale: 1, rotation: totalRotation, });
this.viewport = pdfPage.getViewport({ scale: 1, rotation: totalRotation });
this.reset();
}
@ -159,17 +170,17 @@ class PDFThumbnailView {
this.pageRatio = this.pageWidth / this.pageHeight;
this.canvasHeight = (this.canvasWidth / this.pageRatio) | 0;
this.scale = (this.canvasWidth / this.pageWidth);
this.scale = this.canvasWidth / this.pageWidth;
this.div.removeAttribute('data-loaded');
this.div.removeAttribute("data-loaded");
let ring = this.ring;
let childNodes = ring.childNodes;
for (let i = childNodes.length - 1; i >= 0; i--) {
ring.removeChild(childNodes[i]);
}
let borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
ring.style.width = this.canvasWidth + borderAdjustment + 'px';
ring.style.height = this.canvasHeight + borderAdjustment + 'px';
ring.style.width = this.canvasWidth + borderAdjustment + "px";
ring.style.height = this.canvasHeight + borderAdjustment + "px";
if (this.canvas) {
// Zeroing the width and height causes Firefox to release graphics
@ -179,13 +190,13 @@ class PDFThumbnailView {
delete this.canvas;
}
if (this.image) {
this.image.removeAttribute('src');
this.image.removeAttribute("src");
delete this.image;
}
}
update(rotation) {
if (typeof rotation !== 'undefined') {
if (typeof rotation !== "undefined") {
this.rotation = rotation;
}
let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
@ -212,22 +223,24 @@ class PDFThumbnailView {
* @private
*/
_getPageDrawContext(noCtxScale = false) {
let canvas = document.createElement('canvas');
let canvas = document.createElement("canvas");
// Keep the no-thumbnail outline visible, i.e. `data-loaded === false`,
// until rendering/image conversion is complete, to avoid display issues.
this.canvas = canvas;
if (typeof PDFJSDev === 'undefined' ||
PDFJSDev.test('MOZCENTRAL || FIREFOX || GENERIC')) {
if (
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("MOZCENTRAL || FIREFOX || GENERIC")
) {
canvas.mozOpaque = true;
}
let ctx = canvas.getContext('2d', { alpha: false, });
let ctx = canvas.getContext("2d", { alpha: false });
let outputScale = getOutputScale(ctx);
canvas.width = (this.canvasWidth * outputScale.sx) | 0;
canvas.height = (this.canvasHeight * outputScale.sy) | 0;
canvas.style.width = this.canvasWidth + 'px';
canvas.style.height = this.canvasHeight + 'px';
canvas.style.width = this.canvasWidth + "px";
canvas.style.height = this.canvasHeight + "px";
if (!noCtxScale && outputScale.scaled) {
ctx.scale(outputScale.sx, outputScale.sy);
@ -246,36 +259,45 @@ class PDFThumbnailView {
return;
}
let id = this.renderingId;
let className = 'thumbnailImage';
let className = "thumbnailImage";
if (this.disableCanvasToImageConversion) {
this.canvas.id = id;
this.canvas.className = className;
this.l10n.get('thumb_page_canvas', { page: this.pageId, },
'Thumbnail of Page {{page}}').then((msg) => {
this.canvas.setAttribute('aria-label', msg);
});
this.l10n
.get(
"thumb_page_canvas",
{ page: this.pageId },
"Thumbnail of Page {{page}}"
)
.then(msg => {
this.canvas.setAttribute("aria-label", msg);
});
this.div.setAttribute('data-loaded', true);
this.div.setAttribute("data-loaded", true);
this.ring.appendChild(this.canvas);
return;
}
let image = document.createElement('img');
let image = document.createElement("img");
image.id = id;
image.className = className;
this.l10n.get('thumb_page_canvas', { page: this.pageId, },
'Thumbnail of Page {{page}}').
then((msg) => {
image.setAttribute('aria-label', msg);
});
this.l10n
.get(
"thumb_page_canvas",
{ page: this.pageId },
"Thumbnail of Page {{page}}"
)
.then(msg => {
image.setAttribute("aria-label", msg);
});
image.style.width = this.canvasWidth + 'px';
image.style.height = this.canvasHeight + 'px';
image.style.width = this.canvasWidth + "px";
image.style.height = this.canvasHeight + "px";
image.src = this.canvas.toDataURL();
this.image = image;
this.div.setAttribute('data-loaded', true);
this.div.setAttribute("data-loaded", true);
this.ring.appendChild(image);
// Zeroing the width and height causes Firefox to release graphics
@ -287,13 +309,13 @@ class PDFThumbnailView {
draw() {
if (this.renderingState !== RenderingStates.INITIAL) {
console.error('Must be in new state before drawing');
console.error("Must be in new state before drawing");
return Promise.resolve(undefined);
}
this.renderingState = RenderingStates.RUNNING;
let renderCapability = createPromiseCapability();
let finishRenderTask = (error) => {
let finishRenderTask = error => {
// The renderTask may have been replaced by a new one, so only remove
// the reference to the renderTask if it matches the one that is
// triggering this callback.
@ -317,8 +339,8 @@ class PDFThumbnailView {
};
let ctx = this._getPageDrawContext();
let drawViewport = this.viewport.clone({ scale: this.scale, });
let renderContinueCallback = (cont) => {
let drawViewport = this.viewport.clone({ scale: this.scale });
let renderContinueCallback = cont => {
if (!this.renderingQueue.isHighestPriority(this)) {
this.renderingState = RenderingStates.PAUSED;
this.resume = () => {
@ -334,14 +356,17 @@ class PDFThumbnailView {
canvasContext: ctx,
viewport: drawViewport,
};
let renderTask = this.renderTask = this.pdfPage.render(renderContext);
let renderTask = (this.renderTask = this.pdfPage.render(renderContext));
renderTask.onContinue = renderContinueCallback;
renderTask.promise.then(function() {
finishRenderTask(null);
}, function(error) {
finishRenderTask(error);
});
renderTask.promise.then(
function() {
finishRenderTask(null);
},
function(error) {
finishRenderTask(error);
}
);
return renderCapability.promise;
}
@ -362,8 +387,17 @@ class PDFThumbnailView {
let ctx = this._getPageDrawContext(true);
let canvas = ctx.canvas;
if (img.width <= 2 * canvas.width) {
ctx.drawImage(img, 0, 0, img.width, img.height,
0, 0, canvas.width, canvas.height);
ctx.drawImage(
img,
0,
0,
img.width,
img.height,
0,
0,
canvas.width,
canvas.height
);
this._convertCanvasToImage();
return;
}
@ -371,55 +405,86 @@ class PDFThumbnailView {
// drawImage does an awful job of rescaling the image, doing it gradually.
let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS;
let reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS;
let reducedImage = TempImageFactory.getCanvas(reducedWidth,
reducedHeight);
let reducedImageCtx = reducedImage.getContext('2d');
let reducedImage = TempImageFactory.getCanvas(reducedWidth, reducedHeight);
let reducedImageCtx = reducedImage.getContext("2d");
while (reducedWidth > img.width || reducedHeight > img.height) {
reducedWidth >>= 1;
reducedHeight >>= 1;
}
reducedImageCtx.drawImage(img, 0, 0, img.width, img.height,
0, 0, reducedWidth, reducedHeight);
reducedImageCtx.drawImage(
img,
0,
0,
img.width,
img.height,
0,
0,
reducedWidth,
reducedHeight
);
while (reducedWidth > 2 * canvas.width) {
reducedImageCtx.drawImage(reducedImage,
0, 0, reducedWidth, reducedHeight,
0, 0, reducedWidth >> 1, reducedHeight >> 1);
reducedImageCtx.drawImage(
reducedImage,
0,
0,
reducedWidth,
reducedHeight,
0,
0,
reducedWidth >> 1,
reducedHeight >> 1
);
reducedWidth >>= 1;
reducedHeight >>= 1;
}
ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight,
0, 0, canvas.width, canvas.height);
ctx.drawImage(
reducedImage,
0,
0,
reducedWidth,
reducedHeight,
0,
0,
canvas.width,
canvas.height
);
this._convertCanvasToImage();
}
get pageId() {
return (this.pageLabel !== null ? this.pageLabel : this.id);
return this.pageLabel !== null ? this.pageLabel : this.id;
}
/**
* @param {string|null} label
*/
setPageLabel(label) {
this.pageLabel = (typeof label === 'string' ? label : null);
this.pageLabel = typeof label === "string" ? label : null;
this.l10n.get('thumb_page_title', { page: this.pageId, },
'Page {{page}}').then((msg) => {
this.anchor.title = msg;
});
this.l10n
.get("thumb_page_title", { page: this.pageId }, "Page {{page}}")
.then(msg => {
this.anchor.title = msg;
});
if (this.renderingState !== RenderingStates.FINISHED) {
return;
}
this.l10n.get('thumb_page_canvas', { page: this.pageId, },
'Thumbnail of Page {{page}}').then((ariaLabel) => {
if (this.image) {
this.image.setAttribute('aria-label', ariaLabel);
} else if (this.disableCanvasToImageConversion && this.canvas) {
this.canvas.setAttribute('aria-label', ariaLabel);
}
});
this.l10n
.get(
"thumb_page_canvas",
{ page: this.pageId },
"Thumbnail of Page {{page}}"
)
.then(ariaLabel => {
if (this.image) {
this.image.setAttribute("aria-label", ariaLabel);
} else if (this.disableCanvasToImageConversion && this.canvas) {
this.canvas.setAttribute("aria-label", ariaLabel);
}
});
}
static cleanup() {
@ -427,6 +492,4 @@ class PDFThumbnailView {
}
}
export {
PDFThumbnailView,
};
export { PDFThumbnailView };

View file

@ -15,12 +15,16 @@
/* eslint no-var: error, prefer-const: error */
import {
getVisibleElements, isValidRotation, NullL10n, scrollIntoView, watchScroll
} from './ui_utils';
import { PDFThumbnailView } from './pdf_thumbnail_view';
getVisibleElements,
isValidRotation,
NullL10n,
scrollIntoView,
watchScroll,
} from "./ui_utils";
import { PDFThumbnailView } from "./pdf_thumbnail_view";
const THUMBNAIL_SCROLL_MARGIN = -19;
const THUMBNAIL_SELECTED_CLASS = 'selected';
const THUMBNAIL_SELECTED_CLASS = "selected";
/**
* @typedef {Object} PDFThumbnailViewerOptions
@ -40,7 +44,7 @@ class PDFThumbnailViewer {
/**
* @param {PDFThumbnailViewerOptions} options
*/
constructor({ container, linkService, renderingQueue, l10n = NullL10n, }) {
constructor({ container, linkService, renderingQueue, l10n = NullL10n }) {
this.container = container;
this.linkService = linkService;
this.renderingQueue = renderingQueue;
@ -93,7 +97,7 @@ class PDFThumbnailViewer {
if (numVisibleThumbs > 0) {
const first = visibleThumbs.first.id;
// Account for only one thumbnail being visible.
const last = (numVisibleThumbs > 1 ? visibleThumbs.last.id : first);
const last = numVisibleThumbs > 1 ? visibleThumbs.last.id : first;
let shouldScroll = false;
if (pageNumber <= first || pageNumber >= last) {
@ -108,7 +112,7 @@ class PDFThumbnailViewer {
});
}
if (shouldScroll) {
scrollIntoView(thumbnailView.div, { top: THUMBNAIL_SCROLL_MARGIN, });
scrollIntoView(thumbnailView.div, { top: THUMBNAIL_SCROLL_MARGIN });
}
}
@ -121,7 +125,7 @@ class PDFThumbnailViewer {
set pagesRotation(rotation) {
if (!isValidRotation(rotation)) {
throw new Error('Invalid thumbnails rotation angle.');
throw new Error("Invalid thumbnails rotation angle.");
}
if (!this.pdfDocument) {
return;
@ -151,7 +155,7 @@ class PDFThumbnailViewer {
this._pagesRequests = new WeakMap();
// Remove the thumbnails from the DOM.
this.container.textContent = '';
this.container.textContent = "";
}
setDocument(pdfDocument) {
@ -165,35 +169,38 @@ class PDFThumbnailViewer {
return;
}
pdfDocument.getPage(1).then((firstPdfPage) => {
const pagesCount = pdfDocument.numPages;
const viewport = firstPdfPage.getViewport({ scale: 1, });
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
const thumbnail = new PDFThumbnailView({
container: this.container,
id: pageNum,
defaultViewport: viewport.clone(),
linkService: this.linkService,
renderingQueue: this.renderingQueue,
disableCanvasToImageConversion: false,
l10n: this.l10n,
});
this._thumbnails.push(thumbnail);
}
// Set the first `pdfPage` immediately, since it's already loaded,
// rather than having to repeat the `PDFDocumentProxy.getPage` call in
// the `this._ensurePdfPageLoaded` method before rendering can start.
const firstThumbnailView = this._thumbnails[0];
if (firstThumbnailView) {
firstThumbnailView.setPdfPage(firstPdfPage);
}
pdfDocument
.getPage(1)
.then(firstPdfPage => {
const pagesCount = pdfDocument.numPages;
const viewport = firstPdfPage.getViewport({ scale: 1 });
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
const thumbnail = new PDFThumbnailView({
container: this.container,
id: pageNum,
defaultViewport: viewport.clone(),
linkService: this.linkService,
renderingQueue: this.renderingQueue,
disableCanvasToImageConversion: false,
l10n: this.l10n,
});
this._thumbnails.push(thumbnail);
}
// Set the first `pdfPage` immediately, since it's already loaded,
// rather than having to repeat the `PDFDocumentProxy.getPage` call in
// the `this._ensurePdfPageLoaded` method before rendering can start.
const firstThumbnailView = this._thumbnails[0];
if (firstThumbnailView) {
firstThumbnailView.setPdfPage(firstPdfPage);
}
// Ensure that the current thumbnail is always highlighted on load.
const thumbnailView = this._thumbnails[this._currentPageNumber - 1];
thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
}).catch((reason) => {
console.error('Unable to initialize thumbnail viewer', reason);
});
// Ensure that the current thumbnail is always highlighted on load.
const thumbnailView = this._thumbnails[this._currentPageNumber - 1];
thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
})
.catch(reason => {
console.error("Unable to initialize thumbnail viewer", reason);
});
}
/**
@ -216,10 +223,11 @@ class PDFThumbnailViewer {
}
if (!labels) {
this._pageLabels = null;
} else if (!(Array.isArray(labels) &&
this.pdfDocument.numPages === labels.length)) {
} else if (
!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)
) {
this._pageLabels = null;
console.error('PDFThumbnailViewer_setPageLabels: Invalid page labels.');
console.error("PDFThumbnailViewer_setPageLabels: Invalid page labels.");
} else {
this._pageLabels = labels;
}
@ -242,26 +250,31 @@ class PDFThumbnailViewer {
if (this._pagesRequests.has(thumbView)) {
return this._pagesRequests.get(thumbView);
}
const promise = this.pdfDocument.getPage(thumbView.id).then((pdfPage) => {
if (!thumbView.pdfPage) {
thumbView.setPdfPage(pdfPage);
}
this._pagesRequests.delete(thumbView);
return pdfPage;
}).catch((reason) => {
console.error('Unable to get page for thumb view', reason);
// Page error -- there is nothing that can be done.
this._pagesRequests.delete(thumbView);
});
const promise = this.pdfDocument
.getPage(thumbView.id)
.then(pdfPage => {
if (!thumbView.pdfPage) {
thumbView.setPdfPage(pdfPage);
}
this._pagesRequests.delete(thumbView);
return pdfPage;
})
.catch(reason => {
console.error("Unable to get page for thumb view", reason);
// Page error -- there is nothing that can be done.
this._pagesRequests.delete(thumbView);
});
this._pagesRequests.set(thumbView, promise);
return promise;
}
forceRendering() {
const visibleThumbs = this._getVisibleThumbs();
const thumbView = this.renderingQueue.getHighestPriority(visibleThumbs,
this._thumbnails,
this.scroll.down);
const thumbView = this.renderingQueue.getHighestPriority(
visibleThumbs,
this._thumbnails,
this.scroll.down
);
if (thumbView) {
this._ensurePdfPageLoaded(thumbView).then(() => {
this.renderingQueue.renderView(thumbView);
@ -272,6 +285,4 @@ class PDFThumbnailViewer {
}
}
export {
PDFThumbnailViewer,
};
export { PDFThumbnailViewer };

View file

@ -15,25 +15,30 @@
/* eslint-disable no-unused-vars */
import {
AnnotationLayerBuilder, DefaultAnnotationLayerFactory
} from './annotation_layer_builder.js';
AnnotationLayerBuilder,
DefaultAnnotationLayerFactory,
} from "./annotation_layer_builder.js";
import {
DefaultTextLayerFactory, TextLayerBuilder
} from './text_layer_builder.js';
DefaultTextLayerFactory,
TextLayerBuilder,
} from "./text_layer_builder.js";
import {
EventBus, getGlobalEventBus, NullL10n, ProgressBar
} from './ui_utils.js';
import { PDFLinkService, SimpleLinkService } from './pdf_link_service.js';
import { DownloadManager } from './download_manager.js';
import { GenericL10n } from './genericl10n.js';
import { PDFFindController } from './pdf_find_controller.js';
import { PDFHistory } from './pdf_history.js';
import { PDFPageView } from './pdf_page_view.js';
import { PDFSinglePageViewer } from './pdf_single_page_viewer';
import { PDFViewer } from './pdf_viewer.js';
EventBus,
getGlobalEventBus,
NullL10n,
ProgressBar,
} from "./ui_utils.js";
import { PDFLinkService, SimpleLinkService } from "./pdf_link_service.js";
import { DownloadManager } from "./download_manager.js";
import { GenericL10n } from "./genericl10n.js";
import { PDFFindController } from "./pdf_find_controller.js";
import { PDFHistory } from "./pdf_history.js";
import { PDFPageView } from "./pdf_page_view.js";
import { PDFSinglePageViewer } from "./pdf_single_page_viewer";
import { PDFViewer } from "./pdf_viewer.js";
const pdfjsVersion = PDFJSDev.eval('BUNDLE_VERSION');
const pdfjsBuild = PDFJSDev.eval('BUNDLE_BUILD');
const pdfjsVersion = PDFJSDev.eval("BUNDLE_VERSION");
const pdfjsBuild = PDFJSDev.eval("BUNDLE_BUILD");
// For backwards compatibility, ensure that events are re-dispatched to the DOM.
getGlobalEventBus(/* dispatchToDOM = */ true);

View file

@ -13,25 +13,28 @@
* limitations under the License.
*/
import { BaseViewer } from './base_viewer';
import { shadow } from 'pdfjs-lib';
import { BaseViewer } from "./base_viewer";
import { shadow } from "pdfjs-lib";
class PDFViewer extends BaseViewer {
get _setDocumentViewerElement() {
return shadow(this, '_setDocumentViewerElement', this.viewer);
return shadow(this, "_setDocumentViewerElement", this.viewer);
}
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null, }) {
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null }) {
if (!pageSpot && !this.isInPresentationMode) {
const left = pageDiv.offsetLeft + pageDiv.clientLeft;
const right = left + pageDiv.clientWidth;
const { scrollLeft, clientWidth, } = this.container;
if (this._isScrollModeHorizontal ||
left < scrollLeft || right > scrollLeft + clientWidth) {
pageSpot = { left: 0, top: 0, };
const { scrollLeft, clientWidth } = this.container;
if (
this._isScrollModeHorizontal ||
left < scrollLeft ||
right > scrollLeft + clientWidth
) {
pageSpot = { left: 0, top: 0 };
}
}
super._scrollIntoView({ pageDiv, pageSpot, pageNumber, });
super._scrollIntoView({ pageDiv, pageSpot, pageNumber });
}
_getVisiblePages() {
@ -66,6 +69,4 @@ class PDFViewer extends BaseViewer {
}
}
export {
PDFViewer,
};
export { PDFViewer };

View file

@ -14,12 +14,12 @@
*/
/* globals module, __non_webpack_require__ */
'use strict';
"use strict";
let pdfjsLib;
if (typeof window !== 'undefined' && window['pdfjs-dist/build/pdf']) {
pdfjsLib = window['pdfjs-dist/build/pdf'];
if (typeof window !== "undefined" && window["pdfjs-dist/build/pdf"]) {
pdfjsLib = window["pdfjs-dist/build/pdf"];
} else {
pdfjsLib = __non_webpack_require__('../build/pdf.js');
pdfjsLib = __non_webpack_require__("../build/pdf.js");
}
module.exports = pdfjsLib;

View file

@ -17,17 +17,18 @@
let defaultPreferences = null;
function getDefaultPreferences() {
if (!defaultPreferences) {
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('PRODUCTION')) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("PRODUCTION")) {
defaultPreferences = Promise.resolve(
PDFJSDev.json('$ROOT/build/default_preferences.json'));
PDFJSDev.json("$ROOT/build/default_preferences.json")
);
} else {
defaultPreferences = new Promise(function(resolve, reject) {
if (typeof SystemJS === 'object') {
SystemJS.import('./app_options').then(resolve, reject);
if (typeof SystemJS === "object") {
SystemJS.import("./app_options").then(resolve, reject);
} else {
reject(new Error('SystemJS must be used to load AppOptions.'));
reject(new Error("SystemJS must be used to load AppOptions."));
}
}).then(function({ AppOptions, OptionKind, }) {
}).then(function({ AppOptions, OptionKind }) {
return AppOptions.getAll(OptionKind.PREFERENCE);
});
}
@ -43,35 +44,40 @@ function getDefaultPreferences() {
class BasePreferences {
constructor() {
if (this.constructor === BasePreferences) {
throw new Error('Cannot initialize BasePreferences.');
throw new Error("Cannot initialize BasePreferences.");
}
this.prefs = null;
this._initializedPromise = getDefaultPreferences().then((defaults) => {
Object.defineProperty(this, 'defaults', {
value: Object.freeze(defaults),
writable: false,
enumerable: true,
configurable: false,
});
this._initializedPromise = getDefaultPreferences()
.then(defaults => {
Object.defineProperty(this, "defaults", {
value: Object.freeze(defaults),
writable: false,
enumerable: true,
configurable: false,
});
this.prefs = Object.assign(Object.create(null), defaults);
return this._readFromStorage(defaults);
}).then((prefs) => {
if (!prefs) {
return;
}
for (const name in prefs) {
const defaultValue = this.defaults[name], prefValue = prefs[name];
// Ignore preferences not present in, or whose types don't match,
// the default values.
if (defaultValue === undefined ||
typeof prefValue !== typeof defaultValue) {
continue;
this.prefs = Object.assign(Object.create(null), defaults);
return this._readFromStorage(defaults);
})
.then(prefs => {
if (!prefs) {
return;
}
this.prefs[name] = prefValue;
}
});
for (const name in prefs) {
const defaultValue = this.defaults[name],
prefValue = prefs[name];
// Ignore preferences not present in, or whose types don't match,
// the default values.
if (
defaultValue === undefined ||
typeof prefValue !== typeof defaultValue
) {
continue;
}
this.prefs[name] = prefValue;
}
});
}
/**
@ -81,7 +87,7 @@ class BasePreferences {
* have been written.
*/
async _writeToStorage(prefObj) {
throw new Error('Not implemented: _writeToStorage');
throw new Error("Not implemented: _writeToStorage");
}
/**
@ -91,7 +97,7 @@ class BasePreferences {
* the preferences that have been read.
*/
async _readFromStorage(prefObj) {
throw new Error('Not implemented: _readFromStorage');
throw new Error("Not implemented: _readFromStorage");
}
/**
@ -119,20 +125,22 @@ class BasePreferences {
if (defaultValue === undefined) {
throw new Error(`Set preference: "${name}" is undefined.`);
} else if (value === undefined) {
throw new Error('Set preference: no value is specified.');
throw new Error("Set preference: no value is specified.");
}
const valueType = typeof value;
const defaultType = typeof defaultValue;
if (valueType !== defaultType) {
if (valueType === 'number' && defaultType === 'string') {
if (valueType === "number" && defaultType === "string") {
value = value.toString();
} else {
throw new Error(`Set preference: "${value}" is a ${valueType}, ` +
`expected a ${defaultType}.`);
throw new Error(
`Set preference: "${value}" is a ${valueType}, ` +
`expected a ${defaultType}.`
);
}
} else {
if (valueType === 'number' && !Number.isInteger(value)) {
if (valueType === "number" && !Number.isInteger(value)) {
throw new Error(`Set preference: "${value}" must be an integer.`);
}
}
@ -173,6 +181,4 @@ class BasePreferences {
}
}
export {
BasePreferences,
};
export { BasePreferences };

View file

@ -14,9 +14,9 @@
*/
/* eslint no-var: error, prefer-const: error */
import { SCROLLBAR_PADDING, ScrollMode, SpreadMode } from './ui_utils';
import { CursorTool } from './pdf_cursor_tools';
import { PDFSinglePageViewer } from './pdf_single_page_viewer';
import { SCROLLBAR_PADDING, ScrollMode, SpreadMode } from "./ui_utils";
import { CursorTool } from "./pdf_cursor_tools";
import { PDFSinglePageViewer } from "./pdf_single_page_viewer";
/**
* @typedef {Object} SecondaryToolbarOptions
@ -61,37 +61,80 @@ class SecondaryToolbar {
this.toggleButton = options.toggleButton;
this.toolbarButtonContainer = options.toolbarButtonContainer;
this.buttons = [
{ element: options.presentationModeButton, eventName: 'presentationmode',
close: true, },
{ element: options.openFileButton, eventName: 'openfile', close: true, },
{ element: options.printButton, eventName: 'print', close: true, },
{ element: options.downloadButton, eventName: 'download', close: true, },
{ element: options.viewBookmarkButton, eventName: null, close: true, },
{ element: options.firstPageButton, eventName: 'firstpage',
close: true, },
{ element: options.lastPageButton, eventName: 'lastpage', close: true, },
{ element: options.pageRotateCwButton, eventName: 'rotatecw',
close: false, },
{ element: options.pageRotateCcwButton, eventName: 'rotateccw',
close: false, },
{ element: options.cursorSelectToolButton, eventName: 'switchcursortool',
eventDetails: { tool: CursorTool.SELECT, }, close: true, },
{ element: options.cursorHandToolButton, eventName: 'switchcursortool',
eventDetails: { tool: CursorTool.HAND, }, close: true, },
{ element: options.scrollVerticalButton, eventName: 'switchscrollmode',
eventDetails: { mode: ScrollMode.VERTICAL, }, close: true, },
{ element: options.scrollHorizontalButton, eventName: 'switchscrollmode',
eventDetails: { mode: ScrollMode.HORIZONTAL, }, close: true, },
{ element: options.scrollWrappedButton, eventName: 'switchscrollmode',
eventDetails: { mode: ScrollMode.WRAPPED, }, close: true, },
{ element: options.spreadNoneButton, eventName: 'switchspreadmode',
eventDetails: { mode: SpreadMode.NONE, }, close: true, },
{ element: options.spreadOddButton, eventName: 'switchspreadmode',
eventDetails: { mode: SpreadMode.ODD, }, close: true, },
{ element: options.spreadEvenButton, eventName: 'switchspreadmode',
eventDetails: { mode: SpreadMode.EVEN, }, close: true, },
{ element: options.documentPropertiesButton,
eventName: 'documentproperties', close: true, },
{
element: options.presentationModeButton,
eventName: "presentationmode",
close: true,
},
{ element: options.openFileButton, eventName: "openfile", close: true },
{ element: options.printButton, eventName: "print", close: true },
{ element: options.downloadButton, eventName: "download", close: true },
{ element: options.viewBookmarkButton, eventName: null, close: true },
{ element: options.firstPageButton, eventName: "firstpage", close: true },
{ element: options.lastPageButton, eventName: "lastpage", close: true },
{
element: options.pageRotateCwButton,
eventName: "rotatecw",
close: false,
},
{
element: options.pageRotateCcwButton,
eventName: "rotateccw",
close: false,
},
{
element: options.cursorSelectToolButton,
eventName: "switchcursortool",
eventDetails: { tool: CursorTool.SELECT },
close: true,
},
{
element: options.cursorHandToolButton,
eventName: "switchcursortool",
eventDetails: { tool: CursorTool.HAND },
close: true,
},
{
element: options.scrollVerticalButton,
eventName: "switchscrollmode",
eventDetails: { mode: ScrollMode.VERTICAL },
close: true,
},
{
element: options.scrollHorizontalButton,
eventName: "switchscrollmode",
eventDetails: { mode: ScrollMode.HORIZONTAL },
close: true,
},
{
element: options.scrollWrappedButton,
eventName: "switchscrollmode",
eventDetails: { mode: ScrollMode.WRAPPED },
close: true,
},
{
element: options.spreadNoneButton,
eventName: "switchspreadmode",
eventDetails: { mode: SpreadMode.NONE },
close: true,
},
{
element: options.spreadOddButton,
eventName: "switchspreadmode",
eventDetails: { mode: SpreadMode.ODD },
close: true,
},
{
element: options.spreadEvenButton,
eventName: "switchspreadmode",
eventDetails: { mode: SpreadMode.EVEN },
close: true,
},
{
element: options.documentPropertiesButton,
eventName: "documentproperties",
close: true,
},
];
this.items = {
firstPage: options.firstPageButton,
@ -117,17 +160,21 @@ class SecondaryToolbar {
this._bindSpreadModeListener(options);
// Bind the event listener for adjusting the 'max-height' of the toolbar.
this.eventBus.on('resize', this._setMaxHeight.bind(this));
this.eventBus.on("resize", this._setMaxHeight.bind(this));
// Hide the Scroll/Spread mode buttons, when they're not applicable to the
// current `BaseViewer` instance (in particular `PDFSinglePageViewer`).
this.eventBus.on('baseviewerinit', (evt) => {
this.eventBus.on("baseviewerinit", evt => {
if (evt.source instanceof PDFSinglePageViewer) {
this.toolbarButtonContainer.classList.add('hiddenScrollModeButtons',
'hiddenSpreadModeButtons');
this.toolbarButtonContainer.classList.add(
"hiddenScrollModeButtons",
"hiddenSpreadModeButtons"
);
} else {
this.toolbarButtonContainer.classList.remove('hiddenScrollModeButtons',
'hiddenSpreadModeButtons');
this.toolbarButtonContainer.classList.remove(
"hiddenScrollModeButtons",
"hiddenSpreadModeButtons"
);
}
});
}
@ -155,25 +202,25 @@ class SecondaryToolbar {
this._updateUIState();
// Reset the Scroll/Spread buttons too, since they're document specific.
this.eventBus.dispatch('secondarytoolbarreset', { source: this, });
this.eventBus.dispatch("secondarytoolbarreset", { source: this });
}
_updateUIState() {
this.items.firstPage.disabled = (this.pageNumber <= 1);
this.items.lastPage.disabled = (this.pageNumber >= this.pagesCount);
this.items.firstPage.disabled = this.pageNumber <= 1;
this.items.lastPage.disabled = this.pageNumber >= this.pagesCount;
this.items.pageRotateCw.disabled = this.pagesCount === 0;
this.items.pageRotateCcw.disabled = this.pagesCount === 0;
}
_bindClickListeners() {
// Button to toggle the visibility of the secondary toolbar.
this.toggleButton.addEventListener('click', this.toggle.bind(this));
this.toggleButton.addEventListener("click", this.toggle.bind(this));
// All items within the secondary toolbar.
for (const { element, eventName, close, eventDetails, } of this.buttons) {
element.addEventListener('click', (evt) => {
for (const { element, eventName, close, eventDetails } of this.buttons) {
element.addEventListener("click", evt => {
if (eventName !== null) {
const details = { source: this, };
const details = { source: this };
for (const property in eventDetails) {
details[property] = eventDetails[property];
}
@ -187,53 +234,69 @@ class SecondaryToolbar {
}
_bindCursorToolsListener(buttons) {
this.eventBus.on('cursortoolchanged', function({ tool, }) {
buttons.cursorSelectToolButton.classList.toggle('toggled',
tool === CursorTool.SELECT);
buttons.cursorHandToolButton.classList.toggle('toggled',
tool === CursorTool.HAND);
this.eventBus.on("cursortoolchanged", function({ tool }) {
buttons.cursorSelectToolButton.classList.toggle(
"toggled",
tool === CursorTool.SELECT
);
buttons.cursorHandToolButton.classList.toggle(
"toggled",
tool === CursorTool.HAND
);
});
}
_bindScrollModeListener(buttons) {
function scrollModeChanged({ mode, }) {
buttons.scrollVerticalButton.classList.toggle('toggled',
mode === ScrollMode.VERTICAL);
buttons.scrollHorizontalButton.classList.toggle('toggled',
mode === ScrollMode.HORIZONTAL);
buttons.scrollWrappedButton.classList.toggle('toggled',
mode === ScrollMode.WRAPPED);
function scrollModeChanged({ mode }) {
buttons.scrollVerticalButton.classList.toggle(
"toggled",
mode === ScrollMode.VERTICAL
);
buttons.scrollHorizontalButton.classList.toggle(
"toggled",
mode === ScrollMode.HORIZONTAL
);
buttons.scrollWrappedButton.classList.toggle(
"toggled",
mode === ScrollMode.WRAPPED
);
// Temporarily *disable* the Spread buttons when horizontal scrolling is
// enabled, since the non-default Spread modes doesn't affect the layout.
const isScrollModeHorizontal = (mode === ScrollMode.HORIZONTAL);
const isScrollModeHorizontal = mode === ScrollMode.HORIZONTAL;
buttons.spreadNoneButton.disabled = isScrollModeHorizontal;
buttons.spreadOddButton.disabled = isScrollModeHorizontal;
buttons.spreadEvenButton.disabled = isScrollModeHorizontal;
}
this.eventBus.on('scrollmodechanged', scrollModeChanged);
this.eventBus.on("scrollmodechanged", scrollModeChanged);
this.eventBus.on('secondarytoolbarreset', (evt) => {
this.eventBus.on("secondarytoolbarreset", evt => {
if (evt.source === this) {
scrollModeChanged({ mode: ScrollMode.VERTICAL, });
scrollModeChanged({ mode: ScrollMode.VERTICAL });
}
});
}
_bindSpreadModeListener(buttons) {
function spreadModeChanged({ mode, }) {
buttons.spreadNoneButton.classList.toggle('toggled',
mode === SpreadMode.NONE);
buttons.spreadOddButton.classList.toggle('toggled',
mode === SpreadMode.ODD);
buttons.spreadEvenButton.classList.toggle('toggled',
mode === SpreadMode.EVEN);
function spreadModeChanged({ mode }) {
buttons.spreadNoneButton.classList.toggle(
"toggled",
mode === SpreadMode.NONE
);
buttons.spreadOddButton.classList.toggle(
"toggled",
mode === SpreadMode.ODD
);
buttons.spreadEvenButton.classList.toggle(
"toggled",
mode === SpreadMode.EVEN
);
}
this.eventBus.on('spreadmodechanged', spreadModeChanged);
this.eventBus.on("spreadmodechanged", spreadModeChanged);
this.eventBus.on('secondarytoolbarreset', (evt) => {
this.eventBus.on("secondarytoolbarreset", evt => {
if (evt.source === this) {
spreadModeChanged({ mode: SpreadMode.NONE, });
spreadModeChanged({ mode: SpreadMode.NONE });
}
});
}
@ -245,8 +308,8 @@ class SecondaryToolbar {
this.opened = true;
this._setMaxHeight();
this.toggleButton.classList.add('toggled');
this.toolbar.classList.remove('hidden');
this.toggleButton.classList.add("toggled");
this.toolbar.classList.remove("hidden");
}
close() {
@ -254,8 +317,8 @@ class SecondaryToolbar {
return;
}
this.opened = false;
this.toolbar.classList.add('hidden');
this.toggleButton.classList.remove('toggled');
this.toolbar.classList.add("hidden");
this.toggleButton.classList.remove("toggled");
}
toggle() {
@ -278,13 +341,13 @@ class SecondaryToolbar {
if (this.containerHeight === this.previousContainerHeight) {
return;
}
this.toolbarButtonContainer.setAttribute('style',
'max-height: ' + (this.containerHeight - SCROLLBAR_PADDING) + 'px;');
this.toolbarButtonContainer.setAttribute(
"style",
"max-height: " + (this.containerHeight - SCROLLBAR_PADDING) + "px;"
);
this.previousContainerHeight = this.containerHeight;
}
}
export {
SecondaryToolbar,
};
export { SecondaryToolbar };

View file

@ -14,8 +14,8 @@
*/
/* eslint no-var: error, prefer-const: error */
import { getGlobalEventBus } from './ui_utils';
import { renderTextLayer } from 'pdfjs-lib';
import { getGlobalEventBus } from "./ui_utils";
import { renderTextLayer } from "pdfjs-lib";
const EXPAND_DIVS_TIMEOUT = 300; // ms
@ -37,8 +37,14 @@ const EXPAND_DIVS_TIMEOUT = 300; // ms
* also provides a way to highlight text that is being searched for.
*/
class TextLayerBuilder {
constructor({ textLayerDiv, eventBus, pageIndex, viewport,
findController = null, enhanceTextSelection = false, }) {
constructor({
textLayerDiv,
eventBus,
pageIndex,
viewport,
findController = null,
enhanceTextSelection = false,
}) {
this.textLayerDiv = textLayerDiv;
this.eventBus = eventBus || getGlobalEventBus();
this.textContent = null;
@ -65,12 +71,12 @@ class TextLayerBuilder {
this.renderingDone = true;
if (!this.enhanceTextSelection) {
const endOfContent = document.createElement('div');
endOfContent.className = 'endOfContent';
const endOfContent = document.createElement("div");
endOfContent.className = "endOfContent";
this.textLayerDiv.appendChild(endOfContent);
}
this.eventBus.dispatch('textlayerrendered', {
this.eventBus.dispatch("textlayerrendered", {
source: this,
pageNumber: this.pageNumber,
numTextDivs: this.textDivs.length,
@ -101,22 +107,27 @@ class TextLayerBuilder {
timeout,
enhanceTextSelection: this.enhanceTextSelection,
});
this.textLayerRenderTask.promise.then(() => {
this.textLayerDiv.appendChild(textLayerFrag);
this._finishRendering();
this._updateMatches();
}, function (reason) {
// Cancelled or failed to render text layer; skipping errors.
});
this.textLayerRenderTask.promise.then(
() => {
this.textLayerDiv.appendChild(textLayerFrag);
this._finishRendering();
this._updateMatches();
},
function(reason) {
// Cancelled or failed to render text layer; skipping errors.
}
);
if (!this._onUpdateTextLayerMatches) {
this._onUpdateTextLayerMatches = (evt) => {
this._onUpdateTextLayerMatches = evt => {
if (evt.pageIndex === this.pageIdx || evt.pageIndex === -1) {
this._updateMatches();
}
};
this.eventBus.on('updatetextlayermatches',
this._onUpdateTextLayerMatches);
this.eventBus.on(
"updatetextlayermatches",
this._onUpdateTextLayerMatches
);
}
}
@ -129,8 +140,10 @@ class TextLayerBuilder {
this.textLayerRenderTask = null;
}
if (this._onUpdateTextLayerMatches) {
this.eventBus.off('updatetextlayermatches',
this._onUpdateTextLayerMatches);
this.eventBus.off(
"updatetextlayermatches",
this._onUpdateTextLayerMatches
);
this._onUpdateTextLayerMatches = null;
}
}
@ -150,9 +163,10 @@ class TextLayerBuilder {
if (!matches) {
return [];
}
const { findController, textContentItemsStr, } = this;
const { findController, textContentItemsStr } = this;
let i = 0, iIndex = 0;
let i = 0,
iIndex = 0;
const end = textContentItemsStr.length - 1;
const queryLen = findController.state.query.length;
const result = [];
@ -162,14 +176,13 @@ class TextLayerBuilder {
let matchIdx = matches[m];
// Loop over the divIdxs.
while (i !== end &&
matchIdx >= (iIndex + textContentItemsStr[i].length)) {
while (i !== end && matchIdx >= iIndex + textContentItemsStr[i].length) {
iIndex += textContentItemsStr[i].length;
i++;
}
if (i === textContentItemsStr.length) {
console.error('Could not find a matching mapping');
console.error("Could not find a matching mapping");
}
const match = {
@ -180,16 +193,17 @@ class TextLayerBuilder {
};
// Calculate the end position.
if (matchesLength) { // Multiterm search.
if (matchesLength) {
// Multiterm search.
matchIdx += matchesLength[m];
} else { // Phrase search.
} else {
// Phrase search.
matchIdx += queryLen;
}
// Somewhat the same array as above, but use > instead of >= to get
// the end position right.
while (i !== end &&
matchIdx > (iIndex + textContentItemsStr[i].length)) {
while (i !== end && matchIdx > iIndex + textContentItemsStr[i].length) {
iIndex += textContentItemsStr[i].length;
i++;
}
@ -208,9 +222,9 @@ class TextLayerBuilder {
if (matches.length === 0) {
return;
}
const { findController, pageIdx, textContentItemsStr, textDivs, } = this;
const { findController, pageIdx, textContentItemsStr, textDivs } = this;
const isSelectedPage = (pageIdx === findController.selected.pageIdx);
const isSelectedPage = pageIdx === findController.selected.pageIdx;
const selectedMatchIdx = findController.selected.matchIdx;
const highlightAll = findController.state.highlightAll;
let prevEnd = null;
@ -221,17 +235,19 @@ class TextLayerBuilder {
function beginText(begin, className) {
const divIdx = begin.divIdx;
textDivs[divIdx].textContent = '';
textDivs[divIdx].textContent = "";
appendTextToDiv(divIdx, 0, begin.offset, className);
}
function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
const div = textDivs[divIdx];
const content =
textContentItemsStr[divIdx].substring(fromOffset, toOffset);
const content = textContentItemsStr[divIdx].substring(
fromOffset,
toOffset
);
const node = document.createTextNode(content);
if (className) {
const span = document.createElement('span');
const span = document.createElement("span");
span.className = className;
span.appendChild(node);
div.appendChild(span);
@ -240,7 +256,8 @@ class TextLayerBuilder {
div.appendChild(node);
}
let i0 = selectedMatchIdx, i1 = i0 + 1;
let i0 = selectedMatchIdx,
i1 = i0 + 1;
if (highlightAll) {
i0 = 0;
i1 = matches.length;
@ -253,10 +270,11 @@ class TextLayerBuilder {
const match = matches[i];
const begin = match.begin;
const end = match.end;
const isSelected = (isSelectedPage && i === selectedMatchIdx);
const highlightSuffix = (isSelected ? ' selected' : '');
const isSelected = isSelectedPage && i === selectedMatchIdx;
const highlightSuffix = isSelected ? " selected" : "";
if (isSelected) { // Attempt to scroll the selected match into view.
if (isSelected) {
// Attempt to scroll the selected match into view.
findController.scrollMatchIntoView({
element: textDivs[begin.divIdx],
pageIndex: pageIdx,
@ -277,15 +295,23 @@ class TextLayerBuilder {
}
if (begin.divIdx === end.divIdx) {
appendTextToDiv(begin.divIdx, begin.offset, end.offset,
'highlight' + highlightSuffix);
appendTextToDiv(
begin.divIdx,
begin.offset,
end.offset,
"highlight" + highlightSuffix
);
} else {
appendTextToDiv(begin.divIdx, begin.offset, infinity.offset,
'highlight begin' + highlightSuffix);
appendTextToDiv(
begin.divIdx,
begin.offset,
infinity.offset,
"highlight begin" + highlightSuffix
);
for (let n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
textDivs[n0].className = 'highlight middle' + highlightSuffix;
textDivs[n0].className = "highlight middle" + highlightSuffix;
}
beginText(end, 'highlight end' + highlightSuffix);
beginText(end, "highlight end" + highlightSuffix);
}
prevEnd = end;
}
@ -301,7 +327,11 @@ class TextLayerBuilder {
return;
}
const {
findController, matches, pageIdx, textContentItemsStr, textDivs,
findController,
matches,
pageIdx,
textContentItemsStr,
textDivs,
} = this;
let clearedUntilDivIdx = -1;
@ -312,7 +342,7 @@ class TextLayerBuilder {
for (let n = begin, end = match.end.divIdx; n <= end; n++) {
const div = textDivs[n];
div.textContent = textContentItemsStr[n];
div.className = '';
div.className = "";
}
clearedUntilDivIdx = match.end.divIdx + 1;
}
@ -340,46 +370,55 @@ class TextLayerBuilder {
const div = this.textLayerDiv;
let expandDivsTimer = null;
div.addEventListener('mousedown', (evt) => {
div.addEventListener("mousedown", evt => {
if (this.enhanceTextSelection && this.textLayerRenderTask) {
this.textLayerRenderTask.expandTextDivs(true);
if ((typeof PDFJSDev === 'undefined' ||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) &&
expandDivsTimer) {
if (
(typeof PDFJSDev === "undefined" ||
!PDFJSDev.test("FIREFOX || MOZCENTRAL")) &&
expandDivsTimer
) {
clearTimeout(expandDivsTimer);
expandDivsTimer = null;
}
return;
}
const end = div.querySelector('.endOfContent');
const end = div.querySelector(".endOfContent");
if (!end) {
return;
}
if (typeof PDFJSDev === 'undefined' ||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
if (
typeof PDFJSDev === "undefined" ||
!PDFJSDev.test("FIREFOX || MOZCENTRAL")
) {
// On non-Firefox browsers, the selection will feel better if the height
// of the `endOfContent` div is adjusted to start at mouse click
// location. This avoids flickering when the selection moves up.
// However it does not work when selection is started on empty space.
let adjustTop = evt.target !== div;
if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
adjustTop = adjustTop && window.getComputedStyle(end).
getPropertyValue('-moz-user-select') !== 'none';
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
adjustTop =
adjustTop &&
window
.getComputedStyle(end)
.getPropertyValue("-moz-user-select") !== "none";
}
if (adjustTop) {
const divBounds = div.getBoundingClientRect();
const r = Math.max(0, (evt.pageY - divBounds.top) / divBounds.height);
end.style.top = (r * 100).toFixed(2) + '%';
end.style.top = (r * 100).toFixed(2) + "%";
}
}
end.classList.add('active');
end.classList.add("active");
});
div.addEventListener('mouseup', () => {
div.addEventListener("mouseup", () => {
if (this.enhanceTextSelection && this.textLayerRenderTask) {
if (typeof PDFJSDev === 'undefined' ||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
if (
typeof PDFJSDev === "undefined" ||
!PDFJSDev.test("FIREFOX || MOZCENTRAL")
) {
expandDivsTimer = setTimeout(() => {
if (this.textLayerRenderTask) {
this.textLayerRenderTask.expandTextDivs(false);
@ -392,15 +431,17 @@ class TextLayerBuilder {
return;
}
const end = div.querySelector('.endOfContent');
const end = div.querySelector(".endOfContent");
if (!end) {
return;
}
if (typeof PDFJSDev === 'undefined' ||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
end.style.top = '';
if (
typeof PDFJSDev === "undefined" ||
!PDFJSDev.test("FIREFOX || MOZCENTRAL")
) {
end.style.top = "";
}
end.classList.remove('active');
end.classList.remove("active");
});
}
}
@ -416,8 +457,12 @@ class DefaultTextLayerFactory {
* @param {boolean} enhanceTextSelection
* @returns {TextLayerBuilder}
*/
createTextLayerBuilder(textLayerDiv, pageIndex, viewport,
enhanceTextSelection = false) {
createTextLayerBuilder(
textLayerDiv,
pageIndex,
viewport,
enhanceTextSelection = false
) {
return new TextLayerBuilder({
textLayerDiv,
pageIndex,
@ -427,7 +472,4 @@ class DefaultTextLayerFactory {
}
}
export {
TextLayerBuilder,
DefaultTextLayerFactory,
};
export { TextLayerBuilder, DefaultTextLayerFactory };

View file

@ -15,11 +15,16 @@
/* eslint no-var: error, prefer-const: error */
import {
animationStarted, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, MAX_SCALE,
MIN_SCALE, noContextMenuHandler, NullL10n
} from './ui_utils';
animationStarted,
DEFAULT_SCALE,
DEFAULT_SCALE_VALUE,
MAX_SCALE,
MIN_SCALE,
noContextMenuHandler,
NullL10n,
} from "./ui_utils";
const PAGE_NUMBER_LOADING_INDICATOR = 'visiblePageIsLoading';
const PAGE_NUMBER_LOADING_INDICATOR = "visiblePageIsLoading";
const SCALE_SELECT_CONTAINER_PADDING = 8;
const SCALE_SELECT_PADDING = 22;
@ -58,16 +63,18 @@ class Toolbar {
this.eventBus = eventBus;
this.l10n = l10n;
this.buttons = [
{ element: options.previous, eventName: 'previouspage', },
{ element: options.next, eventName: 'nextpage', },
{ element: options.zoomIn, eventName: 'zoomin', },
{ element: options.zoomOut, eventName: 'zoomout', },
{ element: options.openFile, eventName: 'openfile', },
{ element: options.print, eventName: 'print', },
{ element: options.presentationModeButton,
eventName: 'presentationmode', },
{ element: options.download, eventName: 'download', },
{ element: options.viewBookmark, eventName: null, },
{ element: options.previous, eventName: "previouspage" },
{ element: options.next, eventName: "nextpage" },
{ element: options.zoomIn, eventName: "zoomin" },
{ element: options.zoomOut, eventName: "zoomout" },
{ element: options.openFile, eventName: "openfile" },
{ element: options.print, eventName: "print" },
{
element: options.presentationModeButton,
eventName: "presentationmode",
},
{ element: options.download, eventName: "download" },
{ element: options.viewBookmark, eventName: null },
];
this.items = {
numPages: options.numPages,
@ -118,33 +125,33 @@ class Toolbar {
}
_bindListeners() {
const { pageNumber, scaleSelect, } = this.items;
const { pageNumber, scaleSelect } = this.items;
const self = this;
// The buttons within the toolbar.
for (const { element, eventName, } of this.buttons) {
element.addEventListener('click', (evt) => {
for (const { element, eventName } of this.buttons) {
element.addEventListener("click", evt => {
if (eventName !== null) {
this.eventBus.dispatch(eventName, { source: this, });
this.eventBus.dispatch(eventName, { source: this });
}
});
}
// The non-button elements within the toolbar.
pageNumber.addEventListener('click', function() {
pageNumber.addEventListener("click", function() {
this.select();
});
pageNumber.addEventListener('change', function() {
self.eventBus.dispatch('pagenumberchanged', {
pageNumber.addEventListener("change", function() {
self.eventBus.dispatch("pagenumberchanged", {
source: self,
value: this.value,
});
});
scaleSelect.addEventListener('change', function() {
if (this.value === 'custom') {
scaleSelect.addEventListener("change", function() {
if (this.value === "custom") {
return;
}
self.eventBus.dispatch('scalechanged', {
self.eventBus.dispatch("scalechanged", {
source: self,
value: this.value,
});
@ -152,7 +159,7 @@ class Toolbar {
// Suppress context menus for some controls.
scaleSelect.oncontextmenu = noContextMenuHandler;
this.eventBus.on('localized', () => {
this.eventBus.on("localized", () => {
this._wasLocalized = true;
this._adjustScaleWidth();
this._updateUIState(true);
@ -164,54 +171,61 @@ class Toolbar {
// Don't update the UI state until we localize the toolbar.
return;
}
const { pageNumber, pagesCount, pageScaleValue, pageScale, items, } = this;
const { pageNumber, pagesCount, pageScaleValue, pageScale, items } = this;
if (resetNumPages) {
if (this.hasPageLabels) {
items.pageNumber.type = 'text';
items.pageNumber.type = "text";
} else {
items.pageNumber.type = 'number';
this.l10n.get('of_pages', { pagesCount, }, 'of {{pagesCount}}').
then((msg) => {
items.numPages.textContent = msg;
});
items.pageNumber.type = "number";
this.l10n
.get("of_pages", { pagesCount }, "of {{pagesCount}}")
.then(msg => {
items.numPages.textContent = msg;
});
}
items.pageNumber.max = pagesCount;
}
if (this.hasPageLabels) {
items.pageNumber.value = this.pageLabel;
this.l10n.get('page_of_pages', { pageNumber, pagesCount, },
'({{pageNumber}} of {{pagesCount}})').then((msg) => {
items.numPages.textContent = msg;
});
this.l10n
.get(
"page_of_pages",
{ pageNumber, pagesCount },
"({{pageNumber}} of {{pagesCount}})"
)
.then(msg => {
items.numPages.textContent = msg;
});
} else {
items.pageNumber.value = pageNumber;
}
items.previous.disabled = (pageNumber <= 1);
items.next.disabled = (pageNumber >= pagesCount);
items.previous.disabled = pageNumber <= 1;
items.next.disabled = pageNumber >= pagesCount;
items.zoomOut.disabled = (pageScale <= MIN_SCALE);
items.zoomIn.disabled = (pageScale >= MAX_SCALE);
items.zoomOut.disabled = pageScale <= MIN_SCALE;
items.zoomIn.disabled = pageScale >= MAX_SCALE;
const customScale = Math.round(pageScale * 10000) / 100;
this.l10n.get('page_scale_percent', { scale: customScale, },
'{{scale}}%').then((msg) => {
let predefinedValueFound = false;
for (const option of items.scaleSelect.options) {
if (option.value !== pageScaleValue) {
option.selected = false;
continue;
this.l10n
.get("page_scale_percent", { scale: customScale }, "{{scale}}%")
.then(msg => {
let predefinedValueFound = false;
for (const option of items.scaleSelect.options) {
if (option.value !== pageScaleValue) {
option.selected = false;
continue;
}
option.selected = true;
predefinedValueFound = true;
}
option.selected = true;
predefinedValueFound = true;
}
if (!predefinedValueFound) {
items.customScaleOption.textContent = msg;
items.customScaleOption.selected = true;
}
});
if (!predefinedValueFound) {
items.customScaleOption.textContent = msg;
items.customScaleOption.selected = true;
}
});
}
updateLoadingIndicatorState(loading = false) {
@ -229,20 +243,22 @@ class Toolbar {
// Note: If the window is narrow enough that the zoom box is not
// visible, we temporarily show it to be able to adjust its width.
if (container.clientWidth === 0) {
container.setAttribute('style', 'display: inherit;');
container.setAttribute("style", "display: inherit;");
}
if (container.clientWidth > 0) {
select.setAttribute('style', 'min-width: inherit;');
select.setAttribute("style", "min-width: inherit;");
const width = select.clientWidth + SCALE_SELECT_CONTAINER_PADDING;
select.setAttribute('style', 'min-width: ' +
(width + SCALE_SELECT_PADDING) + 'px;');
container.setAttribute('style', 'min-width: ' + width + 'px; ' +
'max-width: ' + width + 'px;');
select.setAttribute(
"style",
"min-width: " + (width + SCALE_SELECT_PADDING) + "px;"
);
container.setAttribute(
"style",
"min-width: " + width + "px; " + "max-width: " + width + "px;"
);
}
});
}
}
export {
Toolbar,
};
export { Toolbar };

View file

@ -15,9 +15,9 @@
/* eslint no-var: error, prefer-const: error */
const CSS_UNITS = 96.0 / 72.0;
const DEFAULT_SCALE_VALUE = 'auto';
const DEFAULT_SCALE_VALUE = "auto";
const DEFAULT_SCALE = 1.0;
const MIN_SCALE = 0.10;
const MIN_SCALE = 0.1;
const MAX_SCALE = 10.0;
const UNKNOWN_SCALE = 0;
const MAX_AUTO_SCALE = 1.25;
@ -32,8 +32,8 @@ const PresentationModeState = {
};
const RendererType = {
CANVAS: 'canvas',
SVG: 'svg',
CANVAS: "canvas",
SVG: "svg",
};
const TextLayerMode = {
@ -62,7 +62,7 @@ function formatL10nValue(text, args) {
return text;
}
return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (all, name) => {
return (name in args ? args[name] : '{{' + name + '}}');
return name in args ? args[name] : "{{" + name + "}}";
});
}
@ -72,18 +72,18 @@ function formatL10nValue(text, args) {
*/
const NullL10n = {
async getLanguage() {
return 'en-us';
return "en-us";
},
async getDirection() {
return 'ltr';
return "ltr";
},
async get(property, args, fallback) {
return formatL10nValue(fallback, args);
},
async translate(element) { },
async translate(element) {},
};
/**
@ -94,11 +94,13 @@ const NullL10n = {
*/
function getOutputScale(ctx) {
const devicePixelRatio = window.devicePixelRatio || 1;
const backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
const backingStoreRatio =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1;
const pixelRatio = devicePixelRatio / backingStoreRatio;
return {
sx: pixelRatio,
@ -121,15 +123,17 @@ function scrollIntoView(element, spot, skipOverflowHiddenElements = false) {
// producing the error. See also animationStarted.
let parent = element.offsetParent;
if (!parent) {
console.error('offsetParent is not set -- cannot scroll');
console.error("offsetParent is not set -- cannot scroll");
return;
}
let offsetY = element.offsetTop + element.clientTop;
let offsetX = element.offsetLeft + element.clientLeft;
while ((parent.clientHeight === parent.scrollHeight &&
parent.clientWidth === parent.scrollWidth) ||
(skipOverflowHiddenElements &&
getComputedStyle(parent).overflow === 'hidden')) {
while (
(parent.clientHeight === parent.scrollHeight &&
parent.clientWidth === parent.scrollWidth) ||
(skipOverflowHiddenElements &&
getComputedStyle(parent).overflow === "hidden")
) {
if (parent.dataset._scaleY) {
offsetY /= parent.dataset._scaleY;
offsetX /= parent.dataset._scaleX;
@ -191,7 +195,7 @@ function watchScroll(viewAreaElement, callback) {
};
let rAF = null;
viewAreaElement.addEventListener('scroll', debounceScroll, true);
viewAreaElement.addEventListener("scroll", debounceScroll, true);
return state;
}
@ -199,10 +203,10 @@ function watchScroll(viewAreaElement, callback) {
* Helper function to parse query string (e.g. ?param1=value&parm2=...).
*/
function parseQueryString(query) {
const parts = query.split('&');
const parts = query.split("&");
const params = Object.create(null);
for (let i = 0, ii = parts.length; i < ii; ++i) {
const param = parts[i].split('=');
const param = parts[i].split("=");
const key = param[0].toLowerCase();
const value = param.length > 1 ? param[1] : null;
params[decodeURIComponent(key)] = decodeURIComponent(value);
@ -264,18 +268,24 @@ function approximateFraction(x) {
const x_ = x > 1 ? xinv : x;
// a/b and c/d are neighbours in Farey sequence.
let a = 0, b = 1, c = 1, d = 1;
let a = 0,
b = 1,
c = 1,
d = 1;
// Limiting search to order 8.
while (true) {
// Generating next term in sequence (order of q).
const p = a + c, q = b + d;
const p = a + c,
q = b + d;
if (q > limit) {
break;
}
if (x_ <= p / q) {
c = p; d = q;
c = p;
d = q;
} else {
a = p; b = q;
a = p;
b = q;
}
}
let result;
@ -300,17 +310,17 @@ function roundToDivide(x, div) {
* @returns {Object} An Object containing the properties: {number} `width`
* and {number} `height`, given in inches.
*/
function getPageSizeInches({ view, userUnit, rotate, }) {
function getPageSizeInches({ view, userUnit, rotate }) {
const [x1, y1, x2, y2] = view;
// We need to take the page rotation into account as well.
const changeOrientation = rotate % 180 !== 0;
const width = (x2 - x1) / 72 * userUnit;
const height = (y2 - y1) / 72 * userUnit;
const width = ((x2 - x1) / 72) * userUnit;
const height = ((y2 - y1) / 72) * userUnit;
return {
width: (changeOrientation ? height : width),
height: (changeOrientation ? width : height),
width: changeOrientation ? height : width,
height: changeOrientation ? width : height,
};
}
@ -426,10 +436,16 @@ function backtrackBeforeAllVisibleElements(index, views, top) {
* out horizontally instead of vertically
* @returns {Object} `{ first, last, views: [{ id, x, y, view, percent }] }`
*/
function getVisibleElements(scrollEl, views, sortByVisibility = false,
horizontal = false) {
const top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
const left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;
function getVisibleElements(
scrollEl,
views,
sortByVisibility = false,
horizontal = false
) {
const top = scrollEl.scrollTop,
bottom = top + scrollEl.clientHeight;
const left = scrollEl.scrollLeft,
right = left + scrollEl.clientWidth;
// Throughout this "generic" function, comments will assume we're working with
// PDF document pages, which is the most important and complex case. In this
@ -454,22 +470,33 @@ function getVisibleElements(scrollEl, views, sortByVisibility = false,
return elementRight > left;
}
const visible = [], numViews = views.length;
let firstVisibleElementInd = numViews === 0 ? 0 :
binarySearchFirstItem(views, horizontal ? isElementRightAfterViewLeft :
isElementBottomAfterViewTop);
const visible = [],
numViews = views.length;
let firstVisibleElementInd =
numViews === 0
? 0
: binarySearchFirstItem(
views,
horizontal ? isElementRightAfterViewLeft : isElementBottomAfterViewTop
);
// Please note the return value of the `binarySearchFirstItem` function when
// no valid element is found (hence the `firstVisibleElementInd` check below).
if (firstVisibleElementInd > 0 && firstVisibleElementInd < numViews &&
!horizontal) {
if (
firstVisibleElementInd > 0 &&
firstVisibleElementInd < numViews &&
!horizontal
) {
// In wrapped scrolling (or vertical scrolling with spreads), with some page
// sizes, isElementBottomAfterViewTop doesn't satisfy the binary search
// condition: there can be pages with bottoms above the view top between
// pages with bottoms below. This function detects and corrects that error;
// see it for more comments.
firstVisibleElementInd =
backtrackBeforeAllVisibleElements(firstVisibleElementInd, views, top);
firstVisibleElementInd = backtrackBeforeAllVisibleElements(
firstVisibleElementInd,
views,
top
);
}
// lastEdge acts as a cutoff for us to stop looping, because we know all
@ -483,10 +510,12 @@ function getVisibleElements(scrollEl, views, sortByVisibility = false,
let lastEdge = horizontal ? right : -1;
for (let i = firstVisibleElementInd; i < numViews; i++) {
const view = views[i], element = view.div;
const view = views[i],
element = view.div;
const currentWidth = element.offsetLeft + element.clientLeft;
const currentHeight = element.offsetTop + element.clientTop;
const viewWidth = element.clientWidth, viewHeight = element.clientHeight;
const viewWidth = element.clientWidth,
viewHeight = element.clientHeight;
const viewRight = currentWidth + viewWidth;
const viewBottom = currentHeight + viewHeight;
@ -502,17 +531,24 @@ function getVisibleElements(scrollEl, views, sortByVisibility = false,
break;
}
if (viewBottom <= top || currentHeight >= bottom ||
viewRight <= left || currentWidth >= right) {
if (
viewBottom <= top ||
currentHeight >= bottom ||
viewRight <= left ||
currentWidth >= right
) {
continue;
}
const hiddenHeight = Math.max(0, top - currentHeight) +
Math.max(0, viewBottom - bottom);
const hiddenWidth = Math.max(0, left - currentWidth) +
Math.max(0, viewRight - right);
const percent = ((viewHeight - hiddenHeight) * (viewWidth - hiddenWidth) *
100 / viewHeight / viewWidth) | 0;
const hiddenHeight =
Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom);
const hiddenWidth =
Math.max(0, left - currentWidth) + Math.max(0, viewRight - right);
const percent =
(((viewHeight - hiddenHeight) * (viewWidth - hiddenWidth) * 100) /
viewHeight /
viewWidth) |
0;
visible.push({
id: view.id,
x: currentWidth,
@ -522,7 +558,8 @@ function getVisibleElements(scrollEl, views, sortByVisibility = false,
});
}
const first = visible[0], last = visible[visible.length - 1];
const first = visible[0],
last = visible[visible.length - 1];
if (sortByVisibility) {
visible.sort(function(a, b) {
@ -533,7 +570,7 @@ function getVisibleElements(scrollEl, views, sortByVisibility = false,
return a.id - b.id; // ensure stability
});
}
return { first, last, views: visible, };
return { first, last, views: visible };
}
/**
@ -546,10 +583,10 @@ function noContextMenuHandler(evt) {
function isDataSchema(url) {
let i = 0;
const ii = url.length;
while (i < ii && url[i].trim() === '') {
while (i < ii && url[i].trim() === "") {
i++;
}
return url.substring(i, i + 5).toLowerCase() === 'data:';
return url.substring(i, i + 5).toLowerCase() === "data:";
}
/**
@ -559,13 +596,15 @@ function isDataSchema(url) {
* unknown, or the protocol is unsupported.
* @returns {string} Guessed PDF filename.
*/
function getPDFFileNameFromURL(url, defaultFilename = 'document.pdf') {
if (typeof url !== 'string') {
function getPDFFileNameFromURL(url, defaultFilename = "document.pdf") {
if (typeof url !== "string") {
return defaultFilename;
}
if (isDataSchema(url)) {
console.warn('getPDFFileNameFromURL: ' +
'ignoring "data:" URL for performance reasons.');
console.warn(
"getPDFFileNameFromURL: " +
'ignoring "data:" URL for performance reasons.'
);
return defaultFilename;
}
const reURI = /^(?:(?:[^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
@ -573,17 +612,20 @@ function getPDFFileNameFromURL(url, defaultFilename = 'document.pdf') {
// Pattern to get last matching NAME.pdf
const reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
const splitURI = reURI.exec(url);
let suggestedFilename = reFilename.exec(splitURI[1]) ||
reFilename.exec(splitURI[2]) ||
reFilename.exec(splitURI[3]);
let suggestedFilename =
reFilename.exec(splitURI[1]) ||
reFilename.exec(splitURI[2]) ||
reFilename.exec(splitURI[3]);
if (suggestedFilename) {
suggestedFilename = suggestedFilename[0];
if (suggestedFilename.includes('%')) {
if (suggestedFilename.includes("%")) {
// URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
try {
suggestedFilename =
reFilename.exec(decodeURIComponent(suggestedFilename))[0];
} catch (ex) { // Possible (extremely rare) errors:
suggestedFilename = reFilename.exec(
decodeURIComponent(suggestedFilename)
)[0];
} catch (ex) {
// Possible (extremely rare) errors:
// URIError "Malformed URI", e.g. for "%AA.pdf"
// TypeError "null has no properties", e.g. for "%2F.pdf"
}
@ -619,13 +661,19 @@ function isValidRotation(angle) {
}
function isValidScrollMode(mode) {
return (Number.isInteger(mode) && Object.values(ScrollMode).includes(mode) &&
mode !== ScrollMode.UNKNOWN);
return (
Number.isInteger(mode) &&
Object.values(ScrollMode).includes(mode) &&
mode !== ScrollMode.UNKNOWN
);
}
function isValidSpreadMode(mode) {
return (Number.isInteger(mode) && Object.values(SpreadMode).includes(mode) &&
mode !== SpreadMode.UNKNOWN);
return (
Number.isInteger(mode) &&
Object.values(SpreadMode).includes(mode) &&
mode !== SpreadMode.UNKNOWN
);
}
function isPortraitOrientation(size) {
@ -633,8 +681,8 @@ function isPortraitOrientation(size) {
}
const WaitOnType = {
EVENT: 'event',
TIMEOUT: 'timeout',
EVENT: "event",
TIMEOUT: "timeout",
};
/**
@ -654,11 +702,14 @@ const WaitOnType = {
* @param {WaitOnEventOrTimeoutParameters}
* @returns {Promise} A promise that is resolved with a {WaitOnType} value.
*/
function waitOnEventOrTimeout({ target, name, delay = 0, }) {
function waitOnEventOrTimeout({ target, name, delay = 0 }) {
return new Promise(function(resolve, reject) {
if (typeof target !== 'object' || !(name && typeof name === 'string') ||
!(Number.isInteger(delay) && delay >= 0)) {
throw new Error('waitOnEventOrTimeout - invalid parameters.');
if (
typeof target !== "object" ||
!(name && typeof name === "string") ||
!(Number.isInteger(delay) && delay >= 0)
) {
throw new Error("waitOnEventOrTimeout - invalid parameters.");
}
function handler(type) {
@ -689,9 +740,12 @@ function waitOnEventOrTimeout({ target, name, delay = 0, }) {
/**
* Promise that is resolved when DOM window becomes visible.
*/
const animationStarted = new Promise(function (resolve) {
if ((typeof PDFJSDev !== 'undefined' && PDFJSDev.test('LIB')) &&
typeof window === 'undefined') {
const animationStarted = new Promise(function(resolve) {
if (
typeof PDFJSDev !== "undefined" &&
PDFJSDev.test("LIB") &&
typeof window === "undefined"
) {
// Prevent "ReferenceError: window is not defined" errors when running the
// unit-tests in Node.js/Travis.
setTimeout(resolve, 20);
@ -706,7 +760,7 @@ const animationStarted = new Promise(function (resolve) {
* used.
*/
class EventBus {
constructor({ dispatchToDOM = false, } = {}) {
constructor({ dispatchToDOM = false } = {}) {
this._listeners = Object.create(null);
this._dispatchToDOM = dispatchToDOM === true;
}
@ -723,7 +777,7 @@ class EventBus {
off(eventName, listener) {
const eventListeners = this._listeners[eventName];
let i;
if (!eventListeners || ((i = eventListeners.indexOf(listener)) < 0)) {
if (!eventListeners || (i = eventListeners.indexOf(listener)) < 0) {
return;
}
eventListeners.splice(i, 1);
@ -742,7 +796,7 @@ class EventBus {
const args = Array.prototype.slice.call(arguments, 1);
// Making copy of the listeners array in case if it will be modified
// during dispatch.
eventListeners.slice(0).forEach(function (listener) {
eventListeners.slice(0).forEach(function(listener) {
listener.apply(null, args);
});
if (this._dispatchToDOM) {
@ -759,7 +813,7 @@ class EventBus {
const obj = args[0];
for (const key in obj) {
const value = obj[key];
if (key === 'source') {
if (key === "source") {
if (value === window || value === document) {
return; // No need to re-dispatch (already) global events.
}
@ -768,7 +822,7 @@ class EventBus {
details[key] = value;
}
}
const event = document.createEvent('CustomEvent');
const event = document.createEvent("CustomEvent");
event.initCustomEvent(eventName, true, true, details);
document.dispatchEvent(event);
}
@ -777,7 +831,7 @@ class EventBus {
let globalEventBus = null;
function getGlobalEventBus(dispatchToDOM = false) {
if (!globalEventBus) {
globalEventBus = new EventBus({ dispatchToDOM, });
globalEventBus = new EventBus({ dispatchToDOM });
}
return globalEventBus;
}
@ -787,18 +841,18 @@ function clamp(v, min, max) {
}
class ProgressBar {
constructor(id, { height, width, units, } = {}) {
constructor(id, { height, width, units } = {}) {
this.visible = true;
// Fetch the sub-elements for later.
this.div = document.querySelector(id + ' .progress');
this.div = document.querySelector(id + " .progress");
// Get the loading bar element, so it can be resized to fit the viewer.
this.bar = this.div.parentNode;
// Get options, with sensible defaults.
this.height = height || 100;
this.width = width || 100;
this.units = units || '%';
this.units = units || "%";
// Initialize heights.
this.div.style.height = this.height + this.units;
@ -807,13 +861,13 @@ class ProgressBar {
_updateBar() {
if (this._indeterminate) {
this.div.classList.add('indeterminate');
this.div.classList.add("indeterminate");
this.div.style.width = this.width + this.units;
return;
}
this.div.classList.remove('indeterminate');
const progressSize = this.width * this._percent / 100;
this.div.classList.remove("indeterminate");
const progressSize = (this.width * this._percent) / 100;
this.div.style.width = progressSize + this.units;
}
@ -834,8 +888,10 @@ class ProgressBar {
const container = viewer.parentNode;
const scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
if (scrollbarWidth > 0) {
this.bar.setAttribute('style', 'width: calc(100% - ' +
scrollbarWidth + 'px);');
this.bar.setAttribute(
"style",
"width: calc(100% - " + scrollbarWidth + "px);"
);
}
}
@ -844,8 +900,8 @@ class ProgressBar {
return;
}
this.visible = false;
this.bar.classList.add('hidden');
document.body.classList.remove('loadingInProgress');
this.bar.classList.add("hidden");
document.body.classList.remove("loadingInProgress");
}
show() {
@ -853,8 +909,8 @@ class ProgressBar {
return;
}
this.visible = true;
document.body.classList.add('loadingInProgress');
this.bar.classList.remove('hidden');
document.body.classList.add("loadingInProgress");
this.bar.classList.remove("hidden");
}
}
@ -863,7 +919,8 @@ class ProgressBar {
* array, preserving the order of the rest.
*/
function moveToEndOfArray(arr, condition) {
const moved = [], len = arr.length;
const moved = [],
len = arr.length;
let write = 0;
for (let read = 0; read < len; ++read) {
if (condition(arr[read])) {

View file

@ -30,9 +30,9 @@ class ViewHistory {
this.fingerprint = fingerprint;
this.cacheSize = cacheSize;
this._initializedPromise = this._readFromStorage().then((databaseStr) => {
const database = JSON.parse(databaseStr || '{}');
if (!('files' in database)) {
this._initializedPromise = this._readFromStorage().then(databaseStr => {
const database = JSON.parse(databaseStr || "{}");
if (!("files" in database)) {
database.files = [];
} else {
while (database.files.length >= this.cacheSize) {
@ -48,7 +48,7 @@ class ViewHistory {
}
}
if (index === -1) {
index = database.files.push({ fingerprint: this.fingerprint, }) - 1;
index = database.files.push({ fingerprint: this.fingerprint }) - 1;
}
this.file = database.files[index];
this.database = database;
@ -58,20 +58,24 @@ class ViewHistory {
async _writeToStorage() {
const databaseStr = JSON.stringify(this.database);
if (typeof PDFJSDev !== 'undefined' &&
PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
sessionStorage.setItem('pdfjs.history', databaseStr);
if (
typeof PDFJSDev !== "undefined" &&
PDFJSDev.test("FIREFOX || MOZCENTRAL")
) {
sessionStorage.setItem("pdfjs.history", databaseStr);
return;
}
localStorage.setItem('pdfjs.history', databaseStr);
localStorage.setItem("pdfjs.history", databaseStr);
}
async _readFromStorage() {
if (typeof PDFJSDev !== 'undefined' &&
PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
return sessionStorage.getItem('pdfjs.history');
if (
typeof PDFJSDev !== "undefined" &&
PDFJSDev.test("FIREFOX || MOZCENTRAL")
) {
return sessionStorage.getItem("pdfjs.history");
}
return localStorage.getItem('pdfjs.history');
return localStorage.getItem("pdfjs.history");
}
async set(name, val) {
@ -106,6 +110,4 @@ class ViewHistory {
}
}
export {
ViewHistory,
};
export { ViewHistory };

View file

@ -14,9 +14,9 @@
*/
/* eslint no-var: error, prefer-const: error */
'use strict';
"use strict";
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME')) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME")) {
var defaultUrl; // eslint-disable-line no-var
(function rewriteUrlClosure() {
@ -24,193 +24,195 @@ if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME')) {
// is rewritten as soon as possible.
const queryString = document.location.search.slice(1);
const m = /(^|&)file=([^&]*)/.exec(queryString);
defaultUrl = m ? decodeURIComponent(m[2]) : '';
defaultUrl = m ? decodeURIComponent(m[2]) : "";
// Example: chrome-extension://.../http://example.com/file.pdf
const humanReadableUrl = '/' + defaultUrl + location.hash;
history.replaceState(history.state, '', humanReadableUrl);
const humanReadableUrl = "/" + defaultUrl + location.hash;
history.replaceState(history.state, "", humanReadableUrl);
if (top === window) {
// eslint-disable-next-line no-undef
chrome.runtime.sendMessage('showPageAction');
chrome.runtime.sendMessage("showPageAction");
}
})();
}
let pdfjsWebApp, pdfjsWebAppOptions;
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('PRODUCTION')) {
pdfjsWebApp = require('./app.js');
pdfjsWebAppOptions = require('./app_options.js');
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("PRODUCTION")) {
pdfjsWebApp = require("./app.js");
pdfjsWebAppOptions = require("./app_options.js");
}
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
require('./firefoxcom.js');
require('./firefox_print_service.js');
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("FIREFOX || MOZCENTRAL")) {
require("./firefoxcom.js");
require("./firefox_print_service.js");
}
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('GENERIC')) {
require('./genericcom.js');
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
require("./genericcom.js");
}
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME')) {
require('./chromecom.js');
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME")) {
require("./chromecom.js");
}
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME || GENERIC')) {
require('./pdf_print_service.js');
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME || GENERIC")) {
require("./pdf_print_service.js");
}
function getViewerConfiguration() {
return {
appContainer: document.body,
mainContainer: document.getElementById('viewerContainer'),
viewerContainer: document.getElementById('viewer'),
mainContainer: document.getElementById("viewerContainer"),
viewerContainer: document.getElementById("viewer"),
eventBus: null, // Using global event bus with (optional) DOM events.
toolbar: {
container: document.getElementById('toolbarViewer'),
numPages: document.getElementById('numPages'),
pageNumber: document.getElementById('pageNumber'),
scaleSelectContainer: document.getElementById('scaleSelectContainer'),
scaleSelect: document.getElementById('scaleSelect'),
customScaleOption: document.getElementById('customScaleOption'),
previous: document.getElementById('previous'),
next: document.getElementById('next'),
zoomIn: document.getElementById('zoomIn'),
zoomOut: document.getElementById('zoomOut'),
viewFind: document.getElementById('viewFind'),
openFile: document.getElementById('openFile'),
print: document.getElementById('print'),
presentationModeButton: document.getElementById('presentationMode'),
download: document.getElementById('download'),
viewBookmark: document.getElementById('viewBookmark'),
container: document.getElementById("toolbarViewer"),
numPages: document.getElementById("numPages"),
pageNumber: document.getElementById("pageNumber"),
scaleSelectContainer: document.getElementById("scaleSelectContainer"),
scaleSelect: document.getElementById("scaleSelect"),
customScaleOption: document.getElementById("customScaleOption"),
previous: document.getElementById("previous"),
next: document.getElementById("next"),
zoomIn: document.getElementById("zoomIn"),
zoomOut: document.getElementById("zoomOut"),
viewFind: document.getElementById("viewFind"),
openFile: document.getElementById("openFile"),
print: document.getElementById("print"),
presentationModeButton: document.getElementById("presentationMode"),
download: document.getElementById("download"),
viewBookmark: document.getElementById("viewBookmark"),
},
secondaryToolbar: {
toolbar: document.getElementById('secondaryToolbar'),
toggleButton: document.getElementById('secondaryToolbarToggle'),
toolbarButtonContainer:
document.getElementById('secondaryToolbarButtonContainer'),
presentationModeButton:
document.getElementById('secondaryPresentationMode'),
openFileButton: document.getElementById('secondaryOpenFile'),
printButton: document.getElementById('secondaryPrint'),
downloadButton: document.getElementById('secondaryDownload'),
viewBookmarkButton: document.getElementById('secondaryViewBookmark'),
firstPageButton: document.getElementById('firstPage'),
lastPageButton: document.getElementById('lastPage'),
pageRotateCwButton: document.getElementById('pageRotateCw'),
pageRotateCcwButton: document.getElementById('pageRotateCcw'),
cursorSelectToolButton: document.getElementById('cursorSelectTool'),
cursorHandToolButton: document.getElementById('cursorHandTool'),
scrollVerticalButton: document.getElementById('scrollVertical'),
scrollHorizontalButton: document.getElementById('scrollHorizontal'),
scrollWrappedButton: document.getElementById('scrollWrapped'),
spreadNoneButton: document.getElementById('spreadNone'),
spreadOddButton: document.getElementById('spreadOdd'),
spreadEvenButton: document.getElementById('spreadEven'),
documentPropertiesButton: document.getElementById('documentProperties'),
toolbar: document.getElementById("secondaryToolbar"),
toggleButton: document.getElementById("secondaryToolbarToggle"),
toolbarButtonContainer: document.getElementById(
"secondaryToolbarButtonContainer"
),
presentationModeButton: document.getElementById(
"secondaryPresentationMode"
),
openFileButton: document.getElementById("secondaryOpenFile"),
printButton: document.getElementById("secondaryPrint"),
downloadButton: document.getElementById("secondaryDownload"),
viewBookmarkButton: document.getElementById("secondaryViewBookmark"),
firstPageButton: document.getElementById("firstPage"),
lastPageButton: document.getElementById("lastPage"),
pageRotateCwButton: document.getElementById("pageRotateCw"),
pageRotateCcwButton: document.getElementById("pageRotateCcw"),
cursorSelectToolButton: document.getElementById("cursorSelectTool"),
cursorHandToolButton: document.getElementById("cursorHandTool"),
scrollVerticalButton: document.getElementById("scrollVertical"),
scrollHorizontalButton: document.getElementById("scrollHorizontal"),
scrollWrappedButton: document.getElementById("scrollWrapped"),
spreadNoneButton: document.getElementById("spreadNone"),
spreadOddButton: document.getElementById("spreadOdd"),
spreadEvenButton: document.getElementById("spreadEven"),
documentPropertiesButton: document.getElementById("documentProperties"),
},
fullscreen: {
contextFirstPage: document.getElementById('contextFirstPage'),
contextLastPage: document.getElementById('contextLastPage'),
contextPageRotateCw: document.getElementById('contextPageRotateCw'),
contextPageRotateCcw: document.getElementById('contextPageRotateCcw'),
contextFirstPage: document.getElementById("contextFirstPage"),
contextLastPage: document.getElementById("contextLastPage"),
contextPageRotateCw: document.getElementById("contextPageRotateCw"),
contextPageRotateCcw: document.getElementById("contextPageRotateCcw"),
},
sidebar: {
// Divs (and sidebar button)
outerContainer: document.getElementById('outerContainer'),
viewerContainer: document.getElementById('viewerContainer'),
toggleButton: document.getElementById('sidebarToggle'),
outerContainer: document.getElementById("outerContainer"),
viewerContainer: document.getElementById("viewerContainer"),
toggleButton: document.getElementById("sidebarToggle"),
// Buttons
thumbnailButton: document.getElementById('viewThumbnail'),
outlineButton: document.getElementById('viewOutline'),
attachmentsButton: document.getElementById('viewAttachments'),
thumbnailButton: document.getElementById("viewThumbnail"),
outlineButton: document.getElementById("viewOutline"),
attachmentsButton: document.getElementById("viewAttachments"),
// Views
thumbnailView: document.getElementById('thumbnailView'),
outlineView: document.getElementById('outlineView'),
attachmentsView: document.getElementById('attachmentsView'),
thumbnailView: document.getElementById("thumbnailView"),
outlineView: document.getElementById("outlineView"),
attachmentsView: document.getElementById("attachmentsView"),
},
sidebarResizer: {
outerContainer: document.getElementById('outerContainer'),
resizer: document.getElementById('sidebarResizer'),
outerContainer: document.getElementById("outerContainer"),
resizer: document.getElementById("sidebarResizer"),
},
findBar: {
bar: document.getElementById('findbar'),
toggleButton: document.getElementById('viewFind'),
findField: document.getElementById('findInput'),
highlightAllCheckbox: document.getElementById('findHighlightAll'),
caseSensitiveCheckbox: document.getElementById('findMatchCase'),
entireWordCheckbox: document.getElementById('findEntireWord'),
findMsg: document.getElementById('findMsg'),
findResultsCount: document.getElementById('findResultsCount'),
findPreviousButton: document.getElementById('findPrevious'),
findNextButton: document.getElementById('findNext'),
bar: document.getElementById("findbar"),
toggleButton: document.getElementById("viewFind"),
findField: document.getElementById("findInput"),
highlightAllCheckbox: document.getElementById("findHighlightAll"),
caseSensitiveCheckbox: document.getElementById("findMatchCase"),
entireWordCheckbox: document.getElementById("findEntireWord"),
findMsg: document.getElementById("findMsg"),
findResultsCount: document.getElementById("findResultsCount"),
findPreviousButton: document.getElementById("findPrevious"),
findNextButton: document.getElementById("findNext"),
},
passwordOverlay: {
overlayName: 'passwordOverlay',
container: document.getElementById('passwordOverlay'),
label: document.getElementById('passwordText'),
input: document.getElementById('password'),
submitButton: document.getElementById('passwordSubmit'),
cancelButton: document.getElementById('passwordCancel'),
overlayName: "passwordOverlay",
container: document.getElementById("passwordOverlay"),
label: document.getElementById("passwordText"),
input: document.getElementById("password"),
submitButton: document.getElementById("passwordSubmit"),
cancelButton: document.getElementById("passwordCancel"),
},
documentProperties: {
overlayName: 'documentPropertiesOverlay',
container: document.getElementById('documentPropertiesOverlay'),
closeButton: document.getElementById('documentPropertiesClose'),
overlayName: "documentPropertiesOverlay",
container: document.getElementById("documentPropertiesOverlay"),
closeButton: document.getElementById("documentPropertiesClose"),
fields: {
'fileName': document.getElementById('fileNameField'),
'fileSize': document.getElementById('fileSizeField'),
'title': document.getElementById('titleField'),
'author': document.getElementById('authorField'),
'subject': document.getElementById('subjectField'),
'keywords': document.getElementById('keywordsField'),
'creationDate': document.getElementById('creationDateField'),
'modificationDate': document.getElementById('modificationDateField'),
'creator': document.getElementById('creatorField'),
'producer': document.getElementById('producerField'),
'version': document.getElementById('versionField'),
'pageCount': document.getElementById('pageCountField'),
'pageSize': document.getElementById('pageSizeField'),
'linearized': document.getElementById('linearizedField'),
fileName: document.getElementById("fileNameField"),
fileSize: document.getElementById("fileSizeField"),
title: document.getElementById("titleField"),
author: document.getElementById("authorField"),
subject: document.getElementById("subjectField"),
keywords: document.getElementById("keywordsField"),
creationDate: document.getElementById("creationDateField"),
modificationDate: document.getElementById("modificationDateField"),
creator: document.getElementById("creatorField"),
producer: document.getElementById("producerField"),
version: document.getElementById("versionField"),
pageCount: document.getElementById("pageCountField"),
pageSize: document.getElementById("pageSizeField"),
linearized: document.getElementById("linearizedField"),
},
},
errorWrapper: {
container: document.getElementById('errorWrapper'),
errorMessage: document.getElementById('errorMessage'),
closeButton: document.getElementById('errorClose'),
errorMoreInfo: document.getElementById('errorMoreInfo'),
moreInfoButton: document.getElementById('errorShowMore'),
lessInfoButton: document.getElementById('errorShowLess'),
container: document.getElementById("errorWrapper"),
errorMessage: document.getElementById("errorMessage"),
closeButton: document.getElementById("errorClose"),
errorMoreInfo: document.getElementById("errorMoreInfo"),
moreInfoButton: document.getElementById("errorShowMore"),
lessInfoButton: document.getElementById("errorShowLess"),
},
printContainer: document.getElementById('printContainer'),
openFileInputName: 'fileInput',
debuggerScriptPath: './debugger.js',
printContainer: document.getElementById("printContainer"),
openFileInputName: "fileInput",
debuggerScriptPath: "./debugger.js",
};
}
function webViewerLoad() {
const config = getViewerConfiguration();
if (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION')) {
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")) {
Promise.all([
SystemJS.import('pdfjs-web/app'),
SystemJS.import('pdfjs-web/app_options'),
SystemJS.import('pdfjs-web/genericcom'),
SystemJS.import('pdfjs-web/pdf_print_service'),
SystemJS.import("pdfjs-web/app"),
SystemJS.import("pdfjs-web/app_options"),
SystemJS.import("pdfjs-web/genericcom"),
SystemJS.import("pdfjs-web/pdf_print_service"),
]).then(function([app, appOptions, ...otherModules]) {
window.PDFViewerApplication = app.PDFViewerApplication;
window.PDFViewerApplicationOptions = appOptions.AppOptions;
app.PDFViewerApplication.run(config);
});
} else {
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME')) {
pdfjsWebAppOptions.AppOptions.set('defaultUrl', defaultUrl);
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME")) {
pdfjsWebAppOptions.AppOptions.set("defaultUrl", defaultUrl);
}
window.PDFViewerApplication = pdfjsWebApp.PDFViewerApplication;
window.PDFViewerApplicationOptions = pdfjsWebAppOptions.AppOptions;
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('GENERIC')) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
// Give custom implementations of the default viewer a simpler way to
// set various `AppOptions`, by dispatching an event once all viewer
// files are loaded but *before* the viewer initialization has run.
const event = document.createEvent('CustomEvent');
event.initCustomEvent('webviewerloaded', true, true, {});
const event = document.createEvent("CustomEvent");
event.initCustomEvent("webviewerloaded", true, true, {});
document.dispatchEvent(event);
}
@ -218,9 +220,11 @@ function webViewerLoad() {
}
}
if (document.readyState === 'interactive' ||
document.readyState === 'complete') {
if (
document.readyState === "interactive" ||
document.readyState === "complete"
) {
webViewerLoad();
} else {
document.addEventListener('DOMContentLoaded', webViewerLoad, true);
document.addEventListener("DOMContentLoaded", webViewerLoad, true);
}

View file

@ -15,17 +15,18 @@
/* eslint no-var: error, prefer-const: error */
const compatibilityParams = Object.create(null);
if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
const userAgent =
(typeof navigator !== 'undefined' && navigator.userAgent) || '';
(typeof navigator !== "undefined" && navigator.userAgent) || "";
const platform =
(typeof navigator !== 'undefined' && navigator.platform) || '';
(typeof navigator !== "undefined" && navigator.platform) || "";
const maxTouchPoints =
(typeof navigator !== 'undefined' && navigator.maxTouchPoints) || 1;
(typeof navigator !== "undefined" && navigator.maxTouchPoints) || 1;
const isAndroid = /Android/.test(userAgent);
const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) ||
(platform === 'MacIntel' && maxTouchPoints > 1);
const isIOS =
/\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) ||
(platform === "MacIntel" && maxTouchPoints > 1);
// Limit canvas size to 5 mega-pixels on mobile.
// Support: Android, iOS