|
|
@ -15,9 +15,9 @@ |
|
|
|
* limitations under the License. |
|
|
|
* limitations under the License. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
/*jshint globalstrict: false */ |
|
|
|
/*jshint globalstrict: false */ |
|
|
|
/* globals PDFJS, PDFViewer, PDFPageView, TextLayerBuilder, |
|
|
|
/* globals PDFJS, PDFViewer, PDFPageView, TextLayerBuilder, PDFLinkService, |
|
|
|
DefaultTextLayerFactory, AnnotationsLayerBuilder, |
|
|
|
DefaultTextLayerFactory, AnnotationsLayerBuilder, PDFHistory, |
|
|
|
DefaultAnnotationsLayerFactory */ |
|
|
|
DefaultAnnotationsLayerFactory, getFileName */ |
|
|
|
|
|
|
|
|
|
|
|
// Initializing PDFJS global object (if still undefined)
|
|
|
|
// Initializing PDFJS global object (if still undefined)
|
|
|
|
if (typeof PDFJS === 'undefined') { |
|
|
|
if (typeof PDFJS === 'undefined') { |
|
|
@ -191,6 +191,21 @@ function watchScroll(viewAreaElement, callback) { |
|
|
|
return state; |
|
|
|
return state; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Helper function to parse query string (e.g. ?param1=value&parm2=...). |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
function parseQueryString(query) { |
|
|
|
|
|
|
|
var parts = query.split('&'); |
|
|
|
|
|
|
|
var params = {}; |
|
|
|
|
|
|
|
for (var i = 0, ii = parts.length; i < ii; ++i) { |
|
|
|
|
|
|
|
var param = parts[i].split('='); |
|
|
|
|
|
|
|
var key = param[0].toLowerCase(); |
|
|
|
|
|
|
|
var value = param.length > 1 ? param[1] : null; |
|
|
|
|
|
|
|
params[decodeURIComponent(key)] = decodeURIComponent(value); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return params; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Use binary search to find the index of the first item in a given array which |
|
|
|
* Use binary search to find the index of the first item in a given array which |
|
|
|
* passes a given condition. The items are expected to be sorted in the sense |
|
|
|
* passes a given condition. The items are expected to be sorted in the sense |
|
|
@ -406,6 +421,281 @@ var ProgressBar = (function ProgressBarClosure() { |
|
|
|
})(); |
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Performs navigation functions inside PDF, such as opening specified page, |
|
|
|
|
|
|
|
* or destination. |
|
|
|
|
|
|
|
* @class |
|
|
|
|
|
|
|
* @implements {IPDFLinkService} |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
var PDFLinkService = (function () { |
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @constructs PDFLinkService |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
function PDFLinkService() { |
|
|
|
|
|
|
|
this.baseUrl = null; |
|
|
|
|
|
|
|
this.pdfDocument = null; |
|
|
|
|
|
|
|
this.pdfViewer = null; |
|
|
|
|
|
|
|
this.pdfHistory = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this._pagesRefCache = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PDFLinkService.prototype = { |
|
|
|
|
|
|
|
setDocument: function PDFLinkService_setDocument(pdfDocument, baseUrl) { |
|
|
|
|
|
|
|
this.baseUrl = baseUrl; |
|
|
|
|
|
|
|
this.pdfDocument = pdfDocument; |
|
|
|
|
|
|
|
this._pagesRefCache = Object.create(null); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setViewer: function PDFLinkService_setViewer(pdfViewer) { |
|
|
|
|
|
|
|
this.pdfViewer = pdfViewer; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setHistory: function PDFLinkService_setHistory(pdfHistory) { |
|
|
|
|
|
|
|
this.pdfHistory = pdfHistory; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @returns {number} |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
get pagesCount() { |
|
|
|
|
|
|
|
return this.pdfDocument.numPages; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @returns {number} |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
get page() { |
|
|
|
|
|
|
|
return this.pdfViewer.currentPageNumber; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @param {number} value |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
set page(value) { |
|
|
|
|
|
|
|
this.pdfViewer.currentPageNumber = value; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @param dest - The PDF destination object. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
navigateTo: function PDFLinkService_navigateTo(dest) { |
|
|
|
|
|
|
|
var destString = ''; |
|
|
|
|
|
|
|
var self = this; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var goToDestination = function(destRef) { |
|
|
|
|
|
|
|
// dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
|
|
|
|
|
|
|
|
var pageNumber = destRef instanceof Object ? |
|
|
|
|
|
|
|
self._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] : |
|
|
|
|
|
|
|
(destRef + 1); |
|
|
|
|
|
|
|
if (pageNumber) { |
|
|
|
|
|
|
|
if (pageNumber > self.pagesCount) { |
|
|
|
|
|
|
|
pageNumber = self.pagesCount; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
self.pdfViewer.scrollPageIntoView(pageNumber, dest); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update the browsing history.
|
|
|
|
|
|
|
|
self.pdfHistory.push({ |
|
|
|
|
|
|
|
dest: dest, |
|
|
|
|
|
|
|
hash: destString, |
|
|
|
|
|
|
|
page: pageNumber |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) { |
|
|
|
|
|
|
|
var pageNum = pageIndex + 1; |
|
|
|
|
|
|
|
var cacheKey = destRef.num + ' ' + destRef.gen + ' R'; |
|
|
|
|
|
|
|
self._pagesRefCache[cacheKey] = pageNum; |
|
|
|
|
|
|
|
goToDestination(destRef); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var destinationPromise; |
|
|
|
|
|
|
|
if (typeof dest === 'string') { |
|
|
|
|
|
|
|
destString = dest; |
|
|
|
|
|
|
|
destinationPromise = this.pdfDocument.getDestination(dest); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
destinationPromise = Promise.resolve(dest); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
destinationPromise.then(function(destination) { |
|
|
|
|
|
|
|
dest = destination; |
|
|
|
|
|
|
|
if (!(destination instanceof Array)) { |
|
|
|
|
|
|
|
return; // invalid destination
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
goToDestination(destination[0]); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @param dest - The PDF destination object. |
|
|
|
|
|
|
|
* @returns {string} The hyperlink to the PDF object. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
getDestinationHash: function PDFLinkService_getDestinationHash(dest) { |
|
|
|
|
|
|
|
if (typeof dest === 'string') { |
|
|
|
|
|
|
|
return this.getAnchorUrl('#' + escape(dest)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (dest instanceof Array) { |
|
|
|
|
|
|
|
var destRef = dest[0]; // see navigateTo method for dest format
|
|
|
|
|
|
|
|
var pageNumber = destRef instanceof Object ? |
|
|
|
|
|
|
|
this._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] : |
|
|
|
|
|
|
|
(destRef + 1); |
|
|
|
|
|
|
|
if (pageNumber) { |
|
|
|
|
|
|
|
var pdfOpenParams = this.getAnchorUrl('#page=' + pageNumber); |
|
|
|
|
|
|
|
var destKind = dest[1]; |
|
|
|
|
|
|
|
if (typeof destKind === 'object' && 'name' in destKind && |
|
|
|
|
|
|
|
destKind.name === 'XYZ') { |
|
|
|
|
|
|
|
var scale = (dest[4] || this.pdfViewer.currentScaleValue); |
|
|
|
|
|
|
|
var scaleNumber = parseFloat(scale); |
|
|
|
|
|
|
|
if (scaleNumber) { |
|
|
|
|
|
|
|
scale = scaleNumber * 100; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
pdfOpenParams += '&zoom=' + scale; |
|
|
|
|
|
|
|
if (dest[2] || dest[3]) { |
|
|
|
|
|
|
|
pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return pdfOpenParams; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return ''; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Prefix the full url on anchor links to make sure that links are resolved |
|
|
|
|
|
|
|
* relative to the current URL instead of the one defined in <base href>. |
|
|
|
|
|
|
|
* @param {String} anchor The anchor hash, including the #. |
|
|
|
|
|
|
|
* @returns {string} The hyperlink to the PDF object. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
getAnchorUrl: function PDFLinkService_getAnchorUrl(anchor) { |
|
|
|
|
|
|
|
return (this.baseUrl || '') + anchor; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @param {string} hash |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
setHash: function PDFLinkService_setHash(hash) { |
|
|
|
|
|
|
|
if (hash.indexOf('=') >= 0) { |
|
|
|
|
|
|
|
var params = parseQueryString(hash); |
|
|
|
|
|
|
|
// borrowing syntax from "Parameters for Opening PDF Files"
|
|
|
|
|
|
|
|
if ('nameddest' in params) { |
|
|
|
|
|
|
|
this.pdfHistory.updateNextHashParam(params.nameddest); |
|
|
|
|
|
|
|
this.navigateTo(params.nameddest); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var pageNumber, dest; |
|
|
|
|
|
|
|
if ('page' in params) { |
|
|
|
|
|
|
|
pageNumber = (params.page | 0) || 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if ('zoom' in params) { |
|
|
|
|
|
|
|
// Build the destination array.
|
|
|
|
|
|
|
|
var zoomArgs = params.zoom.split(','); // scale,left,top
|
|
|
|
|
|
|
|
var zoomArg = zoomArgs[0]; |
|
|
|
|
|
|
|
var zoomArgNumber = parseFloat(zoomArg); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (zoomArg.indexOf('Fit') === -1) { |
|
|
|
|
|
|
|
// 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)]; |
|
|
|
|
|
|
|
} 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 (zoomArgs.length !== 5) { |
|
|
|
|
|
|
|
console.error('pdfViewSetHash: ' + |
|
|
|
|
|
|
|
'Not enough parameters for \'FitR\'.'); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
dest = [null, { name: zoomArg }, |
|
|
|
|
|
|
|
(zoomArgs[1] | 0), (zoomArgs[2] | 0), |
|
|
|
|
|
|
|
(zoomArgs[3] | 0), (zoomArgs[4] | 0)]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
console.error('pdfViewSetHash: \'' + zoomArg + |
|
|
|
|
|
|
|
'\' is not a valid zoom value.'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (dest) { |
|
|
|
|
|
|
|
this.pdfViewer.scrollPageIntoView(pageNumber || this.page, dest); |
|
|
|
|
|
|
|
} else if (pageNumber) { |
|
|
|
|
|
|
|
this.page = pageNumber; // simple page
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if ('pagemode' in params) { |
|
|
|
|
|
|
|
if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' || |
|
|
|
|
|
|
|
params.pagemode === 'attachments') { |
|
|
|
|
|
|
|
this.switchSidebarView((params.pagemode === 'bookmarks' ? |
|
|
|
|
|
|
|
'outline' : params.pagemode), true); |
|
|
|
|
|
|
|
} else if (params.pagemode === 'none' && this.sidebarOpen) { |
|
|
|
|
|
|
|
document.getElementById('sidebarToggle').click(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if (/^\d+$/.test(hash)) { // page number
|
|
|
|
|
|
|
|
this.page = hash; |
|
|
|
|
|
|
|
} else { // named destination
|
|
|
|
|
|
|
|
this.pdfHistory.updateNextHashParam(unescape(hash)); |
|
|
|
|
|
|
|
this.navigateTo(unescape(hash)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @param {string} action |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
executeNamedAction: function PDFLinkService_executeNamedAction(action) { |
|
|
|
|
|
|
|
// See PDF reference, table 8.45 - Named action
|
|
|
|
|
|
|
|
switch (action) { |
|
|
|
|
|
|
|
case 'GoBack': |
|
|
|
|
|
|
|
this.pdfHistory.back(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'GoForward': |
|
|
|
|
|
|
|
this.pdfHistory.forward(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'NextPage': |
|
|
|
|
|
|
|
this.page++; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'PrevPage': |
|
|
|
|
|
|
|
this.page--; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'LastPage': |
|
|
|
|
|
|
|
this.page = this.pagesCount; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'FirstPage': |
|
|
|
|
|
|
|
this.page = 1; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
break; // No action according to spec
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var event = document.createEvent('CustomEvent'); |
|
|
|
|
|
|
|
event.initCustomEvent('namedaction', true, true, { |
|
|
|
|
|
|
|
action: action |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
this.pdfViewer.container.dispatchEvent(event); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @param {number} pageNum - page number. |
|
|
|
|
|
|
|
* @param {Object} pageRef - reference to the page. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
cachePageRef: function PDFLinkService_cachePageRef(pageNum, pageRef) { |
|
|
|
|
|
|
|
var refStr = pageRef.num + ' ' + pageRef.gen + ' R'; |
|
|
|
|
|
|
|
this._pagesRefCache[refStr] = pageNum; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return PDFLinkService; |
|
|
|
|
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var PresentationModeState = { |
|
|
|
var PresentationModeState = { |
|
|
|
UNKNOWN: 0, |
|
|
|
UNKNOWN: 0, |
|
|
|
NORMAL: 1, |
|
|
|
NORMAL: 1, |
|
|
@ -1843,7 +2133,6 @@ var PDFViewer = (function pdfViewer() { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var pagesCount = pdfDocument.numPages; |
|
|
|
var pagesCount = pdfDocument.numPages; |
|
|
|
var pagesRefMap = this.pagesRefMap = {}; |
|
|
|
|
|
|
|
var self = this; |
|
|
|
var self = this; |
|
|
|
|
|
|
|
|
|
|
|
var resolvePagesPromise; |
|
|
|
var resolvePagesPromise; |
|
|
@ -1908,6 +2197,8 @@ var PDFViewer = (function pdfViewer() { |
|
|
|
this._pages.push(pageView); |
|
|
|
this._pages.push(pageView); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var linkService = this.linkService; |
|
|
|
|
|
|
|
|
|
|
|
// Fetch all the pages since the viewport is needed before printing
|
|
|
|
// Fetch all the pages since the viewport is needed before printing
|
|
|
|
// starts to create the correct size canvas. Wait until one page is
|
|
|
|
// starts to create the correct size canvas. Wait until one page is
|
|
|
|
// rendered so we don't tie up too many resources early on.
|
|
|
|
// rendered so we don't tie up too many resources early on.
|
|
|
@ -1920,8 +2211,7 @@ var PDFViewer = (function pdfViewer() { |
|
|
|
if (!pageView.pdfPage) { |
|
|
|
if (!pageView.pdfPage) { |
|
|
|
pageView.setPdfPage(pdfPage); |
|
|
|
pageView.setPdfPage(pdfPage); |
|
|
|
} |
|
|
|
} |
|
|
|
var refStr = pdfPage.ref.num + ' ' + pdfPage.ref.gen + ' R'; |
|
|
|
linkService.cachePageRef(pageNum, pdfPage.ref); |
|
|
|
pagesRefMap[refStr] = pageNum; |
|
|
|
|
|
|
|
getPagesLeft--; |
|
|
|
getPagesLeft--; |
|
|
|
if (!getPagesLeft) { |
|
|
|
if (!getPagesLeft) { |
|
|
|
resolvePagesPromise(); |
|
|
|
resolvePagesPromise(); |
|
|
@ -2408,16 +2698,389 @@ var SimpleLinkService = (function SimpleLinkServiceClosure() { |
|
|
|
* @param {string} action |
|
|
|
* @param {string} action |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
executeNamedAction: function (action) {}, |
|
|
|
executeNamedAction: function (action) {}, |
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @param {number} pageNum - page number. |
|
|
|
|
|
|
|
* @param {Object} pageRef - reference to the page. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
cachePageRef: function (pageNum, pageRef) {} |
|
|
|
}; |
|
|
|
}; |
|
|
|
return SimpleLinkService; |
|
|
|
return SimpleLinkService; |
|
|
|
})(); |
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var PDFHistory = (function () { |
|
|
|
|
|
|
|
function PDFHistory(options) { |
|
|
|
|
|
|
|
this.linkService = options.linkService; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.initialized = false; |
|
|
|
|
|
|
|
this.initialDestination = null; |
|
|
|
|
|
|
|
this.initialBookmark = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PDFHistory.prototype = { |
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @param {string} fingerprint |
|
|
|
|
|
|
|
* @param {IPDFLinkService} linkService |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
initialize: function pdfHistoryInitialize(fingerprint) { |
|
|
|
|
|
|
|
this.initialized = true; |
|
|
|
|
|
|
|
this.reInitialized = false; |
|
|
|
|
|
|
|
this.allowHashChange = true; |
|
|
|
|
|
|
|
this.historyUnlocked = true; |
|
|
|
|
|
|
|
this.isViewerInPresentationMode = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.previousHash = window.location.hash.substring(1); |
|
|
|
|
|
|
|
this.currentBookmark = ''; |
|
|
|
|
|
|
|
this.currentPage = 0; |
|
|
|
|
|
|
|
this.updatePreviousBookmark = false; |
|
|
|
|
|
|
|
this.previousBookmark = ''; |
|
|
|
|
|
|
|
this.previousPage = 0; |
|
|
|
|
|
|
|
this.nextHashParam = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.fingerprint = fingerprint; |
|
|
|
|
|
|
|
this.currentUid = this.uid = 0; |
|
|
|
|
|
|
|
this.current = {}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var state = window.history.state; |
|
|
|
|
|
|
|
if (this._isStateObjectDefined(state)) { |
|
|
|
|
|
|
|
// This corresponds to navigating back to the document
|
|
|
|
|
|
|
|
// from another page in the browser history.
|
|
|
|
|
|
|
|
if (state.target.dest) { |
|
|
|
|
|
|
|
this.initialDestination = state.target.dest; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.initialBookmark = state.target.hash; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.currentUid = state.uid; |
|
|
|
|
|
|
|
this.uid = state.uid + 1; |
|
|
|
|
|
|
|
this.current = state.target; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// This corresponds to the loading of a new document.
|
|
|
|
|
|
|
|
if (state && state.fingerprint && |
|
|
|
|
|
|
|
this.fingerprint !== state.fingerprint) { |
|
|
|
|
|
|
|
// Reinitialize the browsing history when a new document
|
|
|
|
|
|
|
|
// is opened in the web viewer.
|
|
|
|
|
|
|
|
this.reInitialized = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this._pushOrReplaceState({fingerprint: this.fingerprint}, true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var self = this; |
|
|
|
|
|
|
|
window.addEventListener('popstate', function pdfHistoryPopstate(evt) { |
|
|
|
|
|
|
|
evt.preventDefault(); |
|
|
|
|
|
|
|
evt.stopPropagation(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!self.historyUnlocked) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (evt.state) { |
|
|
|
|
|
|
|
// Move back/forward in the history.
|
|
|
|
|
|
|
|
self._goTo(evt.state); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// Handle the user modifying the hash of a loaded document.
|
|
|
|
|
|
|
|
self.previousHash = window.location.hash.substring(1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If the history is empty when the hash changes,
|
|
|
|
|
|
|
|
// update the previous entry in the browser history.
|
|
|
|
|
|
|
|
if (self.uid === 0) { |
|
|
|
|
|
|
|
var previousParams = (self.previousHash && self.currentBookmark && |
|
|
|
|
|
|
|
self.previousHash !== self.currentBookmark) ? |
|
|
|
|
|
|
|
{hash: self.currentBookmark, page: self.currentPage} : |
|
|
|
|
|
|
|
{page: 1}; |
|
|
|
|
|
|
|
self.historyUnlocked = false; |
|
|
|
|
|
|
|
self.allowHashChange = false; |
|
|
|
|
|
|
|
window.history.back(); |
|
|
|
|
|
|
|
self._pushToHistory(previousParams, false, true); |
|
|
|
|
|
|
|
window.history.forward(); |
|
|
|
|
|
|
|
self.historyUnlocked = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
self._pushToHistory({hash: self.previousHash}, false, true); |
|
|
|
|
|
|
|
self._updatePreviousBookmark(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function pdfHistoryBeforeUnload() { |
|
|
|
|
|
|
|
var previousParams = self._getPreviousParams(null, true); |
|
|
|
|
|
|
|
if (previousParams) { |
|
|
|
|
|
|
|
var replacePrevious = (!self.current.dest && |
|
|
|
|
|
|
|
self.current.hash !== self.previousHash); |
|
|
|
|
|
|
|
self._pushToHistory(previousParams, false, replacePrevious); |
|
|
|
|
|
|
|
self._updatePreviousBookmark(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Remove the event listener when navigating away from the document,
|
|
|
|
|
|
|
|
// since 'beforeunload' prevents Firefox from caching the document.
|
|
|
|
|
|
|
|
window.removeEventListener('beforeunload', pdfHistoryBeforeUnload, |
|
|
|
|
|
|
|
false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('pageshow', function pdfHistoryPageShow(evt) { |
|
|
|
|
|
|
|
// If the entire viewer (including the PDF file) is cached in
|
|
|
|
|
|
|
|
// the browser, we need to reattach the 'beforeunload' event listener
|
|
|
|
|
|
|
|
// since the 'DOMContentLoaded' event is not fired on 'pageshow'.
|
|
|
|
|
|
|
|
window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false); |
|
|
|
|
|
|
|
}, false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('presentationmodechanged', function(e) { |
|
|
|
|
|
|
|
self.isViewerInPresentationMode = !!e.detail.active; |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_isStateObjectDefined: function pdfHistory_isStateObjectDefined(state) { |
|
|
|
|
|
|
|
return (state && state.uid >= 0 && |
|
|
|
|
|
|
|
state.fingerprint && this.fingerprint === state.fingerprint && |
|
|
|
|
|
|
|
state.target && state.target.hash) ? true : false; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_pushOrReplaceState: function pdfHistory_pushOrReplaceState(stateObj, |
|
|
|
|
|
|
|
replace) { |
|
|
|
|
|
|
|
if (replace) { |
|
|
|
|
|
|
|
window.history.replaceState(stateObj, ''); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
window.history.pushState(stateObj, ''); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get isHashChangeUnlocked() { |
|
|
|
|
|
|
|
if (!this.initialized) { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// If the current hash changes when moving back/forward in the history,
|
|
|
|
|
|
|
|
// this will trigger a 'popstate' event *as well* as a 'hashchange' event.
|
|
|
|
|
|
|
|
// Since the hash generally won't correspond to the exact the position
|
|
|
|
|
|
|
|
// stored in the history's state object, triggering the 'hashchange' event
|
|
|
|
|
|
|
|
// can thus corrupt the browser history.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// When the hash changes during a 'popstate' event, we *only* prevent the
|
|
|
|
|
|
|
|
// first 'hashchange' event and immediately reset allowHashChange.
|
|
|
|
|
|
|
|
// If it is not reset, the user would not be able to change the hash.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var temp = this.allowHashChange; |
|
|
|
|
|
|
|
this.allowHashChange = true; |
|
|
|
|
|
|
|
return temp; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_updatePreviousBookmark: function pdfHistory_updatePreviousBookmark() { |
|
|
|
|
|
|
|
if (this.updatePreviousBookmark && |
|
|
|
|
|
|
|
this.currentBookmark && this.currentPage) { |
|
|
|
|
|
|
|
this.previousBookmark = this.currentBookmark; |
|
|
|
|
|
|
|
this.previousPage = this.currentPage; |
|
|
|
|
|
|
|
this.updatePreviousBookmark = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateCurrentBookmark: function pdfHistoryUpdateCurrentBookmark(bookmark, |
|
|
|
|
|
|
|
pageNum) { |
|
|
|
|
|
|
|
if (this.initialized) { |
|
|
|
|
|
|
|
this.currentBookmark = bookmark.substring(1); |
|
|
|
|
|
|
|
this.currentPage = pageNum | 0; |
|
|
|
|
|
|
|
this._updatePreviousBookmark(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateNextHashParam: function pdfHistoryUpdateNextHashParam(param) { |
|
|
|
|
|
|
|
if (this.initialized) { |
|
|
|
|
|
|
|
this.nextHashParam = param; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
push: function pdfHistoryPush(params, isInitialBookmark) { |
|
|
|
|
|
|
|
if (!(this.initialized && this.historyUnlocked)) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (params.dest && !params.hash) { |
|
|
|
|
|
|
|
params.hash = (this.current.hash && this.current.dest && |
|
|
|
|
|
|
|
this.current.dest === params.dest) ? |
|
|
|
|
|
|
|
this.current.hash : |
|
|
|
|
|
|
|
this.linkService.getDestinationHash(params.dest).split('#')[1]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (params.page) { |
|
|
|
|
|
|
|
params.page |= 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (isInitialBookmark) { |
|
|
|
|
|
|
|
var target = window.history.state.target; |
|
|
|
|
|
|
|
if (!target) { |
|
|
|
|
|
|
|
// Invoked when the user specifies an initial bookmark,
|
|
|
|
|
|
|
|
// thus setting initialBookmark, when the document is loaded.
|
|
|
|
|
|
|
|
this._pushToHistory(params, false); |
|
|
|
|
|
|
|
this.previousHash = window.location.hash.substring(1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.updatePreviousBookmark = this.nextHashParam ? false : true; |
|
|
|
|
|
|
|
if (target) { |
|
|
|
|
|
|
|
// If the current document is reloaded,
|
|
|
|
|
|
|
|
// avoid creating duplicate entries in the history.
|
|
|
|
|
|
|
|
this._updatePreviousBookmark(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (this.nextHashParam) { |
|
|
|
|
|
|
|
if (this.nextHashParam === params.hash) { |
|
|
|
|
|
|
|
this.nextHashParam = null; |
|
|
|
|
|
|
|
this.updatePreviousBookmark = true; |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.nextHashParam = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (params.hash) { |
|
|
|
|
|
|
|
if (this.current.hash) { |
|
|
|
|
|
|
|
if (this.current.hash !== params.hash) { |
|
|
|
|
|
|
|
this._pushToHistory(params, true); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (!this.current.page && params.page) { |
|
|
|
|
|
|
|
this._pushToHistory(params, false, true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.updatePreviousBookmark = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this._pushToHistory(params, true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if (this.current.page && params.page && |
|
|
|
|
|
|
|
this.current.page !== params.page) { |
|
|
|
|
|
|
|
this._pushToHistory(params, true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_getPreviousParams: function pdfHistory_getPreviousParams(onlyCheckPage, |
|
|
|
|
|
|
|
beforeUnload) { |
|
|
|
|
|
|
|
if (!(this.currentBookmark && this.currentPage)) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} else if (this.updatePreviousBookmark) { |
|
|
|
|
|
|
|
this.updatePreviousBookmark = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (this.uid > 0 && !(this.previousBookmark && this.previousPage)) { |
|
|
|
|
|
|
|
// Prevent the history from getting stuck in the current state,
|
|
|
|
|
|
|
|
// effectively preventing the user from going back/forward in
|
|
|
|
|
|
|
|
// the history.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// This happens if the current position in the document didn't change
|
|
|
|
|
|
|
|
// when the history was previously updated. The reasons for this are
|
|
|
|
|
|
|
|
// either:
|
|
|
|
|
|
|
|
// 1. The current zoom value is such that the document does not need to,
|
|
|
|
|
|
|
|
// or cannot, be scrolled to display the destination.
|
|
|
|
|
|
|
|
// 2. The previous destination is broken, and doesn't actally point to a
|
|
|
|
|
|
|
|
// position within the document.
|
|
|
|
|
|
|
|
// (This is either due to a bad PDF generator, or the user making a
|
|
|
|
|
|
|
|
// mistake when entering a destination in the hash parameters.)
|
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if ((!this.current.dest && !onlyCheckPage) || beforeUnload) { |
|
|
|
|
|
|
|
if (this.previousBookmark === this.currentBookmark) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if (this.current.page || onlyCheckPage) { |
|
|
|
|
|
|
|
if (this.previousPage === this.currentPage) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var params = {hash: this.currentBookmark, page: this.currentPage}; |
|
|
|
|
|
|
|
if (this.isViewerInPresentationMode) { |
|
|
|
|
|
|
|
params.hash = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return params; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_stateObj: function pdfHistory_stateObj(params) { |
|
|
|
|
|
|
|
return {fingerprint: this.fingerprint, uid: this.uid, target: params}; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_pushToHistory: function pdfHistory_pushToHistory(params, |
|
|
|
|
|
|
|
addPrevious, overwrite) { |
|
|
|
|
|
|
|
if (!this.initialized) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!params.hash && params.page) { |
|
|
|
|
|
|
|
params.hash = ('page=' + params.page); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (addPrevious && !overwrite) { |
|
|
|
|
|
|
|
var previousParams = this._getPreviousParams(); |
|
|
|
|
|
|
|
if (previousParams) { |
|
|
|
|
|
|
|
var replacePrevious = (!this.current.dest && |
|
|
|
|
|
|
|
this.current.hash !== this.previousHash); |
|
|
|
|
|
|
|
this._pushToHistory(previousParams, false, replacePrevious); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this._pushOrReplaceState(this._stateObj(params), |
|
|
|
|
|
|
|
(overwrite || this.uid === 0)); |
|
|
|
|
|
|
|
this.currentUid = this.uid++; |
|
|
|
|
|
|
|
this.current = params; |
|
|
|
|
|
|
|
this.updatePreviousBookmark = true; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_goTo: function pdfHistory_goTo(state) { |
|
|
|
|
|
|
|
if (!(this.initialized && this.historyUnlocked && |
|
|
|
|
|
|
|
this._isStateObjectDefined(state))) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!this.reInitialized && state.uid < this.currentUid) { |
|
|
|
|
|
|
|
var previousParams = this._getPreviousParams(true); |
|
|
|
|
|
|
|
if (previousParams) { |
|
|
|
|
|
|
|
this._pushToHistory(this.current, false); |
|
|
|
|
|
|
|
this._pushToHistory(previousParams, false); |
|
|
|
|
|
|
|
this.currentUid = state.uid; |
|
|
|
|
|
|
|
window.history.back(); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.historyUnlocked = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (state.target.dest) { |
|
|
|
|
|
|
|
this.linkService.navigateTo(state.target.dest); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.linkService.setHash(state.target.hash); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.currentUid = state.uid; |
|
|
|
|
|
|
|
if (state.uid > this.uid) { |
|
|
|
|
|
|
|
this.uid = state.uid; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.current = state.target; |
|
|
|
|
|
|
|
this.updatePreviousBookmark = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var currentHash = window.location.hash.substring(1); |
|
|
|
|
|
|
|
if (this.previousHash !== currentHash) { |
|
|
|
|
|
|
|
this.allowHashChange = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.previousHash = currentHash; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.historyUnlocked = true; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
back: function pdfHistoryBack() { |
|
|
|
|
|
|
|
this.go(-1); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
forward: function pdfHistoryForward() { |
|
|
|
|
|
|
|
this.go(1); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
go: function pdfHistoryGo(direction) { |
|
|
|
|
|
|
|
if (this.initialized && this.historyUnlocked) { |
|
|
|
|
|
|
|
var state = window.history.state; |
|
|
|
|
|
|
|
if (direction === -1 && state && state.uid > 0) { |
|
|
|
|
|
|
|
window.history.back(); |
|
|
|
|
|
|
|
} else if (direction === 1 && state && state.uid < (this.uid - 1)) { |
|
|
|
|
|
|
|
window.history.forward(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return PDFHistory; |
|
|
|
|
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PDFJS.PDFViewer = PDFViewer; |
|
|
|
PDFJS.PDFViewer = PDFViewer; |
|
|
|
PDFJS.PDFPageView = PDFPageView; |
|
|
|
PDFJS.PDFPageView = PDFPageView; |
|
|
|
|
|
|
|
PDFJS.PDFLinkService = PDFLinkService; |
|
|
|
PDFJS.TextLayerBuilder = TextLayerBuilder; |
|
|
|
PDFJS.TextLayerBuilder = TextLayerBuilder; |
|
|
|
PDFJS.DefaultTextLayerFactory = DefaultTextLayerFactory; |
|
|
|
PDFJS.DefaultTextLayerFactory = DefaultTextLayerFactory; |
|
|
|
PDFJS.AnnotationsLayerBuilder = AnnotationsLayerBuilder; |
|
|
|
PDFJS.AnnotationsLayerBuilder = AnnotationsLayerBuilder; |
|
|
|
PDFJS.DefaultAnnotationsLayerFactory = DefaultAnnotationsLayerFactory; |
|
|
|
PDFJS.DefaultAnnotationsLayerFactory = DefaultAnnotationsLayerFactory; |
|
|
|
|
|
|
|
PDFJS.PDFHistory = PDFHistory; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PDFJS.getFileName = getFileName; |
|
|
|
}).call((typeof window === 'undefined') ? this : window); |
|
|
|
}).call((typeof window === 'undefined') ? this : window); |
|
|
|
|
|
|
|
|
|
|
|