text: /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
  /* Copyright 2012 Mozilla Foundation
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, ProgressBar,
             DownloadManager, getFileName, scrollIntoView, getPDFFileNameFromURL,
             PDFHistory, Preferences, SidebarView, ViewHistory, PageView,
             PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar,
             PasswordPrompt, PresentationMode, HandTool, Promise,
             DocumentProperties, DocumentOutlineView, DocumentAttachmentsView,
             OverlayManager, PDFFindController, PDFFindBar, getVisibleElements,
             watchScroll, PDFViewer, PDFRenderingQueue, PresentationModeState,
             RenderingStates, DEFAULT_SCALE, UNKNOWN_SCALE,
             IGNORE_CURRENT_POSITION_ON_ZOOM: true */
  
  'use strict';
  
  var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
  var DEFAULT_SCALE_DELTA = 1.1;
  var MIN_SCALE = 0.25;
  var MAX_SCALE = 10.0;
  var VIEW_HISTORY_MEMORY = 20;
  var SCALE_SELECT_CONTAINER_PADDING = 8;
  var SCALE_SELECT_PADDING = 22;
  var PAGE_NUMBER_LOADING_INDICATOR = 'visiblePageIsLoading';
  //#if B2G
  //PDFJS.useOnlyCssZoom = true;
  //PDFJS.disableTextLayer = true;
  //#endif
  
  PDFJS.imageResourcesPath = './images/';
  //#if (FIREFOX || MOZCENTRAL || B2G || GENERIC || CHROME)
  //PDFJS.workerSrc = '../build/pdf.worker.js';
  //#endif
  //#if !PRODUCTION
  PDFJS.cMapUrl = '../external/bcmaps/';
  PDFJS.cMapPacked = true;
  //#else
  //PDFJS.cMapUrl = '../web/cmaps/';
  //PDFJS.cMapPacked = true;
  //#endif
  
  var mozL10n = document.mozL10n || document.webL10n;
  
  //#include ui_utils.js
  //#include preferences.js
  
  //#if !(FIREFOX || MOZCENTRAL || B2G)
  //#include mozPrintCallback_polyfill.js
  //#endif
  
  //#if GENERIC || CHROME
  //#include download_manager.js
  //#endif
  
  //#if FIREFOX || MOZCENTRAL
  //#include firefoxcom.js
  //#endif
  
  //#if CHROME
  //#include chromecom.js
  //#endif
  
  //#include view_history.js
  //#include pdf_find_bar.js
  //#include pdf_find_controller.js
  //#include pdf_history.js
  //#include secondary_toolbar.js
  //#include presentation_mode.js
  //#include hand_tool.js
  //#include overlay_manager.js
  //#include password_prompt.js
  //#include document_properties.js
  //#include pdf_viewer.js
  
  var PDFViewerApplication = {
    initialBookmark: document.location.hash.substring(1),
    initialized: false,
    fellback: false,
    pdfDocument: null,
    sidebarOpen: false,
    printing: false,
    /** @type {PDFViewer} */
    pdfViewer: null,
    /** @type {PDFThumbnailViewer} */
    pdfThumbnailViewer: null,
    /** @type {PDFRenderingQueue} */
    pdfRenderingQueue: null,
    pageRotation: 0,
    updateScaleControls: true,
    isInitialViewSet: false,
    animationStartedPromise: null,
    mouseScrollTimeStamp: 0,
    mouseScrollDelta: 0,
    preferenceSidebarViewOnLoad: SidebarView.NONE,
    preferencePdfBugEnabled: false,
    isViewerEmbedded: (window.parent !== window),
    url: '',
  
    // called once when the document is loaded
    initialize: function pdfViewInitialize() {
      var pdfRenderingQueue = new PDFRenderingQueue();
      pdfRenderingQueue.onIdle = this.cleanup.bind(this);
      this.pdfRenderingQueue = pdfRenderingQueue;
  
      var container = document.getElementById('viewerContainer');
      var viewer = document.getElementById('viewer');
      this.pdfViewer = new PDFViewer({
        container: container,
        viewer: viewer,
        renderingQueue: pdfRenderingQueue,
        linkService: this
      });
      pdfRenderingQueue.setViewer(this.pdfViewer);
  
      var thumbnailContainer = document.getElementById('thumbnailView');
      this.pdfThumbnailViewer = new PDFThumbnailViewer({
        container: thumbnailContainer,
        renderingQueue: pdfRenderingQueue,
        linkService: this
      });
      pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
  
      Preferences.initialize();
  
      this.findController = new PDFFindController({
        pdfViewer: this.pdfViewer,
        integratedFind: this.supportsIntegratedFind
      });
      this.pdfViewer.setFindController(this.findController);
  
      this.findBar = new PDFFindBar({
        bar: document.getElementById('findbar'),
        toggleButton: document.getElementById('viewFind'),
        findField: document.getElementById('findInput'),
        highlightAllCheckbox: document.getElementById('findHighlightAll'),
        caseSensitiveCheckbox: document.getElementById('findMatchCase'),
        findMsg: document.getElementById('findMsg'),
        findStatusIcon: document.getElementById('findStatusIcon'),
        findPreviousButton: document.getElementById('findPrevious'),
        findNextButton: document.getElementById('findNext'),
        findController: this.findController
      });
  
      this.findController.setFindBar(this.findBar);
  
      HandTool.initialize({
        container: container,
        toggleHandTool: document.getElementById('toggleHandTool')
      });
  
      SecondaryToolbar.initialize({
        toolbar: document.getElementById('secondaryToolbar'),
        presentationMode: PresentationMode,
        toggleButton: document.getElementById('secondaryToolbarToggle'),
        presentationModeButton:
          document.getElementById('secondaryPresentationMode'),
        openFile: document.getElementById('secondaryOpenFile'),
        print: document.getElementById('secondaryPrint'),
        download: document.getElementById('secondaryDownload'),
        viewBookmark: document.getElementById('secondaryViewBookmark'),
        firstPage: document.getElementById('firstPage'),
        lastPage: document.getElementById('lastPage'),
        pageRotateCw: document.getElementById('pageRotateCw'),
        pageRotateCcw: document.getElementById('pageRotateCcw'),
        documentProperties: DocumentProperties,
        documentPropertiesButton: document.getElementById('documentProperties')
      });
  
      PresentationMode.initialize({
        container: container,
        secondaryToolbar: SecondaryToolbar,
        firstPage: document.getElementById('contextFirstPage'),
        lastPage: document.getElementById('contextLastPage'),
        pageRotateCw: document.getElementById('contextPageRotateCw'),
        pageRotateCcw: document.getElementById('contextPageRotateCcw')
      });
  
      PasswordPrompt.initialize({
        overlayName: 'passwordOverlay',
        passwordField: document.getElementById('password'),
        passwordText: document.getElementById('passwordText'),
        passwordSubmit: document.getElementById('passwordSubmit'),
        passwordCancel: document.getElementById('passwordCancel')
      });
  
      DocumentProperties.initialize({
        overlayName: 'documentPropertiesOverlay',
        closeButton: document.getElementById('documentPropertiesClose'),
        fileNameField: document.getElementById('fileNameField'),
        fileSizeField: document.getElementById('fileSizeField'),
        titleField: document.getElementById('titleField'),
        authorField: document.getElementById('authorField'),
        subjectField: document.getElementById('subjectField'),
        keywordsField: document.getElementById('keywordsField'),
        creationDateField: document.getElementById('creationDateField'),
        modificationDateField: document.getElementById('modificationDateField'),
        creatorField: document.getElementById('creatorField'),
        producerField: document.getElementById('producerField'),
        versionField: document.getElementById('versionField'),
        pageCountField: document.getElementById('pageCountField')
      });
  
      var self = this;
      var initializedPromise = Promise.all([
        Preferences.get('enableWebGL').then(function resolved(value) {
          PDFJS.disableWebGL = !value;
        }),
        Preferences.get('sidebarViewOnLoad').then(function resolved(value) {
          self.preferenceSidebarViewOnLoad = value;
        }),
        Preferences.get('pdfBugEnabled').then(function resolved(value) {
          self.preferencePdfBugEnabled = value;
        }),
        Preferences.get('disableTextLayer').then(function resolved(value) {
          if (PDFJS.disableTextLayer === true) {
            return;
          }
          PDFJS.disableTextLayer = value;
        }),
        Preferences.get('disableRange').then(function resolved(value) {
          if (PDFJS.disableRange === true) {
            return;
          }
          PDFJS.disableRange = value;
        }),
        Preferences.get('disableAutoFetch').then(function resolved(value) {
          PDFJS.disableAutoFetch = value;
        }),
        Preferences.get('disableFontFace').then(function resolved(value) {
          if (PDFJS.disableFontFace === true) {
            return;
          }
          PDFJS.disableFontFace = value;
        }),
        Preferences.get('useOnlyCssZoom').then(function resolved(value) {
          PDFJS.useOnlyCssZoom = value;
        })
        // TODO move more preferences and other async stuff here
      ]).catch(function (reason) { });
  
      return initializedPromise.then(function () {
        PDFViewerApplication.initialized = true;
      });
    },
  
    zoomIn: function pdfViewZoomIn(ticks) {
      var newScale = this.pdfViewer.currentScale;
      do {
        newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
        newScale = Math.ceil(newScale * 10) / 10;
        newScale = Math.min(MAX_SCALE, newScale);
      } while (--ticks && newScale < MAX_SCALE);
      this.setScale(newScale, true);
    },
  
    zoomOut: function pdfViewZoomOut(ticks) {
      var newScale = this.pdfViewer.currentScale;
      do {
        newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
        newScale = Math.floor(newScale * 10) / 10;
        newScale = Math.max(MIN_SCALE, newScale);
      } while (--ticks && newScale > MIN_SCALE);
      this.setScale(newScale, true);
    },
  
    get currentScaleValue() {
      return this.pdfViewer.currentScaleValue;
    },
  
    get pagesCount() {
      return this.pdfDocument.numPages;
    },
  
    set page(val) {
      this.pdfViewer.currentPageNumber = val;
    },
  
    get page() {
      return this.pdfViewer.currentPageNumber;
    },
  
    get supportsPrinting() {
      var canvas = document.createElement('canvas');
      var value = 'mozPrintCallback' in canvas;
      // shadow
      Object.defineProperty(this, 'supportsPrinting', { value: value,
                                                        enumerable: true,
                                                        configurable: true,
                                                        writable: false });
      return value;
    },
  
    get supportsFullscreen() {
      var doc = document.documentElement;
      var support = doc.requestFullscreen || doc.mozRequestFullScreen ||
                    doc.webkitRequestFullScreen || doc.msRequestFullscreen;
  
      if (document.fullscreenEnabled === false ||
          document.mozFullScreenEnabled === false ||
          document.webkitFullscreenEnabled === false ||
          document.msFullscreenEnabled === false) {
        support = false;
      }
  
      Object.defineProperty(this, 'supportsFullscreen', { value: support,
                                                          enumerable: true,
                                                          configurable: true,
                                                          writable: false });
      return support;
    },
  
    get supportsIntegratedFind() {
      var support = false;
  //#if !(FIREFOX || MOZCENTRAL)
  //#else
  //  support = FirefoxCom.requestSync('supportsIntegratedFind');
  //#endif
      Object.defineProperty(this, 'supportsIntegratedFind', { value: support,
                                                              enumerable: true,
                                                              configurable: true,
                                                              writable: false });
      return support;
    },
  
    get supportsDocumentFonts() {
      var support = true;
  //#if !(FIREFOX || MOZCENTRAL)
  //#else
  //  support = FirefoxCom.requestSync('supportsDocumentFonts');
  //#endif
      Object.defineProperty(this, 'supportsDocumentFonts', { value: support,
                                                             enumerable: true,
                                                             configurable: true,
                                                             writable: false });
      return support;
    },
  
    get supportsDocumentColors() {
      var support = true;
  //#if !(FIREFOX || MOZCENTRAL)
  //#else
  //  support = FirefoxCom.requestSync('supportsDocumentColors');
  //#endif
      Object.defineProperty(this, 'supportsDocumentColors', { value: support,
                                                              enumerable: true,
                                                              configurable: true,
                                                              writable: false });
      return support;
    },
  
    get loadingBar() {
      var bar = new ProgressBar('#loadingBar', {});
      Object.defineProperty(this, 'loadingBar', { value: bar,
                                                  enumerable: true,
                                                  configurable: true,
                                                  writable: false });
      return bar;
    },
  
  //#if (FIREFOX || MOZCENTRAL)
    initPassiveLoading: function pdfViewInitPassiveLoading() {
      var pdfDataRangeTransportReadyResolve;
      var pdfDataRangeTransportReady = new Promise(function (resolve) {
        pdfDataRangeTransportReadyResolve = resolve;
      });
      var pdfDataRangeTransport = {
        rangeListeners: [],
        progressListeners: [],
        progressiveReadListeners: [],
        ready: pdfDataRangeTransportReady,
  
        addRangeListener: function PdfDataRangeTransport_addRangeListener(
                                     listener) {
          this.rangeListeners.push(listener);
        },
  
        addProgressListener: function PdfDataRangeTransport_addProgressListener(
                                        listener) {
          this.progressListeners.push(listener);
        },
  
        addProgressiveReadListener:
            function PdfDataRangeTransport_addProgressiveReadListener(listener) {
          this.progressiveReadListeners.push(listener);
        },
  
        onDataRange: function PdfDataRangeTransport_onDataRange(begin, chunk) {
          var listeners = this.rangeListeners;
          for (var i = 0, n = listeners.length; i < n; ++i) {
            listeners[i](begin, chunk);
          }
        },
  
        onDataProgress: function PdfDataRangeTransport_onDataProgress(loaded) {
          this.ready.then(function () {
            var listeners = this.progressListeners;
            for (var i = 0, n = listeners.length; i < n; ++i) {
              listeners[i](loaded);
            }
          }.bind(this));
        },
  
        onDataProgressiveRead:
            function PdfDataRangeTransport_onDataProgress(chunk) {
          this.ready.then(function () {
            var listeners = this.progressiveReadListeners;
            for (var i = 0, n = listeners.length; i < n; ++i) {
              listeners[i](chunk);
            }
          }.bind(this));
        },
  
        transportReady: function PdfDataRangeTransport_transportReady() {
          pdfDataRangeTransportReadyResolve();
        },
  
        requestDataRange: function PdfDataRangeTransport_requestDataRange(
                                    begin, end) {
          FirefoxCom.request('requestDataRange', { begin: begin, end: end });
        }
      };
  
      window.addEventListener('message', function windowMessage(e) {
        if (e.source !== null) {
          // The message MUST originate from Chrome code.
          console.warn('Rejected untrusted message from ' + e.origin);
          return;
        }
        var args = e.data;
  
        if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) {
          return;
        }
        switch (args.pdfjsLoadAction) {
          case 'supportsRangedLoading':
            PDFViewerApplication.open(args.pdfUrl, 0, undefined,
                                      pdfDataRangeTransport, {
              length: args.length,
              initialData: args.data
            });
            break;
          case 'range':
            pdfDataRangeTransport.onDataRange(args.begin, args.chunk);
            break;
          case 'rangeProgress':
            pdfDataRangeTransport.onDataProgress(args.loaded);
            break;
          case 'progressiveRead':
            pdfDataRangeTransport.onDataProgressiveRead(args.chunk);
            break;
          case 'progress':
            PDFViewerApplication.progress(args.loaded / args.total);
            break;
          case 'complete':
            if (!args.data) {
              PDFViewerApplication.error(mozL10n.get('loading_error', null,
                'An error occurred while loading the PDF.'), e);
              break;
            }
            PDFViewerApplication.open(args.data, 0);
            break;
        }
      });
      FirefoxCom.requestSync('initPassiveLoading', null);
    },
  //#endif
  
    setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
      this.url = url;
      try {
        this.setTitle(decodeURIComponent(getFileName(url)) || url);
      } catch (e) {
        // decodeURIComponent may throw URIError,
        // fall back to using the unprocessed url in that case
        this.setTitle(url);
      }
    },
  
    setTitle: function pdfViewSetTitle(title) {
      document.title = title;
  //#if B2G
  //  document.getElementById('activityTitle').textContent = title;
  //#endif
    },
  
    close: function pdfViewClose() {
      var errorWrapper = document.getElementById('errorWrapper');
      errorWrapper.setAttribute('hidden', 'true');
  
      if (!this.pdfDocument) {
        return;
      }
  
      this.pdfDocument.destroy();
      this.pdfDocument = null;
  
      this.pdfThumbnailViewer.setDocument(null);
      this.pdfViewer.setDocument(null);
  
      if (typeof PDFBug !== 'undefined') {
        PDFBug.cleanup();
      }
    },
  
    // TODO(mack): This function signature should really be pdfViewOpen(url, args)
    open: function pdfViewOpen(file, scale, password,
                               pdfDataRangeTransport, args) {
      if (this.pdfDocument) {
        // Reload the preferences if a document was previously opened.
        Preferences.reload();
      }
      this.close();
  
      var parameters = {password: password};
      if (typeof file === 'string') { // URL
        this.setTitleUsingUrl(file);
        parameters.url = file;
      } else if (file && 'byteLength' in file) { // ArrayBuffer
        parameters.data = file;
      } else if (file.url && file.originalUrl) {
        this.setTitleUsingUrl(file.originalUrl);
        parameters.url = file.url;
      }
      if (args) {
        for (var prop in args) {
          parameters[prop] = args[prop];
        }
      }
  
      var self = this;
      self.loading = true;
      self.downloadComplete = false;
  
      var passwordNeeded = function passwordNeeded(updatePassword, reason) {
        PasswordPrompt.updatePassword = updatePassword;
        PasswordPrompt.reason = reason;
        PasswordPrompt.open();
      };
  
      function getDocumentProgress(progressData) {
        self.progress(progressData.loaded / progressData.total);
      }
  
      PDFJS.getDocument(parameters, pdfDataRangeTransport, passwordNeeded,
                        getDocumentProgress).then(
        function getDocumentCallback(pdfDocument) {
          self.load(pdfDocument, scale);
          self.loading = false;
        },
        function getDocumentError(exception) {
          var message = exception && exception.message;
          var loadingErrorMessage = mozL10n.get('loading_error', null,
            'An error occurred while loading the PDF.');
  
          if (exception instanceof PDFJS.InvalidPDFException) {
            // change error message also for other builds
            loadingErrorMessage = mozL10n.get('invalid_file_error', null,
                                              'Invalid or corrupted PDF file.');
          } else if (exception instanceof PDFJS.MissingPDFException) {
            // special message for missing PDF's
            loadingErrorMessage = mozL10n.get('missing_file_error', null,
                                              'Missing PDF file.');
          } else if (exception instanceof PDFJS.UnexpectedResponseException) {
            loadingErrorMessage = mozL10n.get('unexpected_response_error', null,
                                              'Unexpected server response.');
          }
  //#if B2G
  //      window.alert(loadingErrorMessage);
  //      return window.close();
  //#endif
  
          var moreInfo = {
            message: message
          };
          self.error(loadingErrorMessage, moreInfo);
          self.loading = false;
        }
      );
  
      if (args && args.length) {
        DocumentProperties.setFileSize(args.length);
      }
    },
  
    download: function pdfViewDownload() {
      function downloadByUrl() {
        downloadManager.downloadUrl(url, filename);
      }
  
      var url = this.url.split('#')[0];
      var filename = getPDFFileNameFromURL(url);
      var downloadManager = new DownloadManager();
      downloadManager.onerror = function (err) {
        // This error won't really be helpful because it's likely the
        // fallback won't work either (or is already open).
        PDFViewerApplication.error('PDF failed to download.');
      };
  
      if (!this.pdfDocument) { // the PDF is not ready yet
        downloadByUrl();
        return;
      }
  
      if (!this.downloadComplete) { // the PDF is still downloading
        downloadByUrl();
        return;
      }
  
      this.pdfDocument.getData().then(
        function getDataSuccess(data) {
          var blob = PDFJS.createBlob(data, 'application/pdf');
          downloadManager.download(blob, url, filename);
        },
        downloadByUrl // Error occurred try downloading with just the url.
      ).then(null, downloadByUrl);
    },
  
    fallback: function pdfViewFallback(featureId) {
  //#if !(FIREFOX || MOZCENTRAL)
  //  return;
  //#else
  //  // Only trigger the fallback once so we don't spam the user with messages
  //  // for one PDF.
  //  if (this.fellback)
  //    return;
  //  this.fellback = true;
  //  var url = this.url.split('#')[0];
  //  FirefoxCom.request('fallback', { featureId: featureId, url: url },
  //    function response(download) {
  //      if (!download) {
  //        return;
  //      }
  //      PDFViewerApplication.download();
  //    });
  //#endif
    },
  
    navigateTo: function pdfViewNavigateTo(dest) {
      var destString = '';
      var self = this;
  
      var goToDestination = function(destRef) {
        self.pendingRefStr = null;
        // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
        var pageNumber = destRef instanceof Object ?
          self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
          (destRef + 1);
        if (pageNumber) {
          if (pageNumber > self.pagesCount) {
            pageNumber = self.pagesCount;
          }
          self.pdfViewer.scrollPageIntoView(pageNumber, dest);
  
          // Update the browsing history.
          PDFHistory.push({ dest: dest, hash: destString, page: pageNumber });
        } else {
          self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) {
            var pageNum = pageIndex + 1;
            self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] = pageNum;
            goToDestination(destRef);
          });
        }
      };
  
      var destinationPromise;
      if (typeof dest === 'string') {
        destString = dest;
        destinationPromise = this.pdfDocument.getDestination(dest);
      } else {
        destinationPromise = Promise.resolve(dest);
      }
      destinationPromise.then(function(destination) {
        dest = destination;
        if (!(destination instanceof Array)) {
          return; // invalid destination
        }
        goToDestination(destination[0]);
      });
    },
  
    executeNamedAction: function pdfViewExecuteNamedAction(action) {
      // See PDF reference, table 8.45 - Named action
      switch (action) {
        case 'GoToPage':
          document.getElementById('pageNumber').focus();
          break;
  
        case 'GoBack':
          PDFHistory.back();
          break;
  
        case 'GoForward':
          PDFHistory.forward();
          break;
  
        case 'Find':
          if (!this.supportsIntegratedFind) {
            this.findBar.toggle();
          }
          break;
  
        case 'NextPage':
          this.page++;
          break;
  
        case 'PrevPage':
          this.page--;
          break;
  
        case 'LastPage':
          this.page = this.pagesCount;
          break;
  
        case 'FirstPage':
          this.page = 1;
          break;
  
        default:
          break; // No action according to spec
      }
    },
  
    getDestinationHash: function pdfViewGetDestinationHash(dest) {
      if (typeof dest === 'string') {
        return this.getAnchorUrl('#' + escape(dest));
      }
      if (dest instanceof Array) {
        var destRef = dest[0]; // see navigateTo method for dest format
        var pageNumber = destRef instanceof Object ?
          this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
          (destRef + 1);
        if (pageNumber) {
          var pdfOpenParams = this.getAnchorUrl('#page=' + pageNumber);
          var destKind = dest[1];
          if (typeof destKind === 'object' && 'name' in destKind &&
              destKind.name === 'XYZ') {
            var scale = (dest[4] || this.currentScaleValue);
            var scaleNumber = parseFloat(scale);
            if (scaleNumber) {
              scale = scaleNumber * 100;
            }
            pdfOpenParams += '&zoom=' + scale;
            if (dest[2] || dest[3]) {
              pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
            }
          }
          return pdfOpenParams;
        }
      }
      return '';
    },
  
    /**
     * Prefix the full url on anchor links to make sure that links are resolved
     * relative to the current URL instead of the one defined in <base href>.
     * @param {String} anchor The anchor hash, including the #.
     */
    getAnchorUrl: function getAnchorUrl(anchor) {
  //#if (GENERIC || B2G)
      return anchor;
  //#endif
  //#if (FIREFOX || MOZCENTRAL)
  //  return this.url.split('#')[0] + anchor;
  //#endif
  //#if CHROME
  //  return location.href.split('#')[0] + anchor;
  //#endif
    },
  
    /**
     * Show the error box.
     * @param {String} message A message that is human readable.
     * @param {Object} moreInfo (optional) Further information about the error
     *                            that is more technical.  Should have a 'message'
     *                            and optionally a 'stack' property.
     */
    error: function pdfViewError(message, moreInfo) {
      var moreInfoText = mozL10n.get('error_version_info',
        {version: PDFJS.version || '?', build: PDFJS.build || '?'},
        'PDF.js v{{version}} (build: {{build}})') + '\n';
      if (moreInfo) {
        moreInfoText +=
          mozL10n.get('error_message', {message: moreInfo.message},
          'Message: {{message}}');
        if (moreInfo.stack) {
          moreInfoText += '\n' +
            mozL10n.get('error_stack', {stack: moreInfo.stack},
            'Stack: {{stack}}');
        } else {
          if (moreInfo.filename) {
            moreInfoText += '\n' +
              mozL10n.get('error_file', {file: moreInfo.filename},
              'File: {{file}}');
          }
          if (moreInfo.lineNumber) {
            moreInfoText += '\n' +
              mozL10n.get('error_line', {line: moreInfo.lineNumber},
              'Line: {{line}}');
          }
        }
      }
  
  //#if !(FIREFOX || MOZCENTRAL)
      var errorWrapper = document.getElementById('errorWrapper');
      errorWrapper.removeAttribute('hidden');
  
      var errorMessage = document.getElementById('errorMessage');
      errorMessage.textContent = message;
  
      var closeButton = document.getElementById('errorClose');
      closeButton.onclick = function() {
        errorWrapper.setAttribute('hidden', 'true');
      };
  
      var errorMoreInfo = document.getElementById('errorMoreInfo');
      var moreInfoButton = document.getElementById('errorShowMore');
      var lessInfoButton = document.getElementById('errorShowLess');
      moreInfoButton.onclick = function() {
        errorMoreInfo.removeAttribute('hidden');
        moreInfoButton.setAttribute('hidden', 'true');
        lessInfoButton.removeAttribute('hidden');
        errorMoreInfo.style.height = errorMoreInfo.scrollHeight + 'px';
      };
      lessInfoButton.onclick = function() {
        errorMoreInfo.setAttribute('hidden', 'true');
        moreInfoButton.removeAttribute('hidden');
        lessInfoButton.setAttribute('hidden', 'true');
      };
      moreInfoButton.oncontextmenu = noContextMenuHandler;
      lessInfoButton.oncontextmenu = noContextMenuHandler;
      closeButton.oncontextmenu = noContextMenuHandler;
      moreInfoButton.removeAttribute('hidden');
      lessInfoButton.setAttribute('hidden', 'true');
      errorMoreInfo.value = moreInfoText;
  //#else
  //  console.error(message + '\n' + moreInfoText);
  //  this.fallback();
  //#endif
    },
  
    progress: function pdfViewProgress(level) {
      var percent = Math.round(level * 100);
      // When we transition from full request to range requests, it's possible
      // that we discard some of the loaded data. This can cause the loading
      // bar to move backwards. So prevent this by only updating the bar if it
      // increases.
      if (percent > this.loadingBar.percent || isNaN(percent)) {
        this.loadingBar.percent = percent;
      }
    },
  
    load: function pdfViewLoad(pdfDocument, scale) {
      var self = this;
      scale = scale || UNKNOWN_SCALE;
  
      this.findController.reset();
  
      this.pdfDocument = pdfDocument;
  
      DocumentProperties.url = this.url;
      DocumentProperties.pdfDocument = pdfDocument;
      DocumentProperties.resolveDataAvailable();
  
      var downloadedPromise = pdfDocument.getDownloadInfo().then(function() {
        self.downloadComplete = true;
        self.loadingBar.hide();
        var outerContainer = document.getElementById('outerContainer');
        outerContainer.classList.remove('loadingInProgress');
      });
  
      var pagesCount = pdfDocument.numPages;
      document.getElementById('numPages').textContent =
        mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
      document.getElementById('pageNumber').max = pagesCount;
  
      var id = this.documentFingerprint = pdfDocument.fingerprint;
      var store = this.store = new ViewHistory(id);
  
      var pdfViewer = this.pdfViewer;
      pdfViewer.currentScale = scale;
      pdfViewer.setDocument(pdfDocument);
      var firstPagePromise = pdfViewer.firstPagePromise;
      var pagesPromise = pdfViewer.pagesPromise;
      var onePageRendered = pdfViewer.onePageRendered;
  
      this.pageRotation = 0;
      this.isInitialViewSet = false;
      this.pagesRefMap = pdfViewer.pagesRefMap;
  
      this.pdfThumbnailViewer.setDocument(pdfDocument);
  
      firstPagePromise.then(function(pdfPage) {
        downloadedPromise.then(function () {
          var event = document.createEvent('CustomEvent');
          event.initCustomEvent('documentload', true, true, {});
          window.dispatchEvent(event);
        });
  
        self.loadingBar.setWidth(document.getElementById('viewer'));
  
        self.findController.resolveFirstPage();
  
        if (!PDFJS.disableHistory && !self.isViewerEmbedded) {
          // The browsing history is only enabled when the viewer is standalone,
          // i.e. not when it is embedded in a web page.
          PDFHistory.initialize(self.documentFingerprint, self);
        }
      });
  
      // Fetch the necessary preference values.
      var showPreviousViewOnLoad;
      var showPreviousViewOnLoadPromise =
        Preferences.get('showPreviousViewOnLoad').then(function (prefValue) {
          showPreviousViewOnLoad = prefValue;
        });
      var defaultZoomValue;
      var defaultZoomValuePromise =
        Preferences.get('defaultZoomValue').then(function (prefValue) {
          defaultZoomValue = prefValue;
        });
  
      var storePromise = store.initializedPromise;
      Promise.all([firstPagePromise, storePromise, showPreviousViewOnLoadPromise,
                   defaultZoomValuePromise]).then(function resolved() {
        var storedHash = null;
        if (showPreviousViewOnLoad && store.get('exists', false)) {
          var pageNum = store.get('page', '1');
          var zoom = defaultZoomValue ||
                     store.get('zoom', self.pdfViewer.currentScale);
          var left = store.get('scrollLeft', '0');
          var top = store.get('scrollTop', '0');
  
          storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' +
                       left + ',' + top;
        } else if (defaultZoomValue) {
          storedHash = 'page=1&zoom=' + defaultZoomValue;
        }
        self.setInitialView(storedHash, scale);
  
        // Make all navigation keys work on document load,
        // unless the viewer is embedded in a web page.
        if (!self.isViewerEmbedded) {
          self.pdfViewer.focus();
  //#if (FIREFOX || MOZCENTRAL)
  //      self.pdfViewer.blur();
  //#endif
        }
      }, function rejected(reason) {
        console.error(reason);
  
        firstPagePromise.then(function () {
          self.setInitialView(null, scale);
        });
      });
  
      pagesPromise.then(function() {
        if (self.supportsPrinting) {
          pdfDocument.getJavaScript().then(function(javaScript) {
            if (javaScript.length) {
              console.warn('Warning: JavaScript is not supported');
              self.fallback(PDFJS.UNSUPPORTED_FEATURES.javaScript);
            }
            // Hack to support auto printing.
            var regex = /\bprint\s*\(/g;
            for (var i = 0, ii = javaScript.length; i < ii; i++) {
              var js = javaScript[i];
              if (js && regex.test(js)) {
                setTimeout(function() {
                  window.print();
                });
                return;
              }
            }
          });
        }
      });
  
      // outline depends on pagesRefMap
      var promises = [pagesPromise, this.animationStartedPromise];
      Promise.all(promises).then(function() {
        pdfDocument.getOutline().then(function(outline) {
          var outlineView = document.getElementById('outlineView');
          self.outline = new DocumentOutlineView({
            outline: outline,
            outlineView: outlineView,
            linkService: self
          });
          document.getElementById('viewOutline').disabled = !outline;
  
          if (!outline && !outlineView.classList.contains('hidden')) {
            self.switchSidebarView('thumbs');
          }
          if (outline &&
              self.preferenceSidebarViewOnLoad === SidebarView.OUTLINE) {
            self.switchSidebarView('outline', true);
          }
        });
        pdfDocument.getAttachments().then(function(attachments) {
          var attachmentsView = document.getElementById('attachmentsView');
          self.attachments = new DocumentAttachmentsView({
            attachments: attachments,
            attachmentsView: attachmentsView
          });
          document.getElementById('viewAttachments').disabled = !attachments;
  
          if (!attachments && !attachmentsView.classList.contains('hidden')) {
            self.switchSidebarView('thumbs');
          }
          if (attachments &&
              self.preferenceSidebarViewOnLoad === SidebarView.ATTACHMENTS) {
            self.switchSidebarView('attachments', true);
          }
        });
      });
  
      if (self.preferenceSidebarViewOnLoad === SidebarView.THUMBS) {
        Promise.all([firstPagePromise, onePageRendered]).then(function () {
          self.switchSidebarView('thumbs', true);
        });
      }
  
      pdfDocument.getMetadata().then(function(data) {
        var info = data.info, metadata = data.metadata;
        self.documentInfo = info;
        self.metadata = metadata;
  
        // Provides some basic debug information
        console.log('PDF ' + pdfDocument.fingerprint + ' [' +
                    info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() +
                    ' / ' + (info.Creator || '-').trim() + ']' +
                    ' (PDF.js: ' + (PDFJS.version || '-') +
                    (!PDFJS.disableWebGL ? ' [WebGL]' : '') + ')');
  
        var pdfTitle;
        if (metadata && metadata.has('dc:title')) {
          var title = metadata.get('dc:title');
          // Ghostscript sometimes return 'Untitled', sets the title to 'Untitled'
          if (title !== 'Untitled') {
            pdfTitle = title;
          }
        }
  
        if (!pdfTitle && info && info['Title']) {
          pdfTitle = info['Title'];
        }
  
        if (pdfTitle) {
          self.setTitle(pdfTitle + ' - ' + document.title);
        }
  
        if (info.IsAcroFormPresent) {
          console.warn('Warning: AcroForm/XFA is not supported');
          self.fallback(PDFJS.UNSUPPORTED_FEATURES.forms);
        }
  
  //#if (FIREFOX || MOZCENTRAL)
  //    var versionId = String(info.PDFFormatVersion).slice(-1) | 0;
  //    var generatorId = 0;
  //    var KNOWN_GENERATORS = ["acrobat distiller", "acrobat pdfwritter",
  //     "adobe livecycle", "adobe pdf library", "adobe photoshop", "ghostscript",
  //     "tcpdf", "cairo", "dvipdfm", "dvips", "pdftex", "pdfkit", "itext",
  //     "prince", "quarkxpress", "mac os x", "microsoft", "openoffice", "oracle",
  //     "luradocument", "pdf-xchange", "antenna house", "aspose.cells", "fpdf"];
  //    var generatorId = 0;
  //    if (info.Producer) {
  //      KNOWN_GENERATORS.some(function (generator, s, i) {
  //        if (generator.indexOf(s) < 0) {
  //          return false;
  //        }
  //        generatorId = i + 1;
  //        return true;
  //      }.bind(null, info.Producer.toLowerCase()));
  //    }
  //    var formType = !info.IsAcroFormPresent ? null : info.IsXFAPresent ?
  //                   'xfa' : 'acroform';
  //    FirefoxCom.request('reportTelemetry', JSON.stringify({
  //      type: 'documentInfo',
  //      version: versionId,
  //      generator: generatorId,
  //      formType: formType
  //    }));
  //#endif
      });
    },
  
    setInitialView: function pdfViewSetInitialView(storedHash, scale) {
      this.isInitialViewSet = true;
  
      // When opening a new file (when one is already loaded in the viewer):
      // Reset 'currentPageNumber', since otherwise the page's scale will be wrong
      // if 'currentPageNumber' is larger than the number of pages in the file.
      document.getElementById('pageNumber').value =
        this.pdfViewer.currentPageNumber = 1;
  
      if (PDFHistory.initialDestination) {
        this.navigateTo(PDFHistory.initialDestination);
        PDFHistory.initialDestination = null;
      } else if (this.initialBookmark) {
        this.setHash(this.initialBookmark);
        PDFHistory.push({ hash: this.initialBookmark }, !!this.initialBookmark);
        this.initialBookmark = null;
      } else if (storedHash) {
        this.setHash(storedHash);
      } else if (scale) {
        this.setScale(scale, true);
        this.page = 1;
      }
  
      if (this.pdfViewer.currentScale === UNKNOWN_SCALE) {
        // Scale was not initialized: invalid bookmark or scale was not specified.
        // Setting the default one.
        this.setScale(DEFAULT_SCALE, true);
      }
    },
  
    cleanup: function pdfViewCleanup() {
      this.pdfViewer.cleanup();
      this.pdfThumbnailViewer.cleanup();
      this.pdfDocument.cleanup();
    },
  
    forceRendering: function pdfViewForceRendering() {
      this.pdfRenderingQueue.printing = this.printing;
      this.pdfRenderingQueue.isThumbnailViewEnabled = this.sidebarOpen;
      this.pdfRenderingQueue.renderHighestPriority();
    },
  
    setHash: function pdfViewSetHash(hash) {
      if (!this.isInitialViewSet) {
        this.initialBookmark = hash;
        return;
      }
  
      var validFitZoomValues = ['Fit','FitB','FitH','FitBH',
        'FitV','FitBV','FitR'];
  
      if (!hash) {
        return;
      }
  
      if (hash.indexOf('=') >= 0) {
        var params = this.parseQueryString(hash);
        // borrowing syntax from "Parameters for Opening PDF Files"
        if ('nameddest' in params) {
          PDFHistory.updateNextHashParam(params.nameddest);
          this.navigateTo(params.nameddest);
          return;
        }
        var pageNumber, dest;
        if ('page' in params) {
          pageNumber = (params.page | 0) || 1;
        }
        if ('zoom' in params) {
          var zoomArgs = params.zoom.split(','); // scale,left,top
          // building destination array
  
          // If the zoom value, it has to get divided by 100. If it is a string,
          // it should stay as it is.
          var zoomArg = zoomArgs[0];
          var zoomArgNumber = parseFloat(zoomArg);
          var destName = 'XYZ';
          if (zoomArgNumber) {
            zoomArg = zoomArgNumber / 100;
          } else if (validFitZoomValues.indexOf(zoomArg) >= 0) {
            destName = zoomArg;
          }
          dest = [null, { name: destName },
                  zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
                  zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
                  zoomArg];
        }
        if (dest) {
          this.pdfViewer.scrollPageIntoView(pageNumber || this.page, dest);
        } else if (pageNumber) {
          this.page = pageNumber; // simple page
        }
        if ('pagemode' in params) {
          if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' ||
              params.pagemode === 'attachments') {
            this.switchSidebarView((params.pagemode === 'bookmarks' ?
                                    'outline' : params.pagemode), true);
          } else if (params.pagemode === 'none' && this.sidebarOpen) {
            document.getElementById('sidebarToggle').click();
          }
        }
      } else if (/^\d+$/.test(hash)) { // page number
        this.page = hash;
      } else { // named destination
        PDFHistory.updateNextHashParam(unescape(hash));
        this.navigateTo(unescape(hash));
      }
    },
  
    switchSidebarView: function pdfViewSwitchSidebarView(view, openSidebar) {
      if (openSidebar && !this.sidebarOpen) {
        document.getElementById('sidebarToggle').click();
      }
      var thumbsView = document.getElementById('thumbnailView');
      var outlineView = document.getElementById('outlineView');
      var attachmentsView = document.getElementById('attachmentsView');
  
      var thumbsButton = document.getElementById('viewThumbnail');
      var outlineButton = document.getElementById('viewOutline');
      var attachmentsButton = document.getElementById('viewAttachments');
  
      switch (view) {
        case 'thumbs':
          var wasAnotherViewVisible = thumbsView.classList.contains('hidden');
  
          thumbsButton.classList.add('toggled');
          outlineButton.classList.remove('toggled');
          attachmentsButton.classList.remove('toggled');
          thumbsView.classList.remove('hidden');
          outlineView.classList.add('hidden');
          attachmentsView.classList.add('hidden');
  
          this.forceRendering();
  
          if (wasAnotherViewVisible) {
            this.pdfThumbnailViewer.ensureThumbnailVisible(this.page);
          }
          break;
  
        case 'outline':
          thumbsButton.classList.remove('toggled');
          outlineButton.classList.add('toggled');
          attachmentsButton.classList.remove('toggled');
          thumbsView.classList.add('hidden');
          outlineView.classList.remove('hidden');
          attachmentsView.classList.add('hidden');
  
          if (outlineButton.getAttribute('disabled')) {
            return;
          }
          break;
  
        case 'attachments':
          thumbsButton.classList.remove('toggled');
          outlineButton.classList.remove('toggled');
          attachmentsButton.classList.add('toggled');
          thumbsView.classList.add('hidden');
          outlineView.classList.add('hidden');
          attachmentsView.classList.remove('hidden');
  
          if (attachmentsButton.getAttribute('disabled')) {
            return;
          }
          break;
      }
    },
  
    // Helper function to parse query string (e.g. ?param1=value&parm2=...).
    parseQueryString: function pdfViewParseQueryString(query) {
      var parts = query.split('&');
      var params = {};
      for (var i = 0, ii = parts.length; i < ii; ++i) {
        var param = parts[i].split('=');
        var key = param[0].toLowerCase();
        var value = param.length > 1 ? param[1] : null;
        params[decodeURIComponent(key)] = decodeURIComponent(value);
      }
      return params;
    },
  
    beforePrint: function pdfViewSetupBeforePrint() {
      if (!this.supportsPrinting) {
        var printMessage = mozL10n.get('printing_not_supported', null,
            'Warning: Printing is not fully supported by this browser.');
        this.error(printMessage);
        return;
      }
  
      var alertNotReady = false;
      var i, ii;
      if (!this.pagesCount) {
        alertNotReady = true;
      } else {
        for (i = 0, ii = this.pagesCount; i < ii; ++i) {
          if (!this.pdfViewer.getPageView(i).pdfPage) {
            alertNotReady = true;
            break;
          }
        }
      }
      if (alertNotReady) {
        var notReadyMessage = mozL10n.get('printing_not_ready', null,
            'Warning: The PDF is not fully loaded for printing.');
        window.alert(notReadyMessage);
        return;
      }
  
      this.printing = true;
      this.forceRendering();
  
      var body = document.querySelector('body');
      body.setAttribute('data-mozPrintCallback', true);
      for (i = 0, ii = this.pagesCount; i < ii; ++i) {
        this.pdfViewer.getPageView(i).beforePrint();
      }
  
  //#if (FIREFOX || MOZCENTRAL)
  //    FirefoxCom.request('reportTelemetry', JSON.stringify({
  //      type: 'print'
  //    }));
  //#endif
    },
  
    afterPrint: function pdfViewSetupAfterPrint() {
      var div = document.getElementById('printContainer');
      while (div.hasChildNodes()) {
        div.removeChild(div.lastChild);
      }
  
      this.printing = false;
      this.forceRendering();
    },
  
    setScale: function (value, resetAutoSettings) {
      this.updateScaleControls = !!resetAutoSettings;
      this.pdfViewer.currentScaleValue = value;
      this.updateScaleControls = true;
    },
  
    rotatePages: function pdfViewRotatePages(delta) {
      var pageNumber = this.page;
  
      this.pageRotation = (this.pageRotation + 360 + delta) % 360;
      this.pdfViewer.pagesRotation = this.pageRotation;
      this.pdfThumbnailViewer.pagesRotation = this.pageRotation;
  
      this.forceRendering();
  
      this.pdfViewer.scrollPageIntoView(pageNumber);
    },
  
    /**
     * This function flips the page in presentation mode if the user scrolls up
     * or down with large enough motion and prevents page flipping too often.
     *
     * @this {PDFView}
     * @param {number} mouseScrollDelta The delta value from the mouse event.
     */
    mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) {
      var MOUSE_SCROLL_COOLDOWN_TIME = 50;
  
      var currentTime = (new Date()).getTime();
      var storedTime = this.mouseScrollTimeStamp;
  
      // In case one page has already been flipped there is a cooldown time
      // which has to expire before next page can be scrolled on to.
      if (currentTime > storedTime &&
          currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
        return;
      }
  
      // In case the user decides to scroll to the opposite direction than before
      // clear the accumulated delta.
      if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) ||
          (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) {
        this.clearMouseScrollState();
      }
  
      this.mouseScrollDelta += mouseScrollDelta;
  
      var PAGE_FLIP_THRESHOLD = 120;
      if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) {
  
        var PageFlipDirection = {
          UP: -1,
          DOWN: 1
        };
  
        // In presentation mode scroll one page at a time.
        var pageFlipDirection = (this.mouseScrollDelta > 0) ?
                                  PageFlipDirection.UP :
                                  PageFlipDirection.DOWN;
        this.clearMouseScrollState();
        var currentPage = this.page;
  
        // In case we are already on the first or the last page there is no need
        // to do anything.
        if ((currentPage === 1 && pageFlipDirection === PageFlipDirection.UP) ||
            (currentPage === this.pagesCount &&
             pageFlipDirection === PageFlipDirection.DOWN)) {
          return;
        }
  
        this.page += pageFlipDirection;
        this.mouseScrollTimeStamp = currentTime;
      }
    },
  
    /**
     * This function clears the member attributes used with mouse scrolling in
     * presentation mode.
     *
     * @this {PDFView}
     */
    clearMouseScrollState: function pdfViewClearMouseScrollState() {
      this.mouseScrollTimeStamp = 0;
      this.mouseScrollDelta = 0;
    }
  };
  //#if GENERIC
  window.PDFView = PDFViewerApplication; // obsolete name, using it as an alias
  //#endif
  
  //#include thumbnail_view.js
  //#include document_outline_view.js
  //#include document_attachments_view.js
  
  //#if CHROME
  //(function rewriteUrlClosure() {
  //  // Run this code outside DOMContentLoaded to make sure that the URL
  //  // is rewritten as soon as possible.
  //  var queryString = document.location.search.slice(1);
  //  var params = PDFViewerApplication.parseQueryString(queryString);
  //  DEFAULT_URL = params.file || '';
  //
  //  // Example: chrome-extension://.../http://example.com/file.pdf
  //  var humanReadableUrl = '/' + DEFAULT_URL + location.hash;
  //  history.replaceState(history.state, '', humanReadableUrl);
  //  if (top === window) {
  //    chrome.runtime.sendMessage('showPageAction');
  //  }
  //})();
  //#endif
  
  function webViewerLoad(evt) {
    PDFViewerApplication.initialize().then(webViewerInitialized);
  }
  
  function webViewerInitialized() {
  //#if (GENERIC || B2G)
    var queryString = document.location.search.substring(1);
    var params = PDFViewerApplication.parseQueryString(queryString);
    var file = 'file' in params ? params.file : DEFAULT_URL;
  //#endif
  //#if (FIREFOX || MOZCENTRAL)
  //var file = window.location.href.split('#')[0];
  //#endif
  //#if CHROME
  //var file = DEFAULT_URL;
  //#endif
  
  //#if !(FIREFOX || MOZCENTRAL || CHROME || B2G)
    var fileInput = document.createElement('input');
    fileInput.id = 'fileInput';
    fileInput.className = 'fileInput';
    fileInput.setAttribute('type', 'file');
    fileInput.oncontextmenu = noContextMenuHandler;
    document.body.appendChild(fileInput);
  
    if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {
      document.getElementById('openFile').setAttribute('hidden', 'true');
      document.getElementById('secondaryOpenFile').setAttribute('hidden', 'true');
    } else {
      document.getElementById('fileInput').value = null;
    }
  //#else
  //document.getElementById('openFile').setAttribute('hidden', 'true');
  //document.getElementById('secondaryOpenFile').setAttribute('hidden', 'true');
  //#endif
  
  //#if !(FIREFOX || MOZCENTRAL)
    var locale = PDFJS.locale || navigator.language;
  //#endif
  
  //#if !PRODUCTION
    if (true) {
  //#else
  //if (PDFViewerApplication.preferencePdfBugEnabled) {
  //#endif
      // Special debugging flags in the hash section of the URL.
      var hash = document.location.hash.substring(1);
      var hashParams = PDFViewerApplication.parseQueryString(hash);
  
      if ('disableworker' in hashParams) {
        PDFJS.disableWorker = (hashParams['disableworker'] === 'true');
      }
      if ('disablerange' in hashParams) {
        PDFJS.disableRange = (hashParams['disablerange'] === 'true');
      }
      if ('disablestream' in hashParams) {
        PDFJS.disableStream = (hashParams['disablestream'] === 'true');
      }
      if ('disableautofetch' in hashParams) {
        PDFJS.disableAutoFetch = (hashParams['disableautofetch'] === 'true');
      }
      if ('disablefontface' in hashParams) {
        PDFJS.disableFontFace = (hashParams['disablefontface'] === 'true');
      }
      if ('disablehistory' in hashParams) {
        PDFJS.disableHistory = (hashParams['disablehistory'] === 'true');
      }
      if ('webgl' in hashParams) {
        PDFJS.disableWebGL = (hashParams['webgl'] !== 'true');
      }
      if ('useonlycsszoom' in hashParams) {
        PDFJS.useOnlyCssZoom = (hashParams['useonlycsszoom'] === 'true');
      }
      if ('verbosity' in hashParams) {
        PDFJS.verbosity = hashParams['verbosity'] | 0;
      }
      if ('ignorecurrentpositiononzoom' in hashParams) {
        IGNORE_CURRENT_POSITION_ON_ZOOM =
          (hashParams['ignorecurrentpositiononzoom'] === 'true');
      }
  //#if !PRODUCTION
      if ('disablebcmaps' in hashParams && hashParams['disablebcmaps']) {
        PDFJS.cMapUrl = '../external/cmaps/';
        PDFJS.cMapPacked = false;
      }
  //#endif
  //#if !(FIREFOX || MOZCENTRAL)
      if ('locale' in hashParams) {
        locale = hashParams['locale'];
      }
  //#endif
      if ('textlayer' in hashParams) {
        switch (hashParams['textlayer']) {
          case 'off':
            PDFJS.disableTextLayer = true;
            break;
          case 'visible':
          case 'shadow':
          case 'hover':
            var viewer = document.getElementById('viewer');
            viewer.classList.add('textLayer-' + hashParams['textlayer']);
            break;
        }
      }
      if ('pdfbug' in hashParams) {
        PDFJS.pdfBug = true;
        var pdfBug = hashParams['pdfbug'];
        var enabled = pdfBug.split(',');
        PDFBug.enable(enabled);
        PDFBug.init();
      }
    }
  
  //#if !(FIREFOX || MOZCENTRAL)
    mozL10n.setLanguage(locale);
  //#endif
  //#if (FIREFOX || MOZCENTRAL)
  //if (!PDFViewerApplication.supportsDocumentFonts) {
  //  PDFJS.disableFontFace = true;
  //  console.warn(mozL10n.get('web_fonts_disabled', null,
  //    'Web fonts are disabled: unable to use embedded PDF fonts.'));
  //}
  //#endif
  
    if (!PDFViewerApplication.supportsPrinting) {
      document.getElementById('print').classList.add('hidden');
      document.getElementById('secondaryPrint').classList.add('hidden');
    }
  
    if (!PDFViewerApplication.supportsFullscreen) {
      document.getElementById('presentationMode').classList.add('hidden');
      document.getElementById('secondaryPresentationMode').
        classList.add('hidden');
    }
  
    if (PDFViewerApplication.supportsIntegratedFind) {
      document.getElementById('viewFind').classList.add('hidden');
    }
  
    // Listen for unsupported features to trigger the fallback UI.
    PDFJS.UnsupportedManager.listen(
      PDFViewerApplication.fallback.bind(PDFViewerApplication));
  
    // Suppress context menus for some controls
    document.getElementById('scaleSelect').oncontextmenu = noContextMenuHandler;
  
    var mainContainer = document.getElementById('mainContainer');
    var outerContainer = document.getElementById('outerContainer');
    mainContainer.addEventListener('transitionend', function(e) {
      if (e.target === mainContainer) {
        var event = document.createEvent('UIEvents');
        event.initUIEvent('resize', false, false, window, 0);
        window.dispatchEvent(event);
        outerContainer.classList.remove('sidebarMoving');
      }
    }, true);
  
    document.getElementById('sidebarToggle').addEventListener('click',
      function() {
        this.classList.toggle('toggled');
        outerContainer.classList.add('sidebarMoving');
        outerContainer.classList.toggle('sidebarOpen');
        PDFViewerApplication.sidebarOpen =
          outerContainer.classList.contains('sidebarOpen');
        PDFViewerApplication.forceRendering();
      });
  
    document.getElementById('viewThumbnail').addEventListener('click',
      function() {
        PDFViewerApplication.switchSidebarView('thumbs');
      });
  
    document.getElementById('viewOutline').addEventListener('click',
      function() {
        PDFViewerApplication.switchSidebarView('outline');
      });
  
    document.getElementById('viewAttachments').addEventListener('click',
      function() {
        PDFViewerApplication.switchSidebarView('attachments');
      });
  
    document.getElementById('previous').addEventListener('click',
      function() {
        PDFViewerApplication.page--;
      });
  
    document.getElementById('next').addEventListener('click',
      function() {
        PDFViewerApplication.page++;
      });
  
    document.getElementById('zoomIn').addEventListener('click',
      function() {
        PDFViewerApplication.zoomIn();
      });
  
    document.getElementById('zoomOut').addEventListener('click',
      function() {
        PDFViewerApplication.zoomOut();
      });
  
    document.getElementById('pageNumber').addEventListener('click', function() {
      this.select();
    });
  
    document.getElementById('pageNumber').addEventListener('change', function() {
      // Handle the user inputting a floating point number.
      PDFViewerApplication.page = (this.value | 0);
  
      if (this.value !== (this.value | 0).toString()) {
        this.value = PDFViewerApplication.page;
      }
    });
  
    document.getElementById('scaleSelect').addEventListener('change',
      function() {
        PDFViewerApplication.setScale(this.value, false);
      });
  
    document.getElementById('presentationMode').addEventListener('click',
      SecondaryToolbar.presentationModeClick.bind(SecondaryToolbar));
  
    document.getElementById('openFile').addEventListener('click',
      SecondaryToolbar.openFileClick.bind(SecondaryToolbar));
  
    document.getElementById('print').addEventListener('click',
      SecondaryToolbar.printClick.bind(SecondaryToolbar));
  
    document.getElementById('download').addEventListener('click',
      SecondaryToolbar.downloadClick.bind(SecondaryToolbar));
  
  //#if (FIREFOX || MOZCENTRAL)
  //PDFViewerApplication.setTitleUsingUrl(file);
  //PDFViewerApplication.initPassiveLoading();
  //return;
  //#endif
  
  //#if GENERIC
    if (file && file.lastIndexOf('file:', 0) === 0) {
      // file:-scheme. Load the contents in the main thread because QtWebKit
      // cannot load file:-URLs in a Web Worker. file:-URLs are usually loaded
      // very quickly, so there is no need to set up progress event listeners.
      PDFViewerApplication.setTitleUsingUrl(file);
      var xhr = new XMLHttpRequest();
      xhr.onload = function() {
        PDFViewerApplication.open(new Uint8Array(xhr.response), 0);
      };
      try {
        xhr.open('GET', file);
        xhr.responseType = 'arraybuffer';
        xhr.send();
      } catch (e) {
        PDFViewerApplication.error(mozL10n.get('loading_error', null,
          'An error occurred while loading the PDF.'), e);
      }
      return;
    }
  //#endif
  
  //#if !B2G && !CHROME
    if (file) {
      PDFViewerApplication.open(file, 0);
    }
  //#endif
  //#if CHROME
  //if (file) {
  //  ChromeCom.openPDFFile(file);
  //}
  //#endif
  }
  
  document.addEventListener('DOMContentLoaded', webViewerLoad, true);
  
  document.addEventListener('pagerendered', function (e) {
    var pageIndex = e.detail.pageNumber - 1;
    var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
    var thumbnailView = PDFViewerApplication.pdfThumbnailViewer.
                        getThumbnail(pageIndex);
    thumbnailView.setImage(pageView.canvas);
  
  //#if (FIREFOX || MOZCENTRAL)
  //if (pageView.textLayer && pageView.textLayer.textDivs &&
  //    pageView.textLayer.textDivs.length > 0 &&
  //    !PDFViewerApplication.supportsDocumentColors) {
  //  console.error(mozL10n.get('document_colors_disabled', null,
  //    'PDF documents are not allowed to use their own colors: ' +
  //    '\'Allow pages to choose their own colors\' ' +
  //    'is deactivated in the browser.'));
  //  PDFViewerApplication.fallback();
  //}
  //#endif
  
    if (pageView.error) {
      PDFViewerApplication.error(mozL10n.get('rendering_error', null,
        'An error occurred while rendering the page.'), pageView.error);
    }
  
  //#if (FIREFOX || MOZCENTRAL)
  //FirefoxCom.request('reportTelemetry', JSON.stringify({
  //  type: 'pageInfo'
  //}));
  //// It is a good time to report stream and font types
  //PDFViewerApplication.pdfDocument.getStats().then(function (stats) {
  //  FirefoxCom.request('reportTelemetry', JSON.stringify({
  //    type: 'documentStats',
  //    stats: stats
  //  }));
  //});
  //#endif
  
    // If the page is still visible when it has finished rendering,
    // ensure that the page number input loading indicator is hidden.
    if ((pageIndex + 1) === PDFViewerApplication.page) {
      var pageNumberInput = document.getElementById('pageNumber');
      pageNumberInput.classList.remove(PAGE_NUMBER_LOADING_INDICATOR);
    }
  }, true);
  
  window.addEventListener('presentationmodechanged', function (e) {
    var active = e.detail.active;
    var switchInProgress = e.detail.switchInProgress;
    PDFViewerApplication.pdfViewer.presentationModeState =
      switchInProgress ? PresentationModeState.CHANGING :
      active ? PresentationModeState.FULLSCREEN : PresentationModeState.NORMAL;
  });
  
  function updateViewarea() {
    if (!PDFViewerApplication.initialized) {
      return;
    }
    PDFViewerApplication.pdfViewer.update();
  }
  
  window.addEventListener('updateviewarea', function () {
    if (!PDFViewerApplication.initialized) {
      return;
    }
  
    var location = PDFViewerApplication.pdfViewer.location;
  
    PDFViewerApplication.store.initializedPromise.then(function() {
      PDFViewerApplication.store.setMultiple({
        'exists': true,
        'page': location.pageNumber,
        'zoom': location.scale,
        'scrollLeft': location.left,
        'scrollTop': location.top
      }).catch(function() {
        // unable to write to storage
      });
    });
    var href = PDFViewerApplication.getAnchorUrl(location.pdfOpenParams);
    document.getElementById('viewBookmark').href = href;
    document.getElementById('secondaryViewBookmark').href = href;
  
    // Update the current bookmark in the browsing history.
    PDFHistory.updateCurrentBookmark(location.pdfOpenParams, location.pageNumber);
  
    // Show/hide the loading indicator in the page number input element.
    var pageNumberInput = document.getElementById('pageNumber');
    var currentPage =
      PDFViewerApplication.pdfViewer.getPageView(PDFViewerApplication.page - 1);
  
    if (currentPage.renderingState === RenderingStates.FINISHED) {
      pageNumberInput.classList.remove(PAGE_NUMBER_LOADING_INDICATOR);
    } else {
      pageNumberInput.classList.add(PAGE_NUMBER_LOADING_INDICATOR);
    }
  }, true);
  
  window.addEventListener('resize', function webViewerResize(evt) {
    if (PDFViewerApplication.initialized &&
        (document.getElementById('pageWidthOption').selected ||
         document.getElementById('pageFitOption').selected ||
         document.getElementById('pageAutoOption').selected)) {
      var selectedScale = document.getElementById('scaleSelect').value;
      PDFViewerApplication.setScale(selectedScale, false);
    }
    updateViewarea();
  
    // Set the 'max-height' CSS property of the secondary toolbar.
    SecondaryToolbar.setMaxHeight(document.getElementById('viewerContainer'));
  });
  
  window.addEventListener('hashchange', function webViewerHashchange(evt) {
    if (PDFHistory.isHashChangeUnlocked) {
      PDFViewerApplication.setHash(document.location.hash.substring(1));
    }
  });
  
  //#if !(FIREFOX || MOZCENTRAL || CHROME)
  window.addEventListener('change', function webViewerChange(evt) {
    var files = evt.target.files;
    if (!files || files.length === 0) {
      return;
    }
    var file = files[0];
  
    if (!PDFJS.disableCreateObjectURL &&
        typeof URL !== 'undefined' && URL.createObjectURL) {
      PDFViewerApplication.open(URL.createObjectURL(file), 0);
    } else {
      // Read the local file into a Uint8Array.
      var fileReader = new FileReader();
      fileReader.onload = function webViewerChangeFileReaderOnload(evt) {
        var buffer = evt.target.result;
        var uint8Array = new Uint8Array(buffer);
        PDFViewerApplication.open(uint8Array, 0);
      };
      fileReader.readAsArrayBuffer(file);
    }
  
    PDFViewerApplication.setTitleUsingUrl(file.name);
  
    // URL does not reflect proper document location - hiding some icons.
    document.getElementById('viewBookmark').setAttribute('hidden', 'true');
    document.getElementById('secondaryViewBookmark').
      setAttribute('hidden', 'true');
    document.getElementById('download').setAttribute('hidden', 'true');
    document.getElementById('secondaryDownload').setAttribute('hidden', 'true');
  }, true);
  //#endif
  
  function selectScaleOption(value) {
    var options = document.getElementById('scaleSelect').options;
    var predefinedValueFound = false;
    for (var i = 0; i < options.length; i++) {
      var option = options[i];
      if (option.value !== value) {
        option.selected = false;
        continue;
      }
      option.selected = true;
      predefinedValueFound = true;
    }
    return predefinedValueFound;
  }
  
  window.addEventListener('localized', function localized(evt) {
    document.getElementsByTagName('html')[0].dir = mozL10n.getDirection();
  
    PDFViewerApplication.animationStartedPromise.then(function() {
      // Adjust the width of the zoom box to fit the content.
      // Note: If the window is narrow enough that the zoom box is not visible,
      //       we temporarily show it to be able to adjust its width.
      var container = document.getElementById('scaleSelectContainer');
      if (container.clientWidth === 0) {
        container.setAttribute('style', 'display: inherit;');
      }
      if (container.clientWidth > 0) {
        var select = document.getElementById('scaleSelect');
        select.setAttribute('style', 'min-width: inherit;');
        var width = select.clientWidth + SCALE_SELECT_CONTAINER_PADDING;
        select.setAttribute('style', 'min-width: ' +
                                     (width + SCALE_SELECT_PADDING) + 'px;');
        container.setAttribute('style', 'min-width: ' + width + 'px; ' +
                                        'max-width: ' + width + 'px;');
      }
  
      // Set the 'max-height' CSS property of the secondary toolbar.
      SecondaryToolbar.setMaxHeight(document.getElementById('viewerContainer'));
    });
  }, true);
  
  window.addEventListener('scalechange', function scalechange(evt) {
    document.getElementById('zoomOut').disabled = (evt.scale === MIN_SCALE);
    document.getElementById('zoomIn').disabled = (evt.scale === MAX_SCALE);
  
    var customScaleOption = document.getElementById('customScaleOption');
    customScaleOption.selected = false;
  
    if (!PDFViewerApplication.updateScaleControls &&
        (document.getElementById('pageWidthOption').selected ||
         document.getElementById('pageFitOption').selected ||
         document.getElementById('pageAutoOption').selected)) {
      updateViewarea();
      return;
    }
  
    if (evt.presetValue) {
      selectScaleOption(evt.presetValue);
      updateViewarea();
      return;
    }
  
    var predefinedValueFound = selectScaleOption('' + evt.scale);
    if (!predefinedValueFound) {
      customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
      customScaleOption.selected = true;
    }
    updateViewarea();
  }, true);
  
  window.addEventListener('pagechange', function pagechange(evt) {
    var page = evt.pageNumber;
    if (evt.previousPageNumber !== page) {
      document.getElementById('pageNumber').value = page;
      PDFViewerApplication.pdfThumbnailViewer.scrollThumbnailIntoView(page);
    }
    var numPages = PDFViewerApplication.pagesCount;
  
    document.getElementById('previous').disabled = (page <= 1);
    document.getElementById('next').disabled = (page >= numPages);
  
    document.getElementById('firstPage').disabled = (page <= 1);
    document.getElementById('lastPage').disabled = (page >= numPages);
  
    // checking if the this.page was called from the updateViewarea function
    if (evt.updateInProgress) {
      return;
    }
    // Avoid scrolling the first page during loading
    if (this.loading && page === 1) {
      return;
    }
    PDFViewerApplication.pdfViewer.scrollPageIntoView(page);
  }, true);
  
  function handleMouseWheel(evt) {
    var MOUSE_WHEEL_DELTA_FACTOR = 40;
    var ticks = (evt.type === 'DOMMouseScroll') ? -evt.detail :
                evt.wheelDelta / MOUSE_WHEEL_DELTA_FACTOR;
    var direction = (ticks < 0) ? 'zoomOut' : 'zoomIn';
  
    if (PresentationMode.active) {
      evt.preventDefault();
      PDFViewerApplication.mouseScroll(ticks * MOUSE_WHEEL_DELTA_FACTOR);
    } else if (evt.ctrlKey) { // Only zoom the pages, not the entire viewer
      evt.preventDefault();
      PDFViewerApplication[direction](Math.abs(ticks));
    }
  }
  
  window.addEventListener('DOMMouseScroll', handleMouseWheel);
  window.addEventListener('mousewheel', handleMouseWheel);
  
  window.addEventListener('click', function click(evt) {
    if (!PresentationMode.active) {
      if (SecondaryToolbar.opened &&
        PDFViewerApplication.pdfViewer.containsElement(evt.target)) {
        SecondaryToolbar.close();
      }
    } else if (evt.button === 0) {
      // Necessary since preventDefault() in 'mousedown' won't stop
      // the event propagation in all circumstances in presentation mode.
      evt.preventDefault();
    }
  }, false);
  
  window.addEventListener('keydown', function keydown(evt) {
    if (OverlayManager.active) {
      return;
    }
  
    var handled = false;
    var cmd = (evt.ctrlKey ? 1 : 0) |
              (evt.altKey ? 2 : 0) |
              (evt.shiftKey ? 4 : 0) |
              (evt.metaKey ? 8 : 0);
  
    // First, handle the key bindings that are independent whether an input
    // control is selected or not.
    if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
      // either CTRL or META key with optional SHIFT.
      var pdfViewer = PDFViewerApplication.pdfViewer;
      var inPresentationMode =
        pdfViewer.presentationModeState === PresentationModeState.CHANGING ||
        pdfViewer.presentationModeState === PresentationModeState.FULLSCREEN;
  
      switch (evt.keyCode) {
        case 70: // f
          if (!PDFViewerApplication.supportsIntegratedFind) {
            PDFViewerApplication.findBar.open();
            handled = true;
          }
          break;
        case 71: // g
          if (!PDFViewerApplication.supportsIntegratedFind) {
            PDFViewerApplication.findBar.dispatchEvent('again',
                                                       cmd === 5 || cmd === 12);
            handled = true;
          }
          break;
        case 61: // FF/Mac '='
        case 107: // FF '+' and '='
        case 187: // Chrome '+'
        case 171: // FF with German keyboard
          if (!inPresentationMode) {
            PDFViewerApplication.zoomIn();
          }
          handled = true;
          break;
        case 173: // FF/Mac '-'
        case 109: // FF '-'
        case 189: // Chrome '-'
          if (!inPresentationMode) {
            PDFViewerApplication.zoomOut();
          }
          handled = true;
          break;
        case 48: // '0'
        case 96: // '0' on Numpad of Swedish keyboard
          if (!inPresentationMode) {
            // keeping it unhandled (to restore page zoom to 100%)
            setTimeout(function () {
              // ... and resetting the scale after browser adjusts its scale
              PDFViewerApplication.setScale(DEFAULT_SCALE, true);
            });
            handled = false;
          }
          break;
      }
    }
  
  //#if !(FIREFOX || MOZCENTRAL)
    // CTRL or META without shift
    if (cmd === 1 || cmd === 8) {
      switch (evt.keyCode) {
        case 83: // s
          PDFViewerApplication.download();
          handled = true;
          break;
      }
    }
  //#endif
  
    // CTRL+ALT or Option+Command
    if (cmd === 3 || cmd === 10) {
      switch (evt.keyCode) {
        case 80: // p
          SecondaryToolbar.presentationModeClick();
          handled = true;
          break;
        case 71: // g
          // focuses input#pageNumber field
          document.getElementById('pageNumber').select();
          handled = true;
          break;
      }
    }
  
    if (handled) {
      evt.preventDefault();
      return;
    }
  
    // Some shortcuts should not get handled if a control/input element
    // is selected.
    var curElement = document.activeElement || document.querySelector(':focus');
    var curElementTagName = curElement && curElement.tagName.toUpperCase();
    if (curElementTagName === 'INPUT' ||
        curElementTagName === 'TEXTAREA' ||
        curElementTagName === 'SELECT') {
      // Make sure that the secondary toolbar is closed when Escape is pressed.
      if (evt.keyCode !== 27) { // 'Esc'
        return;
      }
    }
  
    if (cmd === 0) { // no control key pressed at all.
      switch (evt.keyCode) {
        case 38: // up arrow
        case 33: // pg up
        case 8: // backspace
          if (!PresentationMode.active &&
            PDFViewerApplication.currentScaleValue !== 'page-fit') {
            break;
          }
          /* in presentation mode */
          /* falls through */
        case 37: // left arrow
          // horizontal scrolling using arrow keys
          if (PDFViewerApplication.pdfViewer.isHorizontalScrollbarEnabled) {
            break;
          }
          /* falls through */
        case 75: // 'k'
        case 80: // 'p'
          PDFViewerApplication.page--;
          handled = true;
          break;
        case 27: // esc key
          if (SecondaryToolbar.opened) {
            SecondaryToolbar.close();
            handled = true;
          }
          if (!PDFViewerApplication.supportsIntegratedFind &&
              PDFViewerApplication.findBar.opened) {
            PDFViewerApplication.findBar.close();
            handled = true;
          }
          break;
        case 40: // down arrow
        case 34: // pg down
        case 32: // spacebar
          if (!PresentationMode.active &&
              PDFViewerApplication.currentScaleValue !== 'page-fit') {
            break;
          }
          /* falls through */
        case 39: // right arrow
          // horizontal scrolling using arrow keys
          if (PDFViewerApplication.pdfViewer.isHorizontalScrollbarEnabled) {
            break;
          }
          /* falls through */
        case 74: // 'j'
        case 78: // 'n'
          PDFViewerApplication.page++;
          handled = true;
          break;
  
        case 36: // home
          if (PresentationMode.active || PDFViewerApplication.page > 1) {
            PDFViewerApplication.page = 1;
            handled = true;
          }
          break;
        case 35: // end
          if (PresentationMode.active || (PDFViewerApplication.pdfDocument &&
              PDFViewerApplication.page < PDFViewerApplication.pagesCount)) {
            PDFViewerApplication.page = PDFViewerApplication.pagesCount;
            handled = true;
          }
          break;
  
        case 72: // 'h'
          if (!PresentationMode.active) {
            HandTool.toggle();
          }
          break;
        case 82: // 'r'
          PDFViewerApplication.rotatePages(90);
          break;
      }
    }
  
    if (cmd === 4) { // shift-key
      switch (evt.keyCode) {
        case 32: // spacebar
          if (!PresentationMode.active &&
              PDFViewerApplication.currentScaleValue !== 'page-fit') {
            break;
          }
          PDFViewerApplication.page--;
          handled = true;
          break;
  
        case 82: // 'r'
          PDFViewerApplication.rotatePages(-90);
          break;
      }
    }
  
    if (!handled && !PresentationMode.active) {
      // 33=Page Up  34=Page Down  35=End    36=Home
      // 37=Left     38=Up         39=Right  40=Down
      if (evt.keyCode >= 33 && evt.keyCode <= 40 &&
          !PDFViewerApplication.pdfViewer.containsElement(curElement)) {
        // The page container is not focused, but a page navigation key has been
        // pressed. Change the focus to the viewer container to make sure that
        // navigation by keyboard works as expected.
        PDFViewerApplication.pdfViewer.focus();
      }
      // 32=Spacebar
      if (evt.keyCode === 32 && curElementTagName !== 'BUTTON') {
  //#if (FIREFOX || MOZCENTRAL)
  //    // Workaround for issue in Firefox, that prevents scroll keys from
  //    // working when elements with 'tabindex' are focused. (#3498)
  //    PDFViewerApplication.pdfViewer.blur();
  //#else
        if (!PDFViewerApplication.pdfViewer.containsElement(curElement)) {
          PDFViewerApplication.pdfViewer.focus();
        }
  //#endif
      }
    }
  
    if (cmd === 2) { // alt-key
      switch (evt.keyCode) {
        case 37: // left arrow
          if (PresentationMode.active) {
            PDFHistory.back();
            handled = true;
          }
          break;
        case 39: // right arrow
          if (PresentationMode.active) {
            PDFHistory.forward();
            handled = true;
          }
          break;
      }
    }
  
    if (handled) {
      evt.preventDefault();
      PDFViewerApplication.clearMouseScrollState();
    }
  });
  
  window.addEventListener('beforeprint', function beforePrint(evt) {
    PDFViewerApplication.beforePrint();
  });
  
  window.addEventListener('afterprint', function afterPrint(evt) {
    PDFViewerApplication.afterPrint();
  });
  
  (function animationStartedClosure() {
    // The offsetParent is not set until the pdf.js iframe or object is visible.
    // Waiting for first animation.
    PDFViewerApplication.animationStartedPromise = new Promise(
        function (resolve) {
      window.requestAnimationFrame(resolve);
    });
  })();
  
  //#if B2G
  //window.navigator.mozSetMessageHandler('activity', function(activity) {
  //  var blob = activity.source.data.blob;
  //  PDFJS.maxImageSize = 1024 * 1024;
  //  var fileURL = activity.source.data.url;
  //
  //  var url = URL.createObjectURL(blob);
  //  PDFViewerApplication.open({url : url, originalUrl: fileURL});
  //
  //  var header = document.getElementById('header');
  //  header.addEventListener('action', function() {
  //    activity.postResult('close');
  //  });
  //});
  //#endif
  