viewer.js 284 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904
  1. /* Copyright 2016 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. /*globals require, chrome */
  16. 'use strict';
  17. var DEFAULT_URL = '';
  18. var pdfjsWebLibs = {
  19. pdfjsWebPDFJS: window.pdfjsDistBuildPdf
  20. };
  21. (function () {
  22. (function (root, factory) {
  23. {
  24. factory((root.pdfjsWebGrabToPan = {}));
  25. }
  26. }(this, function (exports) {
  27. /**
  28. * Construct a GrabToPan instance for a given HTML element.
  29. * @param options.element {Element}
  30. * @param options.ignoreTarget {function} optional. See `ignoreTarget(node)`
  31. * @param options.onActiveChanged {function(boolean)} optional. Called
  32. * when grab-to-pan is (de)activated. The first argument is a boolean that
  33. * shows whether grab-to-pan is activated.
  34. */
  35. function GrabToPan(options) {
  36. this.element = options.element;
  37. this.document = options.element.ownerDocument;
  38. if (typeof options.ignoreTarget === 'function') {
  39. this.ignoreTarget = options.ignoreTarget;
  40. }
  41. this.onActiveChanged = options.onActiveChanged;
  42. // Bind the contexts to ensure that `this` always points to
  43. // the GrabToPan instance.
  44. this.activate = this.activate.bind(this);
  45. this.deactivate = this.deactivate.bind(this);
  46. this.toggle = this.toggle.bind(this);
  47. this._onmousedown = this._onmousedown.bind(this);
  48. this._onmousemove = this._onmousemove.bind(this);
  49. this._endPan = this._endPan.bind(this);
  50. // This overlay will be inserted in the document when the mouse moves during
  51. // a grab operation, to ensure that the cursor has the desired appearance.
  52. var overlay = this.overlay = document.createElement('div');
  53. overlay.className = 'grab-to-pan-grabbing';
  54. }
  55. GrabToPan.prototype = {
  56. /**
  57. * Class name of element which can be grabbed
  58. */
  59. CSS_CLASS_GRAB: 'grab-to-pan-grab',
  60. /**
  61. * Bind a mousedown event to the element to enable grab-detection.
  62. */
  63. activate: function GrabToPan_activate() {
  64. if (!this.active) {
  65. this.active = true;
  66. this.element.addEventListener('mousedown', this._onmousedown, true);
  67. this.element.classList.add(this.CSS_CLASS_GRAB);
  68. if (this.onActiveChanged) {
  69. this.onActiveChanged(true);
  70. }
  71. }
  72. },
  73. /**
  74. * Removes all events. Any pending pan session is immediately stopped.
  75. */
  76. deactivate: function GrabToPan_deactivate() {
  77. if (this.active) {
  78. this.active = false;
  79. this.element.removeEventListener('mousedown', this._onmousedown, true);
  80. this._endPan();
  81. this.element.classList.remove(this.CSS_CLASS_GRAB);
  82. if (this.onActiveChanged) {
  83. this.onActiveChanged(false);
  84. }
  85. }
  86. },
  87. toggle: function GrabToPan_toggle() {
  88. if (this.active) {
  89. this.deactivate();
  90. } else {
  91. this.activate();
  92. }
  93. },
  94. /**
  95. * Whether to not pan if the target element is clicked.
  96. * Override this method to change the default behaviour.
  97. *
  98. * @param node {Element} The target of the event
  99. * @return {boolean} Whether to not react to the click event.
  100. */
  101. ignoreTarget: function GrabToPan_ignoreTarget(node) {
  102. // Use matchesSelector to check whether the clicked element
  103. // is (a child of) an input element / link
  104. return node[matchesSelector](
  105. 'a[href], a[href] *, input, textarea, button, button *, select, option'
  106. );
  107. },
  108. /**
  109. * @private
  110. */
  111. _onmousedown: function GrabToPan__onmousedown(event) {
  112. if (event.button !== 0 || this.ignoreTarget(event.target)) {
  113. return;
  114. }
  115. if (event.originalTarget) {
  116. try {
  117. /* jshint expr:true */
  118. event.originalTarget.tagName;
  119. } catch (e) {
  120. // Mozilla-specific: element is a scrollbar (XUL element)
  121. return;
  122. }
  123. }
  124. this.scrollLeftStart = this.element.scrollLeft;
  125. this.scrollTopStart = this.element.scrollTop;
  126. this.clientXStart = event.clientX;
  127. this.clientYStart = event.clientY;
  128. this.document.addEventListener('mousemove', this._onmousemove, true);
  129. this.document.addEventListener('mouseup', this._endPan, true);
  130. // When a scroll event occurs before a mousemove, assume that the user
  131. // dragged a scrollbar (necessary for Opera Presto, Safari and IE)
  132. // (not needed for Chrome/Firefox)
  133. this.element.addEventListener('scroll', this._endPan, true);
  134. event.preventDefault();
  135. event.stopPropagation();
  136. this.document.documentElement.classList.add(this.CSS_CLASS_GRABBING);
  137. var focusedElement = document.activeElement;
  138. if (focusedElement && !focusedElement.contains(event.target)) {
  139. focusedElement.blur();
  140. }
  141. },
  142. /**
  143. * @private
  144. */
  145. _onmousemove: function GrabToPan__onmousemove(event) {
  146. this.element.removeEventListener('scroll', this._endPan, true);
  147. if (isLeftMouseReleased(event)) {
  148. this._endPan();
  149. return;
  150. }
  151. var xDiff = event.clientX - this.clientXStart;
  152. var yDiff = event.clientY - this.clientYStart;
  153. this.element.scrollTop = this.scrollTopStart - yDiff;
  154. this.element.scrollLeft = this.scrollLeftStart - xDiff;
  155. if (!this.overlay.parentNode) {
  156. document.body.appendChild(this.overlay);
  157. }
  158. },
  159. /**
  160. * @private
  161. */
  162. _endPan: function GrabToPan__endPan() {
  163. this.element.removeEventListener('scroll', this._endPan, true);
  164. this.document.removeEventListener('mousemove', this._onmousemove, true);
  165. this.document.removeEventListener('mouseup', this._endPan, true);
  166. if (this.overlay.parentNode) {
  167. this.overlay.parentNode.removeChild(this.overlay);
  168. }
  169. }
  170. };
  171. // Get the correct (vendor-prefixed) name of the matches method.
  172. var matchesSelector;
  173. ['webkitM', 'mozM', 'msM', 'oM', 'm'].some(function(prefix) {
  174. var name = prefix + 'atches';
  175. if (name in document.documentElement) {
  176. matchesSelector = name;
  177. }
  178. name += 'Selector';
  179. if (name in document.documentElement) {
  180. matchesSelector = name;
  181. }
  182. return matchesSelector; // If found, then truthy, and [].some() ends.
  183. });
  184. // Browser sniffing because it's impossible to feature-detect
  185. // whether event.which for onmousemove is reliable
  186. var isNotIEorIsIE10plus = !document.documentMode || document.documentMode > 9;
  187. var chrome = window.chrome;
  188. var isChrome15OrOpera15plus = chrome && (chrome.webstore || chrome.app);
  189. // ^ Chrome 15+ ^ Opera 15+
  190. var isSafari6plus = /Apple/.test(navigator.vendor) &&
  191. /Version\/([6-9]\d*|[1-5]\d+)/.test(navigator.userAgent);
  192. /**
  193. * Whether the left mouse is not pressed.
  194. * @param event {MouseEvent}
  195. * @return {boolean} True if the left mouse button is not pressed.
  196. * False if unsure or if the left mouse button is pressed.
  197. */
  198. function isLeftMouseReleased(event) {
  199. if ('buttons' in event && isNotIEorIsIE10plus) {
  200. // http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-buttons
  201. // Firefox 15+
  202. // Internet Explorer 10+
  203. return !(event.buttons | 1);
  204. }
  205. if (isChrome15OrOpera15plus || isSafari6plus) {
  206. // Chrome 14+
  207. // Opera 15+
  208. // Safari 6.0+
  209. return event.which === 0;
  210. }
  211. }
  212. exports.GrabToPan = GrabToPan;
  213. }));
  214. (function (root, factory) {
  215. {
  216. factory((root.pdfjsWebMozPrintCallbackPolyfill = {}));
  217. }
  218. }(this, function (exports) {
  219. if ('mozPrintCallback' in document.createElement('canvas')) {
  220. return;
  221. }
  222. // Cause positive result on feature-detection:
  223. HTMLCanvasElement.prototype.mozPrintCallback = undefined;
  224. var canvases; // During print task: non-live NodeList of <canvas> elements
  225. var index; // Index of <canvas> element that is being processed
  226. var print = window.print;
  227. window.print = function print() {
  228. if (canvases) {
  229. console.warn('Ignored window.print() because of a pending print job.');
  230. return;
  231. }
  232. try {
  233. dispatchEvent('beforeprint');
  234. } finally {
  235. canvases = document.querySelectorAll('canvas');
  236. index = -1;
  237. next();
  238. }
  239. };
  240. function dispatchEvent(eventType) {
  241. var event = document.createEvent('CustomEvent');
  242. event.initCustomEvent(eventType, false, false, 'custom');
  243. window.dispatchEvent(event);
  244. }
  245. function next() {
  246. if (!canvases) {
  247. return; // Print task cancelled by user (state reset in abort())
  248. }
  249. renderProgress();
  250. if (++index < canvases.length) {
  251. var canvas = canvases[index];
  252. if (typeof canvas.mozPrintCallback === 'function') {
  253. canvas.mozPrintCallback({
  254. context: canvas.getContext('2d'),
  255. abort: abort,
  256. done: next
  257. });
  258. } else {
  259. next();
  260. }
  261. } else {
  262. renderProgress();
  263. print.call(window);
  264. setTimeout(abort, 20); // Tidy-up
  265. }
  266. }
  267. function abort() {
  268. if (canvases) {
  269. canvases = null;
  270. renderProgress();
  271. dispatchEvent('afterprint');
  272. }
  273. }
  274. function renderProgress() {
  275. var progressContainer = document.getElementById('mozPrintCallback-shim');
  276. if (canvases && canvases.length) {
  277. var progress = Math.round(100 * index / canvases.length);
  278. var progressBar = progressContainer.querySelector('progress');
  279. var progressPerc = progressContainer.querySelector('.relative-progress');
  280. progressBar.value = progress;
  281. progressPerc.textContent = progress + '%';
  282. progressContainer.removeAttribute('hidden');
  283. progressContainer.onclick = abort;
  284. } else {
  285. progressContainer.setAttribute('hidden', '');
  286. }
  287. }
  288. var hasAttachEvent = !!document.attachEvent;
  289. window.addEventListener('keydown', function(event) {
  290. // Intercept Cmd/Ctrl + P in all browsers.
  291. // Also intercept Cmd/Ctrl + Shift + P in Chrome and Opera
  292. if (event.keyCode === 80/*P*/ && (event.ctrlKey || event.metaKey) &&
  293. !event.altKey && (!event.shiftKey || window.chrome || window.opera)) {
  294. window.print();
  295. if (hasAttachEvent) {
  296. // Only attachEvent can cancel Ctrl + P dialog in IE <=10
  297. // attachEvent is gone in IE11, so the dialog will re-appear in IE11.
  298. return;
  299. }
  300. event.preventDefault();
  301. if (event.stopImmediatePropagation) {
  302. event.stopImmediatePropagation();
  303. } else {
  304. event.stopPropagation();
  305. }
  306. return;
  307. }
  308. if (event.keyCode === 27 && canvases) { // Esc
  309. abort();
  310. }
  311. }, true);
  312. if (hasAttachEvent) {
  313. document.attachEvent('onkeydown', function(event) {
  314. event = event || window.event;
  315. if (event.keyCode === 80/*P*/ && event.ctrlKey) {
  316. event.keyCode = 0;
  317. return false;
  318. }
  319. });
  320. }
  321. if ('onbeforeprint' in window) {
  322. // Do not propagate before/afterprint events when they are not triggered
  323. // from within this polyfill. (FF/IE).
  324. var stopPropagationIfNeeded = function(event) {
  325. if (event.detail !== 'custom' && event.stopImmediatePropagation) {
  326. event.stopImmediatePropagation();
  327. }
  328. };
  329. window.addEventListener('beforeprint', stopPropagationIfNeeded, false);
  330. window.addEventListener('afterprint', stopPropagationIfNeeded, false);
  331. }
  332. }));
  333. (function (root, factory) {
  334. {
  335. factory((root.pdfjsWebOverlayManager = {}));
  336. }
  337. }(this, function (exports) {
  338. var OverlayManager = {
  339. overlays: {},
  340. active: null,
  341. /**
  342. * @param {string} name The name of the overlay that is registered.
  343. * @param {HTMLDivElement} element The overlay's DOM element.
  344. * @param {function} callerCloseMethod (optional) The method that, if present,
  345. * will call OverlayManager.close from the Object
  346. * registering the overlay. Access to this method is
  347. * necessary in order to run cleanup code when e.g.
  348. * the overlay is force closed. The default is null.
  349. * @param {boolean} canForceClose (optional) Indicates if opening the overlay
  350. * will close an active overlay. The default is false.
  351. * @returns {Promise} A promise that is resolved when the overlay has been
  352. * registered.
  353. */
  354. register: function overlayManagerRegister(name, element,
  355. callerCloseMethod, canForceClose) {
  356. return new Promise(function (resolve) {
  357. var container;
  358. if (!name || !element || !(container = element.parentNode)) {
  359. throw new Error('Not enough parameters.');
  360. } else if (this.overlays[name]) {
  361. throw new Error('The overlay is already registered.');
  362. }
  363. this.overlays[name] = { element: element,
  364. container: container,
  365. callerCloseMethod: (callerCloseMethod || null),
  366. canForceClose: (canForceClose || false) };
  367. resolve();
  368. }.bind(this));
  369. },
  370. /**
  371. * @param {string} name The name of the overlay that is unregistered.
  372. * @returns {Promise} A promise that is resolved when the overlay has been
  373. * unregistered.
  374. */
  375. unregister: function overlayManagerUnregister(name) {
  376. return new Promise(function (resolve) {
  377. if (!this.overlays[name]) {
  378. throw new Error('The overlay does not exist.');
  379. } else if (this.active === name) {
  380. throw new Error('The overlay cannot be removed while it is active.');
  381. }
  382. delete this.overlays[name];
  383. resolve();
  384. }.bind(this));
  385. },
  386. /**
  387. * @param {string} name The name of the overlay that should be opened.
  388. * @returns {Promise} A promise that is resolved when the overlay has been
  389. * opened.
  390. */
  391. open: function overlayManagerOpen(name) {
  392. return new Promise(function (resolve) {
  393. if (!this.overlays[name]) {
  394. throw new Error('The overlay does not exist.');
  395. } else if (this.active) {
  396. if (this.overlays[name].canForceClose) {
  397. this._closeThroughCaller();
  398. } else if (this.active === name) {
  399. throw new Error('The overlay is already active.');
  400. } else {
  401. throw new Error('Another overlay is currently active.');
  402. }
  403. }
  404. this.active = name;
  405. this.overlays[this.active].element.classList.remove('hidden');
  406. this.overlays[this.active].container.classList.remove('hidden');
  407. window.addEventListener('keydown', this._keyDown);
  408. resolve();
  409. }.bind(this));
  410. },
  411. /**
  412. * @param {string} name The name of the overlay that should be closed.
  413. * @returns {Promise} A promise that is resolved when the overlay has been
  414. * closed.
  415. */
  416. close: function overlayManagerClose(name) {
  417. return new Promise(function (resolve) {
  418. if (!this.overlays[name]) {
  419. throw new Error('The overlay does not exist.');
  420. } else if (!this.active) {
  421. throw new Error('The overlay is currently not active.');
  422. } else if (this.active !== name) {
  423. throw new Error('Another overlay is currently active.');
  424. }
  425. this.overlays[this.active].container.classList.add('hidden');
  426. this.overlays[this.active].element.classList.add('hidden');
  427. this.active = null;
  428. window.removeEventListener('keydown', this._keyDown);
  429. resolve();
  430. }.bind(this));
  431. },
  432. /**
  433. * @private
  434. */
  435. _keyDown: function overlayManager_keyDown(evt) {
  436. var self = OverlayManager;
  437. if (self.active && evt.keyCode === 27) { // Esc key.
  438. self._closeThroughCaller();
  439. evt.preventDefault();
  440. }
  441. },
  442. /**
  443. * @private
  444. */
  445. _closeThroughCaller: function overlayManager_closeThroughCaller() {
  446. if (this.overlays[this.active].callerCloseMethod) {
  447. this.overlays[this.active].callerCloseMethod();
  448. }
  449. if (this.active) {
  450. this.close(this.active);
  451. }
  452. }
  453. };
  454. exports.OverlayManager = OverlayManager;
  455. }));
  456. (function (root, factory) {
  457. {
  458. factory((root.pdfjsWebPDFHistory = {}));
  459. }
  460. }(this, function (exports) {
  461. function PDFHistory(options) {
  462. this.linkService = options.linkService;
  463. this.initialized = false;
  464. this.initialDestination = null;
  465. this.initialBookmark = null;
  466. }
  467. PDFHistory.prototype = {
  468. /**
  469. * @param {string} fingerprint
  470. * @param {IPDFLinkService} linkService
  471. */
  472. initialize: function pdfHistoryInitialize(fingerprint) {
  473. this.initialized = true;
  474. this.reInitialized = false;
  475. this.allowHashChange = true;
  476. this.historyUnlocked = true;
  477. this.isViewerInPresentationMode = false;
  478. this.previousHash = window.location.hash.substring(1);
  479. this.currentBookmark = '';
  480. this.currentPage = 0;
  481. this.updatePreviousBookmark = false;
  482. this.previousBookmark = '';
  483. this.previousPage = 0;
  484. this.nextHashParam = '';
  485. this.fingerprint = fingerprint;
  486. this.currentUid = this.uid = 0;
  487. this.current = {};
  488. var state = window.history.state;
  489. if (this._isStateObjectDefined(state)) {
  490. // This corresponds to navigating back to the document
  491. // from another page in the browser history.
  492. if (state.target.dest) {
  493. this.initialDestination = state.target.dest;
  494. } else {
  495. this.initialBookmark = state.target.hash;
  496. }
  497. this.currentUid = state.uid;
  498. this.uid = state.uid + 1;
  499. this.current = state.target;
  500. } else {
  501. // This corresponds to the loading of a new document.
  502. if (state && state.fingerprint &&
  503. this.fingerprint !== state.fingerprint) {
  504. // Reinitialize the browsing history when a new document
  505. // is opened in the web viewer.
  506. this.reInitialized = true;
  507. }
  508. this._pushOrReplaceState({fingerprint: this.fingerprint}, true);
  509. }
  510. var self = this;
  511. window.addEventListener('popstate', function pdfHistoryPopstate(evt) {
  512. if (!self.historyUnlocked) {
  513. return;
  514. }
  515. if (evt.state) {
  516. // Move back/forward in the history.
  517. self._goTo(evt.state);
  518. return;
  519. }
  520. // If the state is not set, then the user tried to navigate to a
  521. // different hash by manually editing the URL and pressing Enter, or by
  522. // clicking on an in-page link (e.g. the "current view" link).
  523. // Save the current view state to the browser history.
  524. // Note: In Firefox, history.null could also be null after an in-page
  525. // navigation to the same URL, and without dispatching the popstate
  526. // event: https://bugzilla.mozilla.org/show_bug.cgi?id=1183881
  527. if (self.uid === 0) {
  528. // Replace the previous state if it was not explicitly set.
  529. var previousParams = (self.previousHash && self.currentBookmark &&
  530. self.previousHash !== self.currentBookmark) ?
  531. {hash: self.currentBookmark, page: self.currentPage} :
  532. {page: 1};
  533. replacePreviousHistoryState(previousParams, function() {
  534. updateHistoryWithCurrentHash();
  535. });
  536. } else {
  537. updateHistoryWithCurrentHash();
  538. }
  539. }, false);
  540. function updateHistoryWithCurrentHash() {
  541. self.previousHash = window.location.hash.slice(1);
  542. self._pushToHistory({hash: self.previousHash}, false, true);
  543. self._updatePreviousBookmark();
  544. }
  545. function replacePreviousHistoryState(params, callback) {
  546. // To modify the previous history entry, the following happens:
  547. // 1. history.back()
  548. // 2. _pushToHistory, which calls history.replaceState( ... )
  549. // 3. history.forward()
  550. // Because a navigation via the history API does not immediately update
  551. // the history state, the popstate event is used for synchronization.
  552. self.historyUnlocked = false;
  553. // Suppress the hashchange event to avoid side effects caused by
  554. // navigating back and forward.
  555. self.allowHashChange = false;
  556. window.addEventListener('popstate', rewriteHistoryAfterBack);
  557. history.back();
  558. function rewriteHistoryAfterBack() {
  559. window.removeEventListener('popstate', rewriteHistoryAfterBack);
  560. window.addEventListener('popstate', rewriteHistoryAfterForward);
  561. self._pushToHistory(params, false, true);
  562. history.forward();
  563. }
  564. function rewriteHistoryAfterForward() {
  565. window.removeEventListener('popstate', rewriteHistoryAfterForward);
  566. self.allowHashChange = true;
  567. self.historyUnlocked = true;
  568. callback();
  569. }
  570. }
  571. function pdfHistoryBeforeUnload() {
  572. var previousParams = self._getPreviousParams(null, true);
  573. if (previousParams) {
  574. var replacePrevious = (!self.current.dest &&
  575. self.current.hash !== self.previousHash);
  576. self._pushToHistory(previousParams, false, replacePrevious);
  577. self._updatePreviousBookmark();
  578. }
  579. // Remove the event listener when navigating away from the document,
  580. // since 'beforeunload' prevents Firefox from caching the document.
  581. window.removeEventListener('beforeunload', pdfHistoryBeforeUnload,
  582. false);
  583. }
  584. window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false);
  585. window.addEventListener('pageshow', function pdfHistoryPageShow(evt) {
  586. // If the entire viewer (including the PDF file) is cached in
  587. // the browser, we need to reattach the 'beforeunload' event listener
  588. // since the 'DOMContentLoaded' event is not fired on 'pageshow'.
  589. window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false);
  590. }, false);
  591. window.addEventListener('presentationmodechanged', function(e) {
  592. self.isViewerInPresentationMode = !!e.detail.active;
  593. });
  594. },
  595. clearHistoryState: function pdfHistory_clearHistoryState() {
  596. this._pushOrReplaceState(null, true);
  597. },
  598. _isStateObjectDefined: function pdfHistory_isStateObjectDefined(state) {
  599. return (state && state.uid >= 0 &&
  600. state.fingerprint && this.fingerprint === state.fingerprint &&
  601. state.target && state.target.hash) ? true : false;
  602. },
  603. _pushOrReplaceState: function pdfHistory_pushOrReplaceState(stateObj,
  604. replace) {
  605. if (replace) {
  606. window.history.replaceState(stateObj, '', document.URL);
  607. } else {
  608. window.history.pushState(stateObj, '', document.URL);
  609. }
  610. },
  611. get isHashChangeUnlocked() {
  612. if (!this.initialized) {
  613. return true;
  614. }
  615. return this.allowHashChange;
  616. },
  617. _updatePreviousBookmark: function pdfHistory_updatePreviousBookmark() {
  618. if (this.updatePreviousBookmark &&
  619. this.currentBookmark && this.currentPage) {
  620. this.previousBookmark = this.currentBookmark;
  621. this.previousPage = this.currentPage;
  622. this.updatePreviousBookmark = false;
  623. }
  624. },
  625. updateCurrentBookmark: function pdfHistoryUpdateCurrentBookmark(bookmark,
  626. pageNum) {
  627. if (this.initialized) {
  628. this.currentBookmark = bookmark.substring(1);
  629. this.currentPage = pageNum | 0;
  630. this._updatePreviousBookmark();
  631. }
  632. },
  633. updateNextHashParam: function pdfHistoryUpdateNextHashParam(param) {
  634. if (this.initialized) {
  635. this.nextHashParam = param;
  636. }
  637. },
  638. push: function pdfHistoryPush(params, isInitialBookmark) {
  639. if (!(this.initialized && this.historyUnlocked)) {
  640. return;
  641. }
  642. if (params.dest && !params.hash) {
  643. params.hash = (this.current.hash && this.current.dest &&
  644. this.current.dest === params.dest) ?
  645. this.current.hash :
  646. this.linkService.getDestinationHash(params.dest).split('#')[1];
  647. }
  648. if (params.page) {
  649. params.page |= 0;
  650. }
  651. if (isInitialBookmark) {
  652. var target = window.history.state.target;
  653. if (!target) {
  654. // Invoked when the user specifies an initial bookmark,
  655. // thus setting initialBookmark, when the document is loaded.
  656. this._pushToHistory(params, false);
  657. this.previousHash = window.location.hash.substring(1);
  658. }
  659. this.updatePreviousBookmark = this.nextHashParam ? false : true;
  660. if (target) {
  661. // If the current document is reloaded,
  662. // avoid creating duplicate entries in the history.
  663. this._updatePreviousBookmark();
  664. }
  665. return;
  666. }
  667. if (this.nextHashParam) {
  668. if (this.nextHashParam === params.hash) {
  669. this.nextHashParam = null;
  670. this.updatePreviousBookmark = true;
  671. return;
  672. } else {
  673. this.nextHashParam = null;
  674. }
  675. }
  676. if (params.hash) {
  677. if (this.current.hash) {
  678. if (this.current.hash !== params.hash) {
  679. this._pushToHistory(params, true);
  680. } else {
  681. if (!this.current.page && params.page) {
  682. this._pushToHistory(params, false, true);
  683. }
  684. this.updatePreviousBookmark = true;
  685. }
  686. } else {
  687. this._pushToHistory(params, true);
  688. }
  689. } else if (this.current.page && params.page &&
  690. this.current.page !== params.page) {
  691. this._pushToHistory(params, true);
  692. }
  693. },
  694. _getPreviousParams: function pdfHistory_getPreviousParams(onlyCheckPage,
  695. beforeUnload) {
  696. if (!(this.currentBookmark && this.currentPage)) {
  697. return null;
  698. } else if (this.updatePreviousBookmark) {
  699. this.updatePreviousBookmark = false;
  700. }
  701. if (this.uid > 0 && !(this.previousBookmark && this.previousPage)) {
  702. // Prevent the history from getting stuck in the current state,
  703. // effectively preventing the user from going back/forward in
  704. // the history.
  705. //
  706. // This happens if the current position in the document didn't change
  707. // when the history was previously updated. The reasons for this are
  708. // either:
  709. // 1. The current zoom value is such that the document does not need to,
  710. // or cannot, be scrolled to display the destination.
  711. // 2. The previous destination is broken, and doesn't actally point to a
  712. // position within the document.
  713. // (This is either due to a bad PDF generator, or the user making a
  714. // mistake when entering a destination in the hash parameters.)
  715. return null;
  716. }
  717. if ((!this.current.dest && !onlyCheckPage) || beforeUnload) {
  718. if (this.previousBookmark === this.currentBookmark) {
  719. return null;
  720. }
  721. } else if (this.current.page || onlyCheckPage) {
  722. if (this.previousPage === this.currentPage) {
  723. return null;
  724. }
  725. } else {
  726. return null;
  727. }
  728. var params = {hash: this.currentBookmark, page: this.currentPage};
  729. if (this.isViewerInPresentationMode) {
  730. params.hash = null;
  731. }
  732. return params;
  733. },
  734. _stateObj: function pdfHistory_stateObj(params) {
  735. return {fingerprint: this.fingerprint, uid: this.uid, target: params};
  736. },
  737. _pushToHistory: function pdfHistory_pushToHistory(params,
  738. addPrevious, overwrite) {
  739. if (!this.initialized) {
  740. return;
  741. }
  742. if (!params.hash && params.page) {
  743. params.hash = ('page=' + params.page);
  744. }
  745. if (addPrevious && !overwrite) {
  746. var previousParams = this._getPreviousParams();
  747. if (previousParams) {
  748. var replacePrevious = (!this.current.dest &&
  749. this.current.hash !== this.previousHash);
  750. this._pushToHistory(previousParams, false, replacePrevious);
  751. }
  752. }
  753. this._pushOrReplaceState(this._stateObj(params),
  754. (overwrite || this.uid === 0));
  755. this.currentUid = this.uid++;
  756. this.current = params;
  757. this.updatePreviousBookmark = true;
  758. },
  759. _goTo: function pdfHistory_goTo(state) {
  760. if (!(this.initialized && this.historyUnlocked &&
  761. this._isStateObjectDefined(state))) {
  762. return;
  763. }
  764. if (!this.reInitialized && state.uid < this.currentUid) {
  765. var previousParams = this._getPreviousParams(true);
  766. if (previousParams) {
  767. this._pushToHistory(this.current, false);
  768. this._pushToHistory(previousParams, false);
  769. this.currentUid = state.uid;
  770. window.history.back();
  771. return;
  772. }
  773. }
  774. this.historyUnlocked = false;
  775. if (state.target.dest) {
  776. this.linkService.navigateTo(state.target.dest);
  777. } else {
  778. this.linkService.setHash(state.target.hash);
  779. }
  780. this.currentUid = state.uid;
  781. if (state.uid > this.uid) {
  782. this.uid = state.uid;
  783. }
  784. this.current = state.target;
  785. this.updatePreviousBookmark = true;
  786. var currentHash = window.location.hash.substring(1);
  787. if (this.previousHash !== currentHash) {
  788. this.allowHashChange = false;
  789. }
  790. this.previousHash = currentHash;
  791. this.historyUnlocked = true;
  792. },
  793. back: function pdfHistoryBack() {
  794. this.go(-1);
  795. },
  796. forward: function pdfHistoryForward() {
  797. this.go(1);
  798. },
  799. go: function pdfHistoryGo(direction) {
  800. if (this.initialized && this.historyUnlocked) {
  801. var state = window.history.state;
  802. if (direction === -1 && state && state.uid > 0) {
  803. window.history.back();
  804. } else if (direction === 1 && state && state.uid < (this.uid - 1)) {
  805. window.history.forward();
  806. }
  807. }
  808. }
  809. };
  810. exports.PDFHistory = PDFHistory;
  811. }));
  812. (function (root, factory) {
  813. {
  814. factory((root.pdfjsWebPDFPresentationMode = {}));
  815. }
  816. }(this, function (exports) {
  817. var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500; // in ms
  818. var DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms
  819. var ACTIVE_SELECTOR = 'pdfPresentationMode';
  820. var CONTROLS_SELECTOR = 'pdfPresentationModeControls';
  821. /**
  822. * @typedef {Object} PDFPresentationModeOptions
  823. * @property {HTMLDivElement} container - The container for the viewer element.
  824. * @property {HTMLDivElement} viewer - (optional) The viewer element.
  825. * @property {PDFViewer} pdfViewer - The document viewer.
  826. * @property {Array} contextMenuItems - (optional) The menuitems that are added
  827. * to the context menu in Presentation Mode.
  828. */
  829. /**
  830. * @class
  831. */
  832. var PDFPresentationMode = (function PDFPresentationModeClosure() {
  833. /**
  834. * @constructs PDFPresentationMode
  835. * @param {PDFPresentationModeOptions} options
  836. */
  837. function PDFPresentationMode(options) {
  838. this.container = options.container;
  839. this.viewer = options.viewer || options.container.firstElementChild;
  840. this.pdfViewer = options.pdfViewer;
  841. var contextMenuItems = options.contextMenuItems || null;
  842. this.active = false;
  843. this.args = null;
  844. this.contextMenuOpen = false;
  845. this.mouseScrollTimeStamp = 0;
  846. this.mouseScrollDelta = 0;
  847. if (contextMenuItems) {
  848. for (var i = 0, ii = contextMenuItems.length; i < ii; i++) {
  849. var item = contextMenuItems[i];
  850. item.element.addEventListener('click', function (handler) {
  851. this.contextMenuOpen = false;
  852. handler();
  853. }.bind(this, item.handler));
  854. }
  855. }
  856. }
  857. PDFPresentationMode.prototype = {
  858. /**
  859. * Request the browser to enter fullscreen mode.
  860. * @returns {boolean} Indicating if the request was successful.
  861. */
  862. request: function PDFPresentationMode_request() {
  863. if (this.switchInProgress || this.active ||
  864. !this.viewer.hasChildNodes()) {
  865. return false;
  866. }
  867. this._addFullscreenChangeListeners();
  868. this._setSwitchInProgress();
  869. this._notifyStateChange();
  870. if (this.container.requestFullscreen) {
  871. this.container.requestFullscreen();
  872. } else if (this.container.mozRequestFullScreen) {
  873. this.container.mozRequestFullScreen();
  874. } else if (this.container.webkitRequestFullscreen) {
  875. this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  876. } else if (this.container.msRequestFullscreen) {
  877. this.container.msRequestFullscreen();
  878. } else {
  879. return false;
  880. }
  881. this.args = {
  882. page: this.pdfViewer.currentPageNumber,
  883. previousScale: this.pdfViewer.currentScaleValue,
  884. };
  885. return true;
  886. },
  887. /**
  888. * Switches page when the user scrolls (using a scroll wheel or a touchpad)
  889. * with large enough motion, to prevent accidental page switches.
  890. * @param {number} delta - The delta value from the mouse event.
  891. */
  892. mouseScroll: function PDFPresentationMode_mouseScroll(delta) {
  893. if (!this.active) {
  894. return;
  895. }
  896. var MOUSE_SCROLL_COOLDOWN_TIME = 50;
  897. var PAGE_SWITCH_THRESHOLD = 120;
  898. var PageSwitchDirection = {
  899. UP: -1,
  900. DOWN: 1
  901. };
  902. var currentTime = (new Date()).getTime();
  903. var storedTime = this.mouseScrollTimeStamp;
  904. // If we've already switched page, avoid accidentally switching again.
  905. if (currentTime > storedTime &&
  906. currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
  907. return;
  908. }
  909. // If the scroll direction changed, reset the accumulated scroll delta.
  910. if ((this.mouseScrollDelta > 0 && delta < 0) ||
  911. (this.mouseScrollDelta < 0 && delta > 0)) {
  912. this._resetMouseScrollState();
  913. }
  914. this.mouseScrollDelta += delta;
  915. if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) {
  916. var pageSwitchDirection = (this.mouseScrollDelta > 0) ?
  917. PageSwitchDirection.UP : PageSwitchDirection.DOWN;
  918. var page = this.pdfViewer.currentPageNumber;
  919. this._resetMouseScrollState();
  920. // If we're at the first/last page, we don't need to do anything.
  921. if ((page === 1 && pageSwitchDirection === PageSwitchDirection.UP) ||
  922. (page === this.pdfViewer.pagesCount &&
  923. pageSwitchDirection === PageSwitchDirection.DOWN)) {
  924. return;
  925. }
  926. this.pdfViewer.currentPageNumber = (page + pageSwitchDirection);
  927. this.mouseScrollTimeStamp = currentTime;
  928. }
  929. },
  930. get isFullscreen() {
  931. return !!(document.fullscreenElement ||
  932. document.mozFullScreen ||
  933. document.webkitIsFullScreen ||
  934. document.msFullscreenElement);
  935. },
  936. /**
  937. * @private
  938. */
  939. _notifyStateChange: function PDFPresentationMode_notifyStateChange() {
  940. var event = document.createEvent('CustomEvent');
  941. event.initCustomEvent('presentationmodechanged', true, true, {
  942. active: this.active,
  943. switchInProgress: !!this.switchInProgress
  944. });
  945. window.dispatchEvent(event);
  946. },
  947. /**
  948. * Used to initialize a timeout when requesting Presentation Mode,
  949. * i.e. when the browser is requested to enter fullscreen mode.
  950. * This timeout is used to prevent the current page from being scrolled
  951. * partially, or completely, out of view when entering Presentation Mode.
  952. * NOTE: This issue seems limited to certain zoom levels (e.g. page-width).
  953. * @private
  954. */
  955. _setSwitchInProgress: function PDFPresentationMode_setSwitchInProgress() {
  956. if (this.switchInProgress) {
  957. clearTimeout(this.switchInProgress);
  958. }
  959. this.switchInProgress = setTimeout(function switchInProgressTimeout() {
  960. this._removeFullscreenChangeListeners();
  961. delete this.switchInProgress;
  962. this._notifyStateChange();
  963. }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS);
  964. },
  965. /**
  966. * @private
  967. */
  968. _resetSwitchInProgress:
  969. function PDFPresentationMode_resetSwitchInProgress() {
  970. if (this.switchInProgress) {
  971. clearTimeout(this.switchInProgress);
  972. delete this.switchInProgress;
  973. }
  974. },
  975. /**
  976. * @private
  977. */
  978. _enter: function PDFPresentationMode_enter() {
  979. this.active = true;
  980. this._resetSwitchInProgress();
  981. this._notifyStateChange();
  982. this.container.classList.add(ACTIVE_SELECTOR);
  983. // Ensure that the correct page is scrolled into view when entering
  984. // Presentation Mode, by waiting until fullscreen mode in enabled.
  985. setTimeout(function enterPresentationModeTimeout() {
  986. this.pdfViewer.currentPageNumber = this.args.page;
  987. this.pdfViewer.currentScaleValue = 'page-fit';
  988. }.bind(this), 0);
  989. this._addWindowListeners();
  990. this._showControls();
  991. this.contextMenuOpen = false;
  992. this.container.setAttribute('contextmenu', 'viewerContextMenu');
  993. // Text selection is disabled in Presentation Mode, thus it's not possible
  994. // for the user to deselect text that is selected (e.g. with "Select all")
  995. // when entering Presentation Mode, hence we remove any active selection.
  996. window.getSelection().removeAllRanges();
  997. },
  998. /**
  999. * @private
  1000. */
  1001. _exit: function PDFPresentationMode_exit() {
  1002. var page = this.pdfViewer.currentPageNumber;
  1003. this.container.classList.remove(ACTIVE_SELECTOR);
  1004. // Ensure that the correct page is scrolled into view when exiting
  1005. // Presentation Mode, by waiting until fullscreen mode is disabled.
  1006. setTimeout(function exitPresentationModeTimeout() {
  1007. this.active = false;
  1008. this._removeFullscreenChangeListeners();
  1009. this._notifyStateChange();
  1010. this.pdfViewer.currentScaleValue = this.args.previousScale;
  1011. this.pdfViewer.currentPageNumber = page;
  1012. this.args = null;
  1013. }.bind(this), 0);
  1014. this._removeWindowListeners();
  1015. this._hideControls();
  1016. this._resetMouseScrollState();
  1017. this.container.removeAttribute('contextmenu');
  1018. this.contextMenuOpen = false;
  1019. },
  1020. /**
  1021. * @private
  1022. */
  1023. _mouseDown: function PDFPresentationMode_mouseDown(evt) {
  1024. if (this.contextMenuOpen) {
  1025. this.contextMenuOpen = false;
  1026. evt.preventDefault();
  1027. return;
  1028. }
  1029. if (evt.button === 0) {
  1030. // Enable clicking of links in presentation mode. Please note:
  1031. // Only links pointing to destinations in the current PDF document work.
  1032. var isInternalLink = (evt.target.href &&
  1033. evt.target.classList.contains('internalLink'));
  1034. if (!isInternalLink) {
  1035. // Unless an internal link was clicked, advance one page.
  1036. evt.preventDefault();
  1037. this.pdfViewer.currentPageNumber += (evt.shiftKey ? -1 : 1);
  1038. }
  1039. }
  1040. },
  1041. /**
  1042. * @private
  1043. */
  1044. _contextMenu: function PDFPresentationMode_contextMenu() {
  1045. this.contextMenuOpen = true;
  1046. },
  1047. /**
  1048. * @private
  1049. */
  1050. _showControls: function PDFPresentationMode_showControls() {
  1051. if (this.controlsTimeout) {
  1052. clearTimeout(this.controlsTimeout);
  1053. } else {
  1054. this.container.classList.add(CONTROLS_SELECTOR);
  1055. }
  1056. this.controlsTimeout = setTimeout(function showControlsTimeout() {
  1057. this.container.classList.remove(CONTROLS_SELECTOR);
  1058. delete this.controlsTimeout;
  1059. }.bind(this), DELAY_BEFORE_HIDING_CONTROLS);
  1060. },
  1061. /**
  1062. * @private
  1063. */
  1064. _hideControls: function PDFPresentationMode_hideControls() {
  1065. if (!this.controlsTimeout) {
  1066. return;
  1067. }
  1068. clearTimeout(this.controlsTimeout);
  1069. this.container.classList.remove(CONTROLS_SELECTOR);
  1070. delete this.controlsTimeout;
  1071. },
  1072. /**
  1073. * Resets the properties used for tracking mouse scrolling events.
  1074. * @private
  1075. */
  1076. _resetMouseScrollState:
  1077. function PDFPresentationMode_resetMouseScrollState() {
  1078. this.mouseScrollTimeStamp = 0;
  1079. this.mouseScrollDelta = 0;
  1080. },
  1081. /**
  1082. * @private
  1083. */
  1084. _addWindowListeners: function PDFPresentationMode_addWindowListeners() {
  1085. this.showControlsBind = this._showControls.bind(this);
  1086. this.mouseDownBind = this._mouseDown.bind(this);
  1087. this.resetMouseScrollStateBind = this._resetMouseScrollState.bind(this);
  1088. this.contextMenuBind = this._contextMenu.bind(this);
  1089. window.addEventListener('mousemove', this.showControlsBind);
  1090. window.addEventListener('mousedown', this.mouseDownBind);
  1091. window.addEventListener('keydown', this.resetMouseScrollStateBind);
  1092. window.addEventListener('contextmenu', this.contextMenuBind);
  1093. },
  1094. /**
  1095. * @private
  1096. */
  1097. _removeWindowListeners:
  1098. function PDFPresentationMode_removeWindowListeners() {
  1099. window.removeEventListener('mousemove', this.showControlsBind);
  1100. window.removeEventListener('mousedown', this.mouseDownBind);
  1101. window.removeEventListener('keydown', this.resetMouseScrollStateBind);
  1102. window.removeEventListener('contextmenu', this.contextMenuBind);
  1103. delete this.showControlsBind;
  1104. delete this.mouseDownBind;
  1105. delete this.resetMouseScrollStateBind;
  1106. delete this.contextMenuBind;
  1107. },
  1108. /**
  1109. * @private
  1110. */
  1111. _fullscreenChange: function PDFPresentationMode_fullscreenChange() {
  1112. if (this.isFullscreen) {
  1113. this._enter();
  1114. } else {
  1115. this._exit();
  1116. }
  1117. },
  1118. /**
  1119. * @private
  1120. */
  1121. _addFullscreenChangeListeners:
  1122. function PDFPresentationMode_addFullscreenChangeListeners() {
  1123. this.fullscreenChangeBind = this._fullscreenChange.bind(this);
  1124. window.addEventListener('fullscreenchange', this.fullscreenChangeBind);
  1125. window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind);
  1126. window.addEventListener('webkitfullscreenchange',
  1127. this.fullscreenChangeBind);
  1128. window.addEventListener('MSFullscreenChange', this.fullscreenChangeBind);
  1129. },
  1130. /**
  1131. * @private
  1132. */
  1133. _removeFullscreenChangeListeners:
  1134. function PDFPresentationMode_removeFullscreenChangeListeners() {
  1135. window.removeEventListener('fullscreenchange', this.fullscreenChangeBind);
  1136. window.removeEventListener('mozfullscreenchange',
  1137. this.fullscreenChangeBind);
  1138. window.removeEventListener('webkitfullscreenchange',
  1139. this.fullscreenChangeBind);
  1140. window.removeEventListener('MSFullscreenChange',
  1141. this.fullscreenChangeBind);
  1142. delete this.fullscreenChangeBind;
  1143. }
  1144. };
  1145. return PDFPresentationMode;
  1146. })();
  1147. exports.PDFPresentationMode = PDFPresentationMode;
  1148. }));
  1149. (function (root, factory) {
  1150. {
  1151. factory((root.pdfjsWebPDFRenderingQueue = {}));
  1152. }
  1153. }(this, function (exports) {
  1154. var CLEANUP_TIMEOUT = 30000;
  1155. var RenderingStates = {
  1156. INITIAL: 0,
  1157. RUNNING: 1,
  1158. PAUSED: 2,
  1159. FINISHED: 3
  1160. };
  1161. /**
  1162. * Controls rendering of the views for pages and thumbnails.
  1163. * @class
  1164. */
  1165. var PDFRenderingQueue = (function PDFRenderingQueueClosure() {
  1166. /**
  1167. * @constructs
  1168. */
  1169. function PDFRenderingQueue() {
  1170. this.pdfViewer = null;
  1171. this.pdfThumbnailViewer = null;
  1172. this.onIdle = null;
  1173. this.highestPriorityPage = null;
  1174. this.idleTimeout = null;
  1175. this.printing = false;
  1176. this.isThumbnailViewEnabled = false;
  1177. }
  1178. PDFRenderingQueue.prototype = /** @lends PDFRenderingQueue.prototype */ {
  1179. /**
  1180. * @param {PDFViewer} pdfViewer
  1181. */
  1182. setViewer: function PDFRenderingQueue_setViewer(pdfViewer) {
  1183. this.pdfViewer = pdfViewer;
  1184. },
  1185. /**
  1186. * @param {PDFThumbnailViewer} pdfThumbnailViewer
  1187. */
  1188. setThumbnailViewer:
  1189. function PDFRenderingQueue_setThumbnailViewer(pdfThumbnailViewer) {
  1190. this.pdfThumbnailViewer = pdfThumbnailViewer;
  1191. },
  1192. /**
  1193. * @param {IRenderableView} view
  1194. * @returns {boolean}
  1195. */
  1196. isHighestPriority: function PDFRenderingQueue_isHighestPriority(view) {
  1197. return this.highestPriorityPage === view.renderingId;
  1198. },
  1199. renderHighestPriority: function
  1200. PDFRenderingQueue_renderHighestPriority(currentlyVisiblePages) {
  1201. if (this.idleTimeout) {
  1202. clearTimeout(this.idleTimeout);
  1203. this.idleTimeout = null;
  1204. }
  1205. // Pages have a higher priority than thumbnails, so check them first.
  1206. if (this.pdfViewer.forceRendering(currentlyVisiblePages)) {
  1207. return;
  1208. }
  1209. // No pages needed rendering so check thumbnails.
  1210. if (this.pdfThumbnailViewer && this.isThumbnailViewEnabled) {
  1211. if (this.pdfThumbnailViewer.forceRendering()) {
  1212. return;
  1213. }
  1214. }
  1215. if (this.printing) {
  1216. // If printing is currently ongoing do not reschedule cleanup.
  1217. return;
  1218. }
  1219. if (this.onIdle) {
  1220. this.idleTimeout = setTimeout(this.onIdle.bind(this), CLEANUP_TIMEOUT);
  1221. }
  1222. },
  1223. getHighestPriority: function
  1224. PDFRenderingQueue_getHighestPriority(visible, views, scrolledDown) {
  1225. // The state has changed figure out which page has the highest priority to
  1226. // render next (if any).
  1227. // Priority:
  1228. // 1 visible pages
  1229. // 2 if last scrolled down page after the visible pages
  1230. // 2 if last scrolled up page before the visible pages
  1231. var visibleViews = visible.views;
  1232. var numVisible = visibleViews.length;
  1233. if (numVisible === 0) {
  1234. return false;
  1235. }
  1236. for (var i = 0; i < numVisible; ++i) {
  1237. var view = visibleViews[i].view;
  1238. if (!this.isViewFinished(view)) {
  1239. return view;
  1240. }
  1241. }
  1242. // All the visible views have rendered, try to render next/previous pages.
  1243. if (scrolledDown) {
  1244. var nextPageIndex = visible.last.id;
  1245. // ID's start at 1 so no need to add 1.
  1246. if (views[nextPageIndex] &&
  1247. !this.isViewFinished(views[nextPageIndex])) {
  1248. return views[nextPageIndex];
  1249. }
  1250. } else {
  1251. var previousPageIndex = visible.first.id - 2;
  1252. if (views[previousPageIndex] &&
  1253. !this.isViewFinished(views[previousPageIndex])) {
  1254. return views[previousPageIndex];
  1255. }
  1256. }
  1257. // Everything that needs to be rendered has been.
  1258. return null;
  1259. },
  1260. /**
  1261. * @param {IRenderableView} view
  1262. * @returns {boolean}
  1263. */
  1264. isViewFinished: function PDFRenderingQueue_isViewFinished(view) {
  1265. return view.renderingState === RenderingStates.FINISHED;
  1266. },
  1267. /**
  1268. * Render a page or thumbnail view. This calls the appropriate function
  1269. * based on the views state. If the view is already rendered it will return
  1270. * false.
  1271. * @param {IRenderableView} view
  1272. */
  1273. renderView: function PDFRenderingQueue_renderView(view) {
  1274. var state = view.renderingState;
  1275. switch (state) {
  1276. case RenderingStates.FINISHED:
  1277. return false;
  1278. case RenderingStates.PAUSED:
  1279. this.highestPriorityPage = view.renderingId;
  1280. view.resume();
  1281. break;
  1282. case RenderingStates.RUNNING:
  1283. this.highestPriorityPage = view.renderingId;
  1284. break;
  1285. case RenderingStates.INITIAL:
  1286. this.highestPriorityPage = view.renderingId;
  1287. var continueRendering = function () {
  1288. this.renderHighestPriority();
  1289. }.bind(this);
  1290. view.draw().then(continueRendering, continueRendering);
  1291. break;
  1292. }
  1293. return true;
  1294. },
  1295. };
  1296. return PDFRenderingQueue;
  1297. })();
  1298. exports.RenderingStates = RenderingStates;
  1299. exports.PDFRenderingQueue = PDFRenderingQueue;
  1300. }));
  1301. (function (root, factory) {
  1302. {
  1303. factory((root.pdfjsWebPreferences = {}));
  1304. }
  1305. }(this, function (exports) {
  1306. var DEFAULT_PREFERENCES = {
  1307. showPreviousViewOnLoad: true,
  1308. defaultZoomValue: '',
  1309. sidebarViewOnLoad: 0,
  1310. enableHandToolOnLoad: false,
  1311. enableWebGL: false,
  1312. pdfBugEnabled: false,
  1313. disableRange: false,
  1314. disableStream: false,
  1315. disableAutoFetch: false,
  1316. disableFontFace: false,
  1317. disableTextLayer: false,
  1318. useOnlyCssZoom: false,
  1319. externalLinkTarget: 0,
  1320. };
  1321. /**
  1322. * Preferences - Utility for storing persistent settings.
  1323. * Used for settings that should be applied to all opened documents,
  1324. * or every time the viewer is loaded.
  1325. */
  1326. var Preferences = {
  1327. prefs: Object.create(DEFAULT_PREFERENCES),
  1328. isInitializedPromiseResolved: false,
  1329. initializedPromise: null,
  1330. /**
  1331. * Initialize and fetch the current preference values from storage.
  1332. * @return {Promise} A promise that is resolved when the preferences
  1333. * have been initialized.
  1334. */
  1335. initialize: function preferencesInitialize() {
  1336. return this.initializedPromise =
  1337. this._readFromStorage(DEFAULT_PREFERENCES).then(function(prefObj) {
  1338. this.isInitializedPromiseResolved = true;
  1339. if (prefObj) {
  1340. this.prefs = prefObj;
  1341. }
  1342. }.bind(this));
  1343. },
  1344. /**
  1345. * Stub function for writing preferences to storage.
  1346. * NOTE: This should be overridden by a build-specific function defined below.
  1347. * @param {Object} prefObj The preferences that should be written to storage.
  1348. * @return {Promise} A promise that is resolved when the preference values
  1349. * have been written.
  1350. */
  1351. _writeToStorage: function preferences_writeToStorage(prefObj) {
  1352. return Promise.resolve();
  1353. },
  1354. /**
  1355. * Stub function for reading preferences from storage.
  1356. * NOTE: This should be overridden by a build-specific function defined below.
  1357. * @param {Object} prefObj The preferences that should be read from storage.
  1358. * @return {Promise} A promise that is resolved with an {Object} containing
  1359. * the preferences that have been read.
  1360. */
  1361. _readFromStorage: function preferences_readFromStorage(prefObj) {
  1362. return Promise.resolve();
  1363. },
  1364. /**
  1365. * Reset the preferences to their default values and update storage.
  1366. * @return {Promise} A promise that is resolved when the preference values
  1367. * have been reset.
  1368. */
  1369. reset: function preferencesReset() {
  1370. return this.initializedPromise.then(function() {
  1371. this.prefs = Object.create(DEFAULT_PREFERENCES);
  1372. return this._writeToStorage(DEFAULT_PREFERENCES);
  1373. }.bind(this));
  1374. },
  1375. /**
  1376. * Replace the current preference values with the ones from storage.
  1377. * @return {Promise} A promise that is resolved when the preference values
  1378. * have been updated.
  1379. */
  1380. reload: function preferencesReload() {
  1381. return this.initializedPromise.then(function () {
  1382. this._readFromStorage(DEFAULT_PREFERENCES).then(function(prefObj) {
  1383. if (prefObj) {
  1384. this.prefs = prefObj;
  1385. }
  1386. }.bind(this));
  1387. }.bind(this));
  1388. },
  1389. /**
  1390. * Set the value of a preference.
  1391. * @param {string} name The name of the preference that should be changed.
  1392. * @param {boolean|number|string} value The new value of the preference.
  1393. * @return {Promise} A promise that is resolved when the value has been set,
  1394. * provided that the preference exists and the types match.
  1395. */
  1396. set: function preferencesSet(name, value) {
  1397. return this.initializedPromise.then(function () {
  1398. if (DEFAULT_PREFERENCES[name] === undefined) {
  1399. throw new Error('preferencesSet: \'' + name + '\' is undefined.');
  1400. } else if (value === undefined) {
  1401. throw new Error('preferencesSet: no value is specified.');
  1402. }
  1403. var valueType = typeof value;
  1404. var defaultType = typeof DEFAULT_PREFERENCES[name];
  1405. if (valueType !== defaultType) {
  1406. if (valueType === 'number' && defaultType === 'string') {
  1407. value = value.toString();
  1408. } else {
  1409. throw new Error('Preferences_set: \'' + value + '\' is a \"' +
  1410. valueType + '\", expected \"' + defaultType + '\".');
  1411. }
  1412. } else {
  1413. if (valueType === 'number' && (value | 0) !== value) {
  1414. throw new Error('Preferences_set: \'' + value +
  1415. '\' must be an \"integer\".');
  1416. }
  1417. }
  1418. this.prefs[name] = value;
  1419. return this._writeToStorage(this.prefs);
  1420. }.bind(this));
  1421. },
  1422. /**
  1423. * Get the value of a preference.
  1424. * @param {string} name The name of the preference whose value is requested.
  1425. * @return {Promise} A promise that is resolved with a {boolean|number|string}
  1426. * containing the value of the preference.
  1427. */
  1428. get: function preferencesGet(name) {
  1429. return this.initializedPromise.then(function () {
  1430. var defaultValue = DEFAULT_PREFERENCES[name];
  1431. if (defaultValue === undefined) {
  1432. throw new Error('preferencesGet: \'' + name + '\' is undefined.');
  1433. } else {
  1434. var prefValue = this.prefs[name];
  1435. if (prefValue !== undefined) {
  1436. return prefValue;
  1437. }
  1438. }
  1439. return defaultValue;
  1440. }.bind(this));
  1441. }
  1442. };
  1443. Preferences._writeToStorage = function (prefObj) {
  1444. return new Promise(function (resolve) {
  1445. localStorage.setItem('pdfjs.preferences', JSON.stringify(prefObj));
  1446. resolve();
  1447. });
  1448. };
  1449. Preferences._readFromStorage = function (prefObj) {
  1450. return new Promise(function (resolve) {
  1451. var readPrefs = JSON.parse(localStorage.getItem('pdfjs.preferences'));
  1452. resolve(readPrefs);
  1453. });
  1454. };
  1455. exports.Preferences = Preferences;
  1456. }));
  1457. (function (root, factory) {
  1458. {
  1459. factory((root.pdfjsWebViewHistory = {}));
  1460. }
  1461. }(this, function (exports) {
  1462. var DEFAULT_VIEW_HISTORY_CACHE_SIZE = 20;
  1463. /**
  1464. * View History - This is a utility for saving various view parameters for
  1465. * recently opened files.
  1466. *
  1467. * The way that the view parameters are stored depends on how PDF.js is built,
  1468. * for 'gulp <flag>' the following cases exist:
  1469. * - FIREFOX or MOZCENTRAL - uses sessionStorage.
  1470. * - GENERIC or CHROME - uses localStorage, if it is available.
  1471. */
  1472. var ViewHistory = (function ViewHistoryClosure() {
  1473. function ViewHistory(fingerprint, cacheSize) {
  1474. this.fingerprint = fingerprint;
  1475. this.cacheSize = cacheSize || DEFAULT_VIEW_HISTORY_CACHE_SIZE;
  1476. this.isInitializedPromiseResolved = false;
  1477. this.initializedPromise =
  1478. this._readFromStorage().then(function (databaseStr) {
  1479. this.isInitializedPromiseResolved = true;
  1480. var database = JSON.parse(databaseStr || '{}');
  1481. if (!('files' in database)) {
  1482. database.files = [];
  1483. }
  1484. if (database.files.length >= this.cacheSize) {
  1485. database.files.shift();
  1486. }
  1487. var index;
  1488. for (var i = 0, length = database.files.length; i < length; i++) {
  1489. var branch = database.files[i];
  1490. if (branch.fingerprint === this.fingerprint) {
  1491. index = i;
  1492. break;
  1493. }
  1494. }
  1495. if (typeof index !== 'number') {
  1496. index = database.files.push({fingerprint: this.fingerprint}) - 1;
  1497. }
  1498. this.file = database.files[index];
  1499. this.database = database;
  1500. }.bind(this));
  1501. }
  1502. ViewHistory.prototype = {
  1503. _writeToStorage: function ViewHistory_writeToStorage() {
  1504. return new Promise(function (resolve) {
  1505. var databaseStr = JSON.stringify(this.database);
  1506. localStorage.setItem('database', databaseStr);
  1507. resolve();
  1508. }.bind(this));
  1509. },
  1510. _readFromStorage: function ViewHistory_readFromStorage() {
  1511. return new Promise(function (resolve) {
  1512. resolve(localStorage.getItem('database'));
  1513. });
  1514. },
  1515. set: function ViewHistory_set(name, val) {
  1516. if (!this.isInitializedPromiseResolved) {
  1517. return;
  1518. }
  1519. this.file[name] = val;
  1520. return this._writeToStorage();
  1521. },
  1522. setMultiple: function ViewHistory_setMultiple(properties) {
  1523. if (!this.isInitializedPromiseResolved) {
  1524. return;
  1525. }
  1526. for (var name in properties) {
  1527. this.file[name] = properties[name];
  1528. }
  1529. return this._writeToStorage();
  1530. },
  1531. get: function ViewHistory_get(name, defaultValue) {
  1532. if (!this.isInitializedPromiseResolved) {
  1533. return defaultValue;
  1534. }
  1535. return this.file[name] || defaultValue;
  1536. }
  1537. };
  1538. return ViewHistory;
  1539. })();
  1540. exports.ViewHistory = ViewHistory;
  1541. }));
  1542. (function (root, factory) {
  1543. {
  1544. factory((root.pdfjsWebDownloadManager = {}), root.pdfjsWebPDFJS);
  1545. }
  1546. }(this, function (exports, pdfjsLib) {
  1547. function download(blobUrl, filename) {
  1548. var a = document.createElement('a');
  1549. if (a.click) {
  1550. // Use a.click() if available. Otherwise, Chrome might show
  1551. // "Unsafe JavaScript attempt to initiate a navigation change
  1552. // for frame with URL" and not open the PDF at all.
  1553. // Supported by (not mentioned = untested):
  1554. // - Firefox 6 - 19 (4- does not support a.click, 5 ignores a.click)
  1555. // - Chrome 19 - 26 (18- does not support a.click)
  1556. // - Opera 9 - 12.15
  1557. // - Internet Explorer 6 - 10
  1558. // - Safari 6 (5.1- does not support a.click)
  1559. a.href = blobUrl;
  1560. a.target = '_parent';
  1561. // Use a.download if available. This increases the likelihood that
  1562. // the file is downloaded instead of opened by another PDF plugin.
  1563. if ('download' in a) {
  1564. a.download = filename;
  1565. }
  1566. // <a> must be in the document for IE and recent Firefox versions.
  1567. // (otherwise .click() is ignored)
  1568. (document.body || document.documentElement).appendChild(a);
  1569. a.click();
  1570. a.parentNode.removeChild(a);
  1571. } else {
  1572. if (window.top === window &&
  1573. blobUrl.split('#')[0] === window.location.href.split('#')[0]) {
  1574. // If _parent == self, then opening an identical URL with different
  1575. // location hash will only cause a navigation, not a download.
  1576. var padCharacter = blobUrl.indexOf('?') === -1 ? '?' : '&';
  1577. blobUrl = blobUrl.replace(/#|$/, padCharacter + '$&');
  1578. }
  1579. window.open(blobUrl, '_parent');
  1580. }
  1581. }
  1582. function DownloadManager() {}
  1583. DownloadManager.prototype = {
  1584. downloadUrl: function DownloadManager_downloadUrl(url, filename) {
  1585. if (!pdfjsLib.isValidUrl(url, true)) {
  1586. return; // restricted/invalid URL
  1587. }
  1588. download(url + '#pdfjs.action=download', filename);
  1589. },
  1590. downloadData: function DownloadManager_downloadData(data, filename,
  1591. contentType) {
  1592. if (navigator.msSaveBlob) { // IE10 and above
  1593. return navigator.msSaveBlob(new Blob([data], { type: contentType }),
  1594. filename);
  1595. }
  1596. var blobUrl = pdfjsLib.createObjectURL(data, contentType,
  1597. pdfjsLib.PDFJS.disableCreateObjectURL);
  1598. download(blobUrl, filename);
  1599. },
  1600. download: function DownloadManager_download(blob, url, filename) {
  1601. if (!URL) {
  1602. // URL.createObjectURL is not supported
  1603. this.downloadUrl(url, filename);
  1604. return;
  1605. }
  1606. if (navigator.msSaveBlob) {
  1607. // IE10 / IE11
  1608. if (!navigator.msSaveBlob(blob, filename)) {
  1609. this.downloadUrl(url, filename);
  1610. }
  1611. return;
  1612. }
  1613. var blobUrl = URL.createObjectURL(blob);
  1614. download(blobUrl, filename);
  1615. }
  1616. };
  1617. exports.DownloadManager = DownloadManager;
  1618. }));
  1619. (function (root, factory) {
  1620. {
  1621. factory((root.pdfjsWebFirefoxCom = {}), root.pdfjsWebPreferences,
  1622. root.pdfjsWebPDFJS);
  1623. }
  1624. }(this, function (exports, preferences, pdfjsLib) {
  1625. }));
  1626. (function (root, factory) {
  1627. {
  1628. factory((root.pdfjsWebPDFAttachmentViewer = {}), root.pdfjsWebPDFJS);
  1629. }
  1630. }(this, function (exports, pdfjsLib) {
  1631. /**
  1632. * @typedef {Object} PDFAttachmentViewerOptions
  1633. * @property {HTMLDivElement} container - The viewer element.
  1634. * @property {DownloadManager} downloadManager - The download manager.
  1635. */
  1636. /**
  1637. * @typedef {Object} PDFAttachmentViewerRenderParameters
  1638. * @property {Array|null} attachments - An array of attachment objects.
  1639. */
  1640. /**
  1641. * @class
  1642. */
  1643. var PDFAttachmentViewer = (function PDFAttachmentViewerClosure() {
  1644. /**
  1645. * @constructs PDFAttachmentViewer
  1646. * @param {PDFAttachmentViewerOptions} options
  1647. */
  1648. function PDFAttachmentViewer(options) {
  1649. this.attachments = null;
  1650. this.container = options.container;
  1651. this.downloadManager = options.downloadManager;
  1652. }
  1653. PDFAttachmentViewer.prototype = {
  1654. reset: function PDFAttachmentViewer_reset() {
  1655. this.attachments = null;
  1656. var container = this.container;
  1657. while (container.firstChild) {
  1658. container.removeChild(container.firstChild);
  1659. }
  1660. },
  1661. /**
  1662. * @private
  1663. */
  1664. _dispatchEvent:
  1665. function PDFAttachmentViewer_dispatchEvent(attachmentsCount) {
  1666. var event = document.createEvent('CustomEvent');
  1667. event.initCustomEvent('attachmentsloaded', true, true, {
  1668. attachmentsCount: attachmentsCount
  1669. });
  1670. this.container.dispatchEvent(event);
  1671. },
  1672. /**
  1673. * @private
  1674. */
  1675. _bindLink:
  1676. function PDFAttachmentViewer_bindLink(button, content, filename) {
  1677. button.onclick = function downloadFile(e) {
  1678. this.downloadManager.downloadData(content, filename, '');
  1679. return false;
  1680. }.bind(this);
  1681. },
  1682. /**
  1683. * @param {PDFAttachmentViewerRenderParameters} params
  1684. */
  1685. render: function PDFAttachmentViewer_render(params) {
  1686. var attachments = (params && params.attachments) || null;
  1687. var attachmentsCount = 0;
  1688. if (this.attachments) {
  1689. this.reset();
  1690. }
  1691. this.attachments = attachments;
  1692. if (!attachments) {
  1693. this._dispatchEvent(attachmentsCount);
  1694. return;
  1695. }
  1696. var names = Object.keys(attachments).sort(function(a, b) {
  1697. return a.toLowerCase().localeCompare(b.toLowerCase());
  1698. });
  1699. attachmentsCount = names.length;
  1700. for (var i = 0; i < attachmentsCount; i++) {
  1701. var item = attachments[names[i]];
  1702. var filename = pdfjsLib.getFilenameFromUrl(item.filename);
  1703. var div = document.createElement('div');
  1704. div.className = 'attachmentsItem';
  1705. var button = document.createElement('button');
  1706. this._bindLink(button, item.content, filename);
  1707. button.textContent = pdfjsLib.removeNullCharacters(filename);
  1708. div.appendChild(button);
  1709. this.container.appendChild(div);
  1710. }
  1711. this._dispatchEvent(attachmentsCount);
  1712. }
  1713. };
  1714. return PDFAttachmentViewer;
  1715. })();
  1716. exports.PDFAttachmentViewer = PDFAttachmentViewer;
  1717. }));
  1718. (function (root, factory) {
  1719. {
  1720. factory((root.pdfjsWebPDFOutlineViewer = {}), root.pdfjsWebPDFJS);
  1721. }
  1722. }(this, function (exports, pdfjsLib) {
  1723. var DEFAULT_TITLE = '\u2013';
  1724. /**
  1725. * @typedef {Object} PDFOutlineViewerOptions
  1726. * @property {HTMLDivElement} container - The viewer element.
  1727. * @property {IPDFLinkService} linkService - The navigation/linking service.
  1728. */
  1729. /**
  1730. * @typedef {Object} PDFOutlineViewerRenderParameters
  1731. * @property {Array|null} outline - An array of outline objects.
  1732. */
  1733. /**
  1734. * @class
  1735. */
  1736. var PDFOutlineViewer = (function PDFOutlineViewerClosure() {
  1737. /**
  1738. * @constructs PDFOutlineViewer
  1739. * @param {PDFOutlineViewerOptions} options
  1740. */
  1741. function PDFOutlineViewer(options) {
  1742. this.outline = null;
  1743. this.lastToggleIsShow = true;
  1744. this.container = options.container;
  1745. this.linkService = options.linkService;
  1746. }
  1747. PDFOutlineViewer.prototype = {
  1748. reset: function PDFOutlineViewer_reset() {
  1749. this.outline = null;
  1750. this.lastToggleIsShow = true;
  1751. var container = this.container;
  1752. while (container.firstChild) {
  1753. container.removeChild(container.firstChild);
  1754. }
  1755. },
  1756. /**
  1757. * @private
  1758. */
  1759. _dispatchEvent: function PDFOutlineViewer_dispatchEvent(outlineCount) {
  1760. var event = document.createEvent('CustomEvent');
  1761. event.initCustomEvent('outlineloaded', true, true, {
  1762. outlineCount: outlineCount
  1763. });
  1764. this.container.dispatchEvent(event);
  1765. },
  1766. /**
  1767. * @private
  1768. */
  1769. _bindLink: function PDFOutlineViewer_bindLink(element, item) {
  1770. if (item.url) {
  1771. pdfjsLib.addLinkAttributes(element, { url: item.url });
  1772. return;
  1773. }
  1774. var linkService = this.linkService;
  1775. element.href = linkService.getDestinationHash(item.dest);
  1776. element.onclick = function goToDestination(e) {
  1777. linkService.navigateTo(item.dest);
  1778. return false;
  1779. };
  1780. },
  1781. /**
  1782. * @private
  1783. */
  1784. _setStyles: function PDFOutlineViewer_setStyles(element, item) {
  1785. var styleStr = '';
  1786. if (item.bold) {
  1787. styleStr += 'font-weight: bold;';
  1788. }
  1789. if (item.italic) {
  1790. styleStr += 'font-style: italic;';
  1791. }
  1792. if (styleStr) {
  1793. element.setAttribute('style', styleStr);
  1794. }
  1795. },
  1796. /**
  1797. * Prepend a button before an outline item which allows the user to toggle
  1798. * the visibility of all outline items at that level.
  1799. *
  1800. * @private
  1801. */
  1802. _addToggleButton: function PDFOutlineViewer_addToggleButton(div) {
  1803. var toggler = document.createElement('div');
  1804. toggler.className = 'outlineItemToggler';
  1805. toggler.onclick = function(event) {
  1806. event.stopPropagation();
  1807. toggler.classList.toggle('outlineItemsHidden');
  1808. if (event.shiftKey) {
  1809. var shouldShowAll = !toggler.classList.contains('outlineItemsHidden');
  1810. this._toggleOutlineItem(div, shouldShowAll);
  1811. }
  1812. }.bind(this);
  1813. div.insertBefore(toggler, div.firstChild);
  1814. },
  1815. /**
  1816. * Toggle the visibility of the subtree of an outline item.
  1817. *
  1818. * @param {Element} root - the root of the outline (sub)tree.
  1819. * @param {boolean} state - whether to show the outline (sub)tree. If false,
  1820. * the outline subtree rooted at |root| will be collapsed.
  1821. *
  1822. * @private
  1823. */
  1824. _toggleOutlineItem:
  1825. function PDFOutlineViewer_toggleOutlineItem(root, show) {
  1826. this.lastToggleIsShow = show;
  1827. var togglers = root.querySelectorAll('.outlineItemToggler');
  1828. for (var i = 0, ii = togglers.length; i < ii; ++i) {
  1829. togglers[i].classList[show ? 'remove' : 'add']('outlineItemsHidden');
  1830. }
  1831. },
  1832. /**
  1833. * Collapse or expand all subtrees of the outline.
  1834. */
  1835. toggleOutlineTree: function PDFOutlineViewer_toggleOutlineTree() {
  1836. if (!this.outline) {
  1837. return;
  1838. }
  1839. this._toggleOutlineItem(this.container, !this.lastToggleIsShow);
  1840. },
  1841. /**
  1842. * @param {PDFOutlineViewerRenderParameters} params
  1843. */
  1844. render: function PDFOutlineViewer_render(params) {
  1845. var outline = (params && params.outline) || null;
  1846. var outlineCount = 0;
  1847. if (this.outline) {
  1848. this.reset();
  1849. }
  1850. this.outline = outline;
  1851. if (!outline) {
  1852. this._dispatchEvent(outlineCount);
  1853. return;
  1854. }
  1855. var fragment = document.createDocumentFragment();
  1856. var queue = [{ parent: fragment, items: this.outline }];
  1857. var hasAnyNesting = false;
  1858. while (queue.length > 0) {
  1859. var levelData = queue.shift();
  1860. for (var i = 0, len = levelData.items.length; i < len; i++) {
  1861. var item = levelData.items[i];
  1862. var div = document.createElement('div');
  1863. div.className = 'outlineItem';
  1864. var element = document.createElement('a');
  1865. this._bindLink(element, item);
  1866. this._setStyles(element, item);
  1867. element.textContent =
  1868. pdfjsLib.removeNullCharacters(item.title) || DEFAULT_TITLE;
  1869. div.appendChild(element);
  1870. if (item.items.length > 0) {
  1871. hasAnyNesting = true;
  1872. this._addToggleButton(div);
  1873. var itemsDiv = document.createElement('div');
  1874. itemsDiv.className = 'outlineItems';
  1875. div.appendChild(itemsDiv);
  1876. queue.push({ parent: itemsDiv, items: item.items });
  1877. }
  1878. levelData.parent.appendChild(div);
  1879. outlineCount++;
  1880. }
  1881. }
  1882. if (hasAnyNesting) {
  1883. this.container.classList.add('outlineWithDeepNesting');
  1884. }
  1885. this.container.appendChild(fragment);
  1886. this._dispatchEvent(outlineCount);
  1887. }
  1888. };
  1889. return PDFOutlineViewer;
  1890. })();
  1891. exports.PDFOutlineViewer = PDFOutlineViewer;
  1892. }));
  1893. (function (root, factory) {
  1894. {
  1895. factory((root.pdfjsWebPDFSidebar = {}), root.pdfjsWebPDFRenderingQueue);
  1896. }
  1897. }(this, function (exports, pdfRenderingQueue) {
  1898. var RenderingStates = pdfRenderingQueue.RenderingStates;
  1899. var SidebarView = {
  1900. NONE: 0,
  1901. THUMBS: 1,
  1902. OUTLINE: 2,
  1903. ATTACHMENTS: 3
  1904. };
  1905. /**
  1906. * @typedef {Object} PDFSidebarOptions
  1907. * @property {PDFViewer} pdfViewer - The document viewer.
  1908. * @property {PDFThumbnailViewer} pdfThumbnailViewer - The thumbnail viewer.
  1909. * @property {PDFOutlineViewer} pdfOutlineViewer - The outline viewer.
  1910. * @property {HTMLDivElement} mainContainer - The main container
  1911. * (in which the viewer element is placed).
  1912. * @property {HTMLDivElement} outerContainer - The outer container
  1913. * (encasing both the viewer and sidebar elements).
  1914. * @property {HTMLButtonElement} toggleButton - The button used for
  1915. * opening/closing the sidebar.
  1916. * @property {HTMLButtonElement} thumbnailButton - The button used to show
  1917. * the thumbnail view.
  1918. * @property {HTMLButtonElement} outlineButton - The button used to show
  1919. * the outline view.
  1920. * @property {HTMLButtonElement} attachmentsButton - The button used to show
  1921. * the attachments view.
  1922. * @property {HTMLDivElement} thumbnailView - The container in which
  1923. * the thumbnails are placed.
  1924. * @property {HTMLDivElement} outlineView - The container in which
  1925. * the outline is placed.
  1926. * @property {HTMLDivElement} attachmentsView - The container in which
  1927. * the attachments are placed.
  1928. */
  1929. /**
  1930. * @class
  1931. */
  1932. var PDFSidebar = (function PDFSidebarClosure() {
  1933. /**
  1934. * @constructs PDFSidebar
  1935. * @param {PDFSidebarOptions} options
  1936. */
  1937. function PDFSidebar(options) {
  1938. this.isOpen = false;
  1939. this.active = SidebarView.THUMBS;
  1940. this.isInitialViewSet = false;
  1941. /**
  1942. * Callback used when the sidebar has been opened/closed, to ensure that
  1943. * the viewers (PDFViewer/PDFThumbnailViewer) are updated correctly.
  1944. */
  1945. this.onToggled = null;
  1946. this.pdfViewer = options.pdfViewer;
  1947. this.pdfThumbnailViewer = options.pdfThumbnailViewer;
  1948. this.pdfOutlineViewer = options.pdfOutlineViewer;
  1949. this.mainContainer = options.mainContainer;
  1950. this.outerContainer = options.outerContainer;
  1951. this.toggleButton = options.toggleButton;
  1952. this.thumbnailButton = options.thumbnailButton;
  1953. this.outlineButton = options.outlineButton;
  1954. this.attachmentsButton = options.attachmentsButton;
  1955. this.thumbnailView = options.thumbnailView;
  1956. this.outlineView = options.outlineView;
  1957. this.attachmentsView = options.attachmentsView;
  1958. this._addEventListeners();
  1959. }
  1960. PDFSidebar.prototype = {
  1961. reset: function PDFSidebar_reset() {
  1962. this.isInitialViewSet = false;
  1963. this.close();
  1964. this.switchView(SidebarView.THUMBS);
  1965. this.outlineButton.disabled = false;
  1966. this.attachmentsButton.disabled = false;
  1967. },
  1968. /**
  1969. * @returns {number} One of the values in {SidebarView}.
  1970. */
  1971. get visibleView() {
  1972. return (this.isOpen ? this.active : SidebarView.NONE);
  1973. },
  1974. get isThumbnailViewVisible() {
  1975. return (this.isOpen && this.active === SidebarView.THUMBS);
  1976. },
  1977. get isOutlineViewVisible() {
  1978. return (this.isOpen && this.active === SidebarView.OUTLINE);
  1979. },
  1980. get isAttachmentsViewVisible() {
  1981. return (this.isOpen && this.active === SidebarView.ATTACHMENTS);
  1982. },
  1983. /**
  1984. * @param {number} view - The sidebar view that should become visible,
  1985. * must be one of the values in {SidebarView}.
  1986. */
  1987. setInitialView: function PDFSidebar_setInitialView(view) {
  1988. if (this.isInitialViewSet) {
  1989. return;
  1990. }
  1991. this.isInitialViewSet = true;
  1992. if (this.isOpen && view === SidebarView.NONE) {
  1993. this._dispatchEvent();
  1994. // If the user has already manually opened the sidebar,
  1995. // immediately closing it would be bad UX.
  1996. return;
  1997. }
  1998. var isViewPreserved = (view === this.visibleView);
  1999. this.switchView(view, /* forceOpen */ true);
  2000. if (isViewPreserved) {
  2001. // Prevent dispatching two back-to-back `sidebarviewchanged` events,
  2002. // since `this.switchView` dispatched the event if the view changed.
  2003. this._dispatchEvent();
  2004. }
  2005. },
  2006. /**
  2007. * @param {number} view - The sidebar view that should be switched to,
  2008. * must be one of the values in {SidebarView}.
  2009. * @param {boolean} forceOpen - (optional) Ensure that the sidebar is open.
  2010. * The default value is false.
  2011. */
  2012. switchView: function PDFSidebar_switchView(view, forceOpen) {
  2013. if (view === SidebarView.NONE) {
  2014. this.close();
  2015. return;
  2016. }
  2017. var isViewChanged = (view !== this.active);
  2018. var shouldForceRendering = false;
  2019. switch (view) {
  2020. case SidebarView.THUMBS:
  2021. this.thumbnailButton.classList.add('toggled');
  2022. this.outlineButton.classList.remove('toggled');
  2023. this.attachmentsButton.classList.remove('toggled');
  2024. this.thumbnailView.classList.remove('hidden');
  2025. this.outlineView.classList.add('hidden');
  2026. this.attachmentsView.classList.add('hidden');
  2027. if (this.isOpen && isViewChanged) {
  2028. this._updateThumbnailViewer();
  2029. shouldForceRendering = true;
  2030. }
  2031. break;
  2032. case SidebarView.OUTLINE:
  2033. if (this.outlineButton.disabled) {
  2034. return;
  2035. }
  2036. this.thumbnailButton.classList.remove('toggled');
  2037. this.outlineButton.classList.add('toggled');
  2038. this.attachmentsButton.classList.remove('toggled');
  2039. this.thumbnailView.classList.add('hidden');
  2040. this.outlineView.classList.remove('hidden');
  2041. this.attachmentsView.classList.add('hidden');
  2042. break;
  2043. case SidebarView.ATTACHMENTS:
  2044. if (this.attachmentsButton.disabled) {
  2045. return;
  2046. }
  2047. this.thumbnailButton.classList.remove('toggled');
  2048. this.outlineButton.classList.remove('toggled');
  2049. this.attachmentsButton.classList.add('toggled');
  2050. this.thumbnailView.classList.add('hidden');
  2051. this.outlineView.classList.add('hidden');
  2052. this.attachmentsView.classList.remove('hidden');
  2053. break;
  2054. default:
  2055. console.error('PDFSidebar_switchView: "' + view +
  2056. '" is an unsupported value.');
  2057. return;
  2058. }
  2059. // Update the active view *after* it has been validated above,
  2060. // in order to prevent setting it to an invalid state.
  2061. this.active = view | 0;
  2062. if (forceOpen && !this.isOpen) {
  2063. this.open();
  2064. // NOTE: `this.open` will trigger rendering, and dispatch the event.
  2065. return;
  2066. }
  2067. if (shouldForceRendering) {
  2068. this._forceRendering();
  2069. }
  2070. if (isViewChanged) {
  2071. this._dispatchEvent();
  2072. }
  2073. },
  2074. open: function PDFSidebar_open() {
  2075. if (this.isOpen) {
  2076. return;
  2077. }
  2078. this.isOpen = true;
  2079. this.toggleButton.classList.add('toggled');
  2080. this.outerContainer.classList.add('sidebarMoving');
  2081. this.outerContainer.classList.add('sidebarOpen');
  2082. if (this.active === SidebarView.THUMBS) {
  2083. this._updateThumbnailViewer();
  2084. }
  2085. this._forceRendering();
  2086. this._dispatchEvent();
  2087. },
  2088. close: function PDFSidebar_close() {
  2089. if (!this.isOpen) {
  2090. return;
  2091. }
  2092. this.isOpen = false;
  2093. this.toggleButton.classList.remove('toggled');
  2094. this.outerContainer.classList.add('sidebarMoving');
  2095. this.outerContainer.classList.remove('sidebarOpen');
  2096. this._forceRendering();
  2097. this._dispatchEvent();
  2098. },
  2099. toggle: function PDFSidebar_toggle() {
  2100. if (this.isOpen) {
  2101. this.close();
  2102. } else {
  2103. this.open();
  2104. }
  2105. },
  2106. /**
  2107. * @private
  2108. */
  2109. _dispatchEvent: function PDFSidebar_dispatchEvent() {
  2110. var event = document.createEvent('CustomEvent');
  2111. event.initCustomEvent('sidebarviewchanged', true, true, {
  2112. view: this.visibleView,
  2113. });
  2114. this.outerContainer.dispatchEvent(event);
  2115. },
  2116. /**
  2117. * @private
  2118. */
  2119. _forceRendering: function PDFSidebar_forceRendering() {
  2120. if (this.onToggled) {
  2121. this.onToggled();
  2122. } else { // Fallback
  2123. this.pdfViewer.forceRendering();
  2124. this.pdfThumbnailViewer.forceRendering();
  2125. }
  2126. },
  2127. /**
  2128. * @private
  2129. */
  2130. _updateThumbnailViewer: function PDFSidebar_updateThumbnailViewer() {
  2131. var pdfViewer = this.pdfViewer;
  2132. var thumbnailViewer = this.pdfThumbnailViewer;
  2133. // Use the rendered pages to set the corresponding thumbnail images.
  2134. var pagesCount = pdfViewer.pagesCount;
  2135. for (var pageIndex = 0; pageIndex < pagesCount; pageIndex++) {
  2136. var pageView = pdfViewer.getPageView(pageIndex);
  2137. if (pageView && pageView.renderingState === RenderingStates.FINISHED) {
  2138. var thumbnailView = thumbnailViewer.getThumbnail(pageIndex);
  2139. thumbnailView.setImage(pageView);
  2140. }
  2141. }
  2142. thumbnailViewer.scrollThumbnailIntoView(pdfViewer.currentPageNumber);
  2143. },
  2144. /**
  2145. * @private
  2146. */
  2147. _addEventListeners: function PDFSidebar_addEventListeners() {
  2148. var self = this;
  2149. self.mainContainer.addEventListener('transitionend', function(evt) {
  2150. if (evt.target === /* mainContainer */ this) {
  2151. self.outerContainer.classList.remove('sidebarMoving');
  2152. }
  2153. });
  2154. // Buttons for switching views.
  2155. self.thumbnailButton.addEventListener('click', function() {
  2156. self.switchView(SidebarView.THUMBS);
  2157. });
  2158. self.outlineButton.addEventListener('click', function() {
  2159. self.switchView(SidebarView.OUTLINE);
  2160. });
  2161. self.outlineButton.addEventListener('dblclick', function() {
  2162. self.pdfOutlineViewer.toggleOutlineTree();
  2163. });
  2164. self.attachmentsButton.addEventListener('click', function() {
  2165. self.switchView(SidebarView.ATTACHMENTS);
  2166. });
  2167. // Disable/enable views.
  2168. self.outlineView.addEventListener('outlineloaded', function(evt) {
  2169. var outlineCount = evt.detail.outlineCount;
  2170. self.outlineButton.disabled = !outlineCount;
  2171. if (!outlineCount && self.active === SidebarView.OUTLINE) {
  2172. self.switchView(SidebarView.THUMBS);
  2173. }
  2174. });
  2175. self.attachmentsView.addEventListener('attachmentsloaded', function(evt) {
  2176. var attachmentsCount = evt.detail.attachmentsCount;
  2177. self.attachmentsButton.disabled = !attachmentsCount;
  2178. if (!attachmentsCount && self.active === SidebarView.ATTACHMENTS) {
  2179. self.switchView(SidebarView.THUMBS);
  2180. }
  2181. });
  2182. // Update the thumbnailViewer, if visible, when exiting presentation mode.
  2183. window.addEventListener('presentationmodechanged', function(evt) {
  2184. if (!evt.detail.active && !evt.detail.switchInProgress &&
  2185. self.isThumbnailViewVisible) {
  2186. self._updateThumbnailViewer();
  2187. }
  2188. });
  2189. },
  2190. };
  2191. return PDFSidebar;
  2192. })();
  2193. exports.SidebarView = SidebarView;
  2194. exports.PDFSidebar = PDFSidebar;
  2195. }));
  2196. (function (root, factory) {
  2197. {
  2198. factory((root.pdfjsWebTextLayerBuilder = {}), root.pdfjsWebPDFJS);
  2199. }
  2200. }(this, function (exports, pdfjsLib) {
  2201. /**
  2202. * @typedef {Object} TextLayerBuilderOptions
  2203. * @property {HTMLDivElement} textLayerDiv - The text layer container.
  2204. * @property {number} pageIndex - The page index.
  2205. * @property {PageViewport} viewport - The viewport of the text layer.
  2206. * @property {PDFFindController} findController
  2207. */
  2208. /**
  2209. * TextLayerBuilder provides text-selection functionality for the PDF.
  2210. * It does this by creating overlay divs over the PDF text. These divs
  2211. * contain text that matches the PDF text they are overlaying. This object
  2212. * also provides a way to highlight text that is being searched for.
  2213. * @class
  2214. */
  2215. var TextLayerBuilder = (function TextLayerBuilderClosure() {
  2216. function TextLayerBuilder(options) {
  2217. this.textLayerDiv = options.textLayerDiv;
  2218. this.renderingDone = false;
  2219. this.divContentDone = false;
  2220. this.pageIdx = options.pageIndex;
  2221. this.pageNumber = this.pageIdx + 1;
  2222. this.matches = [];
  2223. this.viewport = options.viewport;
  2224. this.textDivs = [];
  2225. this.findController = options.findController || null;
  2226. this.textLayerRenderTask = null;
  2227. this._bindMouse();
  2228. }
  2229. TextLayerBuilder.prototype = {
  2230. _finishRendering: function TextLayerBuilder_finishRendering() {
  2231. this.renderingDone = true;
  2232. var endOfContent = document.createElement('div');
  2233. endOfContent.className = 'endOfContent';
  2234. this.textLayerDiv.appendChild(endOfContent);
  2235. var event = document.createEvent('CustomEvent');
  2236. event.initCustomEvent('textlayerrendered', true, true, {
  2237. pageNumber: this.pageNumber
  2238. });
  2239. this.textLayerDiv.dispatchEvent(event);
  2240. },
  2241. /**
  2242. * Renders the text layer.
  2243. * @param {number} timeout (optional) if specified, the rendering waits
  2244. * for specified amount of ms.
  2245. */
  2246. render: function TextLayerBuilder_render(timeout) {
  2247. if (!this.divContentDone || this.renderingDone) {
  2248. return;
  2249. }
  2250. if (this.textLayerRenderTask) {
  2251. this.textLayerRenderTask.cancel();
  2252. this.textLayerRenderTask = null;
  2253. }
  2254. this.textDivs = [];
  2255. var textLayerFrag = document.createDocumentFragment();
  2256. this.textLayerRenderTask = pdfjsLib.renderTextLayer({
  2257. textContent: this.textContent,
  2258. container: textLayerFrag,
  2259. viewport: this.viewport,
  2260. textDivs: this.textDivs,
  2261. timeout: timeout
  2262. });
  2263. this.textLayerRenderTask.promise.then(function () {
  2264. this.textLayerDiv.appendChild(textLayerFrag);
  2265. this._finishRendering();
  2266. this.updateMatches();
  2267. }.bind(this), function (reason) {
  2268. // canceled or failed to render text layer -- skipping errors
  2269. });
  2270. },
  2271. setTextContent: function TextLayerBuilder_setTextContent(textContent) {
  2272. if (this.textLayerRenderTask) {
  2273. this.textLayerRenderTask.cancel();
  2274. this.textLayerRenderTask = null;
  2275. }
  2276. this.textContent = textContent;
  2277. this.divContentDone = true;
  2278. },
  2279. convertMatches: function TextLayerBuilder_convertMatches(matches) {
  2280. var i = 0;
  2281. var iIndex = 0;
  2282. var bidiTexts = this.textContent.items;
  2283. var end = bidiTexts.length - 1;
  2284. var queryLen = (this.findController === null ?
  2285. 0 : this.findController.state.query.length);
  2286. var ret = [];
  2287. for (var m = 0, len = matches.length; m < len; m++) {
  2288. // Calculate the start position.
  2289. var matchIdx = matches[m];
  2290. // Loop over the divIdxs.
  2291. while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
  2292. iIndex += bidiTexts[i].str.length;
  2293. i++;
  2294. }
  2295. if (i === bidiTexts.length) {
  2296. console.error('Could not find a matching mapping');
  2297. }
  2298. var match = {
  2299. begin: {
  2300. divIdx: i,
  2301. offset: matchIdx - iIndex
  2302. }
  2303. };
  2304. // Calculate the end position.
  2305. matchIdx += queryLen;
  2306. // Somewhat the same array as above, but use > instead of >= to get
  2307. // the end position right.
  2308. while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
  2309. iIndex += bidiTexts[i].str.length;
  2310. i++;
  2311. }
  2312. match.end = {
  2313. divIdx: i,
  2314. offset: matchIdx - iIndex
  2315. };
  2316. ret.push(match);
  2317. }
  2318. return ret;
  2319. },
  2320. renderMatches: function TextLayerBuilder_renderMatches(matches) {
  2321. // Early exit if there is nothing to render.
  2322. if (matches.length === 0) {
  2323. return;
  2324. }
  2325. var bidiTexts = this.textContent.items;
  2326. var textDivs = this.textDivs;
  2327. var prevEnd = null;
  2328. var pageIdx = this.pageIdx;
  2329. var isSelectedPage = (this.findController === null ?
  2330. false : (pageIdx === this.findController.selected.pageIdx));
  2331. var selectedMatchIdx = (this.findController === null ?
  2332. -1 : this.findController.selected.matchIdx);
  2333. var highlightAll = (this.findController === null ?
  2334. false : this.findController.state.highlightAll);
  2335. var infinity = {
  2336. divIdx: -1,
  2337. offset: undefined
  2338. };
  2339. function beginText(begin, className) {
  2340. var divIdx = begin.divIdx;
  2341. textDivs[divIdx].textContent = '';
  2342. appendTextToDiv(divIdx, 0, begin.offset, className);
  2343. }
  2344. function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
  2345. var div = textDivs[divIdx];
  2346. var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
  2347. var node = document.createTextNode(content);
  2348. if (className) {
  2349. var span = document.createElement('span');
  2350. span.className = className;
  2351. span.appendChild(node);
  2352. div.appendChild(span);
  2353. return;
  2354. }
  2355. div.appendChild(node);
  2356. }
  2357. var i0 = selectedMatchIdx, i1 = i0 + 1;
  2358. if (highlightAll) {
  2359. i0 = 0;
  2360. i1 = matches.length;
  2361. } else if (!isSelectedPage) {
  2362. // Not highlighting all and this isn't the selected page, so do nothing.
  2363. return;
  2364. }
  2365. for (var i = i0; i < i1; i++) {
  2366. var match = matches[i];
  2367. var begin = match.begin;
  2368. var end = match.end;
  2369. var isSelected = (isSelectedPage && i === selectedMatchIdx);
  2370. var highlightSuffix = (isSelected ? ' selected' : '');
  2371. if (this.findController) {
  2372. this.findController.updateMatchPosition(pageIdx, i, textDivs,
  2373. begin.divIdx, end.divIdx);
  2374. }
  2375. // Match inside new div.
  2376. if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
  2377. // If there was a previous div, then add the text at the end.
  2378. if (prevEnd !== null) {
  2379. appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
  2380. }
  2381. // Clear the divs and set the content until the starting point.
  2382. beginText(begin);
  2383. } else {
  2384. appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
  2385. }
  2386. if (begin.divIdx === end.divIdx) {
  2387. appendTextToDiv(begin.divIdx, begin.offset, end.offset,
  2388. 'highlight' + highlightSuffix);
  2389. } else {
  2390. appendTextToDiv(begin.divIdx, begin.offset, infinity.offset,
  2391. 'highlight begin' + highlightSuffix);
  2392. for (var n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
  2393. textDivs[n0].className = 'highlight middle' + highlightSuffix;
  2394. }
  2395. beginText(end, 'highlight end' + highlightSuffix);
  2396. }
  2397. prevEnd = end;
  2398. }
  2399. if (prevEnd) {
  2400. appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
  2401. }
  2402. },
  2403. updateMatches: function TextLayerBuilder_updateMatches() {
  2404. // Only show matches when all rendering is done.
  2405. if (!this.renderingDone) {
  2406. return;
  2407. }
  2408. // Clear all matches.
  2409. var matches = this.matches;
  2410. var textDivs = this.textDivs;
  2411. var bidiTexts = this.textContent.items;
  2412. var clearedUntilDivIdx = -1;
  2413. // Clear all current matches.
  2414. for (var i = 0, len = matches.length; i < len; i++) {
  2415. var match = matches[i];
  2416. var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
  2417. for (var n = begin, end = match.end.divIdx; n <= end; n++) {
  2418. var div = textDivs[n];
  2419. div.textContent = bidiTexts[n].str;
  2420. div.className = '';
  2421. }
  2422. clearedUntilDivIdx = match.end.divIdx + 1;
  2423. }
  2424. if (this.findController === null || !this.findController.active) {
  2425. return;
  2426. }
  2427. // Convert the matches on the page controller into the match format
  2428. // used for the textLayer.
  2429. this.matches = this.convertMatches(this.findController === null ?
  2430. [] : (this.findController.pageMatches[this.pageIdx] || []));
  2431. this.renderMatches(this.matches);
  2432. },
  2433. /**
  2434. * Fixes text selection: adds additional div where mouse was clicked.
  2435. * This reduces flickering of the content if mouse slowly dragged down/up.
  2436. * @private
  2437. */
  2438. _bindMouse: function TextLayerBuilder_bindMouse() {
  2439. var div = this.textLayerDiv;
  2440. div.addEventListener('mousedown', function (e) {
  2441. var end = div.querySelector('.endOfContent');
  2442. if (!end) {
  2443. return;
  2444. }
  2445. // On non-Firefox browsers, the selection will feel better if the height
  2446. // of the endOfContent div will be adjusted to start at mouse click
  2447. // location -- this will avoid flickering when selections moves up.
  2448. // However it does not work when selection started on empty space.
  2449. var adjustTop = e.target !== div;
  2450. adjustTop = adjustTop && window.getComputedStyle(end).
  2451. getPropertyValue('-moz-user-select') !== 'none';
  2452. if (adjustTop) {
  2453. var divBounds = div.getBoundingClientRect();
  2454. var r = Math.max(0, (e.pageY - divBounds.top) / divBounds.height);
  2455. end.style.top = (r * 100).toFixed(2) + '%';
  2456. }
  2457. end.classList.add('active');
  2458. });
  2459. div.addEventListener('mouseup', function (e) {
  2460. var end = div.querySelector('.endOfContent');
  2461. if (!end) {
  2462. return;
  2463. }
  2464. end.style.top = '';
  2465. end.classList.remove('active');
  2466. });
  2467. },
  2468. };
  2469. return TextLayerBuilder;
  2470. })();
  2471. /**
  2472. * @constructor
  2473. * @implements IPDFTextLayerFactory
  2474. */
  2475. function DefaultTextLayerFactory() {}
  2476. DefaultTextLayerFactory.prototype = {
  2477. /**
  2478. * @param {HTMLDivElement} textLayerDiv
  2479. * @param {number} pageIndex
  2480. * @param {PageViewport} viewport
  2481. * @returns {TextLayerBuilder}
  2482. */
  2483. createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) {
  2484. return new TextLayerBuilder({
  2485. textLayerDiv: textLayerDiv,
  2486. pageIndex: pageIndex,
  2487. viewport: viewport
  2488. });
  2489. }
  2490. };
  2491. exports.TextLayerBuilder = TextLayerBuilder;
  2492. exports.DefaultTextLayerFactory = DefaultTextLayerFactory;
  2493. }));
  2494. (function (root, factory) {
  2495. {
  2496. factory((root.pdfjsWebUIUtils = {}), root.pdfjsWebPDFJS);
  2497. }
  2498. }(this, function (exports, pdfjsLib) {
  2499. var CSS_UNITS = 96.0 / 72.0;
  2500. var DEFAULT_SCALE_VALUE = 'auto';
  2501. var DEFAULT_SCALE = 1.0;
  2502. var UNKNOWN_SCALE = 0;
  2503. var MAX_AUTO_SCALE = 1.25;
  2504. var SCROLLBAR_PADDING = 40;
  2505. var VERTICAL_PADDING = 5;
  2506. var mozL10n = document.mozL10n || document.webL10n;
  2507. var PDFJS = pdfjsLib.PDFJS;
  2508. /**
  2509. * Disables fullscreen support, and by extension Presentation Mode,
  2510. * in browsers which support the fullscreen API.
  2511. * @var {boolean}
  2512. */
  2513. PDFJS.disableFullscreen = (PDFJS.disableFullscreen === undefined ?
  2514. false : PDFJS.disableFullscreen);
  2515. /**
  2516. * Enables CSS only zooming.
  2517. * @var {boolean}
  2518. */
  2519. PDFJS.useOnlyCssZoom = (PDFJS.useOnlyCssZoom === undefined ?
  2520. false : PDFJS.useOnlyCssZoom);
  2521. /**
  2522. * The maximum supported canvas size in total pixels e.g. width * height.
  2523. * The default value is 4096 * 4096. Use -1 for no limit.
  2524. * @var {number}
  2525. */
  2526. PDFJS.maxCanvasPixels = (PDFJS.maxCanvasPixels === undefined ?
  2527. 16777216 : PDFJS.maxCanvasPixels);
  2528. /**
  2529. * Disables saving of the last position of the viewed PDF.
  2530. * @var {boolean}
  2531. */
  2532. PDFJS.disableHistory = (PDFJS.disableHistory === undefined ?
  2533. false : PDFJS.disableHistory);
  2534. /**
  2535. * Disables creation of the text layer that used for text selection and search.
  2536. * @var {boolean}
  2537. */
  2538. PDFJS.disableTextLayer = (PDFJS.disableTextLayer === undefined ?
  2539. false : PDFJS.disableTextLayer);
  2540. /**
  2541. * Disables maintaining the current position in the document when zooming.
  2542. */
  2543. PDFJS.ignoreCurrentPositionOnZoom = (PDFJS.ignoreCurrentPositionOnZoom ===
  2544. undefined ? false : PDFJS.ignoreCurrentPositionOnZoom);
  2545. /**
  2546. * Interface locale settings.
  2547. * @var {string}
  2548. */
  2549. PDFJS.locale = (PDFJS.locale === undefined ? navigator.language : PDFJS.locale);
  2550. /**
  2551. * Returns scale factor for the canvas. It makes sense for the HiDPI displays.
  2552. * @return {Object} The object with horizontal (sx) and vertical (sy)
  2553. scales. The scaled property is set to false if scaling is
  2554. not required, true otherwise.
  2555. */
  2556. function getOutputScale(ctx) {
  2557. var devicePixelRatio = window.devicePixelRatio || 1;
  2558. var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
  2559. ctx.mozBackingStorePixelRatio ||
  2560. ctx.msBackingStorePixelRatio ||
  2561. ctx.oBackingStorePixelRatio ||
  2562. ctx.backingStorePixelRatio || 1;
  2563. var pixelRatio = devicePixelRatio / backingStoreRatio;
  2564. return {
  2565. sx: pixelRatio,
  2566. sy: pixelRatio,
  2567. scaled: pixelRatio !== 1
  2568. };
  2569. }
  2570. /**
  2571. * Scrolls specified element into view of its parent.
  2572. * @param {Object} element - The element to be visible.
  2573. * @param {Object} spot - An object with optional top and left properties,
  2574. * specifying the offset from the top left edge.
  2575. * @param {boolean} skipOverflowHiddenElements - Ignore elements that have
  2576. * the CSS rule `overflow: hidden;` set. The default is false.
  2577. */
  2578. function scrollIntoView(element, spot, skipOverflowHiddenElements) {
  2579. // Assuming offsetParent is available (it's not available when viewer is in
  2580. // hidden iframe or object). We have to scroll: if the offsetParent is not set
  2581. // producing the error. See also animationStartedClosure.
  2582. var parent = element.offsetParent;
  2583. if (!parent) {
  2584. console.error('offsetParent is not set -- cannot scroll');
  2585. return;
  2586. }
  2587. var checkOverflow = skipOverflowHiddenElements || false;
  2588. var offsetY = element.offsetTop + element.clientTop;
  2589. var offsetX = element.offsetLeft + element.clientLeft;
  2590. while (parent.clientHeight === parent.scrollHeight ||
  2591. (checkOverflow && getComputedStyle(parent).overflow === 'hidden')) {
  2592. if (parent.dataset._scaleY) {
  2593. offsetY /= parent.dataset._scaleY;
  2594. offsetX /= parent.dataset._scaleX;
  2595. }
  2596. offsetY += parent.offsetTop;
  2597. offsetX += parent.offsetLeft;
  2598. parent = parent.offsetParent;
  2599. if (!parent) {
  2600. return; // no need to scroll
  2601. }
  2602. }
  2603. if (spot) {
  2604. if (spot.top !== undefined) {
  2605. offsetY += spot.top;
  2606. }
  2607. if (spot.left !== undefined) {
  2608. offsetX += spot.left;
  2609. parent.scrollLeft = offsetX;
  2610. }
  2611. }
  2612. parent.scrollTop = offsetY;
  2613. }
  2614. /**
  2615. * Helper function to start monitoring the scroll event and converting them into
  2616. * PDF.js friendly one: with scroll debounce and scroll direction.
  2617. */
  2618. function watchScroll(viewAreaElement, callback) {
  2619. var debounceScroll = function debounceScroll(evt) {
  2620. if (rAF) {
  2621. return;
  2622. }
  2623. // schedule an invocation of scroll for next animation frame.
  2624. rAF = window.requestAnimationFrame(function viewAreaElementScrolled() {
  2625. rAF = null;
  2626. var currentY = viewAreaElement.scrollTop;
  2627. var lastY = state.lastY;
  2628. if (currentY !== lastY) {
  2629. state.down = currentY > lastY;
  2630. }
  2631. state.lastY = currentY;
  2632. callback(state);
  2633. });
  2634. };
  2635. var state = {
  2636. down: true,
  2637. lastY: viewAreaElement.scrollTop,
  2638. _eventHandler: debounceScroll
  2639. };
  2640. var rAF = null;
  2641. viewAreaElement.addEventListener('scroll', debounceScroll, true);
  2642. return state;
  2643. }
  2644. /**
  2645. * Helper function to parse query string (e.g. ?param1=value&parm2=...).
  2646. */
  2647. function parseQueryString(query) {
  2648. var parts = query.split('&');
  2649. var params = {};
  2650. for (var i = 0, ii = parts.length; i < ii; ++i) {
  2651. var param = parts[i].split('=');
  2652. var key = param[0].toLowerCase();
  2653. var value = param.length > 1 ? param[1] : null;
  2654. params[decodeURIComponent(key)] = decodeURIComponent(value);
  2655. }
  2656. return params;
  2657. }
  2658. /**
  2659. * Use binary search to find the index of the first item in a given array which
  2660. * passes a given condition. The items are expected to be sorted in the sense
  2661. * that if the condition is true for one item in the array, then it is also true
  2662. * for all following items.
  2663. *
  2664. * @returns {Number} Index of the first array element to pass the test,
  2665. * or |items.length| if no such element exists.
  2666. */
  2667. function binarySearchFirstItem(items, condition) {
  2668. var minIndex = 0;
  2669. var maxIndex = items.length - 1;
  2670. if (items.length === 0 || !condition(items[maxIndex])) {
  2671. return items.length;
  2672. }
  2673. if (condition(items[minIndex])) {
  2674. return minIndex;
  2675. }
  2676. while (minIndex < maxIndex) {
  2677. var currentIndex = (minIndex + maxIndex) >> 1;
  2678. var currentItem = items[currentIndex];
  2679. if (condition(currentItem)) {
  2680. maxIndex = currentIndex;
  2681. } else {
  2682. minIndex = currentIndex + 1;
  2683. }
  2684. }
  2685. return minIndex; /* === maxIndex */
  2686. }
  2687. /**
  2688. * Approximates float number as a fraction using Farey sequence (max order
  2689. * of 8).
  2690. * @param {number} x - Positive float number.
  2691. * @returns {Array} Estimated fraction: the first array item is a numerator,
  2692. * the second one is a denominator.
  2693. */
  2694. function approximateFraction(x) {
  2695. // Fast paths for int numbers or their inversions.
  2696. if (Math.floor(x) === x) {
  2697. return [x, 1];
  2698. }
  2699. var xinv = 1 / x;
  2700. var limit = 8;
  2701. if (xinv > limit) {
  2702. return [1, limit];
  2703. } else if (Math.floor(xinv) === xinv) {
  2704. return [1, xinv];
  2705. }
  2706. var x_ = x > 1 ? xinv : x;
  2707. // a/b and c/d are neighbours in Farey sequence.
  2708. var a = 0, b = 1, c = 1, d = 1;
  2709. // Limiting search to order 8.
  2710. while (true) {
  2711. // Generating next term in sequence (order of q).
  2712. var p = a + c, q = b + d;
  2713. if (q > limit) {
  2714. break;
  2715. }
  2716. if (x_ <= p / q) {
  2717. c = p; d = q;
  2718. } else {
  2719. a = p; b = q;
  2720. }
  2721. }
  2722. // Select closest of the neighbours to x.
  2723. if (x_ - a / b < c / d - x_) {
  2724. return x_ === x ? [a, b] : [b, a];
  2725. } else {
  2726. return x_ === x ? [c, d] : [d, c];
  2727. }
  2728. }
  2729. function roundToDivide(x, div) {
  2730. var r = x % div;
  2731. return r === 0 ? x : Math.round(x - r + div);
  2732. }
  2733. /**
  2734. * Generic helper to find out what elements are visible within a scroll pane.
  2735. */
  2736. function getVisibleElements(scrollEl, views, sortByVisibility) {
  2737. var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
  2738. var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;
  2739. function isElementBottomBelowViewTop(view) {
  2740. var element = view.div;
  2741. var elementBottom =
  2742. element.offsetTop + element.clientTop + element.clientHeight;
  2743. return elementBottom > top;
  2744. }
  2745. var visible = [], view, element;
  2746. var currentHeight, viewHeight, hiddenHeight, percentHeight;
  2747. var currentWidth, viewWidth;
  2748. var firstVisibleElementInd = (views.length === 0) ? 0 :
  2749. binarySearchFirstItem(views, isElementBottomBelowViewTop);
  2750. for (var i = firstVisibleElementInd, ii = views.length; i < ii; i++) {
  2751. view = views[i];
  2752. element = view.div;
  2753. currentHeight = element.offsetTop + element.clientTop;
  2754. viewHeight = element.clientHeight;
  2755. if (currentHeight > bottom) {
  2756. break;
  2757. }
  2758. currentWidth = element.offsetLeft + element.clientLeft;
  2759. viewWidth = element.clientWidth;
  2760. if (currentWidth + viewWidth < left || currentWidth > right) {
  2761. continue;
  2762. }
  2763. hiddenHeight = Math.max(0, top - currentHeight) +
  2764. Math.max(0, currentHeight + viewHeight - bottom);
  2765. percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0;
  2766. visible.push({
  2767. id: view.id,
  2768. x: currentWidth,
  2769. y: currentHeight,
  2770. view: view,
  2771. percent: percentHeight
  2772. });
  2773. }
  2774. var first = visible[0];
  2775. var last = visible[visible.length - 1];
  2776. if (sortByVisibility) {
  2777. visible.sort(function(a, b) {
  2778. var pc = a.percent - b.percent;
  2779. if (Math.abs(pc) > 0.001) {
  2780. return -pc;
  2781. }
  2782. return a.id - b.id; // ensure stability
  2783. });
  2784. }
  2785. return {first: first, last: last, views: visible};
  2786. }
  2787. /**
  2788. * Event handler to suppress context menu.
  2789. */
  2790. function noContextMenuHandler(e) {
  2791. e.preventDefault();
  2792. }
  2793. /**
  2794. * Returns the filename or guessed filename from the url (see issue 3455).
  2795. * url {String} The original PDF location.
  2796. * @return {String} Guessed PDF file name.
  2797. */
  2798. function getPDFFileNameFromURL(url) {
  2799. var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
  2800. // SCHEME HOST 1.PATH 2.QUERY 3.REF
  2801. // Pattern to get last matching NAME.pdf
  2802. var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
  2803. var splitURI = reURI.exec(url);
  2804. var suggestedFilename = reFilename.exec(splitURI[1]) ||
  2805. reFilename.exec(splitURI[2]) ||
  2806. reFilename.exec(splitURI[3]);
  2807. if (suggestedFilename) {
  2808. suggestedFilename = suggestedFilename[0];
  2809. if (suggestedFilename.indexOf('%') !== -1) {
  2810. // URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
  2811. try {
  2812. suggestedFilename =
  2813. reFilename.exec(decodeURIComponent(suggestedFilename))[0];
  2814. } catch(e) { // Possible (extremely rare) errors:
  2815. // URIError "Malformed URI", e.g. for "%AA.pdf"
  2816. // TypeError "null has no properties", e.g. for "%2F.pdf"
  2817. }
  2818. }
  2819. }
  2820. return suggestedFilename || 'document.pdf';
  2821. }
  2822. var ProgressBar = (function ProgressBarClosure() {
  2823. function clamp(v, min, max) {
  2824. return Math.min(Math.max(v, min), max);
  2825. }
  2826. function ProgressBar(id, opts) {
  2827. this.visible = true;
  2828. // Fetch the sub-elements for later.
  2829. this.div = document.querySelector(id + ' .progress');
  2830. // Get the loading bar element, so it can be resized to fit the viewer.
  2831. this.bar = this.div.parentNode;
  2832. // Get options, with sensible defaults.
  2833. this.height = opts.height || 100;
  2834. this.width = opts.width || 100;
  2835. this.units = opts.units || '%';
  2836. // Initialize heights.
  2837. this.div.style.height = this.height + this.units;
  2838. this.percent = 0;
  2839. }
  2840. ProgressBar.prototype = {
  2841. updateBar: function ProgressBar_updateBar() {
  2842. if (this._indeterminate) {
  2843. this.div.classList.add('indeterminate');
  2844. this.div.style.width = this.width + this.units;
  2845. return;
  2846. }
  2847. this.div.classList.remove('indeterminate');
  2848. var progressSize = this.width * this._percent / 100;
  2849. this.div.style.width = progressSize + this.units;
  2850. },
  2851. get percent() {
  2852. return this._percent;
  2853. },
  2854. set percent(val) {
  2855. this._indeterminate = isNaN(val);
  2856. this._percent = clamp(val, 0, 100);
  2857. this.updateBar();
  2858. },
  2859. setWidth: function ProgressBar_setWidth(viewer) {
  2860. if (viewer) {
  2861. var container = viewer.parentNode;
  2862. var scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
  2863. if (scrollbarWidth > 0) {
  2864. this.bar.setAttribute('style', 'width: calc(100% - ' +
  2865. scrollbarWidth + 'px);');
  2866. }
  2867. }
  2868. },
  2869. hide: function ProgressBar_hide() {
  2870. if (!this.visible) {
  2871. return;
  2872. }
  2873. this.visible = false;
  2874. this.bar.classList.add('hidden');
  2875. document.body.classList.remove('loadingInProgress');
  2876. },
  2877. show: function ProgressBar_show() {
  2878. if (this.visible) {
  2879. return;
  2880. }
  2881. this.visible = true;
  2882. document.body.classList.add('loadingInProgress');
  2883. this.bar.classList.remove('hidden');
  2884. }
  2885. };
  2886. return ProgressBar;
  2887. })();
  2888. exports.CSS_UNITS = CSS_UNITS;
  2889. exports.DEFAULT_SCALE_VALUE = DEFAULT_SCALE_VALUE;
  2890. exports.DEFAULT_SCALE = DEFAULT_SCALE;
  2891. exports.UNKNOWN_SCALE = UNKNOWN_SCALE;
  2892. exports.MAX_AUTO_SCALE = MAX_AUTO_SCALE;
  2893. exports.SCROLLBAR_PADDING = SCROLLBAR_PADDING;
  2894. exports.VERTICAL_PADDING = VERTICAL_PADDING;
  2895. exports.mozL10n = mozL10n;
  2896. exports.ProgressBar = ProgressBar;
  2897. exports.getPDFFileNameFromURL = getPDFFileNameFromURL;
  2898. exports.noContextMenuHandler = noContextMenuHandler;
  2899. exports.parseQueryString = parseQueryString;
  2900. exports.getVisibleElements = getVisibleElements;
  2901. exports.roundToDivide = roundToDivide;
  2902. exports.approximateFraction = approximateFraction;
  2903. exports.getOutputScale = getOutputScale;
  2904. exports.scrollIntoView = scrollIntoView;
  2905. exports.watchScroll = watchScroll;
  2906. exports.binarySearchFirstItem = binarySearchFirstItem;
  2907. }));
  2908. (function (root, factory) {
  2909. {
  2910. factory((root.pdfjsWebPasswordPrompt = {}), root.pdfjsWebUIUtils,
  2911. root.pdfjsWebOverlayManager, root.pdfjsWebPDFJS);
  2912. }
  2913. }(this, function (exports, uiUtils, overlayManager, pdfjsLib) {
  2914. var mozL10n = uiUtils.mozL10n;
  2915. var OverlayManager = overlayManager.OverlayManager;
  2916. /**
  2917. * @typedef {Object} PasswordPromptOptions
  2918. * @property {string} overlayName - Name of the overlay for the overlay manager.
  2919. * @property {HTMLDivElement} container - Div container for the overlay.
  2920. * @property {HTMLParagraphElement} label - Label containing instructions for
  2921. * entering the password.
  2922. * @property {HTMLInputElement} input - Input field for entering the password.
  2923. * @property {HTMLButtonElement} submitButton - Button for submitting the
  2924. * password.
  2925. * @property {HTMLButtonElement} cancelButton - Button for cancelling password
  2926. * entry.
  2927. */
  2928. /**
  2929. * @class
  2930. */
  2931. var PasswordPrompt = (function PasswordPromptClosure() {
  2932. /**
  2933. * @constructs PasswordPrompt
  2934. * @param {PasswordPromptOptions} options
  2935. */
  2936. function PasswordPrompt(options) {
  2937. this.overlayName = options.overlayName;
  2938. this.container = options.container;
  2939. this.label = options.label;
  2940. this.input = options.input;
  2941. this.submitButton = options.submitButton;
  2942. this.cancelButton = options.cancelButton;
  2943. this.updateCallback = null;
  2944. this.reason = null;
  2945. // Attach the event listeners.
  2946. this.submitButton.addEventListener('click', this.verify.bind(this));
  2947. this.cancelButton.addEventListener('click', this.close.bind(this));
  2948. this.input.addEventListener('keydown', function (e) {
  2949. if (e.keyCode === 13) { // Enter key
  2950. this.verify();
  2951. }
  2952. }.bind(this));
  2953. OverlayManager.register(this.overlayName, this.container,
  2954. this.close.bind(this), true);
  2955. }
  2956. PasswordPrompt.prototype = {
  2957. open: function PasswordPrompt_open() {
  2958. OverlayManager.open(this.overlayName).then(function () {
  2959. this.input.type = 'password';
  2960. this.input.focus();
  2961. var promptString = mozL10n.get('password_label', null,
  2962. 'Enter the password to open this PDF file.');
  2963. if (this.reason === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
  2964. promptString = mozL10n.get('password_invalid', null,
  2965. 'Invalid password. Please try again.');
  2966. }
  2967. this.label.textContent = promptString;
  2968. }.bind(this));
  2969. },
  2970. close: function PasswordPrompt_close() {
  2971. OverlayManager.close(this.overlayName).then(function () {
  2972. this.input.value = '';
  2973. this.input.type = '';
  2974. }.bind(this));
  2975. },
  2976. verify: function PasswordPrompt_verify() {
  2977. var password = this.input.value;
  2978. if (password && password.length > 0) {
  2979. this.close();
  2980. return this.updateCallback(password);
  2981. }
  2982. },
  2983. setUpdateCallback:
  2984. function PasswordPrompt_setUpdateCallback(updateCallback, reason) {
  2985. this.updateCallback = updateCallback;
  2986. this.reason = reason;
  2987. }
  2988. };
  2989. return PasswordPrompt;
  2990. })();
  2991. exports.PasswordPrompt = PasswordPrompt;
  2992. }));
  2993. (function (root, factory) {
  2994. {
  2995. factory((root.pdfjsWebPDFDocumentProperties = {}), root.pdfjsWebUIUtils,
  2996. root.pdfjsWebOverlayManager);
  2997. }
  2998. }(this, function (exports, uiUtils, overlayManager) {
  2999. var getPDFFileNameFromURL = uiUtils.getPDFFileNameFromURL;
  3000. var mozL10n = uiUtils.mozL10n;
  3001. var OverlayManager = overlayManager.OverlayManager;
  3002. /**
  3003. * @typedef {Object} PDFDocumentPropertiesOptions
  3004. * @property {string} overlayName - Name/identifier for the overlay.
  3005. * @property {Object} fields - Names and elements of the overlay's fields.
  3006. * @property {HTMLButtonElement} closeButton - Button for closing the overlay.
  3007. */
  3008. /**
  3009. * @class
  3010. */
  3011. var PDFDocumentProperties = (function PDFDocumentPropertiesClosure() {
  3012. /**
  3013. * @constructs PDFDocumentProperties
  3014. * @param {PDFDocumentPropertiesOptions} options
  3015. */
  3016. function PDFDocumentProperties(options) {
  3017. this.fields = options.fields;
  3018. this.overlayName = options.overlayName;
  3019. this.container = options.container;
  3020. this.rawFileSize = 0;
  3021. this.url = null;
  3022. this.pdfDocument = null;
  3023. // Bind the event listener for the Close button.
  3024. if (options.closeButton) {
  3025. options.closeButton.addEventListener('click', this.close.bind(this));
  3026. }
  3027. this.dataAvailablePromise = new Promise(function (resolve) {
  3028. this.resolveDataAvailable = resolve;
  3029. }.bind(this));
  3030. OverlayManager.register(this.overlayName, this.container,
  3031. this.close.bind(this));
  3032. }
  3033. PDFDocumentProperties.prototype = {
  3034. /**
  3035. * Open the document properties overlay.
  3036. */
  3037. open: function PDFDocumentProperties_open() {
  3038. Promise.all([OverlayManager.open(this.overlayName),
  3039. this.dataAvailablePromise]).then(function () {
  3040. this._getProperties();
  3041. }.bind(this));
  3042. },
  3043. /**
  3044. * Close the document properties overlay.
  3045. */
  3046. close: function PDFDocumentProperties_close() {
  3047. OverlayManager.close(this.overlayName);
  3048. },
  3049. /**
  3050. * Set the file size of the PDF document. This method is used to
  3051. * update the file size in the document properties overlay once it
  3052. * is known so we do not have to wait until the entire file is loaded.
  3053. *
  3054. * @param {number} fileSize - The file size of the PDF document.
  3055. */
  3056. setFileSize: function PDFDocumentProperties_setFileSize(fileSize) {
  3057. if (fileSize > 0) {
  3058. this.rawFileSize = fileSize;
  3059. }
  3060. },
  3061. /**
  3062. * Set a reference to the PDF document and the URL in order
  3063. * to populate the overlay fields with the document properties.
  3064. * Note that the overlay will contain no information if this method
  3065. * is not called.
  3066. *
  3067. * @param {Object} pdfDocument - A reference to the PDF document.
  3068. * @param {string} url - The URL of the document.
  3069. */
  3070. setDocumentAndUrl:
  3071. function PDFDocumentProperties_setDocumentAndUrl(pdfDocument, url) {
  3072. this.pdfDocument = pdfDocument;
  3073. this.url = url;
  3074. this.resolveDataAvailable();
  3075. },
  3076. /**
  3077. * @private
  3078. */
  3079. _getProperties: function PDFDocumentProperties_getProperties() {
  3080. if (!OverlayManager.active) {
  3081. // If the dialog was closed before dataAvailablePromise was resolved,
  3082. // don't bother updating the properties.
  3083. return;
  3084. }
  3085. // Get the file size (if it hasn't already been set).
  3086. this.pdfDocument.getDownloadInfo().then(function(data) {
  3087. if (data.length === this.rawFileSize) {
  3088. return;
  3089. }
  3090. this.setFileSize(data.length);
  3091. this._updateUI(this.fields['fileSize'], this._parseFileSize());
  3092. }.bind(this));
  3093. // Get the document properties.
  3094. this.pdfDocument.getMetadata().then(function(data) {
  3095. var content = {
  3096. 'fileName': getPDFFileNameFromURL(this.url),
  3097. 'fileSize': this._parseFileSize(),
  3098. 'title': data.info.Title,
  3099. 'author': data.info.Author,
  3100. 'subject': data.info.Subject,
  3101. 'keywords': data.info.Keywords,
  3102. 'creationDate': this._parseDate(data.info.CreationDate),
  3103. 'modificationDate': this._parseDate(data.info.ModDate),
  3104. 'creator': data.info.Creator,
  3105. 'producer': data.info.Producer,
  3106. 'version': data.info.PDFFormatVersion,
  3107. 'pageCount': this.pdfDocument.numPages
  3108. };
  3109. // Show the properties in the dialog.
  3110. for (var identifier in content) {
  3111. this._updateUI(this.fields[identifier], content[identifier]);
  3112. }
  3113. }.bind(this));
  3114. },
  3115. /**
  3116. * @private
  3117. */
  3118. _updateUI: function PDFDocumentProperties_updateUI(field, content) {
  3119. if (field && content !== undefined && content !== '') {
  3120. field.textContent = content;
  3121. }
  3122. },
  3123. /**
  3124. * @private
  3125. */
  3126. _parseFileSize: function PDFDocumentProperties_parseFileSize() {
  3127. var fileSize = this.rawFileSize, kb = fileSize / 1024;
  3128. if (!kb) {
  3129. return;
  3130. } else if (kb < 1024) {
  3131. return mozL10n.get('document_properties_kb', {
  3132. size_kb: (+kb.toPrecision(3)).toLocaleString(),
  3133. size_b: fileSize.toLocaleString()
  3134. }, '{{size_kb}} KB ({{size_b}} bytes)');
  3135. } else {
  3136. return mozL10n.get('document_properties_mb', {
  3137. size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(),
  3138. size_b: fileSize.toLocaleString()
  3139. }, '{{size_mb}} MB ({{size_b}} bytes)');
  3140. }
  3141. },
  3142. /**
  3143. * @private
  3144. */
  3145. _parseDate: function PDFDocumentProperties_parseDate(inputDate) {
  3146. // This is implemented according to the PDF specification, but note that
  3147. // Adobe Reader doesn't handle changing the date to universal time
  3148. // and doesn't use the user's time zone (they're effectively ignoring
  3149. // the HH' and mm' parts of the date string).
  3150. var dateToParse = inputDate;
  3151. if (dateToParse === undefined) {
  3152. return '';
  3153. }
  3154. // Remove the D: prefix if it is available.
  3155. if (dateToParse.substring(0,2) === 'D:') {
  3156. dateToParse = dateToParse.substring(2);
  3157. }
  3158. // Get all elements from the PDF date string.
  3159. // JavaScript's Date object expects the month to be between
  3160. // 0 and 11 instead of 1 and 12, so we're correcting for this.
  3161. var year = parseInt(dateToParse.substring(0,4), 10);
  3162. var month = parseInt(dateToParse.substring(4,6), 10) - 1;
  3163. var day = parseInt(dateToParse.substring(6,8), 10);
  3164. var hours = parseInt(dateToParse.substring(8,10), 10);
  3165. var minutes = parseInt(dateToParse.substring(10,12), 10);
  3166. var seconds = parseInt(dateToParse.substring(12,14), 10);
  3167. var utRel = dateToParse.substring(14,15);
  3168. var offsetHours = parseInt(dateToParse.substring(15,17), 10);
  3169. var offsetMinutes = parseInt(dateToParse.substring(18,20), 10);
  3170. // As per spec, utRel = 'Z' means equal to universal time.
  3171. // The other cases ('-' and '+') have to be handled here.
  3172. if (utRel === '-') {
  3173. hours += offsetHours;
  3174. minutes += offsetMinutes;
  3175. } else if (utRel === '+') {
  3176. hours -= offsetHours;
  3177. minutes -= offsetMinutes;
  3178. }
  3179. // Return the new date format from the user's locale.
  3180. var date = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
  3181. var dateString = date.toLocaleDateString();
  3182. var timeString = date.toLocaleTimeString();
  3183. return mozL10n.get('document_properties_date_string',
  3184. {date: dateString, time: timeString},
  3185. '{{date}}, {{time}}');
  3186. }
  3187. };
  3188. return PDFDocumentProperties;
  3189. })();
  3190. exports.PDFDocumentProperties = PDFDocumentProperties;
  3191. }));
  3192. (function (root, factory) {
  3193. {
  3194. factory((root.pdfjsWebPDFFindController = {}), root.pdfjsWebUIUtils,
  3195. root.pdfjsWebFirefoxCom);
  3196. }
  3197. }(this, function (exports, uiUtils, firefoxCom) {
  3198. var scrollIntoView = uiUtils.scrollIntoView;
  3199. var FirefoxCom = firefoxCom.FirefoxCom;
  3200. var FindStates = {
  3201. FIND_FOUND: 0,
  3202. FIND_NOTFOUND: 1,
  3203. FIND_WRAPPED: 2,
  3204. FIND_PENDING: 3
  3205. };
  3206. var FIND_SCROLL_OFFSET_TOP = -50;
  3207. var FIND_SCROLL_OFFSET_LEFT = -400;
  3208. var CHARACTERS_TO_NORMALIZE = {
  3209. '\u2018': '\'', // Left single quotation mark
  3210. '\u2019': '\'', // Right single quotation mark
  3211. '\u201A': '\'', // Single low-9 quotation mark
  3212. '\u201B': '\'', // Single high-reversed-9 quotation mark
  3213. '\u201C': '"', // Left double quotation mark
  3214. '\u201D': '"', // Right double quotation mark
  3215. '\u201E': '"', // Double low-9 quotation mark
  3216. '\u201F': '"', // Double high-reversed-9 quotation mark
  3217. '\u00BC': '1/4', // Vulgar fraction one quarter
  3218. '\u00BD': '1/2', // Vulgar fraction one half
  3219. '\u00BE': '3/4', // Vulgar fraction three quarters
  3220. };
  3221. /**
  3222. * Provides "search" or "find" functionality for the PDF.
  3223. * This object actually performs the search for a given string.
  3224. */
  3225. var PDFFindController = (function PDFFindControllerClosure() {
  3226. function PDFFindController(options) {
  3227. this.pdfViewer = options.pdfViewer || null;
  3228. this.integratedFind = options.integratedFind || false;
  3229. this.findBar = options.findBar || null;
  3230. this.reset();
  3231. // Compile the regular expression for text normalization once.
  3232. var replace = Object.keys(CHARACTERS_TO_NORMALIZE).join('');
  3233. this.normalizationRegex = new RegExp('[' + replace + ']', 'g');
  3234. var events = [
  3235. 'find',
  3236. 'findagain',
  3237. 'findhighlightallchange',
  3238. 'findcasesensitivitychange'
  3239. ];
  3240. this.handleEvent = this.handleEvent.bind(this);
  3241. for (var i = 0, len = events.length; i < len; i++) {
  3242. window.addEventListener(events[i], this.handleEvent);
  3243. }
  3244. }
  3245. PDFFindController.prototype = {
  3246. setFindBar: function PDFFindController_setFindBar(findBar) {
  3247. this.findBar = findBar;
  3248. },
  3249. reset: function PDFFindController_reset() {
  3250. this.startedTextExtraction = false;
  3251. this.extractTextPromises = [];
  3252. this.pendingFindMatches = Object.create(null);
  3253. this.active = false; // If active, find results will be highlighted.
  3254. this.pageContents = []; // Stores the text for each page.
  3255. this.pageMatches = [];
  3256. this.matchCount = 0;
  3257. this.selected = { // Currently selected match.
  3258. pageIdx: -1,
  3259. matchIdx: -1
  3260. };
  3261. this.offset = { // Where the find algorithm currently is in the document.
  3262. pageIdx: null,
  3263. matchIdx: null
  3264. };
  3265. this.pagesToSearch = null;
  3266. this.resumePageIdx = null;
  3267. this.state = null;
  3268. this.dirtyMatch = false;
  3269. this.findTimeout = null;
  3270. this.firstPagePromise = new Promise(function (resolve) {
  3271. this.resolveFirstPage = resolve;
  3272. }.bind(this));
  3273. },
  3274. normalize: function PDFFindController_normalize(text) {
  3275. return text.replace(this.normalizationRegex, function (ch) {
  3276. return CHARACTERS_TO_NORMALIZE[ch];
  3277. });
  3278. },
  3279. calcFindMatch: function PDFFindController_calcFindMatch(pageIndex) {
  3280. var pageContent = this.normalize(this.pageContents[pageIndex]);
  3281. var query = this.normalize(this.state.query);
  3282. var caseSensitive = this.state.caseSensitive;
  3283. var queryLen = query.length;
  3284. if (queryLen === 0) {
  3285. // Do nothing: the matches should be wiped out already.
  3286. return;
  3287. }
  3288. if (!caseSensitive) {
  3289. pageContent = pageContent.toLowerCase();
  3290. query = query.toLowerCase();
  3291. }
  3292. var matches = [];
  3293. var matchIdx = -queryLen;
  3294. while (true) {
  3295. matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
  3296. if (matchIdx === -1) {
  3297. break;
  3298. }
  3299. matches.push(matchIdx);
  3300. }
  3301. this.pageMatches[pageIndex] = matches;
  3302. this.updatePage(pageIndex);
  3303. if (this.resumePageIdx === pageIndex) {
  3304. this.resumePageIdx = null;
  3305. this.nextPageMatch();
  3306. }
  3307. // Update the matches count
  3308. if (matches.length > 0) {
  3309. this.matchCount += matches.length;
  3310. this.updateUIResultsCount();
  3311. }
  3312. },
  3313. extractText: function PDFFindController_extractText() {
  3314. if (this.startedTextExtraction) {
  3315. return;
  3316. }
  3317. this.startedTextExtraction = true;
  3318. this.pageContents = [];
  3319. var extractTextPromisesResolves = [];
  3320. var numPages = this.pdfViewer.pagesCount;
  3321. for (var i = 0; i < numPages; i++) {
  3322. this.extractTextPromises.push(new Promise(function (resolve) {
  3323. extractTextPromisesResolves.push(resolve);
  3324. }));
  3325. }
  3326. var self = this;
  3327. function extractPageText(pageIndex) {
  3328. self.pdfViewer.getPageTextContent(pageIndex).then(
  3329. function textContentResolved(textContent) {
  3330. var textItems = textContent.items;
  3331. var str = [];
  3332. for (var i = 0, len = textItems.length; i < len; i++) {
  3333. str.push(textItems[i].str);
  3334. }
  3335. // Store the pageContent as a string.
  3336. self.pageContents.push(str.join(''));
  3337. extractTextPromisesResolves[pageIndex](pageIndex);
  3338. if ((pageIndex + 1) < self.pdfViewer.pagesCount) {
  3339. extractPageText(pageIndex + 1);
  3340. }
  3341. }
  3342. );
  3343. }
  3344. extractPageText(0);
  3345. },
  3346. handleEvent: function PDFFindController_handleEvent(e) {
  3347. if (this.state === null || e.type !== 'findagain') {
  3348. this.dirtyMatch = true;
  3349. }
  3350. this.state = e.detail;
  3351. this.updateUIState(FindStates.FIND_PENDING);
  3352. this.firstPagePromise.then(function() {
  3353. this.extractText();
  3354. clearTimeout(this.findTimeout);
  3355. if (e.type === 'find') {
  3356. // Only trigger the find action after 250ms of silence.
  3357. this.findTimeout = setTimeout(this.nextMatch.bind(this), 250);
  3358. } else {
  3359. this.nextMatch();
  3360. }
  3361. }.bind(this));
  3362. },
  3363. updatePage: function PDFFindController_updatePage(index) {
  3364. if (this.selected.pageIdx === index) {
  3365. // If the page is selected, scroll the page into view, which triggers
  3366. // rendering the page, which adds the textLayer. Once the textLayer is
  3367. // build, it will scroll onto the selected match.
  3368. this.pdfViewer.scrollPageIntoView(index + 1);
  3369. }
  3370. var page = this.pdfViewer.getPageView(index);
  3371. if (page.textLayer) {
  3372. page.textLayer.updateMatches();
  3373. }
  3374. },
  3375. nextMatch: function PDFFindController_nextMatch() {
  3376. var previous = this.state.findPrevious;
  3377. var currentPageIndex = this.pdfViewer.currentPageNumber - 1;
  3378. var numPages = this.pdfViewer.pagesCount;
  3379. this.active = true;
  3380. if (this.dirtyMatch) {
  3381. // Need to recalculate the matches, reset everything.
  3382. this.dirtyMatch = false;
  3383. this.selected.pageIdx = this.selected.matchIdx = -1;
  3384. this.offset.pageIdx = currentPageIndex;
  3385. this.offset.matchIdx = null;
  3386. this.hadMatch = false;
  3387. this.resumePageIdx = null;
  3388. this.pageMatches = [];
  3389. this.matchCount = 0;
  3390. var self = this;
  3391. for (var i = 0; i < numPages; i++) {
  3392. // Wipe out any previous highlighted matches.
  3393. this.updatePage(i);
  3394. // As soon as the text is extracted start finding the matches.
  3395. if (!(i in this.pendingFindMatches)) {
  3396. this.pendingFindMatches[i] = true;
  3397. this.extractTextPromises[i].then(function(pageIdx) {
  3398. delete self.pendingFindMatches[pageIdx];
  3399. self.calcFindMatch(pageIdx);
  3400. });
  3401. }
  3402. }
  3403. }
  3404. // If there's no query there's no point in searching.
  3405. if (this.state.query === '') {
  3406. this.updateUIState(FindStates.FIND_FOUND);
  3407. return;
  3408. }
  3409. // If we're waiting on a page, we return since we can't do anything else.
  3410. if (this.resumePageIdx) {
  3411. return;
  3412. }
  3413. var offset = this.offset;
  3414. // Keep track of how many pages we should maximally iterate through.
  3415. this.pagesToSearch = numPages;
  3416. // If there's already a matchIdx that means we are iterating through a
  3417. // page's matches.
  3418. if (offset.matchIdx !== null) {
  3419. var numPageMatches = this.pageMatches[offset.pageIdx].length;
  3420. if ((!previous && offset.matchIdx + 1 < numPageMatches) ||
  3421. (previous && offset.matchIdx > 0)) {
  3422. // The simple case; we just have advance the matchIdx to select
  3423. // the next match on the page.
  3424. this.hadMatch = true;
  3425. offset.matchIdx = (previous ? offset.matchIdx - 1 :
  3426. offset.matchIdx + 1);
  3427. this.updateMatch(true);
  3428. return;
  3429. }
  3430. // We went beyond the current page's matches, so we advance to
  3431. // the next page.
  3432. this.advanceOffsetPage(previous);
  3433. }
  3434. // Start searching through the page.
  3435. this.nextPageMatch();
  3436. },
  3437. matchesReady: function PDFFindController_matchesReady(matches) {
  3438. var offset = this.offset;
  3439. var numMatches = matches.length;
  3440. var previous = this.state.findPrevious;
  3441. if (numMatches) {
  3442. // There were matches for the page, so initialize the matchIdx.
  3443. this.hadMatch = true;
  3444. offset.matchIdx = (previous ? numMatches - 1 : 0);
  3445. this.updateMatch(true);
  3446. return true;
  3447. } else {
  3448. // No matches, so attempt to search the next page.
  3449. this.advanceOffsetPage(previous);
  3450. if (offset.wrapped) {
  3451. offset.matchIdx = null;
  3452. if (this.pagesToSearch < 0) {
  3453. // No point in wrapping again, there were no matches.
  3454. this.updateMatch(false);
  3455. // while matches were not found, searching for a page
  3456. // with matches should nevertheless halt.
  3457. return true;
  3458. }
  3459. }
  3460. // Matches were not found (and searching is not done).
  3461. return false;
  3462. }
  3463. },
  3464. /**
  3465. * The method is called back from the text layer when match presentation
  3466. * is updated.
  3467. * @param {number} pageIndex - page index.
  3468. * @param {number} index - match index.
  3469. * @param {Array} elements - text layer div elements array.
  3470. * @param {number} beginIdx - start index of the div array for the match.
  3471. * @param {number} endIdx - end index of the div array for the match.
  3472. */
  3473. updateMatchPosition: function PDFFindController_updateMatchPosition(
  3474. pageIndex, index, elements, beginIdx, endIdx) {
  3475. if (this.selected.matchIdx === index &&
  3476. this.selected.pageIdx === pageIndex) {
  3477. var spot = {
  3478. top: FIND_SCROLL_OFFSET_TOP,
  3479. left: FIND_SCROLL_OFFSET_LEFT
  3480. };
  3481. scrollIntoView(elements[beginIdx], spot,
  3482. /* skipOverflowHiddenElements = */ true);
  3483. }
  3484. },
  3485. nextPageMatch: function PDFFindController_nextPageMatch() {
  3486. if (this.resumePageIdx !== null) {
  3487. console.error('There can only be one pending page.');
  3488. }
  3489. do {
  3490. var pageIdx = this.offset.pageIdx;
  3491. var matches = this.pageMatches[pageIdx];
  3492. if (!matches) {
  3493. // The matches don't exist yet for processing by "matchesReady",
  3494. // so set a resume point for when they do exist.
  3495. this.resumePageIdx = pageIdx;
  3496. break;
  3497. }
  3498. } while (!this.matchesReady(matches));
  3499. },
  3500. advanceOffsetPage: function PDFFindController_advanceOffsetPage(previous) {
  3501. var offset = this.offset;
  3502. var numPages = this.extractTextPromises.length;
  3503. offset.pageIdx = (previous ? offset.pageIdx - 1 : offset.pageIdx + 1);
  3504. offset.matchIdx = null;
  3505. this.pagesToSearch--;
  3506. if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
  3507. offset.pageIdx = (previous ? numPages - 1 : 0);
  3508. offset.wrapped = true;
  3509. }
  3510. },
  3511. updateMatch: function PDFFindController_updateMatch(found) {
  3512. var state = FindStates.FIND_NOTFOUND;
  3513. var wrapped = this.offset.wrapped;
  3514. this.offset.wrapped = false;
  3515. if (found) {
  3516. var previousPage = this.selected.pageIdx;
  3517. this.selected.pageIdx = this.offset.pageIdx;
  3518. this.selected.matchIdx = this.offset.matchIdx;
  3519. state = (wrapped ? FindStates.FIND_WRAPPED : FindStates.FIND_FOUND);
  3520. // Update the currently selected page to wipe out any selected matches.
  3521. if (previousPage !== -1 && previousPage !== this.selected.pageIdx) {
  3522. this.updatePage(previousPage);
  3523. }
  3524. }
  3525. this.updateUIState(state, this.state.findPrevious);
  3526. if (this.selected.pageIdx !== -1) {
  3527. this.updatePage(this.selected.pageIdx);
  3528. }
  3529. },
  3530. updateUIResultsCount:
  3531. function PDFFindController_updateUIResultsCount() {
  3532. if (this.findBar === null) {
  3533. throw new Error('PDFFindController is not initialized with a ' +
  3534. 'PDFFindBar instance.');
  3535. }
  3536. this.findBar.updateResultsCount(this.matchCount);
  3537. },
  3538. updateUIState: function PDFFindController_updateUIState(state, previous) {
  3539. if (this.integratedFind) {
  3540. FirefoxCom.request('updateFindControlState',
  3541. { result: state, findPrevious: previous });
  3542. return;
  3543. }
  3544. if (this.findBar === null) {
  3545. throw new Error('PDFFindController is not initialized with a ' +
  3546. 'PDFFindBar instance.');
  3547. }
  3548. this.findBar.updateUIState(state, previous, this.matchCount);
  3549. }
  3550. };
  3551. return PDFFindController;
  3552. })();
  3553. exports.FindStates = FindStates;
  3554. exports.PDFFindController = PDFFindController;
  3555. }));
  3556. (function (root, factory) {
  3557. {
  3558. factory((root.pdfjsWebPDFLinkService = {}), root.pdfjsWebUIUtils);
  3559. }
  3560. }(this, function (exports, uiUtils) {
  3561. var parseQueryString = uiUtils.parseQueryString;
  3562. /**
  3563. * Performs navigation functions inside PDF, such as opening specified page,
  3564. * or destination.
  3565. * @class
  3566. * @implements {IPDFLinkService}
  3567. */
  3568. var PDFLinkService = (function () {
  3569. /**
  3570. * @constructs PDFLinkService
  3571. */
  3572. function PDFLinkService() {
  3573. this.baseUrl = null;
  3574. this.pdfDocument = null;
  3575. this.pdfViewer = null;
  3576. this.pdfHistory = null;
  3577. this._pagesRefCache = null;
  3578. }
  3579. PDFLinkService.prototype = {
  3580. setDocument: function PDFLinkService_setDocument(pdfDocument, baseUrl) {
  3581. this.baseUrl = baseUrl;
  3582. this.pdfDocument = pdfDocument;
  3583. this._pagesRefCache = Object.create(null);
  3584. },
  3585. setViewer: function PDFLinkService_setViewer(pdfViewer) {
  3586. this.pdfViewer = pdfViewer;
  3587. },
  3588. setHistory: function PDFLinkService_setHistory(pdfHistory) {
  3589. this.pdfHistory = pdfHistory;
  3590. },
  3591. /**
  3592. * @returns {number}
  3593. */
  3594. get pagesCount() {
  3595. return this.pdfDocument.numPages;
  3596. },
  3597. /**
  3598. * @returns {number}
  3599. */
  3600. get page() {
  3601. return this.pdfViewer.currentPageNumber;
  3602. },
  3603. /**
  3604. * @param {number} value
  3605. */
  3606. set page(value) {
  3607. this.pdfViewer.currentPageNumber = value;
  3608. },
  3609. /**
  3610. * @param dest - The PDF destination object.
  3611. */
  3612. navigateTo: function PDFLinkService_navigateTo(dest) {
  3613. var destString = '';
  3614. var self = this;
  3615. var goToDestination = function(destRef) {
  3616. // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
  3617. var pageNumber = destRef instanceof Object ?
  3618. self._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
  3619. (destRef + 1);
  3620. if (pageNumber) {
  3621. if (pageNumber > self.pagesCount) {
  3622. pageNumber = self.pagesCount;
  3623. }
  3624. self.pdfViewer.scrollPageIntoView(pageNumber, dest);
  3625. if (self.pdfHistory) {
  3626. // Update the browsing history.
  3627. self.pdfHistory.push({
  3628. dest: dest,
  3629. hash: destString,
  3630. page: pageNumber
  3631. });
  3632. }
  3633. } else {
  3634. self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) {
  3635. var pageNum = pageIndex + 1;
  3636. var cacheKey = destRef.num + ' ' + destRef.gen + ' R';
  3637. self._pagesRefCache[cacheKey] = pageNum;
  3638. goToDestination(destRef);
  3639. });
  3640. }
  3641. };
  3642. var destinationPromise;
  3643. if (typeof dest === 'string') {
  3644. destString = dest;
  3645. destinationPromise = this.pdfDocument.getDestination(dest);
  3646. } else {
  3647. destinationPromise = Promise.resolve(dest);
  3648. }
  3649. destinationPromise.then(function(destination) {
  3650. dest = destination;
  3651. if (!(destination instanceof Array)) {
  3652. return; // invalid destination
  3653. }
  3654. goToDestination(destination[0]);
  3655. });
  3656. },
  3657. /**
  3658. * @param dest - The PDF destination object.
  3659. * @returns {string} The hyperlink to the PDF object.
  3660. */
  3661. getDestinationHash: function PDFLinkService_getDestinationHash(dest) {
  3662. if (typeof dest === 'string') {
  3663. return this.getAnchorUrl('#' + escape(dest));
  3664. }
  3665. if (dest instanceof Array) {
  3666. var destRef = dest[0]; // see navigateTo method for dest format
  3667. var pageNumber = destRef instanceof Object ?
  3668. this._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
  3669. (destRef + 1);
  3670. if (pageNumber) {
  3671. var pdfOpenParams = this.getAnchorUrl('#page=' + pageNumber);
  3672. var destKind = dest[1];
  3673. if (typeof destKind === 'object' && 'name' in destKind &&
  3674. destKind.name === 'XYZ') {
  3675. var scale = (dest[4] || this.pdfViewer.currentScaleValue);
  3676. var scaleNumber = parseFloat(scale);
  3677. if (scaleNumber) {
  3678. scale = scaleNumber * 100;
  3679. }
  3680. pdfOpenParams += '&zoom=' + scale;
  3681. if (dest[2] || dest[3]) {
  3682. pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
  3683. }
  3684. }
  3685. return pdfOpenParams;
  3686. }
  3687. }
  3688. return this.getAnchorUrl('');
  3689. },
  3690. /**
  3691. * Prefix the full url on anchor links to make sure that links are resolved
  3692. * relative to the current URL instead of the one defined in <base href>.
  3693. * @param {String} anchor The anchor hash, including the #.
  3694. * @returns {string} The hyperlink to the PDF object.
  3695. */
  3696. getAnchorUrl: function PDFLinkService_getAnchorUrl(anchor) {
  3697. return (this.baseUrl || '') + anchor;
  3698. },
  3699. /**
  3700. * @param {string} hash
  3701. */
  3702. setHash: function PDFLinkService_setHash(hash) {
  3703. if (hash.indexOf('=') >= 0) {
  3704. var params = parseQueryString(hash);
  3705. // borrowing syntax from "Parameters for Opening PDF Files"
  3706. if ('nameddest' in params) {
  3707. if (this.pdfHistory) {
  3708. this.pdfHistory.updateNextHashParam(params.nameddest);
  3709. }
  3710. this.navigateTo(params.nameddest);
  3711. return;
  3712. }
  3713. var pageNumber, dest;
  3714. if ('page' in params) {
  3715. pageNumber = (params.page | 0) || 1;
  3716. }
  3717. if ('zoom' in params) {
  3718. // Build the destination array.
  3719. var zoomArgs = params.zoom.split(','); // scale,left,top
  3720. var zoomArg = zoomArgs[0];
  3721. var zoomArgNumber = parseFloat(zoomArg);
  3722. if (zoomArg.indexOf('Fit') === -1) {
  3723. // If the zoomArg is a number, it has to get divided by 100. If it's
  3724. // a string, it should stay as it is.
  3725. dest = [null, { name: 'XYZ' },
  3726. zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
  3727. zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
  3728. (zoomArgNumber ? zoomArgNumber / 100 : zoomArg)];
  3729. } else {
  3730. if (zoomArg === 'Fit' || zoomArg === 'FitB') {
  3731. dest = [null, { name: zoomArg }];
  3732. } else if ((zoomArg === 'FitH' || zoomArg === 'FitBH') ||
  3733. (zoomArg === 'FitV' || zoomArg === 'FitBV')) {
  3734. dest = [null, { name: zoomArg },
  3735. zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null];
  3736. } else if (zoomArg === 'FitR') {
  3737. if (zoomArgs.length !== 5) {
  3738. console.error('PDFLinkService_setHash: ' +
  3739. 'Not enough parameters for \'FitR\'.');
  3740. } else {
  3741. dest = [null, { name: zoomArg },
  3742. (zoomArgs[1] | 0), (zoomArgs[2] | 0),
  3743. (zoomArgs[3] | 0), (zoomArgs[4] | 0)];
  3744. }
  3745. } else {
  3746. console.error('PDFLinkService_setHash: \'' + zoomArg +
  3747. '\' is not a valid zoom value.');
  3748. }
  3749. }
  3750. }
  3751. if (dest) {
  3752. this.pdfViewer.scrollPageIntoView(pageNumber || this.page, dest);
  3753. } else if (pageNumber) {
  3754. this.page = pageNumber; // simple page
  3755. }
  3756. if ('pagemode' in params) {
  3757. var event = document.createEvent('CustomEvent');
  3758. event.initCustomEvent('pagemode', true, true, {
  3759. mode: params.pagemode,
  3760. });
  3761. this.pdfViewer.container.dispatchEvent(event);
  3762. }
  3763. } else if (/^\d+$/.test(hash)) { // page number
  3764. this.page = hash;
  3765. } else { // named destination
  3766. if (this.pdfHistory) {
  3767. this.pdfHistory.updateNextHashParam(unescape(hash));
  3768. }
  3769. this.navigateTo(unescape(hash));
  3770. }
  3771. },
  3772. /**
  3773. * @param {string} action
  3774. */
  3775. executeNamedAction: function PDFLinkService_executeNamedAction(action) {
  3776. // See PDF reference, table 8.45 - Named action
  3777. switch (action) {
  3778. case 'GoBack':
  3779. if (this.pdfHistory) {
  3780. this.pdfHistory.back();
  3781. }
  3782. break;
  3783. case 'GoForward':
  3784. if (this.pdfHistory) {
  3785. this.pdfHistory.forward();
  3786. }
  3787. break;
  3788. case 'NextPage':
  3789. this.page++;
  3790. break;
  3791. case 'PrevPage':
  3792. this.page--;
  3793. break;
  3794. case 'LastPage':
  3795. this.page = this.pagesCount;
  3796. break;
  3797. case 'FirstPage':
  3798. this.page = 1;
  3799. break;
  3800. default:
  3801. break; // No action according to spec
  3802. }
  3803. var event = document.createEvent('CustomEvent');
  3804. event.initCustomEvent('namedaction', true, true, {
  3805. action: action
  3806. });
  3807. this.pdfViewer.container.dispatchEvent(event);
  3808. },
  3809. /**
  3810. * @param {number} pageNum - page number.
  3811. * @param {Object} pageRef - reference to the page.
  3812. */
  3813. cachePageRef: function PDFLinkService_cachePageRef(pageNum, pageRef) {
  3814. var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
  3815. this._pagesRefCache[refStr] = pageNum;
  3816. }
  3817. };
  3818. return PDFLinkService;
  3819. })();
  3820. var SimpleLinkService = (function SimpleLinkServiceClosure() {
  3821. function SimpleLinkService() {}
  3822. SimpleLinkService.prototype = {
  3823. /**
  3824. * @returns {number}
  3825. */
  3826. get page() {
  3827. return 0;
  3828. },
  3829. /**
  3830. * @param {number} value
  3831. */
  3832. set page(value) {},
  3833. /**
  3834. * @param dest - The PDF destination object.
  3835. */
  3836. navigateTo: function (dest) {},
  3837. /**
  3838. * @param dest - The PDF destination object.
  3839. * @returns {string} The hyperlink to the PDF object.
  3840. */
  3841. getDestinationHash: function (dest) {
  3842. return '#';
  3843. },
  3844. /**
  3845. * @param hash - The PDF parameters/hash.
  3846. * @returns {string} The hyperlink to the PDF object.
  3847. */
  3848. getAnchorUrl: function (hash) {
  3849. return '#';
  3850. },
  3851. /**
  3852. * @param {string} hash
  3853. */
  3854. setHash: function (hash) {},
  3855. /**
  3856. * @param {string} action
  3857. */
  3858. executeNamedAction: function (action) {},
  3859. /**
  3860. * @param {number} pageNum - page number.
  3861. * @param {Object} pageRef - reference to the page.
  3862. */
  3863. cachePageRef: function (pageNum, pageRef) {}
  3864. };
  3865. return SimpleLinkService;
  3866. })();
  3867. exports.PDFLinkService = PDFLinkService;
  3868. exports.SimpleLinkService = SimpleLinkService;
  3869. }));
  3870. (function (root, factory) {
  3871. {
  3872. factory((root.pdfjsWebPDFPageView = {}), root.pdfjsWebUIUtils,
  3873. root.pdfjsWebPDFRenderingQueue, root.pdfjsWebPDFJS);
  3874. }
  3875. }(this, function (exports, uiUtils, pdfRenderingQueue, pdfjsLib) {
  3876. var CSS_UNITS = uiUtils.CSS_UNITS;
  3877. var DEFAULT_SCALE = uiUtils.DEFAULT_SCALE;
  3878. var getOutputScale = uiUtils.getOutputScale;
  3879. var approximateFraction = uiUtils.approximateFraction;
  3880. var roundToDivide = uiUtils.roundToDivide;
  3881. var RenderingStates = pdfRenderingQueue.RenderingStates;
  3882. var TEXT_LAYER_RENDER_DELAY = 200; // ms
  3883. /**
  3884. * @typedef {Object} PDFPageViewOptions
  3885. * @property {HTMLDivElement} container - The viewer element.
  3886. * @property {number} id - The page unique ID (normally its number).
  3887. * @property {number} scale - The page scale display.
  3888. * @property {PageViewport} defaultViewport - The page viewport.
  3889. * @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
  3890. * @property {IPDFTextLayerFactory} textLayerFactory
  3891. * @property {IPDFAnnotationLayerFactory} annotationLayerFactory
  3892. */
  3893. /**
  3894. * @class
  3895. * @implements {IRenderableView}
  3896. */
  3897. var PDFPageView = (function PDFPageViewClosure() {
  3898. /**
  3899. * @constructs PDFPageView
  3900. * @param {PDFPageViewOptions} options
  3901. */
  3902. function PDFPageView(options) {
  3903. var container = options.container;
  3904. var id = options.id;
  3905. var scale = options.scale;
  3906. var defaultViewport = options.defaultViewport;
  3907. var renderingQueue = options.renderingQueue;
  3908. var textLayerFactory = options.textLayerFactory;
  3909. var annotationLayerFactory = options.annotationLayerFactory;
  3910. this.id = id;
  3911. this.renderingId = 'page' + id;
  3912. this.rotation = 0;
  3913. this.scale = scale || DEFAULT_SCALE;
  3914. this.viewport = defaultViewport;
  3915. this.pdfPageRotate = defaultViewport.rotation;
  3916. this.hasRestrictedScaling = false;
  3917. this.renderingQueue = renderingQueue;
  3918. this.textLayerFactory = textLayerFactory;
  3919. this.annotationLayerFactory = annotationLayerFactory;
  3920. this.renderingState = RenderingStates.INITIAL;
  3921. this.resume = null;
  3922. this.onBeforeDraw = null;
  3923. this.onAfterDraw = null;
  3924. this.textLayer = null;
  3925. this.zoomLayer = null;
  3926. this.annotationLayer = null;
  3927. var div = document.createElement('div');
  3928. div.id = 'pageContainer' + this.id;
  3929. div.className = 'page';
  3930. div.style.width = Math.floor(this.viewport.width) + 'px';
  3931. div.style.height = Math.floor(this.viewport.height) + 'px';
  3932. div.setAttribute('data-page-number', this.id);
  3933. this.div = div;
  3934. container.appendChild(div);
  3935. }
  3936. PDFPageView.prototype = {
  3937. setPdfPage: function PDFPageView_setPdfPage(pdfPage) {
  3938. this.pdfPage = pdfPage;
  3939. this.pdfPageRotate = pdfPage.rotate;
  3940. var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
  3941. this.viewport = pdfPage.getViewport(this.scale * CSS_UNITS,
  3942. totalRotation);
  3943. this.stats = pdfPage.stats;
  3944. this.reset();
  3945. },
  3946. destroy: function PDFPageView_destroy() {
  3947. this.zoomLayer = null;
  3948. this.reset();
  3949. if (this.pdfPage) {
  3950. this.pdfPage.cleanup();
  3951. }
  3952. },
  3953. reset: function PDFPageView_reset(keepZoomLayer, keepAnnotations) {
  3954. if (this.renderTask) {
  3955. this.renderTask.cancel();
  3956. }
  3957. this.resume = null;
  3958. this.renderingState = RenderingStates.INITIAL;
  3959. var div = this.div;
  3960. div.style.width = Math.floor(this.viewport.width) + 'px';
  3961. div.style.height = Math.floor(this.viewport.height) + 'px';
  3962. var childNodes = div.childNodes;
  3963. var currentZoomLayerNode = (keepZoomLayer && this.zoomLayer) || null;
  3964. var currentAnnotationNode = (keepAnnotations && this.annotationLayer &&
  3965. this.annotationLayer.div) || null;
  3966. for (var i = childNodes.length - 1; i >= 0; i--) {
  3967. var node = childNodes[i];
  3968. if (currentZoomLayerNode === node || currentAnnotationNode === node) {
  3969. continue;
  3970. }
  3971. div.removeChild(node);
  3972. }
  3973. div.removeAttribute('data-loaded');
  3974. if (currentAnnotationNode) {
  3975. // Hide annotationLayer until all elements are resized
  3976. // so they are not displayed on the already-resized page
  3977. this.annotationLayer.hide();
  3978. } else {
  3979. this.annotationLayer = null;
  3980. }
  3981. if (this.canvas && !currentZoomLayerNode) {
  3982. // Zeroing the width and height causes Firefox to release graphics
  3983. // resources immediately, which can greatly reduce memory consumption.
  3984. this.canvas.width = 0;
  3985. this.canvas.height = 0;
  3986. delete this.canvas;
  3987. }
  3988. this.loadingIconDiv = document.createElement('div');
  3989. this.loadingIconDiv.className = 'loadingIcon';
  3990. div.appendChild(this.loadingIconDiv);
  3991. },
  3992. update: function PDFPageView_update(scale, rotation) {
  3993. this.scale = scale || this.scale;
  3994. if (typeof rotation !== 'undefined') {
  3995. this.rotation = rotation;
  3996. }
  3997. var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
  3998. this.viewport = this.viewport.clone({
  3999. scale: this.scale * CSS_UNITS,
  4000. rotation: totalRotation
  4001. });
  4002. var isScalingRestricted = false;
  4003. if (this.canvas && pdfjsLib.PDFJS.maxCanvasPixels > 0) {
  4004. var outputScale = this.outputScale;
  4005. var pixelsInViewport = this.viewport.width * this.viewport.height;
  4006. if (((Math.floor(this.viewport.width) * outputScale.sx) | 0) *
  4007. ((Math.floor(this.viewport.height) * outputScale.sy) | 0) >
  4008. pdfjsLib.PDFJS.maxCanvasPixels) {
  4009. isScalingRestricted = true;
  4010. }
  4011. }
  4012. if (this.canvas) {
  4013. if (pdfjsLib.PDFJS.useOnlyCssZoom ||
  4014. (this.hasRestrictedScaling && isScalingRestricted)) {
  4015. this.cssTransform(this.canvas, true);
  4016. var event = document.createEvent('CustomEvent');
  4017. event.initCustomEvent('pagerendered', true, true, {
  4018. pageNumber: this.id,
  4019. cssTransform: true,
  4020. });
  4021. this.div.dispatchEvent(event);
  4022. return;
  4023. }
  4024. if (!this.zoomLayer) {
  4025. this.zoomLayer = this.canvas.parentNode;
  4026. this.zoomLayer.style.position = 'absolute';
  4027. }
  4028. }
  4029. if (this.zoomLayer) {
  4030. this.cssTransform(this.zoomLayer.firstChild);
  4031. }
  4032. this.reset(/* keepZoomLayer = */ true, /* keepAnnotations = */ true);
  4033. },
  4034. /**
  4035. * Called when moved in the parent's container.
  4036. */
  4037. updatePosition: function PDFPageView_updatePosition() {
  4038. if (this.textLayer) {
  4039. this.textLayer.render(TEXT_LAYER_RENDER_DELAY);
  4040. }
  4041. },
  4042. cssTransform: function PDFPageView_transform(canvas, redrawAnnotations) {
  4043. var CustomStyle = pdfjsLib.CustomStyle;
  4044. // Scale canvas, canvas wrapper, and page container.
  4045. var width = this.viewport.width;
  4046. var height = this.viewport.height;
  4047. var div = this.div;
  4048. canvas.style.width = canvas.parentNode.style.width = div.style.width =
  4049. Math.floor(width) + 'px';
  4050. canvas.style.height = canvas.parentNode.style.height = div.style.height =
  4051. Math.floor(height) + 'px';
  4052. // The canvas may have been originally rotated, rotate relative to that.
  4053. var relativeRotation = this.viewport.rotation - canvas._viewport.rotation;
  4054. var absRotation = Math.abs(relativeRotation);
  4055. var scaleX = 1, scaleY = 1;
  4056. if (absRotation === 90 || absRotation === 270) {
  4057. // Scale x and y because of the rotation.
  4058. scaleX = height / width;
  4059. scaleY = width / height;
  4060. }
  4061. var cssTransform = 'rotate(' + relativeRotation + 'deg) ' +
  4062. 'scale(' + scaleX + ',' + scaleY + ')';
  4063. CustomStyle.setProp('transform', canvas, cssTransform);
  4064. if (this.textLayer) {
  4065. // Rotating the text layer is more complicated since the divs inside the
  4066. // the text layer are rotated.
  4067. // TODO: This could probably be simplified by drawing the text layer in
  4068. // one orientation then rotating overall.
  4069. var textLayerViewport = this.textLayer.viewport;
  4070. var textRelativeRotation = this.viewport.rotation -
  4071. textLayerViewport.rotation;
  4072. var textAbsRotation = Math.abs(textRelativeRotation);
  4073. var scale = width / textLayerViewport.width;
  4074. if (textAbsRotation === 90 || textAbsRotation === 270) {
  4075. scale = width / textLayerViewport.height;
  4076. }
  4077. var textLayerDiv = this.textLayer.textLayerDiv;
  4078. var transX, transY;
  4079. switch (textAbsRotation) {
  4080. case 0:
  4081. transX = transY = 0;
  4082. break;
  4083. case 90:
  4084. transX = 0;
  4085. transY = '-' + textLayerDiv.style.height;
  4086. break;
  4087. case 180:
  4088. transX = '-' + textLayerDiv.style.width;
  4089. transY = '-' + textLayerDiv.style.height;
  4090. break;
  4091. case 270:
  4092. transX = '-' + textLayerDiv.style.width;
  4093. transY = 0;
  4094. break;
  4095. default:
  4096. console.error('Bad rotation value.');
  4097. break;
  4098. }
  4099. CustomStyle.setProp('transform', textLayerDiv,
  4100. 'rotate(' + textAbsRotation + 'deg) ' +
  4101. 'scale(' + scale + ', ' + scale + ') ' +
  4102. 'translate(' + transX + ', ' + transY + ')');
  4103. CustomStyle.setProp('transformOrigin', textLayerDiv, '0% 0%');
  4104. }
  4105. if (redrawAnnotations && this.annotationLayer) {
  4106. this.annotationLayer.render(this.viewport, 'display');
  4107. }
  4108. },
  4109. get width() {
  4110. return this.viewport.width;
  4111. },
  4112. get height() {
  4113. return this.viewport.height;
  4114. },
  4115. getPagePoint: function PDFPageView_getPagePoint(x, y) {
  4116. return this.viewport.convertToPdfPoint(x, y);
  4117. },
  4118. draw: function PDFPageView_draw() {
  4119. if (this.renderingState !== RenderingStates.INITIAL) {
  4120. console.error('Must be in new state before drawing');
  4121. }
  4122. this.renderingState = RenderingStates.RUNNING;
  4123. var pdfPage = this.pdfPage;
  4124. var viewport = this.viewport;
  4125. var div = this.div;
  4126. // Wrap the canvas so if it has a css transform for highdpi the overflow
  4127. // will be hidden in FF.
  4128. var canvasWrapper = document.createElement('div');
  4129. canvasWrapper.style.width = div.style.width;
  4130. canvasWrapper.style.height = div.style.height;
  4131. canvasWrapper.classList.add('canvasWrapper');
  4132. var canvas = document.createElement('canvas');
  4133. canvas.id = 'page' + this.id;
  4134. // Keep the canvas hidden until the first draw callback, or until drawing
  4135. // is complete when `!this.renderingQueue`, to prevent black flickering.
  4136. canvas.setAttribute('hidden', 'hidden');
  4137. var isCanvasHidden = true;
  4138. canvasWrapper.appendChild(canvas);
  4139. if (this.annotationLayer && this.annotationLayer.div) {
  4140. // annotationLayer needs to stay on top
  4141. div.insertBefore(canvasWrapper, this.annotationLayer.div);
  4142. } else {
  4143. div.appendChild(canvasWrapper);
  4144. }
  4145. this.canvas = canvas;
  4146. canvas.mozOpaque = true;
  4147. var ctx = canvas.getContext('2d', {alpha: false});
  4148. var outputScale = getOutputScale(ctx);
  4149. this.outputScale = outputScale;
  4150. if (pdfjsLib.PDFJS.useOnlyCssZoom) {
  4151. var actualSizeViewport = viewport.clone({scale: CSS_UNITS});
  4152. // Use a scale that will make the canvas be the original intended size
  4153. // of the page.
  4154. outputScale.sx *= actualSizeViewport.width / viewport.width;
  4155. outputScale.sy *= actualSizeViewport.height / viewport.height;
  4156. outputScale.scaled = true;
  4157. }
  4158. if (pdfjsLib.PDFJS.maxCanvasPixels > 0) {
  4159. var pixelsInViewport = viewport.width * viewport.height;
  4160. var maxScale =
  4161. Math.sqrt(pdfjsLib.PDFJS.maxCanvasPixels / pixelsInViewport);
  4162. if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
  4163. outputScale.sx = maxScale;
  4164. outputScale.sy = maxScale;
  4165. outputScale.scaled = true;
  4166. this.hasRestrictedScaling = true;
  4167. } else {
  4168. this.hasRestrictedScaling = false;
  4169. }
  4170. }
  4171. var sfx = approximateFraction(outputScale.sx);
  4172. var sfy = approximateFraction(outputScale.sy);
  4173. canvas.width = roundToDivide(viewport.width * outputScale.sx, sfx[0]);
  4174. canvas.height = roundToDivide(viewport.height * outputScale.sy, sfy[0]);
  4175. canvas.style.width = roundToDivide(viewport.width, sfx[1]) + 'px';
  4176. canvas.style.height = roundToDivide(viewport.height, sfy[1]) + 'px';
  4177. // Add the viewport so it's known what it was originally drawn with.
  4178. canvas._viewport = viewport;
  4179. var textLayerDiv = null;
  4180. var textLayer = null;
  4181. if (this.textLayerFactory) {
  4182. textLayerDiv = document.createElement('div');
  4183. textLayerDiv.className = 'textLayer';
  4184. textLayerDiv.style.width = canvasWrapper.style.width;
  4185. textLayerDiv.style.height = canvasWrapper.style.height;
  4186. if (this.annotationLayer && this.annotationLayer.div) {
  4187. // annotationLayer needs to stay on top
  4188. div.insertBefore(textLayerDiv, this.annotationLayer.div);
  4189. } else {
  4190. div.appendChild(textLayerDiv);
  4191. }
  4192. textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv,
  4193. this.id - 1,
  4194. this.viewport);
  4195. }
  4196. this.textLayer = textLayer;
  4197. var resolveRenderPromise, rejectRenderPromise;
  4198. var promise = new Promise(function (resolve, reject) {
  4199. resolveRenderPromise = resolve;
  4200. rejectRenderPromise = reject;
  4201. });
  4202. // Rendering area
  4203. var self = this;
  4204. function pageViewDrawCallback(error) {
  4205. // The renderTask may have been replaced by a new one, so only remove
  4206. // the reference to the renderTask if it matches the one that is
  4207. // triggering this callback.
  4208. if (renderTask === self.renderTask) {
  4209. self.renderTask = null;
  4210. }
  4211. if (error === 'cancelled') {
  4212. rejectRenderPromise(error);
  4213. return;
  4214. }
  4215. self.renderingState = RenderingStates.FINISHED;
  4216. if (isCanvasHidden) {
  4217. self.canvas.removeAttribute('hidden');
  4218. isCanvasHidden = false;
  4219. }
  4220. if (self.loadingIconDiv) {
  4221. div.removeChild(self.loadingIconDiv);
  4222. delete self.loadingIconDiv;
  4223. }
  4224. if (self.zoomLayer) {
  4225. // Zeroing the width and height causes Firefox to release graphics
  4226. // resources immediately, which can greatly reduce memory consumption.
  4227. var zoomLayerCanvas = self.zoomLayer.firstChild;
  4228. zoomLayerCanvas.width = 0;
  4229. zoomLayerCanvas.height = 0;
  4230. div.removeChild(self.zoomLayer);
  4231. self.zoomLayer = null;
  4232. }
  4233. self.error = error;
  4234. self.stats = pdfPage.stats;
  4235. if (self.onAfterDraw) {
  4236. self.onAfterDraw();
  4237. }
  4238. var event = document.createEvent('CustomEvent');
  4239. event.initCustomEvent('pagerendered', true, true, {
  4240. pageNumber: self.id,
  4241. cssTransform: false,
  4242. });
  4243. div.dispatchEvent(event);
  4244. if (!error) {
  4245. resolveRenderPromise(undefined);
  4246. } else {
  4247. rejectRenderPromise(error);
  4248. }
  4249. }
  4250. var renderContinueCallback = null;
  4251. if (this.renderingQueue) {
  4252. renderContinueCallback = function renderContinueCallback(cont) {
  4253. if (!self.renderingQueue.isHighestPriority(self)) {
  4254. self.renderingState = RenderingStates.PAUSED;
  4255. self.resume = function resumeCallback() {
  4256. self.renderingState = RenderingStates.RUNNING;
  4257. cont();
  4258. };
  4259. return;
  4260. }
  4261. if (isCanvasHidden) {
  4262. self.canvas.removeAttribute('hidden');
  4263. isCanvasHidden = false;
  4264. }
  4265. cont();
  4266. };
  4267. }
  4268. var transform = !outputScale.scaled ? null :
  4269. [outputScale.sx, 0, 0, outputScale.sy, 0, 0];
  4270. var renderContext = {
  4271. canvasContext: ctx,
  4272. transform: transform,
  4273. viewport: this.viewport,
  4274. // intent: 'default', // === 'display'
  4275. };
  4276. var renderTask = this.renderTask = this.pdfPage.render(renderContext);
  4277. renderTask.onContinue = renderContinueCallback;
  4278. this.renderTask.promise.then(
  4279. function pdfPageRenderCallback() {
  4280. pageViewDrawCallback(null);
  4281. if (textLayer) {
  4282. self.pdfPage.getTextContent({ normalizeWhitespace: true }).then(
  4283. function textContentResolved(textContent) {
  4284. textLayer.setTextContent(textContent);
  4285. textLayer.render(TEXT_LAYER_RENDER_DELAY);
  4286. }
  4287. );
  4288. }
  4289. },
  4290. function pdfPageRenderError(error) {
  4291. pageViewDrawCallback(error);
  4292. }
  4293. );
  4294. if (this.annotationLayerFactory) {
  4295. if (!this.annotationLayer) {
  4296. this.annotationLayer = this.annotationLayerFactory.
  4297. createAnnotationLayerBuilder(div, this.pdfPage);
  4298. }
  4299. this.annotationLayer.render(this.viewport, 'display');
  4300. }
  4301. div.setAttribute('data-loaded', true);
  4302. if (self.onBeforeDraw) {
  4303. self.onBeforeDraw();
  4304. }
  4305. return promise;
  4306. },
  4307. beforePrint: function PDFPageView_beforePrint(printContainer) {
  4308. var CustomStyle = pdfjsLib.CustomStyle;
  4309. var pdfPage = this.pdfPage;
  4310. var viewport = pdfPage.getViewport(1);
  4311. // Use the same hack we use for high dpi displays for printing to get
  4312. // better output until bug 811002 is fixed in FF.
  4313. var PRINT_OUTPUT_SCALE = 2;
  4314. var canvas = document.createElement('canvas');
  4315. // The logical size of the canvas.
  4316. canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE;
  4317. canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE;
  4318. // The rendered size of the canvas, relative to the size of canvasWrapper.
  4319. canvas.style.width = (PRINT_OUTPUT_SCALE * 100) + '%';
  4320. var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' +
  4321. (1 / PRINT_OUTPUT_SCALE) + ')';
  4322. CustomStyle.setProp('transform' , canvas, cssScale);
  4323. CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
  4324. var canvasWrapper = document.createElement('div');
  4325. canvasWrapper.appendChild(canvas);
  4326. printContainer.appendChild(canvasWrapper);
  4327. canvas.mozPrintCallback = function(obj) {
  4328. var ctx = obj.context;
  4329. ctx.save();
  4330. ctx.fillStyle = 'rgb(255, 255, 255)';
  4331. ctx.fillRect(0, 0, canvas.width, canvas.height);
  4332. ctx.restore();
  4333. // Used by the mozCurrentTransform polyfill in src/display/canvas.js.
  4334. ctx._transformMatrix =
  4335. [PRINT_OUTPUT_SCALE, 0, 0, PRINT_OUTPUT_SCALE, 0, 0];
  4336. ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE);
  4337. var renderContext = {
  4338. canvasContext: ctx,
  4339. viewport: viewport,
  4340. intent: 'print'
  4341. };
  4342. pdfPage.render(renderContext).promise.then(function() {
  4343. // Tell the printEngine that rendering this canvas/page has finished.
  4344. obj.done();
  4345. }, function(error) {
  4346. console.error(error);
  4347. // Tell the printEngine that rendering this canvas/page has failed.
  4348. // This will make the print proces stop.
  4349. if ('abort' in obj) {
  4350. obj.abort();
  4351. } else {
  4352. obj.done();
  4353. }
  4354. });
  4355. };
  4356. },
  4357. };
  4358. return PDFPageView;
  4359. })();
  4360. exports.PDFPageView = PDFPageView;
  4361. }));
  4362. (function (root, factory) {
  4363. {
  4364. factory((root.pdfjsWebPDFThumbnailView = {}), root.pdfjsWebUIUtils,
  4365. root.pdfjsWebPDFRenderingQueue);
  4366. }
  4367. }(this, function (exports, uiUtils, pdfRenderingQueue) {
  4368. var mozL10n = uiUtils.mozL10n;
  4369. var getOutputScale = uiUtils.getOutputScale;
  4370. var RenderingStates = pdfRenderingQueue.RenderingStates;
  4371. var THUMBNAIL_WIDTH = 98; // px
  4372. var THUMBNAIL_CANVAS_BORDER_WIDTH = 1; // px
  4373. /**
  4374. * @typedef {Object} PDFThumbnailViewOptions
  4375. * @property {HTMLDivElement} container - The viewer element.
  4376. * @property {number} id - The thumbnail's unique ID (normally its number).
  4377. * @property {PageViewport} defaultViewport - The page viewport.
  4378. * @property {IPDFLinkService} linkService - The navigation/linking service.
  4379. * @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
  4380. * @property {boolean} disableCanvasToImageConversion - (optional) Don't convert
  4381. * the canvas thumbnails to images. This prevents `toDataURL` calls,
  4382. * but increases the overall memory usage. The default value is false.
  4383. */
  4384. /**
  4385. * @class
  4386. * @implements {IRenderableView}
  4387. */
  4388. var PDFThumbnailView = (function PDFThumbnailViewClosure() {
  4389. function getTempCanvas(width, height) {
  4390. var tempCanvas = PDFThumbnailView.tempImageCache;
  4391. if (!tempCanvas) {
  4392. tempCanvas = document.createElement('canvas');
  4393. PDFThumbnailView.tempImageCache = tempCanvas;
  4394. }
  4395. tempCanvas.width = width;
  4396. tempCanvas.height = height;
  4397. // Since this is a temporary canvas, we need to fill the canvas with a white
  4398. // background ourselves. `_getPageDrawContext` uses CSS rules for this.
  4399. tempCanvas.mozOpaque = true;
  4400. var ctx = tempCanvas.getContext('2d', {alpha: false});
  4401. ctx.save();
  4402. ctx.fillStyle = 'rgb(255, 255, 255)';
  4403. ctx.fillRect(0, 0, width, height);
  4404. ctx.restore();
  4405. return tempCanvas;
  4406. }
  4407. /**
  4408. * @constructs PDFThumbnailView
  4409. * @param {PDFThumbnailViewOptions} options
  4410. */
  4411. function PDFThumbnailView(options) {
  4412. var container = options.container;
  4413. var id = options.id;
  4414. var defaultViewport = options.defaultViewport;
  4415. var linkService = options.linkService;
  4416. var renderingQueue = options.renderingQueue;
  4417. var disableCanvasToImageConversion =
  4418. options.disableCanvasToImageConversion || false;
  4419. this.id = id;
  4420. this.renderingId = 'thumbnail' + id;
  4421. this.pdfPage = null;
  4422. this.rotation = 0;
  4423. this.viewport = defaultViewport;
  4424. this.pdfPageRotate = defaultViewport.rotation;
  4425. this.linkService = linkService;
  4426. this.renderingQueue = renderingQueue;
  4427. this.resume = null;
  4428. this.renderingState = RenderingStates.INITIAL;
  4429. this.disableCanvasToImageConversion = disableCanvasToImageConversion;
  4430. this.pageWidth = this.viewport.width;
  4431. this.pageHeight = this.viewport.height;
  4432. this.pageRatio = this.pageWidth / this.pageHeight;
  4433. this.canvasWidth = THUMBNAIL_WIDTH;
  4434. this.canvasHeight = (this.canvasWidth / this.pageRatio) | 0;
  4435. this.scale = this.canvasWidth / this.pageWidth;
  4436. var anchor = document.createElement('a');
  4437. anchor.href = linkService.getAnchorUrl('#page=' + id);
  4438. anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}');
  4439. anchor.onclick = function stopNavigation() {
  4440. linkService.page = id;
  4441. return false;
  4442. };
  4443. var div = document.createElement('div');
  4444. div.id = 'thumbnailContainer' + id;
  4445. div.className = 'thumbnail';
  4446. this.div = div;
  4447. if (id === 1) {
  4448. // Highlight the thumbnail of the first page when no page number is
  4449. // specified (or exists in cache) when the document is loaded.
  4450. div.classList.add('selected');
  4451. }
  4452. var ring = document.createElement('div');
  4453. ring.className = 'thumbnailSelectionRing';
  4454. var borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
  4455. ring.style.width = this.canvasWidth + borderAdjustment + 'px';
  4456. ring.style.height = this.canvasHeight + borderAdjustment + 'px';
  4457. this.ring = ring;
  4458. div.appendChild(ring);
  4459. anchor.appendChild(div);
  4460. container.appendChild(anchor);
  4461. }
  4462. PDFThumbnailView.prototype = {
  4463. setPdfPage: function PDFThumbnailView_setPdfPage(pdfPage) {
  4464. this.pdfPage = pdfPage;
  4465. this.pdfPageRotate = pdfPage.rotate;
  4466. var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
  4467. this.viewport = pdfPage.getViewport(1, totalRotation);
  4468. this.reset();
  4469. },
  4470. reset: function PDFThumbnailView_reset() {
  4471. if (this.renderTask) {
  4472. this.renderTask.cancel();
  4473. }
  4474. this.resume = null;
  4475. this.renderingState = RenderingStates.INITIAL;
  4476. this.pageWidth = this.viewport.width;
  4477. this.pageHeight = this.viewport.height;
  4478. this.pageRatio = this.pageWidth / this.pageHeight;
  4479. this.canvasHeight = (this.canvasWidth / this.pageRatio) | 0;
  4480. this.scale = (this.canvasWidth / this.pageWidth);
  4481. this.div.removeAttribute('data-loaded');
  4482. var ring = this.ring;
  4483. var childNodes = ring.childNodes;
  4484. for (var i = childNodes.length - 1; i >= 0; i--) {
  4485. ring.removeChild(childNodes[i]);
  4486. }
  4487. var borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
  4488. ring.style.width = this.canvasWidth + borderAdjustment + 'px';
  4489. ring.style.height = this.canvasHeight + borderAdjustment + 'px';
  4490. if (this.canvas) {
  4491. // Zeroing the width and height causes Firefox to release graphics
  4492. // resources immediately, which can greatly reduce memory consumption.
  4493. this.canvas.width = 0;
  4494. this.canvas.height = 0;
  4495. delete this.canvas;
  4496. }
  4497. if (this.image) {
  4498. this.image.removeAttribute('src');
  4499. delete this.image;
  4500. }
  4501. },
  4502. update: function PDFThumbnailView_update(rotation) {
  4503. if (typeof rotation !== 'undefined') {
  4504. this.rotation = rotation;
  4505. }
  4506. var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
  4507. this.viewport = this.viewport.clone({
  4508. scale: 1,
  4509. rotation: totalRotation
  4510. });
  4511. this.reset();
  4512. },
  4513. /**
  4514. * @private
  4515. */
  4516. _getPageDrawContext:
  4517. function PDFThumbnailView_getPageDrawContext(noCtxScale) {
  4518. var canvas = document.createElement('canvas');
  4519. // Keep the no-thumbnail outline visible, i.e. `data-loaded === false`,
  4520. // until rendering/image conversion is complete, to avoid display issues.
  4521. this.canvas = canvas;
  4522. canvas.mozOpaque = true;
  4523. var ctx = canvas.getContext('2d', {alpha: false});
  4524. var outputScale = getOutputScale(ctx);
  4525. canvas.width = (this.canvasWidth * outputScale.sx) | 0;
  4526. canvas.height = (this.canvasHeight * outputScale.sy) | 0;
  4527. canvas.style.width = this.canvasWidth + 'px';
  4528. canvas.style.height = this.canvasHeight + 'px';
  4529. if (!noCtxScale && outputScale.scaled) {
  4530. ctx.scale(outputScale.sx, outputScale.sy);
  4531. }
  4532. return ctx;
  4533. },
  4534. /**
  4535. * @private
  4536. */
  4537. _convertCanvasToImage: function PDFThumbnailView_convertCanvasToImage() {
  4538. if (!this.canvas) {
  4539. return;
  4540. }
  4541. if (this.renderingState !== RenderingStates.FINISHED) {
  4542. return;
  4543. }
  4544. var id = this.renderingId;
  4545. var className = 'thumbnailImage';
  4546. var ariaLabel = mozL10n.get('thumb_page_canvas', { page: this.id },
  4547. 'Thumbnail of Page {{page}}');
  4548. if (this.disableCanvasToImageConversion) {
  4549. this.canvas.id = id;
  4550. this.canvas.className = className;
  4551. this.canvas.setAttribute('aria-label', ariaLabel);
  4552. this.div.setAttribute('data-loaded', true);
  4553. this.ring.appendChild(this.canvas);
  4554. return;
  4555. }
  4556. var image = document.createElement('img');
  4557. image.id = id;
  4558. image.className = className;
  4559. image.setAttribute('aria-label', ariaLabel);
  4560. image.style.width = this.canvasWidth + 'px';
  4561. image.style.height = this.canvasHeight + 'px';
  4562. image.src = this.canvas.toDataURL();
  4563. this.image = image;
  4564. this.div.setAttribute('data-loaded', true);
  4565. this.ring.appendChild(image);
  4566. // Zeroing the width and height causes Firefox to release graphics
  4567. // resources immediately, which can greatly reduce memory consumption.
  4568. this.canvas.width = 0;
  4569. this.canvas.height = 0;
  4570. delete this.canvas;
  4571. },
  4572. draw: function PDFThumbnailView_draw() {
  4573. if (this.renderingState !== RenderingStates.INITIAL) {
  4574. console.error('Must be in new state before drawing');
  4575. return Promise.resolve(undefined);
  4576. }
  4577. this.renderingState = RenderingStates.RUNNING;
  4578. var resolveRenderPromise, rejectRenderPromise;
  4579. var promise = new Promise(function (resolve, reject) {
  4580. resolveRenderPromise = resolve;
  4581. rejectRenderPromise = reject;
  4582. });
  4583. var self = this;
  4584. function thumbnailDrawCallback(error) {
  4585. // The renderTask may have been replaced by a new one, so only remove
  4586. // the reference to the renderTask if it matches the one that is
  4587. // triggering this callback.
  4588. if (renderTask === self.renderTask) {
  4589. self.renderTask = null;
  4590. }
  4591. if (error === 'cancelled') {
  4592. rejectRenderPromise(error);
  4593. return;
  4594. }
  4595. self.renderingState = RenderingStates.FINISHED;
  4596. self._convertCanvasToImage();
  4597. if (!error) {
  4598. resolveRenderPromise(undefined);
  4599. } else {
  4600. rejectRenderPromise(error);
  4601. }
  4602. }
  4603. var ctx = this._getPageDrawContext();
  4604. var drawViewport = this.viewport.clone({ scale: this.scale });
  4605. var renderContinueCallback = function renderContinueCallback(cont) {
  4606. if (!self.renderingQueue.isHighestPriority(self)) {
  4607. self.renderingState = RenderingStates.PAUSED;
  4608. self.resume = function resumeCallback() {
  4609. self.renderingState = RenderingStates.RUNNING;
  4610. cont();
  4611. };
  4612. return;
  4613. }
  4614. cont();
  4615. };
  4616. var renderContext = {
  4617. canvasContext: ctx,
  4618. viewport: drawViewport
  4619. };
  4620. var renderTask = this.renderTask = this.pdfPage.render(renderContext);
  4621. renderTask.onContinue = renderContinueCallback;
  4622. renderTask.promise.then(
  4623. function pdfPageRenderCallback() {
  4624. thumbnailDrawCallback(null);
  4625. },
  4626. function pdfPageRenderError(error) {
  4627. thumbnailDrawCallback(error);
  4628. }
  4629. );
  4630. return promise;
  4631. },
  4632. setImage: function PDFThumbnailView_setImage(pageView) {
  4633. if (this.renderingState !== RenderingStates.INITIAL) {
  4634. return;
  4635. }
  4636. var img = pageView.canvas;
  4637. if (!img) {
  4638. return;
  4639. }
  4640. if (!this.pdfPage) {
  4641. this.setPdfPage(pageView.pdfPage);
  4642. }
  4643. this.renderingState = RenderingStates.FINISHED;
  4644. var ctx = this._getPageDrawContext(true);
  4645. var canvas = ctx.canvas;
  4646. if (img.width <= 2 * canvas.width) {
  4647. ctx.drawImage(img, 0, 0, img.width, img.height,
  4648. 0, 0, canvas.width, canvas.height);
  4649. this._convertCanvasToImage();
  4650. return;
  4651. }
  4652. // drawImage does an awful job of rescaling the image, doing it gradually.
  4653. var MAX_NUM_SCALING_STEPS = 3;
  4654. var reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS;
  4655. var reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS;
  4656. var reducedImage = getTempCanvas(reducedWidth, reducedHeight);
  4657. var reducedImageCtx = reducedImage.getContext('2d');
  4658. while (reducedWidth > img.width || reducedHeight > img.height) {
  4659. reducedWidth >>= 1;
  4660. reducedHeight >>= 1;
  4661. }
  4662. reducedImageCtx.drawImage(img, 0, 0, img.width, img.height,
  4663. 0, 0, reducedWidth, reducedHeight);
  4664. while (reducedWidth > 2 * canvas.width) {
  4665. reducedImageCtx.drawImage(reducedImage,
  4666. 0, 0, reducedWidth, reducedHeight,
  4667. 0, 0, reducedWidth >> 1, reducedHeight >> 1);
  4668. reducedWidth >>= 1;
  4669. reducedHeight >>= 1;
  4670. }
  4671. ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight,
  4672. 0, 0, canvas.width, canvas.height);
  4673. this._convertCanvasToImage();
  4674. }
  4675. };
  4676. return PDFThumbnailView;
  4677. })();
  4678. PDFThumbnailView.tempImageCache = null;
  4679. exports.PDFThumbnailView = PDFThumbnailView;
  4680. }));
  4681. (function (root, factory) {
  4682. {
  4683. factory((root.pdfjsWebSecondaryToolbar = {}), root.pdfjsWebUIUtils);
  4684. }
  4685. }(this, function (exports, uiUtils) {
  4686. var SCROLLBAR_PADDING = uiUtils.SCROLLBAR_PADDING;
  4687. var app; // Avoiding circular reference, see _setApp function below.
  4688. var PDFViewerApplication = null; // = app.PDFViewerApplication;
  4689. var SecondaryToolbar = {
  4690. opened: false,
  4691. previousContainerHeight: null,
  4692. newContainerHeight: null,
  4693. initialize: function secondaryToolbarInitialize(options) {
  4694. this.toolbar = options.toolbar;
  4695. this.buttonContainer = this.toolbar.firstElementChild;
  4696. // Define the toolbar buttons.
  4697. this.toggleButton = options.toggleButton;
  4698. this.presentationModeButton = options.presentationModeButton;
  4699. this.openFile = options.openFile;
  4700. this.print = options.print;
  4701. this.download = options.download;
  4702. this.viewBookmark = options.viewBookmark;
  4703. this.firstPage = options.firstPage;
  4704. this.lastPage = options.lastPage;
  4705. this.pageRotateCw = options.pageRotateCw;
  4706. this.pageRotateCcw = options.pageRotateCcw;
  4707. this.documentPropertiesButton = options.documentPropertiesButton;
  4708. // Attach the event listeners.
  4709. var elements = [
  4710. // Button to toggle the visibility of the secondary toolbar:
  4711. { element: this.toggleButton, handler: this.toggle },
  4712. // All items within the secondary toolbar
  4713. // (except for toggleHandTool, hand_tool.js is responsible for it):
  4714. { element: this.presentationModeButton,
  4715. handler: this.presentationModeClick },
  4716. { element: this.openFile, handler: this.openFileClick },
  4717. { element: this.print, handler: this.printClick },
  4718. { element: this.download, handler: this.downloadClick },
  4719. { element: this.viewBookmark, handler: this.viewBookmarkClick },
  4720. { element: this.firstPage, handler: this.firstPageClick },
  4721. { element: this.lastPage, handler: this.lastPageClick },
  4722. { element: this.pageRotateCw, handler: this.pageRotateCwClick },
  4723. { element: this.pageRotateCcw, handler: this.pageRotateCcwClick },
  4724. { element: this.documentPropertiesButton,
  4725. handler: this.documentPropertiesClick }
  4726. ];
  4727. for (var item in elements) {
  4728. var element = elements[item].element;
  4729. if (element) {
  4730. element.addEventListener('click', elements[item].handler.bind(this));
  4731. }
  4732. }
  4733. },
  4734. // Event handling functions.
  4735. presentationModeClick: function secondaryToolbarPresentationModeClick(evt) {
  4736. PDFViewerApplication.requestPresentationMode();
  4737. this.close();
  4738. },
  4739. openFileClick: function secondaryToolbarOpenFileClick(evt) {
  4740. var openFileInputName = PDFViewerApplication.appConfig.openFileInputName;
  4741. document.getElementById(openFileInputName).click();
  4742. this.close();
  4743. },
  4744. printClick: function secondaryToolbarPrintClick(evt) {
  4745. window.print();
  4746. this.close();
  4747. },
  4748. downloadClick: function secondaryToolbarDownloadClick(evt) {
  4749. PDFViewerApplication.download();
  4750. this.close();
  4751. },
  4752. viewBookmarkClick: function secondaryToolbarViewBookmarkClick(evt) {
  4753. this.close();
  4754. },
  4755. firstPageClick: function secondaryToolbarFirstPageClick(evt) {
  4756. PDFViewerApplication.page = 1;
  4757. this.close();
  4758. },
  4759. lastPageClick: function secondaryToolbarLastPageClick(evt) {
  4760. if (PDFViewerApplication.pdfDocument) {
  4761. PDFViewerApplication.page = PDFViewerApplication.pagesCount;
  4762. }
  4763. this.close();
  4764. },
  4765. pageRotateCwClick: function secondaryToolbarPageRotateCwClick(evt) {
  4766. PDFViewerApplication.rotatePages(90);
  4767. },
  4768. pageRotateCcwClick: function secondaryToolbarPageRotateCcwClick(evt) {
  4769. PDFViewerApplication.rotatePages(-90);
  4770. },
  4771. documentPropertiesClick: function secondaryToolbarDocumentPropsClick(evt) {
  4772. PDFViewerApplication.pdfDocumentProperties.open();
  4773. this.close();
  4774. },
  4775. // Misc. functions for interacting with the toolbar.
  4776. setMaxHeight: function secondaryToolbarSetMaxHeight(container) {
  4777. if (!container || !this.buttonContainer) {
  4778. return;
  4779. }
  4780. this.newContainerHeight = container.clientHeight;
  4781. if (this.previousContainerHeight === this.newContainerHeight) {
  4782. return;
  4783. }
  4784. this.buttonContainer.setAttribute('style',
  4785. 'max-height: ' + (this.newContainerHeight - SCROLLBAR_PADDING) + 'px;');
  4786. this.previousContainerHeight = this.newContainerHeight;
  4787. },
  4788. open: function secondaryToolbarOpen() {
  4789. if (this.opened) {
  4790. return;
  4791. }
  4792. this.opened = true;
  4793. this.toggleButton.classList.add('toggled');
  4794. this.toolbar.classList.remove('hidden');
  4795. },
  4796. close: function secondaryToolbarClose(target) {
  4797. if (!this.opened) {
  4798. return;
  4799. } else if (target && !this.toolbar.contains(target)) {
  4800. return;
  4801. }
  4802. this.opened = false;
  4803. this.toolbar.classList.add('hidden');
  4804. this.toggleButton.classList.remove('toggled');
  4805. },
  4806. toggle: function secondaryToolbarToggle() {
  4807. if (this.opened) {
  4808. this.close();
  4809. } else {
  4810. this.open();
  4811. }
  4812. }
  4813. };
  4814. function _setApp(app_) {
  4815. app = app_;
  4816. PDFViewerApplication = app.PDFViewerApplication;
  4817. }
  4818. exports.SecondaryToolbar = SecondaryToolbar;
  4819. exports._setApp = _setApp;
  4820. }));
  4821. (function (root, factory) {
  4822. {
  4823. factory((root.pdfjsWebAnnotationLayerBuilder = {}), root.pdfjsWebUIUtils,
  4824. root.pdfjsWebPDFLinkService, root.pdfjsWebPDFJS);
  4825. }
  4826. }(this, function (exports, uiUtils, pdfLinkService, pdfjsLib) {
  4827. var mozL10n = uiUtils.mozL10n;
  4828. var SimpleLinkService = pdfLinkService.SimpleLinkService;
  4829. /**
  4830. * @typedef {Object} AnnotationLayerBuilderOptions
  4831. * @property {HTMLDivElement} pageDiv
  4832. * @property {PDFPage} pdfPage
  4833. * @property {IPDFLinkService} linkService
  4834. * @property {DownloadManager} downloadManager
  4835. */
  4836. /**
  4837. * @class
  4838. */
  4839. var AnnotationLayerBuilder = (function AnnotationLayerBuilderClosure() {
  4840. /**
  4841. * @param {AnnotationLayerBuilderOptions} options
  4842. * @constructs AnnotationLayerBuilder
  4843. */
  4844. function AnnotationLayerBuilder(options) {
  4845. this.pageDiv = options.pageDiv;
  4846. this.pdfPage = options.pdfPage;
  4847. this.linkService = options.linkService;
  4848. this.downloadManager = options.downloadManager;
  4849. this.div = null;
  4850. }
  4851. AnnotationLayerBuilder.prototype =
  4852. /** @lends AnnotationLayerBuilder.prototype */ {
  4853. /**
  4854. * @param {PageViewport} viewport
  4855. * @param {string} intent (default value is 'display')
  4856. */
  4857. render: function AnnotationLayerBuilder_render(viewport, intent) {
  4858. var self = this;
  4859. var parameters = {
  4860. intent: (intent === undefined ? 'display' : intent),
  4861. };
  4862. this.pdfPage.getAnnotations(parameters).then(function (annotations) {
  4863. viewport = viewport.clone({ dontFlip: true });
  4864. parameters = {
  4865. viewport: viewport,
  4866. div: self.div,
  4867. annotations: annotations,
  4868. page: self.pdfPage,
  4869. linkService: self.linkService,
  4870. downloadManager: self.downloadManager
  4871. };
  4872. if (self.div) {
  4873. // If an annotationLayer already exists, refresh its children's
  4874. // transformation matrices.
  4875. pdfjsLib.AnnotationLayer.update(parameters);
  4876. } else {
  4877. // Create an annotation layer div and render the annotations
  4878. // if there is at least one annotation.
  4879. if (annotations.length === 0) {
  4880. return;
  4881. }
  4882. self.div = document.createElement('div');
  4883. self.div.className = 'annotationLayer';
  4884. self.pageDiv.appendChild(self.div);
  4885. parameters.div = self.div;
  4886. pdfjsLib.AnnotationLayer.render(parameters);
  4887. if (typeof mozL10n !== 'undefined') {
  4888. mozL10n.translate(self.div);
  4889. }
  4890. }
  4891. });
  4892. },
  4893. hide: function AnnotationLayerBuilder_hide() {
  4894. if (!this.div) {
  4895. return;
  4896. }
  4897. this.div.setAttribute('hidden', 'true');
  4898. }
  4899. };
  4900. return AnnotationLayerBuilder;
  4901. })();
  4902. /**
  4903. * @constructor
  4904. * @implements IPDFAnnotationLayerFactory
  4905. */
  4906. function DefaultAnnotationLayerFactory() {}
  4907. DefaultAnnotationLayerFactory.prototype = {
  4908. /**
  4909. * @param {HTMLDivElement} pageDiv
  4910. * @param {PDFPage} pdfPage
  4911. * @returns {AnnotationLayerBuilder}
  4912. */
  4913. createAnnotationLayerBuilder: function (pageDiv, pdfPage) {
  4914. return new AnnotationLayerBuilder({
  4915. pageDiv: pageDiv,
  4916. pdfPage: pdfPage,
  4917. linkService: new SimpleLinkService(),
  4918. });
  4919. }
  4920. };
  4921. exports.AnnotationLayerBuilder = AnnotationLayerBuilder;
  4922. exports.DefaultAnnotationLayerFactory = DefaultAnnotationLayerFactory;
  4923. }));
  4924. (function (root, factory) {
  4925. {
  4926. factory((root.pdfjsWebHandTool = {}), root.pdfjsWebUIUtils,
  4927. root.pdfjsWebGrabToPan, root.pdfjsWebPreferences,
  4928. root.pdfjsWebSecondaryToolbar);
  4929. }
  4930. }(this, function (exports, uiUtils, grabToPan, preferences, secondaryToolbar) {
  4931. var mozL10n = uiUtils.mozL10n;
  4932. var GrabToPan = grabToPan.GrabToPan;
  4933. var Preferences = preferences.Preferences;
  4934. var SecondaryToolbar = secondaryToolbar.SecondaryToolbar;
  4935. /**
  4936. * @typedef {Object} HandToolOptions
  4937. * @property {HTMLDivElement} container - The document container.
  4938. * @property {HTMLButtonElement} toggleHandTool - The button element for
  4939. * toggling the hand tool.
  4940. */
  4941. /**
  4942. * @class
  4943. */
  4944. var HandTool = (function HandToolClosure() {
  4945. /**
  4946. * @constructs HandTool
  4947. * @param {HandToolOptions} options
  4948. */
  4949. function HandTool(options) {
  4950. this.container = options.container;
  4951. this.toggleHandTool = options.toggleHandTool;
  4952. this.wasActive = false;
  4953. this.handTool = new GrabToPan({
  4954. element: this.container,
  4955. onActiveChanged: function(isActive) {
  4956. if (!this.toggleHandTool) {
  4957. return;
  4958. }
  4959. if (isActive) {
  4960. this.toggleHandTool.title =
  4961. mozL10n.get('hand_tool_disable.title', null, 'Disable hand tool');
  4962. this.toggleHandTool.firstElementChild.textContent =
  4963. mozL10n.get('hand_tool_disable_label', null, 'Disable hand tool');
  4964. } else {
  4965. this.toggleHandTool.title =
  4966. mozL10n.get('hand_tool_enable.title', null, 'Enable hand tool');
  4967. this.toggleHandTool.firstElementChild.textContent =
  4968. mozL10n.get('hand_tool_enable_label', null, 'Enable hand tool');
  4969. }
  4970. }.bind(this)
  4971. });
  4972. if (this.toggleHandTool) {
  4973. this.toggleHandTool.addEventListener('click', this.toggle.bind(this));
  4974. window.addEventListener('localized', function (evt) {
  4975. Preferences.get('enableHandToolOnLoad').then(function resolved(value) {
  4976. if (value) {
  4977. this.handTool.activate();
  4978. }
  4979. }.bind(this), function rejected(reason) {});
  4980. }.bind(this));
  4981. window.addEventListener('presentationmodechanged', function (evt) {
  4982. if (evt.detail.switchInProgress) {
  4983. return;
  4984. }
  4985. if (evt.detail.active) {
  4986. this.enterPresentationMode();
  4987. } else {
  4988. this.exitPresentationMode();
  4989. }
  4990. }.bind(this));
  4991. }
  4992. }
  4993. HandTool.prototype = {
  4994. /**
  4995. * @return {boolean}
  4996. */
  4997. get isActive() {
  4998. return !!this.handTool.active;
  4999. },
  5000. toggle: function HandTool_toggle() {
  5001. this.handTool.toggle();
  5002. SecondaryToolbar.close();
  5003. },
  5004. enterPresentationMode: function HandTool_enterPresentationMode() {
  5005. if (this.isActive) {
  5006. this.wasActive = true;
  5007. this.handTool.deactivate();
  5008. }
  5009. },
  5010. exitPresentationMode: function HandTool_exitPresentationMode() {
  5011. if (this.wasActive) {
  5012. this.wasActive = false;
  5013. this.handTool.activate();
  5014. }
  5015. }
  5016. };
  5017. return HandTool;
  5018. })();
  5019. exports.HandTool = HandTool;
  5020. }));
  5021. (function (root, factory) {
  5022. {
  5023. factory((root.pdfjsWebPDFFindBar = {}), root.pdfjsWebUIUtils,
  5024. root.pdfjsWebPDFFindController);
  5025. }
  5026. }(this, function (exports, uiUtils, pdfFindController) {
  5027. var mozL10n = uiUtils.mozL10n;
  5028. var FindStates = pdfFindController.FindStates;
  5029. /**
  5030. * Creates a "search bar" given a set of DOM elements that act as controls
  5031. * for searching or for setting search preferences in the UI. This object
  5032. * also sets up the appropriate events for the controls. Actual searching
  5033. * is done by PDFFindController.
  5034. */
  5035. var PDFFindBar = (function PDFFindBarClosure() {
  5036. function PDFFindBar(options) {
  5037. this.opened = false;
  5038. this.bar = options.bar || null;
  5039. this.toggleButton = options.toggleButton || null;
  5040. this.findField = options.findField || null;
  5041. this.highlightAll = options.highlightAllCheckbox || null;
  5042. this.caseSensitive = options.caseSensitiveCheckbox || null;
  5043. this.findMsg = options.findMsg || null;
  5044. this.findResultsCount = options.findResultsCount || null;
  5045. this.findStatusIcon = options.findStatusIcon || null;
  5046. this.findPreviousButton = options.findPreviousButton || null;
  5047. this.findNextButton = options.findNextButton || null;
  5048. this.findController = options.findController || null;
  5049. if (this.findController === null) {
  5050. throw new Error('PDFFindBar cannot be used without a ' +
  5051. 'PDFFindController instance.');
  5052. }
  5053. // Add event listeners to the DOM elements.
  5054. var self = this;
  5055. this.toggleButton.addEventListener('click', function() {
  5056. self.toggle();
  5057. });
  5058. this.findField.addEventListener('input', function() {
  5059. self.dispatchEvent('');
  5060. });
  5061. this.bar.addEventListener('keydown', function(evt) {
  5062. switch (evt.keyCode) {
  5063. case 13: // Enter
  5064. if (evt.target === self.findField) {
  5065. self.dispatchEvent('again', evt.shiftKey);
  5066. }
  5067. break;
  5068. case 27: // Escape
  5069. self.close();
  5070. break;
  5071. }
  5072. });
  5073. this.findPreviousButton.addEventListener('click', function() {
  5074. self.dispatchEvent('again', true);
  5075. });
  5076. this.findNextButton.addEventListener('click', function() {
  5077. self.dispatchEvent('again', false);
  5078. });
  5079. this.highlightAll.addEventListener('click', function() {
  5080. self.dispatchEvent('highlightallchange');
  5081. });
  5082. this.caseSensitive.addEventListener('click', function() {
  5083. self.dispatchEvent('casesensitivitychange');
  5084. });
  5085. }
  5086. PDFFindBar.prototype = {
  5087. reset: function PDFFindBar_reset() {
  5088. this.updateUIState();
  5089. },
  5090. dispatchEvent: function PDFFindBar_dispatchEvent(type, findPrev) {
  5091. var event = document.createEvent('CustomEvent');
  5092. event.initCustomEvent('find' + type, true, true, {
  5093. query: this.findField.value,
  5094. caseSensitive: this.caseSensitive.checked,
  5095. highlightAll: this.highlightAll.checked,
  5096. findPrevious: findPrev
  5097. });
  5098. return window.dispatchEvent(event);
  5099. },
  5100. updateUIState:
  5101. function PDFFindBar_updateUIState(state, previous, matchCount) {
  5102. var notFound = false;
  5103. var findMsg = '';
  5104. var status = '';
  5105. switch (state) {
  5106. case FindStates.FIND_FOUND:
  5107. break;
  5108. case FindStates.FIND_PENDING:
  5109. status = 'pending';
  5110. break;
  5111. case FindStates.FIND_NOTFOUND:
  5112. findMsg = mozL10n.get('find_not_found', null, 'Phrase not found');
  5113. notFound = true;
  5114. break;
  5115. case FindStates.FIND_WRAPPED:
  5116. if (previous) {
  5117. findMsg = mozL10n.get('find_reached_top', null,
  5118. 'Reached top of document, continued from bottom');
  5119. } else {
  5120. findMsg = mozL10n.get('find_reached_bottom', null,
  5121. 'Reached end of document, continued from top');
  5122. }
  5123. break;
  5124. }
  5125. if (notFound) {
  5126. this.findField.classList.add('notFound');
  5127. } else {
  5128. this.findField.classList.remove('notFound');
  5129. }
  5130. this.findField.setAttribute('data-status', status);
  5131. this.findMsg.textContent = findMsg;
  5132. this.updateResultsCount(matchCount);
  5133. },
  5134. updateResultsCount: function(matchCount) {
  5135. if (!this.findResultsCount) {
  5136. return; // no UI control is provided
  5137. }
  5138. // If there are no matches, hide the counter
  5139. if (!matchCount) {
  5140. this.findResultsCount.classList.add('hidden');
  5141. return;
  5142. }
  5143. // Create the match counter
  5144. this.findResultsCount.textContent = matchCount.toLocaleString();
  5145. // Show the counter
  5146. this.findResultsCount.classList.remove('hidden');
  5147. },
  5148. open: function PDFFindBar_open() {
  5149. if (!this.opened) {
  5150. this.opened = true;
  5151. this.toggleButton.classList.add('toggled');
  5152. this.bar.classList.remove('hidden');
  5153. }
  5154. this.findField.select();
  5155. this.findField.focus();
  5156. },
  5157. close: function PDFFindBar_close() {
  5158. if (!this.opened) {
  5159. return;
  5160. }
  5161. this.opened = false;
  5162. this.toggleButton.classList.remove('toggled');
  5163. this.bar.classList.add('hidden');
  5164. this.findController.active = false;
  5165. },
  5166. toggle: function PDFFindBar_toggle() {
  5167. if (this.opened) {
  5168. this.close();
  5169. } else {
  5170. this.open();
  5171. }
  5172. }
  5173. };
  5174. return PDFFindBar;
  5175. })();
  5176. exports.PDFFindBar = PDFFindBar;
  5177. }));
  5178. (function (root, factory) {
  5179. {
  5180. factory((root.pdfjsWebPDFThumbnailViewer = {}), root.pdfjsWebUIUtils,
  5181. root.pdfjsWebPDFThumbnailView);
  5182. }
  5183. }(this, function (exports, uiUtils, pdfThumbnailView) {
  5184. var watchScroll = uiUtils.watchScroll;
  5185. var getVisibleElements = uiUtils.getVisibleElements;
  5186. var scrollIntoView = uiUtils.scrollIntoView;
  5187. var PDFThumbnailView = pdfThumbnailView.PDFThumbnailView;
  5188. var THUMBNAIL_SCROLL_MARGIN = -19;
  5189. /**
  5190. * @typedef {Object} PDFThumbnailViewerOptions
  5191. * @property {HTMLDivElement} container - The container for the thumbnail
  5192. * elements.
  5193. * @property {IPDFLinkService} linkService - The navigation/linking service.
  5194. * @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
  5195. */
  5196. /**
  5197. * Simple viewer control to display thumbnails for pages.
  5198. * @class
  5199. * @implements {IRenderableView}
  5200. */
  5201. var PDFThumbnailViewer = (function PDFThumbnailViewerClosure() {
  5202. /**
  5203. * @constructs PDFThumbnailViewer
  5204. * @param {PDFThumbnailViewerOptions} options
  5205. */
  5206. function PDFThumbnailViewer(options) {
  5207. this.container = options.container;
  5208. this.renderingQueue = options.renderingQueue;
  5209. this.linkService = options.linkService;
  5210. this.scroll = watchScroll(this.container, this._scrollUpdated.bind(this));
  5211. this._resetView();
  5212. }
  5213. PDFThumbnailViewer.prototype = {
  5214. /**
  5215. * @private
  5216. */
  5217. _scrollUpdated: function PDFThumbnailViewer_scrollUpdated() {
  5218. this.renderingQueue.renderHighestPriority();
  5219. },
  5220. getThumbnail: function PDFThumbnailViewer_getThumbnail(index) {
  5221. return this.thumbnails[index];
  5222. },
  5223. /**
  5224. * @private
  5225. */
  5226. _getVisibleThumbs: function PDFThumbnailViewer_getVisibleThumbs() {
  5227. return getVisibleElements(this.container, this.thumbnails);
  5228. },
  5229. scrollThumbnailIntoView:
  5230. function PDFThumbnailViewer_scrollThumbnailIntoView(page) {
  5231. var selected = document.querySelector('.thumbnail.selected');
  5232. if (selected) {
  5233. selected.classList.remove('selected');
  5234. }
  5235. var thumbnail = document.getElementById('thumbnailContainer' + page);
  5236. if (thumbnail) {
  5237. thumbnail.classList.add('selected');
  5238. }
  5239. var visibleThumbs = this._getVisibleThumbs();
  5240. var numVisibleThumbs = visibleThumbs.views.length;
  5241. // If the thumbnail isn't currently visible, scroll it into view.
  5242. if (numVisibleThumbs > 0) {
  5243. var first = visibleThumbs.first.id;
  5244. // Account for only one thumbnail being visible.
  5245. var last = (numVisibleThumbs > 1 ? visibleThumbs.last.id : first);
  5246. if (page <= first || page >= last) {
  5247. scrollIntoView(thumbnail, { top: THUMBNAIL_SCROLL_MARGIN });
  5248. }
  5249. }
  5250. },
  5251. get pagesRotation() {
  5252. return this._pagesRotation;
  5253. },
  5254. set pagesRotation(rotation) {
  5255. this._pagesRotation = rotation;
  5256. for (var i = 0, l = this.thumbnails.length; i < l; i++) {
  5257. var thumb = this.thumbnails[i];
  5258. thumb.update(rotation);
  5259. }
  5260. },
  5261. cleanup: function PDFThumbnailViewer_cleanup() {
  5262. var tempCanvas = PDFThumbnailView.tempImageCache;
  5263. if (tempCanvas) {
  5264. // Zeroing the width and height causes Firefox to release graphics
  5265. // resources immediately, which can greatly reduce memory consumption.
  5266. tempCanvas.width = 0;
  5267. tempCanvas.height = 0;
  5268. }
  5269. PDFThumbnailView.tempImageCache = null;
  5270. },
  5271. /**
  5272. * @private
  5273. */
  5274. _resetView: function PDFThumbnailViewer_resetView() {
  5275. this.thumbnails = [];
  5276. this._pagesRotation = 0;
  5277. this._pagesRequests = [];
  5278. },
  5279. setDocument: function PDFThumbnailViewer_setDocument(pdfDocument) {
  5280. if (this.pdfDocument) {
  5281. // cleanup of the elements and views
  5282. var thumbsView = this.container;
  5283. while (thumbsView.hasChildNodes()) {
  5284. thumbsView.removeChild(thumbsView.lastChild);
  5285. }
  5286. this._resetView();
  5287. }
  5288. this.pdfDocument = pdfDocument;
  5289. if (!pdfDocument) {
  5290. return Promise.resolve();
  5291. }
  5292. return pdfDocument.getPage(1).then(function (firstPage) {
  5293. var pagesCount = pdfDocument.numPages;
  5294. var viewport = firstPage.getViewport(1.0);
  5295. for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
  5296. var thumbnail = new PDFThumbnailView({
  5297. container: this.container,
  5298. id: pageNum,
  5299. defaultViewport: viewport.clone(),
  5300. linkService: this.linkService,
  5301. renderingQueue: this.renderingQueue,
  5302. disableCanvasToImageConversion: false,
  5303. });
  5304. this.thumbnails.push(thumbnail);
  5305. }
  5306. }.bind(this));
  5307. },
  5308. /**
  5309. * @param {PDFPageView} pageView
  5310. * @returns {PDFPage}
  5311. * @private
  5312. */
  5313. _ensurePdfPageLoaded:
  5314. function PDFThumbnailViewer_ensurePdfPageLoaded(thumbView) {
  5315. if (thumbView.pdfPage) {
  5316. return Promise.resolve(thumbView.pdfPage);
  5317. }
  5318. var pageNumber = thumbView.id;
  5319. if (this._pagesRequests[pageNumber]) {
  5320. return this._pagesRequests[pageNumber];
  5321. }
  5322. var promise = this.pdfDocument.getPage(pageNumber).then(
  5323. function (pdfPage) {
  5324. thumbView.setPdfPage(pdfPage);
  5325. this._pagesRequests[pageNumber] = null;
  5326. return pdfPage;
  5327. }.bind(this));
  5328. this._pagesRequests[pageNumber] = promise;
  5329. return promise;
  5330. },
  5331. forceRendering: function () {
  5332. var visibleThumbs = this._getVisibleThumbs();
  5333. var thumbView = this.renderingQueue.getHighestPriority(visibleThumbs,
  5334. this.thumbnails,
  5335. this.scroll.down);
  5336. if (thumbView) {
  5337. this._ensurePdfPageLoaded(thumbView).then(function () {
  5338. this.renderingQueue.renderView(thumbView);
  5339. }.bind(this));
  5340. return true;
  5341. }
  5342. return false;
  5343. }
  5344. };
  5345. return PDFThumbnailViewer;
  5346. })();
  5347. exports.PDFThumbnailViewer = PDFThumbnailViewer;
  5348. }));
  5349. (function (root, factory) {
  5350. {
  5351. factory((root.pdfjsWebPDFViewer = {}), root.pdfjsWebUIUtils,
  5352. root.pdfjsWebPDFPageView, root.pdfjsWebPDFRenderingQueue,
  5353. root.pdfjsWebTextLayerBuilder, root.pdfjsWebAnnotationLayerBuilder,
  5354. root.pdfjsWebPDFLinkService, root.pdfjsWebPDFJS);
  5355. }
  5356. }(this, function (exports, uiUtils, pdfPageView, pdfRenderingQueue,
  5357. textLayerBuilder, annotationLayerBuilder, pdfLinkService,
  5358. pdfjsLib) {
  5359. var UNKNOWN_SCALE = uiUtils.UNKNOWN_SCALE;
  5360. var SCROLLBAR_PADDING = uiUtils.SCROLLBAR_PADDING;
  5361. var VERTICAL_PADDING = uiUtils.VERTICAL_PADDING;
  5362. var MAX_AUTO_SCALE = uiUtils.MAX_AUTO_SCALE;
  5363. var CSS_UNITS = uiUtils.CSS_UNITS;
  5364. var DEFAULT_SCALE = uiUtils.DEFAULT_SCALE;
  5365. var DEFAULT_SCALE_VALUE = uiUtils.DEFAULT_SCALE_VALUE;
  5366. var scrollIntoView = uiUtils.scrollIntoView;
  5367. var watchScroll = uiUtils.watchScroll;
  5368. var getVisibleElements = uiUtils.getVisibleElements;
  5369. var PDFPageView = pdfPageView.PDFPageView;
  5370. var RenderingStates = pdfRenderingQueue.RenderingStates;
  5371. var PDFRenderingQueue = pdfRenderingQueue.PDFRenderingQueue;
  5372. var TextLayerBuilder = textLayerBuilder.TextLayerBuilder;
  5373. var AnnotationLayerBuilder = annotationLayerBuilder.AnnotationLayerBuilder;
  5374. var SimpleLinkService = pdfLinkService.SimpleLinkService;
  5375. var PresentationModeState = {
  5376. UNKNOWN: 0,
  5377. NORMAL: 1,
  5378. CHANGING: 2,
  5379. FULLSCREEN: 3,
  5380. };
  5381. var DEFAULT_CACHE_SIZE = 10;
  5382. /**
  5383. * @typedef {Object} PDFViewerOptions
  5384. * @property {HTMLDivElement} container - The container for the viewer element.
  5385. * @property {HTMLDivElement} viewer - (optional) The viewer element.
  5386. * @property {IPDFLinkService} linkService - The navigation/linking service.
  5387. * @property {DownloadManager} downloadManager - (optional) The download
  5388. * manager component.
  5389. * @property {PDFRenderingQueue} renderingQueue - (optional) The rendering
  5390. * queue object.
  5391. * @property {boolean} removePageBorders - (optional) Removes the border shadow
  5392. * around the pages. The default is false.
  5393. */
  5394. /**
  5395. * Simple viewer control to display PDF content/pages.
  5396. * @class
  5397. * @implements {IRenderableView}
  5398. */
  5399. var PDFViewer = (function pdfViewer() {
  5400. function PDFPageViewBuffer(size) {
  5401. var data = [];
  5402. this.push = function cachePush(view) {
  5403. var i = data.indexOf(view);
  5404. if (i >= 0) {
  5405. data.splice(i, 1);
  5406. }
  5407. data.push(view);
  5408. if (data.length > size) {
  5409. data.shift().destroy();
  5410. }
  5411. };
  5412. this.resize = function (newSize) {
  5413. size = newSize;
  5414. while (data.length > size) {
  5415. data.shift().destroy();
  5416. }
  5417. };
  5418. }
  5419. function isSameScale(oldScale, newScale) {
  5420. if (newScale === oldScale) {
  5421. return true;
  5422. }
  5423. if (Math.abs(newScale - oldScale) < 1e-15) {
  5424. // Prevent unnecessary re-rendering of all pages when the scale
  5425. // changes only because of limited numerical precision.
  5426. return true;
  5427. }
  5428. return false;
  5429. }
  5430. /**
  5431. * @constructs PDFViewer
  5432. * @param {PDFViewerOptions} options
  5433. */
  5434. function PDFViewer(options) {
  5435. this.container = options.container;
  5436. this.viewer = options.viewer || options.container.firstElementChild;
  5437. this.linkService = options.linkService || new SimpleLinkService();
  5438. this.downloadManager = options.downloadManager || null;
  5439. this.removePageBorders = options.removePageBorders || false;
  5440. this.defaultRenderingQueue = !options.renderingQueue;
  5441. if (this.defaultRenderingQueue) {
  5442. // Custom rendering queue is not specified, using default one
  5443. this.renderingQueue = new PDFRenderingQueue();
  5444. this.renderingQueue.setViewer(this);
  5445. } else {
  5446. this.renderingQueue = options.renderingQueue;
  5447. }
  5448. this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this));
  5449. this.updateInProgress = false;
  5450. this.presentationModeState = PresentationModeState.UNKNOWN;
  5451. this._resetView();
  5452. if (this.removePageBorders) {
  5453. this.viewer.classList.add('removePageBorders');
  5454. }
  5455. }
  5456. PDFViewer.prototype = /** @lends PDFViewer.prototype */{
  5457. get pagesCount() {
  5458. return this._pages.length;
  5459. },
  5460. getPageView: function (index) {
  5461. return this._pages[index];
  5462. },
  5463. get currentPageNumber() {
  5464. return this._currentPageNumber;
  5465. },
  5466. set currentPageNumber(val) {
  5467. if (!this.pdfDocument) {
  5468. this._currentPageNumber = val;
  5469. return;
  5470. }
  5471. var event = document.createEvent('UIEvents');
  5472. event.initUIEvent('pagechange', true, true, window, 0);
  5473. event.updateInProgress = this.updateInProgress;
  5474. if (!(0 < val && val <= this.pagesCount)) {
  5475. event.pageNumber = this._currentPageNumber;
  5476. event.previousPageNumber = val;
  5477. this.container.dispatchEvent(event);
  5478. return;
  5479. }
  5480. event.previousPageNumber = this._currentPageNumber;
  5481. this._currentPageNumber = val;
  5482. event.pageNumber = val;
  5483. this.container.dispatchEvent(event);
  5484. // Check if the caller is `PDFViewer_update`, to avoid breaking scrolling.
  5485. if (this.updateInProgress) {
  5486. return;
  5487. }
  5488. this.scrollPageIntoView(val);
  5489. },
  5490. /**
  5491. * @returns {number}
  5492. */
  5493. get currentScale() {
  5494. return this._currentScale !== UNKNOWN_SCALE ? this._currentScale :
  5495. DEFAULT_SCALE;
  5496. },
  5497. /**
  5498. * @param {number} val - Scale of the pages in percents.
  5499. */
  5500. set currentScale(val) {
  5501. if (isNaN(val)) {
  5502. throw new Error('Invalid numeric scale');
  5503. }
  5504. if (!this.pdfDocument) {
  5505. this._currentScale = val;
  5506. this._currentScaleValue = val !== UNKNOWN_SCALE ? val.toString() : null;
  5507. return;
  5508. }
  5509. this._setScale(val, false);
  5510. },
  5511. /**
  5512. * @returns {string}
  5513. */
  5514. get currentScaleValue() {
  5515. return this._currentScaleValue;
  5516. },
  5517. /**
  5518. * @param val - The scale of the pages (in percent or predefined value).
  5519. */
  5520. set currentScaleValue(val) {
  5521. if (!this.pdfDocument) {
  5522. this._currentScale = isNaN(val) ? UNKNOWN_SCALE : val;
  5523. this._currentScaleValue = val;
  5524. return;
  5525. }
  5526. this._setScale(val, false);
  5527. },
  5528. /**
  5529. * @returns {number}
  5530. */
  5531. get pagesRotation() {
  5532. return this._pagesRotation;
  5533. },
  5534. /**
  5535. * @param {number} rotation - The rotation of the pages (0, 90, 180, 270).
  5536. */
  5537. set pagesRotation(rotation) {
  5538. this._pagesRotation = rotation;
  5539. for (var i = 0, l = this._pages.length; i < l; i++) {
  5540. var pageView = this._pages[i];
  5541. pageView.update(pageView.scale, rotation);
  5542. }
  5543. this._setScale(this._currentScaleValue, true);
  5544. if (this.defaultRenderingQueue) {
  5545. this.update();
  5546. }
  5547. },
  5548. /**
  5549. * @param pdfDocument {PDFDocument}
  5550. */
  5551. setDocument: function (pdfDocument) {
  5552. if (this.pdfDocument) {
  5553. this._resetView();
  5554. }
  5555. this.pdfDocument = pdfDocument;
  5556. if (!pdfDocument) {
  5557. return;
  5558. }
  5559. var pagesCount = pdfDocument.numPages;
  5560. var self = this;
  5561. var resolvePagesPromise;
  5562. var pagesPromise = new Promise(function (resolve) {
  5563. resolvePagesPromise = resolve;
  5564. });
  5565. this.pagesPromise = pagesPromise;
  5566. pagesPromise.then(function () {
  5567. var event = document.createEvent('CustomEvent');
  5568. event.initCustomEvent('pagesloaded', true, true, {
  5569. pagesCount: pagesCount
  5570. });
  5571. self.container.dispatchEvent(event);
  5572. });
  5573. var isOnePageRenderedResolved = false;
  5574. var resolveOnePageRendered = null;
  5575. var onePageRendered = new Promise(function (resolve) {
  5576. resolveOnePageRendered = resolve;
  5577. });
  5578. this.onePageRendered = onePageRendered;
  5579. var bindOnAfterAndBeforeDraw = function (pageView) {
  5580. pageView.onBeforeDraw = function pdfViewLoadOnBeforeDraw() {
  5581. // Add the page to the buffer at the start of drawing. That way it can
  5582. // be evicted from the buffer and destroyed even if we pause its
  5583. // rendering.
  5584. self._buffer.push(this);
  5585. };
  5586. // when page is painted, using the image as thumbnail base
  5587. pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
  5588. if (!isOnePageRenderedResolved) {
  5589. isOnePageRenderedResolved = true;
  5590. resolveOnePageRendered();
  5591. }
  5592. };
  5593. };
  5594. var firstPagePromise = pdfDocument.getPage(1);
  5595. this.firstPagePromise = firstPagePromise;
  5596. // Fetch a single page so we can get a viewport that will be the default
  5597. // viewport for all pages
  5598. return firstPagePromise.then(function(pdfPage) {
  5599. var scale = this.currentScale;
  5600. var viewport = pdfPage.getViewport(scale * CSS_UNITS);
  5601. for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
  5602. var textLayerFactory = null;
  5603. if (!pdfjsLib.PDFJS.disableTextLayer) {
  5604. textLayerFactory = this;
  5605. }
  5606. var pageView = new PDFPageView({
  5607. container: this.viewer,
  5608. id: pageNum,
  5609. scale: scale,
  5610. defaultViewport: viewport.clone(),
  5611. renderingQueue: this.renderingQueue,
  5612. textLayerFactory: textLayerFactory,
  5613. annotationLayerFactory: this
  5614. });
  5615. bindOnAfterAndBeforeDraw(pageView);
  5616. this._pages.push(pageView);
  5617. }
  5618. var linkService = this.linkService;
  5619. // Fetch all the pages since the viewport is needed before printing
  5620. // starts to create the correct size canvas. Wait until one page is
  5621. // rendered so we don't tie up too many resources early on.
  5622. onePageRendered.then(function () {
  5623. if (!pdfjsLib.PDFJS.disableAutoFetch) {
  5624. var getPagesLeft = pagesCount;
  5625. for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
  5626. pdfDocument.getPage(pageNum).then(function (pageNum, pdfPage) {
  5627. var pageView = self._pages[pageNum - 1];
  5628. if (!pageView.pdfPage) {
  5629. pageView.setPdfPage(pdfPage);
  5630. }
  5631. linkService.cachePageRef(pageNum, pdfPage.ref);
  5632. getPagesLeft--;
  5633. if (!getPagesLeft) {
  5634. resolvePagesPromise();
  5635. }
  5636. }.bind(null, pageNum));
  5637. }
  5638. } else {
  5639. // XXX: Printing is semi-broken with auto fetch disabled.
  5640. resolvePagesPromise();
  5641. }
  5642. });
  5643. var event = document.createEvent('CustomEvent');
  5644. event.initCustomEvent('pagesinit', true, true, null);
  5645. self.container.dispatchEvent(event);
  5646. if (this.defaultRenderingQueue) {
  5647. this.update();
  5648. }
  5649. if (this.findController) {
  5650. this.findController.resolveFirstPage();
  5651. }
  5652. }.bind(this));
  5653. },
  5654. _resetView: function () {
  5655. this._pages = [];
  5656. this._currentPageNumber = 1;
  5657. this._currentScale = UNKNOWN_SCALE;
  5658. this._currentScaleValue = null;
  5659. this._buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE);
  5660. this._location = null;
  5661. this._pagesRotation = 0;
  5662. this._pagesRequests = [];
  5663. var container = this.viewer;
  5664. while (container.hasChildNodes()) {
  5665. container.removeChild(container.lastChild);
  5666. }
  5667. },
  5668. _scrollUpdate: function PDFViewer_scrollUpdate() {
  5669. if (this.pagesCount === 0) {
  5670. return;
  5671. }
  5672. this.update();
  5673. for (var i = 0, ii = this._pages.length; i < ii; i++) {
  5674. this._pages[i].updatePosition();
  5675. }
  5676. },
  5677. _setScaleDispatchEvent: function pdfViewer_setScaleDispatchEvent(
  5678. newScale, newValue, preset) {
  5679. var event = document.createEvent('UIEvents');
  5680. event.initUIEvent('scalechange', true, true, window, 0);
  5681. event.scale = newScale;
  5682. if (preset) {
  5683. event.presetValue = newValue;
  5684. }
  5685. this.container.dispatchEvent(event);
  5686. },
  5687. _setScaleUpdatePages: function pdfViewer_setScaleUpdatePages(
  5688. newScale, newValue, noScroll, preset) {
  5689. this._currentScaleValue = newValue;
  5690. if (isSameScale(this._currentScale, newScale)) {
  5691. if (preset) {
  5692. this._setScaleDispatchEvent(newScale, newValue, true);
  5693. }
  5694. return;
  5695. }
  5696. for (var i = 0, ii = this._pages.length; i < ii; i++) {
  5697. this._pages[i].update(newScale);
  5698. }
  5699. this._currentScale = newScale;
  5700. if (!noScroll) {
  5701. var page = this._currentPageNumber, dest;
  5702. if (this._location && !pdfjsLib.PDFJS.ignoreCurrentPositionOnZoom &&
  5703. !(this.isInPresentationMode || this.isChangingPresentationMode)) {
  5704. page = this._location.pageNumber;
  5705. dest = [null, { name: 'XYZ' }, this._location.left,
  5706. this._location.top, null];
  5707. }
  5708. this.scrollPageIntoView(page, dest);
  5709. }
  5710. this._setScaleDispatchEvent(newScale, newValue, preset);
  5711. if (this.defaultRenderingQueue) {
  5712. this.update();
  5713. }
  5714. },
  5715. _setScale: function pdfViewer_setScale(value, noScroll) {
  5716. var scale = parseFloat(value);
  5717. if (scale > 0) {
  5718. this._setScaleUpdatePages(scale, value, noScroll, false);
  5719. } else {
  5720. var currentPage = this._pages[this._currentPageNumber - 1];
  5721. if (!currentPage) {
  5722. return;
  5723. }
  5724. var hPadding = (this.isInPresentationMode || this.removePageBorders) ?
  5725. 0 : SCROLLBAR_PADDING;
  5726. var vPadding = (this.isInPresentationMode || this.removePageBorders) ?
  5727. 0 : VERTICAL_PADDING;
  5728. var pageWidthScale = (this.container.clientWidth - hPadding) /
  5729. currentPage.width * currentPage.scale;
  5730. var pageHeightScale = (this.container.clientHeight - vPadding) /
  5731. currentPage.height * currentPage.scale;
  5732. switch (value) {
  5733. case 'page-actual':
  5734. scale = 1;
  5735. break;
  5736. case 'page-width':
  5737. scale = pageWidthScale;
  5738. break;
  5739. case 'page-height':
  5740. scale = pageHeightScale;
  5741. break;
  5742. case 'page-fit':
  5743. scale = Math.min(pageWidthScale, pageHeightScale);
  5744. break;
  5745. case 'auto':
  5746. var isLandscape = (currentPage.width > currentPage.height);
  5747. // For pages in landscape mode, fit the page height to the viewer
  5748. // *unless* the page would thus become too wide to fit horizontally.
  5749. var horizontalScale = isLandscape ?
  5750. Math.min(pageHeightScale, pageWidthScale) : pageWidthScale;
  5751. scale = Math.min(MAX_AUTO_SCALE, horizontalScale);
  5752. break;
  5753. default:
  5754. console.error('pdfViewSetScale: \'' + value +
  5755. '\' is an unknown zoom value.');
  5756. return;
  5757. }
  5758. this._setScaleUpdatePages(scale, value, noScroll, true);
  5759. }
  5760. },
  5761. /**
  5762. * Scrolls page into view.
  5763. * @param {number} pageNumber
  5764. * @param {Array} dest - (optional) original PDF destination array:
  5765. * <page-ref> </XYZ|FitXXX> <args..>
  5766. */
  5767. scrollPageIntoView: function PDFViewer_scrollPageIntoView(pageNumber,
  5768. dest) {
  5769. if (!this.pdfDocument) {
  5770. return;
  5771. }
  5772. var pageView = this._pages[pageNumber - 1];
  5773. if (this.isInPresentationMode) {
  5774. if (this._currentPageNumber !== pageView.id) {
  5775. // Avoid breaking getVisiblePages in presentation mode.
  5776. this.currentPageNumber = pageView.id;
  5777. return;
  5778. }
  5779. dest = null;
  5780. // Fixes the case when PDF has different page sizes.
  5781. this._setScale(this._currentScaleValue, true);
  5782. }
  5783. if (!dest) {
  5784. scrollIntoView(pageView.div);
  5785. return;
  5786. }
  5787. var x = 0, y = 0;
  5788. var width = 0, height = 0, widthScale, heightScale;
  5789. var changeOrientation = (pageView.rotation % 180 === 0 ? false : true);
  5790. var pageWidth = (changeOrientation ? pageView.height : pageView.width) /
  5791. pageView.scale / CSS_UNITS;
  5792. var pageHeight = (changeOrientation ? pageView.width : pageView.height) /
  5793. pageView.scale / CSS_UNITS;
  5794. var scale = 0;
  5795. switch (dest[1].name) {
  5796. case 'XYZ':
  5797. x = dest[2];
  5798. y = dest[3];
  5799. scale = dest[4];
  5800. // If x and/or y coordinates are not supplied, default to
  5801. // _top_ left of the page (not the obvious bottom left,
  5802. // since aligning the bottom of the intended page with the
  5803. // top of the window is rarely helpful).
  5804. x = x !== null ? x : 0;
  5805. y = y !== null ? y : pageHeight;
  5806. break;
  5807. case 'Fit':
  5808. case 'FitB':
  5809. scale = 'page-fit';
  5810. break;
  5811. case 'FitH':
  5812. case 'FitBH':
  5813. y = dest[2];
  5814. scale = 'page-width';
  5815. // According to the PDF spec, section 12.3.2.2, a `null` value in the
  5816. // parameter should maintain the position relative to the new page.
  5817. if (y === null && this._location) {
  5818. x = this._location.left;
  5819. y = this._location.top;
  5820. }
  5821. break;
  5822. case 'FitV':
  5823. case 'FitBV':
  5824. x = dest[2];
  5825. width = pageWidth;
  5826. height = pageHeight;
  5827. scale = 'page-height';
  5828. break;
  5829. case 'FitR':
  5830. x = dest[2];
  5831. y = dest[3];
  5832. width = dest[4] - x;
  5833. height = dest[5] - y;
  5834. var hPadding = this.removePageBorders ? 0 : SCROLLBAR_PADDING;
  5835. var vPadding = this.removePageBorders ? 0 : VERTICAL_PADDING;
  5836. widthScale = (this.container.clientWidth - hPadding) /
  5837. width / CSS_UNITS;
  5838. heightScale = (this.container.clientHeight - vPadding) /
  5839. height / CSS_UNITS;
  5840. scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
  5841. break;
  5842. default:
  5843. return;
  5844. }
  5845. if (scale && scale !== this._currentScale) {
  5846. this.currentScaleValue = scale;
  5847. } else if (this._currentScale === UNKNOWN_SCALE) {
  5848. this.currentScaleValue = DEFAULT_SCALE_VALUE;
  5849. }
  5850. if (scale === 'page-fit' && !dest[4]) {
  5851. scrollIntoView(pageView.div);
  5852. return;
  5853. }
  5854. var boundingRect = [
  5855. pageView.viewport.convertToViewportPoint(x, y),
  5856. pageView.viewport.convertToViewportPoint(x + width, y + height)
  5857. ];
  5858. var left = Math.min(boundingRect[0][0], boundingRect[1][0]);
  5859. var top = Math.min(boundingRect[0][1], boundingRect[1][1]);
  5860. scrollIntoView(pageView.div, { left: left, top: top });
  5861. },
  5862. _updateLocation: function (firstPage) {
  5863. var currentScale = this._currentScale;
  5864. var currentScaleValue = this._currentScaleValue;
  5865. var normalizedScaleValue =
  5866. parseFloat(currentScaleValue) === currentScale ?
  5867. Math.round(currentScale * 10000) / 100 : currentScaleValue;
  5868. var pageNumber = firstPage.id;
  5869. var pdfOpenParams = '#page=' + pageNumber;
  5870. pdfOpenParams += '&zoom=' + normalizedScaleValue;
  5871. var currentPageView = this._pages[pageNumber - 1];
  5872. var container = this.container;
  5873. var topLeft = currentPageView.getPagePoint(
  5874. (container.scrollLeft - firstPage.x),
  5875. (container.scrollTop - firstPage.y));
  5876. var intLeft = Math.round(topLeft[0]);
  5877. var intTop = Math.round(topLeft[1]);
  5878. pdfOpenParams += ',' + intLeft + ',' + intTop;
  5879. this._location = {
  5880. pageNumber: pageNumber,
  5881. scale: normalizedScaleValue,
  5882. top: intTop,
  5883. left: intLeft,
  5884. pdfOpenParams: pdfOpenParams
  5885. };
  5886. },
  5887. update: function PDFViewer_update() {
  5888. var visible = this._getVisiblePages();
  5889. var visiblePages = visible.views;
  5890. if (visiblePages.length === 0) {
  5891. return;
  5892. }
  5893. this.updateInProgress = true;
  5894. var suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE,
  5895. 2 * visiblePages.length + 1);
  5896. this._buffer.resize(suggestedCacheSize);
  5897. this.renderingQueue.renderHighestPriority(visible);
  5898. var currentId = this._currentPageNumber;
  5899. var firstPage = visible.first;
  5900. for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
  5901. i < ii; ++i) {
  5902. var page = visiblePages[i];
  5903. if (page.percent < 100) {
  5904. break;
  5905. }
  5906. if (page.id === currentId) {
  5907. stillFullyVisible = true;
  5908. break;
  5909. }
  5910. }
  5911. if (!stillFullyVisible) {
  5912. currentId = visiblePages[0].id;
  5913. }
  5914. if (!this.isInPresentationMode) {
  5915. this.currentPageNumber = currentId;
  5916. }
  5917. this._updateLocation(firstPage);
  5918. this.updateInProgress = false;
  5919. var event = document.createEvent('UIEvents');
  5920. event.initUIEvent('updateviewarea', true, true, window, 0);
  5921. event.location = this._location;
  5922. this.container.dispatchEvent(event);
  5923. },
  5924. containsElement: function (element) {
  5925. return this.container.contains(element);
  5926. },
  5927. focus: function () {
  5928. this.container.focus();
  5929. },
  5930. get isInPresentationMode() {
  5931. return this.presentationModeState === PresentationModeState.FULLSCREEN;
  5932. },
  5933. get isChangingPresentationMode() {
  5934. return this.presentationModeState === PresentationModeState.CHANGING;
  5935. },
  5936. get isHorizontalScrollbarEnabled() {
  5937. return (this.isInPresentationMode ?
  5938. false : (this.container.scrollWidth > this.container.clientWidth));
  5939. },
  5940. _getVisiblePages: function () {
  5941. if (!this.isInPresentationMode) {
  5942. return getVisibleElements(this.container, this._pages, true);
  5943. } else {
  5944. // The algorithm in getVisibleElements doesn't work in all browsers and
  5945. // configurations when presentation mode is active.
  5946. var visible = [];
  5947. var currentPage = this._pages[this._currentPageNumber - 1];
  5948. visible.push({ id: currentPage.id, view: currentPage });
  5949. return { first: currentPage, last: currentPage, views: visible };
  5950. }
  5951. },
  5952. cleanup: function () {
  5953. for (var i = 0, ii = this._pages.length; i < ii; i++) {
  5954. if (this._pages[i] &&
  5955. this._pages[i].renderingState !== RenderingStates.FINISHED) {
  5956. this._pages[i].reset();
  5957. }
  5958. }
  5959. },
  5960. /**
  5961. * @param {PDFPageView} pageView
  5962. * @returns {PDFPage}
  5963. * @private
  5964. */
  5965. _ensurePdfPageLoaded: function (pageView) {
  5966. if (pageView.pdfPage) {
  5967. return Promise.resolve(pageView.pdfPage);
  5968. }
  5969. var pageNumber = pageView.id;
  5970. if (this._pagesRequests[pageNumber]) {
  5971. return this._pagesRequests[pageNumber];
  5972. }
  5973. var promise = this.pdfDocument.getPage(pageNumber).then(
  5974. function (pdfPage) {
  5975. pageView.setPdfPage(pdfPage);
  5976. this._pagesRequests[pageNumber] = null;
  5977. return pdfPage;
  5978. }.bind(this));
  5979. this._pagesRequests[pageNumber] = promise;
  5980. return promise;
  5981. },
  5982. forceRendering: function (currentlyVisiblePages) {
  5983. var visiblePages = currentlyVisiblePages || this._getVisiblePages();
  5984. var pageView = this.renderingQueue.getHighestPriority(visiblePages,
  5985. this._pages,
  5986. this.scroll.down);
  5987. if (pageView) {
  5988. this._ensurePdfPageLoaded(pageView).then(function () {
  5989. this.renderingQueue.renderView(pageView);
  5990. }.bind(this));
  5991. return true;
  5992. }
  5993. return false;
  5994. },
  5995. getPageTextContent: function (pageIndex) {
  5996. return this.pdfDocument.getPage(pageIndex + 1).then(function (page) {
  5997. return page.getTextContent({ normalizeWhitespace: true });
  5998. });
  5999. },
  6000. /**
  6001. * @param {HTMLDivElement} textLayerDiv
  6002. * @param {number} pageIndex
  6003. * @param {PageViewport} viewport
  6004. * @returns {TextLayerBuilder}
  6005. */
  6006. createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) {
  6007. return new TextLayerBuilder({
  6008. textLayerDiv: textLayerDiv,
  6009. pageIndex: pageIndex,
  6010. viewport: viewport,
  6011. findController: this.isInPresentationMode ? null : this.findController
  6012. });
  6013. },
  6014. /**
  6015. * @param {HTMLDivElement} pageDiv
  6016. * @param {PDFPage} pdfPage
  6017. * @returns {AnnotationLayerBuilder}
  6018. */
  6019. createAnnotationLayerBuilder: function (pageDiv, pdfPage) {
  6020. return new AnnotationLayerBuilder({
  6021. pageDiv: pageDiv,
  6022. pdfPage: pdfPage,
  6023. linkService: this.linkService,
  6024. downloadManager: this.downloadManager
  6025. });
  6026. },
  6027. setFindController: function (findController) {
  6028. this.findController = findController;
  6029. },
  6030. };
  6031. return PDFViewer;
  6032. })();
  6033. exports.PresentationModeState = PresentationModeState;
  6034. exports.PDFViewer = PDFViewer;
  6035. }));
  6036. (function (root, factory) {
  6037. {
  6038. factory((root.pdfjsWebApp = {}), root.pdfjsWebUIUtils,
  6039. root.pdfjsWebFirefoxCom, root.pdfjsWebDownloadManager,
  6040. root.pdfjsWebPDFHistory, root.pdfjsWebPreferences,
  6041. root.pdfjsWebPDFSidebar, root.pdfjsWebViewHistory,
  6042. root.pdfjsWebPDFThumbnailViewer, root.pdfjsWebSecondaryToolbar,
  6043. root.pdfjsWebPasswordPrompt, root.pdfjsWebPDFPresentationMode,
  6044. root.pdfjsWebPDFDocumentProperties, root.pdfjsWebHandTool,
  6045. root.pdfjsWebPDFViewer, root.pdfjsWebPDFRenderingQueue,
  6046. root.pdfjsWebPDFLinkService, root.pdfjsWebPDFOutlineViewer,
  6047. root.pdfjsWebOverlayManager, root.pdfjsWebPDFAttachmentViewer,
  6048. root.pdfjsWebPDFFindController, root.pdfjsWebPDFFindBar,
  6049. root.pdfjsWebMozPrintCallbackPolyfill, root.pdfjsWebPDFJS);
  6050. }
  6051. }(this, function (exports, uiUtilsLib, firefoxComLib, downloadManagerLib,
  6052. pdfHistoryLib, preferencesLib, pdfSidebarLib, viewHistoryLib,
  6053. pdfThumbnailViewerLib, secondaryToolbarLib, passwordPromptLib,
  6054. pdfPresentationModeLib, pdfDocumentPropertiesLib, handToolLib,
  6055. pdfViewerLib, pdfRenderingQueueLib, pdfLinkServiceLib,
  6056. pdfOutlineViewerLib, overlayManagerLib,
  6057. pdfAttachmentViewerLib, pdfFindControllerLib, pdfFindBarLib,
  6058. mozPrintCallbackPolyfillLib, pdfjsLib) {
  6059. var FirefoxCom = firefoxComLib.FirefoxCom;
  6060. var UNKNOWN_SCALE = uiUtilsLib.UNKNOWN_SCALE;
  6061. var DEFAULT_SCALE_VALUE = uiUtilsLib.DEFAULT_SCALE_VALUE;
  6062. var ProgressBar = uiUtilsLib.ProgressBar;
  6063. var getPDFFileNameFromURL = uiUtilsLib.getPDFFileNameFromURL;
  6064. var noContextMenuHandler = uiUtilsLib.noContextMenuHandler;
  6065. var mozL10n = uiUtilsLib.mozL10n;
  6066. var parseQueryString = uiUtilsLib.parseQueryString;
  6067. var DownloadManager = downloadManagerLib.DownloadManager ||
  6068. firefoxComLib.DownloadManager;
  6069. var PDFHistory = pdfHistoryLib.PDFHistory;
  6070. var Preferences = preferencesLib.Preferences;
  6071. var SidebarView = pdfSidebarLib.SidebarView;
  6072. var PDFSidebar = pdfSidebarLib.PDFSidebar;
  6073. var ViewHistory = viewHistoryLib.ViewHistory;
  6074. var PDFThumbnailViewer = pdfThumbnailViewerLib.PDFThumbnailViewer;
  6075. var SecondaryToolbar = secondaryToolbarLib.SecondaryToolbar;
  6076. var PasswordPrompt = passwordPromptLib.PasswordPrompt;
  6077. var PDFPresentationMode = pdfPresentationModeLib.PDFPresentationMode;
  6078. var PDFDocumentProperties = pdfDocumentPropertiesLib.PDFDocumentProperties;
  6079. var HandTool = handToolLib.HandTool;
  6080. var PresentationModeState = pdfViewerLib.PresentationModeState;
  6081. var PDFViewer = pdfViewerLib.PDFViewer;
  6082. var RenderingStates = pdfRenderingQueueLib.RenderingStates;
  6083. var PDFRenderingQueue = pdfRenderingQueueLib.PDFRenderingQueue;
  6084. var PDFLinkService = pdfLinkServiceLib.PDFLinkService;
  6085. var PDFOutlineViewer = pdfOutlineViewerLib.PDFOutlineViewer;
  6086. var OverlayManager = overlayManagerLib.OverlayManager;
  6087. var PDFAttachmentViewer = pdfAttachmentViewerLib.PDFAttachmentViewer;
  6088. var PDFFindController = pdfFindControllerLib.PDFFindController;
  6089. var PDFFindBar = pdfFindBarLib.PDFFindBar;
  6090. var DEFAULT_SCALE_DELTA = 1.1;
  6091. var MIN_SCALE = 0.25;
  6092. var MAX_SCALE = 10.0;
  6093. var SCALE_SELECT_CONTAINER_PADDING = 8;
  6094. var SCALE_SELECT_PADDING = 22;
  6095. var PAGE_NUMBER_LOADING_INDICATOR = 'visiblePageIsLoading';
  6096. var DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000;
  6097. function configure(PDFJS) {
  6098. PDFJS.imageResourcesPath = './images/';
  6099. PDFJS.workerSrc = '../build/pdf.worker.js';
  6100. PDFJS.cMapUrl = '../web/cmaps/';
  6101. PDFJS.cMapPacked = true;
  6102. }
  6103. var PDFViewerApplication = {
  6104. initialBookmark: document.location.hash.substring(1),
  6105. initialDestination: null,
  6106. initialized: false,
  6107. fellback: false,
  6108. appConfig: null,
  6109. pdfDocument: null,
  6110. pdfLoadingTask: null,
  6111. printing: false,
  6112. /** @type {PDFViewer} */
  6113. pdfViewer: null,
  6114. /** @type {PDFThumbnailViewer} */
  6115. pdfThumbnailViewer: null,
  6116. /** @type {PDFRenderingQueue} */
  6117. pdfRenderingQueue: null,
  6118. /** @type {PDFPresentationMode} */
  6119. pdfPresentationMode: null,
  6120. /** @type {PDFDocumentProperties} */
  6121. pdfDocumentProperties: null,
  6122. /** @type {PDFLinkService} */
  6123. pdfLinkService: null,
  6124. /** @type {PDFHistory} */
  6125. pdfHistory: null,
  6126. /** @type {PDFSidebar} */
  6127. pdfSidebar: null,
  6128. /** @type {PDFOutlineViewer} */
  6129. pdfOutlineViewer: null,
  6130. /** @type {PDFAttachmentViewer} */
  6131. pdfAttachmentViewer: null,
  6132. /** @type {ViewHistory} */
  6133. store: null,
  6134. pageRotation: 0,
  6135. isInitialViewSet: false,
  6136. animationStartedPromise: null,
  6137. preferenceSidebarViewOnLoad: SidebarView.NONE,
  6138. preferencePdfBugEnabled: false,
  6139. preferenceShowPreviousViewOnLoad: true,
  6140. preferenceDefaultZoomValue: '',
  6141. isViewerEmbedded: (window.parent !== window),
  6142. url: '',
  6143. // called once when the document is loaded
  6144. initialize: function pdfViewInitialize(appConfig) {
  6145. configure(pdfjsLib.PDFJS);
  6146. this.appConfig = appConfig;
  6147. var pdfRenderingQueue = new PDFRenderingQueue();
  6148. pdfRenderingQueue.onIdle = this.cleanup.bind(this);
  6149. this.pdfRenderingQueue = pdfRenderingQueue;
  6150. var pdfLinkService = new PDFLinkService();
  6151. this.pdfLinkService = pdfLinkService;
  6152. var container = appConfig.mainContainer;
  6153. var viewer = appConfig.viewerContainer;
  6154. this.pdfViewer = new PDFViewer({
  6155. container: container,
  6156. viewer: viewer,
  6157. renderingQueue: pdfRenderingQueue,
  6158. linkService: pdfLinkService,
  6159. downloadManager: new DownloadManager()
  6160. });
  6161. pdfRenderingQueue.setViewer(this.pdfViewer);
  6162. pdfLinkService.setViewer(this.pdfViewer);
  6163. var thumbnailContainer = appConfig.sidebar.thumbnailView;
  6164. this.pdfThumbnailViewer = new PDFThumbnailViewer({
  6165. container: thumbnailContainer,
  6166. renderingQueue: pdfRenderingQueue,
  6167. linkService: pdfLinkService
  6168. });
  6169. pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
  6170. Preferences.initialize();
  6171. this.preferences = Preferences;
  6172. this.pdfHistory = new PDFHistory({
  6173. linkService: pdfLinkService
  6174. });
  6175. pdfLinkService.setHistory(this.pdfHistory);
  6176. this.findController = new PDFFindController({
  6177. pdfViewer: this.pdfViewer,
  6178. integratedFind: this.supportsIntegratedFind
  6179. });
  6180. this.pdfViewer.setFindController(this.findController);
  6181. // FIXME better PDFFindBar constructor parameters
  6182. var findBarConfig = Object.create(appConfig.findBar);
  6183. findBarConfig.findController = this.findController;
  6184. this.findBar = new PDFFindBar(findBarConfig);
  6185. this.findController.setFindBar(this.findBar);
  6186. this.overlayManager = OverlayManager;
  6187. this.handTool = new HandTool({
  6188. container: container,
  6189. toggleHandTool: appConfig.secondaryToolbar.toggleHandTool
  6190. });
  6191. this.pdfDocumentProperties =
  6192. new PDFDocumentProperties(appConfig.documentProperties);
  6193. SecondaryToolbar.initialize(appConfig.secondaryToolbar);
  6194. this.secondaryToolbar = SecondaryToolbar;
  6195. if (this.supportsFullscreen) {
  6196. var toolbar = SecondaryToolbar;
  6197. this.pdfPresentationMode = new PDFPresentationMode({
  6198. container: container,
  6199. viewer: viewer,
  6200. pdfViewer: this.pdfViewer,
  6201. contextMenuItems: [
  6202. { element: appConfig.fullscreen.contextFirstPage,
  6203. handler: toolbar.firstPageClick.bind(toolbar) },
  6204. { element: appConfig.fullscreen.contextLastPage,
  6205. handler: toolbar.lastPageClick.bind(toolbar) },
  6206. { element: appConfig.fullscreen.contextPageRotateCw,
  6207. handler: toolbar.pageRotateCwClick.bind(toolbar) },
  6208. { element: appConfig.fullscreen.contextPageRotateCcw,
  6209. handler: toolbar.pageRotateCcwClick.bind(toolbar) }
  6210. ]
  6211. });
  6212. }
  6213. this.passwordPrompt = new PasswordPrompt(appConfig.passwordOverlay);
  6214. this.pdfOutlineViewer = new PDFOutlineViewer({
  6215. container: appConfig.sidebar.outlineView,
  6216. linkService: pdfLinkService,
  6217. });
  6218. this.pdfAttachmentViewer = new PDFAttachmentViewer({
  6219. container: appConfig.sidebar.attachmentsView,
  6220. downloadManager: new DownloadManager(),
  6221. });
  6222. // FIXME better PDFSidebar constructor parameters
  6223. var sidebarConfig = Object.create(appConfig.sidebar);
  6224. sidebarConfig.pdfViewer = this.pdfViewer;
  6225. sidebarConfig.pdfThumbnailViewer = this.pdfThumbnailViewer;
  6226. sidebarConfig.pdfOutlineViewer = this.pdfOutlineViewer;
  6227. this.pdfSidebar = new PDFSidebar(sidebarConfig);
  6228. this.pdfSidebar.onToggled = this.forceRendering.bind(this);
  6229. var self = this;
  6230. var PDFJS = pdfjsLib.PDFJS;
  6231. var initializedPromise = Promise.all([
  6232. Preferences.get('enableWebGL').then(function resolved(value) {
  6233. PDFJS.disableWebGL = !value;
  6234. }),
  6235. Preferences.get('sidebarViewOnLoad').then(function resolved(value) {
  6236. self.preferenceSidebarViewOnLoad = value;
  6237. }),
  6238. Preferences.get('pdfBugEnabled').then(function resolved(value) {
  6239. self.preferencePdfBugEnabled = value;
  6240. }),
  6241. Preferences.get('showPreviousViewOnLoad').then(function resolved(value) {
  6242. self.preferenceShowPreviousViewOnLoad = value;
  6243. }),
  6244. Preferences.get('defaultZoomValue').then(function resolved(value) {
  6245. self.preferenceDefaultZoomValue = value;
  6246. }),
  6247. Preferences.get('disableTextLayer').then(function resolved(value) {
  6248. if (PDFJS.disableTextLayer === true) {
  6249. return;
  6250. }
  6251. PDFJS.disableTextLayer = value;
  6252. }),
  6253. Preferences.get('disableRange').then(function resolved(value) {
  6254. if (PDFJS.disableRange === true) {
  6255. return;
  6256. }
  6257. PDFJS.disableRange = value;
  6258. }),
  6259. Preferences.get('disableStream').then(function resolved(value) {
  6260. if (PDFJS.disableStream === true) {
  6261. return;
  6262. }
  6263. PDFJS.disableStream = value;
  6264. }),
  6265. Preferences.get('disableAutoFetch').then(function resolved(value) {
  6266. PDFJS.disableAutoFetch = value;
  6267. }),
  6268. Preferences.get('disableFontFace').then(function resolved(value) {
  6269. if (PDFJS.disableFontFace === true) {
  6270. return;
  6271. }
  6272. PDFJS.disableFontFace = value;
  6273. }),
  6274. Preferences.get('useOnlyCssZoom').then(function resolved(value) {
  6275. PDFJS.useOnlyCssZoom = value;
  6276. }),
  6277. Preferences.get('externalLinkTarget').then(function resolved(value) {
  6278. if (PDFJS.isExternalLinkTargetSet()) {
  6279. return;
  6280. }
  6281. PDFJS.externalLinkTarget = value;
  6282. }),
  6283. // TODO move more preferences and other async stuff here
  6284. ]).catch(function (reason) { });
  6285. return initializedPromise.then(function () {
  6286. if (self.isViewerEmbedded && !PDFJS.isExternalLinkTargetSet()) {
  6287. // Prevent external links from "replacing" the viewer,
  6288. // when it's embedded in e.g. an iframe or an object.
  6289. PDFJS.externalLinkTarget = PDFJS.LinkTarget.TOP;
  6290. }
  6291. self.initialized = true;
  6292. });
  6293. },
  6294. run: function pdfViewRun(config) {
  6295. this.initialize(config).then(webViewerInitialized);
  6296. },
  6297. zoomIn: function pdfViewZoomIn(ticks) {
  6298. var newScale = this.pdfViewer.currentScale;
  6299. do {
  6300. newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
  6301. newScale = Math.ceil(newScale * 10) / 10;
  6302. newScale = Math.min(MAX_SCALE, newScale);
  6303. } while (--ticks > 0 && newScale < MAX_SCALE);
  6304. this.pdfViewer.currentScaleValue = newScale;
  6305. },
  6306. zoomOut: function pdfViewZoomOut(ticks) {
  6307. var newScale = this.pdfViewer.currentScale;
  6308. do {
  6309. newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
  6310. newScale = Math.floor(newScale * 10) / 10;
  6311. newScale = Math.max(MIN_SCALE, newScale);
  6312. } while (--ticks > 0 && newScale > MIN_SCALE);
  6313. this.pdfViewer.currentScaleValue = newScale;
  6314. },
  6315. get pagesCount() {
  6316. return this.pdfDocument.numPages;
  6317. },
  6318. set page(val) {
  6319. this.pdfLinkService.page = val;
  6320. },
  6321. get page() { // TODO remove
  6322. return this.pdfLinkService.page;
  6323. },
  6324. get supportsPrinting() {
  6325. var canvas = document.createElement('canvas');
  6326. var value = 'mozPrintCallback' in canvas;
  6327. return pdfjsLib.shadow(this, 'supportsPrinting', value);
  6328. },
  6329. get supportsFullscreen() {
  6330. var doc = document.documentElement;
  6331. var support = !!(doc.requestFullscreen || doc.mozRequestFullScreen ||
  6332. doc.webkitRequestFullScreen || doc.msRequestFullscreen);
  6333. if (document.fullscreenEnabled === false ||
  6334. document.mozFullScreenEnabled === false ||
  6335. document.webkitFullscreenEnabled === false ||
  6336. document.msFullscreenEnabled === false) {
  6337. support = false;
  6338. }
  6339. if (support && pdfjsLib.PDFJS.disableFullscreen === true) {
  6340. support = false;
  6341. }
  6342. return pdfjsLib.shadow(this, 'supportsFullscreen', support);
  6343. },
  6344. get supportsIntegratedFind() {
  6345. var support = false;
  6346. return pdfjsLib.shadow(this, 'supportsIntegratedFind', support);
  6347. },
  6348. get supportsDocumentFonts() {
  6349. var support = true;
  6350. return pdfjsLib.shadow(this, 'supportsDocumentFonts', support);
  6351. },
  6352. get supportsDocumentColors() {
  6353. var support = true;
  6354. return pdfjsLib.shadow(this, 'supportsDocumentColors', support);
  6355. },
  6356. get loadingBar() {
  6357. var bar = new ProgressBar('#loadingBar', {});
  6358. return pdfjsLib.shadow(this, 'loadingBar', bar);
  6359. },
  6360. get supportedMouseWheelZoomModifierKeys() {
  6361. var support = {
  6362. ctrlKey: true,
  6363. metaKey: true,
  6364. };
  6365. return pdfjsLib.shadow(this, 'supportedMouseWheelZoomModifierKeys',
  6366. support);
  6367. },
  6368. setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
  6369. this.url = url;
  6370. try {
  6371. this.setTitle(decodeURIComponent(
  6372. pdfjsLib.getFilenameFromUrl(url)) || url);
  6373. } catch (e) {
  6374. // decodeURIComponent may throw URIError,
  6375. // fall back to using the unprocessed url in that case
  6376. this.setTitle(url);
  6377. }
  6378. },
  6379. setTitle: function pdfViewSetTitle(title) {
  6380. if (this.isViewerEmbedded) {
  6381. // Embedded PDF viewers should not be changing their parent page's title.
  6382. return;
  6383. }
  6384. //document.title = pdftitle;
  6385. },
  6386. /**
  6387. * Closes opened PDF document.
  6388. * @returns {Promise} - Returns the promise, which is resolved when all
  6389. * destruction is completed.
  6390. */
  6391. close: function pdfViewClose() {
  6392. var errorWrapper = this.appConfig.errorWrapper.container;
  6393. errorWrapper.setAttribute('hidden', 'true');
  6394. if (!this.pdfLoadingTask) {
  6395. return Promise.resolve();
  6396. }
  6397. var promise = this.pdfLoadingTask.destroy();
  6398. this.pdfLoadingTask = null;
  6399. if (this.pdfDocument) {
  6400. this.pdfDocument = null;
  6401. this.pdfThumbnailViewer.setDocument(null);
  6402. this.pdfViewer.setDocument(null);
  6403. this.pdfLinkService.setDocument(null, null);
  6404. }
  6405. this.store = null;
  6406. this.isInitialViewSet = false;
  6407. this.pdfSidebar.reset();
  6408. this.pdfOutlineViewer.reset();
  6409. this.pdfAttachmentViewer.reset();
  6410. this.findController.reset();
  6411. this.findBar.reset();
  6412. if (typeof PDFBug !== 'undefined') {
  6413. PDFBug.cleanup();
  6414. }
  6415. return promise;
  6416. },
  6417. /**
  6418. * Opens PDF document specified by URL or array with additional arguments.
  6419. * @param {string|TypedArray|ArrayBuffer} file - PDF location or binary data.
  6420. * @param {Object} args - (optional) Additional arguments for the getDocument
  6421. * call, e.g. HTTP headers ('httpHeaders') or
  6422. * alternative data transport ('range').
  6423. * @returns {Promise} - Returns the promise, which is resolved when document
  6424. * is opened.
  6425. */
  6426. open: function pdfViewOpen(file, args) {
  6427. var scale = 0;
  6428. if (arguments.length > 2 || typeof args === 'number') {
  6429. console.warn('Call of open() with obsolete signature.');
  6430. if (typeof args === 'number') {
  6431. scale = args; // scale argument was found
  6432. }
  6433. args = arguments[4] || null;
  6434. if (arguments[3] && typeof arguments[3] === 'object') {
  6435. // The pdfDataRangeTransport argument is present.
  6436. args = Object.create(args);
  6437. args.range = arguments[3];
  6438. }
  6439. if (typeof arguments[2] === 'string') {
  6440. // The password argument is present.
  6441. args = Object.create(args);
  6442. args.password = arguments[2];
  6443. }
  6444. }
  6445. if (this.pdfLoadingTask) {
  6446. // We need to destroy already opened document.
  6447. return this.close().then(function () {
  6448. // Reload the preferences if a document was previously opened.
  6449. Preferences.reload();
  6450. // ... and repeat the open() call.
  6451. return this.open(file, args);
  6452. }.bind(this));
  6453. }
  6454. var parameters = Object.create(null);
  6455. if (typeof file === 'string') { // URL
  6456. this.setTitleUsingUrl(file);
  6457. parameters.url = file;
  6458. } else if (file && 'byteLength' in file) { // ArrayBuffer
  6459. parameters.data = file;
  6460. } else if (file.url && file.originalUrl) {
  6461. this.setTitleUsingUrl(file.originalUrl);
  6462. parameters.url = file.url;
  6463. }
  6464. if (args) {
  6465. for (var prop in args) {
  6466. parameters[prop] = args[prop];
  6467. }
  6468. }
  6469. var self = this;
  6470. self.downloadComplete = false;
  6471. var loadingTask = pdfjsLib.getDocument(parameters);
  6472. this.pdfLoadingTask = loadingTask;
  6473. loadingTask.onPassword = function passwordNeeded(updateCallback, reason) {
  6474. self.passwordPrompt.setUpdateCallback(updateCallback, reason);
  6475. self.passwordPrompt.open();
  6476. };
  6477. loadingTask.onProgress = function getDocumentProgress(progressData) {
  6478. self.progress(progressData.loaded / progressData.total);
  6479. };
  6480. // Listen for unsupported features to trigger the fallback UI.
  6481. loadingTask.onUnsupportedFeature = this.fallback.bind(this);
  6482. var result = loadingTask.promise.then(
  6483. function getDocumentCallback(pdfDocument) {
  6484. self.load(pdfDocument, scale);
  6485. },
  6486. function getDocumentError(exception) {
  6487. var message = exception && exception.message;
  6488. var loadingErrorMessage = mozL10n.get('loading_error', null,
  6489. 'An error occurred while loading the PDF.');
  6490. if (exception instanceof pdfjsLib.InvalidPDFException) {
  6491. // change error message also for other builds
  6492. loadingErrorMessage = mozL10n.get('invalid_file_error', null,
  6493. 'Invalid or corrupted PDF file.');
  6494. } else if (exception instanceof pdfjsLib.MissingPDFException) {
  6495. // special message for missing PDF's
  6496. loadingErrorMessage = mozL10n.get('missing_file_error', null,
  6497. 'Missing PDF file.');
  6498. } else if (exception instanceof pdfjsLib.UnexpectedResponseException) {
  6499. loadingErrorMessage = mozL10n.get('unexpected_response_error', null,
  6500. 'Unexpected server response.');
  6501. }
  6502. var moreInfo = {
  6503. message: message
  6504. };
  6505. self.error(loadingErrorMessage, moreInfo);
  6506. throw new Error(loadingErrorMessage);
  6507. }
  6508. );
  6509. if (args && args.length) {
  6510. PDFViewerApplication.pdfDocumentProperties.setFileSize(args.length);
  6511. }
  6512. return result;
  6513. },
  6514. download: function pdfViewDownload() {
  6515. function downloadByUrl() {
  6516. downloadManager.downloadUrl(url, filename);
  6517. }
  6518. var url = this.url.split('#')[0];
  6519. var filename = getPDFFileNameFromURL(url);
  6520. var downloadManager = new DownloadManager();
  6521. downloadManager.onerror = function (err) {
  6522. // This error won't really be helpful because it's likely the
  6523. // fallback won't work either (or is already open).
  6524. PDFViewerApplication.error('PDF failed to download.');
  6525. };
  6526. if (!this.pdfDocument) { // the PDF is not ready yet
  6527. downloadByUrl();
  6528. return;
  6529. }
  6530. if (!this.downloadComplete) { // the PDF is still downloading
  6531. downloadByUrl();
  6532. return;
  6533. }
  6534. this.pdfDocument.getData().then(
  6535. function getDataSuccess(data) {
  6536. var blob = pdfjsLib.createBlob(data, 'application/pdf');
  6537. downloadManager.download(blob, url, filename);
  6538. },
  6539. downloadByUrl // Error occurred try downloading with just the url.
  6540. ).then(null, downloadByUrl);
  6541. },
  6542. fallback: function pdfViewFallback(featureId) {
  6543. },
  6544. /**
  6545. * Show the error box.
  6546. * @param {String} message A message that is human readable.
  6547. * @param {Object} moreInfo (optional) Further information about the error
  6548. * that is more technical. Should have a 'message'
  6549. * and optionally a 'stack' property.
  6550. */
  6551. error: function pdfViewError(message, moreInfo) {
  6552. var moreInfoText = mozL10n.get('error_version_info',
  6553. {version: pdfjsLib.version || '?', build: pdfjsLib.build || '?'},
  6554. 'PDF.js v{{version}} (build: {{build}})') + '\n';
  6555. if (moreInfo) {
  6556. moreInfoText +=
  6557. mozL10n.get('error_message', {message: moreInfo.message},
  6558. 'Message: {{message}}');
  6559. if (moreInfo.stack) {
  6560. moreInfoText += '\n' +
  6561. mozL10n.get('error_stack', {stack: moreInfo.stack},
  6562. 'Stack: {{stack}}');
  6563. } else {
  6564. if (moreInfo.filename) {
  6565. moreInfoText += '\n' +
  6566. mozL10n.get('error_file', {file: moreInfo.filename},
  6567. 'File: {{file}}');
  6568. }
  6569. if (moreInfo.lineNumber) {
  6570. moreInfoText += '\n' +
  6571. mozL10n.get('error_line', {line: moreInfo.lineNumber},
  6572. 'Line: {{line}}');
  6573. }
  6574. }
  6575. }
  6576. var errorWrapperConfig = this.appConfig.errorWrapper;
  6577. var errorWrapper = errorWrapperConfig.container;
  6578. errorWrapper.removeAttribute('hidden');
  6579. var errorMessage = errorWrapperConfig.errorMessage;
  6580. errorMessage.textContent = message;
  6581. var closeButton = errorWrapperConfig.closeButton;
  6582. closeButton.onclick = function() {
  6583. errorWrapper.setAttribute('hidden', 'true');
  6584. };
  6585. var errorMoreInfo = errorWrapperConfig.errorMoreInfo;
  6586. var moreInfoButton = errorWrapperConfig.moreInfoButton;
  6587. var lessInfoButton = errorWrapperConfig.lessInfoButton;
  6588. moreInfoButton.onclick = function() {
  6589. errorMoreInfo.removeAttribute('hidden');
  6590. moreInfoButton.setAttribute('hidden', 'true');
  6591. lessInfoButton.removeAttribute('hidden');
  6592. errorMoreInfo.style.height = errorMoreInfo.scrollHeight + 'px';
  6593. };
  6594. lessInfoButton.onclick = function() {
  6595. errorMoreInfo.setAttribute('hidden', 'true');
  6596. moreInfoButton.removeAttribute('hidden');
  6597. lessInfoButton.setAttribute('hidden', 'true');
  6598. };
  6599. moreInfoButton.oncontextmenu = noContextMenuHandler;
  6600. lessInfoButton.oncontextmenu = noContextMenuHandler;
  6601. closeButton.oncontextmenu = noContextMenuHandler;
  6602. moreInfoButton.removeAttribute('hidden');
  6603. lessInfoButton.setAttribute('hidden', 'true');
  6604. errorMoreInfo.value = moreInfoText;
  6605. },
  6606. progress: function pdfViewProgress(level) {
  6607. var percent = Math.round(level * 100);
  6608. // When we transition from full request to range requests, it's possible
  6609. // that we discard some of the loaded data. This can cause the loading
  6610. // bar to move backwards. So prevent this by only updating the bar if it
  6611. // increases.
  6612. if (percent > this.loadingBar.percent || isNaN(percent)) {
  6613. this.loadingBar.percent = percent;
  6614. // When disableAutoFetch is enabled, it's not uncommon for the entire file
  6615. // to never be fetched (depends on e.g. the file structure). In this case
  6616. // the loading bar will not be completely filled, nor will it be hidden.
  6617. // To prevent displaying a partially filled loading bar permanently, we
  6618. // hide it when no data has been loaded during a certain amount of time.
  6619. if (pdfjsLib.PDFJS.disableAutoFetch && percent) {
  6620. if (this.disableAutoFetchLoadingBarTimeout) {
  6621. clearTimeout(this.disableAutoFetchLoadingBarTimeout);
  6622. this.disableAutoFetchLoadingBarTimeout = null;
  6623. }
  6624. this.loadingBar.show();
  6625. this.disableAutoFetchLoadingBarTimeout = setTimeout(function () {
  6626. this.loadingBar.hide();
  6627. this.disableAutoFetchLoadingBarTimeout = null;
  6628. }.bind(this), DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT);
  6629. }
  6630. }
  6631. },
  6632. load: function pdfViewLoad(pdfDocument, scale) {
  6633. var self = this;
  6634. scale = scale || UNKNOWN_SCALE;
  6635. this.pdfDocument = pdfDocument;
  6636. this.pdfDocumentProperties.setDocumentAndUrl(pdfDocument, this.url);
  6637. var downloadedPromise = pdfDocument.getDownloadInfo().then(function() {
  6638. self.downloadComplete = true;
  6639. self.loadingBar.hide();
  6640. });
  6641. var pagesCount = pdfDocument.numPages;
  6642. var toolbarConfig = this.appConfig.toolbar;
  6643. toolbarConfig.numPages.textContent =
  6644. mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
  6645. toolbarConfig.pageNumber.max = pagesCount;
  6646. var id = this.documentFingerprint = pdfDocument.fingerprint;
  6647. var store = this.store = new ViewHistory(id);
  6648. var baseDocumentUrl = null;
  6649. this.pdfLinkService.setDocument(pdfDocument, baseDocumentUrl);
  6650. var pdfViewer = this.pdfViewer;
  6651. pdfViewer.currentScale = scale;
  6652. pdfViewer.setDocument(pdfDocument);
  6653. var firstPagePromise = pdfViewer.firstPagePromise;
  6654. var pagesPromise = pdfViewer.pagesPromise;
  6655. var onePageRendered = pdfViewer.onePageRendered;
  6656. this.pageRotation = 0;
  6657. this.pdfThumbnailViewer.setDocument(pdfDocument);
  6658. firstPagePromise.then(function(pdfPage) {
  6659. downloadedPromise.then(function () {
  6660. var event = document.createEvent('CustomEvent');
  6661. event.initCustomEvent('documentload', true, true, {});
  6662. window.dispatchEvent(event);
  6663. });
  6664. self.loadingBar.setWidth(self.appConfig.viewerContainer);
  6665. if (!pdfjsLib.PDFJS.disableHistory && !self.isViewerEmbedded) {
  6666. // The browsing history is only enabled when the viewer is standalone,
  6667. // i.e. not when it is embedded in a web page.
  6668. if (!self.preferenceShowPreviousViewOnLoad) {
  6669. self.pdfHistory.clearHistoryState();
  6670. }
  6671. self.pdfHistory.initialize(self.documentFingerprint);
  6672. if (self.pdfHistory.initialDestination) {
  6673. self.initialDestination = self.pdfHistory.initialDestination;
  6674. } else if (self.pdfHistory.initialBookmark) {
  6675. self.initialBookmark = self.pdfHistory.initialBookmark;
  6676. }
  6677. }
  6678. var initialParams = {
  6679. destination: self.initialDestination,
  6680. bookmark: self.initialBookmark,
  6681. hash: null,
  6682. };
  6683. store.initializedPromise.then(function resolved() {
  6684. var storedHash = null, sidebarView = null;
  6685. if (self.preferenceShowPreviousViewOnLoad &&
  6686. store.get('exists', false)) {
  6687. var pageNum = store.get('page', '1');
  6688. var zoom = self.preferenceDefaultZoomValue ||
  6689. store.get('zoom', DEFAULT_SCALE_VALUE);
  6690. var left = store.get('scrollLeft', '0');
  6691. var top = store.get('scrollTop', '0');
  6692. storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' +
  6693. left + ',' + top;
  6694. sidebarView = store.get('sidebarView', SidebarView.NONE);
  6695. } else if (self.preferenceDefaultZoomValue) {
  6696. storedHash = 'page=1&zoom=' + self.preferenceDefaultZoomValue;
  6697. }
  6698. self.setInitialView(storedHash,
  6699. { scale: scale, sidebarView: sidebarView });
  6700. initialParams.hash = storedHash;
  6701. // Make all navigation keys work on document load,
  6702. // unless the viewer is embedded in a web page.
  6703. if (!self.isViewerEmbedded) {
  6704. self.pdfViewer.focus();
  6705. }
  6706. }, function rejected(reason) {
  6707. console.error(reason);
  6708. self.setInitialView(null, { scale: scale });
  6709. });
  6710. // For documents with different page sizes,
  6711. // ensure that the correct location becomes visible on load.
  6712. pagesPromise.then(function resolved() {
  6713. if (!initialParams.destination && !initialParams.bookmark &&
  6714. !initialParams.hash) {
  6715. return;
  6716. }
  6717. if (self.hasEqualPageSizes) {
  6718. return;
  6719. }
  6720. self.initialDestination = initialParams.destination;
  6721. self.initialBookmark = initialParams.bookmark;
  6722. self.pdfViewer.currentScaleValue = self.pdfViewer.currentScaleValue;
  6723. self.setInitialView(initialParams.hash);
  6724. });
  6725. });
  6726. pagesPromise.then(function() {
  6727. if (self.supportsPrinting) {
  6728. pdfDocument.getJavaScript().then(function(javaScript) {
  6729. if (javaScript.length) {
  6730. console.warn('Warning: JavaScript is not supported');
  6731. self.fallback(pdfjsLib.UNSUPPORTED_FEATURES.javaScript);
  6732. }
  6733. // Hack to support auto printing.
  6734. var regex = /\bprint\s*\(/;
  6735. for (var i = 0, ii = javaScript.length; i < ii; i++) {
  6736. var js = javaScript[i];
  6737. if (js && regex.test(js)) {
  6738. setTimeout(function() {
  6739. window.print();
  6740. });
  6741. return;
  6742. }
  6743. }
  6744. });
  6745. }
  6746. });
  6747. // outline depends on pagesRefMap
  6748. var promises = [pagesPromise, this.animationStartedPromise];
  6749. Promise.all(promises).then(function() {
  6750. pdfDocument.getOutline().then(function(outline) {
  6751. self.pdfOutlineViewer.render({ outline: outline });
  6752. });
  6753. pdfDocument.getAttachments().then(function(attachments) {
  6754. self.pdfAttachmentViewer.render({ attachments: attachments });
  6755. });
  6756. });
  6757. pdfDocument.getMetadata().then(function(data) {
  6758. var info = data.info, metadata = data.metadata;
  6759. self.documentInfo = info;
  6760. self.metadata = metadata;
  6761. // Provides some basic debug information
  6762. console.log('PDF ' + pdfDocument.fingerprint + ' [' +
  6763. info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() +
  6764. ' / ' + (info.Creator || '-').trim() + ']' +
  6765. ' (PDF.js: ' + (pdfjsLib.version || '-') +
  6766. (!pdfjsLib.PDFJS.disableWebGL ? ' [WebGL]' : '') + ')');
  6767. var pdfTitle;
  6768. if (metadata && metadata.has('dc:title')) {
  6769. var title = metadata.get('dc:title');
  6770. // Ghostscript sometimes return 'Untitled', sets the title to 'Untitled'
  6771. if (title !== 'Untitled') {
  6772. pdfTitle = title;
  6773. }
  6774. }
  6775. if (!pdfTitle && info && info['Title']) {
  6776. pdfTitle = info['Title'];
  6777. }
  6778. if (pdfTitle) {
  6779. self.setTitle(pdfTitle + ' - ' + document.title);
  6780. }
  6781. if (info.IsAcroFormPresent) {
  6782. console.warn('Warning: AcroForm/XFA is not supported');
  6783. self.fallback(pdfjsLib.UNSUPPORTED_FEATURES.forms);
  6784. }
  6785. });
  6786. },
  6787. setInitialView: function pdfViewSetInitialView(storedHash, options) {
  6788. var scale = options && options.scale;
  6789. var sidebarView = options && options.sidebarView;
  6790. this.isInitialViewSet = true;
  6791. // When opening a new file, when one is already loaded in the viewer,
  6792. // ensure that the 'pageNumber' element displays the correct value.
  6793. this.appConfig.toolbar.pageNumber.value = this.pdfViewer.currentPageNumber;
  6794. this.pdfSidebar.setInitialView(this.preferenceSidebarViewOnLoad ||
  6795. (sidebarView | 0));
  6796. if (this.initialDestination) {
  6797. this.pdfLinkService.navigateTo(this.initialDestination);
  6798. this.initialDestination = null;
  6799. } else if (this.initialBookmark) {
  6800. this.pdfLinkService.setHash(this.initialBookmark);
  6801. this.pdfHistory.push({ hash: this.initialBookmark }, true);
  6802. this.initialBookmark = null;
  6803. } else if (storedHash) {
  6804. this.pdfLinkService.setHash(storedHash);
  6805. } else if (scale) {
  6806. this.pdfViewer.currentScaleValue = scale;
  6807. this.page = 1;
  6808. }
  6809. if (!this.pdfViewer.currentScaleValue) {
  6810. // Scale was not initialized: invalid bookmark or scale was not specified.
  6811. // Setting the default one.
  6812. this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
  6813. }
  6814. },
  6815. cleanup: function pdfViewCleanup() {
  6816. if (!this.pdfDocument) {
  6817. return; // run cleanup when document is loaded
  6818. }
  6819. this.pdfViewer.cleanup();
  6820. this.pdfThumbnailViewer.cleanup();
  6821. this.pdfDocument.cleanup();
  6822. },
  6823. forceRendering: function pdfViewForceRendering() {
  6824. this.pdfRenderingQueue.printing = this.printing;
  6825. this.pdfRenderingQueue.isThumbnailViewEnabled =
  6826. this.pdfSidebar.isThumbnailViewVisible;
  6827. this.pdfRenderingQueue.renderHighestPriority();
  6828. },
  6829. beforePrint: function pdfViewSetupBeforePrint() {
  6830. if (!this.supportsPrinting) {
  6831. var printMessage = mozL10n.get('printing_not_supported', null,
  6832. 'Warning: Printing is not fully supported by this browser.');
  6833. this.error(printMessage);
  6834. return;
  6835. }
  6836. var alertNotReady = false;
  6837. var i, ii;
  6838. if (!this.pdfDocument || !this.pagesCount) {
  6839. alertNotReady = true;
  6840. } else {
  6841. for (i = 0, ii = this.pagesCount; i < ii; ++i) {
  6842. if (!this.pdfViewer.getPageView(i).pdfPage) {
  6843. alertNotReady = true;
  6844. break;
  6845. }
  6846. }
  6847. }
  6848. if (alertNotReady) {
  6849. var notReadyMessage = mozL10n.get('printing_not_ready', null,
  6850. 'Warning: The PDF is not fully loaded for printing.');
  6851. window.alert(notReadyMessage);
  6852. return;
  6853. }
  6854. this.printing = true;
  6855. this.forceRendering();
  6856. var printContainer = this.appConfig.printContainer;
  6857. var body = document.querySelector('body');
  6858. body.setAttribute('data-mozPrintCallback', true);
  6859. if (!this.hasEqualPageSizes) {
  6860. console.warn('Not all pages have the same size. The printed result ' +
  6861. 'may be incorrect!');
  6862. }
  6863. // Insert a @page + size rule to make sure that the page size is correctly
  6864. // set. Note that we assume that all pages have the same size, because
  6865. // variable-size pages are not supported yet (at least in Chrome & Firefox).
  6866. // TODO(robwu): Use named pages when size calculation bugs get resolved
  6867. // (e.g. https://crbug.com/355116) AND when support for named pages is
  6868. // added (http://www.w3.org/TR/css3-page/#using-named-pages).
  6869. // In browsers where @page + size is not supported (such as Firefox,
  6870. // https://bugzil.la/851441), the next stylesheet will be ignored and the
  6871. // user has to select the correct paper size in the UI if wanted.
  6872. this.pageStyleSheet = document.createElement('style');
  6873. var pageSize = this.pdfViewer.getPageView(0).pdfPage.getViewport(1);
  6874. this.pageStyleSheet.textContent =
  6875. // "size:<width> <height>" is what we need. But also add "A4" because
  6876. // Firefox incorrectly reports support for the other value.
  6877. '@supports ((size:A4) and (size:1pt 1pt)) {' +
  6878. '@page { size: ' + pageSize.width + 'pt ' + pageSize.height + 'pt;}' +
  6879. '}';
  6880. body.appendChild(this.pageStyleSheet);
  6881. for (i = 0, ii = this.pagesCount; i < ii; ++i) {
  6882. this.pdfViewer.getPageView(i).beforePrint(printContainer);
  6883. }
  6884. },
  6885. // Whether all pages of the PDF have the same width and height.
  6886. get hasEqualPageSizes() {
  6887. var firstPage = this.pdfViewer.getPageView(0);
  6888. for (var i = 1, ii = this.pagesCount; i < ii; ++i) {
  6889. var pageView = this.pdfViewer.getPageView(i);
  6890. if (pageView.width !== firstPage.width ||
  6891. pageView.height !== firstPage.height) {
  6892. return false;
  6893. }
  6894. }
  6895. return true;
  6896. },
  6897. afterPrint: function pdfViewSetupAfterPrint() {
  6898. var div = this.appConfig.printContainer;
  6899. while (div.hasChildNodes()) {
  6900. div.removeChild(div.lastChild);
  6901. }
  6902. if (this.pageStyleSheet && this.pageStyleSheet.parentNode) {
  6903. this.pageStyleSheet.parentNode.removeChild(this.pageStyleSheet);
  6904. this.pageStyleSheet = null;
  6905. }
  6906. this.printing = false;
  6907. this.forceRendering();
  6908. },
  6909. rotatePages: function pdfViewRotatePages(delta) {
  6910. var pageNumber = this.page;
  6911. this.pageRotation = (this.pageRotation + 360 + delta) % 360;
  6912. this.pdfViewer.pagesRotation = this.pageRotation;
  6913. this.pdfThumbnailViewer.pagesRotation = this.pageRotation;
  6914. this.forceRendering();
  6915. this.pdfViewer.scrollPageIntoView(pageNumber);
  6916. },
  6917. requestPresentationMode: function pdfViewRequestPresentationMode() {
  6918. if (!this.pdfPresentationMode) {
  6919. return;
  6920. }
  6921. this.pdfPresentationMode.request();
  6922. },
  6923. /**
  6924. * @param {number} delta - The delta value from the mouse event.
  6925. */
  6926. scrollPresentationMode: function pdfViewScrollPresentationMode(delta) {
  6927. if (!this.pdfPresentationMode) {
  6928. return;
  6929. }
  6930. this.pdfPresentationMode.mouseScroll(delta);
  6931. }
  6932. };
  6933. var HOSTED_VIEWER_ORIGINS = ['null',
  6934. 'http://mozilla.github.io', 'https://mozilla.github.io'];
  6935. function validateFileURL(file) {
  6936. try {
  6937. var viewerOrigin = new URL(window.location.href).origin || 'null';
  6938. if (HOSTED_VIEWER_ORIGINS.indexOf(viewerOrigin) >= 0) {
  6939. // Hosted or local viewer, allow for any file locations
  6940. return;
  6941. }
  6942. var fileOrigin = new URL(file, window.location.href).origin;
  6943. // Removing of the following line will not guarantee that the viewer will
  6944. // start accepting URLs from foreign origin -- CORS headers on the remote
  6945. // server must be properly configured.
  6946. if (fileOrigin !== viewerOrigin) {
  6947. throw new Error('file origin does not match viewer\'s');
  6948. }
  6949. } catch (e) {
  6950. var message = e && e.message;
  6951. var loadingErrorMessage = mozL10n.get('loading_error', null,
  6952. 'An error occurred while loading the PDF.');
  6953. var moreInfo = {
  6954. message: message
  6955. };
  6956. PDFViewerApplication.error(loadingErrorMessage, moreInfo);
  6957. throw e;
  6958. }
  6959. }
  6960. function webViewerInitialized() {
  6961. var queryString = document.location.search.substring(1);
  6962. var params = parseQueryString(queryString);
  6963. var file = 'file' in params ? params.file : DEFAULT_URL;
  6964. validateFileURL(file);
  6965. var appConfig = PDFViewerApplication.appConfig;
  6966. var fileInput = document.createElement('input');
  6967. fileInput.id = appConfig.openFileInputName;
  6968. fileInput.className = 'fileInput';
  6969. fileInput.setAttribute('type', 'file');
  6970. fileInput.oncontextmenu = noContextMenuHandler;
  6971. document.body.appendChild(fileInput);
  6972. if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {
  6973. appConfig.toolbar.openFile.setAttribute('hidden', 'true');
  6974. appConfig.secondaryToolbar.openFile.setAttribute('hidden', 'true');
  6975. } else {
  6976. fileInput.value = null;
  6977. }
  6978. var PDFJS = pdfjsLib.PDFJS;
  6979. if (PDFViewerApplication.preferencePdfBugEnabled) {
  6980. // Special debugging flags in the hash section of the URL.
  6981. var hash = document.location.hash.substring(1);
  6982. var hashParams = parseQueryString(hash);
  6983. if ('disableworker' in hashParams) {
  6984. PDFJS.disableWorker = (hashParams['disableworker'] === 'true');
  6985. }
  6986. if ('disablerange' in hashParams) {
  6987. PDFJS.disableRange = (hashParams['disablerange'] === 'true');
  6988. }
  6989. if ('disablestream' in hashParams) {
  6990. PDFJS.disableStream = (hashParams['disablestream'] === 'true');
  6991. }
  6992. if ('disableautofetch' in hashParams) {
  6993. PDFJS.disableAutoFetch = (hashParams['disableautofetch'] === 'true');
  6994. }
  6995. if ('disablefontface' in hashParams) {
  6996. PDFJS.disableFontFace = (hashParams['disablefontface'] === 'true');
  6997. }
  6998. if ('disablehistory' in hashParams) {
  6999. PDFJS.disableHistory = (hashParams['disablehistory'] === 'true');
  7000. }
  7001. if ('webgl' in hashParams) {
  7002. PDFJS.disableWebGL = (hashParams['webgl'] !== 'true');
  7003. }
  7004. if ('useonlycsszoom' in hashParams) {
  7005. PDFJS.useOnlyCssZoom = (hashParams['useonlycsszoom'] === 'true');
  7006. }
  7007. if ('verbosity' in hashParams) {
  7008. PDFJS.verbosity = hashParams['verbosity'] | 0;
  7009. }
  7010. if ('ignorecurrentpositiononzoom' in hashParams) {
  7011. PDFJS.ignoreCurrentPositionOnZoom =
  7012. (hashParams['ignorecurrentpositiononzoom'] === 'true');
  7013. }
  7014. if ('locale' in hashParams) {
  7015. PDFJS.locale = hashParams['locale'];
  7016. }
  7017. if ('textlayer' in hashParams) {
  7018. switch (hashParams['textlayer']) {
  7019. case 'off':
  7020. PDFJS.disableTextLayer = true;
  7021. break;
  7022. case 'visible':
  7023. case 'shadow':
  7024. case 'hover':
  7025. var viewer = appConfig.viewerContainer;
  7026. viewer.classList.add('textLayer-' + hashParams['textlayer']);
  7027. break;
  7028. }
  7029. }
  7030. if ('pdfbug' in hashParams) {
  7031. PDFJS.pdfBug = true;
  7032. var pdfBug = hashParams['pdfbug'];
  7033. var enabled = pdfBug.split(',');
  7034. PDFBug.enable(enabled);
  7035. PDFBug.init(pdfjsLib, appConfig.mainContainer);
  7036. }
  7037. }
  7038. mozL10n.setLanguage(PDFJS.locale);
  7039. if (!PDFViewerApplication.supportsPrinting) {
  7040. appConfig.toolbar.print.classList.add('hidden');
  7041. appConfig.secondaryToolbar.print.classList.add('hidden');
  7042. }
  7043. if (!PDFViewerApplication.supportsFullscreen) {
  7044. appConfig.toolbar.presentationModeButton.classList.add('hidden');
  7045. appConfig.secondaryToolbar.presentationModeButton.classList.add('hidden');
  7046. }
  7047. if (PDFViewerApplication.supportsIntegratedFind) {
  7048. appConfig.toolbar.viewFind.classList.add('hidden');
  7049. }
  7050. // Suppress context menus for some controls
  7051. appConfig.toolbar.scaleSelect.oncontextmenu = noContextMenuHandler;
  7052. appConfig.sidebar.mainContainer.addEventListener('transitionend',
  7053. function(e) {
  7054. if (e.target === /* mainContainer */ this) {
  7055. var event = document.createEvent('UIEvents');
  7056. event.initUIEvent('resize', false, false, window, 0);
  7057. window.dispatchEvent(event);
  7058. }
  7059. }, true);
  7060. appConfig.sidebar.toggleButton.addEventListener('click',
  7061. function() {
  7062. PDFViewerApplication.pdfSidebar.toggle();
  7063. });
  7064. appConfig.toolbar.previous.addEventListener('click',
  7065. function() {
  7066. PDFViewerApplication.page--;
  7067. });
  7068. appConfig.toolbar.next.addEventListener('click',
  7069. function() {
  7070. PDFViewerApplication.page++;
  7071. });
  7072. appConfig.toolbar.zoomIn.addEventListener('click',
  7073. function() {
  7074. PDFViewerApplication.zoomIn();
  7075. });
  7076. appConfig.toolbar.zoomOut.addEventListener('click',
  7077. function() {
  7078. PDFViewerApplication.zoomOut();
  7079. });
  7080. appConfig.toolbar.pageNumber.addEventListener('click', function() {
  7081. this.select();
  7082. });
  7083. appConfig.toolbar.pageNumber.addEventListener('change', function() {
  7084. // Handle the user inputting a floating point number.
  7085. PDFViewerApplication.page = (this.value | 0);
  7086. if (this.value !== (this.value | 0).toString()) {
  7087. this.value = PDFViewerApplication.page;
  7088. }
  7089. });
  7090. appConfig.toolbar.scaleSelect.addEventListener('change', function() {
  7091. if (this.value === 'custom') {
  7092. return;
  7093. }
  7094. PDFViewerApplication.pdfViewer.currentScaleValue = this.value;
  7095. });
  7096. appConfig.toolbar.presentationModeButton.addEventListener('click',
  7097. SecondaryToolbar.presentationModeClick.bind(SecondaryToolbar));
  7098. appConfig.toolbar.openFile.addEventListener('click',
  7099. SecondaryToolbar.openFileClick.bind(SecondaryToolbar));
  7100. appConfig.toolbar.print.addEventListener('click',
  7101. SecondaryToolbar.printClick.bind(SecondaryToolbar));
  7102. appConfig.toolbar.download.addEventListener('click',
  7103. SecondaryToolbar.downloadClick.bind(SecondaryToolbar));
  7104. if (file && file.lastIndexOf('file:', 0) === 0) {
  7105. // file:-scheme. Load the contents in the main thread because QtWebKit
  7106. // cannot load file:-URLs in a Web Worker. file:-URLs are usually loaded
  7107. // very quickly, so there is no need to set up progress event listeners.
  7108. PDFViewerApplication.setTitleUsingUrl(file);
  7109. var xhr = new XMLHttpRequest();
  7110. xhr.onload = function() {
  7111. PDFViewerApplication.open(new Uint8Array(xhr.response));
  7112. };
  7113. try {
  7114. xhr.open('GET', file);
  7115. xhr.responseType = 'arraybuffer';
  7116. xhr.send();
  7117. } catch (e) {
  7118. PDFViewerApplication.error(mozL10n.get('loading_error', null,
  7119. 'An error occurred while loading the PDF.'), e);
  7120. }
  7121. return;
  7122. }
  7123. if (file) {
  7124. PDFViewerApplication.open(file);
  7125. }
  7126. }
  7127. document.addEventListener('pagerendered', function (e) {
  7128. var pageNumber = e.detail.pageNumber;
  7129. var pageIndex = pageNumber - 1;
  7130. var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
  7131. // Use the rendered page to set the corresponding thumbnail image.
  7132. if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) {
  7133. var thumbnailView = PDFViewerApplication.pdfThumbnailViewer.
  7134. getThumbnail(pageIndex);
  7135. thumbnailView.setImage(pageView);
  7136. }
  7137. if (pdfjsLib.PDFJS.pdfBug && Stats.enabled && pageView.stats) {
  7138. Stats.add(pageNumber, pageView.stats);
  7139. }
  7140. if (pageView.error) {
  7141. PDFViewerApplication.error(mozL10n.get('rendering_error', null,
  7142. 'An error occurred while rendering the page.'), pageView.error);
  7143. }
  7144. // If the page is still visible when it has finished rendering,
  7145. // ensure that the page number input loading indicator is hidden.
  7146. if (pageNumber === PDFViewerApplication.page) {
  7147. var pageNumberInput = PDFViewerApplication.appConfig.toolbar.pageNumber;
  7148. pageNumberInput.classList.remove(PAGE_NUMBER_LOADING_INDICATOR);
  7149. }
  7150. }, true);
  7151. document.addEventListener('textlayerrendered', function (e) {
  7152. var pageIndex = e.detail.pageNumber - 1;
  7153. var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
  7154. }, true);
  7155. document.addEventListener('pagemode', function (evt) {
  7156. if (!PDFViewerApplication.initialized) {
  7157. return;
  7158. }
  7159. // Handle the 'pagemode' hash parameter, see also `PDFLinkService_setHash`.
  7160. var mode = evt.detail.mode, view;
  7161. switch (mode) {
  7162. case 'thumbs':
  7163. view = SidebarView.THUMBS;
  7164. break;
  7165. case 'bookmarks':
  7166. case 'outline':
  7167. view = SidebarView.OUTLINE;
  7168. break;
  7169. case 'attachments':
  7170. view = SidebarView.ATTACHMENTS;
  7171. break;
  7172. case 'none':
  7173. view = SidebarView.NONE;
  7174. break;
  7175. default:
  7176. console.error('Invalid "pagemode" hash parameter: ' + mode);
  7177. return;
  7178. }
  7179. PDFViewerApplication.pdfSidebar.switchView(view, /* forceOpen = */ true);
  7180. }, true);
  7181. document.addEventListener('namedaction', function (e) {
  7182. if (!PDFViewerApplication.initialized) {
  7183. return;
  7184. }
  7185. // Processing couple of named actions that might be useful.
  7186. // See also PDFLinkService.executeNamedAction
  7187. var action = e.detail.action;
  7188. switch (action) {
  7189. case 'GoToPage':
  7190. PDFViewerApplication.appConfig.toolbar.pageNumber.focus();
  7191. break;
  7192. case 'Find':
  7193. if (!PDFViewerApplication.supportsIntegratedFind) {
  7194. PDFViewerApplication.findBar.toggle();
  7195. }
  7196. break;
  7197. }
  7198. }, true);
  7199. window.addEventListener('presentationmodechanged', function (e) {
  7200. var active = e.detail.active;
  7201. var switchInProgress = e.detail.switchInProgress;
  7202. PDFViewerApplication.pdfViewer.presentationModeState =
  7203. switchInProgress ? PresentationModeState.CHANGING :
  7204. active ? PresentationModeState.FULLSCREEN : PresentationModeState.NORMAL;
  7205. });
  7206. window.addEventListener('sidebarviewchanged', function (evt) {
  7207. if (!PDFViewerApplication.initialized) {
  7208. return;
  7209. }
  7210. PDFViewerApplication.pdfRenderingQueue.isThumbnailViewEnabled =
  7211. PDFViewerApplication.pdfSidebar.isThumbnailViewVisible;
  7212. var store = PDFViewerApplication.store;
  7213. if (!store || !PDFViewerApplication.isInitialViewSet) {
  7214. // Only update the storage when the document has been loaded *and* rendered.
  7215. return;
  7216. }
  7217. store.initializedPromise.then(function() {
  7218. store.set('sidebarView', evt.detail.view).catch(function() {});
  7219. });
  7220. }, true);
  7221. window.addEventListener('updateviewarea', function (evt) {
  7222. if (!PDFViewerApplication.initialized) {
  7223. return;
  7224. }
  7225. var location = evt.location, store = PDFViewerApplication.store;
  7226. if (store) {
  7227. store.initializedPromise.then(function() {
  7228. store.setMultiple({
  7229. 'exists': true,
  7230. 'page': location.pageNumber,
  7231. 'zoom': location.scale,
  7232. 'scrollLeft': location.left,
  7233. 'scrollTop': location.top,
  7234. }).catch(function() { /* unable to write to storage */ });
  7235. });
  7236. }
  7237. var href =
  7238. PDFViewerApplication.pdfLinkService.getAnchorUrl(location.pdfOpenParams);
  7239. PDFViewerApplication.appConfig.toolbar.viewBookmark.href = href;
  7240. PDFViewerApplication.appConfig.secondaryToolbar.viewBookmark.href = href;
  7241. // Update the current bookmark in the browsing history.
  7242. PDFViewerApplication.pdfHistory.updateCurrentBookmark(location.pdfOpenParams,
  7243. location.pageNumber);
  7244. // Show/hide the loading indicator in the page number input element.
  7245. var pageNumberInput = PDFViewerApplication.appConfig.toolbar.pageNumber;
  7246. var currentPage =
  7247. PDFViewerApplication.pdfViewer.getPageView(PDFViewerApplication.page - 1);
  7248. if (currentPage.renderingState === RenderingStates.FINISHED) {
  7249. pageNumberInput.classList.remove(PAGE_NUMBER_LOADING_INDICATOR);
  7250. } else {
  7251. pageNumberInput.classList.add(PAGE_NUMBER_LOADING_INDICATOR);
  7252. }
  7253. }, true);
  7254. window.addEventListener('resize', function webViewerResize(evt) {
  7255. if (PDFViewerApplication.initialized) {
  7256. var currentScaleValue = PDFViewerApplication.pdfViewer.currentScaleValue;
  7257. if (currentScaleValue === 'auto' ||
  7258. currentScaleValue === 'page-fit' ||
  7259. currentScaleValue === 'page-width') {
  7260. // Note: the scale is constant for 'page-actual'.
  7261. PDFViewerApplication.pdfViewer.currentScaleValue = currentScaleValue;
  7262. } else if (!currentScaleValue) {
  7263. // Normally this shouldn't happen, but if the scale wasn't initialized
  7264. // we set it to the default value in order to prevent any issues.
  7265. // (E.g. the document being rendered with the wrong scale on load.)
  7266. PDFViewerApplication.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
  7267. }
  7268. PDFViewerApplication.pdfViewer.update();
  7269. }
  7270. // Set the 'max-height' CSS property of the secondary toolbar.
  7271. SecondaryToolbar.setMaxHeight(PDFViewerApplication.appConfig.mainContainer);
  7272. });
  7273. window.addEventListener('hashchange', function webViewerHashchange(evt) {
  7274. if (PDFViewerApplication.pdfHistory.isHashChangeUnlocked) {
  7275. var hash = document.location.hash.substring(1);
  7276. if (!hash) {
  7277. return;
  7278. }
  7279. if (!PDFViewerApplication.isInitialViewSet) {
  7280. PDFViewerApplication.initialBookmark = hash;
  7281. } else {
  7282. PDFViewerApplication.pdfLinkService.setHash(hash);
  7283. }
  7284. }
  7285. });
  7286. window.addEventListener('change', function webViewerChange(evt) {
  7287. var files = evt.target.files;
  7288. if (!files || files.length === 0) {
  7289. return;
  7290. }
  7291. var file = files[0];
  7292. if (!pdfjsLib.PDFJS.disableCreateObjectURL &&
  7293. typeof URL !== 'undefined' && URL.createObjectURL) {
  7294. PDFViewerApplication.open(URL.createObjectURL(file));
  7295. } else {
  7296. // Read the local file into a Uint8Array.
  7297. var fileReader = new FileReader();
  7298. fileReader.onload = function webViewerChangeFileReaderOnload(evt) {
  7299. var buffer = evt.target.result;
  7300. var uint8Array = new Uint8Array(buffer);
  7301. PDFViewerApplication.open(uint8Array);
  7302. };
  7303. fileReader.readAsArrayBuffer(file);
  7304. }
  7305. PDFViewerApplication.setTitleUsingUrl(file.name);
  7306. // URL does not reflect proper document location - hiding some icons.
  7307. var appConfig = PDFViewerApplication.appConfig;
  7308. appConfig.toolbar.viewBookmark.setAttribute('hidden', 'true');
  7309. appConfig.secondaryToolbar.viewBookmark.setAttribute('hidden', 'true');
  7310. appConfig.toolbar.download.setAttribute('hidden', 'true');
  7311. appConfig.secondaryToolbar.download.setAttribute('hidden', 'true');
  7312. }, true);
  7313. function selectScaleOption(value) {
  7314. var options = PDFViewerApplication.appConfig.toolbar.scaleSelect.options;
  7315. var predefinedValueFound = false;
  7316. for (var i = 0, ii = options.length; i < ii; i++) {
  7317. var option = options[i];
  7318. if (option.value !== value) {
  7319. option.selected = false;
  7320. continue;
  7321. }
  7322. option.selected = true;
  7323. predefinedValueFound = true;
  7324. }
  7325. return predefinedValueFound;
  7326. }
  7327. window.addEventListener('localized', function localized(evt) {
  7328. document.getElementsByTagName('html')[0].dir = mozL10n.getDirection();
  7329. PDFViewerApplication.animationStartedPromise.then(function() {
  7330. // Adjust the width of the zoom box to fit the content.
  7331. // Note: If the window is narrow enough that the zoom box is not visible,
  7332. // we temporarily show it to be able to adjust its width.
  7333. var container = PDFViewerApplication.appConfig.toolbar.scaleSelectContainer;
  7334. if (container.clientWidth === 0) {
  7335. container.setAttribute('style', 'display: inherit;');
  7336. }
  7337. if (container.clientWidth > 0) {
  7338. var select = PDFViewerApplication.appConfig.toolbar.scaleSelect;
  7339. select.setAttribute('style', 'min-width: inherit;');
  7340. var width = select.clientWidth + SCALE_SELECT_CONTAINER_PADDING;
  7341. select.setAttribute('style', 'min-width: ' +
  7342. (width + SCALE_SELECT_PADDING) + 'px;');
  7343. container.setAttribute('style', 'min-width: ' + width + 'px; ' +
  7344. 'max-width: ' + width + 'px;');
  7345. }
  7346. // Set the 'max-height' CSS property of the secondary toolbar.
  7347. SecondaryToolbar.setMaxHeight(PDFViewerApplication.appConfig.mainContainer);
  7348. });
  7349. }, true);
  7350. window.addEventListener('scalechange', function scalechange(evt) {
  7351. var appConfig = PDFViewerApplication.appConfig;
  7352. appConfig.toolbar.zoomOut.disabled = (evt.scale === MIN_SCALE);
  7353. appConfig.toolbar.zoomIn.disabled = (evt.scale === MAX_SCALE);
  7354. // Update the 'scaleSelect' DOM element.
  7355. var predefinedValueFound = selectScaleOption(evt.presetValue ||
  7356. '' + evt.scale);
  7357. if (!predefinedValueFound) {
  7358. var customScaleOption = appConfig.toolbar.customScaleOption;
  7359. var customScale = Math.round(evt.scale * 10000) / 100;
  7360. customScaleOption.textContent =
  7361. mozL10n.get('page_scale_percent', { scale: customScale }, '{{scale}}%');
  7362. customScaleOption.selected = true;
  7363. }
  7364. if (!PDFViewerApplication.initialized) {
  7365. return;
  7366. }
  7367. PDFViewerApplication.pdfViewer.update();
  7368. }, true);
  7369. window.addEventListener('pagechange', function pagechange(evt) {
  7370. var page = evt.pageNumber;
  7371. if (evt.previousPageNumber !== page) {
  7372. PDFViewerApplication.appConfig.toolbar.pageNumber.value = page;
  7373. if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) {
  7374. PDFViewerApplication.pdfThumbnailViewer.scrollThumbnailIntoView(page);
  7375. }
  7376. }
  7377. var numPages = PDFViewerApplication.pagesCount;
  7378. PDFViewerApplication.appConfig.toolbar.previous.disabled = (page <= 1);
  7379. PDFViewerApplication.appConfig.toolbar.next.disabled = (page >= numPages);
  7380. PDFViewerApplication.appConfig.toolbar.firstPage.disabled = (page <= 1);
  7381. PDFViewerApplication.appConfig.toolbar.lastPage.disabled = (page >= numPages);
  7382. // we need to update stats
  7383. if (pdfjsLib.PDFJS.pdfBug && Stats.enabled) {
  7384. var pageView = PDFViewerApplication.pdfViewer.getPageView(page - 1);
  7385. if (pageView.stats) {
  7386. Stats.add(page, pageView.stats);
  7387. }
  7388. }
  7389. }, true);
  7390. var zoomDisabled = false, zoomDisabledTimeout;
  7391. function handleMouseWheel(evt) {
  7392. var MOUSE_WHEEL_DELTA_FACTOR = 40;
  7393. var ticks = (evt.type === 'DOMMouseScroll') ? -evt.detail :
  7394. evt.wheelDelta / MOUSE_WHEEL_DELTA_FACTOR;
  7395. var direction = (ticks < 0) ? 'zoomOut' : 'zoomIn';
  7396. var pdfViewer = PDFViewerApplication.pdfViewer;
  7397. if (pdfViewer.isInPresentationMode) {
  7398. evt.preventDefault();
  7399. PDFViewerApplication.scrollPresentationMode(ticks *
  7400. MOUSE_WHEEL_DELTA_FACTOR);
  7401. } else if (evt.ctrlKey || evt.metaKey) {
  7402. var support = PDFViewerApplication.supportedMouseWheelZoomModifierKeys;
  7403. if ((evt.ctrlKey && !support.ctrlKey) ||
  7404. (evt.metaKey && !support.metaKey)) {
  7405. return;
  7406. }
  7407. // Only zoom the pages, not the entire viewer.
  7408. evt.preventDefault();
  7409. // NOTE: this check must be placed *after* preventDefault.
  7410. if (zoomDisabled) {
  7411. return;
  7412. }
  7413. var previousScale = pdfViewer.currentScale;
  7414. PDFViewerApplication[direction](Math.abs(ticks));
  7415. var currentScale = pdfViewer.currentScale;
  7416. if (previousScale !== currentScale) {
  7417. // After scaling the page via zoomIn/zoomOut, the position of the upper-
  7418. // left corner is restored. When the mouse wheel is used, the position
  7419. // under the cursor should be restored instead.
  7420. var scaleCorrectionFactor = currentScale / previousScale - 1;
  7421. var rect = pdfViewer.container.getBoundingClientRect();
  7422. var dx = evt.clientX - rect.left;
  7423. var dy = evt.clientY - rect.top;
  7424. pdfViewer.container.scrollLeft += dx * scaleCorrectionFactor;
  7425. pdfViewer.container.scrollTop += dy * scaleCorrectionFactor;
  7426. }
  7427. } else {
  7428. zoomDisabled = true;
  7429. clearTimeout(zoomDisabledTimeout);
  7430. zoomDisabledTimeout = setTimeout(function () {
  7431. zoomDisabled = false;
  7432. }, 1000);
  7433. }
  7434. }
  7435. window.addEventListener('DOMMouseScroll', handleMouseWheel);
  7436. window.addEventListener('mousewheel', handleMouseWheel);
  7437. window.addEventListener('click', function click(evt) {
  7438. if (SecondaryToolbar.opened &&
  7439. PDFViewerApplication.pdfViewer.containsElement(evt.target)) {
  7440. SecondaryToolbar.close();
  7441. }
  7442. }, false);
  7443. window.addEventListener('keydown', function keydown(evt) {
  7444. if (OverlayManager.active) {
  7445. return;
  7446. }
  7447. var handled = false;
  7448. var cmd = (evt.ctrlKey ? 1 : 0) |
  7449. (evt.altKey ? 2 : 0) |
  7450. (evt.shiftKey ? 4 : 0) |
  7451. (evt.metaKey ? 8 : 0);
  7452. var pdfViewer = PDFViewerApplication.pdfViewer;
  7453. var isViewerInPresentationMode = pdfViewer && pdfViewer.isInPresentationMode;
  7454. // First, handle the key bindings that are independent whether an input
  7455. // control is selected or not.
  7456. if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
  7457. // either CTRL or META key with optional SHIFT.
  7458. switch (evt.keyCode) {
  7459. case 70: // f
  7460. if (!PDFViewerApplication.supportsIntegratedFind) {
  7461. PDFViewerApplication.findBar.open();
  7462. handled = true;
  7463. }
  7464. break;
  7465. case 71: // g
  7466. if (!PDFViewerApplication.supportsIntegratedFind) {
  7467. PDFViewerApplication.findBar.dispatchEvent('again',
  7468. cmd === 5 || cmd === 12);
  7469. handled = true;
  7470. }
  7471. break;
  7472. case 61: // FF/Mac '='
  7473. case 107: // FF '+' and '='
  7474. case 187: // Chrome '+'
  7475. case 171: // FF with German keyboard
  7476. if (!isViewerInPresentationMode) {
  7477. PDFViewerApplication.zoomIn();
  7478. }
  7479. handled = true;
  7480. break;
  7481. case 173: // FF/Mac '-'
  7482. case 109: // FF '-'
  7483. case 189: // Chrome '-'
  7484. if (!isViewerInPresentationMode) {
  7485. PDFViewerApplication.zoomOut();
  7486. }
  7487. handled = true;
  7488. break;
  7489. case 48: // '0'
  7490. case 96: // '0' on Numpad of Swedish keyboard
  7491. if (!isViewerInPresentationMode) {
  7492. // keeping it unhandled (to restore page zoom to 100%)
  7493. setTimeout(function () {
  7494. // ... and resetting the scale after browser adjusts its scale
  7495. pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
  7496. });
  7497. handled = false;
  7498. }
  7499. break;
  7500. }
  7501. }
  7502. // CTRL or META without shift
  7503. if (cmd === 1 || cmd === 8) {
  7504. switch (evt.keyCode) {
  7505. case 83: // s
  7506. PDFViewerApplication.download();
  7507. handled = true;
  7508. break;
  7509. }
  7510. }
  7511. // CTRL+ALT or Option+Command
  7512. if (cmd === 3 || cmd === 10) {
  7513. switch (evt.keyCode) {
  7514. case 80: // p
  7515. PDFViewerApplication.requestPresentationMode();
  7516. handled = true;
  7517. break;
  7518. case 71: // g
  7519. // focuses input#pageNumber field
  7520. PDFViewerApplication.appConfig.toolbar.pageNumber.select();
  7521. handled = true;
  7522. break;
  7523. }
  7524. }
  7525. if (handled) {
  7526. evt.preventDefault();
  7527. return;
  7528. }
  7529. // Some shortcuts should not get handled if a control/input element
  7530. // is selected.
  7531. var curElement = document.activeElement || document.querySelector(':focus');
  7532. var curElementTagName = curElement && curElement.tagName.toUpperCase();
  7533. if (curElementTagName === 'INPUT' ||
  7534. curElementTagName === 'TEXTAREA' ||
  7535. curElementTagName === 'SELECT') {
  7536. // Make sure that the secondary toolbar is closed when Escape is pressed.
  7537. if (evt.keyCode !== 27) { // 'Esc'
  7538. return;
  7539. }
  7540. }
  7541. var ensureViewerFocused = false;
  7542. if (cmd === 0) { // no control key pressed at all.
  7543. switch (evt.keyCode) {
  7544. case 38: // up arrow
  7545. case 33: // pg up
  7546. case 8: // backspace
  7547. if (!isViewerInPresentationMode &&
  7548. pdfViewer.currentScaleValue !== 'page-fit') {
  7549. break;
  7550. }
  7551. /* in presentation mode */
  7552. /* falls through */
  7553. case 37: // left arrow
  7554. // horizontal scrolling using arrow keys
  7555. if (pdfViewer.isHorizontalScrollbarEnabled) {
  7556. break;
  7557. }
  7558. /* falls through */
  7559. case 75: // 'k'
  7560. case 80: // 'p'
  7561. PDFViewerApplication.page--;
  7562. handled = true;
  7563. break;
  7564. case 27: // esc key
  7565. if (SecondaryToolbar.opened) {
  7566. SecondaryToolbar.close();
  7567. handled = true;
  7568. }
  7569. if (!PDFViewerApplication.supportsIntegratedFind &&
  7570. PDFViewerApplication.findBar.opened) {
  7571. PDFViewerApplication.findBar.close();
  7572. handled = true;
  7573. }
  7574. break;
  7575. case 40: // down arrow
  7576. case 34: // pg down
  7577. case 32: // spacebar
  7578. if (!isViewerInPresentationMode &&
  7579. pdfViewer.currentScaleValue !== 'page-fit') {
  7580. break;
  7581. }
  7582. /* falls through */
  7583. case 39: // right arrow
  7584. // horizontal scrolling using arrow keys
  7585. if (pdfViewer.isHorizontalScrollbarEnabled) {
  7586. break;
  7587. }
  7588. /* falls through */
  7589. case 74: // 'j'
  7590. case 78: // 'n'
  7591. PDFViewerApplication.page++;
  7592. handled = true;
  7593. break;
  7594. case 36: // home
  7595. if (isViewerInPresentationMode || PDFViewerApplication.page > 1) {
  7596. PDFViewerApplication.page = 1;
  7597. handled = true;
  7598. ensureViewerFocused = true;
  7599. }
  7600. break;
  7601. case 35: // end
  7602. if (isViewerInPresentationMode || (PDFViewerApplication.pdfDocument &&
  7603. PDFViewerApplication.page < PDFViewerApplication.pagesCount)) {
  7604. PDFViewerApplication.page = PDFViewerApplication.pagesCount;
  7605. handled = true;
  7606. ensureViewerFocused = true;
  7607. }
  7608. break;
  7609. case 72: // 'h'
  7610. if (!isViewerInPresentationMode) {
  7611. PDFViewerApplication.handTool.toggle();
  7612. }
  7613. break;
  7614. case 82: // 'r'
  7615. PDFViewerApplication.rotatePages(90);
  7616. break;
  7617. }
  7618. }
  7619. if (cmd === 4) { // shift-key
  7620. switch (evt.keyCode) {
  7621. case 32: // spacebar
  7622. if (!isViewerInPresentationMode &&
  7623. pdfViewer.currentScaleValue !== 'page-fit') {
  7624. break;
  7625. }
  7626. PDFViewerApplication.page--;
  7627. handled = true;
  7628. break;
  7629. case 82: // 'r'
  7630. PDFViewerApplication.rotatePages(-90);
  7631. break;
  7632. }
  7633. }
  7634. if (!handled && !isViewerInPresentationMode) {
  7635. // 33=Page Up 34=Page Down 35=End 36=Home
  7636. // 37=Left 38=Up 39=Right 40=Down
  7637. // 32=Spacebar
  7638. if ((evt.keyCode >= 33 && evt.keyCode <= 40) ||
  7639. (evt.keyCode === 32 && curElementTagName !== 'BUTTON')) {
  7640. ensureViewerFocused = true;
  7641. }
  7642. }
  7643. if (cmd === 2) { // alt-key
  7644. switch (evt.keyCode) {
  7645. case 37: // left arrow
  7646. if (isViewerInPresentationMode) {
  7647. PDFViewerApplication.pdfHistory.back();
  7648. handled = true;
  7649. }
  7650. break;
  7651. case 39: // right arrow
  7652. if (isViewerInPresentationMode) {
  7653. PDFViewerApplication.pdfHistory.forward();
  7654. handled = true;
  7655. }
  7656. break;
  7657. }
  7658. }
  7659. if (ensureViewerFocused && !pdfViewer.containsElement(curElement)) {
  7660. // The page container is not focused, but a page navigation key has been
  7661. // pressed. Change the focus to the viewer container to make sure that
  7662. // navigation by keyboard works as expected.
  7663. pdfViewer.focus();
  7664. }
  7665. if (handled) {
  7666. evt.preventDefault();
  7667. }
  7668. });
  7669. window.addEventListener('beforeprint', function beforePrint(evt) {
  7670. PDFViewerApplication.beforePrint();
  7671. });
  7672. window.addEventListener('afterprint', function afterPrint(evt) {
  7673. PDFViewerApplication.afterPrint();
  7674. });
  7675. (function animationStartedClosure() {
  7676. // The offsetParent is not set until the pdf.js iframe or object is visible.
  7677. // Waiting for first animation.
  7678. PDFViewerApplication.animationStartedPromise = new Promise(
  7679. function (resolve) {
  7680. window.requestAnimationFrame(resolve);
  7681. });
  7682. })();
  7683. exports.PDFViewerApplication = PDFViewerApplication;
  7684. // TODO remove circular reference of pdfjs-web/secondary_toolbar on app.
  7685. secondaryToolbarLib._setApp(exports);
  7686. }));
  7687. }).call(pdfjsWebLibs);
  7688. function getViewerConfiguration() {
  7689. return {
  7690. appContainer: document.body,
  7691. mainContainer: document.getElementById('viewerContainer'),
  7692. viewerContainer: document.getElementById('viewer'),
  7693. toolbar: {
  7694. numPages: document.getElementById('numPages'),
  7695. pageNumber: document.getElementById('pageNumber'),
  7696. scaleSelectContainer: document.getElementById('scaleSelectContainer'),
  7697. scaleSelect: document.getElementById('scaleSelect'),
  7698. customScaleOption: document.getElementById('customScaleOption'),
  7699. previous: document.getElementById('previous'),
  7700. next: document.getElementById('next'),
  7701. firstPage: document.getElementById('firstPage'),
  7702. lastPage: document.getElementById('lastPage'),
  7703. zoomIn: document.getElementById('zoomIn'),
  7704. zoomOut: document.getElementById('zoomOut'),
  7705. viewFind: document.getElementById('viewFind'),
  7706. openFile: document.getElementById('openFile'),
  7707. print: document.getElementById('print'),
  7708. presentationModeButton: document.getElementById('presentationMode'),
  7709. download: document.getElementById('download'),
  7710. viewBookmark: document.getElementById('viewBookmark'),
  7711. },
  7712. secondaryToolbar: {
  7713. toolbar: document.getElementById('secondaryToolbar'),
  7714. toggleButton: document.getElementById('secondaryToolbarToggle'),
  7715. presentationModeButton:
  7716. document.getElementById('secondaryPresentationMode'),
  7717. openFile: document.getElementById('secondaryOpenFile'),
  7718. print: document.getElementById('secondaryPrint'),
  7719. download: document.getElementById('secondaryDownload'),
  7720. viewBookmark: document.getElementById('secondaryViewBookmark'),
  7721. firstPage: document.getElementById('firstPage'),
  7722. lastPage: document.getElementById('lastPage'),
  7723. pageRotateCw: document.getElementById('pageRotateCw'),
  7724. pageRotateCcw: document.getElementById('pageRotateCcw'),
  7725. documentPropertiesButton: document.getElementById('documentProperties'),
  7726. toggleHandTool: document.getElementById('toggleHandTool'),
  7727. },
  7728. fullscreen: {
  7729. contextFirstPage: document.getElementById('contextFirstPage'),
  7730. contextLastPage: document.getElementById('contextLastPage'),
  7731. contextPageRotateCw: document.getElementById('contextPageRotateCw'),
  7732. contextPageRotateCcw: document.getElementById('contextPageRotateCcw'),
  7733. },
  7734. sidebar: {
  7735. // Divs (and sidebar button)
  7736. mainContainer: document.getElementById('mainContainer'),
  7737. outerContainer: document.getElementById('outerContainer'),
  7738. toggleButton: document.getElementById('sidebarToggle'),
  7739. // Buttons
  7740. thumbnailButton: document.getElementById('viewThumbnail'),
  7741. outlineButton: document.getElementById('viewOutline'),
  7742. attachmentsButton: document.getElementById('viewAttachments'),
  7743. // Views
  7744. thumbnailView: document.getElementById('thumbnailView'),
  7745. outlineView: document.getElementById('outlineView'),
  7746. attachmentsView: document.getElementById('attachmentsView'),
  7747. },
  7748. findBar: {
  7749. bar: document.getElementById('findbar'),
  7750. toggleButton: document.getElementById('viewFind'),
  7751. findField: document.getElementById('findInput'),
  7752. highlightAllCheckbox: document.getElementById('findHighlightAll'),
  7753. caseSensitiveCheckbox: document.getElementById('findMatchCase'),
  7754. findMsg: document.getElementById('findMsg'),
  7755. findResultsCount: document.getElementById('findResultsCount'),
  7756. findStatusIcon: document.getElementById('findStatusIcon'),
  7757. findPreviousButton: document.getElementById('findPrevious'),
  7758. findNextButton: document.getElementById('findNext')
  7759. },
  7760. passwordOverlay: {
  7761. overlayName: 'passwordOverlay',
  7762. container: document.getElementById('passwordOverlay'),
  7763. label: document.getElementById('passwordText'),
  7764. input: document.getElementById('password'),
  7765. submitButton: document.getElementById('passwordSubmit'),
  7766. cancelButton: document.getElementById('passwordCancel')
  7767. },
  7768. documentProperties: {
  7769. overlayName: 'documentPropertiesOverlay',
  7770. container: document.getElementById('documentPropertiesOverlay'),
  7771. closeButton: document.getElementById('documentPropertiesClose'),
  7772. fields: {
  7773. 'fileName': document.getElementById('fileNameField'),
  7774. 'fileSize': document.getElementById('fileSizeField'),
  7775. 'title': document.getElementById('titleField'),
  7776. 'author': document.getElementById('authorField'),
  7777. 'subject': document.getElementById('subjectField'),
  7778. 'keywords': document.getElementById('keywordsField'),
  7779. 'creationDate': document.getElementById('creationDateField'),
  7780. 'modificationDate': document.getElementById('modificationDateField'),
  7781. 'creator': document.getElementById('creatorField'),
  7782. 'producer': document.getElementById('producerField'),
  7783. 'version': document.getElementById('versionField'),
  7784. 'pageCount': document.getElementById('pageCountField')
  7785. }
  7786. },
  7787. errorWrapper: {
  7788. container: document.getElementById('errorWrapper'),
  7789. errorMessage: document.getElementById('errorMessage'),
  7790. closeButton: document.getElementById('errorClose'),
  7791. errorMoreInfo: document.getElementById('errorMoreInfo'),
  7792. moreInfoButton: document.getElementById('errorShowMore'),
  7793. lessInfoButton: document.getElementById('errorShowLess'),
  7794. },
  7795. printContainer: document.getElementById('printContainer'),
  7796. openFileInputName: 'fileInput',
  7797. };
  7798. }
  7799. function webViewerLoad() {
  7800. var config = getViewerConfiguration();
  7801. window.PDFViewerApplication = pdfjsWebLibs.pdfjsWebApp.PDFViewerApplication;
  7802. pdfjsWebLibs.pdfjsWebApp.PDFViewerApplication.run(config);
  7803. }
  7804. document.addEventListener('DOMContentLoaded', webViewerLoad, true);