pdfjs-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ })();