pdfjs_rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +37 -0
  6. data/Rakefile +1 -0
  7. data/lib/generators/pdfjs_rails/install_generator.rb +13 -0
  8. data/lib/pdfjs/build/pdf.js +7071 -0
  9. data/lib/pdfjs/build/pdf.worker.js +38687 -0
  10. data/lib/pdfjs/web/compatibility.js +471 -0
  11. data/lib/pdfjs/web/debugger.js +532 -0
  12. data/lib/pdfjs/web/images/annotation-check.svg +10 -0
  13. data/lib/pdfjs/web/images/annotation-comment.svg +15 -0
  14. data/lib/pdfjs/web/images/annotation-help.svg +25 -0
  15. data/lib/pdfjs/web/images/annotation-insert.svg +9 -0
  16. data/lib/pdfjs/web/images/annotation-key.svg +10 -0
  17. data/lib/pdfjs/web/images/annotation-newparagraph.svg +10 -0
  18. data/lib/pdfjs/web/images/annotation-note.svg +41 -0
  19. data/lib/pdfjs/web/images/annotation-paragraph.svg +15 -0
  20. data/lib/pdfjs/web/images/findbarButton-next-rtl.png +0 -0
  21. data/lib/pdfjs/web/images/findbarButton-next.png +0 -0
  22. data/lib/pdfjs/web/images/findbarButton-previous-rtl.png +0 -0
  23. data/lib/pdfjs/web/images/findbarButton-previous.png +0 -0
  24. data/lib/pdfjs/web/images/loading-icon.gif +0 -0
  25. data/lib/pdfjs/web/images/loading-small.png +0 -0
  26. data/lib/pdfjs/web/images/secondaryToolbarButton-firstPage.png +0 -0
  27. data/lib/pdfjs/web/images/secondaryToolbarButton-lastPage.png +0 -0
  28. data/lib/pdfjs/web/images/secondaryToolbarButton-rotateCcw.png +0 -0
  29. data/lib/pdfjs/web/images/secondaryToolbarButton-rotateCw.png +0 -0
  30. data/lib/pdfjs/web/images/shadow.png +0 -0
  31. data/lib/pdfjs/web/images/texture.png +0 -0
  32. data/lib/pdfjs/web/images/toolbarButton-bookmark.png +0 -0
  33. data/lib/pdfjs/web/images/toolbarButton-download.png +0 -0
  34. data/lib/pdfjs/web/images/toolbarButton-menuArrows.png +0 -0
  35. data/lib/pdfjs/web/images/toolbarButton-openFile.png +0 -0
  36. data/lib/pdfjs/web/images/toolbarButton-pageDown-rtl.png +0 -0
  37. data/lib/pdfjs/web/images/toolbarButton-pageDown.png +0 -0
  38. data/lib/pdfjs/web/images/toolbarButton-pageUp-rtl.png +0 -0
  39. data/lib/pdfjs/web/images/toolbarButton-pageUp.png +0 -0
  40. data/lib/pdfjs/web/images/toolbarButton-presentationMode.png +0 -0
  41. data/lib/pdfjs/web/images/toolbarButton-print.png +0 -0
  42. data/lib/pdfjs/web/images/toolbarButton-search.png +0 -0
  43. data/lib/pdfjs/web/images/toolbarButton-secondaryToolbarToggle.png +0 -0
  44. data/lib/pdfjs/web/images/toolbarButton-sidebarToggle.png +0 -0
  45. data/lib/pdfjs/web/images/toolbarButton-viewOutline.png +0 -0
  46. data/lib/pdfjs/web/images/toolbarButton-viewThumbnail.png +0 -0
  47. data/lib/pdfjs/web/images/toolbarButton-zoomIn.png +0 -0
  48. data/lib/pdfjs/web/images/toolbarButton-zoomOut.png +0 -0
  49. data/lib/pdfjs/web/l10n.js +922 -0
  50. data/lib/pdfjs/web/locale/ar/viewer.properties +112 -0
  51. data/lib/pdfjs/web/locale/ca/viewer.properties +131 -0
  52. data/lib/pdfjs/web/locale/cs/viewer.properties +58 -0
  53. data/lib/pdfjs/web/locale/da/viewer.properties +132 -0
  54. data/lib/pdfjs/web/locale/de/viewer.properties +128 -0
  55. data/lib/pdfjs/web/locale/el/viewer.properties +124 -0
  56. data/lib/pdfjs/web/locale/en-US/viewer.properties +134 -0
  57. data/lib/pdfjs/web/locale/es/viewer.properties +131 -0
  58. data/lib/pdfjs/web/locale/fi/viewer.properties +129 -0
  59. data/lib/pdfjs/web/locale/fr/viewer.properties +130 -0
  60. data/lib/pdfjs/web/locale/he/viewer.properties +59 -0
  61. data/lib/pdfjs/web/locale/it/viewer.properties +44 -0
  62. data/lib/pdfjs/web/locale/ja/viewer.properties +132 -0
  63. data/lib/pdfjs/web/locale/ko/viewer.properties +131 -0
  64. data/lib/pdfjs/web/locale/locale.properties +78 -0
  65. data/lib/pdfjs/web/locale/lt/viewer.properties +129 -0
  66. data/lib/pdfjs/web/locale/nl/viewer.properties +134 -0
  67. data/lib/pdfjs/web/locale/pl/viewer.properties +132 -0
  68. data/lib/pdfjs/web/locale/pt-BR/viewer.properties +44 -0
  69. data/lib/pdfjs/web/locale/ro/viewer.properties +55 -0
  70. data/lib/pdfjs/web/locale/ru/viewer.properties +62 -0
  71. data/lib/pdfjs/web/locale/sr/viewer.properties +55 -0
  72. data/lib/pdfjs/web/locale/sv/viewer.properties +134 -0
  73. data/lib/pdfjs/web/locale/tr/viewer.properties +129 -0
  74. data/lib/pdfjs/web/locale/vi/viewer.properties +131 -0
  75. data/lib/pdfjs/web/locale/zh-CN/viewer.properties +129 -0
  76. data/lib/pdfjs/web/locale/zh-TW/viewer.properties +132 -0
  77. data/lib/pdfjs/web/viewer.css +1544 -0
  78. data/lib/pdfjs/web/viewer.html +250 -0
  79. data/lib/pdfjs/web/viewer.js +4477 -0
  80. data/lib/pdfjs_rails.rb +6 -0
  81. data/lib/pdfjs_rails/railtie.rb +9 -0
  82. data/lib/pdfjs_rails/version.rb +3 -0
  83. data/lib/pdfjs_rails/view_helpers.rb +225 -0
  84. data/pdfjs_rails.gemspec +24 -0
  85. data/vender/assets/images/annotation-check.svg +10 -0
  86. data/vender/assets/images/annotation-comment.svg +15 -0
  87. data/vender/assets/images/annotation-help.svg +25 -0
  88. data/vender/assets/images/annotation-insert.svg +9 -0
  89. data/vender/assets/images/annotation-key.svg +10 -0
  90. data/vender/assets/images/annotation-newparagraph.svg +10 -0
  91. data/vender/assets/images/annotation-note.svg +41 -0
  92. data/vender/assets/images/annotation-paragraph.svg +15 -0
  93. data/vender/assets/images/findbarButton-next-rtl.png +0 -0
  94. data/vender/assets/images/findbarButton-next.png +0 -0
  95. data/vender/assets/images/findbarButton-previous-rtl.png +0 -0
  96. data/vender/assets/images/findbarButton-previous.png +0 -0
  97. data/vender/assets/images/loading-icon.gif +0 -0
  98. data/vender/assets/images/loading-small.png +0 -0
  99. data/vender/assets/images/shadow.png +0 -0
  100. data/vender/assets/images/texture.png +0 -0
  101. data/vender/assets/images/toolbarButton-bookmark.png +0 -0
  102. data/vender/assets/images/toolbarButton-download.png +0 -0
  103. data/vender/assets/images/toolbarButton-menuArrows.png +0 -0
  104. data/vender/assets/images/toolbarButton-openFile.png +0 -0
  105. data/vender/assets/images/toolbarButton-pageDown-rtl.png +0 -0
  106. data/vender/assets/images/toolbarButton-pageDown.png +0 -0
  107. data/vender/assets/images/toolbarButton-pageUp-rtl.png +0 -0
  108. data/vender/assets/images/toolbarButton-pageUp.png +0 -0
  109. data/vender/assets/images/toolbarButton-presentationMode.png +0 -0
  110. data/vender/assets/images/toolbarButton-print.png +0 -0
  111. data/vender/assets/images/toolbarButton-search.png +0 -0
  112. data/vender/assets/images/toolbarButton-sidebarToggle.png +0 -0
  113. data/vender/assets/images/toolbarButton-viewOutline.png +0 -0
  114. data/vender/assets/images/toolbarButton-viewThumbnail.png +0 -0
  115. data/vender/assets/images/toolbarButton-zoomIn.png +0 -0
  116. data/vender/assets/images/toolbarButton-zoomOut.png +0 -0
  117. data/vender/assets/javascripts/application.js +22 -0
  118. data/vender/assets/javascripts/compatibility.js +471 -0
  119. data/vender/assets/javascripts/debugger.js +532 -0
  120. data/vender/assets/javascripts/l10n.js +922 -0
  121. data/vender/assets/javascripts/pdf.js +7063 -0
  122. data/vender/assets/javascripts/pdf.worker.js +38679 -0
  123. data/vender/assets/javascripts/viewer.js +4317 -0
  124. data/vender/assets/stylesheets/viewer.css +1382 -0
  125. metadata +209 -0
@@ -0,0 +1,4317 @@
1
+ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
+ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
3
+ /* Copyright 2012 Mozilla Foundation
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, PDFFindBar, CustomStyle,
18
+ PDFFindController, ProgressBar, TextLayerBuilder, DownloadManager,
19
+ getFileName, getOutputScale, scrollIntoView, getPDFFileNameFromURL,
20
+ PDFHistory, ThumbnailView, noContextMenuHandler */
21
+
22
+ 'use strict';
23
+
24
+ var DEFAULT_URL = 'SPPC1992.pdf';
25
+ var DEFAULT_SCALE = 'auto';
26
+ var DEFAULT_SCALE_DELTA = 1.1;
27
+ var UNKNOWN_SCALE = 0;
28
+ var CACHE_SIZE = 20;
29
+ var CSS_UNITS = 96.0 / 72.0;
30
+ var SCROLLBAR_PADDING = 40;
31
+ var VERTICAL_PADDING = 5;
32
+ var MIN_SCALE = 0.25;
33
+ var MAX_SCALE = 4.0;
34
+ var SETTINGS_MEMORY = 20;
35
+ var SCALE_SELECT_CONTAINER_PADDING = 8;
36
+ var SCALE_SELECT_PADDING = 22;
37
+ var RenderingStates = {
38
+ INITIAL: 0,
39
+ RUNNING: 1,
40
+ PAUSED: 2,
41
+ FINISHED: 3
42
+ };
43
+ var FindStates = {
44
+ FIND_FOUND: 0,
45
+ FIND_NOTFOUND: 1,
46
+ FIND_WRAPPED: 2,
47
+ FIND_PENDING: 3
48
+ };
49
+
50
+ PDFJS.imageResourcesPath = 'assets/images/';
51
+ PDFJS.workerSrc = 'assets/pdf.worker.js';
52
+
53
+ var mozL10n = document.mozL10n || document.webL10n;
54
+
55
+
56
+ // optimised CSS custom property getter/setter
57
+ var CustomStyle = (function CustomStyleClosure() {
58
+
59
+ // As noted on: http://www.zachstronaut.com/posts/2009/02/17/
60
+ // animate-css-transforms-firefox-webkit.html
61
+ // in some versions of IE9 it is critical that ms appear in this list
62
+ // before Moz
63
+ var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
64
+ var _cache = { };
65
+
66
+ function CustomStyle() {
67
+ }
68
+
69
+ CustomStyle.getProp = function get(propName, element) {
70
+ // check cache only when no element is given
71
+ if (arguments.length == 1 && typeof _cache[propName] == 'string') {
72
+ return _cache[propName];
73
+ }
74
+
75
+ element = element || document.documentElement;
76
+ var style = element.style, prefixed, uPropName;
77
+
78
+ // test standard property first
79
+ if (typeof style[propName] == 'string') {
80
+ return (_cache[propName] = propName);
81
+ }
82
+
83
+ // capitalize
84
+ uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
85
+
86
+ // test vendor specific properties
87
+ for (var i = 0, l = prefixes.length; i < l; i++) {
88
+ prefixed = prefixes[i] + uPropName;
89
+ if (typeof style[prefixed] == 'string') {
90
+ return (_cache[propName] = prefixed);
91
+ }
92
+ }
93
+
94
+ //if all fails then set to undefined
95
+ return (_cache[propName] = 'undefined');
96
+ };
97
+
98
+ CustomStyle.setProp = function set(propName, element, str) {
99
+ var prop = this.getProp(propName);
100
+ if (prop != 'undefined')
101
+ element.style[prop] = str;
102
+ };
103
+
104
+ return CustomStyle;
105
+ })();
106
+
107
+ function getFileName(url) {
108
+ var anchor = url.indexOf('#');
109
+ var query = url.indexOf('?');
110
+ var end = Math.min(
111
+ anchor > 0 ? anchor : url.length,
112
+ query > 0 ? query : url.length);
113
+ return url.substring(url.lastIndexOf('/', end) + 1, end);
114
+ }
115
+
116
+ /**
117
+ * Returns scale factor for the canvas. It makes sense for the HiDPI displays.
118
+ * @return {Object} The object with horizontal (sx) and vertical (sy)
119
+ scales. The scaled property is set to false if scaling is
120
+ not required, true otherwise.
121
+ */
122
+ function getOutputScale() {
123
+ var pixelRatio = 'devicePixelRatio' in window ? window.devicePixelRatio : 1;
124
+ return {
125
+ sx: pixelRatio,
126
+ sy: pixelRatio,
127
+ scaled: pixelRatio != 1
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Scrolls specified element into view of its parent.
133
+ * element {Object} The element to be visible.
134
+ * spot {Object} The object with the top property -- offset from the top edge.
135
+ */
136
+ function scrollIntoView(element, spot) {
137
+ // Assuming offsetParent is available (it's not available when viewer is in
138
+ // hidden iframe or object). We have to scroll: if the offsetParent is not set
139
+ // producing the error. See also animationStartedClosure.
140
+ var parent = element.offsetParent;
141
+ var offsetY = element.offsetTop + element.clientTop;
142
+ if (!parent) {
143
+ console.error('offsetParent is not set -- cannot scroll');
144
+ return;
145
+ }
146
+ while (parent.clientHeight == parent.scrollHeight) {
147
+ offsetY += parent.offsetTop;
148
+ parent = parent.offsetParent;
149
+ if (!parent)
150
+ return; // no need to scroll
151
+ }
152
+ if (spot)
153
+ offsetY += spot.top;
154
+ parent.scrollTop = offsetY;
155
+ }
156
+
157
+ /**
158
+ * Event handler to suppress context menu.
159
+ */
160
+ function noContextMenuHandler(e) {
161
+ e.preventDefault();
162
+ }
163
+
164
+ /**
165
+ * Returns the filename or guessed filename from the url (see issue 3455).
166
+ * url {String} The original PDF location.
167
+ * @return {String} Guessed PDF file name.
168
+ */
169
+ function getPDFFileNameFromURL(url) {
170
+ var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
171
+ // SCHEME HOST 1.PATH 2.QUERY 3.REF
172
+ // Pattern to get last matching NAME.pdf
173
+ var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
174
+ var splitURI = reURI.exec(url);
175
+ var suggestedFilename = reFilename.exec(splitURI[1]) ||
176
+ reFilename.exec(splitURI[2]) ||
177
+ reFilename.exec(splitURI[3]);
178
+ if (suggestedFilename) {
179
+ suggestedFilename = suggestedFilename[0];
180
+ if (suggestedFilename.indexOf('%') != -1) {
181
+ // URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
182
+ try {
183
+ suggestedFilename =
184
+ reFilename.exec(decodeURIComponent(suggestedFilename))[0];
185
+ } catch(e) { // Possible (extremely rare) errors:
186
+ // URIError "Malformed URI", e.g. for "%AA.pdf"
187
+ // TypeError "null has no properties", e.g. for "%2F.pdf"
188
+ }
189
+ }
190
+ }
191
+ return suggestedFilename || 'document.pdf';
192
+ }
193
+
194
+ var ProgressBar = (function ProgressBarClosure() {
195
+
196
+ function clamp(v, min, max) {
197
+ return Math.min(Math.max(v, min), max);
198
+ }
199
+
200
+ function ProgressBar(id, opts) {
201
+
202
+ // Fetch the sub-elements for later.
203
+ this.div = document.querySelector(id + ' .progress');
204
+
205
+ // Get the loading bar element, so it can be resized to fit the viewer.
206
+ this.bar = this.div.parentNode;
207
+
208
+ // Get options, with sensible defaults.
209
+ this.height = opts.height || 100;
210
+ this.width = opts.width || 100;
211
+ this.units = opts.units || '%';
212
+
213
+ // Initialize heights.
214
+ this.div.style.height = this.height + this.units;
215
+ this.percent = 0;
216
+ }
217
+
218
+ ProgressBar.prototype = {
219
+
220
+ updateBar: function ProgressBar_updateBar() {
221
+ if (this._indeterminate) {
222
+ this.div.classList.add('indeterminate');
223
+ this.div.style.width = this.width + this.units;
224
+ return;
225
+ }
226
+
227
+ this.div.classList.remove('indeterminate');
228
+ var progressSize = this.width * this._percent / 100;
229
+ this.div.style.width = progressSize + this.units;
230
+ },
231
+
232
+ get percent() {
233
+ return this._percent;
234
+ },
235
+
236
+ set percent(val) {
237
+ this._indeterminate = isNaN(val);
238
+ this._percent = clamp(val, 0, 100);
239
+ this.updateBar();
240
+ },
241
+
242
+ setWidth: function ProgressBar_setWidth(viewer) {
243
+ if (viewer) {
244
+ var container = viewer.parentNode;
245
+ var scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
246
+ if (scrollbarWidth > 0) {
247
+ this.bar.setAttribute('style', 'width: calc(100% - ' +
248
+ scrollbarWidth + 'px);');
249
+ }
250
+ }
251
+ },
252
+
253
+ hide: function ProgressBar_hide() {
254
+ this.bar.classList.add('hidden');
255
+ this.bar.removeAttribute('style');
256
+ }
257
+ };
258
+
259
+ return ProgressBar;
260
+ })();
261
+
262
+ var Cache = function cacheCache(size) {
263
+ var data = [];
264
+ this.push = function cachePush(view) {
265
+ var i = data.indexOf(view);
266
+ if (i >= 0)
267
+ data.splice(i);
268
+ data.push(view);
269
+ if (data.length > size)
270
+ data.shift().destroy();
271
+ };
272
+ };
273
+
274
+
275
+
276
+ var DownloadManager = (function DownloadManagerClosure() {
277
+
278
+ function download(blobUrl, filename) {
279
+ var a = document.createElement('a');
280
+ if (a.click) {
281
+ // Use a.click() if available. Otherwise, Chrome might show
282
+ // "Unsafe JavaScript attempt to initiate a navigation change
283
+ // for frame with URL" and not open the PDF at all.
284
+ // Supported by (not mentioned = untested):
285
+ // - Firefox 6 - 19 (4- does not support a.click, 5 ignores a.click)
286
+ // - Chrome 19 - 26 (18- does not support a.click)
287
+ // - Opera 9 - 12.15
288
+ // - Internet Explorer 6 - 10
289
+ // - Safari 6 (5.1- does not support a.click)
290
+ a.href = blobUrl;
291
+ a.target = '_parent';
292
+ // Use a.download if available. This increases the likelihood that
293
+ // the file is downloaded instead of opened by another PDF plugin.
294
+ if ('download' in a) {
295
+ a.download = filename;
296
+ }
297
+ // <a> must be in the document for IE and recent Firefox versions.
298
+ // (otherwise .click() is ignored)
299
+ (document.body || document.documentElement).appendChild(a);
300
+ a.click();
301
+ a.parentNode.removeChild(a);
302
+ } else {
303
+ if (window.top === window &&
304
+ blobUrl.split('#')[0] === window.location.href.split('#')[0]) {
305
+ // If _parent == self, then opening an identical URL with different
306
+ // location hash will only cause a navigation, not a download.
307
+ var padCharacter = blobUrl.indexOf('?') === -1 ? '?' : '&';
308
+ blobUrl = blobUrl.replace(/#|$/, padCharacter + '$&');
309
+ }
310
+ window.open(blobUrl, '_parent');
311
+ }
312
+ }
313
+
314
+ function DownloadManager() {}
315
+
316
+ DownloadManager.prototype = {
317
+ downloadUrl: function DownloadManager_downloadUrl(url, filename) {
318
+ if (!PDFJS.isValidUrl(url, true)) {
319
+ return; // restricted/invalid URL
320
+ }
321
+
322
+ download(url + '#pdfjs.action=download', filename);
323
+ },
324
+
325
+ download: function DownloadManager_download(blob, url, filename) {
326
+ if (!URL) {
327
+ // URL.createObjectURL is not supported
328
+ this.downloadUrl(url, filename);
329
+ return;
330
+ }
331
+
332
+ if (navigator.msSaveBlob) {
333
+ // IE10 / IE11
334
+ if (!navigator.msSaveBlob(blob, filename)) {
335
+ this.downloadUrl(url, filename);
336
+ }
337
+ return;
338
+ }
339
+
340
+ var blobUrl = URL.createObjectURL(blob);
341
+ download(blobUrl, filename);
342
+ }
343
+ };
344
+
345
+ return DownloadManager;
346
+ })();
347
+
348
+
349
+
350
+ // Settings Manager - This is a utility for saving settings
351
+ // First we see if localStorage is available
352
+ // If not, we use FUEL in FF
353
+ // Use asyncStorage for B2G
354
+ var Settings = (function SettingsClosure() {
355
+ var isLocalStorageEnabled = (function localStorageEnabledTest() {
356
+ // Feature test as per http://diveintohtml5.info/storage.html
357
+ // The additional localStorage call is to get around a FF quirk, see
358
+ // bug #495747 in bugzilla
359
+ try {
360
+ return 'localStorage' in window && window['localStorage'] !== null &&
361
+ localStorage;
362
+ } catch (e) {
363
+ return false;
364
+ }
365
+ })();
366
+
367
+ function Settings(fingerprint) {
368
+ this.fingerprint = fingerprint;
369
+ this.initializedPromise = new PDFJS.Promise();
370
+
371
+ var resolvePromise = (function settingsResolvePromise(db) {
372
+ this.initialize(db || '{}');
373
+ this.initializedPromise.resolve();
374
+ }).bind(this);
375
+
376
+
377
+
378
+ if (isLocalStorageEnabled)
379
+ resolvePromise(localStorage.getItem('database'));
380
+ }
381
+
382
+ Settings.prototype = {
383
+ initialize: function settingsInitialize(database) {
384
+ database = JSON.parse(database);
385
+ if (!('files' in database))
386
+ database.files = [];
387
+ if (database.files.length >= SETTINGS_MEMORY)
388
+ database.files.shift();
389
+ var index;
390
+ for (var i = 0, length = database.files.length; i < length; i++) {
391
+ var branch = database.files[i];
392
+ if (branch.fingerprint == this.fingerprint) {
393
+ index = i;
394
+ break;
395
+ }
396
+ }
397
+ if (typeof index != 'number')
398
+ index = database.files.push({fingerprint: this.fingerprint}) - 1;
399
+ this.file = database.files[index];
400
+ this.database = database;
401
+ },
402
+
403
+ set: function settingsSet(name, val) {
404
+ if (!this.initializedPromise.isResolved)
405
+ return;
406
+
407
+ var file = this.file;
408
+ file[name] = val;
409
+ var database = JSON.stringify(this.database);
410
+
411
+
412
+
413
+ if (isLocalStorageEnabled)
414
+ localStorage.setItem('database', database);
415
+ },
416
+
417
+ get: function settingsGet(name, defaultValue) {
418
+ if (!this.initializedPromise.isResolved)
419
+ return defaultValue;
420
+
421
+ return this.file[name] || defaultValue;
422
+ }
423
+ };
424
+
425
+ return Settings;
426
+ })();
427
+
428
+ var cache = new Cache(CACHE_SIZE);
429
+ var currentPageNumber = 1;
430
+
431
+ // TODO: Enable the FindBar *AFTER* the pagesPromise in the load function
432
+ // got resolved
433
+
434
+ /* globals PDFFindController, FindStates, mozL10n */
435
+
436
+ /**
437
+ * Creates a "search bar" given set of DOM elements
438
+ * that act as controls for searching, or for setting
439
+ * search preferences in the UI. This object also sets
440
+ * up the appropriate events for the controls. Actual
441
+ * searching is done by PDFFindController
442
+ */
443
+ var PDFFindBar = {
444
+
445
+ opened: false,
446
+ bar: null,
447
+ toggleButton: null,
448
+ findField: null,
449
+ highlightAll: null,
450
+ caseSensitive: null,
451
+ findMsg: null,
452
+ findStatusIcon: null,
453
+ findPreviousButton: null,
454
+ findNextButton: null,
455
+
456
+ initialize: function(options) {
457
+ if(typeof PDFFindController === 'undefined' || PDFFindController === null) {
458
+ throw 'PDFFindBar cannot be initialized ' +
459
+ 'without a PDFFindController instance.';
460
+ }
461
+
462
+ this.bar = options.bar;
463
+ this.toggleButton = options.toggleButton;
464
+ this.findField = options.findField;
465
+ this.highlightAll = options.highlightAllCheckbox;
466
+ this.caseSensitive = options.caseSensitiveCheckbox;
467
+ this.findMsg = options.findMsg;
468
+ this.findStatusIcon = options.findStatusIcon;
469
+ this.findPreviousButton = options.findPreviousButton;
470
+ this.findNextButton = options.findNextButton;
471
+
472
+ var self = this;
473
+ this.toggleButton.addEventListener('click', function() {
474
+ self.toggle();
475
+ });
476
+
477
+ this.findField.addEventListener('input', function() {
478
+ self.dispatchEvent('');
479
+ });
480
+
481
+ this.bar.addEventListener('keydown', function(evt) {
482
+ switch (evt.keyCode) {
483
+ case 13: // Enter
484
+ if (evt.target === self.findField) {
485
+ self.dispatchEvent('again', evt.shiftKey);
486
+ }
487
+ break;
488
+ case 27: // Escape
489
+ self.close();
490
+ break;
491
+ }
492
+ });
493
+
494
+ this.findPreviousButton.addEventListener('click',
495
+ function() { self.dispatchEvent('again', true); }
496
+ );
497
+
498
+ this.findNextButton.addEventListener('click', function() {
499
+ self.dispatchEvent('again', false);
500
+ });
501
+
502
+ this.highlightAll.addEventListener('click', function() {
503
+ self.dispatchEvent('highlightallchange');
504
+ });
505
+
506
+ this.caseSensitive.addEventListener('click', function() {
507
+ self.dispatchEvent('casesensitivitychange');
508
+ });
509
+ },
510
+
511
+ dispatchEvent: function(aType, aFindPrevious) {
512
+ var event = document.createEvent('CustomEvent');
513
+ event.initCustomEvent('find' + aType, true, true, {
514
+ query: this.findField.value,
515
+ caseSensitive: this.caseSensitive.checked,
516
+ highlightAll: this.highlightAll.checked,
517
+ findPrevious: aFindPrevious
518
+ });
519
+ return window.dispatchEvent(event);
520
+ },
521
+
522
+ updateUIState: function(state, previous) {
523
+ var notFound = false;
524
+ var findMsg = '';
525
+ var status = '';
526
+
527
+ switch (state) {
528
+ case FindStates.FIND_FOUND:
529
+ break;
530
+
531
+ case FindStates.FIND_PENDING:
532
+ status = 'pending';
533
+ break;
534
+
535
+ case FindStates.FIND_NOTFOUND:
536
+ findMsg = mozL10n.get('find_not_found', null, 'Phrase not found');
537
+ notFound = true;
538
+ break;
539
+
540
+ case FindStates.FIND_WRAPPED:
541
+ if (previous) {
542
+ findMsg = mozL10n.get('find_reached_top', null,
543
+ 'Reached top of document, continued from bottom');
544
+ } else {
545
+ findMsg = mozL10n.get('find_reached_bottom', null,
546
+ 'Reached end of document, continued from top');
547
+ }
548
+ break;
549
+ }
550
+
551
+ if (notFound) {
552
+ this.findField.classList.add('notFound');
553
+ } else {
554
+ this.findField.classList.remove('notFound');
555
+ }
556
+
557
+ this.findField.setAttribute('data-status', status);
558
+ this.findMsg.textContent = findMsg;
559
+ },
560
+
561
+ open: function() {
562
+ if (this.opened) return;
563
+
564
+ this.opened = true;
565
+ this.toggleButton.classList.add('toggled');
566
+ this.bar.classList.remove('hidden');
567
+ this.findField.select();
568
+ this.findField.focus();
569
+ },
570
+
571
+ close: function() {
572
+ if (!this.opened) return;
573
+
574
+ this.opened = false;
575
+ this.toggleButton.classList.remove('toggled');
576
+ this.bar.classList.add('hidden');
577
+
578
+ PDFFindController.active = false;
579
+ },
580
+
581
+ toggle: function() {
582
+ if (this.opened) {
583
+ this.close();
584
+ } else {
585
+ this.open();
586
+ }
587
+ }
588
+ };
589
+
590
+
591
+
592
+ /* globals PDFFindBar, PDFJS, FindStates, FirefoxCom */
593
+
594
+ /**
595
+ * Provides a "search" or "find" functionality for the PDF.
596
+ * This object actually performs the search for a given string.
597
+ */
598
+
599
+ var PDFFindController = {
600
+ startedTextExtraction: false,
601
+
602
+ extractTextPromises: [],
603
+
604
+ pendingFindMatches: {},
605
+
606
+ // If active, find results will be highlighted.
607
+ active: false,
608
+
609
+ // Stores the text for each page.
610
+ pageContents: [],
611
+
612
+ pageMatches: [],
613
+
614
+ // Currently selected match.
615
+ selected: {
616
+ pageIdx: -1,
617
+ matchIdx: -1
618
+ },
619
+
620
+ // Where find algorithm currently is in the document.
621
+ offset: {
622
+ pageIdx: null,
623
+ matchIdx: null
624
+ },
625
+
626
+ resumePageIdx: null,
627
+
628
+ resumeCallback: null,
629
+
630
+ state: null,
631
+
632
+ dirtyMatch: false,
633
+
634
+ findTimeout: null,
635
+
636
+ pdfPageSource: null,
637
+
638
+ integratedFind: false,
639
+
640
+ initialize: function(options) {
641
+ if(typeof PDFFindBar === 'undefined' || PDFFindBar === null) {
642
+ throw 'PDFFindController cannot be initialized ' +
643
+ 'without a PDFFindController instance';
644
+ }
645
+
646
+ this.pdfPageSource = options.pdfPageSource;
647
+ this.integratedFind = options.integratedFind;
648
+
649
+ var events = [
650
+ 'find',
651
+ 'findagain',
652
+ 'findhighlightallchange',
653
+ 'findcasesensitivitychange'
654
+ ];
655
+
656
+ this.handleEvent = this.handleEvent.bind(this);
657
+
658
+ for (var i = 0; i < events.length; i++) {
659
+ window.addEventListener(events[i], this.handleEvent);
660
+ }
661
+ },
662
+
663
+ reset: function pdfFindControllerReset() {
664
+ this.startedTextExtraction = false;
665
+ this.extractTextPromises = [];
666
+ this.active = false;
667
+ },
668
+
669
+ calcFindMatch: function(pageIndex) {
670
+ var pageContent = this.pageContents[pageIndex];
671
+ var query = this.state.query;
672
+ var caseSensitive = this.state.caseSensitive;
673
+ var queryLen = query.length;
674
+
675
+ if (queryLen === 0) {
676
+ // Do nothing the matches should be wiped out already.
677
+ return;
678
+ }
679
+
680
+ if (!caseSensitive) {
681
+ pageContent = pageContent.toLowerCase();
682
+ query = query.toLowerCase();
683
+ }
684
+
685
+ var matches = [];
686
+
687
+ var matchIdx = -queryLen;
688
+ while (true) {
689
+ matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
690
+ if (matchIdx === -1) {
691
+ break;
692
+ }
693
+
694
+ matches.push(matchIdx);
695
+ }
696
+ this.pageMatches[pageIndex] = matches;
697
+ this.updatePage(pageIndex);
698
+ if (this.resumePageIdx === pageIndex) {
699
+ var callback = this.resumeCallback;
700
+ this.resumePageIdx = null;
701
+ this.resumeCallback = null;
702
+ callback();
703
+ }
704
+ },
705
+
706
+ extractText: function() {
707
+ if (this.startedTextExtraction) {
708
+ return;
709
+ }
710
+ this.startedTextExtraction = true;
711
+
712
+ this.pageContents = [];
713
+ for (var i = 0, ii = this.pdfPageSource.pdfDocument.numPages; i < ii; i++) {
714
+ this.extractTextPromises.push(new PDFJS.Promise());
715
+ }
716
+
717
+ var self = this;
718
+ function extractPageText(pageIndex) {
719
+ self.pdfPageSource.pages[pageIndex].getTextContent().then(
720
+ function textContentResolved(data) {
721
+ // Build the find string.
722
+ var bidiTexts = data.bidiTexts;
723
+ var str = '';
724
+
725
+ for (var i = 0; i < bidiTexts.length; i++) {
726
+ str += bidiTexts[i].str;
727
+ }
728
+
729
+ // Store the pageContent as a string.
730
+ self.pageContents.push(str);
731
+
732
+ self.extractTextPromises[pageIndex].resolve(pageIndex);
733
+ if ((pageIndex + 1) < self.pdfPageSource.pages.length)
734
+ extractPageText(pageIndex + 1);
735
+ }
736
+ );
737
+ }
738
+ extractPageText(0);
739
+ },
740
+
741
+ handleEvent: function(e) {
742
+ if (this.state === null || e.type !== 'findagain') {
743
+ this.dirtyMatch = true;
744
+ }
745
+ this.state = e.detail;
746
+ this.updateUIState(FindStates.FIND_PENDING);
747
+
748
+ this.extractText();
749
+
750
+ clearTimeout(this.findTimeout);
751
+ if (e.type === 'find') {
752
+ // Only trigger the find action after 250ms of silence.
753
+ this.findTimeout = setTimeout(this.nextMatch.bind(this), 250);
754
+ } else {
755
+ this.nextMatch();
756
+ }
757
+ },
758
+
759
+ updatePage: function(idx) {
760
+ var page = this.pdfPageSource.pages[idx];
761
+
762
+ if (this.selected.pageIdx === idx) {
763
+ // If the page is selected, scroll the page into view, which triggers
764
+ // rendering the page, which adds the textLayer. Once the textLayer is
765
+ // build, it will scroll onto the selected match.
766
+ page.scrollIntoView();
767
+ }
768
+
769
+ if (page.textLayer) {
770
+ page.textLayer.updateMatches();
771
+ }
772
+ },
773
+
774
+ nextMatch: function() {
775
+ var pages = this.pdfPageSource.pages;
776
+ var previous = this.state.findPrevious;
777
+ var numPages = this.pdfPageSource.pages.length;
778
+
779
+ this.active = true;
780
+
781
+ if (this.dirtyMatch) {
782
+ // Need to recalculate the matches, reset everything.
783
+ this.dirtyMatch = false;
784
+ this.selected.pageIdx = this.selected.matchIdx = -1;
785
+ this.offset.pageIdx = previous ? numPages - 1 : 0;
786
+ this.offset.matchIdx = null;
787
+ this.hadMatch = false;
788
+ this.resumeCallback = null;
789
+ this.resumePageIdx = null;
790
+ this.pageMatches = [];
791
+ var self = this;
792
+
793
+ for (var i = 0; i < numPages; i++) {
794
+ // Wipe out any previous highlighted matches.
795
+ this.updatePage(i);
796
+
797
+ // As soon as the text is extracted start finding the matches.
798
+ if (!(i in this.pendingFindMatches)) {
799
+ this.pendingFindMatches[i] = true;
800
+ this.extractTextPromises[i].then(function(pageIdx) {
801
+ delete self.pendingFindMatches[pageIdx];
802
+ self.calcFindMatch(pageIdx);
803
+ });
804
+ }
805
+ }
806
+ }
807
+
808
+ // If there's no query there's no point in searching.
809
+ if (this.state.query === '') {
810
+ this.updateUIState(FindStates.FIND_FOUND);
811
+ return;
812
+ }
813
+
814
+ // If we're waiting on a page, we return since we can't do anything else.
815
+ if (this.resumeCallback) {
816
+ return;
817
+ }
818
+
819
+ var offset = this.offset;
820
+ // If there's already a matchIdx that means we are iterating through a
821
+ // page's matches.
822
+ if (offset.matchIdx !== null) {
823
+ var numPageMatches = this.pageMatches[offset.pageIdx].length;
824
+ if ((!previous && offset.matchIdx + 1 < numPageMatches) ||
825
+ (previous && offset.matchIdx > 0)) {
826
+ // The simple case, we just have advance the matchIdx to select the next
827
+ // match on the page.
828
+ this.hadMatch = true;
829
+ offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1;
830
+ this.updateMatch(true);
831
+ return;
832
+ }
833
+ // We went beyond the current page's matches, so we advance to the next
834
+ // page.
835
+ this.advanceOffsetPage(previous);
836
+ }
837
+ // Start searching through the page.
838
+ this.nextPageMatch();
839
+ },
840
+
841
+ nextPageMatch: function() {
842
+ if (this.resumePageIdx !== null)
843
+ console.error('There can only be one pending page.');
844
+
845
+ var matchesReady = function(matches) {
846
+ var offset = this.offset;
847
+ var numMatches = matches.length;
848
+ var previous = this.state.findPrevious;
849
+ if (numMatches) {
850
+ // There were matches for the page, so initialize the matchIdx.
851
+ this.hadMatch = true;
852
+ offset.matchIdx = previous ? numMatches - 1 : 0;
853
+ this.updateMatch(true);
854
+ } else {
855
+ // No matches attempt to search the next page.
856
+ this.advanceOffsetPage(previous);
857
+ if (offset.wrapped) {
858
+ offset.matchIdx = null;
859
+ if (!this.hadMatch) {
860
+ // No point in wrapping there were no matches.
861
+ this.updateMatch(false);
862
+ return;
863
+ }
864
+ }
865
+ // Search the next page.
866
+ this.nextPageMatch();
867
+ }
868
+ }.bind(this);
869
+
870
+ var pageIdx = this.offset.pageIdx;
871
+ var pageMatches = this.pageMatches;
872
+ if (!pageMatches[pageIdx]) {
873
+ // The matches aren't ready setup a callback so we can be notified,
874
+ // when they are ready.
875
+ this.resumeCallback = function() {
876
+ matchesReady(pageMatches[pageIdx]);
877
+ };
878
+ this.resumePageIdx = pageIdx;
879
+ return;
880
+ }
881
+ // The matches are finished already.
882
+ matchesReady(pageMatches[pageIdx]);
883
+ },
884
+
885
+ advanceOffsetPage: function(previous) {
886
+ var offset = this.offset;
887
+ var numPages = this.extractTextPromises.length;
888
+ offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1;
889
+ offset.matchIdx = null;
890
+ if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
891
+ offset.pageIdx = previous ? numPages - 1 : 0;
892
+ offset.wrapped = true;
893
+ return;
894
+ }
895
+ },
896
+
897
+ updateMatch: function(found) {
898
+ var state = FindStates.FIND_NOTFOUND;
899
+ var wrapped = this.offset.wrapped;
900
+ this.offset.wrapped = false;
901
+ if (found) {
902
+ var previousPage = this.selected.pageIdx;
903
+ this.selected.pageIdx = this.offset.pageIdx;
904
+ this.selected.matchIdx = this.offset.matchIdx;
905
+ state = wrapped ? FindStates.FIND_WRAPPED : FindStates.FIND_FOUND;
906
+ // Update the currently selected page to wipe out any selected matches.
907
+ if (previousPage !== -1 && previousPage !== this.selected.pageIdx) {
908
+ this.updatePage(previousPage);
909
+ }
910
+ }
911
+ this.updateUIState(state, this.state.findPrevious);
912
+ if (this.selected.pageIdx !== -1) {
913
+ this.updatePage(this.selected.pageIdx, true);
914
+ }
915
+ },
916
+
917
+ updateUIState: function(state, previous) {
918
+ if (this.integratedFind) {
919
+ FirefoxCom.request('updateFindControlState',
920
+ {result: state, findPrevious: previous});
921
+ return;
922
+ }
923
+ PDFFindBar.updateUIState(state, previous);
924
+ }
925
+ };
926
+
927
+
928
+
929
+ var PDFHistory = {
930
+ initialized: false,
931
+ initialDestination: null,
932
+
933
+ initialize: function pdfHistoryInitialize(fingerprint) {
934
+ if (PDFJS.disableHistory || PDFView.isViewerEmbedded) {
935
+ // The browsing history is only enabled when the viewer is standalone,
936
+ // i.e. not when it is embedded in a web page.
937
+ return;
938
+ }
939
+ this.initialized = true;
940
+ this.reInitialized = false;
941
+ this.allowHashChange = true;
942
+ this.historyUnlocked = true;
943
+
944
+ this.previousHash = window.location.hash.substring(1);
945
+ this.currentBookmark = '';
946
+ this.currentPage = 0;
947
+ this.updatePreviousBookmark = false;
948
+ this.previousBookmark = '';
949
+ this.previousPage = 0;
950
+ this.nextHashParam = '';
951
+
952
+ this.fingerprint = fingerprint;
953
+ this.currentUid = this.uid = 0;
954
+ this.current = {};
955
+
956
+ var state = window.history.state;
957
+ if (this._isStateObjectDefined(state)) {
958
+ // This corresponds to navigating back to the document
959
+ // from another page in the browser history.
960
+ if (state.target.dest) {
961
+ this.initialDestination = state.target.dest;
962
+ } else {
963
+ PDFView.initialBookmark = state.target.hash;
964
+ }
965
+ this.currentUid = state.uid;
966
+ this.uid = state.uid + 1;
967
+ this.current = state.target;
968
+ } else {
969
+ // This corresponds to the loading of a new document.
970
+ if (state && state.fingerprint &&
971
+ this.fingerprint !== state.fingerprint) {
972
+ // Reinitialize the browsing history when a new document
973
+ // is opened in the web viewer.
974
+ this.reInitialized = true;
975
+ }
976
+ window.history.replaceState({ fingerprint: this.fingerprint }, '');
977
+ }
978
+
979
+ var self = this;
980
+ window.addEventListener('popstate', function pdfHistoryPopstate(evt) {
981
+ evt.preventDefault();
982
+ evt.stopPropagation();
983
+
984
+ if (!self.historyUnlocked) {
985
+ return;
986
+ }
987
+ if (evt.state) {
988
+ // Move back/forward in the history.
989
+ self._goTo(evt.state);
990
+ } else {
991
+ // Handle the user modifying the hash of a loaded document.
992
+ self.previousHash = window.location.hash.substring(1);
993
+
994
+ // If the history is empty when the hash changes,
995
+ // update the previous entry in the browser history.
996
+ if (self.uid === 0) {
997
+ var previousParams = (self.previousHash && self.currentBookmark &&
998
+ self.previousHash !== self.currentBookmark) ?
999
+ { hash: self.currentBookmark, page: self.currentPage } :
1000
+ { page: 1 };
1001
+ self.historyUnlocked = false;
1002
+ self.allowHashChange = false;
1003
+ window.history.back();
1004
+ self._pushToHistory(previousParams, false, true);
1005
+ window.history.forward();
1006
+ self.historyUnlocked = true;
1007
+ }
1008
+ self._pushToHistory({ hash: self.previousHash }, false, true);
1009
+ self._updatePreviousBookmark();
1010
+ }
1011
+ }, false);
1012
+
1013
+ function pdfHistoryBeforeUnload() {
1014
+ var previousParams = self._getPreviousParams(null, true);
1015
+ if (previousParams) {
1016
+ var replacePrevious = (!self.current.dest &&
1017
+ self.current.hash !== self.previousHash);
1018
+ self._pushToHistory(previousParams, false, replacePrevious);
1019
+ self._updatePreviousBookmark();
1020
+ }
1021
+ // Remove the event listener when navigating away from the document,
1022
+ // since 'beforeunload' prevents Firefox from caching the document.
1023
+ window.removeEventListener('beforeunload', pdfHistoryBeforeUnload, false);
1024
+ }
1025
+ window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false);
1026
+
1027
+ window.addEventListener('pageshow', function pdfHistoryPageShow(evt) {
1028
+ // If the entire viewer (including the PDF file) is cached in the browser,
1029
+ // we need to reattach the 'beforeunload' event listener since
1030
+ // the 'DOMContentLoaded' event is not fired on 'pageshow'.
1031
+ window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false);
1032
+ }, false);
1033
+ },
1034
+
1035
+ _isStateObjectDefined: function pdfHistory_isStateObjectDefined(state) {
1036
+ return (state && state.uid >= 0 &&
1037
+ state.fingerprint && this.fingerprint === state.fingerprint &&
1038
+ state.target && state.target.hash) ? true : false;
1039
+ },
1040
+
1041
+ get isHashChangeUnlocked() {
1042
+ if (!this.initialized) {
1043
+ return true;
1044
+ }
1045
+ // If the current hash changes when moving back/forward in the history,
1046
+ // this will trigger a 'popstate' event *as well* as a 'hashchange' event.
1047
+ // Since the hash generally won't correspond to the exact the position
1048
+ // stored in the history's state object, triggering the 'hashchange' event
1049
+ // can thus corrupt the browser history.
1050
+ //
1051
+ // When the hash changes during a 'popstate' event, we *only* prevent the
1052
+ // first 'hashchange' event and immediately reset allowHashChange.
1053
+ // If it is not reset, the user would not be able to change the hash.
1054
+
1055
+ var temp = this.allowHashChange;
1056
+ this.allowHashChange = true;
1057
+ return temp;
1058
+ },
1059
+
1060
+ _updatePreviousBookmark: function pdfHistory_updatePreviousBookmark() {
1061
+ if (this.updatePreviousBookmark &&
1062
+ this.currentBookmark && this.currentPage) {
1063
+ this.previousBookmark = this.currentBookmark;
1064
+ this.previousPage = this.currentPage;
1065
+ this.updatePreviousBookmark = false;
1066
+ }
1067
+ },
1068
+
1069
+ updateCurrentBookmark: function pdfHistoryUpdateCurrentBookmark(bookmark,
1070
+ pageNum) {
1071
+ if (this.initialized) {
1072
+ this.currentBookmark = bookmark.substring(1);
1073
+ this.currentPage = pageNum | 0;
1074
+ this._updatePreviousBookmark();
1075
+ }
1076
+ },
1077
+
1078
+ updateNextHashParam: function pdfHistoryUpdateNextHashParam(param) {
1079
+ if (this.initialized) {
1080
+ this.nextHashParam = param;
1081
+ }
1082
+ },
1083
+
1084
+ push: function pdfHistoryPush(params, isInitialBookmark) {
1085
+ if (!(this.initialized && this.historyUnlocked)) {
1086
+ return;
1087
+ }
1088
+ if (params.dest && !params.hash) {
1089
+ params.hash = (this.current.hash && this.current.dest &&
1090
+ this.current.dest === params.dest) ?
1091
+ this.current.hash :
1092
+ PDFView.getDestinationHash(params.dest).split('#')[1];
1093
+ }
1094
+ if (params.page) {
1095
+ params.page |= 0;
1096
+ }
1097
+ if (isInitialBookmark) {
1098
+ var target = window.history.state.target;
1099
+ if (!target) {
1100
+ // Invoked when the user specifies an initial bookmark,
1101
+ // thus setting PDFView.initialBookmark, when the document is loaded.
1102
+ this._pushToHistory(params, false);
1103
+ this.previousHash = window.location.hash.substring(1);
1104
+ }
1105
+ this.updatePreviousBookmark = this.nextHashParam ? false : true;
1106
+ if (target) {
1107
+ // If the current document is reloaded,
1108
+ // avoid creating duplicate entries in the history.
1109
+ this._updatePreviousBookmark();
1110
+ }
1111
+ return;
1112
+ }
1113
+ if (this.nextHashParam && this.nextHashParam === params.hash) {
1114
+ this.nextHashParam = null;
1115
+ this.updatePreviousBookmark = true;
1116
+ return;
1117
+ }
1118
+
1119
+ if (params.hash) {
1120
+ if (this.current.hash) {
1121
+ if (this.current.hash !== params.hash) {
1122
+ this._pushToHistory(params, true);
1123
+ } else {
1124
+ if (!this.current.page && params.page) {
1125
+ this._pushToHistory(params, false, true);
1126
+ }
1127
+ this.updatePreviousBookmark = true;
1128
+ }
1129
+ } else {
1130
+ this._pushToHistory(params, true);
1131
+ }
1132
+ } else if (this.current.page && params.page &&
1133
+ this.current.page !== params.page) {
1134
+ this._pushToHistory(params, true);
1135
+ }
1136
+ },
1137
+
1138
+ _getPreviousParams: function pdfHistory_getPreviousParams(onlyCheckPage,
1139
+ beforeUnload) {
1140
+ if (!(this.currentBookmark && this.currentPage)) {
1141
+ return null;
1142
+ }
1143
+ if ((!this.current.dest && !onlyCheckPage) || beforeUnload) {
1144
+ if (this.previousBookmark === this.currentBookmark) {
1145
+ return null;
1146
+ }
1147
+ } else if (this.current.page || onlyCheckPage) {
1148
+ if (this.previousPage === this.currentPage) {
1149
+ return null;
1150
+ }
1151
+ } else {
1152
+ return null;
1153
+ }
1154
+ var params = { hash: this.currentBookmark, page: this.currentPage };
1155
+ if (PDFView.isPresentationMode) {
1156
+ params.hash = null;
1157
+ }
1158
+ return params;
1159
+ },
1160
+
1161
+ _stateObj: function pdfHistory_stateObj(params) {
1162
+ return { fingerprint: this.fingerprint, uid: this.uid, target: params };
1163
+ },
1164
+
1165
+ _pushToHistory: function pdfHistory_pushToHistory(params,
1166
+ addPrevious, overwrite) {
1167
+ if (!this.initialized) {
1168
+ return;
1169
+ }
1170
+ if (!params.hash && params.page) {
1171
+ params.hash = ('page=' + params.page);
1172
+ }
1173
+ if (addPrevious && !overwrite) {
1174
+ var previousParams = this._getPreviousParams();
1175
+ if (previousParams) {
1176
+ var replacePrevious = (this.current.hash !== this.previousHash);
1177
+ this._pushToHistory(previousParams, false, replacePrevious);
1178
+ }
1179
+ }
1180
+ if (overwrite || this.uid === 0) {
1181
+ window.history.replaceState(this._stateObj(params), '');
1182
+ } else {
1183
+ window.history.pushState(this._stateObj(params), '');
1184
+ }
1185
+ this.currentUid = this.uid++;
1186
+ this.current = params;
1187
+ this.updatePreviousBookmark = true;
1188
+ },
1189
+
1190
+ _goTo: function pdfHistory_goTo(state) {
1191
+ if (!(this.initialized && this.historyUnlocked &&
1192
+ this._isStateObjectDefined(state))) {
1193
+ return;
1194
+ }
1195
+ if (!this.reInitialized && state.uid < this.currentUid) {
1196
+ var previousParams = this._getPreviousParams(true);
1197
+ if (previousParams) {
1198
+ this._pushToHistory(this.current, false);
1199
+ this._pushToHistory(previousParams, false);
1200
+ this.currentUid = state.uid;
1201
+ window.history.back();
1202
+ return;
1203
+ }
1204
+ }
1205
+ this.historyUnlocked = false;
1206
+
1207
+ if (state.target.dest) {
1208
+ PDFView.navigateTo(state.target.dest);
1209
+ } else {
1210
+ PDFView.setHash(state.target.hash);
1211
+ }
1212
+ this.currentUid = state.uid;
1213
+ if (state.uid > this.uid) {
1214
+ this.uid = state.uid;
1215
+ }
1216
+ this.current = state.target;
1217
+ this.updatePreviousBookmark = true;
1218
+
1219
+ var currentHash = window.location.hash.substring(1);
1220
+ if (this.previousHash !== currentHash) {
1221
+ this.allowHashChange = false;
1222
+ }
1223
+ this.previousHash = currentHash;
1224
+
1225
+ this.historyUnlocked = true;
1226
+ },
1227
+
1228
+ back: function pdfHistoryBack() {
1229
+ this.go(-1);
1230
+ },
1231
+
1232
+ forward: function pdfHistoryForward() {
1233
+ this.go(1);
1234
+ },
1235
+
1236
+ go: function pdfHistoryGo(direction) {
1237
+ if (this.initialized && this.historyUnlocked) {
1238
+ var state = window.history.state;
1239
+ if (direction === -1 && state && state.uid > 0) {
1240
+ window.history.back();
1241
+ } else if (direction === 1 && state && state.uid < (this.uid - 1)) {
1242
+ window.history.forward();
1243
+ }
1244
+ }
1245
+ }
1246
+ };
1247
+
1248
+
1249
+ var PDFView = {
1250
+ pages: [],
1251
+ thumbnails: [],
1252
+ currentScale: UNKNOWN_SCALE,
1253
+ currentScaleValue: null,
1254
+ initialBookmark: document.location.hash.substring(1),
1255
+ container: null,
1256
+ thumbnailContainer: null,
1257
+ initialized: false,
1258
+ fellback: false,
1259
+ pdfDocument: null,
1260
+ sidebarOpen: false,
1261
+ pageViewScroll: null,
1262
+ thumbnailViewScroll: null,
1263
+ isPresentationMode: false,
1264
+ presentationModeArgs: null,
1265
+ pageRotation: 0,
1266
+ mouseScrollTimeStamp: 0,
1267
+ mouseScrollDelta: 0,
1268
+ lastScroll: 0,
1269
+ previousPageNumber: 1,
1270
+ isViewerEmbedded: (window.parent !== window),
1271
+
1272
+ // called once when the document is loaded
1273
+ initialize: function pdfViewInitialize() {
1274
+ var self = this;
1275
+ var container = this.container = document.getElementById('viewerContainer');
1276
+ this.pageViewScroll = {};
1277
+ this.watchScroll(container, this.pageViewScroll, updateViewarea);
1278
+
1279
+ var thumbnailContainer = this.thumbnailContainer =
1280
+ document.getElementById('thumbnailView');
1281
+ this.thumbnailViewScroll = {};
1282
+ this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
1283
+ this.renderHighestPriority.bind(this));
1284
+
1285
+ PDFFindBar.initialize({
1286
+ bar: document.getElementById('findbar'),
1287
+ toggleButton: document.getElementById('viewFind'),
1288
+ findField: document.getElementById('findInput'),
1289
+ highlightAllCheckbox: document.getElementById('findHighlightAll'),
1290
+ caseSensitiveCheckbox: document.getElementById('findMatchCase'),
1291
+ findMsg: document.getElementById('findMsg'),
1292
+ findStatusIcon: document.getElementById('findStatusIcon'),
1293
+ findPreviousButton: document.getElementById('findPrevious'),
1294
+ findNextButton: document.getElementById('findNext')
1295
+ });
1296
+
1297
+ PDFFindController.initialize({
1298
+ pdfPageSource: this,
1299
+ integratedFind: this.supportsIntegratedFind
1300
+ });
1301
+
1302
+ this.initialized = true;
1303
+ container.addEventListener('scroll', function() {
1304
+ self.lastScroll = Date.now();
1305
+ }, false);
1306
+ },
1307
+
1308
+ getPage: function pdfViewGetPage(n) {
1309
+ return this.pdfDocument.getPage(n);
1310
+ },
1311
+
1312
+ // Helper function to keep track whether a div was scrolled up or down and
1313
+ // then call a callback.
1314
+ watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) {
1315
+ state.down = true;
1316
+ state.lastY = viewAreaElement.scrollTop;
1317
+ viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) {
1318
+ var currentY = viewAreaElement.scrollTop;
1319
+ var lastY = state.lastY;
1320
+ if (currentY > lastY)
1321
+ state.down = true;
1322
+ else if (currentY < lastY)
1323
+ state.down = false;
1324
+ // else do nothing and use previous value
1325
+ state.lastY = currentY;
1326
+ callback();
1327
+ }, true);
1328
+ },
1329
+
1330
+ setScale: function pdfViewSetScale(val, resetAutoSettings, noScroll) {
1331
+ if (val == this.currentScale)
1332
+ return;
1333
+
1334
+ var pages = this.pages;
1335
+ for (var i = 0; i < pages.length; i++)
1336
+ pages[i].update(val * CSS_UNITS);
1337
+
1338
+ if (!noScroll && this.currentScale != val)
1339
+ this.pages[this.page - 1].scrollIntoView();
1340
+ this.currentScale = val;
1341
+
1342
+ var event = document.createEvent('UIEvents');
1343
+ event.initUIEvent('scalechange', false, false, window, 0);
1344
+ event.scale = val;
1345
+ event.resetAutoSettings = resetAutoSettings;
1346
+ window.dispatchEvent(event);
1347
+ },
1348
+
1349
+ parseScale: function pdfViewParseScale(value, resetAutoSettings, noScroll) {
1350
+ if ('custom' == value)
1351
+ return;
1352
+
1353
+ var scale = parseFloat(value);
1354
+ this.currentScaleValue = value;
1355
+ if (scale) {
1356
+ this.setScale(scale, true, noScroll);
1357
+ return;
1358
+ }
1359
+
1360
+ var container = this.container;
1361
+ var currentPage = this.pages[this.page - 1];
1362
+ if (!currentPage) {
1363
+ return;
1364
+ }
1365
+
1366
+ var pageWidthScale = (container.clientWidth - SCROLLBAR_PADDING) /
1367
+ currentPage.width * currentPage.scale / CSS_UNITS;
1368
+ var pageHeightScale = (container.clientHeight - VERTICAL_PADDING) /
1369
+ currentPage.height * currentPage.scale / CSS_UNITS;
1370
+ switch (value) {
1371
+ case 'page-actual':
1372
+ scale = 1;
1373
+ break;
1374
+ case 'page-width':
1375
+ scale = pageWidthScale;
1376
+ break;
1377
+ case 'page-height':
1378
+ scale = pageHeightScale;
1379
+ break;
1380
+ case 'page-fit':
1381
+ scale = Math.min(pageWidthScale, pageHeightScale);
1382
+ break;
1383
+ case 'auto':
1384
+ scale = Math.min(1.0, pageWidthScale);
1385
+ break;
1386
+ }
1387
+ this.setScale(scale, resetAutoSettings, noScroll);
1388
+
1389
+ selectScaleOption(value);
1390
+ },
1391
+
1392
+ zoomIn: function pdfViewZoomIn(ticks) {
1393
+ var newScale = this.currentScale;
1394
+ do {
1395
+ newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
1396
+ newScale = Math.ceil(newScale * 10) / 10;
1397
+ newScale = Math.min(MAX_SCALE, newScale);
1398
+ } while (--ticks && newScale < MAX_SCALE);
1399
+ this.parseScale(newScale, true);
1400
+ },
1401
+
1402
+ zoomOut: function pdfViewZoomOut(ticks) {
1403
+ var newScale = this.currentScale;
1404
+ do {
1405
+ newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
1406
+ newScale = Math.floor(newScale * 10) / 10;
1407
+ newScale = Math.max(MIN_SCALE, newScale);
1408
+ } while (--ticks && newScale > MIN_SCALE);
1409
+ this.parseScale(newScale, true);
1410
+ },
1411
+
1412
+ set page(val) {
1413
+ var pages = this.pages;
1414
+ var input = document.getElementById('pageNumber');
1415
+ var event = document.createEvent('UIEvents');
1416
+ event.initUIEvent('pagechange', false, false, window, 0);
1417
+
1418
+ if (!(0 < val && val <= pages.length)) {
1419
+ this.previousPageNumber = val;
1420
+ event.pageNumber = this.page;
1421
+ window.dispatchEvent(event);
1422
+ return;
1423
+ }
1424
+
1425
+ pages[val - 1].updateStats();
1426
+ this.previousPageNumber = currentPageNumber;
1427
+ currentPageNumber = val;
1428
+ event.pageNumber = val;
1429
+ window.dispatchEvent(event);
1430
+
1431
+ // checking if the this.page was called from the updateViewarea function:
1432
+ // avoiding the creation of two "set page" method (internal and public)
1433
+ if (updateViewarea.inProgress)
1434
+ return;
1435
+
1436
+ // Avoid scrolling the first page during loading
1437
+ if (this.loading && val == 1)
1438
+ return;
1439
+
1440
+ pages[val - 1].scrollIntoView();
1441
+ },
1442
+
1443
+ get page() {
1444
+ return currentPageNumber;
1445
+ },
1446
+
1447
+ get supportsPrinting() {
1448
+ var canvas = document.createElement('canvas');
1449
+ var value = 'mozPrintCallback' in canvas;
1450
+ // shadow
1451
+ Object.defineProperty(this, 'supportsPrinting', { value: value,
1452
+ enumerable: true,
1453
+ configurable: true,
1454
+ writable: false });
1455
+ return value;
1456
+ },
1457
+
1458
+ get supportsFullscreen() {
1459
+ var doc = document.documentElement;
1460
+ var support = doc.requestFullscreen || doc.mozRequestFullScreen ||
1461
+ doc.webkitRequestFullScreen;
1462
+
1463
+ if (document.fullscreenEnabled === false ||
1464
+ document.mozFullScreenEnabled === false ||
1465
+ document.webkitFullscreenEnabled === false ) {
1466
+ support = false;
1467
+ }
1468
+
1469
+ Object.defineProperty(this, 'supportsFullscreen', { value: support,
1470
+ enumerable: true,
1471
+ configurable: true,
1472
+ writable: false });
1473
+ return support;
1474
+ },
1475
+
1476
+ get supportsIntegratedFind() {
1477
+ var support = false;
1478
+ Object.defineProperty(this, 'supportsIntegratedFind', { value: support,
1479
+ enumerable: true,
1480
+ configurable: true,
1481
+ writable: false });
1482
+ return support;
1483
+ },
1484
+
1485
+ get supportsDocumentFonts() {
1486
+ var support = true;
1487
+ Object.defineProperty(this, 'supportsDocumentFonts', { value: support,
1488
+ enumerable: true,
1489
+ configurable: true,
1490
+ writable: false });
1491
+ return support;
1492
+ },
1493
+
1494
+ get supportsDocumentColors() {
1495
+ var support = true;
1496
+ Object.defineProperty(this, 'supportsDocumentColors', { value: support,
1497
+ enumerable: true,
1498
+ configurable: true,
1499
+ writable: false });
1500
+ return support;
1501
+ },
1502
+
1503
+ get loadingBar() {
1504
+ var bar = new ProgressBar('#loadingBar', {});
1505
+ Object.defineProperty(this, 'loadingBar', { value: bar,
1506
+ enumerable: true,
1507
+ configurable: true,
1508
+ writable: false });
1509
+ return bar;
1510
+ },
1511
+
1512
+ get isHorizontalScrollbarEnabled() {
1513
+ var div = document.getElementById('viewerContainer');
1514
+ return div.scrollWidth > div.clientWidth;
1515
+ },
1516
+
1517
+ initPassiveLoading: function pdfViewInitPassiveLoading() {
1518
+ var pdfDataRangeTransport = {
1519
+ rangeListeners: [],
1520
+ progressListeners: [],
1521
+
1522
+ addRangeListener: function PdfDataRangeTransport_addRangeListener(
1523
+ listener) {
1524
+ this.rangeListeners.push(listener);
1525
+ },
1526
+
1527
+ addProgressListener: function PdfDataRangeTransport_addProgressListener(
1528
+ listener) {
1529
+ this.progressListeners.push(listener);
1530
+ },
1531
+
1532
+ onDataRange: function PdfDataRangeTransport_onDataRange(begin, chunk) {
1533
+ var listeners = this.rangeListeners;
1534
+ for (var i = 0, n = listeners.length; i < n; ++i) {
1535
+ listeners[i](begin, chunk);
1536
+ }
1537
+ },
1538
+
1539
+ onDataProgress: function PdfDataRangeTransport_onDataProgress(loaded) {
1540
+ var listeners = this.progressListeners;
1541
+ for (var i = 0, n = listeners.length; i < n; ++i) {
1542
+ listeners[i](loaded);
1543
+ }
1544
+ },
1545
+
1546
+ requestDataRange: function PdfDataRangeTransport_requestDataRange(
1547
+ begin, end) {
1548
+ FirefoxCom.request('requestDataRange', { begin: begin, end: end });
1549
+ }
1550
+ };
1551
+
1552
+ window.addEventListener('message', function windowMessage(e) {
1553
+ var args = e.data;
1554
+
1555
+ if (typeof args !== 'object' || !('pdfjsLoadAction' in args))
1556
+ return;
1557
+ switch (args.pdfjsLoadAction) {
1558
+ case 'supportsRangedLoading':
1559
+ PDFView.open(args.pdfUrl, 0, undefined, pdfDataRangeTransport, {
1560
+ length: args.length
1561
+ });
1562
+ break;
1563
+ case 'range':
1564
+ pdfDataRangeTransport.onDataRange(args.begin, args.chunk);
1565
+ break;
1566
+ case 'rangeProgress':
1567
+ pdfDataRangeTransport.onDataProgress(args.loaded);
1568
+ break;
1569
+ case 'progress':
1570
+ PDFView.progress(args.loaded / args.total);
1571
+ break;
1572
+ case 'complete':
1573
+ if (!args.data) {
1574
+ PDFView.error(mozL10n.get('loading_error', null,
1575
+ 'An error occurred while loading the PDF.'), e);
1576
+ break;
1577
+ }
1578
+ PDFView.open(args.data, 0);
1579
+ break;
1580
+ }
1581
+ });
1582
+ FirefoxCom.requestSync('initPassiveLoading', null);
1583
+ },
1584
+
1585
+ setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
1586
+ this.url = url;
1587
+ try {
1588
+ this.setTitle(decodeURIComponent(getFileName(url)) || url);
1589
+ } catch (e) {
1590
+ // decodeURIComponent may throw URIError,
1591
+ // fall back to using the unprocessed url in that case
1592
+ this.setTitle(url);
1593
+ }
1594
+ },
1595
+
1596
+ setTitle: function pdfViewSetTitle(title) {
1597
+ document.title = title;
1598
+ },
1599
+
1600
+ // TODO(mack): This function signature should really be pdfViewOpen(url, args)
1601
+ open: function pdfViewOpen(url, scale, password,
1602
+ pdfDataRangeTransport, args) {
1603
+ var parameters = {password: password};
1604
+ if (typeof url === 'string') { // URL
1605
+ this.setTitleUsingUrl(url);
1606
+ parameters.url = url;
1607
+ } else if (url && 'byteLength' in url) { // ArrayBuffer
1608
+ parameters.data = url;
1609
+ }
1610
+ if (args) {
1611
+ for (var prop in args) {
1612
+ parameters[prop] = args[prop];
1613
+ }
1614
+ }
1615
+
1616
+ this.pdfDocument = null;
1617
+ var self = this;
1618
+ self.loading = true;
1619
+ var passwordNeeded = function passwordNeeded(updatePassword, reason) {
1620
+ var promptString = mozL10n.get('request_password', null,
1621
+ 'PDF is protected by a password:');
1622
+
1623
+ if (reason === PDFJS.PasswordResponses.INCORRECT_PASSWORD) {
1624
+ promptString += '\n' + mozL10n.get('invalid_password', null,
1625
+ 'Invalid Password.');
1626
+ }
1627
+
1628
+ password = prompt(promptString);
1629
+ if (password && password.length > 0) {
1630
+ return updatePassword(password);
1631
+ }
1632
+ };
1633
+
1634
+ function getDocumentProgress(progressData) {
1635
+ self.progress(progressData.loaded / progressData.total);
1636
+ }
1637
+
1638
+ PDFJS.getDocument(parameters, pdfDataRangeTransport, passwordNeeded,
1639
+ getDocumentProgress).then(
1640
+ function getDocumentCallback(pdfDocument) {
1641
+ self.load(pdfDocument, scale);
1642
+ self.loading = false;
1643
+ },
1644
+ function getDocumentError(message, exception) {
1645
+ var loadingErrorMessage = mozL10n.get('loading_error', null,
1646
+ 'An error occurred while loading the PDF.');
1647
+
1648
+ if (exception && exception.name === 'InvalidPDFException') {
1649
+ // change error message also for other builds
1650
+ var loadingErrorMessage = mozL10n.get('invalid_file_error', null,
1651
+ 'Invalid or corrupted PDF file.');
1652
+ }
1653
+
1654
+ if (exception && exception.name === 'MissingPDFException') {
1655
+ // special message for missing PDF's
1656
+ var loadingErrorMessage = mozL10n.get('missing_file_error', null,
1657
+ 'Missing PDF file.');
1658
+
1659
+ }
1660
+
1661
+ var moreInfo = {
1662
+ message: message
1663
+ };
1664
+ self.error(loadingErrorMessage, moreInfo);
1665
+ self.loading = false;
1666
+ }
1667
+ );
1668
+ },
1669
+
1670
+ download: function pdfViewDownload() {
1671
+ function noData() {
1672
+ downloadManager.downloadUrl(url, filename);
1673
+ }
1674
+
1675
+ var url = this.url.split('#')[0];
1676
+ var filename = getPDFFileNameFromURL(url);
1677
+ var downloadManager = new DownloadManager();
1678
+ downloadManager.onerror = function (err) {
1679
+ // This error won't really be helpful because it's likely the
1680
+ // fallback won't work either (or is already open).
1681
+ PDFView.error('PDF failed to download.');
1682
+ };
1683
+
1684
+ if (!this.pdfDocument) { // the PDF is not ready yet
1685
+ noData();
1686
+ return;
1687
+ }
1688
+
1689
+ this.pdfDocument.getData().then(
1690
+ function getDataSuccess(data) {
1691
+ var blob = PDFJS.createBlob(data, 'application/pdf');
1692
+ downloadManager.download(blob, url, filename);
1693
+ },
1694
+ noData // Error occurred try downloading with just the url.
1695
+ ).then(null, noData);
1696
+ },
1697
+
1698
+ fallback: function pdfViewFallback() {
1699
+ return;
1700
+ },
1701
+
1702
+ navigateTo: function pdfViewNavigateTo(dest) {
1703
+ var destString = '';
1704
+ var self = this;
1705
+
1706
+ var goToDestination = function(destRef) {
1707
+ self.pendingRefStr = null;
1708
+ // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
1709
+ var pageNumber = destRef instanceof Object ?
1710
+ self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
1711
+ (destRef + 1);
1712
+ if (pageNumber) {
1713
+ if (pageNumber > self.pages.length) {
1714
+ pageNumber = self.pages.length;
1715
+ }
1716
+ var currentPage = self.pages[pageNumber - 1];
1717
+ currentPage.scrollIntoView(dest);
1718
+
1719
+ // Update the browsing history.
1720
+ PDFHistory.push({ dest: dest, hash: destString, page: pageNumber });
1721
+ } else {
1722
+ self.pendingRefStrLoaded = new PDFJS.Promise();
1723
+ self.pendingRefStr = destRef.num + ' ' + destRef.gen + ' R';
1724
+ self.pendingRefStrLoaded.then(function() {
1725
+ goToDestination(destRef);
1726
+ });
1727
+ }
1728
+ };
1729
+
1730
+ this.destinationsPromise.then(function() {
1731
+ if (typeof dest === 'string') {
1732
+ destString = dest;
1733
+ dest = self.destinations[dest];
1734
+ }
1735
+ if (!(dest instanceof Array)) {
1736
+ return; // invalid destination
1737
+ }
1738
+ goToDestination(dest[0]);
1739
+ });
1740
+ },
1741
+
1742
+ getDestinationHash: function pdfViewGetDestinationHash(dest) {
1743
+ if (typeof dest === 'string')
1744
+ return PDFView.getAnchorUrl('#' + escape(dest));
1745
+ if (dest instanceof Array) {
1746
+ var destRef = dest[0]; // see navigateTo method for dest format
1747
+ var pageNumber = destRef instanceof Object ?
1748
+ this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
1749
+ (destRef + 1);
1750
+ if (pageNumber) {
1751
+ var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber);
1752
+ var destKind = dest[1];
1753
+ if (typeof destKind === 'object' && 'name' in destKind &&
1754
+ destKind.name == 'XYZ') {
1755
+ var scale = (dest[4] || this.currentScaleValue);
1756
+ var scaleNumber = parseFloat(scale);
1757
+ if (scaleNumber) {
1758
+ scale = scaleNumber * 100;
1759
+ }
1760
+ pdfOpenParams += '&zoom=' + scale;
1761
+ if (dest[2] || dest[3]) {
1762
+ pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
1763
+ }
1764
+ }
1765
+ return pdfOpenParams;
1766
+ }
1767
+ }
1768
+ return '';
1769
+ },
1770
+
1771
+ /**
1772
+ * For the firefox extension we prefix the full url on anchor links so they
1773
+ * don't come up as resource:// urls and so open in new tab/window works.
1774
+ * @param {String} anchor The anchor hash include the #.
1775
+ */
1776
+ getAnchorUrl: function getAnchorUrl(anchor) {
1777
+ return anchor;
1778
+ },
1779
+
1780
+
1781
+ /**
1782
+ * Show the error box.
1783
+ * @param {String} message A message that is human readable.
1784
+ * @param {Object} moreInfo (optional) Further information about the error
1785
+ * that is more technical. Should have a 'message'
1786
+ * and optionally a 'stack' property.
1787
+ */
1788
+ error: function pdfViewError(message, moreInfo) {
1789
+ var moreInfoText = mozL10n.get('error_version_info',
1790
+ {version: PDFJS.version || '?', build: PDFJS.build || '?'},
1791
+ 'PDF.js v{{version}} (build: {{build}})') + '\n';
1792
+ if (moreInfo) {
1793
+ moreInfoText +=
1794
+ mozL10n.get('error_message', {message: moreInfo.message},
1795
+ 'Message: {{message}}');
1796
+ if (moreInfo.stack) {
1797
+ moreInfoText += '\n' +
1798
+ mozL10n.get('error_stack', {stack: moreInfo.stack},
1799
+ 'Stack: {{stack}}');
1800
+ } else {
1801
+ if (moreInfo.filename) {
1802
+ moreInfoText += '\n' +
1803
+ mozL10n.get('error_file', {file: moreInfo.filename},
1804
+ 'File: {{file}}');
1805
+ }
1806
+ if (moreInfo.lineNumber) {
1807
+ moreInfoText += '\n' +
1808
+ mozL10n.get('error_line', {line: moreInfo.lineNumber},
1809
+ 'Line: {{line}}');
1810
+ }
1811
+ }
1812
+ }
1813
+
1814
+ var errorWrapper = document.getElementById('errorWrapper');
1815
+ errorWrapper.removeAttribute('hidden');
1816
+
1817
+ var errorMessage = document.getElementById('errorMessage');
1818
+ errorMessage.textContent = message;
1819
+
1820
+ var closeButton = document.getElementById('errorClose');
1821
+ closeButton.onclick = function() {
1822
+ errorWrapper.setAttribute('hidden', 'true');
1823
+ };
1824
+
1825
+ var errorMoreInfo = document.getElementById('errorMoreInfo');
1826
+ var moreInfoButton = document.getElementById('errorShowMore');
1827
+ var lessInfoButton = document.getElementById('errorShowLess');
1828
+ moreInfoButton.onclick = function() {
1829
+ errorMoreInfo.removeAttribute('hidden');
1830
+ moreInfoButton.setAttribute('hidden', 'true');
1831
+ lessInfoButton.removeAttribute('hidden');
1832
+ errorMoreInfo.style.height = errorMoreInfo.scrollHeight + 'px';
1833
+ };
1834
+ lessInfoButton.onclick = function() {
1835
+ errorMoreInfo.setAttribute('hidden', 'true');
1836
+ moreInfoButton.removeAttribute('hidden');
1837
+ lessInfoButton.setAttribute('hidden', 'true');
1838
+ };
1839
+ moreInfoButton.oncontextmenu = noContextMenuHandler;
1840
+ lessInfoButton.oncontextmenu = noContextMenuHandler;
1841
+ closeButton.oncontextmenu = noContextMenuHandler;
1842
+ moreInfoButton.removeAttribute('hidden');
1843
+ lessInfoButton.setAttribute('hidden', 'true');
1844
+ errorMoreInfo.value = moreInfoText;
1845
+ },
1846
+
1847
+ progress: function pdfViewProgress(level) {
1848
+ var percent = Math.round(level * 100);
1849
+ // When we transition from full request to range requests, it's possible
1850
+ // that we discard some of the loaded data. This can cause the loading
1851
+ // bar to move backwards. So prevent this by only updating the bar if it
1852
+ // increases.
1853
+ if (percent > PDFView.loadingBar.percent) {
1854
+ PDFView.loadingBar.percent = percent;
1855
+ }
1856
+ },
1857
+
1858
+ load: function pdfViewLoad(pdfDocument, scale) {
1859
+ function bindOnAfterDraw(pageView, thumbnailView) {
1860
+ // when page is painted, using the image as thumbnail base
1861
+ pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
1862
+ thumbnailView.setImage(pageView.canvas);
1863
+ };
1864
+ }
1865
+
1866
+ PDFFindController.reset();
1867
+
1868
+ this.pdfDocument = pdfDocument;
1869
+
1870
+ var errorWrapper = document.getElementById('errorWrapper');
1871
+ errorWrapper.setAttribute('hidden', 'true');
1872
+
1873
+ pdfDocument.dataLoaded().then(function() {
1874
+ PDFView.loadingBar.hide();
1875
+ var outerContainer = document.getElementById('outerContainer');
1876
+ outerContainer.classList.remove('loadingInProgress');
1877
+ });
1878
+
1879
+ var thumbsView = document.getElementById('thumbnailView');
1880
+ thumbsView.parentNode.scrollTop = 0;
1881
+
1882
+ while (thumbsView.hasChildNodes())
1883
+ thumbsView.removeChild(thumbsView.lastChild);
1884
+
1885
+ if ('_loadingInterval' in thumbsView)
1886
+ clearInterval(thumbsView._loadingInterval);
1887
+
1888
+ var container = document.getElementById('viewer');
1889
+ while (container.hasChildNodes())
1890
+ container.removeChild(container.lastChild);
1891
+
1892
+ var pagesCount = pdfDocument.numPages;
1893
+
1894
+ var id = pdfDocument.fingerprint;
1895
+ document.getElementById('numPages').textContent =
1896
+ mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
1897
+ document.getElementById('pageNumber').max = pagesCount;
1898
+
1899
+ PDFView.documentFingerprint = id;
1900
+ var store = PDFView.store = new Settings(id);
1901
+
1902
+ this.pageRotation = 0;
1903
+
1904
+ var pages = this.pages = [];
1905
+ var pagesRefMap = this.pagesRefMap = {};
1906
+ var thumbnails = this.thumbnails = [];
1907
+
1908
+ var pagesPromise = this.pagesPromise = new PDFJS.Promise();
1909
+ var self = this;
1910
+
1911
+ var firstPagePromise = pdfDocument.getPage(1);
1912
+
1913
+ // Fetch a single page so we can get a viewport that will be the default
1914
+ // viewport for all pages
1915
+ firstPagePromise.then(function(pdfPage) {
1916
+ var viewport = pdfPage.getViewport(scale || 1.0);
1917
+ var pagePromises = [];
1918
+ for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
1919
+ var viewportClone = viewport.clone();
1920
+ var pageView = new PageView(container, pageNum, scale,
1921
+ self.navigateTo.bind(self),
1922
+ viewportClone);
1923
+ var thumbnailView = new ThumbnailView(thumbsView, pageNum,
1924
+ viewportClone);
1925
+ bindOnAfterDraw(pageView, thumbnailView);
1926
+ pages.push(pageView);
1927
+ thumbnails.push(thumbnailView);
1928
+ }
1929
+
1930
+ var event = document.createEvent('CustomEvent');
1931
+ event.initCustomEvent('documentload', true, true, {});
1932
+ window.dispatchEvent(event);
1933
+
1934
+ PDFView.loadingBar.setWidth(container);
1935
+
1936
+ for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
1937
+ var pagePromise = pdfDocument.getPage(pageNum);
1938
+ pagePromise.then(function(pdfPage) {
1939
+ var pageNum = pdfPage.pageNumber;
1940
+ var pageView = pages[pageNum - 1];
1941
+ if (!pageView.pdfPage) {
1942
+ // The pdfPage might already be set if we've already entered
1943
+ // pageView.draw()
1944
+ pageView.setPdfPage(pdfPage);
1945
+ }
1946
+ var thumbnailView = thumbnails[pageNum - 1];
1947
+ if (!thumbnailView.pdfPage) {
1948
+ thumbnailView.setPdfPage(pdfPage);
1949
+ }
1950
+
1951
+ var pageRef = pdfPage.ref;
1952
+ var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
1953
+ pagesRefMap[refStr] = pdfPage.pageNumber;
1954
+
1955
+ if (self.pendingRefStr && self.pendingRefStr === refStr) {
1956
+ self.pendingRefStrLoaded.resolve();
1957
+ }
1958
+ });
1959
+ pagePromises.push(pagePromise);
1960
+ }
1961
+
1962
+ PDFJS.Promise.all(pagePromises).then(function(pages) {
1963
+ pagesPromise.resolve(pages);
1964
+ });
1965
+ });
1966
+
1967
+ var storePromise = store.initializedPromise;
1968
+ PDFJS.Promise.all([firstPagePromise, storePromise]).then(function() {
1969
+ var storedHash = null;
1970
+ if (store.get('exists', false)) {
1971
+ var pageNum = store.get('page', '1');
1972
+ var zoom = store.get('zoom', PDFView.currentScale);
1973
+ var left = store.get('scrollLeft', '0');
1974
+ var top = store.get('scrollTop', '0');
1975
+
1976
+ storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' +
1977
+ left + ',' + top;
1978
+ }
1979
+ // Initialize the browsing history.
1980
+ PDFHistory.initialize(self.documentFingerprint);
1981
+
1982
+ self.setInitialView(storedHash, scale);
1983
+
1984
+ // Make all navigation keys work on document load,
1985
+ // unless the viewer is embedded in a web page.
1986
+ if (!self.isViewerEmbedded) {
1987
+ self.container.focus();
1988
+ }
1989
+ });
1990
+
1991
+ pagesPromise.then(function() {
1992
+ if (PDFView.supportsPrinting) {
1993
+ pdfDocument.getJavaScript().then(function(javaScript) {
1994
+ if (javaScript.length) {
1995
+ console.warn('Warning: JavaScript is not supported');
1996
+ PDFView.fallback();
1997
+ }
1998
+ // Hack to support auto printing.
1999
+ var regex = /\bprint\s*\(/g;
2000
+ for (var i = 0, ii = javaScript.length; i < ii; i++) {
2001
+ var js = javaScript[i];
2002
+ if (js && regex.test(js)) {
2003
+ setTimeout(function() {
2004
+ window.print();
2005
+ });
2006
+ return;
2007
+ }
2008
+ }
2009
+ });
2010
+ }
2011
+ });
2012
+
2013
+ var destinationsPromise =
2014
+ this.destinationsPromise = pdfDocument.getDestinations();
2015
+ destinationsPromise.then(function(destinations) {
2016
+ self.destinations = destinations;
2017
+ });
2018
+
2019
+ // outline depends on destinations and pagesRefMap
2020
+ var promises = [pagesPromise, destinationsPromise,
2021
+ PDFView.animationStartedPromise];
2022
+ PDFJS.Promise.all(promises).then(function() {
2023
+ pdfDocument.getOutline().then(function(outline) {
2024
+ self.outline = new DocumentOutlineView(outline);
2025
+ document.getElementById('viewOutline').disabled = !outline;
2026
+ });
2027
+ });
2028
+
2029
+ pdfDocument.getMetadata().then(function(data) {
2030
+ var info = data.info, metadata = data.metadata;
2031
+ self.documentInfo = info;
2032
+ self.metadata = metadata;
2033
+
2034
+ // Provides some basic debug information
2035
+ console.log('PDF ' + pdfDocument.fingerprint + ' [' +
2036
+ info.PDFFormatVersion + ' ' + (info.Producer || '-') +
2037
+ ' / ' + (info.Creator || '-') + ']' +
2038
+ (PDFJS.version ? ' (PDF.js: ' + PDFJS.version + ')' : ''));
2039
+
2040
+ var pdfTitle;
2041
+ if (metadata) {
2042
+ if (metadata.has('dc:title'))
2043
+ pdfTitle = metadata.get('dc:title');
2044
+ }
2045
+
2046
+ if (!pdfTitle && info && info['Title'])
2047
+ pdfTitle = info['Title'];
2048
+
2049
+ if (pdfTitle)
2050
+ self.setTitle(pdfTitle + ' - ' + document.title);
2051
+
2052
+ if (info.IsAcroFormPresent) {
2053
+ console.warn('Warning: AcroForm/XFA is not supported');
2054
+ PDFView.fallback();
2055
+ }
2056
+
2057
+ });
2058
+ },
2059
+
2060
+ setInitialView: function pdfViewSetInitialView(storedHash, scale) {
2061
+ // Reset the current scale, as otherwise the page's scale might not get
2062
+ // updated if the zoom level stayed the same.
2063
+ this.currentScale = 0;
2064
+ this.currentScaleValue = null;
2065
+ // When opening a new file (when one is already loaded in the viewer):
2066
+ // Reset 'currentPageNumber', since otherwise the page's scale will be wrong
2067
+ // if 'currentPageNumber' is larger than the number of pages in the file.
2068
+ document.getElementById('pageNumber').value = currentPageNumber = 1;
2069
+
2070
+ if (PDFHistory.initialDestination) {
2071
+ this.navigateTo(PDFHistory.initialDestination);
2072
+ PDFHistory.initialDestination = null;
2073
+ } else if (this.initialBookmark) {
2074
+ this.setHash(this.initialBookmark);
2075
+ PDFHistory.push({ hash: this.initialBookmark }, !!this.initialBookmark);
2076
+ this.initialBookmark = null;
2077
+ } else if (storedHash) {
2078
+ this.setHash(storedHash);
2079
+ } else if (scale) {
2080
+ this.parseScale(scale, true);
2081
+ this.page = 1;
2082
+ }
2083
+
2084
+ if (PDFView.currentScale === UNKNOWN_SCALE) {
2085
+ // Scale was not initialized: invalid bookmark or scale was not specified.
2086
+ // Setting the default one.
2087
+ this.parseScale(DEFAULT_SCALE, true);
2088
+ }
2089
+ },
2090
+
2091
+ renderHighestPriority: function pdfViewRenderHighestPriority() {
2092
+ // Pages have a higher priority than thumbnails, so check them first.
2093
+ var visiblePages = this.getVisiblePages();
2094
+ var pageView = this.getHighestPriority(visiblePages, this.pages,
2095
+ this.pageViewScroll.down);
2096
+ if (pageView) {
2097
+ this.renderView(pageView, 'page');
2098
+ return;
2099
+ }
2100
+ // No pages needed rendering so check thumbnails.
2101
+ if (this.sidebarOpen) {
2102
+ var visibleThumbs = this.getVisibleThumbs();
2103
+ var thumbView = this.getHighestPriority(visibleThumbs,
2104
+ this.thumbnails,
2105
+ this.thumbnailViewScroll.down);
2106
+ if (thumbView)
2107
+ this.renderView(thumbView, 'thumbnail');
2108
+ }
2109
+ },
2110
+
2111
+ getHighestPriority: function pdfViewGetHighestPriority(visible, views,
2112
+ scrolledDown) {
2113
+ // The state has changed figure out which page has the highest priority to
2114
+ // render next (if any).
2115
+ // Priority:
2116
+ // 1 visible pages
2117
+ // 2 if last scrolled down page after the visible pages
2118
+ // 2 if last scrolled up page before the visible pages
2119
+ var visibleViews = visible.views;
2120
+
2121
+ var numVisible = visibleViews.length;
2122
+ if (numVisible === 0) {
2123
+ return false;
2124
+ }
2125
+ for (var i = 0; i < numVisible; ++i) {
2126
+ var view = visibleViews[i].view;
2127
+ if (!this.isViewFinished(view))
2128
+ return view;
2129
+ }
2130
+
2131
+ // All the visible views have rendered, try to render next/previous pages.
2132
+ if (scrolledDown) {
2133
+ var nextPageIndex = visible.last.id;
2134
+ // ID's start at 1 so no need to add 1.
2135
+ if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex]))
2136
+ return views[nextPageIndex];
2137
+ } else {
2138
+ var previousPageIndex = visible.first.id - 2;
2139
+ if (views[previousPageIndex] &&
2140
+ !this.isViewFinished(views[previousPageIndex]))
2141
+ return views[previousPageIndex];
2142
+ }
2143
+ // Everything that needs to be rendered has been.
2144
+ return false;
2145
+ },
2146
+
2147
+ isViewFinished: function pdfViewNeedsRendering(view) {
2148
+ return view.renderingState === RenderingStates.FINISHED;
2149
+ },
2150
+
2151
+ // Render a page or thumbnail view. This calls the appropriate function based
2152
+ // on the views state. If the view is already rendered it will return false.
2153
+ renderView: function pdfViewRender(view, type) {
2154
+ var state = view.renderingState;
2155
+ switch (state) {
2156
+ case RenderingStates.FINISHED:
2157
+ return false;
2158
+ case RenderingStates.PAUSED:
2159
+ PDFView.highestPriorityPage = type + view.id;
2160
+ view.resume();
2161
+ break;
2162
+ case RenderingStates.RUNNING:
2163
+ PDFView.highestPriorityPage = type + view.id;
2164
+ break;
2165
+ case RenderingStates.INITIAL:
2166
+ PDFView.highestPriorityPage = type + view.id;
2167
+ view.draw(this.renderHighestPriority.bind(this));
2168
+ break;
2169
+ }
2170
+ return true;
2171
+ },
2172
+
2173
+ setHash: function pdfViewSetHash(hash) {
2174
+ if (!hash)
2175
+ return;
2176
+
2177
+ if (hash.indexOf('=') >= 0) {
2178
+ var params = PDFView.parseQueryString(hash);
2179
+ // borrowing syntax from "Parameters for Opening PDF Files"
2180
+ if ('nameddest' in params) {
2181
+ PDFHistory.updateNextHashParam(params.nameddest);
2182
+ PDFView.navigateTo(params.nameddest);
2183
+ return;
2184
+ }
2185
+ if ('page' in params) {
2186
+ var pageNumber = (params.page | 0) || 1;
2187
+ if ('zoom' in params) {
2188
+ var zoomArgs = params.zoom.split(','); // scale,left,top
2189
+ // building destination array
2190
+
2191
+ // If the zoom value, it has to get divided by 100. If it is a string,
2192
+ // it should stay as it is.
2193
+ var zoomArg = zoomArgs[0];
2194
+ var zoomArgNumber = parseFloat(zoomArg);
2195
+ if (zoomArgNumber)
2196
+ zoomArg = zoomArgNumber / 100;
2197
+
2198
+ var dest = [null, {name: 'XYZ'},
2199
+ zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
2200
+ zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
2201
+ zoomArg];
2202
+ var currentPage = this.pages[pageNumber - 1];
2203
+ currentPage.scrollIntoView(dest);
2204
+ } else {
2205
+ this.page = pageNumber; // simple page
2206
+ }
2207
+ }
2208
+ if ('pagemode' in params) {
2209
+ var toggle = document.getElementById('sidebarToggle');
2210
+ if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks') {
2211
+ if (!this.sidebarOpen) {
2212
+ toggle.click();
2213
+ }
2214
+ this.switchSidebarView(params.pagemode === 'thumbs' ?
2215
+ 'thumbs' : 'outline');
2216
+ } else if (params.pagemode === 'none' && this.sidebarOpen) {
2217
+ toggle.click();
2218
+ }
2219
+ }
2220
+ } else if (/^\d+$/.test(hash)) { // page number
2221
+ this.page = hash;
2222
+ } else { // named destination
2223
+ PDFHistory.updateNextHashParam(unescape(hash));
2224
+ PDFView.navigateTo(unescape(hash));
2225
+ }
2226
+ },
2227
+
2228
+ switchSidebarView: function pdfViewSwitchSidebarView(view) {
2229
+ var thumbsView = document.getElementById('thumbnailView');
2230
+ var outlineView = document.getElementById('outlineView');
2231
+
2232
+ var thumbsButton = document.getElementById('viewThumbnail');
2233
+ var outlineButton = document.getElementById('viewOutline');
2234
+
2235
+ switch (view) {
2236
+ case 'thumbs':
2237
+ var wasOutlineViewVisible = thumbsView.classList.contains('hidden');
2238
+
2239
+ thumbsButton.classList.add('toggled');
2240
+ outlineButton.classList.remove('toggled');
2241
+ thumbsView.classList.remove('hidden');
2242
+ outlineView.classList.add('hidden');
2243
+
2244
+ PDFView.renderHighestPriority();
2245
+
2246
+ if (wasOutlineViewVisible) {
2247
+ // Ensure that the thumbnail of the current page is visible
2248
+ // when switching from the outline view.
2249
+ scrollIntoView(document.getElementById('thumbnailContainer' +
2250
+ this.page));
2251
+ }
2252
+ break;
2253
+
2254
+ case 'outline':
2255
+ thumbsButton.classList.remove('toggled');
2256
+ outlineButton.classList.add('toggled');
2257
+ thumbsView.classList.add('hidden');
2258
+ outlineView.classList.remove('hidden');
2259
+
2260
+ if (outlineButton.getAttribute('disabled'))
2261
+ return;
2262
+ break;
2263
+ }
2264
+ },
2265
+
2266
+ getVisiblePages: function pdfViewGetVisiblePages() {
2267
+ if (!this.isPresentationMode) {
2268
+ return this.getVisibleElements(this.container, this.pages, true);
2269
+ } else {
2270
+ // The algorithm in getVisibleElements is broken in presentation mode.
2271
+ var visible = [], page = this.page;
2272
+ var currentPage = this.pages[page - 1];
2273
+ visible.push({ id: currentPage.id, view: currentPage });
2274
+
2275
+ return { first: currentPage, last: currentPage, views: visible};
2276
+ }
2277
+ },
2278
+
2279
+ getVisibleThumbs: function pdfViewGetVisibleThumbs() {
2280
+ return this.getVisibleElements(this.thumbnailContainer, this.thumbnails);
2281
+ },
2282
+
2283
+ // Generic helper to find out what elements are visible within a scroll pane.
2284
+ getVisibleElements: function pdfViewGetVisibleElements(
2285
+ scrollEl, views, sortByVisibility) {
2286
+ var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
2287
+ var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;
2288
+
2289
+ var visible = [], view;
2290
+ var currentHeight, viewHeight, hiddenHeight, percentHeight;
2291
+ var currentWidth, viewWidth;
2292
+ for (var i = 0, ii = views.length; i < ii; ++i) {
2293
+ view = views[i];
2294
+ currentHeight = view.el.offsetTop + view.el.clientTop;
2295
+ viewHeight = view.el.clientHeight;
2296
+ if ((currentHeight + viewHeight) < top) {
2297
+ continue;
2298
+ }
2299
+ if (currentHeight > bottom) {
2300
+ break;
2301
+ }
2302
+ currentWidth = view.el.offsetLeft + view.el.clientLeft;
2303
+ viewWidth = view.el.clientWidth;
2304
+ if ((currentWidth + viewWidth) < left || currentWidth > right) {
2305
+ continue;
2306
+ }
2307
+ hiddenHeight = Math.max(0, top - currentHeight) +
2308
+ Math.max(0, currentHeight + viewHeight - bottom);
2309
+ percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0;
2310
+
2311
+ visible.push({ id: view.id, y: currentHeight,
2312
+ view: view, percent: percentHeight });
2313
+ }
2314
+
2315
+ var first = visible[0];
2316
+ var last = visible[visible.length - 1];
2317
+
2318
+ if (sortByVisibility) {
2319
+ visible.sort(function(a, b) {
2320
+ var pc = a.percent - b.percent;
2321
+ if (Math.abs(pc) > 0.001) {
2322
+ return -pc;
2323
+ }
2324
+ return a.id - b.id; // ensure stability
2325
+ });
2326
+ }
2327
+ return {first: first, last: last, views: visible};
2328
+ },
2329
+
2330
+ // Helper function to parse query string (e.g. ?param1=value&parm2=...).
2331
+ parseQueryString: function pdfViewParseQueryString(query) {
2332
+ var parts = query.split('&');
2333
+ var params = {};
2334
+ for (var i = 0, ii = parts.length; i < parts.length; ++i) {
2335
+ var param = parts[i].split('=');
2336
+ var key = param[0];
2337
+ var value = param.length > 1 ? param[1] : null;
2338
+ params[decodeURIComponent(key)] = decodeURIComponent(value);
2339
+ }
2340
+ return params;
2341
+ },
2342
+
2343
+ beforePrint: function pdfViewSetupBeforePrint() {
2344
+ if (!this.supportsPrinting) {
2345
+ var printMessage = mozL10n.get('printing_not_supported', null,
2346
+ 'Warning: Printing is not fully supported by this browser.');
2347
+ this.error(printMessage);
2348
+ return;
2349
+ }
2350
+
2351
+ var alertNotReady = false;
2352
+ if (!this.pages.length) {
2353
+ alertNotReady = true;
2354
+ } else {
2355
+ for (var i = 0, ii = this.pages.length; i < ii; ++i) {
2356
+ if (!this.pages[i].pdfPage) {
2357
+ alertNotReady = true;
2358
+ break;
2359
+ }
2360
+ }
2361
+ }
2362
+ if (alertNotReady) {
2363
+ var notReadyMessage = mozL10n.get('printing_not_ready', null,
2364
+ 'Warning: The PDF is not fully loaded for printing.');
2365
+ window.alert(notReadyMessage);
2366
+ return;
2367
+ }
2368
+
2369
+ var body = document.querySelector('body');
2370
+ body.setAttribute('data-mozPrintCallback', true);
2371
+ for (var i = 0, ii = this.pages.length; i < ii; ++i) {
2372
+ this.pages[i].beforePrint();
2373
+ }
2374
+ },
2375
+
2376
+ afterPrint: function pdfViewSetupAfterPrint() {
2377
+ var div = document.getElementById('printContainer');
2378
+ while (div.hasChildNodes())
2379
+ div.removeChild(div.lastChild);
2380
+ },
2381
+
2382
+ presentationMode: function pdfViewPresentationMode() {
2383
+ var isPresentationMode = document.fullscreenElement ||
2384
+ document.mozFullScreen ||
2385
+ document.webkitIsFullScreen;
2386
+
2387
+ if (isPresentationMode) {
2388
+ return false;
2389
+ }
2390
+
2391
+ var wrapper = document.getElementById('viewerContainer');
2392
+ if (document.documentElement.requestFullscreen) {
2393
+ wrapper.requestFullscreen();
2394
+ } else if (document.documentElement.mozRequestFullScreen) {
2395
+ wrapper.mozRequestFullScreen();
2396
+ } else if (document.documentElement.webkitRequestFullScreen) {
2397
+ wrapper.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
2398
+ } else {
2399
+ return false;
2400
+ }
2401
+
2402
+ this.presentationModeArgs = {
2403
+ page: this.page,
2404
+ previousScale: this.currentScaleValue
2405
+ };
2406
+
2407
+ return true;
2408
+ },
2409
+
2410
+ enterPresentationMode: function pdfViewEnterPresentationMode() {
2411
+ this.isPresentationMode = true;
2412
+ this.page = this.presentationModeArgs.page;
2413
+ this.parseScale('page-fit', true);
2414
+ this.showPresentationControls();
2415
+ },
2416
+
2417
+ exitPresentationMode: function pdfViewExitPresentationMode() {
2418
+ this.isPresentationMode = false;
2419
+ this.parseScale(this.presentationModeArgs.previousScale);
2420
+ this.page = this.page;
2421
+ this.clearMouseScrollState();
2422
+ this.hidePresentationControls();
2423
+ this.presentationModeArgs = null;
2424
+
2425
+ // Ensure that the thumbnail of the current page is visible
2426
+ // when exiting presentation mode.
2427
+ scrollIntoView(document.getElementById('thumbnailContainer' + this.page));
2428
+ },
2429
+
2430
+ showPresentationControls: function pdfViewShowPresentationControls() {
2431
+ var DELAY_BEFORE_HIDING_CONTROLS = 3000;
2432
+ var wrapper = document.getElementById('viewerContainer');
2433
+ if (this.presentationControlsTimeout) {
2434
+ clearTimeout(this.presentationControlsTimeout);
2435
+ } else {
2436
+ wrapper.classList.add('presentationControls');
2437
+ }
2438
+ this.presentationControlsTimeout = setTimeout(function hideControls() {
2439
+ wrapper.classList.remove('presentationControls');
2440
+ delete PDFView.presentationControlsTimeout;
2441
+ }, DELAY_BEFORE_HIDING_CONTROLS);
2442
+ },
2443
+
2444
+ hidePresentationControls: function pdfViewShowPresentationControls() {
2445
+ if (!this.presentationControlsTimeout) {
2446
+ return;
2447
+ }
2448
+ clearTimeout(this.presentationControlsTimeout);
2449
+ delete this.presentationControlsTimeout;
2450
+
2451
+ var wrapper = document.getElementById('viewerContainer');
2452
+ wrapper.classList.remove('presentationControls');
2453
+ },
2454
+
2455
+ rotatePages: function pdfViewPageRotation(delta) {
2456
+
2457
+ this.pageRotation = (this.pageRotation + 360 + delta) % 360;
2458
+
2459
+ for (var i = 0, l = this.pages.length; i < l; i++) {
2460
+ var page = this.pages[i];
2461
+ page.update(page.scale, this.pageRotation);
2462
+ }
2463
+
2464
+ for (var i = 0, l = this.thumbnails.length; i < l; i++) {
2465
+ var thumb = this.thumbnails[i];
2466
+ thumb.update(this.pageRotation);
2467
+ }
2468
+
2469
+ this.parseScale(this.currentScaleValue, true);
2470
+
2471
+ this.renderHighestPriority();
2472
+
2473
+ var currentPage = this.pages[this.page - 1];
2474
+ if (!currentPage) {
2475
+ return;
2476
+ }
2477
+
2478
+ // Wait for presentation mode to take effect
2479
+ setTimeout(function() {
2480
+ currentPage.scrollIntoView();
2481
+ }, 0);
2482
+ },
2483
+
2484
+ /**
2485
+ * This function flips the page in presentation mode if the user scrolls up
2486
+ * or down with large enough motion and prevents page flipping too often.
2487
+ *
2488
+ * @this {PDFView}
2489
+ * @param {number} mouseScrollDelta The delta value from the mouse event.
2490
+ */
2491
+ mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) {
2492
+ var MOUSE_SCROLL_COOLDOWN_TIME = 50;
2493
+
2494
+ var currentTime = (new Date()).getTime();
2495
+ var storedTime = this.mouseScrollTimeStamp;
2496
+
2497
+ // In case one page has already been flipped there is a cooldown time
2498
+ // which has to expire before next page can be scrolled on to.
2499
+ if (currentTime > storedTime &&
2500
+ currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME)
2501
+ return;
2502
+
2503
+ // In case the user decides to scroll to the opposite direction than before
2504
+ // clear the accumulated delta.
2505
+ if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) ||
2506
+ (this.mouseScrollDelta < 0 && mouseScrollDelta > 0))
2507
+ this.clearMouseScrollState();
2508
+
2509
+ this.mouseScrollDelta += mouseScrollDelta;
2510
+
2511
+ var PAGE_FLIP_THRESHOLD = 120;
2512
+ if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) {
2513
+
2514
+ var PageFlipDirection = {
2515
+ UP: -1,
2516
+ DOWN: 1
2517
+ };
2518
+
2519
+ // In presentation mode scroll one page at a time.
2520
+ var pageFlipDirection = (this.mouseScrollDelta > 0) ?
2521
+ PageFlipDirection.UP :
2522
+ PageFlipDirection.DOWN;
2523
+ this.clearMouseScrollState();
2524
+ var currentPage = this.page;
2525
+
2526
+ // In case we are already on the first or the last page there is no need
2527
+ // to do anything.
2528
+ if ((currentPage == 1 && pageFlipDirection == PageFlipDirection.UP) ||
2529
+ (currentPage == this.pages.length &&
2530
+ pageFlipDirection == PageFlipDirection.DOWN))
2531
+ return;
2532
+
2533
+ this.page += pageFlipDirection;
2534
+ this.mouseScrollTimeStamp = currentTime;
2535
+ }
2536
+ },
2537
+
2538
+ /**
2539
+ * This function clears the member attributes used with mouse scrolling in
2540
+ * presentation mode.
2541
+ *
2542
+ * @this {PDFView}
2543
+ */
2544
+ clearMouseScrollState: function pdfViewClearMouseScrollState() {
2545
+ this.mouseScrollTimeStamp = 0;
2546
+ this.mouseScrollDelta = 0;
2547
+ }
2548
+ };
2549
+
2550
+ var PageView = function pageView(container, id, scale,
2551
+ navigateTo, defaultViewport) {
2552
+ this.id = id;
2553
+
2554
+ this.rotation = 0;
2555
+ this.scale = scale || 1.0;
2556
+ this.viewport = defaultViewport;
2557
+ this.pdfPageRotate = defaultViewport.rotate;
2558
+
2559
+ this.renderingState = RenderingStates.INITIAL;
2560
+ this.resume = null;
2561
+
2562
+ this.textContent = null;
2563
+ this.textLayer = null;
2564
+
2565
+ this.annotationLayer = null;
2566
+
2567
+ var anchor = document.createElement('a');
2568
+ anchor.name = '' + this.id;
2569
+
2570
+ var div = this.el = document.createElement('div');
2571
+ div.id = 'pageContainer' + this.id;
2572
+ div.className = 'page';
2573
+ div.style.width = Math.floor(this.viewport.width) + 'px';
2574
+ div.style.height = Math.floor(this.viewport.height) + 'px';
2575
+
2576
+ container.appendChild(anchor);
2577
+ container.appendChild(div);
2578
+
2579
+ this.setPdfPage = function pageViewSetPdfPage(pdfPage) {
2580
+ this.pdfPage = pdfPage;
2581
+ this.pdfPageRotate = pdfPage.rotate;
2582
+ this.viewport = pdfPage.getViewport(this.scale);
2583
+ this.stats = pdfPage.stats;
2584
+ this.update();
2585
+ };
2586
+
2587
+ this.destroy = function pageViewDestroy() {
2588
+ this.update();
2589
+ if (this.pdfPage) {
2590
+ this.pdfPage.destroy();
2591
+ }
2592
+ };
2593
+
2594
+ this.update = function pageViewUpdate(scale, rotation) {
2595
+ if (this.renderTask) {
2596
+ this.renderTask.cancel();
2597
+ }
2598
+ this.resume = null;
2599
+ this.renderingState = RenderingStates.INITIAL;
2600
+
2601
+ if (typeof rotation !== 'undefined') {
2602
+ this.rotation = rotation;
2603
+ }
2604
+
2605
+ this.scale = scale || this.scale;
2606
+
2607
+ var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
2608
+ this.viewport = this.viewport.clone({
2609
+ scale: this.scale,
2610
+ rotation: totalRotation
2611
+ });
2612
+
2613
+ div.style.width = Math.floor(this.viewport.width) + 'px';
2614
+ div.style.height = Math.floor(this.viewport.height) + 'px';
2615
+
2616
+ while (div.hasChildNodes())
2617
+ div.removeChild(div.lastChild);
2618
+ div.removeAttribute('data-loaded');
2619
+
2620
+ this.annotationLayer = null;
2621
+
2622
+ delete this.canvas;
2623
+
2624
+ this.loadingIconDiv = document.createElement('div');
2625
+ this.loadingIconDiv.className = 'loadingIcon';
2626
+ div.appendChild(this.loadingIconDiv);
2627
+ };
2628
+
2629
+ Object.defineProperty(this, 'width', {
2630
+ get: function PageView_getWidth() {
2631
+ return this.viewport.width;
2632
+ },
2633
+ enumerable: true
2634
+ });
2635
+
2636
+ Object.defineProperty(this, 'height', {
2637
+ get: function PageView_getHeight() {
2638
+ return this.viewport.height;
2639
+ },
2640
+ enumerable: true
2641
+ });
2642
+
2643
+ var self = this;
2644
+
2645
+ function setupAnnotations(pageDiv, pdfPage, viewport) {
2646
+
2647
+ function bindLink(link, dest) {
2648
+ link.href = PDFView.getDestinationHash(dest);
2649
+ link.onclick = function pageViewSetupLinksOnclick() {
2650
+ if (dest)
2651
+ PDFView.navigateTo(dest);
2652
+ return false;
2653
+ };
2654
+ link.className = 'internalLink';
2655
+ }
2656
+
2657
+ function bindNamedAction(link, action) {
2658
+ link.href = PDFView.getAnchorUrl('');
2659
+ link.onclick = function pageViewSetupNamedActionOnClick() {
2660
+ // See PDF reference, table 8.45 - Named action
2661
+ switch (action) {
2662
+ case 'GoToPage':
2663
+ document.getElementById('pageNumber').focus();
2664
+ break;
2665
+
2666
+ case 'GoBack':
2667
+ PDFHistory.back();
2668
+ break;
2669
+
2670
+ case 'GoForward':
2671
+ PDFHistory.forward();
2672
+ break;
2673
+
2674
+ case 'Find':
2675
+ if (!PDFView.supportsIntegratedFind) {
2676
+ PDFFindBar.toggle();
2677
+ }
2678
+ break;
2679
+
2680
+ case 'NextPage':
2681
+ PDFView.page++;
2682
+ break;
2683
+
2684
+ case 'PrevPage':
2685
+ PDFView.page--;
2686
+ break;
2687
+
2688
+ case 'LastPage':
2689
+ PDFView.page = PDFView.pages.length;
2690
+ break;
2691
+
2692
+ case 'FirstPage':
2693
+ PDFView.page = 1;
2694
+ break;
2695
+
2696
+ default:
2697
+ break; // No action according to spec
2698
+ }
2699
+ return false;
2700
+ };
2701
+ link.className = 'internalLink';
2702
+ }
2703
+
2704
+ pdfPage.getAnnotations().then(function(annotationsData) {
2705
+ if (self.annotationLayer) {
2706
+ // If an annotationLayer already exists, delete it to avoid creating
2707
+ // duplicate annotations when rapidly re-zooming the document.
2708
+ pageDiv.removeChild(self.annotationLayer);
2709
+ self.annotationLayer = null;
2710
+ }
2711
+ viewport = viewport.clone({ dontFlip: true });
2712
+ for (var i = 0; i < annotationsData.length; i++) {
2713
+ var data = annotationsData[i];
2714
+ var annotation = PDFJS.Annotation.fromData(data);
2715
+ if (!annotation || !annotation.hasHtml()) {
2716
+ continue;
2717
+ }
2718
+
2719
+ var element = annotation.getHtmlElement(pdfPage.commonObjs);
2720
+ mozL10n.translate(element);
2721
+
2722
+ data = annotation.getData();
2723
+ var rect = data.rect;
2724
+ var view = pdfPage.view;
2725
+ rect = PDFJS.Util.normalizeRect([
2726
+ rect[0],
2727
+ view[3] - rect[1] + view[1],
2728
+ rect[2],
2729
+ view[3] - rect[3] + view[1]
2730
+ ]);
2731
+ element.style.left = rect[0] + 'px';
2732
+ element.style.top = rect[1] + 'px';
2733
+ element.style.position = 'absolute';
2734
+
2735
+ var transform = viewport.transform;
2736
+ var transformStr = 'matrix(' + transform.join(',') + ')';
2737
+ CustomStyle.setProp('transform', element, transformStr);
2738
+ var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
2739
+ CustomStyle.setProp('transformOrigin', element, transformOriginStr);
2740
+
2741
+ if (data.subtype === 'Link' && !data.url) {
2742
+ if (data.action) {
2743
+ bindNamedAction(element, data.action);
2744
+ } else {
2745
+ bindLink(element, ('dest' in data) ? data.dest : null);
2746
+ }
2747
+ }
2748
+
2749
+ if (!self.annotationLayer) {
2750
+ var annotationLayerDiv = document.createElement('div');
2751
+ annotationLayerDiv.className = 'annotationLayer';
2752
+ pageDiv.appendChild(annotationLayerDiv);
2753
+ self.annotationLayer = annotationLayerDiv;
2754
+ }
2755
+ self.annotationLayer.appendChild(element);
2756
+ }
2757
+ });
2758
+ }
2759
+
2760
+ this.getPagePoint = function pageViewGetPagePoint(x, y) {
2761
+ return this.viewport.convertToPdfPoint(x, y);
2762
+ };
2763
+
2764
+ this.scrollIntoView = function pageViewScrollIntoView(dest) {
2765
+ if (!dest) {
2766
+ scrollIntoView(div);
2767
+ return;
2768
+ }
2769
+ if (PDFView.isPresentationMode) { // Avoid breaking presentation mode.
2770
+ PDFView.page = id;
2771
+ return;
2772
+ }
2773
+
2774
+ var x = 0, y = 0;
2775
+ var width = 0, height = 0, widthScale, heightScale;
2776
+ var scale = 0;
2777
+ switch (dest[1].name) {
2778
+ case 'XYZ':
2779
+ x = dest[2];
2780
+ y = dest[3];
2781
+ scale = dest[4];
2782
+ // If x and/or y coordinates are not supplied, default to
2783
+ // _top_ left of the page (not the obvious bottom left,
2784
+ // since aligning the bottom of the intended page with the
2785
+ // top of the window is rarely helpful).
2786
+ x = x !== null ? x : 0;
2787
+ y = y !== null ? y : this.height / this.scale;
2788
+ break;
2789
+ case 'Fit':
2790
+ case 'FitB':
2791
+ scale = 'page-fit';
2792
+ break;
2793
+ case 'FitH':
2794
+ case 'FitBH':
2795
+ y = dest[2];
2796
+ scale = 'page-width';
2797
+ break;
2798
+ case 'FitV':
2799
+ case 'FitBV':
2800
+ x = dest[2];
2801
+ scale = 'page-height';
2802
+ break;
2803
+ case 'FitR':
2804
+ x = dest[2];
2805
+ y = dest[3];
2806
+ width = dest[4] - x;
2807
+ height = dest[5] - y;
2808
+ widthScale = (PDFView.container.clientWidth - SCROLLBAR_PADDING) /
2809
+ width / CSS_UNITS;
2810
+ heightScale = (PDFView.container.clientHeight - SCROLLBAR_PADDING) /
2811
+ height / CSS_UNITS;
2812
+ scale = Math.min(widthScale, heightScale);
2813
+ break;
2814
+ default:
2815
+ return;
2816
+ }
2817
+
2818
+ if (scale && scale !== PDFView.currentScale) {
2819
+ PDFView.parseScale(scale, true, true);
2820
+ } else if (PDFView.currentScale === UNKNOWN_SCALE) {
2821
+ PDFView.parseScale(DEFAULT_SCALE, true, true);
2822
+ }
2823
+
2824
+ if (scale === 'page-fit' && !dest[4]) {
2825
+ scrollIntoView(div);
2826
+ return;
2827
+ }
2828
+
2829
+ var boundingRect = [
2830
+ this.viewport.convertToViewportPoint(x, y),
2831
+ this.viewport.convertToViewportPoint(x + width, y + height)
2832
+ ];
2833
+ setTimeout(function pageViewScrollIntoViewRelayout() {
2834
+ // letting page to re-layout before scrolling
2835
+ var scale = PDFView.currentScale;
2836
+ var x = Math.min(boundingRect[0][0], boundingRect[1][0]);
2837
+ var y = Math.min(boundingRect[0][1], boundingRect[1][1]);
2838
+ var width = Math.abs(boundingRect[0][0] - boundingRect[1][0]);
2839
+ var height = Math.abs(boundingRect[0][1] - boundingRect[1][1]);
2840
+
2841
+ scrollIntoView(div, {left: x, top: y, width: width, height: height});
2842
+ }, 0);
2843
+ };
2844
+
2845
+ this.getTextContent = function pageviewGetTextContent() {
2846
+ if (!this.textContent) {
2847
+ this.textContent = this.pdfPage.getTextContent();
2848
+ }
2849
+ return this.textContent;
2850
+ };
2851
+
2852
+ this.draw = function pageviewDraw(callback) {
2853
+ var pdfPage = this.pdfPage;
2854
+
2855
+ if (!pdfPage) {
2856
+ var promise = PDFView.getPage(this.id);
2857
+ promise.then(function(pdfPage) {
2858
+ this.setPdfPage(pdfPage);
2859
+ this.draw(callback);
2860
+ }.bind(this));
2861
+ return;
2862
+ }
2863
+
2864
+ if (this.renderingState !== RenderingStates.INITIAL) {
2865
+ console.error('Must be in new state before drawing');
2866
+ }
2867
+
2868
+ this.renderingState = RenderingStates.RUNNING;
2869
+
2870
+ var viewport = this.viewport;
2871
+ // Wrap the canvas so if it has a css transform for highdpi the overflow
2872
+ // will be hidden in FF.
2873
+ var canvasWrapper = document.createElement('div');
2874
+ canvasWrapper.style.width = div.style.width;
2875
+ canvasWrapper.style.height = div.style.height;
2876
+ canvasWrapper.classList.add('canvasWrapper');
2877
+
2878
+ var canvas = document.createElement('canvas');
2879
+ canvas.id = 'page' + this.id;
2880
+ canvasWrapper.appendChild(canvas);
2881
+ div.appendChild(canvasWrapper);
2882
+ this.canvas = canvas;
2883
+
2884
+ var scale = this.scale;
2885
+ var outputScale = getOutputScale();
2886
+ canvas.width = Math.floor(viewport.width) * outputScale.sx;
2887
+ canvas.height = Math.floor(viewport.height) * outputScale.sy;
2888
+
2889
+ var textLayerDiv = null;
2890
+ if (!PDFJS.disableTextLayer) {
2891
+ textLayerDiv = document.createElement('div');
2892
+ textLayerDiv.className = 'textLayer';
2893
+ textLayerDiv.style.width = canvas.width + 'px';
2894
+ textLayerDiv.style.height = canvas.height + 'px';
2895
+ div.appendChild(textLayerDiv);
2896
+ }
2897
+ var textLayer = this.textLayer =
2898
+ textLayerDiv ? new TextLayerBuilder({
2899
+ textLayerDiv: textLayerDiv,
2900
+ pageIndex: this.id - 1,
2901
+ lastScrollSource: PDFView
2902
+ }) : null;
2903
+
2904
+ if (outputScale.scaled) {
2905
+ var cssScale = 'scale(' + (1 / outputScale.sx) + ', ' +
2906
+ (1 / outputScale.sy) + ')';
2907
+ CustomStyle.setProp('transform' , canvas, cssScale);
2908
+ CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
2909
+ if (textLayerDiv) {
2910
+ CustomStyle.setProp('transform' , textLayerDiv, cssScale);
2911
+ CustomStyle.setProp('transformOrigin' , textLayerDiv, '0% 0%');
2912
+ }
2913
+ }
2914
+
2915
+ var ctx = canvas.getContext('2d');
2916
+ // TODO(mack): use data attributes to store these
2917
+ ctx._scaleX = outputScale.sx;
2918
+ ctx._scaleY = outputScale.sy;
2919
+ if (outputScale.scaled) {
2920
+ ctx.scale(outputScale.sx, outputScale.sy);
2921
+ }
2922
+
2923
+ // Rendering area
2924
+
2925
+ var self = this;
2926
+ function pageViewDrawCallback(error) {
2927
+ // The renderTask may have been replaced by a new one, so only remove the
2928
+ // reference to the renderTask if it matches the one that is triggering
2929
+ // this callback.
2930
+ if (renderTask === self.renderTask) {
2931
+ self.renderTask = null;
2932
+ }
2933
+
2934
+ if (error === 'cancelled') {
2935
+ return;
2936
+ }
2937
+
2938
+ self.renderingState = RenderingStates.FINISHED;
2939
+
2940
+ if (self.loadingIconDiv) {
2941
+ div.removeChild(self.loadingIconDiv);
2942
+ delete self.loadingIconDiv;
2943
+ }
2944
+
2945
+ if (error) {
2946
+ PDFView.error(mozL10n.get('rendering_error', null,
2947
+ 'An error occurred while rendering the page.'), error);
2948
+ }
2949
+
2950
+ self.stats = pdfPage.stats;
2951
+ self.updateStats();
2952
+ if (self.onAfterDraw)
2953
+ self.onAfterDraw();
2954
+
2955
+ cache.push(self);
2956
+
2957
+ var event = document.createEvent('CustomEvent');
2958
+ event.initCustomEvent('pagerender', true, true, {
2959
+ pageNumber: pdfPage.pageNumber
2960
+ });
2961
+ div.dispatchEvent(event);
2962
+
2963
+ callback();
2964
+ }
2965
+
2966
+ var renderContext = {
2967
+ canvasContext: ctx,
2968
+ viewport: this.viewport,
2969
+ textLayer: textLayer,
2970
+ continueCallback: function pdfViewcContinueCallback(cont) {
2971
+ if (PDFView.highestPriorityPage !== 'page' + self.id) {
2972
+ self.renderingState = RenderingStates.PAUSED;
2973
+ self.resume = function resumeCallback() {
2974
+ self.renderingState = RenderingStates.RUNNING;
2975
+ cont();
2976
+ };
2977
+ return;
2978
+ }
2979
+ cont();
2980
+ }
2981
+ };
2982
+ var renderTask = this.renderTask = this.pdfPage.render(renderContext);
2983
+
2984
+ this.renderTask.then(
2985
+ function pdfPageRenderCallback() {
2986
+ pageViewDrawCallback(null);
2987
+ },
2988
+ function pdfPageRenderError(error) {
2989
+ pageViewDrawCallback(error);
2990
+ }
2991
+ );
2992
+
2993
+ if (textLayer) {
2994
+ this.getTextContent().then(
2995
+ function textContentResolved(textContent) {
2996
+ textLayer.setTextContent(textContent);
2997
+ }
2998
+ );
2999
+ }
3000
+
3001
+ setupAnnotations(div, pdfPage, this.viewport);
3002
+ div.setAttribute('data-loaded', true);
3003
+ };
3004
+
3005
+ this.beforePrint = function pageViewBeforePrint() {
3006
+ var pdfPage = this.pdfPage;
3007
+
3008
+ var viewport = pdfPage.getViewport(1);
3009
+ // Use the same hack we use for high dpi displays for printing to get better
3010
+ // output until bug 811002 is fixed in FF.
3011
+ var PRINT_OUTPUT_SCALE = 2;
3012
+ var canvas = this.canvas = document.createElement('canvas');
3013
+ canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE;
3014
+ canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE;
3015
+ canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt';
3016
+ canvas.style.height = (PRINT_OUTPUT_SCALE * viewport.height) + 'pt';
3017
+ var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' +
3018
+ (1 / PRINT_OUTPUT_SCALE) + ')';
3019
+ CustomStyle.setProp('transform' , canvas, cssScale);
3020
+ CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
3021
+
3022
+ var printContainer = document.getElementById('printContainer');
3023
+ printContainer.appendChild(canvas);
3024
+
3025
+ var self = this;
3026
+ canvas.mozPrintCallback = function(obj) {
3027
+ var ctx = obj.context;
3028
+
3029
+ ctx.save();
3030
+ ctx.fillStyle = 'rgb(255, 255, 255)';
3031
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
3032
+ ctx.restore();
3033
+ ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE);
3034
+
3035
+ var renderContext = {
3036
+ canvasContext: ctx,
3037
+ viewport: viewport
3038
+ };
3039
+
3040
+ pdfPage.render(renderContext).then(function() {
3041
+ // Tell the printEngine that rendering this canvas/page has finished.
3042
+ obj.done();
3043
+ self.pdfPage.destroy();
3044
+ }, function(error) {
3045
+ console.error(error);
3046
+ // Tell the printEngine that rendering this canvas/page has failed.
3047
+ // This will make the print proces stop.
3048
+ if ('abort' in obj)
3049
+ obj.abort();
3050
+ else
3051
+ obj.done();
3052
+ self.pdfPage.destroy();
3053
+ });
3054
+ };
3055
+ };
3056
+
3057
+ this.updateStats = function pageViewUpdateStats() {
3058
+ if (!this.stats) {
3059
+ return;
3060
+ }
3061
+
3062
+ if (PDFJS.pdfBug && Stats.enabled) {
3063
+ var stats = this.stats;
3064
+ Stats.add(this.id, stats);
3065
+ }
3066
+ };
3067
+ };
3068
+
3069
+
3070
+ var ThumbnailView = function thumbnailView(container, id, defaultViewport) {
3071
+ var anchor = document.createElement('a');
3072
+ anchor.href = PDFView.getAnchorUrl('#page=' + id);
3073
+ anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}');
3074
+ anchor.onclick = function stopNavigation() {
3075
+ PDFView.page = id;
3076
+ return false;
3077
+ };
3078
+
3079
+ this.pdfPage = undefined;
3080
+ this.viewport = defaultViewport;
3081
+ this.pdfPageRotate = defaultViewport.rotate;
3082
+
3083
+ this.rotation = 0;
3084
+ this.pageWidth = this.viewport.width;
3085
+ this.pageHeight = this.viewport.height;
3086
+ this.pageRatio = this.pageWidth / this.pageHeight;
3087
+ this.id = id;
3088
+
3089
+ this.canvasWidth = 98;
3090
+ this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight;
3091
+ this.scale = (this.canvasWidth / this.pageWidth);
3092
+
3093
+ var div = this.el = document.createElement('div');
3094
+ div.id = 'thumbnailContainer' + id;
3095
+ div.className = 'thumbnail';
3096
+
3097
+ if (id === 1) {
3098
+ // Highlight the thumbnail of the first page when no page number is
3099
+ // specified (or exists in cache) when the document is loaded.
3100
+ div.classList.add('selected');
3101
+ }
3102
+
3103
+ var ring = document.createElement('div');
3104
+ ring.className = 'thumbnailSelectionRing';
3105
+ ring.style.width = this.canvasWidth + 'px';
3106
+ ring.style.height = this.canvasHeight + 'px';
3107
+
3108
+ div.appendChild(ring);
3109
+ anchor.appendChild(div);
3110
+ container.appendChild(anchor);
3111
+
3112
+ this.hasImage = false;
3113
+ this.renderingState = RenderingStates.INITIAL;
3114
+
3115
+ this.setPdfPage = function thumbnailViewSetPdfPage(pdfPage) {
3116
+ this.pdfPage = pdfPage;
3117
+ this.pdfPageRotate = pdfPage.rotate;
3118
+ this.viewport = pdfPage.getViewport(1);
3119
+ this.update();
3120
+ };
3121
+
3122
+ this.update = function thumbnailViewUpdate(rot) {
3123
+ if (!this.pdfPage) {
3124
+ return;
3125
+ }
3126
+
3127
+ if (rot !== undefined) {
3128
+ this.rotation = rot;
3129
+ }
3130
+
3131
+ var totalRotation = (this.rotation + this.pdfPage.rotate) % 360;
3132
+ this.viewport = this.viewport.clone({
3133
+ scale: 1,
3134
+ rotation: totalRotation
3135
+ });
3136
+ this.pageWidth = this.viewport.width;
3137
+ this.pageHeight = this.viewport.height;
3138
+ this.pageRatio = this.pageWidth / this.pageHeight;
3139
+
3140
+ this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight;
3141
+ this.scale = (this.canvasWidth / this.pageWidth);
3142
+
3143
+ div.removeAttribute('data-loaded');
3144
+ ring.textContent = '';
3145
+ ring.style.width = this.canvasWidth + 'px';
3146
+ ring.style.height = this.canvasHeight + 'px';
3147
+
3148
+ this.hasImage = false;
3149
+ this.renderingState = RenderingStates.INITIAL;
3150
+ this.resume = null;
3151
+ };
3152
+
3153
+ this.getPageDrawContext = function thumbnailViewGetPageDrawContext() {
3154
+ var canvas = document.createElement('canvas');
3155
+ canvas.id = 'thumbnail' + id;
3156
+
3157
+ canvas.width = this.canvasWidth;
3158
+ canvas.height = this.canvasHeight;
3159
+ canvas.className = 'thumbnailImage';
3160
+ canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas',
3161
+ {page: id}, 'Thumbnail of Page {{page}}'));
3162
+
3163
+ div.setAttribute('data-loaded', true);
3164
+
3165
+ ring.appendChild(canvas);
3166
+
3167
+ var ctx = canvas.getContext('2d');
3168
+ ctx.save();
3169
+ ctx.fillStyle = 'rgb(255, 255, 255)';
3170
+ ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
3171
+ ctx.restore();
3172
+ return ctx;
3173
+ };
3174
+
3175
+ this.drawingRequired = function thumbnailViewDrawingRequired() {
3176
+ return !this.hasImage;
3177
+ };
3178
+
3179
+ this.draw = function thumbnailViewDraw(callback) {
3180
+ if (!this.pdfPage) {
3181
+ var promise = PDFView.getPage(this.id);
3182
+ promise.then(function(pdfPage) {
3183
+ this.setPdfPage(pdfPage);
3184
+ this.draw(callback);
3185
+ }.bind(this));
3186
+ return;
3187
+ }
3188
+
3189
+ if (this.renderingState !== RenderingStates.INITIAL) {
3190
+ console.error('Must be in new state before drawing');
3191
+ }
3192
+
3193
+ this.renderingState = RenderingStates.RUNNING;
3194
+ if (this.hasImage) {
3195
+ callback();
3196
+ return;
3197
+ }
3198
+
3199
+ var self = this;
3200
+ var ctx = this.getPageDrawContext();
3201
+ var drawViewport = this.viewport.clone({ scale: this.scale });
3202
+ var renderContext = {
3203
+ canvasContext: ctx,
3204
+ viewport: drawViewport,
3205
+ continueCallback: function(cont) {
3206
+ if (PDFView.highestPriorityPage !== 'thumbnail' + self.id) {
3207
+ self.renderingState = RenderingStates.PAUSED;
3208
+ self.resume = function() {
3209
+ self.renderingState = RenderingStates.RUNNING;
3210
+ cont();
3211
+ };
3212
+ return;
3213
+ }
3214
+ cont();
3215
+ }
3216
+ };
3217
+ this.pdfPage.render(renderContext).then(
3218
+ function pdfPageRenderCallback() {
3219
+ self.renderingState = RenderingStates.FINISHED;
3220
+ callback();
3221
+ },
3222
+ function pdfPageRenderError(error) {
3223
+ self.renderingState = RenderingStates.FINISHED;
3224
+ callback();
3225
+ }
3226
+ );
3227
+ this.hasImage = true;
3228
+ };
3229
+
3230
+ this.setImage = function thumbnailViewSetImage(img) {
3231
+ if (this.hasImage || !img)
3232
+ return;
3233
+ this.renderingState = RenderingStates.FINISHED;
3234
+ var ctx = this.getPageDrawContext();
3235
+ ctx.drawImage(img, 0, 0, img.width, img.height,
3236
+ 0, 0, ctx.canvas.width, ctx.canvas.height);
3237
+
3238
+ this.hasImage = true;
3239
+ };
3240
+ };
3241
+
3242
+
3243
+ /* globals CustomStyle, PDFFindController, scrollIntoView */
3244
+
3245
+ /**
3246
+ * TextLayerBuilder provides text-selection
3247
+ * functionality for the PDF. It does this
3248
+ * by creating overlay divs over the PDF
3249
+ * text. This divs contain text that matches
3250
+ * the PDF text they are overlaying. This
3251
+ * object also provides for a way to highlight
3252
+ * text that is being searched for.
3253
+ */
3254
+ var TextLayerBuilder = function textLayerBuilder(options) {
3255
+ var textLayerFrag = document.createDocumentFragment();
3256
+
3257
+ this.textLayerDiv = options.textLayerDiv;
3258
+ this.layoutDone = false;
3259
+ this.divContentDone = false;
3260
+ this.pageIdx = options.pageIndex;
3261
+ this.matches = [];
3262
+ this.lastScrollSource = options.lastScrollSource;
3263
+
3264
+ if(typeof PDFFindController === 'undefined') {
3265
+ window.PDFFindController = null;
3266
+ }
3267
+
3268
+ if(typeof this.lastScrollSource === 'undefined') {
3269
+ this.lastScrollSource = null;
3270
+ }
3271
+
3272
+ this.beginLayout = function textLayerBuilderBeginLayout() {
3273
+ this.textDivs = [];
3274
+ this.renderingDone = false;
3275
+ };
3276
+
3277
+ this.endLayout = function textLayerBuilderEndLayout() {
3278
+ this.layoutDone = true;
3279
+ this.insertDivContent();
3280
+ };
3281
+
3282
+ this.renderLayer = function textLayerBuilderRenderLayer() {
3283
+ var self = this;
3284
+ var textDivs = this.textDivs;
3285
+ var bidiTexts = this.textContent.bidiTexts;
3286
+ var textLayerDiv = this.textLayerDiv;
3287
+ var canvas = document.createElement('canvas');
3288
+ var ctx = canvas.getContext('2d');
3289
+
3290
+ // No point in rendering so many divs as it'd make the browser unusable
3291
+ // even after the divs are rendered
3292
+ var MAX_TEXT_DIVS_TO_RENDER = 100000;
3293
+ if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER)
3294
+ return;
3295
+
3296
+ for (var i = 0, ii = textDivs.length; i < ii; i++) {
3297
+ var textDiv = textDivs[i];
3298
+ if ('isWhitespace' in textDiv.dataset) {
3299
+ continue;
3300
+ }
3301
+ textLayerFrag.appendChild(textDiv);
3302
+
3303
+ ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
3304
+ var width = ctx.measureText(textDiv.textContent).width;
3305
+
3306
+ if (width > 0) {
3307
+ var textScale = textDiv.dataset.canvasWidth / width;
3308
+ var rotation = textDiv.dataset.angle;
3309
+ var transform = 'scale(' + textScale + ', 1)';
3310
+ transform = 'rotate(' + rotation + 'deg) ' + transform;
3311
+ CustomStyle.setProp('transform' , textDiv, transform);
3312
+ CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
3313
+
3314
+ textLayerDiv.appendChild(textDiv);
3315
+ }
3316
+ }
3317
+
3318
+ this.renderingDone = true;
3319
+ this.updateMatches();
3320
+
3321
+ textLayerDiv.appendChild(textLayerFrag);
3322
+ };
3323
+
3324
+ this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
3325
+ // Schedule renderLayout() if user has been scrolling, otherwise
3326
+ // run it right away
3327
+ var RENDER_DELAY = 200; // in ms
3328
+ var self = this;
3329
+ var lastScroll = this.lastScrollSource === null ?
3330
+ 0 : this.lastScrollSource.lastScroll;
3331
+
3332
+ if (Date.now() - lastScroll > RENDER_DELAY) {
3333
+ // Render right away
3334
+ this.renderLayer();
3335
+ } else {
3336
+ // Schedule
3337
+ if (this.renderTimer)
3338
+ clearTimeout(this.renderTimer);
3339
+ this.renderTimer = setTimeout(function() {
3340
+ self.setupRenderLayoutTimer();
3341
+ }, RENDER_DELAY);
3342
+ }
3343
+ };
3344
+
3345
+ this.appendText = function textLayerBuilderAppendText(geom) {
3346
+ var textDiv = document.createElement('div');
3347
+
3348
+ // vScale and hScale already contain the scaling to pixel units
3349
+ var fontHeight = geom.fontSize * Math.abs(geom.vScale);
3350
+ textDiv.dataset.canvasWidth = geom.canvasWidth * Math.abs(geom.hScale);
3351
+ textDiv.dataset.fontName = geom.fontName;
3352
+ textDiv.dataset.angle = geom.angle * (180 / Math.PI);
3353
+
3354
+ textDiv.style.fontSize = fontHeight + 'px';
3355
+ textDiv.style.fontFamily = geom.fontFamily;
3356
+ textDiv.style.left = (geom.x + (fontHeight * Math.sin(geom.angle))) + 'px';
3357
+ textDiv.style.top = (geom.y - (fontHeight * Math.cos(geom.angle))) + 'px';
3358
+
3359
+ // The content of the div is set in the `setTextContent` function.
3360
+
3361
+ this.textDivs.push(textDiv);
3362
+ };
3363
+
3364
+ this.insertDivContent = function textLayerUpdateTextContent() {
3365
+ // Only set the content of the divs once layout has finished, the content
3366
+ // for the divs is available and content is not yet set on the divs.
3367
+ if (!this.layoutDone || this.divContentDone || !this.textContent)
3368
+ return;
3369
+
3370
+ this.divContentDone = true;
3371
+
3372
+ var textDivs = this.textDivs;
3373
+ var bidiTexts = this.textContent.bidiTexts;
3374
+
3375
+ for (var i = 0; i < bidiTexts.length; i++) {
3376
+ var bidiText = bidiTexts[i];
3377
+ var textDiv = textDivs[i];
3378
+ if (!/\S/.test(bidiText.str)) {
3379
+ textDiv.dataset.isWhitespace = true;
3380
+ continue;
3381
+ }
3382
+
3383
+ textDiv.textContent = bidiText.str;
3384
+ // bidiText.dir may be 'ttb' for vertical texts.
3385
+ textDiv.dir = bidiText.dir;
3386
+ }
3387
+
3388
+ this.setupRenderLayoutTimer();
3389
+ };
3390
+
3391
+ this.setTextContent = function textLayerBuilderSetTextContent(textContent) {
3392
+ this.textContent = textContent;
3393
+ this.insertDivContent();
3394
+ };
3395
+
3396
+ this.convertMatches = function textLayerBuilderConvertMatches(matches) {
3397
+ var i = 0;
3398
+ var iIndex = 0;
3399
+ var bidiTexts = this.textContent.bidiTexts;
3400
+ var end = bidiTexts.length - 1;
3401
+ var queryLen = PDFFindController === null ?
3402
+ 0 : PDFFindController.state.query.length;
3403
+
3404
+ var lastDivIdx = -1;
3405
+ var pos;
3406
+
3407
+ var ret = [];
3408
+
3409
+ // Loop over all the matches.
3410
+ for (var m = 0; m < matches.length; m++) {
3411
+ var matchIdx = matches[m];
3412
+ // # Calculate the begin position.
3413
+
3414
+ // Loop over the divIdxs.
3415
+ while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
3416
+ iIndex += bidiTexts[i].str.length;
3417
+ i++;
3418
+ }
3419
+
3420
+ // TODO: Do proper handling here if something goes wrong.
3421
+ if (i == bidiTexts.length) {
3422
+ console.error('Could not find matching mapping');
3423
+ }
3424
+
3425
+ var match = {
3426
+ begin: {
3427
+ divIdx: i,
3428
+ offset: matchIdx - iIndex
3429
+ }
3430
+ };
3431
+
3432
+ // # Calculate the end position.
3433
+ matchIdx += queryLen;
3434
+
3435
+ // Somewhat same array as above, but use a > instead of >= to get the end
3436
+ // position right.
3437
+ while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
3438
+ iIndex += bidiTexts[i].str.length;
3439
+ i++;
3440
+ }
3441
+
3442
+ match.end = {
3443
+ divIdx: i,
3444
+ offset: matchIdx - iIndex
3445
+ };
3446
+ ret.push(match);
3447
+ }
3448
+
3449
+ return ret;
3450
+ };
3451
+
3452
+ this.renderMatches = function textLayerBuilder_renderMatches(matches) {
3453
+ // Early exit if there is nothing to render.
3454
+ if (matches.length === 0) {
3455
+ return;
3456
+ }
3457
+
3458
+ var bidiTexts = this.textContent.bidiTexts;
3459
+ var textDivs = this.textDivs;
3460
+ var prevEnd = null;
3461
+ var isSelectedPage = PDFFindController === null ?
3462
+ false : (this.pageIdx === PDFFindController.selected.pageIdx);
3463
+
3464
+ var selectedMatchIdx = PDFFindController === null ?
3465
+ -1 : PDFFindController.selected.matchIdx;
3466
+
3467
+ var highlightAll = PDFFindController === null ?
3468
+ false : PDFFindController.state.highlightAll;
3469
+
3470
+ var infty = {
3471
+ divIdx: -1,
3472
+ offset: undefined
3473
+ };
3474
+
3475
+ function beginText(begin, className) {
3476
+ var divIdx = begin.divIdx;
3477
+ var div = textDivs[divIdx];
3478
+ div.textContent = '';
3479
+
3480
+ var content = bidiTexts[divIdx].str.substring(0, begin.offset);
3481
+ var node = document.createTextNode(content);
3482
+ if (className) {
3483
+ var isSelected = isSelectedPage &&
3484
+ divIdx === selectedMatchIdx;
3485
+ var span = document.createElement('span');
3486
+ span.className = className + (isSelected ? ' selected' : '');
3487
+ span.appendChild(node);
3488
+ div.appendChild(span);
3489
+ return;
3490
+ }
3491
+ div.appendChild(node);
3492
+ }
3493
+
3494
+ function appendText(from, to, className) {
3495
+ var divIdx = from.divIdx;
3496
+ var div = textDivs[divIdx];
3497
+
3498
+ var content = bidiTexts[divIdx].str.substring(from.offset, to.offset);
3499
+ var node = document.createTextNode(content);
3500
+ if (className) {
3501
+ var span = document.createElement('span');
3502
+ span.className = className;
3503
+ span.appendChild(node);
3504
+ div.appendChild(span);
3505
+ return;
3506
+ }
3507
+ div.appendChild(node);
3508
+ }
3509
+
3510
+ function highlightDiv(divIdx, className) {
3511
+ textDivs[divIdx].className = className;
3512
+ }
3513
+
3514
+ var i0 = selectedMatchIdx, i1 = i0 + 1, i;
3515
+
3516
+ if (highlightAll) {
3517
+ i0 = 0;
3518
+ i1 = matches.length;
3519
+ } else if (!isSelectedPage) {
3520
+ // Not highlighting all and this isn't the selected page, so do nothing.
3521
+ return;
3522
+ }
3523
+
3524
+ for (i = i0; i < i1; i++) {
3525
+ var match = matches[i];
3526
+ var begin = match.begin;
3527
+ var end = match.end;
3528
+
3529
+ var isSelected = isSelectedPage && i === selectedMatchIdx;
3530
+ var highlightSuffix = (isSelected ? ' selected' : '');
3531
+ if (isSelected)
3532
+ scrollIntoView(textDivs[begin.divIdx], {top: -50});
3533
+
3534
+ // Match inside new div.
3535
+ if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
3536
+ // If there was a previous div, then add the text at the end
3537
+ if (prevEnd !== null) {
3538
+ appendText(prevEnd, infty);
3539
+ }
3540
+ // clears the divs and set the content until the begin point.
3541
+ beginText(begin);
3542
+ } else {
3543
+ appendText(prevEnd, begin);
3544
+ }
3545
+
3546
+ if (begin.divIdx === end.divIdx) {
3547
+ appendText(begin, end, 'highlight' + highlightSuffix);
3548
+ } else {
3549
+ appendText(begin, infty, 'highlight begin' + highlightSuffix);
3550
+ for (var n = begin.divIdx + 1; n < end.divIdx; n++) {
3551
+ highlightDiv(n, 'highlight middle' + highlightSuffix);
3552
+ }
3553
+ beginText(end, 'highlight end' + highlightSuffix);
3554
+ }
3555
+ prevEnd = end;
3556
+ }
3557
+
3558
+ if (prevEnd) {
3559
+ appendText(prevEnd, infty);
3560
+ }
3561
+ };
3562
+
3563
+ this.updateMatches = function textLayerUpdateMatches() {
3564
+ // Only show matches, once all rendering is done.
3565
+ if (!this.renderingDone)
3566
+ return;
3567
+
3568
+ // Clear out all matches.
3569
+ var matches = this.matches;
3570
+ var textDivs = this.textDivs;
3571
+ var bidiTexts = this.textContent.bidiTexts;
3572
+ var clearedUntilDivIdx = -1;
3573
+
3574
+ // Clear out all current matches.
3575
+ for (var i = 0; i < matches.length; i++) {
3576
+ var match = matches[i];
3577
+ var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
3578
+ for (var n = begin; n <= match.end.divIdx; n++) {
3579
+ var div = textDivs[n];
3580
+ div.textContent = bidiTexts[n].str;
3581
+ div.className = '';
3582
+ }
3583
+ clearedUntilDivIdx = match.end.divIdx + 1;
3584
+ }
3585
+
3586
+ if (PDFFindController === null || !PDFFindController.active)
3587
+ return;
3588
+
3589
+ // Convert the matches on the page controller into the match format used
3590
+ // for the textLayer.
3591
+ this.matches = matches =
3592
+ this.convertMatches(PDFFindController === null ?
3593
+ [] : (PDFFindController.pageMatches[this.pageIdx] || []));
3594
+
3595
+ this.renderMatches(this.matches);
3596
+ };
3597
+ };
3598
+
3599
+
3600
+
3601
+ var DocumentOutlineView = function documentOutlineView(outline) {
3602
+ var outlineView = document.getElementById('outlineView');
3603
+ var outlineButton = document.getElementById('viewOutline');
3604
+ while (outlineView.firstChild)
3605
+ outlineView.removeChild(outlineView.firstChild);
3606
+
3607
+ if (!outline) {
3608
+ if (!outlineView.classList.contains('hidden'))
3609
+ PDFView.switchSidebarView('thumbs');
3610
+
3611
+ return;
3612
+ }
3613
+
3614
+ function bindItemLink(domObj, item) {
3615
+ domObj.href = PDFView.getDestinationHash(item.dest);
3616
+ domObj.onclick = function documentOutlineViewOnclick(e) {
3617
+ PDFView.navigateTo(item.dest);
3618
+ return false;
3619
+ };
3620
+ }
3621
+
3622
+
3623
+ var queue = [{parent: outlineView, items: outline}];
3624
+ while (queue.length > 0) {
3625
+ var levelData = queue.shift();
3626
+ var i, n = levelData.items.length;
3627
+ for (i = 0; i < n; i++) {
3628
+ var item = levelData.items[i];
3629
+ var div = document.createElement('div');
3630
+ div.className = 'outlineItem';
3631
+ var a = document.createElement('a');
3632
+ bindItemLink(a, item);
3633
+ a.textContent = item.title;
3634
+ div.appendChild(a);
3635
+
3636
+ if (item.items.length > 0) {
3637
+ var itemsDiv = document.createElement('div');
3638
+ itemsDiv.className = 'outlineItems';
3639
+ div.appendChild(itemsDiv);
3640
+ queue.push({parent: itemsDiv, items: item.items});
3641
+ }
3642
+
3643
+ levelData.parent.appendChild(div);
3644
+ }
3645
+ }
3646
+ };
3647
+
3648
+ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
3649
+ PDFView.initialize();
3650
+
3651
+ var params = PDFView.parseQueryString(document.location.search.substring(1));
3652
+ var file = params.file || DEFAULT_URL;
3653
+
3654
+
3655
+ var fileInput = document.createElement('input');
3656
+ fileInput.id = 'fileInput';
3657
+ fileInput.className = 'fileInput';
3658
+ fileInput.setAttribute('type', 'file');
3659
+ fileInput.setAttribute('style',
3660
+ 'visibility: hidden; position: fixed; right: 0; top: 0');
3661
+ fileInput.oncontextmenu = noContextMenuHandler;
3662
+ document.body.appendChild(fileInput);
3663
+
3664
+ if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {
3665
+ document.getElementById('openFile').setAttribute('hidden', 'true');
3666
+ } else {
3667
+ document.getElementById('fileInput').value = null;
3668
+ }
3669
+
3670
+ // Special debugging flags in the hash section of the URL.
3671
+ var hash = document.location.hash.substring(1);
3672
+ var hashParams = PDFView.parseQueryString(hash);
3673
+
3674
+ if ('disableWorker' in hashParams) {
3675
+ PDFJS.disableWorker = (hashParams['disableWorker'] === 'true');
3676
+ }
3677
+
3678
+ if ('disableRange' in hashParams) {
3679
+ PDFJS.disableRange = (hashParams['disableRange'] === 'true');
3680
+ }
3681
+
3682
+ if ('disableAutoFetch' in hashParams) {
3683
+ PDFJS.disableAutoFetch = (hashParams['disableAutoFetch'] === 'true');
3684
+ }
3685
+
3686
+ if ('disableFontFace' in hashParams) {
3687
+ PDFJS.disableFontFace = (hashParams['disableFontFace'] === 'true');
3688
+ }
3689
+
3690
+ if ('disableHistory' in hashParams) {
3691
+ PDFJS.disableHistory = (hashParams['disableHistory'] === 'true');
3692
+ }
3693
+
3694
+ var locale = navigator.language;
3695
+ if ('locale' in hashParams)
3696
+ locale = hashParams['locale'];
3697
+ mozL10n.setLanguage(locale);
3698
+
3699
+ if ('textLayer' in hashParams) {
3700
+ switch (hashParams['textLayer']) {
3701
+ case 'off':
3702
+ PDFJS.disableTextLayer = true;
3703
+ break;
3704
+ case 'visible':
3705
+ case 'shadow':
3706
+ case 'hover':
3707
+ var viewer = document.getElementById('viewer');
3708
+ viewer.classList.add('textLayer-' + hashParams['textLayer']);
3709
+ break;
3710
+ }
3711
+ }
3712
+
3713
+ if ('pdfBug' in hashParams) {
3714
+ PDFJS.pdfBug = true;
3715
+ var pdfBug = hashParams['pdfBug'];
3716
+ var enabled = pdfBug.split(',');
3717
+ PDFBug.enable(enabled);
3718
+ PDFBug.init();
3719
+ }
3720
+
3721
+ if (!PDFView.supportsPrinting) {
3722
+ document.getElementById('print').classList.add('hidden');
3723
+ }
3724
+
3725
+ if (!PDFView.supportsFullscreen) {
3726
+ document.getElementById('presentationMode').classList.add('hidden');
3727
+ }
3728
+
3729
+ if (PDFView.supportsIntegratedFind) {
3730
+ document.getElementById('viewFind').classList.add('hidden');
3731
+ }
3732
+
3733
+ // Listen for warnings to trigger the fallback UI. Errors should be caught
3734
+ // and call PDFView.error() so we don't need to listen for those.
3735
+ PDFJS.LogManager.addLogger({
3736
+ warn: function() {
3737
+ PDFView.fallback();
3738
+ }
3739
+ });
3740
+
3741
+ // Suppress context menus for some controls
3742
+ document.getElementById('scaleSelect').oncontextmenu = noContextMenuHandler;
3743
+
3744
+ var mainContainer = document.getElementById('mainContainer');
3745
+ var outerContainer = document.getElementById('outerContainer');
3746
+ mainContainer.addEventListener('transitionend', function(e) {
3747
+ if (e.target == mainContainer) {
3748
+ var event = document.createEvent('UIEvents');
3749
+ event.initUIEvent('resize', false, false, window, 0);
3750
+ window.dispatchEvent(event);
3751
+ outerContainer.classList.remove('sidebarMoving');
3752
+ }
3753
+ }, true);
3754
+
3755
+ document.getElementById('sidebarToggle').addEventListener('click',
3756
+ function() {
3757
+ this.classList.toggle('toggled');
3758
+ outerContainer.classList.add('sidebarMoving');
3759
+ outerContainer.classList.toggle('sidebarOpen');
3760
+ PDFView.sidebarOpen = outerContainer.classList.contains('sidebarOpen');
3761
+ PDFView.renderHighestPriority();
3762
+ });
3763
+
3764
+ document.getElementById('viewThumbnail').addEventListener('click',
3765
+ function() {
3766
+ PDFView.switchSidebarView('thumbs');
3767
+ });
3768
+
3769
+ document.getElementById('viewOutline').addEventListener('click',
3770
+ function() {
3771
+ PDFView.switchSidebarView('outline');
3772
+ });
3773
+
3774
+ document.getElementById('previous').addEventListener('click',
3775
+ function() {
3776
+ PDFView.page--;
3777
+ });
3778
+
3779
+ document.getElementById('next').addEventListener('click',
3780
+ function() {
3781
+ PDFView.page++;
3782
+ });
3783
+
3784
+ document.getElementById('zoomIn').addEventListener('click',
3785
+ function() {
3786
+ PDFView.zoomIn();
3787
+ });
3788
+
3789
+ document.getElementById('zoomOut').addEventListener('click',
3790
+ function() {
3791
+ PDFView.zoomOut();
3792
+ });
3793
+
3794
+ document.getElementById('presentationMode').addEventListener('click',
3795
+ function() {
3796
+ PDFView.presentationMode();
3797
+ });
3798
+
3799
+ document.getElementById('openFile').addEventListener('click',
3800
+ function() {
3801
+ document.getElementById('fileInput').click();
3802
+ });
3803
+
3804
+ document.getElementById('print').addEventListener('click',
3805
+ function() {
3806
+ window.print();
3807
+ });
3808
+
3809
+ document.getElementById('download').addEventListener('click',
3810
+ function() {
3811
+ PDFView.download();
3812
+ });
3813
+
3814
+ document.getElementById('pageNumber').addEventListener('click',
3815
+ function() {
3816
+ this.select();
3817
+ });
3818
+
3819
+ document.getElementById('pageNumber').addEventListener('change',
3820
+ function() {
3821
+ // Handle the user inputting a floating point number.
3822
+ PDFView.page = (this.value | 0);
3823
+
3824
+ if (this.value !== (this.value | 0).toString()) {
3825
+ this.value = PDFView.page;
3826
+ }
3827
+ });
3828
+
3829
+ document.getElementById('scaleSelect').addEventListener('change',
3830
+ function() {
3831
+ PDFView.parseScale(this.value);
3832
+ });
3833
+
3834
+ document.getElementById('firstPage').addEventListener('click',
3835
+ function() {
3836
+ PDFView.page = 1;
3837
+ });
3838
+
3839
+ document.getElementById('lastPage').addEventListener('click',
3840
+ function() {
3841
+ PDFView.page = PDFView.pdfDocument.numPages;
3842
+ });
3843
+
3844
+ document.getElementById('pageRotateCcw').addEventListener('click',
3845
+ function() {
3846
+ PDFView.rotatePages(-90);
3847
+ });
3848
+
3849
+ document.getElementById('pageRotateCw').addEventListener('click',
3850
+ function() {
3851
+ PDFView.rotatePages(90);
3852
+ });
3853
+
3854
+
3855
+ PDFView.open(file, 0);
3856
+ }, true);
3857
+
3858
+ function updateViewarea() {
3859
+
3860
+ if (!PDFView.initialized)
3861
+ return;
3862
+ var visible = PDFView.getVisiblePages();
3863
+ var visiblePages = visible.views;
3864
+ if (visiblePages.length === 0) {
3865
+ return;
3866
+ }
3867
+
3868
+ PDFView.renderHighestPriority();
3869
+
3870
+ var currentId = PDFView.page;
3871
+ var firstPage = visible.first;
3872
+
3873
+ for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
3874
+ i < ii; ++i) {
3875
+ var page = visiblePages[i];
3876
+
3877
+ if (page.percent < 100)
3878
+ break;
3879
+
3880
+ if (page.id === PDFView.page) {
3881
+ stillFullyVisible = true;
3882
+ break;
3883
+ }
3884
+ }
3885
+
3886
+ if (!stillFullyVisible) {
3887
+ currentId = visiblePages[0].id;
3888
+ }
3889
+
3890
+ if (!PDFView.isPresentationMode) {
3891
+ updateViewarea.inProgress = true; // used in "set page"
3892
+ PDFView.page = currentId;
3893
+ updateViewarea.inProgress = false;
3894
+ }
3895
+
3896
+ var currentScale = PDFView.currentScale;
3897
+ var currentScaleValue = PDFView.currentScaleValue;
3898
+ var normalizedScaleValue = currentScaleValue == currentScale ?
3899
+ currentScale * 100 : currentScaleValue;
3900
+
3901
+ var pageNumber = firstPage.id;
3902
+ var pdfOpenParams = '#page=' + pageNumber;
3903
+ pdfOpenParams += '&zoom=' + normalizedScaleValue;
3904
+ var currentPage = PDFView.pages[pageNumber - 1];
3905
+ var topLeft = currentPage.getPagePoint(PDFView.container.scrollLeft,
3906
+ (PDFView.container.scrollTop - firstPage.y));
3907
+ pdfOpenParams += ',' + Math.round(topLeft[0]) + ',' + Math.round(topLeft[1]);
3908
+
3909
+ var store = PDFView.store;
3910
+ store.initializedPromise.then(function() {
3911
+ store.set('exists', true);
3912
+ store.set('page', pageNumber);
3913
+ store.set('zoom', normalizedScaleValue);
3914
+ store.set('scrollLeft', Math.round(topLeft[0]));
3915
+ store.set('scrollTop', Math.round(topLeft[1]));
3916
+ });
3917
+ var href = PDFView.getAnchorUrl(pdfOpenParams);
3918
+ document.getElementById('viewBookmark').href = href;
3919
+
3920
+ // Update the current bookmark in the browsing history.
3921
+ PDFHistory.updateCurrentBookmark(pdfOpenParams, pageNumber);
3922
+ }
3923
+
3924
+ window.addEventListener('resize', function webViewerResize(evt) {
3925
+ if (PDFView.initialized &&
3926
+ (document.getElementById('pageWidthOption').selected ||
3927
+ document.getElementById('pageFitOption').selected ||
3928
+ document.getElementById('pageAutoOption').selected)) {
3929
+ PDFView.parseScale(document.getElementById('scaleSelect').value);
3930
+ }
3931
+ updateViewarea();
3932
+ });
3933
+
3934
+ window.addEventListener('hashchange', function webViewerHashchange(evt) {
3935
+ if (PDFHistory.isHashChangeUnlocked) {
3936
+ PDFView.setHash(document.location.hash.substring(1));
3937
+ }
3938
+ });
3939
+
3940
+ window.addEventListener('change', function webViewerChange(evt) {
3941
+ var files = evt.target.files;
3942
+ if (!files || files.length === 0)
3943
+ return;
3944
+
3945
+ // Read the local file into a Uint8Array.
3946
+ var fileReader = new FileReader();
3947
+ fileReader.onload = function webViewerChangeFileReaderOnload(evt) {
3948
+ var buffer = evt.target.result;
3949
+ var uint8Array = new Uint8Array(buffer);
3950
+ PDFView.open(uint8Array, 0);
3951
+ };
3952
+
3953
+ var file = files[0];
3954
+ fileReader.readAsArrayBuffer(file);
3955
+ PDFView.setTitleUsingUrl(file.name);
3956
+
3957
+ // URL does not reflect proper document location - hiding some icons.
3958
+ document.getElementById('viewBookmark').setAttribute('hidden', 'true');
3959
+ document.getElementById('download').setAttribute('hidden', 'true');
3960
+ }, true);
3961
+
3962
+ function selectScaleOption(value) {
3963
+ var options = document.getElementById('scaleSelect').options;
3964
+ var predefinedValueFound = false;
3965
+ for (var i = 0; i < options.length; i++) {
3966
+ var option = options[i];
3967
+ if (option.value != value) {
3968
+ option.selected = false;
3969
+ continue;
3970
+ }
3971
+ option.selected = true;
3972
+ predefinedValueFound = true;
3973
+ }
3974
+ return predefinedValueFound;
3975
+ }
3976
+
3977
+ window.addEventListener('localized', function localized(evt) {
3978
+ document.getElementsByTagName('html')[0].dir = mozL10n.getDirection();
3979
+
3980
+ // Adjust the width of the zoom box to fit the content.
3981
+ // Note: This is only done if the zoom box is actually visible,
3982
+ // since otherwise element.clientWidth will return 0.
3983
+ PDFView.animationStartedPromise.then(function() {
3984
+ var container = document.getElementById('scaleSelectContainer');
3985
+ if (container.clientWidth > 0) {
3986
+ var select = document.getElementById('scaleSelect');
3987
+ select.setAttribute('style', 'min-width: inherit;');
3988
+ var width = select.clientWidth + SCALE_SELECT_CONTAINER_PADDING;
3989
+ select.setAttribute('style', 'min-width: ' +
3990
+ (width + SCALE_SELECT_PADDING) + 'px;');
3991
+ container.setAttribute('style', 'min-width: ' + width + 'px; ' +
3992
+ 'max-width: ' + width + 'px;');
3993
+ }
3994
+ });
3995
+ }, true);
3996
+
3997
+ window.addEventListener('scalechange', function scalechange(evt) {
3998
+ document.getElementById('zoomOut').disabled = (evt.scale === MIN_SCALE);
3999
+ document.getElementById('zoomIn').disabled = (evt.scale === MAX_SCALE);
4000
+
4001
+ var customScaleOption = document.getElementById('customScaleOption');
4002
+ customScaleOption.selected = false;
4003
+
4004
+ if (!evt.resetAutoSettings &&
4005
+ (document.getElementById('pageWidthOption').selected ||
4006
+ document.getElementById('pageFitOption').selected ||
4007
+ document.getElementById('pageAutoOption').selected)) {
4008
+ updateViewarea();
4009
+ return;
4010
+ }
4011
+
4012
+ var predefinedValueFound = selectScaleOption('' + evt.scale);
4013
+ if (!predefinedValueFound) {
4014
+ customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
4015
+ customScaleOption.selected = true;
4016
+ }
4017
+ updateViewarea();
4018
+ }, true);
4019
+
4020
+ window.addEventListener('pagechange', function pagechange(evt) {
4021
+ var page = evt.pageNumber;
4022
+ if (PDFView.previousPageNumber !== page) {
4023
+ document.getElementById('pageNumber').value = page;
4024
+ var selected = document.querySelector('.thumbnail.selected');
4025
+ if (selected)
4026
+ selected.classList.remove('selected');
4027
+ var thumbnail = document.getElementById('thumbnailContainer' + page);
4028
+ thumbnail.classList.add('selected');
4029
+ var visibleThumbs = PDFView.getVisibleThumbs();
4030
+ var numVisibleThumbs = visibleThumbs.views.length;
4031
+ // If the thumbnail isn't currently visible scroll it into view.
4032
+ if (numVisibleThumbs > 0) {
4033
+ var first = visibleThumbs.first.id;
4034
+ // Account for only one thumbnail being visible.
4035
+ var last = numVisibleThumbs > 1 ?
4036
+ visibleThumbs.last.id : first;
4037
+ if (page <= first || page >= last)
4038
+ scrollIntoView(thumbnail);
4039
+ }
4040
+
4041
+ }
4042
+ document.getElementById('previous').disabled = (page <= 1);
4043
+ document.getElementById('next').disabled = (page >= PDFView.pages.length);
4044
+ }, true);
4045
+
4046
+ // Firefox specific event, so that we can prevent browser from zooming
4047
+ window.addEventListener('DOMMouseScroll', function(evt) {
4048
+ if (evt.ctrlKey) {
4049
+ evt.preventDefault();
4050
+
4051
+ var ticks = evt.detail;
4052
+ var direction = (ticks > 0) ? 'zoomOut' : 'zoomIn';
4053
+ PDFView[direction](Math.abs(ticks));
4054
+ } else if (PDFView.isPresentationMode) {
4055
+ var FIREFOX_DELTA_FACTOR = -40;
4056
+ PDFView.mouseScroll(evt.detail * FIREFOX_DELTA_FACTOR);
4057
+ }
4058
+ }, false);
4059
+
4060
+ window.addEventListener('mousemove', function mousemove(evt) {
4061
+ if (PDFView.isPresentationMode) {
4062
+ PDFView.showPresentationControls();
4063
+ }
4064
+ }, false);
4065
+
4066
+ window.addEventListener('mousedown', function mousedown(evt) {
4067
+ if (PDFView.isPresentationMode && evt.button === 0) {
4068
+ // Enable clicking of links in presentation mode.
4069
+ // Note: Only links that point to the currently loaded PDF document works.
4070
+ var targetHref = evt.target.href;
4071
+ var internalLink = targetHref && (targetHref.replace(/#.*$/, '') ===
4072
+ window.location.href.replace(/#.*$/, ''));
4073
+ if (!internalLink) {
4074
+ // Unless an internal link was clicked, advance a page in presentation
4075
+ // mode.
4076
+ evt.preventDefault();
4077
+ PDFView.page++;
4078
+ }
4079
+ }
4080
+ }, false);
4081
+
4082
+ window.addEventListener('click', function click(evt) {
4083
+ if (PDFView.isPresentationMode && evt.button === 0) {
4084
+ // Necessary since preventDefault() in 'mousedown' won't stop
4085
+ // the event propagation in all circumstances.
4086
+ evt.preventDefault();
4087
+ }
4088
+ }, false);
4089
+
4090
+ window.addEventListener('keydown', function keydown(evt) {
4091
+ var handled = false;
4092
+ var cmd = (evt.ctrlKey ? 1 : 0) |
4093
+ (evt.altKey ? 2 : 0) |
4094
+ (evt.shiftKey ? 4 : 0) |
4095
+ (evt.metaKey ? 8 : 0);
4096
+
4097
+ // First, handle the key bindings that are independent whether an input
4098
+ // control is selected or not.
4099
+ if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
4100
+ // either CTRL or META key with optional SHIFT.
4101
+ switch (evt.keyCode) {
4102
+ case 70: // f
4103
+ if (!PDFView.supportsIntegratedFind) {
4104
+ PDFFindBar.toggle();
4105
+ handled = true;
4106
+ }
4107
+ break;
4108
+ case 71: // g
4109
+ if (!PDFView.supportsIntegratedFind) {
4110
+ PDFFindBar.dispatchEvent('again', cmd === 5 || cmd === 12);
4111
+ handled = true;
4112
+ }
4113
+ break;
4114
+ case 61: // FF/Mac '='
4115
+ case 107: // FF '+' and '='
4116
+ case 187: // Chrome '+'
4117
+ case 171: // FF with German keyboard
4118
+ PDFView.zoomIn();
4119
+ handled = true;
4120
+ break;
4121
+ case 173: // FF/Mac '-'
4122
+ case 109: // FF '-'
4123
+ case 189: // Chrome '-'
4124
+ PDFView.zoomOut();
4125
+ handled = true;
4126
+ break;
4127
+ case 48: // '0'
4128
+ case 96: // '0' on Numpad of Swedish keyboard
4129
+ // keeping it unhandled (to restore page zoom to 100%)
4130
+ setTimeout(function () {
4131
+ // ... and resetting the scale after browser adjusts its scale
4132
+ PDFView.parseScale(DEFAULT_SCALE, true);
4133
+ });
4134
+ handled = false;
4135
+ break;
4136
+ }
4137
+ }
4138
+
4139
+ // CTRL+ALT or Option+Command
4140
+ if (cmd === 3 || cmd === 10) {
4141
+ switch (evt.keyCode) {
4142
+ case 80: // p
4143
+ PDFView.presentationMode();
4144
+ handled = true;
4145
+ break;
4146
+ }
4147
+ }
4148
+
4149
+ if (handled) {
4150
+ evt.preventDefault();
4151
+ return;
4152
+ }
4153
+
4154
+ // Some shortcuts should not get handled if a control/input element
4155
+ // is selected.
4156
+ var curElement = document.activeElement || document.querySelector(':focus');
4157
+ if (curElement && (curElement.tagName.toUpperCase() === 'INPUT' ||
4158
+ curElement.tagName.toUpperCase() === 'SELECT')) {
4159
+ return;
4160
+ }
4161
+ var controlsElement = document.getElementById('toolbar');
4162
+ while (curElement) {
4163
+ if (curElement === controlsElement && !PDFView.isPresentationMode)
4164
+ return; // ignoring if the 'toolbar' element is focused
4165
+ curElement = curElement.parentNode;
4166
+ }
4167
+
4168
+ if (cmd === 0) { // no control key pressed at all.
4169
+ switch (evt.keyCode) {
4170
+ case 38: // up arrow
4171
+ case 33: // pg up
4172
+ case 8: // backspace
4173
+ if (!PDFView.isPresentationMode &&
4174
+ PDFView.currentScaleValue !== 'page-fit') {
4175
+ break;
4176
+ }
4177
+ /* in presentation mode */
4178
+ /* falls through */
4179
+ case 37: // left arrow
4180
+ // horizontal scrolling using arrow keys
4181
+ if (PDFView.isHorizontalScrollbarEnabled) {
4182
+ break;
4183
+ }
4184
+ /* falls through */
4185
+ case 75: // 'k'
4186
+ case 80: // 'p'
4187
+ PDFView.page--;
4188
+ handled = true;
4189
+ break;
4190
+ case 27: // esc key
4191
+ if (!PDFView.supportsIntegratedFind && PDFFindBar.opened) {
4192
+ PDFFindBar.close();
4193
+ handled = true;
4194
+ }
4195
+ break;
4196
+ case 40: // down arrow
4197
+ case 34: // pg down
4198
+ case 32: // spacebar
4199
+ if (!PDFView.isPresentationMode &&
4200
+ PDFView.currentScaleValue !== 'page-fit') {
4201
+ break;
4202
+ }
4203
+ /* falls through */
4204
+ case 39: // right arrow
4205
+ // horizontal scrolling using arrow keys
4206
+ if (PDFView.isHorizontalScrollbarEnabled) {
4207
+ break;
4208
+ }
4209
+ /* falls through */
4210
+ case 74: // 'j'
4211
+ case 78: // 'n'
4212
+ PDFView.page++;
4213
+ handled = true;
4214
+ break;
4215
+
4216
+ case 36: // home
4217
+ if (PDFView.isPresentationMode) {
4218
+ PDFView.page = 1;
4219
+ handled = true;
4220
+ }
4221
+ break;
4222
+ case 35: // end
4223
+ if (PDFView.isPresentationMode) {
4224
+ PDFView.page = PDFView.pdfDocument.numPages;
4225
+ handled = true;
4226
+ }
4227
+ break;
4228
+
4229
+ case 82: // 'r'
4230
+ PDFView.rotatePages(90);
4231
+ break;
4232
+ }
4233
+ }
4234
+
4235
+ if (cmd === 4) { // shift-key
4236
+ switch (evt.keyCode) {
4237
+ case 32: // spacebar
4238
+ if (!PDFView.isPresentationMode &&
4239
+ PDFView.currentScaleValue !== 'page-fit') {
4240
+ break;
4241
+ }
4242
+ PDFView.page--;
4243
+ handled = true;
4244
+ break;
4245
+
4246
+ case 82: // 'r'
4247
+ PDFView.rotatePages(-90);
4248
+ break;
4249
+ }
4250
+ }
4251
+
4252
+ if (cmd === 2) { // alt-key
4253
+ switch (evt.keyCode) {
4254
+ case 37: // left arrow
4255
+ if (PDFView.isPresentationMode) {
4256
+ PDFHistory.back();
4257
+ handled = true;
4258
+ }
4259
+ break;
4260
+ case 39: // right arrow
4261
+ if (PDFView.isPresentationMode) {
4262
+ PDFHistory.forward();
4263
+ handled = true;
4264
+ }
4265
+ break;
4266
+ }
4267
+ }
4268
+
4269
+ if (handled) {
4270
+ evt.preventDefault();
4271
+ PDFView.clearMouseScrollState();
4272
+ }
4273
+ });
4274
+
4275
+ window.addEventListener('beforeprint', function beforePrint(evt) {
4276
+ PDFView.beforePrint();
4277
+ });
4278
+
4279
+ window.addEventListener('afterprint', function afterPrint(evt) {
4280
+ PDFView.afterPrint();
4281
+ });
4282
+
4283
+ (function presentationModeClosure() {
4284
+ function presentationModeChange(e) {
4285
+ var isPresentationMode = document.fullscreenElement ||
4286
+ document.mozFullScreen ||
4287
+ document.webkitIsFullScreen;
4288
+
4289
+ if (isPresentationMode) {
4290
+ PDFView.enterPresentationMode();
4291
+ } else {
4292
+ PDFView.exitPresentationMode();
4293
+ }
4294
+ }
4295
+
4296
+ window.addEventListener('fullscreenchange', presentationModeChange, false);
4297
+ window.addEventListener('mozfullscreenchange', presentationModeChange, false);
4298
+ window.addEventListener('webkitfullscreenchange', presentationModeChange,
4299
+ false);
4300
+ })();
4301
+
4302
+ (function animationStartedClosure() {
4303
+ // The offsetParent is not set until the pdf.js iframe or object is visible.
4304
+ // Waiting for first animation.
4305
+ var requestAnimationFrame = window.requestAnimationFrame ||
4306
+ window.mozRequestAnimationFrame ||
4307
+ window.webkitRequestAnimationFrame ||
4308
+ window.oRequestAnimationFrame ||
4309
+ window.msRequestAnimationFrame ||
4310
+ function startAtOnce(callback) { callback(); };
4311
+ PDFView.animationStartedPromise = new PDFJS.Promise();
4312
+ requestAnimationFrame(function onAnimationFrame() {
4313
+ PDFView.animationStartedPromise.resolve();
4314
+ });
4315
+ })();
4316
+
4317
+