pdfjs_rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +37 -0
  6. data/Rakefile +1 -0
  7. data/lib/generators/pdfjs_rails/install_generator.rb +13 -0
  8. data/lib/pdfjs/build/pdf.js +7071 -0
  9. data/lib/pdfjs/build/pdf.worker.js +38687 -0
  10. data/lib/pdfjs/web/compatibility.js +471 -0
  11. data/lib/pdfjs/web/debugger.js +532 -0
  12. data/lib/pdfjs/web/images/annotation-check.svg +10 -0
  13. data/lib/pdfjs/web/images/annotation-comment.svg +15 -0
  14. data/lib/pdfjs/web/images/annotation-help.svg +25 -0
  15. data/lib/pdfjs/web/images/annotation-insert.svg +9 -0
  16. data/lib/pdfjs/web/images/annotation-key.svg +10 -0
  17. data/lib/pdfjs/web/images/annotation-newparagraph.svg +10 -0
  18. data/lib/pdfjs/web/images/annotation-note.svg +41 -0
  19. data/lib/pdfjs/web/images/annotation-paragraph.svg +15 -0
  20. data/lib/pdfjs/web/images/findbarButton-next-rtl.png +0 -0
  21. data/lib/pdfjs/web/images/findbarButton-next.png +0 -0
  22. data/lib/pdfjs/web/images/findbarButton-previous-rtl.png +0 -0
  23. data/lib/pdfjs/web/images/findbarButton-previous.png +0 -0
  24. data/lib/pdfjs/web/images/loading-icon.gif +0 -0
  25. data/lib/pdfjs/web/images/loading-small.png +0 -0
  26. data/lib/pdfjs/web/images/secondaryToolbarButton-firstPage.png +0 -0
  27. data/lib/pdfjs/web/images/secondaryToolbarButton-lastPage.png +0 -0
  28. data/lib/pdfjs/web/images/secondaryToolbarButton-rotateCcw.png +0 -0
  29. data/lib/pdfjs/web/images/secondaryToolbarButton-rotateCw.png +0 -0
  30. data/lib/pdfjs/web/images/shadow.png +0 -0
  31. data/lib/pdfjs/web/images/texture.png +0 -0
  32. data/lib/pdfjs/web/images/toolbarButton-bookmark.png +0 -0
  33. data/lib/pdfjs/web/images/toolbarButton-download.png +0 -0
  34. data/lib/pdfjs/web/images/toolbarButton-menuArrows.png +0 -0
  35. data/lib/pdfjs/web/images/toolbarButton-openFile.png +0 -0
  36. data/lib/pdfjs/web/images/toolbarButton-pageDown-rtl.png +0 -0
  37. data/lib/pdfjs/web/images/toolbarButton-pageDown.png +0 -0
  38. data/lib/pdfjs/web/images/toolbarButton-pageUp-rtl.png +0 -0
  39. data/lib/pdfjs/web/images/toolbarButton-pageUp.png +0 -0
  40. data/lib/pdfjs/web/images/toolbarButton-presentationMode.png +0 -0
  41. data/lib/pdfjs/web/images/toolbarButton-print.png +0 -0
  42. data/lib/pdfjs/web/images/toolbarButton-search.png +0 -0
  43. data/lib/pdfjs/web/images/toolbarButton-secondaryToolbarToggle.png +0 -0
  44. data/lib/pdfjs/web/images/toolbarButton-sidebarToggle.png +0 -0
  45. data/lib/pdfjs/web/images/toolbarButton-viewOutline.png +0 -0
  46. data/lib/pdfjs/web/images/toolbarButton-viewThumbnail.png +0 -0
  47. data/lib/pdfjs/web/images/toolbarButton-zoomIn.png +0 -0
  48. data/lib/pdfjs/web/images/toolbarButton-zoomOut.png +0 -0
  49. data/lib/pdfjs/web/l10n.js +922 -0
  50. data/lib/pdfjs/web/locale/ar/viewer.properties +112 -0
  51. data/lib/pdfjs/web/locale/ca/viewer.properties +131 -0
  52. data/lib/pdfjs/web/locale/cs/viewer.properties +58 -0
  53. data/lib/pdfjs/web/locale/da/viewer.properties +132 -0
  54. data/lib/pdfjs/web/locale/de/viewer.properties +128 -0
  55. data/lib/pdfjs/web/locale/el/viewer.properties +124 -0
  56. data/lib/pdfjs/web/locale/en-US/viewer.properties +134 -0
  57. data/lib/pdfjs/web/locale/es/viewer.properties +131 -0
  58. data/lib/pdfjs/web/locale/fi/viewer.properties +129 -0
  59. data/lib/pdfjs/web/locale/fr/viewer.properties +130 -0
  60. data/lib/pdfjs/web/locale/he/viewer.properties +59 -0
  61. data/lib/pdfjs/web/locale/it/viewer.properties +44 -0
  62. data/lib/pdfjs/web/locale/ja/viewer.properties +132 -0
  63. data/lib/pdfjs/web/locale/ko/viewer.properties +131 -0
  64. data/lib/pdfjs/web/locale/locale.properties +78 -0
  65. data/lib/pdfjs/web/locale/lt/viewer.properties +129 -0
  66. data/lib/pdfjs/web/locale/nl/viewer.properties +134 -0
  67. data/lib/pdfjs/web/locale/pl/viewer.properties +132 -0
  68. data/lib/pdfjs/web/locale/pt-BR/viewer.properties +44 -0
  69. data/lib/pdfjs/web/locale/ro/viewer.properties +55 -0
  70. data/lib/pdfjs/web/locale/ru/viewer.properties +62 -0
  71. data/lib/pdfjs/web/locale/sr/viewer.properties +55 -0
  72. data/lib/pdfjs/web/locale/sv/viewer.properties +134 -0
  73. data/lib/pdfjs/web/locale/tr/viewer.properties +129 -0
  74. data/lib/pdfjs/web/locale/vi/viewer.properties +131 -0
  75. data/lib/pdfjs/web/locale/zh-CN/viewer.properties +129 -0
  76. data/lib/pdfjs/web/locale/zh-TW/viewer.properties +132 -0
  77. data/lib/pdfjs/web/viewer.css +1544 -0
  78. data/lib/pdfjs/web/viewer.html +250 -0
  79. data/lib/pdfjs/web/viewer.js +4477 -0
  80. data/lib/pdfjs_rails.rb +6 -0
  81. data/lib/pdfjs_rails/railtie.rb +9 -0
  82. data/lib/pdfjs_rails/version.rb +3 -0
  83. data/lib/pdfjs_rails/view_helpers.rb +225 -0
  84. data/pdfjs_rails.gemspec +24 -0
  85. data/vender/assets/images/annotation-check.svg +10 -0
  86. data/vender/assets/images/annotation-comment.svg +15 -0
  87. data/vender/assets/images/annotation-help.svg +25 -0
  88. data/vender/assets/images/annotation-insert.svg +9 -0
  89. data/vender/assets/images/annotation-key.svg +10 -0
  90. data/vender/assets/images/annotation-newparagraph.svg +10 -0
  91. data/vender/assets/images/annotation-note.svg +41 -0
  92. data/vender/assets/images/annotation-paragraph.svg +15 -0
  93. data/vender/assets/images/findbarButton-next-rtl.png +0 -0
  94. data/vender/assets/images/findbarButton-next.png +0 -0
  95. data/vender/assets/images/findbarButton-previous-rtl.png +0 -0
  96. data/vender/assets/images/findbarButton-previous.png +0 -0
  97. data/vender/assets/images/loading-icon.gif +0 -0
  98. data/vender/assets/images/loading-small.png +0 -0
  99. data/vender/assets/images/shadow.png +0 -0
  100. data/vender/assets/images/texture.png +0 -0
  101. data/vender/assets/images/toolbarButton-bookmark.png +0 -0
  102. data/vender/assets/images/toolbarButton-download.png +0 -0
  103. data/vender/assets/images/toolbarButton-menuArrows.png +0 -0
  104. data/vender/assets/images/toolbarButton-openFile.png +0 -0
  105. data/vender/assets/images/toolbarButton-pageDown-rtl.png +0 -0
  106. data/vender/assets/images/toolbarButton-pageDown.png +0 -0
  107. data/vender/assets/images/toolbarButton-pageUp-rtl.png +0 -0
  108. data/vender/assets/images/toolbarButton-pageUp.png +0 -0
  109. data/vender/assets/images/toolbarButton-presentationMode.png +0 -0
  110. data/vender/assets/images/toolbarButton-print.png +0 -0
  111. data/vender/assets/images/toolbarButton-search.png +0 -0
  112. data/vender/assets/images/toolbarButton-sidebarToggle.png +0 -0
  113. data/vender/assets/images/toolbarButton-viewOutline.png +0 -0
  114. data/vender/assets/images/toolbarButton-viewThumbnail.png +0 -0
  115. data/vender/assets/images/toolbarButton-zoomIn.png +0 -0
  116. data/vender/assets/images/toolbarButton-zoomOut.png +0 -0
  117. data/vender/assets/javascripts/application.js +22 -0
  118. data/vender/assets/javascripts/compatibility.js +471 -0
  119. data/vender/assets/javascripts/debugger.js +532 -0
  120. data/vender/assets/javascripts/l10n.js +922 -0
  121. data/vender/assets/javascripts/pdf.js +7063 -0
  122. data/vender/assets/javascripts/pdf.worker.js +38679 -0
  123. data/vender/assets/javascripts/viewer.js +4317 -0
  124. data/vender/assets/stylesheets/viewer.css +1382 -0
  125. metadata +209 -0
@@ -0,0 +1,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
+