PageRenderTime 73ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/web/viewer.js

https://github.com/hashir/pdf.js
JavaScript | 2429 lines | 1779 code | 282 blank | 368 comment | 307 complexity | f80dea653934ba02b6dbb83ed0fc6e29 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
  3. /* Copyright 2012 Mozilla Foundation
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, PDFFindBar, CustomStyle,
  18. PDFFindController, ProgressBar, TextLayerBuilder, DownloadManager,
  19. getFileName, scrollIntoView, getPDFFileNameFromURL, PDFHistory,
  20. Preferences, SidebarView, ViewHistory, PageView, ThumbnailView, URL,
  21. noContextMenuHandler, SecondaryToolbar, PasswordPrompt,
  22. PresentationMode, HandTool, Promise, DocumentProperties,
  23. DocumentOutlineView, DocumentAttachmentsView */
  24. 'use strict';
  25. var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
  26. var DEFAULT_SCALE = 'auto';
  27. var DEFAULT_SCALE_DELTA = 1.1;
  28. var UNKNOWN_SCALE = 0;
  29. var CACHE_SIZE = 20;
  30. var CSS_UNITS = 96.0 / 72.0;
  31. var SCROLLBAR_PADDING = 40;
  32. var VERTICAL_PADDING = 5;
  33. var MAX_AUTO_SCALE = 1.25;
  34. var MIN_SCALE = 0.25;
  35. var MAX_SCALE = 4.0;
  36. var VIEW_HISTORY_MEMORY = 20;
  37. var SCALE_SELECT_CONTAINER_PADDING = 8;
  38. var SCALE_SELECT_PADDING = 22;
  39. var THUMBNAIL_SCROLL_MARGIN = -19;
  40. var USE_ONLY_CSS_ZOOM = false;
  41. var CLEANUP_TIMEOUT = 30000;
  42. var IGNORE_CURRENT_POSITION_ON_ZOOM = false;
  43. //#if B2G
  44. //USE_ONLY_CSS_ZOOM = true;
  45. //PDFJS.disableTextLayer = true;
  46. //#endif
  47. var RenderingStates = {
  48. INITIAL: 0,
  49. RUNNING: 1,
  50. PAUSED: 2,
  51. FINISHED: 3
  52. };
  53. var FindStates = {
  54. FIND_FOUND: 0,
  55. FIND_NOTFOUND: 1,
  56. FIND_WRAPPED: 2,
  57. FIND_PENDING: 3
  58. };
  59. PDFJS.imageResourcesPath = './images/';
  60. //#if (FIREFOX || MOZCENTRAL || B2G || GENERIC || CHROME)
  61. //PDFJS.workerSrc = '../build/pdf.worker.js';
  62. //#endif
  63. //#if !PRODUCTION
  64. PDFJS.cMapUrl = '../external/bcmaps/';
  65. PDFJS.cMapPacked = true;
  66. //#else
  67. //PDFJS.cMapUrl = '../web/cmaps/';
  68. //PDFJS.cMapPacked = true;
  69. //#endif
  70. var mozL10n = document.mozL10n || document.webL10n;
  71. //#include ui_utils.js
  72. //#include preferences.js
  73. //#if !(FIREFOX || MOZCENTRAL || B2G)
  74. //#include mozPrintCallback_polyfill.js
  75. //#endif
  76. //#if GENERIC || CHROME
  77. //#include download_manager.js
  78. //#endif
  79. //#if FIREFOX || MOZCENTRAL
  80. //#include firefoxcom.js
  81. //#endif
  82. //#if CHROME
  83. //#include chromecom.js
  84. //#endif
  85. var cache = new Cache(CACHE_SIZE);
  86. var currentPageNumber = 1;
  87. //#include view_history.js
  88. //#include pdf_find_bar.js
  89. //#include pdf_find_controller.js
  90. //#include pdf_history.js
  91. //#include secondary_toolbar.js
  92. //#include password_prompt.js
  93. //#include presentation_mode.js
  94. //#include hand_tool.js
  95. //#include document_properties.js
  96. var PDFView = {
  97. pages: [],
  98. thumbnails: [],
  99. currentScale: UNKNOWN_SCALE,
  100. currentScaleValue: null,
  101. initialBookmark: document.location.hash.substring(1),
  102. container: null,
  103. thumbnailContainer: null,
  104. initialized: false,
  105. fellback: false,
  106. pdfDocument: null,
  107. sidebarOpen: false,
  108. pageViewScroll: null,
  109. thumbnailViewScroll: null,
  110. pageRotation: 0,
  111. mouseScrollTimeStamp: 0,
  112. mouseScrollDelta: 0,
  113. lastScroll: 0,
  114. previousPageNumber: 1,
  115. isViewerEmbedded: (window.parent !== window),
  116. idleTimeout: null,
  117. currentPosition: null,
  118. // called once when the document is loaded
  119. initialize: function pdfViewInitialize() {
  120. var self = this;
  121. var container = this.container = document.getElementById('viewerContainer');
  122. this.pageViewScroll = {};
  123. this.watchScroll(container, this.pageViewScroll, updateViewarea);
  124. var thumbnailContainer = this.thumbnailContainer =
  125. document.getElementById('thumbnailView');
  126. this.thumbnailViewScroll = {};
  127. this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
  128. this.renderHighestPriority.bind(this));
  129. Preferences.initialize();
  130. PDFFindBar.initialize({
  131. bar: document.getElementById('findbar'),
  132. toggleButton: document.getElementById('viewFind'),
  133. findField: document.getElementById('findInput'),
  134. highlightAllCheckbox: document.getElementById('findHighlightAll'),
  135. caseSensitiveCheckbox: document.getElementById('findMatchCase'),
  136. findMsg: document.getElementById('findMsg'),
  137. findStatusIcon: document.getElementById('findStatusIcon'),
  138. findPreviousButton: document.getElementById('findPrevious'),
  139. findNextButton: document.getElementById('findNext')
  140. });
  141. PDFFindController.initialize({
  142. pdfPageSource: this,
  143. integratedFind: this.supportsIntegratedFind
  144. });
  145. HandTool.initialize({
  146. container: container,
  147. toggleHandTool: document.getElementById('toggleHandTool')
  148. });
  149. SecondaryToolbar.initialize({
  150. toolbar: document.getElementById('secondaryToolbar'),
  151. presentationMode: PresentationMode,
  152. toggleButton: document.getElementById('secondaryToolbarToggle'),
  153. presentationModeButton:
  154. document.getElementById('secondaryPresentationMode'),
  155. openFile: document.getElementById('secondaryOpenFile'),
  156. print: document.getElementById('secondaryPrint'),
  157. download: document.getElementById('secondaryDownload'),
  158. viewBookmark: document.getElementById('secondaryViewBookmark'),
  159. firstPage: document.getElementById('firstPage'),
  160. lastPage: document.getElementById('lastPage'),
  161. pageRotateCw: document.getElementById('pageRotateCw'),
  162. pageRotateCcw: document.getElementById('pageRotateCcw'),
  163. documentProperties: DocumentProperties,
  164. documentPropertiesButton: document.getElementById('documentProperties')
  165. });
  166. PasswordPrompt.initialize({
  167. overlayContainer: document.getElementById('overlayContainer'),
  168. passwordField: document.getElementById('password'),
  169. passwordText: document.getElementById('passwordText'),
  170. passwordSubmit: document.getElementById('passwordSubmit'),
  171. passwordCancel: document.getElementById('passwordCancel')
  172. });
  173. PresentationMode.initialize({
  174. container: container,
  175. secondaryToolbar: SecondaryToolbar,
  176. firstPage: document.getElementById('contextFirstPage'),
  177. lastPage: document.getElementById('contextLastPage'),
  178. pageRotateCw: document.getElementById('contextPageRotateCw'),
  179. pageRotateCcw: document.getElementById('contextPageRotateCcw')
  180. });
  181. DocumentProperties.initialize({
  182. overlayContainer: document.getElementById('overlayContainer'),
  183. closeButton: document.getElementById('documentPropertiesClose'),
  184. fileNameField: document.getElementById('fileNameField'),
  185. fileSizeField: document.getElementById('fileSizeField'),
  186. titleField: document.getElementById('titleField'),
  187. authorField: document.getElementById('authorField'),
  188. subjectField: document.getElementById('subjectField'),
  189. keywordsField: document.getElementById('keywordsField'),
  190. creationDateField: document.getElementById('creationDateField'),
  191. modificationDateField: document.getElementById('modificationDateField'),
  192. creatorField: document.getElementById('creatorField'),
  193. producerField: document.getElementById('producerField'),
  194. versionField: document.getElementById('versionField'),
  195. pageCountField: document.getElementById('pageCountField')
  196. });
  197. container.addEventListener('scroll', function() {
  198. self.lastScroll = Date.now();
  199. }, false);
  200. var initializedPromise = Promise.all([
  201. Preferences.get('enableWebGL').then(function resolved(value) {
  202. PDFJS.disableWebGL = !value;
  203. }, function rejected(reason) {}),
  204. Preferences.get('sidebarViewOnLoad').then(function resolved(value) {
  205. self.preferenceSidebarViewOnLoad = value;
  206. }, function rejected(reason) {})
  207. // TODO move more preferences and other async stuff here
  208. ]);
  209. return initializedPromise.then(function () {
  210. PDFView.initialized = true;
  211. });
  212. },
  213. getPage: function pdfViewGetPage(n) {
  214. return this.pdfDocument.getPage(n);
  215. },
  216. // Helper function to keep track whether a div was scrolled up or down and
  217. // then call a callback.
  218. watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) {
  219. state.down = true;
  220. state.lastY = viewAreaElement.scrollTop;
  221. viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) {
  222. if (!PDFView.pdfDocument) {
  223. return;
  224. }
  225. var currentY = viewAreaElement.scrollTop;
  226. var lastY = state.lastY;
  227. if (currentY > lastY) {
  228. state.down = true;
  229. } else if (currentY < lastY) {
  230. state.down = false;
  231. }
  232. // else do nothing and use previous value
  233. state.lastY = currentY;
  234. callback();
  235. }, true);
  236. },
  237. _setScaleUpdatePages: function pdfView_setScaleUpdatePages(
  238. newScale, newValue, resetAutoSettings, noScroll) {
  239. this.currentScaleValue = newValue;
  240. if (newScale === this.currentScale) {
  241. return;
  242. }
  243. for (var i = 0, ii = this.pages.length; i < ii; i++) {
  244. this.pages[i].update(newScale);
  245. }
  246. this.currentScale = newScale;
  247. if (!noScroll) {
  248. var page = this.page, dest;
  249. if (this.currentPosition && !IGNORE_CURRENT_POSITION_ON_ZOOM) {
  250. page = this.currentPosition.page;
  251. dest = [null, { name: 'XYZ' }, this.currentPosition.left,
  252. this.currentPosition.top, null];
  253. }
  254. this.pages[page - 1].scrollIntoView(dest);
  255. }
  256. var event = document.createEvent('UIEvents');
  257. event.initUIEvent('scalechange', false, false, window, 0);
  258. event.scale = newScale;
  259. event.resetAutoSettings = resetAutoSettings;
  260. window.dispatchEvent(event);
  261. },
  262. setScale: function pdfViewSetScale(value, resetAutoSettings, noScroll) {
  263. if (value === 'custom') {
  264. return;
  265. }
  266. var scale = parseFloat(value);
  267. if (scale > 0) {
  268. this._setScaleUpdatePages(scale, value, true, noScroll);
  269. } else {
  270. var currentPage = this.pages[this.page - 1];
  271. if (!currentPage) {
  272. return;
  273. }
  274. var hPadding = PresentationMode.active ? 0 : SCROLLBAR_PADDING;
  275. var vPadding = PresentationMode.active ? 0 : VERTICAL_PADDING;
  276. var pageWidthScale = (this.container.clientWidth - hPadding) /
  277. currentPage.width * currentPage.scale;
  278. var pageHeightScale = (this.container.clientHeight - vPadding) /
  279. currentPage.height * currentPage.scale;
  280. switch (value) {
  281. case 'page-actual':
  282. scale = 1;
  283. break;
  284. case 'page-width':
  285. scale = pageWidthScale;
  286. break;
  287. case 'page-height':
  288. scale = pageHeightScale;
  289. break;
  290. case 'page-fit':
  291. scale = Math.min(pageWidthScale, pageHeightScale);
  292. break;
  293. case 'auto':
  294. scale = Math.min(MAX_AUTO_SCALE, pageWidthScale);
  295. break;
  296. default:
  297. console.error('pdfViewSetScale: \'' + value +
  298. '\' is an unknown zoom value.');
  299. return;
  300. }
  301. this._setScaleUpdatePages(scale, value, resetAutoSettings, noScroll);
  302. selectScaleOption(value);
  303. }
  304. },
  305. zoomIn: function pdfViewZoomIn(ticks) {
  306. var newScale = this.currentScale;
  307. do {
  308. newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
  309. newScale = Math.ceil(newScale * 10) / 10;
  310. newScale = Math.min(MAX_SCALE, newScale);
  311. } while (--ticks && newScale < MAX_SCALE);
  312. this.setScale(newScale, true);
  313. },
  314. zoomOut: function pdfViewZoomOut(ticks) {
  315. var newScale = this.currentScale;
  316. do {
  317. newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
  318. newScale = Math.floor(newScale * 10) / 10;
  319. newScale = Math.max(MIN_SCALE, newScale);
  320. } while (--ticks && newScale > MIN_SCALE);
  321. this.setScale(newScale, true);
  322. },
  323. set page(val) {
  324. var pages = this.pages;
  325. var event = document.createEvent('UIEvents');
  326. event.initUIEvent('pagechange', false, false, window, 0);
  327. if (!(0 < val && val <= pages.length)) {
  328. this.previousPageNumber = val;
  329. event.pageNumber = this.page;
  330. window.dispatchEvent(event);
  331. return;
  332. }
  333. pages[val - 1].updateStats();
  334. this.previousPageNumber = currentPageNumber;
  335. currentPageNumber = val;
  336. event.pageNumber = val;
  337. window.dispatchEvent(event);
  338. // checking if the this.page was called from the updateViewarea function:
  339. // avoiding the creation of two "set page" method (internal and public)
  340. if (updateViewarea.inProgress) {
  341. return;
  342. }
  343. // Avoid scrolling the first page during loading
  344. if (this.loading && val === 1) {
  345. return;
  346. }
  347. pages[val - 1].scrollIntoView();
  348. },
  349. get page() {
  350. return currentPageNumber;
  351. },
  352. get supportsPrinting() {
  353. var canvas = document.createElement('canvas');
  354. var value = 'mozPrintCallback' in canvas;
  355. // shadow
  356. Object.defineProperty(this, 'supportsPrinting', { value: value,
  357. enumerable: true,
  358. configurable: true,
  359. writable: false });
  360. return value;
  361. },
  362. get supportsFullscreen() {
  363. var doc = document.documentElement;
  364. var support = doc.requestFullscreen || doc.mozRequestFullScreen ||
  365. doc.webkitRequestFullScreen || doc.msRequestFullscreen;
  366. if (document.fullscreenEnabled === false ||
  367. document.mozFullScreenEnabled === false ||
  368. document.webkitFullscreenEnabled === false ||
  369. document.msFullscreenEnabled === false) {
  370. support = false;
  371. }
  372. Object.defineProperty(this, 'supportsFullscreen', { value: support,
  373. enumerable: true,
  374. configurable: true,
  375. writable: false });
  376. return support;
  377. },
  378. get supportsIntegratedFind() {
  379. var support = false;
  380. //#if !(FIREFOX || MOZCENTRAL)
  381. //#else
  382. // support = FirefoxCom.requestSync('supportsIntegratedFind');
  383. //#endif
  384. Object.defineProperty(this, 'supportsIntegratedFind', { value: support,
  385. enumerable: true,
  386. configurable: true,
  387. writable: false });
  388. return support;
  389. },
  390. get supportsDocumentFonts() {
  391. var support = true;
  392. //#if !(FIREFOX || MOZCENTRAL)
  393. //#else
  394. // support = FirefoxCom.requestSync('supportsDocumentFonts');
  395. //#endif
  396. Object.defineProperty(this, 'supportsDocumentFonts', { value: support,
  397. enumerable: true,
  398. configurable: true,
  399. writable: false });
  400. return support;
  401. },
  402. get supportsDocumentColors() {
  403. var support = true;
  404. //#if !(FIREFOX || MOZCENTRAL)
  405. //#else
  406. // support = FirefoxCom.requestSync('supportsDocumentColors');
  407. //#endif
  408. Object.defineProperty(this, 'supportsDocumentColors', { value: support,
  409. enumerable: true,
  410. configurable: true,
  411. writable: false });
  412. return support;
  413. },
  414. get loadingBar() {
  415. var bar = new ProgressBar('#loadingBar', {});
  416. Object.defineProperty(this, 'loadingBar', { value: bar,
  417. enumerable: true,
  418. configurable: true,
  419. writable: false });
  420. return bar;
  421. },
  422. get isHorizontalScrollbarEnabled() {
  423. return (PresentationMode.active ? false :
  424. (this.container.scrollWidth > this.container.clientWidth));
  425. },
  426. //#if (FIREFOX || MOZCENTRAL)
  427. initPassiveLoading: function pdfViewInitPassiveLoading() {
  428. var pdfDataRangeTransport = {
  429. rangeListeners: [],
  430. progressListeners: [],
  431. addRangeListener: function PdfDataRangeTransport_addRangeListener(
  432. listener) {
  433. this.rangeListeners.push(listener);
  434. },
  435. addProgressListener: function PdfDataRangeTransport_addProgressListener(
  436. listener) {
  437. this.progressListeners.push(listener);
  438. },
  439. onDataRange: function PdfDataRangeTransport_onDataRange(begin, chunk) {
  440. var listeners = this.rangeListeners;
  441. for (var i = 0, n = listeners.length; i < n; ++i) {
  442. listeners[i](begin, chunk);
  443. }
  444. },
  445. onDataProgress: function PdfDataRangeTransport_onDataProgress(loaded) {
  446. var listeners = this.progressListeners;
  447. for (var i = 0, n = listeners.length; i < n; ++i) {
  448. listeners[i](loaded);
  449. }
  450. },
  451. requestDataRange: function PdfDataRangeTransport_requestDataRange(
  452. begin, end) {
  453. FirefoxCom.request('requestDataRange', { begin: begin, end: end });
  454. }
  455. };
  456. window.addEventListener('message', function windowMessage(e) {
  457. if (e.source !== null) {
  458. // The message MUST originate from Chrome code.
  459. console.warn('Rejected untrusted message from ' + e.origin);
  460. return;
  461. }
  462. var args = e.data;
  463. if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) {
  464. return;
  465. }
  466. switch (args.pdfjsLoadAction) {
  467. case 'supportsRangedLoading':
  468. PDFView.open(args.pdfUrl, 0, undefined, pdfDataRangeTransport, {
  469. length: args.length,
  470. initialData: args.data
  471. });
  472. break;
  473. case 'range':
  474. pdfDataRangeTransport.onDataRange(args.begin, args.chunk);
  475. break;
  476. case 'rangeProgress':
  477. pdfDataRangeTransport.onDataProgress(args.loaded);
  478. break;
  479. case 'progress':
  480. PDFView.progress(args.loaded / args.total);
  481. break;
  482. case 'complete':
  483. if (!args.data) {
  484. PDFView.error(mozL10n.get('loading_error', null,
  485. 'An error occurred while loading the PDF.'), e);
  486. break;
  487. }
  488. PDFView.open(args.data, 0);
  489. break;
  490. }
  491. });
  492. FirefoxCom.requestSync('initPassiveLoading', null);
  493. },
  494. //#endif
  495. setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
  496. this.url = url;
  497. try {
  498. this.setTitle(decodeURIComponent(getFileName(url)) || url);
  499. } catch (e) {
  500. // decodeURIComponent may throw URIError,
  501. // fall back to using the unprocessed url in that case
  502. this.setTitle(url);
  503. }
  504. },
  505. setTitle: function pdfViewSetTitle(title) {
  506. document.title = title;
  507. //#if B2G
  508. // document.getElementById('activityTitle').textContent = title;
  509. //#endif
  510. },
  511. close: function pdfViewClose() {
  512. var errorWrapper = document.getElementById('errorWrapper');
  513. errorWrapper.setAttribute('hidden', 'true');
  514. if (!this.pdfDocument) {
  515. return;
  516. }
  517. this.pdfDocument.destroy();
  518. this.pdfDocument = null;
  519. var thumbsView = document.getElementById('thumbnailView');
  520. while (thumbsView.hasChildNodes()) {
  521. thumbsView.removeChild(thumbsView.lastChild);
  522. }
  523. if ('_loadingInterval' in thumbsView) {
  524. clearInterval(thumbsView._loadingInterval);
  525. }
  526. var container = document.getElementById('viewer');
  527. while (container.hasChildNodes()) {
  528. container.removeChild(container.lastChild);
  529. }
  530. if (typeof PDFBug !== 'undefined') {
  531. PDFBug.cleanup();
  532. }
  533. },
  534. // TODO(mack): This function signature should really be pdfViewOpen(url, args)
  535. open: function pdfViewOpen(url, scale, password,
  536. pdfDataRangeTransport, args) {
  537. if (this.pdfDocument) {
  538. // Reload the preferences if a document was previously opened.
  539. Preferences.reload();
  540. }
  541. this.close();
  542. var parameters = {password: password};
  543. if (typeof url === 'string') { // URL
  544. this.setTitleUsingUrl(url);
  545. parameters.url = url;
  546. } else if (url && 'byteLength' in url) { // ArrayBuffer
  547. parameters.data = url;
  548. }
  549. if (args) {
  550. for (var prop in args) {
  551. parameters[prop] = args[prop];
  552. }
  553. }
  554. var self = this;
  555. self.loading = true;
  556. self.downloadComplete = false;
  557. var passwordNeeded = function passwordNeeded(updatePassword, reason) {
  558. PasswordPrompt.updatePassword = updatePassword;
  559. PasswordPrompt.reason = reason;
  560. PasswordPrompt.show();
  561. };
  562. function getDocumentProgress(progressData) {
  563. self.progress(progressData.loaded / progressData.total);
  564. }
  565. PDFJS.getDocument(parameters, pdfDataRangeTransport, passwordNeeded,
  566. getDocumentProgress).then(
  567. function getDocumentCallback(pdfDocument) {
  568. self.load(pdfDocument, scale);
  569. self.loading = false;
  570. },
  571. function getDocumentError(message, exception) {
  572. var loadingErrorMessage = mozL10n.get('loading_error', null,
  573. 'An error occurred while loading the PDF.');
  574. if (exception && exception.name === 'InvalidPDFException') {
  575. // change error message also for other builds
  576. loadingErrorMessage = mozL10n.get('invalid_file_error', null,
  577. 'Invalid or corrupted PDF file.');
  578. //#if B2G
  579. // window.alert(loadingErrorMessage);
  580. // return window.close();
  581. //#endif
  582. }
  583. if (exception && exception.name === 'MissingPDFException') {
  584. // special message for missing PDF's
  585. loadingErrorMessage = mozL10n.get('missing_file_error', null,
  586. 'Missing PDF file.');
  587. //#if B2G
  588. // window.alert(loadingErrorMessage);
  589. // return window.close();
  590. //#endif
  591. }
  592. var moreInfo = {
  593. message: message
  594. };
  595. self.error(loadingErrorMessage, moreInfo);
  596. self.loading = false;
  597. }
  598. );
  599. },
  600. download: function pdfViewDownload() {
  601. function downloadByUrl() {
  602. downloadManager.downloadUrl(url, filename);
  603. }
  604. var url = this.url.split('#')[0];
  605. var filename = getPDFFileNameFromURL(url);
  606. var downloadManager = new DownloadManager();
  607. downloadManager.onerror = function (err) {
  608. // This error won't really be helpful because it's likely the
  609. // fallback won't work either (or is already open).
  610. PDFView.error('PDF failed to download.');
  611. };
  612. if (!this.pdfDocument) { // the PDF is not ready yet
  613. downloadByUrl();
  614. return;
  615. }
  616. if (!this.downloadComplete) { // the PDF is still downloading
  617. downloadByUrl();
  618. return;
  619. }
  620. this.pdfDocument.getData().then(
  621. function getDataSuccess(data) {
  622. var blob = PDFJS.createBlob(data, 'application/pdf');
  623. downloadManager.download(blob, url, filename);
  624. },
  625. downloadByUrl // Error occurred try downloading with just the url.
  626. ).then(null, downloadByUrl);
  627. },
  628. fallback: function pdfViewFallback(featureId) {
  629. //#if !(FIREFOX || MOZCENTRAL)
  630. // return;
  631. //#else
  632. // // Only trigger the fallback once so we don't spam the user with messages
  633. // // for one PDF.
  634. // if (this.fellback)
  635. // return;
  636. // this.fellback = true;
  637. // var url = this.url.split('#')[0];
  638. // FirefoxCom.request('fallback', { featureId: featureId, url: url },
  639. // function response(download) {
  640. // if (!download) {
  641. // return;
  642. // }
  643. // PDFView.download();
  644. // });
  645. //#endif
  646. },
  647. navigateTo: function pdfViewNavigateTo(dest) {
  648. var destString = '';
  649. var self = this;
  650. var goToDestination = function(destRef) {
  651. self.pendingRefStr = null;
  652. // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
  653. var pageNumber = destRef instanceof Object ?
  654. self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
  655. (destRef + 1);
  656. if (pageNumber) {
  657. if (pageNumber > self.pages.length) {
  658. pageNumber = self.pages.length;
  659. }
  660. var currentPage = self.pages[pageNumber - 1];
  661. currentPage.scrollIntoView(dest);
  662. // Update the browsing history.
  663. PDFHistory.push({ dest: dest, hash: destString, page: pageNumber });
  664. } else {
  665. self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) {
  666. var pageNum = pageIndex + 1;
  667. self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] = pageNum;
  668. goToDestination(destRef);
  669. });
  670. }
  671. };
  672. this.destinationsPromise.then(function() {
  673. if (typeof dest === 'string') {
  674. destString = dest;
  675. dest = self.destinations[dest];
  676. }
  677. if (!(dest instanceof Array)) {
  678. return; // invalid destination
  679. }
  680. goToDestination(dest[0]);
  681. });
  682. },
  683. getDestinationHash: function pdfViewGetDestinationHash(dest) {
  684. if (typeof dest === 'string') {
  685. return PDFView.getAnchorUrl('#' + escape(dest));
  686. }
  687. if (dest instanceof Array) {
  688. var destRef = dest[0]; // see navigateTo method for dest format
  689. var pageNumber = destRef instanceof Object ?
  690. this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
  691. (destRef + 1);
  692. if (pageNumber) {
  693. var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber);
  694. var destKind = dest[1];
  695. if (typeof destKind === 'object' && 'name' in destKind &&
  696. destKind.name == 'XYZ') {
  697. var scale = (dest[4] || this.currentScaleValue);
  698. var scaleNumber = parseFloat(scale);
  699. if (scaleNumber) {
  700. scale = scaleNumber * 100;
  701. }
  702. pdfOpenParams += '&zoom=' + scale;
  703. if (dest[2] || dest[3]) {
  704. pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
  705. }
  706. }
  707. return pdfOpenParams;
  708. }
  709. }
  710. return '';
  711. },
  712. /**
  713. * Prefix the full url on anchor links to make sure that links are resolved
  714. * relative to the current URL instead of the one defined in <base href>.
  715. * @param {String} anchor The anchor hash, including the #.
  716. */
  717. getAnchorUrl: function getAnchorUrl(anchor) {
  718. //#if (GENERIC || B2G)
  719. return anchor;
  720. //#endif
  721. //#if (FIREFOX || MOZCENTRAL)
  722. // return this.url.split('#')[0] + anchor;
  723. //#endif
  724. //#if CHROME
  725. // return location.href.split('#')[0] + anchor;
  726. //#endif
  727. },
  728. /**
  729. * Show the error box.
  730. * @param {String} message A message that is human readable.
  731. * @param {Object} moreInfo (optional) Further information about the error
  732. * that is more technical. Should have a 'message'
  733. * and optionally a 'stack' property.
  734. */
  735. error: function pdfViewError(message, moreInfo) {
  736. var moreInfoText = mozL10n.get('error_version_info',
  737. {version: PDFJS.version || '?', build: PDFJS.build || '?'},
  738. 'PDF.js v{{version}} (build: {{build}})') + '\n';
  739. if (moreInfo) {
  740. moreInfoText +=
  741. mozL10n.get('error_message', {message: moreInfo.message},
  742. 'Message: {{message}}');
  743. if (moreInfo.stack) {
  744. moreInfoText += '\n' +
  745. mozL10n.get('error_stack', {stack: moreInfo.stack},
  746. 'Stack: {{stack}}');
  747. } else {
  748. if (moreInfo.filename) {
  749. moreInfoText += '\n' +
  750. mozL10n.get('error_file', {file: moreInfo.filename},
  751. 'File: {{file}}');
  752. }
  753. if (moreInfo.lineNumber) {
  754. moreInfoText += '\n' +
  755. mozL10n.get('error_line', {line: moreInfo.lineNumber},
  756. 'Line: {{line}}');
  757. }
  758. }
  759. }
  760. //#if !(FIREFOX || MOZCENTRAL)
  761. var errorWrapper = document.getElementById('errorWrapper');
  762. errorWrapper.removeAttribute('hidden');
  763. var errorMessage = document.getElementById('errorMessage');
  764. errorMessage.textContent = message;
  765. var closeButton = document.getElementById('errorClose');
  766. closeButton.onclick = function() {
  767. errorWrapper.setAttribute('hidden', 'true');
  768. };
  769. var errorMoreInfo = document.getElementById('errorMoreInfo');
  770. var moreInfoButton = document.getElementById('errorShowMore');
  771. var lessInfoButton = document.getElementById('errorShowLess');
  772. moreInfoButton.onclick = function() {
  773. errorMoreInfo.removeAttribute('hidden');
  774. moreInfoButton.setAttribute('hidden', 'true');
  775. lessInfoButton.removeAttribute('hidden');
  776. errorMoreInfo.style.height = errorMoreInfo.scrollHeight + 'px';
  777. };
  778. lessInfoButton.onclick = function() {
  779. errorMoreInfo.setAttribute('hidden', 'true');
  780. moreInfoButton.removeAttribute('hidden');
  781. lessInfoButton.setAttribute('hidden', 'true');
  782. };
  783. moreInfoButton.oncontextmenu = noContextMenuHandler;
  784. lessInfoButton.oncontextmenu = noContextMenuHandler;
  785. closeButton.oncontextmenu = noContextMenuHandler;
  786. moreInfoButton.removeAttribute('hidden');
  787. lessInfoButton.setAttribute('hidden', 'true');
  788. errorMoreInfo.value = moreInfoText;
  789. //#else
  790. // console.error(message + '\n' + moreInfoText);
  791. // this.fallback();
  792. //#endif
  793. },
  794. progress: function pdfViewProgress(level) {
  795. var percent = Math.round(level * 100);
  796. // When we transition from full request to range requests, it's possible
  797. // that we discard some of the loaded data. This can cause the loading
  798. // bar to move backwards. So prevent this by only updating the bar if it
  799. // increases.
  800. if (percent > PDFView.loadingBar.percent || isNaN(percent)) {
  801. PDFView.loadingBar.percent = percent;
  802. }
  803. },
  804. load: function pdfViewLoad(pdfDocument, scale) {
  805. var self = this;
  806. var isOnePageRenderedResolved = false;
  807. var resolveOnePageRendered = null;
  808. var onePageRendered = new Promise(function (resolve) {
  809. resolveOnePageRendered = resolve;
  810. });
  811. function bindOnAfterDraw(pageView, thumbnailView) {
  812. // when page is painted, using the image as thumbnail base
  813. pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
  814. if (!isOnePageRenderedResolved) {
  815. isOnePageRenderedResolved = true;
  816. resolveOnePageRendered();
  817. }
  818. thumbnailView.setImage(pageView.canvas);
  819. };
  820. }
  821. PDFFindController.reset();
  822. this.pdfDocument = pdfDocument;
  823. DocumentProperties.resolveDataAvailable();
  824. var downloadedPromise = pdfDocument.getDownloadInfo().then(function() {
  825. self.downloadComplete = true;
  826. PDFView.loadingBar.hide();
  827. var outerContainer = document.getElementById('outerContainer');
  828. outerContainer.classList.remove('loadingInProgress');
  829. });
  830. var pagesCount = pdfDocument.numPages;
  831. var id = pdfDocument.fingerprint;
  832. document.getElementById('numPages').textContent =
  833. mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
  834. document.getElementById('pageNumber').max = pagesCount;
  835. PDFView.documentFingerprint = id;
  836. var store = PDFView.store = new ViewHistory(id);
  837. this.pageRotation = 0;
  838. var pages = this.pages = [];
  839. var pagesRefMap = this.pagesRefMap = {};
  840. var thumbnails = this.thumbnails = [];
  841. var resolvePagesPromise;
  842. var pagesPromise = new Promise(function (resolve) {
  843. resolvePagesPromise = resolve;
  844. });
  845. this.pagesPromise = pagesPromise;
  846. var firstPagePromise = pdfDocument.getPage(1);
  847. var container = document.getElementById('viewer');
  848. var thumbsView = document.getElementById('thumbnailView');
  849. // Fetch a single page so we can get a viewport that will be the default
  850. // viewport for all pages
  851. firstPagePromise.then(function(pdfPage) {
  852. var viewport = pdfPage.getViewport((scale || 1.0) * CSS_UNITS);
  853. for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
  854. var viewportClone = viewport.clone();
  855. var pageView = new PageView(container, pageNum, scale,
  856. self.navigateTo.bind(self),
  857. viewportClone);
  858. var thumbnailView = new ThumbnailView(thumbsView, pageNum,
  859. viewportClone);
  860. bindOnAfterDraw(pageView, thumbnailView);
  861. pages.push(pageView);
  862. thumbnails.push(thumbnailView);
  863. }
  864. // Fetch all the pages since the viewport is needed before printing
  865. // starts to create the correct size canvas. Wait until one page is
  866. // rendered so we don't tie up too many resources early on.
  867. onePageRendered.then(function () {
  868. if (!PDFJS.disableAutoFetch) {
  869. var getPagesLeft = pagesCount;
  870. for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
  871. pdfDocument.getPage(pageNum).then(function (pageNum, pdfPage) {
  872. var pageView = pages[pageNum - 1];
  873. if (!pageView.pdfPage) {
  874. pageView.setPdfPage(pdfPage);
  875. }
  876. var refStr = pdfPage.ref.num + ' ' + pdfPage.ref.gen + ' R';
  877. pagesRefMap[refStr] = pageNum;
  878. getPagesLeft--;
  879. if (!getPagesLeft) {
  880. resolvePagesPromise();
  881. }
  882. }.bind(null, pageNum));
  883. }
  884. } else {
  885. // XXX: Printing is semi-broken with auto fetch disabled.
  886. resolvePagesPromise();
  887. }
  888. });
  889. downloadedPromise.then(function () {
  890. var event = document.createEvent('CustomEvent');
  891. event.initCustomEvent('documentload', true, true, {});
  892. window.dispatchEvent(event);
  893. });
  894. PDFView.loadingBar.setWidth(container);
  895. PDFFindController.resolveFirstPage();
  896. // Initialize the browsing history.
  897. PDFHistory.initialize(self.documentFingerprint);
  898. });
  899. // Fetch the necessary preference values.
  900. var showPreviousViewOnLoad;
  901. var showPreviousViewOnLoadPromise =
  902. Preferences.get('showPreviousViewOnLoad').then(function (prefValue) {
  903. showPreviousViewOnLoad = prefValue;
  904. });
  905. var defaultZoomValue;
  906. var defaultZoomValuePromise =
  907. Preferences.get('defaultZoomValue').then(function (prefValue) {
  908. defaultZoomValue = prefValue;
  909. });
  910. var storePromise = store.initializedPromise;
  911. Promise.all([firstPagePromise, storePromise, showPreviousViewOnLoadPromise,
  912. defaultZoomValuePromise]).then(function resolved() {
  913. var storedHash = null;
  914. if (showPreviousViewOnLoad && store.get('exists', false)) {
  915. var pageNum = store.get('page', '1');
  916. var zoom = defaultZoomValue || store.get('zoom', PDFView.currentScale);
  917. var left = store.get('scrollLeft', '0');
  918. var top = store.get('scrollTop', '0');
  919. storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' +
  920. left + ',' + top;
  921. } else if (defaultZoomValue) {
  922. storedHash = 'page=1&zoom=' + defaultZoomValue;
  923. }
  924. self.setInitialView(storedHash, scale);
  925. // Make all navigation keys work on document load,
  926. // unless the viewer is embedded in a web page.
  927. if (!self.isViewerEmbedded) {
  928. self.container.focus();
  929. //#if (FIREFOX || MOZCENTRAL)
  930. // self.container.blur();
  931. //#endif
  932. }
  933. }, function rejected(reason) {
  934. console.error(reason);
  935. firstPagePromise.then(function () {
  936. self.setInitialView(null, scale);
  937. });
  938. });
  939. pagesPromise.then(function() {
  940. if (PDFView.supportsPrinting) {
  941. pdfDocument.getJavaScript().then(function(javaScript) {
  942. if (javaScript.length) {
  943. console.warn('Warning: JavaScript is not supported');
  944. PDFView.fallback(PDFJS.UNSUPPORTED_FEATURES.javaScript);
  945. }
  946. // Hack to support auto printing.
  947. var regex = /\bprint\s*\(/g;
  948. for (var i = 0, ii = javaScript.length; i < ii; i++) {
  949. var js = javaScript[i];
  950. if (js && regex.test(js)) {
  951. setTimeout(function() {
  952. window.print();
  953. });
  954. return;
  955. }
  956. }
  957. });
  958. }
  959. });
  960. var destinationsPromise =
  961. this.destinationsPromise = pdfDocument.getDestinations();
  962. destinationsPromise.then(function(destinations) {
  963. self.destinations = destinations;
  964. });
  965. // outline depends on destinations and pagesRefMap
  966. var promises = [pagesPromise, destinationsPromise,
  967. PDFView.animationStartedPromise];
  968. Promise.all(promises).then(function() {
  969. pdfDocument.getOutline().then(function(outline) {
  970. self.outline = new DocumentOutlineView(outline);
  971. document.getElementById('viewOutline').disabled = !outline;
  972. if (outline &&
  973. self.preferenceSidebarViewOnLoad === SidebarView.OUTLINE) {
  974. self.switchSidebarView('outline', true);
  975. }
  976. });
  977. pdfDocument.getAttachments().then(function(attachments) {
  978. self.attachments = new DocumentAttachmentsView(attachments);
  979. document.getElementById('viewAttachments').disabled = !attachments;
  980. if (attachments &&
  981. self.preferenceSidebarViewOnLoad === SidebarView.ATTACHMENTS) {
  982. self.switchSidebarView('attachments', true);
  983. }
  984. });
  985. });
  986. if (self.preferenceSidebarViewOnLoad === SidebarView.THUMBS) {
  987. Promise.all([firstPagePromise, onePageRendered]).then(function () {
  988. self.switchSidebarView('thumbs', true);
  989. });
  990. }
  991. pdfDocument.getMetadata().then(function(data) {
  992. var info = data.info, metadata = data.metadata;
  993. self.documentInfo = info;
  994. self.metadata = metadata;
  995. // Provides some basic debug information
  996. console.log('PDF ' + pdfDocument.fingerprint + ' [' +
  997. info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() +
  998. ' / ' + (info.Creator || '-').trim() + ']' +
  999. ' (PDF.js: ' + (PDFJS.version || '-') +
  1000. (!PDFJS.disableWebGL ? ' [WebGL]' : '') + ')');
  1001. var pdfTitle;
  1002. if (metadata && metadata.has('dc:title')) {
  1003. pdfTitle = metadata.get('dc:title');
  1004. }
  1005. if (!pdfTitle && info && info['Title']) {
  1006. pdfTitle = info['Title'];
  1007. }
  1008. if (pdfTitle) {
  1009. self.setTitle(pdfTitle + ' - ' + document.title);
  1010. }
  1011. if (info.IsAcroFormPresent) {
  1012. console.warn('Warning: AcroForm/XFA is not supported');
  1013. PDFView.fallback(PDFJS.UNSUPPORTED_FEATURES.forms);
  1014. }
  1015. //#if (FIREFOX || MOZCENTRAL)
  1016. // var versionId = String(info.PDFFormatVersion).slice(-1) | 0;
  1017. // var generatorId = 0;
  1018. // var KNOWN_GENERATORS = ["acrobat distiller", "acrobat pdfwritter",
  1019. // "adobe livecycle", "adobe pdf library", "adobe photoshop", "ghostscript",
  1020. // "tcpdf", "cairo", "dvipdfm", "dvips", "pdftex", "pdfkit", "itext",
  1021. // "prince", "quarkxpress", "mac os x", "microsoft", "openoffice", "oracle",
  1022. // "luradocument", "pdf-xchange", "antenna house", "aspose.cells", "fpdf"];
  1023. // var generatorId = 0;
  1024. // if (info.Producer) {
  1025. // KNOWN_GENERATORS.some(function (generator, s, i) {
  1026. // if (generator.indexOf(s) < 0) {
  1027. // return false;
  1028. // }
  1029. // generatorId = i + 1;
  1030. // return true;
  1031. // }.bind(null, info.Producer.toLowerCase()));
  1032. // }
  1033. // var formType = !info.IsAcroFormPresent ? null : info.IsXFAPresent ?
  1034. // 'xfa' : 'acroform';
  1035. // FirefoxCom.request('reportTelemetry', JSON.stringify({
  1036. // type: 'documentInfo',
  1037. // version: versionId,
  1038. // generator: generatorId,
  1039. // formType: formType
  1040. // }));
  1041. //#endif
  1042. });
  1043. },
  1044. setInitialView: function pdfViewSetInitialView(storedHash, scale) {
  1045. // Reset the current scale, as otherwise the page's scale might not get
  1046. // updated if the zoom level stayed the same.
  1047. this.currentScale = 0;
  1048. this.currentScaleValue = null;
  1049. // When opening a new file (when one is already loaded in the viewer):
  1050. // Reset 'currentPageNumber', since otherwise the page's scale will be wrong
  1051. // if 'currentPageNumber' is larger than the number of pages in the file.
  1052. document.getElementById('pageNumber').value = currentPageNumber = 1;
  1053. // Reset the current position when loading a new file,
  1054. // to prevent displaying the wrong position in the document.
  1055. this.currentPosition = null;
  1056. if (PDFHistory.initialDestination) {
  1057. this.navigateTo(PDFHistory.initialDestination);
  1058. PDFHistory.initialDestination = null;
  1059. } else if (this.initialBookmark) {
  1060. this.setHash(this.initialBookmark);
  1061. PDFHistory.push({ hash: this.initialBookmark }, !!this.initialBookmark);
  1062. this.initialBookmark = null;
  1063. } else if (storedHash) {
  1064. this.setHash(storedHash);
  1065. } else if (scale) {
  1066. this.setScale(scale, true);
  1067. this.page = 1;
  1068. }
  1069. if (PDFView.currentScale === UNKNOWN_SCALE) {
  1070. // Scale was not initialized: invalid bookmark or scale was not specified.
  1071. // Setting the default one.
  1072. this.setScale(DEFAULT_SCALE, true);
  1073. }
  1074. },
  1075. renderHighestPriority:
  1076. function pdfViewRenderHighestPriority(currentlyVisiblePages) {
  1077. if (PDFView.idleTimeout) {
  1078. clearTimeout(PDFView.idleTimeout);
  1079. PDFView.idleTimeout = null;
  1080. }
  1081. // Pages have a higher priority than thumbnails, so check them first.
  1082. var visiblePages = currentlyVisiblePages || this.getVisiblePages();
  1083. var pageView = this.getHighestPriority(visiblePages, this.pages,
  1084. this.pageViewScroll.down);
  1085. if (pageView) {
  1086. this.renderView(pageView, 'page');
  1087. return;
  1088. }
  1089. // No pages needed rendering so check thumbnails.
  1090. if (this.sidebarOpen) {
  1091. var visibleThumbs = this.getVisibleThumbs();
  1092. var thumbView = this.getHighestPriority(visibleThumbs,
  1093. this.thumbnails,
  1094. this.thumbnailViewScroll.down);
  1095. if (thumbView) {
  1096. this.renderView(thumbView, 'thumbnail');
  1097. return;
  1098. }
  1099. }
  1100. PDFView.idleTimeout = setTimeout(function () {
  1101. PDFView.cleanup();
  1102. }, CLEANUP_TIMEOUT);
  1103. },
  1104. cleanup: function pdfViewCleanup() {
  1105. for (var i = 0, ii = this.pages.length; i < ii; i++) {
  1106. if (this.pages[i] &&
  1107. this.pages[i].renderingState !== RenderingStates.FINISHED) {
  1108. this.pages[i].reset();
  1109. }
  1110. }
  1111. this.pdfDocument.cleanup();
  1112. },
  1113. getHighestPriority: function pdfViewGetHighestPriority(visible, views,
  1114. scrolledDown) {
  1115. // The state has changed figure out which page has the highest priority to
  1116. // render next (if any).
  1117. // Priority:
  1118. // 1 visible pages
  1119. // 2 if last scrolled down page after the visible pages
  1120. // 2 if last scrolled up page before the visible pages
  1121. var visibleViews = visible.views;
  1122. var numVisible = visibleViews.length;
  1123. if (numVisible === 0) {
  1124. return false;
  1125. }
  1126. for (var i = 0; i < numVisible; ++i) {
  1127. var view = visibleViews[i].view;
  1128. if (!this.isViewFinished(view)) {
  1129. return view;
  1130. }
  1131. }
  1132. // All the visible views have rendered, try to render next/previous pages.
  1133. if (scrolledDown) {
  1134. var nextPageIndex = visible.last.id;
  1135. // ID's start at 1 so no need to add 1.
  1136. if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) {
  1137. return views[nextPageIndex];
  1138. }
  1139. } else {
  1140. var previousPageIndex = visible.first.id - 2;
  1141. if (views[previousPageIndex] &&
  1142. !this.isViewFinished(views[previousPageIndex])) {
  1143. return views[previousPageIndex];
  1144. }
  1145. }
  1146. // Everything that needs to be rendered has been.
  1147. return false;
  1148. },
  1149. isViewFinished: function pdfViewIsViewFinished(view) {
  1150. return view.renderingState === RenderingStates.FINISHED;
  1151. },
  1152. // Render a page or thumbnail view. This calls the appropriate function based
  1153. // on the views state. If the view is already rendered it will return false.
  1154. renderView: function pdfViewRender(view, type) {
  1155. var state = view.renderingState;
  1156. switch (state) {
  1157. case RenderingStates.FINISHED:
  1158. return false;
  1159. case RenderingStates.PAUSED:
  1160. PDFView.highestPriorityPage = type + view.id;
  1161. view.resume();
  1162. break;
  1163. case RenderingStates.RUNNING:
  1164. PDFView.highestPriorityPage = type + view.id;
  1165. break;
  1166. case RenderingStates.INITIAL:
  1167. PDFView.highestPriorityPage = type + view.id;
  1168. view.draw(this.renderHighestPriority.bind(this));
  1169. break;
  1170. }
  1171. return true;
  1172. },
  1173. setHash: function pdfViewSetHash(hash) {
  1174. if (!hash) {
  1175. return;
  1176. }
  1177. if (hash.indexOf('=') >= 0) {
  1178. var params = PDFView.parseQueryString(hash);
  1179. // borrowing syntax from "Parameters for Opening PDF Files"
  1180. if ('nameddest' in params) {
  1181. PDFHistory.updateNextHashParam(params.nameddest);
  1182. PDFView.navigateTo(params.nameddest);
  1183. return;
  1184. }
  1185. var pageNumber, dest;
  1186. if ('page' in params) {
  1187. pageNumber = (params.page | 0) || 1;
  1188. }
  1189. if ('zoom' in params) {
  1190. var zoomArgs = params.zoom.split(','); // scale,left,top
  1191. // building destination array
  1192. // If the zoom value, it has to get divided by 100. If it is a string,
  1193. // it should stay as it is.
  1194. var zoomArg = zoomArgs[0];
  1195. var zoomArgNumber = parseFloat(zoomArg);
  1196. if (zoomArgNumber) {
  1197. zoomArg = zoomArgNumber / 100;
  1198. }
  1199. dest = [null, {name: 'XYZ'},
  1200. zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
  1201. zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
  1202. zoomArg];
  1203. }
  1204. if (dest) {
  1205. var currentPage = this.pages[(pageNumber || this.page) - 1];
  1206. currentPage.scrollIntoView(dest);
  1207. } else if (pageNumber) {
  1208. this.page = pageNumber; // simple page
  1209. }
  1210. if ('pagemode' in params) {
  1211. if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' ||
  1212. params.pagemode === 'attachments') {
  1213. this.switchSidebarView((params.pagemode === 'bookmarks' ?
  1214. 'outline' : params.pagemode), true);
  1215. } else if (params.pagemode === 'none' && this.sidebarOpen) {
  1216. document.getElementById('sidebarToggle').click();
  1217. }
  1218. }
  1219. } else if (/^\d+$/.test(hash)) { // page number
  1220. this.page = hash;
  1221. } else { // named destination
  1222. PDFHistory.updateNextHashParam(unescape(hash));
  1223. PDFView.navigateTo(unescape(hash));
  1224. }
  1225. },
  1226. switchSidebarView: function pdfViewSwitchSidebarView(view, openSidebar) {
  1227. if (openSidebar && !this.sidebarOpen) {
  1228. document.getElementById('sidebarToggle').click();
  1229. }
  1230. var thumbsView = document.getElementById('thumbnailView');
  1231. var outlineView = document.getElementById('outlineView');
  1232. var attachmentsView = document.getElementById('attachmentsView');
  1233. var thumbsButton = document.getElementById('viewThumbnail');
  1234. var outlineButton = document.getElementById('viewOutline');
  1235. var attachmentsButton = document.getElementById('viewAttachments');
  1236. switch (view) {
  1237. case 'thumbs':
  1238. var wasAnotherViewVisible = thumbsView.classList.contains('hidden');
  1239. thumbsButton.classList.add('toggled');
  1240. outlineButton.classList.remove('toggled');
  1241. attachmentsButton.classList.remove('toggled');
  1242. thumbsView.classList.remove('hidden');
  1243. outlineView.classList.add('hidden');
  1244. attachmentsView.classList.add('hidden');
  1245. PDFView.renderHighestPriority();
  1246. if (wasAnotherViewVisible) {
  1247. // Ensure that the thumbnail of the current page is visible
  1248. // when switching from another view.
  1249. scrollIntoView(document.getElementById('thumbnailContainer' +
  1250. this.page));
  1251. }
  1252. break;
  1253. case 'outline':
  1254. thumbsButton.classList.remove('toggled');
  1255. outlineButton.classList.add('toggled');
  1256. attachmentsButton.classList.remove('toggled');
  1257. thumbsView.classList.add('hidden');
  1258. outlineView.classList.remove('hidden');
  1259. attachmentsView.classList.add('hidden');
  1260. if (outlineButton.getAttribute('disabled')) {
  1261. return;
  1262. }
  1263. break;
  1264. case 'attachments':
  1265. thumbsButton.classList.remove('toggled');
  1266. outlineButton.classList.remove('toggled');
  1267. attachmentsButton.classList.add('toggled');
  1268. thumbsView.classList.add('hidden');
  1269. outlineView.classList.add('hidden');
  1270. attachmentsView.classList.remove('hidden');
  1271. if (attachmentsButton.getAttribute('disabled')) {
  1272. return;
  1273. }
  1274. break;
  1275. }
  1276. },
  1277. getVisiblePages: function pdfViewGetVisiblePages() {
  1278. if (!PresentationMode.active) {
  1279. return this.getVisibleElements(this.container, this.pages, true);
  1280. } else {
  1281. // The algorithm in getVisibleElements doesn't work in all browsers and
  1282. // configurations when presentation mode is active.
  1283. var visible = [];
  1284. var currentPage = this.pages[this.page - 1];
  1285. visible.push({ id: currentPage.id, view: currentPage });
  1286. return { first: currentPage, last: currentPage, views: visible };
  1287. }
  1288. },
  1289. getVisibleThumbs: function pdfViewGetVisibleThumbs() {
  1290. return this.getVisibleElements(this.thumbnailContainer, this.thumbnails);
  1291. },
  1292. // Generic helper to find out what elements are visible within a scroll pane.
  1293. getVisibleElements: function pdfViewGetVisibleElements(
  1294. scrollEl, views, sortByVisibility) {
  1295. var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
  1296. var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;
  1297. var visible = [], view;
  1298. var currentHeight, viewHeight, hiddenHeight, percentHeight;
  1299. var currentWidth, viewWidth;
  1300. for (var i = 0, ii = views.length; i < ii; ++i) {
  1301. view = views[i];
  1302. currentHeight = view.el.offsetTop + view.el.clientTop;
  1303. viewHeight = view.el.clientHeight;
  1304. if ((currentHeight + viewHeight) < top) {
  1305. continue;
  1306. }
  1307. if (currentHeight > bottom) {
  1308. break;
  1309. }
  1310. currentWidth = view.el.offsetLeft + view.el.clientLeft;
  1311. viewWidth = view.el.clientWidth;
  1312. if ((currentWidth + viewWidth) < left || currentWidth > right) {
  1313. continue;
  1314. }
  1315. hiddenHeight = Math.max(0, top - currentHeight) +
  1316. Math.max(0, currentHeight + viewHeight - bottom);
  1317. percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0;
  1318. visible.push({ id: view.id, x: currentWidth, y: currentHeight,
  1319. view: view, percent: percentHeight });
  1320. }
  1321. var first = visible[0];
  1322. var last = visible[visible.length - 1];
  1323. if (sortByVisibility) {
  1324. visible.sort(function(a, b) {
  1325. var pc = a.percent - b.percent;
  1326. if (Math.abs(pc) > 0.001) {
  1327. return -pc;
  1328. }
  1329. return a.id - b.id; // ensure stability
  1330. });
  1331. }
  1332. return {first: first, last: last, views: visible};
  1333. },
  1334. // Helper function to parse query string (e.g. ?param1=value&parm2=...).
  1335. parseQueryString: function pdfViewParseQueryString(query) {
  1336. var parts = query.split('&');
  1337. var params = {};
  1338. for (var i = 0, ii = parts.length; i < ii; ++i) {
  1339. var param = parts[i].split('=');
  1340. var key = param[0];
  1341. var value = param.length > 1 ? param[1] : null;
  1342. params[decodeURIComponent(key)] = decodeURIComponent(value);
  1343. }
  1344. return params;
  1345. },
  1346. beforePrint: function pdfViewSetupBeforePrint() {
  1347. if (!this.supportsPrinting) {
  1348. var printMessage = mozL10n.get('printing_not_supported', null,
  1349. 'Warning: Printing is not fully supported by this browser.');
  1350. this.error(printMessage);
  1351. return;
  1352. }
  1353. var alertNotReady = false;
  1354. var i, ii;
  1355. if (!this.pages.length) {
  1356. alertNotReady = true;
  1357. } else {
  1358. for (i = 0, ii = this.pages.length; i < ii; ++i) {
  1359. if (!this.pages[i].pdfPage) {
  1360. alertNotReady = true;
  1361. break;
  1362. }
  1363. }
  1364. }
  1365. if (alertNotReady) {
  1366. var notReadyMessage = mozL10n.get('printing_not_ready', null,
  1367. 'Warning: The PDF is not fully loaded for printing.');
  1368. window.alert(notReadyMessage);
  1369. return;
  1370. }
  1371. var body = document.querySelector('body');
  1372. body.setAttribute('data-mozPrintCallback', true);
  1373. for (i = 0, ii = this.pages.length; i < ii; ++i) {
  1374. this.pages[i].beforePrint();
  1375. }
  1376. },
  1377. afterPrint: function pdfViewSetupAfterPrint() {
  1378. var div = document.getElementById('printContainer');
  1379. while (div.hasChildNodes()) {
  1380. div.removeChild(div.lastChild);
  1381. }
  1382. },
  1383. rotatePages: function pdfViewRotatePages(delta) {
  1384. var currentPage = this.pages[this.page - 1];
  1385. var i, l;
  1386. this.pageRotation = (this.pageRotation + 360 + delta) % 360;
  1387. for (i = 0, l = this.pages.length; i < l; i++) {
  1388. var page = this.pages[i];
  1389. page.update(page.scale, this.pageRotation);
  1390. }
  1391. for (i = 0, l = this.thumbnails.length; i < l; i++) {
  1392. var thumb = this.thumbnails[i];
  1393. thumb.update(this.pageRotation);
  1394. }
  1395. this.setScale(this.currentScaleValue, true, true);
  1396. this.renderHighestPriority();
  1397. if (currentPage) {
  1398. currentPage.scrollIntoView();
  1399. }
  1400. },
  1401. /**
  1402. * This function flips the page in presentation mode if the user scrolls up
  1403. * or down with large enough motion and prevents page flipping too often.
  1404. *
  1405. * @this {PDFView}
  1406. * @param {number} mouseScrollDelta The delta value from the mouse event.
  1407. */
  1408. mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) {
  1409. var MOUSE_SCROLL_COOLDOWN_TIME = 50;
  1410. var currentTime = (new Date()).getTime();
  1411. var storedTime = this.mouseScrollTimeStamp;
  1412. // In case one page has already been flipped there is a cooldown time
  1413. // which has to expire before next page can be scrolled on to.
  1414. if (currentTime > storedTime &&
  1415. currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
  1416. return;
  1417. }
  1418. // In case the user decides to scroll to the opposite direction than before
  1419. // clear the accumulated delta.
  1420. if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) ||
  1421. (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) {
  1422. this.clearMouseScrollState();
  1423. }
  1424. this.mouseScrollDelta += mouseScrollDelta;
  1425. var PAGE_FLIP_THRESHOLD = 120;
  1426. if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) {
  1427. var PageFlipDirection = {
  1428. UP: -1,
  1429. DOWN: 1
  1430. };
  1431. // In presentation mode scroll one page at a time.
  1432. var pageFlipDirection = (this.mouseScrollDelta > 0) ?
  1433. PageFlipDirection.UP :
  1434. PageFlipDirection.DOWN;
  1435. this.clearMouseScrollState();
  1436. var currentPage = this.page;
  1437. // In case we are already on the first or the last page there is no need
  1438. // to do anything.
  1439. if ((currentPage == 1 && pageFlipDirection == PageFlipDirection.UP) ||
  1440. (currentPage == this.pages.length &&
  1441. pageFlipDirection == PageFlipDirection.DOWN)) {
  1442. return;
  1443. }
  1444. this.page += pageFlipDirection;
  1445. this.mouseScrollTimeStamp = currentTime;
  1446. }
  1447. },
  1448. /**
  1449. * This function clears the member attributes used with mouse scrolling in
  1450. * presentation mode.
  1451. *
  1452. * @this {PDFView}
  1453. */
  1454. clearMouseScrollState: function pdfViewClearMouseScrollState() {
  1455. this.mouseScrollTimeStamp = 0;
  1456. this.mouseScrollDelta = 0;
  1457. }
  1458. };
  1459. //#include page_view.js
  1460. //#include thumbnail_view.js
  1461. //#include text_layer_builder.js
  1462. //#include document_outline_view.js
  1463. //#include document_attachments_view.js
  1464. //#if CHROME
  1465. //(function rewriteUrlClosure() {
  1466. // // Run this code outside DOMContentLoaded to make sure that the URL
  1467. // // is rewritten as soon as possible.
  1468. // var params = PDFView.parseQueryString(document.location.search.slice(1));
  1469. // DEFAULT_URL = params.file || '';
  1470. //
  1471. // // Example: chrome-extension://.../http://example.com/file.pdf
  1472. // var humanReadableUrl = '/' + DEFAULT_URL + location.hash;
  1473. // history.replaceState(history.state, '', humanReadableUrl);
  1474. // if (top === window) {
  1475. // chrome.runtime.sendMessage('showPageAction');
  1476. // }
  1477. //})();
  1478. //#endif
  1479. function webViewerLoad(evt) {
  1480. PDFView.initialize().then(webViewerInitialized);
  1481. }
  1482. function webViewerInitialized() {
  1483. //#if (GENERIC || B2G)
  1484. var params = PDFView.parseQueryString(document.location.search.substring(1));
  1485. var file = 'file' in params ? params.file : DEFAULT_URL;
  1486. //#endif
  1487. //#if (FIREFOX || MOZCENTRAL)
  1488. //var file = window.location.href.split('#')[0];
  1489. //#endif
  1490. //#if CHROME
  1491. //var file = DEFAULT_URL;
  1492. //#endif
  1493. //#if !(FIREFOX || MOZCENTRAL || CHROME || B2G)
  1494. var fileInput = document.createElement('input');
  1495. fileInput.id = 'fileInput';
  1496. fileInput.className = 'fileInput';
  1497. fileInput.setAttribute('type', 'file');
  1498. fileInput.oncontextmenu = noContextMenuHandler;
  1499. document.body.appendChild(fileInput);
  1500. if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {
  1501. document.getElementById('openFile').setAttribute('hidden', 'true');
  1502. document.getElementById('secondaryOpenFile').setAttribute('hidden', 'true');
  1503. } else {
  1504. document.getElementById('fileInput').value = null;
  1505. }
  1506. //#else
  1507. //document.getElementById('openFile').setAttribute('hidden', 'true');
  1508. //document.getElementById('secondaryOpenFile').setAttribute('hidden', 'true');
  1509. //#endif
  1510. // Special debugging flags in the hash section of the URL.
  1511. var hash = document.location.hash.substring(1);
  1512. var hashParams = PDFView.parseQueryString(hash);
  1513. if ('disableWorker' in hashParams) {
  1514. PDFJS.disableWorker = (hashParams['disableWorker'] === 'true');
  1515. }
  1516. if ('disableRange' in hashParams) {
  1517. PDFJS.disableRange = (hashParams['disableRange'] === 'true');
  1518. }
  1519. if ('disableAutoFetch' in hashParams) {
  1520. PDFJS.disableAutoFetch = (hashParams['disableAutoFetch'] === 'true');
  1521. }
  1522. if ('disableFontFace' in hashParams) {
  1523. PDFJS.disableFontFace = (hashParams['disableFontFace'] === 'true');
  1524. }
  1525. if ('disableHistory' in hashParams) {
  1526. PDFJS.disableHistory = (hashParams['disableHistory'] === 'true');
  1527. }
  1528. if ('webgl' in hashParams) {
  1529. PDFJS.disableWebGL = (hashParams['webgl'] !== 'true');
  1530. }
  1531. if ('useOnlyCssZoom' in hashParams) {
  1532. USE_ONLY_CSS_ZOOM = (hashParams['useOnlyCssZoom'] === 'true');
  1533. }
  1534. if ('verbosity' in hashParams) {
  1535. PDFJS.verbosity = hashParams['verbosity'] | 0;
  1536. }
  1537. if ('ignoreCurrentPositionOnZoom' in hashParams) {
  1538. IGNORE_CURRENT_POSITION_ON_ZOOM =
  1539. (hashParams['ignoreCurrentPositionOnZoom'] === 'true');
  1540. }
  1541. //#if !PRODUCTION
  1542. if ('disableBcmaps' in hashParams && hashParams['disableBcmaps']) {
  1543. PDFJS.cMapUrl = '../external/cmaps/';
  1544. PDFJS.cMapPacked = false;
  1545. }
  1546. //#endif
  1547. //#if !(FIREFOX || MOZCENTRAL)
  1548. var locale = PDFJS.locale || navigator.language;
  1549. if ('locale' in hashParams) {
  1550. locale = hashParams['locale'];
  1551. }
  1552. mozL10n.setLanguage(locale);
  1553. //#endif
  1554. //#if (FIREFOX || MOZCENTRAL)
  1555. //if (!PDFView.supportsDocumentFonts) {
  1556. // PDFJS.disableFontFace = true;
  1557. // console.warn(mozL10n.get('web_fonts_disabled', null,
  1558. // 'Web fonts are disabled: unable to use embedded PDF fonts.'));
  1559. //}
  1560. //#endif
  1561. if ('textLayer' in hashParams) {
  1562. switch (hashParams['textLayer']) {
  1563. case 'off':
  1564. PDFJS.disableTextLayer = true;
  1565. break;
  1566. case 'visible':
  1567. case 'shadow':
  1568. case 'hover':
  1569. var viewer = document.getElementById('viewer');
  1570. viewer.classList.add('textLayer-' + hashParams['textLayer']);
  1571. break;
  1572. }
  1573. }
  1574. //#if !(FIREFOX || MOZCENTRAL)
  1575. if ('pdfBug' in hashParams) {
  1576. //#else
  1577. //if ('pdfBug' in hashParams && FirefoxCom.requestSync('pdfBugEnabled')) {
  1578. //#endif
  1579. PDFJS.pdfBug = true;
  1580. var pdfBug = hashParams['pdfBug'];
  1581. var enabled = pdfBug.split(',');
  1582. PDFBug.enable(enabled);
  1583. PDFBug.init();
  1584. }
  1585. if (!PDFView.supportsPrinting) {
  1586. document.getElementById('print').classList.add('hidden');
  1587. document.getElementById('secondaryPrint').classList.add('hidden');
  1588. }
  1589. if (!PDFView.supportsFullscreen) {
  1590. document.getElementById('presentationMode').classList.add('hidden');
  1591. document.getElementById('secondaryPresentationMode').
  1592. classList.add('hidden');
  1593. }
  1594. if (PDFView.supportsIntegratedFind) {
  1595. document.getElementById('viewFind').classList.add('hidden');
  1596. }
  1597. // Listen for unsuporrted features to trigger the fallback UI.
  1598. PDFJS.UnsupportedManager.listen(PDFView.fallback.bind(PDFView));
  1599. // Suppress context menus for some controls
  1600. document.getElementById('scaleSelect').oncontextmenu = noContextMenuHandler;
  1601. var mainContainer = document.getElementById('mainContainer');
  1602. var outerContainer = document.getElementById('outerContainer');
  1603. mainContainer.addEventListener('transitionend', function(e) {
  1604. if (e.target == mainContainer) {
  1605. var event = document.createEvent('UIEvents');
  1606. event.initUIEvent('resize', false, false, window, 0);
  1607. window.dispatchEvent(event);
  1608. outerContainer.classList.remove('sidebarMoving');
  1609. }
  1610. }, true);
  1611. document.getElementById('sidebarToggle').addEventListener('click',
  1612. function() {
  1613. this.classList.toggle('toggled');
  1614. outerContainer.classList.add('sidebarMoving');
  1615. outerContainer.classList.toggle('sidebarOpen');
  1616. PDFView.sidebarOpen = outerContainer.classList.contains('sidebarOpen');
  1617. PDFView.renderHighestPriority();
  1618. });
  1619. document.getElementById('viewThumbnail').addEventListener('click',
  1620. function() {
  1621. PDFView.switchSidebarView('thumbs');
  1622. });
  1623. document.getElementById('viewOutline').addEventListener('click',
  1624. function() {
  1625. PDFView.switchSidebarView('outline');
  1626. });
  1627. document.getElementById('viewAttachments').addEventListener('click',
  1628. function() {
  1629. PDFView.switchSidebarView('attachments');
  1630. });
  1631. document.getElementById('previous').addEventListener('click',
  1632. function() {
  1633. PDFView.page--;
  1634. });
  1635. document.getElementById('next').addEventListener('click',
  1636. function() {
  1637. PDFView.page++;
  1638. });
  1639. document.getElementById('zoomIn').addEventListener('click',
  1640. function() {
  1641. PDFView.zoomIn();
  1642. });
  1643. document.getElementById('zoomOut').addEventListener('click',
  1644. function() {
  1645. PDFView.zoomOut();
  1646. });
  1647. document.getElementById('pageNumber').addEventListener('click',
  1648. function() {
  1649. this.select();
  1650. });
  1651. document.getElementById('pageNumber').addEventListener('change',
  1652. function() {
  1653. // Handle the user inputting a floating point number.
  1654. PDFView.page = (this.value | 0);
  1655. if (this.value !== (this.value | 0).toString()) {
  1656. this.value = PDFView.page;
  1657. }
  1658. });
  1659. document.getElementById('scaleSelect').addEventListener('change',
  1660. function() {
  1661. PDFView.setScale(this.value);
  1662. });
  1663. document.getElementById('presentationMode').addEventListener('click',
  1664. SecondaryToolbar.presentationModeClick.bind(SecondaryToolbar));
  1665. document.getElementById('openFile').addEventListener('click',
  1666. SecondaryToolbar.openFileClick.bind(SecondaryToolbar));
  1667. document.getElementById('print').addEventListener('click',
  1668. SecondaryToolbar.printClick.bind(SecondaryToolbar));
  1669. document.getElementById('download').addEventListener('click',
  1670. SecondaryToolbar.downloadClick.bind(SecondaryToolbar));
  1671. //#if (FIREFOX || MOZCENTRAL)
  1672. //PDFView.setTitleUsingUrl(file);
  1673. //PDFView.initPassiveLoading();
  1674. //return;
  1675. //#endif
  1676. //#if !B2G && !CHROME
  1677. if (file) {
  1678. PDFView.open(file, 0);
  1679. }
  1680. //#endif
  1681. //#if CHROME
  1682. //if (file) {
  1683. // ChromeCom.openPDFFile(file);
  1684. //}
  1685. //#endif
  1686. }
  1687. document.addEventListener('DOMContentLoaded', webViewerLoad, true);
  1688. function updateViewarea() {
  1689. if (!PDFView.initialized) {
  1690. return;
  1691. }
  1692. var visible = PDFView.getVisiblePages();
  1693. var visiblePages = visible.views;
  1694. if (visiblePages.length === 0) {
  1695. return;
  1696. }
  1697. PDFView.renderHighestPriority(visible);
  1698. var currentId = PDFView.page;
  1699. var firstPage = visible.first;
  1700. for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
  1701. i < ii; ++i) {
  1702. var page = visiblePages[i];
  1703. if (page.percent < 100) {
  1704. break;
  1705. }
  1706. if (page.id === PDFView.page) {
  1707. stillFullyVisible = true;
  1708. break;
  1709. }
  1710. }
  1711. if (!stillFullyVisible) {
  1712. currentId = visiblePages[0].id;
  1713. }
  1714. if (!PresentationMode.active) {
  1715. updateViewarea.inProgress = true; // used in "set page"
  1716. PDFView.page = currentId;
  1717. updateViewarea.inProgress = false;
  1718. }
  1719. var currentScale = PDFView.currentScale;
  1720. var currentScaleValue = PDFView.currentScaleValue;
  1721. var normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ?
  1722. Math.round(currentScale * 10000) / 100 : currentScaleValue;
  1723. var pageNumber = firstPage.id;
  1724. var pdfOpenParams = '#page=' + pageNumber;
  1725. pdfOpenParams += '&zoom=' + normalizedScaleValue;
  1726. var currentPage = PDFView.pages[pageNumber - 1];
  1727. var container = PDFView.container;
  1728. var topLeft = currentPage.getPagePoint((container.scrollLeft - firstPage.x),
  1729. (container.scrollTop - firstPage.y));
  1730. var intLeft = Math.round(topLeft[0]);
  1731. var intTop = Math.round(topLeft[1]);
  1732. pdfOpenParams += ',' + intLeft + ',' + intTop;
  1733. if (PresentationMode.active || PresentationMode.switchInProgress) {
  1734. PDFView.currentPosition = null;
  1735. } else {
  1736. PDFView.currentPosition = { page: pageNumber, left: intLeft, top: intTop };
  1737. }
  1738. PDFView.store.initializedPromise.then(function() {
  1739. PDFView.store.setMultiple({
  1740. 'exists': true,
  1741. 'page': pageNumber,
  1742. 'zoom': normalizedScaleValue,
  1743. 'scrollLeft': intLeft,
  1744. 'scrollTop': intTop
  1745. });
  1746. });
  1747. var href = PDFView.getAnchorUrl(pdfOpenParams);
  1748. document.getElementById('viewBookmark').href = href;
  1749. document.getElementById('secondaryViewBookmark').href = href;
  1750. // Update the current bookmark in the browsing history.
  1751. PDFHistory.updateCurrentBookmark(pdfOpenParams, pageNumber);
  1752. }
  1753. window.addEventListener('resize', function webViewerResize(evt) {
  1754. if (PDFView.initialized &&
  1755. (document.getElementById('pageWidthOption').selected ||
  1756. document.getElementById('pageFitOption').selected ||
  1757. document.getElementById('pageAutoOption').selected)) {
  1758. PDFView.setScale(document.getElementById('scaleSelect').value);
  1759. }
  1760. updateViewarea();
  1761. // Set the 'max-height' CSS property of the secondary toolbar.
  1762. SecondaryToolbar.setMaxHeight(PDFView.container);
  1763. });
  1764. window.addEventListener('hashchange', function webViewerHashchange(evt) {
  1765. if (PDFHistory.isHashChangeUnlocked) {
  1766. PDFView.setHash(document.location.hash.substring(1));
  1767. }
  1768. });
  1769. //#if !(FIREFOX || MOZCENTRAL || CHROME)
  1770. window.addEventListener('change', function webViewerChange(evt) {
  1771. var files = evt.target.files;
  1772. if (!files || files.length === 0) {
  1773. return;
  1774. }
  1775. var file = files[0];
  1776. if (!PDFJS.disableCreateObjectURL &&
  1777. typeof URL !== 'undefined' && URL.createObjectURL) {
  1778. PDFView.open(URL.createObjectURL(file), 0);
  1779. } else {
  1780. // Read the local file into a Uint8Array.
  1781. var fileReader = new FileReader();
  1782. fileReader.onload = function webViewerChangeFileReaderOnload(evt) {
  1783. var buffer = evt.target.result;
  1784. var uint8Array = new Uint8Array(buffer);
  1785. PDFView.open(uint8Array, 0);
  1786. };
  1787. fileReader.readAsArrayBuffer(file);
  1788. }
  1789. PDFView.setTitleUsingUrl(file.name);
  1790. // URL does not reflect proper document location - hiding some icons.
  1791. document.getElementById('viewBookmark').setAttribute('hidden', 'true');
  1792. document.getElementById('secondaryViewBookmark').
  1793. setAttribute('hidden', 'true');
  1794. document.getElementById('download').setAttribute('hidden', 'true');
  1795. document.getElementById('secondaryDownload').setAttribute('hidden', 'true');
  1796. }, true);
  1797. //#endif
  1798. function selectScaleOption(value) {
  1799. var options = document.getElementById('scaleSelect').options;
  1800. var predefinedValueFound = false;
  1801. for (var i = 0; i < options.length; i++) {
  1802. var option = options[i];
  1803. if (option.value != value) {
  1804. option.selected = false;
  1805. continue;
  1806. }
  1807. option.selected = true;
  1808. predefinedValueFound = true;
  1809. }
  1810. return predefinedValueFound;
  1811. }
  1812. window.addEventListener('localized', function localized(evt) {
  1813. document.getElementsByTagName('html')[0].dir = mozL10n.getDirection();
  1814. PDFView.animationStartedPromise.then(function() {
  1815. // Adjust the width of the zoom box to fit the content.
  1816. // Note: This is only done if the zoom box is actually visible,
  1817. // since otherwise element.clientWidth will return 0.
  1818. var container = document.getElementById('scaleSelectContainer');
  1819. if (container.clientWidth > 0) {
  1820. var select = document.getElementById('scaleSelect');
  1821. select.setAttribute('style', 'min-width: inherit;');
  1822. var width = select.clientWidth + SCALE_SELECT_CONTAINER_PADDING;
  1823. select.setAttribute('style', 'min-width: ' +
  1824. (width + SCALE_SELECT_PADDING) + 'px;');
  1825. container.setAttribute('style', 'min-width: ' + width + 'px; ' +
  1826. 'max-width: ' + width + 'px;');
  1827. }
  1828. // Set the 'max-height' CSS property of the secondary toolbar.
  1829. SecondaryToolbar.setMaxHeight(PDFView.container);
  1830. });
  1831. }, true);
  1832. window.addEventListener('scalechange', function scalechange(evt) {
  1833. document.getElementById('zoomOut').disabled = (evt.scale === MIN_SCALE);
  1834. document.getElementById('zoomIn').disabled = (evt.scale === MAX_SCALE);
  1835. var customScaleOption = document.getElementById('customScaleOption');
  1836. customScaleOption.selected = false;
  1837. if (!evt.resetAutoSettings &&
  1838. (document.getElementById('pageWidthOption').selected ||
  1839. document.getElementById('pageFitOption').selected ||
  1840. document.getElementById('pageAutoOption').selected)) {
  1841. updateViewarea();
  1842. return;
  1843. }
  1844. var predefinedValueFound = selectScaleOption('' + evt.scale);
  1845. if (!predefinedValueFound) {
  1846. customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
  1847. customScaleOption.selected = true;
  1848. }
  1849. updateViewarea();
  1850. }, true);
  1851. window.addEventListener('pagechange', function pagechange(evt) {
  1852. var page = evt.pageNumber;
  1853. if (PDFView.previousPageNumber !== page) {
  1854. document.getElementById('pageNumber').value = page;
  1855. var selected = document.querySelector('.thumbnail.selected');
  1856. if (selected) {
  1857. selected.classList.remove('selected');
  1858. }
  1859. var thumbnail = document.getElementById('thumbnailContainer' + page);
  1860. thumbnail.classList.add('selected');
  1861. var visibleThumbs = PDFView.getVisibleThumbs();
  1862. var numVisibleThumbs = visibleThumbs.views.length;
  1863. // If the thumbnail isn't currently visible, scroll it into view.
  1864. if (numVisibleThumbs > 0) {
  1865. var first = visibleThumbs.first.id;
  1866. // Account for only one thumbnail being visible.
  1867. var last = (numVisibleThumbs > 1 ? visibleThumbs.last.id : first);
  1868. if (page <= first || page >= last) {
  1869. scrollIntoView(thumbnail, { top: THUMBNAIL_SCROLL_MARGIN });
  1870. }
  1871. }
  1872. }
  1873. document.getElementById('previous').disabled = (page <= 1);
  1874. document.getElementById('next').disabled = (page >= PDFView.pages.length);
  1875. }, true);
  1876. function handleMouseWheel(evt) {
  1877. var MOUSE_WHEEL_DELTA_FACTOR = 40;
  1878. var ticks = (evt.type === 'DOMMouseScroll') ? -evt.detail :
  1879. evt.wheelDelta / MOUSE_WHEEL_DELTA_FACTOR;
  1880. var direction = (ticks < 0) ? 'zoomOut' : 'zoomIn';
  1881. if (evt.ctrlKey) { // Only zoom the pages, not the entire viewer
  1882. evt.preventDefault();
  1883. PDFView[direction](Math.abs(ticks));
  1884. } else if (PresentationMode.active) {
  1885. PDFView.mouseScroll(ticks * MOUSE_WHEEL_DELTA_FACTOR);
  1886. }
  1887. }
  1888. window.addEventListener('DOMMouseScroll', handleMouseWheel);
  1889. window.addEventListener('mousewheel', handleMouseWheel);
  1890. window.addEventListener('click', function click(evt) {
  1891. if (!PresentationMode.active) {
  1892. if (SecondaryToolbar.opened && PDFView.container.contains(evt.target)) {
  1893. SecondaryToolbar.close();
  1894. }
  1895. } else if (evt.button === 0) {
  1896. // Necessary since preventDefault() in 'mousedown' won't stop
  1897. // the event propagation in all circumstances in presentation mode.
  1898. evt.preventDefault();
  1899. }
  1900. }, false);
  1901. window.addEventListener('keydown', function keydown(evt) {
  1902. if (PasswordPrompt.visible) {
  1903. return;
  1904. }
  1905. var handled = false;
  1906. var cmd = (evt.ctrlKey ? 1 : 0) |
  1907. (evt.altKey ? 2 : 0) |
  1908. (evt.shiftKey ? 4 : 0) |
  1909. (evt.metaKey ? 8 : 0);
  1910. // First, handle the key bindings that are independent whether an input
  1911. // control is selected or not.
  1912. if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
  1913. // either CTRL or META key with optional SHIFT.
  1914. switch (evt.keyCode) {
  1915. case 70: // f
  1916. if (!PDFView.supportsIntegratedFind) {
  1917. PDFFindBar.open();
  1918. handled = true;
  1919. }
  1920. break;
  1921. case 71: // g
  1922. if (!PDFView.supportsIntegratedFind) {
  1923. PDFFindBar.dispatchEvent('again', cmd === 5 || cmd === 12);
  1924. handled = true;
  1925. }
  1926. break;
  1927. case 61: // FF/Mac '='
  1928. case 107: // FF '+' and '='
  1929. case 187: // Chrome '+'
  1930. case 171: // FF with German keyboard
  1931. PDFView.zoomIn();
  1932. handled = true;
  1933. break;
  1934. case 173: // FF/Mac '-'
  1935. case 109: // FF '-'
  1936. case 189: // Chrome '-'
  1937. PDFView.zoomOut();
  1938. handled = true;
  1939. break;
  1940. case 48: // '0'
  1941. case 96: // '0' on Numpad of Swedish keyboard
  1942. // keeping it unhandled (to restore page zoom to 100%)
  1943. setTimeout(function () {
  1944. // ... and resetting the scale after browser adjusts its scale
  1945. PDFView.setScale(DEFAULT_SCALE, true);
  1946. });
  1947. handled = false;
  1948. break;
  1949. }
  1950. }
  1951. //#if !(FIREFOX || MOZCENTRAL)
  1952. // CTRL or META without shift
  1953. if (cmd === 1 || cmd === 8) {
  1954. switch (evt.keyCode) {
  1955. case 83: // s
  1956. PDFView.download();
  1957. handled = true;
  1958. break;
  1959. }
  1960. }
  1961. //#endif
  1962. // CTRL+ALT or Option+Command
  1963. if (cmd === 3 || cmd === 10) {
  1964. switch (evt.keyCode) {
  1965. case 80: // p
  1966. SecondaryToolbar.presentationModeClick();
  1967. handled = true;
  1968. break;
  1969. case 71: // g
  1970. // focuses input#pageNumber field
  1971. document.getElementById('pageNumber').select();
  1972. handled = true;
  1973. break;
  1974. }
  1975. }
  1976. if (handled) {
  1977. evt.preventDefault();
  1978. return;
  1979. }
  1980. // Some shortcuts should not get handled if a control/input element
  1981. // is selected.
  1982. var curElement = document.activeElement || document.querySelector(':focus');
  1983. var curElementTagName = curElement && curElement.tagName.toUpperCase();
  1984. if (curElementTagName === 'INPUT' ||
  1985. curElementTagName === 'TEXTAREA' ||
  1986. curElementTagName === 'SELECT') {
  1987. // Make sure that the secondary toolbar is closed when Escape is pressed.
  1988. if (evt.keyCode !== 27) { // 'Esc'
  1989. return;
  1990. }
  1991. }
  1992. if (cmd === 0) { // no control key pressed at all.
  1993. switch (evt.keyCode) {
  1994. case 38: // up arrow
  1995. case 33: // pg up
  1996. case 8: // backspace
  1997. if (!PresentationMode.active &&
  1998. PDFView.currentScaleValue !== 'page-fit') {
  1999. break;
  2000. }
  2001. /* in presentation mode */
  2002. /* falls through */
  2003. case 37: // left arrow
  2004. // horizontal scrolling using arrow keys
  2005. if (PDFView.isHorizontalScrollbarEnabled) {
  2006. break;
  2007. }
  2008. /* falls through */
  2009. case 75: // 'k'
  2010. case 80: // 'p'
  2011. PDFView.page--;
  2012. handled = true;
  2013. break;
  2014. case 27: // esc key
  2015. if (SecondaryToolbar.opened) {
  2016. SecondaryToolbar.close();
  2017. handled = true;
  2018. }
  2019. if (!PDFView.supportsIntegratedFind && PDFFindBar.opened) {
  2020. PDFFindBar.close();
  2021. handled = true;
  2022. }
  2023. break;
  2024. case 40: // down arrow
  2025. case 34: // pg down
  2026. case 32: // spacebar
  2027. if (!PresentationMode.active &&
  2028. PDFView.currentScaleValue !== 'page-fit') {
  2029. break;
  2030. }
  2031. /* falls through */
  2032. case 39: // right arrow
  2033. // horizontal scrolling using arrow keys
  2034. if (PDFView.isHorizontalScrollbarEnabled) {
  2035. break;
  2036. }
  2037. /* falls through */
  2038. case 74: // 'j'
  2039. case 78: // 'n'
  2040. PDFView.page++;
  2041. handled = true;
  2042. break;
  2043. case 36: // home
  2044. if (PresentationMode.active) {
  2045. PDFView.page = 1;
  2046. handled = true;
  2047. }
  2048. break;
  2049. case 35: // end
  2050. if (PresentationMode.active) {
  2051. PDFView.page = PDFView.pdfDocument.numPages;
  2052. handled = true;
  2053. }
  2054. break;
  2055. case 72: // 'h'
  2056. if (!PresentationMode.active) {
  2057. HandTool.toggle();
  2058. }
  2059. break;
  2060. case 82: // 'r'
  2061. PDFView.rotatePages(90);
  2062. break;
  2063. }
  2064. }
  2065. if (cmd === 4) { // shift-key
  2066. switch (evt.keyCode) {
  2067. case 32: // spacebar
  2068. if (!PresentationMode.active &&
  2069. PDFView.currentScaleValue !== 'page-fit') {
  2070. break;
  2071. }
  2072. PDFView.page--;
  2073. handled = true;
  2074. break;
  2075. case 82: // 'r'
  2076. PDFView.rotatePages(-90);
  2077. break;
  2078. }
  2079. }
  2080. if (!handled && !PresentationMode.active) {
  2081. // 33=Page Up 34=Page Down 35=End 36=Home
  2082. // 37=Left 38=Up 39=Right 40=Down
  2083. if (evt.keyCode >= 33 && evt.keyCode <= 40 &&
  2084. !PDFView.container.contains(curElement)) {
  2085. // The page container is not focused, but a page navigation key has been
  2086. // pressed. Change the focus to the viewer container to make sure that
  2087. // navigation by keyboard works as expected.
  2088. PDFView.container.focus();
  2089. }
  2090. // 32=Spacebar
  2091. if (evt.keyCode === 32 && curElementTagName !== 'BUTTON') {
  2092. //#if (FIREFOX || MOZCENTRAL)
  2093. // // Workaround for issue in Firefox, that prevents scroll keys from
  2094. // // working when elements with 'tabindex' are focused. (#3498)
  2095. // PDFView.container.blur();
  2096. //#else
  2097. if (!PDFView.container.contains(curElement)) {
  2098. PDFView.container.focus();
  2099. }
  2100. //#endif
  2101. }
  2102. }
  2103. if (cmd === 2) { // alt-key
  2104. switch (evt.keyCode) {
  2105. case 37: // left arrow
  2106. if (PresentationMode.active) {
  2107. PDFHistory.back();
  2108. handled = true;
  2109. }
  2110. break;
  2111. case 39: // right arrow
  2112. if (PresentationMode.active) {
  2113. PDFHistory.forward();
  2114. handled = true;
  2115. }
  2116. break;
  2117. }
  2118. }
  2119. if (handled) {
  2120. evt.preventDefault();
  2121. PDFView.clearMouseScrollState();
  2122. }
  2123. });
  2124. window.addEventListener('beforeprint', function beforePrint(evt) {
  2125. PDFView.beforePrint();
  2126. });
  2127. window.addEventListener('afterprint', function afterPrint(evt) {
  2128. PDFView.afterPrint();
  2129. });
  2130. (function animationStartedClosure() {
  2131. // The offsetParent is not set until the pdf.js iframe or object is visible.
  2132. // Waiting for first animation.
  2133. PDFView.animationStartedPromise = new Promise(function (resolve) {
  2134. window.requestAnimationFrame(resolve);
  2135. });
  2136. })();
  2137. //#if B2G
  2138. //window.navigator.mozSetMessageHandler('activity', function(activity) {
  2139. // var url = activity.source.data.url;
  2140. // PDFJS.maxImageSize = 1024 * 1024;
  2141. // PDFView.open(url);
  2142. // var cancelButton = document.getElementById('activityClose');
  2143. // cancelButton.addEventListener('click', function() {
  2144. // activity.postResult('close');
  2145. // });
  2146. //});
  2147. //#endif