pdfjs-rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +29 -0
  5. data/Rakefile +2 -0
  6. data/app/helpers/pdfjs/viewer_helper.rb +162 -0
  7. data/lib/pdfjs-rails.rb +13 -0
  8. data/lib/pdfjs-rails/version.rb +5 -0
  9. data/pdfjs-rails.gemspec +17 -0
  10. data/vendor/assets/images/annotation-check.svg +3 -0
  11. data/vendor/assets/images/annotation-comment.svg +3 -0
  12. data/vendor/assets/images/annotation-text.svg +46 -0
  13. data/vendor/assets/images/loading-icon.gif +0 -0
  14. data/vendor/assets/images/texture.png +0 -0
  15. data/vendor/assets/images/toolbarButton-bookmark.png +0 -0
  16. data/vendor/assets/images/toolbarButton-download.png +0 -0
  17. data/vendor/assets/images/toolbarButton-fullscreen.png +0 -0
  18. data/vendor/assets/images/toolbarButton-menuArrows.png +0 -0
  19. data/vendor/assets/images/toolbarButton-openFile.png +0 -0
  20. data/vendor/assets/images/toolbarButton-pageDown-rtl.png +0 -0
  21. data/vendor/assets/images/toolbarButton-pageDown.png +0 -0
  22. data/vendor/assets/images/toolbarButton-pageUp-rtl.png +0 -0
  23. data/vendor/assets/images/toolbarButton-pageUp.png +0 -0
  24. data/vendor/assets/images/toolbarButton-print.png +0 -0
  25. data/vendor/assets/images/toolbarButton-search.png +0 -0
  26. data/vendor/assets/images/toolbarButton-sidebarToggle.png +0 -0
  27. data/vendor/assets/images/toolbarButton-viewOutline.png +0 -0
  28. data/vendor/assets/images/toolbarButton-viewThumbnail.png +0 -0
  29. data/vendor/assets/images/toolbarButton-zoomIn.png +0 -0
  30. data/vendor/assets/images/toolbarButton-zoomOut.png +0 -0
  31. data/vendor/assets/javascripts/compatibility.js +413 -0
  32. data/vendor/assets/javascripts/l10n.js +322 -0
  33. data/vendor/assets/javascripts/pdf.js +35257 -0
  34. data/vendor/assets/javascripts/viewer.js +2297 -0
  35. data/vendor/assets/stylesheets/viewer.css.erb +1298 -0
  36. metadata +80 -0
@@ -0,0 +1,2297 @@
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
+
18
+ 'use strict';
19
+
20
+ var kDefaultScale = 'auto';
21
+ var kDefaultScaleDelta = 1.1;
22
+ var kUnknownScale = 0;
23
+ var kCacheSize = 20;
24
+ var kCssUnits = 96.0 / 72.0;
25
+ var kScrollbarPadding = 40;
26
+ var kMinScale = 0.25;
27
+ var kMaxScale = 4.0;
28
+ var kImageDirectory = './images/';
29
+ var kSettingsMemory = 20;
30
+ var RenderingStates = {
31
+ INITIAL: 0,
32
+ RUNNING: 1,
33
+ PAUSED: 2,
34
+ FINISHED: 3
35
+ };
36
+
37
+ // !NOTE: PDF.JS is able to use a background "Web Worker"
38
+ // to crunch some numbers. We are disabling this functionality
39
+ // for the moment because pdf.js will be compiled with other
40
+ // application assets in the Asset Pipeline.
41
+ //
42
+ // It wouldn't be too much trouble to reenable it. We may
43
+ // revisit that later.
44
+ //
45
+ PDFJS.disableWorker = true;
46
+ // PDFJS.workerSrc = '/assets/pdf.js';
47
+
48
+ var mozL10n = document.mozL10n || document.webL10n;
49
+
50
+ function getFileName(url) {
51
+ var anchor = url.indexOf('#');
52
+ var query = url.indexOf('?');
53
+ var end = Math.min(
54
+ anchor > 0 ? anchor : url.length,
55
+ query > 0 ? query : url.length);
56
+ return url.substring(url.lastIndexOf('/', end) + 1, end);
57
+ }
58
+
59
+ function scrollIntoView(element, spot) {
60
+ var parent = element.offsetParent, offsetY = element.offsetTop;
61
+ while (parent.clientHeight == parent.scrollHeight) {
62
+ offsetY += parent.offsetTop;
63
+ parent = parent.offsetParent;
64
+ if (!parent)
65
+ return; // no need to scroll
66
+ }
67
+ if (spot)
68
+ offsetY += spot.top;
69
+ parent.scrollTop = offsetY;
70
+ }
71
+
72
+ var Cache = function cacheCache(size) {
73
+ var data = [];
74
+ this.push = function cachePush(view) {
75
+ var i = data.indexOf(view);
76
+ if (i >= 0)
77
+ data.splice(i);
78
+ data.push(view);
79
+ if (data.length > size)
80
+ data.shift().destroy();
81
+ };
82
+ };
83
+
84
+ var ProgressBar = (function ProgressBarClosure() {
85
+
86
+ function clamp(v, min, max) {
87
+ return Math.min(Math.max(v, min), max);
88
+ }
89
+
90
+ function ProgressBar(id, opts) {
91
+
92
+ // Fetch the sub-elements for later
93
+ this.div = document.querySelector(id + ' .progress');
94
+
95
+ // Get options, with sensible defaults
96
+ this.height = opts.height || 100;
97
+ this.width = opts.width || 100;
98
+ this.units = opts.units || '%';
99
+
100
+ // Initialize heights
101
+ this.div.style.height = this.height + this.units;
102
+ }
103
+
104
+ ProgressBar.prototype = {
105
+
106
+ updateBar: function ProgressBar_updateBar() {
107
+ if (this._indeterminate) {
108
+ this.div.classList.add('indeterminate');
109
+ return;
110
+ }
111
+
112
+ var progressSize = this.width * this._percent / 100;
113
+
114
+ if (this._percent > 95)
115
+ this.div.classList.add('full');
116
+ else
117
+ this.div.classList.remove('full');
118
+ this.div.classList.remove('indeterminate');
119
+
120
+ this.div.style.width = progressSize + this.units;
121
+ },
122
+
123
+ get percent() {
124
+ return this._percent;
125
+ },
126
+
127
+ set percent(val) {
128
+ this._indeterminate = isNaN(val);
129
+ this._percent = clamp(val, 0, 100);
130
+ this.updateBar();
131
+ }
132
+ };
133
+
134
+ return ProgressBar;
135
+ })();
136
+
137
+
138
+ // Settings Manager - This is a utility for saving settings
139
+ // First we see if localStorage is available
140
+ // If not, we use FUEL in FF
141
+ var Settings = (function SettingsClosure() {
142
+ var isLocalStorageEnabled = (function localStorageEnabledTest() {
143
+ // Feature test as per http://diveintohtml5.info/storage.html
144
+ // The additional localStorage call is to get around a FF quirk, see
145
+ // bug #495747 in bugzilla
146
+ try {
147
+ return 'localStorage' in window && window['localStorage'] !== null &&
148
+ localStorage;
149
+ } catch (e) {
150
+ return false;
151
+ }
152
+ })();
153
+
154
+ function Settings(fingerprint) {
155
+ var database = null;
156
+ var index;
157
+ if (isLocalStorageEnabled)
158
+ database = localStorage.getItem('database') || '{}';
159
+ else
160
+ return;
161
+
162
+ database = JSON.parse(database);
163
+ if (!('files' in database))
164
+ database.files = [];
165
+ if (database.files.length >= kSettingsMemory)
166
+ database.files.shift();
167
+ for (var i = 0, length = database.files.length; i < length; i++) {
168
+ var branch = database.files[i];
169
+ if (branch.fingerprint == fingerprint) {
170
+ index = i;
171
+ break;
172
+ }
173
+ }
174
+ if (typeof index != 'number')
175
+ index = database.files.push({fingerprint: fingerprint}) - 1;
176
+ this.file = database.files[index];
177
+ this.database = database;
178
+ }
179
+
180
+ Settings.prototype = {
181
+ set: function settingsSet(name, val) {
182
+ if (!('file' in this))
183
+ return false;
184
+
185
+ var file = this.file;
186
+ file[name] = val;
187
+ var database = JSON.stringify(this.database);
188
+ if (isLocalStorageEnabled)
189
+ localStorage.setItem('database', database);
190
+ },
191
+
192
+ get: function settingsGet(name, defaultValue) {
193
+ if (!('file' in this))
194
+ return defaultValue;
195
+
196
+ return this.file[name] || defaultValue;
197
+ }
198
+ };
199
+
200
+ return Settings;
201
+ })();
202
+
203
+ var cache = new Cache(kCacheSize);
204
+ var currentPageNumber = 1;
205
+
206
+ var PDFView = {
207
+ pages: [],
208
+ thumbnails: [],
209
+ currentScale: kUnknownScale,
210
+ currentScaleValue: null,
211
+ initialBookmark: document.location.hash.substring(1),
212
+ startedTextExtraction: false,
213
+ pageText: [],
214
+ container: null,
215
+ thumbnailContainer: null,
216
+ initialized: false,
217
+ fellback: false,
218
+ pdfDocument: null,
219
+ sidebarOpen: false,
220
+ pageViewScroll: null,
221
+ thumbnailViewScroll: null,
222
+ isFullscreen: false,
223
+ previousScale: null,
224
+ pageRotation: 0,
225
+
226
+ // called once when the document is loaded
227
+ initialize: function pdfViewInitialize() {
228
+ var container = this.container = document.getElementById('viewerContainer');
229
+ this.pageViewScroll = {};
230
+ this.watchScroll(container, this.pageViewScroll, updateViewarea);
231
+
232
+ var thumbnailContainer = this.thumbnailContainer =
233
+ document.getElementById('thumbnailView');
234
+ this.thumbnailViewScroll = {};
235
+ this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
236
+ this.renderHighestPriority.bind(this));
237
+
238
+ this.initialized = true;
239
+ },
240
+
241
+ // Helper function to keep track whether a div was scrolled up or down and
242
+ // then call a callback.
243
+ watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) {
244
+ state.down = true;
245
+ state.lastY = viewAreaElement.scrollTop;
246
+ viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) {
247
+ var currentY = viewAreaElement.scrollTop;
248
+ var lastY = state.lastY;
249
+ if (currentY > lastY)
250
+ state.down = true;
251
+ else if (currentY < lastY)
252
+ state.down = false;
253
+ // else do nothing and use previous value
254
+ state.lastY = currentY;
255
+ callback();
256
+ }, true);
257
+ },
258
+
259
+ setScale: function pdfViewSetScale(val, resetAutoSettings, noScroll) {
260
+ if (val == this.currentScale)
261
+ return;
262
+
263
+ var pages = this.pages;
264
+ for (var i = 0; i < pages.length; i++)
265
+ pages[i].update(val * kCssUnits);
266
+
267
+ if (!noScroll && this.currentScale != val)
268
+ this.pages[this.page - 1].scrollIntoView();
269
+ this.currentScale = val;
270
+
271
+ var event = document.createEvent('UIEvents');
272
+ event.initUIEvent('scalechange', false, false, window, 0);
273
+ event.scale = val;
274
+ event.resetAutoSettings = resetAutoSettings;
275
+ window.dispatchEvent(event);
276
+ },
277
+
278
+ parseScale: function pdfViewParseScale(value, resetAutoSettings, noScroll) {
279
+ if ('custom' == value)
280
+ return;
281
+
282
+ var scale = parseFloat(value);
283
+ this.currentScaleValue = value;
284
+ if (scale) {
285
+ this.setScale(scale, true, noScroll);
286
+ return;
287
+ }
288
+
289
+ var container = this.container;
290
+ var currentPage = this.pages[this.page - 1];
291
+
292
+ var pageWidthScale = (container.clientWidth - kScrollbarPadding) /
293
+ currentPage.width * currentPage.scale / kCssUnits;
294
+ var pageHeightScale = (container.clientHeight - kScrollbarPadding) /
295
+ currentPage.height * currentPage.scale / kCssUnits;
296
+ switch (value) {
297
+ case 'page-actual':
298
+ scale = 1;
299
+ break;
300
+ case 'page-width':
301
+ scale = pageWidthScale;
302
+ break;
303
+ case 'page-height':
304
+ scale = pageHeightScale;
305
+ break;
306
+ case 'page-fit':
307
+ scale = Math.min(pageWidthScale, pageHeightScale);
308
+ break;
309
+ case 'auto':
310
+ scale = Math.min(1.0, pageWidthScale);
311
+ break;
312
+ }
313
+ this.setScale(scale, resetAutoSettings, noScroll);
314
+
315
+ selectScaleOption(value);
316
+ },
317
+
318
+ zoomIn: function pdfViewZoomIn() {
319
+ var newScale = (this.currentScale * kDefaultScaleDelta).toFixed(2);
320
+ newScale = Math.min(kMaxScale, newScale);
321
+ this.parseScale(newScale, true);
322
+ },
323
+
324
+ zoomOut: function pdfViewZoomOut() {
325
+ var newScale = (this.currentScale / kDefaultScaleDelta).toFixed(2);
326
+ newScale = Math.max(kMinScale, newScale);
327
+ this.parseScale(newScale, true);
328
+ },
329
+
330
+ set page(val) {
331
+ var pages = this.pages;
332
+ var input = document.getElementById('pageNumber');
333
+ var event = document.createEvent('UIEvents');
334
+ event.initUIEvent('pagechange', false, false, window, 0);
335
+
336
+ if (!(0 < val && val <= pages.length)) {
337
+ event.pageNumber = this.page;
338
+ window.dispatchEvent(event);
339
+ return;
340
+ }
341
+
342
+ pages[val - 1].updateStats();
343
+ currentPageNumber = val;
344
+ event.pageNumber = val;
345
+ window.dispatchEvent(event);
346
+
347
+ // checking if the this.page was called from the updateViewarea function:
348
+ // avoiding the creation of two "set page" method (internal and public)
349
+ if (updateViewarea.inProgress)
350
+ return;
351
+
352
+ // Avoid scrolling the first page during loading
353
+ if (this.loading && val == 1)
354
+ return;
355
+
356
+ pages[val - 1].scrollIntoView();
357
+ },
358
+
359
+ get page() {
360
+ return currentPageNumber;
361
+ },
362
+
363
+ get supportsPrinting() {
364
+ var canvas = document.createElement('canvas');
365
+ var value = 'mozPrintCallback' in canvas;
366
+ // shadow
367
+ Object.defineProperty(this, 'supportsPrinting', { value: value,
368
+ enumerable: true,
369
+ configurable: true,
370
+ writable: false });
371
+ return value;
372
+ },
373
+
374
+ get supportsFullscreen() {
375
+ var doc = document.documentElement;
376
+ var support = doc.requestFullScreen || doc.mozRequestFullScreen ||
377
+ doc.webkitRequestFullScreen;
378
+ Object.defineProperty(this, 'supportsFullScreen', { value: support,
379
+ enumerable: true,
380
+ configurable: true,
381
+ writable: false });
382
+ return support;
383
+ },
384
+
385
+ initPassiveLoading: function pdfViewInitPassiveLoading() {
386
+ if (!PDFView.loadingBar) {
387
+ PDFView.loadingBar = new ProgressBar('#loadingBar', {});
388
+ }
389
+
390
+ window.addEventListener('message', function window_message(e) {
391
+ var args = e.data;
392
+
393
+ if (typeof args !== 'object' || !('pdfjsLoadAction' in args))
394
+ return;
395
+ switch (args.pdfjsLoadAction) {
396
+ case 'progress':
397
+ PDFView.progress(args.loaded / args.total);
398
+ break;
399
+ case 'complete':
400
+ if (!args.data) {
401
+ PDFView.error(mozL10n.get('loading_error', null,
402
+ 'An error occurred while loading the PDF.'), e);
403
+ break;
404
+ }
405
+ PDFView.open(args.data, 0);
406
+ break;
407
+ }
408
+ });
409
+ FirefoxCom.requestSync('initPassiveLoading', null);
410
+ },
411
+
412
+ setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
413
+ this.url = url;
414
+ try {
415
+ document.title = decodeURIComponent(getFileName(url)) || url;
416
+ } catch (e) {
417
+ // decodeURIComponent may throw URIError,
418
+ // fall back to using the unprocessed url in that case
419
+ document.title = url;
420
+ }
421
+ },
422
+
423
+ open: function pdfViewOpen(url, scale, password) {
424
+ var parameters = {password: password};
425
+ if (typeof url === 'string') { // URL
426
+ this.setTitleUsingUrl(url);
427
+ parameters.url = url;
428
+ } else if (url && 'byteLength' in url) { // ArrayBuffer
429
+ parameters.data = url;
430
+ }
431
+
432
+ if (!PDFView.loadingBar) {
433
+ PDFView.loadingBar = new ProgressBar('#loadingBar', {});
434
+ }
435
+
436
+ this.pdfDocument = null;
437
+ var self = this;
438
+ self.loading = true;
439
+ PDFJS.getDocument(parameters).then(
440
+ function getDocumentCallback(pdfDocument) {
441
+ self.load(pdfDocument, scale);
442
+ self.loading = false;
443
+ },
444
+ function getDocumentError(message, exception) {
445
+ if (exception && exception.name === 'PasswordException') {
446
+ if (exception.code === 'needpassword') {
447
+ var promptString = mozL10n.get('request_password', null,
448
+ 'PDF is protected by a password:');
449
+ password = prompt(promptString);
450
+ if (password && password.length > 0) {
451
+ return PDFView.open(url, scale, password);
452
+ }
453
+ }
454
+ }
455
+
456
+ var loadingIndicator = document.getElementById('loading');
457
+ loadingIndicator.textContent = mozL10n.get('loading_error_indicator',
458
+ null, 'Error');
459
+ var moreInfo = {
460
+ message: message
461
+ };
462
+ self.error(mozL10n.get('loading_error', null,
463
+ 'An error occurred while loading the PDF.'), moreInfo);
464
+ self.loading = false;
465
+ },
466
+ function getDocumentProgress(progressData) {
467
+ self.progress(progressData.loaded / progressData.total);
468
+ }
469
+ );
470
+ },
471
+
472
+ download: function pdfViewDownload() {
473
+ function noData() {
474
+ FirefoxCom.request('download', { originalUrl: url });
475
+ }
476
+
477
+ var url = this.url.split('#')[0];
478
+ url += '#pdfjs.action=download';
479
+ window.open(url, '_parent');
480
+ },
481
+
482
+ fallback: function pdfViewFallback() {
483
+ return;
484
+ },
485
+
486
+ navigateTo: function pdfViewNavigateTo(dest) {
487
+ if (typeof dest === 'string')
488
+ dest = this.destinations[dest];
489
+ if (!(dest instanceof Array))
490
+ return; // invalid destination
491
+ // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
492
+ var destRef = dest[0];
493
+ var pageNumber = destRef instanceof Object ?
494
+ this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : (destRef + 1);
495
+ if (pageNumber > this.pages.length)
496
+ pageNumber = this.pages.length;
497
+ if (pageNumber) {
498
+ this.page = pageNumber;
499
+ var currentPage = this.pages[pageNumber - 1];
500
+ currentPage.scrollIntoView(dest);
501
+ }
502
+ },
503
+
504
+ getDestinationHash: function pdfViewGetDestinationHash(dest) {
505
+ if (typeof dest === 'string')
506
+ return PDFView.getAnchorUrl('#' + escape(dest));
507
+ if (dest instanceof Array) {
508
+ var destRef = dest[0]; // see navigateTo method for dest format
509
+ var pageNumber = destRef instanceof Object ?
510
+ this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
511
+ (destRef + 1);
512
+ if (pageNumber) {
513
+ var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber);
514
+ var destKind = dest[1];
515
+ if (typeof destKind === 'object' && 'name' in destKind &&
516
+ destKind.name == 'XYZ') {
517
+ var scale = (dest[4] || this.currentScale);
518
+ pdfOpenParams += '&zoom=' + (scale * 100);
519
+ if (dest[2] || dest[3]) {
520
+ pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
521
+ }
522
+ }
523
+ return pdfOpenParams;
524
+ }
525
+ }
526
+ return '';
527
+ },
528
+
529
+ /**
530
+ * For the firefox extension we prefix the full url on anchor links so they
531
+ * don't come up as resource:// urls and so open in new tab/window works.
532
+ * @param {String} anchor The anchor hash include the #.
533
+ */
534
+ getAnchorUrl: function getAnchorUrl(anchor) {
535
+ return anchor;
536
+ },
537
+
538
+ /**
539
+ * Show the error box.
540
+ * @param {String} message A message that is human readable.
541
+ * @param {Object} moreInfo (optional) Further information about the error
542
+ * that is more technical. Should have a 'message'
543
+ * and optionally a 'stack' property.
544
+ */
545
+ error: function pdfViewError(message, moreInfo) {
546
+ var moreInfoText = mozL10n.get('error_build', {build: PDFJS.build},
547
+ 'PDF.JS Build: {{build}}') + '\n';
548
+ if (moreInfo) {
549
+ moreInfoText +=
550
+ mozL10n.get('error_message', {message: moreInfo.message},
551
+ 'Message: {{message}}');
552
+ if (moreInfo.stack) {
553
+ moreInfoText += '\n' +
554
+ mozL10n.get('error_stack', {stack: moreInfo.stack},
555
+ 'Stack: {{stack}}');
556
+ } else {
557
+ if (moreInfo.filename) {
558
+ moreInfoText += '\n' +
559
+ mozL10n.get('error_file', {file: moreInfo.filename},
560
+ 'File: {{file}}');
561
+ }
562
+ if (moreInfo.lineNumber) {
563
+ moreInfoText += '\n' +
564
+ mozL10n.get('error_line', {line: moreInfo.lineNumber},
565
+ 'Line: {{line}}');
566
+ }
567
+ }
568
+ }
569
+
570
+ var loadingBox = document.getElementById('loadingBox');
571
+ loadingBox.setAttribute('hidden', 'true');
572
+
573
+ var errorWrapper = document.getElementById('errorWrapper');
574
+ errorWrapper.removeAttribute('hidden');
575
+
576
+ var errorMessage = document.getElementById('errorMessage');
577
+ errorMessage.textContent = message;
578
+
579
+ var closeButton = document.getElementById('errorClose');
580
+ closeButton.onclick = function() {
581
+ errorWrapper.setAttribute('hidden', 'true');
582
+ };
583
+
584
+ var errorMoreInfo = document.getElementById('errorMoreInfo');
585
+ var moreInfoButton = document.getElementById('errorShowMore');
586
+ var lessInfoButton = document.getElementById('errorShowLess');
587
+ moreInfoButton.onclick = function() {
588
+ errorMoreInfo.removeAttribute('hidden');
589
+ moreInfoButton.setAttribute('hidden', 'true');
590
+ lessInfoButton.removeAttribute('hidden');
591
+ };
592
+ lessInfoButton.onclick = function() {
593
+ errorMoreInfo.setAttribute('hidden', 'true');
594
+ moreInfoButton.removeAttribute('hidden');
595
+ lessInfoButton.setAttribute('hidden', 'true');
596
+ };
597
+ moreInfoButton.removeAttribute('hidden');
598
+ lessInfoButton.setAttribute('hidden', 'true');
599
+ errorMoreInfo.value = moreInfoText;
600
+
601
+ errorMoreInfo.rows = moreInfoText.split('\n').length - 1;
602
+ },
603
+
604
+ progress: function pdfViewProgress(level) {
605
+ var percent = Math.round(level * 100);
606
+ PDFView.loadingBar.percent = percent;
607
+ },
608
+
609
+ load: function pdfViewLoad(pdfDocument, scale) {
610
+ function bindOnAfterDraw(pageView, thumbnailView) {
611
+ // when page is painted, using the image as thumbnail base
612
+ pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
613
+ thumbnailView.setImage(pageView.canvas);
614
+ };
615
+ }
616
+
617
+ this.pdfDocument = pdfDocument;
618
+
619
+ var errorWrapper = document.getElementById('errorWrapper');
620
+ errorWrapper.setAttribute('hidden', 'true');
621
+
622
+ var loadingBox = document.getElementById('loadingBox');
623
+ loadingBox.setAttribute('hidden', 'true');
624
+ var loadingIndicator = document.getElementById('loading');
625
+ loadingIndicator.textContent = '';
626
+
627
+ var thumbsView = document.getElementById('thumbnailView');
628
+ thumbsView.parentNode.scrollTop = 0;
629
+
630
+ while (thumbsView.hasChildNodes())
631
+ thumbsView.removeChild(thumbsView.lastChild);
632
+
633
+ if ('_loadingInterval' in thumbsView)
634
+ clearInterval(thumbsView._loadingInterval);
635
+
636
+ var container = document.getElementById('viewer');
637
+ while (container.hasChildNodes())
638
+ container.removeChild(container.lastChild);
639
+
640
+ var pagesCount = pdfDocument.numPages;
641
+ var id = pdfDocument.fingerprint;
642
+ var storedHash = null;
643
+ document.getElementById('numPages').textContent =
644
+ mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
645
+ document.getElementById('pageNumber').max = pagesCount;
646
+ PDFView.documentFingerprint = id;
647
+ var store = PDFView.store = new Settings(id);
648
+ if (store.get('exists', false)) {
649
+ var page = store.get('page', '1');
650
+ var zoom = store.get('zoom', PDFView.currentScale);
651
+ var left = store.get('scrollLeft', '0');
652
+ var top = store.get('scrollTop', '0');
653
+
654
+ storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
655
+ }
656
+
657
+ this.pageRotation = 0;
658
+
659
+ var pages = this.pages = [];
660
+ this.pageText = [];
661
+ this.startedTextExtraction = false;
662
+ var pagesRefMap = {};
663
+ var thumbnails = this.thumbnails = [];
664
+ var pagePromises = [];
665
+ for (var i = 1; i <= pagesCount; i++)
666
+ pagePromises.push(pdfDocument.getPage(i));
667
+ var self = this;
668
+ var pagesPromise = PDFJS.Promise.all(pagePromises);
669
+ pagesPromise.then(function(promisedPages) {
670
+ for (var i = 1; i <= pagesCount; i++) {
671
+ var page = promisedPages[i - 1];
672
+ var pageView = new PageView(container, page, i, scale,
673
+ page.stats, self.navigateTo.bind(self));
674
+ var thumbnailView = new ThumbnailView(thumbsView, page, i);
675
+ bindOnAfterDraw(pageView, thumbnailView);
676
+
677
+ pages.push(pageView);
678
+ thumbnails.push(thumbnailView);
679
+ var pageRef = page.ref;
680
+ pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
681
+ }
682
+
683
+ self.pagesRefMap = pagesRefMap;
684
+ });
685
+
686
+ var destinationsPromise = pdfDocument.getDestinations();
687
+ destinationsPromise.then(function(destinations) {
688
+ self.destinations = destinations;
689
+ });
690
+
691
+ // outline and initial view depends on destinations and pagesRefMap
692
+ PDFJS.Promise.all([pagesPromise, destinationsPromise]).then(function() {
693
+ pdfDocument.getOutline().then(function(outline) {
694
+ self.outline = new DocumentOutlineView(outline);
695
+ });
696
+
697
+ self.setInitialView(storedHash, scale);
698
+ });
699
+
700
+ pdfDocument.getMetadata().then(function(data) {
701
+ var info = data.info, metadata = data.metadata;
702
+ self.documentInfo = info;
703
+ self.metadata = metadata;
704
+
705
+ var pdfTitle;
706
+ if (metadata) {
707
+ if (metadata.has('dc:title'))
708
+ pdfTitle = metadata.get('dc:title');
709
+ }
710
+
711
+ if (!pdfTitle && info && info['Title'])
712
+ pdfTitle = info['Title'];
713
+
714
+ if (pdfTitle)
715
+ document.title = pdfTitle + ' - ' + document.title;
716
+ });
717
+ },
718
+
719
+ setInitialView: function pdfViewSetInitialView(storedHash, scale) {
720
+ // Reset the current scale, as otherwise the page's scale might not get
721
+ // updated if the zoom level stayed the same.
722
+ this.currentScale = 0;
723
+ this.currentScaleValue = null;
724
+ if (this.initialBookmark) {
725
+ this.setHash(this.initialBookmark);
726
+ this.initialBookmark = null;
727
+ }
728
+ else if (storedHash)
729
+ this.setHash(storedHash);
730
+ else if (scale) {
731
+ this.parseScale(scale, true);
732
+ this.page = 1;
733
+ }
734
+
735
+ if (PDFView.currentScale === kUnknownScale) {
736
+ // Scale was not initialized: invalid bookmark or scale was not specified.
737
+ // Setting the default one.
738
+ this.parseScale(kDefaultScale, true);
739
+ }
740
+ },
741
+
742
+ renderHighestPriority: function pdfViewRenderHighestPriority() {
743
+ // Pages have a higher priority than thumbnails, so check them first.
744
+ var visiblePages = this.getVisiblePages();
745
+ var pageView = this.getHighestPriority(visiblePages, this.pages,
746
+ this.pageViewScroll.down);
747
+ if (pageView) {
748
+ this.renderView(pageView, 'page');
749
+ return;
750
+ }
751
+ // No pages needed rendering so check thumbnails.
752
+ if (this.sidebarOpen) {
753
+ var visibleThumbs = this.getVisibleThumbs();
754
+ var thumbView = this.getHighestPriority(visibleThumbs,
755
+ this.thumbnails,
756
+ this.thumbnailViewScroll.down);
757
+ if (thumbView)
758
+ this.renderView(thumbView, 'thumbnail');
759
+ }
760
+ },
761
+
762
+ getHighestPriority: function pdfViewGetHighestPriority(visible, views,
763
+ scrolledDown) {
764
+ // The state has changed figure out which page has the highest priority to
765
+ // render next (if any).
766
+ // Priority:
767
+ // 1 visible pages
768
+ // 2 if last scrolled down page after the visible pages
769
+ // 2 if last scrolled up page before the visible pages
770
+ var visibleViews = visible.views;
771
+
772
+ var numVisible = visibleViews.length;
773
+ if (numVisible === 0) {
774
+ return false;
775
+ }
776
+ for (var i = 0; i < numVisible; ++i) {
777
+ var view = visibleViews[i].view;
778
+ if (!this.isViewFinished(view))
779
+ return view;
780
+ }
781
+
782
+ // All the visible views have rendered, try to render next/previous pages.
783
+ if (scrolledDown) {
784
+ var nextPageIndex = visible.last.id;
785
+ // ID's start at 1 so no need to add 1.
786
+ if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex]))
787
+ return views[nextPageIndex];
788
+ } else {
789
+ var previousPageIndex = visible.first.id - 2;
790
+ if (views[previousPageIndex] &&
791
+ !this.isViewFinished(views[previousPageIndex]))
792
+ return views[previousPageIndex];
793
+ }
794
+ // Everything that needs to be rendered has been.
795
+ return false;
796
+ },
797
+
798
+ isViewFinished: function pdfViewNeedsRendering(view) {
799
+ return view.renderingState === RenderingStates.FINISHED;
800
+ },
801
+
802
+ // Render a page or thumbnail view. This calls the appropriate function based
803
+ // on the views state. If the view is already rendered it will return false.
804
+ renderView: function pdfViewRender(view, type) {
805
+ var state = view.renderingState;
806
+ switch (state) {
807
+ case RenderingStates.FINISHED:
808
+ return false;
809
+ case RenderingStates.PAUSED:
810
+ PDFView.highestPriorityPage = type + view.id;
811
+ view.resume();
812
+ break;
813
+ case RenderingStates.RUNNING:
814
+ PDFView.highestPriorityPage = type + view.id;
815
+ break;
816
+ case RenderingStates.INITIAL:
817
+ PDFView.highestPriorityPage = type + view.id;
818
+ view.draw(this.renderHighestPriority.bind(this));
819
+ break;
820
+ }
821
+ return true;
822
+ },
823
+
824
+ search: function pdfViewStartSearch() {
825
+ // Limit this function to run every <SEARCH_TIMEOUT>ms.
826
+ var SEARCH_TIMEOUT = 250;
827
+ var lastSearch = this.lastSearch;
828
+ var now = Date.now();
829
+ if (lastSearch && (now - lastSearch) < SEARCH_TIMEOUT) {
830
+ if (!this.searchTimer) {
831
+ this.searchTimer = setTimeout(function resumeSearch() {
832
+ PDFView.search();
833
+ },
834
+ SEARCH_TIMEOUT - (now - lastSearch)
835
+ );
836
+ }
837
+ return;
838
+ }
839
+ this.searchTimer = null;
840
+ this.lastSearch = now;
841
+
842
+ function bindLink(link, pageNumber) {
843
+ link.href = '#' + pageNumber;
844
+ link.onclick = function searchBindLink() {
845
+ PDFView.page = pageNumber;
846
+ return false;
847
+ };
848
+ }
849
+
850
+ var searchResults = document.getElementById('searchResults');
851
+
852
+ var searchTermsInput = document.getElementById('searchTermsInput');
853
+ searchResults.removeAttribute('hidden');
854
+ searchResults.textContent = '';
855
+
856
+ var terms = searchTermsInput.value;
857
+
858
+ if (!terms)
859
+ return;
860
+
861
+ // simple search: removing spaces and hyphens, then scanning every
862
+ terms = terms.replace(/\s-/g, '').toLowerCase();
863
+ var index = PDFView.pageText;
864
+ var pageFound = false;
865
+ for (var i = 0, ii = index.length; i < ii; i++) {
866
+ var pageText = index[i].replace(/\s-/g, '').toLowerCase();
867
+ var j = pageText.indexOf(terms);
868
+ if (j < 0)
869
+ continue;
870
+
871
+ var pageNumber = i + 1;
872
+ var textSample = index[i].substr(j, 50);
873
+ var link = document.createElement('a');
874
+ bindLink(link, pageNumber);
875
+ link.textContent = 'Page ' + pageNumber + ': ' + textSample;
876
+ searchResults.appendChild(link);
877
+
878
+ pageFound = true;
879
+ }
880
+ if (!pageFound) {
881
+ searchResults.textContent = '';
882
+ var noResults = document.createElement('div');
883
+ noResults.classList.add('noResults');
884
+ noResults.textContent = mozL10n.get('search_terms_not_found', null,
885
+ '(Not found)');
886
+ searchResults.appendChild(noResults);
887
+ }
888
+ },
889
+
890
+ setHash: function pdfViewSetHash(hash) {
891
+ if (!hash)
892
+ return;
893
+
894
+ if (hash.indexOf('=') >= 0) {
895
+ var params = PDFView.parseQueryString(hash);
896
+ // borrowing syntax from "Parameters for Opening PDF Files"
897
+ if ('nameddest' in params) {
898
+ PDFView.navigateTo(params.nameddest);
899
+ return;
900
+ }
901
+ if ('page' in params) {
902
+ var pageNumber = (params.page | 0) || 1;
903
+ if ('zoom' in params) {
904
+ var zoomArgs = params.zoom.split(','); // scale,left,top
905
+ // building destination array
906
+
907
+ // If the zoom value, it has to get divided by 100. If it is a string,
908
+ // it should stay as it is.
909
+ var zoomArg = zoomArgs[0];
910
+ var zoomArgNumber = parseFloat(zoomArg);
911
+ if (zoomArgNumber)
912
+ zoomArg = zoomArgNumber / 100;
913
+
914
+ var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0),
915
+ (zoomArgs[2] | 0), zoomArg];
916
+ var currentPage = this.pages[pageNumber - 1];
917
+ currentPage.scrollIntoView(dest);
918
+ } else {
919
+ this.page = pageNumber; // simple page
920
+ }
921
+ }
922
+ } else if (/^\d+$/.test(hash)) // page number
923
+ this.page = hash;
924
+ else // named destination
925
+ PDFView.navigateTo(unescape(hash));
926
+ },
927
+
928
+ switchSidebarView: function pdfViewSwitchSidebarView(view) {
929
+ var thumbsView = document.getElementById('thumbnailView');
930
+ var outlineView = document.getElementById('outlineView');
931
+ var searchView = document.getElementById('searchView');
932
+
933
+ var thumbsButton = document.getElementById('viewThumbnail');
934
+ var outlineButton = document.getElementById('viewOutline');
935
+ var searchButton = document.getElementById('viewSearch');
936
+
937
+ switch (view) {
938
+ case 'thumbs':
939
+ thumbsButton.classList.add('toggled');
940
+ outlineButton.classList.remove('toggled');
941
+ searchButton.classList.remove('toggled');
942
+ thumbsView.classList.remove('hidden');
943
+ outlineView.classList.add('hidden');
944
+ searchView.classList.add('hidden');
945
+
946
+ PDFView.renderHighestPriority();
947
+ break;
948
+
949
+ case 'outline':
950
+ thumbsButton.classList.remove('toggled');
951
+ outlineButton.classList.add('toggled');
952
+ searchButton.classList.remove('toggled');
953
+ thumbsView.classList.add('hidden');
954
+ outlineView.classList.remove('hidden');
955
+ searchView.classList.add('hidden');
956
+
957
+ if (outlineButton.getAttribute('disabled'))
958
+ return;
959
+ break;
960
+
961
+ case 'search':
962
+ thumbsButton.classList.remove('toggled');
963
+ outlineButton.classList.remove('toggled');
964
+ searchButton.classList.add('toggled');
965
+ thumbsView.classList.add('hidden');
966
+ outlineView.classList.add('hidden');
967
+ searchView.classList.remove('hidden');
968
+
969
+ var searchTermsInput = document.getElementById('searchTermsInput');
970
+ searchTermsInput.focus();
971
+ // Start text extraction as soon as the search gets displayed.
972
+ this.extractText();
973
+ break;
974
+ }
975
+ },
976
+
977
+ extractText: function() {
978
+ if (this.startedTextExtraction)
979
+ return;
980
+ this.startedTextExtraction = true;
981
+ var self = this;
982
+ function extractPageText(pageIndex) {
983
+ self.pages[pageIndex].pdfPage.getTextContent().then(
984
+ function textContentResolved(textContent) {
985
+ self.pageText[pageIndex] = textContent;
986
+ self.search();
987
+ if ((pageIndex + 1) < self.pages.length)
988
+ extractPageText(pageIndex + 1);
989
+ }
990
+ );
991
+ }
992
+ extractPageText(0);
993
+ },
994
+
995
+ getVisiblePages: function pdfViewGetVisiblePages() {
996
+ return this.getVisibleElements(this.container,
997
+ this.pages, true);
998
+ },
999
+
1000
+ getVisibleThumbs: function pdfViewGetVisibleThumbs() {
1001
+ return this.getVisibleElements(this.thumbnailContainer,
1002
+ this.thumbnails);
1003
+ },
1004
+
1005
+ // Generic helper to find out what elements are visible within a scroll pane.
1006
+ getVisibleElements: function pdfViewGetVisibleElements(
1007
+ scrollEl, views, sortByVisibility) {
1008
+ var currentHeight = 0, view;
1009
+ var top = scrollEl.scrollTop;
1010
+
1011
+ for (var i = 1, ii = views.length; i <= ii; ++i) {
1012
+ view = views[i - 1];
1013
+ currentHeight = view.el.offsetTop;
1014
+ if (currentHeight + view.el.clientHeight > top)
1015
+ break;
1016
+ currentHeight += view.el.clientHeight;
1017
+ }
1018
+
1019
+ var visible = [];
1020
+
1021
+ // Algorithm broken in fullscreen mode
1022
+ if (this.isFullscreen) {
1023
+ var currentPage = this.pages[this.page - 1];
1024
+ visible.push({
1025
+ id: currentPage.id,
1026
+ view: currentPage
1027
+ });
1028
+
1029
+ return { first: currentPage, last: currentPage, views: visible};
1030
+ }
1031
+
1032
+ var bottom = top + scrollEl.clientHeight;
1033
+ var nextHeight, hidden, percent, viewHeight;
1034
+ for (; i <= ii && currentHeight < bottom; ++i) {
1035
+ view = views[i - 1];
1036
+ viewHeight = view.el.clientHeight;
1037
+ currentHeight = view.el.offsetTop;
1038
+ nextHeight = currentHeight + viewHeight;
1039
+ hidden = Math.max(0, top - currentHeight) +
1040
+ Math.max(0, nextHeight - bottom);
1041
+ percent = Math.floor((viewHeight - hidden) * 100.0 / viewHeight);
1042
+ visible.push({ id: view.id, y: currentHeight,
1043
+ view: view, percent: percent });
1044
+ currentHeight = nextHeight;
1045
+ }
1046
+
1047
+ var first = visible[0];
1048
+ var last = visible[visible.length - 1];
1049
+
1050
+ if (sortByVisibility) {
1051
+ visible.sort(function(a, b) {
1052
+ var pc = a.percent - b.percent;
1053
+ if (Math.abs(pc) > 0.001)
1054
+ return -pc;
1055
+
1056
+ return a.id - b.id; // ensure stability
1057
+ });
1058
+ }
1059
+
1060
+ return {first: first, last: last, views: visible};
1061
+ },
1062
+
1063
+ // Helper function to parse query string (e.g. ?param1=value&parm2=...).
1064
+ parseQueryString: function pdfViewParseQueryString(query) {
1065
+ var parts = query.split('&');
1066
+ var params = {};
1067
+ for (var i = 0, ii = parts.length; i < parts.length; ++i) {
1068
+ var param = parts[i].split('=');
1069
+ var key = param[0];
1070
+ var value = param.length > 1 ? param[1] : null;
1071
+ params[unescape(key)] = unescape(value);
1072
+ }
1073
+ return params;
1074
+ },
1075
+
1076
+ beforePrint: function pdfViewSetupBeforePrint() {
1077
+ if (!this.supportsPrinting) {
1078
+ var printMessage = mozL10n.get('printing_not_supported', null,
1079
+ 'Warning: Printing is not fully supported by this browser.');
1080
+ this.error(printMessage);
1081
+ return;
1082
+ }
1083
+ var body = document.querySelector('body');
1084
+ body.setAttribute('data-mozPrintCallback', true);
1085
+ for (var i = 0, ii = this.pages.length; i < ii; ++i) {
1086
+ this.pages[i].beforePrint();
1087
+ }
1088
+ },
1089
+
1090
+ afterPrint: function pdfViewSetupAfterPrint() {
1091
+ var div = document.getElementById('printContainer');
1092
+ while (div.hasChildNodes())
1093
+ div.removeChild(div.lastChild);
1094
+ },
1095
+
1096
+ fullscreen: function pdfViewFullscreen() {
1097
+ var isFullscreen = document.fullscreen || document.mozFullScreen ||
1098
+ document.webkitIsFullScreen;
1099
+
1100
+ if (isFullscreen) {
1101
+ return false;
1102
+ }
1103
+
1104
+ var wrapper = document.getElementById('viewerContainer');
1105
+ if (document.documentElement.requestFullScreen) {
1106
+ wrapper.requestFullScreen();
1107
+ } else if (document.documentElement.mozRequestFullScreen) {
1108
+ wrapper.mozRequestFullScreen();
1109
+ } else if (document.documentElement.webkitRequestFullScreen) {
1110
+ wrapper.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
1111
+ } else {
1112
+ return false;
1113
+ }
1114
+
1115
+ this.isFullscreen = true;
1116
+ var currentPage = this.pages[this.page - 1];
1117
+ this.previousScale = this.currentScaleValue;
1118
+ this.parseScale('page-fit', true);
1119
+
1120
+ // Wait for fullscreen to take effect
1121
+ setTimeout(function() {
1122
+ currentPage.scrollIntoView();
1123
+ }, 0);
1124
+
1125
+ return true;
1126
+ },
1127
+
1128
+ exitFullscreen: function pdfViewExitFullscreen() {
1129
+ this.isFullscreen = false;
1130
+ this.parseScale(this.previousScale);
1131
+ this.page = this.page;
1132
+ },
1133
+
1134
+ rotatePages: function pdfViewPageRotation(delta) {
1135
+
1136
+ this.pageRotation = (this.pageRotation + 360 + delta) % 360;
1137
+
1138
+ for (var i = 0, l = this.pages.length; i < l; i++) {
1139
+ var page = this.pages[i];
1140
+ page.update(page.scale, this.pageRotation);
1141
+ }
1142
+
1143
+ for (var i = 0, l = this.thumbnails.length; i < l; i++) {
1144
+ var thumb = this.thumbnails[i];
1145
+ thumb.updateRotation(this.pageRotation);
1146
+ }
1147
+
1148
+ var currentPage = this.pages[this.page - 1];
1149
+
1150
+ this.parseScale(this.currentScaleValue, true);
1151
+
1152
+ this.renderHighestPriority();
1153
+
1154
+ // Wait for fullscreen to take effect
1155
+ setTimeout(function() {
1156
+ currentPage.scrollIntoView();
1157
+ }, 0);
1158
+ }
1159
+ };
1160
+
1161
+ var PageView = function pageView(container, pdfPage, id, scale,
1162
+ stats, navigateTo) {
1163
+ this.id = id;
1164
+ this.pdfPage = pdfPage;
1165
+
1166
+ this.rotation = 0;
1167
+ this.scale = scale || 1.0;
1168
+ this.viewport = this.pdfPage.getViewport(this.scale, this.pdfPage.rotate);
1169
+
1170
+ this.renderingState = RenderingStates.INITIAL;
1171
+ this.resume = null;
1172
+
1173
+ var anchor = document.createElement('a');
1174
+ anchor.name = '' + this.id;
1175
+
1176
+ var div = this.el = document.createElement('div');
1177
+ div.id = 'pageContainer' + this.id;
1178
+ div.className = 'page';
1179
+ div.style.width = this.viewport.width + 'px';
1180
+ div.style.height = this.viewport.height + 'px';
1181
+
1182
+ container.appendChild(anchor);
1183
+ container.appendChild(div);
1184
+
1185
+ this.destroy = function pageViewDestroy() {
1186
+ this.update();
1187
+ this.pdfPage.destroy();
1188
+ };
1189
+
1190
+ this.update = function pageViewUpdate(scale, rotation) {
1191
+ this.renderingState = RenderingStates.INITIAL;
1192
+ this.resume = null;
1193
+
1194
+ if (typeof rotation !== 'undefined') {
1195
+ this.rotation = rotation;
1196
+ }
1197
+
1198
+ this.scale = scale || this.scale;
1199
+
1200
+ var totalRotation = (this.rotation + this.pdfPage.rotate) % 360;
1201
+ var viewport = this.pdfPage.getViewport(this.scale, totalRotation);
1202
+
1203
+ this.viewport = viewport;
1204
+ div.style.width = viewport.width + 'px';
1205
+ div.style.height = viewport.height + 'px';
1206
+
1207
+ while (div.hasChildNodes())
1208
+ div.removeChild(div.lastChild);
1209
+ div.removeAttribute('data-loaded');
1210
+
1211
+ delete this.canvas;
1212
+
1213
+ this.loadingIconDiv = document.createElement('div');
1214
+ this.loadingIconDiv.className = 'loadingIcon';
1215
+ div.appendChild(this.loadingIconDiv);
1216
+ };
1217
+
1218
+ Object.defineProperty(this, 'width', {
1219
+ get: function PageView_getWidth() {
1220
+ return this.viewport.width;
1221
+ },
1222
+ enumerable: true
1223
+ });
1224
+
1225
+ Object.defineProperty(this, 'height', {
1226
+ get: function PageView_getHeight() {
1227
+ return this.viewport.height;
1228
+ },
1229
+ enumerable: true
1230
+ });
1231
+
1232
+ function setupAnnotations(pdfPage, viewport) {
1233
+ function bindLink(link, dest) {
1234
+ link.href = PDFView.getDestinationHash(dest);
1235
+ link.onclick = function pageViewSetupLinksOnclick() {
1236
+ if (dest)
1237
+ PDFView.navigateTo(dest);
1238
+ return false;
1239
+ };
1240
+ }
1241
+ function createElementWithStyle(tagName, item) {
1242
+ var rect = viewport.convertToViewportRectangle(item.rect);
1243
+ rect = PDFJS.Util.normalizeRect(rect);
1244
+ var element = document.createElement(tagName);
1245
+ element.style.left = Math.floor(rect[0]) + 'px';
1246
+ element.style.top = Math.floor(rect[1]) + 'px';
1247
+ element.style.width = Math.ceil(rect[2] - rect[0]) + 'px';
1248
+ element.style.height = Math.ceil(rect[3] - rect[1]) + 'px';
1249
+ return element;
1250
+ }
1251
+ function createCommentAnnotation(type, item) {
1252
+ var container = document.createElement('section');
1253
+ container.className = 'annotComment';
1254
+
1255
+ var image = createElementWithStyle('img', item);
1256
+ var type = item.type;
1257
+ var rect = viewport.convertToViewportRectangle(item.rect);
1258
+ rect = PDFJS.Util.normalizeRect(rect);
1259
+ image.src = kImageDirectory + 'annotation-' + type.toLowerCase() + '.svg';
1260
+ image.alt = mozL10n.get('text_annotation_type', {type: type},
1261
+ '[{{type}} Annotation]');
1262
+ var content = document.createElement('div');
1263
+ content.setAttribute('hidden', true);
1264
+ var title = document.createElement('h1');
1265
+ var text = document.createElement('p');
1266
+ content.style.left = Math.floor(rect[2]) + 'px';
1267
+ content.style.top = Math.floor(rect[1]) + 'px';
1268
+ title.textContent = item.title;
1269
+
1270
+ if (!item.content && !item.title) {
1271
+ content.setAttribute('hidden', true);
1272
+ } else {
1273
+ var e = document.createElement('span');
1274
+ var lines = item.content.split('\n');
1275
+ for (var i = 0, ii = lines.length; i < ii; ++i) {
1276
+ var line = lines[i];
1277
+ e.appendChild(document.createTextNode(line));
1278
+ if (i < (ii - 1))
1279
+ e.appendChild(document.createElement('br'));
1280
+ }
1281
+ text.appendChild(e);
1282
+ image.addEventListener('mouseover', function annotationImageOver() {
1283
+ content.removeAttribute('hidden');
1284
+ }, false);
1285
+
1286
+ image.addEventListener('mouseout', function annotationImageOut() {
1287
+ content.setAttribute('hidden', true);
1288
+ }, false);
1289
+ }
1290
+
1291
+ content.appendChild(title);
1292
+ content.appendChild(text);
1293
+ container.appendChild(image);
1294
+ container.appendChild(content);
1295
+
1296
+ return container;
1297
+ }
1298
+
1299
+ pdfPage.getAnnotations().then(function(items) {
1300
+ for (var i = 0; i < items.length; i++) {
1301
+ var item = items[i];
1302
+ switch (item.type) {
1303
+ case 'Link':
1304
+ var link = createElementWithStyle('a', item);
1305
+ link.href = item.url || '';
1306
+ if (!item.url)
1307
+ bindLink(link, ('dest' in item) ? item.dest : null);
1308
+ div.appendChild(link);
1309
+ break;
1310
+ case 'Text':
1311
+ var comment = createCommentAnnotation(item.name, item);
1312
+ if (comment)
1313
+ div.appendChild(comment);
1314
+ break;
1315
+ case 'Widget':
1316
+ // TODO: support forms
1317
+ PDFView.fallback();
1318
+ break;
1319
+ }
1320
+ }
1321
+ });
1322
+ }
1323
+
1324
+ this.getPagePoint = function pageViewGetPagePoint(x, y) {
1325
+ return this.viewport.convertToPdfPoint(x, y);
1326
+ };
1327
+
1328
+ this.scrollIntoView = function pageViewScrollIntoView(dest) {
1329
+ if (!dest) {
1330
+ scrollIntoView(div);
1331
+ return;
1332
+ }
1333
+
1334
+ var x = 0, y = 0;
1335
+ var width = 0, height = 0, widthScale, heightScale;
1336
+ var scale = 0;
1337
+ switch (dest[1].name) {
1338
+ case 'XYZ':
1339
+ x = dest[2];
1340
+ y = dest[3];
1341
+ scale = dest[4];
1342
+ break;
1343
+ case 'Fit':
1344
+ case 'FitB':
1345
+ scale = 'page-fit';
1346
+ break;
1347
+ case 'FitH':
1348
+ case 'FitBH':
1349
+ y = dest[2];
1350
+ scale = 'page-width';
1351
+ break;
1352
+ case 'FitV':
1353
+ case 'FitBV':
1354
+ x = dest[2];
1355
+ scale = 'page-height';
1356
+ break;
1357
+ case 'FitR':
1358
+ x = dest[2];
1359
+ y = dest[3];
1360
+ width = dest[4] - x;
1361
+ height = dest[5] - y;
1362
+ widthScale = (this.container.clientWidth - kScrollbarPadding) /
1363
+ width / kCssUnits;
1364
+ heightScale = (this.container.clientHeight - kScrollbarPadding) /
1365
+ height / kCssUnits;
1366
+ scale = Math.min(widthScale, heightScale);
1367
+ break;
1368
+ default:
1369
+ return;
1370
+ }
1371
+
1372
+ if (scale && scale !== PDFView.currentScale)
1373
+ PDFView.parseScale(scale, true, true);
1374
+ else if (PDFView.currentScale === kUnknownScale)
1375
+ PDFView.parseScale(kDefaultScale, true, true);
1376
+
1377
+ var boundingRect = [
1378
+ this.viewport.convertToViewportPoint(x, y),
1379
+ this.viewport.convertToViewportPoint(x + width, y + height)
1380
+ ];
1381
+ setTimeout(function pageViewScrollIntoViewRelayout() {
1382
+ // letting page to re-layout before scrolling
1383
+ var scale = PDFView.currentScale;
1384
+ var x = Math.min(boundingRect[0][0], boundingRect[1][0]);
1385
+ var y = Math.min(boundingRect[0][1], boundingRect[1][1]);
1386
+ var width = Math.abs(boundingRect[0][0] - boundingRect[1][0]);
1387
+ var height = Math.abs(boundingRect[0][1] - boundingRect[1][1]);
1388
+
1389
+ scrollIntoView(div, {left: x, top: y, width: width, height: height});
1390
+ }, 0);
1391
+ };
1392
+
1393
+ this.draw = function pageviewDraw(callback) {
1394
+ if (this.renderingState !== RenderingStates.INITIAL)
1395
+ error('Must be in new state before drawing');
1396
+
1397
+ this.renderingState = RenderingStates.RUNNING;
1398
+
1399
+ var canvas = document.createElement('canvas');
1400
+ canvas.id = 'page' + this.id;
1401
+ canvas.mozOpaque = true;
1402
+ div.appendChild(canvas);
1403
+ this.canvas = canvas;
1404
+
1405
+ var textLayerDiv = null;
1406
+ if (!PDFJS.disableTextLayer) {
1407
+ textLayerDiv = document.createElement('div');
1408
+ textLayerDiv.className = 'textLayer';
1409
+ div.appendChild(textLayerDiv);
1410
+ }
1411
+ var textLayer = textLayerDiv ? new TextLayerBuilder(textLayerDiv) : null;
1412
+
1413
+ var scale = this.scale, viewport = this.viewport;
1414
+ canvas.width = viewport.width;
1415
+ canvas.height = viewport.height;
1416
+
1417
+ var ctx = canvas.getContext('2d');
1418
+ ctx.save();
1419
+ ctx.fillStyle = 'rgb(255, 255, 255)';
1420
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
1421
+ ctx.restore();
1422
+
1423
+ // Rendering area
1424
+
1425
+ var self = this;
1426
+ function pageViewDrawCallback(error) {
1427
+ self.renderingState = RenderingStates.FINISHED;
1428
+
1429
+ if (self.loadingIconDiv) {
1430
+ div.removeChild(self.loadingIconDiv);
1431
+ delete self.loadingIconDiv;
1432
+ }
1433
+
1434
+ if (error) {
1435
+ PDFView.error(mozL10n.get('rendering_error', null,
1436
+ 'An error occurred while rendering the page.'), error);
1437
+ }
1438
+
1439
+ self.stats = pdfPage.stats;
1440
+ self.updateStats();
1441
+ if (self.onAfterDraw)
1442
+ self.onAfterDraw();
1443
+
1444
+ cache.push(self);
1445
+ callback();
1446
+ }
1447
+
1448
+ var renderContext = {
1449
+ canvasContext: ctx,
1450
+ viewport: this.viewport,
1451
+ textLayer: textLayer,
1452
+ continueCallback: function pdfViewcContinueCallback(cont) {
1453
+ if (PDFView.highestPriorityPage !== 'page' + self.id) {
1454
+ self.renderingState = RenderingStates.PAUSED;
1455
+ self.resume = function resumeCallback() {
1456
+ self.renderingState = RenderingStates.RUNNING;
1457
+ cont();
1458
+ };
1459
+ return;
1460
+ }
1461
+ cont();
1462
+ }
1463
+ };
1464
+ this.pdfPage.render(renderContext).then(
1465
+ function pdfPageRenderCallback() {
1466
+ pageViewDrawCallback(null);
1467
+ },
1468
+ function pdfPageRenderError(error) {
1469
+ pageViewDrawCallback(error);
1470
+ }
1471
+ );
1472
+
1473
+ setupAnnotations(this.pdfPage, this.viewport);
1474
+ div.setAttribute('data-loaded', true);
1475
+ };
1476
+
1477
+ this.beforePrint = function pageViewBeforePrint() {
1478
+ var pdfPage = this.pdfPage;
1479
+ var viewport = pdfPage.getViewport(1);
1480
+
1481
+ var canvas = this.canvas = document.createElement('canvas');
1482
+ canvas.width = viewport.width;
1483
+ canvas.height = viewport.height;
1484
+ canvas.style.width = viewport.width + 'pt';
1485
+ canvas.style.height = viewport.height + 'pt';
1486
+
1487
+ var printContainer = document.getElementById('printContainer');
1488
+ printContainer.appendChild(canvas);
1489
+
1490
+ var self = this;
1491
+ canvas.mozPrintCallback = function(obj) {
1492
+ var ctx = obj.context;
1493
+ var renderContext = {
1494
+ canvasContext: ctx,
1495
+ viewport: viewport
1496
+ };
1497
+
1498
+ pdfPage.render(renderContext).then(function() {
1499
+ // Tell the printEngine that rendering this canvas/page has finished.
1500
+ obj.done();
1501
+ self.pdfPage.destroy();
1502
+ }, function(error) {
1503
+ console.error(error);
1504
+ // Tell the printEngine that rendering this canvas/page has failed.
1505
+ // This will make the print proces stop.
1506
+ if ('abort' in object)
1507
+ obj.abort();
1508
+ else
1509
+ obj.done();
1510
+ self.pdfPage.destroy();
1511
+ });
1512
+ };
1513
+ };
1514
+
1515
+ this.updateStats = function pageViewUpdateStats() {
1516
+ if (PDFJS.pdfBug && Stats.enabled) {
1517
+ var stats = this.stats;
1518
+ Stats.add(this.id, stats);
1519
+ }
1520
+ };
1521
+ };
1522
+
1523
+ var ThumbnailView = function thumbnailView(container, pdfPage, id) {
1524
+ var anchor = document.createElement('a');
1525
+ anchor.href = PDFView.getAnchorUrl('#page=' + id);
1526
+ anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}');
1527
+ anchor.onclick = function stopNavigation() {
1528
+ PDFView.page = id;
1529
+ return false;
1530
+ };
1531
+
1532
+ var rotation = 0;
1533
+ var totalRotation = (rotation + pdfPage.rotate) % 360;
1534
+ var viewport = pdfPage.getViewport(1, totalRotation);
1535
+ var pageWidth = this.width = viewport.width;
1536
+ var pageHeight = this.height = viewport.height;
1537
+ var pageRatio = pageWidth / pageHeight;
1538
+ this.id = id;
1539
+
1540
+ var canvasWidth = 98;
1541
+ var canvasHeight = canvasWidth / this.width * this.height;
1542
+ var scaleX = this.scaleX = (canvasWidth / pageWidth);
1543
+ var scaleY = this.scaleY = (canvasHeight / pageHeight);
1544
+
1545
+ var div = this.el = document.createElement('div');
1546
+ div.id = 'thumbnailContainer' + id;
1547
+ div.className = 'thumbnail';
1548
+
1549
+ var ring = document.createElement('div');
1550
+ ring.className = 'thumbnailSelectionRing';
1551
+ ring.style.width = canvasWidth + 'px';
1552
+ ring.style.height = canvasHeight + 'px';
1553
+
1554
+ div.appendChild(ring);
1555
+ anchor.appendChild(div);
1556
+ container.appendChild(anchor);
1557
+
1558
+ this.hasImage = false;
1559
+ this.renderingState = RenderingStates.INITIAL;
1560
+
1561
+ this.updateRotation = function(rot) {
1562
+
1563
+ rotation = rot;
1564
+ totalRotation = (rotation + pdfPage.rotate) % 360;
1565
+ viewport = pdfPage.getViewport(1, totalRotation);
1566
+ pageWidth = this.width = viewport.width;
1567
+ pageHeight = this.height = viewport.height;
1568
+ pageRatio = pageWidth / pageHeight;
1569
+
1570
+ canvasHeight = canvasWidth / this.width * this.height;
1571
+ scaleX = this.scaleX = (canvasWidth / pageWidth);
1572
+ scaleY = this.scaleY = (canvasHeight / pageHeight);
1573
+
1574
+ div.removeAttribute('data-loaded');
1575
+ ring.textContent = '';
1576
+ ring.style.width = canvasWidth + 'px';
1577
+ ring.style.height = canvasHeight + 'px';
1578
+
1579
+ this.hasImage = false;
1580
+ this.renderingState = RenderingStates.INITIAL;
1581
+ this.resume = null;
1582
+ }
1583
+
1584
+ function getPageDrawContext() {
1585
+ var canvas = document.createElement('canvas');
1586
+ canvas.id = 'thumbnail' + id;
1587
+ canvas.mozOpaque = true;
1588
+
1589
+ canvas.width = canvasWidth;
1590
+ canvas.height = canvasHeight;
1591
+ canvas.className = 'thumbnailImage';
1592
+ canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas',
1593
+ {page: id}, 'Thumbnail of Page {{page}}'));
1594
+
1595
+ div.setAttribute('data-loaded', true);
1596
+
1597
+ ring.appendChild(canvas);
1598
+
1599
+ var ctx = canvas.getContext('2d');
1600
+ ctx.save();
1601
+ ctx.fillStyle = 'rgb(255, 255, 255)';
1602
+ ctx.fillRect(0, 0, canvasWidth, canvasHeight);
1603
+ ctx.restore();
1604
+ return ctx;
1605
+ }
1606
+
1607
+ this.drawingRequired = function thumbnailViewDrawingRequired() {
1608
+ return !this.hasImage;
1609
+ };
1610
+
1611
+ this.draw = function thumbnailViewDraw(callback) {
1612
+ if (this.renderingState !== RenderingStates.INITIAL)
1613
+ error('Must be in new state before drawing');
1614
+
1615
+ this.renderingState = RenderingStates.RUNNING;
1616
+ if (this.hasImage) {
1617
+ callback();
1618
+ return;
1619
+ }
1620
+
1621
+ var self = this;
1622
+ var ctx = getPageDrawContext();
1623
+ var drawViewport = pdfPage.getViewport(scaleX, totalRotation);
1624
+ var renderContext = {
1625
+ canvasContext: ctx,
1626
+ viewport: drawViewport,
1627
+ continueCallback: function(cont) {
1628
+ if (PDFView.highestPriorityPage !== 'thumbnail' + self.id) {
1629
+ self.renderingState = RenderingStates.PAUSED;
1630
+ self.resume = function() {
1631
+ self.renderingState = RenderingStates.RUNNING;
1632
+ cont();
1633
+ };
1634
+ return;
1635
+ }
1636
+ cont();
1637
+ }
1638
+ };
1639
+ pdfPage.render(renderContext).then(
1640
+ function pdfPageRenderCallback() {
1641
+ self.renderingState = RenderingStates.FINISHED;
1642
+ callback();
1643
+ },
1644
+ function pdfPageRenderError(error) {
1645
+ self.renderingState = RenderingStates.FINISHED;
1646
+ callback();
1647
+ }
1648
+ );
1649
+ this.hasImage = true;
1650
+ };
1651
+
1652
+ this.setImage = function thumbnailViewSetImage(img) {
1653
+ if (this.hasImage || !img)
1654
+ return;
1655
+ this.renderingState = RenderingStates.FINISHED;
1656
+ var ctx = getPageDrawContext();
1657
+ ctx.drawImage(img, 0, 0, img.width, img.height,
1658
+ 0, 0, ctx.canvas.width, ctx.canvas.height);
1659
+
1660
+ this.hasImage = true;
1661
+ };
1662
+ };
1663
+
1664
+ var DocumentOutlineView = function documentOutlineView(outline) {
1665
+ var outlineView = document.getElementById('outlineView');
1666
+ while (outlineView.firstChild)
1667
+ outlineView.removeChild(outlineView.firstChild);
1668
+
1669
+ function bindItemLink(domObj, item) {
1670
+ domObj.href = PDFView.getDestinationHash(item.dest);
1671
+ domObj.onclick = function documentOutlineViewOnclick(e) {
1672
+ PDFView.navigateTo(item.dest);
1673
+ return false;
1674
+ };
1675
+ }
1676
+
1677
+ if (!outline) {
1678
+ var noOutline = document.createElement('div');
1679
+ noOutline.classList.add('noOutline');
1680
+ noOutline.textContent = mozL10n.get('no_outline', null,
1681
+ 'No Outline Available');
1682
+ outlineView.appendChild(noOutline);
1683
+ return;
1684
+ }
1685
+
1686
+ var queue = [{parent: outlineView, items: outline}];
1687
+ while (queue.length > 0) {
1688
+ var levelData = queue.shift();
1689
+ var i, n = levelData.items.length;
1690
+ for (i = 0; i < n; i++) {
1691
+ var item = levelData.items[i];
1692
+ var div = document.createElement('div');
1693
+ div.className = 'outlineItem';
1694
+ var a = document.createElement('a');
1695
+ bindItemLink(a, item);
1696
+ a.textContent = item.title;
1697
+ div.appendChild(a);
1698
+
1699
+ if (item.items.length > 0) {
1700
+ var itemsDiv = document.createElement('div');
1701
+ itemsDiv.className = 'outlineItems';
1702
+ div.appendChild(itemsDiv);
1703
+ queue.push({parent: itemsDiv, items: item.items});
1704
+ }
1705
+
1706
+ levelData.parent.appendChild(div);
1707
+ }
1708
+ }
1709
+ };
1710
+
1711
+ // optimised CSS custom property getter/setter
1712
+ var CustomStyle = (function CustomStyleClosure() {
1713
+
1714
+ // As noted on: http://www.zachstronaut.com/posts/2009/02/17/
1715
+ // animate-css-transforms-firefox-webkit.html
1716
+ // in some versions of IE9 it is critical that ms appear in this list
1717
+ // before Moz
1718
+ var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
1719
+ var _cache = { };
1720
+
1721
+ function CustomStyle() {
1722
+ }
1723
+
1724
+ CustomStyle.getProp = function get(propName, element) {
1725
+ // check cache only when no element is given
1726
+ if (arguments.length == 1 && typeof _cache[propName] == 'string') {
1727
+ return _cache[propName];
1728
+ }
1729
+
1730
+ element = element || document.documentElement;
1731
+ var style = element.style, prefixed, uPropName;
1732
+
1733
+ // test standard property first
1734
+ if (typeof style[propName] == 'string') {
1735
+ return (_cache[propName] = propName);
1736
+ }
1737
+
1738
+ // capitalize
1739
+ uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
1740
+
1741
+ // test vendor specific properties
1742
+ for (var i = 0, l = prefixes.length; i < l; i++) {
1743
+ prefixed = prefixes[i] + uPropName;
1744
+ if (typeof style[prefixed] == 'string') {
1745
+ return (_cache[propName] = prefixed);
1746
+ }
1747
+ }
1748
+
1749
+ //if all fails then set to undefined
1750
+ return (_cache[propName] = 'undefined');
1751
+ };
1752
+
1753
+ CustomStyle.setProp = function set(propName, element, str) {
1754
+ var prop = this.getProp(propName);
1755
+ if (prop != 'undefined')
1756
+ element.style[prop] = str;
1757
+ };
1758
+
1759
+ return CustomStyle;
1760
+ })();
1761
+
1762
+ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
1763
+ this.textLayerDiv = textLayerDiv;
1764
+
1765
+ this.beginLayout = function textLayerBuilderBeginLayout() {
1766
+ this.textDivs = [];
1767
+ this.textLayerQueue = [];
1768
+ };
1769
+
1770
+ this.endLayout = function textLayerBuilderEndLayout() {
1771
+ var self = this;
1772
+ var textDivs = this.textDivs;
1773
+ var textLayerDiv = this.textLayerDiv;
1774
+ var renderTimer = null;
1775
+ var renderingDone = false;
1776
+ var renderInterval = 0;
1777
+ var resumeInterval = 500; // in ms
1778
+
1779
+ var canvas = document.createElement('canvas');
1780
+ var ctx = canvas.getContext('2d');
1781
+
1782
+ // Render the text layer, one div at a time
1783
+ function renderTextLayer() {
1784
+ if (textDivs.length === 0) {
1785
+ clearInterval(renderTimer);
1786
+ renderingDone = true;
1787
+ self.textLayerDiv = textLayerDiv = canvas = ctx = null;
1788
+ return;
1789
+ }
1790
+ var textDiv = textDivs.shift();
1791
+ textLayerDiv.appendChild(textDiv);
1792
+
1793
+ ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
1794
+ var width = ctx.measureText(textDiv.textContent).width;
1795
+
1796
+ if (width > 0) {
1797
+ var textScale = textDiv.dataset.canvasWidth / width;
1798
+
1799
+ CustomStyle.setProp('transform' , textDiv,
1800
+ 'scale(' + textScale + ', 1)');
1801
+ CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
1802
+ }
1803
+ }
1804
+ renderTimer = setInterval(renderTextLayer, renderInterval);
1805
+
1806
+ // Stop rendering when user scrolls. Resume after XXX milliseconds
1807
+ // of no scroll events
1808
+ var scrollTimer = null;
1809
+ function textLayerOnScroll() {
1810
+ if (renderingDone) {
1811
+ window.removeEventListener('scroll', textLayerOnScroll, false);
1812
+ return;
1813
+ }
1814
+
1815
+ // Immediately pause rendering
1816
+ clearInterval(renderTimer);
1817
+
1818
+ clearTimeout(scrollTimer);
1819
+ scrollTimer = setTimeout(function textLayerScrollTimer() {
1820
+ // Resume rendering
1821
+ renderTimer = setInterval(renderTextLayer, renderInterval);
1822
+ }, resumeInterval);
1823
+ } // textLayerOnScroll
1824
+
1825
+ window.addEventListener('scroll', textLayerOnScroll, false);
1826
+ }; // endLayout
1827
+
1828
+ this.appendText = function textLayerBuilderAppendText(text,
1829
+ fontName, fontSize) {
1830
+ var textDiv = document.createElement('div');
1831
+
1832
+ // vScale and hScale already contain the scaling to pixel units
1833
+ var fontHeight = fontSize * text.geom.vScale;
1834
+ textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale;
1835
+
1836
+ textDiv.style.fontSize = fontHeight + 'px';
1837
+ textDiv.style.fontFamily = fontName;
1838
+ textDiv.style.left = text.geom.x + 'px';
1839
+ textDiv.style.top = (text.geom.y - fontHeight) + 'px';
1840
+ textDiv.textContent = PDFJS.bidi(text, -1);
1841
+ textDiv.dir = text.direction;
1842
+ this.textDivs.push(textDiv);
1843
+ };
1844
+ };
1845
+
1846
+ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
1847
+ PDFView.initialize();
1848
+ var params = PDFView.parseQueryString(document.location.search.substring(1));
1849
+
1850
+ if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {
1851
+ document.getElementById('openFile').setAttribute('hidden', 'true');
1852
+ } else {
1853
+ document.getElementById('fileInput').value = null;
1854
+ }
1855
+
1856
+ // Special debugging flags in the hash section of the URL.
1857
+ var hash = document.location.hash.substring(1);
1858
+ var hashParams = PDFView.parseQueryString(hash);
1859
+
1860
+ if ('disableWorker' in hashParams)
1861
+ PDFJS.disableWorker = (hashParams['disableWorker'] === 'true');
1862
+
1863
+ var locale = navigator.language;
1864
+ if ('locale' in hashParams)
1865
+ locale = hashParams['locale'];
1866
+ mozL10n.language.code = locale;
1867
+
1868
+ if ('textLayer' in hashParams) {
1869
+ switch (hashParams['textLayer']) {
1870
+ case 'off':
1871
+ PDFJS.disableTextLayer = true;
1872
+ break;
1873
+ case 'visible':
1874
+ case 'shadow':
1875
+ case 'hover':
1876
+ var viewer = document.getElementById('viewer');
1877
+ viewer.classList.add('textLayer-' + hashParams['textLayer']);
1878
+ break;
1879
+ }
1880
+ }
1881
+
1882
+ if ('pdfBug' in hashParams) {
1883
+ PDFJS.pdfBug = true;
1884
+ var pdfBug = hashParams['pdfBug'];
1885
+ var enabled = pdfBug.split(',');
1886
+ PDFBug.enable(enabled);
1887
+ PDFBug.init();
1888
+ }
1889
+
1890
+
1891
+ if (!PDFView.supportsPrinting) {
1892
+ document.getElementById('print').classList.add('hidden');
1893
+ }
1894
+
1895
+ if (!PDFView.supportsFullscreen) {
1896
+ document.getElementById('fullscreen').classList.add('hidden');
1897
+ }
1898
+
1899
+ // Listen for warnings to trigger the fallback UI. Errors should be caught
1900
+ // and call PDFView.error() so we don't need to listen for those.
1901
+ PDFJS.LogManager.addLogger({
1902
+ warn: function() {
1903
+ PDFView.fallback();
1904
+ }
1905
+ });
1906
+
1907
+ var mainContainer = document.getElementById('mainContainer');
1908
+ var outerContainer = document.getElementById('outerContainer');
1909
+ mainContainer.addEventListener('transitionend', function(e) {
1910
+ if (e.target == mainContainer) {
1911
+ var event = document.createEvent('UIEvents');
1912
+ event.initUIEvent('resize', false, false, window, 0);
1913
+ window.dispatchEvent(event);
1914
+ outerContainer.classList.remove('sidebarMoving');
1915
+ }
1916
+ }, true);
1917
+
1918
+ document.getElementById('sidebarToggle').addEventListener('click',
1919
+ function() {
1920
+ this.classList.toggle('toggled');
1921
+ outerContainer.classList.add('sidebarMoving');
1922
+ outerContainer.classList.toggle('sidebarOpen');
1923
+ PDFView.sidebarOpen = outerContainer.classList.contains('sidebarOpen');
1924
+ PDFView.renderHighestPriority();
1925
+ });
1926
+
1927
+ document.getElementById('viewThumbnail').addEventListener('click',
1928
+ function() {
1929
+ PDFView.switchSidebarView('thumbs');
1930
+ });
1931
+
1932
+ document.getElementById('viewOutline').addEventListener('click',
1933
+ function() {
1934
+ PDFView.switchSidebarView('outline');
1935
+ });
1936
+
1937
+ document.getElementById('viewSearch').addEventListener('click',
1938
+ function() {
1939
+ PDFView.switchSidebarView('search');
1940
+ });
1941
+
1942
+ document.getElementById('searchButton').addEventListener('click',
1943
+ function() {
1944
+ PDFView.search();
1945
+ });
1946
+
1947
+ document.getElementById('previous').addEventListener('click',
1948
+ function() {
1949
+ PDFView.page--;
1950
+ });
1951
+
1952
+ document.getElementById('next').addEventListener('click',
1953
+ function() {
1954
+ PDFView.page++;
1955
+ });
1956
+
1957
+ document.querySelector('.zoomIn').addEventListener('click',
1958
+ function() {
1959
+ PDFView.zoomIn();
1960
+ });
1961
+
1962
+ document.querySelector('.zoomOut').addEventListener('click',
1963
+ function() {
1964
+ PDFView.zoomOut();
1965
+ });
1966
+
1967
+ document.getElementById('fullscreen').addEventListener('click',
1968
+ function() {
1969
+ PDFView.fullscreen();
1970
+ });
1971
+
1972
+ document.getElementById('openFile').addEventListener('click',
1973
+ function() {
1974
+ document.getElementById('fileInput').click();
1975
+ });
1976
+
1977
+ document.getElementById('print').addEventListener('click',
1978
+ function() {
1979
+ window.print();
1980
+ });
1981
+
1982
+ document.getElementById('download').addEventListener('click',
1983
+ function() {
1984
+ PDFView.download();
1985
+ });
1986
+
1987
+ document.getElementById('searchTermsInput').addEventListener('keydown',
1988
+ function(event) {
1989
+ if (event.keyCode == 13) {
1990
+ PDFView.search();
1991
+ }
1992
+ });
1993
+
1994
+ document.getElementById('pageNumber').addEventListener('change',
1995
+ function() {
1996
+ PDFView.page = this.value;
1997
+ });
1998
+
1999
+ document.getElementById('scaleSelect').addEventListener('change',
2000
+ function() {
2001
+ PDFView.parseScale(this.value);
2002
+ });
2003
+
2004
+ document.getElementById('page_rotate_ccw').addEventListener('click',
2005
+ function() {
2006
+ PDFView.rotatePages(-90);
2007
+ });
2008
+
2009
+ document.getElementById('page_rotate_cw').addEventListener('click',
2010
+ function() {
2011
+ PDFView.rotatePages(90);
2012
+ });
2013
+
2014
+
2015
+ }, true);
2016
+
2017
+ function updateViewarea() {
2018
+
2019
+ if (!PDFView.initialized)
2020
+ return;
2021
+ var visible = PDFView.getVisiblePages();
2022
+ var visiblePages = visible.views;
2023
+
2024
+ PDFView.renderHighestPriority();
2025
+
2026
+ var currentId = PDFView.page;
2027
+ var firstPage = visible.first;
2028
+
2029
+ for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
2030
+ i < ii; ++i) {
2031
+ var page = visiblePages[i];
2032
+
2033
+ if (page.percent < 100)
2034
+ break;
2035
+
2036
+ if (page.id === PDFView.page) {
2037
+ stillFullyVisible = true;
2038
+ break;
2039
+ }
2040
+ }
2041
+
2042
+ if (!stillFullyVisible) {
2043
+ currentId = visiblePages[0].id;
2044
+ }
2045
+
2046
+ if (!PDFView.isFullscreen) {
2047
+ updateViewarea.inProgress = true; // used in "set page"
2048
+ PDFView.page = currentId;
2049
+ updateViewarea.inProgress = false;
2050
+ }
2051
+
2052
+ var currentScale = PDFView.currentScale;
2053
+ var currentScaleValue = PDFView.currentScaleValue;
2054
+ var normalizedScaleValue = currentScaleValue == currentScale ?
2055
+ currentScale * 100 : currentScaleValue;
2056
+
2057
+ var pageNumber = firstPage.id;
2058
+ var pdfOpenParams = '#page=' + pageNumber;
2059
+ pdfOpenParams += '&zoom=' + normalizedScaleValue;
2060
+ var currentPage = PDFView.pages[pageNumber - 1];
2061
+ var topLeft = currentPage.getPagePoint(PDFView.container.scrollLeft,
2062
+ (PDFView.container.scrollTop - firstPage.y));
2063
+ pdfOpenParams += ',' + Math.round(topLeft[0]) + ',' + Math.round(topLeft[1]);
2064
+
2065
+ var store = PDFView.store;
2066
+ store.set('exists', true);
2067
+ store.set('page', pageNumber);
2068
+ store.set('zoom', normalizedScaleValue);
2069
+ store.set('scrollLeft', Math.round(topLeft[0]));
2070
+ store.set('scrollTop', Math.round(topLeft[1]));
2071
+ var href = PDFView.getAnchorUrl(pdfOpenParams);
2072
+ document.getElementById('viewBookmark').href = href;
2073
+ }
2074
+
2075
+ window.addEventListener('resize', function webViewerResize(evt) {
2076
+ if (PDFView.initialized &&
2077
+ (document.getElementById('pageWidthOption').selected ||
2078
+ document.getElementById('pageFitOption').selected ||
2079
+ document.getElementById('pageAutoOption').selected))
2080
+ PDFView.parseScale(document.getElementById('scaleSelect').value);
2081
+ updateViewarea();
2082
+ });
2083
+
2084
+ window.addEventListener('hashchange', function webViewerHashchange(evt) {
2085
+ PDFView.setHash(document.location.hash.substring(1));
2086
+ });
2087
+
2088
+ window.addEventListener('change', function webViewerChange(evt) {
2089
+ var files = evt.target.files;
2090
+ if (!files || files.length == 0)
2091
+ return;
2092
+
2093
+ // Read the local file into a Uint8Array.
2094
+ var fileReader = new FileReader();
2095
+ fileReader.onload = function webViewerChangeFileReaderOnload(evt) {
2096
+ var buffer = evt.target.result;
2097
+ var uint8Array = new Uint8Array(buffer);
2098
+ PDFView.open(uint8Array, 0);
2099
+ };
2100
+
2101
+ var file = files[0];
2102
+ fileReader.readAsArrayBuffer(file);
2103
+ PDFView.setTitleUsingUrl(file.name);
2104
+
2105
+ // URL does not reflect proper document location - hiding some icons.
2106
+ document.getElementById('viewBookmark').setAttribute('hidden', 'true');
2107
+ document.getElementById('download').setAttribute('hidden', 'true');
2108
+ }, true);
2109
+
2110
+ function selectScaleOption(value) {
2111
+ var options = document.getElementById('scaleSelect').options;
2112
+ var predefinedValueFound = false;
2113
+ for (var i = 0; i < options.length; i++) {
2114
+ var option = options[i];
2115
+ if (option.value != value) {
2116
+ option.selected = false;
2117
+ continue;
2118
+ }
2119
+ option.selected = true;
2120
+ predefinedValueFound = true;
2121
+ }
2122
+ return predefinedValueFound;
2123
+ }
2124
+
2125
+ window.addEventListener('localized', function localized(evt) {
2126
+ document.getElementsByTagName('html')[0].dir = mozL10n.language.direction;
2127
+ }, true);
2128
+
2129
+ window.addEventListener('scalechange', function scalechange(evt) {
2130
+ var customScaleOption = document.getElementById('customScaleOption');
2131
+ customScaleOption.selected = false;
2132
+
2133
+ if (!evt.resetAutoSettings &&
2134
+ (document.getElementById('pageWidthOption').selected ||
2135
+ document.getElementById('pageFitOption').selected ||
2136
+ document.getElementById('pageAutoOption').selected)) {
2137
+ updateViewarea();
2138
+ return;
2139
+ }
2140
+
2141
+ var predefinedValueFound = selectScaleOption('' + evt.scale);
2142
+ if (!predefinedValueFound) {
2143
+ customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
2144
+ customScaleOption.selected = true;
2145
+ }
2146
+
2147
+ updateViewarea();
2148
+ }, true);
2149
+
2150
+ window.addEventListener('pagechange', function pagechange(evt) {
2151
+ var page = evt.pageNumber;
2152
+ if (document.getElementById('pageNumber').value != page) {
2153
+ document.getElementById('pageNumber').value = page;
2154
+ var selected = document.querySelector('.thumbnail.selected');
2155
+ if (selected)
2156
+ selected.classList.remove('selected');
2157
+ var thumbnail = document.getElementById('thumbnailContainer' + page);
2158
+ thumbnail.classList.add('selected');
2159
+ var visibleThumbs = PDFView.getVisibleThumbs();
2160
+ var numVisibleThumbs = visibleThumbs.views.length;
2161
+ // If the thumbnail isn't currently visible scroll it into view.
2162
+ if (numVisibleThumbs > 0) {
2163
+ var first = visibleThumbs.first.id;
2164
+ // Account for only one thumbnail being visible.
2165
+ var last = numVisibleThumbs > 1 ?
2166
+ visibleThumbs.last.id : first;
2167
+ if (page <= first || page >= last)
2168
+ scrollIntoView(thumbnail);
2169
+ }
2170
+
2171
+ }
2172
+ document.getElementById('previous').disabled = (page <= 1);
2173
+ document.getElementById('next').disabled = (page >= PDFView.pages.length);
2174
+ }, true);
2175
+
2176
+ // Firefox specific event, so that we can prevent browser from zooming
2177
+ window.addEventListener('DOMMouseScroll', function(evt) {
2178
+ if (evt.ctrlKey) {
2179
+ evt.preventDefault();
2180
+
2181
+ var ticks = evt.detail;
2182
+ var direction = (ticks > 0) ? 'zoomOut' : 'zoomIn';
2183
+ for (var i = 0, length = Math.abs(ticks); i < length; i++)
2184
+ PDFView[direction]();
2185
+ }
2186
+ }, false);
2187
+
2188
+ window.addEventListener('keydown', function keydown(evt) {
2189
+ var handled = false;
2190
+ var cmd = (evt.ctrlKey ? 1 : 0) |
2191
+ (evt.altKey ? 2 : 0) |
2192
+ (evt.shiftKey ? 4 : 0) |
2193
+ (evt.metaKey ? 8 : 0);
2194
+
2195
+ // First, handle the key bindings that are independent whether an input
2196
+ // control is selected or not.
2197
+ if (cmd == 1 || cmd == 8) { // either CTRL or META key.
2198
+ switch (evt.keyCode) {
2199
+ case 61: // FF/Mac '='
2200
+ case 107: // FF '+' and '='
2201
+ case 187: // Chrome '+'
2202
+ PDFView.zoomIn();
2203
+ handled = true;
2204
+ break;
2205
+ case 173: // FF/Mac '-'
2206
+ case 109: // FF '-'
2207
+ case 189: // Chrome '-'
2208
+ PDFView.zoomOut();
2209
+ handled = true;
2210
+ break;
2211
+ case 48: // '0'
2212
+ PDFView.parseScale(kDefaultScale, true);
2213
+ handled = true;
2214
+ break;
2215
+ }
2216
+ }
2217
+
2218
+ if (handled) {
2219
+ evt.preventDefault();
2220
+ return;
2221
+ }
2222
+
2223
+ // Some shortcuts should not get handled if a control/input element
2224
+ // is selected.
2225
+ var curElement = document.activeElement;
2226
+ if (curElement && curElement.tagName == 'INPUT')
2227
+ return;
2228
+ var controlsElement = document.getElementById('controls');
2229
+ while (curElement) {
2230
+ if (curElement === controlsElement && !PDFView.isFullscreen)
2231
+ return; // ignoring if the 'controls' element is focused
2232
+ curElement = curElement.parentNode;
2233
+ }
2234
+
2235
+ if (cmd == 0) { // no control key pressed at all.
2236
+ switch (evt.keyCode) {
2237
+ case 37: // left arrow
2238
+ case 75: // 'k'
2239
+ case 80: // 'p'
2240
+ PDFView.page--;
2241
+ handled = true;
2242
+ break;
2243
+ case 39: // right arrow
2244
+ case 74: // 'j'
2245
+ case 78: // 'n'
2246
+ PDFView.page++;
2247
+ handled = true;
2248
+ break;
2249
+
2250
+ case 32: // spacebar
2251
+ if (PDFView.isFullscreen) {
2252
+ PDFView.page++;
2253
+ handled = true;
2254
+ }
2255
+ break;
2256
+
2257
+ case 82: // 'r'
2258
+ PDFView.rotatePages(90);
2259
+ break;
2260
+ }
2261
+ }
2262
+
2263
+ if (cmd == 4) { // shift-key
2264
+ switch (evt.keyCode) {
2265
+ case 82: // 'r'
2266
+ PDFView.rotatePages(-90);
2267
+ break;
2268
+ }
2269
+ }
2270
+
2271
+ if (handled) {
2272
+ evt.preventDefault();
2273
+ }
2274
+ });
2275
+
2276
+ window.addEventListener('beforeprint', function beforePrint(evt) {
2277
+ PDFView.beforePrint();
2278
+ });
2279
+
2280
+ window.addEventListener('afterprint', function afterPrint(evt) {
2281
+ PDFView.afterPrint();
2282
+ });
2283
+
2284
+ (function fullscreenClosure() {
2285
+ function fullscreenChange(e) {
2286
+ var isFullscreen = document.fullscreen || document.mozFullScreen ||
2287
+ document.webkitIsFullScreen;
2288
+
2289
+ if (!isFullscreen) {
2290
+ PDFView.exitFullscreen();
2291
+ }
2292
+ }
2293
+
2294
+ window.addEventListener('fullscreenchange', fullscreenChange, false);
2295
+ window.addEventListener('mozfullscreenchange', fullscreenChange, false);
2296
+ window.addEventListener('webkitfullscreenchange', fullscreenChange, false);
2297
+ })();