browser/extensions/pdfjs/content/web/viewer.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/extensions/pdfjs/content/web/viewer.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,5805 @@
     1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
     1.6 +/* Copyright 2012 Mozilla Foundation
     1.7 + *
     1.8 + * Licensed under the Apache License, Version 2.0 (the "License");
     1.9 + * you may not use this file except in compliance with the License.
    1.10 + * You may obtain a copy of the License at
    1.11 + *
    1.12 + *     http://www.apache.org/licenses/LICENSE-2.0
    1.13 + *
    1.14 + * Unless required by applicable law or agreed to in writing, software
    1.15 + * distributed under the License is distributed on an "AS IS" BASIS,
    1.16 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1.17 + * See the License for the specific language governing permissions and
    1.18 + * limitations under the License.
    1.19 + */
    1.20 +/* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, PDFFindBar, CustomStyle,
    1.21 +           PDFFindController, ProgressBar, TextLayerBuilder, DownloadManager,
    1.22 +           getFileName, scrollIntoView, getPDFFileNameFromURL, PDFHistory,
    1.23 +           Preferences, ViewHistory, PageView, ThumbnailView, URL,
    1.24 +           noContextMenuHandler, SecondaryToolbar, PasswordPrompt,
    1.25 +           PresentationMode, HandTool, Promise, DocumentProperties */
    1.26 +
    1.27 +'use strict';
    1.28 +
    1.29 +var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
    1.30 +var DEFAULT_SCALE = 'auto';
    1.31 +var DEFAULT_SCALE_DELTA = 1.1;
    1.32 +var UNKNOWN_SCALE = 0;
    1.33 +var CACHE_SIZE = 20;
    1.34 +var CSS_UNITS = 96.0 / 72.0;
    1.35 +var SCROLLBAR_PADDING = 40;
    1.36 +var VERTICAL_PADDING = 5;
    1.37 +var MAX_AUTO_SCALE = 1.25;
    1.38 +var MIN_SCALE = 0.25;
    1.39 +var MAX_SCALE = 4.0;
    1.40 +var VIEW_HISTORY_MEMORY = 20;
    1.41 +var SCALE_SELECT_CONTAINER_PADDING = 8;
    1.42 +var SCALE_SELECT_PADDING = 22;
    1.43 +var THUMBNAIL_SCROLL_MARGIN = -19;
    1.44 +var USE_ONLY_CSS_ZOOM = false;
    1.45 +var CLEANUP_TIMEOUT = 30000;
    1.46 +var IGNORE_CURRENT_POSITION_ON_ZOOM = false;
    1.47 +var RenderingStates = {
    1.48 +  INITIAL: 0,
    1.49 +  RUNNING: 1,
    1.50 +  PAUSED: 2,
    1.51 +  FINISHED: 3
    1.52 +};
    1.53 +var FindStates = {
    1.54 +  FIND_FOUND: 0,
    1.55 +  FIND_NOTFOUND: 1,
    1.56 +  FIND_WRAPPED: 2,
    1.57 +  FIND_PENDING: 3
    1.58 +};
    1.59 +
    1.60 +PDFJS.imageResourcesPath = './images/';
    1.61 +  PDFJS.workerSrc = '../build/pdf.worker.js';
    1.62 +  PDFJS.cMapUrl = '../web/cmaps/';
    1.63 +  PDFJS.cMapPacked = true;
    1.64 +
    1.65 +var mozL10n = document.mozL10n || document.webL10n;
    1.66 +
    1.67 +
    1.68 +// optimised CSS custom property getter/setter
    1.69 +var CustomStyle = (function CustomStyleClosure() {
    1.70 +
    1.71 +  // As noted on: http://www.zachstronaut.com/posts/2009/02/17/
    1.72 +  //              animate-css-transforms-firefox-webkit.html
    1.73 +  // in some versions of IE9 it is critical that ms appear in this list
    1.74 +  // before Moz
    1.75 +  var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
    1.76 +  var _cache = {};
    1.77 +
    1.78 +  function CustomStyle() {}
    1.79 +
    1.80 +  CustomStyle.getProp = function get(propName, element) {
    1.81 +    // check cache only when no element is given
    1.82 +    if (arguments.length == 1 && typeof _cache[propName] == 'string') {
    1.83 +      return _cache[propName];
    1.84 +    }
    1.85 +
    1.86 +    element = element || document.documentElement;
    1.87 +    var style = element.style, prefixed, uPropName;
    1.88 +
    1.89 +    // test standard property first
    1.90 +    if (typeof style[propName] == 'string') {
    1.91 +      return (_cache[propName] = propName);
    1.92 +    }
    1.93 +
    1.94 +    // capitalize
    1.95 +    uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
    1.96 +
    1.97 +    // test vendor specific properties
    1.98 +    for (var i = 0, l = prefixes.length; i < l; i++) {
    1.99 +      prefixed = prefixes[i] + uPropName;
   1.100 +      if (typeof style[prefixed] == 'string') {
   1.101 +        return (_cache[propName] = prefixed);
   1.102 +      }
   1.103 +    }
   1.104 +
   1.105 +    //if all fails then set to undefined
   1.106 +    return (_cache[propName] = 'undefined');
   1.107 +  };
   1.108 +
   1.109 +  CustomStyle.setProp = function set(propName, element, str) {
   1.110 +    var prop = this.getProp(propName);
   1.111 +    if (prop != 'undefined') {
   1.112 +      element.style[prop] = str;
   1.113 +    }
   1.114 +  };
   1.115 +
   1.116 +  return CustomStyle;
   1.117 +})();
   1.118 +
   1.119 +function getFileName(url) {
   1.120 +  var anchor = url.indexOf('#');
   1.121 +  var query = url.indexOf('?');
   1.122 +  var end = Math.min(
   1.123 +    anchor > 0 ? anchor : url.length,
   1.124 +    query > 0 ? query : url.length);
   1.125 +  return url.substring(url.lastIndexOf('/', end) + 1, end);
   1.126 +}
   1.127 +
   1.128 +/**
   1.129 + * Returns scale factor for the canvas. It makes sense for the HiDPI displays.
   1.130 + * @return {Object} The object with horizontal (sx) and vertical (sy)
   1.131 +                    scales. The scaled property is set to false if scaling is
   1.132 +                    not required, true otherwise.
   1.133 + */
   1.134 +function getOutputScale(ctx) {
   1.135 +  var devicePixelRatio = window.devicePixelRatio || 1;
   1.136 +  var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
   1.137 +                          ctx.mozBackingStorePixelRatio ||
   1.138 +                          ctx.msBackingStorePixelRatio ||
   1.139 +                          ctx.oBackingStorePixelRatio ||
   1.140 +                          ctx.backingStorePixelRatio || 1;
   1.141 +  var pixelRatio = devicePixelRatio / backingStoreRatio;
   1.142 +  return {
   1.143 +    sx: pixelRatio,
   1.144 +    sy: pixelRatio,
   1.145 +    scaled: pixelRatio != 1
   1.146 +  };
   1.147 +}
   1.148 +
   1.149 +/**
   1.150 + * Scrolls specified element into view of its parent.
   1.151 + * element {Object} The element to be visible.
   1.152 + * spot {Object} An object with optional top and left properties,
   1.153 + *               specifying the offset from the top left edge.
   1.154 + */
   1.155 +function scrollIntoView(element, spot) {
   1.156 +  // Assuming offsetParent is available (it's not available when viewer is in
   1.157 +  // hidden iframe or object). We have to scroll: if the offsetParent is not set
   1.158 +  // producing the error. See also animationStartedClosure.
   1.159 +  var parent = element.offsetParent;
   1.160 +  var offsetY = element.offsetTop + element.clientTop;
   1.161 +  var offsetX = element.offsetLeft + element.clientLeft;
   1.162 +  if (!parent) {
   1.163 +    console.error('offsetParent is not set -- cannot scroll');
   1.164 +    return;
   1.165 +  }
   1.166 +  while (parent.clientHeight === parent.scrollHeight) {
   1.167 +    if (parent.dataset._scaleY) {
   1.168 +      offsetY /= parent.dataset._scaleY;
   1.169 +      offsetX /= parent.dataset._scaleX;
   1.170 +    }
   1.171 +    offsetY += parent.offsetTop;
   1.172 +    offsetX += parent.offsetLeft;
   1.173 +    parent = parent.offsetParent;
   1.174 +    if (!parent) {
   1.175 +      return; // no need to scroll
   1.176 +    }
   1.177 +  }
   1.178 +  if (spot) {
   1.179 +    if (spot.top !== undefined) {
   1.180 +      offsetY += spot.top;
   1.181 +    }
   1.182 +    if (spot.left !== undefined) {
   1.183 +      offsetX += spot.left;
   1.184 +      parent.scrollLeft = offsetX;
   1.185 +    }
   1.186 +  }
   1.187 +  parent.scrollTop = offsetY;
   1.188 +}
   1.189 +
   1.190 +/**
   1.191 + * Event handler to suppress context menu.
   1.192 + */
   1.193 +function noContextMenuHandler(e) {
   1.194 +  e.preventDefault();
   1.195 +}
   1.196 +
   1.197 +/**
   1.198 + * Returns the filename or guessed filename from the url (see issue 3455).
   1.199 + * url {String} The original PDF location.
   1.200 + * @return {String} Guessed PDF file name.
   1.201 + */
   1.202 +function getPDFFileNameFromURL(url) {
   1.203 +  var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
   1.204 +  //            SCHEME      HOST         1.PATH  2.QUERY   3.REF
   1.205 +  // Pattern to get last matching NAME.pdf
   1.206 +  var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
   1.207 +  var splitURI = reURI.exec(url);
   1.208 +  var suggestedFilename = reFilename.exec(splitURI[1]) ||
   1.209 +                           reFilename.exec(splitURI[2]) ||
   1.210 +                           reFilename.exec(splitURI[3]);
   1.211 +  if (suggestedFilename) {
   1.212 +    suggestedFilename = suggestedFilename[0];
   1.213 +    if (suggestedFilename.indexOf('%') != -1) {
   1.214 +      // URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
   1.215 +      try {
   1.216 +        suggestedFilename =
   1.217 +          reFilename.exec(decodeURIComponent(suggestedFilename))[0];
   1.218 +      } catch(e) { // Possible (extremely rare) errors:
   1.219 +        // URIError "Malformed URI", e.g. for "%AA.pdf"
   1.220 +        // TypeError "null has no properties", e.g. for "%2F.pdf"
   1.221 +      }
   1.222 +    }
   1.223 +  }
   1.224 +  return suggestedFilename || 'document.pdf';
   1.225 +}
   1.226 +
   1.227 +var ProgressBar = (function ProgressBarClosure() {
   1.228 +
   1.229 +  function clamp(v, min, max) {
   1.230 +    return Math.min(Math.max(v, min), max);
   1.231 +  }
   1.232 +
   1.233 +  function ProgressBar(id, opts) {
   1.234 +
   1.235 +    // Fetch the sub-elements for later.
   1.236 +    this.div = document.querySelector(id + ' .progress');
   1.237 +
   1.238 +    // Get the loading bar element, so it can be resized to fit the viewer.
   1.239 +    this.bar = this.div.parentNode;
   1.240 +
   1.241 +    // Get options, with sensible defaults.
   1.242 +    this.height = opts.height || 100;
   1.243 +    this.width = opts.width || 100;
   1.244 +    this.units = opts.units || '%';
   1.245 +
   1.246 +    // Initialize heights.
   1.247 +    this.div.style.height = this.height + this.units;
   1.248 +    this.percent = 0;
   1.249 +  }
   1.250 +
   1.251 +  ProgressBar.prototype = {
   1.252 +
   1.253 +    updateBar: function ProgressBar_updateBar() {
   1.254 +      if (this._indeterminate) {
   1.255 +        this.div.classList.add('indeterminate');
   1.256 +        this.div.style.width = this.width + this.units;
   1.257 +        return;
   1.258 +      }
   1.259 +
   1.260 +      this.div.classList.remove('indeterminate');
   1.261 +      var progressSize = this.width * this._percent / 100;
   1.262 +      this.div.style.width = progressSize + this.units;
   1.263 +    },
   1.264 +
   1.265 +    get percent() {
   1.266 +      return this._percent;
   1.267 +    },
   1.268 +
   1.269 +    set percent(val) {
   1.270 +      this._indeterminate = isNaN(val);
   1.271 +      this._percent = clamp(val, 0, 100);
   1.272 +      this.updateBar();
   1.273 +    },
   1.274 +
   1.275 +    setWidth: function ProgressBar_setWidth(viewer) {
   1.276 +      if (viewer) {
   1.277 +        var container = viewer.parentNode;
   1.278 +        var scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
   1.279 +        if (scrollbarWidth > 0) {
   1.280 +          this.bar.setAttribute('style', 'width: calc(100% - ' +
   1.281 +                                         scrollbarWidth + 'px);');
   1.282 +        }
   1.283 +      }
   1.284 +    },
   1.285 +
   1.286 +    hide: function ProgressBar_hide() {
   1.287 +      this.bar.classList.add('hidden');
   1.288 +      this.bar.removeAttribute('style');
   1.289 +    }
   1.290 +  };
   1.291 +
   1.292 +  return ProgressBar;
   1.293 +})();
   1.294 +
   1.295 +var Cache = function cacheCache(size) {
   1.296 +  var data = [];
   1.297 +  this.push = function cachePush(view) {
   1.298 +    var i = data.indexOf(view);
   1.299 +    if (i >= 0) {
   1.300 +      data.splice(i);
   1.301 +    }
   1.302 +    data.push(view);
   1.303 +    if (data.length > size) {
   1.304 +      data.shift().destroy();
   1.305 +    }
   1.306 +  };
   1.307 +};
   1.308 +
   1.309 +
   1.310 +
   1.311 +
   1.312 +var DEFAULT_PREFERENCES = {
   1.313 +  showPreviousViewOnLoad: true,
   1.314 +  defaultZoomValue: '',
   1.315 +  ifAvailableShowOutlineOnLoad: false,
   1.316 +  enableHandToolOnLoad: false,
   1.317 +  enableWebGL: false
   1.318 +};
   1.319 +
   1.320 +
   1.321 +/**
   1.322 + * Preferences - Utility for storing persistent settings.
   1.323 + *   Used for settings that should be applied to all opened documents,
   1.324 + *   or every time the viewer is loaded.
   1.325 + */
   1.326 +var Preferences = {
   1.327 +  prefs: Object.create(DEFAULT_PREFERENCES),
   1.328 +  isInitializedPromiseResolved: false,
   1.329 +  initializedPromise: null,
   1.330 +
   1.331 +  /**
   1.332 +   * Initialize and fetch the current preference values from storage.
   1.333 +   * @return {Promise} A promise that is resolved when the preferences
   1.334 +   *                   have been initialized.
   1.335 +   */
   1.336 +  initialize: function preferencesInitialize() {
   1.337 +    return this.initializedPromise =
   1.338 +        this._readFromStorage(DEFAULT_PREFERENCES).then(function(prefObj) {
   1.339 +      this.isInitializedPromiseResolved = true;
   1.340 +      if (prefObj) {
   1.341 +        this.prefs = prefObj;
   1.342 +      }
   1.343 +    }.bind(this));
   1.344 +  },
   1.345 +
   1.346 +  /**
   1.347 +   * Stub function for writing preferences to storage.
   1.348 +   * NOTE: This should be overridden by a build-specific function defined below.
   1.349 +   * @param {Object} prefObj The preferences that should be written to storage.
   1.350 +   * @return {Promise} A promise that is resolved when the preference values
   1.351 +   *                   have been written.
   1.352 +   */
   1.353 +  _writeToStorage: function preferences_writeToStorage(prefObj) {
   1.354 +    return Promise.resolve();
   1.355 +  },
   1.356 +
   1.357 +  /**
   1.358 +   * Stub function for reading preferences from storage.
   1.359 +   * NOTE: This should be overridden by a build-specific function defined below.
   1.360 +   * @param {Object} prefObj The preferences that should be read from storage.
   1.361 +   * @return {Promise} A promise that is resolved with an {Object} containing
   1.362 +   *                   the preferences that have been read.
   1.363 +   */
   1.364 +  _readFromStorage: function preferences_readFromStorage(prefObj) {
   1.365 +    return Promise.resolve();
   1.366 +  },
   1.367 +
   1.368 +  /**
   1.369 +   * Reset the preferences to their default values and update storage.
   1.370 +   * @return {Promise} A promise that is resolved when the preference values
   1.371 +   *                   have been reset.
   1.372 +   */
   1.373 +  reset: function preferencesReset() {
   1.374 +    return this.initializedPromise.then(function() {
   1.375 +      this.prefs = Object.create(DEFAULT_PREFERENCES);
   1.376 +      return this._writeToStorage(DEFAULT_PREFERENCES);
   1.377 +    }.bind(this));
   1.378 +  },
   1.379 +
   1.380 +  /**
   1.381 +   * Replace the current preference values with the ones from storage.
   1.382 +   * @return {Promise} A promise that is resolved when the preference values
   1.383 +   *                   have been updated.
   1.384 +   */
   1.385 +  reload: function preferencesReload() {
   1.386 +    return this.initializedPromise.then(function () {
   1.387 +      this._readFromStorage(DEFAULT_PREFERENCES).then(function(prefObj) {
   1.388 +        if (prefObj) {
   1.389 +          this.prefs = prefObj;
   1.390 +        }
   1.391 +      }.bind(this));
   1.392 +    }.bind(this));
   1.393 +  },
   1.394 +
   1.395 +  /**
   1.396 +   * Set the value of a preference.
   1.397 +   * @param {string} name The name of the preference that should be changed.
   1.398 +   * @param {boolean|number|string} value The new value of the preference.
   1.399 +   * @return {Promise} A promise that is resolved when the value has been set,
   1.400 +   *                   provided that the preference exists and the types match.
   1.401 +   */
   1.402 +  set: function preferencesSet(name, value) {
   1.403 +    return this.initializedPromise.then(function () {
   1.404 +      if (DEFAULT_PREFERENCES[name] === undefined) {
   1.405 +        throw new Error('preferencesSet: \'' + name + '\' is undefined.');
   1.406 +      } else if (value === undefined) {
   1.407 +        throw new Error('preferencesSet: no value is specified.');
   1.408 +      }
   1.409 +      var valueType = typeof value;
   1.410 +      var defaultType = typeof DEFAULT_PREFERENCES[name];
   1.411 +
   1.412 +      if (valueType !== defaultType) {
   1.413 +        if (valueType === 'number' && defaultType === 'string') {
   1.414 +          value = value.toString();
   1.415 +        } else {
   1.416 +          throw new Error('Preferences_set: \'' + value + '\' is a \"' +
   1.417 +                          valueType + '\", expected \"' + defaultType + '\".');
   1.418 +        }
   1.419 +      } else {
   1.420 +        if (valueType === 'number' && (value | 0) !== value) {
   1.421 +          throw new Error('Preferences_set: \'' + value +
   1.422 +                          '\' must be an \"integer\".');
   1.423 +        }
   1.424 +      }
   1.425 +      this.prefs[name] = value;
   1.426 +      return this._writeToStorage(this.prefs);
   1.427 +    }.bind(this));
   1.428 +  },
   1.429 +
   1.430 +  /**
   1.431 +   * Get the value of a preference.
   1.432 +   * @param {string} name The name of the preference whose value is requested.
   1.433 +   * @return {Promise} A promise that is resolved with a {boolean|number|string}
   1.434 +   *                   containing the value of the preference.
   1.435 +   */
   1.436 +  get: function preferencesGet(name) {
   1.437 +    return this.initializedPromise.then(function () {
   1.438 +      var defaultValue = DEFAULT_PREFERENCES[name];
   1.439 +
   1.440 +      if (defaultValue === undefined) {
   1.441 +        throw new Error('preferencesGet: \'' + name + '\' is undefined.');
   1.442 +      } else {
   1.443 +        var prefValue = this.prefs[name];
   1.444 +
   1.445 +        if (prefValue !== undefined) {
   1.446 +          return prefValue;
   1.447 +        }
   1.448 +      }
   1.449 +      return defaultValue;
   1.450 +    }.bind(this));
   1.451 +  }
   1.452 +};
   1.453 +
   1.454 +
   1.455 +
   1.456 +
   1.457 +
   1.458 +
   1.459 +
   1.460 +var FirefoxCom = (function FirefoxComClosure() {
   1.461 +  return {
   1.462 +    /**
   1.463 +     * Creates an event that the extension is listening for and will
   1.464 +     * synchronously respond to.
   1.465 +     * NOTE: It is reccomended to use request() instead since one day we may not
   1.466 +     * be able to synchronously reply.
   1.467 +     * @param {String} action The action to trigger.
   1.468 +     * @param {String} data Optional data to send.
   1.469 +     * @return {*} The response.
   1.470 +     */
   1.471 +    requestSync: function(action, data) {
   1.472 +      var request = document.createTextNode('');
   1.473 +      document.documentElement.appendChild(request);
   1.474 +
   1.475 +      var sender = document.createEvent('CustomEvent');
   1.476 +      sender.initCustomEvent('pdf.js.message', true, false,
   1.477 +                             {action: action, data: data, sync: true});
   1.478 +      request.dispatchEvent(sender);
   1.479 +      var response = sender.detail.response;
   1.480 +      document.documentElement.removeChild(request);
   1.481 +      return response;
   1.482 +    },
   1.483 +    /**
   1.484 +     * Creates an event that the extension is listening for and will
   1.485 +     * asynchronously respond by calling the callback.
   1.486 +     * @param {String} action The action to trigger.
   1.487 +     * @param {String} data Optional data to send.
   1.488 +     * @param {Function} callback Optional response callback that will be called
   1.489 +     * with one data argument.
   1.490 +     */
   1.491 +    request: function(action, data, callback) {
   1.492 +      var request = document.createTextNode('');
   1.493 +      if (callback) {
   1.494 +        document.addEventListener('pdf.js.response', function listener(event) {
   1.495 +          var node = event.target;
   1.496 +          var response = event.detail.response;
   1.497 +
   1.498 +          document.documentElement.removeChild(node);
   1.499 +
   1.500 +          document.removeEventListener('pdf.js.response', listener, false);
   1.501 +          return callback(response);
   1.502 +        }, false);
   1.503 +      }
   1.504 +      document.documentElement.appendChild(request);
   1.505 +
   1.506 +      var sender = document.createEvent('CustomEvent');
   1.507 +      sender.initCustomEvent('pdf.js.message', true, false,
   1.508 +                             {action: action, data: data, sync: false,
   1.509 +                              callback: callback});
   1.510 +      return request.dispatchEvent(sender);
   1.511 +    }
   1.512 +  };
   1.513 +})();
   1.514 +
   1.515 +var DownloadManager = (function DownloadManagerClosure() {
   1.516 +  function DownloadManager() {}
   1.517 +
   1.518 +  DownloadManager.prototype = {
   1.519 +    downloadUrl: function DownloadManager_downloadUrl(url, filename) {
   1.520 +      FirefoxCom.request('download', {
   1.521 +        originalUrl: url,
   1.522 +        filename: filename
   1.523 +      });
   1.524 +    },
   1.525 +
   1.526 +    downloadData: function DownloadManager_downloadData(data, filename,
   1.527 +                                                        contentType) {
   1.528 +      var blobUrl = PDFJS.createObjectURL(data, contentType);
   1.529 +      
   1.530 +      FirefoxCom.request('download', {
   1.531 +        blobUrl: blobUrl,
   1.532 +        originalUrl: blobUrl,
   1.533 +        filename: filename,
   1.534 +        isAttachment: true
   1.535 +      });
   1.536 +    },
   1.537 +
   1.538 +    download: function DownloadManager_download(blob, url, filename) {
   1.539 +      var blobUrl = window.URL.createObjectURL(blob);
   1.540 +
   1.541 +      FirefoxCom.request('download', {
   1.542 +        blobUrl: blobUrl,
   1.543 +        originalUrl: url,
   1.544 +        filename: filename
   1.545 +      },
   1.546 +        function response(err) {
   1.547 +          if (err && this.onerror) {
   1.548 +            this.onerror(err);
   1.549 +          }
   1.550 +          window.URL.revokeObjectURL(blobUrl);
   1.551 +        }.bind(this)
   1.552 +      );
   1.553 +    }
   1.554 +  };
   1.555 +
   1.556 +  return DownloadManager;
   1.557 +})();
   1.558 +
   1.559 +Preferences._writeToStorage = function (prefObj) {
   1.560 +  return new Promise(function (resolve) {
   1.561 +    FirefoxCom.request('setPreferences', prefObj, resolve);
   1.562 +  });
   1.563 +};
   1.564 +
   1.565 +Preferences._readFromStorage = function (prefObj) {
   1.566 +  return new Promise(function (resolve) {
   1.567 +    FirefoxCom.request('getPreferences', prefObj, function (prefStr) {
   1.568 +      var readPrefs = JSON.parse(prefStr);
   1.569 +      resolve(readPrefs);
   1.570 +    });
   1.571 +  });
   1.572 +};
   1.573 +
   1.574 +
   1.575 +
   1.576 +var cache = new Cache(CACHE_SIZE);
   1.577 +var currentPageNumber = 1;
   1.578 +
   1.579 +
   1.580 +/**
   1.581 + * View History - This is a utility for saving various view parameters for
   1.582 + *                recently opened files.
   1.583 + *
   1.584 + * The way that the view parameters are stored depends on how PDF.js is built,
   1.585 + * for 'node make <flag>' the following cases exist:
   1.586 + *  - FIREFOX or MOZCENTRAL - uses sessionStorage.
   1.587 + *  - B2G                   - uses asyncStorage.
   1.588 + *  - GENERIC or CHROME     - uses localStorage, if it is available.
   1.589 + */
   1.590 +var ViewHistory = (function ViewHistoryClosure() {
   1.591 +  function ViewHistory(fingerprint) {
   1.592 +    this.fingerprint = fingerprint;
   1.593 +    var initializedPromiseResolve;
   1.594 +    this.isInitializedPromiseResolved = false;
   1.595 +    this.initializedPromise = new Promise(function (resolve) {
   1.596 +      initializedPromiseResolve = resolve;
   1.597 +    });
   1.598 +
   1.599 +    var resolvePromise = (function ViewHistoryResolvePromise(db) {
   1.600 +      this.isInitializedPromiseResolved = true;
   1.601 +      this.initialize(db || '{}');
   1.602 +      initializedPromiseResolve();
   1.603 +    }).bind(this);
   1.604 +
   1.605 +
   1.606 +    var sessionHistory;
   1.607 +    try {
   1.608 +      // Workaround for security error when the preference
   1.609 +      // network.cookie.lifetimePolicy is set to 1, see Mozilla Bug 365772.
   1.610 +      sessionHistory = sessionStorage.getItem('pdfjsHistory');
   1.611 +    } catch (ex) {}
   1.612 +    resolvePromise(sessionHistory);
   1.613 +
   1.614 +  }
   1.615 +
   1.616 +  ViewHistory.prototype = {
   1.617 +    initialize: function ViewHistory_initialize(database) {
   1.618 +      database = JSON.parse(database);
   1.619 +      if (!('files' in database)) {
   1.620 +        database.files = [];
   1.621 +      }
   1.622 +      if (database.files.length >= VIEW_HISTORY_MEMORY) {
   1.623 +        database.files.shift();
   1.624 +      }
   1.625 +      var index;
   1.626 +      for (var i = 0, length = database.files.length; i < length; i++) {
   1.627 +        var branch = database.files[i];
   1.628 +        if (branch.fingerprint === this.fingerprint) {
   1.629 +          index = i;
   1.630 +          break;
   1.631 +        }
   1.632 +      }
   1.633 +      if (typeof index !== 'number') {
   1.634 +        index = database.files.push({fingerprint: this.fingerprint}) - 1;
   1.635 +      }
   1.636 +      this.file = database.files[index];
   1.637 +      this.database = database;
   1.638 +    },
   1.639 +
   1.640 +    set: function ViewHistory_set(name, val) {
   1.641 +      if (!this.isInitializedPromiseResolved) {
   1.642 +        return;
   1.643 +      }
   1.644 +      var file = this.file;
   1.645 +      file[name] = val;
   1.646 +      var database = JSON.stringify(this.database);
   1.647 +
   1.648 +
   1.649 +      try {
   1.650 +        // See comment in try-catch block above.
   1.651 +        sessionStorage.setItem('pdfjsHistory', database);
   1.652 +      } catch (ex) {}
   1.653 +
   1.654 +    },
   1.655 +
   1.656 +    get: function ViewHistory_get(name, defaultValue) {
   1.657 +      if (!this.isInitializedPromiseResolved) {
   1.658 +        return defaultValue;
   1.659 +      }
   1.660 +      return this.file[name] || defaultValue;
   1.661 +    }
   1.662 +  };
   1.663 +
   1.664 +  return ViewHistory;
   1.665 +})();
   1.666 +
   1.667 +
   1.668 +/**
   1.669 + * Creates a "search bar" given set of DOM elements
   1.670 + * that act as controls for searching, or for setting
   1.671 + * search preferences in the UI. This object also sets
   1.672 + * up the appropriate events for the controls. Actual
   1.673 + * searching is done by PDFFindController
   1.674 + */
   1.675 +var PDFFindBar = {
   1.676 +  opened: false,
   1.677 +  bar: null,
   1.678 +  toggleButton: null,
   1.679 +  findField: null,
   1.680 +  highlightAll: null,
   1.681 +  caseSensitive: null,
   1.682 +  findMsg: null,
   1.683 +  findStatusIcon: null,
   1.684 +  findPreviousButton: null,
   1.685 +  findNextButton: null,
   1.686 +
   1.687 +  initialize: function(options) {
   1.688 +    if(typeof PDFFindController === 'undefined' || PDFFindController === null) {
   1.689 +      throw 'PDFFindBar cannot be initialized ' +
   1.690 +            'without a PDFFindController instance.';
   1.691 +    }
   1.692 +
   1.693 +    this.bar = options.bar;
   1.694 +    this.toggleButton = options.toggleButton;
   1.695 +    this.findField = options.findField;
   1.696 +    this.highlightAll = options.highlightAllCheckbox;
   1.697 +    this.caseSensitive = options.caseSensitiveCheckbox;
   1.698 +    this.findMsg = options.findMsg;
   1.699 +    this.findStatusIcon = options.findStatusIcon;
   1.700 +    this.findPreviousButton = options.findPreviousButton;
   1.701 +    this.findNextButton = options.findNextButton;
   1.702 +
   1.703 +    var self = this;
   1.704 +    this.toggleButton.addEventListener('click', function() {
   1.705 +      self.toggle();
   1.706 +    });
   1.707 +
   1.708 +    this.findField.addEventListener('input', function() {
   1.709 +      self.dispatchEvent('');
   1.710 +    });
   1.711 +
   1.712 +    this.bar.addEventListener('keydown', function(evt) {
   1.713 +      switch (evt.keyCode) {
   1.714 +        case 13: // Enter
   1.715 +          if (evt.target === self.findField) {
   1.716 +            self.dispatchEvent('again', evt.shiftKey);
   1.717 +          }
   1.718 +          break;
   1.719 +        case 27: // Escape
   1.720 +          self.close();
   1.721 +          break;
   1.722 +      }
   1.723 +    });
   1.724 +
   1.725 +    this.findPreviousButton.addEventListener('click',
   1.726 +      function() { self.dispatchEvent('again', true); }
   1.727 +    );
   1.728 +
   1.729 +    this.findNextButton.addEventListener('click', function() {
   1.730 +      self.dispatchEvent('again', false);
   1.731 +    });
   1.732 +
   1.733 +    this.highlightAll.addEventListener('click', function() {
   1.734 +      self.dispatchEvent('highlightallchange');
   1.735 +    });
   1.736 +
   1.737 +    this.caseSensitive.addEventListener('click', function() {
   1.738 +      self.dispatchEvent('casesensitivitychange');
   1.739 +    });
   1.740 +  },
   1.741 +
   1.742 +  dispatchEvent: function(aType, aFindPrevious) {
   1.743 +    var event = document.createEvent('CustomEvent');
   1.744 +    event.initCustomEvent('find' + aType, true, true, {
   1.745 +      query: this.findField.value,
   1.746 +      caseSensitive: this.caseSensitive.checked,
   1.747 +      highlightAll: this.highlightAll.checked,
   1.748 +      findPrevious: aFindPrevious
   1.749 +    });
   1.750 +    return window.dispatchEvent(event);
   1.751 +  },
   1.752 +
   1.753 +  updateUIState: function(state, previous) {
   1.754 +    var notFound = false;
   1.755 +    var findMsg = '';
   1.756 +    var status = '';
   1.757 +
   1.758 +    switch (state) {
   1.759 +      case FindStates.FIND_FOUND:
   1.760 +        break;
   1.761 +
   1.762 +      case FindStates.FIND_PENDING:
   1.763 +        status = 'pending';
   1.764 +        break;
   1.765 +
   1.766 +      case FindStates.FIND_NOTFOUND:
   1.767 +        findMsg = mozL10n.get('find_not_found', null, 'Phrase not found');
   1.768 +        notFound = true;
   1.769 +        break;
   1.770 +
   1.771 +      case FindStates.FIND_WRAPPED:
   1.772 +        if (previous) {
   1.773 +          findMsg = mozL10n.get('find_reached_top', null,
   1.774 +                      'Reached top of document, continued from bottom');
   1.775 +        } else {
   1.776 +          findMsg = mozL10n.get('find_reached_bottom', null,
   1.777 +                                'Reached end of document, continued from top');
   1.778 +        }
   1.779 +        break;
   1.780 +    }
   1.781 +
   1.782 +    if (notFound) {
   1.783 +      this.findField.classList.add('notFound');
   1.784 +    } else {
   1.785 +      this.findField.classList.remove('notFound');
   1.786 +    }
   1.787 +
   1.788 +    this.findField.setAttribute('data-status', status);
   1.789 +    this.findMsg.textContent = findMsg;
   1.790 +  },
   1.791 +
   1.792 +  open: function() {
   1.793 +    if (!this.opened) {
   1.794 +      this.opened = true;
   1.795 +      this.toggleButton.classList.add('toggled');
   1.796 +      this.bar.classList.remove('hidden');
   1.797 +    }
   1.798 +
   1.799 +    this.findField.select();
   1.800 +    this.findField.focus();
   1.801 +  },
   1.802 +
   1.803 +  close: function() {
   1.804 +    if (!this.opened) {
   1.805 +      return;
   1.806 +    }
   1.807 +    this.opened = false;
   1.808 +    this.toggleButton.classList.remove('toggled');
   1.809 +    this.bar.classList.add('hidden');
   1.810 +
   1.811 +    PDFFindController.active = false;
   1.812 +  },
   1.813 +
   1.814 +  toggle: function() {
   1.815 +    if (this.opened) {
   1.816 +      this.close();
   1.817 +    } else {
   1.818 +      this.open();
   1.819 +    }
   1.820 +  }
   1.821 +};
   1.822 +
   1.823 +
   1.824 +
   1.825 +/**
   1.826 + * Provides a "search" or "find" functionality for the PDF.
   1.827 + * This object actually performs the search for a given string.
   1.828 + */
   1.829 +
   1.830 +var PDFFindController = {
   1.831 +  startedTextExtraction: false,
   1.832 +
   1.833 +  extractTextPromises: [],
   1.834 +
   1.835 +  pendingFindMatches: {},
   1.836 +
   1.837 +  // If active, find results will be highlighted.
   1.838 +  active: false,
   1.839 +
   1.840 +  // Stores the text for each page.
   1.841 +  pageContents: [],
   1.842 +
   1.843 +  pageMatches: [],
   1.844 +
   1.845 +  // Currently selected match.
   1.846 +  selected: {
   1.847 +    pageIdx: -1,
   1.848 +    matchIdx: -1
   1.849 +  },
   1.850 +
   1.851 +  // Where find algorithm currently is in the document.
   1.852 +  offset: {
   1.853 +    pageIdx: null,
   1.854 +    matchIdx: null
   1.855 +  },
   1.856 +
   1.857 +  resumePageIdx: null,
   1.858 +
   1.859 +  state: null,
   1.860 +
   1.861 +  dirtyMatch: false,
   1.862 +
   1.863 +  findTimeout: null,
   1.864 +
   1.865 +  pdfPageSource: null,
   1.866 +
   1.867 +  integratedFind: false,
   1.868 +
   1.869 +  initialize: function(options) {
   1.870 +    if(typeof PDFFindBar === 'undefined' || PDFFindBar === null) {
   1.871 +      throw 'PDFFindController cannot be initialized ' +
   1.872 +            'without a PDFFindBar instance';
   1.873 +    }
   1.874 +
   1.875 +    this.pdfPageSource = options.pdfPageSource;
   1.876 +    this.integratedFind = options.integratedFind;
   1.877 +
   1.878 +    var events = [
   1.879 +      'find',
   1.880 +      'findagain',
   1.881 +      'findhighlightallchange',
   1.882 +      'findcasesensitivitychange'
   1.883 +    ];
   1.884 +
   1.885 +    this.firstPagePromise = new Promise(function (resolve) {
   1.886 +      this.resolveFirstPage = resolve;
   1.887 +    }.bind(this));
   1.888 +    this.handleEvent = this.handleEvent.bind(this);
   1.889 +
   1.890 +    for (var i = 0; i < events.length; i++) {
   1.891 +      window.addEventListener(events[i], this.handleEvent);
   1.892 +    }
   1.893 +  },
   1.894 +
   1.895 +  reset: function pdfFindControllerReset() {
   1.896 +    this.startedTextExtraction = false;
   1.897 +    this.extractTextPromises = [];
   1.898 +    this.active = false;
   1.899 +  },
   1.900 +
   1.901 +  calcFindMatch: function(pageIndex) {
   1.902 +    var pageContent = this.pageContents[pageIndex];
   1.903 +    var query = this.state.query;
   1.904 +    var caseSensitive = this.state.caseSensitive;
   1.905 +    var queryLen = query.length;
   1.906 +
   1.907 +    if (queryLen === 0) {
   1.908 +      // Do nothing the matches should be wiped out already.
   1.909 +      return;
   1.910 +    }
   1.911 +
   1.912 +    if (!caseSensitive) {
   1.913 +      pageContent = pageContent.toLowerCase();
   1.914 +      query = query.toLowerCase();
   1.915 +    }
   1.916 +
   1.917 +    var matches = [];
   1.918 +
   1.919 +    var matchIdx = -queryLen;
   1.920 +    while (true) {
   1.921 +      matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
   1.922 +      if (matchIdx === -1) {
   1.923 +        break;
   1.924 +      }
   1.925 +
   1.926 +      matches.push(matchIdx);
   1.927 +    }
   1.928 +    this.pageMatches[pageIndex] = matches;
   1.929 +    this.updatePage(pageIndex);
   1.930 +    if (this.resumePageIdx === pageIndex) {
   1.931 +      this.resumePageIdx = null;
   1.932 +      this.nextPageMatch();
   1.933 +    }
   1.934 +  },
   1.935 +
   1.936 +  extractText: function() {
   1.937 +    if (this.startedTextExtraction) {
   1.938 +      return;
   1.939 +    }
   1.940 +    this.startedTextExtraction = true;
   1.941 +
   1.942 +    this.pageContents = [];
   1.943 +    var extractTextPromisesResolves = [];
   1.944 +    for (var i = 0, ii = this.pdfPageSource.pdfDocument.numPages; i < ii; i++) {
   1.945 +      this.extractTextPromises.push(new Promise(function (resolve) {
   1.946 +        extractTextPromisesResolves.push(resolve);
   1.947 +      }));
   1.948 +    }
   1.949 +
   1.950 +    var self = this;
   1.951 +    function extractPageText(pageIndex) {
   1.952 +      self.pdfPageSource.pages[pageIndex].getTextContent().then(
   1.953 +        function textContentResolved(textContent) {
   1.954 +          var textItems = textContent.items;
   1.955 +          var str = '';
   1.956 +
   1.957 +          for (var i = 0; i < textItems.length; i++) {
   1.958 +            str += textItems[i].str;
   1.959 +          }
   1.960 +
   1.961 +          // Store the pageContent as a string.
   1.962 +          self.pageContents.push(str);
   1.963 +
   1.964 +          extractTextPromisesResolves[pageIndex](pageIndex);
   1.965 +          if ((pageIndex + 1) < self.pdfPageSource.pages.length) {
   1.966 +            extractPageText(pageIndex + 1);
   1.967 +          }
   1.968 +        }
   1.969 +      );
   1.970 +    }
   1.971 +    extractPageText(0);
   1.972 +  },
   1.973 +
   1.974 +  handleEvent: function(e) {
   1.975 +    if (this.state === null || e.type !== 'findagain') {
   1.976 +      this.dirtyMatch = true;
   1.977 +    }
   1.978 +    this.state = e.detail;
   1.979 +    this.updateUIState(FindStates.FIND_PENDING);
   1.980 +
   1.981 +    this.firstPagePromise.then(function() {
   1.982 +      this.extractText();
   1.983 +
   1.984 +      clearTimeout(this.findTimeout);
   1.985 +      if (e.type === 'find') {
   1.986 +        // Only trigger the find action after 250ms of silence.
   1.987 +        this.findTimeout = setTimeout(this.nextMatch.bind(this), 250);
   1.988 +      } else {
   1.989 +        this.nextMatch();
   1.990 +      }
   1.991 +    }.bind(this));
   1.992 +  },
   1.993 +
   1.994 +  updatePage: function(idx) {
   1.995 +    var page = this.pdfPageSource.pages[idx];
   1.996 +
   1.997 +    if (this.selected.pageIdx === idx) {
   1.998 +      // If the page is selected, scroll the page into view, which triggers
   1.999 +      // rendering the page, which adds the textLayer. Once the textLayer is
  1.1000 +      // build, it will scroll onto the selected match.
  1.1001 +      page.scrollIntoView();
  1.1002 +    }
  1.1003 +
  1.1004 +    if (page.textLayer) {
  1.1005 +      page.textLayer.updateMatches();
  1.1006 +    }
  1.1007 +  },
  1.1008 +
  1.1009 +  nextMatch: function() {
  1.1010 +    var previous = this.state.findPrevious;
  1.1011 +    var currentPageIndex = this.pdfPageSource.page - 1;
  1.1012 +    var numPages = this.pdfPageSource.pages.length;
  1.1013 +
  1.1014 +    this.active = true;
  1.1015 +
  1.1016 +    if (this.dirtyMatch) {
  1.1017 +      // Need to recalculate the matches, reset everything.
  1.1018 +      this.dirtyMatch = false;
  1.1019 +      this.selected.pageIdx = this.selected.matchIdx = -1;
  1.1020 +      this.offset.pageIdx = currentPageIndex;
  1.1021 +      this.offset.matchIdx = null;
  1.1022 +      this.hadMatch = false;
  1.1023 +      this.resumePageIdx = null;
  1.1024 +      this.pageMatches = [];
  1.1025 +      var self = this;
  1.1026 +
  1.1027 +      for (var i = 0; i < numPages; i++) {
  1.1028 +        // Wipe out any previous highlighted matches.
  1.1029 +        this.updatePage(i);
  1.1030 +
  1.1031 +        // As soon as the text is extracted start finding the matches.
  1.1032 +        if (!(i in this.pendingFindMatches)) {
  1.1033 +          this.pendingFindMatches[i] = true;
  1.1034 +          this.extractTextPromises[i].then(function(pageIdx) {
  1.1035 +            delete self.pendingFindMatches[pageIdx];
  1.1036 +            self.calcFindMatch(pageIdx);
  1.1037 +          });
  1.1038 +        }
  1.1039 +      }
  1.1040 +    }
  1.1041 +
  1.1042 +    // If there's no query there's no point in searching.
  1.1043 +    if (this.state.query === '') {
  1.1044 +      this.updateUIState(FindStates.FIND_FOUND);
  1.1045 +      return;
  1.1046 +    }
  1.1047 +
  1.1048 +    // If we're waiting on a page, we return since we can't do anything else.
  1.1049 +    if (this.resumePageIdx) {
  1.1050 +      return;
  1.1051 +    }
  1.1052 +
  1.1053 +    var offset = this.offset;
  1.1054 +    // If there's already a matchIdx that means we are iterating through a
  1.1055 +    // page's matches.
  1.1056 +    if (offset.matchIdx !== null) {
  1.1057 +      var numPageMatches = this.pageMatches[offset.pageIdx].length;
  1.1058 +      if ((!previous && offset.matchIdx + 1 < numPageMatches) ||
  1.1059 +          (previous && offset.matchIdx > 0)) {
  1.1060 +        // The simple case, we just have advance the matchIdx to select the next
  1.1061 +        // match on the page.
  1.1062 +        this.hadMatch = true;
  1.1063 +        offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1;
  1.1064 +        this.updateMatch(true);
  1.1065 +        return;
  1.1066 +      }
  1.1067 +      // We went beyond the current page's matches, so we advance to the next
  1.1068 +      // page.
  1.1069 +      this.advanceOffsetPage(previous);
  1.1070 +    }
  1.1071 +    // Start searching through the page.
  1.1072 +    this.nextPageMatch();
  1.1073 +  },
  1.1074 +
  1.1075 +  matchesReady: function(matches) {
  1.1076 +    var offset = this.offset;
  1.1077 +    var numMatches = matches.length;
  1.1078 +    var previous = this.state.findPrevious;
  1.1079 +    if (numMatches) {
  1.1080 +      // There were matches for the page, so initialize the matchIdx.
  1.1081 +      this.hadMatch = true;
  1.1082 +      offset.matchIdx = previous ? numMatches - 1 : 0;
  1.1083 +      this.updateMatch(true);
  1.1084 +      // matches were found
  1.1085 +      return true;
  1.1086 +    } else {
  1.1087 +      // No matches attempt to search the next page.
  1.1088 +      this.advanceOffsetPage(previous);
  1.1089 +      if (offset.wrapped) {
  1.1090 +        offset.matchIdx = null;
  1.1091 +        if (!this.hadMatch) {
  1.1092 +          // No point in wrapping there were no matches.
  1.1093 +          this.updateMatch(false);
  1.1094 +          // while matches were not found, searching for a page 
  1.1095 +          // with matches should nevertheless halt.
  1.1096 +          return true;
  1.1097 +        }
  1.1098 +      }
  1.1099 +      // matches were not found (and searching is not done)
  1.1100 +      return false;
  1.1101 +    }
  1.1102 +  },
  1.1103 +
  1.1104 +  nextPageMatch: function() {
  1.1105 +    if (this.resumePageIdx !== null) {
  1.1106 +      console.error('There can only be one pending page.');
  1.1107 +    }
  1.1108 +    do {
  1.1109 +      var pageIdx = this.offset.pageIdx;
  1.1110 +      var matches = this.pageMatches[pageIdx];
  1.1111 +      if (!matches) {
  1.1112 +        // The matches don't exist yet for processing by "matchesReady",
  1.1113 +        // so set a resume point for when they do exist.
  1.1114 +        this.resumePageIdx = pageIdx;
  1.1115 +        break;
  1.1116 +      }
  1.1117 +    } while (!this.matchesReady(matches));
  1.1118 +  },
  1.1119 +
  1.1120 +  advanceOffsetPage: function(previous) {
  1.1121 +    var offset = this.offset;
  1.1122 +    var numPages = this.extractTextPromises.length;
  1.1123 +    offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1;
  1.1124 +    offset.matchIdx = null;
  1.1125 +    if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
  1.1126 +      offset.pageIdx = previous ? numPages - 1 : 0;
  1.1127 +      offset.wrapped = true;
  1.1128 +      return;
  1.1129 +    }
  1.1130 +  },
  1.1131 +
  1.1132 +  updateMatch: function(found) {
  1.1133 +    var state = FindStates.FIND_NOTFOUND;
  1.1134 +    var wrapped = this.offset.wrapped;
  1.1135 +    this.offset.wrapped = false;
  1.1136 +    if (found) {
  1.1137 +      var previousPage = this.selected.pageIdx;
  1.1138 +      this.selected.pageIdx = this.offset.pageIdx;
  1.1139 +      this.selected.matchIdx = this.offset.matchIdx;
  1.1140 +      state = wrapped ? FindStates.FIND_WRAPPED : FindStates.FIND_FOUND;
  1.1141 +      // Update the currently selected page to wipe out any selected matches.
  1.1142 +      if (previousPage !== -1 && previousPage !== this.selected.pageIdx) {
  1.1143 +        this.updatePage(previousPage);
  1.1144 +      }
  1.1145 +    }
  1.1146 +    this.updateUIState(state, this.state.findPrevious);
  1.1147 +    if (this.selected.pageIdx !== -1) {
  1.1148 +      this.updatePage(this.selected.pageIdx, true);
  1.1149 +    }
  1.1150 +  },
  1.1151 +
  1.1152 +  updateUIState: function(state, previous) {
  1.1153 +    if (this.integratedFind) {
  1.1154 +      FirefoxCom.request('updateFindControlState',
  1.1155 +                         {result: state, findPrevious: previous});
  1.1156 +      return;
  1.1157 +    }
  1.1158 +    PDFFindBar.updateUIState(state, previous);
  1.1159 +  }
  1.1160 +};
  1.1161 +
  1.1162 +
  1.1163 +
  1.1164 +var PDFHistory = {
  1.1165 +  initialized: false,
  1.1166 +  initialDestination: null,
  1.1167 +
  1.1168 +  initialize: function pdfHistoryInitialize(fingerprint) {
  1.1169 +    if (PDFJS.disableHistory || PDFView.isViewerEmbedded) {
  1.1170 +      // The browsing history is only enabled when the viewer is standalone,
  1.1171 +      // i.e. not when it is embedded in a web page.
  1.1172 +      return;
  1.1173 +    }
  1.1174 +    this.initialized = true;
  1.1175 +    this.reInitialized = false;
  1.1176 +    this.allowHashChange = true;
  1.1177 +    this.historyUnlocked = true;
  1.1178 +
  1.1179 +    this.previousHash = window.location.hash.substring(1);
  1.1180 +    this.currentBookmark = '';
  1.1181 +    this.currentPage = 0;
  1.1182 +    this.updatePreviousBookmark = false;
  1.1183 +    this.previousBookmark = '';
  1.1184 +    this.previousPage = 0;
  1.1185 +    this.nextHashParam = '';
  1.1186 +
  1.1187 +    this.fingerprint = fingerprint;
  1.1188 +    this.currentUid = this.uid = 0;
  1.1189 +    this.current = {};
  1.1190 +
  1.1191 +    var state = window.history.state;
  1.1192 +    if (this._isStateObjectDefined(state)) {
  1.1193 +      // This corresponds to navigating back to the document
  1.1194 +      // from another page in the browser history.
  1.1195 +      if (state.target.dest) {
  1.1196 +        this.initialDestination = state.target.dest;
  1.1197 +      } else {
  1.1198 +        PDFView.initialBookmark = state.target.hash;
  1.1199 +      }
  1.1200 +      this.currentUid = state.uid;
  1.1201 +      this.uid = state.uid + 1;
  1.1202 +      this.current = state.target;
  1.1203 +    } else {
  1.1204 +      // This corresponds to the loading of a new document.
  1.1205 +      if (state && state.fingerprint &&
  1.1206 +          this.fingerprint !== state.fingerprint) {
  1.1207 +        // Reinitialize the browsing history when a new document
  1.1208 +        // is opened in the web viewer.
  1.1209 +        this.reInitialized = true;
  1.1210 +      }
  1.1211 +      this._pushOrReplaceState({ fingerprint: this.fingerprint }, true);
  1.1212 +    }
  1.1213 +
  1.1214 +    var self = this;
  1.1215 +    window.addEventListener('popstate', function pdfHistoryPopstate(evt) {
  1.1216 +      evt.preventDefault();
  1.1217 +      evt.stopPropagation();
  1.1218 +
  1.1219 +      if (!self.historyUnlocked) {
  1.1220 +        return;
  1.1221 +      }
  1.1222 +      if (evt.state) {
  1.1223 +        // Move back/forward in the history.
  1.1224 +        self._goTo(evt.state);
  1.1225 +      } else {
  1.1226 +        // Handle the user modifying the hash of a loaded document.
  1.1227 +        self.previousHash = window.location.hash.substring(1);
  1.1228 +
  1.1229 +        // If the history is empty when the hash changes,
  1.1230 +        // update the previous entry in the browser history.
  1.1231 +        if (self.uid === 0) {
  1.1232 +          var previousParams = (self.previousHash && self.currentBookmark &&
  1.1233 +                                self.previousHash !== self.currentBookmark) ?
  1.1234 +            { hash: self.currentBookmark, page: self.currentPage } :
  1.1235 +            { page: 1 };
  1.1236 +          self.historyUnlocked = false;
  1.1237 +          self.allowHashChange = false;
  1.1238 +          window.history.back();
  1.1239 +          self._pushToHistory(previousParams, false, true);
  1.1240 +          window.history.forward();
  1.1241 +          self.historyUnlocked = true;
  1.1242 +        }
  1.1243 +        self._pushToHistory({ hash: self.previousHash }, false, true);
  1.1244 +        self._updatePreviousBookmark();
  1.1245 +      }
  1.1246 +    }, false);
  1.1247 +
  1.1248 +    function pdfHistoryBeforeUnload() {
  1.1249 +      var previousParams = self._getPreviousParams(null, true);
  1.1250 +      if (previousParams) {
  1.1251 +        var replacePrevious = (!self.current.dest &&
  1.1252 +                               self.current.hash !== self.previousHash);
  1.1253 +        self._pushToHistory(previousParams, false, replacePrevious);
  1.1254 +        self._updatePreviousBookmark();
  1.1255 +      }
  1.1256 +      // Remove the event listener when navigating away from the document,
  1.1257 +      // since 'beforeunload' prevents Firefox from caching the document.
  1.1258 +      window.removeEventListener('beforeunload', pdfHistoryBeforeUnload, false);
  1.1259 +    }
  1.1260 +    window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false);
  1.1261 +
  1.1262 +    window.addEventListener('pageshow', function pdfHistoryPageShow(evt) {
  1.1263 +      // If the entire viewer (including the PDF file) is cached in the browser,
  1.1264 +      // we need to reattach the 'beforeunload' event listener since
  1.1265 +      // the 'DOMContentLoaded' event is not fired on 'pageshow'.
  1.1266 +      window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false);
  1.1267 +    }, false);
  1.1268 +  },
  1.1269 +
  1.1270 +  _isStateObjectDefined: function pdfHistory_isStateObjectDefined(state) {
  1.1271 +    return (state && state.uid >= 0 &&
  1.1272 +            state.fingerprint && this.fingerprint === state.fingerprint &&
  1.1273 +            state.target && state.target.hash) ? true : false;
  1.1274 +  },
  1.1275 +
  1.1276 +  _pushOrReplaceState: function pdfHistory_pushOrReplaceState(stateObj,
  1.1277 +                                                              replace) {
  1.1278 +    if (replace) {
  1.1279 +      window.history.replaceState(stateObj, '');
  1.1280 +    } else {
  1.1281 +      window.history.pushState(stateObj, '');
  1.1282 +    }
  1.1283 +  },
  1.1284 +
  1.1285 +  get isHashChangeUnlocked() {
  1.1286 +    if (!this.initialized) {
  1.1287 +      return true;
  1.1288 +    }
  1.1289 +    // If the current hash changes when moving back/forward in the history,
  1.1290 +    // this will trigger a 'popstate' event *as well* as a 'hashchange' event.
  1.1291 +    // Since the hash generally won't correspond to the exact the position
  1.1292 +    // stored in the history's state object, triggering the 'hashchange' event
  1.1293 +    // can thus corrupt the browser history.
  1.1294 +    //
  1.1295 +    // When the hash changes during a 'popstate' event, we *only* prevent the
  1.1296 +    // first 'hashchange' event and immediately reset allowHashChange.
  1.1297 +    // If it is not reset, the user would not be able to change the hash.
  1.1298 +
  1.1299 +    var temp = this.allowHashChange;
  1.1300 +    this.allowHashChange = true;
  1.1301 +    return temp;
  1.1302 +  },
  1.1303 +
  1.1304 +  _updatePreviousBookmark: function pdfHistory_updatePreviousBookmark() {
  1.1305 +    if (this.updatePreviousBookmark &&
  1.1306 +        this.currentBookmark && this.currentPage) {
  1.1307 +      this.previousBookmark = this.currentBookmark;
  1.1308 +      this.previousPage = this.currentPage;
  1.1309 +      this.updatePreviousBookmark = false;
  1.1310 +    }
  1.1311 +  },
  1.1312 +
  1.1313 +  updateCurrentBookmark: function pdfHistoryUpdateCurrentBookmark(bookmark,
  1.1314 +                                                                  pageNum) {
  1.1315 +    if (this.initialized) {
  1.1316 +      this.currentBookmark = bookmark.substring(1);
  1.1317 +      this.currentPage = pageNum | 0;
  1.1318 +      this._updatePreviousBookmark();
  1.1319 +    }
  1.1320 +  },
  1.1321 +
  1.1322 +  updateNextHashParam: function pdfHistoryUpdateNextHashParam(param) {
  1.1323 +    if (this.initialized) {
  1.1324 +      this.nextHashParam = param;
  1.1325 +    }
  1.1326 +  },
  1.1327 +
  1.1328 +  push: function pdfHistoryPush(params, isInitialBookmark) {
  1.1329 +    if (!(this.initialized && this.historyUnlocked)) {
  1.1330 +      return;
  1.1331 +    }
  1.1332 +    if (params.dest && !params.hash) {
  1.1333 +      params.hash = (this.current.hash && this.current.dest &&
  1.1334 +                     this.current.dest === params.dest) ?
  1.1335 +        this.current.hash :
  1.1336 +        PDFView.getDestinationHash(params.dest).split('#')[1];
  1.1337 +    }
  1.1338 +    if (params.page) {
  1.1339 +      params.page |= 0;
  1.1340 +    }
  1.1341 +    if (isInitialBookmark) {
  1.1342 +      var target = window.history.state.target;
  1.1343 +      if (!target) {
  1.1344 +        // Invoked when the user specifies an initial bookmark,
  1.1345 +        // thus setting PDFView.initialBookmark, when the document is loaded.
  1.1346 +        this._pushToHistory(params, false);
  1.1347 +        this.previousHash = window.location.hash.substring(1);
  1.1348 +      }
  1.1349 +      this.updatePreviousBookmark = this.nextHashParam ? false : true;
  1.1350 +      if (target) {
  1.1351 +        // If the current document is reloaded,
  1.1352 +        // avoid creating duplicate entries in the history.
  1.1353 +        this._updatePreviousBookmark();
  1.1354 +      }
  1.1355 +      return;
  1.1356 +    }
  1.1357 +    if (this.nextHashParam) {
  1.1358 +      if (this.nextHashParam === params.hash) {
  1.1359 +        this.nextHashParam = null;
  1.1360 +        this.updatePreviousBookmark = true;
  1.1361 +        return;
  1.1362 +      } else {
  1.1363 +        this.nextHashParam = null;
  1.1364 +      }
  1.1365 +    }
  1.1366 +
  1.1367 +    if (params.hash) {
  1.1368 +      if (this.current.hash) {
  1.1369 +        if (this.current.hash !== params.hash) {
  1.1370 +          this._pushToHistory(params, true);
  1.1371 +        } else {
  1.1372 +          if (!this.current.page && params.page) {
  1.1373 +            this._pushToHistory(params, false, true);
  1.1374 +          }
  1.1375 +          this.updatePreviousBookmark = true;
  1.1376 +        }
  1.1377 +      } else {
  1.1378 +        this._pushToHistory(params, true);
  1.1379 +      }
  1.1380 +    } else if (this.current.page && params.page &&
  1.1381 +               this.current.page !== params.page) {
  1.1382 +      this._pushToHistory(params, true);
  1.1383 +    }
  1.1384 +  },
  1.1385 +
  1.1386 +  _getPreviousParams: function pdfHistory_getPreviousParams(onlyCheckPage,
  1.1387 +                                                            beforeUnload) {
  1.1388 +    if (!(this.currentBookmark && this.currentPage)) {
  1.1389 +      return null;
  1.1390 +    } else if (this.updatePreviousBookmark) {
  1.1391 +      this.updatePreviousBookmark = false;
  1.1392 +    }
  1.1393 +    if (this.uid > 0 && !(this.previousBookmark && this.previousPage)) {
  1.1394 +      // Prevent the history from getting stuck in the current state,
  1.1395 +      // effectively preventing the user from going back/forward in the history.
  1.1396 +      //
  1.1397 +      // This happens if the current position in the document didn't change when
  1.1398 +      // the history was previously updated. The reasons for this are either:
  1.1399 +      // 1. The current zoom value is such that the document does not need to,
  1.1400 +      //    or cannot, be scrolled to display the destination.
  1.1401 +      // 2. The previous destination is broken, and doesn't actally point to a
  1.1402 +      //    position within the document.
  1.1403 +      //    (This is either due to a bad PDF generator, or the user making a
  1.1404 +      //     mistake when entering a destination in the hash parameters.)
  1.1405 +      return null;
  1.1406 +    }
  1.1407 +    if ((!this.current.dest && !onlyCheckPage) || beforeUnload) {
  1.1408 +      if (this.previousBookmark === this.currentBookmark) {
  1.1409 +        return null;
  1.1410 +      }
  1.1411 +    } else if (this.current.page || onlyCheckPage) {
  1.1412 +      if (this.previousPage === this.currentPage) {
  1.1413 +        return null;
  1.1414 +      }
  1.1415 +    } else {
  1.1416 +      return null;
  1.1417 +    }
  1.1418 +    var params = { hash: this.currentBookmark, page: this.currentPage };
  1.1419 +    if (PresentationMode.active) {
  1.1420 +      params.hash = null;
  1.1421 +    }
  1.1422 +    return params;
  1.1423 +  },
  1.1424 +
  1.1425 +  _stateObj: function pdfHistory_stateObj(params) {
  1.1426 +    return { fingerprint: this.fingerprint, uid: this.uid, target: params };
  1.1427 +  },
  1.1428 +
  1.1429 +  _pushToHistory: function pdfHistory_pushToHistory(params,
  1.1430 +                                                    addPrevious, overwrite) {
  1.1431 +    if (!this.initialized) {
  1.1432 +      return;
  1.1433 +    }
  1.1434 +    if (!params.hash && params.page) {
  1.1435 +      params.hash = ('page=' + params.page);
  1.1436 +    }
  1.1437 +    if (addPrevious && !overwrite) {
  1.1438 +      var previousParams = this._getPreviousParams();
  1.1439 +      if (previousParams) {
  1.1440 +        var replacePrevious = (!this.current.dest &&
  1.1441 +                               this.current.hash !== this.previousHash);
  1.1442 +        this._pushToHistory(previousParams, false, replacePrevious);
  1.1443 +      }
  1.1444 +    }
  1.1445 +    this._pushOrReplaceState(this._stateObj(params),
  1.1446 +                             (overwrite || this.uid === 0));
  1.1447 +    this.currentUid = this.uid++;
  1.1448 +    this.current = params;
  1.1449 +    this.updatePreviousBookmark = true;
  1.1450 +  },
  1.1451 +
  1.1452 +  _goTo: function pdfHistory_goTo(state) {
  1.1453 +    if (!(this.initialized && this.historyUnlocked &&
  1.1454 +          this._isStateObjectDefined(state))) {
  1.1455 +      return;
  1.1456 +    }
  1.1457 +    if (!this.reInitialized && state.uid < this.currentUid) {
  1.1458 +      var previousParams = this._getPreviousParams(true);
  1.1459 +      if (previousParams) {
  1.1460 +        this._pushToHistory(this.current, false);
  1.1461 +        this._pushToHistory(previousParams, false);
  1.1462 +        this.currentUid = state.uid;
  1.1463 +        window.history.back();
  1.1464 +        return;
  1.1465 +      }
  1.1466 +    }
  1.1467 +    this.historyUnlocked = false;
  1.1468 +
  1.1469 +    if (state.target.dest) {
  1.1470 +      PDFView.navigateTo(state.target.dest);
  1.1471 +    } else {
  1.1472 +      PDFView.setHash(state.target.hash);
  1.1473 +    }
  1.1474 +    this.currentUid = state.uid;
  1.1475 +    if (state.uid > this.uid) {
  1.1476 +      this.uid = state.uid;
  1.1477 +    }
  1.1478 +    this.current = state.target;
  1.1479 +    this.updatePreviousBookmark = true;
  1.1480 +
  1.1481 +    var currentHash = window.location.hash.substring(1);
  1.1482 +    if (this.previousHash !== currentHash) {
  1.1483 +      this.allowHashChange = false;
  1.1484 +    }
  1.1485 +    this.previousHash = currentHash;
  1.1486 +
  1.1487 +    this.historyUnlocked = true;
  1.1488 +  },
  1.1489 +
  1.1490 +  back: function pdfHistoryBack() {
  1.1491 +    this.go(-1);
  1.1492 +  },
  1.1493 +
  1.1494 +  forward: function pdfHistoryForward() {
  1.1495 +    this.go(1);
  1.1496 +  },
  1.1497 +
  1.1498 +  go: function pdfHistoryGo(direction) {
  1.1499 +    if (this.initialized && this.historyUnlocked) {
  1.1500 +      var state = window.history.state;
  1.1501 +      if (direction === -1 && state && state.uid > 0) {
  1.1502 +        window.history.back();
  1.1503 +      } else if (direction === 1 && state && state.uid < (this.uid - 1)) {
  1.1504 +        window.history.forward();
  1.1505 +      }
  1.1506 +    }
  1.1507 +  }
  1.1508 +};
  1.1509 +
  1.1510 +
  1.1511 +var SecondaryToolbar = {
  1.1512 +  opened: false,
  1.1513 +  previousContainerHeight: null,
  1.1514 +  newContainerHeight: null,
  1.1515 +
  1.1516 +  initialize: function secondaryToolbarInitialize(options) {
  1.1517 +    this.toolbar = options.toolbar;
  1.1518 +    this.presentationMode = options.presentationMode;
  1.1519 +    this.documentProperties = options.documentProperties;
  1.1520 +    this.buttonContainer = this.toolbar.firstElementChild;
  1.1521 +
  1.1522 +    // Define the toolbar buttons.
  1.1523 +    this.toggleButton = options.toggleButton;
  1.1524 +    this.presentationModeButton = options.presentationModeButton;
  1.1525 +    this.openFile = options.openFile;
  1.1526 +    this.print = options.print;
  1.1527 +    this.download = options.download;
  1.1528 +    this.viewBookmark = options.viewBookmark;
  1.1529 +    this.firstPage = options.firstPage;
  1.1530 +    this.lastPage = options.lastPage;
  1.1531 +    this.pageRotateCw = options.pageRotateCw;
  1.1532 +    this.pageRotateCcw = options.pageRotateCcw;
  1.1533 +    this.documentPropertiesButton = options.documentPropertiesButton;
  1.1534 +
  1.1535 +    // Attach the event listeners.
  1.1536 +    var elements = [
  1.1537 +      // Button to toggle the visibility of the secondary toolbar:
  1.1538 +      { element: this.toggleButton, handler: this.toggle },
  1.1539 +      // All items within the secondary toolbar
  1.1540 +      // (except for toggleHandTool, hand_tool.js is responsible for it):
  1.1541 +      { element: this.presentationModeButton,
  1.1542 +        handler: this.presentationModeClick },
  1.1543 +      { element: this.openFile, handler: this.openFileClick },
  1.1544 +      { element: this.print, handler: this.printClick },
  1.1545 +      { element: this.download, handler: this.downloadClick },
  1.1546 +      { element: this.viewBookmark, handler: this.viewBookmarkClick },
  1.1547 +      { element: this.firstPage, handler: this.firstPageClick },
  1.1548 +      { element: this.lastPage, handler: this.lastPageClick },
  1.1549 +      { element: this.pageRotateCw, handler: this.pageRotateCwClick },
  1.1550 +      { element: this.pageRotateCcw, handler: this.pageRotateCcwClick },
  1.1551 +      { element: this.documentPropertiesButton,
  1.1552 +        handler: this.documentPropertiesClick }
  1.1553 +    ];
  1.1554 +
  1.1555 +    for (var item in elements) {
  1.1556 +      var element = elements[item].element;
  1.1557 +      if (element) {
  1.1558 +        element.addEventListener('click', elements[item].handler.bind(this));
  1.1559 +      }
  1.1560 +    }
  1.1561 +  },
  1.1562 +
  1.1563 +  // Event handling functions.
  1.1564 +  presentationModeClick: function secondaryToolbarPresentationModeClick(evt) {
  1.1565 +    this.presentationMode.request();
  1.1566 +    this.close();
  1.1567 +  },
  1.1568 +
  1.1569 +  openFileClick: function secondaryToolbarOpenFileClick(evt) {
  1.1570 +    document.getElementById('fileInput').click();
  1.1571 +    this.close();
  1.1572 +  },
  1.1573 +
  1.1574 +  printClick: function secondaryToolbarPrintClick(evt) {
  1.1575 +    window.print();
  1.1576 +    this.close();
  1.1577 +  },
  1.1578 +
  1.1579 +  downloadClick: function secondaryToolbarDownloadClick(evt) {
  1.1580 +    PDFView.download();
  1.1581 +    this.close();
  1.1582 +  },
  1.1583 +
  1.1584 +  viewBookmarkClick: function secondaryToolbarViewBookmarkClick(evt) {
  1.1585 +    this.close();
  1.1586 +  },
  1.1587 +
  1.1588 +  firstPageClick: function secondaryToolbarFirstPageClick(evt) {
  1.1589 +    PDFView.page = 1;
  1.1590 +    this.close();
  1.1591 +  },
  1.1592 +
  1.1593 +  lastPageClick: function secondaryToolbarLastPageClick(evt) {
  1.1594 +    PDFView.page = PDFView.pdfDocument.numPages;
  1.1595 +    this.close();
  1.1596 +  },
  1.1597 +
  1.1598 +  pageRotateCwClick: function secondaryToolbarPageRotateCwClick(evt) {
  1.1599 +    PDFView.rotatePages(90);
  1.1600 +  },
  1.1601 +
  1.1602 +  pageRotateCcwClick: function secondaryToolbarPageRotateCcwClick(evt) {
  1.1603 +    PDFView.rotatePages(-90);
  1.1604 +  },
  1.1605 +
  1.1606 +  documentPropertiesClick: function secondaryToolbarDocumentPropsClick(evt) {
  1.1607 +    this.documentProperties.show();
  1.1608 +    this.close();
  1.1609 +  },
  1.1610 +
  1.1611 +  // Misc. functions for interacting with the toolbar.
  1.1612 +  setMaxHeight: function secondaryToolbarSetMaxHeight(container) {
  1.1613 +    if (!container || !this.buttonContainer) {
  1.1614 +      return;
  1.1615 +    }
  1.1616 +    this.newContainerHeight = container.clientHeight;
  1.1617 +    if (this.previousContainerHeight === this.newContainerHeight) {
  1.1618 +      return;
  1.1619 +    }
  1.1620 +    this.buttonContainer.setAttribute('style',
  1.1621 +      'max-height: ' + (this.newContainerHeight - SCROLLBAR_PADDING) + 'px;');
  1.1622 +    this.previousContainerHeight = this.newContainerHeight;
  1.1623 +  },
  1.1624 +
  1.1625 +  open: function secondaryToolbarOpen() {
  1.1626 +    if (this.opened) {
  1.1627 +      return;
  1.1628 +    }
  1.1629 +    this.opened = true;
  1.1630 +    this.toggleButton.classList.add('toggled');
  1.1631 +    this.toolbar.classList.remove('hidden');
  1.1632 +  },
  1.1633 +
  1.1634 +  close: function secondaryToolbarClose(target) {
  1.1635 +    if (!this.opened) {
  1.1636 +      return;
  1.1637 +    } else if (target && !this.toolbar.contains(target)) {
  1.1638 +      return;
  1.1639 +    }
  1.1640 +    this.opened = false;
  1.1641 +    this.toolbar.classList.add('hidden');
  1.1642 +    this.toggleButton.classList.remove('toggled');
  1.1643 +  },
  1.1644 +
  1.1645 +  toggle: function secondaryToolbarToggle() {
  1.1646 +    if (this.opened) {
  1.1647 +      this.close();
  1.1648 +    } else {
  1.1649 +      this.open();
  1.1650 +    }
  1.1651 +  }
  1.1652 +};
  1.1653 +
  1.1654 +
  1.1655 +var PasswordPrompt = {
  1.1656 +  visible: false,
  1.1657 +  updatePassword: null,
  1.1658 +  reason: null,
  1.1659 +  overlayContainer: null,
  1.1660 +  passwordField: null,
  1.1661 +  passwordText: null,
  1.1662 +  passwordSubmit: null,
  1.1663 +  passwordCancel: null,
  1.1664 +
  1.1665 +  initialize: function secondaryToolbarInitialize(options) {
  1.1666 +    this.overlayContainer = options.overlayContainer;
  1.1667 +    this.passwordField = options.passwordField;
  1.1668 +    this.passwordText = options.passwordText;
  1.1669 +    this.passwordSubmit = options.passwordSubmit;
  1.1670 +    this.passwordCancel = options.passwordCancel;
  1.1671 +
  1.1672 +    // Attach the event listeners.
  1.1673 +    this.passwordSubmit.addEventListener('click',
  1.1674 +      this.verifyPassword.bind(this));
  1.1675 +
  1.1676 +    this.passwordCancel.addEventListener('click', this.hide.bind(this));
  1.1677 +
  1.1678 +    this.passwordField.addEventListener('keydown',
  1.1679 +      function (e) {
  1.1680 +        if (e.keyCode === 13) { // Enter key
  1.1681 +          this.verifyPassword();
  1.1682 +        }
  1.1683 +      }.bind(this));
  1.1684 +
  1.1685 +    window.addEventListener('keydown',
  1.1686 +      function (e) {
  1.1687 +        if (e.keyCode === 27) { // Esc key
  1.1688 +          this.hide();
  1.1689 +        }
  1.1690 +      }.bind(this));
  1.1691 +  },
  1.1692 +
  1.1693 +  show: function passwordPromptShow() {
  1.1694 +    if (this.visible) {
  1.1695 +      return;
  1.1696 +    }
  1.1697 +    this.visible = true;
  1.1698 +    this.overlayContainer.classList.remove('hidden');
  1.1699 +    this.overlayContainer.firstElementChild.classList.remove('hidden');
  1.1700 +    this.passwordField.focus();
  1.1701 +
  1.1702 +    var promptString = mozL10n.get('password_label', null,
  1.1703 +      'Enter the password to open this PDF file.');
  1.1704 +
  1.1705 +    if (this.reason === PDFJS.PasswordResponses.INCORRECT_PASSWORD) {
  1.1706 +      promptString = mozL10n.get('password_invalid', null,
  1.1707 +        'Invalid password. Please try again.');
  1.1708 +    }
  1.1709 +
  1.1710 +    this.passwordText.textContent = promptString;
  1.1711 +  },
  1.1712 +
  1.1713 +  hide: function passwordPromptClose() {
  1.1714 +    if (!this.visible) {
  1.1715 +      return;
  1.1716 +    }
  1.1717 +    this.visible = false;
  1.1718 +    this.passwordField.value = '';
  1.1719 +    this.overlayContainer.classList.add('hidden');
  1.1720 +    this.overlayContainer.firstElementChild.classList.add('hidden');
  1.1721 +  },
  1.1722 +
  1.1723 +  verifyPassword: function passwordPromptVerifyPassword() {
  1.1724 +    var password = this.passwordField.value;
  1.1725 +    if (password && password.length > 0) {
  1.1726 +      this.hide();
  1.1727 +      return this.updatePassword(password);
  1.1728 +    }
  1.1729 +  }
  1.1730 +};
  1.1731 +
  1.1732 +
  1.1733 +var DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms
  1.1734 +var SELECTOR = 'presentationControls';
  1.1735 +var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1000; // in ms
  1.1736 +
  1.1737 +var PresentationMode = {
  1.1738 +  active: false,
  1.1739 +  args: null,
  1.1740 +  contextMenuOpen: false,
  1.1741 +
  1.1742 +  initialize: function presentationModeInitialize(options) {
  1.1743 +    this.container = options.container;
  1.1744 +    this.secondaryToolbar = options.secondaryToolbar;
  1.1745 +
  1.1746 +    this.viewer = this.container.firstElementChild;
  1.1747 +
  1.1748 +    this.firstPage = options.firstPage;
  1.1749 +    this.lastPage = options.lastPage;
  1.1750 +    this.pageRotateCw = options.pageRotateCw;
  1.1751 +    this.pageRotateCcw = options.pageRotateCcw;
  1.1752 +
  1.1753 +    this.firstPage.addEventListener('click', function() {
  1.1754 +      this.contextMenuOpen = false;
  1.1755 +      this.secondaryToolbar.firstPageClick();
  1.1756 +    }.bind(this));
  1.1757 +    this.lastPage.addEventListener('click', function() {
  1.1758 +      this.contextMenuOpen = false;
  1.1759 +      this.secondaryToolbar.lastPageClick();
  1.1760 +    }.bind(this));
  1.1761 +
  1.1762 +    this.pageRotateCw.addEventListener('click', function() {
  1.1763 +      this.contextMenuOpen = false;
  1.1764 +      this.secondaryToolbar.pageRotateCwClick();
  1.1765 +    }.bind(this));
  1.1766 +    this.pageRotateCcw.addEventListener('click', function() {
  1.1767 +      this.contextMenuOpen = false;
  1.1768 +      this.secondaryToolbar.pageRotateCcwClick();
  1.1769 +    }.bind(this));
  1.1770 +  },
  1.1771 +
  1.1772 +  get isFullscreen() {
  1.1773 +    return (document.fullscreenElement ||
  1.1774 +            document.mozFullScreen ||
  1.1775 +            document.webkitIsFullScreen ||
  1.1776 +            document.msFullscreenElement);
  1.1777 +  },
  1.1778 +
  1.1779 +  /**
  1.1780 +   * Initialize a timeout that is used to reset PDFView.currentPosition when the
  1.1781 +   * browser transitions to fullscreen mode. Since resize events are triggered
  1.1782 +   * multiple times during the switch to fullscreen mode, this is necessary in
  1.1783 +   * order to prevent the page from being scrolled partially, or completely,
  1.1784 +   * out of view when Presentation Mode is enabled.
  1.1785 +   * Note: This is only an issue at certain zoom levels, e.g. 'page-width'.
  1.1786 +   */
  1.1787 +  _setSwitchInProgress: function presentationMode_setSwitchInProgress() {
  1.1788 +    if (this.switchInProgress) {
  1.1789 +      clearTimeout(this.switchInProgress);
  1.1790 +    }
  1.1791 +    this.switchInProgress = setTimeout(function switchInProgressTimeout() {
  1.1792 +      delete this.switchInProgress;
  1.1793 +    }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS);
  1.1794 +
  1.1795 +    PDFView.currentPosition = null;
  1.1796 +  },
  1.1797 +
  1.1798 +  _resetSwitchInProgress: function presentationMode_resetSwitchInProgress() {
  1.1799 +    if (this.switchInProgress) {
  1.1800 +      clearTimeout(this.switchInProgress);
  1.1801 +      delete this.switchInProgress;
  1.1802 +    }
  1.1803 +  },
  1.1804 +
  1.1805 +  request: function presentationModeRequest() {
  1.1806 +    if (!PDFView.supportsFullscreen || this.isFullscreen ||
  1.1807 +        !this.viewer.hasChildNodes()) {
  1.1808 +      return false;
  1.1809 +    }
  1.1810 +    this._setSwitchInProgress();
  1.1811 +
  1.1812 +    if (this.container.requestFullscreen) {
  1.1813 +      this.container.requestFullscreen();
  1.1814 +    } else if (this.container.mozRequestFullScreen) {
  1.1815 +      this.container.mozRequestFullScreen();
  1.1816 +    } else if (this.container.webkitRequestFullScreen) {
  1.1817 +      this.container.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
  1.1818 +    } else if (this.container.msRequestFullscreen) {
  1.1819 +      this.container.msRequestFullscreen();
  1.1820 +    } else {
  1.1821 +      return false;
  1.1822 +    }
  1.1823 +
  1.1824 +    this.args = {
  1.1825 +      page: PDFView.page,
  1.1826 +      previousScale: PDFView.currentScaleValue
  1.1827 +    };
  1.1828 +
  1.1829 +    return true;
  1.1830 +  },
  1.1831 +
  1.1832 +  enter: function presentationModeEnter() {
  1.1833 +    this.active = true;
  1.1834 +    this._resetSwitchInProgress();
  1.1835 +
  1.1836 +    // Ensure that the correct page is scrolled into view when entering
  1.1837 +    // Presentation Mode, by waiting until fullscreen mode in enabled.
  1.1838 +    // Note: This is only necessary in non-Mozilla browsers.
  1.1839 +    setTimeout(function enterPresentationModeTimeout() {
  1.1840 +      PDFView.page = this.args.page;
  1.1841 +      PDFView.setScale('page-fit', true);
  1.1842 +    }.bind(this), 0);
  1.1843 +
  1.1844 +    window.addEventListener('mousemove', this.mouseMove, false);
  1.1845 +    window.addEventListener('mousedown', this.mouseDown, false);
  1.1846 +    window.addEventListener('contextmenu', this.contextMenu, false);
  1.1847 +
  1.1848 +    this.showControls();
  1.1849 +    HandTool.enterPresentationMode();
  1.1850 +    this.contextMenuOpen = false;
  1.1851 +    this.container.setAttribute('contextmenu', 'viewerContextMenu');
  1.1852 +  },
  1.1853 +
  1.1854 +  exit: function presentationModeExit() {
  1.1855 +    var page = PDFView.page;
  1.1856 +
  1.1857 +    // Ensure that the correct page is scrolled into view when exiting
  1.1858 +    // Presentation Mode, by waiting until fullscreen mode is disabled.
  1.1859 +    // Note: This is only necessary in non-Mozilla browsers.
  1.1860 +    setTimeout(function exitPresentationModeTimeout() {
  1.1861 +      this.active = false;
  1.1862 +      PDFView.setScale(this.args.previousScale);
  1.1863 +      PDFView.page = page;
  1.1864 +      this.args = null;
  1.1865 +    }.bind(this), 0);
  1.1866 +
  1.1867 +    window.removeEventListener('mousemove', this.mouseMove, false);
  1.1868 +    window.removeEventListener('mousedown', this.mouseDown, false);
  1.1869 +    window.removeEventListener('contextmenu', this.contextMenu, false);
  1.1870 +
  1.1871 +    this.hideControls();
  1.1872 +    PDFView.clearMouseScrollState();
  1.1873 +    HandTool.exitPresentationMode();
  1.1874 +    this.container.removeAttribute('contextmenu');
  1.1875 +    this.contextMenuOpen = false;
  1.1876 +
  1.1877 +    // Ensure that the thumbnail of the current page is visible
  1.1878 +    // when exiting presentation mode.
  1.1879 +    scrollIntoView(document.getElementById('thumbnailContainer' + page));
  1.1880 +  },
  1.1881 +
  1.1882 +  showControls: function presentationModeShowControls() {
  1.1883 +    if (this.controlsTimeout) {
  1.1884 +      clearTimeout(this.controlsTimeout);
  1.1885 +    } else {
  1.1886 +      this.container.classList.add(SELECTOR);
  1.1887 +    }
  1.1888 +    this.controlsTimeout = setTimeout(function hideControlsTimeout() {
  1.1889 +      this.container.classList.remove(SELECTOR);
  1.1890 +      delete this.controlsTimeout;
  1.1891 +    }.bind(this), DELAY_BEFORE_HIDING_CONTROLS);
  1.1892 +  },
  1.1893 +
  1.1894 +  hideControls: function presentationModeHideControls() {
  1.1895 +    if (!this.controlsTimeout) {
  1.1896 +      return;
  1.1897 +    }
  1.1898 +    this.container.classList.remove(SELECTOR);
  1.1899 +    clearTimeout(this.controlsTimeout);
  1.1900 +    delete this.controlsTimeout;
  1.1901 +  },
  1.1902 +
  1.1903 +  mouseMove: function presentationModeMouseMove(evt) {
  1.1904 +    PresentationMode.showControls();
  1.1905 +  },
  1.1906 +
  1.1907 +  mouseDown: function presentationModeMouseDown(evt) {
  1.1908 +    var self = PresentationMode;
  1.1909 +    if (self.contextMenuOpen) {
  1.1910 +      self.contextMenuOpen = false;
  1.1911 +      evt.preventDefault();
  1.1912 +      return;
  1.1913 +    }
  1.1914 +
  1.1915 +    if (evt.button === 0) {
  1.1916 +      // Enable clicking of links in presentation mode. Please note:
  1.1917 +      // Only links pointing to destinations in the current PDF document work.
  1.1918 +      var isInternalLink = (evt.target.href &&
  1.1919 +                            evt.target.classList.contains('internalLink'));
  1.1920 +      if (!isInternalLink) {
  1.1921 +        // Unless an internal link was clicked, advance one page.
  1.1922 +        evt.preventDefault();
  1.1923 +        PDFView.page += (evt.shiftKey ? -1 : 1);
  1.1924 +      }
  1.1925 +    }
  1.1926 +  },
  1.1927 +
  1.1928 +  contextMenu: function presentationModeContextMenu(evt) {
  1.1929 +    PresentationMode.contextMenuOpen = true;
  1.1930 +  }
  1.1931 +};
  1.1932 +
  1.1933 +(function presentationModeClosure() {
  1.1934 +  function presentationModeChange(e) {
  1.1935 +    if (PresentationMode.isFullscreen) {
  1.1936 +      PresentationMode.enter();
  1.1937 +    } else {
  1.1938 +      PresentationMode.exit();
  1.1939 +    }
  1.1940 +  }
  1.1941 +
  1.1942 +  window.addEventListener('fullscreenchange', presentationModeChange, false);
  1.1943 +  window.addEventListener('mozfullscreenchange', presentationModeChange, false);
  1.1944 +  window.addEventListener('webkitfullscreenchange', presentationModeChange,
  1.1945 +                          false);
  1.1946 +  window.addEventListener('MSFullscreenChange', presentationModeChange, false);
  1.1947 +})();
  1.1948 +
  1.1949 +
  1.1950 +/* Copyright 2013 Rob Wu <gwnRob@gmail.com>
  1.1951 + * https://github.com/Rob--W/grab-to-pan.js
  1.1952 + *
  1.1953 + * Licensed under the Apache License, Version 2.0 (the "License");
  1.1954 + * you may not use this file except in compliance with the License.
  1.1955 + * You may obtain a copy of the License at
  1.1956 + *
  1.1957 + *     http://www.apache.org/licenses/LICENSE-2.0
  1.1958 + *
  1.1959 + * Unless required by applicable law or agreed to in writing, software
  1.1960 + * distributed under the License is distributed on an "AS IS" BASIS,
  1.1961 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1.1962 + * See the License for the specific language governing permissions and
  1.1963 + * limitations under the License.
  1.1964 + */
  1.1965 +
  1.1966 +'use strict';
  1.1967 +
  1.1968 +var GrabToPan = (function GrabToPanClosure() {
  1.1969 +  /**
  1.1970 +   * Construct a GrabToPan instance for a given HTML element.
  1.1971 +   * @param options.element {Element}
  1.1972 +   * @param options.ignoreTarget {function} optional. See `ignoreTarget(node)`
  1.1973 +   * @param options.onActiveChanged {function(boolean)} optional. Called
  1.1974 +   *  when grab-to-pan is (de)activated. The first argument is a boolean that
  1.1975 +   *  shows whether grab-to-pan is activated.
  1.1976 +   */
  1.1977 +  function GrabToPan(options) {
  1.1978 +    this.element = options.element;
  1.1979 +    this.document = options.element.ownerDocument;
  1.1980 +    if (typeof options.ignoreTarget === 'function') {
  1.1981 +      this.ignoreTarget = options.ignoreTarget;
  1.1982 +    }
  1.1983 +    this.onActiveChanged = options.onActiveChanged;
  1.1984 +
  1.1985 +    // Bind the contexts to ensure that `this` always points to
  1.1986 +    // the GrabToPan instance.
  1.1987 +    this.activate = this.activate.bind(this);
  1.1988 +    this.deactivate = this.deactivate.bind(this);
  1.1989 +    this.toggle = this.toggle.bind(this);
  1.1990 +    this._onmousedown = this._onmousedown.bind(this);
  1.1991 +    this._onmousemove = this._onmousemove.bind(this);
  1.1992 +    this._endPan = this._endPan.bind(this);
  1.1993 +
  1.1994 +    // This overlay will be inserted in the document when the mouse moves during
  1.1995 +    // a grab operation, to ensure that the cursor has the desired appearance.
  1.1996 +    var overlay = this.overlay = document.createElement('div');
  1.1997 +    overlay.className = 'grab-to-pan-grabbing';
  1.1998 +  }
  1.1999 +  GrabToPan.prototype = {
  1.2000 +    /**
  1.2001 +     * Class name of element which can be grabbed
  1.2002 +     */
  1.2003 +    CSS_CLASS_GRAB: 'grab-to-pan-grab',
  1.2004 +
  1.2005 +    /**
  1.2006 +     * Bind a mousedown event to the element to enable grab-detection.
  1.2007 +     */
  1.2008 +    activate: function GrabToPan_activate() {
  1.2009 +      if (!this.active) {
  1.2010 +        this.active = true;
  1.2011 +        this.element.addEventListener('mousedown', this._onmousedown, true);
  1.2012 +        this.element.classList.add(this.CSS_CLASS_GRAB);
  1.2013 +        if (this.onActiveChanged) {
  1.2014 +          this.onActiveChanged(true);
  1.2015 +        }
  1.2016 +      }
  1.2017 +    },
  1.2018 +
  1.2019 +    /**
  1.2020 +     * Removes all events. Any pending pan session is immediately stopped.
  1.2021 +     */
  1.2022 +    deactivate: function GrabToPan_deactivate() {
  1.2023 +      if (this.active) {
  1.2024 +        this.active = false;
  1.2025 +        this.element.removeEventListener('mousedown', this._onmousedown, true);
  1.2026 +        this._endPan();
  1.2027 +        this.element.classList.remove(this.CSS_CLASS_GRAB);
  1.2028 +        if (this.onActiveChanged) {
  1.2029 +          this.onActiveChanged(false);
  1.2030 +        }
  1.2031 +      }
  1.2032 +    },
  1.2033 +
  1.2034 +    toggle: function GrabToPan_toggle() {
  1.2035 +      if (this.active) {
  1.2036 +        this.deactivate();
  1.2037 +      } else {
  1.2038 +        this.activate();
  1.2039 +      }
  1.2040 +    },
  1.2041 +
  1.2042 +    /**
  1.2043 +     * Whether to not pan if the target element is clicked.
  1.2044 +     * Override this method to change the default behaviour.
  1.2045 +     *
  1.2046 +     * @param node {Element} The target of the event
  1.2047 +     * @return {boolean} Whether to not react to the click event.
  1.2048 +     */
  1.2049 +    ignoreTarget: function GrabToPan_ignoreTarget(node) {
  1.2050 +      // Use matchesSelector to check whether the clicked element
  1.2051 +      // is (a child of) an input element / link
  1.2052 +      return node[matchesSelector](
  1.2053 +        'a[href], a[href] *, input, textarea, button, button *, select, option'
  1.2054 +      );
  1.2055 +    },
  1.2056 +
  1.2057 +    /**
  1.2058 +     * @private
  1.2059 +     */
  1.2060 +    _onmousedown: function GrabToPan__onmousedown(event) {
  1.2061 +      if (event.button !== 0 || this.ignoreTarget(event.target)) {
  1.2062 +        return;
  1.2063 +      }
  1.2064 +      if (event.originalTarget) {
  1.2065 +        try {
  1.2066 +          /* jshint expr:true */
  1.2067 +          event.originalTarget.tagName;
  1.2068 +        } catch (e) {
  1.2069 +          // Mozilla-specific: element is a scrollbar (XUL element)
  1.2070 +          return;
  1.2071 +        }
  1.2072 +      }
  1.2073 +
  1.2074 +      this.scrollLeftStart = this.element.scrollLeft;
  1.2075 +      this.scrollTopStart = this.element.scrollTop;
  1.2076 +      this.clientXStart = event.clientX;
  1.2077 +      this.clientYStart = event.clientY;
  1.2078 +      this.document.addEventListener('mousemove', this._onmousemove, true);
  1.2079 +      this.document.addEventListener('mouseup', this._endPan, true);
  1.2080 +      // When a scroll event occurs before a mousemove, assume that the user
  1.2081 +      // dragged a scrollbar (necessary for Opera Presto, Safari and IE)
  1.2082 +      // (not needed for Chrome/Firefox)
  1.2083 +      this.element.addEventListener('scroll', this._endPan, true);
  1.2084 +      event.preventDefault();
  1.2085 +      event.stopPropagation();
  1.2086 +      this.document.documentElement.classList.add(this.CSS_CLASS_GRABBING);
  1.2087 +    },
  1.2088 +
  1.2089 +    /**
  1.2090 +     * @private
  1.2091 +     */
  1.2092 +    _onmousemove: function GrabToPan__onmousemove(event) {
  1.2093 +      this.element.removeEventListener('scroll', this._endPan, true);
  1.2094 +      if (isLeftMouseReleased(event)) {
  1.2095 +        this._endPan();
  1.2096 +        return;
  1.2097 +      }
  1.2098 +      var xDiff = event.clientX - this.clientXStart;
  1.2099 +      var yDiff = event.clientY - this.clientYStart;
  1.2100 +      this.element.scrollTop = this.scrollTopStart - yDiff;
  1.2101 +      this.element.scrollLeft = this.scrollLeftStart - xDiff;
  1.2102 +      if (!this.overlay.parentNode) {
  1.2103 +        document.body.appendChild(this.overlay);
  1.2104 +      }
  1.2105 +    },
  1.2106 +
  1.2107 +    /**
  1.2108 +     * @private
  1.2109 +     */
  1.2110 +    _endPan: function GrabToPan__endPan() {
  1.2111 +      this.element.removeEventListener('scroll', this._endPan, true);
  1.2112 +      this.document.removeEventListener('mousemove', this._onmousemove, true);
  1.2113 +      this.document.removeEventListener('mouseup', this._endPan, true);
  1.2114 +      if (this.overlay.parentNode) {
  1.2115 +        this.overlay.parentNode.removeChild(this.overlay);
  1.2116 +      }
  1.2117 +    }
  1.2118 +  };
  1.2119 +
  1.2120 +  // Get the correct (vendor-prefixed) name of the matches method.
  1.2121 +  var matchesSelector;
  1.2122 +  ['webkitM', 'mozM', 'msM', 'oM', 'm'].some(function(prefix) {
  1.2123 +    var name = prefix + 'atches';
  1.2124 +    if (name in document.documentElement) {
  1.2125 +      matchesSelector = name;
  1.2126 +    }
  1.2127 +    name += 'Selector';
  1.2128 +    if (name in document.documentElement) {
  1.2129 +      matchesSelector = name;
  1.2130 +    }
  1.2131 +    return matchesSelector; // If found, then truthy, and [].some() ends.
  1.2132 +  });
  1.2133 +
  1.2134 +  // Browser sniffing because it's impossible to feature-detect
  1.2135 +  // whether event.which for onmousemove is reliable
  1.2136 +  var isNotIEorIsIE10plus = !document.documentMode || document.documentMode > 9;
  1.2137 +  var chrome = window.chrome;
  1.2138 +  var isChrome15OrOpera15plus = chrome && (chrome.webstore || chrome.app);
  1.2139 +  //                                       ^ Chrome 15+       ^ Opera 15+
  1.2140 +  var isSafari6plus = /Apple/.test(navigator.vendor) &&
  1.2141 +                      /Version\/([6-9]\d*|[1-5]\d+)/.test(navigator.userAgent);
  1.2142 +
  1.2143 +  /**
  1.2144 +   * Whether the left mouse is not pressed.
  1.2145 +   * @param event {MouseEvent}
  1.2146 +   * @return {boolean} True if the left mouse button is not pressed.
  1.2147 +   *                   False if unsure or if the left mouse button is pressed.
  1.2148 +   */
  1.2149 +  function isLeftMouseReleased(event) {
  1.2150 +    if ('buttons' in event && isNotIEorIsIE10plus) {
  1.2151 +      // http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-buttons
  1.2152 +      // Firefox 15+
  1.2153 +      // Internet Explorer 10+
  1.2154 +      return !(event.buttons | 1);
  1.2155 +    }
  1.2156 +    if (isChrome15OrOpera15plus || isSafari6plus) {
  1.2157 +      // Chrome 14+
  1.2158 +      // Opera 15+
  1.2159 +      // Safari 6.0+
  1.2160 +      return event.which === 0;
  1.2161 +    }
  1.2162 +  }
  1.2163 +
  1.2164 +  return GrabToPan;
  1.2165 +})();
  1.2166 +
  1.2167 +var HandTool = {
  1.2168 +  initialize: function handToolInitialize(options) {
  1.2169 +    var toggleHandTool = options.toggleHandTool;
  1.2170 +    this.handTool = new GrabToPan({
  1.2171 +      element: options.container,
  1.2172 +      onActiveChanged: function(isActive) {
  1.2173 +        if (!toggleHandTool) {
  1.2174 +          return;
  1.2175 +        }
  1.2176 +        if (isActive) {
  1.2177 +          toggleHandTool.title =
  1.2178 +            mozL10n.get('hand_tool_disable.title', null, 'Disable hand tool');
  1.2179 +          toggleHandTool.firstElementChild.textContent =
  1.2180 +            mozL10n.get('hand_tool_disable_label', null, 'Disable hand tool');
  1.2181 +        } else {
  1.2182 +          toggleHandTool.title =
  1.2183 +            mozL10n.get('hand_tool_enable.title', null, 'Enable hand tool');
  1.2184 +          toggleHandTool.firstElementChild.textContent =
  1.2185 +            mozL10n.get('hand_tool_enable_label', null, 'Enable hand tool');
  1.2186 +        }
  1.2187 +      }
  1.2188 +    });
  1.2189 +    if (toggleHandTool) {
  1.2190 +      toggleHandTool.addEventListener('click', this.toggle.bind(this), false);
  1.2191 +
  1.2192 +      window.addEventListener('localized', function (evt) {
  1.2193 +        Preferences.get('enableHandToolOnLoad').then(function (prefValue) {
  1.2194 +          if (prefValue) {
  1.2195 +            this.handTool.activate();
  1.2196 +          }
  1.2197 +        }.bind(this));
  1.2198 +      }.bind(this));
  1.2199 +    }
  1.2200 +  },
  1.2201 +
  1.2202 +  toggle: function handToolToggle() {
  1.2203 +    this.handTool.toggle();
  1.2204 +    SecondaryToolbar.close();
  1.2205 +  },
  1.2206 +
  1.2207 +  enterPresentationMode: function handToolEnterPresentationMode() {
  1.2208 +    if (this.handTool.active) {
  1.2209 +      this.wasActive = true;
  1.2210 +      this.handTool.deactivate();
  1.2211 +    }
  1.2212 +  },
  1.2213 +
  1.2214 +  exitPresentationMode: function handToolExitPresentationMode() {
  1.2215 +    if (this.wasActive) {
  1.2216 +      this.wasActive = null;
  1.2217 +      this.handTool.activate();
  1.2218 +    }
  1.2219 +  }
  1.2220 +};
  1.2221 +
  1.2222 +
  1.2223 +var DocumentProperties = {
  1.2224 +  overlayContainer: null,
  1.2225 +  fileName: '',
  1.2226 +  fileSize: '',
  1.2227 +  visible: false,
  1.2228 +
  1.2229 +  // Document property fields (in the viewer).
  1.2230 +  fileNameField: null,
  1.2231 +  fileSizeField: null,
  1.2232 +  titleField: null,
  1.2233 +  authorField: null,
  1.2234 +  subjectField: null,
  1.2235 +  keywordsField: null,
  1.2236 +  creationDateField: null,
  1.2237 +  modificationDateField: null,
  1.2238 +  creatorField: null,
  1.2239 +  producerField: null,
  1.2240 +  versionField: null,
  1.2241 +  pageCountField: null,
  1.2242 +
  1.2243 +  initialize: function documentPropertiesInitialize(options) {
  1.2244 +    this.overlayContainer = options.overlayContainer;
  1.2245 +
  1.2246 +    // Set the document property fields.
  1.2247 +    this.fileNameField = options.fileNameField;
  1.2248 +    this.fileSizeField = options.fileSizeField;
  1.2249 +    this.titleField = options.titleField;
  1.2250 +    this.authorField = options.authorField;
  1.2251 +    this.subjectField = options.subjectField;
  1.2252 +    this.keywordsField = options.keywordsField;
  1.2253 +    this.creationDateField = options.creationDateField;
  1.2254 +    this.modificationDateField = options.modificationDateField;
  1.2255 +    this.creatorField = options.creatorField;
  1.2256 +    this.producerField = options.producerField;
  1.2257 +    this.versionField = options.versionField;
  1.2258 +    this.pageCountField = options.pageCountField;
  1.2259 +
  1.2260 +    // Bind the event listener for the Close button.
  1.2261 +    if (options.closeButton) {
  1.2262 +      options.closeButton.addEventListener('click', this.hide.bind(this));
  1.2263 +    }
  1.2264 +
  1.2265 +    this.dataAvailablePromise = new Promise(function (resolve) {
  1.2266 +      this.resolveDataAvailable = resolve;
  1.2267 +    }.bind(this));
  1.2268 +
  1.2269 +    // Bind the event listener for the Esc key (to close the dialog).
  1.2270 +    window.addEventListener('keydown',
  1.2271 +      function (e) {
  1.2272 +        if (e.keyCode === 27) { // Esc key
  1.2273 +          this.hide();
  1.2274 +        }
  1.2275 +      }.bind(this));
  1.2276 +  },
  1.2277 +
  1.2278 +  getProperties: function documentPropertiesGetProperties() {
  1.2279 +    if (!this.visible) {
  1.2280 +      // If the dialog was closed before dataAvailablePromise was resolved,
  1.2281 +      // don't bother updating the properties.
  1.2282 +      return;
  1.2283 +    }
  1.2284 +    // Get the file name.
  1.2285 +    this.fileName = getPDFFileNameFromURL(PDFView.url);
  1.2286 +
  1.2287 +    // Get the file size.
  1.2288 +    PDFView.pdfDocument.getDownloadInfo().then(function(data) {
  1.2289 +      this.setFileSize(data.length);
  1.2290 +      this.updateUI(this.fileSizeField, this.fileSize);
  1.2291 +    }.bind(this));
  1.2292 +
  1.2293 +    // Get the other document properties.
  1.2294 +    PDFView.pdfDocument.getMetadata().then(function(data) {
  1.2295 +      var fields = [
  1.2296 +        { field: this.fileNameField, content: this.fileName },
  1.2297 +        // The fileSize field is updated once getDownloadInfo is resolved.
  1.2298 +        { field: this.titleField, content: data.info.Title },
  1.2299 +        { field: this.authorField, content: data.info.Author },
  1.2300 +        { field: this.subjectField, content: data.info.Subject },
  1.2301 +        { field: this.keywordsField, content: data.info.Keywords },
  1.2302 +        { field: this.creationDateField,
  1.2303 +          content: this.parseDate(data.info.CreationDate) },
  1.2304 +        { field: this.modificationDateField,
  1.2305 +          content: this.parseDate(data.info.ModDate) },
  1.2306 +        { field: this.creatorField, content: data.info.Creator },
  1.2307 +        { field: this.producerField, content: data.info.Producer },
  1.2308 +        { field: this.versionField, content: data.info.PDFFormatVersion },
  1.2309 +        { field: this.pageCountField, content: PDFView.pdfDocument.numPages }
  1.2310 +      ];
  1.2311 +
  1.2312 +      // Show the properties in the dialog.
  1.2313 +      for (var item in fields) {
  1.2314 +        var element = fields[item];
  1.2315 +        this.updateUI(element.field, element.content);
  1.2316 +      }
  1.2317 +    }.bind(this));
  1.2318 +  },
  1.2319 +
  1.2320 +  updateUI: function documentPropertiesUpdateUI(field, content) {
  1.2321 +    if (field && content !== undefined && content !== '') {
  1.2322 +      field.textContent = content;
  1.2323 +    }
  1.2324 +  },
  1.2325 +
  1.2326 +  setFileSize: function documentPropertiesSetFileSize(fileSize) {
  1.2327 +    var kb = fileSize / 1024;
  1.2328 +    if (kb < 1024) {
  1.2329 +      this.fileSize = mozL10n.get('document_properties_kb', {
  1.2330 +        size_kb: (+kb.toPrecision(3)).toLocaleString(),
  1.2331 +        size_b: fileSize.toLocaleString()
  1.2332 +      }, '{{size_kb}} KB ({{size_b}} bytes)');
  1.2333 +    } else {
  1.2334 +      this.fileSize = mozL10n.get('document_properties_mb', {
  1.2335 +        size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(),
  1.2336 +        size_b: fileSize.toLocaleString()
  1.2337 +      }, '{{size_mb}} MB ({{size_b}} bytes)');
  1.2338 +    }
  1.2339 +  },
  1.2340 +
  1.2341 +  show: function documentPropertiesShow() {
  1.2342 +    if (this.visible) {
  1.2343 +      return;
  1.2344 +    }
  1.2345 +    this.visible = true;
  1.2346 +    this.overlayContainer.classList.remove('hidden');
  1.2347 +    this.overlayContainer.lastElementChild.classList.remove('hidden');
  1.2348 +
  1.2349 +    this.dataAvailablePromise.then(function () {
  1.2350 +      this.getProperties();
  1.2351 +    }.bind(this));
  1.2352 +  },
  1.2353 +
  1.2354 +  hide: function documentPropertiesClose() {
  1.2355 +    if (!this.visible) {
  1.2356 +      return;
  1.2357 +    }
  1.2358 +    this.visible = false;
  1.2359 +    this.overlayContainer.classList.add('hidden');
  1.2360 +    this.overlayContainer.lastElementChild.classList.add('hidden');
  1.2361 +  },
  1.2362 +
  1.2363 +  parseDate: function documentPropertiesParseDate(inputDate) {
  1.2364 +    // This is implemented according to the PDF specification (see
  1.2365 +    // http://www.gnupdf.org/Date for an overview), but note that 
  1.2366 +    // Adobe Reader doesn't handle changing the date to universal time
  1.2367 +    // and doesn't use the user's time zone (they're effectively ignoring
  1.2368 +    // the HH' and mm' parts of the date string).
  1.2369 +    var dateToParse = inputDate;
  1.2370 +    if (dateToParse === undefined) {
  1.2371 +      return '';
  1.2372 +    }
  1.2373 +
  1.2374 +    // Remove the D: prefix if it is available.
  1.2375 +    if (dateToParse.substring(0,2) === 'D:') {
  1.2376 +      dateToParse = dateToParse.substring(2);
  1.2377 +    }
  1.2378 +
  1.2379 +    // Get all elements from the PDF date string.
  1.2380 +    // JavaScript's Date object expects the month to be between
  1.2381 +    // 0 and 11 instead of 1 and 12, so we're correcting for this.
  1.2382 +    var year = parseInt(dateToParse.substring(0,4), 10);
  1.2383 +    var month = parseInt(dateToParse.substring(4,6), 10) - 1;
  1.2384 +    var day = parseInt(dateToParse.substring(6,8), 10);
  1.2385 +    var hours = parseInt(dateToParse.substring(8,10), 10);
  1.2386 +    var minutes = parseInt(dateToParse.substring(10,12), 10);
  1.2387 +    var seconds = parseInt(dateToParse.substring(12,14), 10);
  1.2388 +    var utRel = dateToParse.substring(14,15);
  1.2389 +    var offsetHours = parseInt(dateToParse.substring(15,17), 10);
  1.2390 +    var offsetMinutes = parseInt(dateToParse.substring(18,20), 10);
  1.2391 +
  1.2392 +    // As per spec, utRel = 'Z' means equal to universal time.
  1.2393 +    // The other cases ('-' and '+') have to be handled here.
  1.2394 +    if (utRel == '-') {
  1.2395 +      hours += offsetHours;
  1.2396 +      minutes += offsetMinutes;
  1.2397 +    } else if (utRel == '+') {
  1.2398 +      hours -= offsetHours;
  1.2399 +      minutes += offsetMinutes;
  1.2400 +    }
  1.2401 +
  1.2402 +    // Return the new date format from the user's locale.
  1.2403 +    var date = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
  1.2404 +    var dateString = date.toLocaleDateString();
  1.2405 +    var timeString = date.toLocaleTimeString();
  1.2406 +    return mozL10n.get('document_properties_date_string',
  1.2407 +                       {date: dateString, time: timeString},
  1.2408 +                       '{{date}}, {{time}}');
  1.2409 +  }
  1.2410 +};
  1.2411 +
  1.2412 +
  1.2413 +var PDFView = {
  1.2414 +  pages: [],
  1.2415 +  thumbnails: [],
  1.2416 +  currentScale: UNKNOWN_SCALE,
  1.2417 +  currentScaleValue: null,
  1.2418 +  initialBookmark: document.location.hash.substring(1),
  1.2419 +  container: null,
  1.2420 +  thumbnailContainer: null,
  1.2421 +  initialized: false,
  1.2422 +  fellback: false,
  1.2423 +  pdfDocument: null,
  1.2424 +  sidebarOpen: false,
  1.2425 +  pageViewScroll: null,
  1.2426 +  thumbnailViewScroll: null,
  1.2427 +  pageRotation: 0,
  1.2428 +  mouseScrollTimeStamp: 0,
  1.2429 +  mouseScrollDelta: 0,
  1.2430 +  lastScroll: 0,
  1.2431 +  previousPageNumber: 1,
  1.2432 +  isViewerEmbedded: (window.parent !== window),
  1.2433 +  idleTimeout: null,
  1.2434 +  currentPosition: null,
  1.2435 +
  1.2436 +  // called once when the document is loaded
  1.2437 +  initialize: function pdfViewInitialize() {
  1.2438 +    var self = this;
  1.2439 +    var container = this.container = document.getElementById('viewerContainer');
  1.2440 +    this.pageViewScroll = {};
  1.2441 +    this.watchScroll(container, this.pageViewScroll, updateViewarea);
  1.2442 +
  1.2443 +    var thumbnailContainer = this.thumbnailContainer =
  1.2444 +                             document.getElementById('thumbnailView');
  1.2445 +    this.thumbnailViewScroll = {};
  1.2446 +    this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
  1.2447 +                     this.renderHighestPriority.bind(this));
  1.2448 +
  1.2449 +    Preferences.initialize();
  1.2450 +
  1.2451 +    PDFFindBar.initialize({
  1.2452 +      bar: document.getElementById('findbar'),
  1.2453 +      toggleButton: document.getElementById('viewFind'),
  1.2454 +      findField: document.getElementById('findInput'),
  1.2455 +      highlightAllCheckbox: document.getElementById('findHighlightAll'),
  1.2456 +      caseSensitiveCheckbox: document.getElementById('findMatchCase'),
  1.2457 +      findMsg: document.getElementById('findMsg'),
  1.2458 +      findStatusIcon: document.getElementById('findStatusIcon'),
  1.2459 +      findPreviousButton: document.getElementById('findPrevious'),
  1.2460 +      findNextButton: document.getElementById('findNext')
  1.2461 +    });
  1.2462 +
  1.2463 +    PDFFindController.initialize({
  1.2464 +      pdfPageSource: this,
  1.2465 +      integratedFind: this.supportsIntegratedFind
  1.2466 +    });
  1.2467 +
  1.2468 +    HandTool.initialize({
  1.2469 +      container: container,
  1.2470 +      toggleHandTool: document.getElementById('toggleHandTool')
  1.2471 +    });
  1.2472 +
  1.2473 +    SecondaryToolbar.initialize({
  1.2474 +      toolbar: document.getElementById('secondaryToolbar'),
  1.2475 +      presentationMode: PresentationMode,
  1.2476 +      toggleButton: document.getElementById('secondaryToolbarToggle'),
  1.2477 +      presentationModeButton:
  1.2478 +        document.getElementById('secondaryPresentationMode'),
  1.2479 +      openFile: document.getElementById('secondaryOpenFile'),
  1.2480 +      print: document.getElementById('secondaryPrint'),
  1.2481 +      download: document.getElementById('secondaryDownload'),
  1.2482 +      viewBookmark: document.getElementById('secondaryViewBookmark'),
  1.2483 +      firstPage: document.getElementById('firstPage'),
  1.2484 +      lastPage: document.getElementById('lastPage'),
  1.2485 +      pageRotateCw: document.getElementById('pageRotateCw'),
  1.2486 +      pageRotateCcw: document.getElementById('pageRotateCcw'),
  1.2487 +      documentProperties: DocumentProperties,
  1.2488 +      documentPropertiesButton: document.getElementById('documentProperties')
  1.2489 +    });
  1.2490 +
  1.2491 +    PasswordPrompt.initialize({
  1.2492 +      overlayContainer: document.getElementById('overlayContainer'),
  1.2493 +      passwordField: document.getElementById('password'),
  1.2494 +      passwordText: document.getElementById('passwordText'),
  1.2495 +      passwordSubmit: document.getElementById('passwordSubmit'),
  1.2496 +      passwordCancel: document.getElementById('passwordCancel')
  1.2497 +    });
  1.2498 +
  1.2499 +    PresentationMode.initialize({
  1.2500 +      container: container,
  1.2501 +      secondaryToolbar: SecondaryToolbar,
  1.2502 +      firstPage: document.getElementById('contextFirstPage'),
  1.2503 +      lastPage: document.getElementById('contextLastPage'),
  1.2504 +      pageRotateCw: document.getElementById('contextPageRotateCw'),
  1.2505 +      pageRotateCcw: document.getElementById('contextPageRotateCcw')
  1.2506 +    });
  1.2507 +
  1.2508 +    DocumentProperties.initialize({
  1.2509 +      overlayContainer: document.getElementById('overlayContainer'),
  1.2510 +      closeButton: document.getElementById('documentPropertiesClose'),
  1.2511 +      fileNameField: document.getElementById('fileNameField'),
  1.2512 +      fileSizeField: document.getElementById('fileSizeField'),
  1.2513 +      titleField: document.getElementById('titleField'),
  1.2514 +      authorField: document.getElementById('authorField'),
  1.2515 +      subjectField: document.getElementById('subjectField'),
  1.2516 +      keywordsField: document.getElementById('keywordsField'),
  1.2517 +      creationDateField: document.getElementById('creationDateField'),
  1.2518 +      modificationDateField: document.getElementById('modificationDateField'),
  1.2519 +      creatorField: document.getElementById('creatorField'),
  1.2520 +      producerField: document.getElementById('producerField'),
  1.2521 +      versionField: document.getElementById('versionField'),
  1.2522 +      pageCountField: document.getElementById('pageCountField')
  1.2523 +    });
  1.2524 +
  1.2525 +    container.addEventListener('scroll', function() {
  1.2526 +      self.lastScroll = Date.now();
  1.2527 +    }, false);
  1.2528 +
  1.2529 +    var initializedPromise = Promise.all([
  1.2530 +      Preferences.get('enableWebGL').then(function (value) {
  1.2531 +        PDFJS.disableWebGL = !value;
  1.2532 +      })
  1.2533 +      // TODO move more preferences and other async stuff here
  1.2534 +    ]);
  1.2535 +
  1.2536 +    return initializedPromise.then(function () {
  1.2537 +      PDFView.initialized = true;
  1.2538 +    });
  1.2539 +  },
  1.2540 +
  1.2541 +  getPage: function pdfViewGetPage(n) {
  1.2542 +    return this.pdfDocument.getPage(n);
  1.2543 +  },
  1.2544 +
  1.2545 +  // Helper function to keep track whether a div was scrolled up or down and
  1.2546 +  // then call a callback.
  1.2547 +  watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) {
  1.2548 +    state.down = true;
  1.2549 +    state.lastY = viewAreaElement.scrollTop;
  1.2550 +    viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) {
  1.2551 +      var currentY = viewAreaElement.scrollTop;
  1.2552 +      var lastY = state.lastY;
  1.2553 +      if (currentY > lastY) {
  1.2554 +        state.down = true;
  1.2555 +      } else if (currentY < lastY) {
  1.2556 +        state.down = false;
  1.2557 +      }
  1.2558 +      // else do nothing and use previous value
  1.2559 +      state.lastY = currentY;
  1.2560 +      callback();
  1.2561 +    }, true);
  1.2562 +  },
  1.2563 +
  1.2564 +  _setScaleUpdatePages: function pdfView_setScaleUpdatePages(
  1.2565 +      newScale, newValue, resetAutoSettings, noScroll) {
  1.2566 +    this.currentScaleValue = newValue;
  1.2567 +    if (newScale === this.currentScale) {
  1.2568 +      return;
  1.2569 +    }
  1.2570 +    for (var i = 0, ii = this.pages.length; i < ii; i++) {
  1.2571 +      this.pages[i].update(newScale);
  1.2572 +    }
  1.2573 +    this.currentScale = newScale;
  1.2574 +
  1.2575 +    if (!noScroll) {
  1.2576 +      var page = this.page, dest;
  1.2577 +      if (this.currentPosition && !IGNORE_CURRENT_POSITION_ON_ZOOM) {
  1.2578 +        page = this.currentPosition.page;
  1.2579 +        dest = [null, { name: 'XYZ' }, this.currentPosition.left,
  1.2580 +                this.currentPosition.top, null];
  1.2581 +      }
  1.2582 +      this.pages[page - 1].scrollIntoView(dest);
  1.2583 +    }
  1.2584 +    var event = document.createEvent('UIEvents');
  1.2585 +    event.initUIEvent('scalechange', false, false, window, 0);
  1.2586 +    event.scale = newScale;
  1.2587 +    event.resetAutoSettings = resetAutoSettings;
  1.2588 +    window.dispatchEvent(event);
  1.2589 +  },
  1.2590 +
  1.2591 +  setScale: function pdfViewSetScale(value, resetAutoSettings, noScroll) {
  1.2592 +    if (value === 'custom') {
  1.2593 +      return;
  1.2594 +    }
  1.2595 +    var scale = parseFloat(value);
  1.2596 +
  1.2597 +    if (scale > 0) {
  1.2598 +      this._setScaleUpdatePages(scale, value, true, noScroll);
  1.2599 +    } else {
  1.2600 +      var currentPage = this.pages[this.page - 1];
  1.2601 +      if (!currentPage) {
  1.2602 +        return;
  1.2603 +      }
  1.2604 +      var hPadding = PresentationMode.active ? 0 : SCROLLBAR_PADDING;
  1.2605 +      var vPadding = PresentationMode.active ? 0 : VERTICAL_PADDING;
  1.2606 +      var pageWidthScale = (this.container.clientWidth - hPadding) /
  1.2607 +                            currentPage.width * currentPage.scale;
  1.2608 +      var pageHeightScale = (this.container.clientHeight - vPadding) /
  1.2609 +                             currentPage.height * currentPage.scale;
  1.2610 +      switch (value) {
  1.2611 +        case 'page-actual':
  1.2612 +          scale = 1;
  1.2613 +          break;
  1.2614 +        case 'page-width':
  1.2615 +          scale = pageWidthScale;
  1.2616 +          break;
  1.2617 +        case 'page-height':
  1.2618 +          scale = pageHeightScale;
  1.2619 +          break;
  1.2620 +        case 'page-fit':
  1.2621 +          scale = Math.min(pageWidthScale, pageHeightScale);
  1.2622 +          break;
  1.2623 +        case 'auto':
  1.2624 +          scale = Math.min(MAX_AUTO_SCALE, pageWidthScale);
  1.2625 +          break;
  1.2626 +        default:
  1.2627 +          console.error('pdfViewSetScale: \'' + value +
  1.2628 +                        '\' is an unknown zoom value.');
  1.2629 +          return;
  1.2630 +      }
  1.2631 +      this._setScaleUpdatePages(scale, value, resetAutoSettings, noScroll);
  1.2632 +
  1.2633 +      selectScaleOption(value);
  1.2634 +    }
  1.2635 +  },
  1.2636 +
  1.2637 +  zoomIn: function pdfViewZoomIn(ticks) {
  1.2638 +    var newScale = this.currentScale;
  1.2639 +    do {
  1.2640 +      newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
  1.2641 +      newScale = Math.ceil(newScale * 10) / 10;
  1.2642 +      newScale = Math.min(MAX_SCALE, newScale);
  1.2643 +    } while (--ticks && newScale < MAX_SCALE);
  1.2644 +    this.setScale(newScale, true);
  1.2645 +  },
  1.2646 +
  1.2647 +  zoomOut: function pdfViewZoomOut(ticks) {
  1.2648 +    var newScale = this.currentScale;
  1.2649 +    do {
  1.2650 +      newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
  1.2651 +      newScale = Math.floor(newScale * 10) / 10;
  1.2652 +      newScale = Math.max(MIN_SCALE, newScale);
  1.2653 +    } while (--ticks && newScale > MIN_SCALE);
  1.2654 +    this.setScale(newScale, true);
  1.2655 +  },
  1.2656 +
  1.2657 +  set page(val) {
  1.2658 +    var pages = this.pages;
  1.2659 +    var event = document.createEvent('UIEvents');
  1.2660 +    event.initUIEvent('pagechange', false, false, window, 0);
  1.2661 +
  1.2662 +    if (!(0 < val && val <= pages.length)) {
  1.2663 +      this.previousPageNumber = val;
  1.2664 +      event.pageNumber = this.page;
  1.2665 +      window.dispatchEvent(event);
  1.2666 +      return;
  1.2667 +    }
  1.2668 +
  1.2669 +    pages[val - 1].updateStats();
  1.2670 +    this.previousPageNumber = currentPageNumber;
  1.2671 +    currentPageNumber = val;
  1.2672 +    event.pageNumber = val;
  1.2673 +    window.dispatchEvent(event);
  1.2674 +
  1.2675 +    // checking if the this.page was called from the updateViewarea function:
  1.2676 +    // avoiding the creation of two "set page" method (internal and public)
  1.2677 +    if (updateViewarea.inProgress) {
  1.2678 +      return;
  1.2679 +    }
  1.2680 +    // Avoid scrolling the first page during loading
  1.2681 +    if (this.loading && val === 1) {
  1.2682 +      return;
  1.2683 +    }
  1.2684 +    pages[val - 1].scrollIntoView();
  1.2685 +  },
  1.2686 +
  1.2687 +  get page() {
  1.2688 +    return currentPageNumber;
  1.2689 +  },
  1.2690 +
  1.2691 +  get supportsPrinting() {
  1.2692 +    var canvas = document.createElement('canvas');
  1.2693 +    var value = 'mozPrintCallback' in canvas;
  1.2694 +    // shadow
  1.2695 +    Object.defineProperty(this, 'supportsPrinting', { value: value,
  1.2696 +                                                      enumerable: true,
  1.2697 +                                                      configurable: true,
  1.2698 +                                                      writable: false });
  1.2699 +    return value;
  1.2700 +  },
  1.2701 +
  1.2702 +  get supportsFullscreen() {
  1.2703 +    var doc = document.documentElement;
  1.2704 +    var support = doc.requestFullscreen || doc.mozRequestFullScreen ||
  1.2705 +                  doc.webkitRequestFullScreen || doc.msRequestFullscreen;
  1.2706 +
  1.2707 +    if (document.fullscreenEnabled === false ||
  1.2708 +        document.mozFullScreenEnabled === false ||
  1.2709 +        document.webkitFullscreenEnabled === false ||
  1.2710 +        document.msFullscreenEnabled === false) {
  1.2711 +      support = false;
  1.2712 +    }
  1.2713 +
  1.2714 +    Object.defineProperty(this, 'supportsFullscreen', { value: support,
  1.2715 +                                                        enumerable: true,
  1.2716 +                                                        configurable: true,
  1.2717 +                                                        writable: false });
  1.2718 +    return support;
  1.2719 +  },
  1.2720 +
  1.2721 +  get supportsIntegratedFind() {
  1.2722 +    var support = false;
  1.2723 +    support = FirefoxCom.requestSync('supportsIntegratedFind');
  1.2724 +    Object.defineProperty(this, 'supportsIntegratedFind', { value: support,
  1.2725 +                                                            enumerable: true,
  1.2726 +                                                            configurable: true,
  1.2727 +                                                            writable: false });
  1.2728 +    return support;
  1.2729 +  },
  1.2730 +
  1.2731 +  get supportsDocumentFonts() {
  1.2732 +    var support = true;
  1.2733 +    support = FirefoxCom.requestSync('supportsDocumentFonts');
  1.2734 +    Object.defineProperty(this, 'supportsDocumentFonts', { value: support,
  1.2735 +                                                           enumerable: true,
  1.2736 +                                                           configurable: true,
  1.2737 +                                                           writable: false });
  1.2738 +    return support;
  1.2739 +  },
  1.2740 +
  1.2741 +  get supportsDocumentColors() {
  1.2742 +    var support = true;
  1.2743 +    support = FirefoxCom.requestSync('supportsDocumentColors');
  1.2744 +    Object.defineProperty(this, 'supportsDocumentColors', { value: support,
  1.2745 +                                                            enumerable: true,
  1.2746 +                                                            configurable: true,
  1.2747 +                                                            writable: false });
  1.2748 +    return support;
  1.2749 +  },
  1.2750 +
  1.2751 +  get loadingBar() {
  1.2752 +    var bar = new ProgressBar('#loadingBar', {});
  1.2753 +    Object.defineProperty(this, 'loadingBar', { value: bar,
  1.2754 +                                                enumerable: true,
  1.2755 +                                                configurable: true,
  1.2756 +                                                writable: false });
  1.2757 +    return bar;
  1.2758 +  },
  1.2759 +
  1.2760 +  get isHorizontalScrollbarEnabled() {
  1.2761 +    return (PresentationMode.active ? false :
  1.2762 +            (this.container.scrollWidth > this.container.clientWidth));
  1.2763 +  },
  1.2764 +
  1.2765 +  initPassiveLoading: function pdfViewInitPassiveLoading() {
  1.2766 +    var pdfDataRangeTransport = {
  1.2767 +      rangeListeners: [],
  1.2768 +      progressListeners: [],
  1.2769 +
  1.2770 +      addRangeListener: function PdfDataRangeTransport_addRangeListener(
  1.2771 +                                   listener) {
  1.2772 +        this.rangeListeners.push(listener);
  1.2773 +      },
  1.2774 +
  1.2775 +      addProgressListener: function PdfDataRangeTransport_addProgressListener(
  1.2776 +                                      listener) {
  1.2777 +        this.progressListeners.push(listener);
  1.2778 +      },
  1.2779 +
  1.2780 +      onDataRange: function PdfDataRangeTransport_onDataRange(begin, chunk) {
  1.2781 +        var listeners = this.rangeListeners;
  1.2782 +        for (var i = 0, n = listeners.length; i < n; ++i) {
  1.2783 +          listeners[i](begin, chunk);
  1.2784 +        }
  1.2785 +      },
  1.2786 +
  1.2787 +      onDataProgress: function PdfDataRangeTransport_onDataProgress(loaded) {
  1.2788 +        var listeners = this.progressListeners;
  1.2789 +        for (var i = 0, n = listeners.length; i < n; ++i) {
  1.2790 +          listeners[i](loaded);
  1.2791 +        }
  1.2792 +      },
  1.2793 +
  1.2794 +      requestDataRange: function PdfDataRangeTransport_requestDataRange(
  1.2795 +                                  begin, end) {
  1.2796 +        FirefoxCom.request('requestDataRange', { begin: begin, end: end });
  1.2797 +      }
  1.2798 +    };
  1.2799 +
  1.2800 +    window.addEventListener('message', function windowMessage(e) {
  1.2801 +      if (e.source !== null) {
  1.2802 +        // The message MUST originate from Chrome code.
  1.2803 +        console.warn('Rejected untrusted message from ' + e.origin);
  1.2804 +        return;
  1.2805 +      }
  1.2806 +      var args = e.data;
  1.2807 +
  1.2808 +      if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) {
  1.2809 +        return;
  1.2810 +      }
  1.2811 +      switch (args.pdfjsLoadAction) {
  1.2812 +        case 'supportsRangedLoading':
  1.2813 +          PDFView.open(args.pdfUrl, 0, undefined, pdfDataRangeTransport, {
  1.2814 +            length: args.length,
  1.2815 +            initialData: args.data
  1.2816 +          });
  1.2817 +          break;
  1.2818 +        case 'range':
  1.2819 +          pdfDataRangeTransport.onDataRange(args.begin, args.chunk);
  1.2820 +          break;
  1.2821 +        case 'rangeProgress':
  1.2822 +          pdfDataRangeTransport.onDataProgress(args.loaded);
  1.2823 +          break;
  1.2824 +        case 'progress':
  1.2825 +          PDFView.progress(args.loaded / args.total);
  1.2826 +          break;
  1.2827 +        case 'complete':
  1.2828 +          if (!args.data) {
  1.2829 +            PDFView.error(mozL10n.get('loading_error', null,
  1.2830 +                          'An error occurred while loading the PDF.'), e);
  1.2831 +            break;
  1.2832 +          }
  1.2833 +          PDFView.open(args.data, 0);
  1.2834 +          break;
  1.2835 +      }
  1.2836 +    });
  1.2837 +    FirefoxCom.requestSync('initPassiveLoading', null);
  1.2838 +  },
  1.2839 +
  1.2840 +  setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
  1.2841 +    this.url = url;
  1.2842 +    try {
  1.2843 +      this.setTitle(decodeURIComponent(getFileName(url)) || url);
  1.2844 +    } catch (e) {
  1.2845 +      // decodeURIComponent may throw URIError,
  1.2846 +      // fall back to using the unprocessed url in that case
  1.2847 +      this.setTitle(url);
  1.2848 +    }
  1.2849 +  },
  1.2850 +
  1.2851 +  setTitle: function pdfViewSetTitle(title) {
  1.2852 +    document.title = title;
  1.2853 +  },
  1.2854 +
  1.2855 +  close: function pdfViewClose() {
  1.2856 +    var errorWrapper = document.getElementById('errorWrapper');
  1.2857 +    errorWrapper.setAttribute('hidden', 'true');
  1.2858 +
  1.2859 +    if (!this.pdfDocument) {
  1.2860 +      return;
  1.2861 +    }
  1.2862 +
  1.2863 +    this.pdfDocument.destroy();
  1.2864 +    this.pdfDocument = null;
  1.2865 +
  1.2866 +    var thumbsView = document.getElementById('thumbnailView');
  1.2867 +    while (thumbsView.hasChildNodes()) {
  1.2868 +      thumbsView.removeChild(thumbsView.lastChild);
  1.2869 +    }
  1.2870 +
  1.2871 +    if ('_loadingInterval' in thumbsView) {
  1.2872 +      clearInterval(thumbsView._loadingInterval);
  1.2873 +    }
  1.2874 +
  1.2875 +    var container = document.getElementById('viewer');
  1.2876 +    while (container.hasChildNodes()) {
  1.2877 +      container.removeChild(container.lastChild);
  1.2878 +    }
  1.2879 +
  1.2880 +    if (typeof PDFBug !== 'undefined') {
  1.2881 +      PDFBug.cleanup();
  1.2882 +    }
  1.2883 +  },
  1.2884 +
  1.2885 +  // TODO(mack): This function signature should really be pdfViewOpen(url, args)
  1.2886 +  open: function pdfViewOpen(url, scale, password,
  1.2887 +                             pdfDataRangeTransport, args) {
  1.2888 +    if (this.pdfDocument) {
  1.2889 +      // Reload the preferences if a document was previously opened.
  1.2890 +      Preferences.reload();
  1.2891 +    }
  1.2892 +    this.close();
  1.2893 +
  1.2894 +    var parameters = {password: password};
  1.2895 +    if (typeof url === 'string') { // URL
  1.2896 +      this.setTitleUsingUrl(url);
  1.2897 +      parameters.url = url;
  1.2898 +    } else if (url && 'byteLength' in url) { // ArrayBuffer
  1.2899 +      parameters.data = url;
  1.2900 +    }
  1.2901 +    if (args) {
  1.2902 +      for (var prop in args) {
  1.2903 +        parameters[prop] = args[prop];
  1.2904 +      }
  1.2905 +    }
  1.2906 +
  1.2907 +    var self = this;
  1.2908 +    self.loading = true;
  1.2909 +    self.downloadComplete = false;
  1.2910 +
  1.2911 +    var passwordNeeded = function passwordNeeded(updatePassword, reason) {
  1.2912 +      PasswordPrompt.updatePassword = updatePassword;
  1.2913 +      PasswordPrompt.reason = reason;
  1.2914 +      PasswordPrompt.show();
  1.2915 +    };
  1.2916 +
  1.2917 +    function getDocumentProgress(progressData) {
  1.2918 +      self.progress(progressData.loaded / progressData.total);
  1.2919 +    }
  1.2920 +
  1.2921 +    PDFJS.getDocument(parameters, pdfDataRangeTransport, passwordNeeded,
  1.2922 +                      getDocumentProgress).then(
  1.2923 +      function getDocumentCallback(pdfDocument) {
  1.2924 +        self.load(pdfDocument, scale);
  1.2925 +        self.loading = false;
  1.2926 +      },
  1.2927 +      function getDocumentError(message, exception) {
  1.2928 +        var loadingErrorMessage = mozL10n.get('loading_error', null,
  1.2929 +          'An error occurred while loading the PDF.');
  1.2930 +
  1.2931 +        if (exception && exception.name === 'InvalidPDFException') {
  1.2932 +          // change error message also for other builds
  1.2933 +          loadingErrorMessage = mozL10n.get('invalid_file_error', null,
  1.2934 +                                        'Invalid or corrupted PDF file.');
  1.2935 +        }
  1.2936 +
  1.2937 +        if (exception && exception.name === 'MissingPDFException') {
  1.2938 +          // special message for missing PDF's
  1.2939 +          loadingErrorMessage = mozL10n.get('missing_file_error', null,
  1.2940 +                                        'Missing PDF file.');
  1.2941 +
  1.2942 +        }
  1.2943 +
  1.2944 +        var moreInfo = {
  1.2945 +          message: message
  1.2946 +        };
  1.2947 +        self.error(loadingErrorMessage, moreInfo);
  1.2948 +        self.loading = false;
  1.2949 +      }
  1.2950 +    );
  1.2951 +  },
  1.2952 +
  1.2953 +  download: function pdfViewDownload() {
  1.2954 +    function downloadByUrl() {
  1.2955 +      downloadManager.downloadUrl(url, filename);
  1.2956 +    }
  1.2957 +
  1.2958 +    var url = this.url.split('#')[0];
  1.2959 +    var filename = getPDFFileNameFromURL(url);
  1.2960 +    var downloadManager = new DownloadManager();
  1.2961 +    downloadManager.onerror = function (err) {
  1.2962 +      // This error won't really be helpful because it's likely the
  1.2963 +      // fallback won't work either (or is already open).
  1.2964 +      PDFView.error('PDF failed to download.');
  1.2965 +    };
  1.2966 +
  1.2967 +    if (!this.pdfDocument) { // the PDF is not ready yet
  1.2968 +      downloadByUrl();
  1.2969 +      return;
  1.2970 +    }
  1.2971 +
  1.2972 +    if (!this.downloadComplete) { // the PDF is still downloading
  1.2973 +      downloadByUrl();
  1.2974 +      return;
  1.2975 +    }
  1.2976 +
  1.2977 +    this.pdfDocument.getData().then(
  1.2978 +      function getDataSuccess(data) {
  1.2979 +        var blob = PDFJS.createBlob(data, 'application/pdf');
  1.2980 +        downloadManager.download(blob, url, filename);
  1.2981 +      },
  1.2982 +      downloadByUrl // Error occurred try downloading with just the url.
  1.2983 +    ).then(null, downloadByUrl);
  1.2984 +  },
  1.2985 +
  1.2986 +  fallback: function pdfViewFallback(featureId) {
  1.2987 +    // Only trigger the fallback once so we don't spam the user with messages
  1.2988 +    // for one PDF.
  1.2989 +    if (this.fellback)
  1.2990 +      return;
  1.2991 +    this.fellback = true;
  1.2992 +    var url = this.url.split('#')[0];
  1.2993 +    FirefoxCom.request('fallback', { featureId: featureId, url: url },
  1.2994 +      function response(download) {
  1.2995 +        if (!download) {
  1.2996 +          return;
  1.2997 +        }
  1.2998 +        PDFView.download();
  1.2999 +      });
  1.3000 +  },
  1.3001 +
  1.3002 +  navigateTo: function pdfViewNavigateTo(dest) {
  1.3003 +    var destString = '';
  1.3004 +    var self = this;
  1.3005 +
  1.3006 +    var goToDestination = function(destRef) {
  1.3007 +      self.pendingRefStr = null;
  1.3008 +      // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
  1.3009 +      var pageNumber = destRef instanceof Object ?
  1.3010 +        self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
  1.3011 +        (destRef + 1);
  1.3012 +      if (pageNumber) {
  1.3013 +        if (pageNumber > self.pages.length) {
  1.3014 +          pageNumber = self.pages.length;
  1.3015 +        }
  1.3016 +        var currentPage = self.pages[pageNumber - 1];
  1.3017 +        currentPage.scrollIntoView(dest);
  1.3018 +
  1.3019 +        // Update the browsing history.
  1.3020 +        PDFHistory.push({ dest: dest, hash: destString, page: pageNumber });
  1.3021 +      } else {
  1.3022 +        self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) {
  1.3023 +          var pageNum = pageIndex + 1;
  1.3024 +          self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] = pageNum;
  1.3025 +          goToDestination(destRef);
  1.3026 +        });
  1.3027 +      }
  1.3028 +    };
  1.3029 +
  1.3030 +    this.destinationsPromise.then(function() {
  1.3031 +      if (typeof dest === 'string') {
  1.3032 +        destString = dest;
  1.3033 +        dest = self.destinations[dest];
  1.3034 +      }
  1.3035 +      if (!(dest instanceof Array)) {
  1.3036 +        return; // invalid destination
  1.3037 +      }
  1.3038 +      goToDestination(dest[0]);
  1.3039 +    });
  1.3040 +  },
  1.3041 +
  1.3042 +  getDestinationHash: function pdfViewGetDestinationHash(dest) {
  1.3043 +    if (typeof dest === 'string') {
  1.3044 +      return PDFView.getAnchorUrl('#' + escape(dest));
  1.3045 +    }
  1.3046 +    if (dest instanceof Array) {
  1.3047 +      var destRef = dest[0]; // see navigateTo method for dest format
  1.3048 +      var pageNumber = destRef instanceof Object ?
  1.3049 +        this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
  1.3050 +        (destRef + 1);
  1.3051 +      if (pageNumber) {
  1.3052 +        var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber);
  1.3053 +        var destKind = dest[1];
  1.3054 +        if (typeof destKind === 'object' && 'name' in destKind &&
  1.3055 +            destKind.name == 'XYZ') {
  1.3056 +          var scale = (dest[4] || this.currentScaleValue);
  1.3057 +          var scaleNumber = parseFloat(scale);
  1.3058 +          if (scaleNumber) {
  1.3059 +            scale = scaleNumber * 100;
  1.3060 +          }
  1.3061 +          pdfOpenParams += '&zoom=' + scale;
  1.3062 +          if (dest[2] || dest[3]) {
  1.3063 +            pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
  1.3064 +          }
  1.3065 +        }
  1.3066 +        return pdfOpenParams;
  1.3067 +      }
  1.3068 +    }
  1.3069 +    return '';
  1.3070 +  },
  1.3071 +
  1.3072 +  /**
  1.3073 +   * Prefix the full url on anchor links to make sure that links are resolved
  1.3074 +   * relative to the current URL instead of the one defined in <base href>.
  1.3075 +   * @param {String} anchor The anchor hash, including the #.
  1.3076 +   */
  1.3077 +  getAnchorUrl: function getAnchorUrl(anchor) {
  1.3078 +    return this.url.split('#')[0] + anchor;
  1.3079 +  },
  1.3080 +
  1.3081 +  /**
  1.3082 +   * Show the error box.
  1.3083 +   * @param {String} message A message that is human readable.
  1.3084 +   * @param {Object} moreInfo (optional) Further information about the error
  1.3085 +   *                            that is more technical.  Should have a 'message'
  1.3086 +   *                            and optionally a 'stack' property.
  1.3087 +   */
  1.3088 +  error: function pdfViewError(message, moreInfo) {
  1.3089 +    var moreInfoText = mozL10n.get('error_version_info',
  1.3090 +      {version: PDFJS.version || '?', build: PDFJS.build || '?'},
  1.3091 +      'PDF.js v{{version}} (build: {{build}})') + '\n';
  1.3092 +    if (moreInfo) {
  1.3093 +      moreInfoText +=
  1.3094 +        mozL10n.get('error_message', {message: moreInfo.message},
  1.3095 +        'Message: {{message}}');
  1.3096 +      if (moreInfo.stack) {
  1.3097 +        moreInfoText += '\n' +
  1.3098 +          mozL10n.get('error_stack', {stack: moreInfo.stack},
  1.3099 +          'Stack: {{stack}}');
  1.3100 +      } else {
  1.3101 +        if (moreInfo.filename) {
  1.3102 +          moreInfoText += '\n' +
  1.3103 +            mozL10n.get('error_file', {file: moreInfo.filename},
  1.3104 +            'File: {{file}}');
  1.3105 +        }
  1.3106 +        if (moreInfo.lineNumber) {
  1.3107 +          moreInfoText += '\n' +
  1.3108 +            mozL10n.get('error_line', {line: moreInfo.lineNumber},
  1.3109 +            'Line: {{line}}');
  1.3110 +        }
  1.3111 +      }
  1.3112 +    }
  1.3113 +
  1.3114 +    console.error(message + '\n' + moreInfoText);
  1.3115 +    this.fallback();
  1.3116 +  },
  1.3117 +
  1.3118 +  progress: function pdfViewProgress(level) {
  1.3119 +    var percent = Math.round(level * 100);
  1.3120 +    // When we transition from full request to range requests, it's possible
  1.3121 +    // that we discard some of the loaded data. This can cause the loading
  1.3122 +    // bar to move backwards. So prevent this by only updating the bar if it
  1.3123 +    // increases.
  1.3124 +    if (percent > PDFView.loadingBar.percent) {
  1.3125 +      PDFView.loadingBar.percent = percent;
  1.3126 +    }
  1.3127 +  },
  1.3128 +
  1.3129 +  load: function pdfViewLoad(pdfDocument, scale) {
  1.3130 +    var self = this;
  1.3131 +    var isOnePageRenderedResolved = false;
  1.3132 +    var resolveOnePageRendered = null;
  1.3133 +    var onePageRendered = new Promise(function (resolve) {
  1.3134 +      resolveOnePageRendered = resolve;
  1.3135 +    });
  1.3136 +    function bindOnAfterDraw(pageView, thumbnailView) {
  1.3137 +      // when page is painted, using the image as thumbnail base
  1.3138 +      pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
  1.3139 +        if (!isOnePageRenderedResolved) {
  1.3140 +          isOnePageRenderedResolved = true;
  1.3141 +          resolveOnePageRendered();
  1.3142 +        }
  1.3143 +        thumbnailView.setImage(pageView.canvas);
  1.3144 +      };
  1.3145 +    }
  1.3146 +
  1.3147 +    PDFFindController.reset();
  1.3148 +
  1.3149 +    this.pdfDocument = pdfDocument;
  1.3150 +
  1.3151 +    DocumentProperties.resolveDataAvailable();
  1.3152 +
  1.3153 +    var downloadedPromise = pdfDocument.getDownloadInfo().then(function() {
  1.3154 +      self.downloadComplete = true;
  1.3155 +      PDFView.loadingBar.hide();
  1.3156 +      var outerContainer = document.getElementById('outerContainer');
  1.3157 +      outerContainer.classList.remove('loadingInProgress');
  1.3158 +    });
  1.3159 +
  1.3160 +    var pagesCount = pdfDocument.numPages;
  1.3161 +
  1.3162 +    var id = pdfDocument.fingerprint;
  1.3163 +    document.getElementById('numPages').textContent =
  1.3164 +      mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
  1.3165 +    document.getElementById('pageNumber').max = pagesCount;
  1.3166 +
  1.3167 +    PDFView.documentFingerprint = id;
  1.3168 +    var store = PDFView.store = new ViewHistory(id);
  1.3169 +
  1.3170 +    this.pageRotation = 0;
  1.3171 +
  1.3172 +    var pages = this.pages = [];
  1.3173 +    var pagesRefMap = this.pagesRefMap = {};
  1.3174 +    var thumbnails = this.thumbnails = [];
  1.3175 +
  1.3176 +    var resolvePagesPromise;
  1.3177 +    var pagesPromise = new Promise(function (resolve) {
  1.3178 +      resolvePagesPromise = resolve;
  1.3179 +    });
  1.3180 +    this.pagesPromise = pagesPromise;
  1.3181 +
  1.3182 +    var firstPagePromise = pdfDocument.getPage(1);
  1.3183 +    var container = document.getElementById('viewer');
  1.3184 +    var thumbsView = document.getElementById('thumbnailView');
  1.3185 +
  1.3186 +    // Fetch a single page so we can get a viewport that will be the default
  1.3187 +    // viewport for all pages
  1.3188 +    firstPagePromise.then(function(pdfPage) {
  1.3189 +      var viewport = pdfPage.getViewport((scale || 1.0) * CSS_UNITS);
  1.3190 +      for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
  1.3191 +        var viewportClone = viewport.clone();
  1.3192 +        var pageView = new PageView(container, pageNum, scale,
  1.3193 +                                    self.navigateTo.bind(self),
  1.3194 +                                    viewportClone);
  1.3195 +        var thumbnailView = new ThumbnailView(thumbsView, pageNum,
  1.3196 +                                              viewportClone);
  1.3197 +        bindOnAfterDraw(pageView, thumbnailView);
  1.3198 +        pages.push(pageView);
  1.3199 +        thumbnails.push(thumbnailView);
  1.3200 +      }
  1.3201 +
  1.3202 +      // Fetch all the pages since the viewport is needed before printing
  1.3203 +      // starts to create the correct size canvas. Wait until one page is
  1.3204 +      // rendered so we don't tie up too many resources early on.
  1.3205 +      onePageRendered.then(function () {
  1.3206 +        if (!PDFJS.disableAutoFetch) {
  1.3207 +          var getPagesLeft = pagesCount;
  1.3208 +          for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
  1.3209 +            pdfDocument.getPage(pageNum).then(function (pageNum, pdfPage) {
  1.3210 +              var pageView = pages[pageNum - 1];
  1.3211 +              if (!pageView.pdfPage) {
  1.3212 +                pageView.setPdfPage(pdfPage);
  1.3213 +              }
  1.3214 +              var refStr = pdfPage.ref.num + ' ' + pdfPage.ref.gen + ' R';
  1.3215 +              pagesRefMap[refStr] = pageNum;
  1.3216 +              getPagesLeft--;
  1.3217 +              if (!getPagesLeft) {
  1.3218 +                resolvePagesPromise();
  1.3219 +              }
  1.3220 +            }.bind(null, pageNum));
  1.3221 +          }
  1.3222 +        } else {
  1.3223 +          // XXX: Printing is semi-broken with auto fetch disabled.
  1.3224 +          resolvePagesPromise();
  1.3225 +        }
  1.3226 +      });
  1.3227 +
  1.3228 +      downloadedPromise.then(function () {
  1.3229 +        var event = document.createEvent('CustomEvent');
  1.3230 +        event.initCustomEvent('documentload', true, true, {});
  1.3231 +        window.dispatchEvent(event);
  1.3232 +      });
  1.3233 +
  1.3234 +      PDFView.loadingBar.setWidth(container);
  1.3235 +
  1.3236 +      PDFFindController.resolveFirstPage();
  1.3237 +
  1.3238 +      // Initialize the browsing history.
  1.3239 +      PDFHistory.initialize(self.documentFingerprint);
  1.3240 +    });
  1.3241 +
  1.3242 +    // Fetch the necessary preference values.
  1.3243 +    var showPreviousViewOnLoad;
  1.3244 +    var showPreviousViewOnLoadPromise =
  1.3245 +      Preferences.get('showPreviousViewOnLoad').then(function (prefValue) {
  1.3246 +        showPreviousViewOnLoad = prefValue;
  1.3247 +      });
  1.3248 +    var defaultZoomValue;
  1.3249 +    var defaultZoomValuePromise =
  1.3250 +      Preferences.get('defaultZoomValue').then(function (prefValue) {
  1.3251 +        defaultZoomValue = prefValue;
  1.3252 +      });
  1.3253 +
  1.3254 +    var storePromise = store.initializedPromise;
  1.3255 +    Promise.all([firstPagePromise, storePromise, showPreviousViewOnLoadPromise,
  1.3256 +                 defaultZoomValuePromise]).then(function resolved() {
  1.3257 +      var storedHash = null;
  1.3258 +      if (showPreviousViewOnLoad && store.get('exists', false)) {
  1.3259 +        var pageNum = store.get('page', '1');
  1.3260 +        var zoom = defaultZoomValue || store.get('zoom', PDFView.currentScale);
  1.3261 +        var left = store.get('scrollLeft', '0');
  1.3262 +        var top = store.get('scrollTop', '0');
  1.3263 +
  1.3264 +        storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' +
  1.3265 +                     left + ',' + top;
  1.3266 +      } else if (defaultZoomValue) {
  1.3267 +        storedHash = 'page=1&zoom=' + defaultZoomValue;
  1.3268 +      }
  1.3269 +      self.setInitialView(storedHash, scale);
  1.3270 +
  1.3271 +      // Make all navigation keys work on document load,
  1.3272 +      // unless the viewer is embedded in a web page.
  1.3273 +      if (!self.isViewerEmbedded) {
  1.3274 +        self.container.focus();
  1.3275 +        self.container.blur();
  1.3276 +      }
  1.3277 +    }, function rejected(errorMsg) {
  1.3278 +      console.error(errorMsg);
  1.3279 +
  1.3280 +      firstPagePromise.then(function () {
  1.3281 +        self.setInitialView(null, scale);
  1.3282 +      });
  1.3283 +    });
  1.3284 +
  1.3285 +    pagesPromise.then(function() {
  1.3286 +      if (PDFView.supportsPrinting) {
  1.3287 +        pdfDocument.getJavaScript().then(function(javaScript) {
  1.3288 +          if (javaScript.length) {
  1.3289 +            console.warn('Warning: JavaScript is not supported');
  1.3290 +            PDFView.fallback(PDFJS.UNSUPPORTED_FEATURES.javaScript);
  1.3291 +          }
  1.3292 +          // Hack to support auto printing.
  1.3293 +          var regex = /\bprint\s*\(/g;
  1.3294 +          for (var i = 0, ii = javaScript.length; i < ii; i++) {
  1.3295 +            var js = javaScript[i];
  1.3296 +            if (js && regex.test(js)) {
  1.3297 +              setTimeout(function() {
  1.3298 +                window.print();
  1.3299 +              });
  1.3300 +              return;
  1.3301 +            }
  1.3302 +          }
  1.3303 +        });
  1.3304 +      }
  1.3305 +    });
  1.3306 +
  1.3307 +    var destinationsPromise =
  1.3308 +      this.destinationsPromise = pdfDocument.getDestinations();
  1.3309 +    destinationsPromise.then(function(destinations) {
  1.3310 +      self.destinations = destinations;
  1.3311 +    });
  1.3312 +
  1.3313 +    // outline depends on destinations and pagesRefMap
  1.3314 +    var promises = [pagesPromise, destinationsPromise,
  1.3315 +                    PDFView.animationStartedPromise];
  1.3316 +    Promise.all(promises).then(function() {
  1.3317 +      pdfDocument.getOutline().then(function(outline) {
  1.3318 +        self.outline = new DocumentOutlineView(outline);
  1.3319 +        document.getElementById('viewOutline').disabled = !outline;
  1.3320 +
  1.3321 +        if (outline) {
  1.3322 +          Preferences.get('ifAvailableShowOutlineOnLoad').then(
  1.3323 +            function (prefValue) {
  1.3324 +              if (prefValue) {
  1.3325 +                if (!self.sidebarOpen) {
  1.3326 +                  document.getElementById('sidebarToggle').click();
  1.3327 +                }
  1.3328 +                self.switchSidebarView('outline');
  1.3329 +              }
  1.3330 +            });
  1.3331 +        }
  1.3332 +      });
  1.3333 +      pdfDocument.getAttachments().then(function(attachments) {
  1.3334 +        self.attachments = new DocumentAttachmentsView(attachments);
  1.3335 +        document.getElementById('viewAttachments').disabled = !attachments;
  1.3336 +      });
  1.3337 +    });
  1.3338 +
  1.3339 +    pdfDocument.getMetadata().then(function(data) {
  1.3340 +      var info = data.info, metadata = data.metadata;
  1.3341 +      self.documentInfo = info;
  1.3342 +      self.metadata = metadata;
  1.3343 +
  1.3344 +      // Provides some basic debug information
  1.3345 +      console.log('PDF ' + pdfDocument.fingerprint + ' [' +
  1.3346 +                  info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() +
  1.3347 +                  ' / ' + (info.Creator || '-').trim() + ']' +
  1.3348 +                  ' (PDF.js: ' + (PDFJS.version || '-') +
  1.3349 +                  (!PDFJS.disableWebGL ? ' [WebGL]' : '') + ')');
  1.3350 +
  1.3351 +      var pdfTitle;
  1.3352 +      if (metadata && metadata.has('dc:title')) {
  1.3353 +        pdfTitle = metadata.get('dc:title');
  1.3354 +      }
  1.3355 +
  1.3356 +      if (!pdfTitle && info && info['Title']) {
  1.3357 +        pdfTitle = info['Title'];
  1.3358 +      }
  1.3359 +
  1.3360 +      if (pdfTitle) {
  1.3361 +        self.setTitle(pdfTitle + ' - ' + document.title);
  1.3362 +      }
  1.3363 +
  1.3364 +      if (info.IsAcroFormPresent) {
  1.3365 +        console.warn('Warning: AcroForm/XFA is not supported');
  1.3366 +        PDFView.fallback(PDFJS.UNSUPPORTED_FEATURES.forms);
  1.3367 +      }
  1.3368 +
  1.3369 +      var versionId = String(info.PDFFormatVersion).slice(-1) | 0;
  1.3370 +      var generatorId = 0;
  1.3371 +      var KNOWN_GENERATORS = ["acrobat distiller", "acrobat pdfwritter",
  1.3372 +       "adobe livecycle", "adobe pdf library", "adobe photoshop", "ghostscript",
  1.3373 +       "tcpdf", "cairo", "dvipdfm", "dvips", "pdftex", "pdfkit", "itext",
  1.3374 +       "prince", "quarkxpress", "mac os x", "microsoft", "openoffice", "oracle",
  1.3375 +       "luradocument", "pdf-xchange", "antenna house", "aspose.cells", "fpdf"];
  1.3376 +      var generatorId = 0;
  1.3377 +      if (info.Producer) {
  1.3378 +        KNOWN_GENERATORS.some(function (generator, s, i) {
  1.3379 +          if (generator.indexOf(s) < 0) {
  1.3380 +            return false;
  1.3381 +          }
  1.3382 +          generatorId = i + 1;
  1.3383 +          return true;
  1.3384 +        }.bind(null, info.Producer.toLowerCase()));
  1.3385 +      }
  1.3386 +      var formType = !info.IsAcroFormPresent ? null : info.IsXFAPresent ?
  1.3387 +                     'xfa' : 'acroform';
  1.3388 +      FirefoxCom.request('reportTelemetry', JSON.stringify({
  1.3389 +        type: 'documentInfo',
  1.3390 +        version: versionId,
  1.3391 +        generator: generatorId,
  1.3392 +        formType: formType
  1.3393 +      }));
  1.3394 +    });
  1.3395 +  },
  1.3396 +
  1.3397 +  setInitialView: function pdfViewSetInitialView(storedHash, scale) {
  1.3398 +    // Reset the current scale, as otherwise the page's scale might not get
  1.3399 +    // updated if the zoom level stayed the same.
  1.3400 +    this.currentScale = 0;
  1.3401 +    this.currentScaleValue = null;
  1.3402 +    // When opening a new file (when one is already loaded in the viewer):
  1.3403 +    // Reset 'currentPageNumber', since otherwise the page's scale will be wrong
  1.3404 +    // if 'currentPageNumber' is larger than the number of pages in the file.
  1.3405 +    document.getElementById('pageNumber').value = currentPageNumber = 1;
  1.3406 +    // Reset the current position when loading a new file,
  1.3407 +    // to prevent displaying the wrong position in the document.
  1.3408 +    this.currentPosition = null;
  1.3409 +
  1.3410 +    if (PDFHistory.initialDestination) {
  1.3411 +      this.navigateTo(PDFHistory.initialDestination);
  1.3412 +      PDFHistory.initialDestination = null;
  1.3413 +    } else if (this.initialBookmark) {
  1.3414 +      this.setHash(this.initialBookmark);
  1.3415 +      PDFHistory.push({ hash: this.initialBookmark }, !!this.initialBookmark);
  1.3416 +      this.initialBookmark = null;
  1.3417 +    } else if (storedHash) {
  1.3418 +      this.setHash(storedHash);
  1.3419 +    } else if (scale) {
  1.3420 +      this.setScale(scale, true);
  1.3421 +      this.page = 1;
  1.3422 +    }
  1.3423 +
  1.3424 +    if (PDFView.currentScale === UNKNOWN_SCALE) {
  1.3425 +      // Scale was not initialized: invalid bookmark or scale was not specified.
  1.3426 +      // Setting the default one.
  1.3427 +      this.setScale(DEFAULT_SCALE, true);
  1.3428 +    }
  1.3429 +  },
  1.3430 +
  1.3431 +  renderHighestPriority: function pdfViewRenderHighestPriority() {
  1.3432 +    if (PDFView.idleTimeout) {
  1.3433 +      clearTimeout(PDFView.idleTimeout);
  1.3434 +      PDFView.idleTimeout = null;
  1.3435 +    }
  1.3436 +
  1.3437 +    // Pages have a higher priority than thumbnails, so check them first.
  1.3438 +    var visiblePages = this.getVisiblePages();
  1.3439 +    var pageView = this.getHighestPriority(visiblePages, this.pages,
  1.3440 +                                           this.pageViewScroll.down);
  1.3441 +    if (pageView) {
  1.3442 +      this.renderView(pageView, 'page');
  1.3443 +      return;
  1.3444 +    }
  1.3445 +    // No pages needed rendering so check thumbnails.
  1.3446 +    if (this.sidebarOpen) {
  1.3447 +      var visibleThumbs = this.getVisibleThumbs();
  1.3448 +      var thumbView = this.getHighestPriority(visibleThumbs,
  1.3449 +                                              this.thumbnails,
  1.3450 +                                              this.thumbnailViewScroll.down);
  1.3451 +      if (thumbView) {
  1.3452 +        this.renderView(thumbView, 'thumbnail');
  1.3453 +        return;
  1.3454 +      }
  1.3455 +    }
  1.3456 +
  1.3457 +    PDFView.idleTimeout = setTimeout(function () {
  1.3458 +      PDFView.cleanup();
  1.3459 +    }, CLEANUP_TIMEOUT);
  1.3460 +  },
  1.3461 +
  1.3462 +  cleanup: function pdfViewCleanup() {
  1.3463 +    for (var i = 0, ii = this.pages.length; i < ii; i++) {
  1.3464 +      if (this.pages[i] &&
  1.3465 +          this.pages[i].renderingState !== RenderingStates.FINISHED) {
  1.3466 +        this.pages[i].reset();
  1.3467 +      }
  1.3468 +    }
  1.3469 +    this.pdfDocument.cleanup();
  1.3470 +  },
  1.3471 +
  1.3472 +  getHighestPriority: function pdfViewGetHighestPriority(visible, views,
  1.3473 +                                                         scrolledDown) {
  1.3474 +    // The state has changed figure out which page has the highest priority to
  1.3475 +    // render next (if any).
  1.3476 +    // Priority:
  1.3477 +    // 1 visible pages
  1.3478 +    // 2 if last scrolled down page after the visible pages
  1.3479 +    // 2 if last scrolled up page before the visible pages
  1.3480 +    var visibleViews = visible.views;
  1.3481 +
  1.3482 +    var numVisible = visibleViews.length;
  1.3483 +    if (numVisible === 0) {
  1.3484 +      return false;
  1.3485 +    }
  1.3486 +    for (var i = 0; i < numVisible; ++i) {
  1.3487 +      var view = visibleViews[i].view;
  1.3488 +      if (!this.isViewFinished(view)) {
  1.3489 +        return view;
  1.3490 +      }
  1.3491 +    }
  1.3492 +
  1.3493 +    // All the visible views have rendered, try to render next/previous pages.
  1.3494 +    if (scrolledDown) {
  1.3495 +      var nextPageIndex = visible.last.id;
  1.3496 +      // ID's start at 1 so no need to add 1.
  1.3497 +      if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) {
  1.3498 +        return views[nextPageIndex];
  1.3499 +      }
  1.3500 +    } else {
  1.3501 +      var previousPageIndex = visible.first.id - 2;
  1.3502 +      if (views[previousPageIndex] &&
  1.3503 +          !this.isViewFinished(views[previousPageIndex])) {
  1.3504 +        return views[previousPageIndex];
  1.3505 +      }
  1.3506 +    }
  1.3507 +    // Everything that needs to be rendered has been.
  1.3508 +    return false;
  1.3509 +  },
  1.3510 +
  1.3511 +  isViewFinished: function pdfViewIsViewFinished(view) {
  1.3512 +    return view.renderingState === RenderingStates.FINISHED;
  1.3513 +  },
  1.3514 +
  1.3515 +  // Render a page or thumbnail view. This calls the appropriate function based
  1.3516 +  // on the views state. If the view is already rendered it will return false.
  1.3517 +  renderView: function pdfViewRender(view, type) {
  1.3518 +    var state = view.renderingState;
  1.3519 +    switch (state) {
  1.3520 +      case RenderingStates.FINISHED:
  1.3521 +        return false;
  1.3522 +      case RenderingStates.PAUSED:
  1.3523 +        PDFView.highestPriorityPage = type + view.id;
  1.3524 +        view.resume();
  1.3525 +        break;
  1.3526 +      case RenderingStates.RUNNING:
  1.3527 +        PDFView.highestPriorityPage = type + view.id;
  1.3528 +        break;
  1.3529 +      case RenderingStates.INITIAL:
  1.3530 +        PDFView.highestPriorityPage = type + view.id;
  1.3531 +        view.draw(this.renderHighestPriority.bind(this));
  1.3532 +        break;
  1.3533 +    }
  1.3534 +    return true;
  1.3535 +  },
  1.3536 +
  1.3537 +  setHash: function pdfViewSetHash(hash) {
  1.3538 +    if (!hash) {
  1.3539 +      return;
  1.3540 +    }
  1.3541 +
  1.3542 +    if (hash.indexOf('=') >= 0) {
  1.3543 +      var params = PDFView.parseQueryString(hash);
  1.3544 +      // borrowing syntax from "Parameters for Opening PDF Files"
  1.3545 +      if ('nameddest' in params) {
  1.3546 +        PDFHistory.updateNextHashParam(params.nameddest);
  1.3547 +        PDFView.navigateTo(params.nameddest);
  1.3548 +        return;
  1.3549 +      }
  1.3550 +      var pageNumber, dest;
  1.3551 +      if ('page' in params) {
  1.3552 +        pageNumber = (params.page | 0) || 1;
  1.3553 +      }
  1.3554 +      if ('zoom' in params) {
  1.3555 +        var zoomArgs = params.zoom.split(','); // scale,left,top
  1.3556 +        // building destination array
  1.3557 +
  1.3558 +        // If the zoom value, it has to get divided by 100. If it is a string,
  1.3559 +        // it should stay as it is.
  1.3560 +        var zoomArg = zoomArgs[0];
  1.3561 +        var zoomArgNumber = parseFloat(zoomArg);
  1.3562 +        if (zoomArgNumber) {
  1.3563 +          zoomArg = zoomArgNumber / 100;
  1.3564 +        }
  1.3565 +        dest = [null, {name: 'XYZ'},
  1.3566 +                zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
  1.3567 +                zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
  1.3568 +                zoomArg];
  1.3569 +      }
  1.3570 +      if (dest) {
  1.3571 +        var currentPage = this.pages[(pageNumber || this.page) - 1];
  1.3572 +        currentPage.scrollIntoView(dest);
  1.3573 +      } else if (pageNumber) {
  1.3574 +        this.page = pageNumber; // simple page
  1.3575 +      }
  1.3576 +      if ('pagemode' in params) {
  1.3577 +        var toggle = document.getElementById('sidebarToggle');
  1.3578 +        if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' ||
  1.3579 +            params.pagemode === 'attachments') {
  1.3580 +          if (!this.sidebarOpen) {
  1.3581 +            toggle.click();
  1.3582 +          }
  1.3583 +          this.switchSidebarView(params.pagemode === 'bookmarks' ?
  1.3584 +                                   'outline' :
  1.3585 +                                   params.pagemode);
  1.3586 +        } else if (params.pagemode === 'none' && this.sidebarOpen) {
  1.3587 +          toggle.click();
  1.3588 +        }
  1.3589 +      }
  1.3590 +    } else if (/^\d+$/.test(hash)) { // page number
  1.3591 +      this.page = hash;
  1.3592 +    } else { // named destination
  1.3593 +      PDFHistory.updateNextHashParam(unescape(hash));
  1.3594 +      PDFView.navigateTo(unescape(hash));
  1.3595 +    }
  1.3596 +  },
  1.3597 +
  1.3598 +  switchSidebarView: function pdfViewSwitchSidebarView(view) {
  1.3599 +    var thumbsView = document.getElementById('thumbnailView');
  1.3600 +    var outlineView = document.getElementById('outlineView');
  1.3601 +    var attachmentsView = document.getElementById('attachmentsView');
  1.3602 +
  1.3603 +    var thumbsButton = document.getElementById('viewThumbnail');
  1.3604 +    var outlineButton = document.getElementById('viewOutline');
  1.3605 +    var attachmentsButton = document.getElementById('viewAttachments');
  1.3606 +
  1.3607 +    switch (view) {
  1.3608 +      case 'thumbs':
  1.3609 +        var wasAnotherViewVisible = thumbsView.classList.contains('hidden');
  1.3610 +
  1.3611 +        thumbsButton.classList.add('toggled');
  1.3612 +        outlineButton.classList.remove('toggled');
  1.3613 +        attachmentsButton.classList.remove('toggled');
  1.3614 +        thumbsView.classList.remove('hidden');
  1.3615 +        outlineView.classList.add('hidden');
  1.3616 +        attachmentsView.classList.add('hidden');
  1.3617 +
  1.3618 +        PDFView.renderHighestPriority();
  1.3619 +
  1.3620 +        if (wasAnotherViewVisible) {
  1.3621 +          // Ensure that the thumbnail of the current page is visible
  1.3622 +          // when switching from another view.
  1.3623 +          scrollIntoView(document.getElementById('thumbnailContainer' +
  1.3624 +                                                 this.page));
  1.3625 +        }
  1.3626 +        break;
  1.3627 +
  1.3628 +      case 'outline':
  1.3629 +        thumbsButton.classList.remove('toggled');
  1.3630 +        outlineButton.classList.add('toggled');
  1.3631 +        attachmentsButton.classList.remove('toggled');
  1.3632 +        thumbsView.classList.add('hidden');
  1.3633 +        outlineView.classList.remove('hidden');
  1.3634 +        attachmentsView.classList.add('hidden');
  1.3635 +
  1.3636 +        if (outlineButton.getAttribute('disabled')) {
  1.3637 +          return;
  1.3638 +        }
  1.3639 +        break;
  1.3640 +
  1.3641 +      case 'attachments':
  1.3642 +        thumbsButton.classList.remove('toggled');
  1.3643 +        outlineButton.classList.remove('toggled');
  1.3644 +        attachmentsButton.classList.add('toggled');
  1.3645 +        thumbsView.classList.add('hidden');
  1.3646 +        outlineView.classList.add('hidden');
  1.3647 +        attachmentsView.classList.remove('hidden');
  1.3648 +
  1.3649 +        if (attachmentsButton.getAttribute('disabled')) {
  1.3650 +          return;
  1.3651 +        }
  1.3652 +        break;
  1.3653 +    }
  1.3654 +  },
  1.3655 +
  1.3656 +  getVisiblePages: function pdfViewGetVisiblePages() {
  1.3657 +    if (!PresentationMode.active) {
  1.3658 +      return this.getVisibleElements(this.container, this.pages, true);
  1.3659 +    } else {
  1.3660 +      // The algorithm in getVisibleElements doesn't work in all browsers and
  1.3661 +      // configurations when presentation mode is active.
  1.3662 +      var visible = [];
  1.3663 +      var currentPage = this.pages[this.page - 1];
  1.3664 +      visible.push({ id: currentPage.id, view: currentPage });
  1.3665 +      return { first: currentPage, last: currentPage, views: visible };
  1.3666 +    }
  1.3667 +  },
  1.3668 +
  1.3669 +  getVisibleThumbs: function pdfViewGetVisibleThumbs() {
  1.3670 +    return this.getVisibleElements(this.thumbnailContainer, this.thumbnails);
  1.3671 +  },
  1.3672 +
  1.3673 +  // Generic helper to find out what elements are visible within a scroll pane.
  1.3674 +  getVisibleElements: function pdfViewGetVisibleElements(
  1.3675 +      scrollEl, views, sortByVisibility) {
  1.3676 +    var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
  1.3677 +    var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;
  1.3678 +
  1.3679 +    var visible = [], view;
  1.3680 +    var currentHeight, viewHeight, hiddenHeight, percentHeight;
  1.3681 +    var currentWidth, viewWidth;
  1.3682 +    for (var i = 0, ii = views.length; i < ii; ++i) {
  1.3683 +      view = views[i];
  1.3684 +      currentHeight = view.el.offsetTop + view.el.clientTop;
  1.3685 +      viewHeight = view.el.clientHeight;
  1.3686 +      if ((currentHeight + viewHeight) < top) {
  1.3687 +        continue;
  1.3688 +      }
  1.3689 +      if (currentHeight > bottom) {
  1.3690 +        break;
  1.3691 +      }
  1.3692 +      currentWidth = view.el.offsetLeft + view.el.clientLeft;
  1.3693 +      viewWidth = view.el.clientWidth;
  1.3694 +      if ((currentWidth + viewWidth) < left || currentWidth > right) {
  1.3695 +        continue;
  1.3696 +      }
  1.3697 +      hiddenHeight = Math.max(0, top - currentHeight) +
  1.3698 +                     Math.max(0, currentHeight + viewHeight - bottom);
  1.3699 +      percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0;
  1.3700 +
  1.3701 +      visible.push({ id: view.id, x: currentWidth, y: currentHeight,
  1.3702 +                     view: view, percent: percentHeight });
  1.3703 +    }
  1.3704 +
  1.3705 +    var first = visible[0];
  1.3706 +    var last = visible[visible.length - 1];
  1.3707 +
  1.3708 +    if (sortByVisibility) {
  1.3709 +      visible.sort(function(a, b) {
  1.3710 +        var pc = a.percent - b.percent;
  1.3711 +        if (Math.abs(pc) > 0.001) {
  1.3712 +          return -pc;
  1.3713 +        }
  1.3714 +        return a.id - b.id; // ensure stability
  1.3715 +      });
  1.3716 +    }
  1.3717 +    return {first: first, last: last, views: visible};
  1.3718 +  },
  1.3719 +
  1.3720 +  // Helper function to parse query string (e.g. ?param1=value&parm2=...).
  1.3721 +  parseQueryString: function pdfViewParseQueryString(query) {
  1.3722 +    var parts = query.split('&');
  1.3723 +    var params = {};
  1.3724 +    for (var i = 0, ii = parts.length; i < ii; ++i) {
  1.3725 +      var param = parts[i].split('=');
  1.3726 +      var key = param[0];
  1.3727 +      var value = param.length > 1 ? param[1] : null;
  1.3728 +      params[decodeURIComponent(key)] = decodeURIComponent(value);
  1.3729 +    }
  1.3730 +    return params;
  1.3731 +  },
  1.3732 +
  1.3733 +  beforePrint: function pdfViewSetupBeforePrint() {
  1.3734 +    if (!this.supportsPrinting) {
  1.3735 +      var printMessage = mozL10n.get('printing_not_supported', null,
  1.3736 +          'Warning: Printing is not fully supported by this browser.');
  1.3737 +      this.error(printMessage);
  1.3738 +      return;
  1.3739 +    }
  1.3740 +
  1.3741 +    var alertNotReady = false;
  1.3742 +    var i, ii;
  1.3743 +    if (!this.pages.length) {
  1.3744 +      alertNotReady = true;
  1.3745 +    } else {
  1.3746 +      for (i = 0, ii = this.pages.length; i < ii; ++i) {
  1.3747 +        if (!this.pages[i].pdfPage) {
  1.3748 +          alertNotReady = true;
  1.3749 +          break;
  1.3750 +        }
  1.3751 +      }
  1.3752 +    }
  1.3753 +    if (alertNotReady) {
  1.3754 +      var notReadyMessage = mozL10n.get('printing_not_ready', null,
  1.3755 +          'Warning: The PDF is not fully loaded for printing.');
  1.3756 +      window.alert(notReadyMessage);
  1.3757 +      return;
  1.3758 +    }
  1.3759 +
  1.3760 +    var body = document.querySelector('body');
  1.3761 +    body.setAttribute('data-mozPrintCallback', true);
  1.3762 +    for (i = 0, ii = this.pages.length; i < ii; ++i) {
  1.3763 +      this.pages[i].beforePrint();
  1.3764 +    }
  1.3765 +  },
  1.3766 +
  1.3767 +  afterPrint: function pdfViewSetupAfterPrint() {
  1.3768 +    var div = document.getElementById('printContainer');
  1.3769 +    while (div.hasChildNodes()) {
  1.3770 +      div.removeChild(div.lastChild);
  1.3771 +    }
  1.3772 +  },
  1.3773 +
  1.3774 +  rotatePages: function pdfViewRotatePages(delta) {
  1.3775 +    var currentPage = this.pages[this.page - 1];
  1.3776 +    var i, l;
  1.3777 +    this.pageRotation = (this.pageRotation + 360 + delta) % 360;
  1.3778 +
  1.3779 +    for (i = 0, l = this.pages.length; i < l; i++) {
  1.3780 +      var page = this.pages[i];
  1.3781 +      page.update(page.scale, this.pageRotation);
  1.3782 +    }
  1.3783 +
  1.3784 +    for (i = 0, l = this.thumbnails.length; i < l; i++) {
  1.3785 +      var thumb = this.thumbnails[i];
  1.3786 +      thumb.update(this.pageRotation);
  1.3787 +    }
  1.3788 +
  1.3789 +    this.setScale(this.currentScaleValue, true, true);
  1.3790 +
  1.3791 +    this.renderHighestPriority();
  1.3792 +
  1.3793 +    if (currentPage) {
  1.3794 +      currentPage.scrollIntoView();
  1.3795 +    }
  1.3796 +  },
  1.3797 +
  1.3798 +  /**
  1.3799 +   * This function flips the page in presentation mode if the user scrolls up
  1.3800 +   * or down with large enough motion and prevents page flipping too often.
  1.3801 +   *
  1.3802 +   * @this {PDFView}
  1.3803 +   * @param {number} mouseScrollDelta The delta value from the mouse event.
  1.3804 +   */
  1.3805 +  mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) {
  1.3806 +    var MOUSE_SCROLL_COOLDOWN_TIME = 50;
  1.3807 +
  1.3808 +    var currentTime = (new Date()).getTime();
  1.3809 +    var storedTime = this.mouseScrollTimeStamp;
  1.3810 +
  1.3811 +    // In case one page has already been flipped there is a cooldown time
  1.3812 +    // which has to expire before next page can be scrolled on to.
  1.3813 +    if (currentTime > storedTime &&
  1.3814 +        currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
  1.3815 +      return;
  1.3816 +    }
  1.3817 +
  1.3818 +    // In case the user decides to scroll to the opposite direction than before
  1.3819 +    // clear the accumulated delta.
  1.3820 +    if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) ||
  1.3821 +        (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) {
  1.3822 +      this.clearMouseScrollState();
  1.3823 +    }
  1.3824 +
  1.3825 +    this.mouseScrollDelta += mouseScrollDelta;
  1.3826 +
  1.3827 +    var PAGE_FLIP_THRESHOLD = 120;
  1.3828 +    if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) {
  1.3829 +
  1.3830 +      var PageFlipDirection = {
  1.3831 +        UP: -1,
  1.3832 +        DOWN: 1
  1.3833 +      };
  1.3834 +
  1.3835 +      // In presentation mode scroll one page at a time.
  1.3836 +      var pageFlipDirection = (this.mouseScrollDelta > 0) ?
  1.3837 +                                PageFlipDirection.UP :
  1.3838 +                                PageFlipDirection.DOWN;
  1.3839 +      this.clearMouseScrollState();
  1.3840 +      var currentPage = this.page;
  1.3841 +
  1.3842 +      // In case we are already on the first or the last page there is no need
  1.3843 +      // to do anything.
  1.3844 +      if ((currentPage == 1 && pageFlipDirection == PageFlipDirection.UP) ||
  1.3845 +          (currentPage == this.pages.length &&
  1.3846 +           pageFlipDirection == PageFlipDirection.DOWN)) {
  1.3847 +        return;
  1.3848 +      }
  1.3849 +
  1.3850 +      this.page += pageFlipDirection;
  1.3851 +      this.mouseScrollTimeStamp = currentTime;
  1.3852 +    }
  1.3853 +  },
  1.3854 +
  1.3855 +  /**
  1.3856 +   * This function clears the member attributes used with mouse scrolling in
  1.3857 +   * presentation mode.
  1.3858 +   *
  1.3859 +   * @this {PDFView}
  1.3860 +   */
  1.3861 +  clearMouseScrollState: function pdfViewClearMouseScrollState() {
  1.3862 +    this.mouseScrollTimeStamp = 0;
  1.3863 +    this.mouseScrollDelta = 0;
  1.3864 +  }
  1.3865 +};
  1.3866 +
  1.3867 +
  1.3868 +var PageView = function pageView(container, id, scale,
  1.3869 +                                 navigateTo, defaultViewport) {
  1.3870 +  this.id = id;
  1.3871 +
  1.3872 +  this.rotation = 0;
  1.3873 +  this.scale = scale || 1.0;
  1.3874 +  this.viewport = defaultViewport;
  1.3875 +  this.pdfPageRotate = defaultViewport.rotation;
  1.3876 +
  1.3877 +  this.renderingState = RenderingStates.INITIAL;
  1.3878 +  this.resume = null;
  1.3879 +
  1.3880 +  this.textLayer = null;
  1.3881 +
  1.3882 +  this.zoomLayer = null;
  1.3883 +
  1.3884 +  this.annotationLayer = null;
  1.3885 +
  1.3886 +  var anchor = document.createElement('a');
  1.3887 +  anchor.name = '' + this.id;
  1.3888 +
  1.3889 +  var div = this.el = document.createElement('div');
  1.3890 +  div.id = 'pageContainer' + this.id;
  1.3891 +  div.className = 'page';
  1.3892 +  div.style.width = Math.floor(this.viewport.width) + 'px';
  1.3893 +  div.style.height = Math.floor(this.viewport.height) + 'px';
  1.3894 +
  1.3895 +  container.appendChild(anchor);
  1.3896 +  container.appendChild(div);
  1.3897 +
  1.3898 +  this.setPdfPage = function pageViewSetPdfPage(pdfPage) {
  1.3899 +    this.pdfPage = pdfPage;
  1.3900 +    this.pdfPageRotate = pdfPage.rotate;
  1.3901 +    var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
  1.3902 +    this.viewport = pdfPage.getViewport(this.scale * CSS_UNITS, totalRotation);
  1.3903 +    this.stats = pdfPage.stats;
  1.3904 +    this.reset();
  1.3905 +  };
  1.3906 +
  1.3907 +  this.destroy = function pageViewDestroy() {
  1.3908 +    this.zoomLayer = null;
  1.3909 +    this.reset();
  1.3910 +    if (this.pdfPage) {
  1.3911 +      this.pdfPage.destroy();
  1.3912 +    }
  1.3913 +  };
  1.3914 +
  1.3915 +  this.reset = function pageViewReset(keepAnnotations) {
  1.3916 +    if (this.renderTask) {
  1.3917 +      this.renderTask.cancel();
  1.3918 +    }
  1.3919 +    this.resume = null;
  1.3920 +    this.renderingState = RenderingStates.INITIAL;
  1.3921 +
  1.3922 +    div.style.width = Math.floor(this.viewport.width) + 'px';
  1.3923 +    div.style.height = Math.floor(this.viewport.height) + 'px';
  1.3924 +
  1.3925 +    var childNodes = div.childNodes;
  1.3926 +    for (var i = div.childNodes.length - 1; i >= 0; i--) {
  1.3927 +      var node = childNodes[i];
  1.3928 +      if ((this.zoomLayer && this.zoomLayer === node) ||
  1.3929 +          (keepAnnotations && this.annotationLayer === node)) {
  1.3930 +        continue;
  1.3931 +      }
  1.3932 +      div.removeChild(node);
  1.3933 +    }
  1.3934 +    div.removeAttribute('data-loaded');
  1.3935 +
  1.3936 +    if (keepAnnotations) {
  1.3937 +      if (this.annotationLayer) {
  1.3938 +        // Hide annotationLayer until all elements are resized
  1.3939 +        // so they are not displayed on the already-resized page
  1.3940 +        this.annotationLayer.setAttribute('hidden', 'true');
  1.3941 +      }
  1.3942 +    } else {
  1.3943 +      this.annotationLayer = null;
  1.3944 +    }
  1.3945 +
  1.3946 +    delete this.canvas;
  1.3947 +
  1.3948 +    this.loadingIconDiv = document.createElement('div');
  1.3949 +    this.loadingIconDiv.className = 'loadingIcon';
  1.3950 +    div.appendChild(this.loadingIconDiv);
  1.3951 +  };
  1.3952 +
  1.3953 +  this.update = function pageViewUpdate(scale, rotation) {
  1.3954 +    this.scale = scale || this.scale;
  1.3955 +
  1.3956 +    if (typeof rotation !== 'undefined') {
  1.3957 +      this.rotation = rotation;
  1.3958 +    }
  1.3959 +
  1.3960 +    var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
  1.3961 +    this.viewport = this.viewport.clone({
  1.3962 +      scale: this.scale * CSS_UNITS,
  1.3963 +      rotation: totalRotation
  1.3964 +    });
  1.3965 +
  1.3966 +    if (USE_ONLY_CSS_ZOOM && this.canvas) {
  1.3967 +      this.cssTransform(this.canvas);
  1.3968 +      return;
  1.3969 +    } else if (this.canvas && !this.zoomLayer) {
  1.3970 +      this.zoomLayer = this.canvas.parentNode;
  1.3971 +      this.zoomLayer.style.position = 'absolute';
  1.3972 +    }
  1.3973 +    if (this.zoomLayer) {
  1.3974 +      this.cssTransform(this.zoomLayer.firstChild);
  1.3975 +    }
  1.3976 +    this.reset(true);
  1.3977 +  };
  1.3978 +
  1.3979 +  this.cssTransform = function pageCssTransform(canvas) {
  1.3980 +    // Scale canvas, canvas wrapper, and page container.
  1.3981 +    var width = this.viewport.width;
  1.3982 +    var height = this.viewport.height;
  1.3983 +    canvas.style.width = canvas.parentNode.style.width = div.style.width =
  1.3984 +        Math.floor(width) + 'px';
  1.3985 +    canvas.style.height = canvas.parentNode.style.height = div.style.height =
  1.3986 +        Math.floor(height) + 'px';
  1.3987 +    // The canvas may have been originally rotated, so rotate relative to that.
  1.3988 +    var relativeRotation = this.viewport.rotation - canvas._viewport.rotation;
  1.3989 +    var absRotation = Math.abs(relativeRotation);
  1.3990 +    var scaleX = 1, scaleY = 1;
  1.3991 +    if (absRotation === 90 || absRotation === 270) {
  1.3992 +      // Scale x and y because of the rotation.
  1.3993 +      scaleX = height / width;
  1.3994 +      scaleY = width / height;
  1.3995 +    }
  1.3996 +    var cssTransform = 'rotate(' + relativeRotation + 'deg) ' +
  1.3997 +                       'scale(' + scaleX + ',' + scaleY + ')';
  1.3998 +    CustomStyle.setProp('transform', canvas, cssTransform);
  1.3999 +
  1.4000 +    if (this.textLayer) {
  1.4001 +      // Rotating the text layer is more complicated since the divs inside the
  1.4002 +      // the text layer are rotated.
  1.4003 +      // TODO: This could probably be simplified by drawing the text layer in
  1.4004 +      // one orientation then rotating overall.
  1.4005 +      var textRelativeRotation = this.viewport.rotation -
  1.4006 +                                 this.textLayer.viewport.rotation;
  1.4007 +      var textAbsRotation = Math.abs(textRelativeRotation);
  1.4008 +      var scale = (width / canvas.width);
  1.4009 +      if (textAbsRotation === 90 || textAbsRotation === 270) {
  1.4010 +        scale = width / canvas.height;
  1.4011 +      }
  1.4012 +      var textLayerDiv = this.textLayer.textLayerDiv;
  1.4013 +      var transX, transY;
  1.4014 +      switch (textAbsRotation) {
  1.4015 +        case 0:
  1.4016 +          transX = transY = 0;
  1.4017 +          break;
  1.4018 +        case 90:
  1.4019 +          transX = 0;
  1.4020 +          transY = '-' + textLayerDiv.style.height;
  1.4021 +          break;
  1.4022 +        case 180:
  1.4023 +          transX = '-' + textLayerDiv.style.width;
  1.4024 +          transY = '-' + textLayerDiv.style.height;
  1.4025 +          break;
  1.4026 +        case 270:
  1.4027 +          transX = '-' + textLayerDiv.style.width;
  1.4028 +          transY = 0;
  1.4029 +          break;
  1.4030 +        default:
  1.4031 +          console.error('Bad rotation value.');
  1.4032 +          break;
  1.4033 +      }
  1.4034 +      CustomStyle.setProp('transform', textLayerDiv,
  1.4035 +                          'rotate(' + textAbsRotation + 'deg) ' +
  1.4036 +                            'scale(' + scale + ', ' + scale + ') ' +
  1.4037 +                            'translate(' + transX + ', ' + transY + ')');
  1.4038 +      CustomStyle.setProp('transformOrigin', textLayerDiv, '0% 0%');
  1.4039 +    }
  1.4040 +
  1.4041 +    if (USE_ONLY_CSS_ZOOM && this.annotationLayer) {
  1.4042 +      setupAnnotations(div, this.pdfPage, this.viewport);
  1.4043 +    }
  1.4044 +  };
  1.4045 +
  1.4046 +  Object.defineProperty(this, 'width', {
  1.4047 +    get: function PageView_getWidth() {
  1.4048 +      return this.viewport.width;
  1.4049 +    },
  1.4050 +    enumerable: true
  1.4051 +  });
  1.4052 +
  1.4053 +  Object.defineProperty(this, 'height', {
  1.4054 +    get: function PageView_getHeight() {
  1.4055 +      return this.viewport.height;
  1.4056 +    },
  1.4057 +    enumerable: true
  1.4058 +  });
  1.4059 +
  1.4060 +  var self = this;
  1.4061 +
  1.4062 +  function setupAnnotations(pageDiv, pdfPage, viewport) {
  1.4063 +
  1.4064 +    function bindLink(link, dest) {
  1.4065 +      link.href = PDFView.getDestinationHash(dest);
  1.4066 +      link.onclick = function pageViewSetupLinksOnclick() {
  1.4067 +        if (dest) {
  1.4068 +          PDFView.navigateTo(dest);
  1.4069 +        }
  1.4070 +        return false;
  1.4071 +      };
  1.4072 +      if (dest) {
  1.4073 +        link.className = 'internalLink';
  1.4074 +      }
  1.4075 +    }
  1.4076 +
  1.4077 +    function bindNamedAction(link, action) {
  1.4078 +      link.href = PDFView.getAnchorUrl('');
  1.4079 +      link.onclick = function pageViewSetupNamedActionOnClick() {
  1.4080 +        // See PDF reference, table 8.45 - Named action
  1.4081 +        switch (action) {
  1.4082 +          case 'GoToPage':
  1.4083 +            document.getElementById('pageNumber').focus();
  1.4084 +            break;
  1.4085 +
  1.4086 +          case 'GoBack':
  1.4087 +            PDFHistory.back();
  1.4088 +            break;
  1.4089 +
  1.4090 +          case 'GoForward':
  1.4091 +            PDFHistory.forward();
  1.4092 +            break;
  1.4093 +
  1.4094 +          case 'Find':
  1.4095 +            if (!PDFView.supportsIntegratedFind) {
  1.4096 +              PDFFindBar.toggle();
  1.4097 +            }
  1.4098 +            break;
  1.4099 +
  1.4100 +          case 'NextPage':
  1.4101 +            PDFView.page++;
  1.4102 +            break;
  1.4103 +
  1.4104 +          case 'PrevPage':
  1.4105 +            PDFView.page--;
  1.4106 +            break;
  1.4107 +
  1.4108 +          case 'LastPage':
  1.4109 +            PDFView.page = PDFView.pages.length;
  1.4110 +            break;
  1.4111 +
  1.4112 +          case 'FirstPage':
  1.4113 +            PDFView.page = 1;
  1.4114 +            break;
  1.4115 +
  1.4116 +          default:
  1.4117 +            break; // No action according to spec
  1.4118 +        }
  1.4119 +        return false;
  1.4120 +      };
  1.4121 +      link.className = 'internalLink';
  1.4122 +    }
  1.4123 +
  1.4124 +    pdfPage.getAnnotations().then(function(annotationsData) {
  1.4125 +      viewport = viewport.clone({ dontFlip: true });
  1.4126 +      var transform = viewport.transform;
  1.4127 +      var transformStr = 'matrix(' + transform.join(',') + ')';
  1.4128 +      var data, element, i, ii;
  1.4129 +
  1.4130 +      if (self.annotationLayer) {
  1.4131 +        // If an annotationLayer already exists, refresh its children's
  1.4132 +        // transformation matrices
  1.4133 +        for (i = 0, ii = annotationsData.length; i < ii; i++) {
  1.4134 +          data = annotationsData[i];
  1.4135 +          element = self.annotationLayer.querySelector(
  1.4136 +            '[data-annotation-id="' + data.id + '"]');
  1.4137 +          if (element) {
  1.4138 +            CustomStyle.setProp('transform', element, transformStr);
  1.4139 +          }
  1.4140 +        }
  1.4141 +        // See this.reset()
  1.4142 +        self.annotationLayer.removeAttribute('hidden');
  1.4143 +      } else {
  1.4144 +        for (i = 0, ii = annotationsData.length; i < ii; i++) {
  1.4145 +          data = annotationsData[i];
  1.4146 +          var annotation = PDFJS.Annotation.fromData(data);
  1.4147 +          if (!annotation || !annotation.hasHtml()) {
  1.4148 +            continue;
  1.4149 +          }
  1.4150 +
  1.4151 +          element = annotation.getHtmlElement(pdfPage.commonObjs);
  1.4152 +          element.setAttribute('data-annotation-id', data.id);
  1.4153 +          mozL10n.translate(element);
  1.4154 +
  1.4155 +          data = annotation.getData();
  1.4156 +          var rect = data.rect;
  1.4157 +          var view = pdfPage.view;
  1.4158 +          rect = PDFJS.Util.normalizeRect([
  1.4159 +            rect[0],
  1.4160 +            view[3] - rect[1] + view[1],
  1.4161 +            rect[2],
  1.4162 +            view[3] - rect[3] + view[1]
  1.4163 +          ]);
  1.4164 +          element.style.left = rect[0] + 'px';
  1.4165 +          element.style.top = rect[1] + 'px';
  1.4166 +          element.style.position = 'absolute';
  1.4167 +
  1.4168 +          CustomStyle.setProp('transform', element, transformStr);
  1.4169 +          var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
  1.4170 +          CustomStyle.setProp('transformOrigin', element, transformOriginStr);
  1.4171 +
  1.4172 +          if (data.subtype === 'Link' && !data.url) {
  1.4173 +            var link = element.getElementsByTagName('a')[0];
  1.4174 +            if (link) {
  1.4175 +              if (data.action) {
  1.4176 +                bindNamedAction(link, data.action);
  1.4177 +              } else {
  1.4178 +                bindLink(link, ('dest' in data) ? data.dest : null);
  1.4179 +              }
  1.4180 +            }
  1.4181 +          }
  1.4182 +
  1.4183 +          if (!self.annotationLayer) {
  1.4184 +            var annotationLayerDiv = document.createElement('div');
  1.4185 +            annotationLayerDiv.className = 'annotationLayer';
  1.4186 +            pageDiv.appendChild(annotationLayerDiv);
  1.4187 +            self.annotationLayer = annotationLayerDiv;
  1.4188 +          }
  1.4189 +
  1.4190 +          self.annotationLayer.appendChild(element);
  1.4191 +        }
  1.4192 +      }
  1.4193 +    });
  1.4194 +  }
  1.4195 +
  1.4196 +  this.getPagePoint = function pageViewGetPagePoint(x, y) {
  1.4197 +    return this.viewport.convertToPdfPoint(x, y);
  1.4198 +  };
  1.4199 +
  1.4200 +  this.scrollIntoView = function pageViewScrollIntoView(dest) {
  1.4201 +    if (PresentationMode.active) {
  1.4202 +      if (PDFView.page !== this.id) {
  1.4203 +        // Avoid breaking PDFView.getVisiblePages in presentation mode.
  1.4204 +        PDFView.page = this.id;
  1.4205 +        return;
  1.4206 +      }
  1.4207 +      dest = null;
  1.4208 +      PDFView.setScale(PDFView.currentScaleValue, true, true);
  1.4209 +    }
  1.4210 +    if (!dest) {
  1.4211 +      scrollIntoView(div);
  1.4212 +      return;
  1.4213 +    }
  1.4214 +
  1.4215 +    var x = 0, y = 0;
  1.4216 +    var width = 0, height = 0, widthScale, heightScale;
  1.4217 +    var changeOrientation = (this.rotation % 180 === 0 ? false : true);
  1.4218 +    var pageWidth = (changeOrientation ? this.height : this.width) /
  1.4219 +      this.scale / CSS_UNITS;
  1.4220 +    var pageHeight = (changeOrientation ? this.width : this.height) /
  1.4221 +      this.scale / CSS_UNITS;
  1.4222 +    var scale = 0;
  1.4223 +    switch (dest[1].name) {
  1.4224 +      case 'XYZ':
  1.4225 +        x = dest[2];
  1.4226 +        y = dest[3];
  1.4227 +        scale = dest[4];
  1.4228 +        // If x and/or y coordinates are not supplied, default to
  1.4229 +        // _top_ left of the page (not the obvious bottom left,
  1.4230 +        // since aligning the bottom of the intended page with the
  1.4231 +        // top of the window is rarely helpful).
  1.4232 +        x = x !== null ? x : 0;
  1.4233 +        y = y !== null ? y : pageHeight;
  1.4234 +        break;
  1.4235 +      case 'Fit':
  1.4236 +      case 'FitB':
  1.4237 +        scale = 'page-fit';
  1.4238 +        break;
  1.4239 +      case 'FitH':
  1.4240 +      case 'FitBH':
  1.4241 +        y = dest[2];
  1.4242 +        scale = 'page-width';
  1.4243 +        break;
  1.4244 +      case 'FitV':
  1.4245 +      case 'FitBV':
  1.4246 +        x = dest[2];
  1.4247 +        width = pageWidth;
  1.4248 +        height = pageHeight;
  1.4249 +        scale = 'page-height';
  1.4250 +        break;
  1.4251 +      case 'FitR':
  1.4252 +        x = dest[2];
  1.4253 +        y = dest[3];
  1.4254 +        width = dest[4] - x;
  1.4255 +        height = dest[5] - y;
  1.4256 +        widthScale = (PDFView.container.clientWidth - SCROLLBAR_PADDING) /
  1.4257 +          width / CSS_UNITS;
  1.4258 +        heightScale = (PDFView.container.clientHeight - SCROLLBAR_PADDING) /
  1.4259 +          height / CSS_UNITS;
  1.4260 +        scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
  1.4261 +        break;
  1.4262 +      default:
  1.4263 +        return;
  1.4264 +    }
  1.4265 +
  1.4266 +    if (scale && scale !== PDFView.currentScale) {
  1.4267 +      PDFView.setScale(scale, true, true);
  1.4268 +    } else if (PDFView.currentScale === UNKNOWN_SCALE) {
  1.4269 +      PDFView.setScale(DEFAULT_SCALE, true, true);
  1.4270 +    }
  1.4271 +
  1.4272 +    if (scale === 'page-fit' && !dest[4]) {
  1.4273 +      scrollIntoView(div);
  1.4274 +      return;
  1.4275 +    }
  1.4276 +
  1.4277 +    var boundingRect = [
  1.4278 +      this.viewport.convertToViewportPoint(x, y),
  1.4279 +      this.viewport.convertToViewportPoint(x + width, y + height)
  1.4280 +    ];
  1.4281 +    var left = Math.min(boundingRect[0][0], boundingRect[1][0]);
  1.4282 +    var top = Math.min(boundingRect[0][1], boundingRect[1][1]);
  1.4283 +
  1.4284 +    scrollIntoView(div, { left: left, top: top });
  1.4285 +  };
  1.4286 +
  1.4287 +  this.getTextContent = function pageviewGetTextContent() {
  1.4288 +    return PDFView.getPage(this.id).then(function(pdfPage) {
  1.4289 +      return pdfPage.getTextContent();
  1.4290 +    });
  1.4291 +  };
  1.4292 +
  1.4293 +  this.draw = function pageviewDraw(callback) {
  1.4294 +    var pdfPage = this.pdfPage;
  1.4295 +
  1.4296 +    if (this.pagePdfPromise) {
  1.4297 +      return;
  1.4298 +    }
  1.4299 +    if (!pdfPage) {
  1.4300 +      var promise = PDFView.getPage(this.id);
  1.4301 +      promise.then(function(pdfPage) {
  1.4302 +        delete this.pagePdfPromise;
  1.4303 +        this.setPdfPage(pdfPage);
  1.4304 +        this.draw(callback);
  1.4305 +      }.bind(this));
  1.4306 +      this.pagePdfPromise = promise;
  1.4307 +      return;
  1.4308 +    }
  1.4309 +
  1.4310 +    if (this.renderingState !== RenderingStates.INITIAL) {
  1.4311 +      console.error('Must be in new state before drawing');
  1.4312 +    }
  1.4313 +
  1.4314 +    this.renderingState = RenderingStates.RUNNING;
  1.4315 +
  1.4316 +    var viewport = this.viewport;
  1.4317 +    // Wrap the canvas so if it has a css transform for highdpi the overflow
  1.4318 +    // will be hidden in FF.
  1.4319 +    var canvasWrapper = document.createElement('div');
  1.4320 +    canvasWrapper.style.width = div.style.width;
  1.4321 +    canvasWrapper.style.height = div.style.height;
  1.4322 +    canvasWrapper.classList.add('canvasWrapper');
  1.4323 +
  1.4324 +    var canvas = document.createElement('canvas');
  1.4325 +    canvas.id = 'page' + this.id;
  1.4326 +    canvasWrapper.appendChild(canvas);
  1.4327 +    if (this.annotationLayer) {
  1.4328 +      // annotationLayer needs to stay on top
  1.4329 +      div.insertBefore(canvasWrapper, this.annotationLayer);
  1.4330 +    } else {
  1.4331 +      div.appendChild(canvasWrapper);
  1.4332 +    }
  1.4333 +    this.canvas = canvas;
  1.4334 +
  1.4335 +    var ctx = canvas.getContext('2d');
  1.4336 +    var outputScale = getOutputScale(ctx);
  1.4337 +
  1.4338 +    if (USE_ONLY_CSS_ZOOM) {
  1.4339 +      var actualSizeViewport = viewport.clone({ scale: CSS_UNITS });
  1.4340 +      // Use a scale that will make the canvas be the original intended size
  1.4341 +      // of the page.
  1.4342 +      outputScale.sx *= actualSizeViewport.width / viewport.width;
  1.4343 +      outputScale.sy *= actualSizeViewport.height / viewport.height;
  1.4344 +      outputScale.scaled = true;
  1.4345 +    }
  1.4346 +
  1.4347 +    canvas.width = (Math.floor(viewport.width) * outputScale.sx) | 0;
  1.4348 +    canvas.height = (Math.floor(viewport.height) * outputScale.sy) | 0;
  1.4349 +    canvas.style.width = Math.floor(viewport.width) + 'px';
  1.4350 +    canvas.style.height = Math.floor(viewport.height) + 'px';
  1.4351 +    // Add the viewport so it's known what it was originally drawn with.
  1.4352 +    canvas._viewport = viewport;
  1.4353 +
  1.4354 +    var textLayerDiv = null;
  1.4355 +    if (!PDFJS.disableTextLayer) {
  1.4356 +      textLayerDiv = document.createElement('div');
  1.4357 +      textLayerDiv.className = 'textLayer';
  1.4358 +      textLayerDiv.style.width = canvas.style.width;
  1.4359 +      textLayerDiv.style.height = canvas.style.height;
  1.4360 +      if (this.annotationLayer) {
  1.4361 +        // annotationLayer needs to stay on top
  1.4362 +        div.insertBefore(textLayerDiv, this.annotationLayer);
  1.4363 +      } else {
  1.4364 +        div.appendChild(textLayerDiv);
  1.4365 +      }
  1.4366 +    }
  1.4367 +    var textLayer = this.textLayer =
  1.4368 +      textLayerDiv ? new TextLayerBuilder({
  1.4369 +        textLayerDiv: textLayerDiv,
  1.4370 +        pageIndex: this.id - 1,
  1.4371 +        lastScrollSource: PDFView,
  1.4372 +        viewport: this.viewport,
  1.4373 +        isViewerInPresentationMode: PresentationMode.active
  1.4374 +      }) : null;
  1.4375 +    // TODO(mack): use data attributes to store these
  1.4376 +    ctx._scaleX = outputScale.sx;
  1.4377 +    ctx._scaleY = outputScale.sy;
  1.4378 +    if (outputScale.scaled) {
  1.4379 +      ctx.scale(outputScale.sx, outputScale.sy);
  1.4380 +    }
  1.4381 +
  1.4382 +    // Rendering area
  1.4383 +
  1.4384 +    var self = this;
  1.4385 +    function pageViewDrawCallback(error) {
  1.4386 +      // The renderTask may have been replaced by a new one, so only remove the
  1.4387 +      // reference to the renderTask if it matches the one that is triggering
  1.4388 +      // this callback.
  1.4389 +      if (renderTask === self.renderTask) {
  1.4390 +        self.renderTask = null;
  1.4391 +      }
  1.4392 +
  1.4393 +      if (error === 'cancelled') {
  1.4394 +        return;
  1.4395 +      }
  1.4396 +
  1.4397 +      self.renderingState = RenderingStates.FINISHED;
  1.4398 +
  1.4399 +      if (self.loadingIconDiv) {
  1.4400 +        div.removeChild(self.loadingIconDiv);
  1.4401 +        delete self.loadingIconDiv;
  1.4402 +      }
  1.4403 +
  1.4404 +      if (self.zoomLayer) {
  1.4405 +        div.removeChild(self.zoomLayer);
  1.4406 +        self.zoomLayer = null;
  1.4407 +      }
  1.4408 +
  1.4409 +      if (self.textLayer && self.textLayer.textDivs &&
  1.4410 +          self.textLayer.textDivs.length > 0 &&
  1.4411 +          !PDFView.supportsDocumentColors) {
  1.4412 +        console.error(mozL10n.get('document_colors_disabled', null,
  1.4413 +          'PDF documents are not allowed to use their own colors: ' +
  1.4414 +          '\'Allow pages to choose their own colors\' ' +
  1.4415 +          'is deactivated in the browser.'));
  1.4416 +        PDFView.fallback();
  1.4417 +      }
  1.4418 +      if (error) {
  1.4419 +        PDFView.error(mozL10n.get('rendering_error', null,
  1.4420 +          'An error occurred while rendering the page.'), error);
  1.4421 +      }
  1.4422 +
  1.4423 +      self.stats = pdfPage.stats;
  1.4424 +      self.updateStats();
  1.4425 +      if (self.onAfterDraw) {
  1.4426 +        self.onAfterDraw();
  1.4427 +      }
  1.4428 +
  1.4429 +      cache.push(self);
  1.4430 +
  1.4431 +      var event = document.createEvent('CustomEvent');
  1.4432 +      event.initCustomEvent('pagerender', true, true, {
  1.4433 +        pageNumber: pdfPage.pageNumber
  1.4434 +      });
  1.4435 +      div.dispatchEvent(event);
  1.4436 +
  1.4437 +      FirefoxCom.request('reportTelemetry', JSON.stringify({
  1.4438 +        type: 'pageInfo'
  1.4439 +      }));
  1.4440 +      // TODO add stream types report here
  1.4441 +      callback();
  1.4442 +    }
  1.4443 +
  1.4444 +    var renderContext = {
  1.4445 +      canvasContext: ctx,
  1.4446 +      viewport: this.viewport,
  1.4447 +      textLayer: textLayer,
  1.4448 +      // intent: 'default', // === 'display'
  1.4449 +      continueCallback: function pdfViewcContinueCallback(cont) {
  1.4450 +        if (PDFView.highestPriorityPage !== 'page' + self.id) {
  1.4451 +          self.renderingState = RenderingStates.PAUSED;
  1.4452 +          self.resume = function resumeCallback() {
  1.4453 +            self.renderingState = RenderingStates.RUNNING;
  1.4454 +            cont();
  1.4455 +          };
  1.4456 +          return;
  1.4457 +        }
  1.4458 +        cont();
  1.4459 +      }
  1.4460 +    };
  1.4461 +    var renderTask = this.renderTask = this.pdfPage.render(renderContext);
  1.4462 +
  1.4463 +    this.renderTask.promise.then(
  1.4464 +      function pdfPageRenderCallback() {
  1.4465 +        pageViewDrawCallback(null);
  1.4466 +        if (textLayer) {
  1.4467 +          self.getTextContent().then(
  1.4468 +            function textContentResolved(textContent) {
  1.4469 +              textLayer.setTextContent(textContent);
  1.4470 +            }
  1.4471 +          );
  1.4472 +        }
  1.4473 +      },
  1.4474 +      function pdfPageRenderError(error) {
  1.4475 +        pageViewDrawCallback(error);
  1.4476 +      }
  1.4477 +    );
  1.4478 +
  1.4479 +    setupAnnotations(div, pdfPage, this.viewport);
  1.4480 +    div.setAttribute('data-loaded', true);
  1.4481 +  };
  1.4482 +
  1.4483 +  this.beforePrint = function pageViewBeforePrint() {
  1.4484 +    var pdfPage = this.pdfPage;
  1.4485 +
  1.4486 +    var viewport = pdfPage.getViewport(1);
  1.4487 +    // Use the same hack we use for high dpi displays for printing to get better
  1.4488 +    // output until bug 811002 is fixed in FF.
  1.4489 +    var PRINT_OUTPUT_SCALE = 2;
  1.4490 +    var canvas = document.createElement('canvas');
  1.4491 +    canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE;
  1.4492 +    canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE;
  1.4493 +    canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt';
  1.4494 +    canvas.style.height = (PRINT_OUTPUT_SCALE * viewport.height) + 'pt';
  1.4495 +    var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' +
  1.4496 +                              (1 / PRINT_OUTPUT_SCALE) + ')';
  1.4497 +    CustomStyle.setProp('transform' , canvas, cssScale);
  1.4498 +    CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
  1.4499 +
  1.4500 +    var printContainer = document.getElementById('printContainer');
  1.4501 +    var canvasWrapper = document.createElement('div');
  1.4502 +    canvasWrapper.style.width = viewport.width + 'pt';
  1.4503 +    canvasWrapper.style.height = viewport.height + 'pt';
  1.4504 +    canvasWrapper.appendChild(canvas);
  1.4505 +    printContainer.appendChild(canvasWrapper);
  1.4506 +
  1.4507 +    canvas.mozPrintCallback = function(obj) {
  1.4508 +      var ctx = obj.context;
  1.4509 +
  1.4510 +      ctx.save();
  1.4511 +      ctx.fillStyle = 'rgb(255, 255, 255)';
  1.4512 +      ctx.fillRect(0, 0, canvas.width, canvas.height);
  1.4513 +      ctx.restore();
  1.4514 +      ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE);
  1.4515 +
  1.4516 +      var renderContext = {
  1.4517 +        canvasContext: ctx,
  1.4518 +        viewport: viewport,
  1.4519 +        intent: 'print'
  1.4520 +      };
  1.4521 +
  1.4522 +      pdfPage.render(renderContext).promise.then(function() {
  1.4523 +        // Tell the printEngine that rendering this canvas/page has finished.
  1.4524 +        obj.done();
  1.4525 +      }, function(error) {
  1.4526 +        console.error(error);
  1.4527 +        // Tell the printEngine that rendering this canvas/page has failed.
  1.4528 +        // This will make the print proces stop.
  1.4529 +        if ('abort' in obj) {
  1.4530 +          obj.abort();
  1.4531 +        } else {
  1.4532 +          obj.done();
  1.4533 +        }
  1.4534 +      });
  1.4535 +    };
  1.4536 +  };
  1.4537 +
  1.4538 +  this.updateStats = function pageViewUpdateStats() {
  1.4539 +    if (!this.stats) {
  1.4540 +      return;
  1.4541 +    }
  1.4542 +
  1.4543 +    if (PDFJS.pdfBug && Stats.enabled) {
  1.4544 +      var stats = this.stats;
  1.4545 +      Stats.add(this.id, stats);
  1.4546 +    }
  1.4547 +  };
  1.4548 +};
  1.4549 +
  1.4550 +
  1.4551 +var ThumbnailView = function thumbnailView(container, id, defaultViewport) {
  1.4552 +  var anchor = document.createElement('a');
  1.4553 +  anchor.href = PDFView.getAnchorUrl('#page=' + id);
  1.4554 +  anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}');
  1.4555 +  anchor.onclick = function stopNavigation() {
  1.4556 +    PDFView.page = id;
  1.4557 +    return false;
  1.4558 +  };
  1.4559 +
  1.4560 +  this.pdfPage = undefined;
  1.4561 +  this.viewport = defaultViewport;
  1.4562 +  this.pdfPageRotate = defaultViewport.rotation;
  1.4563 +
  1.4564 +  this.rotation = 0;
  1.4565 +  this.pageWidth = this.viewport.width;
  1.4566 +  this.pageHeight = this.viewport.height;
  1.4567 +  this.pageRatio = this.pageWidth / this.pageHeight;
  1.4568 +  this.id = id;
  1.4569 +
  1.4570 +  this.canvasWidth = 98;
  1.4571 +  this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight;
  1.4572 +  this.scale = (this.canvasWidth / this.pageWidth);
  1.4573 +
  1.4574 +  var div = this.el = document.createElement('div');
  1.4575 +  div.id = 'thumbnailContainer' + id;
  1.4576 +  div.className = 'thumbnail';
  1.4577 +
  1.4578 +  if (id === 1) {
  1.4579 +    // Highlight the thumbnail of the first page when no page number is
  1.4580 +    // specified (or exists in cache) when the document is loaded.
  1.4581 +    div.classList.add('selected');
  1.4582 +  }
  1.4583 +
  1.4584 +  var ring = document.createElement('div');
  1.4585 +  ring.className = 'thumbnailSelectionRing';
  1.4586 +  ring.style.width = this.canvasWidth + 'px';
  1.4587 +  ring.style.height = this.canvasHeight + 'px';
  1.4588 +
  1.4589 +  div.appendChild(ring);
  1.4590 +  anchor.appendChild(div);
  1.4591 +  container.appendChild(anchor);
  1.4592 +
  1.4593 +  this.hasImage = false;
  1.4594 +  this.renderingState = RenderingStates.INITIAL;
  1.4595 +
  1.4596 +  this.setPdfPage = function thumbnailViewSetPdfPage(pdfPage) {
  1.4597 +    this.pdfPage = pdfPage;
  1.4598 +    this.pdfPageRotate = pdfPage.rotate;
  1.4599 +    var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
  1.4600 +    this.viewport = pdfPage.getViewport(1, totalRotation);
  1.4601 +    this.update();
  1.4602 +  };
  1.4603 +
  1.4604 +  this.update = function thumbnailViewUpdate(rotation) {
  1.4605 +    if (rotation !== undefined) {
  1.4606 +      this.rotation = rotation;
  1.4607 +    }
  1.4608 +    var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
  1.4609 +    this.viewport = this.viewport.clone({
  1.4610 +      scale: 1,
  1.4611 +      rotation: totalRotation
  1.4612 +    });
  1.4613 +    this.pageWidth = this.viewport.width;
  1.4614 +    this.pageHeight = this.viewport.height;
  1.4615 +    this.pageRatio = this.pageWidth / this.pageHeight;
  1.4616 +
  1.4617 +    this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight;
  1.4618 +    this.scale = (this.canvasWidth / this.pageWidth);
  1.4619 +
  1.4620 +    div.removeAttribute('data-loaded');
  1.4621 +    ring.textContent = '';
  1.4622 +    ring.style.width = this.canvasWidth + 'px';
  1.4623 +    ring.style.height = this.canvasHeight + 'px';
  1.4624 +
  1.4625 +    this.hasImage = false;
  1.4626 +    this.renderingState = RenderingStates.INITIAL;
  1.4627 +    this.resume = null;
  1.4628 +  };
  1.4629 +
  1.4630 +  this.getPageDrawContext = function thumbnailViewGetPageDrawContext() {
  1.4631 +    var canvas = document.createElement('canvas');
  1.4632 +    canvas.id = 'thumbnail' + id;
  1.4633 +
  1.4634 +    canvas.width = this.canvasWidth;
  1.4635 +    canvas.height = this.canvasHeight;
  1.4636 +    canvas.className = 'thumbnailImage';
  1.4637 +    canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas',
  1.4638 +      {page: id}, 'Thumbnail of Page {{page}}'));
  1.4639 +
  1.4640 +    div.setAttribute('data-loaded', true);
  1.4641 +
  1.4642 +    ring.appendChild(canvas);
  1.4643 +
  1.4644 +    var ctx = canvas.getContext('2d');
  1.4645 +    ctx.save();
  1.4646 +    ctx.fillStyle = 'rgb(255, 255, 255)';
  1.4647 +    ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
  1.4648 +    ctx.restore();
  1.4649 +    return ctx;
  1.4650 +  };
  1.4651 +
  1.4652 +  this.drawingRequired = function thumbnailViewDrawingRequired() {
  1.4653 +    return !this.hasImage;
  1.4654 +  };
  1.4655 +
  1.4656 +  this.draw = function thumbnailViewDraw(callback) {
  1.4657 +    if (!this.pdfPage) {
  1.4658 +      var promise = PDFView.getPage(this.id);
  1.4659 +      promise.then(function(pdfPage) {
  1.4660 +        this.setPdfPage(pdfPage);
  1.4661 +        this.draw(callback);
  1.4662 +      }.bind(this));
  1.4663 +      return;
  1.4664 +    }
  1.4665 +
  1.4666 +    if (this.renderingState !== RenderingStates.INITIAL) {
  1.4667 +      console.error('Must be in new state before drawing');
  1.4668 +    }
  1.4669 +
  1.4670 +    this.renderingState = RenderingStates.RUNNING;
  1.4671 +    if (this.hasImage) {
  1.4672 +      callback();
  1.4673 +      return;
  1.4674 +    }
  1.4675 +
  1.4676 +    var self = this;
  1.4677 +    var ctx = this.getPageDrawContext();
  1.4678 +    var drawViewport = this.viewport.clone({ scale: this.scale });
  1.4679 +    var renderContext = {
  1.4680 +      canvasContext: ctx,
  1.4681 +      viewport: drawViewport,
  1.4682 +      continueCallback: function(cont) {
  1.4683 +        if (PDFView.highestPriorityPage !== 'thumbnail' + self.id) {
  1.4684 +          self.renderingState = RenderingStates.PAUSED;
  1.4685 +          self.resume = function() {
  1.4686 +            self.renderingState = RenderingStates.RUNNING;
  1.4687 +            cont();
  1.4688 +          };
  1.4689 +          return;
  1.4690 +        }
  1.4691 +        cont();
  1.4692 +      }
  1.4693 +    };
  1.4694 +    this.pdfPage.render(renderContext).promise.then(
  1.4695 +      function pdfPageRenderCallback() {
  1.4696 +        self.renderingState = RenderingStates.FINISHED;
  1.4697 +        callback();
  1.4698 +      },
  1.4699 +      function pdfPageRenderError(error) {
  1.4700 +        self.renderingState = RenderingStates.FINISHED;
  1.4701 +        callback();
  1.4702 +      }
  1.4703 +    );
  1.4704 +    this.hasImage = true;
  1.4705 +  };
  1.4706 +
  1.4707 +  this.setImage = function thumbnailViewSetImage(img) {
  1.4708 +    if (!this.pdfPage) {
  1.4709 +      var promise = PDFView.getPage(this.id);
  1.4710 +      promise.then(function(pdfPage) {
  1.4711 +        this.setPdfPage(pdfPage);
  1.4712 +        this.setImage(img);
  1.4713 +      }.bind(this));
  1.4714 +      return;
  1.4715 +    }
  1.4716 +    if (this.hasImage || !img) {
  1.4717 +      return;
  1.4718 +    }
  1.4719 +    this.renderingState = RenderingStates.FINISHED;
  1.4720 +    var ctx = this.getPageDrawContext();
  1.4721 +    ctx.drawImage(img, 0, 0, img.width, img.height,
  1.4722 +                  0, 0, ctx.canvas.width, ctx.canvas.height);
  1.4723 +
  1.4724 +    this.hasImage = true;
  1.4725 +  };
  1.4726 +};
  1.4727 +
  1.4728 +
  1.4729 +var FIND_SCROLL_OFFSET_TOP = -50;
  1.4730 +var FIND_SCROLL_OFFSET_LEFT = -400;
  1.4731 +
  1.4732 +/**
  1.4733 + * TextLayerBuilder provides text-selection
  1.4734 + * functionality for the PDF. It does this
  1.4735 + * by creating overlay divs over the PDF
  1.4736 + * text. This divs contain text that matches
  1.4737 + * the PDF text they are overlaying. This
  1.4738 + * object also provides for a way to highlight
  1.4739 + * text that is being searched for.
  1.4740 + */
  1.4741 +var TextLayerBuilder = function textLayerBuilder(options) {
  1.4742 +  var textLayerFrag = document.createDocumentFragment();
  1.4743 +
  1.4744 +  this.textLayerDiv = options.textLayerDiv;
  1.4745 +  this.layoutDone = false;
  1.4746 +  this.divContentDone = false;
  1.4747 +  this.pageIdx = options.pageIndex;
  1.4748 +  this.matches = [];
  1.4749 +  this.lastScrollSource = options.lastScrollSource;
  1.4750 +  this.viewport = options.viewport;
  1.4751 +  this.isViewerInPresentationMode = options.isViewerInPresentationMode;
  1.4752 +  this.textDivs = [];
  1.4753 +
  1.4754 +  if (typeof PDFFindController === 'undefined') {
  1.4755 +    window.PDFFindController = null;
  1.4756 +  }
  1.4757 +
  1.4758 +  if (typeof this.lastScrollSource === 'undefined') {
  1.4759 +    this.lastScrollSource = null;
  1.4760 +  }
  1.4761 +
  1.4762 +  this.renderLayer = function textLayerBuilderRenderLayer() {
  1.4763 +    var textDivs = this.textDivs;
  1.4764 +    var canvas = document.createElement('canvas');
  1.4765 +    var ctx = canvas.getContext('2d');
  1.4766 +
  1.4767 +    // No point in rendering so many divs as it'd make the browser unusable
  1.4768 +    // even after the divs are rendered
  1.4769 +    var MAX_TEXT_DIVS_TO_RENDER = 100000;
  1.4770 +    if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) {
  1.4771 +      return;
  1.4772 +    }
  1.4773 +
  1.4774 +    for (var i = 0, ii = textDivs.length; i < ii; i++) {
  1.4775 +      var textDiv = textDivs[i];
  1.4776 +      if ('isWhitespace' in textDiv.dataset) {
  1.4777 +        continue;
  1.4778 +      }
  1.4779 +
  1.4780 +      ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
  1.4781 +      var width = ctx.measureText(textDiv.textContent).width;
  1.4782 +
  1.4783 +      if (width > 0) {
  1.4784 +        textLayerFrag.appendChild(textDiv);
  1.4785 +        var textScale = textDiv.dataset.canvasWidth / width;
  1.4786 +        var rotation = textDiv.dataset.angle;
  1.4787 +        var transform = 'scale(' + textScale + ', 1)';
  1.4788 +        transform = 'rotate(' + rotation + 'deg) ' + transform;
  1.4789 +        CustomStyle.setProp('transform' , textDiv, transform);
  1.4790 +        CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
  1.4791 +      }
  1.4792 +    }
  1.4793 +
  1.4794 +    this.textLayerDiv.appendChild(textLayerFrag);
  1.4795 +    this.renderingDone = true;
  1.4796 +    this.updateMatches();
  1.4797 +  };
  1.4798 +
  1.4799 +  this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
  1.4800 +    // Schedule renderLayout() if user has been scrolling, otherwise
  1.4801 +    // run it right away
  1.4802 +    var RENDER_DELAY = 200; // in ms
  1.4803 +    var self = this;
  1.4804 +    var lastScroll = (this.lastScrollSource === null ?
  1.4805 +                      0 : this.lastScrollSource.lastScroll);
  1.4806 +
  1.4807 +    if (Date.now() - lastScroll > RENDER_DELAY) {
  1.4808 +      // Render right away
  1.4809 +      this.renderLayer();
  1.4810 +    } else {
  1.4811 +      // Schedule
  1.4812 +      if (this.renderTimer) {
  1.4813 +        clearTimeout(this.renderTimer);
  1.4814 +      }
  1.4815 +      this.renderTimer = setTimeout(function() {
  1.4816 +        self.setupRenderLayoutTimer();
  1.4817 +      }, RENDER_DELAY);
  1.4818 +    }
  1.4819 +  };
  1.4820 +
  1.4821 +  this.appendText = function textLayerBuilderAppendText(geom, styles) {
  1.4822 +    var style = styles[geom.fontName];
  1.4823 +    var textDiv = document.createElement('div');
  1.4824 +    this.textDivs.push(textDiv);
  1.4825 +    if (!/\S/.test(geom.str)) {
  1.4826 +      textDiv.dataset.isWhitespace = true;
  1.4827 +      return;
  1.4828 +    }
  1.4829 +    var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform);
  1.4830 +    var angle = Math.atan2(tx[1], tx[0]);
  1.4831 +    if (style.vertical) {
  1.4832 +      angle += Math.PI / 2;
  1.4833 +    }
  1.4834 +    var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]));
  1.4835 +    var fontAscent = (style.ascent ? style.ascent * fontHeight :
  1.4836 +      (style.descent ? (1 + style.descent) * fontHeight : fontHeight));
  1.4837 +
  1.4838 +    textDiv.style.position = 'absolute';
  1.4839 +    textDiv.style.left = (tx[4] + (fontAscent * Math.sin(angle))) + 'px';
  1.4840 +    textDiv.style.top = (tx[5] - (fontAscent * Math.cos(angle))) + 'px';
  1.4841 +    textDiv.style.fontSize = fontHeight + 'px';
  1.4842 +    textDiv.style.fontFamily = style.fontFamily;
  1.4843 +
  1.4844 +    textDiv.textContent = geom.str;
  1.4845 +    textDiv.dataset.fontName = geom.fontName;
  1.4846 +    textDiv.dataset.angle = angle * (180 / Math.PI);
  1.4847 +    if (style.vertical) {
  1.4848 +      textDiv.dataset.canvasWidth = geom.height * this.viewport.scale;
  1.4849 +    } else {
  1.4850 +      textDiv.dataset.canvasWidth = geom.width * this.viewport.scale;
  1.4851 +    }
  1.4852 +
  1.4853 +  };
  1.4854 +
  1.4855 +  this.setTextContent = function textLayerBuilderSetTextContent(textContent) {
  1.4856 +    this.textContent = textContent;
  1.4857 +
  1.4858 +    var textItems = textContent.items;
  1.4859 +    for (var i = 0; i < textItems.length; i++) {
  1.4860 +      this.appendText(textItems[i], textContent.styles);
  1.4861 +    }
  1.4862 +    this.divContentDone = true;
  1.4863 +
  1.4864 +    this.setupRenderLayoutTimer();
  1.4865 +  };
  1.4866 +
  1.4867 +  this.convertMatches = function textLayerBuilderConvertMatches(matches) {
  1.4868 +    var i = 0;
  1.4869 +    var iIndex = 0;
  1.4870 +    var bidiTexts = this.textContent.items;
  1.4871 +    var end = bidiTexts.length - 1;
  1.4872 +    var queryLen = (PDFFindController === null ?
  1.4873 +                    0 : PDFFindController.state.query.length);
  1.4874 +
  1.4875 +    var ret = [];
  1.4876 +
  1.4877 +    // Loop over all the matches.
  1.4878 +    for (var m = 0; m < matches.length; m++) {
  1.4879 +      var matchIdx = matches[m];
  1.4880 +      // # Calculate the begin position.
  1.4881 +
  1.4882 +      // Loop over the divIdxs.
  1.4883 +      while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
  1.4884 +        iIndex += bidiTexts[i].str.length;
  1.4885 +        i++;
  1.4886 +      }
  1.4887 +
  1.4888 +      // TODO: Do proper handling here if something goes wrong.
  1.4889 +      if (i == bidiTexts.length) {
  1.4890 +        console.error('Could not find matching mapping');
  1.4891 +      }
  1.4892 +
  1.4893 +      var match = {
  1.4894 +        begin: {
  1.4895 +          divIdx: i,
  1.4896 +          offset: matchIdx - iIndex
  1.4897 +        }
  1.4898 +      };
  1.4899 +
  1.4900 +      // # Calculate the end position.
  1.4901 +      matchIdx += queryLen;
  1.4902 +
  1.4903 +      // Somewhat same array as above, but use a > instead of >= to get the end
  1.4904 +      // position right.
  1.4905 +      while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
  1.4906 +        iIndex += bidiTexts[i].str.length;
  1.4907 +        i++;
  1.4908 +      }
  1.4909 +
  1.4910 +      match.end = {
  1.4911 +        divIdx: i,
  1.4912 +        offset: matchIdx - iIndex
  1.4913 +      };
  1.4914 +      ret.push(match);
  1.4915 +    }
  1.4916 +
  1.4917 +    return ret;
  1.4918 +  };
  1.4919 +
  1.4920 +  this.renderMatches = function textLayerBuilder_renderMatches(matches) {
  1.4921 +    // Early exit if there is nothing to render.
  1.4922 +    if (matches.length === 0) {
  1.4923 +      return;
  1.4924 +    }
  1.4925 +
  1.4926 +    var bidiTexts = this.textContent.items;
  1.4927 +    var textDivs = this.textDivs;
  1.4928 +    var prevEnd = null;
  1.4929 +    var isSelectedPage = (PDFFindController === null ?
  1.4930 +      false : (this.pageIdx === PDFFindController.selected.pageIdx));
  1.4931 +
  1.4932 +    var selectedMatchIdx = (PDFFindController === null ?
  1.4933 +                            -1 : PDFFindController.selected.matchIdx);
  1.4934 +
  1.4935 +    var highlightAll = (PDFFindController === null ?
  1.4936 +                        false : PDFFindController.state.highlightAll);
  1.4937 +
  1.4938 +    var infty = {
  1.4939 +      divIdx: -1,
  1.4940 +      offset: undefined
  1.4941 +    };
  1.4942 +
  1.4943 +    function beginText(begin, className) {
  1.4944 +      var divIdx = begin.divIdx;
  1.4945 +      var div = textDivs[divIdx];
  1.4946 +      div.textContent = '';
  1.4947 +      appendTextToDiv(divIdx, 0, begin.offset, className);
  1.4948 +    }
  1.4949 +
  1.4950 +    function appendText(from, to, className) {
  1.4951 +      appendTextToDiv(from.divIdx, from.offset, to.offset, className);
  1.4952 +    }
  1.4953 +
  1.4954 +    function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
  1.4955 +      var div = textDivs[divIdx];
  1.4956 +
  1.4957 +      var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
  1.4958 +      var node = document.createTextNode(content);
  1.4959 +      if (className) {
  1.4960 +        var span = document.createElement('span');
  1.4961 +        span.className = className;
  1.4962 +        span.appendChild(node);
  1.4963 +        div.appendChild(span);
  1.4964 +        return;
  1.4965 +      }
  1.4966 +      div.appendChild(node);
  1.4967 +    }
  1.4968 +
  1.4969 +    function highlightDiv(divIdx, className) {
  1.4970 +      textDivs[divIdx].className = className;
  1.4971 +    }
  1.4972 +
  1.4973 +    var i0 = selectedMatchIdx, i1 = i0 + 1, i;
  1.4974 +
  1.4975 +    if (highlightAll) {
  1.4976 +      i0 = 0;
  1.4977 +      i1 = matches.length;
  1.4978 +    } else if (!isSelectedPage) {
  1.4979 +      // Not highlighting all and this isn't the selected page, so do nothing.
  1.4980 +      return;
  1.4981 +    }
  1.4982 +
  1.4983 +    for (i = i0; i < i1; i++) {
  1.4984 +      var match = matches[i];
  1.4985 +      var begin = match.begin;
  1.4986 +      var end = match.end;
  1.4987 +
  1.4988 +      var isSelected = isSelectedPage && i === selectedMatchIdx;
  1.4989 +      var highlightSuffix = (isSelected ? ' selected' : '');
  1.4990 +      if (isSelected && !this.isViewerInPresentationMode) {
  1.4991 +        scrollIntoView(textDivs[begin.divIdx], { top: FIND_SCROLL_OFFSET_TOP,
  1.4992 +                                               left: FIND_SCROLL_OFFSET_LEFT });
  1.4993 +      }
  1.4994 +
  1.4995 +      // Match inside new div.
  1.4996 +      if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
  1.4997 +        // If there was a previous div, then add the text at the end
  1.4998 +        if (prevEnd !== null) {
  1.4999 +          appendText(prevEnd, infty);
  1.5000 +        }
  1.5001 +        // clears the divs and set the content until the begin point.
  1.5002 +        beginText(begin);
  1.5003 +      } else {
  1.5004 +        appendText(prevEnd, begin);
  1.5005 +      }
  1.5006 +
  1.5007 +      if (begin.divIdx === end.divIdx) {
  1.5008 +        appendText(begin, end, 'highlight' + highlightSuffix);
  1.5009 +      } else {
  1.5010 +        appendText(begin, infty, 'highlight begin' + highlightSuffix);
  1.5011 +        for (var n = begin.divIdx + 1; n < end.divIdx; n++) {
  1.5012 +          highlightDiv(n, 'highlight middle' + highlightSuffix);
  1.5013 +        }
  1.5014 +        beginText(end, 'highlight end' + highlightSuffix);
  1.5015 +      }
  1.5016 +      prevEnd = end;
  1.5017 +    }
  1.5018 +
  1.5019 +    if (prevEnd) {
  1.5020 +      appendText(prevEnd, infty);
  1.5021 +    }
  1.5022 +  };
  1.5023 +
  1.5024 +  this.updateMatches = function textLayerUpdateMatches() {
  1.5025 +    // Only show matches, once all rendering is done.
  1.5026 +    if (!this.renderingDone) {
  1.5027 +      return;
  1.5028 +    }
  1.5029 +
  1.5030 +    // Clear out all matches.
  1.5031 +    var matches = this.matches;
  1.5032 +    var textDivs = this.textDivs;
  1.5033 +    var bidiTexts = this.textContent.items;
  1.5034 +    var clearedUntilDivIdx = -1;
  1.5035 +
  1.5036 +    // Clear out all current matches.
  1.5037 +    for (var i = 0; i < matches.length; i++) {
  1.5038 +      var match = matches[i];
  1.5039 +      var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
  1.5040 +      for (var n = begin; n <= match.end.divIdx; n++) {
  1.5041 +        var div = textDivs[n];
  1.5042 +        div.textContent = bidiTexts[n].str;
  1.5043 +        div.className = '';
  1.5044 +      }
  1.5045 +      clearedUntilDivIdx = match.end.divIdx + 1;
  1.5046 +    }
  1.5047 +
  1.5048 +    if (PDFFindController === null || !PDFFindController.active) {
  1.5049 +      return;
  1.5050 +    }
  1.5051 +
  1.5052 +    // Convert the matches on the page controller into the match format used
  1.5053 +    // for the textLayer.
  1.5054 +    this.matches = matches = (this.convertMatches(PDFFindController === null ?
  1.5055 +      [] : (PDFFindController.pageMatches[this.pageIdx] || [])));
  1.5056 +
  1.5057 +    this.renderMatches(this.matches);
  1.5058 +  };
  1.5059 +};
  1.5060 +
  1.5061 +
  1.5062 +
  1.5063 +var DocumentOutlineView = function documentOutlineView(outline) {
  1.5064 +  var outlineView = document.getElementById('outlineView');
  1.5065 +  while (outlineView.firstChild) {
  1.5066 +    outlineView.removeChild(outlineView.firstChild);
  1.5067 +  }
  1.5068 +
  1.5069 +  if (!outline) {
  1.5070 +    if (!outlineView.classList.contains('hidden')) {
  1.5071 +      PDFView.switchSidebarView('thumbs');
  1.5072 +    }
  1.5073 +    return;
  1.5074 +  }
  1.5075 +
  1.5076 +  function bindItemLink(domObj, item) {
  1.5077 +    domObj.href = PDFView.getDestinationHash(item.dest);
  1.5078 +    domObj.onclick = function documentOutlineViewOnclick(e) {
  1.5079 +      PDFView.navigateTo(item.dest);
  1.5080 +      return false;
  1.5081 +    };
  1.5082 +  }
  1.5083 +
  1.5084 +
  1.5085 +  var queue = [{parent: outlineView, items: outline}];
  1.5086 +  while (queue.length > 0) {
  1.5087 +    var levelData = queue.shift();
  1.5088 +    var i, n = levelData.items.length;
  1.5089 +    for (i = 0; i < n; i++) {
  1.5090 +      var item = levelData.items[i];
  1.5091 +      var div = document.createElement('div');
  1.5092 +      div.className = 'outlineItem';
  1.5093 +      var a = document.createElement('a');
  1.5094 +      bindItemLink(a, item);
  1.5095 +      a.textContent = item.title;
  1.5096 +      div.appendChild(a);
  1.5097 +
  1.5098 +      if (item.items.length > 0) {
  1.5099 +        var itemsDiv = document.createElement('div');
  1.5100 +        itemsDiv.className = 'outlineItems';
  1.5101 +        div.appendChild(itemsDiv);
  1.5102 +        queue.push({parent: itemsDiv, items: item.items});
  1.5103 +      }
  1.5104 +
  1.5105 +      levelData.parent.appendChild(div);
  1.5106 +    }
  1.5107 +  }
  1.5108 +};
  1.5109 +
  1.5110 +var DocumentAttachmentsView = function documentAttachmentsView(attachments) {
  1.5111 +  var attachmentsView = document.getElementById('attachmentsView');
  1.5112 +  while (attachmentsView.firstChild) {
  1.5113 +    attachmentsView.removeChild(attachmentsView.firstChild);
  1.5114 +  }
  1.5115 +
  1.5116 +  if (!attachments) {
  1.5117 +    if (!attachmentsView.classList.contains('hidden')) {
  1.5118 +      PDFView.switchSidebarView('thumbs');
  1.5119 +    }
  1.5120 +    return;
  1.5121 +  }
  1.5122 +
  1.5123 +  function bindItemLink(domObj, item) {
  1.5124 +    domObj.href = '#';
  1.5125 +    domObj.onclick = function documentAttachmentsViewOnclick(e) {
  1.5126 +      var downloadManager = new DownloadManager();
  1.5127 +      downloadManager.downloadData(item.content, getFileName(item.filename),
  1.5128 +                                   '');
  1.5129 +      return false;
  1.5130 +    };
  1.5131 +  }
  1.5132 +
  1.5133 +  var names = Object.keys(attachments).sort(function(a,b) {
  1.5134 +    return a.toLowerCase().localeCompare(b.toLowerCase());
  1.5135 +  });
  1.5136 +  for (var i = 0, ii = names.length; i < ii; i++) {
  1.5137 +    var item = attachments[names[i]];
  1.5138 +    var div = document.createElement('div');
  1.5139 +    div.className = 'attachmentsItem';
  1.5140 +    var a = document.createElement('a');
  1.5141 +    bindItemLink(a, item);
  1.5142 +    a.textContent = getFileName(item.filename);
  1.5143 +    div.appendChild(a);
  1.5144 +    attachmentsView.appendChild(div);
  1.5145 +  }
  1.5146 +};
  1.5147 +
  1.5148 +
  1.5149 +function webViewerLoad(evt) {
  1.5150 +  PDFView.initialize().then(webViewerInitialized);
  1.5151 +}
  1.5152 +
  1.5153 +function webViewerInitialized() {
  1.5154 +  var file = window.location.href.split('#')[0];
  1.5155 +
  1.5156 +  document.getElementById('openFile').setAttribute('hidden', 'true');
  1.5157 +  document.getElementById('secondaryOpenFile').setAttribute('hidden', 'true');
  1.5158 +
  1.5159 +  // Special debugging flags in the hash section of the URL.
  1.5160 +  var hash = document.location.hash.substring(1);
  1.5161 +  var hashParams = PDFView.parseQueryString(hash);
  1.5162 +
  1.5163 +  if ('disableWorker' in hashParams) {
  1.5164 +    PDFJS.disableWorker = (hashParams['disableWorker'] === 'true');
  1.5165 +  }
  1.5166 +
  1.5167 +  if ('disableRange' in hashParams) {
  1.5168 +    PDFJS.disableRange = (hashParams['disableRange'] === 'true');
  1.5169 +  }
  1.5170 +
  1.5171 +  if ('disableAutoFetch' in hashParams) {
  1.5172 +    PDFJS.disableAutoFetch = (hashParams['disableAutoFetch'] === 'true');
  1.5173 +  }
  1.5174 +
  1.5175 +  if ('disableFontFace' in hashParams) {
  1.5176 +    PDFJS.disableFontFace = (hashParams['disableFontFace'] === 'true');
  1.5177 +  }
  1.5178 +
  1.5179 +  if ('disableHistory' in hashParams) {
  1.5180 +    PDFJS.disableHistory = (hashParams['disableHistory'] === 'true');
  1.5181 +  }
  1.5182 +
  1.5183 +  if ('webgl' in hashParams) {
  1.5184 +    PDFJS.disableWebGL = (hashParams['webgl'] !== 'true');
  1.5185 +  }
  1.5186 +
  1.5187 +  if ('useOnlyCssZoom' in hashParams) {
  1.5188 +    USE_ONLY_CSS_ZOOM = (hashParams['useOnlyCssZoom'] === 'true');
  1.5189 +  }
  1.5190 +
  1.5191 +  if ('verbosity' in hashParams) {
  1.5192 +    PDFJS.verbosity = hashParams['verbosity'] | 0;
  1.5193 +  }
  1.5194 +
  1.5195 +  if ('ignoreCurrentPositionOnZoom' in hashParams) {
  1.5196 +    IGNORE_CURRENT_POSITION_ON_ZOOM =
  1.5197 +      (hashParams['ignoreCurrentPositionOnZoom'] === 'true');
  1.5198 +  }
  1.5199 +
  1.5200 +
  1.5201 +
  1.5202 +  if (!PDFView.supportsDocumentFonts) {
  1.5203 +    PDFJS.disableFontFace = true;
  1.5204 +    console.warn(mozL10n.get('web_fonts_disabled', null,
  1.5205 +      'Web fonts are disabled: unable to use embedded PDF fonts.'));
  1.5206 +  }
  1.5207 +
  1.5208 +  if ('textLayer' in hashParams) {
  1.5209 +    switch (hashParams['textLayer']) {
  1.5210 +      case 'off':
  1.5211 +        PDFJS.disableTextLayer = true;
  1.5212 +        break;
  1.5213 +      case 'visible':
  1.5214 +      case 'shadow':
  1.5215 +      case 'hover':
  1.5216 +        var viewer = document.getElementById('viewer');
  1.5217 +        viewer.classList.add('textLayer-' + hashParams['textLayer']);
  1.5218 +        break;
  1.5219 +    }
  1.5220 +  }
  1.5221 +
  1.5222 +  if ('pdfBug' in hashParams && FirefoxCom.requestSync('pdfBugEnabled')) {
  1.5223 +    PDFJS.pdfBug = true;
  1.5224 +    var pdfBug = hashParams['pdfBug'];
  1.5225 +    var enabled = pdfBug.split(',');
  1.5226 +    PDFBug.enable(enabled);
  1.5227 +    PDFBug.init();
  1.5228 +  }
  1.5229 +
  1.5230 +  if (!PDFView.supportsPrinting) {
  1.5231 +    document.getElementById('print').classList.add('hidden');
  1.5232 +    document.getElementById('secondaryPrint').classList.add('hidden');
  1.5233 +  }
  1.5234 +
  1.5235 +  if (!PDFView.supportsFullscreen) {
  1.5236 +    document.getElementById('presentationMode').classList.add('hidden');
  1.5237 +    document.getElementById('secondaryPresentationMode').
  1.5238 +      classList.add('hidden');
  1.5239 +  }
  1.5240 +
  1.5241 +  if (PDFView.supportsIntegratedFind) {
  1.5242 +    document.getElementById('viewFind').classList.add('hidden');
  1.5243 +  }
  1.5244 +
  1.5245 +  // Listen for unsuporrted features to trigger the fallback UI.
  1.5246 +  PDFJS.UnsupportedManager.listen(PDFView.fallback.bind(PDFView));
  1.5247 +
  1.5248 +  // Suppress context menus for some controls
  1.5249 +  document.getElementById('scaleSelect').oncontextmenu = noContextMenuHandler;
  1.5250 +
  1.5251 +  var mainContainer = document.getElementById('mainContainer');
  1.5252 +  var outerContainer = document.getElementById('outerContainer');
  1.5253 +  mainContainer.addEventListener('transitionend', function(e) {
  1.5254 +    if (e.target == mainContainer) {
  1.5255 +      var event = document.createEvent('UIEvents');
  1.5256 +      event.initUIEvent('resize', false, false, window, 0);
  1.5257 +      window.dispatchEvent(event);
  1.5258 +      outerContainer.classList.remove('sidebarMoving');
  1.5259 +    }
  1.5260 +  }, true);
  1.5261 +
  1.5262 +  document.getElementById('sidebarToggle').addEventListener('click',
  1.5263 +    function() {
  1.5264 +      this.classList.toggle('toggled');
  1.5265 +      outerContainer.classList.add('sidebarMoving');
  1.5266 +      outerContainer.classList.toggle('sidebarOpen');
  1.5267 +      PDFView.sidebarOpen = outerContainer.classList.contains('sidebarOpen');
  1.5268 +      PDFView.renderHighestPriority();
  1.5269 +    });
  1.5270 +
  1.5271 +  document.getElementById('viewThumbnail').addEventListener('click',
  1.5272 +    function() {
  1.5273 +      PDFView.switchSidebarView('thumbs');
  1.5274 +    });
  1.5275 +
  1.5276 +  document.getElementById('viewOutline').addEventListener('click',
  1.5277 +    function() {
  1.5278 +      PDFView.switchSidebarView('outline');
  1.5279 +    });
  1.5280 +
  1.5281 +  document.getElementById('viewAttachments').addEventListener('click',
  1.5282 +    function() {
  1.5283 +      PDFView.switchSidebarView('attachments');
  1.5284 +    });
  1.5285 +
  1.5286 +  document.getElementById('previous').addEventListener('click',
  1.5287 +    function() {
  1.5288 +      PDFView.page--;
  1.5289 +    });
  1.5290 +
  1.5291 +  document.getElementById('next').addEventListener('click',
  1.5292 +    function() {
  1.5293 +      PDFView.page++;
  1.5294 +    });
  1.5295 +
  1.5296 +  document.getElementById('zoomIn').addEventListener('click',
  1.5297 +    function() {
  1.5298 +      PDFView.zoomIn();
  1.5299 +    });
  1.5300 +
  1.5301 +  document.getElementById('zoomOut').addEventListener('click',
  1.5302 +    function() {
  1.5303 +      PDFView.zoomOut();
  1.5304 +    });
  1.5305 +
  1.5306 +  document.getElementById('pageNumber').addEventListener('click',
  1.5307 +    function() {
  1.5308 +      this.select();
  1.5309 +    });
  1.5310 +
  1.5311 +  document.getElementById('pageNumber').addEventListener('change',
  1.5312 +    function() {
  1.5313 +      // Handle the user inputting a floating point number.
  1.5314 +      PDFView.page = (this.value | 0);
  1.5315 +
  1.5316 +      if (this.value !== (this.value | 0).toString()) {
  1.5317 +        this.value = PDFView.page;
  1.5318 +      }
  1.5319 +    });
  1.5320 +
  1.5321 +  document.getElementById('scaleSelect').addEventListener('change',
  1.5322 +    function() {
  1.5323 +      PDFView.setScale(this.value);
  1.5324 +    });
  1.5325 +
  1.5326 +  document.getElementById('presentationMode').addEventListener('click',
  1.5327 +    SecondaryToolbar.presentationModeClick.bind(SecondaryToolbar));
  1.5328 +
  1.5329 +  document.getElementById('openFile').addEventListener('click',
  1.5330 +    SecondaryToolbar.openFileClick.bind(SecondaryToolbar));
  1.5331 +
  1.5332 +  document.getElementById('print').addEventListener('click',
  1.5333 +    SecondaryToolbar.printClick.bind(SecondaryToolbar));
  1.5334 +
  1.5335 +  document.getElementById('download').addEventListener('click',
  1.5336 +    SecondaryToolbar.downloadClick.bind(SecondaryToolbar));
  1.5337 +
  1.5338 +  PDFView.setTitleUsingUrl(file);
  1.5339 +  PDFView.initPassiveLoading();
  1.5340 +  return;
  1.5341 +
  1.5342 +  if (file) {
  1.5343 +    PDFView.open(file, 0);
  1.5344 +  }
  1.5345 +}
  1.5346 +
  1.5347 +document.addEventListener('DOMContentLoaded', webViewerLoad, true);
  1.5348 +
  1.5349 +function updateViewarea() {
  1.5350 +
  1.5351 +  if (!PDFView.initialized) {
  1.5352 +    return;
  1.5353 +  }
  1.5354 +  var visible = PDFView.getVisiblePages();
  1.5355 +  var visiblePages = visible.views;
  1.5356 +  if (visiblePages.length === 0) {
  1.5357 +    return;
  1.5358 +  }
  1.5359 +
  1.5360 +  PDFView.renderHighestPriority();
  1.5361 +
  1.5362 +  var currentId = PDFView.page;
  1.5363 +  var firstPage = visible.first;
  1.5364 +
  1.5365 +  for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
  1.5366 +       i < ii; ++i) {
  1.5367 +    var page = visiblePages[i];
  1.5368 +
  1.5369 +    if (page.percent < 100) {
  1.5370 +      break;
  1.5371 +    }
  1.5372 +    if (page.id === PDFView.page) {
  1.5373 +      stillFullyVisible = true;
  1.5374 +      break;
  1.5375 +    }
  1.5376 +  }
  1.5377 +
  1.5378 +  if (!stillFullyVisible) {
  1.5379 +    currentId = visiblePages[0].id;
  1.5380 +  }
  1.5381 +
  1.5382 +  if (!PresentationMode.active) {
  1.5383 +    updateViewarea.inProgress = true; // used in "set page"
  1.5384 +    PDFView.page = currentId;
  1.5385 +    updateViewarea.inProgress = false;
  1.5386 +  }
  1.5387 +
  1.5388 +  var currentScale = PDFView.currentScale;
  1.5389 +  var currentScaleValue = PDFView.currentScaleValue;
  1.5390 +  var normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ?
  1.5391 +    Math.round(currentScale * 10000) / 100 : currentScaleValue;
  1.5392 +
  1.5393 +  var pageNumber = firstPage.id;
  1.5394 +  var pdfOpenParams = '#page=' + pageNumber;
  1.5395 +  pdfOpenParams += '&zoom=' + normalizedScaleValue;
  1.5396 +  var currentPage = PDFView.pages[pageNumber - 1];
  1.5397 +  var container = PDFView.container;
  1.5398 +  var topLeft = currentPage.getPagePoint((container.scrollLeft - firstPage.x),
  1.5399 +                                         (container.scrollTop - firstPage.y));
  1.5400 +  var intLeft = Math.round(topLeft[0]);
  1.5401 +  var intTop = Math.round(topLeft[1]);
  1.5402 +  pdfOpenParams += ',' + intLeft + ',' + intTop;
  1.5403 +
  1.5404 +  if (PresentationMode.active || PresentationMode.switchInProgress) {
  1.5405 +    PDFView.currentPosition = null;
  1.5406 +  } else {
  1.5407 +    PDFView.currentPosition = { page: pageNumber, left: intLeft, top: intTop };
  1.5408 +  }
  1.5409 +
  1.5410 +  var store = PDFView.store;
  1.5411 +  store.initializedPromise.then(function() {
  1.5412 +    store.set('exists', true);
  1.5413 +    store.set('page', pageNumber);
  1.5414 +    store.set('zoom', normalizedScaleValue);
  1.5415 +    store.set('scrollLeft', intLeft);
  1.5416 +    store.set('scrollTop', intTop);
  1.5417 +  });
  1.5418 +  var href = PDFView.getAnchorUrl(pdfOpenParams);
  1.5419 +  document.getElementById('viewBookmark').href = href;
  1.5420 +  document.getElementById('secondaryViewBookmark').href = href;
  1.5421 +
  1.5422 +  // Update the current bookmark in the browsing history.
  1.5423 +  PDFHistory.updateCurrentBookmark(pdfOpenParams, pageNumber);
  1.5424 +}
  1.5425 +
  1.5426 +window.addEventListener('resize', function webViewerResize(evt) {
  1.5427 +  if (PDFView.initialized &&
  1.5428 +      (document.getElementById('pageWidthOption').selected ||
  1.5429 +       document.getElementById('pageFitOption').selected ||
  1.5430 +       document.getElementById('pageAutoOption').selected)) {
  1.5431 +    PDFView.setScale(document.getElementById('scaleSelect').value);
  1.5432 +  }
  1.5433 +  updateViewarea();
  1.5434 +
  1.5435 +  // Set the 'max-height' CSS property of the secondary toolbar.
  1.5436 +  SecondaryToolbar.setMaxHeight(PDFView.container);
  1.5437 +});
  1.5438 +
  1.5439 +window.addEventListener('hashchange', function webViewerHashchange(evt) {
  1.5440 +  if (PDFHistory.isHashChangeUnlocked) {
  1.5441 +    PDFView.setHash(document.location.hash.substring(1));
  1.5442 +  }
  1.5443 +});
  1.5444 +
  1.5445 +
  1.5446 +function selectScaleOption(value) {
  1.5447 +  var options = document.getElementById('scaleSelect').options;
  1.5448 +  var predefinedValueFound = false;
  1.5449 +  for (var i = 0; i < options.length; i++) {
  1.5450 +    var option = options[i];
  1.5451 +    if (option.value != value) {
  1.5452 +      option.selected = false;
  1.5453 +      continue;
  1.5454 +    }
  1.5455 +    option.selected = true;
  1.5456 +    predefinedValueFound = true;
  1.5457 +  }
  1.5458 +  return predefinedValueFound;
  1.5459 +}
  1.5460 +
  1.5461 +window.addEventListener('localized', function localized(evt) {
  1.5462 +  document.getElementsByTagName('html')[0].dir = mozL10n.getDirection();
  1.5463 +
  1.5464 +  PDFView.animationStartedPromise.then(function() {
  1.5465 +    // Adjust the width of the zoom box to fit the content.
  1.5466 +    // Note: This is only done if the zoom box is actually visible,
  1.5467 +    // since otherwise element.clientWidth will return 0.
  1.5468 +    var container = document.getElementById('scaleSelectContainer');
  1.5469 +    if (container.clientWidth > 0) {
  1.5470 +      var select = document.getElementById('scaleSelect');
  1.5471 +      select.setAttribute('style', 'min-width: inherit;');
  1.5472 +      var width = select.clientWidth + SCALE_SELECT_CONTAINER_PADDING;
  1.5473 +      select.setAttribute('style', 'min-width: ' +
  1.5474 +                                   (width + SCALE_SELECT_PADDING) + 'px;');
  1.5475 +      container.setAttribute('style', 'min-width: ' + width + 'px; ' +
  1.5476 +                                      'max-width: ' + width + 'px;');
  1.5477 +    }
  1.5478 +
  1.5479 +    // Set the 'max-height' CSS property of the secondary toolbar.
  1.5480 +    SecondaryToolbar.setMaxHeight(PDFView.container);
  1.5481 +  });
  1.5482 +}, true);
  1.5483 +
  1.5484 +window.addEventListener('scalechange', function scalechange(evt) {
  1.5485 +  document.getElementById('zoomOut').disabled = (evt.scale === MIN_SCALE);
  1.5486 +  document.getElementById('zoomIn').disabled = (evt.scale === MAX_SCALE);
  1.5487 +
  1.5488 +  var customScaleOption = document.getElementById('customScaleOption');
  1.5489 +  customScaleOption.selected = false;
  1.5490 +
  1.5491 +  if (!evt.resetAutoSettings &&
  1.5492 +      (document.getElementById('pageWidthOption').selected ||
  1.5493 +       document.getElementById('pageFitOption').selected ||
  1.5494 +       document.getElementById('pageAutoOption').selected)) {
  1.5495 +    updateViewarea();
  1.5496 +    return;
  1.5497 +  }
  1.5498 +
  1.5499 +  var predefinedValueFound = selectScaleOption('' + evt.scale);
  1.5500 +  if (!predefinedValueFound) {
  1.5501 +    customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
  1.5502 +    customScaleOption.selected = true;
  1.5503 +  }
  1.5504 +  updateViewarea();
  1.5505 +}, true);
  1.5506 +
  1.5507 +window.addEventListener('pagechange', function pagechange(evt) {
  1.5508 +  var page = evt.pageNumber;
  1.5509 +  if (PDFView.previousPageNumber !== page) {
  1.5510 +    document.getElementById('pageNumber').value = page;
  1.5511 +    var selected = document.querySelector('.thumbnail.selected');
  1.5512 +    if (selected) {
  1.5513 +      selected.classList.remove('selected');
  1.5514 +    }
  1.5515 +    var thumbnail = document.getElementById('thumbnailContainer' + page);
  1.5516 +    thumbnail.classList.add('selected');
  1.5517 +    var visibleThumbs = PDFView.getVisibleThumbs();
  1.5518 +    var numVisibleThumbs = visibleThumbs.views.length;
  1.5519 +
  1.5520 +    // If the thumbnail isn't currently visible, scroll it into view.
  1.5521 +    if (numVisibleThumbs > 0) {
  1.5522 +      var first = visibleThumbs.first.id;
  1.5523 +      // Account for only one thumbnail being visible.
  1.5524 +      var last = (numVisibleThumbs > 1 ? visibleThumbs.last.id : first);
  1.5525 +      if (page <= first || page >= last) {
  1.5526 +        scrollIntoView(thumbnail, { top: THUMBNAIL_SCROLL_MARGIN });
  1.5527 +      }
  1.5528 +    }
  1.5529 +  }
  1.5530 +  document.getElementById('previous').disabled = (page <= 1);
  1.5531 +  document.getElementById('next').disabled = (page >= PDFView.pages.length);
  1.5532 +}, true);
  1.5533 +
  1.5534 +function handleMouseWheel(evt) {
  1.5535 +  var MOUSE_WHEEL_DELTA_FACTOR = 40;
  1.5536 +  var ticks = (evt.type === 'DOMMouseScroll') ? -evt.detail :
  1.5537 +              evt.wheelDelta / MOUSE_WHEEL_DELTA_FACTOR;
  1.5538 +  var direction = (ticks < 0) ? 'zoomOut' : 'zoomIn';
  1.5539 +
  1.5540 +  if (evt.ctrlKey) { // Only zoom the pages, not the entire viewer
  1.5541 +    evt.preventDefault();
  1.5542 +    PDFView[direction](Math.abs(ticks));
  1.5543 +  } else if (PresentationMode.active) {
  1.5544 +    PDFView.mouseScroll(ticks * MOUSE_WHEEL_DELTA_FACTOR);
  1.5545 +  }
  1.5546 +}
  1.5547 +
  1.5548 +window.addEventListener('DOMMouseScroll', handleMouseWheel);
  1.5549 +window.addEventListener('mousewheel', handleMouseWheel);
  1.5550 +
  1.5551 +window.addEventListener('click', function click(evt) {
  1.5552 +  if (!PresentationMode.active) {
  1.5553 +    if (SecondaryToolbar.opened && PDFView.container.contains(evt.target)) {
  1.5554 +      SecondaryToolbar.close();
  1.5555 +    }
  1.5556 +  } else if (evt.button === 0) {
  1.5557 +    // Necessary since preventDefault() in 'mousedown' won't stop
  1.5558 +    // the event propagation in all circumstances in presentation mode.
  1.5559 +    evt.preventDefault();
  1.5560 +  }
  1.5561 +}, false);
  1.5562 +
  1.5563 +window.addEventListener('keydown', function keydown(evt) {
  1.5564 +  if (PasswordPrompt.visible) {
  1.5565 +    return;
  1.5566 +  }
  1.5567 +
  1.5568 +  var handled = false;
  1.5569 +  var cmd = (evt.ctrlKey ? 1 : 0) |
  1.5570 +            (evt.altKey ? 2 : 0) |
  1.5571 +            (evt.shiftKey ? 4 : 0) |
  1.5572 +            (evt.metaKey ? 8 : 0);
  1.5573 +
  1.5574 +  // First, handle the key bindings that are independent whether an input
  1.5575 +  // control is selected or not.
  1.5576 +  if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
  1.5577 +    // either CTRL or META key with optional SHIFT.
  1.5578 +    switch (evt.keyCode) {
  1.5579 +      case 70: // f
  1.5580 +        if (!PDFView.supportsIntegratedFind) {
  1.5581 +          PDFFindBar.open();
  1.5582 +          handled = true;
  1.5583 +        }
  1.5584 +        break;
  1.5585 +      case 71: // g
  1.5586 +        if (!PDFView.supportsIntegratedFind) {
  1.5587 +          PDFFindBar.dispatchEvent('again', cmd === 5 || cmd === 12);
  1.5588 +          handled = true;
  1.5589 +        }
  1.5590 +        break;
  1.5591 +      case 61: // FF/Mac '='
  1.5592 +      case 107: // FF '+' and '='
  1.5593 +      case 187: // Chrome '+'
  1.5594 +      case 171: // FF with German keyboard
  1.5595 +        PDFView.zoomIn();
  1.5596 +        handled = true;
  1.5597 +        break;
  1.5598 +      case 173: // FF/Mac '-'
  1.5599 +      case 109: // FF '-'
  1.5600 +      case 189: // Chrome '-'
  1.5601 +        PDFView.zoomOut();
  1.5602 +        handled = true;
  1.5603 +        break;
  1.5604 +      case 48: // '0'
  1.5605 +      case 96: // '0' on Numpad of Swedish keyboard
  1.5606 +        // keeping it unhandled (to restore page zoom to 100%)
  1.5607 +        setTimeout(function () {
  1.5608 +          // ... and resetting the scale after browser adjusts its scale
  1.5609 +          PDFView.setScale(DEFAULT_SCALE, true);
  1.5610 +        });
  1.5611 +        handled = false;
  1.5612 +        break;
  1.5613 +    }
  1.5614 +  }
  1.5615 +
  1.5616 +
  1.5617 +  // CTRL+ALT or Option+Command
  1.5618 +  if (cmd === 3 || cmd === 10) {
  1.5619 +    switch (evt.keyCode) {
  1.5620 +      case 80: // p
  1.5621 +        SecondaryToolbar.presentationModeClick();
  1.5622 +        handled = true;
  1.5623 +        break;
  1.5624 +      case 71: // g
  1.5625 +        // focuses input#pageNumber field
  1.5626 +        document.getElementById('pageNumber').select();
  1.5627 +        handled = true;
  1.5628 +        break;
  1.5629 +    }
  1.5630 +  }
  1.5631 +
  1.5632 +  if (handled) {
  1.5633 +    evt.preventDefault();
  1.5634 +    return;
  1.5635 +  }
  1.5636 +
  1.5637 +  // Some shortcuts should not get handled if a control/input element
  1.5638 +  // is selected.
  1.5639 +  var curElement = document.activeElement || document.querySelector(':focus');
  1.5640 +  var curElementTagName = curElement && curElement.tagName.toUpperCase();
  1.5641 +  if (curElementTagName === 'INPUT' ||
  1.5642 +      curElementTagName === 'TEXTAREA' ||
  1.5643 +      curElementTagName === 'SELECT') {
  1.5644 +    // Make sure that the secondary toolbar is closed when Escape is pressed.
  1.5645 +    if (evt.keyCode !== 27) { // 'Esc'
  1.5646 +      return;
  1.5647 +    }
  1.5648 +  }
  1.5649 +
  1.5650 +  if (cmd === 0) { // no control key pressed at all.
  1.5651 +    switch (evt.keyCode) {
  1.5652 +      case 38: // up arrow
  1.5653 +      case 33: // pg up
  1.5654 +      case 8: // backspace
  1.5655 +        if (!PresentationMode.active &&
  1.5656 +            PDFView.currentScaleValue !== 'page-fit') {
  1.5657 +          break;
  1.5658 +        }
  1.5659 +        /* in presentation mode */
  1.5660 +        /* falls through */
  1.5661 +      case 37: // left arrow
  1.5662 +        // horizontal scrolling using arrow keys
  1.5663 +        if (PDFView.isHorizontalScrollbarEnabled) {
  1.5664 +          break;
  1.5665 +        }
  1.5666 +        /* falls through */
  1.5667 +      case 75: // 'k'
  1.5668 +      case 80: // 'p'
  1.5669 +        PDFView.page--;
  1.5670 +        handled = true;
  1.5671 +        break;
  1.5672 +      case 27: // esc key
  1.5673 +        if (SecondaryToolbar.opened) {
  1.5674 +          SecondaryToolbar.close();
  1.5675 +          handled = true;
  1.5676 +        }
  1.5677 +        if (!PDFView.supportsIntegratedFind && PDFFindBar.opened) {
  1.5678 +          PDFFindBar.close();
  1.5679 +          handled = true;
  1.5680 +        }
  1.5681 +        break;
  1.5682 +      case 40: // down arrow
  1.5683 +      case 34: // pg down
  1.5684 +      case 32: // spacebar
  1.5685 +        if (!PresentationMode.active &&
  1.5686 +            PDFView.currentScaleValue !== 'page-fit') {
  1.5687 +          break;
  1.5688 +        }
  1.5689 +        /* falls through */
  1.5690 +      case 39: // right arrow
  1.5691 +        // horizontal scrolling using arrow keys
  1.5692 +        if (PDFView.isHorizontalScrollbarEnabled) {
  1.5693 +          break;
  1.5694 +        }
  1.5695 +        /* falls through */
  1.5696 +      case 74: // 'j'
  1.5697 +      case 78: // 'n'
  1.5698 +        PDFView.page++;
  1.5699 +        handled = true;
  1.5700 +        break;
  1.5701 +
  1.5702 +      case 36: // home
  1.5703 +        if (PresentationMode.active) {
  1.5704 +          PDFView.page = 1;
  1.5705 +          handled = true;
  1.5706 +        }
  1.5707 +        break;
  1.5708 +      case 35: // end
  1.5709 +        if (PresentationMode.active) {
  1.5710 +          PDFView.page = PDFView.pdfDocument.numPages;
  1.5711 +          handled = true;
  1.5712 +        }
  1.5713 +        break;
  1.5714 +
  1.5715 +      case 72: // 'h'
  1.5716 +        if (!PresentationMode.active) {
  1.5717 +          HandTool.toggle();
  1.5718 +        }
  1.5719 +        break;
  1.5720 +      case 82: // 'r'
  1.5721 +        PDFView.rotatePages(90);
  1.5722 +        break;
  1.5723 +    }
  1.5724 +  }
  1.5725 +
  1.5726 +  if (cmd === 4) { // shift-key
  1.5727 +    switch (evt.keyCode) {
  1.5728 +      case 32: // spacebar
  1.5729 +        if (!PresentationMode.active &&
  1.5730 +            PDFView.currentScaleValue !== 'page-fit') {
  1.5731 +          break;
  1.5732 +        }
  1.5733 +        PDFView.page--;
  1.5734 +        handled = true;
  1.5735 +        break;
  1.5736 +
  1.5737 +      case 82: // 'r'
  1.5738 +        PDFView.rotatePages(-90);
  1.5739 +        break;
  1.5740 +    }
  1.5741 +  }
  1.5742 +
  1.5743 +  if (!handled && !PresentationMode.active) {
  1.5744 +    // 33=Page Up  34=Page Down  35=End    36=Home
  1.5745 +    // 37=Left     38=Up         39=Right  40=Down
  1.5746 +    if (evt.keyCode >= 33 && evt.keyCode <= 40 &&
  1.5747 +        !PDFView.container.contains(curElement)) {
  1.5748 +      // The page container is not focused, but a page navigation key has been
  1.5749 +      // pressed. Change the focus to the viewer container to make sure that
  1.5750 +      // navigation by keyboard works as expected.
  1.5751 +      PDFView.container.focus();
  1.5752 +    }
  1.5753 +    // 32=Spacebar
  1.5754 +    if (evt.keyCode === 32 && curElementTagName !== 'BUTTON') {
  1.5755 +      // Workaround for issue in Firefox, that prevents scroll keys from
  1.5756 +      // working when elements with 'tabindex' are focused. (#3498)
  1.5757 +      PDFView.container.blur();
  1.5758 +    }
  1.5759 +  }
  1.5760 +
  1.5761 +  if (cmd === 2) { // alt-key
  1.5762 +    switch (evt.keyCode) {
  1.5763 +      case 37: // left arrow
  1.5764 +        if (PresentationMode.active) {
  1.5765 +          PDFHistory.back();
  1.5766 +          handled = true;
  1.5767 +        }
  1.5768 +        break;
  1.5769 +      case 39: // right arrow
  1.5770 +        if (PresentationMode.active) {
  1.5771 +          PDFHistory.forward();
  1.5772 +          handled = true;
  1.5773 +        }
  1.5774 +        break;
  1.5775 +    }
  1.5776 +  }
  1.5777 +
  1.5778 +  if (handled) {
  1.5779 +    evt.preventDefault();
  1.5780 +    PDFView.clearMouseScrollState();
  1.5781 +  }
  1.5782 +});
  1.5783 +
  1.5784 +window.addEventListener('beforeprint', function beforePrint(evt) {
  1.5785 +  PDFView.beforePrint();
  1.5786 +});
  1.5787 +
  1.5788 +window.addEventListener('afterprint', function afterPrint(evt) {
  1.5789 +  PDFView.afterPrint();
  1.5790 +});
  1.5791 +
  1.5792 +(function animationStartedClosure() {
  1.5793 +  // The offsetParent is not set until the pdf.js iframe or object is visible.
  1.5794 +  // Waiting for first animation.
  1.5795 +  var requestAnimationFrame = window.requestAnimationFrame ||
  1.5796 +                              window.mozRequestAnimationFrame ||
  1.5797 +                              window.webkitRequestAnimationFrame ||
  1.5798 +                              window.oRequestAnimationFrame ||
  1.5799 +                              window.msRequestAnimationFrame ||
  1.5800 +                              function startAtOnce(callback) { callback(); };
  1.5801 +  PDFView.animationStartedPromise = new Promise(function (resolve) {
  1.5802 +    requestAnimationFrame(function onAnimationFrame() {
  1.5803 +      resolve();
  1.5804 +    });
  1.5805 +  });
  1.5806 +})();
  1.5807 +
  1.5808 +

mercurial