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