2018-01-19 03:59:58 +08:00
/ * C o p y r i g h t 2 0 1 6 M o z i l l a F o u n d a t i o n
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
'use strict' ;
var DEFAULT _URL = 'compressed.tracemonkey-pldi-09.pdf' ;
;
var pdfjsWebLibs ;
{
pdfjsWebLibs = { pdfjsWebPDFJS : window . pdfjsDistBuildPdf } ;
( function ( ) {
( function ( root , factory ) {
factory ( root . pdfjsWebGrabToPan = { } ) ;
} ( this , function ( exports ) {
/ * *
* Construct a GrabToPan instance for a given HTML element .
* @ param options . element { Element }
* @ param options . ignoreTarget { function } optional . See ` ignoreTarget(node) `
* @ param options . onActiveChanged { function ( boolean ) } optional . Called
* when grab - to - pan is ( de ) activated . The first argument is a boolean that
* shows whether grab - to - pan is activated .
* /
function GrabToPan ( options ) {
this . element = options . element ;
this . document = options . element . ownerDocument ;
if ( typeof options . ignoreTarget === 'function' ) {
this . ignoreTarget = options . ignoreTarget ;
}
this . onActiveChanged = options . onActiveChanged ;
// Bind the contexts to ensure that `this` always points to
// the GrabToPan instance.
this . activate = this . activate . bind ( this ) ;
this . deactivate = this . deactivate . bind ( this ) ;
this . toggle = this . toggle . bind ( this ) ;
this . _onmousedown = this . _onmousedown . bind ( this ) ;
this . _onmousemove = this . _onmousemove . bind ( this ) ;
this . _endPan = this . _endPan . bind ( this ) ;
// 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' ;
}
GrabToPan . prototype = {
/ * *
* Class name of element which can be grabbed
* /
CSS _CLASS _GRAB : 'grab-to-pan-grab' ,
/ * *
* Bind a mousedown event to the element to enable grab - detection .
* /
activate : function GrabToPan _activate ( ) {
if ( ! this . active ) {
this . active = true ;
this . element . addEventListener ( 'mousedown' , this . _onmousedown , true ) ;
this . element . classList . add ( this . CSS _CLASS _GRAB ) ;
if ( this . onActiveChanged ) {
this . onActiveChanged ( true ) ;
}
}
} ,
/ * *
* Removes all events . Any pending pan session is immediately stopped .
* /
deactivate : function GrabToPan _deactivate ( ) {
if ( this . active ) {
this . active = false ;
this . element . removeEventListener ( 'mousedown' , this . _onmousedown , true ) ;
this . _endPan ( ) ;
this . element . classList . remove ( this . CSS _CLASS _GRAB ) ;
if ( this . onActiveChanged ) {
this . onActiveChanged ( false ) ;
}
}
} ,
toggle : function GrabToPan _toggle ( ) {
if ( this . active ) {
this . deactivate ( ) ;
} else {
this . activate ( ) ;
}
} ,
/ * *
* Whether to not pan if the target element is clicked .
* Override this method to change the default behaviour .
*
* @ param node { Element } The target of the event
* @ return { boolean } Whether to not react to the click event .
* /
ignoreTarget : function GrabToPan _ignoreTarget ( node ) {
// 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' ) ;
} ,
/ * *
* @ private
* /
_onmousedown : function GrabToPan _ _onmousedown ( event ) {
if ( event . button !== 0 || this . ignoreTarget ( event . target ) ) {
return ;
}
if ( event . originalTarget ) {
try {
event . originalTarget . tagName ;
} catch ( e ) {
// Mozilla-specific: element is a scrollbar (XUL element)
return ;
}
}
this . scrollLeftStart = this . element . scrollLeft ;
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 ) ;
// 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 ) ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
var focusedElement = document . activeElement ;
if ( focusedElement && ! focusedElement . contains ( event . target ) ) {
focusedElement . blur ( ) ;
}
} ,
/ * *
* @ private
* /
_onmousemove : function GrabToPan _ _onmousemove ( event ) {
this . element . removeEventListener ( 'scroll' , this . _endPan , true ) ;
if ( isLeftMouseReleased ( event ) ) {
this . _endPan ( ) ;
return ;
}
var xDiff = event . clientX - this . clientXStart ;
var yDiff = event . clientY - this . clientYStart ;
var scrollTop = this . scrollTopStart - yDiff ;
var scrollLeft = this . scrollLeftStart - xDiff ;
if ( this . element . scrollTo ) {
this . element . scrollTo ( {
top : scrollTop ,
left : scrollLeft ,
behavior : 'instant'
} ) ;
} else {
this . element . scrollTop = scrollTop ;
this . element . scrollLeft = scrollLeft ;
}
if ( ! this . overlay . parentNode ) {
document . body . appendChild ( this . overlay ) ;
}
} ,
/ * *
* @ 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 ) ;
if ( this . overlay . parentNode ) {
this . overlay . parentNode . removeChild ( this . overlay ) ;
}
}
} ;
// Get the correct (vendor-prefixed) name of the matches method.
var matchesSelector ;
[
'webkitM' ,
'mozM' ,
'msM' ,
'oM' ,
'm'
] . some ( function ( prefix ) {
var name = prefix + 'atches' ;
if ( name in document . documentElement ) {
matchesSelector = name ;
}
name += 'Selector' ;
if ( name in document . documentElement ) {
matchesSelector = name ;
}
return matchesSelector ;
} ) ;
// If found, then truthy, and [].some() ends.
// Browser sniffing because it's impossible to feature-detect
// whether event.which for onmousemove is reliable
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 ) ;
/ * *
* Whether the left mouse is not pressed .
* @ param event { MouseEvent }
* @ return { boolean } True if the left mouse button is not pressed .
* False if unsure or if the left mouse button is pressed .
* /
function isLeftMouseReleased ( event ) {
if ( 'buttons' in event && isNotIEorIsIE10plus ) {
// http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-buttons
// Firefox 15+
// Internet Explorer 10+
return ! ( event . buttons & 1 ) ;
}
if ( isChrome15OrOpera15plus || isSafari6plus ) {
// Chrome 14+
// Opera 15+
// Safari 6.0+
return event . which === 0 ;
}
}
exports . GrabToPan = GrabToPan ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebOverlayManager = { } ) ;
} ( this , function ( exports ) {
var OverlayManager = {
overlays : { } ,
active : null ,
/ * *
* @ param { string } name The name of the overlay that is registered .
* @ param { HTMLDivElement } element The overlay ' s DOM element .
* @ param { function } callerCloseMethod ( optional ) The method that , if present ,
* will call OverlayManager . close from the Object
* registering the overlay . Access to this method is
* necessary in order to run cleanup code when e . g .
* the overlay is force closed . The default is null .
* @ param { boolean } canForceClose ( optional ) Indicates if opening the overlay
* will close an active overlay . The default is false .
* @ returns { Promise } A promise that is resolved when the overlay has been
* registered .
* /
register : function overlayManagerRegister ( name , element , callerCloseMethod , canForceClose ) {
return new Promise ( function ( resolve ) {
var container ;
if ( ! name || ! element || ! ( container = element . parentNode ) ) {
throw new Error ( 'Not enough parameters.' ) ;
} else if ( this . overlays [ name ] ) {
throw new Error ( 'The overlay is already registered.' ) ;
}
this . overlays [ name ] = {
element : element ,
container : container ,
callerCloseMethod : callerCloseMethod || null ,
canForceClose : canForceClose || false
} ;
resolve ( ) ;
} . bind ( this ) ) ;
} ,
/ * *
* @ param { string } name The name of the overlay that is unregistered .
* @ returns { Promise } A promise that is resolved when the overlay has been
* unregistered .
* /
unregister : function overlayManagerUnregister ( name ) {
return new Promise ( function ( resolve ) {
if ( ! this . overlays [ name ] ) {
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.' ) ;
}
delete this . overlays [ name ] ;
resolve ( ) ;
} . bind ( this ) ) ;
} ,
/ * *
* @ param { string } name The name of the overlay that should be opened .
* @ returns { Promise } A promise that is resolved when the overlay has been
* opened .
* /
open : function overlayManagerOpen ( name ) {
return new Promise ( function ( resolve ) {
if ( ! this . overlays [ name ] ) {
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.' ) ;
} else {
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' ) ;
window . addEventListener ( 'keydown' , this . _keyDown ) ;
resolve ( ) ;
} . bind ( this ) ) ;
} ,
/ * *
* @ param { string } name The name of the overlay that should be closed .
* @ returns { Promise } A promise that is resolved when the overlay has been
* closed .
* /
close : function overlayManagerClose ( name ) {
return new Promise ( function ( resolve ) {
if ( ! this . overlays [ name ] ) {
throw new Error ( 'The overlay does not exist.' ) ;
} else if ( ! this . active ) {
throw new Error ( 'The overlay is currently not active.' ) ;
} else if ( this . active !== name ) {
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 . active = null ;
window . removeEventListener ( 'keydown' , this . _keyDown ) ;
resolve ( ) ;
} . bind ( this ) ) ;
} ,
/ * *
* @ private
* /
_keyDown : function overlayManager _keyDown ( evt ) {
var self = OverlayManager ;
if ( self . active && evt . keyCode === 27 ) {
// Esc key.
self . _closeThroughCaller ( ) ;
evt . preventDefault ( ) ;
}
} ,
/ * *
* @ private
* /
_closeThroughCaller : function overlayManager _closeThroughCaller ( ) {
if ( this . overlays [ this . active ] . callerCloseMethod ) {
this . overlays [ this . active ] . callerCloseMethod ( ) ;
}
if ( this . active ) {
this . close ( this . active ) ;
}
}
} ;
exports . OverlayManager = OverlayManager ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFRenderingQueue = { } ) ;
} ( this , function ( exports ) {
var CLEANUP _TIMEOUT = 30000 ;
var RenderingStates = {
INITIAL : 0 ,
RUNNING : 1 ,
PAUSED : 2 ,
FINISHED : 3
} ;
/ * *
* Controls rendering of the views for pages and thumbnails .
* @ class
* /
var PDFRenderingQueue = function PDFRenderingQueueClosure ( ) {
/ * *
* @ constructs
* /
function PDFRenderingQueue ( ) {
this . pdfViewer = null ;
this . pdfThumbnailViewer = null ;
this . onIdle = null ;
this . highestPriorityPage = null ;
this . idleTimeout = null ;
this . printing = false ;
this . isThumbnailViewEnabled = false ;
}
PDFRenderingQueue . prototype = /** @lends PDFRenderingQueue.prototype */
{
/ * *
* @ param { PDFViewer } pdfViewer
* /
setViewer : function PDFRenderingQueue _setViewer ( pdfViewer ) {
this . pdfViewer = pdfViewer ;
} ,
/ * *
* @ param { PDFThumbnailViewer } pdfThumbnailViewer
* /
setThumbnailViewer : function PDFRenderingQueue _setThumbnailViewer ( pdfThumbnailViewer ) {
this . pdfThumbnailViewer = pdfThumbnailViewer ;
} ,
/ * *
* @ param { IRenderableView } view
* @ returns { boolean }
* /
isHighestPriority : function PDFRenderingQueue _isHighestPriority ( view ) {
return this . highestPriorityPage === view . renderingId ;
} ,
renderHighestPriority : function PDFRenderingQueue _renderHighestPriority ( currentlyVisiblePages ) {
if ( this . idleTimeout ) {
clearTimeout ( this . idleTimeout ) ;
this . idleTimeout = null ;
}
// Pages have a higher priority than thumbnails, so check them first.
if ( this . pdfViewer . forceRendering ( currentlyVisiblePages ) ) {
return ;
}
// No pages needed rendering so check thumbnails.
if ( this . pdfThumbnailViewer && this . isThumbnailViewEnabled ) {
if ( this . pdfThumbnailViewer . forceRendering ( ) ) {
return ;
}
}
if ( this . printing ) {
// If printing is currently ongoing do not reschedule cleanup.
return ;
}
if ( this . onIdle ) {
this . idleTimeout = setTimeout ( this . onIdle . bind ( this ) , CLEANUP _TIMEOUT ) ;
}
} ,
getHighestPriority : function PDFRenderingQueue _getHighestPriority ( visible , views , scrolledDown ) {
// The state has changed figure out which page has the highest priority to
// render next (if any).
// Priority:
// 1 visible pages
// 2 if last scrolled down page after the visible pages
// 2 if last scrolled up page before the visible pages
var visibleViews = visible . views ;
var numVisible = visibleViews . length ;
if ( numVisible === 0 ) {
return false ;
}
for ( var i = 0 ; i < numVisible ; ++ i ) {
var view = visibleViews [ i ] . view ;
if ( ! this . isViewFinished ( view ) ) {
return view ;
}
}
// All the visible views have rendered, try to render next/previous pages.
if ( scrolledDown ) {
var nextPageIndex = visible . last . id ;
// ID's start at 1 so no need to add 1.
if ( views [ nextPageIndex ] && ! this . isViewFinished ( views [ nextPageIndex ] ) ) {
return views [ nextPageIndex ] ;
}
} else {
var previousPageIndex = visible . first . id - 2 ;
if ( views [ previousPageIndex ] && ! this . isViewFinished ( views [ previousPageIndex ] ) ) {
return views [ previousPageIndex ] ;
}
}
// Everything that needs to be rendered has been.
return null ;
} ,
/ * *
* @ param { IRenderableView } view
* @ returns { boolean }
* /
isViewFinished : function PDFRenderingQueue _isViewFinished ( view ) {
return view . renderingState === RenderingStates . FINISHED ;
} ,
/ * *
* Render a page or thumbnail view . This calls the appropriate function
* based on the views state . If the view is already rendered it will return
* false .
* @ param { IRenderableView } view
* /
renderView : function PDFRenderingQueue _renderView ( view ) {
var state = view . renderingState ;
switch ( state ) {
case RenderingStates . FINISHED :
return false ;
case RenderingStates . PAUSED :
this . highestPriorityPage = view . renderingId ;
view . resume ( ) ;
break ;
case RenderingStates . RUNNING :
this . highestPriorityPage = view . renderingId ;
break ;
case RenderingStates . INITIAL :
this . highestPriorityPage = view . renderingId ;
var continueRendering = function ( ) {
this . renderHighestPriority ( ) ;
} . bind ( this ) ;
view . draw ( ) . then ( continueRendering , continueRendering ) ;
break ;
}
return true ;
}
} ;
return PDFRenderingQueue ;
} ( ) ;
exports . RenderingStates = RenderingStates ;
exports . PDFRenderingQueue = PDFRenderingQueue ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPreferences = { } ) ;
} ( this , function ( exports ) {
var defaultPreferences ;
defaultPreferences = Promise . resolve ( {
"showPreviousViewOnLoad" : true ,
"defaultZoomValue" : "" ,
"sidebarViewOnLoad" : 0 ,
"enableHandToolOnLoad" : false ,
"enableWebGL" : false ,
"pdfBugEnabled" : false ,
"disableRange" : false ,
"disableStream" : false ,
"disableAutoFetch" : false ,
"disableFontFace" : false ,
"disableTextLayer" : false ,
"useOnlyCssZoom" : false ,
"externalLinkTarget" : 0 ,
"enhanceTextSelection" : false ,
"renderInteractiveForms" : false ,
"disablePageLabels" : false
} ) ;
function cloneObj ( obj ) {
var result = { } ;
for ( var i in obj ) {
if ( Object . prototype . hasOwnProperty . call ( obj , i ) ) {
result [ i ] = obj [ i ] ;
}
}
return result ;
}
/ * *
* Preferences - Utility for storing persistent settings .
* Used for settings that should be applied to all opened documents ,
* or every time the viewer is loaded .
* /
var Preferences = {
prefs : null ,
isInitializedPromiseResolved : false ,
initializedPromise : null ,
/ * *
* Initialize and fetch the current preference values from storage .
* @ return { Promise } A promise that is resolved when the preferences
* have been initialized .
* /
initialize : function preferencesInitialize ( ) {
return this . initializedPromise = defaultPreferences . then ( function ( defaults ) {
Object . defineProperty ( this , 'defaults' , {
value : Object . freeze ( defaults ) ,
writable : false ,
enumerable : true ,
configurable : false
} ) ;
this . prefs = cloneObj ( defaults ) ;
return this . _readFromStorage ( defaults ) ;
} . bind ( this ) ) . then ( function ( prefObj ) {
this . isInitializedPromiseResolved = true ;
if ( prefObj ) {
this . prefs = prefObj ;
}
} . bind ( this ) ) ;
} ,
/ * *
* Stub function for writing preferences to storage .
* NOTE : This should be overridden by a build - specific function defined below .
* @ param { Object } prefObj The preferences that should be written to storage .
* @ return { Promise } A promise that is resolved when the preference values
* have been written .
* /
_writeToStorage : function preferences _writeToStorage ( prefObj ) {
return Promise . resolve ( ) ;
} ,
/ * *
* Stub function for reading preferences from storage .
* NOTE : This should be overridden by a build - specific function defined below .
* @ param { Object } prefObj The preferences that should be read from storage .
* @ return { Promise } A promise that is resolved with an { Object } containing
* the preferences that have been read .
* /
_readFromStorage : function preferences _readFromStorage ( prefObj ) {
return Promise . resolve ( ) ;
} ,
/ * *
* Reset the preferences to their default values and update storage .
* @ return { Promise } A promise that is resolved when the preference values
* have been reset .
* /
reset : function preferencesReset ( ) {
return this . initializedPromise . then ( function ( ) {
this . prefs = cloneObj ( this . defaults ) ;
return this . _writeToStorage ( this . defaults ) ;
} . bind ( this ) ) ;
} ,
/ * *
* Replace the current preference values with the ones from storage .
* @ return { Promise } A promise that is resolved when the preference values
* have been updated .
* /
reload : function preferencesReload ( ) {
return this . initializedPromise . then ( function ( ) {
this . _readFromStorage ( this . defaults ) . then ( function ( prefObj ) {
if ( prefObj ) {
this . prefs = prefObj ;
}
} . bind ( this ) ) ;
} . bind ( this ) ) ;
} ,
/ * *
* Set the value of a preference .
* @ param { string } name The name of the preference that should be changed .
* @ param { boolean | number | string } value The new value of the preference .
* @ return { Promise } A promise that is resolved when the value has been set ,
* provided that the preference exists and the types match .
* /
set : function preferencesSet ( name , value ) {
return this . initializedPromise . then ( function ( ) {
if ( this . defaults [ name ] === undefined ) {
throw new Error ( 'preferencesSet: \'' + name + '\' is undefined.' ) ;
} else if ( value === undefined ) {
throw new Error ( 'preferencesSet: no value is specified.' ) ;
}
var valueType = typeof value ;
var defaultType = typeof this . defaults [ name ] ;
if ( valueType !== defaultType ) {
if ( valueType === 'number' && defaultType === 'string' ) {
value = value . toString ( ) ;
} else {
throw new Error ( 'Preferences_set: \'' + value + '\' is a \"' + valueType + '\", expected \"' + defaultType + '\".' ) ;
}
} else {
if ( valueType === 'number' && ( value | 0 ) !== value ) {
throw new Error ( 'Preferences_set: \'' + value + '\' must be an \"integer\".' ) ;
}
}
this . prefs [ name ] = value ;
return this . _writeToStorage ( this . prefs ) ;
} . bind ( this ) ) ;
} ,
/ * *
* Get the value of a preference .
* @ param { string } name The name of the preference whose value is requested .
* @ return { Promise } A promise that is resolved with a { boolean | number | string }
* containing the value of the preference .
* /
get : function preferencesGet ( name ) {
return this . initializedPromise . then ( function ( ) {
var defaultValue = this . defaults [ name ] ;
if ( defaultValue === undefined ) {
throw new Error ( 'preferencesGet: \'' + name + '\' is undefined.' ) ;
} else {
var prefValue = this . prefs [ name ] ;
if ( prefValue !== undefined ) {
return prefValue ;
}
}
return defaultValue ;
} . bind ( this ) ) ;
}
} ;
exports . Preferences = Preferences ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebViewHistory = { } ) ;
} ( this , function ( exports ) {
var DEFAULT _VIEW _HISTORY _CACHE _SIZE = 20 ;
/ * *
* View History - This is a utility for saving various view parameters for
* recently opened files .
*
* The way that the view parameters are stored depends on how PDF . js is built ,
* for 'gulp <flag>' the following cases exist :
* - FIREFOX or MOZCENTRAL - uses sessionStorage .
* - GENERIC or CHROME - uses localStorage , if it is available .
* /
var ViewHistory = function ViewHistoryClosure ( ) {
function ViewHistory ( fingerprint , cacheSize ) {
this . fingerprint = fingerprint ;
this . cacheSize = cacheSize || DEFAULT _VIEW _HISTORY _CACHE _SIZE ;
this . isInitializedPromiseResolved = false ;
this . initializedPromise = this . _readFromStorage ( ) . then ( function ( databaseStr ) {
this . isInitializedPromiseResolved = true ;
var database = JSON . parse ( databaseStr || '{}' ) ;
if ( ! ( 'files' in database ) ) {
database . files = [ ] ;
}
if ( database . files . length >= this . cacheSize ) {
database . files . shift ( ) ;
}
var index ;
for ( var i = 0 , length = database . files . length ; i < length ; i ++ ) {
var branch = database . files [ i ] ;
if ( branch . fingerprint === this . fingerprint ) {
index = i ;
break ;
}
}
if ( typeof index !== 'number' ) {
index = database . files . push ( { fingerprint : this . fingerprint } ) - 1 ;
}
this . file = database . files [ index ] ;
this . database = database ;
} . bind ( this ) ) ;
}
ViewHistory . prototype = {
_writeToStorage : function ViewHistory _writeToStorage ( ) {
return new Promise ( function ( resolve ) {
var databaseStr = JSON . stringify ( this . database ) ;
sessionStorage . setItem ( 'pdfjsHistory' , databaseStr ) ;
resolve ( ) ;
} . bind ( this ) ) ;
} ,
_readFromStorage : function ViewHistory _readFromStorage ( ) {
return new Promise ( function ( resolve ) {
resolve ( sessionStorage . getItem ( 'pdfjsHistory' ) ) ;
} ) ;
} ,
set : function ViewHistory _set ( name , val ) {
if ( ! this . isInitializedPromiseResolved ) {
return ;
}
this . file [ name ] = val ;
return this . _writeToStorage ( ) ;
} ,
setMultiple : function ViewHistory _setMultiple ( properties ) {
if ( ! this . isInitializedPromiseResolved ) {
return ;
}
for ( var name in properties ) {
this . file [ name ] = properties [ name ] ;
}
return this . _writeToStorage ( ) ;
} ,
get : function ViewHistory _get ( name , defaultValue ) {
if ( ! this . isInitializedPromiseResolved ) {
return defaultValue ;
}
return this . file [ name ] || defaultValue ;
}
} ;
return ViewHistory ;
} ( ) ;
exports . ViewHistory = ViewHistory ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebDownloadManager = { } , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , pdfjsLib ) {
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebHandTool = { } , root . pdfjsWebGrabToPan , root . pdfjsWebPreferences ) ;
} ( this , function ( exports , grabToPan , preferences ) {
var GrabToPan = grabToPan . GrabToPan ;
var Preferences = preferences . Preferences ;
/ * *
* @ typedef { Object } HandToolOptions
* @ property { HTMLDivElement } container - The document container .
* @ property { EventBus } eventBus - The application event bus .
* /
/ * *
* @ class
* /
var HandTool = function HandToolClosure ( ) {
/ * *
* @ constructs HandTool
* @ param { HandToolOptions } options
* /
function HandTool ( options ) {
this . container = options . container ;
this . eventBus = options . eventBus ;
this . wasActive = false ;
this . handTool = new GrabToPan ( {
element : this . container ,
onActiveChanged : function ( isActive ) {
this . eventBus . dispatch ( 'handtoolchanged' , { isActive : isActive } ) ;
} . bind ( this )
} ) ;
this . eventBus . on ( 'togglehandtool' , this . toggle . bind ( this ) ) ;
this . eventBus . on ( 'localized' , function ( e ) {
Preferences . get ( 'enableHandToolOnLoad' ) . then ( function resolved ( value ) {
if ( value ) {
this . handTool . activate ( ) ;
}
} . bind ( this ) , function rejected ( reason ) {
} ) ;
} . bind ( this ) ) ;
this . eventBus . on ( 'presentationmodechanged' , function ( e ) {
if ( e . switchInProgress ) {
return ;
}
if ( e . active ) {
this . enterPresentationMode ( ) ;
} else {
this . exitPresentationMode ( ) ;
}
} . bind ( this ) ) ;
}
HandTool . prototype = {
/ * *
* @ return { boolean }
* /
get isActive ( ) {
return ! ! this . handTool . active ;
} ,
toggle : function HandTool _toggle ( ) {
this . handTool . toggle ( ) ;
} ,
enterPresentationMode : function HandTool _enterPresentationMode ( ) {
if ( this . isActive ) {
this . wasActive = true ;
this . handTool . deactivate ( ) ;
}
} ,
exitPresentationMode : function HandTool _exitPresentationMode ( ) {
if ( this . wasActive ) {
this . wasActive = false ;
this . handTool . activate ( ) ;
}
}
} ;
return HandTool ;
} ( ) ;
exports . HandTool = HandTool ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFAttachmentViewer = { } , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , pdfjsLib ) {
/ * *
* @ typedef { Object } PDFAttachmentViewerOptions
* @ property { HTMLDivElement } container - The viewer element .
* @ property { EventBus } eventBus - The application event bus .
* @ property { DownloadManager } downloadManager - The download manager .
* /
/ * *
* @ typedef { Object } PDFAttachmentViewerRenderParameters
* @ property { Array | null } attachments - An array of attachment objects .
* /
/ * *
* @ class
* /
var PDFAttachmentViewer = function PDFAttachmentViewerClosure ( ) {
/ * *
* @ constructs PDFAttachmentViewer
* @ param { PDFAttachmentViewerOptions } options
* /
function PDFAttachmentViewer ( options ) {
this . attachments = null ;
this . container = options . container ;
this . eventBus = options . eventBus ;
this . downloadManager = options . downloadManager ;
}
PDFAttachmentViewer . prototype = {
reset : function PDFAttachmentViewer _reset ( ) {
this . attachments = null ;
var container = this . container ;
while ( container . firstChild ) {
container . removeChild ( container . firstChild ) ;
}
} ,
/ * *
* @ private
* /
_dispatchEvent : function PDFAttachmentViewer _dispatchEvent ( attachmentsCount ) {
this . eventBus . dispatch ( 'attachmentsloaded' , {
source : this ,
attachmentsCount : attachmentsCount
} ) ;
} ,
/ * *
* @ private
* /
_bindLink : function PDFAttachmentViewer _bindLink ( button , content , filename ) {
button . onclick = function downloadFile ( e ) {
this . downloadManager . downloadData ( content , filename , '' ) ;
return false ;
} . bind ( this ) ;
} ,
/ * *
* @ param { PDFAttachmentViewerRenderParameters } params
* /
render : function PDFAttachmentViewer _render ( params ) {
var attachments = params && params . attachments || null ;
var attachmentsCount = 0 ;
if ( this . attachments ) {
this . reset ( ) ;
}
this . attachments = attachments ;
if ( ! attachments ) {
this . _dispatchEvent ( attachmentsCount ) ;
return ;
}
var names = Object . keys ( attachments ) . sort ( function ( a , b ) {
return a . toLowerCase ( ) . localeCompare ( b . toLowerCase ( ) ) ;
} ) ;
attachmentsCount = names . length ;
for ( var i = 0 ; i < attachmentsCount ; i ++ ) {
var item = attachments [ names [ i ] ] ;
var filename = pdfjsLib . getFilenameFromUrl ( item . filename ) ;
var div = document . createElement ( 'div' ) ;
div . className = 'attachmentsItem' ;
var button = document . createElement ( 'button' ) ;
this . _bindLink ( button , item . content , filename ) ;
button . textContent = pdfjsLib . removeNullCharacters ( filename ) ;
div . appendChild ( button ) ;
this . container . appendChild ( div ) ;
}
this . _dispatchEvent ( attachmentsCount ) ;
}
} ;
return PDFAttachmentViewer ;
} ( ) ;
exports . PDFAttachmentViewer = PDFAttachmentViewer ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFOutlineViewer = { } , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , pdfjsLib ) {
var PDFJS = pdfjsLib . PDFJS ;
var DEFAULT _TITLE = '\u2013' ;
/ * *
* @ typedef { Object } PDFOutlineViewerOptions
* @ property { HTMLDivElement } container - The viewer element .
* @ property { IPDFLinkService } linkService - The navigation / linking service .
* @ property { EventBus } eventBus - The application event bus .
* /
/ * *
* @ typedef { Object } PDFOutlineViewerRenderParameters
* @ property { Array | null } outline - An array of outline objects .
* /
/ * *
* @ class
* /
var PDFOutlineViewer = function PDFOutlineViewerClosure ( ) {
/ * *
* @ constructs PDFOutlineViewer
* @ param { PDFOutlineViewerOptions } options
* /
function PDFOutlineViewer ( options ) {
this . outline = null ;
this . lastToggleIsShow = true ;
this . container = options . container ;
this . linkService = options . linkService ;
this . eventBus = options . eventBus ;
}
PDFOutlineViewer . prototype = {
reset : function PDFOutlineViewer _reset ( ) {
this . outline = null ;
this . lastToggleIsShow = true ;
var container = this . container ;
while ( container . firstChild ) {
container . removeChild ( container . firstChild ) ;
}
} ,
/ * *
* @ private
* /
_dispatchEvent : function PDFOutlineViewer _dispatchEvent ( outlineCount ) {
this . eventBus . dispatch ( 'outlineloaded' , {
source : this ,
outlineCount : outlineCount
} ) ;
} ,
/ * *
* @ private
* /
_bindLink : function PDFOutlineViewer _bindLink ( element , item ) {
if ( item . url ) {
pdfjsLib . addLinkAttributes ( element , {
url : item . url ,
target : item . newWindow ? PDFJS . LinkTarget . BLANK : undefined
} ) ;
return ;
}
var self = this , destination = item . dest ;
element . href = self . linkService . getDestinationHash ( destination ) ;
element . onclick = function ( ) {
if ( destination ) {
self . linkService . navigateTo ( destination ) ;
}
return false ;
} ;
} ,
/ * *
* @ private
* /
_setStyles : function PDFOutlineViewer _setStyles ( element , item ) {
var styleStr = '' ;
if ( item . bold ) {
styleStr += 'font-weight: bold;' ;
}
if ( item . italic ) {
styleStr += 'font-style: italic;' ;
}
if ( styleStr ) {
element . setAttribute ( 'style' , styleStr ) ;
}
} ,
/ * *
* Prepend a button before an outline item which allows the user to toggle
* the visibility of all outline items at that level .
*
* @ private
* /
_addToggleButton : function PDFOutlineViewer _addToggleButton ( div ) {
var toggler = document . createElement ( 'div' ) ;
toggler . className = 'outlineItemToggler' ;
toggler . onclick = function ( event ) {
event . stopPropagation ( ) ;
toggler . classList . toggle ( 'outlineItemsHidden' ) ;
if ( event . shiftKey ) {
var shouldShowAll = ! toggler . classList . contains ( 'outlineItemsHidden' ) ;
this . _toggleOutlineItem ( div , shouldShowAll ) ;
}
} . bind ( this ) ;
div . insertBefore ( toggler , div . firstChild ) ;
} ,
/ * *
* Toggle the visibility of the subtree of an outline item .
*
* @ param { Element } root - the root of the outline ( sub ) tree .
* @ param { boolean } show - whether to show the outline ( sub ) tree . If false ,
* the outline subtree rooted at | root | will be collapsed .
*
* @ private
* /
_toggleOutlineItem : function PDFOutlineViewer _toggleOutlineItem ( root , show ) {
this . lastToggleIsShow = show ;
var togglers = root . querySelectorAll ( '.outlineItemToggler' ) ;
for ( var i = 0 , ii = togglers . length ; i < ii ; ++ i ) {
togglers [ i ] . classList [ show ? 'remove' : 'add' ] ( 'outlineItemsHidden' ) ;
}
} ,
/ * *
* Collapse or expand all subtrees of the outline .
* /
toggleOutlineTree : function PDFOutlineViewer _toggleOutlineTree ( ) {
if ( ! this . outline ) {
return ;
}
this . _toggleOutlineItem ( this . container , ! this . lastToggleIsShow ) ;
} ,
/ * *
* @ param { PDFOutlineViewerRenderParameters } params
* /
render : function PDFOutlineViewer _render ( params ) {
var outline = params && params . outline || null ;
var outlineCount = 0 ;
if ( this . outline ) {
this . reset ( ) ;
}
this . outline = outline ;
if ( ! outline ) {
this . _dispatchEvent ( outlineCount ) ;
return ;
}
var fragment = document . createDocumentFragment ( ) ;
var queue = [ {
parent : fragment ,
items : this . outline
} ] ;
var hasAnyNesting = false ;
while ( queue . length > 0 ) {
var levelData = queue . shift ( ) ;
for ( var i = 0 , len = levelData . items . length ; i < len ; i ++ ) {
var item = levelData . items [ i ] ;
var div = document . createElement ( 'div' ) ;
div . className = 'outlineItem' ;
var element = document . createElement ( 'a' ) ;
this . _bindLink ( element , item ) ;
this . _setStyles ( element , item ) ;
element . textContent = pdfjsLib . removeNullCharacters ( item . title ) || DEFAULT _TITLE ;
div . appendChild ( element ) ;
if ( item . items . length > 0 ) {
hasAnyNesting = true ;
this . _addToggleButton ( div ) ;
var itemsDiv = document . createElement ( 'div' ) ;
itemsDiv . className = 'outlineItems' ;
div . appendChild ( itemsDiv ) ;
queue . push ( {
parent : itemsDiv ,
items : item . items
} ) ;
}
levelData . parent . appendChild ( div ) ;
outlineCount ++ ;
}
}
if ( hasAnyNesting ) {
this . container . classList . add ( 'outlineWithDeepNesting' ) ;
}
this . container . appendChild ( fragment ) ;
this . _dispatchEvent ( outlineCount ) ;
}
} ;
return PDFOutlineViewer ;
} ( ) ;
exports . PDFOutlineViewer = PDFOutlineViewer ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFSidebar = { } , root . pdfjsWebPDFRenderingQueue ) ;
} ( this , function ( exports , pdfRenderingQueue ) {
var RenderingStates = pdfRenderingQueue . RenderingStates ;
var SidebarView = {
NONE : 0 ,
THUMBS : 1 ,
OUTLINE : 2 ,
ATTACHMENTS : 3
} ;
/ * *
* @ typedef { Object } PDFSidebarOptions
* @ property { PDFViewer } pdfViewer - The document viewer .
* @ property { PDFThumbnailViewer } pdfThumbnailViewer - The thumbnail viewer .
* @ property { PDFOutlineViewer } pdfOutlineViewer - The outline viewer .
* @ property { HTMLDivElement } mainContainer - The main container
* ( in which the viewer element is placed ) .
* @ property { HTMLDivElement } outerContainer - The outer container
* ( encasing both the viewer and sidebar elements ) .
* @ property { EventBus } eventBus - The application event bus .
* @ property { HTMLButtonElement } toggleButton - The button used for
* opening / closing the sidebar .
* @ property { HTMLButtonElement } thumbnailButton - The button used to show
* the thumbnail view .
* @ property { HTMLButtonElement } outlineButton - The button used to show
* the outline view .
* @ property { HTMLButtonElement } attachmentsButton - The button used to show
* the attachments view .
* @ property { HTMLDivElement } thumbnailView - The container in which
* the thumbnails are placed .
* @ property { HTMLDivElement } outlineView - The container in which
* the outline is placed .
* @ property { HTMLDivElement } attachmentsView - The container in which
* the attachments are placed .
* /
/ * *
* @ class
* /
var PDFSidebar = function PDFSidebarClosure ( ) {
/ * *
* @ constructs PDFSidebar
* @ param { PDFSidebarOptions } options
* /
function PDFSidebar ( options ) {
this . isOpen = false ;
this . active = SidebarView . THUMBS ;
this . isInitialViewSet = false ;
/ * *
* Callback used when the sidebar has been opened / closed , to ensure that
* the viewers ( PDFViewer / PDFThumbnailViewer ) are updated correctly .
* /
this . onToggled = null ;
this . pdfViewer = options . pdfViewer ;
this . pdfThumbnailViewer = options . pdfThumbnailViewer ;
this . pdfOutlineViewer = options . pdfOutlineViewer ;
this . mainContainer = options . mainContainer ;
this . outerContainer = options . outerContainer ;
this . eventBus = options . eventBus ;
this . toggleButton = options . toggleButton ;
this . thumbnailButton = options . thumbnailButton ;
this . outlineButton = options . outlineButton ;
this . attachmentsButton = options . attachmentsButton ;
this . thumbnailView = options . thumbnailView ;
this . outlineView = options . outlineView ;
this . attachmentsView = options . attachmentsView ;
this . _addEventListeners ( ) ;
}
PDFSidebar . prototype = {
reset : function PDFSidebar _reset ( ) {
this . isInitialViewSet = false ;
this . close ( ) ;
this . switchView ( SidebarView . THUMBS ) ;
this . outlineButton . disabled = false ;
this . attachmentsButton . disabled = false ;
} ,
/ * *
* @ returns { number } One of the values in { SidebarView } .
* /
get visibleView ( ) {
return this . isOpen ? this . active : SidebarView . NONE ;
} ,
get isThumbnailViewVisible ( ) {
return this . isOpen && this . active === SidebarView . THUMBS ;
} ,
get isOutlineViewVisible ( ) {
return this . isOpen && this . active === SidebarView . OUTLINE ;
} ,
get isAttachmentsViewVisible ( ) {
return this . isOpen && this . active === SidebarView . ATTACHMENTS ;
} ,
/ * *
* @ param { number } view - The sidebar view that should become visible ,
* must be one of the values in { SidebarView } .
* /
setInitialView : function PDFSidebar _setInitialView ( view ) {
if ( this . isInitialViewSet ) {
return ;
}
this . isInitialViewSet = true ;
if ( this . isOpen && view === SidebarView . NONE ) {
this . _dispatchEvent ( ) ;
// If the user has already manually opened the sidebar,
// immediately closing it would be bad UX.
return ;
}
var isViewPreserved = view === this . visibleView ;
this . switchView ( view , /* forceOpen */
true ) ;
if ( isViewPreserved ) {
// Prevent dispatching two back-to-back `sidebarviewchanged` events,
// since `this.switchView` dispatched the event if the view changed.
this . _dispatchEvent ( ) ;
}
} ,
/ * *
* @ param { number } view - The sidebar view that should be switched to ,
* must be one of the values in { SidebarView } .
* @ param { boolean } forceOpen - ( optional ) Ensure that the sidebar is open .
* The default value is false .
* /
switchView : function PDFSidebar _switchView ( view , forceOpen ) {
if ( view === SidebarView . NONE ) {
this . close ( ) ;
return ;
}
var isViewChanged = view !== this . active ;
var shouldForceRendering = false ;
switch ( view ) {
case SidebarView . THUMBS :
this . thumbnailButton . classList . add ( 'toggled' ) ;
this . outlineButton . classList . remove ( 'toggled' ) ;
this . attachmentsButton . classList . remove ( 'toggled' ) ;
this . thumbnailView . classList . remove ( 'hidden' ) ;
this . outlineView . classList . add ( 'hidden' ) ;
this . attachmentsView . classList . add ( 'hidden' ) ;
if ( this . isOpen && isViewChanged ) {
this . _updateThumbnailViewer ( ) ;
shouldForceRendering = true ;
}
break ;
case SidebarView . OUTLINE :
if ( this . outlineButton . disabled ) {
return ;
}
this . thumbnailButton . classList . remove ( 'toggled' ) ;
this . outlineButton . classList . add ( 'toggled' ) ;
this . attachmentsButton . classList . remove ( 'toggled' ) ;
this . thumbnailView . classList . add ( 'hidden' ) ;
this . outlineView . classList . remove ( 'hidden' ) ;
this . attachmentsView . classList . add ( 'hidden' ) ;
break ;
case SidebarView . ATTACHMENTS :
if ( this . attachmentsButton . disabled ) {
return ;
}
this . thumbnailButton . classList . remove ( 'toggled' ) ;
this . outlineButton . classList . remove ( 'toggled' ) ;
this . attachmentsButton . classList . add ( 'toggled' ) ;
this . thumbnailView . classList . add ( 'hidden' ) ;
this . outlineView . classList . add ( 'hidden' ) ;
this . attachmentsView . classList . remove ( 'hidden' ) ;
break ;
default :
console . error ( 'PDFSidebar_switchView: "' + view + '" is an unsupported value.' ) ;
return ;
}
// Update the active view *after* it has been validated above,
// in order to prevent setting it to an invalid state.
this . active = view | 0 ;
if ( forceOpen && ! this . isOpen ) {
this . open ( ) ;
// NOTE: `this.open` will trigger rendering, and dispatch the event.
return ;
}
if ( shouldForceRendering ) {
this . _forceRendering ( ) ;
}
if ( isViewChanged ) {
this . _dispatchEvent ( ) ;
}
} ,
open : function PDFSidebar _open ( ) {
if ( this . isOpen ) {
return ;
}
this . isOpen = true ;
this . toggleButton . classList . add ( 'toggled' ) ;
this . outerContainer . classList . add ( 'sidebarMoving' ) ;
this . outerContainer . classList . add ( 'sidebarOpen' ) ;
if ( this . active === SidebarView . THUMBS ) {
this . _updateThumbnailViewer ( ) ;
}
this . _forceRendering ( ) ;
this . _dispatchEvent ( ) ;
} ,
close : function PDFSidebar _close ( ) {
if ( ! this . isOpen ) {
return ;
}
this . isOpen = false ;
this . toggleButton . classList . remove ( 'toggled' ) ;
this . outerContainer . classList . add ( 'sidebarMoving' ) ;
this . outerContainer . classList . remove ( 'sidebarOpen' ) ;
this . _forceRendering ( ) ;
this . _dispatchEvent ( ) ;
} ,
toggle : function PDFSidebar _toggle ( ) {
if ( this . isOpen ) {
this . close ( ) ;
} else {
this . open ( ) ;
}
} ,
/ * *
* @ private
* /
_dispatchEvent : function PDFSidebar _dispatchEvent ( ) {
this . eventBus . dispatch ( 'sidebarviewchanged' , {
source : this ,
view : this . visibleView
} ) ;
} ,
/ * *
* @ private
* /
_forceRendering : function PDFSidebar _forceRendering ( ) {
if ( this . onToggled ) {
this . onToggled ( ) ;
} else {
// Fallback
this . pdfViewer . forceRendering ( ) ;
this . pdfThumbnailViewer . forceRendering ( ) ;
}
} ,
/ * *
* @ private
* /
_updateThumbnailViewer : function PDFSidebar _updateThumbnailViewer ( ) {
var pdfViewer = this . pdfViewer ;
var thumbnailViewer = this . pdfThumbnailViewer ;
// Use the rendered pages to set the corresponding thumbnail images.
var pagesCount = pdfViewer . pagesCount ;
for ( var pageIndex = 0 ; pageIndex < pagesCount ; pageIndex ++ ) {
var pageView = pdfViewer . getPageView ( pageIndex ) ;
if ( pageView && pageView . renderingState === RenderingStates . FINISHED ) {
var thumbnailView = thumbnailViewer . getThumbnail ( pageIndex ) ;
thumbnailView . setImage ( pageView ) ;
}
}
thumbnailViewer . scrollThumbnailIntoView ( pdfViewer . currentPageNumber ) ;
} ,
/ * *
* @ private
* /
_addEventListeners : function PDFSidebar _addEventListeners ( ) {
var self = this ;
self . mainContainer . addEventListener ( 'transitionend' , function ( evt ) {
if ( evt . target === /* mainContainer */
this ) {
self . outerContainer . classList . remove ( 'sidebarMoving' ) ;
}
} ) ;
// Buttons for switching views.
self . thumbnailButton . addEventListener ( 'click' , function ( ) {
self . switchView ( SidebarView . THUMBS ) ;
} ) ;
self . outlineButton . addEventListener ( 'click' , function ( ) {
self . switchView ( SidebarView . OUTLINE ) ;
} ) ;
self . outlineButton . addEventListener ( 'dblclick' , function ( ) {
self . pdfOutlineViewer . toggleOutlineTree ( ) ;
} ) ;
self . attachmentsButton . addEventListener ( 'click' , function ( ) {
self . switchView ( SidebarView . ATTACHMENTS ) ;
} ) ;
// Disable/enable views.
self . eventBus . on ( 'outlineloaded' , function ( e ) {
var outlineCount = e . outlineCount ;
self . outlineButton . disabled = ! outlineCount ;
if ( ! outlineCount && self . active === SidebarView . OUTLINE ) {
self . switchView ( SidebarView . THUMBS ) ;
}
} ) ;
self . eventBus . on ( 'attachmentsloaded' , function ( e ) {
var attachmentsCount = e . attachmentsCount ;
self . attachmentsButton . disabled = ! attachmentsCount ;
if ( ! attachmentsCount && self . active === SidebarView . ATTACHMENTS ) {
self . switchView ( SidebarView . THUMBS ) ;
}
} ) ;
// Update the thumbnailViewer, if visible, when exiting presentation mode.
self . eventBus . on ( 'presentationmodechanged' , function ( e ) {
if ( ! e . active && ! e . switchInProgress && self . isThumbnailViewVisible ) {
self . _updateThumbnailViewer ( ) ;
}
} ) ;
}
} ;
return PDFSidebar ;
} ( ) ;
exports . SidebarView = SidebarView ;
exports . PDFSidebar = PDFSidebar ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebUIUtils = { } , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , pdfjsLib ) {
var CSS _UNITS = 96.0 / 72.0 ;
var DEFAULT _SCALE _VALUE = 'auto' ;
var DEFAULT _SCALE = 1.0 ;
var UNKNOWN _SCALE = 0 ;
var MAX _AUTO _SCALE = 1.25 ;
var SCROLLBAR _PADDING = 40 ;
var VERTICAL _PADDING = 5 ;
var mozL10n = document . mozL10n || document . webL10n ;
var PDFJS = pdfjsLib . PDFJS ;
/ * *
* Disables fullscreen support , and by extension Presentation Mode ,
* in browsers which support the fullscreen API .
* @ var { boolean }
* /
PDFJS . disableFullscreen = PDFJS . disableFullscreen === undefined ? false : PDFJS . disableFullscreen ;
/ * *
* Enables CSS only zooming .
* @ var { boolean }
* /
PDFJS . useOnlyCssZoom = PDFJS . useOnlyCssZoom === undefined ? false : PDFJS . useOnlyCssZoom ;
/ * *
* The maximum supported canvas size in total pixels e . g . width * height .
* The default value is 4096 * 4096. Use - 1 for no limit .
* @ var { number }
* /
PDFJS . maxCanvasPixels = PDFJS . maxCanvasPixels === undefined ? 16777216 : PDFJS . maxCanvasPixels ;
/ * *
* Disables saving of the last position of the viewed PDF .
* @ var { boolean }
* /
PDFJS . disableHistory = PDFJS . disableHistory === undefined ? false : PDFJS . disableHistory ;
/ * *
* Disables creation of the text layer that used for text selection and search .
* @ var { boolean }
* /
PDFJS . disableTextLayer = PDFJS . disableTextLayer === undefined ? false : PDFJS . disableTextLayer ;
/ * *
* Disables maintaining the current position in the document when zooming .
* /
PDFJS . ignoreCurrentPositionOnZoom = PDFJS . ignoreCurrentPositionOnZoom === undefined ? false : PDFJS . ignoreCurrentPositionOnZoom ;
/ * *
* Returns scale factor for the canvas . It makes sense for the HiDPI displays .
* @ return { Object } The object with horizontal ( sx ) and vertical ( sy )
scales . The scaled property is set to false if scaling is
not required , true otherwise .
* /
function getOutputScale ( ctx ) {
var devicePixelRatio = window . devicePixelRatio || 1 ;
var backingStoreRatio = ctx . webkitBackingStorePixelRatio || ctx . mozBackingStorePixelRatio || ctx . msBackingStorePixelRatio || ctx . oBackingStorePixelRatio || ctx . backingStorePixelRatio || 1 ;
var pixelRatio = devicePixelRatio / backingStoreRatio ;
return {
sx : pixelRatio ,
sy : pixelRatio ,
scaled : pixelRatio !== 1
} ;
}
/ * *
* Scrolls specified element into view of its parent .
* @ param { Object } element - The element to be visible .
* @ param { Object } spot - An object with optional top and left properties ,
* specifying the offset from the top left edge .
* @ param { boolean } skipOverflowHiddenElements - Ignore elements that have
* the CSS rule ` overflow: hidden; ` set . The default is false .
* /
function scrollIntoView ( element , spot , skipOverflowHiddenElements ) {
// Assuming offsetParent is available (it's not available when viewer is in
// hidden iframe or object). We have to scroll: if the offsetParent is not set
// producing the error. See also animationStartedClosure.
var parent = element . offsetParent ;
if ( ! parent ) {
console . error ( 'offsetParent is not set -- cannot scroll' ) ;
return ;
}
var checkOverflow = skipOverflowHiddenElements || false ;
var offsetY = element . offsetTop + element . clientTop ;
var offsetX = element . offsetLeft + element . clientLeft ;
while ( parent . clientHeight === parent . scrollHeight || checkOverflow && getComputedStyle ( parent ) . overflow === 'hidden' ) {
if ( parent . dataset . _scaleY ) {
offsetY /= parent . dataset . _scaleY ;
offsetX /= parent . dataset . _scaleX ;
}
offsetY += parent . offsetTop ;
offsetX += parent . offsetLeft ;
parent = parent . offsetParent ;
if ( ! parent ) {
return ;
}
}
// no need to scroll
if ( spot ) {
if ( spot . top !== undefined ) {
offsetY += spot . top ;
}
if ( spot . left !== undefined ) {
offsetX += spot . left ;
parent . scrollLeft = offsetX ;
}
}
parent . scrollTop = offsetY ;
}
/ * *
* Helper function to start monitoring the scroll event and converting them into
* PDF . js friendly one : with scroll debounce and scroll direction .
* /
function watchScroll ( viewAreaElement , callback ) {
var debounceScroll = function debounceScroll ( evt ) {
if ( rAF ) {
return ;
}
// schedule an invocation of scroll for next animation frame.
rAF = window . requestAnimationFrame ( function viewAreaElementScrolled ( ) {
rAF = null ;
var currentY = viewAreaElement . scrollTop ;
var lastY = state . lastY ;
if ( currentY !== lastY ) {
state . down = currentY > lastY ;
}
state . lastY = currentY ;
callback ( state ) ;
} ) ;
} ;
var state = {
down : true ,
lastY : viewAreaElement . scrollTop ,
_eventHandler : debounceScroll
} ;
var rAF = null ;
viewAreaElement . addEventListener ( 'scroll' , debounceScroll , true ) ;
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
* passes a given condition . The items are expected to be sorted in the sense
* that if the condition is true for one item in the array , then it is also true
* for all following items .
*
* @ returns { Number } Index of the first array element to pass the test ,
* or | items . length | if no such element exists .
* /
function binarySearchFirstItem ( items , condition ) {
var minIndex = 0 ;
var maxIndex = items . length - 1 ;
if ( items . length === 0 || ! condition ( items [ maxIndex ] ) ) {
return items . length ;
}
if ( condition ( items [ minIndex ] ) ) {
return minIndex ;
}
while ( minIndex < maxIndex ) {
var currentIndex = minIndex + maxIndex >> 1 ;
var currentItem = items [ currentIndex ] ;
if ( condition ( currentItem ) ) {
maxIndex = currentIndex ;
} else {
minIndex = currentIndex + 1 ;
}
}
return minIndex ;
}
/* === maxIndex */
/ * *
* Approximates float number as a fraction using Farey sequence ( max order
* of 8 ) .
* @ param { number } x - Positive float number .
* @ returns { Array } Estimated fraction : the first array item is a numerator ,
* the second one is a denominator .
* /
function approximateFraction ( x ) {
// Fast paths for int numbers or their inversions.
if ( Math . floor ( x ) === x ) {
return [
x ,
1
] ;
}
var xinv = 1 / x ;
var limit = 8 ;
if ( xinv > limit ) {
return [
1 ,
limit
] ;
} else if ( Math . floor ( xinv ) === xinv ) {
return [
1 ,
xinv
] ;
}
var x _ = x > 1 ? xinv : x ;
// a/b and c/d are neighbours in Farey sequence.
var a = 0 , b = 1 , c = 1 , d = 1 ;
// Limiting search to order 8.
while ( true ) {
// Generating next term in sequence (order of q).
var p = a + c , q = b + d ;
if ( q > limit ) {
break ;
}
if ( x _ <= p / q ) {
c = p ;
d = q ;
} else {
a = p ;
b = q ;
}
}
// Select closest of the neighbours to x.
if ( x _ - a / b < c / d - x _ ) {
return x _ === x ? [
a ,
b
] : [
b ,
a
] ;
} else {
return x _ === x ? [
c ,
d
] : [
d ,
c
] ;
}
}
function roundToDivide ( x , div ) {
var r = x % div ;
return r === 0 ? x : Math . round ( x - r + div ) ;
}
/ * *
* Generic helper to find out what elements are visible within a scroll pane .
* /
function getVisibleElements ( scrollEl , views , sortByVisibility ) {
var top = scrollEl . scrollTop , bottom = top + scrollEl . clientHeight ;
var left = scrollEl . scrollLeft , right = left + scrollEl . clientWidth ;
function isElementBottomBelowViewTop ( view ) {
var element = view . div ;
var elementBottom = element . offsetTop + element . clientTop + element . clientHeight ;
return elementBottom > top ;
}
var visible = [ ] , view , element ;
var currentHeight , viewHeight , hiddenHeight , percentHeight ;
var currentWidth , viewWidth ;
var firstVisibleElementInd = views . length === 0 ? 0 : binarySearchFirstItem ( views , isElementBottomBelowViewTop ) ;
for ( var i = firstVisibleElementInd , ii = views . length ; i < ii ; i ++ ) {
view = views [ i ] ;
element = view . div ;
currentHeight = element . offsetTop + element . clientTop ;
viewHeight = element . clientHeight ;
if ( currentHeight > bottom ) {
break ;
}
currentWidth = element . offsetLeft + element . clientLeft ;
viewWidth = element . clientWidth ;
if ( currentWidth + viewWidth < left || currentWidth > right ) {
continue ;
}
hiddenHeight = Math . max ( 0 , top - currentHeight ) + Math . max ( 0 , currentHeight + viewHeight - bottom ) ;
percentHeight = ( viewHeight - hiddenHeight ) * 100 / viewHeight | 0 ;
visible . push ( {
id : view . id ,
x : currentWidth ,
y : currentHeight ,
view : view ,
percent : percentHeight
} ) ;
}
var first = visible [ 0 ] ;
var last = visible [ visible . length - 1 ] ;
if ( sortByVisibility ) {
visible . sort ( function ( a , b ) {
var pc = a . percent - b . percent ;
if ( Math . abs ( pc ) > 0.001 ) {
return - pc ;
}
return a . id - b . id ;
} ) ;
}
// ensure stability
return {
first : first ,
last : last ,
views : visible
} ;
}
/ * *
* Event handler to suppress context menu .
* /
function noContextMenuHandler ( e ) {
e . preventDefault ( ) ;
}
/ * *
* Returns the filename or guessed filename from the url ( see issue 3455 ) .
* url { String } The original PDF location .
* @ return { String } Guessed PDF file name .
* /
function getPDFFileNameFromURL ( url ) {
var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/ ;
// SCHEME HOST 1.PATH 2.QUERY 3.REF
// Pattern to get last matching NAME.pdf
var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i ;
var splitURI = reURI . exec ( url ) ;
var suggestedFilename = reFilename . exec ( splitURI [ 1 ] ) || reFilename . exec ( splitURI [ 2 ] ) || reFilename . exec ( splitURI [ 3 ] ) ;
if ( suggestedFilename ) {
suggestedFilename = suggestedFilename [ 0 ] ;
if ( suggestedFilename . indexOf ( '%' ) !== - 1 ) {
// URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
try {
suggestedFilename = reFilename . exec ( decodeURIComponent ( suggestedFilename ) ) [ 0 ] ;
} catch ( e ) {
}
}
}
return suggestedFilename || 'document.pdf' ;
}
function normalizeWheelEventDelta ( evt ) {
var delta = Math . sqrt ( evt . deltaX * evt . deltaX + evt . deltaY * evt . deltaY ) ;
var angle = Math . atan2 ( evt . deltaY , evt . deltaX ) ;
if ( - 0.25 * Math . PI < angle && angle < 0.75 * Math . PI ) {
// All that is left-up oriented has to change the sign.
delta = - delta ;
}
var MOUSE _DOM _DELTA _PIXEL _MODE = 0 ;
var MOUSE _DOM _DELTA _LINE _MODE = 1 ;
var MOUSE _PIXELS _PER _LINE = 30 ;
var MOUSE _LINES _PER _PAGE = 30 ;
// Converts delta to per-page units
if ( evt . deltaMode === MOUSE _DOM _DELTA _PIXEL _MODE ) {
delta /= MOUSE _PIXELS _PER _LINE * MOUSE _LINES _PER _PAGE ;
} else if ( evt . deltaMode === MOUSE _DOM _DELTA _LINE _MODE ) {
delta /= MOUSE _LINES _PER _PAGE ;
}
return delta ;
}
/ * *
* Simple event bus for an application . Listeners are attached using the
* ` on ` and ` off ` methods . To raise an event , the ` dispatch ` method shall be
* used .
* /
var EventBus = function EventBusClosure ( ) {
function EventBus ( ) {
this . _listeners = Object . create ( null ) ;
}
EventBus . prototype = {
on : function EventBus _on ( eventName , listener ) {
var eventListeners = this . _listeners [ eventName ] ;
if ( ! eventListeners ) {
eventListeners = [ ] ;
this . _listeners [ eventName ] = eventListeners ;
}
eventListeners . push ( listener ) ;
} ,
off : function EventBus _on ( eventName , listener ) {
var eventListeners = this . _listeners [ eventName ] ;
var i ;
if ( ! eventListeners || ( i = eventListeners . indexOf ( listener ) ) < 0 ) {
return ;
}
eventListeners . splice ( i , 1 ) ;
} ,
dispatch : function EventBus _dispath ( eventName ) {
var eventListeners = this . _listeners [ eventName ] ;
if ( ! eventListeners || eventListeners . length === 0 ) {
return ;
}
// Passing all arguments after the eventName to the listeners.
var 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 ) {
listener . apply ( null , args ) ;
} ) ;
}
} ;
return EventBus ;
} ( ) ;
var ProgressBar = function ProgressBarClosure ( ) {
function clamp ( v , min , max ) {
return Math . min ( Math . max ( v , min ) , max ) ;
}
function ProgressBar ( id , opts ) {
this . visible = true ;
// Fetch the sub-elements for later.
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 = opts . height || 100 ;
this . width = opts . width || 100 ;
this . units = opts . units || '%' ;
// Initialize heights.
this . div . style . height = this . height + this . units ;
this . percent = 0 ;
}
ProgressBar . prototype = {
updateBar : function ProgressBar _updateBar ( ) {
if ( this . _indeterminate ) {
this . div . classList . add ( 'indeterminate' ) ;
this . div . style . width = this . width + this . units ;
return ;
}
this . div . classList . remove ( 'indeterminate' ) ;
var progressSize = this . width * this . _percent / 100 ;
this . div . style . width = progressSize + this . units ;
} ,
get percent ( ) {
return this . _percent ;
} ,
set percent ( val ) {
this . _indeterminate = isNaN ( val ) ;
this . _percent = clamp ( val , 0 , 100 ) ;
this . updateBar ( ) ;
} ,
setWidth : function ProgressBar _setWidth ( viewer ) {
if ( viewer ) {
var container = viewer . parentNode ;
var scrollbarWidth = container . offsetWidth - viewer . offsetWidth ;
if ( scrollbarWidth > 0 ) {
this . bar . setAttribute ( 'style' , 'width: calc(100% - ' + scrollbarWidth + 'px);' ) ;
}
}
} ,
hide : function ProgressBar _hide ( ) {
if ( ! this . visible ) {
return ;
}
this . visible = false ;
this . bar . classList . add ( 'hidden' ) ;
document . body . classList . remove ( 'loadingInProgress' ) ;
} ,
show : function ProgressBar _show ( ) {
if ( this . visible ) {
return ;
}
this . visible = true ;
document . body . classList . add ( 'loadingInProgress' ) ;
this . bar . classList . remove ( 'hidden' ) ;
}
} ;
return ProgressBar ;
} ( ) ;
exports . CSS _UNITS = CSS _UNITS ;
exports . DEFAULT _SCALE _VALUE = DEFAULT _SCALE _VALUE ;
exports . DEFAULT _SCALE = DEFAULT _SCALE ;
exports . UNKNOWN _SCALE = UNKNOWN _SCALE ;
exports . MAX _AUTO _SCALE = MAX _AUTO _SCALE ;
exports . SCROLLBAR _PADDING = SCROLLBAR _PADDING ;
exports . VERTICAL _PADDING = VERTICAL _PADDING ;
exports . mozL10n = mozL10n ;
exports . EventBus = EventBus ;
exports . ProgressBar = ProgressBar ;
exports . getPDFFileNameFromURL = getPDFFileNameFromURL ;
exports . noContextMenuHandler = noContextMenuHandler ;
exports . parseQueryString = parseQueryString ;
exports . getVisibleElements = getVisibleElements ;
exports . roundToDivide = roundToDivide ;
exports . approximateFraction = approximateFraction ;
exports . getOutputScale = getOutputScale ;
exports . scrollIntoView = scrollIntoView ;
exports . watchScroll = watchScroll ;
exports . binarySearchFirstItem = binarySearchFirstItem ;
exports . normalizeWheelEventDelta = normalizeWheelEventDelta ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebDOMEvents = { } , root . pdfjsWebUIUtils ) ;
} ( this , function ( exports , uiUtils ) {
var EventBus = uiUtils . EventBus ;
// Attaching to the application event bus to dispatch events to the DOM for
// backwards viewer API compatibility.
function attachDOMEventsToEventBus ( eventBus ) {
eventBus . on ( 'documentload' , function ( ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'documentload' , true , true , { } ) ;
window . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'pagerendered' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'pagerendered' , true , true , {
pageNumber : e . pageNumber ,
cssTransform : e . cssTransform
} ) ;
e . source . div . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'textlayerrendered' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'textlayerrendered' , true , true , { pageNumber : e . pageNumber } ) ;
e . source . textLayerDiv . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'pagechange' , function ( e ) {
var event = document . createEvent ( 'UIEvents' ) ;
event . initUIEvent ( 'pagechange' , true , true , window , 0 ) ;
event . pageNumber = e . pageNumber ;
e . source . container . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'pagesinit' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'pagesinit' , true , true , null ) ;
e . source . container . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'pagesloaded' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'pagesloaded' , true , true , { pagesCount : e . pagesCount } ) ;
e . source . container . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'scalechange' , function ( e ) {
var event = document . createEvent ( 'UIEvents' ) ;
event . initUIEvent ( 'scalechange' , true , true , window , 0 ) ;
event . scale = e . scale ;
event . presetValue = e . presetValue ;
e . source . container . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'updateviewarea' , function ( e ) {
var event = document . createEvent ( 'UIEvents' ) ;
event . initUIEvent ( 'updateviewarea' , true , true , window , 0 ) ;
event . location = e . location ;
e . source . container . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'find' , function ( e ) {
if ( e . source === window ) {
return ;
}
// event comes from FirefoxCom, no need to replicate
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'find' + e . type , true , true , {
query : e . query ,
phraseSearch : e . phraseSearch ,
caseSensitive : e . caseSensitive ,
highlightAll : e . highlightAll ,
findPrevious : e . findPrevious
} ) ;
window . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'attachmentsloaded' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'attachmentsloaded' , true , true , { attachmentsCount : e . attachmentsCount } ) ;
e . source . container . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'sidebarviewchanged' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'sidebarviewchanged' , true , true , { view : e . view } ) ;
e . source . outerContainer . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'pagemode' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'pagemode' , true , true , { mode : e . mode } ) ;
e . source . pdfViewer . container . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'namedaction' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'namedaction' , true , true , { action : e . action } ) ;
e . source . pdfViewer . container . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'presentationmodechanged' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'presentationmodechanged' , true , true , {
active : e . active ,
switchInProgress : e . switchInProgress
} ) ;
window . dispatchEvent ( event ) ;
} ) ;
eventBus . on ( 'outlineloaded' , function ( e ) {
var event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( 'outlineloaded' , true , true , { outlineCount : e . outlineCount } ) ;
e . source . container . dispatchEvent ( event ) ;
} ) ;
}
var globalEventBus = null ;
function getGlobalEventBus ( ) {
if ( globalEventBus ) {
return globalEventBus ;
}
globalEventBus = new EventBus ( ) ;
attachDOMEventsToEventBus ( globalEventBus ) ;
return globalEventBus ;
}
exports . attachDOMEventsToEventBus = attachDOMEventsToEventBus ;
exports . getGlobalEventBus = getGlobalEventBus ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPasswordPrompt = { } , root . pdfjsWebUIUtils , root . pdfjsWebOverlayManager , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , uiUtils , overlayManager , pdfjsLib ) {
var mozL10n = uiUtils . mozL10n ;
var OverlayManager = overlayManager . OverlayManager ;
/ * *
* @ typedef { Object } PasswordPromptOptions
* @ property { string } overlayName - Name of the overlay for the overlay manager .
* @ property { HTMLDivElement } container - Div container for the overlay .
* @ property { HTMLParagraphElement } label - Label containing instructions for
* entering the password .
* @ property { HTMLInputElement } input - Input field for entering the password .
* @ property { HTMLButtonElement } submitButton - Button for submitting the
* password .
* @ property { HTMLButtonElement } cancelButton - Button for cancelling password
* entry .
* /
/ * *
* @ class
* /
var PasswordPrompt = function PasswordPromptClosure ( ) {
/ * *
* @ constructs PasswordPrompt
* @ param { PasswordPromptOptions } options
* /
function PasswordPrompt ( options ) {
this . overlayName = options . overlayName ;
this . container = options . container ;
this . label = options . label ;
this . input = options . input ;
this . submitButton = options . submitButton ;
this . cancelButton = options . cancelButton ;
this . updateCallback = null ;
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' , function ( e ) {
if ( e . keyCode === 13 ) {
// Enter key
this . verify ( ) ;
}
} . bind ( this ) ) ;
OverlayManager . register ( this . overlayName , this . container , this . close . bind ( this ) , true ) ;
}
PasswordPrompt . prototype = {
open : function PasswordPrompt _open ( ) {
OverlayManager . open ( this . overlayName ) . then ( function ( ) {
this . input . type = 'password' ;
this . input . focus ( ) ;
var promptString = mozL10n . get ( 'password_label' , null , 'Enter the password to open this PDF file.' ) ;
if ( this . reason === pdfjsLib . PasswordResponses . INCORRECT _PASSWORD ) {
promptString = mozL10n . get ( 'password_invalid' , null , 'Invalid password. Please try again.' ) ;
}
this . label . textContent = promptString ;
} . bind ( this ) ) ;
} ,
close : function PasswordPrompt _close ( ) {
OverlayManager . close ( this . overlayName ) . then ( function ( ) {
this . input . value = '' ;
this . input . type = '' ;
} . bind ( this ) ) ;
} ,
verify : function PasswordPrompt _verify ( ) {
var password = this . input . value ;
if ( password && password . length > 0 ) {
this . close ( ) ;
return this . updateCallback ( password ) ;
}
} ,
setUpdateCallback : function PasswordPrompt _setUpdateCallback ( updateCallback , reason ) {
this . updateCallback = updateCallback ;
this . reason = reason ;
}
} ;
return PasswordPrompt ;
} ( ) ;
exports . PasswordPrompt = PasswordPrompt ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFDocumentProperties = { } , root . pdfjsWebUIUtils , root . pdfjsWebOverlayManager ) ;
} ( this , function ( exports , uiUtils , overlayManager ) {
var getPDFFileNameFromURL = uiUtils . getPDFFileNameFromURL ;
var mozL10n = uiUtils . mozL10n ;
var OverlayManager = overlayManager . OverlayManager ;
/ * *
* @ typedef { Object } PDFDocumentPropertiesOptions
* @ property { string } overlayName - Name / identifier for the overlay .
* @ property { Object } fields - Names and elements of the overlay ' s fields .
* @ property { HTMLButtonElement } closeButton - Button for closing the overlay .
* /
/ * *
* @ class
* /
var PDFDocumentProperties = function PDFDocumentPropertiesClosure ( ) {
/ * *
* @ constructs PDFDocumentProperties
* @ param { PDFDocumentPropertiesOptions } options
* /
function PDFDocumentProperties ( options ) {
this . fields = options . fields ;
this . overlayName = options . overlayName ;
this . container = options . container ;
this . rawFileSize = 0 ;
this . url = null ;
this . pdfDocument = null ;
// Bind the event listener for the Close button.
if ( options . closeButton ) {
options . closeButton . addEventListener ( 'click' , this . close . bind ( this ) ) ;
}
this . dataAvailablePromise = new Promise ( function ( resolve ) {
this . resolveDataAvailable = resolve ;
} . bind ( this ) ) ;
OverlayManager . register ( this . overlayName , this . container , this . close . bind ( this ) ) ;
}
PDFDocumentProperties . prototype = {
/ * *
* Open the document properties overlay .
* /
open : function PDFDocumentProperties _open ( ) {
Promise . all ( [
OverlayManager . open ( this . overlayName ) ,
this . dataAvailablePromise
] ) . then ( function ( ) {
this . _getProperties ( ) ;
} . bind ( this ) ) ;
} ,
/ * *
* Close the document properties overlay .
* /
close : function PDFDocumentProperties _close ( ) {
OverlayManager . close ( this . overlayName ) ;
} ,
/ * *
* Set the file size of the PDF document . This method is used to
* update the file size in the document properties overlay once it
* is known so we do not have to wait until the entire file is loaded .
*
* @ param { number } fileSize - The file size of the PDF document .
* /
setFileSize : function PDFDocumentProperties _setFileSize ( fileSize ) {
if ( fileSize > 0 ) {
this . rawFileSize = fileSize ;
}
} ,
/ * *
* Set a reference to the PDF document and the URL in order
* to populate the overlay fields with the document properties .
* Note that the overlay will contain no information if this method
* is not called .
*
* @ param { Object } pdfDocument - A reference to the PDF document .
* @ param { string } url - The URL of the document .
* /
setDocumentAndUrl : function PDFDocumentProperties _setDocumentAndUrl ( pdfDocument , url ) {
this . pdfDocument = pdfDocument ;
this . url = url ;
this . resolveDataAvailable ( ) ;
} ,
/ * *
* @ private
* /
_getProperties : function PDFDocumentProperties _getProperties ( ) {
if ( ! OverlayManager . active ) {
// If the dialog was closed before dataAvailablePromise was resolved,
// don't bother updating the properties.
return ;
}
// Get the file size (if it hasn't already been set).
this . pdfDocument . getDownloadInfo ( ) . then ( function ( data ) {
if ( data . length === this . rawFileSize ) {
return ;
}
this . setFileSize ( data . length ) ;
this . _updateUI ( this . fields [ 'fileSize' ] , this . _parseFileSize ( ) ) ;
} . bind ( this ) ) ;
// Get the document properties.
this . pdfDocument . getMetadata ( ) . then ( function ( data ) {
var content = {
'fileName' : getPDFFileNameFromURL ( this . url ) ,
'fileSize' : this . _parseFileSize ( ) ,
'title' : data . info . Title ,
'author' : data . info . Author ,
'subject' : data . info . Subject ,
'keywords' : data . info . Keywords ,
'creationDate' : this . _parseDate ( data . info . CreationDate ) ,
'modificationDate' : this . _parseDate ( data . info . ModDate ) ,
'creator' : data . info . Creator ,
'producer' : data . info . Producer ,
'version' : data . info . PDFFormatVersion ,
'pageCount' : this . pdfDocument . numPages
} ;
// Show the properties in the dialog.
for ( var identifier in content ) {
this . _updateUI ( this . fields [ identifier ] , content [ identifier ] ) ;
}
} . bind ( this ) ) ;
} ,
/ * *
* @ private
* /
_updateUI : function PDFDocumentProperties _updateUI ( field , content ) {
if ( field && content !== undefined && content !== '' ) {
field . textContent = content ;
}
} ,
/ * *
* @ private
* /
_parseFileSize : function PDFDocumentProperties _parseFileSize ( ) {
var fileSize = this . rawFileSize , kb = fileSize / 1024 ;
if ( ! kb ) {
return ;
} else if ( kb < 1024 ) {
return mozL10n . get ( 'document_properties_kb' , {
size _kb : ( + kb . toPrecision ( 3 ) ) . toLocaleString ( ) ,
size _b : fileSize . toLocaleString ( )
} , '{{size_kb}} KB ({{size_b}} bytes)' ) ;
} else {
return mozL10n . get ( 'document_properties_mb' , {
size _mb : ( + ( kb / 1024 ) . toPrecision ( 3 ) ) . toLocaleString ( ) ,
size _b : fileSize . toLocaleString ( )
} , '{{size_mb}} MB ({{size_b}} bytes)' ) ;
}
} ,
/ * *
* @ private
* /
_parseDate : function PDFDocumentProperties _parseDate ( inputDate ) {
// This is implemented according to the PDF specification, but note that
// Adobe Reader doesn't handle changing the date to universal time
// and doesn't use the user's time zone (they're effectively ignoring
// the HH' and mm' parts of the date string).
var dateToParse = inputDate ;
if ( dateToParse === undefined ) {
return '' ;
}
// Remove the D: prefix if it is available.
if ( dateToParse . substring ( 0 , 2 ) === 'D:' ) {
dateToParse = dateToParse . substring ( 2 ) ;
}
// Get all elements from the PDF date string.
// JavaScript's Date object expects the month to be between
// 0 and 11 instead of 1 and 12, so we're correcting for this.
var year = parseInt ( dateToParse . substring ( 0 , 4 ) , 10 ) ;
var month = parseInt ( dateToParse . substring ( 4 , 6 ) , 10 ) - 1 ;
var day = parseInt ( dateToParse . substring ( 6 , 8 ) , 10 ) ;
var hours = parseInt ( dateToParse . substring ( 8 , 10 ) , 10 ) ;
var minutes = parseInt ( dateToParse . substring ( 10 , 12 ) , 10 ) ;
var seconds = parseInt ( dateToParse . substring ( 12 , 14 ) , 10 ) ;
var utRel = dateToParse . substring ( 14 , 15 ) ;
var offsetHours = parseInt ( dateToParse . substring ( 15 , 17 ) , 10 ) ;
var offsetMinutes = parseInt ( dateToParse . substring ( 18 , 20 ) , 10 ) ;
// As per spec, utRel = 'Z' means equal to universal time.
// The other cases ('-' and '+') have to be handled here.
if ( utRel === '-' ) {
hours += offsetHours ;
minutes += offsetMinutes ;
} else if ( utRel === '+' ) {
hours -= offsetHours ;
minutes -= offsetMinutes ;
}
// Return the new date format from the user's locale.
var date = new Date ( Date . UTC ( year , month , day , hours , minutes , seconds ) ) ;
var dateString = date . toLocaleDateString ( ) ;
var timeString = date . toLocaleTimeString ( ) ;
return mozL10n . get ( 'document_properties_date_string' , {
date : dateString ,
time : timeString
} , '{{date}}, {{time}}' ) ;
}
} ;
return PDFDocumentProperties ;
} ( ) ;
exports . PDFDocumentProperties = PDFDocumentProperties ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFFindController = { } , root . pdfjsWebUIUtils ) ;
} ( this , function ( exports , uiUtils ) {
var scrollIntoView = uiUtils . scrollIntoView ;
var FindStates = {
FIND _FOUND : 0 ,
FIND _NOTFOUND : 1 ,
FIND _WRAPPED : 2 ,
FIND _PENDING : 3
} ;
var FIND _SCROLL _OFFSET _TOP = - 50 ;
var FIND _SCROLL _OFFSET _LEFT = - 400 ;
var 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
/ * *
* Provides "search" or "find" functionality for the PDF .
* This object actually performs the search for a given string .
* /
var PDFFindController = function PDFFindControllerClosure ( ) {
function PDFFindController ( options ) {
this . pdfViewer = options . pdfViewer || null ;
this . onUpdateResultsCount = null ;
this . onUpdateState = null ;
this . reset ( ) ;
// Compile the regular expression for text normalization once.
var replace = Object . keys ( CHARACTERS _TO _NORMALIZE ) . join ( '' ) ;
this . normalizationRegex = new RegExp ( '[' + replace + ']' , 'g' ) ;
}
PDFFindController . prototype = {
reset : function PDFFindController _reset ( ) {
this . startedTextExtraction = false ;
this . extractTextPromises = [ ] ;
this . pendingFindMatches = Object . create ( null ) ;
this . active = false ;
// If active, find results will be highlighted.
this . pageContents = [ ] ;
// Stores the text for each page.
this . pageMatches = [ ] ;
this . pageMatchesLength = null ;
this . matchCount = 0 ;
this . selected = {
// Currently selected match.
pageIdx : - 1 ,
matchIdx : - 1
} ;
this . offset = {
// Where the find algorithm currently is in the document.
pageIdx : null ,
matchIdx : null
} ;
this . pagesToSearch = null ;
this . resumePageIdx = null ;
this . state = null ;
this . dirtyMatch = false ;
this . findTimeout = null ;
this . firstPagePromise = new Promise ( function ( resolve ) {
this . resolveFirstPage = resolve ;
} . bind ( this ) ) ;
} ,
normalize : function PDFFindController _normalize ( text ) {
return text . replace ( this . normalizationRegex , function ( ch ) {
return CHARACTERS _TO _NORMALIZE [ ch ] ;
} ) ;
} ,
// Helper for multiple search - fills matchesWithLength array
// and takes into account cases when one search term
// include another search term (for example, "tamed tame" or "this is").
// Looking for intersecting terms in the 'matches' and
// leave elements with a longer match-length.
_prepareMatches : function PDFFindController _prepareMatches ( matchesWithLength , matches , matchesLength ) {
function isSubTerm ( matchesWithLength , currentIndex ) {
var currentElem , prevElem , nextElem ;
currentElem = matchesWithLength [ currentIndex ] ;
nextElem = matchesWithLength [ currentIndex + 1 ] ;
// checking for cases like "TAMEd TAME"
if ( currentIndex < matchesWithLength . length - 1 && currentElem . match === nextElem . match ) {
currentElem . skipped = true ;
return true ;
}
// checking for cases like "thIS IS"
for ( var i = currentIndex - 1 ; i >= 0 ; i -- ) {
prevElem = matchesWithLength [ i ] ;
if ( prevElem . skipped ) {
continue ;
}
if ( prevElem . match + prevElem . matchLength < currentElem . match ) {
break ;
}
if ( prevElem . match + prevElem . matchLength >= currentElem . match + currentElem . matchLength ) {
currentElem . skipped = true ;
return true ;
}
}
return false ;
}
var i , len ;
// Sorting array of objects { match: <match>, matchLength: <matchLength> }
// in increasing index first and then the lengths.
matchesWithLength . sort ( function ( a , b ) {
return a . match === b . match ? a . matchLength - b . matchLength : a . match - b . match ;
} ) ;
for ( i = 0 , len = matchesWithLength . length ; i < len ; i ++ ) {
if ( isSubTerm ( matchesWithLength , i ) ) {
continue ;
}
matches . push ( matchesWithLength [ i ] . match ) ;
matchesLength . push ( matchesWithLength [ i ] . matchLength ) ;
}
} ,
calcFindPhraseMatch : function PDFFindController _calcFindPhraseMatch ( query , pageIndex , pageContent ) {
var matches = [ ] ;
var queryLen = query . length ;
var matchIdx = - queryLen ;
while ( true ) {
matchIdx = pageContent . indexOf ( query , matchIdx + queryLen ) ;
if ( matchIdx === - 1 ) {
break ;
}
matches . push ( matchIdx ) ;
}
this . pageMatches [ pageIndex ] = matches ;
} ,
calcFindWordMatch : function PDFFindController _calcFindWordMatch ( query , pageIndex , pageContent ) {
var matchesWithLength = [ ] ;
// Divide the query into pieces and search for text on each piece.
var queryArray = query . match ( /\S+/g ) ;
var subquery , subqueryLen , matchIdx ;
for ( var i = 0 , len = queryArray . length ; i < len ; i ++ ) {
subquery = queryArray [ i ] ;
subqueryLen = subquery . length ;
matchIdx = - subqueryLen ;
while ( true ) {
matchIdx = pageContent . indexOf ( subquery , matchIdx + subqueryLen ) ;
if ( matchIdx === - 1 ) {
break ;
}
// Other searches do not, so we store the length.
matchesWithLength . push ( {
match : matchIdx ,
matchLength : subqueryLen ,
skipped : false
} ) ;
}
}
// Prepare arrays for store the matches.
if ( ! this . pageMatchesLength ) {
this . pageMatchesLength = [ ] ;
}
this . pageMatchesLength [ pageIndex ] = [ ] ;
this . pageMatches [ pageIndex ] = [ ] ;
// Sort matchesWithLength, clean up intersecting terms
// and put the result into the two arrays.
this . _prepareMatches ( matchesWithLength , this . pageMatches [ pageIndex ] , this . pageMatchesLength [ pageIndex ] ) ;
} ,
calcFindMatch : function PDFFindController _calcFindMatch ( pageIndex ) {
var pageContent = this . normalize ( this . pageContents [ pageIndex ] ) ;
var query = this . normalize ( this . state . query ) ;
var caseSensitive = this . state . caseSensitive ;
var phraseSearch = this . state . phraseSearch ;
var queryLen = query . length ;
if ( queryLen === 0 ) {
// Do nothing: the matches should be wiped out already.
return ;
}
if ( ! caseSensitive ) {
pageContent = pageContent . toLowerCase ( ) ;
query = query . toLowerCase ( ) ;
}
if ( phraseSearch ) {
this . calcFindPhraseMatch ( query , pageIndex , pageContent ) ;
} else {
this . calcFindWordMatch ( query , pageIndex , pageContent ) ;
}
this . updatePage ( pageIndex ) ;
if ( this . resumePageIdx === pageIndex ) {
this . resumePageIdx = null ;
this . nextPageMatch ( ) ;
}
// Update the matches count
if ( this . pageMatches [ pageIndex ] . length > 0 ) {
this . matchCount += this . pageMatches [ pageIndex ] . length ;
this . updateUIResultsCount ( ) ;
}
} ,
extractText : function PDFFindController _extractText ( ) {
if ( this . startedTextExtraction ) {
return ;
}
this . startedTextExtraction = true ;
this . pageContents = [ ] ;
var extractTextPromisesResolves = [ ] ;
var numPages = this . pdfViewer . pagesCount ;
for ( var i = 0 ; i < numPages ; i ++ ) {
this . extractTextPromises . push ( new Promise ( function ( resolve ) {
extractTextPromisesResolves . push ( resolve ) ;
} ) ) ;
}
var self = this ;
function extractPageText ( pageIndex ) {
self . pdfViewer . getPageTextContent ( pageIndex ) . then ( function textContentResolved ( textContent ) {
var textItems = textContent . items ;
var str = [ ] ;
for ( var i = 0 , len = textItems . length ; i < len ; i ++ ) {
str . push ( textItems [ i ] . str ) ;
}
// Store the pageContent as a string.
self . pageContents . push ( str . join ( '' ) ) ;
extractTextPromisesResolves [ pageIndex ] ( pageIndex ) ;
if ( pageIndex + 1 < self . pdfViewer . pagesCount ) {
extractPageText ( pageIndex + 1 ) ;
}
} ) ;
}
extractPageText ( 0 ) ;
} ,
executeCommand : function PDFFindController _executeCommand ( cmd , state ) {
if ( this . state === null || cmd !== 'findagain' ) {
this . dirtyMatch = true ;
}
this . state = state ;
this . updateUIState ( FindStates . FIND _PENDING ) ;
this . firstPagePromise . then ( function ( ) {
this . extractText ( ) ;
clearTimeout ( this . findTimeout ) ;
if ( cmd === 'find' ) {
// Only trigger the find action after 250ms of silence.
this . findTimeout = setTimeout ( this . nextMatch . bind ( this ) , 250 ) ;
} else {
this . nextMatch ( ) ;
}
} . bind ( this ) ) ;
} ,
updatePage : function PDFFindController _updatePage ( index ) {
if ( this . selected . pageIdx === index ) {
// If the page is selected, scroll the page into view, which triggers
// rendering the page, which adds the textLayer. Once the textLayer is
// build, it will scroll onto the selected match.
this . pdfViewer . currentPageNumber = index + 1 ;
}
var page = this . pdfViewer . getPageView ( index ) ;
if ( page . textLayer ) {
page . textLayer . updateMatches ( ) ;
}
} ,
nextMatch : function PDFFindController _nextMatch ( ) {
var previous = this . state . findPrevious ;
var currentPageIndex = this . pdfViewer . currentPageNumber - 1 ;
var numPages = this . pdfViewer . pagesCount ;
this . active = true ;
if ( this . dirtyMatch ) {
// Need to recalculate the matches, reset everything.
this . dirtyMatch = false ;
this . selected . pageIdx = this . selected . matchIdx = - 1 ;
this . offset . pageIdx = currentPageIndex ;
this . offset . matchIdx = null ;
this . hadMatch = false ;
this . resumePageIdx = null ;
this . pageMatches = [ ] ;
this . matchCount = 0 ;
this . pageMatchesLength = null ;
var self = this ;
for ( var i = 0 ; i < numPages ; i ++ ) {
// Wipe out any previous highlighted matches.
this . updatePage ( i ) ;
// As soon as the text is extracted start finding the matches.
if ( ! ( i in this . pendingFindMatches ) ) {
this . pendingFindMatches [ i ] = true ;
this . extractTextPromises [ i ] . then ( function ( pageIdx ) {
delete self . pendingFindMatches [ pageIdx ] ;
self . calcFindMatch ( pageIdx ) ;
} ) ;
}
}
}
// If there's no query there's no point in searching.
if ( this . state . query === '' ) {
this . updateUIState ( FindStates . FIND _FOUND ) ;
return ;
}
// If we're waiting on a page, we return since we can't do anything else.
if ( this . resumePageIdx ) {
return ;
}
var offset = this . offset ;
// Keep track of how many pages we should maximally iterate through.
this . pagesToSearch = numPages ;
// If there's already a matchIdx that means we are iterating through a
// page's matches.
if ( offset . matchIdx !== null ) {
var numPageMatches = this . pageMatches [ offset . pageIdx ] . length ;
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.
this . hadMatch = true ;
offset . matchIdx = previous ? offset . matchIdx - 1 : offset . matchIdx + 1 ;
this . updateMatch ( true ) ;
return ;
}
// We went beyond the current page's matches, so we advance to
// the next page.
this . advanceOffsetPage ( previous ) ;
}
// Start searching through the page.
this . nextPageMatch ( ) ;
} ,
matchesReady : function PDFFindController _matchesReady ( matches ) {
var offset = this . offset ;
var numMatches = matches . length ;
var previous = this . state . findPrevious ;
if ( numMatches ) {
// There were matches for the page, so initialize the matchIdx.
this . hadMatch = true ;
offset . matchIdx = previous ? numMatches - 1 : 0 ;
this . updateMatch ( true ) ;
return true ;
} else {
// No matches, so attempt to search the next page.
this . advanceOffsetPage ( previous ) ;
if ( offset . wrapped ) {
offset . matchIdx = null ;
if ( this . pagesToSearch < 0 ) {
// No point in wrapping again, there were no matches.
this . updateMatch ( false ) ;
// while matches were not found, searching for a page
// with matches should nevertheless halt.
return true ;
}
}
// Matches were not found (and searching is not done).
return false ;
}
} ,
/ * *
* The method is called back from the text layer when match presentation
* is updated .
* @ param { number } pageIndex - page index .
* @ param { number } index - match index .
* @ param { Array } elements - text layer div elements array .
* @ param { number } beginIdx - start index of the div array for the match .
* /
updateMatchPosition : function PDFFindController _updateMatchPosition ( pageIndex , index , elements , beginIdx ) {
if ( this . selected . matchIdx === index && this . selected . pageIdx === pageIndex ) {
var spot = {
top : FIND _SCROLL _OFFSET _TOP ,
left : FIND _SCROLL _OFFSET _LEFT
} ;
scrollIntoView ( elements [ beginIdx ] , spot , /* skipOverflowHiddenElements = */
true ) ;
}
} ,
nextPageMatch : function PDFFindController _nextPageMatch ( ) {
if ( this . resumePageIdx !== null ) {
console . error ( 'There can only be one pending page.' ) ;
}
do {
var pageIdx = this . offset . pageIdx ;
var matches = this . pageMatches [ pageIdx ] ;
if ( ! matches ) {
// The matches don't exist yet for processing by "matchesReady",
// so set a resume point for when they do exist.
this . resumePageIdx = pageIdx ;
break ;
}
} while ( ! this . matchesReady ( matches ) ) ;
} ,
advanceOffsetPage : function PDFFindController _advanceOffsetPage ( previous ) {
var offset = this . offset ;
var numPages = this . extractTextPromises . length ;
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 . wrapped = true ;
}
} ,
updateMatch : function PDFFindController _updateMatch ( found ) {
var state = FindStates . FIND _NOTFOUND ;
var wrapped = this . offset . wrapped ;
this . offset . wrapped = false ;
if ( found ) {
var previousPage = this . selected . pageIdx ;
this . selected . pageIdx = this . offset . pageIdx ;
this . selected . matchIdx = this . offset . matchIdx ;
state = wrapped ? FindStates . FIND _WRAPPED : FindStates . FIND _FOUND ;
// Update the currently selected page to wipe out any selected matches.
if ( previousPage !== - 1 && previousPage !== this . selected . pageIdx ) {
this . updatePage ( previousPage ) ;
}
}
this . updateUIState ( state , this . state . findPrevious ) ;
if ( this . selected . pageIdx !== - 1 ) {
this . updatePage ( this . selected . pageIdx ) ;
}
} ,
updateUIResultsCount : function PDFFindController _updateUIResultsCount ( ) {
if ( this . onUpdateResultsCount ) {
this . onUpdateResultsCount ( this . matchCount ) ;
}
} ,
updateUIState : function PDFFindController _updateUIState ( state , previous ) {
if ( this . onUpdateState ) {
this . onUpdateState ( state , previous , this . matchCount ) ;
}
}
} ;
return PDFFindController ;
} ( ) ;
exports . FindStates = FindStates ;
exports . PDFFindController = PDFFindController ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFPresentationMode = { } , root . pdfjsWebUIUtils ) ;
} ( this , function ( exports , uiUtils ) {
var normalizeWheelEventDelta = uiUtils . normalizeWheelEventDelta ;
var DELAY _BEFORE _RESETTING _SWITCH _IN _PROGRESS = 1500 ;
// in ms
var DELAY _BEFORE _HIDING _CONTROLS = 3000 ;
// in ms
var ACTIVE _SELECTOR = 'pdfPresentationMode' ;
var CONTROLS _SELECTOR = 'pdfPresentationModeControls' ;
/ * *
* @ typedef { Object } PDFPresentationModeOptions
* @ property { HTMLDivElement } container - The container for the viewer element .
* @ property { HTMLDivElement } viewer - ( optional ) The viewer element .
* @ property { PDFViewer } pdfViewer - The document viewer .
* @ property { EventBus } eventBus - The application event bus .
* @ property { Array } contextMenuItems - ( optional ) The menuitems that are added
* to the context menu in Presentation Mode .
* /
/ * *
* @ class
* /
var PDFPresentationMode = function PDFPresentationModeClosure ( ) {
/ * *
* @ constructs PDFPresentationMode
* @ param { PDFPresentationModeOptions } options
* /
function PDFPresentationMode ( options ) {
this . container = options . container ;
this . viewer = options . viewer || options . container . firstElementChild ;
this . pdfViewer = options . pdfViewer ;
this . eventBus = options . eventBus ;
var contextMenuItems = options . contextMenuItems || null ;
this . active = false ;
this . args = null ;
this . contextMenuOpen = false ;
this . mouseScrollTimeStamp = 0 ;
this . mouseScrollDelta = 0 ;
this . touchSwipeState = null ;
if ( contextMenuItems ) {
contextMenuItems . contextFirstPage . addEventListener ( 'click' , function PDFPresentationMode _contextFirstPageClick ( e ) {
this . contextMenuOpen = false ;
this . eventBus . dispatch ( 'firstpage' ) ;
} . bind ( this ) ) ;
contextMenuItems . contextLastPage . addEventListener ( 'click' , function PDFPresentationMode _contextLastPageClick ( e ) {
this . contextMenuOpen = false ;
this . eventBus . dispatch ( 'lastpage' ) ;
} . bind ( this ) ) ;
contextMenuItems . contextPageRotateCw . addEventListener ( 'click' , function PDFPresentationMode _contextPageRotateCwClick ( e ) {
this . contextMenuOpen = false ;
this . eventBus . dispatch ( 'rotatecw' ) ;
} . bind ( this ) ) ;
contextMenuItems . contextPageRotateCcw . addEventListener ( 'click' , function PDFPresentationMode _contextPageRotateCcwClick ( e ) {
this . contextMenuOpen = false ;
this . eventBus . dispatch ( 'rotateccw' ) ;
} . bind ( this ) ) ;
}
}
PDFPresentationMode . prototype = {
/ * *
* Request the browser to enter fullscreen mode .
* @ returns { boolean } Indicating if the request was successful .
* /
request : function PDFPresentationMode _request ( ) {
if ( this . switchInProgress || this . active || ! this . viewer . hasChildNodes ( ) ) {
return false ;
}
this . _addFullscreenChangeListeners ( ) ;
this . _setSwitchInProgress ( ) ;
this . _notifyStateChange ( ) ;
if ( this . container . requestFullscreen ) {
this . container . requestFullscreen ( ) ;
} else if ( this . container . mozRequestFullScreen ) {
this . container . mozRequestFullScreen ( ) ;
} else if ( this . container . webkitRequestFullscreen ) {
this . container . webkitRequestFullscreen ( Element . ALLOW _KEYBOARD _INPUT ) ;
} else if ( this . container . msRequestFullscreen ) {
this . container . msRequestFullscreen ( ) ;
} else {
return false ;
}
this . args = {
page : this . pdfViewer . currentPageNumber ,
previousScale : this . pdfViewer . currentScaleValue
} ;
return true ;
} ,
/ * *
* @ private
* /
_mouseWheel : function PDFPresentationMode _mouseWheel ( evt ) {
if ( ! this . active ) {
return ;
}
evt . preventDefault ( ) ;
var delta = normalizeWheelEventDelta ( evt ) ;
var MOUSE _SCROLL _COOLDOWN _TIME = 50 ;
var PAGE _SWITCH _THRESHOLD = 0.1 ;
var currentTime = new Date ( ) . getTime ( ) ;
var storedTime = this . mouseScrollTimeStamp ;
// If we've already switched page, avoid accidentally switching again.
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 ) {
this . _resetMouseScrollState ( ) ;
}
this . mouseScrollDelta += delta ;
if ( Math . abs ( this . mouseScrollDelta ) >= PAGE _SWITCH _THRESHOLD ) {
var totalDelta = this . mouseScrollDelta ;
this . _resetMouseScrollState ( ) ;
var success = totalDelta > 0 ? this . _goToPreviousPage ( ) : this . _goToNextPage ( ) ;
if ( success ) {
this . mouseScrollTimeStamp = currentTime ;
}
}
} ,
get isFullscreen ( ) {
return ! ! ( document . fullscreenElement || document . mozFullScreen || document . webkitIsFullScreen || document . msFullscreenElement ) ;
} ,
/ * *
* @ private
* /
_goToPreviousPage : function PDFPresentationMode _goToPreviousPage ( ) {
var page = this . pdfViewer . currentPageNumber ;
// If we're at the first page, we don't need to do anything.
if ( page <= 1 ) {
return false ;
}
this . pdfViewer . currentPageNumber = page - 1 ;
return true ;
} ,
/ * *
* @ private
* /
_goToNextPage : function PDFPresentationMode _goToNextPage ( ) {
var page = this . pdfViewer . currentPageNumber ;
// If we're at the last page, we don't need to do anything.
if ( page >= this . pdfViewer . pagesCount ) {
return false ;
}
this . pdfViewer . currentPageNumber = page + 1 ;
return true ;
} ,
/ * *
* @ private
* /
_notifyStateChange : function PDFPresentationMode _notifyStateChange ( ) {
this . eventBus . dispatch ( 'presentationmodechanged' , {
source : this ,
active : this . active ,
switchInProgress : ! ! this . switchInProgress
} ) ;
} ,
/ * *
* Used to initialize a timeout when requesting Presentation Mode ,
* i . e . when the browser is requested to enter fullscreen mode .
* This timeout is used to prevent the current page from being scrolled
* partially , or completely , out of view when entering Presentation Mode .
* NOTE : This issue seems limited to certain zoom levels ( e . g . page - width ) .
* @ private
* /
_setSwitchInProgress : function PDFPresentationMode _setSwitchInProgress ( ) {
if ( this . switchInProgress ) {
clearTimeout ( this . switchInProgress ) ;
}
this . switchInProgress = setTimeout ( function switchInProgressTimeout ( ) {
this . _removeFullscreenChangeListeners ( ) ;
delete this . switchInProgress ;
this . _notifyStateChange ( ) ;
} . bind ( this ) , DELAY _BEFORE _RESETTING _SWITCH _IN _PROGRESS ) ;
} ,
/ * *
* @ private
* /
_resetSwitchInProgress : function PDFPresentationMode _resetSwitchInProgress ( ) {
if ( this . switchInProgress ) {
clearTimeout ( this . switchInProgress ) ;
delete this . switchInProgress ;
}
} ,
/ * *
* @ private
* /
_enter : function PDFPresentationMode _enter ( ) {
this . active = true ;
this . _resetSwitchInProgress ( ) ;
this . _notifyStateChange ( ) ;
this . container . classList . add ( ACTIVE _SELECTOR ) ;
// Ensure that the correct page is scrolled into view when entering
// Presentation Mode, by waiting until fullscreen mode in enabled.
setTimeout ( function enterPresentationModeTimeout ( ) {
this . pdfViewer . currentPageNumber = this . args . page ;
this . pdfViewer . currentScaleValue = 'page-fit' ;
} . bind ( this ) , 0 ) ;
this . _addWindowListeners ( ) ;
this . _showControls ( ) ;
this . contextMenuOpen = false ;
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")
// when entering Presentation Mode, hence we remove any active selection.
window . getSelection ( ) . removeAllRanges ( ) ;
} ,
/ * *
* @ private
* /
_exit : function PDFPresentationMode _exit ( ) {
var page = this . pdfViewer . currentPageNumber ;
this . container . classList . remove ( ACTIVE _SELECTOR ) ;
// Ensure that the correct page is scrolled into view when exiting
// Presentation Mode, by waiting until fullscreen mode is disabled.
setTimeout ( function exitPresentationModeTimeout ( ) {
this . active = false ;
this . _removeFullscreenChangeListeners ( ) ;
this . _notifyStateChange ( ) ;
this . pdfViewer . currentScaleValue = this . args . previousScale ;
this . pdfViewer . currentPageNumber = page ;
this . args = null ;
} . bind ( this ) , 0 ) ;
this . _removeWindowListeners ( ) ;
this . _hideControls ( ) ;
this . _resetMouseScrollState ( ) ;
this . container . removeAttribute ( 'contextmenu' ) ;
this . contextMenuOpen = false ;
} ,
/ * *
* @ private
* /
_mouseDown : function PDFPresentationMode _mouseDown ( evt ) {
if ( this . contextMenuOpen ) {
this . contextMenuOpen = false ;
evt . preventDefault ( ) ;
return ;
}
if ( evt . button === 0 ) {
// Enable clicking of links in presentation mode. Please note:
// Only links pointing to destinations in the current PDF document work.
var isInternalLink = evt . target . href && evt . target . classList . contains ( 'internalLink' ) ;
if ( ! isInternalLink ) {
// Unless an internal link was clicked, advance one page.
evt . preventDefault ( ) ;
this . pdfViewer . currentPageNumber += evt . shiftKey ? - 1 : 1 ;
}
}
} ,
/ * *
* @ private
* /
_contextMenu : function PDFPresentationMode _contextMenu ( ) {
this . contextMenuOpen = true ;
} ,
/ * *
* @ private
* /
_showControls : function PDFPresentationMode _showControls ( ) {
if ( this . controlsTimeout ) {
clearTimeout ( this . controlsTimeout ) ;
} else {
this . container . classList . add ( CONTROLS _SELECTOR ) ;
}
this . controlsTimeout = setTimeout ( function showControlsTimeout ( ) {
this . container . classList . remove ( CONTROLS _SELECTOR ) ;
delete this . controlsTimeout ;
} . bind ( this ) , DELAY _BEFORE _HIDING _CONTROLS ) ;
} ,
/ * *
* @ private
* /
_hideControls : function PDFPresentationMode _hideControls ( ) {
if ( ! this . controlsTimeout ) {
return ;
}
clearTimeout ( this . controlsTimeout ) ;
this . container . classList . remove ( CONTROLS _SELECTOR ) ;
delete this . controlsTimeout ;
} ,
/ * *
* Resets the properties used for tracking mouse scrolling events .
* @ private
* /
_resetMouseScrollState : function PDFPresentationMode _resetMouseScrollState ( ) {
this . mouseScrollTimeStamp = 0 ;
this . mouseScrollDelta = 0 ;
} ,
/ * *
* @ private
* /
_touchSwipe : function PDFPresentationMode _touchSwipe ( evt ) {
if ( ! this . active ) {
return ;
}
// Must move at least these many CSS pixels for it to count as a swipe
var SWIPE _MIN _DISTANCE _THRESHOLD = 50 ;
// The swipe angle is allowed to deviate from the x or y axis by this much
// before it is not considered a swipe in that direction any more.
var SWIPE _ANGLE _THRESHOLD = Math . PI / 6 ;
if ( evt . touches . length > 1 ) {
// Multiple touch points detected, cancel the swipe.
this . touchSwipeState = null ;
return ;
}
switch ( evt . type ) {
case 'touchstart' :
this . touchSwipeState = {
startX : evt . touches [ 0 ] . pageX ,
startY : evt . touches [ 0 ] . pageY ,
endX : evt . touches [ 0 ] . pageX ,
endY : evt . touches [ 0 ] . pageY
} ;
break ;
case 'touchmove' :
if ( this . touchSwipeState === null ) {
return ;
}
this . touchSwipeState . endX = evt . touches [ 0 ] . pageX ;
this . touchSwipeState . endY = evt . touches [ 0 ] . pageY ;
// Do a preventDefault to avoid the swipe from triggering browser
// gestures (Chrome in particular has some sort of swipe gesture in
// fullscreen mode).
evt . preventDefault ( ) ;
break ;
case 'touchend' :
if ( this . touchSwipeState === null ) {
return ;
}
var delta = 0 ;
var dx = this . touchSwipeState . endX - this . touchSwipeState . startX ;
var dy = this . touchSwipeState . endY - this . touchSwipeState . startY ;
var 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 ) ) {
// horizontal swipe
delta = dx ;
} else if ( Math . abs ( dy ) > SWIPE _MIN _DISTANCE _THRESHOLD && Math . abs ( absAngle - Math . PI / 2 ) <= SWIPE _ANGLE _THRESHOLD ) {
// vertical swipe
delta = dy ;
}
if ( delta > 0 ) {
this . _goToPreviousPage ( ) ;
} else if ( delta < 0 ) {
this . _goToNextPage ( ) ;
}
break ;
}
} ,
/ * *
* @ private
* /
_addWindowListeners : function PDFPresentationMode _addWindowListeners ( ) {
this . showControlsBind = this . _showControls . bind ( this ) ;
this . mouseDownBind = this . _mouseDown . bind ( this ) ;
this . mouseWheelBind = this . _mouseWheel . bind ( this ) ;
this . resetMouseScrollStateBind = this . _resetMouseScrollState . bind ( this ) ;
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 ) ;
} ,
/ * *
* @ private
* /
_removeWindowListeners : function PDFPresentationMode _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 ) ;
delete this . showControlsBind ;
delete this . mouseDownBind ;
delete this . mouseWheelBind ;
delete this . resetMouseScrollStateBind ;
delete this . contextMenuBind ;
delete this . touchSwipeBind ;
} ,
/ * *
* @ private
* /
_fullscreenChange : function PDFPresentationMode _fullscreenChange ( ) {
if ( this . isFullscreen ) {
this . _enter ( ) ;
} else {
this . _exit ( ) ;
}
} ,
/ * *
* @ private
* /
_addFullscreenChangeListeners : function PDFPresentationMode _addFullscreenChangeListeners ( ) {
this . fullscreenChangeBind = this . _fullscreenChange . bind ( this ) ;
window . addEventListener ( 'fullscreenchange' , this . fullscreenChangeBind ) ;
window . addEventListener ( 'mozfullscreenchange' , this . fullscreenChangeBind ) ;
} ,
/ * *
* @ private
* /
_removeFullscreenChangeListeners : function PDFPresentationMode _removeFullscreenChangeListeners ( ) {
window . removeEventListener ( 'fullscreenchange' , this . fullscreenChangeBind ) ;
window . removeEventListener ( 'mozfullscreenchange' , this . fullscreenChangeBind ) ;
delete this . fullscreenChangeBind ;
}
} ;
return PDFPresentationMode ;
} ( ) ;
exports . PDFPresentationMode = PDFPresentationMode ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFThumbnailView = { } , root . pdfjsWebUIUtils , root . pdfjsWebPDFRenderingQueue ) ;
} ( this , function ( exports , uiUtils , pdfRenderingQueue ) {
var mozL10n = uiUtils . mozL10n ;
var getOutputScale = uiUtils . getOutputScale ;
var RenderingStates = pdfRenderingQueue . RenderingStates ;
var THUMBNAIL _WIDTH = 98 ;
// px
var THUMBNAIL _CANVAS _BORDER _WIDTH = 1 ;
// px
/ * *
* @ typedef { Object } PDFThumbnailViewOptions
* @ property { HTMLDivElement } container - The viewer element .
* @ property { number } id - The thumbnail ' s unique ID ( normally its number ) .
* @ property { PageViewport } defaultViewport - The page viewport .
* @ property { IPDFLinkService } linkService - The navigation / linking service .
* @ property { PDFRenderingQueue } renderingQueue - The rendering queue object .
* @ property { boolean } disableCanvasToImageConversion - ( optional ) Don ' t convert
* the canvas thumbnails to images . This prevents ` toDataURL ` calls ,
* but increases the overall memory usage . The default value is false .
* /
/ * *
* @ class
* @ implements { IRenderableView }
* /
var PDFThumbnailView = function PDFThumbnailViewClosure ( ) {
function getTempCanvas ( width , height ) {
var tempCanvas = PDFThumbnailView . tempImageCache ;
if ( ! tempCanvas ) {
tempCanvas = document . createElement ( 'canvas' ) ;
PDFThumbnailView . tempImageCache = tempCanvas ;
}
tempCanvas . width = width ;
tempCanvas . height = height ;
tempCanvas . mozOpaque = true ;
var ctx = tempCanvas . getContext ( '2d' , { alpha : false } ) ;
ctx . save ( ) ;
ctx . fillStyle = 'rgb(255, 255, 255)' ;
ctx . fillRect ( 0 , 0 , width , height ) ;
ctx . restore ( ) ;
return tempCanvas ;
}
/ * *
* @ constructs PDFThumbnailView
* @ param { PDFThumbnailViewOptions } options
* /
function PDFThumbnailView ( options ) {
var container = options . container ;
var id = options . id ;
var defaultViewport = options . defaultViewport ;
var linkService = options . linkService ;
var renderingQueue = options . renderingQueue ;
var disableCanvasToImageConversion = options . disableCanvasToImageConversion || false ;
this . id = id ;
this . renderingId = 'thumbnail' + id ;
this . pageLabel = null ;
this . pdfPage = null ;
this . rotation = 0 ;
this . viewport = defaultViewport ;
this . pdfPageRotate = defaultViewport . rotation ;
this . linkService = linkService ;
this . renderingQueue = renderingQueue ;
this . renderTask = null ;
this . renderingState = RenderingStates . INITIAL ;
this . resume = null ;
this . disableCanvasToImageConversion = disableCanvasToImageConversion ;
this . pageWidth = this . viewport . width ;
this . pageHeight = this . viewport . height ;
this . pageRatio = this . pageWidth / this . pageHeight ;
this . canvasWidth = THUMBNAIL _WIDTH ;
this . canvasHeight = this . canvasWidth / this . pageRatio | 0 ;
this . scale = this . canvasWidth / this . pageWidth ;
var anchor = document . createElement ( 'a' ) ;
anchor . href = linkService . getAnchorUrl ( '#page=' + id ) ;
anchor . title = mozL10n . get ( 'thumb_page_title' , { page : id } , 'Page {{page}}' ) ;
anchor . onclick = function stopNavigation ( ) {
linkService . page = id ;
return false ;
} ;
this . anchor = anchor ;
var div = document . createElement ( 'div' ) ;
div . id = 'thumbnailContainer' + id ;
div . className = 'thumbnail' ;
this . div = div ;
if ( id === 1 ) {
// Highlight the thumbnail of the first page when no page number is
// specified (or exists in cache) when the document is loaded.
div . classList . add ( 'selected' ) ;
}
var ring = document . createElement ( 'div' ) ;
ring . className = 'thumbnailSelectionRing' ;
var borderAdjustment = 2 * THUMBNAIL _CANVAS _BORDER _WIDTH ;
ring . style . width = this . canvasWidth + borderAdjustment + 'px' ;
ring . style . height = this . canvasHeight + borderAdjustment + 'px' ;
this . ring = ring ;
div . appendChild ( ring ) ;
anchor . appendChild ( div ) ;
container . appendChild ( anchor ) ;
}
PDFThumbnailView . prototype = {
setPdfPage : function PDFThumbnailView _setPdfPage ( pdfPage ) {
this . pdfPage = pdfPage ;
this . pdfPageRotate = pdfPage . rotate ;
var totalRotation = ( this . rotation + this . pdfPageRotate ) % 360 ;
this . viewport = pdfPage . getViewport ( 1 , totalRotation ) ;
this . reset ( ) ;
} ,
reset : function PDFThumbnailView _reset ( ) {
this . cancelRendering ( ) ;
this . pageWidth = this . viewport . width ;
this . pageHeight = this . viewport . height ;
this . pageRatio = this . pageWidth / this . pageHeight ;
this . canvasHeight = this . canvasWidth / this . pageRatio | 0 ;
this . scale = this . canvasWidth / this . pageWidth ;
this . div . removeAttribute ( 'data-loaded' ) ;
var ring = this . ring ;
var childNodes = ring . childNodes ;
for ( var i = childNodes . length - 1 ; i >= 0 ; i -- ) {
ring . removeChild ( childNodes [ i ] ) ;
}
var borderAdjustment = 2 * THUMBNAIL _CANVAS _BORDER _WIDTH ;
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
// resources immediately, which can greatly reduce memory consumption.
this . canvas . width = 0 ;
this . canvas . height = 0 ;
delete this . canvas ;
}
if ( this . image ) {
this . image . removeAttribute ( 'src' ) ;
delete this . image ;
}
} ,
update : function PDFThumbnailView _update ( rotation ) {
if ( typeof rotation !== 'undefined' ) {
this . rotation = rotation ;
}
var totalRotation = ( this . rotation + this . pdfPageRotate ) % 360 ;
this . viewport = this . viewport . clone ( {
scale : 1 ,
rotation : totalRotation
} ) ;
this . reset ( ) ;
} ,
cancelRendering : function PDFThumbnailView _cancelRendering ( ) {
if ( this . renderTask ) {
this . renderTask . cancel ( ) ;
this . renderTask = null ;
}
this . renderingState = RenderingStates . INITIAL ;
this . resume = null ;
} ,
/ * *
* @ private
* /
_getPageDrawContext : function PDFThumbnailView _getPageDrawContext ( noCtxScale ) {
var 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 ;
canvas . mozOpaque = true ;
var ctx = canvas . getContext ( '2d' , { alpha : false } ) ;
var 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' ;
if ( ! noCtxScale && outputScale . scaled ) {
ctx . scale ( outputScale . sx , outputScale . sy ) ;
}
return ctx ;
} ,
/ * *
* @ private
* /
_convertCanvasToImage : function PDFThumbnailView _convertCanvasToImage ( ) {
if ( ! this . canvas ) {
return ;
}
if ( this . renderingState !== RenderingStates . FINISHED ) {
return ;
}
var id = this . renderingId ;
var className = 'thumbnailImage' ;
var ariaLabel = mozL10n . get ( 'thumb_page_canvas' , { page : this . pageId } , 'Thumbnail of Page {{page}}' ) ;
if ( this . disableCanvasToImageConversion ) {
this . canvas . id = id ;
this . canvas . className = className ;
this . canvas . setAttribute ( 'aria-label' , ariaLabel ) ;
this . div . setAttribute ( 'data-loaded' , true ) ;
this . ring . appendChild ( this . canvas ) ;
return ;
}
var image = document . createElement ( 'img' ) ;
image . id = id ;
image . className = className ;
image . setAttribute ( 'aria-label' , ariaLabel ) ;
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 . ring . appendChild ( image ) ;
// Zeroing the width and height causes Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
this . canvas . width = 0 ;
this . canvas . height = 0 ;
delete this . canvas ;
} ,
draw : function PDFThumbnailView _draw ( ) {
if ( this . renderingState !== RenderingStates . INITIAL ) {
console . error ( 'Must be in new state before drawing' ) ;
return Promise . resolve ( undefined ) ;
}
this . renderingState = RenderingStates . RUNNING ;
var resolveRenderPromise , rejectRenderPromise ;
var promise = new Promise ( function ( resolve , reject ) {
resolveRenderPromise = resolve ;
rejectRenderPromise = reject ;
} ) ;
var self = this ;
function thumbnailDrawCallback ( 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.
if ( renderTask === self . renderTask ) {
self . renderTask = null ;
}
if ( error === 'cancelled' ) {
rejectRenderPromise ( error ) ;
return ;
}
self . renderingState = RenderingStates . FINISHED ;
self . _convertCanvasToImage ( ) ;
if ( ! error ) {
resolveRenderPromise ( undefined ) ;
} else {
rejectRenderPromise ( error ) ;
}
}
var ctx = this . _getPageDrawContext ( ) ;
var drawViewport = this . viewport . clone ( { scale : this . scale } ) ;
var renderContinueCallback = function renderContinueCallback ( cont ) {
if ( ! self . renderingQueue . isHighestPriority ( self ) ) {
self . renderingState = RenderingStates . PAUSED ;
self . resume = function resumeCallback ( ) {
self . renderingState = RenderingStates . RUNNING ;
cont ( ) ;
} ;
return ;
}
cont ( ) ;
} ;
var renderContext = {
canvasContext : ctx ,
viewport : drawViewport
} ;
var renderTask = this . renderTask = this . pdfPage . render ( renderContext ) ;
renderTask . onContinue = renderContinueCallback ;
renderTask . promise . then ( function pdfPageRenderCallback ( ) {
thumbnailDrawCallback ( null ) ;
} , function pdfPageRenderError ( error ) {
thumbnailDrawCallback ( error ) ;
} ) ;
return promise ;
} ,
setImage : function PDFThumbnailView _setImage ( pageView ) {
if ( this . renderingState !== RenderingStates . INITIAL ) {
return ;
}
var img = pageView . canvas ;
if ( ! img ) {
return ;
}
if ( ! this . pdfPage ) {
this . setPdfPage ( pageView . pdfPage ) ;
}
this . renderingState = RenderingStates . FINISHED ;
var ctx = this . _getPageDrawContext ( true ) ;
var canvas = ctx . canvas ;
if ( img . width <= 2 * canvas . width ) {
ctx . drawImage ( img , 0 , 0 , img . width , img . height , 0 , 0 , canvas . width , canvas . height ) ;
this . _convertCanvasToImage ( ) ;
return ;
}
// drawImage does an awful job of rescaling the image, doing it gradually.
var MAX _NUM _SCALING _STEPS = 3 ;
var reducedWidth = canvas . width << MAX _NUM _SCALING _STEPS ;
var reducedHeight = canvas . height << MAX _NUM _SCALING _STEPS ;
var reducedImage = getTempCanvas ( reducedWidth , reducedHeight ) ;
var 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 ) ;
while ( reducedWidth > 2 * canvas . width ) {
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 ) ;
this . _convertCanvasToImage ( ) ;
} ,
get pageId ( ) {
return this . pageLabel !== null ? this . pageLabel : this . id ;
} ,
/ * *
* @ param { string | null } label
* /
setPageLabel : function PDFThumbnailView _setPageLabel ( label ) {
this . pageLabel = typeof label === 'string' ? label : null ;
this . anchor . title = mozL10n . get ( 'thumb_page_title' , { page : this . pageId } , 'Page {{page}}' ) ;
if ( this . renderingState !== RenderingStates . FINISHED ) {
return ;
}
var ariaLabel = mozL10n . get ( 'thumb_page_canvas' , { page : this . pageId } , 'Thumbnail of Page {{page}}' ) ;
if ( this . image ) {
this . image . setAttribute ( 'aria-label' , ariaLabel ) ;
} else if ( this . disableCanvasToImageConversion && this . canvas ) {
this . canvas . setAttribute ( 'aria-label' , ariaLabel ) ;
}
}
} ;
return PDFThumbnailView ;
} ( ) ;
PDFThumbnailView . tempImageCache = null ;
exports . PDFThumbnailView = PDFThumbnailView ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebSecondaryToolbar = { } , root . pdfjsWebUIUtils ) ;
} ( this , function ( exports , uiUtils ) {
var SCROLLBAR _PADDING = uiUtils . SCROLLBAR _PADDING ;
var mozL10n = uiUtils . mozL10n ;
/ * *
* @ typedef { Object } SecondaryToolbarOptions
* @ property { HTMLDivElement } toolbar - Container for the secondary toolbar .
* @ property { HTMLButtonElement } toggleButton - Button to toggle the visibility
* of the secondary toolbar .
* @ property { HTMLDivElement } toolbarButtonContainer - Container where all the
* toolbar buttons are placed . The maximum height of the toolbar is controlled
* dynamically by adjusting the 'max-height' CSS property of this DOM element .
* @ property { HTMLButtonElement } presentationModeButton - Button for entering
* presentation mode .
* @ property { HTMLButtonElement } openFileButton - Button to open a file .
* @ property { HTMLButtonElement } printButton - Button to print the document .
* @ property { HTMLButtonElement } downloadButton - Button to download the
* document .
* @ property { HTMLLinkElement } viewBookmarkButton - Button to obtain a bookmark
* link to the current location in the document .
* @ property { HTMLButtonElement } firstPageButton - Button to go to the first
* page in the document .
* @ property { HTMLButtonElement } lastPageButton - Button to go to the last page
* in the document .
* @ property { HTMLButtonElement } pageRotateCwButton - Button to rotate the pages
* clockwise .
* @ property { HTMLButtonElement } pageRotateCcwButton - Button to rotate the
* pages counterclockwise .
* @ property { HTMLButtonElement } toggleHandToolButton - Button to toggle the
* hand tool .
* @ property { HTMLButtonElement } documentPropertiesButton - Button for opening
* the document properties dialog .
* /
/ * *
* @ class
* /
var SecondaryToolbar = function SecondaryToolbarClosure ( ) {
/ * *
* @ constructs SecondaryToolbar
* @ param { SecondaryToolbarOptions } options
* @ param { HTMLDivElement } mainContainer
* @ param { EventBus } eventBus
* /
function SecondaryToolbar ( options , mainContainer , eventBus ) {
this . toolbar = options . toolbar ;
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 . toggleHandToolButton ,
eventName : 'togglehandtool' ,
close : true
} ,
{
element : options . documentPropertiesButton ,
eventName : 'documentproperties' ,
close : true
}
] ;
this . mainContainer = mainContainer ;
this . eventBus = eventBus ;
this . opened = false ;
this . containerHeight = null ;
this . previousContainerHeight = null ;
// Bind the event listeners for click and hand tool actions.
this . _bindClickListeners ( ) ;
this . _bindHandToolListener ( options . toggleHandToolButton ) ;
// Bind the event listener for adjusting the 'max-height' of the toolbar.
this . eventBus . on ( 'resize' , this . _setMaxHeight . bind ( this ) ) ;
}
SecondaryToolbar . prototype = {
/ * *
* @ return { boolean }
* /
get isOpen ( ) {
return this . opened ;
} ,
_bindClickListeners : function SecondaryToolbar _bindClickListeners ( ) {
// Button to toggle the visibility of the secondary toolbar.
this . toggleButton . addEventListener ( 'click' , this . toggle . bind ( this ) ) ;
// All items within the secondary toolbar.
for ( var button in this . buttons ) {
var element = this . buttons [ button ] . element ;
var eventName = this . buttons [ button ] . eventName ;
var close = this . buttons [ button ] . close ;
element . addEventListener ( 'click' , function ( eventName , close ) {
if ( eventName !== null ) {
this . eventBus . dispatch ( eventName , { source : this } ) ;
}
if ( close ) {
this . close ( ) ;
}
} . bind ( this , eventName , close ) ) ;
}
} ,
_bindHandToolListener : function SecondaryToolbar _bindHandToolListener ( toggleHandToolButton ) {
var isHandToolActive = false ;
this . eventBus . on ( 'handtoolchanged' , function ( e ) {
if ( isHandToolActive === e . isActive ) {
return ;
}
isHandToolActive = e . isActive ;
if ( isHandToolActive ) {
toggleHandToolButton . title = mozL10n . get ( 'hand_tool_disable.title' , null , 'Disable hand tool' ) ;
toggleHandToolButton . firstElementChild . textContent = mozL10n . get ( 'hand_tool_disable_label' , null , 'Disable hand tool' ) ;
} else {
toggleHandToolButton . title = mozL10n . get ( 'hand_tool_enable.title' , null , 'Enable hand tool' ) ;
toggleHandToolButton . firstElementChild . textContent = mozL10n . get ( 'hand_tool_enable_label' , null , 'Enable hand tool' ) ;
}
} . bind ( this ) ) ;
} ,
open : function SecondaryToolbar _open ( ) {
if ( this . opened ) {
return ;
}
this . opened = true ;
this . _setMaxHeight ( ) ;
this . toggleButton . classList . add ( 'toggled' ) ;
this . toolbar . classList . remove ( 'hidden' ) ;
} ,
close : function SecondaryToolbar _close ( ) {
if ( ! this . opened ) {
return ;
}
this . opened = false ;
this . toolbar . classList . add ( 'hidden' ) ;
this . toggleButton . classList . remove ( 'toggled' ) ;
} ,
toggle : function SecondaryToolbar _toggle ( ) {
if ( this . opened ) {
this . close ( ) ;
} else {
this . open ( ) ;
}
} ,
/ * *
* @ private
* /
_setMaxHeight : function SecondaryToolbar _setMaxHeight ( ) {
if ( ! this . opened ) {
return ;
}
// Only adjust the 'max-height' if the toolbar is visible.
this . containerHeight = this . mainContainer . clientHeight ;
if ( this . containerHeight === this . previousContainerHeight ) {
return ;
}
this . toolbarButtonContainer . setAttribute ( 'style' , 'max-height: ' + ( this . containerHeight - SCROLLBAR _PADDING ) + 'px;' ) ;
this . previousContainerHeight = this . containerHeight ;
}
} ;
return SecondaryToolbar ;
} ( ) ;
exports . SecondaryToolbar = SecondaryToolbar ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFFindBar = { } , root . pdfjsWebUIUtils , root . pdfjsWebPDFFindController ) ;
} ( this , function ( exports , uiUtils , pdfFindController ) {
var mozL10n = uiUtils . mozL10n ;
var FindStates = pdfFindController . FindStates ;
/ * *
* Creates a "search bar" given a set of DOM elements that act as controls
* for searching or for setting search preferences in the UI . This object
* also sets up the appropriate events for the controls . Actual searching
* is done by PDFFindController .
* /
var PDFFindBar = function PDFFindBarClosure ( ) {
function PDFFindBar ( options ) {
this . opened = false ;
this . bar = options . bar || null ;
this . toggleButton = options . toggleButton || null ;
this . findField = options . findField || null ;
this . highlightAll = options . highlightAllCheckbox || null ;
this . caseSensitive = options . caseSensitiveCheckbox || null ;
this . findMsg = options . findMsg || null ;
this . findResultsCount = options . findResultsCount || null ;
this . findStatusIcon = options . findStatusIcon || null ;
this . findPreviousButton = options . findPreviousButton || null ;
this . findNextButton = options . findNextButton || null ;
this . findController = options . findController || null ;
this . eventBus = options . eventBus ;
if ( this . findController === null ) {
throw new Error ( 'PDFFindBar cannot be used without a ' + 'PDFFindController instance.' ) ;
}
// Add event listeners to the DOM elements.
var self = this ;
this . toggleButton . addEventListener ( 'click' , function ( ) {
self . toggle ( ) ;
} ) ;
this . findField . addEventListener ( 'input' , function ( ) {
self . dispatchEvent ( '' ) ;
} ) ;
this . bar . addEventListener ( 'keydown' , function ( evt ) {
switch ( evt . keyCode ) {
case 13 :
// Enter
if ( evt . target === self . findField ) {
self . dispatchEvent ( 'again' , evt . shiftKey ) ;
}
break ;
case 27 :
// Escape
self . close ( ) ;
break ;
}
} ) ;
this . findPreviousButton . addEventListener ( 'click' , function ( ) {
self . dispatchEvent ( 'again' , true ) ;
} ) ;
this . findNextButton . addEventListener ( 'click' , function ( ) {
self . dispatchEvent ( 'again' , false ) ;
} ) ;
this . highlightAll . addEventListener ( 'click' , function ( ) {
self . dispatchEvent ( 'highlightallchange' ) ;
} ) ;
this . caseSensitive . addEventListener ( 'click' , function ( ) {
self . dispatchEvent ( 'casesensitivitychange' ) ;
} ) ;
}
PDFFindBar . prototype = {
reset : function PDFFindBar _reset ( ) {
this . updateUIState ( ) ;
} ,
dispatchEvent : function PDFFindBar _dispatchEvent ( type , findPrev ) {
this . eventBus . dispatch ( 'find' , {
source : this ,
type : type ,
query : this . findField . value ,
caseSensitive : this . caseSensitive . checked ,
phraseSearch : true ,
highlightAll : this . highlightAll . checked ,
findPrevious : findPrev
} ) ;
} ,
updateUIState : function PDFFindBar _updateUIState ( state , previous , matchCount ) {
var notFound = false ;
var findMsg = '' ;
var status = '' ;
switch ( state ) {
case FindStates . FIND _FOUND :
break ;
case FindStates . FIND _PENDING :
status = 'pending' ;
break ;
case FindStates . FIND _NOTFOUND :
findMsg = mozL10n . get ( 'find_not_found' , null , 'Phrase not found' ) ;
notFound = true ;
break ;
case FindStates . FIND _WRAPPED :
if ( previous ) {
findMsg = mozL10n . get ( 'find_reached_top' , null , 'Reached top of document, continued from bottom' ) ;
} else {
findMsg = mozL10n . get ( 'find_reached_bottom' , null , 'Reached end of document, continued from top' ) ;
}
break ;
}
if ( notFound ) {
this . findField . classList . add ( 'notFound' ) ;
} else {
this . findField . classList . remove ( 'notFound' ) ;
}
this . findField . setAttribute ( 'data-status' , status ) ;
this . findMsg . textContent = findMsg ;
this . updateResultsCount ( matchCount ) ;
} ,
updateResultsCount : function ( matchCount ) {
if ( ! this . findResultsCount ) {
return ;
}
// no UI control is provided
// If there are no matches, hide the counter
if ( ! matchCount ) {
this . findResultsCount . classList . add ( 'hidden' ) ;
return ;
}
// Create the match counter
this . findResultsCount . textContent = matchCount . toLocaleString ( ) ;
// Show the counter
this . findResultsCount . classList . remove ( 'hidden' ) ;
} ,
open : function PDFFindBar _open ( ) {
if ( ! this . opened ) {
this . opened = true ;
this . toggleButton . classList . add ( 'toggled' ) ;
this . bar . classList . remove ( 'hidden' ) ;
}
this . findField . select ( ) ;
this . findField . focus ( ) ;
} ,
close : function PDFFindBar _close ( ) {
if ( ! this . opened ) {
return ;
}
this . opened = false ;
this . toggleButton . classList . remove ( 'toggled' ) ;
this . bar . classList . add ( 'hidden' ) ;
this . findController . active = false ;
} ,
toggle : function PDFFindBar _toggle ( ) {
if ( this . opened ) {
this . close ( ) ;
} else {
this . open ( ) ;
}
}
} ;
return PDFFindBar ;
} ( ) ;
exports . PDFFindBar = PDFFindBar ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFHistory = { } , root . pdfjsWebDOMEvents ) ;
} ( this , function ( exports , domEvents ) {
function PDFHistory ( options ) {
this . linkService = options . linkService ;
this . eventBus = options . eventBus || domEvents . getGlobalEventBus ( ) ;
this . initialized = false ;
this . initialDestination = null ;
this . initialBookmark = null ;
}
PDFHistory . prototype = {
/ * *
* @ param { string } fingerprint
* /
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 ) {
if ( ! self . historyUnlocked ) {
return ;
}
if ( evt . state ) {
// Move back/forward in the history.
self . _goTo ( evt . state ) ;
return ;
}
// If the state is not set, then the user tried to navigate to a
// different hash by manually editing the URL and pressing Enter, or by
// clicking on an in-page link (e.g. the "current view" link).
// Save the current view state to the browser history.
// Note: In Firefox, history.null could also be null after an in-page
// navigation to the same URL, and without dispatching the popstate
// event: https://bugzilla.mozilla.org/show_bug.cgi?id=1183881
if ( self . uid === 0 ) {
// Replace the previous state if it was not explicitly set.
var previousParams = self . previousHash && self . currentBookmark && self . previousHash !== self . currentBookmark ? {
hash : self . currentBookmark ,
page : self . currentPage
} : { page : 1 } ;
replacePreviousHistoryState ( previousParams , function ( ) {
updateHistoryWithCurrentHash ( ) ;
} ) ;
} else {
updateHistoryWithCurrentHash ( ) ;
}
} , false ) ;
function updateHistoryWithCurrentHash ( ) {
self . previousHash = window . location . hash . slice ( 1 ) ;
self . _pushToHistory ( { hash : self . previousHash } , false , true ) ;
self . _updatePreviousBookmark ( ) ;
}
function replacePreviousHistoryState ( params , callback ) {
// To modify the previous history entry, the following happens:
// 1. history.back()
// 2. _pushToHistory, which calls history.replaceState( ... )
// 3. history.forward()
// Because a navigation via the history API does not immediately update
// the history state, the popstate event is used for synchronization.
self . historyUnlocked = false ;
// Suppress the hashchange event to avoid side effects caused by
// navigating back and forward.
self . allowHashChange = false ;
window . addEventListener ( 'popstate' , rewriteHistoryAfterBack ) ;
history . back ( ) ;
function rewriteHistoryAfterBack ( ) {
window . removeEventListener ( 'popstate' , rewriteHistoryAfterBack ) ;
window . addEventListener ( 'popstate' , rewriteHistoryAfterForward ) ;
self . _pushToHistory ( params , false , true ) ;
history . forward ( ) ;
}
function rewriteHistoryAfterForward ( ) {
window . removeEventListener ( 'popstate' , rewriteHistoryAfterForward ) ;
self . allowHashChange = true ;
self . historyUnlocked = true ;
callback ( ) ;
}
}
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 ) ;
self . eventBus . on ( 'presentationmodechanged' , function ( e ) {
self . isViewerInPresentationMode = e . active ;
} ) ;
} ,
clearHistoryState : function pdfHistory _clearHistoryState ( ) {
this . _pushOrReplaceState ( null , true ) ;
} ,
_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 ;
}
return this . allowHashChange ;
} ,
_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 ( ) ;
}
}
}
} ;
exports . PDFHistory = PDFHistory ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFLinkService = { } , root . pdfjsWebUIUtils , root . pdfjsWebDOMEvents ) ;
} ( this , function ( exports , uiUtils , domEvents ) {
var parseQueryString = uiUtils . parseQueryString ;
var PageNumberRegExp = /^\d+$/ ;
function isPageNumber ( str ) {
return PageNumberRegExp . test ( str ) ;
}
/ * *
* @ typedef { Object } PDFLinkServiceOptions
* @ property { EventBus } eventBus - The application event bus .
* /
/ * *
* Performs navigation functions inside PDF , such as opening specified page ,
* or destination .
* @ class
* @ implements { IPDFLinkService }
* /
var PDFLinkService = function PDFLinkServiceClosure ( ) {
/ * *
* @ constructs PDFLinkService
* @ param { PDFLinkServiceOptions } options
* /
function PDFLinkService ( options ) {
options = options || { } ;
this . eventBus = options . eventBus || domEvents . getGlobalEventBus ( ) ;
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 ? this . pdfDocument . numPages : 0 ;
} ,
/ * *
* @ 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 ;
if ( destRef instanceof Object ) {
pageNumber = self . _cachedPageNumber ( destRef ) ;
} else if ( ( destRef | 0 ) === destRef ) {
// Integer
pageNumber = destRef + 1 ;
} else {
console . error ( 'PDFLinkService_navigateTo: "' + destRef + '" is not a valid destination reference.' ) ;
return ;
}
if ( pageNumber ) {
if ( pageNumber < 1 || pageNumber > self . pagesCount ) {
console . error ( 'PDFLinkService_navigateTo: "' + pageNumber + '" is a non-existent page number.' ) ;
return ;
}
self . pdfViewer . scrollPageIntoView ( {
pageNumber : pageNumber ,
destArray : dest
} ) ;
if ( self . pdfHistory ) {
// Update the browsing history.
self . pdfHistory . push ( {
dest : dest ,
hash : destString ,
page : pageNumber
} ) ;
}
} else {
self . pdfDocument . getPageIndex ( destRef ) . then ( function ( pageIndex ) {
self . cachePageRef ( pageIndex + 1 , destRef ) ;
goToDestination ( destRef ) ;
} ) . catch ( function ( ) {
console . error ( 'PDFLinkService_navigateTo: "' + destRef + '" is not a valid page reference.' ) ;
return ;
} ) ;
}
} ;
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 ) ) {
console . error ( 'PDFLinkService_navigateTo: "' + destination + '" is not a valid destination array.' ) ;
return ;
}
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' ) {
// In practice, a named destination may contain only a number.
// If that happens, use the '#nameddest=' form to avoid the link
// redirecting to a page, instead of the correct destination.
return this . getAnchorUrl ( '#' + ( isPageNumber ( dest ) ? 'nameddest=' : '' ) + escape ( dest ) ) ;
}
if ( dest instanceof Array ) {
var str = JSON . stringify ( dest ) ;
return this . getAnchorUrl ( '#' + escape ( str ) ) ;
}
return this . getAnchorUrl ( '' ) ;
} ,
/ * *
* 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 ) {
var pageNumber , dest ;
if ( hash . indexOf ( '=' ) >= 0 ) {
var params = parseQueryString ( hash ) ;
if ( 'search' in params ) {
this . eventBus . dispatch ( 'findfromurlhash' , {
source : this ,
query : params [ 'search' ] . replace ( /"/g , '' ) ,
phraseSearch : params [ 'phrase' ] === 'true'
} ) ;
}
// borrowing syntax from "Parameters for Opening PDF Files"
if ( 'nameddest' in params ) {
if ( this . pdfHistory ) {
this . pdfHistory . updateNextHashParam ( params . nameddest ) ;
}
this . navigateTo ( params . nameddest ) ;
return ;
}
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 ( 'PDFLinkService_setHash: ' + '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 ( 'PDFLinkService_setHash: \'' + zoomArg + '\' is not a valid zoom value.' ) ;
}
}
}
if ( dest ) {
this . pdfViewer . scrollPageIntoView ( {
pageNumber : pageNumber || this . page ,
destArray : dest ,
allowNegativeOffset : true
} ) ;
} else if ( pageNumber ) {
this . page = pageNumber ;
}
// simple page
if ( 'pagemode' in params ) {
this . eventBus . dispatch ( 'pagemode' , {
source : this ,
mode : params . pagemode
} ) ;
}
} else {
dest = unescape ( hash ) ;
try {
dest = JSON . parse ( dest ) ;
if ( ! ( dest instanceof Array ) ) {
// Avoid incorrectly rejecting a valid named destination, such as
// e.g. "4.3" or "true", because `JSON.parse` converted its type.
dest = dest . toString ( ) ;
}
} catch ( ex ) {
}
if ( typeof dest === 'string' || isValidExplicitDestination ( dest ) ) {
if ( this . pdfHistory ) {
this . pdfHistory . updateNextHashParam ( dest ) ;
}
this . navigateTo ( dest ) ;
return ;
}
console . error ( 'PDFLinkService_setHash: \'' + unescape ( hash ) + '\' is not a valid destination.' ) ;
}
} ,
/ * *
* @ param { string } action
* /
executeNamedAction : function PDFLinkService _executeNamedAction ( action ) {
// See PDF reference, table 8.45 - Named action
switch ( action ) {
case 'GoBack' :
if ( this . pdfHistory ) {
this . pdfHistory . back ( ) ;
}
break ;
case 'GoForward' :
if ( this . pdfHistory ) {
this . pdfHistory . forward ( ) ;
}
break ;
case 'NextPage' :
if ( this . page < this . pagesCount ) {
this . page ++ ;
}
break ;
case 'PrevPage' :
if ( this . page > 1 ) {
this . page -- ;
}
break ;
case 'LastPage' :
this . page = this . pagesCount ;
break ;
case 'FirstPage' :
this . page = 1 ;
break ;
default :
break ;
}
// No action according to spec
this . eventBus . dispatch ( 'namedaction' , {
source : this ,
action : action
} ) ;
} ,
/ * *
* @ 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 ;
} ,
_cachedPageNumber : function PDFLinkService _cachedPageNumber ( pageRef ) {
var refStr = pageRef . num + ' ' + pageRef . gen + ' R' ;
return this . _pagesRefCache && this . _pagesRefCache [ refStr ] || null ;
}
} ;
function isValidExplicitDestination ( dest ) {
if ( ! ( dest instanceof Array ) ) {
return false ;
}
var destLength = dest . length , allowNull = true ;
if ( destLength < 2 ) {
return false ;
}
var page = dest [ 0 ] ;
if ( ! ( typeof page === 'object' && typeof page . num === 'number' && ( page . num | 0 ) === page . num && typeof page . gen === 'number' && ( page . gen | 0 ) === page . gen ) && ! ( typeof page === 'number' && ( page | 0 ) === page && page >= 0 ) ) {
return false ;
}
var zoom = dest [ 1 ] ;
if ( ! ( typeof zoom === 'object' && typeof zoom . name === 'string' ) ) {
return false ;
}
switch ( zoom . name ) {
case 'XYZ' :
if ( destLength !== 5 ) {
return false ;
}
break ;
case 'Fit' :
case 'FitB' :
return destLength === 2 ;
case 'FitH' :
case 'FitBH' :
case 'FitV' :
case 'FitBV' :
if ( destLength !== 3 ) {
return false ;
}
break ;
case 'FitR' :
if ( destLength !== 6 ) {
return false ;
}
allowNull = false ;
break ;
default :
return false ;
}
for ( var i = 2 ; i < destLength ; i ++ ) {
var param = dest [ i ] ;
if ( ! ( typeof param === 'number' || allowNull && param === null ) ) {
return false ;
}
}
return true ;
}
return PDFLinkService ;
} ( ) ;
var SimpleLinkService = function SimpleLinkServiceClosure ( ) {
function SimpleLinkService ( ) {
}
SimpleLinkService . prototype = {
/ * *
* @ returns { number }
* /
get page ( ) {
return 0 ;
} ,
/ * *
* @ param { number } value
* /
set page ( value ) {
} ,
/ * *
* @ param dest - The PDF destination object .
* /
navigateTo : function ( dest ) {
} ,
/ * *
* @ param dest - The PDF destination object .
* @ returns { string } The hyperlink to the PDF object .
* /
getDestinationHash : function ( dest ) {
return '#' ;
} ,
/ * *
* @ param hash - The PDF parameters / hash .
* @ returns { string } The hyperlink to the PDF object .
* /
getAnchorUrl : function ( hash ) {
return '#' ;
} ,
/ * *
* @ param { string } hash
* /
setHash : function ( hash ) {
} ,
/ * *
* @ param { string } action
* /
executeNamedAction : function ( action ) {
} ,
/ * *
* @ param { number } pageNum - page number .
* @ param { Object } pageRef - reference to the page .
* /
cachePageRef : function ( pageNum , pageRef ) {
}
} ;
return SimpleLinkService ;
} ( ) ;
exports . PDFLinkService = PDFLinkService ;
exports . SimpleLinkService = SimpleLinkService ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFPageView = { } , root . pdfjsWebUIUtils , root . pdfjsWebPDFRenderingQueue , root . pdfjsWebDOMEvents , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , uiUtils , pdfRenderingQueue , domEvents , pdfjsLib ) {
var CSS _UNITS = uiUtils . CSS _UNITS ;
var DEFAULT _SCALE = uiUtils . DEFAULT _SCALE ;
var getOutputScale = uiUtils . getOutputScale ;
var approximateFraction = uiUtils . approximateFraction ;
var roundToDivide = uiUtils . roundToDivide ;
var RenderingStates = pdfRenderingQueue . RenderingStates ;
var TEXT _LAYER _RENDER _DELAY = 200 ;
// ms
/ * *
* @ typedef { Object } PDFPageViewOptions
* @ property { HTMLDivElement } container - The viewer element .
* @ property { EventBus } eventBus - The application event bus .
* @ property { number } id - The page unique ID ( normally its number ) .
* @ property { number } scale - The page scale display .
* @ property { PageViewport } defaultViewport - The page viewport .
* @ property { PDFRenderingQueue } renderingQueue - The rendering queue object .
* @ property { IPDFTextLayerFactory } textLayerFactory
* @ property { IPDFAnnotationLayerFactory } annotationLayerFactory
* @ property { boolean } enhanceTextSelection - Turns on the text selection
* enhancement . The default is ` false ` .
* @ property { boolean } renderInteractiveForms - Turns on rendering of
* interactive form elements . The default is ` false ` .
* /
/ * *
* @ class
* @ implements { IRenderableView }
* /
var PDFPageView = function PDFPageViewClosure ( ) {
/ * *
* @ constructs PDFPageView
* @ param { PDFPageViewOptions } options
* /
function PDFPageView ( options ) {
var container = options . container ;
var id = options . id ;
var scale = options . scale ;
var defaultViewport = options . defaultViewport ;
var renderingQueue = options . renderingQueue ;
var textLayerFactory = options . textLayerFactory ;
var annotationLayerFactory = options . annotationLayerFactory ;
var enhanceTextSelection = options . enhanceTextSelection || false ;
var renderInteractiveForms = options . renderInteractiveForms || false ;
this . id = id ;
this . renderingId = 'page' + id ;
this . pageLabel = null ;
this . rotation = 0 ;
this . scale = scale || DEFAULT _SCALE ;
this . viewport = defaultViewport ;
this . pdfPageRotate = defaultViewport . rotation ;
this . hasRestrictedScaling = false ;
this . enhanceTextSelection = enhanceTextSelection ;
this . renderInteractiveForms = renderInteractiveForms ;
this . eventBus = options . eventBus || domEvents . getGlobalEventBus ( ) ;
this . renderingQueue = renderingQueue ;
this . textLayerFactory = textLayerFactory ;
this . annotationLayerFactory = annotationLayerFactory ;
this . renderTask = null ;
this . renderingState = RenderingStates . INITIAL ;
this . resume = null ;
this . onBeforeDraw = null ;
this . onAfterDraw = null ;
this . textLayer = null ;
this . zoomLayer = null ;
this . annotationLayer = null ;
var div = document . createElement ( 'div' ) ;
div . id = 'pageContainer' + this . id ;
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 ) ;
}
PDFPageView . prototype = {
setPdfPage : function PDFPageView _setPdfPage ( pdfPage ) {
this . pdfPage = pdfPage ;
this . pdfPageRotate = pdfPage . rotate ;
var totalRotation = ( this . rotation + this . pdfPageRotate ) % 360 ;
this . viewport = pdfPage . getViewport ( this . scale * CSS _UNITS , totalRotation ) ;
this . stats = pdfPage . stats ;
this . reset ( ) ;
} ,
destroy : function PDFPageView _destroy ( ) {
this . zoomLayer = null ;
this . reset ( ) ;
if ( this . pdfPage ) {
this . pdfPage . cleanup ( ) ;
}
} ,
reset : function PDFPageView _reset ( keepZoomLayer , keepAnnotations ) {
this . cancelRendering ( ) ;
var div = this . div ;
div . style . width = Math . floor ( this . viewport . width ) + 'px' ;
div . style . height = Math . floor ( this . viewport . height ) + 'px' ;
var childNodes = div . childNodes ;
var currentZoomLayerNode = keepZoomLayer && this . zoomLayer || null ;
var currentAnnotationNode = keepAnnotations && this . annotationLayer && this . annotationLayer . div || null ;
for ( var i = childNodes . length - 1 ; i >= 0 ; i -- ) {
var node = childNodes [ i ] ;
if ( currentZoomLayerNode === node || currentAnnotationNode === node ) {
continue ;
}
div . removeChild ( node ) ;
}
div . removeAttribute ( 'data-loaded' ) ;
if ( currentAnnotationNode ) {
// Hide annotationLayer until all elements are resized
// so they are not displayed on the already-resized page
this . annotationLayer . hide ( ) ;
} else {
this . annotationLayer = null ;
}
if ( this . canvas && ! currentZoomLayerNode ) {
// Zeroing the width and height causes Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
this . canvas . width = 0 ;
this . canvas . height = 0 ;
delete this . canvas ;
}
this . loadingIconDiv = document . createElement ( 'div' ) ;
this . loadingIconDiv . className = 'loadingIcon' ;
div . appendChild ( this . loadingIconDiv ) ;
} ,
update : function PDFPageView _update ( scale , rotation ) {
this . scale = scale || this . scale ;
if ( typeof rotation !== 'undefined' ) {
this . rotation = rotation ;
}
var totalRotation = ( this . rotation + this . pdfPageRotate ) % 360 ;
this . viewport = this . viewport . clone ( {
scale : this . scale * CSS _UNITS ,
rotation : totalRotation
} ) ;
var isScalingRestricted = false ;
if ( this . canvas && pdfjsLib . PDFJS . maxCanvasPixels > 0 ) {
var outputScale = this . outputScale ;
if ( ( Math . floor ( this . viewport . width ) * outputScale . sx | 0 ) * ( Math . floor ( this . viewport . height ) * outputScale . sy | 0 ) > pdfjsLib . PDFJS . maxCanvasPixels ) {
isScalingRestricted = true ;
}
}
if ( this . canvas ) {
if ( pdfjsLib . PDFJS . useOnlyCssZoom || this . hasRestrictedScaling && isScalingRestricted ) {
this . cssTransform ( this . canvas , true ) ;
this . eventBus . dispatch ( 'pagerendered' , {
source : this ,
pageNumber : this . id ,
cssTransform : true
} ) ;
return ;
}
if ( ! this . zoomLayer ) {
this . zoomLayer = this . canvas . parentNode ;
this . zoomLayer . style . position = 'absolute' ;
}
}
if ( this . zoomLayer ) {
this . cssTransform ( this . zoomLayer . firstChild ) ;
}
this . reset ( /* keepZoomLayer = */
true , /* keepAnnotations = */
true ) ;
} ,
cancelRendering : function PDFPageView _cancelRendering ( ) {
if ( this . renderTask ) {
this . renderTask . cancel ( ) ;
this . renderTask = null ;
}
this . renderingState = RenderingStates . INITIAL ;
this . resume = null ;
if ( this . textLayer ) {
this . textLayer . cancel ( ) ;
this . textLayer = null ;
}
} ,
/ * *
* Called when moved in the parent ' s container .
* /
updatePosition : function PDFPageView _updatePosition ( ) {
if ( this . textLayer ) {
this . textLayer . render ( TEXT _LAYER _RENDER _DELAY ) ;
}
} ,
cssTransform : function PDFPageView _transform ( canvas , redrawAnnotations ) {
var CustomStyle = pdfjsLib . CustomStyle ;
// Scale canvas, canvas wrapper, and page container.
var width = this . viewport . width ;
var height = this . viewport . height ;
var div = this . div ;
canvas . style . width = canvas . parentNode . style . width = div . style . width = Math . floor ( width ) + 'px' ;
canvas . style . height = canvas . parentNode . style . height = div . style . height = Math . floor ( height ) + 'px' ;
// The canvas may have been originally rotated, rotate relative to that.
var relativeRotation = this . viewport . rotation - canvas . _viewport . rotation ;
var absRotation = Math . abs ( relativeRotation ) ;
var scaleX = 1 , scaleY = 1 ;
if ( absRotation === 90 || absRotation === 270 ) {
// Scale x and y because of the rotation.
scaleX = height / width ;
scaleY = width / height ;
}
var cssTransform = 'rotate(' + relativeRotation + 'deg) ' + 'scale(' + scaleX + ',' + scaleY + ')' ;
CustomStyle . setProp ( 'transform' , canvas , cssTransform ) ;
if ( this . textLayer ) {
// Rotating the text layer is more complicated since the divs inside the
// the text layer are rotated.
// TODO: This could probably be simplified by drawing the text layer in
// one orientation then rotating overall.
var textLayerViewport = this . textLayer . viewport ;
var textRelativeRotation = this . viewport . rotation - textLayerViewport . rotation ;
var textAbsRotation = Math . abs ( textRelativeRotation ) ;
var scale = width / textLayerViewport . width ;
if ( textAbsRotation === 90 || textAbsRotation === 270 ) {
scale = width / textLayerViewport . height ;
}
var textLayerDiv = this . textLayer . textLayerDiv ;
var transX , transY ;
switch ( textAbsRotation ) {
case 0 :
transX = transY = 0 ;
break ;
case 90 :
transX = 0 ;
transY = '-' + textLayerDiv . style . height ;
break ;
case 180 :
transX = '-' + textLayerDiv . style . width ;
transY = '-' + textLayerDiv . style . height ;
break ;
case 270 :
transX = '-' + textLayerDiv . style . width ;
transY = 0 ;
break ;
default :
console . error ( 'Bad rotation value.' ) ;
break ;
}
CustomStyle . setProp ( 'transform' , textLayerDiv , 'rotate(' + textAbsRotation + 'deg) ' + 'scale(' + scale + ', ' + scale + ') ' + 'translate(' + transX + ', ' + transY + ')' ) ;
CustomStyle . setProp ( 'transformOrigin' , textLayerDiv , '0% 0%' ) ;
}
if ( redrawAnnotations && this . annotationLayer ) {
this . annotationLayer . render ( this . viewport , 'display' ) ;
}
} ,
get width ( ) {
return this . viewport . width ;
} ,
get height ( ) {
return this . viewport . height ;
} ,
getPagePoint : function PDFPageView _getPagePoint ( x , y ) {
return this . viewport . convertToPdfPoint ( x , y ) ;
} ,
draw : function PDFPageView _draw ( ) {
if ( this . renderingState !== RenderingStates . INITIAL ) {
console . error ( 'Must be in new state before drawing' ) ;
this . reset ( ) ;
}
// Ensure that we reset all state to prevent issues.
this . renderingState = RenderingStates . RUNNING ;
var pdfPage = this . pdfPage ;
var viewport = this . viewport ;
var div = this . div ;
// Wrap the canvas so if it has a css transform for highdpi the overflow
// will be hidden in FF.
var canvasWrapper = document . createElement ( 'div' ) ;
canvasWrapper . style . width = div . style . width ;
canvasWrapper . style . height = div . style . height ;
canvasWrapper . classList . add ( 'canvasWrapper' ) ;
var canvas = document . createElement ( 'canvas' ) ;
canvas . id = 'page' + this . id ;
// 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' ) ;
var isCanvasHidden = true ;
canvasWrapper . appendChild ( canvas ) ;
if ( this . annotationLayer && this . annotationLayer . div ) {
// annotationLayer needs to stay on top
div . insertBefore ( canvasWrapper , this . annotationLayer . div ) ;
} else {
div . appendChild ( canvasWrapper ) ;
}
this . canvas = canvas ;
canvas . mozOpaque = true ;
var ctx = canvas . getContext ( '2d' , { alpha : false } ) ;
var outputScale = getOutputScale ( ctx ) ;
this . outputScale = outputScale ;
if ( pdfjsLib . PDFJS . useOnlyCssZoom ) {
var actualSizeViewport = viewport . clone ( { scale : CSS _UNITS } ) ;
// Use a scale that will make the canvas be the original intended size
// of the page.
outputScale . sx *= actualSizeViewport . width / viewport . width ;
outputScale . sy *= actualSizeViewport . height / viewport . height ;
outputScale . scaled = true ;
}
if ( pdfjsLib . PDFJS . maxCanvasPixels > 0 ) {
var pixelsInViewport = viewport . width * viewport . height ;
var maxScale = Math . sqrt ( pdfjsLib . PDFJS . maxCanvasPixels / pixelsInViewport ) ;
if ( outputScale . sx > maxScale || outputScale . sy > maxScale ) {
outputScale . sx = maxScale ;
outputScale . sy = maxScale ;
outputScale . scaled = true ;
this . hasRestrictedScaling = true ;
} else {
this . hasRestrictedScaling = false ;
}
}
var sfx = approximateFraction ( outputScale . sx ) ;
var 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' ;
// Add the viewport so it's known what it was originally drawn with.
canvas . _viewport = viewport ;
var textLayerDiv = null ;
var textLayer = null ;
if ( this . textLayerFactory ) {
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 ) {
// annotationLayer needs to stay on top
div . insertBefore ( textLayerDiv , this . annotationLayer . div ) ;
} else {
div . appendChild ( textLayerDiv ) ;
}
textLayer = this . textLayerFactory . createTextLayerBuilder ( textLayerDiv , this . id - 1 , this . viewport , this . enhanceTextSelection ) ;
}
this . textLayer = textLayer ;
var resolveRenderPromise , rejectRenderPromise ;
var promise = new Promise ( function ( resolve , reject ) {
resolveRenderPromise = resolve ;
rejectRenderPromise = reject ;
} ) ;
// Rendering area
var self = this ;
function pageViewDrawCallback ( 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.
if ( renderTask === self . renderTask ) {
self . renderTask = null ;
}
if ( error === 'cancelled' ) {
rejectRenderPromise ( error ) ;
return ;
}
self . renderingState = RenderingStates . FINISHED ;
if ( isCanvasHidden ) {
self . canvas . removeAttribute ( 'hidden' ) ;
isCanvasHidden = false ;
}
if ( self . loadingIconDiv ) {
div . removeChild ( self . loadingIconDiv ) ;
delete self . loadingIconDiv ;
}
if ( self . zoomLayer ) {
// Zeroing the width and height causes Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
var zoomLayerCanvas = self . zoomLayer . firstChild ;
zoomLayerCanvas . width = 0 ;
zoomLayerCanvas . height = 0 ;
if ( div . contains ( self . zoomLayer ) ) {
// Prevent "Node was not found" errors if the `zoomLayer` was
// already removed. This may occur intermittently if the scale
// changes many times in very quick succession.
div . removeChild ( self . zoomLayer ) ;
}
self . zoomLayer = null ;
}
self . error = error ;
self . stats = pdfPage . stats ;
if ( self . onAfterDraw ) {
self . onAfterDraw ( ) ;
}
self . eventBus . dispatch ( 'pagerendered' , {
source : self ,
pageNumber : self . id ,
cssTransform : false
} ) ;
if ( ! error ) {
resolveRenderPromise ( undefined ) ;
} else {
rejectRenderPromise ( error ) ;
}
}
var renderContinueCallback = null ;
if ( this . renderingQueue ) {
renderContinueCallback = function renderContinueCallback ( cont ) {
if ( ! self . renderingQueue . isHighestPriority ( self ) ) {
self . renderingState = RenderingStates . PAUSED ;
self . resume = function resumeCallback ( ) {
self . renderingState = RenderingStates . RUNNING ;
cont ( ) ;
} ;
return ;
}
if ( isCanvasHidden ) {
self . canvas . removeAttribute ( 'hidden' ) ;
isCanvasHidden = false ;
}
cont ( ) ;
} ;
}
var transform = ! outputScale . scaled ? null : [
outputScale . sx ,
0 ,
0 ,
outputScale . sy ,
0 ,
0
] ;
var renderContext = {
canvasContext : ctx ,
transform : transform ,
viewport : this . viewport ,
renderInteractiveForms : this . renderInteractiveForms
} ;
// intent: 'default', // === 'display'
var renderTask = this . renderTask = this . pdfPage . render ( renderContext ) ;
renderTask . onContinue = renderContinueCallback ;
this . renderTask . promise . then ( function pdfPageRenderCallback ( ) {
pageViewDrawCallback ( null ) ;
if ( textLayer ) {
self . pdfPage . getTextContent ( { normalizeWhitespace : true } ) . then ( function textContentResolved ( textContent ) {
textLayer . setTextContent ( textContent ) ;
textLayer . render ( TEXT _LAYER _RENDER _DELAY ) ;
} ) ;
}
} , function pdfPageRenderError ( error ) {
pageViewDrawCallback ( error ) ;
} ) ;
if ( this . annotationLayerFactory ) {
if ( ! this . annotationLayer ) {
this . annotationLayer = this . annotationLayerFactory . createAnnotationLayerBuilder ( div , this . pdfPage , this . renderInteractiveForms ) ;
}
this . annotationLayer . render ( this . viewport , 'display' ) ;
}
div . setAttribute ( 'data-loaded' , true ) ;
if ( self . onBeforeDraw ) {
self . onBeforeDraw ( ) ;
}
return promise ;
} ,
/ * *
* @ param { string | null } label
* /
setPageLabel : function PDFView _setPageLabel ( label ) {
this . pageLabel = typeof label === 'string' ? label : null ;
if ( this . pageLabel !== null ) {
this . div . setAttribute ( 'data-page-label' , this . pageLabel ) ;
} else {
this . div . removeAttribute ( 'data-page-label' ) ;
}
}
} ;
return PDFPageView ;
} ( ) ;
exports . PDFPageView = PDFPageView ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFThumbnailViewer = { } , root . pdfjsWebUIUtils , root . pdfjsWebPDFThumbnailView ) ;
} ( this , function ( exports , uiUtils , pdfThumbnailView ) {
var watchScroll = uiUtils . watchScroll ;
var getVisibleElements = uiUtils . getVisibleElements ;
var scrollIntoView = uiUtils . scrollIntoView ;
var PDFThumbnailView = pdfThumbnailView . PDFThumbnailView ;
var THUMBNAIL _SCROLL _MARGIN = - 19 ;
/ * *
* @ typedef { Object } PDFThumbnailViewerOptions
* @ property { HTMLDivElement } container - The container for the thumbnail
* elements .
* @ property { IPDFLinkService } linkService - The navigation / linking service .
* @ property { PDFRenderingQueue } renderingQueue - The rendering queue object .
* /
/ * *
* Simple viewer control to display thumbnails for pages .
* @ class
* @ implements { IRenderableView }
* /
var PDFThumbnailViewer = function PDFThumbnailViewerClosure ( ) {
/ * *
* @ constructs PDFThumbnailViewer
* @ param { PDFThumbnailViewerOptions } options
* /
function PDFThumbnailViewer ( options ) {
this . container = options . container ;
this . renderingQueue = options . renderingQueue ;
this . linkService = options . linkService ;
this . scroll = watchScroll ( this . container , this . _scrollUpdated . bind ( this ) ) ;
this . _resetView ( ) ;
}
PDFThumbnailViewer . prototype = {
/ * *
* @ private
* /
_scrollUpdated : function PDFThumbnailViewer _scrollUpdated ( ) {
this . renderingQueue . renderHighestPriority ( ) ;
} ,
getThumbnail : function PDFThumbnailViewer _getThumbnail ( index ) {
return this . thumbnails [ index ] ;
} ,
/ * *
* @ private
* /
_getVisibleThumbs : function PDFThumbnailViewer _getVisibleThumbs ( ) {
return getVisibleElements ( this . container , this . thumbnails ) ;
} ,
scrollThumbnailIntoView : function PDFThumbnailViewer _scrollThumbnailIntoView ( page ) {
var selected = document . querySelector ( '.thumbnail.selected' ) ;
if ( selected ) {
selected . classList . remove ( 'selected' ) ;
}
var thumbnail = document . getElementById ( 'thumbnailContainer' + page ) ;
if ( thumbnail ) {
thumbnail . classList . add ( 'selected' ) ;
}
var visibleThumbs = this . _getVisibleThumbs ( ) ;
var numVisibleThumbs = visibleThumbs . views . length ;
// If the thumbnail isn't currently visible, scroll it into view.
if ( numVisibleThumbs > 0 ) {
var first = visibleThumbs . first . id ;
// Account for only one thumbnail being visible.
var last = numVisibleThumbs > 1 ? visibleThumbs . last . id : first ;
if ( page <= first || page >= last ) {
scrollIntoView ( thumbnail , { top : THUMBNAIL _SCROLL _MARGIN } ) ;
}
}
} ,
get pagesRotation ( ) {
return this . _pagesRotation ;
} ,
set pagesRotation ( rotation ) {
this . _pagesRotation = rotation ;
for ( var i = 0 , l = this . thumbnails . length ; i < l ; i ++ ) {
var thumb = this . thumbnails [ i ] ;
thumb . update ( rotation ) ;
}
} ,
cleanup : function PDFThumbnailViewer _cleanup ( ) {
var tempCanvas = PDFThumbnailView . tempImageCache ;
if ( tempCanvas ) {
// Zeroing the width and height causes Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
tempCanvas . width = 0 ;
tempCanvas . height = 0 ;
}
PDFThumbnailView . tempImageCache = null ;
} ,
/ * *
* @ private
* /
_resetView : function PDFThumbnailViewer _resetView ( ) {
this . thumbnails = [ ] ;
this . _pageLabels = null ;
this . _pagesRotation = 0 ;
this . _pagesRequests = [ ] ;
// Remove the thumbnails from the DOM.
this . container . textContent = '' ;
} ,
setDocument : function PDFThumbnailViewer _setDocument ( pdfDocument ) {
if ( this . pdfDocument ) {
this . _cancelRendering ( ) ;
this . _resetView ( ) ;
}
this . pdfDocument = pdfDocument ;
if ( ! pdfDocument ) {
return Promise . resolve ( ) ;
}
return pdfDocument . getPage ( 1 ) . then ( function ( firstPage ) {
var pagesCount = pdfDocument . numPages ;
var viewport = firstPage . getViewport ( 1.0 ) ;
for ( var pageNum = 1 ; pageNum <= pagesCount ; ++ pageNum ) {
var thumbnail = new PDFThumbnailView ( {
container : this . container ,
id : pageNum ,
defaultViewport : viewport . clone ( ) ,
linkService : this . linkService ,
renderingQueue : this . renderingQueue ,
disableCanvasToImageConversion : false
} ) ;
this . thumbnails . push ( thumbnail ) ;
}
} . bind ( this ) ) ;
} ,
/ * *
* @ private
* /
_cancelRendering : function PDFThumbnailViewer _cancelRendering ( ) {
for ( var i = 0 , ii = this . thumbnails . length ; i < ii ; i ++ ) {
if ( this . thumbnails [ i ] ) {
this . thumbnails [ i ] . cancelRendering ( ) ;
}
}
} ,
/ * *
* @ param { Array | null } labels
* /
setPageLabels : function PDFThumbnailViewer _setPageLabels ( labels ) {
if ( ! this . pdfDocument ) {
return ;
}
if ( ! labels ) {
this . _pageLabels = null ;
} else if ( ! ( labels instanceof Array && this . pdfDocument . numPages === labels . length ) ) {
this . _pageLabels = null ;
console . error ( 'PDFThumbnailViewer_setPageLabels: Invalid page labels.' ) ;
} else {
this . _pageLabels = labels ;
}
// Update all the `PDFThumbnailView` instances.
for ( var i = 0 , ii = this . thumbnails . length ; i < ii ; i ++ ) {
var thumbnailView = this . thumbnails [ i ] ;
var label = this . _pageLabels && this . _pageLabels [ i ] ;
thumbnailView . setPageLabel ( label ) ;
}
} ,
/ * *
* @ param { PDFThumbnailView } thumbView
* @ returns { PDFPage }
* @ private
* /
_ensurePdfPageLoaded : function PDFThumbnailViewer _ensurePdfPageLoaded ( thumbView ) {
if ( thumbView . pdfPage ) {
return Promise . resolve ( thumbView . pdfPage ) ;
}
var pageNumber = thumbView . id ;
if ( this . _pagesRequests [ pageNumber ] ) {
return this . _pagesRequests [ pageNumber ] ;
}
var promise = this . pdfDocument . getPage ( pageNumber ) . then ( function ( pdfPage ) {
thumbView . setPdfPage ( pdfPage ) ;
this . _pagesRequests [ pageNumber ] = null ;
return pdfPage ;
} . bind ( this ) ) ;
this . _pagesRequests [ pageNumber ] = promise ;
return promise ;
} ,
forceRendering : function ( ) {
var visibleThumbs = this . _getVisibleThumbs ( ) ;
var thumbView = this . renderingQueue . getHighestPriority ( visibleThumbs , this . thumbnails , this . scroll . down ) ;
if ( thumbView ) {
this . _ensurePdfPageLoaded ( thumbView ) . then ( function ( ) {
this . renderingQueue . renderView ( thumbView ) ;
} . bind ( this ) ) ;
return true ;
}
return false ;
}
} ;
return PDFThumbnailViewer ;
} ( ) ;
exports . PDFThumbnailViewer = PDFThumbnailViewer ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebTextLayerBuilder = { } , root . pdfjsWebDOMEvents , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , domEvents , pdfjsLib ) {
var EXPAND _DIVS _TIMEOUT = 300 ;
// ms
/ * *
* @ typedef { Object } TextLayerBuilderOptions
* @ property { HTMLDivElement } textLayerDiv - The text layer container .
* @ property { EventBus } eventBus - The application event bus .
* @ property { number } pageIndex - The page index .
* @ property { PageViewport } viewport - The viewport of the text layer .
* @ property { PDFFindController } findController
* @ property { boolean } enhanceTextSelection - Option to turn on improved
* text selection .
* /
/ * *
* TextLayerBuilder provides text - selection functionality for the PDF .
* It does this by creating overlay divs over the PDF text . These divs
* contain text that matches the PDF text they are overlaying . This object
* also provides a way to highlight text that is being searched for .
* @ class
* /
var TextLayerBuilder = function TextLayerBuilderClosure ( ) {
function TextLayerBuilder ( options ) {
this . textLayerDiv = options . textLayerDiv ;
this . eventBus = options . eventBus || domEvents . getGlobalEventBus ( ) ;
this . textContent = null ;
this . renderingDone = false ;
this . pageIdx = options . pageIndex ;
this . pageNumber = this . pageIdx + 1 ;
this . matches = [ ] ;
this . viewport = options . viewport ;
this . textDivs = [ ] ;
this . findController = options . findController || null ;
this . textLayerRenderTask = null ;
this . enhanceTextSelection = options . enhanceTextSelection ;
this . _bindMouse ( ) ;
}
TextLayerBuilder . prototype = {
/ * *
* @ private
* /
_finishRendering : function TextLayerBuilder _finishRendering ( ) {
this . renderingDone = true ;
if ( ! this . enhanceTextSelection ) {
var endOfContent = document . createElement ( 'div' ) ;
endOfContent . className = 'endOfContent' ;
this . textLayerDiv . appendChild ( endOfContent ) ;
}
this . eventBus . dispatch ( 'textlayerrendered' , {
source : this ,
pageNumber : this . pageNumber ,
numTextDivs : this . textDivs . length
} ) ;
} ,
/ * *
* Renders the text layer .
* @ param { number } timeout ( optional ) if specified , the rendering waits
* for specified amount of ms .
* /
render : function TextLayerBuilder _render ( timeout ) {
if ( ! this . textContent || this . renderingDone ) {
return ;
}
this . cancel ( ) ;
this . textDivs = [ ] ;
var textLayerFrag = document . createDocumentFragment ( ) ;
this . textLayerRenderTask = pdfjsLib . renderTextLayer ( {
textContent : this . textContent ,
container : textLayerFrag ,
viewport : this . viewport ,
textDivs : this . textDivs ,
timeout : timeout ,
enhanceTextSelection : this . enhanceTextSelection
} ) ;
this . textLayerRenderTask . promise . then ( function ( ) {
this . textLayerDiv . appendChild ( textLayerFrag ) ;
this . _finishRendering ( ) ;
this . updateMatches ( ) ;
} . bind ( this ) , function ( reason ) {
} ) ;
} ,
/ * *
* Cancels rendering of the text layer .
* /
cancel : function TextLayerBuilder _cancel ( ) {
if ( this . textLayerRenderTask ) {
this . textLayerRenderTask . cancel ( ) ;
this . textLayerRenderTask = null ;
}
} ,
setTextContent : function TextLayerBuilder _setTextContent ( textContent ) {
this . cancel ( ) ;
this . textContent = textContent ;
} ,
convertMatches : function TextLayerBuilder _convertMatches ( matches , matchesLength ) {
var i = 0 ;
var iIndex = 0 ;
var bidiTexts = this . textContent . items ;
var end = bidiTexts . length - 1 ;
var queryLen = this . findController === null ? 0 : this . findController . state . query . length ;
var ret = [ ] ;
if ( ! matches ) {
return ret ;
}
for ( var m = 0 , len = matches . length ; m < len ; m ++ ) {
// Calculate the start position.
var matchIdx = matches [ m ] ;
// Loop over the divIdxs.
while ( i !== end && matchIdx >= iIndex + bidiTexts [ i ] . str . length ) {
iIndex += bidiTexts [ i ] . str . length ;
i ++ ;
}
if ( i === bidiTexts . length ) {
console . error ( 'Could not find a matching mapping' ) ;
}
var match = {
begin : {
divIdx : i ,
offset : matchIdx - iIndex
}
} ;
// Calculate the end position.
if ( matchesLength ) {
// multiterm search
matchIdx += matchesLength [ m ] ;
} 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 + bidiTexts [ i ] . str . length ) {
iIndex += bidiTexts [ i ] . str . length ;
i ++ ;
}
match . end = {
divIdx : i ,
offset : matchIdx - iIndex
} ;
ret . push ( match ) ;
}
return ret ;
} ,
renderMatches : function TextLayerBuilder _renderMatches ( matches ) {
// Early exit if there is nothing to render.
if ( matches . length === 0 ) {
return ;
}
var bidiTexts = this . textContent . items ;
var textDivs = this . textDivs ;
var prevEnd = null ;
var pageIdx = this . pageIdx ;
var isSelectedPage = this . findController === null ? false : pageIdx === this . findController . selected . pageIdx ;
var selectedMatchIdx = this . findController === null ? - 1 : this . findController . selected . matchIdx ;
var highlightAll = this . findController === null ? false : this . findController . state . highlightAll ;
var infinity = {
divIdx : - 1 ,
offset : undefined
} ;
function beginText ( begin , className ) {
var divIdx = begin . divIdx ;
textDivs [ divIdx ] . textContent = '' ;
appendTextToDiv ( divIdx , 0 , begin . offset , className ) ;
}
function appendTextToDiv ( divIdx , fromOffset , toOffset , className ) {
var div = textDivs [ divIdx ] ;
var content = bidiTexts [ divIdx ] . str . substring ( fromOffset , toOffset ) ;
var node = document . createTextNode ( content ) ;
if ( className ) {
var span = document . createElement ( 'span' ) ;
span . className = className ;
span . appendChild ( node ) ;
div . appendChild ( span ) ;
return ;
}
div . appendChild ( node ) ;
}
var i0 = selectedMatchIdx , i1 = i0 + 1 ;
if ( highlightAll ) {
i0 = 0 ;
i1 = matches . length ;
} else if ( ! isSelectedPage ) {
// Not highlighting all and this isn't the selected page, so do nothing.
return ;
}
for ( var i = i0 ; i < i1 ; i ++ ) {
var match = matches [ i ] ;
var begin = match . begin ;
var end = match . end ;
var isSelected = isSelectedPage && i === selectedMatchIdx ;
var highlightSuffix = isSelected ? ' selected' : '' ;
if ( this . findController ) {
this . findController . updateMatchPosition ( pageIdx , i , textDivs , begin . divIdx ) ;
}
// Match inside new div.
if ( ! prevEnd || begin . divIdx !== prevEnd . divIdx ) {
// If there was a previous div, then add the text at the end.
if ( prevEnd !== null ) {
appendTextToDiv ( prevEnd . divIdx , prevEnd . offset , infinity . offset ) ;
}
// Clear the divs and set the content until the starting point.
beginText ( begin ) ;
} else {
appendTextToDiv ( prevEnd . divIdx , prevEnd . offset , begin . offset ) ;
}
if ( begin . divIdx === end . divIdx ) {
appendTextToDiv ( begin . divIdx , begin . offset , end . offset , 'highlight' + highlightSuffix ) ;
} else {
appendTextToDiv ( begin . divIdx , begin . offset , infinity . offset , 'highlight begin' + highlightSuffix ) ;
for ( var n0 = begin . divIdx + 1 , n1 = end . divIdx ; n0 < n1 ; n0 ++ ) {
textDivs [ n0 ] . className = 'highlight middle' + highlightSuffix ;
}
beginText ( end , 'highlight end' + highlightSuffix ) ;
}
prevEnd = end ;
}
if ( prevEnd ) {
appendTextToDiv ( prevEnd . divIdx , prevEnd . offset , infinity . offset ) ;
}
} ,
updateMatches : function TextLayerBuilder _updateMatches ( ) {
// Only show matches when all rendering is done.
if ( ! this . renderingDone ) {
return ;
}
// Clear all matches.
var matches = this . matches ;
var textDivs = this . textDivs ;
var bidiTexts = this . textContent . items ;
var clearedUntilDivIdx = - 1 ;
// Clear all current matches.
for ( var i = 0 , len = matches . length ; i < len ; i ++ ) {
var match = matches [ i ] ;
var begin = Math . max ( clearedUntilDivIdx , match . begin . divIdx ) ;
for ( var n = begin , end = match . end . divIdx ; n <= end ; n ++ ) {
var div = textDivs [ n ] ;
div . textContent = bidiTexts [ n ] . str ;
div . className = '' ;
}
clearedUntilDivIdx = match . end . divIdx + 1 ;
}
if ( this . findController === null || ! this . findController . active ) {
return ;
}
// Convert the matches on the page controller into the match format
// used for the textLayer.
var pageMatches , pageMatchesLength ;
if ( this . findController !== null ) {
pageMatches = this . findController . pageMatches [ this . pageIdx ] || null ;
pageMatchesLength = this . findController . pageMatchesLength ? this . findController . pageMatchesLength [ this . pageIdx ] || null : null ;
}
this . matches = this . convertMatches ( pageMatches , pageMatchesLength ) ;
this . renderMatches ( this . matches ) ;
} ,
/ * *
* Fixes text selection : adds additional div where mouse was clicked .
* This reduces flickering of the content if mouse slowly dragged down / up .
* @ private
* /
_bindMouse : function TextLayerBuilder _bindMouse ( ) {
var div = this . textLayerDiv ;
var self = this ;
var expandDivsTimer = null ;
div . addEventListener ( 'mousedown' , function ( e ) {
if ( self . enhanceTextSelection && self . textLayerRenderTask ) {
self . textLayerRenderTask . expandTextDivs ( true ) ;
return ;
}
var end = div . querySelector ( '.endOfContent' ) ;
if ( ! end ) {
return ;
}
end . classList . add ( 'active' ) ;
} ) ;
div . addEventListener ( 'mouseup' , function ( e ) {
if ( self . enhanceTextSelection && self . textLayerRenderTask ) {
self . textLayerRenderTask . expandTextDivs ( false ) ;
return ;
}
var end = div . querySelector ( '.endOfContent' ) ;
if ( ! end ) {
return ;
}
end . classList . remove ( 'active' ) ;
} ) ;
}
} ;
return TextLayerBuilder ;
} ( ) ;
/ * *
* @ constructor
* @ implements IPDFTextLayerFactory
* /
function DefaultTextLayerFactory ( ) {
}
DefaultTextLayerFactory . prototype = {
/ * *
* @ param { HTMLDivElement } textLayerDiv
* @ param { number } pageIndex
* @ param { PageViewport } viewport
* @ param { boolean } enhanceTextSelection
* @ returns { TextLayerBuilder }
* /
createTextLayerBuilder : function ( textLayerDiv , pageIndex , viewport , enhanceTextSelection ) {
return new TextLayerBuilder ( {
textLayerDiv : textLayerDiv ,
pageIndex : pageIndex ,
viewport : viewport ,
enhanceTextSelection : enhanceTextSelection
} ) ;
}
} ;
exports . TextLayerBuilder = TextLayerBuilder ;
exports . DefaultTextLayerFactory = DefaultTextLayerFactory ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebAnnotationLayerBuilder = { } , root . pdfjsWebUIUtils , root . pdfjsWebPDFLinkService , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , uiUtils , pdfLinkService , pdfjsLib ) {
var mozL10n = uiUtils . mozL10n ;
var SimpleLinkService = pdfLinkService . SimpleLinkService ;
/ * *
* @ typedef { Object } AnnotationLayerBuilderOptions
* @ property { HTMLDivElement } pageDiv
* @ property { PDFPage } pdfPage
* @ property { boolean } renderInteractiveForms
* @ property { IPDFLinkService } linkService
* @ property { DownloadManager } downloadManager
* /
/ * *
* @ class
* /
var AnnotationLayerBuilder = function AnnotationLayerBuilderClosure ( ) {
/ * *
* @ param { AnnotationLayerBuilderOptions } options
* @ constructs AnnotationLayerBuilder
* /
function AnnotationLayerBuilder ( options ) {
this . pageDiv = options . pageDiv ;
this . pdfPage = options . pdfPage ;
this . renderInteractiveForms = options . renderInteractiveForms ;
this . linkService = options . linkService ;
this . downloadManager = options . downloadManager ;
this . div = null ;
}
AnnotationLayerBuilder . prototype = /** @lends AnnotationLayerBuilder.prototype */
{
/ * *
* @ param { PageViewport } viewport
* @ param { string } intent ( default value is 'display' )
* /
render : function AnnotationLayerBuilder _render ( viewport , intent ) {
var self = this ;
var parameters = { intent : intent === undefined ? 'display' : intent } ;
this . pdfPage . getAnnotations ( parameters ) . then ( function ( annotations ) {
viewport = viewport . clone ( { dontFlip : true } ) ;
parameters = {
viewport : viewport ,
div : self . div ,
annotations : annotations ,
page : self . pdfPage ,
renderInteractiveForms : self . renderInteractiveForms ,
linkService : self . linkService ,
downloadManager : self . downloadManager
} ;
if ( self . div ) {
// If an annotationLayer already exists, refresh its children's
// transformation matrices.
pdfjsLib . AnnotationLayer . update ( parameters ) ;
} else {
// Create an annotation layer div and render the annotations
// if there is at least one annotation.
if ( annotations . length === 0 ) {
return ;
}
self . div = document . createElement ( 'div' ) ;
self . div . className = 'annotationLayer' ;
self . pageDiv . appendChild ( self . div ) ;
parameters . div = self . div ;
pdfjsLib . AnnotationLayer . render ( parameters ) ;
if ( typeof mozL10n !== 'undefined' ) {
mozL10n . translate ( self . div ) ;
}
}
} ) ;
} ,
hide : function AnnotationLayerBuilder _hide ( ) {
if ( ! this . div ) {
return ;
}
this . div . setAttribute ( 'hidden' , 'true' ) ;
}
} ;
return AnnotationLayerBuilder ;
} ( ) ;
/ * *
* @ constructor
* @ implements IPDFAnnotationLayerFactory
* /
function DefaultAnnotationLayerFactory ( ) {
}
DefaultAnnotationLayerFactory . prototype = {
/ * *
* @ param { HTMLDivElement } pageDiv
* @ param { PDFPage } pdfPage
* @ param { boolean } renderInteractiveForms
* @ returns { AnnotationLayerBuilder }
* /
createAnnotationLayerBuilder : function ( pageDiv , pdfPage , renderInteractiveForms ) {
return new AnnotationLayerBuilder ( {
pageDiv : pageDiv ,
pdfPage : pdfPage ,
renderInteractiveForms : renderInteractiveForms ,
linkService : new SimpleLinkService ( )
} ) ;
}
} ;
exports . AnnotationLayerBuilder = AnnotationLayerBuilder ;
exports . DefaultAnnotationLayerFactory = DefaultAnnotationLayerFactory ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebPDFViewer = { } , root . pdfjsWebUIUtils , root . pdfjsWebPDFPageView , root . pdfjsWebPDFRenderingQueue , root . pdfjsWebTextLayerBuilder , root . pdfjsWebAnnotationLayerBuilder , root . pdfjsWebPDFLinkService , root . pdfjsWebDOMEvents , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , uiUtils , pdfPageView , pdfRenderingQueue , textLayerBuilder , annotationLayerBuilder , pdfLinkService , domEvents , pdfjsLib ) {
var UNKNOWN _SCALE = uiUtils . UNKNOWN _SCALE ;
var SCROLLBAR _PADDING = uiUtils . SCROLLBAR _PADDING ;
var VERTICAL _PADDING = uiUtils . VERTICAL _PADDING ;
var MAX _AUTO _SCALE = uiUtils . MAX _AUTO _SCALE ;
var CSS _UNITS = uiUtils . CSS _UNITS ;
var DEFAULT _SCALE = uiUtils . DEFAULT _SCALE ;
var DEFAULT _SCALE _VALUE = uiUtils . DEFAULT _SCALE _VALUE ;
var scrollIntoView = uiUtils . scrollIntoView ;
var watchScroll = uiUtils . watchScroll ;
var getVisibleElements = uiUtils . getVisibleElements ;
var PDFPageView = pdfPageView . PDFPageView ;
var RenderingStates = pdfRenderingQueue . RenderingStates ;
var PDFRenderingQueue = pdfRenderingQueue . PDFRenderingQueue ;
var TextLayerBuilder = textLayerBuilder . TextLayerBuilder ;
var AnnotationLayerBuilder = annotationLayerBuilder . AnnotationLayerBuilder ;
var SimpleLinkService = pdfLinkService . SimpleLinkService ;
var PresentationModeState = {
UNKNOWN : 0 ,
NORMAL : 1 ,
CHANGING : 2 ,
FULLSCREEN : 3
} ;
var DEFAULT _CACHE _SIZE = 10 ;
/ * *
* @ typedef { Object } PDFViewerOptions
* @ property { HTMLDivElement } container - The container for the viewer element .
* @ property { HTMLDivElement } viewer - ( optional ) The viewer element .
* @ property { EventBus } eventBus - The application event bus .
* @ property { IPDFLinkService } linkService - The navigation / linking service .
* @ property { DownloadManager } downloadManager - ( optional ) The download
* manager component .
* @ property { PDFRenderingQueue } renderingQueue - ( optional ) The rendering
* queue object .
* @ property { boolean } removePageBorders - ( optional ) Removes the border shadow
* around the pages . The default is false .
* @ property { boolean } enhanceTextSelection - ( optional ) Enables the improved
* text selection behaviour . The default is ` false ` .
* @ property { boolean } renderInteractiveForms - ( optional ) Enables rendering of
* interactive form elements . The default is ` false ` .
* /
/ * *
* Simple viewer control to display PDF content / pages .
* @ class
* @ implements { IRenderableView }
* /
var PDFViewer = function pdfViewer ( ) {
function PDFPageViewBuffer ( size ) {
var data = [ ] ;
this . push = function cachePush ( view ) {
var i = data . indexOf ( view ) ;
if ( i >= 0 ) {
data . splice ( i , 1 ) ;
}
data . push ( view ) ;
if ( data . length > size ) {
data . shift ( ) . destroy ( ) ;
}
} ;
this . resize = function ( newSize ) {
size = newSize ;
while ( data . length > size ) {
data . shift ( ) . destroy ( ) ;
}
} ;
}
function isSameScale ( oldScale , newScale ) {
if ( newScale === oldScale ) {
return true ;
}
if ( Math . abs ( newScale - oldScale ) < 1e-15 ) {
// Prevent unnecessary re-rendering of all pages when the scale
// changes only because of limited numerical precision.
return true ;
}
return false ;
}
/ * *
* @ constructs PDFViewer
* @ param { PDFViewerOptions } options
* /
function PDFViewer ( options ) {
this . container = options . container ;
this . viewer = options . viewer || options . container . firstElementChild ;
this . eventBus = options . eventBus || domEvents . getGlobalEventBus ( ) ;
this . linkService = options . linkService || new SimpleLinkService ( ) ;
this . downloadManager = options . downloadManager || null ;
this . removePageBorders = options . removePageBorders || false ;
this . enhanceTextSelection = options . enhanceTextSelection || false ;
this . renderInteractiveForms = options . renderInteractiveForms || false ;
this . defaultRenderingQueue = ! options . renderingQueue ;
if ( this . defaultRenderingQueue ) {
// Custom rendering queue is not specified, using default one
this . renderingQueue = new PDFRenderingQueue ( ) ;
this . renderingQueue . setViewer ( this ) ;
} else {
this . renderingQueue = options . renderingQueue ;
}
this . scroll = watchScroll ( this . container , this . _scrollUpdate . bind ( this ) ) ;
this . presentationModeState = PresentationModeState . UNKNOWN ;
this . _resetView ( ) ;
if ( this . removePageBorders ) {
this . viewer . classList . add ( 'removePageBorders' ) ;
}
}
PDFViewer . prototype = /** @lends PDFViewer.prototype */
{
get pagesCount ( ) {
return this . _pages . length ;
} ,
getPageView : function ( index ) {
return this . _pages [ index ] ;
} ,
/ * *
* @ returns { boolean } true if all { PDFPageView } objects are initialized .
* /
get pageViewsReady ( ) {
return this . _pageViewsReady ;
} ,
/ * *
* @ returns { number }
* /
get currentPageNumber ( ) {
return this . _currentPageNumber ;
} ,
/ * *
* @ param { number } val - The page number .
* /
set currentPageNumber ( val ) {
if ( ( val | 0 ) !== val ) {
// Ensure that `val` is an integer.
throw new Error ( 'Invalid page number.' ) ;
}
if ( ! this . pdfDocument ) {
this . _currentPageNumber = val ;
return ;
}
// The intent can be to just reset a scroll position and/or scale.
this . _setCurrentPageNumber ( val , /* resetCurrentPageView = */
true ) ;
} ,
/ * *
* @ private
* /
_setCurrentPageNumber : function PDFViewer _setCurrentPageNumber ( val , resetCurrentPageView ) {
if ( this . _currentPageNumber === val ) {
if ( resetCurrentPageView ) {
this . _resetCurrentPageView ( ) ;
}
return ;
}
if ( ! ( 0 < val && val <= this . pagesCount ) ) {
console . error ( 'PDFViewer_setCurrentPageNumber: "' + val + '" is out of bounds.' ) ;
return ;
}
var arg = {
source : this ,
pageNumber : val ,
pageLabel : this . _pageLabels && this . _pageLabels [ val - 1 ]
} ;
this . _currentPageNumber = val ;
this . eventBus . dispatch ( 'pagechanging' , arg ) ;
this . eventBus . dispatch ( 'pagechange' , arg ) ;
if ( resetCurrentPageView ) {
this . _resetCurrentPageView ( ) ;
}
} ,
/ * *
* @ returns { string | null } Returns the current page label ,
* or ` null ` if no page labels exist .
* /
get currentPageLabel ( ) {
return this . _pageLabels && this . _pageLabels [ this . _currentPageNumber - 1 ] ;
} ,
/ * *
* @ param { string } val - The page label .
* /
set currentPageLabel ( val ) {
var pageNumber = val | 0 ;
// Fallback page number.
if ( this . _pageLabels ) {
var i = this . _pageLabels . indexOf ( val ) ;
if ( i >= 0 ) {
pageNumber = i + 1 ;
}
}
this . currentPageNumber = pageNumber ;
} ,
/ * *
* @ returns { number }
* /
get currentScale ( ) {
return this . _currentScale !== UNKNOWN _SCALE ? this . _currentScale : DEFAULT _SCALE ;
} ,
/ * *
* @ param { number } val - Scale of the pages in percents .
* /
set currentScale ( val ) {
if ( isNaN ( val ) ) {
throw new Error ( 'Invalid numeric scale' ) ;
}
if ( ! this . pdfDocument ) {
this . _currentScale = val ;
this . _currentScaleValue = val !== UNKNOWN _SCALE ? val . toString ( ) : null ;
return ;
}
this . _setScale ( val , false ) ;
} ,
/ * *
* @ returns { string }
* /
get currentScaleValue ( ) {
return this . _currentScaleValue ;
} ,
/ * *
* @ param val - The scale of the pages ( in percent or predefined value ) .
* /
set currentScaleValue ( val ) {
if ( ! this . pdfDocument ) {
this . _currentScale = isNaN ( val ) ? UNKNOWN _SCALE : val ;
this . _currentScaleValue = val . toString ( ) ;
return ;
}
this . _setScale ( val , false ) ;
} ,
/ * *
* @ returns { number }
* /
get pagesRotation ( ) {
return this . _pagesRotation ;
} ,
/ * *
* @ param { number } rotation - The rotation of the pages ( 0 , 90 , 180 , 270 ) .
* /
set pagesRotation ( rotation ) {
if ( ! ( typeof rotation === 'number' && rotation % 90 === 0 ) ) {
throw new Error ( 'Invalid pages rotation angle.' ) ;
}
this . _pagesRotation = rotation ;
if ( ! this . pdfDocument ) {
return ;
}
for ( var i = 0 , l = this . _pages . length ; i < l ; i ++ ) {
var pageView = this . _pages [ i ] ;
pageView . update ( pageView . scale , rotation ) ;
}
this . _setScale ( this . _currentScaleValue , true ) ;
if ( this . defaultRenderingQueue ) {
this . update ( ) ;
}
} ,
/ * *
* @ param pdfDocument { PDFDocument }
* /
setDocument : function ( pdfDocument ) {
if ( this . pdfDocument ) {
this . _cancelRendering ( ) ;
this . _resetView ( ) ;
}
this . pdfDocument = pdfDocument ;
if ( ! pdfDocument ) {
return ;
}
var pagesCount = pdfDocument . numPages ;
var self = this ;
var resolvePagesPromise ;
var pagesPromise = new Promise ( function ( resolve ) {
resolvePagesPromise = resolve ;
} ) ;
this . pagesPromise = pagesPromise ;
pagesPromise . then ( function ( ) {
self . _pageViewsReady = true ;
self . eventBus . dispatch ( 'pagesloaded' , {
source : self ,
pagesCount : pagesCount
} ) ;
} ) ;
var isOnePageRenderedResolved = false ;
var resolveOnePageRendered = null ;
var onePageRendered = new Promise ( function ( resolve ) {
resolveOnePageRendered = resolve ;
} ) ;
this . onePageRendered = onePageRendered ;
var bindOnAfterAndBeforeDraw = function ( pageView ) {
pageView . onBeforeDraw = function pdfViewLoadOnBeforeDraw ( ) {
// Add the page to the buffer at the start of drawing. That way it can
// be evicted from the buffer and destroyed even if we pause its
// rendering.
self . _buffer . push ( this ) ;
} ;
pageView . onAfterDraw = function pdfViewLoadOnAfterDraw ( ) {
if ( ! isOnePageRenderedResolved ) {
isOnePageRenderedResolved = true ;
resolveOnePageRendered ( ) ;
}
} ;
} ;
var firstPagePromise = pdfDocument . getPage ( 1 ) ;
this . firstPagePromise = firstPagePromise ;
// Fetch a single page so we can get a viewport that will be the default
// viewport for all pages
return firstPagePromise . then ( function ( pdfPage ) {
var scale = this . currentScale ;
var viewport = pdfPage . getViewport ( scale * CSS _UNITS ) ;
for ( var pageNum = 1 ; pageNum <= pagesCount ; ++ pageNum ) {
var textLayerFactory = null ;
if ( ! pdfjsLib . PDFJS . disableTextLayer ) {
textLayerFactory = this ;
}
var pageView = new PDFPageView ( {
container : this . viewer ,
eventBus : this . eventBus ,
id : pageNum ,
scale : scale ,
defaultViewport : viewport . clone ( ) ,
renderingQueue : this . renderingQueue ,
textLayerFactory : textLayerFactory ,
annotationLayerFactory : this ,
enhanceTextSelection : this . enhanceTextSelection ,
renderInteractiveForms : this . renderInteractiveForms
} ) ;
bindOnAfterAndBeforeDraw ( pageView ) ;
this . _pages . push ( pageView ) ;
}
var linkService = this . linkService ;
// 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.
onePageRendered . then ( function ( ) {
if ( ! pdfjsLib . PDFJS . disableAutoFetch ) {
var getPagesLeft = pagesCount ;
for ( var pageNum = 1 ; pageNum <= pagesCount ; ++ pageNum ) {
pdfDocument . getPage ( pageNum ) . then ( function ( pageNum , pdfPage ) {
var pageView = self . _pages [ pageNum - 1 ] ;
if ( ! pageView . pdfPage ) {
pageView . setPdfPage ( pdfPage ) ;
}
linkService . cachePageRef ( pageNum , pdfPage . ref ) ;
getPagesLeft -- ;
if ( ! getPagesLeft ) {
resolvePagesPromise ( ) ;
}
} . bind ( null , pageNum ) ) ;
}
} else {
// XXX: Printing is semi-broken with auto fetch disabled.
resolvePagesPromise ( ) ;
}
} ) ;
self . eventBus . dispatch ( 'pagesinit' , { source : self } ) ;
if ( this . defaultRenderingQueue ) {
this . update ( ) ;
}
if ( this . findController ) {
this . findController . resolveFirstPage ( ) ;
}
} . bind ( this ) ) ;
} ,
/ * *
* @ param { Array | null } labels
* /
setPageLabels : function PDFViewer _setPageLabels ( labels ) {
if ( ! this . pdfDocument ) {
return ;
}
if ( ! labels ) {
this . _pageLabels = null ;
} else if ( ! ( labels instanceof Array && this . pdfDocument . numPages === labels . length ) ) {
this . _pageLabels = null ;
console . error ( 'PDFViewer_setPageLabels: Invalid page labels.' ) ;
} else {
this . _pageLabels = labels ;
}
// Update all the `PDFPageView` instances.
for ( var i = 0 , ii = this . _pages . length ; i < ii ; i ++ ) {
var pageView = this . _pages [ i ] ;
var label = this . _pageLabels && this . _pageLabels [ i ] ;
pageView . setPageLabel ( label ) ;
}
} ,
_resetView : function ( ) {
this . _pages = [ ] ;
this . _currentPageNumber = 1 ;
this . _currentScale = UNKNOWN _SCALE ;
this . _currentScaleValue = null ;
this . _pageLabels = null ;
this . _buffer = new PDFPageViewBuffer ( DEFAULT _CACHE _SIZE ) ;
this . _location = null ;
this . _pagesRotation = 0 ;
this . _pagesRequests = [ ] ;
this . _pageViewsReady = false ;
// Remove the pages from the DOM.
this . viewer . textContent = '' ;
} ,
_scrollUpdate : function PDFViewer _scrollUpdate ( ) {
if ( this . pagesCount === 0 ) {
return ;
}
this . update ( ) ;
for ( var i = 0 , ii = this . _pages . length ; i < ii ; i ++ ) {
this . _pages [ i ] . updatePosition ( ) ;
}
} ,
_setScaleDispatchEvent : function pdfViewer _setScaleDispatchEvent ( newScale , newValue , preset ) {
var arg = {
source : this ,
scale : newScale ,
presetValue : preset ? newValue : undefined
} ;
this . eventBus . dispatch ( 'scalechanging' , arg ) ;
this . eventBus . dispatch ( 'scalechange' , arg ) ;
} ,
_setScaleUpdatePages : function pdfViewer _setScaleUpdatePages ( newScale , newValue , noScroll , preset ) {
this . _currentScaleValue = newValue . toString ( ) ;
if ( isSameScale ( this . _currentScale , newScale ) ) {
if ( preset ) {
this . _setScaleDispatchEvent ( newScale , newValue , true ) ;
}
return ;
}
for ( var i = 0 , ii = this . _pages . length ; i < ii ; i ++ ) {
this . _pages [ i ] . update ( newScale ) ;
}
this . _currentScale = newScale ;
if ( ! noScroll ) {
var page = this . _currentPageNumber , dest ;
if ( this . _location && ! pdfjsLib . PDFJS . ignoreCurrentPositionOnZoom && ! ( this . isInPresentationMode || this . isChangingPresentationMode ) ) {
page = this . _location . pageNumber ;
dest = [
null ,
{ name : 'XYZ' } ,
this . _location . left ,
this . _location . top ,
null
] ;
}
this . scrollPageIntoView ( {
pageNumber : page ,
destArray : dest ,
allowNegativeOffset : true
} ) ;
}
this . _setScaleDispatchEvent ( newScale , newValue , preset ) ;
if ( this . defaultRenderingQueue ) {
this . update ( ) ;
}
} ,
_setScale : function PDFViewer _setScale ( value , noScroll ) {
var scale = parseFloat ( value ) ;
if ( scale > 0 ) {
this . _setScaleUpdatePages ( scale , value , noScroll , false ) ;
} else {
var currentPage = this . _pages [ this . _currentPageNumber - 1 ] ;
if ( ! currentPage ) {
return ;
}
var hPadding = this . isInPresentationMode || this . removePageBorders ? 0 : SCROLLBAR _PADDING ;
var vPadding = this . isInPresentationMode || this . removePageBorders ? 0 : VERTICAL _PADDING ;
var pageWidthScale = ( this . container . clientWidth - hPadding ) / currentPage . width * currentPage . scale ;
var pageHeightScale = ( this . container . clientHeight - vPadding ) / currentPage . height * currentPage . scale ;
switch ( value ) {
case 'page-actual' :
scale = 1 ;
break ;
case 'page-width' :
scale = pageWidthScale ;
break ;
case 'page-height' :
scale = pageHeightScale ;
break ;
case 'page-fit' :
scale = Math . min ( pageWidthScale , pageHeightScale ) ;
break ;
case 'auto' :
var isLandscape = currentPage . width > currentPage . height ;
// For pages in landscape mode, fit the page height to the viewer
// *unless* the page would thus become too wide to fit horizontally.
var horizontalScale = isLandscape ? Math . min ( pageHeightScale , pageWidthScale ) : pageWidthScale ;
scale = Math . min ( MAX _AUTO _SCALE , horizontalScale ) ;
break ;
default :
console . error ( 'PDFViewer_setScale: "' + value + '" is an unknown zoom value.' ) ;
return ;
}
this . _setScaleUpdatePages ( scale , value , noScroll , true ) ;
}
} ,
/ * *
* Refreshes page view : scrolls to the current page and updates the scale .
* @ private
* /
_resetCurrentPageView : function ( ) {
if ( this . isInPresentationMode ) {
// Fixes the case when PDF has different page sizes.
this . _setScale ( this . _currentScaleValue , true ) ;
}
var pageView = this . _pages [ this . _currentPageNumber - 1 ] ;
scrollIntoView ( pageView . div ) ;
} ,
/ * *
* @ typedef ScrollPageIntoViewParameters
* @ property { number } pageNumber - The page number .
* @ property { Array } destArray - ( optional ) The original PDF destination
* array , in the format : < page - ref > < / X Y Z | / F i t X X X > < a r g s . . >
* @ property { boolean } allowNegativeOffset - ( optional ) Allow negative page
* offsets . The default value is ` false ` .
* /
/ * *
* Scrolls page into view .
* @ param { ScrollPageIntoViewParameters } params
* /
scrollPageIntoView : function PDFViewer _scrollPageIntoView ( params ) {
if ( ! this . pdfDocument ) {
return ;
}
var pageNumber = params . pageNumber || 0 ;
var dest = params . destArray || null ;
var allowNegativeOffset = params . allowNegativeOffset || false ;
if ( this . isInPresentationMode || ! dest ) {
this . _setCurrentPageNumber ( pageNumber , /* resetCurrentPageView */
true ) ;
return ;
}
var pageView = this . _pages [ pageNumber - 1 ] ;
if ( ! pageView ) {
console . error ( 'PDFViewer_scrollPageIntoView: ' + 'Invalid "pageNumber" parameter.' ) ;
return ;
}
var x = 0 , y = 0 ;
var width = 0 , height = 0 , widthScale , heightScale ;
var changeOrientation = pageView . rotation % 180 === 0 ? false : true ;
var pageWidth = ( changeOrientation ? pageView . height : pageView . width ) / pageView . scale / CSS _UNITS ;
var pageHeight = ( changeOrientation ? pageView . width : pageView . height ) / pageView . scale / CSS _UNITS ;
var scale = 0 ;
switch ( dest [ 1 ] . name ) {
case 'XYZ' :
x = dest [ 2 ] ;
y = dest [ 3 ] ;
scale = dest [ 4 ] ;
// If x and/or y coordinates are not supplied, default to
// _top_ left of the page (not the obvious bottom left,
// since aligning the bottom of the intended page with the
// top of the window is rarely helpful).
x = x !== null ? x : 0 ;
y = y !== null ? y : pageHeight ;
break ;
case 'Fit' :
case 'FitB' :
scale = 'page-fit' ;
break ;
case 'FitH' :
case 'FitBH' :
y = dest [ 2 ] ;
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 ) {
x = this . _location . left ;
y = this . _location . top ;
}
break ;
case 'FitV' :
case 'FitBV' :
x = dest [ 2 ] ;
width = pageWidth ;
height = pageHeight ;
scale = 'page-height' ;
break ;
case 'FitR' :
x = dest [ 2 ] ;
y = dest [ 3 ] ;
width = dest [ 4 ] - x ;
height = dest [ 5 ] - y ;
var hPadding = this . removePageBorders ? 0 : SCROLLBAR _PADDING ;
var vPadding = this . removePageBorders ? 0 : VERTICAL _PADDING ;
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 ( 'PDFViewer_scrollPageIntoView: \'' + dest [ 1 ] . name + '\' is not a valid destination type.' ) ;
return ;
}
if ( scale && scale !== this . _currentScale ) {
this . currentScaleValue = scale ;
} else if ( this . _currentScale === UNKNOWN _SCALE ) {
this . currentScaleValue = DEFAULT _SCALE _VALUE ;
}
if ( scale === 'page-fit' && ! dest [ 4 ] ) {
scrollIntoView ( pageView . div ) ;
return ;
}
var boundingRect = [
pageView . viewport . convertToViewportPoint ( x , y ) ,
pageView . viewport . convertToViewportPoint ( x + width , y + height )
] ;
var left = Math . min ( boundingRect [ 0 ] [ 0 ] , boundingRect [ 1 ] [ 0 ] ) ;
var top = Math . min ( boundingRect [ 0 ] [ 1 ] , boundingRect [ 1 ] [ 1 ] ) ;
if ( ! allowNegativeOffset ) {
// Some bad PDF generators will create destinations with e.g. top values
// that exceeds the page height. Ensure that offsets are not negative,
// to prevent a previous page from becoming visible (fixes bug 874482).
left = Math . max ( left , 0 ) ;
top = Math . max ( top , 0 ) ;
}
scrollIntoView ( pageView . div , {
left : left ,
top : top
} ) ;
} ,
_updateLocation : function ( firstPage ) {
var currentScale = this . _currentScale ;
var currentScaleValue = this . _currentScaleValue ;
var normalizedScaleValue = parseFloat ( currentScaleValue ) === currentScale ? Math . round ( currentScale * 10000 ) / 100 : currentScaleValue ;
var pageNumber = firstPage . id ;
var pdfOpenParams = '#page=' + pageNumber ;
pdfOpenParams += '&zoom=' + normalizedScaleValue ;
var currentPageView = this . _pages [ pageNumber - 1 ] ;
var container = this . container ;
var topLeft = currentPageView . getPagePoint ( container . scrollLeft - firstPage . x , container . scrollTop - firstPage . y ) ;
var intLeft = Math . round ( topLeft [ 0 ] ) ;
var intTop = Math . round ( topLeft [ 1 ] ) ;
pdfOpenParams += ',' + intLeft + ',' + intTop ;
this . _location = {
pageNumber : pageNumber ,
scale : normalizedScaleValue ,
top : intTop ,
left : intLeft ,
pdfOpenParams : pdfOpenParams
} ;
} ,
update : function PDFViewer _update ( ) {
var visible = this . _getVisiblePages ( ) ;
var visiblePages = visible . views ;
if ( visiblePages . length === 0 ) {
return ;
}
var suggestedCacheSize = Math . max ( DEFAULT _CACHE _SIZE , 2 * visiblePages . length + 1 ) ;
this . _buffer . resize ( suggestedCacheSize ) ;
this . renderingQueue . renderHighestPriority ( visible ) ;
var currentId = this . _currentPageNumber ;
var firstPage = visible . first ;
for ( var i = 0 , ii = visiblePages . length , stillFullyVisible = false ; i < ii ; ++ i ) {
var page = visiblePages [ i ] ;
if ( page . percent < 100 ) {
break ;
}
if ( page . id === currentId ) {
stillFullyVisible = true ;
break ;
}
}
if ( ! stillFullyVisible ) {
currentId = visiblePages [ 0 ] . id ;
}
if ( ! this . isInPresentationMode ) {
this . _setCurrentPageNumber ( currentId ) ;
}
this . _updateLocation ( firstPage ) ;
this . eventBus . dispatch ( 'updateviewarea' , {
source : this ,
location : this . _location
} ) ;
} ,
containsElement : function ( element ) {
return this . container . contains ( element ) ;
} ,
focus : function ( ) {
this . container . focus ( ) ;
} ,
get isInPresentationMode ( ) {
return this . presentationModeState === PresentationModeState . FULLSCREEN ;
} ,
get isChangingPresentationMode ( ) {
return this . presentationModeState === PresentationModeState . CHANGING ;
} ,
get isHorizontalScrollbarEnabled ( ) {
return this . isInPresentationMode ? false : this . container . scrollWidth > this . container . clientWidth ;
} ,
_getVisiblePages : function ( ) {
if ( ! this . isInPresentationMode ) {
return getVisibleElements ( this . container , this . _pages , true ) ;
} else {
// The algorithm in getVisibleElements doesn't work in all browsers and
// configurations when presentation mode is active.
var visible = [ ] ;
var currentPage = this . _pages [ this . _currentPageNumber - 1 ] ;
visible . push ( {
id : currentPage . id ,
view : currentPage
} ) ;
return {
first : currentPage ,
last : currentPage ,
views : visible
} ;
}
} ,
cleanup : function ( ) {
for ( var i = 0 , ii = this . _pages . length ; i < ii ; i ++ ) {
if ( this . _pages [ i ] && this . _pages [ i ] . renderingState !== RenderingStates . FINISHED ) {
this . _pages [ i ] . reset ( ) ;
}
}
} ,
/ * *
* @ private
* /
_cancelRendering : function PDFViewer _cancelRendering ( ) {
for ( var i = 0 , ii = this . _pages . length ; i < ii ; i ++ ) {
if ( this . _pages [ i ] ) {
this . _pages [ i ] . cancelRendering ( ) ;
}
}
} ,
/ * *
* @ param { PDFPageView } pageView
* @ returns { PDFPage }
* @ private
* /
_ensurePdfPageLoaded : function ( pageView ) {
if ( pageView . pdfPage ) {
return Promise . resolve ( pageView . pdfPage ) ;
}
var pageNumber = pageView . id ;
if ( this . _pagesRequests [ pageNumber ] ) {
return this . _pagesRequests [ pageNumber ] ;
}
var promise = this . pdfDocument . getPage ( pageNumber ) . then ( function ( pdfPage ) {
pageView . setPdfPage ( pdfPage ) ;
this . _pagesRequests [ pageNumber ] = null ;
return pdfPage ;
} . bind ( this ) ) ;
this . _pagesRequests [ pageNumber ] = promise ;
return promise ;
} ,
forceRendering : function ( currentlyVisiblePages ) {
var visiblePages = currentlyVisiblePages || this . _getVisiblePages ( ) ;
var pageView = this . renderingQueue . getHighestPriority ( visiblePages , this . _pages , this . scroll . down ) ;
if ( pageView ) {
this . _ensurePdfPageLoaded ( pageView ) . then ( function ( ) {
this . renderingQueue . renderView ( pageView ) ;
} . bind ( this ) ) ;
return true ;
}
return false ;
} ,
getPageTextContent : function ( pageIndex ) {
return this . pdfDocument . getPage ( pageIndex + 1 ) . then ( function ( page ) {
return page . getTextContent ( { normalizeWhitespace : true } ) ;
} ) ;
} ,
/ * *
* @ param { HTMLDivElement } textLayerDiv
* @ param { number } pageIndex
* @ param { PageViewport } viewport
* @ returns { TextLayerBuilder }
* /
createTextLayerBuilder : function ( textLayerDiv , pageIndex , viewport , enhanceTextSelection ) {
return new TextLayerBuilder ( {
textLayerDiv : textLayerDiv ,
eventBus : this . eventBus ,
pageIndex : pageIndex ,
viewport : viewport ,
findController : this . isInPresentationMode ? null : this . findController ,
enhanceTextSelection : this . isInPresentationMode ? false : enhanceTextSelection
} ) ;
} ,
/ * *
* @ param { HTMLDivElement } pageDiv
* @ param { PDFPage } pdfPage
* @ param { boolean } renderInteractiveForms
* @ returns { AnnotationLayerBuilder }
* /
createAnnotationLayerBuilder : function ( pageDiv , pdfPage , renderInteractiveForms ) {
return new AnnotationLayerBuilder ( {
pageDiv : pageDiv ,
pdfPage : pdfPage ,
renderInteractiveForms : renderInteractiveForms ,
linkService : this . linkService ,
downloadManager : this . downloadManager
} ) ;
} ,
setFindController : function ( findController ) {
this . findController = findController ;
} ,
/ * *
* Returns sizes of the pages .
* @ returns { Array } Array of objects with width / height fields .
* /
getPagesOverview : function ( ) {
return this . _pages . map ( function ( pageView ) {
var viewport = pageView . pdfPage . getViewport ( 1 ) ;
return {
width : viewport . width ,
height : viewport . height
} ;
} ) ;
}
} ;
return PDFViewer ;
} ( ) ;
exports . PresentationModeState = PresentationModeState ;
exports . PDFViewer = PDFViewer ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebApp = { } , root . pdfjsWebUIUtils , root . pdfjsWebDownloadManager , root . pdfjsWebPDFHistory , root . pdfjsWebPreferences , root . pdfjsWebPDFSidebar , root . pdfjsWebViewHistory , root . pdfjsWebPDFThumbnailViewer , root . pdfjsWebSecondaryToolbar , root . pdfjsWebPasswordPrompt , root . pdfjsWebPDFPresentationMode , root . pdfjsWebPDFDocumentProperties , root . pdfjsWebHandTool , root . pdfjsWebPDFViewer , root . pdfjsWebPDFRenderingQueue , root . pdfjsWebPDFLinkService , root . pdfjsWebPDFOutlineViewer , root . pdfjsWebOverlayManager , root . pdfjsWebPDFAttachmentViewer , root . pdfjsWebPDFFindController , root . pdfjsWebPDFFindBar , root . pdfjsWebDOMEvents , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , uiUtilsLib , downloadManagerLib , pdfHistoryLib , preferencesLib , pdfSidebarLib , viewHistoryLib , pdfThumbnailViewerLib , secondaryToolbarLib , passwordPromptLib , pdfPresentationModeLib , pdfDocumentPropertiesLib , handToolLib , pdfViewerLib , pdfRenderingQueueLib , pdfLinkServiceLib , pdfOutlineViewerLib , overlayManagerLib , pdfAttachmentViewerLib , pdfFindControllerLib , pdfFindBarLib , domEventsLib , pdfjsLib ) {
var UNKNOWN _SCALE = uiUtilsLib . UNKNOWN _SCALE ;
var DEFAULT _SCALE _VALUE = uiUtilsLib . DEFAULT _SCALE _VALUE ;
var ProgressBar = uiUtilsLib . ProgressBar ;
var getPDFFileNameFromURL = uiUtilsLib . getPDFFileNameFromURL ;
var noContextMenuHandler = uiUtilsLib . noContextMenuHandler ;
var mozL10n = uiUtilsLib . mozL10n ;
var parseQueryString = uiUtilsLib . parseQueryString ;
var PDFHistory = pdfHistoryLib . PDFHistory ;
var Preferences = preferencesLib . Preferences ;
var SidebarView = pdfSidebarLib . SidebarView ;
var PDFSidebar = pdfSidebarLib . PDFSidebar ;
var ViewHistory = viewHistoryLib . ViewHistory ;
var PDFThumbnailViewer = pdfThumbnailViewerLib . PDFThumbnailViewer ;
var SecondaryToolbar = secondaryToolbarLib . SecondaryToolbar ;
var PasswordPrompt = passwordPromptLib . PasswordPrompt ;
var PDFPresentationMode = pdfPresentationModeLib . PDFPresentationMode ;
var PDFDocumentProperties = pdfDocumentPropertiesLib . PDFDocumentProperties ;
var HandTool = handToolLib . HandTool ;
var PresentationModeState = pdfViewerLib . PresentationModeState ;
var PDFViewer = pdfViewerLib . PDFViewer ;
var RenderingStates = pdfRenderingQueueLib . RenderingStates ;
var PDFRenderingQueue = pdfRenderingQueueLib . PDFRenderingQueue ;
var PDFLinkService = pdfLinkServiceLib . PDFLinkService ;
var PDFOutlineViewer = pdfOutlineViewerLib . PDFOutlineViewer ;
var OverlayManager = overlayManagerLib . OverlayManager ;
var PDFAttachmentViewer = pdfAttachmentViewerLib . PDFAttachmentViewer ;
var PDFFindController = pdfFindControllerLib . PDFFindController ;
var PDFFindBar = pdfFindBarLib . PDFFindBar ;
var getGlobalEventBus = domEventsLib . getGlobalEventBus ;
var normalizeWheelEventDelta = uiUtilsLib . normalizeWheelEventDelta ;
var DEFAULT _SCALE _DELTA = 1.1 ;
var MIN _SCALE = 0.25 ;
var MAX _SCALE = 10.0 ;
var SCALE _SELECT _CONTAINER _PADDING = 8 ;
var SCALE _SELECT _PADDING = 22 ;
var PAGE _NUMBER _LOADING _INDICATOR = 'visiblePageIsLoading' ;
var DISABLE _AUTO _FETCH _LOADING _BAR _TIMEOUT = 5000 ;
function configure ( PDFJS ) {
PDFJS . imageResourcesPath = './images/' ;
PDFJS . workerSrc = '../build/pdf.worker.js' ;
PDFJS . cMapUrl = '../web/cmaps/' ;
PDFJS . cMapPacked = true ;
}
var DefaultExernalServices = {
updateFindControlState : function ( data ) {
} ,
initPassiveLoading : function ( callbacks ) {
} ,
fallback : function ( data , callback ) {
} ,
createDownloadManager : function ( ) {
return new downloadManagerLib . DownloadManager ( ) ;
} ,
supportsIntegratedFind : false ,
supportsDocumentFonts : true ,
supportsDocumentColors : true ,
supportedMouseWheelZoomModifierKeys : {
ctrlKey : true ,
metaKey : true
}
} ;
var PDFViewerApplication = {
initialBookmark : document . location . hash . substring ( 1 ) ,
initialDestination : null ,
initialized : false ,
fellback : false ,
appConfig : null ,
pdfDocument : null ,
pdfLoadingTask : null ,
printService : null ,
/** @type {PDFViewer} */
pdfViewer : null ,
/** @type {PDFThumbnailViewer} */
pdfThumbnailViewer : null ,
/** @type {PDFRenderingQueue} */
pdfRenderingQueue : null ,
/** @type {PDFPresentationMode} */
pdfPresentationMode : null ,
/** @type {PDFDocumentProperties} */
pdfDocumentProperties : null ,
/** @type {PDFLinkService} */
pdfLinkService : null ,
/** @type {PDFHistory} */
pdfHistory : null ,
/** @type {PDFSidebar} */
pdfSidebar : null ,
/** @type {PDFOutlineViewer} */
pdfOutlineViewer : null ,
/** @type {PDFAttachmentViewer} */
pdfAttachmentViewer : null ,
/** @type {ViewHistory} */
store : null ,
/** @type {DownloadManager} */
downloadManager : null ,
/** @type {EventBus} */
eventBus : null ,
pageRotation : 0 ,
isInitialViewSet : false ,
animationStartedPromise : null ,
preferenceSidebarViewOnLoad : SidebarView . NONE ,
preferencePdfBugEnabled : false ,
preferenceShowPreviousViewOnLoad : true ,
preferenceDefaultZoomValue : '' ,
preferenceDisablePageLabels : false ,
isViewerEmbedded : window . parent !== window ,
url : '' ,
baseUrl : '' ,
externalServices : DefaultExernalServices ,
hasPageLabels : false ,
// called once when the document is loaded
initialize : function pdfViewInitialize ( appConfig ) {
configure ( pdfjsLib . PDFJS ) ;
this . appConfig = appConfig ;
var eventBus = appConfig . eventBus || getGlobalEventBus ( ) ;
this . eventBus = eventBus ;
this . bindEvents ( ) ;
var pdfRenderingQueue = new PDFRenderingQueue ( ) ;
pdfRenderingQueue . onIdle = this . cleanup . bind ( this ) ;
this . pdfRenderingQueue = pdfRenderingQueue ;
var pdfLinkService = new PDFLinkService ( { eventBus : eventBus } ) ;
this . pdfLinkService = pdfLinkService ;
var downloadManager = this . externalServices . createDownloadManager ( ) ;
this . downloadManager = downloadManager ;
var container = appConfig . mainContainer ;
var viewer = appConfig . viewerContainer ;
this . pdfViewer = new PDFViewer ( {
container : container ,
viewer : viewer ,
eventBus : eventBus ,
renderingQueue : pdfRenderingQueue ,
linkService : pdfLinkService ,
downloadManager : downloadManager ,
enhanceTextSelection : false ,
renderInteractiveForms : false
} ) ;
pdfRenderingQueue . setViewer ( this . pdfViewer ) ;
pdfLinkService . setViewer ( this . pdfViewer ) ;
var thumbnailContainer = appConfig . sidebar . thumbnailView ;
this . pdfThumbnailViewer = new PDFThumbnailViewer ( {
container : thumbnailContainer ,
renderingQueue : pdfRenderingQueue ,
linkService : pdfLinkService
} ) ;
pdfRenderingQueue . setThumbnailViewer ( this . pdfThumbnailViewer ) ;
Preferences . initialize ( ) ;
this . preferences = Preferences ;
this . pdfHistory = new PDFHistory ( {
linkService : pdfLinkService ,
eventBus : this . eventBus
} ) ;
pdfLinkService . setHistory ( this . pdfHistory ) ;
this . findController = new PDFFindController ( { pdfViewer : this . pdfViewer } ) ;
this . findController . onUpdateResultsCount = function ( matchCount ) {
if ( this . supportsIntegratedFind ) {
return ;
}
this . findBar . updateResultsCount ( matchCount ) ;
} . bind ( this ) ;
this . findController . onUpdateState = function ( state , previous , matchCount ) {
if ( this . supportsIntegratedFind ) {
this . externalServices . updateFindControlState ( {
result : state ,
findPrevious : previous
} ) ;
} else {
this . findBar . updateUIState ( state , previous , matchCount ) ;
}
} . bind ( this ) ;
this . pdfViewer . setFindController ( this . findController ) ;
// FIXME better PDFFindBar constructor parameters
var findBarConfig = Object . create ( appConfig . findBar ) ;
findBarConfig . findController = this . findController ;
findBarConfig . eventBus = this . eventBus ;
this . findBar = new PDFFindBar ( findBarConfig ) ;
this . overlayManager = OverlayManager ;
this . handTool = new HandTool ( {
container : container ,
eventBus : this . eventBus
} ) ;
this . pdfDocumentProperties = new PDFDocumentProperties ( appConfig . documentProperties ) ;
this . secondaryToolbar = new SecondaryToolbar ( appConfig . secondaryToolbar , container , eventBus ) ;
if ( this . supportsFullscreen ) {
this . pdfPresentationMode = new PDFPresentationMode ( {
container : container ,
viewer : viewer ,
pdfViewer : this . pdfViewer ,
eventBus : this . eventBus ,
contextMenuItems : appConfig . fullscreen
} ) ;
}
this . passwordPrompt = new PasswordPrompt ( appConfig . passwordOverlay ) ;
this . pdfOutlineViewer = new PDFOutlineViewer ( {
container : appConfig . sidebar . outlineView ,
eventBus : this . eventBus ,
linkService : pdfLinkService
} ) ;
this . pdfAttachmentViewer = new PDFAttachmentViewer ( {
container : appConfig . sidebar . attachmentsView ,
eventBus : this . eventBus ,
downloadManager : downloadManager
} ) ;
// FIXME better PDFSidebar constructor parameters
var sidebarConfig = Object . create ( appConfig . sidebar ) ;
sidebarConfig . pdfViewer = this . pdfViewer ;
sidebarConfig . pdfThumbnailViewer = this . pdfThumbnailViewer ;
sidebarConfig . pdfOutlineViewer = this . pdfOutlineViewer ;
sidebarConfig . eventBus = this . eventBus ;
this . pdfSidebar = new PDFSidebar ( sidebarConfig ) ;
this . pdfSidebar . onToggled = this . forceRendering . bind ( this ) ;
var self = this ;
var PDFJS = pdfjsLib . PDFJS ;
var initializedPromise = Promise . all ( [
Preferences . get ( 'enableWebGL' ) . then ( function resolved ( value ) {
PDFJS . disableWebGL = ! value ;
} ) ,
Preferences . get ( 'sidebarViewOnLoad' ) . then ( function resolved ( value ) {
self . preferenceSidebarViewOnLoad = value ;
} ) ,
Preferences . get ( 'pdfBugEnabled' ) . then ( function resolved ( value ) {
self . preferencePdfBugEnabled = value ;
} ) ,
Preferences . get ( 'showPreviousViewOnLoad' ) . then ( function resolved ( value ) {
self . preferenceShowPreviousViewOnLoad = value ;
} ) ,
Preferences . get ( 'defaultZoomValue' ) . then ( function resolved ( value ) {
self . preferenceDefaultZoomValue = value ;
} ) ,
Preferences . get ( 'enhanceTextSelection' ) . then ( function resolved ( value ) {
// TODO: Move the initialization and fetching of `Preferences` to occur
// before the various viewer components are initialized.
//
// This was attempted in: https://github.com/mozilla/pdf.js/pull/7586,
// but it had to be backed out since it violated implicit assumptions
// about some viewer components being synchronously available.
//
// NOTE: This hack works since the `enhanceTextSelection` option is not
// needed until `PDFViewer.setDocument` has been called.
self . pdfViewer . enhanceTextSelection = value ;
} ) ,
Preferences . get ( 'disableTextLayer' ) . then ( function resolved ( value ) {
if ( PDFJS . disableTextLayer === true ) {
return ;
}
PDFJS . disableTextLayer = value ;
} ) ,
Preferences . get ( 'disableRange' ) . then ( function resolved ( value ) {
if ( PDFJS . disableRange === true ) {
return ;
}
PDFJS . disableRange = value ;
} ) ,
Preferences . get ( 'disableStream' ) . then ( function resolved ( value ) {
if ( PDFJS . disableStream === true ) {
return ;
}
PDFJS . disableStream = value ;
} ) ,
Preferences . get ( 'disableAutoFetch' ) . then ( function resolved ( value ) {
PDFJS . disableAutoFetch = value ;
} ) ,
Preferences . get ( 'disableFontFace' ) . then ( function resolved ( value ) {
if ( PDFJS . disableFontFace === true ) {
return ;
}
PDFJS . disableFontFace = value ;
} ) ,
Preferences . get ( 'useOnlyCssZoom' ) . then ( function resolved ( value ) {
PDFJS . useOnlyCssZoom = value ;
} ) ,
Preferences . get ( 'externalLinkTarget' ) . then ( function resolved ( value ) {
if ( PDFJS . isExternalLinkTargetSet ( ) ) {
return ;
}
PDFJS . externalLinkTarget = value ;
} ) ,
Preferences . get ( 'renderInteractiveForms' ) . then ( function resolved ( value ) {
// TODO: Like the `enhanceTextSelection` preference, move the
// initialization and fetching of `Preferences` to occur
// before the various viewer components are initialized.
self . pdfViewer . renderInteractiveForms = value ;
} ) ,
Preferences . get ( 'disablePageLabels' ) . then ( function resolved ( value ) {
self . preferenceDisablePageLabels = value ;
} )
] ) . catch ( function ( reason ) {
} ) ;
return initializedPromise . then ( function ( ) {
if ( self . isViewerEmbedded && ! PDFJS . isExternalLinkTargetSet ( ) ) {
// Prevent external links from "replacing" the viewer,
// when it's embedded in e.g. an iframe or an object.
PDFJS . externalLinkTarget = PDFJS . LinkTarget . TOP ;
}
self . initialized = true ;
} ) ;
} ,
run : function pdfViewRun ( config ) {
this . initialize ( config ) . then ( webViewerInitialized ) ;
} ,
zoomIn : function pdfViewZoomIn ( ticks ) {
var newScale = this . pdfViewer . currentScale ;
do {
newScale = ( newScale * DEFAULT _SCALE _DELTA ) . toFixed ( 2 ) ;
newScale = Math . ceil ( newScale * 10 ) / 10 ;
newScale = Math . min ( MAX _SCALE , newScale ) ;
} while ( -- ticks > 0 && newScale < MAX _SCALE ) ;
this . pdfViewer . currentScaleValue = newScale ;
} ,
zoomOut : function pdfViewZoomOut ( ticks ) {
var newScale = this . pdfViewer . currentScale ;
do {
newScale = ( newScale / DEFAULT _SCALE _DELTA ) . toFixed ( 2 ) ;
newScale = Math . floor ( newScale * 10 ) / 10 ;
newScale = Math . max ( MIN _SCALE , newScale ) ;
} while ( -- ticks > 0 && newScale > MIN _SCALE ) ;
this . pdfViewer . currentScaleValue = newScale ;
} ,
get pagesCount ( ) {
return this . pdfDocument ? this . pdfDocument . numPages : 0 ;
} ,
set page ( val ) {
this . pdfViewer . currentPageNumber = val ;
} ,
get page ( ) {
return this . pdfViewer . currentPageNumber ;
} ,
get printing ( ) {
return ! ! this . printService ;
} ,
get supportsPrinting ( ) {
return PDFPrintServiceFactory . instance . supportsPrinting ;
} ,
get supportsFullscreen ( ) {
var support ;
support = document . fullscreenEnabled === true || document . mozFullScreenEnabled === true ;
if ( support && pdfjsLib . PDFJS . disableFullscreen === true ) {
support = false ;
}
return pdfjsLib . shadow ( this , 'supportsFullscreen' , support ) ;
} ,
get supportsIntegratedFind ( ) {
return this . externalServices . supportsIntegratedFind ;
} ,
get supportsDocumentFonts ( ) {
return this . externalServices . supportsDocumentFonts ;
} ,
get supportsDocumentColors ( ) {
return this . externalServices . supportsDocumentColors ;
} ,
get loadingBar ( ) {
var bar = new ProgressBar ( '#loadingBar' , { } ) ;
return pdfjsLib . shadow ( this , 'loadingBar' , bar ) ;
} ,
get supportedMouseWheelZoomModifierKeys ( ) {
return this . externalServices . supportedMouseWheelZoomModifierKeys ;
} ,
initPassiveLoading : function pdfViewInitPassiveLoading ( ) {
this . externalServices . initPassiveLoading ( {
onOpenWithTransport : function ( url , length , transport ) {
PDFViewerApplication . open ( url , { range : transport } ) ;
if ( length ) {
PDFViewerApplication . pdfDocumentProperties . setFileSize ( length ) ;
}
} ,
onOpenWithData : function ( data ) {
PDFViewerApplication . open ( data ) ;
} ,
onOpenWithURL : function ( url , length , originalURL ) {
var file = url , args = null ;
if ( length !== undefined ) {
args = { length : length } ;
}
if ( originalURL !== undefined ) {
file = {
file : url ,
originalURL : originalURL
} ;
}
PDFViewerApplication . open ( file , args ) ;
} ,
onError : function ( e ) {
PDFViewerApplication . error ( mozL10n . get ( 'loading_error' , null , 'An error occurred while loading the PDF.' ) , e ) ;
} ,
onProgress : function ( loaded , total ) {
PDFViewerApplication . progress ( loaded / total ) ;
}
} ) ;
} ,
setTitleUsingUrl : function pdfViewSetTitleUsingUrl ( url ) {
this . url = url ;
this . baseUrl = url . split ( '#' ) [ 0 ] ;
try {
this . setTitle ( decodeURIComponent ( pdfjsLib . getFilenameFromUrl ( url ) ) || url ) ;
} catch ( e ) {
// decodeURIComponent may throw URIError,
// fall back to using the unprocessed url in that case
this . setTitle ( url ) ;
}
} ,
setTitle : function pdfViewSetTitle ( title ) {
if ( this . isViewerEmbedded ) {
// Embedded PDF viewers should not be changing their parent page's title.
return ;
}
document . title = title ;
} ,
/ * *
* Closes opened PDF document .
* @ returns { Promise } - Returns the promise , which is resolved when all
* destruction is completed .
* /
close : function pdfViewClose ( ) {
var errorWrapper = this . appConfig . errorWrapper . container ;
errorWrapper . setAttribute ( 'hidden' , 'true' ) ;
if ( ! this . pdfLoadingTask ) {
return Promise . resolve ( ) ;
}
var promise = this . pdfLoadingTask . destroy ( ) ;
this . pdfLoadingTask = null ;
if ( this . pdfDocument ) {
this . pdfDocument = null ;
this . pdfThumbnailViewer . setDocument ( null ) ;
this . pdfViewer . setDocument ( null ) ;
this . pdfLinkService . setDocument ( null , null ) ;
}
this . store = null ;
this . isInitialViewSet = false ;
this . hasPageLabels = false ;
this . pdfSidebar . reset ( ) ;
this . pdfOutlineViewer . reset ( ) ;
this . pdfAttachmentViewer . reset ( ) ;
this . findController . reset ( ) ;
this . findBar . reset ( ) ;
if ( typeof PDFBug !== 'undefined' ) {
PDFBug . cleanup ( ) ;
}
return promise ;
} ,
/ * *
* Opens PDF document specified by URL or array with additional arguments .
* @ param { string | TypedArray | ArrayBuffer } file - PDF location or binary data .
* @ param { Object } args - ( optional ) Additional arguments for the getDocument
* call , e . g . HTTP headers ( 'httpHeaders' ) or
* alternative data transport ( 'range' ) .
* @ returns { Promise } - Returns the promise , which is resolved when document
* is opened .
* /
open : function pdfViewOpen ( file , args ) {
if ( this . pdfLoadingTask ) {
// We need to destroy already opened document.
return this . close ( ) . then ( function ( ) {
// Reload the preferences if a document was previously opened.
Preferences . reload ( ) ;
// ... and repeat the open() call.
return this . open ( file , args ) ;
} . bind ( this ) ) ;
}
var parameters = Object . create ( null ) , scale ;
if ( typeof file === 'string' ) {
// URL
this . setTitleUsingUrl ( file ) ;
parameters . url = file ;
} else if ( file && 'byteLength' in file ) {
// ArrayBuffer
parameters . data = file ;
} else if ( file . url && file . originalUrl ) {
this . setTitleUsingUrl ( file . originalUrl ) ;
parameters . url = file . url ;
}
parameters . docBaseUrl = this . baseUrl ;
if ( args ) {
for ( var prop in args ) {
parameters [ prop ] = args [ prop ] ;
}
if ( args . scale ) {
scale = args . scale ;
}
if ( args . length ) {
this . pdfDocumentProperties . setFileSize ( args . length ) ;
}
}
var self = this ;
self . downloadComplete = false ;
var loadingTask = pdfjsLib . getDocument ( parameters ) ;
this . pdfLoadingTask = loadingTask ;
loadingTask . onPassword = function passwordNeeded ( updateCallback , reason ) {
self . passwordPrompt . setUpdateCallback ( updateCallback , reason ) ;
self . passwordPrompt . open ( ) ;
} ;
loadingTask . onProgress = function getDocumentProgress ( progressData ) {
self . progress ( progressData . loaded / progressData . total ) ;
} ;
// Listen for unsupported features to trigger the fallback UI.
loadingTask . onUnsupportedFeature = this . fallback . bind ( this ) ;
return loadingTask . promise . then ( function getDocumentCallback ( pdfDocument ) {
self . load ( pdfDocument , scale ) ;
} , function getDocumentError ( exception ) {
var message = exception && exception . message ;
var loadingErrorMessage = mozL10n . get ( 'loading_error' , null , 'An error occurred while loading the PDF.' ) ;
if ( exception instanceof pdfjsLib . InvalidPDFException ) {
// change error message also for other builds
loadingErrorMessage = mozL10n . get ( 'invalid_file_error' , null , 'Invalid or corrupted PDF file.' ) ;
} else if ( exception instanceof pdfjsLib . MissingPDFException ) {
// special message for missing PDF's
loadingErrorMessage = mozL10n . get ( 'missing_file_error' , null , 'Missing PDF file.' ) ;
} else if ( exception instanceof pdfjsLib . UnexpectedResponseException ) {
loadingErrorMessage = mozL10n . get ( 'unexpected_response_error' , null , 'Unexpected server response.' ) ;
}
var moreInfo = { message : message } ;
self . error ( loadingErrorMessage , moreInfo ) ;
throw new Error ( loadingErrorMessage ) ;
} ) ;
} ,
download : function pdfViewDownload ( ) {
function downloadByUrl ( ) {
downloadManager . downloadUrl ( url , filename ) ;
}
var url = this . baseUrl ;
var filename = getPDFFileNameFromURL ( url ) ;
var downloadManager = this . downloadManager ;
downloadManager . onerror = function ( err ) {
// This error won't really be helpful because it's likely the
// fallback won't work either (or is already open).
PDFViewerApplication . error ( 'PDF failed to download.' ) ;
} ;
if ( ! this . pdfDocument ) {
// the PDF is not ready yet
downloadByUrl ( ) ;
return ;
}
if ( ! this . downloadComplete ) {
// the PDF is still downloading
downloadByUrl ( ) ;
return ;
}
this . pdfDocument . getData ( ) . then ( function getDataSuccess ( data ) {
var blob = pdfjsLib . createBlob ( data , 'application/pdf' ) ;
downloadManager . download ( blob , url , filename ) ;
} , downloadByUrl ) . then ( null , downloadByUrl ) ;
} ,
fallback : function pdfViewFallback ( featureId ) {
// Only trigger the fallback once so we don't spam the user with messages
// for one PDF.
if ( this . fellback ) {
return ;
}
this . fellback = true ;
this . externalServices . fallback ( {
featureId : featureId ,
url : this . baseUrl
} , function response ( download ) {
if ( ! download ) {
return ;
}
PDFViewerApplication . download ( ) ;
} ) ;
} ,
/ * *
* Show the error box .
* @ param { String } message A message that is human readable .
* @ param { Object } moreInfo ( optional ) Further information about the error
* that is more technical . Should have a 'message'
* and optionally a 'stack' property .
* /
error : function pdfViewError ( message , moreInfo ) {
var moreInfoText = mozL10n . get ( 'error_version_info' , {
version : pdfjsLib . version || '?' ,
build : pdfjsLib . build || '?'
} , 'PDF.js v{{version}} (build: {{build}})' ) + '\n' ;
if ( moreInfo ) {
moreInfoText += mozL10n . get ( 'error_message' , { message : moreInfo . message } , 'Message: {{message}}' ) ;
if ( moreInfo . stack ) {
moreInfoText += '\n' + mozL10n . get ( 'error_stack' , { stack : moreInfo . stack } , 'Stack: {{stack}}' ) ;
} else {
if ( moreInfo . filename ) {
moreInfoText += '\n' + mozL10n . get ( 'error_file' , { file : moreInfo . filename } , 'File: {{file}}' ) ;
}
if ( moreInfo . lineNumber ) {
moreInfoText += '\n' + mozL10n . get ( 'error_line' , { line : moreInfo . lineNumber } , 'Line: {{line}}' ) ;
}
}
}
console . error ( message + '\n' + moreInfoText ) ;
this . fallback ( ) ;
} ,
progress : function pdfViewProgress ( level ) {
var percent = Math . round ( level * 100 ) ;
// When we transition from full request to range requests, it's possible
// that we discard some of the loaded data. This can cause the loading
// bar to move backwards. So prevent this by only updating the bar if it
// increases.
if ( percent > this . loadingBar . percent || isNaN ( percent ) ) {
this . loadingBar . percent = percent ;
// When disableAutoFetch is enabled, it's not uncommon for the entire file
// to never be fetched (depends on e.g. the file structure). In this case
// the loading bar will not be completely filled, nor will it be hidden.
// To prevent displaying a partially filled loading bar permanently, we
// hide it when no data has been loaded during a certain amount of time.
if ( pdfjsLib . PDFJS . disableAutoFetch && percent ) {
if ( this . disableAutoFetchLoadingBarTimeout ) {
clearTimeout ( this . disableAutoFetchLoadingBarTimeout ) ;
this . disableAutoFetchLoadingBarTimeout = null ;
}
this . loadingBar . show ( ) ;
this . disableAutoFetchLoadingBarTimeout = setTimeout ( function ( ) {
this . loadingBar . hide ( ) ;
this . disableAutoFetchLoadingBarTimeout = null ;
} . bind ( this ) , DISABLE _AUTO _FETCH _LOADING _BAR _TIMEOUT ) ;
}
}
} ,
load : function pdfViewLoad ( pdfDocument , scale ) {
var self = this ;
scale = scale || UNKNOWN _SCALE ;
this . pdfDocument = pdfDocument ;
this . pdfDocumentProperties . setDocumentAndUrl ( pdfDocument , this . url ) ;
var downloadedPromise = pdfDocument . getDownloadInfo ( ) . then ( function ( ) {
self . downloadComplete = true ;
self . loadingBar . hide ( ) ;
} ) ;
this . _updateUIToolbar ( { resetNumPages : true } ) ;
var id = this . documentFingerprint = pdfDocument . fingerprint ;
var store = this . store = new ViewHistory ( id ) ;
var baseDocumentUrl ;
baseDocumentUrl = this . baseUrl ;
this . pdfLinkService . setDocument ( pdfDocument , baseDocumentUrl ) ;
var pdfViewer = this . pdfViewer ;
pdfViewer . currentScale = scale ;
pdfViewer . setDocument ( pdfDocument ) ;
var firstPagePromise = pdfViewer . firstPagePromise ;
var pagesPromise = pdfViewer . pagesPromise ;
var onePageRendered = pdfViewer . onePageRendered ;
this . pageRotation = 0 ;
var pdfThumbnailViewer = this . pdfThumbnailViewer ;
pdfThumbnailViewer . setDocument ( pdfDocument ) ;
firstPagePromise . then ( function ( pdfPage ) {
downloadedPromise . then ( function ( ) {
self . eventBus . dispatch ( 'documentload' , { source : self } ) ;
} ) ;
self . loadingBar . setWidth ( self . appConfig . viewerContainer ) ;
if ( ! pdfjsLib . PDFJS . disableHistory && ! self . isViewerEmbedded ) {
// The browsing history is only enabled when the viewer is standalone,
// i.e. not when it is embedded in a web page.
if ( ! self . preferenceShowPreviousViewOnLoad ) {
self . pdfHistory . clearHistoryState ( ) ;
}
self . pdfHistory . initialize ( self . documentFingerprint ) ;
if ( self . pdfHistory . initialDestination ) {
self . initialDestination = self . pdfHistory . initialDestination ;
} else if ( self . pdfHistory . initialBookmark ) {
self . initialBookmark = self . pdfHistory . initialBookmark ;
}
}
var initialParams = {
destination : self . initialDestination ,
bookmark : self . initialBookmark ,
hash : null
} ;
store . initializedPromise . then ( function resolved ( ) {
var storedHash = null , sidebarView = null ;
if ( self . preferenceShowPreviousViewOnLoad && store . get ( 'exists' , false ) ) {
var pageNum = store . get ( 'page' , '1' ) ;
var zoom = self . preferenceDefaultZoomValue || store . get ( 'zoom' , DEFAULT _SCALE _VALUE ) ;
var left = store . get ( 'scrollLeft' , '0' ) ;
var top = store . get ( 'scrollTop' , '0' ) ;
storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' + left + ',' + top ;
sidebarView = store . get ( 'sidebarView' , SidebarView . NONE ) ;
} else if ( self . preferenceDefaultZoomValue ) {
storedHash = 'page=1&zoom=' + self . preferenceDefaultZoomValue ;
}
self . setInitialView ( storedHash , {
scale : scale ,
sidebarView : sidebarView
} ) ;
initialParams . hash = storedHash ;
// Make all navigation keys work on document load,
// unless the viewer is embedded in a web page.
if ( ! self . isViewerEmbedded ) {
self . pdfViewer . focus ( ) ;
}
} , function rejected ( reason ) {
console . error ( reason ) ;
self . setInitialView ( null , { scale : scale } ) ;
} ) ;
// For documents with different page sizes,
// ensure that the correct location becomes visible on load.
pagesPromise . then ( function resolved ( ) {
if ( ! initialParams . destination && ! initialParams . bookmark && ! initialParams . hash ) {
return ;
}
if ( self . hasEqualPageSizes ) {
return ;
}
self . initialDestination = initialParams . destination ;
self . initialBookmark = initialParams . bookmark ;
self . pdfViewer . currentScaleValue = self . pdfViewer . currentScaleValue ;
self . setInitialView ( initialParams . hash ) ;
} ) ;
} ) ;
pdfDocument . getPageLabels ( ) . then ( function ( labels ) {
if ( ! labels || self . preferenceDisablePageLabels ) {
return ;
}
var i = 0 , numLabels = labels . length ;
if ( numLabels !== self . pagesCount ) {
console . error ( 'The number of Page Labels does not match ' + 'the number of pages in the document.' ) ;
return ;
}
// Ignore page labels that correspond to standard page numbering.
while ( i < numLabels && labels [ i ] === ( i + 1 ) . toString ( ) ) {
i ++ ;
}
if ( i === numLabels ) {
return ;
}
pdfViewer . setPageLabels ( labels ) ;
pdfThumbnailViewer . setPageLabels ( labels ) ;
self . hasPageLabels = true ;
self . _updateUIToolbar ( { resetNumPages : true } ) ;
} ) ;
pagesPromise . then ( function ( ) {
if ( self . supportsPrinting ) {
pdfDocument . getJavaScript ( ) . then ( function ( javaScript ) {
if ( javaScript . length ) {
console . warn ( 'Warning: JavaScript is not supported' ) ;
self . fallback ( pdfjsLib . UNSUPPORTED _FEATURES . javaScript ) ;
}
// Hack to support auto printing.
var regex = /\bprint\s*\(/ ;
for ( var i = 0 , ii = javaScript . length ; i < ii ; i ++ ) {
var js = javaScript [ i ] ;
if ( js && regex . test ( js ) ) {
setTimeout ( function ( ) {
window . print ( ) ;
} ) ;
return ;
}
}
} ) ;
}
} ) ;
Promise . all ( [
onePageRendered ,
this . animationStartedPromise
] ) . then ( function ( ) {
pdfDocument . getOutline ( ) . then ( function ( outline ) {
self . pdfOutlineViewer . render ( { outline : outline } ) ;
} ) ;
pdfDocument . getAttachments ( ) . then ( function ( attachments ) {
self . pdfAttachmentViewer . render ( { attachments : attachments } ) ;
} ) ;
} ) ;
pdfDocument . getMetadata ( ) . then ( function ( data ) {
var info = data . info , metadata = data . metadata ;
self . documentInfo = info ;
self . metadata = metadata ;
// Provides some basic debug information
console . log ( 'PDF ' + pdfDocument . fingerprint + ' [' + info . PDFFormatVersion + ' ' + ( info . Producer || '-' ) . trim ( ) + ' / ' + ( info . Creator || '-' ) . trim ( ) + ']' + ' (PDF.js: ' + ( pdfjsLib . version || '-' ) + ( ! pdfjsLib . PDFJS . disableWebGL ? ' [WebGL]' : '' ) + ')' ) ;
var pdfTitle ;
if ( metadata && metadata . has ( 'dc:title' ) ) {
var title = metadata . get ( 'dc:title' ) ;
// Ghostscript sometimes return 'Untitled', sets the title to 'Untitled'
if ( title !== 'Untitled' ) {
pdfTitle = title ;
}
}
if ( ! pdfTitle && info && info [ 'Title' ] ) {
pdfTitle = info [ 'Title' ] ;
}
if ( pdfTitle ) {
self . setTitle ( pdfTitle + ' - ' + document . title ) ;
}
if ( info . IsAcroFormPresent ) {
console . warn ( 'Warning: AcroForm/XFA is not supported' ) ;
self . fallback ( pdfjsLib . UNSUPPORTED _FEATURES . forms ) ;
}
var versionId = String ( info . PDFFormatVersion ) . slice ( - 1 ) | 0 ;
var generatorId = 0 ;
var KNOWN _GENERATORS = [
'acrobat distiller' ,
'acrobat pdfwriter' ,
'adobe livecycle' ,
'adobe pdf library' ,
'adobe photoshop' ,
'ghostscript' ,
'tcpdf' ,
'cairo' ,
'dvipdfm' ,
'dvips' ,
'pdftex' ,
'pdfkit' ,
'itext' ,
'prince' ,
'quarkxpress' ,
'mac os x' ,
'microsoft' ,
'openoffice' ,
'oracle' ,
'luradocument' ,
'pdf-xchange' ,
'antenna house' ,
'aspose.cells' ,
'fpdf'
] ;
if ( info . Producer ) {
KNOWN _GENERATORS . some ( function ( generator , s , i ) {
if ( generator . indexOf ( s ) < 0 ) {
return false ;
}
generatorId = i + 1 ;
return true ;
} . bind ( null , info . Producer . toLowerCase ( ) ) ) ;
}
var formType = ! info . IsAcroFormPresent ? null : info . IsXFAPresent ? 'xfa' : 'acroform' ;
} ) ;
} ,
setInitialView : function pdfViewSetInitialView ( storedHash , options ) {
var scale = options && options . scale ;
var sidebarView = options && options . sidebarView ;
this . isInitialViewSet = true ;
this . pdfSidebar . setInitialView ( this . preferenceSidebarViewOnLoad || sidebarView | 0 ) ;
if ( this . initialDestination ) {
this . pdfLinkService . navigateTo ( this . initialDestination ) ;
this . initialDestination = null ;
} else if ( this . initialBookmark ) {
this . pdfLinkService . setHash ( this . initialBookmark ) ;
this . pdfHistory . push ( { hash : this . initialBookmark } , true ) ;
this . initialBookmark = null ;
} else if ( storedHash ) {
this . pdfLinkService . setHash ( storedHash ) ;
} else if ( scale ) {
this . pdfViewer . currentScaleValue = scale ;
this . page = 1 ;
}
if ( ! this . pdfViewer . currentScaleValue ) {
// Scale was not initialized: invalid bookmark or scale was not specified.
// Setting the default one.
this . pdfViewer . currentScaleValue = DEFAULT _SCALE _VALUE ;
}
} ,
cleanup : function pdfViewCleanup ( ) {
if ( ! this . pdfDocument ) {
return ;
}
// run cleanup when document is loaded
this . pdfViewer . cleanup ( ) ;
this . pdfThumbnailViewer . cleanup ( ) ;
this . pdfDocument . cleanup ( ) ;
} ,
forceRendering : function pdfViewForceRendering ( ) {
this . pdfRenderingQueue . printing = this . printing ;
this . pdfRenderingQueue . isThumbnailViewEnabled = this . pdfSidebar . isThumbnailViewVisible ;
this . pdfRenderingQueue . renderHighestPriority ( ) ;
} ,
beforePrint : function pdfViewSetupBeforePrint ( ) {
if ( this . printService ) {
// There is no way to suppress beforePrint/afterPrint events,
// but PDFPrintService may generate double events -- this will ignore
// the second event that will be coming from native window.print().
return ;
}
if ( ! this . supportsPrinting ) {
var printMessage = mozL10n . get ( 'printing_not_supported' , null , 'Warning: Printing is not fully supported by this browser.' ) ;
this . error ( printMessage ) ;
return ;
}
// The beforePrint is a sync method and we need to know layout before
// returning from this method. Ensure that we can get sizes of the pages.
if ( ! this . pdfViewer . pageViewsReady ) {
var notReadyMessage = mozL10n . get ( 'printing_not_ready' , null , 'Warning: The PDF is not fully loaded for printing.' ) ;
window . alert ( notReadyMessage ) ;
return ;
}
var pagesOverview = this . pdfViewer . getPagesOverview ( ) ;
var printContainer = this . appConfig . printContainer ;
var printService = PDFPrintServiceFactory . instance . createPrintService ( this . pdfDocument , pagesOverview , printContainer ) ;
this . printService = printService ;
this . forceRendering ( ) ;
printService . layout ( ) ;
} ,
// Whether all pages of the PDF have the same width and height.
get hasEqualPageSizes ( ) {
var firstPage = this . pdfViewer . getPageView ( 0 ) ;
for ( var i = 1 , ii = this . pagesCount ; i < ii ; ++ i ) {
var pageView = this . pdfViewer . getPageView ( i ) ;
if ( pageView . width !== firstPage . width || pageView . height !== firstPage . height ) {
return false ;
}
}
return true ;
} ,
afterPrint : function pdfViewSetupAfterPrint ( ) {
if ( this . printService ) {
this . printService . destroy ( ) ;
this . printService = null ;
}
this . forceRendering ( ) ;
} ,
rotatePages : function pdfViewRotatePages ( delta ) {
var pageNumber = this . page ;
this . pageRotation = ( this . pageRotation + 360 + delta ) % 360 ;
this . pdfViewer . pagesRotation = this . pageRotation ;
this . pdfThumbnailViewer . pagesRotation = this . pageRotation ;
this . forceRendering ( ) ;
this . pdfViewer . currentPageNumber = pageNumber ;
} ,
requestPresentationMode : function pdfViewRequestPresentationMode ( ) {
if ( ! this . pdfPresentationMode ) {
return ;
}
this . pdfPresentationMode . request ( ) ;
} ,
/ * *
* @ typedef UpdateUIToolbarParameters
* @ property { number } pageNumber
* @ property { string } pageLabel
* @ property { string } scaleValue
* @ property { number } scale
* @ property { boolean } resetNumPages
* /
/ * *
* @ param { Object } UpdateUIToolbarParameters
* @ private
* /
_updateUIToolbar : function ( params ) {
function selectScaleOption ( value , scale ) {
var options = toolbarConfig . scaleSelect . options ;
var predefinedValueFound = false ;
for ( var i = 0 , ii = options . length ; i < ii ; i ++ ) {
var option = options [ i ] ;
if ( option . value !== value ) {
option . selected = false ;
continue ;
}
option . selected = true ;
predefinedValueFound = true ;
}
if ( ! predefinedValueFound ) {
var customScale = Math . round ( scale * 10000 ) / 100 ;
toolbarConfig . customScaleOption . textContent = mozL10n . get ( 'page_scale_percent' , { scale : customScale } , '{{scale}}%' ) ;
toolbarConfig . customScaleOption . selected = true ;
}
}
var pageNumber = params . pageNumber || this . pdfViewer . currentPageNumber ;
var scaleValue = ( params . scaleValue || params . scale || this . pdfViewer . currentScaleValue || DEFAULT _SCALE _VALUE ) . toString ( ) ;
var scale = params . scale || this . pdfViewer . currentScale ;
var resetNumPages = params . resetNumPages || false ;
var toolbarConfig = this . appConfig . toolbar ;
var pagesCount = this . pagesCount ;
if ( resetNumPages ) {
if ( this . hasPageLabels ) {
toolbarConfig . pageNumber . type = 'text' ;
} else {
toolbarConfig . pageNumber . type = 'number' ;
toolbarConfig . numPages . textContent = mozL10n . get ( 'of_pages' , { pagesCount : pagesCount } , 'of {{pagesCount}}' ) ;
}
toolbarConfig . pageNumber . max = pagesCount ;
}
if ( this . hasPageLabels ) {
toolbarConfig . pageNumber . value = params . pageLabel || this . pdfViewer . currentPageLabel ;
toolbarConfig . numPages . textContent = mozL10n . get ( 'page_of_pages' , {
pageNumber : pageNumber ,
pagesCount : pagesCount
} , '({{pageNumber}} of {{pagesCount}})' ) ;
} else {
toolbarConfig . pageNumber . value = pageNumber ;
}
toolbarConfig . previous . disabled = pageNumber <= 1 ;
toolbarConfig . next . disabled = pageNumber >= pagesCount ;
toolbarConfig . firstPage . disabled = pageNumber <= 1 ;
toolbarConfig . lastPage . disabled = pageNumber >= pagesCount ;
toolbarConfig . zoomOut . disabled = scale <= MIN _SCALE ;
toolbarConfig . zoomIn . disabled = scale >= MAX _SCALE ;
selectScaleOption ( scaleValue , scale ) ;
} ,
bindEvents : function pdfViewBindEvents ( ) {
var eventBus = this . eventBus ;
eventBus . on ( 'resize' , webViewerResize ) ;
eventBus . on ( 'localized' , webViewerLocalized ) ;
eventBus . on ( 'hashchange' , webViewerHashchange ) ;
eventBus . on ( 'beforeprint' , this . beforePrint . bind ( this ) ) ;
eventBus . on ( 'afterprint' , this . afterPrint . bind ( this ) ) ;
eventBus . on ( 'pagerendered' , webViewerPageRendered ) ;
eventBus . on ( 'textlayerrendered' , webViewerTextLayerRendered ) ;
eventBus . on ( 'updateviewarea' , webViewerUpdateViewarea ) ;
eventBus . on ( 'pagechanging' , webViewerPageChanging ) ;
eventBus . on ( 'scalechanging' , webViewerScaleChanging ) ;
eventBus . on ( 'sidebarviewchanged' , webViewerSidebarViewChanged ) ;
eventBus . on ( 'pagemode' , webViewerPageMode ) ;
eventBus . on ( 'namedaction' , webViewerNamedAction ) ;
eventBus . on ( 'presentationmodechanged' , webViewerPresentationModeChanged ) ;
eventBus . on ( 'presentationmode' , webViewerPresentationMode ) ;
eventBus . on ( 'openfile' , webViewerOpenFile ) ;
eventBus . on ( 'print' , webViewerPrint ) ;
eventBus . on ( 'download' , webViewerDownload ) ;
eventBus . on ( 'firstpage' , webViewerFirstPage ) ;
eventBus . on ( 'lastpage' , webViewerLastPage ) ;
eventBus . on ( 'rotatecw' , webViewerRotateCw ) ;
eventBus . on ( 'rotateccw' , webViewerRotateCcw ) ;
eventBus . on ( 'documentproperties' , webViewerDocumentProperties ) ;
eventBus . on ( 'find' , webViewerFind ) ;
eventBus . on ( 'findfromurlhash' , webViewerFindFromUrlHash ) ;
}
} ;
var validateFileURL ;
function loadAndEnablePDFBug ( enabledTabs ) {
return new Promise ( function ( resolve , reject ) {
var appConfig = PDFViewerApplication . appConfig ;
var script = document . createElement ( 'script' ) ;
script . src = appConfig . debuggerScriptPath ;
script . onload = function ( ) {
PDFBug . enable ( enabledTabs ) ;
PDFBug . init ( pdfjsLib , appConfig . mainContainer ) ;
resolve ( ) ;
} ;
script . onerror = function ( ) {
reject ( new Error ( 'Cannot load debugger at ' + script . src ) ) ;
} ;
( document . getElementsByTagName ( 'head' ) [ 0 ] || document . body ) . appendChild ( script ) ;
} ) ;
}
function webViewerInitialized ( ) {
var file ;
file = window . location . href . split ( '#' ) [ 0 ] ;
var waitForBeforeOpening = [ ] ;
var appConfig = PDFViewerApplication . appConfig ;
appConfig . toolbar . openFile . setAttribute ( 'hidden' , 'true' ) ;
appConfig . secondaryToolbar . openFileButton . setAttribute ( 'hidden' , 'true' ) ;
var PDFJS = pdfjsLib . PDFJS ;
if ( PDFViewerApplication . preferencePdfBugEnabled ) {
// Special debugging flags in the hash section of the URL.
var hash = document . location . hash . substring ( 1 ) ;
var hashParams = parseQueryString ( hash ) ;
if ( 'disableworker' in hashParams ) {
PDFJS . disableWorker = hashParams [ 'disableworker' ] === 'true' ;
}
if ( 'disablerange' in hashParams ) {
PDFJS . disableRange = hashParams [ 'disablerange' ] === 'true' ;
}
if ( 'disablestream' in hashParams ) {
PDFJS . disableStream = hashParams [ 'disablestream' ] === 'true' ;
}
if ( 'disableautofetch' in hashParams ) {
PDFJS . disableAutoFetch = hashParams [ 'disableautofetch' ] === 'true' ;
}
if ( 'disablefontface' in hashParams ) {
PDFJS . disableFontFace = hashParams [ 'disablefontface' ] === 'true' ;
}
if ( 'disablehistory' in hashParams ) {
PDFJS . disableHistory = hashParams [ 'disablehistory' ] === 'true' ;
}
if ( 'webgl' in hashParams ) {
PDFJS . disableWebGL = hashParams [ 'webgl' ] !== 'true' ;
}
if ( 'useonlycsszoom' in hashParams ) {
PDFJS . useOnlyCssZoom = hashParams [ 'useonlycsszoom' ] === 'true' ;
}
if ( 'verbosity' in hashParams ) {
PDFJS . verbosity = hashParams [ 'verbosity' ] | 0 ;
}
if ( 'ignorecurrentpositiononzoom' in hashParams ) {
PDFJS . ignoreCurrentPositionOnZoom = hashParams [ 'ignorecurrentpositiononzoom' ] === 'true' ;
}
if ( 'textlayer' in hashParams ) {
switch ( hashParams [ 'textlayer' ] ) {
case 'off' :
PDFJS . disableTextLayer = true ;
break ;
case 'visible' :
case 'shadow' :
case 'hover' :
var viewer = appConfig . viewerContainer ;
viewer . classList . add ( 'textLayer-' + hashParams [ 'textlayer' ] ) ;
break ;
}
}
if ( 'pdfbug' in hashParams ) {
PDFJS . pdfBug = true ;
var pdfBug = hashParams [ 'pdfbug' ] ;
var enabled = pdfBug . split ( ',' ) ;
waitForBeforeOpening . push ( loadAndEnablePDFBug ( enabled ) ) ;
}
}
if ( ! PDFViewerApplication . supportsDocumentFonts ) {
PDFJS . disableFontFace = true ;
console . warn ( mozL10n . get ( 'web_fonts_disabled' , null , 'Web fonts are disabled: unable to use embedded PDF fonts.' ) ) ;
}
if ( ! PDFViewerApplication . supportsPrinting ) {
appConfig . toolbar . print . classList . add ( 'hidden' ) ;
appConfig . secondaryToolbar . printButton . classList . add ( 'hidden' ) ;
}
if ( ! PDFViewerApplication . supportsFullscreen ) {
appConfig . toolbar . presentationModeButton . classList . add ( 'hidden' ) ;
appConfig . secondaryToolbar . presentationModeButton . classList . add ( 'hidden' ) ;
}
if ( PDFViewerApplication . supportsIntegratedFind ) {
appConfig . toolbar . viewFind . classList . add ( 'hidden' ) ;
}
// Suppress context menus for some controls
appConfig . toolbar . scaleSelect . oncontextmenu = noContextMenuHandler ;
appConfig . sidebar . mainContainer . addEventListener ( 'transitionend' , function ( e ) {
if ( e . target === /* mainContainer */
this ) {
PDFViewerApplication . eventBus . dispatch ( 'resize' ) ;
}
} , true ) ;
appConfig . sidebar . toggleButton . addEventListener ( 'click' , function ( ) {
PDFViewerApplication . pdfSidebar . toggle ( ) ;
} ) ;
appConfig . toolbar . previous . addEventListener ( 'click' , function ( ) {
PDFViewerApplication . page -- ;
} ) ;
appConfig . toolbar . next . addEventListener ( 'click' , function ( ) {
PDFViewerApplication . page ++ ;
} ) ;
appConfig . toolbar . zoomIn . addEventListener ( 'click' , function ( ) {
PDFViewerApplication . zoomIn ( ) ;
} ) ;
appConfig . toolbar . zoomOut . addEventListener ( 'click' , function ( ) {
PDFViewerApplication . zoomOut ( ) ;
} ) ;
appConfig . toolbar . pageNumber . addEventListener ( 'click' , function ( ) {
this . select ( ) ;
} ) ;
appConfig . toolbar . pageNumber . addEventListener ( 'change' , function ( ) {
var pdfViewer = PDFViewerApplication . pdfViewer ;
pdfViewer . currentPageLabel = this . value ;
// Ensure that the page number input displays the correct value, even if the
// value entered by the user was invalid (e.g. a floating point number).
if ( this . value !== pdfViewer . currentPageNumber . toString ( ) && this . value !== pdfViewer . currentPageLabel ) {
PDFViewerApplication . _updateUIToolbar ( { } ) ;
}
} ) ;
appConfig . toolbar . scaleSelect . addEventListener ( 'change' , function ( ) {
if ( this . value === 'custom' ) {
return ;
}
PDFViewerApplication . pdfViewer . currentScaleValue = this . value ;
} ) ;
appConfig . toolbar . presentationModeButton . addEventListener ( 'click' , function ( e ) {
PDFViewerApplication . eventBus . dispatch ( 'presentationmode' ) ;
} ) ;
appConfig . toolbar . openFile . addEventListener ( 'click' , function ( e ) {
PDFViewerApplication . eventBus . dispatch ( 'openfile' ) ;
} ) ;
appConfig . toolbar . print . addEventListener ( 'click' , function ( e ) {
PDFViewerApplication . eventBus . dispatch ( 'print' ) ;
} ) ;
appConfig . toolbar . download . addEventListener ( 'click' , function ( e ) {
PDFViewerApplication . eventBus . dispatch ( 'download' ) ;
} ) ;
Promise . all ( waitForBeforeOpening ) . then ( function ( ) {
webViewerOpenFileViaURL ( file ) ;
} ) . catch ( function ( reason ) {
PDFViewerApplication . error ( mozL10n . get ( 'loading_error' , null , 'An error occurred while opening.' ) , reason ) ;
} ) ;
}
var webViewerOpenFileViaURL ;
webViewerOpenFileViaURL = function webViewerOpenFileViaURL ( file ) {
PDFViewerApplication . setTitleUsingUrl ( file ) ;
PDFViewerApplication . initPassiveLoading ( ) ;
} ;
function webViewerPageRendered ( e ) {
var pageNumber = e . pageNumber ;
var pageIndex = pageNumber - 1 ;
var pageView = PDFViewerApplication . pdfViewer . getPageView ( pageIndex ) ;
// If the page is still visible when it has finished rendering,
// ensure that the page number input loading indicator is hidden.
if ( pageNumber === PDFViewerApplication . page ) {
var pageNumberInput = PDFViewerApplication . appConfig . toolbar . pageNumber ;
pageNumberInput . classList . remove ( PAGE _NUMBER _LOADING _INDICATOR ) ;
}
// Prevent errors in the edge-case where the PDF document is removed *before*
// the 'pagerendered' event handler is invoked.
if ( ! pageView ) {
return ;
}
// Use the rendered page to set the corresponding thumbnail image.
if ( PDFViewerApplication . pdfSidebar . isThumbnailViewVisible ) {
var thumbnailView = PDFViewerApplication . pdfThumbnailViewer . getThumbnail ( pageIndex ) ;
thumbnailView . setImage ( pageView ) ;
}
if ( pdfjsLib . PDFJS . pdfBug && Stats . enabled && pageView . stats ) {
Stats . add ( pageNumber , pageView . stats ) ;
}
if ( pageView . error ) {
PDFViewerApplication . error ( mozL10n . get ( 'rendering_error' , null , 'An error occurred while rendering the page.' ) , pageView . error ) ;
}
}
function webViewerTextLayerRendered ( e ) {
if ( e . numTextDivs > 0 && ! PDFViewerApplication . supportsDocumentColors ) {
console . error ( mozL10n . get ( 'document_colors_not_allowed' , null , 'PDF documents are not allowed to use their own colors: ' + '\'Allow pages to choose their own colors\' ' + 'is deactivated in the browser.' ) ) ;
PDFViewerApplication . fallback ( ) ;
}
}
function webViewerPageMode ( e ) {
if ( ! PDFViewerApplication . initialized ) {
return ;
}
// Handle the 'pagemode' hash parameter, see also `PDFLinkService_setHash`.
var mode = e . mode , view ;
switch ( mode ) {
case 'thumbs' :
view = SidebarView . THUMBS ;
break ;
case 'bookmarks' :
case 'outline' :
view = SidebarView . OUTLINE ;
break ;
case 'attachments' :
view = SidebarView . ATTACHMENTS ;
break ;
case 'none' :
view = SidebarView . NONE ;
break ;
default :
console . error ( 'Invalid "pagemode" hash parameter: ' + mode ) ;
return ;
}
PDFViewerApplication . pdfSidebar . switchView ( view , /* forceOpen = */
true ) ;
}
function webViewerNamedAction ( e ) {
if ( ! PDFViewerApplication . initialized ) {
return ;
}
// Processing couple of named actions that might be useful.
// See also PDFLinkService.executeNamedAction
var action = e . action ;
switch ( action ) {
case 'GoToPage' :
PDFViewerApplication . appConfig . toolbar . pageNumber . select ( ) ;
break ;
case 'Find' :
if ( ! PDFViewerApplication . supportsIntegratedFind ) {
PDFViewerApplication . findBar . toggle ( ) ;
}
break ;
}
}
function webViewerPresentationModeChanged ( e ) {
var active = e . active ;
var switchInProgress = e . switchInProgress ;
PDFViewerApplication . pdfViewer . presentationModeState = switchInProgress ? PresentationModeState . CHANGING : active ? PresentationModeState . FULLSCREEN : PresentationModeState . NORMAL ;
}
function webViewerSidebarViewChanged ( e ) {
if ( ! PDFViewerApplication . initialized ) {
return ;
}
PDFViewerApplication . pdfRenderingQueue . isThumbnailViewEnabled = PDFViewerApplication . pdfSidebar . isThumbnailViewVisible ;
var store = PDFViewerApplication . store ;
if ( ! store || ! PDFViewerApplication . isInitialViewSet ) {
// Only update the storage when the document has been loaded *and* rendered.
return ;
}
store . initializedPromise . then ( function ( ) {
store . set ( 'sidebarView' , e . view ) . catch ( function ( ) {
} ) ;
} ) ;
}
function webViewerUpdateViewarea ( e ) {
if ( ! PDFViewerApplication . initialized ) {
return ;
}
var location = e . location , store = PDFViewerApplication . store ;
if ( store ) {
store . initializedPromise . then ( function ( ) {
store . setMultiple ( {
'exists' : true ,
'page' : location . pageNumber ,
'zoom' : location . scale ,
'scrollLeft' : location . left ,
'scrollTop' : location . top
} ) . catch ( function ( ) {
} ) ;
} ) ;
}
var href = PDFViewerApplication . pdfLinkService . getAnchorUrl ( location . pdfOpenParams ) ;
PDFViewerApplication . appConfig . toolbar . viewBookmark . href = href ;
PDFViewerApplication . appConfig . secondaryToolbar . viewBookmarkButton . href = href ;
// Update the current bookmark in the browsing history.
PDFViewerApplication . pdfHistory . updateCurrentBookmark ( location . pdfOpenParams , location . pageNumber ) ;
// Show/hide the loading indicator in the page number input element.
var pageNumberInput = PDFViewerApplication . appConfig . toolbar . pageNumber ;
var currentPage = PDFViewerApplication . pdfViewer . getPageView ( PDFViewerApplication . page - 1 ) ;
if ( currentPage . renderingState === RenderingStates . FINISHED ) {
pageNumberInput . classList . remove ( PAGE _NUMBER _LOADING _INDICATOR ) ;
} else {
pageNumberInput . classList . add ( PAGE _NUMBER _LOADING _INDICATOR ) ;
}
}
window . addEventListener ( 'resize' , function webViewerResize ( evt ) {
if ( ! PDFViewerApplication . eventBus ) {
return ;
}
PDFViewerApplication . eventBus . dispatch ( 'resize' ) ;
} ) ;
function webViewerResize ( ) {
if ( PDFViewerApplication . initialized ) {
var currentScaleValue = PDFViewerApplication . pdfViewer . currentScaleValue ;
if ( currentScaleValue === 'auto' || currentScaleValue === 'page-fit' || currentScaleValue === 'page-width' ) {
// Note: the scale is constant for 'page-actual'.
PDFViewerApplication . pdfViewer . currentScaleValue = currentScaleValue ;
} else if ( ! currentScaleValue ) {
// Normally this shouldn't happen, but if the scale wasn't initialized
// we set it to the default value in order to prevent any issues.
// (E.g. the document being rendered with the wrong scale on load.)
PDFViewerApplication . pdfViewer . currentScaleValue = DEFAULT _SCALE _VALUE ;
}
PDFViewerApplication . pdfViewer . update ( ) ;
}
}
window . addEventListener ( 'hashchange' , function webViewerHashchange ( evt ) {
var hash = document . location . hash . substring ( 1 ) ;
PDFViewerApplication . eventBus . dispatch ( 'hashchange' , { hash : hash } ) ;
} ) ;
function webViewerHashchange ( e ) {
if ( PDFViewerApplication . pdfHistory . isHashChangeUnlocked ) {
var hash = e . hash ;
if ( ! hash ) {
return ;
}
if ( ! PDFViewerApplication . isInitialViewSet ) {
PDFViewerApplication . initialBookmark = hash ;
} else {
PDFViewerApplication . pdfLinkService . setHash ( hash ) ;
}
}
}
var webViewerFileInputChange ;
window . addEventListener ( 'localized' , function localized ( evt ) {
PDFViewerApplication . eventBus . dispatch ( 'localized' ) ;
} ) ;
function webViewerLocalized ( ) {
document . getElementsByTagName ( 'html' ) [ 0 ] . dir = mozL10n . getDirection ( ) ;
PDFViewerApplication . animationStartedPromise . then ( function ( ) {
// Adjust the width of the zoom box to fit the content.
// 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.
var container = PDFViewerApplication . appConfig . toolbar . scaleSelectContainer ;
if ( container . clientWidth === 0 ) {
container . setAttribute ( 'style' , 'display: inherit;' ) ;
}
if ( container . clientWidth > 0 ) {
var select = PDFViewerApplication . appConfig . toolbar . scaleSelect ;
select . setAttribute ( 'style' , 'min-width: inherit;' ) ;
var 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;' ) ;
}
} ) ;
}
function webViewerPresentationMode ( ) {
PDFViewerApplication . requestPresentationMode ( ) ;
}
function webViewerOpenFile ( ) {
var openFileInputName = PDFViewerApplication . appConfig . openFileInputName ;
document . getElementById ( openFileInputName ) . click ( ) ;
}
function webViewerPrint ( ) {
window . print ( ) ;
}
function webViewerDownload ( ) {
PDFViewerApplication . download ( ) ;
}
function webViewerFirstPage ( ) {
if ( PDFViewerApplication . pdfDocument ) {
PDFViewerApplication . page = 1 ;
}
}
function webViewerLastPage ( ) {
if ( PDFViewerApplication . pdfDocument ) {
PDFViewerApplication . page = PDFViewerApplication . pagesCount ;
}
}
function webViewerRotateCw ( ) {
PDFViewerApplication . rotatePages ( 90 ) ;
}
function webViewerRotateCcw ( ) {
PDFViewerApplication . rotatePages ( - 90 ) ;
}
function webViewerDocumentProperties ( ) {
PDFViewerApplication . pdfDocumentProperties . open ( ) ;
}
function webViewerFind ( e ) {
PDFViewerApplication . findController . executeCommand ( 'find' + e . type , {
query : e . query ,
phraseSearch : e . phraseSearch ,
caseSensitive : e . caseSensitive ,
highlightAll : e . highlightAll ,
findPrevious : e . findPrevious
} ) ;
}
function webViewerFindFromUrlHash ( e ) {
PDFViewerApplication . findController . executeCommand ( 'find' , {
query : e . query ,
phraseSearch : e . phraseSearch ,
caseSensitive : false ,
highlightAll : true ,
findPrevious : false
} ) ;
}
function webViewerScaleChanging ( e ) {
PDFViewerApplication . _updateUIToolbar ( {
scaleValue : e . presetValue ,
scale : e . scale
} ) ;
if ( ! PDFViewerApplication . initialized ) {
return ;
}
PDFViewerApplication . pdfViewer . update ( ) ;
}
function webViewerPageChanging ( e ) {
var page = e . pageNumber ;
PDFViewerApplication . _updateUIToolbar ( {
pageNumber : page ,
pageLabel : e . pageLabel
} ) ;
if ( PDFViewerApplication . pdfSidebar . isThumbnailViewVisible ) {
PDFViewerApplication . pdfThumbnailViewer . scrollThumbnailIntoView ( page ) ;
}
// we need to update stats
if ( pdfjsLib . PDFJS . pdfBug && Stats . enabled ) {
var pageView = PDFViewerApplication . pdfViewer . getPageView ( page - 1 ) ;
if ( pageView . stats ) {
Stats . add ( page , pageView . stats ) ;
}
}
}
var zoomDisabled = false , zoomDisabledTimeout ;
function handleMouseWheel ( evt ) {
var pdfViewer = PDFViewerApplication . pdfViewer ;
if ( ! pdfViewer || pdfViewer . isInPresentationMode ) {
return ;
}
if ( evt . ctrlKey || evt . metaKey ) {
var support = PDFViewerApplication . supportedMouseWheelZoomModifierKeys ;
if ( evt . ctrlKey && ! support . ctrlKey || evt . metaKey && ! support . metaKey ) {
return ;
}
// Only zoom the pages, not the entire viewer.
evt . preventDefault ( ) ;
// NOTE: this check must be placed *after* preventDefault.
if ( zoomDisabled ) {
return ;
}
var previousScale = pdfViewer . currentScale ;
var delta = normalizeWheelEventDelta ( evt ) ;
var MOUSE _WHEEL _DELTA _PER _PAGE _SCALE = 3.0 ;
var ticks = delta * MOUSE _WHEEL _DELTA _PER _PAGE _SCALE ;
if ( ticks < 0 ) {
PDFViewerApplication . zoomOut ( - ticks ) ;
} else {
PDFViewerApplication . zoomIn ( ticks ) ;
}
var currentScale = pdfViewer . currentScale ;
if ( previousScale !== currentScale ) {
// After scaling the page via zoomIn/zoomOut, the position of the upper-
// left corner is restored. When the mouse wheel is used, the position
// under the cursor should be restored instead.
var scaleCorrectionFactor = currentScale / previousScale - 1 ;
var rect = pdfViewer . container . getBoundingClientRect ( ) ;
var dx = evt . clientX - rect . left ;
var dy = evt . clientY - rect . top ;
pdfViewer . container . scrollLeft += dx * scaleCorrectionFactor ;
pdfViewer . container . scrollTop += dy * scaleCorrectionFactor ;
}
} else {
zoomDisabled = true ;
clearTimeout ( zoomDisabledTimeout ) ;
zoomDisabledTimeout = setTimeout ( function ( ) {
zoomDisabled = false ;
} , 1000 ) ;
}
}
window . addEventListener ( 'wheel' , handleMouseWheel ) ;
window . addEventListener ( 'click' , function click ( evt ) {
if ( ! PDFViewerApplication . secondaryToolbar . isOpen ) {
return ;
}
var appConfig = PDFViewerApplication . appConfig ;
if ( PDFViewerApplication . pdfViewer . containsElement ( evt . target ) || appConfig . toolbar . container . contains ( evt . target ) && evt . target !== appConfig . secondaryToolbar . toggleButton ) {
PDFViewerApplication . secondaryToolbar . close ( ) ;
}
} , true ) ;
window . addEventListener ( 'keydown' , function keydown ( evt ) {
if ( OverlayManager . active ) {
return ;
}
var handled = false ;
var cmd = ( evt . ctrlKey ? 1 : 0 ) | ( evt . altKey ? 2 : 0 ) | ( evt . shiftKey ? 4 : 0 ) | ( evt . metaKey ? 8 : 0 ) ;
var pdfViewer = PDFViewerApplication . pdfViewer ;
var isViewerInPresentationMode = pdfViewer && pdfViewer . isInPresentationMode ;
// First, handle the key bindings that are independent whether an input
// control is selected or not.
if ( cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12 ) {
// either CTRL or META key with optional SHIFT.
switch ( evt . keyCode ) {
case 70 :
// f
if ( ! PDFViewerApplication . supportsIntegratedFind ) {
PDFViewerApplication . findBar . open ( ) ;
handled = true ;
}
break ;
case 71 :
// g
if ( ! PDFViewerApplication . supportsIntegratedFind ) {
var findState = PDFViewerApplication . findController . state ;
if ( findState ) {
PDFViewerApplication . findController . executeCommand ( 'findagain' , {
query : findState . query ,
phraseSearch : findState . phraseSearch ,
caseSensitive : findState . caseSensitive ,
highlightAll : findState . highlightAll ,
findPrevious : cmd === 5 || cmd === 12
} ) ;
}
handled = true ;
}
break ;
case 61 :
// FF/Mac '='
case 107 :
// FF '+' and '='
case 187 :
// Chrome '+'
case 171 :
// FF with German keyboard
if ( ! isViewerInPresentationMode ) {
PDFViewerApplication . zoomIn ( ) ;
}
handled = true ;
break ;
case 173 :
// FF/Mac '-'
case 109 :
// FF '-'
case 189 :
// Chrome '-'
if ( ! isViewerInPresentationMode ) {
PDFViewerApplication . zoomOut ( ) ;
}
handled = true ;
break ;
case 48 :
// '0'
case 96 :
// '0' on Numpad of Swedish keyboard
if ( ! isViewerInPresentationMode ) {
// keeping it unhandled (to restore page zoom to 100%)
setTimeout ( function ( ) {
// ... and resetting the scale after browser adjusts its scale
pdfViewer . currentScaleValue = DEFAULT _SCALE _VALUE ;
} ) ;
handled = false ;
}
break ;
}
}
// CTRL+ALT or Option+Command
if ( cmd === 3 || cmd === 10 ) {
switch ( evt . keyCode ) {
case 80 :
// p
PDFViewerApplication . requestPresentationMode ( ) ;
handled = true ;
break ;
case 71 :
// g
// focuses input#pageNumber field
PDFViewerApplication . appConfig . toolbar . pageNumber . select ( ) ;
handled = true ;
break ;
}
}
if ( handled ) {
evt . preventDefault ( ) ;
return ;
}
// Some shortcuts should not get handled if a control/input element
// is selected.
var curElement = document . activeElement || document . querySelector ( ':focus' ) ;
var curElementTagName = curElement && curElement . tagName . toUpperCase ( ) ;
if ( curElementTagName === 'INPUT' || curElementTagName === 'TEXTAREA' || curElementTagName === 'SELECT' ) {
// Make sure that the secondary toolbar is closed when Escape is pressed.
if ( evt . keyCode !== 27 ) {
// 'Esc'
return ;
}
}
var ensureViewerFocused = false ;
if ( cmd === 0 ) {
// no control key pressed at all.
switch ( evt . keyCode ) {
case 38 :
// up arrow
case 33 :
// pg up
case 8 :
// backspace
if ( ! isViewerInPresentationMode && pdfViewer . currentScaleValue !== 'page-fit' ) {
break ;
}
/* in presentation mode */
case 37 :
// left arrow
// horizontal scrolling using arrow keys
if ( pdfViewer . isHorizontalScrollbarEnabled ) {
break ;
}
case 75 :
// 'k'
case 80 :
// 'p'
if ( PDFViewerApplication . page > 1 ) {
PDFViewerApplication . page -- ;
}
handled = true ;
break ;
case 27 :
// esc key
if ( PDFViewerApplication . secondaryToolbar . isOpen ) {
PDFViewerApplication . secondaryToolbar . close ( ) ;
handled = true ;
}
if ( ! PDFViewerApplication . supportsIntegratedFind && PDFViewerApplication . findBar . opened ) {
PDFViewerApplication . findBar . close ( ) ;
handled = true ;
}
break ;
case 40 :
// down arrow
case 34 :
// pg down
case 32 :
// spacebar
if ( ! isViewerInPresentationMode && pdfViewer . currentScaleValue !== 'page-fit' ) {
break ;
}
case 39 :
// right arrow
// horizontal scrolling using arrow keys
if ( pdfViewer . isHorizontalScrollbarEnabled ) {
break ;
}
case 74 :
// 'j'
case 78 :
// 'n'
if ( PDFViewerApplication . page < PDFViewerApplication . pagesCount ) {
PDFViewerApplication . page ++ ;
}
handled = true ;
break ;
case 36 :
// home
if ( isViewerInPresentationMode || PDFViewerApplication . page > 1 ) {
PDFViewerApplication . page = 1 ;
handled = true ;
ensureViewerFocused = true ;
}
break ;
case 35 :
// end
if ( isViewerInPresentationMode || PDFViewerApplication . page < PDFViewerApplication . pagesCount ) {
PDFViewerApplication . page = PDFViewerApplication . pagesCount ;
handled = true ;
ensureViewerFocused = true ;
}
break ;
case 72 :
// 'h'
if ( ! isViewerInPresentationMode ) {
PDFViewerApplication . handTool . toggle ( ) ;
}
break ;
case 82 :
// 'r'
PDFViewerApplication . rotatePages ( 90 ) ;
break ;
}
}
if ( cmd === 4 ) {
// shift-key
switch ( evt . keyCode ) {
case 32 :
// spacebar
if ( ! isViewerInPresentationMode && pdfViewer . currentScaleValue !== 'page-fit' ) {
break ;
}
if ( PDFViewerApplication . page > 1 ) {
PDFViewerApplication . page -- ;
}
handled = true ;
break ;
case 82 :
// 'r'
PDFViewerApplication . rotatePages ( - 90 ) ;
break ;
}
}
if ( ! handled && ! isViewerInPresentationMode ) {
// 33=Page Up 34=Page Down 35=End 36=Home
// 37=Left 38=Up 39=Right 40=Down
// 32=Spacebar
if ( evt . keyCode >= 33 && evt . keyCode <= 40 || evt . keyCode === 32 && curElementTagName !== 'BUTTON' ) {
ensureViewerFocused = true ;
}
}
if ( cmd === 2 ) {
// alt-key
switch ( evt . keyCode ) {
case 37 :
// left arrow
if ( isViewerInPresentationMode ) {
PDFViewerApplication . pdfHistory . back ( ) ;
handled = true ;
}
break ;
case 39 :
// right arrow
if ( isViewerInPresentationMode ) {
PDFViewerApplication . pdfHistory . forward ( ) ;
handled = true ;
}
break ;
}
}
if ( ensureViewerFocused && ! pdfViewer . containsElement ( curElement ) ) {
// The page container is not focused, but a page navigation key has been
// pressed. Change the focus to the viewer container to make sure that
// navigation by keyboard works as expected.
pdfViewer . focus ( ) ;
}
if ( handled ) {
evt . preventDefault ( ) ;
}
} ) ;
window . addEventListener ( 'beforeprint' , function beforePrint ( evt ) {
PDFViewerApplication . eventBus . dispatch ( 'beforeprint' ) ;
} ) ;
window . addEventListener ( 'afterprint' , function afterPrint ( evt ) {
PDFViewerApplication . eventBus . dispatch ( 'afterprint' ) ;
} ) ;
( function animationStartedClosure ( ) {
// The offsetParent is not set until the pdf.js iframe or object is visible.
// Waiting for first animation.
PDFViewerApplication . animationStartedPromise = new Promise ( function ( resolve ) {
window . requestAnimationFrame ( resolve ) ;
} ) ;
} ( ) ) ;
/* Abstract factory for the print service. */
var PDFPrintServiceFactory = {
instance : {
supportsPrinting : false ,
createPrintService : function ( ) {
throw new Error ( 'Not implemented: createPrintService' ) ;
}
}
} ;
exports . PDFViewerApplication = PDFViewerApplication ;
exports . DefaultExernalServices = DefaultExernalServices ;
exports . PDFPrintServiceFactory = PDFPrintServiceFactory ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebFirefoxPrintService = { } , root . pdfjsWebUIUtils , root . pdfjsWebApp , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , uiUtils , app , pdfjsLib ) {
var CSS _UNITS = uiUtils . CSS _UNITS ;
var PDFPrintServiceFactory = app . PDFPrintServiceFactory ;
// Creates a placeholder with div and canvas with right size for the page.
function composePage ( pdfDocument , pageNumber , size , printContainer ) {
var canvas = document . createElement ( 'canvas' ) ;
// The size of the canvas in pixels for printing.
var PRINT _RESOLUTION = 150 ;
var 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' ;
var canvasWrapper = document . createElement ( 'div' ) ;
canvasWrapper . appendChild ( canvas ) ;
printContainer . appendChild ( canvasWrapper ) ;
canvas . mozPrintCallback = function ( obj ) {
// Printing/rendering the page.
var ctx = obj . context ;
ctx . save ( ) ;
ctx . fillStyle = 'rgb(255, 255, 255)' ;
ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
ctx . restore ( ) ;
pdfDocument . getPage ( pageNumber ) . then ( function ( pdfPage ) {
var renderContext = {
canvasContext : ctx ,
transform : [
PRINT _UNITS ,
0 ,
0 ,
PRINT _UNITS ,
0 ,
0
] ,
viewport : pdfPage . getViewport ( 1 ) ,
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 ( ) ;
}
} ) ;
} ;
}
function FirefoxPrintService ( pdfDocument , pagesOverview , printContainer ) {
this . pdfDocument = pdfDocument ;
this . pagesOverview = pagesOverview ;
this . printContainer = printContainer ;
}
FirefoxPrintService . prototype = {
layout : function ( ) {
var pdfDocument = this . pdfDocument ;
var printContainer = this . printContainer ;
var body = document . querySelector ( 'body' ) ;
body . setAttribute ( 'data-pdfjsprinting' , true ) ;
for ( var i = 0 , ii = this . pagesOverview . length ; i < ii ; ++ i ) {
composePage ( pdfDocument , i + 1 , this . pagesOverview [ i ] , printContainer ) ;
}
} ,
destroy : function ( ) {
this . printContainer . textContent = '' ;
}
} ;
PDFPrintServiceFactory . instance = {
get supportsPrinting ( ) {
var canvas = document . createElement ( 'canvas' ) ;
var value = 'mozPrintCallback' in canvas ;
return pdfjsLib . shadow ( this , 'supportsPrinting' , value ) ;
} ,
createPrintService : function ( pdfDocument , pagesOverview , printContainer ) {
return new FirefoxPrintService ( pdfDocument , pagesOverview , printContainer ) ;
}
} ;
exports . FirefoxPrintService = FirefoxPrintService ;
} ) ) ;
( function ( root , factory ) {
factory ( root . pdfjsWebFirefoxCom = { } , root . pdfjsWebPreferences , root . pdfjsWebApp , root . pdfjsWebPDFJS ) ;
} ( this , function ( exports , preferences , app , pdfjsLib ) {
var Preferences = preferences . Preferences ;
var PDFViewerApplication = app . PDFViewerApplication ;
var FirefoxCom = function FirefoxComClosure ( ) {
return {
/ * *
* Creates an event that the extension is listening for and will
* synchronously respond to .
* NOTE : It is reccomended to use request ( ) instead since one day we may not
* be able to synchronously reply .
* @ param { String } action The action to trigger .
* @ param { String } data Optional data to send .
* @ return { * } The response .
* /
requestSync : function ( action , data ) {
var request = document . createTextNode ( '' ) ;
document . documentElement . appendChild ( request ) ;
var sender = document . createEvent ( 'CustomEvent' ) ;
sender . initCustomEvent ( 'pdf.js.message' , true , false , {
action : action ,
data : data ,
sync : true
} ) ;
request . dispatchEvent ( sender ) ;
var response = sender . detail . response ;
document . documentElement . removeChild ( request ) ;
return response ;
} ,
/ * *
* Creates an event that the extension is listening for and will
* asynchronously respond by calling the callback .
* @ param { String } action The action to trigger .
* @ param { String } data Optional data to send .
* @ param { Function } callback Optional response callback that will be called
* with one data argument .
* /
request : function ( action , data , callback ) {
var request = document . createTextNode ( '' ) ;
if ( callback ) {
document . addEventListener ( 'pdf.js.response' , function listener ( event ) {
var node = event . target ;
var response = event . detail . response ;
document . documentElement . removeChild ( node ) ;
document . removeEventListener ( 'pdf.js.response' , listener , false ) ;
return callback ( response ) ;
} , false ) ;
}
document . documentElement . appendChild ( request ) ;
var sender = document . createEvent ( 'CustomEvent' ) ;
sender . initCustomEvent ( 'pdf.js.message' , true , false , {
action : action ,
data : data ,
sync : false ,
responseExpected : ! ! callback
} ) ;
return request . dispatchEvent ( sender ) ;
}
} ;
} ( ) ;
var DownloadManager = function DownloadManagerClosure ( ) {
function DownloadManager ( ) {
}
DownloadManager . prototype = {
downloadUrl : function DownloadManager _downloadUrl ( url , filename ) {
FirefoxCom . request ( 'download' , {
originalUrl : url ,
filename : filename
} ) ;
} ,
downloadData : function DownloadManager _downloadData ( data , filename , contentType ) {
var blobUrl = pdfjsLib . createObjectURL ( data , contentType , false ) ;
FirefoxCom . request ( 'download' , {
blobUrl : blobUrl ,
originalUrl : blobUrl ,
filename : filename ,
isAttachment : true
} ) ;
} ,
download : function DownloadManager _download ( blob , url , filename ) {
var blobUrl = window . URL . createObjectURL ( blob ) ;
FirefoxCom . request ( 'download' , {
blobUrl : blobUrl ,
originalUrl : url ,
filename : filename
} , function response ( err ) {
if ( err && this . onerror ) {
this . onerror ( err ) ;
}
window . URL . revokeObjectURL ( blobUrl ) ;
} . bind ( this ) ) ;
}
} ;
return DownloadManager ;
} ( ) ;
Preferences . _writeToStorage = function ( prefObj ) {
return new Promise ( function ( resolve ) {
FirefoxCom . request ( 'setPreferences' , prefObj , resolve ) ;
} ) ;
} ;
Preferences . _readFromStorage = function ( prefObj ) {
return new Promise ( function ( resolve ) {
FirefoxCom . request ( 'getPreferences' , prefObj , function ( prefStr ) {
var readPrefs = JSON . parse ( prefStr ) ;
resolve ( readPrefs ) ;
} ) ;
} ) ;
} ;
( function listenFindEvents ( ) {
var events = [
'find' ,
'findagain' ,
'findhighlightallchange' ,
'findcasesensitivitychange'
] ;
var handleEvent = function ( evt ) {
if ( ! PDFViewerApplication . initialized ) {
return ;
}
PDFViewerApplication . eventBus . dispatch ( 'find' , {
source : window ,
type : evt . type . substring ( 'find' . length ) ,
query : evt . detail . query ,
phraseSearch : true ,
caseSensitive : ! ! evt . detail . caseSensitive ,
highlightAll : ! ! evt . detail . highlightAll ,
findPrevious : ! ! evt . detail . findPrevious
} ) ;
} . bind ( this ) ;
for ( var i = 0 , len = events . length ; i < len ; i ++ ) {
window . addEventListener ( events [ i ] , handleEvent ) ;
}
} ( ) ) ;
function FirefoxComDataRangeTransport ( length , initialData ) {
pdfjsLib . PDFDataRangeTransport . call ( this , length , initialData ) ;
}
FirefoxComDataRangeTransport . prototype = Object . create ( pdfjsLib . PDFDataRangeTransport . prototype ) ;
FirefoxComDataRangeTransport . prototype . requestDataRange = function FirefoxComDataRangeTransport _requestDataRange ( begin , end ) {
FirefoxCom . request ( 'requestDataRange' , {
begin : begin ,
end : end
} ) ;
} ;
FirefoxComDataRangeTransport . prototype . abort = function FirefoxComDataRangeTransport _abort ( ) {
// Sync call to ensure abort is really started.
FirefoxCom . requestSync ( 'abortLoading' , null ) ;
} ;
PDFViewerApplication . externalServices = {
updateFindControlState : function ( data ) {
FirefoxCom . request ( 'updateFindControlState' , data ) ;
} ,
initPassiveLoading : function ( callbacks ) {
var pdfDataRangeTransport ;
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 ) ;
return ;
}
var args = e . data ;
if ( typeof args !== 'object' || ! ( 'pdfjsLoadAction' in args ) ) {
return ;
}
switch ( args . pdfjsLoadAction ) {
case 'supportsRangedLoading' :
pdfDataRangeTransport = new FirefoxComDataRangeTransport ( args . length , args . data ) ;
callbacks . onOpenWithTransport ( args . pdfUrl , args . length , pdfDataRangeTransport ) ;
break ;
case 'range' :
pdfDataRangeTransport . onDataRange ( args . begin , args . chunk ) ;
break ;
case 'rangeProgress' :
pdfDataRangeTransport . onDataProgress ( args . loaded ) ;
break ;
case 'progressiveRead' :
pdfDataRangeTransport . onDataProgressiveRead ( args . chunk ) ;
break ;
case 'progress' :
callbacks . onProgress ( args . loaded , args . total ) ;
break ;
case 'complete' :
if ( ! args . data ) {
callbacks . onError ( args . errorCode ) ;
break ;
}
callbacks . onOpenWithData ( args . data ) ;
break ;
}
} ) ;
FirefoxCom . requestSync ( 'initPassiveLoading' , null ) ;
} ,
fallback : function ( data , callback ) {
FirefoxCom . request ( 'fallback' , data , callback ) ;
} ,
createDownloadManager : function ( ) {
return new DownloadManager ( ) ;
} ,
get supportsIntegratedFind ( ) {
var support = FirefoxCom . requestSync ( 'supportsIntegratedFind' ) ;
return pdfjsLib . shadow ( this , 'supportsIntegratedFind' , support ) ;
} ,
get supportsDocumentFonts ( ) {
var support = FirefoxCom . requestSync ( 'supportsDocumentFonts' ) ;
return pdfjsLib . shadow ( this , 'supportsDocumentFonts' , support ) ;
} ,
get supportsDocumentColors ( ) {
var support = FirefoxCom . requestSync ( 'supportsDocumentColors' ) ;
return pdfjsLib . shadow ( this , 'supportsDocumentColors' , support ) ;
} ,
get supportedMouseWheelZoomModifierKeys ( ) {
var support = FirefoxCom . requestSync ( 'supportedMouseWheelZoomModifierKeys' ) ;
return pdfjsLib . shadow ( this , 'supportedMouseWheelZoomModifierKeys' , support ) ;
}
} ;
//// l10n.js for Firefox extension expects services to be set.
document . mozL10n . setExternalLocalizerServices ( {
getLocale : function ( ) {
return FirefoxCom . requestSync ( 'getLocale' , null ) ;
} ,
getStrings : function ( key ) {
return FirefoxCom . requestSync ( 'getStrings' , key ) ;
}
} ) ;
exports . DownloadManager = DownloadManager ;
exports . FirefoxCom = FirefoxCom ;
} ) ) ;
} . call ( pdfjsWebLibs ) ) ;
}
{
// FIXME the l10n.js file in the Firefox extension needs global FirefoxCom.
window . FirefoxCom = pdfjsWebLibs . pdfjsWebFirefoxCom . FirefoxCom ;
}
function getViewerConfiguration ( ) {
return {
appContainer : document . body ,
mainContainer : document . getElementById ( 'viewerContainer' ) ,
viewerContainer : document . getElementById ( 'viewer' ) ,
eventBus : null ,
// using global event bus with 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' ) ,
firstPage : document . getElementById ( 'firstPage' ) ,
lastPage : document . getElementById ( 'lastPage' ) ,
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' ) ,
toggleHandToolButton : document . getElementById ( 'toggleHandTool' ) ,
documentPropertiesButton : document . getElementById ( 'documentProperties' )
} ,
fullscreen : {
contextFirstPage : document . getElementById ( 'contextFirstPage' ) ,
contextLastPage : document . getElementById ( 'contextLastPage' ) ,
contextPageRotateCw : document . getElementById ( 'contextPageRotateCw' ) ,
contextPageRotateCcw : document . getElementById ( 'contextPageRotateCcw' )
} ,
sidebar : {
// Divs (and sidebar button)
mainContainer : document . getElementById ( 'mainContainer' ) ,
outerContainer : document . getElementById ( 'outerContainer' ) ,
toggleButton : document . getElementById ( 'sidebarToggle' ) ,
// Buttons
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' )
} ,
findBar : {
bar : document . getElementById ( 'findbar' ) ,
toggleButton : document . getElementById ( 'viewFind' ) ,
findField : document . getElementById ( 'findInput' ) ,
highlightAllCheckbox : document . getElementById ( 'findHighlightAll' ) ,
caseSensitiveCheckbox : document . getElementById ( 'findMatchCase' ) ,
findMsg : document . getElementById ( 'findMsg' ) ,
findResultsCount : document . getElementById ( 'findResultsCount' ) ,
findStatusIcon : document . getElementById ( 'findStatusIcon' ) ,
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' )
} ,
documentProperties : {
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' )
}
} ,
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' )
} ,
printContainer : document . getElementById ( 'printContainer' ) ,
openFileInputName : 'fileInput' ,
debuggerScriptPath : './debugger.js'
} ;
}
function webViewerLoad ( ) {
var config = getViewerConfiguration ( ) ;
window . PDFViewerApplication = pdfjsWebLibs . pdfjsWebApp . PDFViewerApplication ;
pdfjsWebLibs . pdfjsWebApp . PDFViewerApplication . run ( config ) ;
}
2022-08-29 23:08:01 -04:00
document . addEventListener ( 'DOMContentLoaded' , webViewerLoad , true ) ;