@internetarchive/bookreader 5.0.0-38 → 5.0.0-39

Sign up to get free protection for your applications and to get access to all the features.
Files changed (222) hide show
  1. package/BookReader/BookReader.css +8 -0
  2. package/BookReader/BookReader.js +1 -1
  3. package/BookReader/BookReader.js.map +1 -1
  4. package/BookReader/ia-bookreader-bundle.js +99 -75
  5. package/BookReader/ia-bookreader-bundle.js.map +1 -1
  6. package/BookReader/icons/magnify-minus.svg +1 -1
  7. package/BookReader/icons/magnify-plus.svg +1 -1
  8. package/BookReader/plugins/plugin.autoplay.js +1 -1
  9. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  10. package/BookReader/plugins/plugin.chapters.js +1 -1
  11. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  12. package/BookReader/plugins/plugin.mobile_nav.js +1 -1
  13. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  14. package/BookReader/plugins/plugin.resume.js +1 -1
  15. package/BookReader/plugins/plugin.resume.js.map +1 -1
  16. package/BookReader/plugins/plugin.search.js +1 -1
  17. package/BookReader/plugins/plugin.search.js.map +1 -1
  18. package/BookReader/plugins/plugin.text_selection.js +1 -1
  19. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  20. package/BookReader/plugins/plugin.tts.js +1 -1
  21. package/BookReader/plugins/plugin.tts.js.map +1 -1
  22. package/BookReader/plugins/plugin.url.js +1 -1
  23. package/BookReader/plugins/plugin.url.js.map +1 -1
  24. package/CHANGELOG.md +5 -0
  25. package/README.md +13 -0
  26. package/package.json +14 -14
  27. package/renovate.json +1 -1
  28. package/src/BookReader/Mode1UpLit.js +7 -1
  29. package/src/BookReader/Mode2Up.js +11 -0
  30. package/src/BookReader/ModeSmoothZoom.js +2 -0
  31. package/src/BookReader/PageContainer.js +10 -4
  32. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  33. package/src/assets/icons/magnify-minus.svg +3 -7
  34. package/src/assets/icons/magnify-plus.svg +3 -7
  35. package/src/css/_TextSelection.scss +13 -0
  36. package/tests/jest/BookReader/PageContainer.test.js +5 -4
  37. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  38. package/.husky/_/husky.sh +0 -30
  39. package/stat/BookNavigator/BookModel.js +0 -14
  40. package/stat/BookNavigator/BookNavigator.js +0 -524
  41. package/stat/BookNavigator/assets/bookmark-colors.js +0 -15
  42. package/stat/BookNavigator/assets/button-base.js +0 -61
  43. package/stat/BookNavigator/assets/ia-logo.js +0 -17
  44. package/stat/BookNavigator/assets/icon_checkmark.js +0 -6
  45. package/stat/BookNavigator/assets/icon_close.js +0 -3
  46. package/stat/BookNavigator/assets/icon_sort_asc.js +0 -5
  47. package/stat/BookNavigator/assets/icon_sort_desc.js +0 -5
  48. package/stat/BookNavigator/assets/icon_sort_neutral.js +0 -5
  49. package/stat/BookNavigator/assets/icon_volumes.js +0 -11
  50. package/stat/BookNavigator/bookmarks/bookmark-button.js +0 -64
  51. package/stat/BookNavigator/bookmarks/bookmark-edit.js +0 -215
  52. package/stat/BookNavigator/bookmarks/bookmarks-list.js +0 -285
  53. package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +0 -28
  54. package/stat/BookNavigator/bookmarks/bookmarks-provider.js +0 -56
  55. package/stat/BookNavigator/bookmarks/ia-bookmarks.js +0 -523
  56. package/stat/BookNavigator/br-fullscreen-mgr.js +0 -82
  57. package/stat/BookNavigator/delete-modal-actions.js +0 -49
  58. package/stat/BookNavigator/downloads/downloads-provider.js +0 -72
  59. package/stat/BookNavigator/downloads/downloads.js +0 -139
  60. package/stat/BookNavigator/provider-config.js +0 -0
  61. package/stat/BookNavigator/search/a-search-result.js +0 -55
  62. package/stat/BookNavigator/search/search-provider.js +0 -180
  63. package/stat/BookNavigator/search/search-results.js +0 -360
  64. package/stat/BookNavigator/sharing.js +0 -31
  65. package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +0 -94
  66. package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +0 -280
  67. package/stat/BookNavigator/volumes/volumes-provider.js +0 -83
  68. package/stat/BookNavigator/volumes/volumes.js +0 -178
  69. package/stat/BookReader/BookModel.js +0 -518
  70. package/stat/BookReader/DebugConsole.js +0 -54
  71. package/stat/BookReader/DragScrollable.js +0 -233
  72. package/stat/BookReader/ImageCache.js +0 -116
  73. package/stat/BookReader/Mode1Up.js +0 -102
  74. package/stat/BookReader/Mode1UpLit.js +0 -434
  75. package/stat/BookReader/Mode2Up.js +0 -1372
  76. package/stat/BookReader/ModeSmoothZoom.js +0 -177
  77. package/stat/BookReader/ModeThumb.js +0 -344
  78. package/stat/BookReader/Navbar/Navbar.js +0 -310
  79. package/stat/BookReader/PageContainer.js +0 -120
  80. package/stat/BookReader/ReduceSet.js +0 -26
  81. package/stat/BookReader/Toolbar/Toolbar.js +0 -384
  82. package/stat/BookReader/events.js +0 -20
  83. package/stat/BookReader/options.js +0 -324
  84. package/stat/BookReader/utils/HTMLDimensionsCacher.js +0 -44
  85. package/stat/BookReader/utils/classes.js +0 -36
  86. package/stat/BookReader/utils.js +0 -240
  87. package/stat/BookReader.js +0 -2550
  88. package/stat/BookReaderComponent/BookReaderComponent.js +0 -117
  89. package/stat/assets/icons/1up.svg +0 -12
  90. package/stat/assets/icons/2up.svg +0 -15
  91. package/stat/assets/icons/advance.svg +0 -26
  92. package/stat/assets/icons/chevron-right.svg +0 -1
  93. package/stat/assets/icons/close-circle-dark.svg +0 -1
  94. package/stat/assets/icons/close-circle.svg +0 -1
  95. package/stat/assets/icons/fullscreen.svg +0 -17
  96. package/stat/assets/icons/fullscreen_exit.svg +0 -17
  97. package/stat/assets/icons/hamburger.svg +0 -15
  98. package/stat/assets/icons/left-arrow.svg +0 -12
  99. package/stat/assets/icons/magnify-minus.svg +0 -16
  100. package/stat/assets/icons/magnify-plus.svg +0 -17
  101. package/stat/assets/icons/magnify.svg +0 -15
  102. package/stat/assets/icons/pause.svg +0 -23
  103. package/stat/assets/icons/play.svg +0 -22
  104. package/stat/assets/icons/playback-speed.svg +0 -34
  105. package/stat/assets/icons/read-aloud.svg +0 -22
  106. package/stat/assets/icons/review.svg +0 -22
  107. package/stat/assets/icons/thumbnails.svg +0 -17
  108. package/stat/assets/icons/voice.svg +0 -1
  109. package/stat/assets/icons/volume-full.svg +0 -22
  110. package/stat/assets/images/BRicons.png +0 -0
  111. package/stat/assets/images/BRicons.svg +0 -94
  112. package/stat/assets/images/BRicons_ia.png +0 -0
  113. package/stat/assets/images/back_pages.png +0 -0
  114. package/stat/assets/images/book_bottom_icon.png +0 -0
  115. package/stat/assets/images/book_down_icon.png +0 -0
  116. package/stat/assets/images/book_left_icon.png +0 -0
  117. package/stat/assets/images/book_leftmost_icon.png +0 -0
  118. package/stat/assets/images/book_right_icon.png +0 -0
  119. package/stat/assets/images/book_rightmost_icon.png +0 -0
  120. package/stat/assets/images/book_top_icon.png +0 -0
  121. package/stat/assets/images/book_up_icon.png +0 -0
  122. package/stat/assets/images/books_graphic.svg +0 -177
  123. package/stat/assets/images/booksplit.png +0 -0
  124. package/stat/assets/images/control_pause_icon.png +0 -0
  125. package/stat/assets/images/control_play_icon.png +0 -0
  126. package/stat/assets/images/embed_icon.png +0 -0
  127. package/stat/assets/images/icon-home-ia.png +0 -0
  128. package/stat/assets/images/icon_OL-logo-xs.png +0 -0
  129. package/stat/assets/images/icon_alert-xs.png +0 -0
  130. package/stat/assets/images/icon_book.svg +0 -12
  131. package/stat/assets/images/icon_bookmark.svg +0 -12
  132. package/stat/assets/images/icon_close-pop.png +0 -0
  133. package/stat/assets/images/icon_download.png +0 -0
  134. package/stat/assets/images/icon_gear.svg +0 -14
  135. package/stat/assets/images/icon_hamburger.svg +0 -20
  136. package/stat/assets/images/icon_home.png +0 -0
  137. package/stat/assets/images/icon_home.svg +0 -21
  138. package/stat/assets/images/icon_home_ia.png +0 -0
  139. package/stat/assets/images/icon_indicator.png +0 -0
  140. package/stat/assets/images/icon_info.svg +0 -11
  141. package/stat/assets/images/icon_one_page.svg +0 -8
  142. package/stat/assets/images/icon_pause.svg +0 -1
  143. package/stat/assets/images/icon_play.svg +0 -1
  144. package/stat/assets/images/icon_playback-rate.svg +0 -15
  145. package/stat/assets/images/icon_return.png +0 -0
  146. package/stat/assets/images/icon_search_button.svg +0 -8
  147. package/stat/assets/images/icon_share.svg +0 -9
  148. package/stat/assets/images/icon_skip-ahead.svg +0 -6
  149. package/stat/assets/images/icon_skip-back.svg +0 -13
  150. package/stat/assets/images/icon_speaker.svg +0 -18
  151. package/stat/assets/images/icon_speaker_open.svg +0 -10
  152. package/stat/assets/images/icon_thumbnails.svg +0 -12
  153. package/stat/assets/images/icon_toc.svg +0 -5
  154. package/stat/assets/images/icon_two_pages.svg +0 -9
  155. package/stat/assets/images/icon_zoomer.png +0 -0
  156. package/stat/assets/images/loading.gif +0 -0
  157. package/stat/assets/images/logo_icon.png +0 -0
  158. package/stat/assets/images/marker_chap-off.png +0 -0
  159. package/stat/assets/images/marker_chap-off.svg +0 -11
  160. package/stat/assets/images/marker_chap-off_ia.png +0 -0
  161. package/stat/assets/images/marker_chap-on.png +0 -0
  162. package/stat/assets/images/marker_chap-on.svg +0 -11
  163. package/stat/assets/images/marker_srch-on.svg +0 -11
  164. package/stat/assets/images/marker_srchchap-off.png +0 -0
  165. package/stat/assets/images/marker_srchchap-on.png +0 -0
  166. package/stat/assets/images/nav_control-dn.png +0 -0
  167. package/stat/assets/images/nav_control-dn_ia.png +0 -0
  168. package/stat/assets/images/nav_control-up.png +0 -0
  169. package/stat/assets/images/nav_control-up_ia.png +0 -0
  170. package/stat/assets/images/nav_control.png +0 -0
  171. package/stat/assets/images/one_page_mode_icon.png +0 -0
  172. package/stat/assets/images/paper-badge.png +0 -0
  173. package/stat/assets/images/print_icon.png +0 -0
  174. package/stat/assets/images/progressbar.gif +0 -0
  175. package/stat/assets/images/right_edges.png +0 -0
  176. package/stat/assets/images/slider.png +0 -0
  177. package/stat/assets/images/slider_ia.png +0 -0
  178. package/stat/assets/images/thumbnail_mode_icon.png +0 -0
  179. package/stat/assets/images/transparent.png +0 -0
  180. package/stat/assets/images/two_page_mode_icon.png +0 -0
  181. package/stat/assets/images/zoom_in_icon.png +0 -0
  182. package/stat/assets/images/zoom_out_icon.png +0 -0
  183. package/stat/css/BookReader.scss +0 -89
  184. package/stat/css/_BRBookmarks.scss +0 -29
  185. package/stat/css/_BRComponent.scss +0 -13
  186. package/stat/css/_BRfloat.scss +0 -197
  187. package/stat/css/_BRicon.scss +0 -48
  188. package/stat/css/_BRmain.scss +0 -251
  189. package/stat/css/_BRnav.scss +0 -359
  190. package/stat/css/_BRpages.scss +0 -139
  191. package/stat/css/_BRsearch.scss +0 -226
  192. package/stat/css/_BRtoolbar.scss +0 -84
  193. package/stat/css/_BRvendor.scss +0 -5
  194. package/stat/css/_MobileNav.scss +0 -194
  195. package/stat/css/_TextSelection.scss +0 -32
  196. package/stat/css/_colorbox.scss +0 -52
  197. package/stat/css/_controls.scss +0 -253
  198. package/stat/css/_icons.scss +0 -121
  199. package/stat/jquery-wrapper.js +0 -4
  200. package/stat/plugins/plugin.archive_analytics.js +0 -86
  201. package/stat/plugins/plugin.autoplay.js +0 -129
  202. package/stat/plugins/plugin.chapters.js +0 -248
  203. package/stat/plugins/plugin.iframe.js +0 -48
  204. package/stat/plugins/plugin.mobile_nav.js +0 -288
  205. package/stat/plugins/plugin.resume.js +0 -68
  206. package/stat/plugins/plugin.text_selection.js +0 -291
  207. package/stat/plugins/plugin.url.js +0 -198
  208. package/stat/plugins/plugin.vendor-fullscreen.js +0 -247
  209. package/stat/plugins/search/plugin.search.js +0 -439
  210. package/stat/plugins/search/view.js +0 -439
  211. package/stat/plugins/tts/AbstractTTSEngine.js +0 -249
  212. package/stat/plugins/tts/FestivalTTSEngine.js +0 -169
  213. package/stat/plugins/tts/PageChunk.js +0 -107
  214. package/stat/plugins/tts/PageChunkIterator.js +0 -163
  215. package/stat/plugins/tts/WebTTSEngine.js +0 -357
  216. package/stat/plugins/tts/plugin.tts.js +0 -357
  217. package/stat/plugins/tts/tooltip_dict.js +0 -15
  218. package/stat/plugins/tts/utils.js +0 -91
  219. package/stat/util/browserSniffing.js +0 -30
  220. package/stat/util/debouncer.js +0 -26
  221. package/stat/util/docCookies.js +0 -67
  222. package/stat/util/strings.js +0 -34
@@ -1,2550 +0,0 @@
1
- /*
2
- Copyright(c)2008-2019 Internet Archive. Software license AGPL version 3.
3
-
4
- This file is part of BookReader.
5
-
6
- BookReader is free software: you can redistribute it and/or modify
7
- it under the terms of the GNU Affero General Public License as published by
8
- the Free Software Foundation, either version 3 of the License, or
9
- (at your option) any later version.
10
-
11
- BookReader is distributed in the hope that it will be useful,
12
- but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- GNU Affero General Public License for more details.
15
-
16
- You should have received a copy of the GNU Affero General Public License
17
- along with BookReader. If not, see <http://www.gnu.org/licenses/>.
18
-
19
- The BookReader source is hosted at http://github.com/internetarchive/bookreader/
20
-
21
- */
22
- // effect.js gives acces to extra easing function (e.g. easeInOutExpo)
23
- import 'jquery-ui/ui/effect.js';
24
-
25
- // Needed by touch-punch
26
- import 'jquery-ui/ui/widget.js';
27
- import 'jquery-ui/ui/widgets/mouse.js';
28
- import 'jquery-ui-touch-punch';
29
-
30
- import PACKAGE_JSON from '../package.json';
31
- import * as utils from './BookReader/utils.js';
32
- import { exposeOverrideable } from './BookReader/utils/classes.js';
33
- import { Navbar, getNavPageNumHtml } from './BookReader/Navbar/Navbar.js';
34
- import { DEFAULT_OPTIONS } from './BookReader/options.js';
35
- /** @typedef {import('./BookReader/options.js').BookReaderOptions} BookReaderOptions */
36
- /** @typedef {import('./BookReader/options.js').ReductionFactor} ReductionFactor */
37
- /** @typedef {import('./BookReader/BookModel.js').PageIndex} PageIndex */
38
- import { EVENTS } from './BookReader/events.js';
39
- import { DebugConsole } from './BookReader/DebugConsole.js';
40
- import {
41
- Toolbar,
42
- blankInfoDiv,
43
- blankShareDiv,
44
- createPopup,
45
- } from './BookReader/Toolbar/Toolbar.js';
46
- import { BookModel } from './BookReader/BookModel.js';
47
- import { Mode1Up } from './BookReader/Mode1Up.js';
48
- import { Mode2Up } from './BookReader/Mode2Up.js';
49
- import { ModeThumb } from './BookReader/ModeThumb';
50
- import { ImageCache } from './BookReader/ImageCache.js';
51
- import { PageContainer } from './BookReader/PageContainer.js';
52
- import { NAMED_REDUCE_SETS } from './BookReader/ReduceSet';
53
-
54
- if (location.toString().indexOf('_debugShowConsole=true') != -1) {
55
- $(() => new DebugConsole().init());
56
- }
57
-
58
- /**
59
- * BookReader
60
- * @param {BookReaderOptions} options
61
- * TODO document all options properties
62
- * @constructor
63
- */
64
- export default function BookReader(overrides = {}) {
65
- const options = jQuery.extend(true, {}, BookReader.defaultOptions, overrides, BookReader.optionOverrides);
66
- this.setup(options);
67
- }
68
-
69
- BookReader.version = PACKAGE_JSON.version;
70
-
71
- // Mode constants
72
- /** 1 page view */
73
- BookReader.constMode1up = 1;
74
- /** 2 pages view */
75
- BookReader.constMode2up = 2;
76
- /** thumbnails view */
77
- BookReader.constModeThumb = 3;
78
- /** image cache */
79
- BookReader.imageCache = null;
80
-
81
- // Animation constants
82
- BookReader.constNavAnimationDuration = 300;
83
- BookReader.constResizeAnimationDuration = 100;
84
-
85
- // Names of events that can be triggered via BookReader.prototype.trigger()
86
- BookReader.eventNames = EVENTS;
87
-
88
- BookReader.defaultOptions = DEFAULT_OPTIONS;
89
-
90
- /**
91
- * @type {BookReaderOptions}
92
- * This is here, just in case you need to absolutely override an option.
93
- */
94
- BookReader.optionOverrides = {};
95
-
96
- /**
97
- * Setup
98
- * It is separate from the constructor, so plugins can extend.
99
- * @param {BookReaderOptions} options
100
- */
101
- BookReader.prototype.setup = function(options) {
102
- // Store the options used to setup bookreader
103
- this.options = options;
104
-
105
- /** @type {number} @deprecated some past iterations set this */
106
- this.numLeafs = undefined;
107
-
108
- /** Overridden by plugin.search.js */
109
- this.enableSearch = false;
110
-
111
- /**
112
- * Store viewModeOrder states
113
- * @var {boolean}
114
- */
115
- this.viewModeOrder = [];
116
-
117
- /**
118
- * Used to suppress fragment change for init with canonical URLs
119
- * @var {boolean}
120
- */
121
- this.suppressFragmentChange = false;
122
-
123
- /** @type {function(): void} */
124
- this.animationFinishedCallback = null;
125
-
126
- // @deprecated: Instance constants. Use Class constants instead
127
- /** 1 page view */
128
- this.constMode1up = BookReader.constMode1up;
129
- /** 2 pages view */
130
- this.constMode2up = BookReader.constMode2up;
131
- /** thumbnails view */
132
- this.constModeThumb = BookReader.constModeThumb;
133
-
134
- // Private properties below. Configuration should be done with options.
135
- /** @type {number} TODO: Make private */
136
- this.reduce = 8; /* start very small */
137
- this.defaults = options.defaults;
138
- this.padding = options.padding;
139
-
140
- this.reduceSet = NAMED_REDUCE_SETS[options.reduceSet];
141
- if (!this.reduceSet) {
142
- console.warn(`Invalid reduceSet ${options.reduceSet}. Ignoring.`);
143
- this.reduceSet = NAMED_REDUCE_SETS[DEFAULT_OPTIONS.reduceSet];
144
- }
145
-
146
- /** @type {number}
147
- * can be 1 or 2 or 3 based on the display mode const value
148
- */
149
- this.mode = null;
150
- this.prevReadMode = null;
151
- this.ui = options.ui;
152
- this.uiAutoHide = options.uiAutoHide;
153
-
154
- this.thumbWidth = 100; // will be overridden during prepareThumbnailView
155
- this.thumbRowBuffer = options.thumbRowBuffer;
156
- this.thumbColumns = options.thumbColumns;
157
- this.thumbMaxLoading = options.thumbMaxLoading;
158
- this.thumbPadding = options.thumbPadding;
159
- this.displayedRows = [];
160
-
161
- this.displayedIndices = [];
162
- /** @deprecated Unused; will be remove in v5 */
163
- this.imgs = {};
164
- /** @deprecated No longer used; will be remove in v5 */
165
- this.prefetchedImgs = {}; //an object with numeric keys corresponding to page index, reduce
166
-
167
- this.animating = false;
168
- this.flipSpeed = options.flipSpeed;
169
- this.flipDelay = options.flipDelay;
170
- this.twoPagePopUp = null;
171
- this.leafEdgeTmp = null;
172
-
173
- /**
174
- * Represents the first displayed index
175
- * In 2up mode it will be the left page
176
- * In 1 up mode it is the highest page
177
- * @property {number|null} firstIndex
178
- */
179
- this.firstIndex = null;
180
- this.lastDisplayableIndex2up = null;
181
- this.isFullscreenActive = false;
182
- this.lastScroll = null;
183
-
184
- this.showLogo = options.showLogo;
185
- this.logoURL = options.logoURL;
186
- this.imagesBaseURL = options.imagesBaseURL;
187
-
188
- this.reductionFactors = options.reductionFactors;
189
- this.onePage = options.onePage;
190
- /** @type {import('./BookReader/Mode2Up').TwoPageState} */
191
- this.twoPage = options.twoPage;
192
- this.onePageMinBreakpoint = options.onePageMinBreakpoint;
193
-
194
- this.bookTitle = options.bookTitle;
195
- this.bookUrl = options.bookUrl;
196
- this.bookUrlText = options.bookUrlText;
197
- this.bookUrlTitle = options.bookUrlTitle;
198
- this.titleLeaf = options.titleLeaf;
199
-
200
- this.metadata = options.metadata;
201
- this.thumbnail = options.thumbnail;
202
- this.bookUrlMoreInfo = options.bookUrlMoreInfo;
203
-
204
- this.enableExperimentalControls = options.enableExperimentalControls;
205
- this.el = options.el;
206
-
207
- this.pageProgression = options.pageProgression;
208
- this.protected = options.protected;
209
- this.getEmbedCode = options.getEmbedCode;
210
- this.popup = null;
211
-
212
- // Assign the data methods
213
- this.data = options.data;
214
- if (options.getNumLeafs) BookReader.prototype.getNumLeafs = options.getNumLeafs;
215
- if (options.getPageWidth) BookReader.prototype.getPageWidth = options.getPageWidth;
216
- if (options.getPageHeight) BookReader.prototype.getPageHeight = options.getPageHeight;
217
- if (options.getPageURI) BookReader.prototype.getPageURI = options.getPageURI;
218
- if (options.getPageSide) BookReader.prototype.getPageSide = options.getPageSide;
219
- if (options.getPageNum) BookReader.prototype.getPageNum = options.getPageNum;
220
- if (options.getPageProp) BookReader.prototype.getPageProp = options.getPageProp;
221
- if (options.getSpreadIndices) BookReader.prototype.getSpreadIndices = options.getSpreadIndices;
222
- if (options.leafNumToIndex) BookReader.prototype.leafNumToIndex = options.leafNumToIndex;
223
-
224
- /** @type {{[name: string]: JQuery}} */
225
- this.refs = {};
226
-
227
- /**
228
- * @private (for now) Models are largely state storing classes. This might be too much
229
- * granularity, but time will tell!
230
- */
231
- this._models = {
232
- book: new BookModel(this),
233
- };
234
-
235
- /**
236
- * @private Components are 'subchunks' of bookreader functionality, usually UI related
237
- * They should be relatively decoupled from each other/bookreader.
238
- * Note there are no hooks right now; components just provide methods that bookreader
239
- * calls at the correct moments.
240
- **/
241
- this._components = {
242
- navbar: new Navbar(this),
243
- toolbar: new Toolbar(this),
244
- };
245
-
246
- this._modes = {
247
- mode1Up: new Mode1Up(this, this._models.book),
248
- mode2Up: new Mode2Up(this, this._models.book),
249
- modeThumb: new ModeThumb(this, this._models.book),
250
- };
251
-
252
- /** Stores classes which we want to expose (selectively) some methods as overridable */
253
- this._overrideable = {
254
- '_models.book': this._models.book,
255
- '_components.navbar': this._components.navbar,
256
- '_components.toolbar': this._components.toolbar,
257
- '_modes.mode1Up': this._modes.mode1Up,
258
- '_modes.mode2Up': this._modes.mode2Up,
259
- '_modes.modeThumb': this._modes.modeThumb,
260
- };
261
-
262
- /** Image cache for general image fetching */
263
- this.imageCache = new ImageCache(this._models.book, {
264
- useSrcSet: this.options.useSrcSet,
265
- reduceSet: this.reduceSet,
266
- });
267
- };
268
-
269
- /**
270
- * Get all the HTML Elements that are being/can be rendered.
271
- * Includes cached elements which might be rendered again.
272
- */
273
- BookReader.prototype.getActivePageContainerElements = function() {
274
- let containerEls = Object.values(this._modes.mode2Up.pageContainers).map(pc => pc.$container[0])
275
- .concat(Object.values(this._modes.mode1Up.mode1UpLit.pageContainerCache).map(pc => pc.$container[0]));
276
- if (this.mode == this.constModeThumb) {
277
- containerEls = containerEls.concat(this.$('.BRpagecontainer').toArray());
278
- }
279
- return containerEls;
280
- };
281
-
282
- /**
283
- * Get the HTML Elements for the rendered page. Note there can be more than one, since
284
- * (at least as of writing) different modes can maintain different caches.
285
- * @param {PageIndex} pageIndex
286
- */
287
- BookReader.prototype.getActivePageContainerElementsForIndex = function(pageIndex) {
288
- return [
289
- this._modes.mode2Up.pageContainers[pageIndex]?.$container?.[0],
290
- this._modes.mode1Up.mode1UpLit.pageContainerCache[pageIndex]?.$container?.[0],
291
- ...(this.mode == this.constModeThumb ? this.$(`.pagediv${pageIndex}`).toArray() : [])
292
- ].filter(x => x);
293
- };
294
-
295
- Object.defineProperty(BookReader.prototype, 'activeMode', {
296
- /** @return {Mode1Up | Mode2Up | ModeThumb} */
297
- get() { return {
298
- 1: this._modes.mode1Up,
299
- 2: this._modes.mode2Up,
300
- 3: this._modes.modeThumb,
301
- }[this.mode]; },
302
- });
303
-
304
- /** @deprecated unused outside Mode2Up */
305
- Object.defineProperty(BookReader.prototype, 'leafEdgeL', {
306
- get() { return this._modes.mode2Up.leafEdgeL; },
307
- set(newVal) { this._modes.mode2Up.leafEdgeL = newVal; }
308
- });
309
- /** @deprecated unused outside Mode2Up */
310
- Object.defineProperty(BookReader.prototype, 'leafEdgeR', {
311
- get() { return this._modes.mode2Up.leafEdgeR; },
312
- set(newVal) { this._modes.mode2Up.leafEdgeR = newVal; }
313
- });
314
-
315
- /**
316
- * BookReader.util are static library functions
317
- * At top of file so they can be used below
318
- */
319
- BookReader.util = utils;
320
-
321
- /**
322
- * Helper to merge in params in to a params object.
323
- * It normalizes "page" into the "index" field to disambiguate and prevent concflicts
324
- * @private
325
- */
326
- BookReader.prototype.extendParams = function(params, newParams) {
327
- var modifiedNewParams = $.extend({}, newParams);
328
- if ('undefined' != typeof(modifiedNewParams.page)) {
329
- var pageIndex = this._models.book.parsePageString(modifiedNewParams.page);
330
- if (!isNaN(pageIndex))
331
- modifiedNewParams.index = pageIndex;
332
- delete modifiedNewParams.page;
333
- }
334
- $.extend(params, modifiedNewParams);
335
- };
336
-
337
- /**
338
- * Parses params from from various initialization contexts (url, cookie, options)
339
- * @private
340
- * @return {object} the parsed params
341
- */
342
- BookReader.prototype.initParams = function() {
343
- var params = {};
344
- // Flag initializing for updateFromParams()
345
- params.init = true;
346
-
347
- // Flag if page given in defaults or URL (not cookie)
348
- // Used for overriding goToFirstResult in plugin.search.js
349
- // Note: extendParams() converts params.page to index and gets rid of page
350
- // so check and set before extendParams()
351
- params.pageFound = false;
352
-
353
- // True if changing the URL
354
- params.fragmentChange = false;
355
-
356
- // This is ordered from lowest to highest priority
357
-
358
- // If we have a title leaf, use that as the default instead of index 0,
359
- // but only use as default if book has a few pages
360
- if ('undefined' != typeof(this.titleLeaf) && this._models.book.getNumLeafs() > 2) {
361
- params.index = this._models.book.leafNumToIndex(this.titleLeaf);
362
- } else {
363
- params.index = 0;
364
- }
365
-
366
- // this.defaults is a string passed in the url format. eg "page/1/mode/1up"
367
- if (this.defaults) {
368
- const defaultParams = this.paramsFromFragment(this.defaults);
369
- if ('undefined' != typeof(defaultParams.page)) {
370
- params.pageFound = true;
371
- }
372
- this.extendParams(params, defaultParams);
373
- }
374
-
375
- // Check for Resume plugin
376
- if (this.options.enablePageResume) {
377
- // Check cookies
378
- const val = this.getResumeValue();
379
- if (val !== null) {
380
- // If page index different from default
381
- if (params.index !== val) {
382
- // Show in URL
383
- params.fragmentChange = true;
384
- }
385
- params.index = val;
386
- }
387
- }
388
-
389
- // Check for URL plugin
390
- if (this.options.enableUrlPlugin) {
391
- // Params explicitly set in URL take precedence over all other methods
392
- var urlParams = this.paramsFromFragment(this.urlReadFragment());
393
-
394
- // Get params if hash fragment available with 'history' urlMode
395
- const hasHashURL = !Object.keys(urlParams).length && this.urlReadHashFragment();
396
- if (hasHashURL && (this.options.urlMode === 'history')) {
397
- urlParams = this.paramsFromFragment(this.urlReadHashFragment());
398
- }
399
-
400
- // If there were any parameters
401
- if (Object.keys(urlParams).length) {
402
- if ('undefined' != typeof(urlParams.page)) {
403
- params.pageFound = true;
404
- }
405
- this.extendParams(params, urlParams);
406
- // Show in URL
407
- params.fragmentChange = true;
408
- }
409
- }
410
-
411
- // Check for Search plugin
412
- if (this.options.enableSearch) {
413
- // Go to first result only if no default or URL page
414
- this.options.goToFirstResult = !params.pageFound;
415
-
416
- // If initialSearchTerm not set
417
- if (!this.options.initialSearchTerm) {
418
- // Look for any term in URL
419
- if (params.search) {
420
- // Old style: /search/[term]
421
- this.options.initialSearchTerm = params.search;
422
- this.searchTerm = params.search;
423
- } else {
424
- // If we have a query string: q=[term]
425
- const searchParams = new URLSearchParams(this.readQueryString());
426
- const searchTerm = searchParams.get('q');
427
- if (searchTerm) {
428
- this.options.initialSearchTerm = utils.decodeURIComponentPlus(searchTerm);
429
- }
430
- }
431
- }
432
- }
433
-
434
- // Set for init process, return to false at end of init()
435
- this.suppressFragmentChange = !params.fragmentChange;
436
-
437
- return params;
438
- };
439
-
440
- /**
441
- * Allow mocking of window.location.search
442
- */
443
- BookReader.prototype.getLocationSearch = function () {
444
- return window.location.search;
445
- };
446
-
447
- /**
448
- * Allow mocking of window.location.hash
449
- */
450
- BookReader.prototype.getLocationHash = function () {
451
- return window.location.hash;
452
- };
453
-
454
- /**
455
- * Return URL or fragment querystring
456
- */
457
- BookReader.prototype.readQueryString = function() {
458
- const queryString = this.getLocationSearch();
459
- if (queryString) {
460
- return queryString;
461
- }
462
- const hash = this.getLocationHash();
463
- const found = hash.search(/\?\w+=/);
464
- return found > -1 ? hash.slice(found) : '';
465
- };
466
-
467
- /**
468
- * Determines the initial mode for starting if a mode is not already
469
- * present in the params argument
470
- * @param {object} params
471
- * @return {number} the mode
472
- */
473
- BookReader.prototype.getInitialMode = function(params) {
474
- // Use params or browser width to set view mode
475
- var windowWidth = $(window).width();
476
- var nextMode;
477
- if ('undefined' != typeof(params.mode)) {
478
- nextMode = params.mode;
479
- } else if (this.ui == 'full'
480
- && this.enableMobileNav
481
- && this.isFullscreenActive
482
- && windowWidth <= this.onePageMinBreakpoint
483
- ) {
484
- // In full mode, we set the default based on width
485
- nextMode = this.constMode1up;
486
- } else {
487
- nextMode = this.constMode2up;
488
- }
489
-
490
- if (!this.canSwitchToMode(nextMode)) {
491
- nextMode = this.constMode1up;
492
- }
493
- return nextMode;
494
- };
495
-
496
- /**
497
- * This is called by the client to initialize BookReader.
498
- * It renders onto the DOM. It should only be called once.
499
- */
500
- BookReader.prototype.init = function() {
501
- this.init.initComplete = false;
502
- this.pageScale = this.reduce; // preserve current reduce
503
-
504
- var params = this.initParams();
505
-
506
- this.firstIndex = params.index ? params.index : 0;
507
-
508
- // Setup Navbars and other UI
509
- this.isTouchDevice = !!('ontouchstart' in window) || !!('msmaxtouchpoints' in window.navigator);
510
-
511
- this.refs.$br = $(this.el)
512
- .empty()
513
- .removeClass()
514
- .addClass("ui-" + this.ui)
515
- .addClass("br-ui-" + this.ui)
516
- .addClass('BookReader');
517
-
518
- // Add a class if this is a touch enabled device
519
- if (this.isTouchDevice) {
520
- this.refs.$br.addClass("touch");
521
- } else {
522
- this.refs.$br.addClass("no-touch");
523
- }
524
-
525
- this.refs.$brContainer = $("<div class='BRcontainer' dir='ltr'></div>");
526
- this.refs.$br.append(this.refs.$brContainer);
527
-
528
- // Explicitly ensure params.mode exists for updateFromParams() below
529
- params.mode = this.getInitialMode(params);
530
-
531
- // Explicitly ensure this.mode exists for initNavbar() below
532
- this.mode = params.mode;
533
-
534
- // Display Navigation
535
- if (this.options.showToolbar) {
536
- this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
537
- }
538
- if (this.options.showNavbar) { // default navigation
539
- this.initNavbar();
540
- }
541
-
542
- // Switch navbar controls on mobile/desktop
543
- this.switchNavbarControls();
544
-
545
- this.resizeBRcontainer();
546
- this.updateFromParams(params);
547
- this.initUIStrings();
548
-
549
- // Bind to events
550
-
551
- this.bindNavigationHandlers();
552
- this.setupKeyListeners();
553
-
554
- this.lastScroll = (new Date().getTime());
555
- this.refs.$brContainer.on('scroll', this, function(e) {
556
- // Note, this scroll event fires for both user, and js generated calls
557
- // It is functioning in some cases as the primary triggerer for rendering
558
- e.data.lastScroll = (new Date().getTime());
559
- if (e.data.constModeThumb == e.data.mode) {
560
- e.data.drawLeafsThrottled();
561
- }
562
- });
563
-
564
- if (this.options.autoResize) {
565
- $(window).on('resize', this, function(e) {
566
- e.data.resize();
567
- });
568
- $(window).on("orientationchange", this, function(e) {
569
- e.data.resize();
570
- }.bind(this));
571
- }
572
-
573
- if (this.protected) {
574
- this.$('.BRicon.share').hide();
575
- }
576
-
577
- // If not searching, set to allow on-going fragment changes
578
- if (!this.options.initialSearchTerm) {
579
- this.suppressFragmentChange = false;
580
- }
581
-
582
- this.init.initComplete = true;
583
- this.trigger(BookReader.eventNames.PostInit);
584
-
585
- // Must be called after this.init.initComplete set to true to allow
586
- // BookReader.prototype.resize to run.
587
- if (this.options.startFullscreen) {
588
- this.toggleFullscreen();
589
- }
590
- };
591
-
592
- /**
593
- * @param {EVENTS} name
594
- * @param {array | object} [props]
595
- */
596
- BookReader.prototype.trigger = function(name, props = this) {
597
- const eventName = 'BookReader:' + name;
598
- $(document).trigger(eventName, props);
599
-
600
- utils.polyfillCustomEvent(window);
601
- window.dispatchEvent(new CustomEvent(eventName, {
602
- bubbles: true,
603
- composed: true,
604
- detail: { props },
605
- }));
606
- };
607
-
608
- BookReader.prototype.bind = function(name, callback) {
609
- $(document).on('BookReader:' + name, callback);
610
- };
611
-
612
- BookReader.prototype.unbind = function(name, callback) {
613
- $(document).off('BookReader:' + name, callback);
614
- };
615
-
616
- /**
617
- * Resizes based on the container width and height
618
- */
619
- BookReader.prototype.resize = function() {
620
- if (!this.init.initComplete) return;
621
-
622
- this.resizeBRcontainer();
623
-
624
- // Switch navbar controls on mobile/desktop
625
- this.switchNavbarControls();
626
-
627
- if (this.constMode1up == this.mode) {
628
- if (this.onePage.autofit != 'none') {
629
- this.resizePageView1up();
630
- this.centerPageView();
631
- } else {
632
- this.centerPageView();
633
- this.displayedIndices = [];
634
- this.drawLeafsThrottled();
635
- }
636
- } else if (this.constModeThumb == this.mode) {
637
- this.prepareThumbnailView();
638
- } else {
639
- // We only need to prepare again in autofit (size of spread changes)
640
- if (this.twoPage.autofit) {
641
- // most common path, esp. for archive.org books
642
- this.prepareTwoPageView();
643
- } else {
644
- // used when zoomed in
645
- // Re-center if the scrollbars have disappeared
646
- var center = this.twoPageGetViewCenter();
647
- var doRecenter = false;
648
- if (this.twoPage.totalWidth < this.refs.$brContainer.prop('clientWidth')) {
649
- center.percentageX = 0.5;
650
- doRecenter = true;
651
- }
652
- if (this.twoPage.totalHeight < this.refs.$brContainer.prop('clientHeight')) {
653
- center.percentageY = 0.5;
654
- doRecenter = true;
655
- }
656
- if (doRecenter) {
657
- this.twoPageCenterView(center.percentageX, center.percentageY);
658
- }
659
- }
660
- }
661
- this.trigger(BookReader.eventNames.resize);
662
- };
663
-
664
- /**
665
- * Binds keyboard event listeners
666
- */
667
- BookReader.prototype.setupKeyListeners = function() {
668
- var self = this;
669
-
670
- var KEY_PGUP = 33;
671
- var KEY_PGDOWN = 34;
672
- var KEY_END = 35;
673
- var KEY_HOME = 36;
674
-
675
- var KEY_LEFT = 37;
676
- var KEY_UP = 38;
677
- var KEY_RIGHT = 39;
678
- var KEY_DOWN = 40;
679
- // The minus(-) and equal(=) keys have different mappings for different browsers
680
- var KEY_MINUS = 189; // Chrome
681
- var KEY_MINUS_F = 173; // Firefox
682
- var KEY_NUMPAD_SUBTRACT = 109;
683
- var KEY_EQUAL = 187; // Chrome
684
- var KEY_EQUAL_F = 61; // Firefox
685
- var KEY_NUMPAD_ADD = 107;
686
-
687
- // We use document here instead of window to avoid a bug in jQuery on IE7
688
- $(document).on("keydown", function(e) {
689
-
690
- // Keyboard navigation
691
- switch (e.keyCode) {
692
- case KEY_PGUP:
693
- case KEY_UP:
694
- // In 1up mode page scrolling is handled by browser
695
- if (!utils.isInputActive() && self.constMode2up == self.mode) {
696
- e.preventDefault();
697
- self.prev();
698
- }
699
- break;
700
- case KEY_DOWN:
701
- case KEY_PGDOWN:
702
- if (!utils.isInputActive() && self.constMode2up == self.mode) {
703
- e.preventDefault();
704
- self.next();
705
- }
706
- break;
707
- case KEY_END:
708
- if (!utils.isInputActive()) {
709
- e.preventDefault();
710
- self.last();
711
- }
712
- break;
713
- case KEY_HOME:
714
- if (!utils.isInputActive()) {
715
- e.preventDefault();
716
- self.first();
717
- }
718
- break;
719
- case KEY_LEFT:
720
- if (!utils.isInputActive() && self.constModeThumb != self.mode) {
721
- e.preventDefault();
722
- self.left();
723
- }
724
- break;
725
- case KEY_RIGHT:
726
- if (!utils.isInputActive() && self.constModeThumb != self.mode) {
727
- e.preventDefault();
728
- self.right();
729
- }
730
- break;
731
- case KEY_MINUS:
732
- case KEY_MINUS_F:
733
- case KEY_NUMPAD_SUBTRACT:
734
- if (!utils.isInputActive()) {
735
- e.preventDefault();
736
- self.zoom(-1);
737
- }
738
- break;
739
- case KEY_EQUAL:
740
- case KEY_EQUAL_F:
741
- case KEY_NUMPAD_ADD:
742
- if (!utils.isInputActive()) {
743
- e.preventDefault();
744
- self.zoom(+1);
745
- }
746
- break;
747
- }
748
- });
749
- };
750
-
751
- BookReader.prototype.drawLeafs = function() {
752
- if (this.constMode1up == this.mode) {
753
- // Not needed for Mode1Up anymore
754
- } else if (this.constModeThumb == this.mode) {
755
- this.drawLeafsThumbnail();
756
- } else {
757
- this.drawLeafsTwoPage();
758
- }
759
- };
760
-
761
- /**
762
- * @protected
763
- * @param {PageIndex} index
764
- */
765
- BookReader.prototype._createPageContainer = function(index) {
766
- return new PageContainer(this._models.book.getPage(index, false), {
767
- isProtected: this.protected,
768
- imageCache: this.imageCache,
769
- loadingImage: this.imagesBaseURL + 'loading.gif',
770
- });
771
- };
772
-
773
- BookReader.prototype.bindGestures = function(jElement) {
774
- // TODO support gesture change is only iOS. Support android.
775
- // HACK(2017-01-20) - Momentum scrolling is causing the scroll position
776
- // to jump after zooming in on mobile device. I am able to reproduce
777
- // when you move the book with one finger and then add another
778
- // finger to pinch. Gestures are aware of scroll state.
779
-
780
- var self = this;
781
- var numTouches = 1;
782
-
783
- jElement.unbind('touchmove').bind('touchmove', function(e) {
784
- if (e.originalEvent.cancelable) numTouches = e.originalEvent.touches.length;
785
- e.stopPropagation();
786
- });
787
-
788
- jElement.unbind('gesturechange').bind('gesturechange', function(e) {
789
- e.preventDefault();
790
- // These are two very important fixes to adjust for the scroll position
791
- // issues described below
792
- if (!(numTouches !== 2 || (new Date().getTime()) - self.lastScroll < 500)) {
793
- if (e.originalEvent.scale > 1.5) {
794
- self.zoom(1);
795
- } else if (e.originalEvent.scale < 0.6) {
796
- self.zoom(-1);
797
- }
798
- }
799
- });
800
- };
801
-
802
- /** @deprecated Not used outside ModeThumb */
803
- BookReader.prototype.drawLeafsThumbnail = ModeThumb.prototype.drawLeafs;
804
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'drawLeafs', 'drawLeafsThumbnail');
805
- /** @deprecated Not used outside ModeThumb */
806
- BookReader.prototype.lazyLoadThumbnails = ModeThumb.prototype.lazyLoadThumbnails;
807
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'lazyLoadThumbnails', 'lazyLoadThumbnails');
808
- BookReader.prototype.lazyLoadImage = ModeThumb.prototype.lazyLoadImage;
809
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'lazyLoadImage', 'lazyLoadImage');
810
- /** @deprecated Internal use only */
811
- BookReader.prototype.zoomThumb = ModeThumb.prototype.zoom;
812
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'zoom', 'zoomThumb');
813
- /** @deprecated Not used outside ModeThumb */
814
- BookReader.prototype.getThumbnailWidth = ModeThumb.prototype.getThumbnailWidth;
815
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'getThumbnailWidth', 'getThumbnailWidth');
816
- /** @deprecated Not used outside ModeThumb */
817
- BookReader.prototype.prepareThumbnailView = ModeThumb.prototype.prepare;
818
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'prepare', 'prepareThumbnailView');
819
-
820
- /**
821
- * A throttled version of drawLeafs
822
- */
823
- BookReader.prototype.drawLeafsThrottled = utils.throttle(
824
- BookReader.prototype.drawLeafs,
825
- 250 // 250 ms gives quick feedback, but doesn't eat cpu
826
- );
827
-
828
- /**
829
- * @param {number} direction Pass 1 to zoom in, anything else to zoom out
830
- */
831
- BookReader.prototype.zoom = function(direction) {
832
- if (direction == 1) {
833
- this.activeMode.zoom('in');
834
- } else {
835
- this.activeMode.zoom('out');
836
- }
837
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
838
- return;
839
- };
840
-
841
- /**
842
- * Resizes the inner container to fit within the visible space to prevent
843
- * the top toolbar and bottom navbar from clipping the visible book
844
- *
845
- * @param { boolean } animate - optional
846
- * When used, BookReader will fill the main container with the book's content.
847
- * This is primarily for 1up view - a follow up animation to the nav animation
848
- * So resize isn't perceived sharp/jerky
849
- */
850
- BookReader.prototype.resizeBRcontainer = function(animate) {
851
- if (animate) {
852
- this.refs.$brContainer.animate({
853
- top: this.getToolBarHeight(),
854
- bottom: this.getFooterHeight()
855
- }, this.constResizeAnimationDuration, 'linear');
856
- } else {
857
- this.refs.$brContainer.css({
858
- top: this.getToolBarHeight(),
859
- bottom: this.getFooterHeight()
860
- });
861
- }
862
- };
863
-
864
- BookReader.prototype.centerPageView = function() {
865
- var scrollWidth = this.refs.$brContainer.prop('scrollWidth');
866
- var clientWidth = this.refs.$brContainer.prop('clientWidth');
867
- if (scrollWidth > clientWidth) {
868
- this.refs.$brContainer.prop('scrollLeft', (scrollWidth - clientWidth) / 2);
869
- }
870
- };
871
-
872
- /**
873
- * Quantizes the given reduction factor to closest power of two from set from 12.5% to 200%
874
- * @param {number} reduce
875
- * @param {ReductionFactor[]} reductionFactors
876
- * @return {number}
877
- */
878
- BookReader.prototype.quantizeReduce = function(reduce, reductionFactors) {
879
- let quantized = reductionFactors[0].reduce;
880
- let distance = Math.abs(reduce - quantized);
881
-
882
- for (let i = 1; i < reductionFactors.length; i++) {
883
- const newDistance = Math.abs(reduce - reductionFactors[i].reduce);
884
- if (newDistance < distance) {
885
- distance = newDistance;
886
- quantized = reductionFactors[i].reduce;
887
- }
888
- }
889
- return quantized;
890
- };
891
-
892
- /**
893
- * @param {number} currentReduce
894
- * @param {'in' | 'out' | 'auto' | 'height' | 'width'} direction
895
- * @param {ReductionFactor[]} reductionFactors Must be sorted
896
- * @returns {ReductionFactor}
897
- */
898
- BookReader.prototype.nextReduce = function(currentReduce, direction, reductionFactors) {
899
- // XXX add 'closest', to replace quantize function
900
-
901
- if (direction === 'in') {
902
- let newReduceIndex = 0;
903
- for (let i = 1; i < reductionFactors.length; i++) {
904
- if (reductionFactors[i].reduce < currentReduce) {
905
- newReduceIndex = i;
906
- }
907
- }
908
- return reductionFactors[newReduceIndex];
909
- } else if (direction === 'out') {
910
- const lastIndex = reductionFactors.length - 1;
911
- let newReduceIndex = lastIndex;
912
-
913
- for (let i = lastIndex; i >= 0; i--) {
914
- if (reductionFactors[i].reduce > currentReduce) {
915
- newReduceIndex = i;
916
- }
917
- }
918
- return reductionFactors[newReduceIndex];
919
- } else if (direction === 'auto') {
920
- // If an 'auto' is specified, use that
921
- const autoMatch = reductionFactors.find(rf => rf.autofit == 'auto');
922
- if (autoMatch) return autoMatch;
923
-
924
- // Otherwise, choose the least reduction from height/width
925
- const candidates = reductionFactors.filter(({autofit}) => autofit == 'height' || autofit == 'width');
926
- let choice = null;
927
- for (let i = 0; i < candidates.length; i++) {
928
- if (choice === null || choice.reduce < candidates[i].reduce) {
929
- choice = candidates[i];
930
- }
931
- }
932
- if (choice) return choice;
933
- } else if (direction === 'height' || direction === 'width') {
934
- // Asked for specific autofit mode
935
- const match = reductionFactors.find(rf => rf.autofit == direction);
936
- if (match) return match;
937
- }
938
-
939
- return reductionFactors[0];
940
- };
941
-
942
- /**
943
- * @param {ReductionFactor} a
944
- * @param {ReductionFactor} b
945
- */
946
- BookReader.prototype._reduceSort = (a, b) => a.reduce - b.reduce;
947
-
948
- /**
949
- * Attempts to jump to page
950
- * @param {string}
951
- * @return {boolean} Returns true if page could be found, false otherwise.
952
- */
953
- BookReader.prototype.jumpToPage = function(pageNum) {
954
- var pageIndex = this._models.book.parsePageString(pageNum);
955
-
956
- if ('undefined' != typeof(pageIndex)) {
957
- this.jumpToIndex(pageIndex);
958
- return true;
959
- }
960
-
961
- // Page not found
962
- return false;
963
- };
964
-
965
- /**
966
- * Check whether the specified index is currently displayed
967
- * @param {PageIndex} index
968
- */
969
- BookReader.prototype._isIndexDisplayed = function(index) {
970
- // One up "caches" pages +- current, so exclude those in the test.
971
- return this.constMode1up == this.mode ? this.displayedIndices.slice(1, -1).includes(index) :
972
- this.displayedIndices ? this.displayedIndices.includes(index) :
973
- this.currentIndex() == index;
974
- };
975
-
976
- /**
977
- * Changes the current page
978
- * @param {PageIndex} index
979
- * @param {number} [pageX]
980
- * @param {number} [pageY]
981
- * @param {boolean} [noAnimate]
982
- */
983
- BookReader.prototype.jumpToIndex = function(index, pageX, pageY, noAnimate) {
984
- // Don't jump into specific unviewable page
985
- const page = this._models.book.getPage(index);
986
- if (!page.isViewable && page.unviewablesStart != page.index) {
987
- // If already in unviewable range, jump to end of that range
988
- const alreadyInPreview = this._isIndexDisplayed(page.unviewablesStart);
989
- const newIndex = alreadyInPreview ? page.findNext({ combineConsecutiveUnviewables: true }).index : page.unviewablesStart;
990
- return this.jumpToIndex(newIndex, pageX, pageY, noAnimate);
991
- }
992
-
993
- this.trigger(BookReader.eventNames.stop);
994
-
995
- if (this.constMode2up == this.mode) {
996
- this._modes.mode2Up.jumpToIndex(index);
997
- } else if (this.constModeThumb == this.mode) {
998
- this._modes.modeThumb.jumpToIndex(index);
999
- } else { // 1up
1000
- this._modes.mode1Up.jumpToIndex(index, pageX, pageY, noAnimate);
1001
- }
1002
- };
1003
-
1004
- /**
1005
- * Return mode or 1up if initial thumb
1006
- * @param {number}
1007
- * @see BookReader.prototype.drawLeafsThumbnail
1008
- */
1009
- BookReader.prototype.getPrevReadMode = function(mode) {
1010
- if (mode === BookReader.constMode1up || mode === BookReader.constMode2up) {
1011
- return mode;
1012
- } else if (this.prevReadMode === null) {
1013
- // Initial thumb, return 1up
1014
- return BookReader.constMode1up;
1015
- }
1016
- };
1017
-
1018
- /**
1019
- * Switches the mode (eg 1up 2up thumb)
1020
- * @param {number}
1021
- * @param {object} [options]
1022
- * @param {boolean} [options.suppressFragmentChange = false]
1023
- * @param {boolean} [options.onInit = false] - this
1024
- */
1025
- BookReader.prototype.switchMode = function(
1026
- mode,
1027
- {
1028
- suppressFragmentChange = false,
1029
- init = false,
1030
- pageFound = false
1031
- } = {}
1032
- ) {
1033
- // Skip checks before init() complete
1034
- if (this.init.initComplete) {
1035
- if (mode === this.mode) {
1036
- return;
1037
- }
1038
- if (!this.canSwitchToMode(mode)) {
1039
- return;
1040
- }
1041
- }
1042
-
1043
- this.trigger(BookReader.eventNames.stop);
1044
-
1045
- this.prevReadMode = this.getPrevReadMode(this.mode);
1046
-
1047
- if (this.mode != mode) {
1048
- this.activeMode.unprepare?.();
1049
- }
1050
-
1051
- this.mode = mode;
1052
-
1053
- // reinstate scale if moving from thumbnail view
1054
- if (this.pageScale !== this.reduce) {
1055
- this.reduce = this.pageScale;
1056
- }
1057
-
1058
- // $$$ TODO preserve center of view when switching between mode
1059
- // See https://bugs.edge.launchpad.net/gnubook/+bug/416682
1060
-
1061
- // XXX maybe better to preserve zoom in each mode
1062
- if (this.constMode1up == mode) {
1063
- this.prepareOnePageView();
1064
- } else if (this.constModeThumb == mode) {
1065
- this.reduce = this.quantizeReduce(this.reduce, this.reductionFactors);
1066
- this.prepareThumbnailView();
1067
- } else {
1068
- // $$$ why don't we save autofit?
1069
- // this.twoPage.autofit = null; // Take zoom level from other mode
1070
- // spread indices not set, so let's set them
1071
- if (init || !pageFound) {
1072
- this.setSpreadIndices();
1073
- }
1074
-
1075
- this.twoPageCalculateReductionFactors(); // this sets this.twoPage && this.reduce
1076
- this.prepareTwoPageView();
1077
- this.twoPageCenterView(0.5, 0.5); // $$$ TODO preserve center
1078
- }
1079
-
1080
- if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1081
- this.trigger(BookReader.eventNames.fragmentChange);
1082
- }
1083
- var eventName = mode + 'PageViewSelected';
1084
- this.trigger(BookReader.eventNames[eventName]);
1085
-
1086
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1087
- };
1088
-
1089
- BookReader.prototype.updateBrClasses = function() {
1090
- var modeToClass = {};
1091
- modeToClass[this.constMode1up] = 'BRmode1up';
1092
- modeToClass[this.constMode2up] = 'BRmode2Up';
1093
- modeToClass[this.constModeThumb] = 'BRmodeThumb';
1094
-
1095
- this.refs.$br
1096
- .removeClass('BRmode1up BRmode2Up BRmodeThumb')
1097
- .addClass(modeToClass[this.mode]);
1098
-
1099
- if (this.isFullscreen()) {
1100
- this.refs.$br.addClass('fullscreenActive');
1101
- $(document.body).addClass('BRfullscreenActive');
1102
- } else {
1103
- this.refs.$br.removeClass('fullscreenActive');
1104
- $(document.body).removeClass('BRfullscreenActive');
1105
- }
1106
- };
1107
-
1108
- BookReader.prototype.isFullscreen = function() {
1109
- return this.isFullscreenActive;
1110
- };
1111
-
1112
- /**
1113
- * Toggles fullscreen
1114
- * @param { boolean } bindKeyboardControls
1115
- */
1116
- BookReader.prototype.toggleFullscreen = async function(bindKeyboardControls = true) {
1117
- if (this.isFullscreen()) {
1118
- await this.exitFullScreen();
1119
- } else {
1120
- await this.enterFullscreen(bindKeyboardControls);
1121
- }
1122
- };
1123
-
1124
- /**
1125
- * Enters fullscreen
1126
- * including:
1127
- * - animation
1128
- * - binds keyboard controls
1129
- * - fires custom event
1130
- * @param { boolean } bindKeyboardControls
1131
- */
1132
- BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = true) {
1133
- const currentIndex = this.currentIndex();
1134
- this.refs.$brContainer.css('opacity', 0);
1135
-
1136
- if (bindKeyboardControls) {
1137
- this._fullscreenCloseHandler = (e) => {
1138
- if (e.keyCode === 27) this.toggleFullscreen();
1139
- };
1140
- $(document).on("keyup", this._fullscreenCloseHandler);
1141
- }
1142
-
1143
- const windowWidth = $(window).width();
1144
- if (windowWidth <= this.onePageMinBreakpoint) {
1145
- this.switchMode(this.constMode1up);
1146
- }
1147
-
1148
- this.isFullscreenActive = true;
1149
- this.animating = true;
1150
- await new Promise(res => this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', res));
1151
- this.resize();
1152
- if (this.activeMode instanceof Mode1Up) {
1153
- this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this._models.book.getPage(currentIndex));
1154
- // Need the new scale to be applied before calling jumpToIndex
1155
- await this.activeMode.mode1UpLit.requestUpdate();
1156
- }
1157
- this.jumpToIndex(currentIndex);
1158
- this.animating = false;
1159
-
1160
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1161
- // Add "?view=theater"
1162
- this.trigger(BookReader.eventNames.fragmentChange);
1163
- this.trigger(BookReader.eventNames.fullscreenToggled);
1164
- };
1165
-
1166
- /**
1167
- * Exits fullscreen
1168
- * - toggles fullscreen
1169
- * - binds keyboard controls
1170
- * - fires custom event
1171
- * @param { boolean } bindKeyboardControls
1172
- */
1173
- BookReader.prototype.exitFullScreen = async function () {
1174
- this.refs.$brContainer.css('opacity', 0);
1175
-
1176
- $(document).off('keyup', this._fullscreenCloseHandler);
1177
-
1178
- var windowWidth = $(window).width();
1179
-
1180
- const canShow2up = this.options.controls.twoPage.visible;
1181
- if (canShow2up && (windowWidth <= this.onePageMinBreakpoint)) {
1182
- this.switchMode(this.constMode2up);
1183
- }
1184
-
1185
- this.isFullscreenActive = false;
1186
- this.updateBrClasses();
1187
- this.animating = true;
1188
- await new Promise((res => this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', res)));
1189
- this.resize();
1190
-
1191
- if (this.activeMode instanceof Mode1Up) {
1192
- this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this._models.book.getPage(this.currentIndex()));
1193
- await this.activeMode.mode1UpLit.requestUpdate();
1194
- }
1195
-
1196
- this.animating = false;
1197
-
1198
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1199
- // Remove "?view=theater"
1200
- this.trigger(BookReader.eventNames.fragmentChange);
1201
- this.trigger(BookReader.eventNames.fullscreenToggled);
1202
- };
1203
-
1204
- /**
1205
- * Returns the currently active index
1206
- * @return {number}
1207
- * @throws
1208
- */
1209
- BookReader.prototype.currentIndex = function() {
1210
- // $$$ we should be cleaner with our idea of which index is active in 1up/2up
1211
- if (this.mode == this.constMode1up || this.mode == this.constModeThumb) {
1212
- return this.firstIndex; // $$$ TODO page in center of view would be better
1213
- } else if (this.mode == this.constMode2up) {
1214
- // Only allow indices that are actually present in book
1215
- return utils.clamp(this.firstIndex, 0, this._models.book.getNumLeafs() - 1);
1216
- } else {
1217
- throw 'currentIndex called for unimplemented mode ' + this.mode;
1218
- }
1219
- };
1220
-
1221
- /**
1222
- * Setter for this.firstIndex
1223
- * Also triggers an event and updates the navbar slider position
1224
- * @param {number} index
1225
- * @param {object} [options]
1226
- * @param {boolean} [options.suppressFragmentChange = false]
1227
- */
1228
- BookReader.prototype.updateFirstIndex = function(
1229
- index,
1230
- { suppressFragmentChange = false } = {}
1231
- ) {
1232
- // If there's no change, do nothing
1233
- if (this.firstIndex === index) return;
1234
-
1235
- this.firstIndex = index;
1236
- if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1237
- this.trigger(BookReader.eventNames.fragmentChange);
1238
- }
1239
- // If there's an initial search we stop suppressing global URL changes
1240
- // when local suppression ends
1241
- // This seems to correctly handle multiple calls during mode/1up
1242
- if (this.options.initialSearchTerm && !suppressFragmentChange) {
1243
- this.suppressFragmentChange = false;
1244
- }
1245
- this.trigger('pageChanged');
1246
- this.updateNavIndexThrottled(index);
1247
- };
1248
-
1249
- /**
1250
- * Flip the right page over onto the left
1251
- */
1252
- BookReader.prototype.right = function() {
1253
- if ('rl' != this.pageProgression) {
1254
- this.next();
1255
- } else {
1256
- this.prev();
1257
- }
1258
- };
1259
-
1260
- /**
1261
- * Flip to the rightmost page
1262
- */
1263
- BookReader.prototype.rightmost = function() {
1264
- if ('rl' != this.pageProgression) {
1265
- this.last();
1266
- } else {
1267
- this.first();
1268
- }
1269
- };
1270
-
1271
- /**
1272
- * Flip the left page over onto the right
1273
- */
1274
- BookReader.prototype.left = function() {
1275
- if ('rl' != this.pageProgression) {
1276
- this.prev();
1277
- } else {
1278
- this.next();
1279
- }
1280
- };
1281
-
1282
- /**
1283
- * Flip to the leftmost page
1284
- */
1285
- BookReader.prototype.leftmost = function() {
1286
- if ('rl' != this.pageProgression) {
1287
- this.first();
1288
- } else {
1289
- this.last();
1290
- }
1291
- };
1292
-
1293
- BookReader.prototype.next = function() {
1294
- if (this.constMode2up == this.mode) {
1295
- this.trigger(BookReader.eventNames.stop);
1296
- this.flipFwdToIndex(null);
1297
- } else {
1298
- if (this.firstIndex < this.lastDisplayableIndex()) {
1299
- this.jumpToIndex(this.firstIndex + 1);
1300
- }
1301
- }
1302
- };
1303
-
1304
- BookReader.prototype.prev = function() {
1305
- const isOnFrontPage = this.firstIndex < 1;
1306
- if (isOnFrontPage) return;
1307
-
1308
- if (this.constMode2up == this.mode) {
1309
- this.trigger(BookReader.eventNames.stop);
1310
- this.flipBackToIndex(null);
1311
- } else {
1312
- if (this.firstIndex >= 1) {
1313
- this.jumpToIndex(this.firstIndex - 1);
1314
- }
1315
- }
1316
- };
1317
-
1318
- BookReader.prototype.first = function() {
1319
- this.jumpToIndex(this.firstDisplayableIndex());
1320
- };
1321
-
1322
- BookReader.prototype.last = function() {
1323
- this.jumpToIndex(this.lastDisplayableIndex());
1324
- };
1325
-
1326
- /**
1327
- * Scrolls down one screen view
1328
- */
1329
- BookReader.prototype.scrollDown = function() {
1330
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1331
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1332
- // Whole pages are visible, scroll whole page only
1333
- return this.next();
1334
- }
1335
-
1336
- this.refs.$brContainer.stop(true).animate(
1337
- { scrollTop: '+=' + this._scrollAmount() + 'px'},
1338
- 400, 'easeInOutExpo'
1339
- );
1340
- return true;
1341
- } else {
1342
- return false;
1343
- }
1344
- };
1345
-
1346
- /**
1347
- * Scrolls up one screen view
1348
- */
1349
- BookReader.prototype.scrollUp = function() {
1350
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1351
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1352
- // Whole pages are visible, scroll whole page only
1353
- return this.prev();
1354
- }
1355
-
1356
- this.refs.$brContainer.stop(true).animate(
1357
- { scrollTop: '-=' + this._scrollAmount() + 'px'},
1358
- 400, 'easeInOutExpo'
1359
- );
1360
- return true;
1361
- } else {
1362
- return false;
1363
- }
1364
- };
1365
-
1366
- /**
1367
- * The amount to scroll vertically in integer pixels
1368
- */
1369
- BookReader.prototype._scrollAmount = function() {
1370
- if (this.constMode1up == this.mode) {
1371
- // Overlap by % of page size
1372
- return parseInt(this.refs.$brContainer.prop('clientHeight') - this._models.book.getPageHeight(this.currentIndex()) / this.reduce * 0.03);
1373
- }
1374
-
1375
- return parseInt(0.9 * this.refs.$brContainer.prop('clientHeight'));
1376
- };
1377
-
1378
- /**
1379
- * @deprecated No longer used; will be remove in v5
1380
- */
1381
- BookReader.prototype.prefetchImg = async function(index, fetchNow = false) {
1382
- console.warn('Call to deprecated function: BookReader.prefetchImg. No-op.');
1383
- };
1384
-
1385
- /**
1386
- * @deprecated No longer used; will be remove in v5
1387
- */
1388
- BookReader.prototype.pruneUnusedImgs = function() {
1389
- console.warn('Call to deprecated function: BookReader.pruneUnused. No-op.');
1390
- };
1391
-
1392
- /************************/
1393
- /** Mode1Up extensions **/
1394
- /************************/
1395
- /** @deprecated not used outside BookReader */
1396
- BookReader.prototype.prepareOnePageView = Mode1Up.prototype.prepare;
1397
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'prepare', 'prepareOnePageView');
1398
- /** @deprecated not used outside BookReader */
1399
- BookReader.prototype.zoom1up = Mode1Up.prototype.zoom;
1400
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'zoom', 'zoom1up');
1401
- /** @deprecated not used outside Mode1Up, BookReader */
1402
- BookReader.prototype.resizePageView1up = Mode1Up.prototype.resizePageView;
1403
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'resizePageView', 'resizePageView1up');
1404
- /** @deprecated not used outside Mode1Up */
1405
- BookReader.prototype.onePageCalculateViewDimensions = Mode1Up.prototype.calculateViewDimensions;
1406
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'calculateViewDimensions', 'onePageCalculateViewDimensions');
1407
-
1408
- /************************/
1409
- /** Mode2Up extensions **/
1410
- /************************/
1411
- /** @deprecated not used outside Mode2Up */
1412
- BookReader.prototype.zoom2up = Mode2Up.prototype.zoom;
1413
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'zoom', 'zoom2up');
1414
- BookReader.prototype.twoPageGetAutofitReduce = Mode2Up.prototype.getAutofitReduce;
1415
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getAutofitReduce', 'twoPageGetAutofitReduce');
1416
- BookReader.prototype.flipBackToIndex = Mode2Up.prototype.flipBackToIndex;
1417
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipBackToIndex', 'flipBackToIndex');
1418
- BookReader.prototype.flipFwdToIndex = Mode2Up.prototype.flipFwdToIndex;
1419
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipFwdToIndex', 'flipFwdToIndex');
1420
- BookReader.prototype.setHilightCss2UP = Mode2Up.prototype.setHilightCss;
1421
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setHilightCss', 'setHilightCss2UP');
1422
- /** @deprecated not used outside Mode2Up */
1423
- BookReader.prototype.drawLeafsTwoPage = Mode2Up.prototype.drawLeafs;
1424
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'drawLeafs', 'drawLeafsTwoPage');
1425
- /** @deprecated not used outside BookReader */
1426
- BookReader.prototype.prepareTwoPageView = Mode2Up.prototype.prepareTwoPageView;
1427
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareTwoPageView', 'prepareTwoPageView');
1428
- /** @deprecated not used outside Mode2Up */
1429
- BookReader.prototype.prepareTwoPagePopUp = Mode2Up.prototype.preparePopUp;
1430
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'preparePopUp', 'prepareTwoPagePopUp');
1431
- /** @deprecated not used outside BookReader, Mode2Up */
1432
- BookReader.prototype.calculateSpreadSize = Mode2Up.prototype.calculateSpreadSize;
1433
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateSpreadSize', 'calculateSpreadSize');
1434
- /** @deprecated not used outside BookReader, Mode2Up */
1435
- BookReader.prototype.getIdealSpreadSize = Mode2Up.prototype.getIdealSpreadSize;
1436
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getIdealSpreadSize', 'getIdealSpreadSize');
1437
- /** @deprecated not used outside BookReader, Mode2Up */
1438
- BookReader.prototype.getSpreadSizeFromReduce = Mode2Up.prototype.getSpreadSizeFromReduce;
1439
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getSpreadSizeFromReduce', 'getSpreadSizeFromReduce');
1440
- /** @deprecated unused */
1441
- BookReader.prototype.twoPageIsZoomedIn = Mode2Up.prototype.isZoomedIn;
1442
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'isZoomedIn', 'twoPageIsZoomedIn');
1443
- /** @deprecated not used outside BookReader */
1444
- BookReader.prototype.twoPageCalculateReductionFactors = Mode2Up.prototype.calculateReductionFactors;
1445
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateReductionFactors', 'twoPageCalculateReductionFactors');
1446
- /** @deprecated unused */
1447
- BookReader.prototype.twoPageSetCursor = Mode2Up.prototype.setCursor;
1448
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setCursor', 'twoPageSetCursor');
1449
- /** @deprecated unused outside BookReader, Mode2Up */
1450
- BookReader.prototype.flipLeftToRight = Mode2Up.prototype.flipLeftToRight;
1451
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipLeftToRight', 'flipLeftToRight');
1452
- /** @deprecated unused outside BookReader, Mode2Up */
1453
- BookReader.prototype.flipRightToLeft = Mode2Up.prototype.flipRightToLeft;
1454
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipRightToLeft', 'flipRightToLeft');
1455
- /** @deprecated unused outside BookReader, Mode2Up */
1456
- BookReader.prototype.prepareFlipLeftToRight = Mode2Up.prototype.prepareFlipLeftToRight;
1457
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipLeftToRight', 'prepareFlipLeftToRight');
1458
- /** @deprecated unused outside BookReader, Mode2Up */
1459
- BookReader.prototype.prepareFlipRightToLeft = Mode2Up.prototype.prepareFlipRightToLeft;
1460
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipRightToLeft', 'prepareFlipRightToLeft');
1461
- /** @deprecated unused outside Mode2Up */
1462
- BookReader.prototype.getPageWidth2UP = Mode2Up.prototype.getPageWidth;
1463
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getPageWidth', 'getPageWidth2UP');
1464
- /** @deprecated unused outside Mode2Up */
1465
- BookReader.prototype.twoPageGutter = Mode2Up.prototype.gutter;
1466
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutter', 'twoPageGutter');
1467
- /** @deprecated unused outside Mode2Up */
1468
- BookReader.prototype.twoPageTop = Mode2Up.prototype.top;
1469
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'top', 'twoPageTop');
1470
- /** @deprecated unused outside Mode2Up */
1471
- BookReader.prototype.twoPageCoverWidth = Mode2Up.prototype.coverWidth;
1472
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'coverWidth', 'twoPageCoverWidth');
1473
- /** @deprecated unused outside Mode2Up */
1474
- BookReader.prototype.twoPageGetViewCenter = Mode2Up.prototype.getViewCenter;
1475
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getViewCenter', 'twoPageGetViewCenter');
1476
- /** @deprecated unused outside BookReader, Mode2Up */
1477
- BookReader.prototype.twoPageCenterView = Mode2Up.prototype.centerView;
1478
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'centerView', 'twoPageCenterView');
1479
- /** @deprecated unused outside Mode2Up */
1480
- BookReader.prototype.twoPageFlipAreaHeight = Mode2Up.prototype.flipAreaHeight;
1481
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaHeight', 'twoPageFlipAreaHeight');
1482
- /** @deprecated unused outside Mode2Up */
1483
- BookReader.prototype.twoPageFlipAreaWidth = Mode2Up.prototype.flipAreaWidth;
1484
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaWidth', 'twoPageFlipAreaWidth');
1485
- /** @deprecated unused outside BookReader, Mode2Up */
1486
- BookReader.prototype.twoPageFlipAreaTop = Mode2Up.prototype.flipAreaTop;
1487
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaTop', 'twoPageFlipAreaTop');
1488
- /** @deprecated unused outside Mode2Up */
1489
- BookReader.prototype.twoPageLeftFlipAreaLeft = Mode2Up.prototype.leftFlipAreaLeft;
1490
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leftFlipAreaLeft', 'twoPageLeftFlipAreaLeft');
1491
- /** @deprecated unused outside Mode2Up */
1492
- BookReader.prototype.twoPageRightFlipAreaLeft = Mode2Up.prototype.rightFlipAreaLeft;
1493
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'rightFlipAreaLeft', 'twoPageRightFlipAreaLeft');
1494
- /** @deprecated unused outside BookReader, Mode2Up */
1495
- BookReader.prototype.gutterOffsetForIndex = Mode2Up.prototype.gutterOffsetForIndex;
1496
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutterOffsetForIndex', 'gutterOffsetForIndex');
1497
- /** @deprecated unused outside BookReader, Mode2Up */
1498
- BookReader.prototype.leafEdgeWidth = Mode2Up.prototype.leafEdgeWidth;
1499
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leafEdgeWidth', 'leafEdgeWidth');
1500
- /** @deprecated unused outside BookReader, Mode2Up */
1501
- BookReader.prototype.jumpIndexForLeftEdgePageX = Mode2Up.prototype.jumpIndexForLeftEdgePageX;
1502
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForLeftEdgePageX', 'jumpIndexForLeftEdgePageX');
1503
- /** @deprecated unused outside BookReader, Mode2Up */
1504
- BookReader.prototype.jumpIndexForRightEdgePageX = Mode2Up.prototype.jumpIndexForRightEdgePageX;
1505
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForRightEdgePageX', 'jumpIndexForRightEdgePageX');
1506
- /** @deprecated unused outside Mode2Up */
1507
- BookReader.prototype.prefetch = Mode2Up.prototype.prefetch;
1508
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prefetch', 'prefetch');
1509
- /** @deprecated unused outside Mode2Up */
1510
- BookReader.prototype.setSpreadIndices = Mode2Up.prototype.setSpreadIndices;
1511
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setSpreadIndices', 'setSpreadIndices');
1512
- /**
1513
- * Immediately stop flip animations. Callbacks are triggered.
1514
- */
1515
- BookReader.prototype.stopFlipAnimations = function() {
1516
- this.trigger(BookReader.eventNames.stop);
1517
-
1518
- // Stop animation, clear queue, trigger callbacks
1519
- if (this.leafEdgeTmp) {
1520
- $(this.leafEdgeTmp).stop(false, true);
1521
- }
1522
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1523
- $(this.$container).stop(false, true);
1524
- });
1525
-
1526
- // And again since animations also queued in callbacks
1527
- if (this.leafEdgeTmp) {
1528
- $(this.leafEdgeTmp).stop(false, true);
1529
- }
1530
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1531
- $(this.$container).stop(false, true);
1532
- });
1533
- };
1534
-
1535
- /**
1536
- * @template TClass extends { br: BookReader }
1537
- * Helper method to expose a method onto BookReader from a composed class.
1538
- * Only composed classes in BookReader._overridable can be exposed in this
1539
- * way.
1540
- * @param {new () => TClass} Class
1541
- * @param {keyof BookReader['_overrideable']} classKey
1542
- * @param {keyof TClass} method
1543
- * @param {string} [brMethod]
1544
- */
1545
- function exposeOverrideableMethod(Class, classKey, method, brMethod = method) {
1546
- /** @type {function(TClass): BookReader} */
1547
- const classToBr = cls => cls.br;
1548
- /** @type {function(BookReader): TClass} */
1549
- const brToClass = br => br._overrideable[classKey];
1550
- exposeOverrideable(Class, method, classToBr, BookReader, brMethod, brToClass);
1551
- }
1552
-
1553
-
1554
- /***********************/
1555
- /** Navbar extensions **/
1556
- /***********************/
1557
- BookReader.prototype.initNavbar = Navbar.prototype.init;
1558
- exposeOverrideableMethod(Navbar, '_components.navbar', 'init', 'initNavbar');
1559
- BookReader.prototype.switchNavbarControls = Navbar.prototype.switchNavbarControls;
1560
- exposeOverrideableMethod(Navbar, '_components.navbar', 'switchNavbarControls');
1561
- BookReader.prototype.updateViewModeButton = Navbar.prototype.updateViewModeButton;
1562
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateViewModeButton');
1563
- BookReader.prototype.getNavPageNumString = Navbar.prototype.getNavPageNumString;
1564
- exposeOverrideableMethod(Navbar, '_components.navbar', 'getNavPageNumString');
1565
- /** @deprecated unused */
1566
- BookReader.prototype.getNavPageNumHtml = getNavPageNumHtml;
1567
- /** @deprecated unused outside this file */
1568
- BookReader.prototype.updateNavPageNum = Navbar.prototype.updateNavPageNum;
1569
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavPageNum');
1570
- /** @deprecated unused outside this file */
1571
- BookReader.prototype.updateNavIndex = Navbar.prototype.updateNavIndex;
1572
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavIndex');
1573
- /** @deprecated unused outside this file */
1574
- BookReader.prototype.updateNavIndexThrottled = utils.throttle(BookReader.prototype.updateNavIndex, 250, false);
1575
- /** @deprecated unused */
1576
- BookReader.prototype.updateNavIndexDebounced = utils.debounce(BookReader.prototype.updateNavIndex, 500, false);
1577
-
1578
-
1579
- /************************/
1580
- /** Toolbar extensions **/
1581
- /************************/
1582
- BookReader.prototype.buildToolbarElement = Toolbar.prototype.buildToolbarElement;
1583
- exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildToolbarElement');
1584
- BookReader.prototype.initToolbar = Toolbar.prototype.initToolbar;
1585
- exposeOverrideableMethod(Toolbar, '_components.toolbar', 'initToolbar');
1586
- BookReader.prototype.buildShareDiv = Toolbar.prototype.buildShareDiv;
1587
- exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildShareDiv');
1588
- BookReader.prototype.buildInfoDiv = Toolbar.prototype.buildInfoDiv;
1589
- exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildInfoDiv');
1590
- BookReader.prototype.getToolBarHeight = Toolbar.prototype.getToolBarHeight;
1591
- exposeOverrideableMethod(Toolbar, '_components.toolbar', 'getToolBarHeight');
1592
- /** @deprecated zoom no longer in toolbar */
1593
- BookReader.prototype.updateToolbarZoom = Toolbar.prototype.updateToolbarZoom;
1594
- exposeOverrideableMethod(Toolbar, '_components.toolbar', 'updateToolbarZoom');
1595
- /** @deprecated unused */
1596
- BookReader.prototype.blankInfoDiv = blankInfoDiv;
1597
- /** @deprecated unused */
1598
- BookReader.prototype.blankShareDiv = blankShareDiv;
1599
- /** @deprecated unused */
1600
- BookReader.prototype.createPopup = createPopup;
1601
-
1602
- /**
1603
- * Bind navigation handlers
1604
- */
1605
- BookReader.prototype.bindNavigationHandlers = function() {
1606
- const self = this;
1607
-
1608
- // Note the mobile plugin attaches itself to body, so we need to select outside
1609
- const jIcons = this.$('.BRicon').add('.BRmobileMenu .BRicon');
1610
- // Map of jIcon class -> click handler
1611
- const navigationControls = {
1612
- book_left: () => {
1613
- this.trigger(BookReader.eventNames.stop);
1614
- this.left();
1615
- },
1616
- book_right: () => {
1617
- this.trigger(BookReader.eventNames.stop);
1618
- this.right();
1619
- },
1620
- book_up: () => {
1621
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1622
- this.scrollUp();
1623
- } else {
1624
- this.prev();
1625
- }
1626
- },
1627
- book_down: () => {
1628
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1629
- this.scrollDown();
1630
- } else {
1631
- this.next();
1632
- }
1633
- },
1634
- book_top: this.first.bind(this),
1635
- book_bottom: this.last.bind(this),
1636
- book_leftmost: this.leftmost.bind(this),
1637
- book_rightmost: this.rightmost.bind(this),
1638
- onepg: () => {
1639
- this.switchMode(self.constMode1up);
1640
- },
1641
- thumb: () => {
1642
- this.switchMode(self.constModeThumb);
1643
- },
1644
- twopg: () => {
1645
- this.switchMode(self.constMode2up);
1646
- },
1647
- zoom_in: () => {
1648
- this.trigger(BookReader.eventNames.stop);
1649
- this.zoom(1);
1650
- this.trigger(BookReader.eventNames.zoomIn);
1651
- },
1652
- zoom_out: () => {
1653
- this.trigger(BookReader.eventNames.stop);
1654
- this.zoom(-1);
1655
- this.trigger(BookReader.eventNames.zoomOut);
1656
- },
1657
- full: () => {
1658
- if (this.ui == 'embed') {
1659
- var url = this.$('.BRembedreturn a').attr('href');
1660
- window.open(url);
1661
- } else {
1662
- this.toggleFullscreen();
1663
- }
1664
- },
1665
- };
1666
-
1667
- jIcons.filter('.fit').bind('fit', function() {
1668
- // XXXmang implement autofit zoom
1669
- });
1670
-
1671
- for (const control in navigationControls) {
1672
- jIcons.filter(`.${control}`).on('click.bindNavigationHandlers', () => {
1673
- navigationControls[control]();
1674
- return false;
1675
- });
1676
- }
1677
-
1678
- var $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
1679
- var $brNavCntlTopEl = this.$('.BRnavCntlTop');
1680
-
1681
- this.$('.BRnavCntl').click(
1682
- function() {
1683
- var promises = [];
1684
- // TODO don't use magic constants
1685
- // TODO move this to a function
1686
- if ($brNavCntlBtmEl.hasClass('BRdn')) {
1687
- if (self.refs.$BRtoolbar)
1688
- promises.push(self.refs.$BRtoolbar.animate(
1689
- {top: self.getToolBarHeight() * -1}
1690
- ).promise());
1691
- promises.push(self.$('.BRfooter').animate({bottom: self.getFooterHeight() * -1}).promise());
1692
- $brNavCntlBtmEl.addClass('BRup').removeClass('BRdn');
1693
- $brNavCntlTopEl.addClass('BRdn').removeClass('BRup');
1694
- self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'45px'});
1695
- self.$('.BRnavCntl').delay(1000).animate({opacity:.75}, 1000);
1696
- } else {
1697
- if (self.refs.$BRtoolbar)
1698
- promises.push(self.refs.$BRtoolbar.animate({top:0}).promise());
1699
- promises.push(self.$('.BRfooter').animate({bottom:0}).promise());
1700
- $brNavCntlBtmEl.addClass('BRdn').removeClass('BRup');
1701
- $brNavCntlTopEl.addClass('BRup').removeClass('BRdn');
1702
- self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
1703
- self.$('.BRvavCntl').animate({opacity:1});
1704
- }
1705
- $.when.apply($, promises).done(function() {
1706
- // Only do full resize in auto mode and need to recalc. size
1707
- if (self.mode == self.constMode2up && self.twoPage.autofit != null
1708
- && self.twoPage.autofit != 'none'
1709
- ) {
1710
- self.resize();
1711
- } else if (self.mode == self.constMode1up && self.onePage.autofit != null
1712
- && self.onePage.autofit != 'none') {
1713
- self.resize();
1714
- } else {
1715
- // Don't do a full resize to avoid redrawing images
1716
- self.resizeBRcontainer();
1717
- }
1718
- });
1719
- }
1720
- );
1721
- $brNavCntlBtmEl
1722
- .on("mouseover", function() {
1723
- if ($(this).hasClass('BRup')) {
1724
- self.$('.BRnavCntl').animate({opacity:1},250);
1725
- }
1726
- })
1727
- .on("mouseleave", function() {
1728
- if ($(this).hasClass('BRup')) {
1729
- self.$('.BRnavCntl').animate({opacity:.75},250);
1730
- }
1731
- });
1732
- $brNavCntlTopEl
1733
- .on("mouseover", function() {
1734
- if ($(this).hasClass('BRdn')) {
1735
- self.$('.BRnavCntl').animate({opacity:1},250);
1736
- }
1737
- })
1738
- .on("mouseleave", function() {
1739
- if ($(this).hasClass('BRdn')) {
1740
- self.$('.BRnavCntl').animate({opacity:.75},250);
1741
- }
1742
- });
1743
-
1744
- this.initSwipeData();
1745
-
1746
- $(document).off('mousemove.navigation', this.el);
1747
- $(document).on(
1748
- 'mousemove.navigation',
1749
- this.el,
1750
- { 'br': this },
1751
- this.navigationMousemoveHandler
1752
- );
1753
-
1754
- $(document).off('mousedown.swipe', '.BRpageimage');
1755
- $(document).on(
1756
- 'mousedown.swipe',
1757
- '.BRpageimage',
1758
- { 'br': this },
1759
- this.swipeMousedownHandler
1760
- );
1761
-
1762
- this.bindMozTouchHandlers();
1763
- };
1764
-
1765
- /**
1766
- * Unbind navigation handlers
1767
- */
1768
- BookReader.prototype.unbindNavigationHandlers = function() {
1769
- $(document).off('mousemove.navigation', this.el);
1770
- };
1771
-
1772
- /**
1773
- * Handle mousemove related to navigation. Bind at #BookReader level to allow autohide.
1774
- */
1775
- BookReader.prototype.navigationMousemoveHandler = function(event) {
1776
- // $$$ possibly not great to be calling this for every mousemove
1777
- if (event.data['br'].uiAutoHide) {
1778
- // 77px is an approximate height of the Internet Archive Top Nav
1779
- // 75 & 76 (pixels) provide used in this context is checked against the IA top nav height
1780
- var navkey = $(document).height() - 75;
1781
- if ((event.pageY < 76) || (event.pageY > navkey)) {
1782
- // inside or near navigation elements
1783
- event.data['br'].hideNavigation();
1784
- } else {
1785
- event.data['br'].showNavigation();
1786
- }
1787
- }
1788
- };
1789
-
1790
- BookReader.prototype.initSwipeData = function(clientX, clientY) {
1791
- /*
1792
- * Based on the really quite awesome "Today's Guardian" at http://guardian.gyford.com/
1793
- */
1794
- this._swipe = {
1795
- mightBeSwiping: false,
1796
- didSwipe: false,
1797
- mightBeDraggin: false,
1798
- didDrag: false,
1799
- startTime: (new Date).getTime(),
1800
- startX: clientX,
1801
- startY: clientY,
1802
- lastX: clientX,
1803
- lastY: clientY,
1804
- deltaX: 0,
1805
- deltaY: 0,
1806
- deltaT: 0
1807
- };
1808
- };
1809
-
1810
- BookReader.prototype.swipeMousedownHandler = function(event) {
1811
- var self = event.data['br'];
1812
-
1813
- // We should be the last bubble point for the page images
1814
- // Disable image drag and select, but keep right-click
1815
- if (event.which == 3) {
1816
- return !self.protected;
1817
- }
1818
-
1819
- $(event.target).on('mouseout.swipe',
1820
- { 'br': self},
1821
- self.swipeMouseupHandler
1822
- ).on('mouseup.swipe',
1823
- { 'br': self},
1824
- self.swipeMouseupHandler
1825
- ).on('mousemove.swipe',
1826
- { 'br': self },
1827
- self.swipeMousemoveHandler
1828
- );
1829
-
1830
- self.initSwipeData(event.clientX, event.clientY);
1831
- self._swipe.mightBeSwiping = true;
1832
- self._swipe.mightBeDragging = true;
1833
-
1834
- event.preventDefault();
1835
- event.returnValue = false;
1836
- event.cancelBubble = true;
1837
- return false;
1838
- };
1839
-
1840
- BookReader.prototype.swipeMousemoveHandler = function(event) {
1841
- var self = event.data['br'];
1842
- var _swipe = self._swipe;
1843
- if (! _swipe.mightBeSwiping) {
1844
- return;
1845
- }
1846
-
1847
- // Update swipe data
1848
- _swipe.deltaX = event.clientX - _swipe.startX;
1849
- _swipe.deltaY = event.clientY - _swipe.startY;
1850
- _swipe.deltaT = (new Date).getTime() - _swipe.startTime;
1851
-
1852
- var absX = Math.abs(_swipe.deltaX);
1853
- var absY = Math.abs(_swipe.deltaY);
1854
-
1855
- // Minimum distance in the amount of tim to trigger the swipe
1856
- var minSwipeLength = Math.min(self.refs.$br.width() / 5, 80);
1857
- var maxSwipeTime = 400;
1858
-
1859
- // Check for horizontal swipe
1860
- if (absX > absY && (absX > minSwipeLength) && _swipe.deltaT < maxSwipeTime) {
1861
- _swipe.mightBeSwiping = false; // only trigger once
1862
- _swipe.didSwipe = true;
1863
- if (self.mode == self.constMode2up) {
1864
- if (_swipe.deltaX < 0) {
1865
- self.right();
1866
- } else {
1867
- self.left();
1868
- }
1869
- }
1870
- }
1871
-
1872
- if ( _swipe.deltaT > maxSwipeTime && !_swipe.didSwipe) {
1873
- if (_swipe.mightBeDragging) {
1874
- // Dragging
1875
- _swipe.didDrag = true;
1876
- self.refs.$brContainer
1877
- .scrollTop(self.refs.$brContainer.scrollTop() - event.clientY + _swipe.lastY)
1878
- .scrollLeft(self.refs.$brContainer.scrollLeft() - event.clientX + _swipe.lastX);
1879
- }
1880
- }
1881
- _swipe.lastX = event.clientX;
1882
- _swipe.lastY = event.clientY;
1883
-
1884
- event.preventDefault();
1885
- event.returnValue = false;
1886
- event.cancelBubble = true;
1887
- return false;
1888
- };
1889
-
1890
- BookReader.prototype.swipeMouseupHandler = function(event) {
1891
- var _swipe = event.data['br']._swipe;
1892
- _swipe.mightBeSwiping = false;
1893
- _swipe.mightBeDragging = false;
1894
-
1895
- $(event.target).off('mouseout.swipe').off('mouseup.swipe').off('mousemove.swipe');
1896
-
1897
- if (_swipe.didSwipe || _swipe.didDrag) {
1898
- // Swallow event if completed swipe gesture
1899
- event.preventDefault();
1900
- event.returnValue = false;
1901
- event.cancelBubble = true;
1902
- return false;
1903
- }
1904
- return true;
1905
- };
1906
-
1907
- BookReader.prototype.bindMozTouchHandlers = function() {
1908
- var self = this;
1909
-
1910
- // Currently only want touch handlers in 2up
1911
- this.refs.$br
1912
- .on('MozTouchDown', function(event) {
1913
- if (this.mode == self.constMode2up) {
1914
- event.preventDefault();
1915
- }
1916
- })
1917
- .on('MozTouchMove', function(event) {
1918
- if (this.mode == self.constMode2up) {
1919
- event.preventDefault();
1920
- }
1921
- })
1922
- .on('MozTouchUp', function(event) {
1923
- if (this.mode == self.constMode2up) {
1924
- event.preventDefault();
1925
- }
1926
- });
1927
- };
1928
-
1929
- /**
1930
- * Returns true if the navigation elements are currently visible
1931
- * @return {boolean}
1932
- */
1933
- BookReader.prototype.navigationIsVisible = function() {
1934
- // $$$ doesn't account for transitioning states, nav must be fully visible to return true
1935
- var toolpos = this.refs.$BRtoolbar.position();
1936
- var tooltop = toolpos.top;
1937
- return tooltop == 0;
1938
- };
1939
-
1940
- /**
1941
- * Main controller that sets navigation into view.
1942
- * Defaults to SHOW the navigation chrome
1943
- */
1944
- BookReader.prototype.setNavigationView = function brSetNavigationView(hide) {
1945
- var animationLength = this.constNavAnimationDuration;
1946
- var animationType = 'linear';
1947
- var resizePageContainer = function resizePageContainer () {
1948
- /* main page container fills whole container */
1949
- if (this.constMode2up !== this.mode) {
1950
- var animate = true;
1951
- this.resizeBRcontainer(animate);
1952
- }
1953
- this.trigger(BookReader.eventNames.navToggled);
1954
- }.bind(this);
1955
-
1956
- var toolbarHeight = 0;
1957
- var navbarHeight = 0;
1958
- if (hide) {
1959
- toolbarHeight = this.getToolBarHeight() * -1;
1960
- navbarHeight = this.getFooterHeight() * -1;
1961
-
1962
- this.refs.$BRtoolbar.addClass('js-menu-hide');
1963
- this.refs.$BRfooter.addClass('js-menu-hide');
1964
- } else {
1965
- this.refs.$BRtoolbar.removeClass('js-menu-hide');
1966
- this.refs.$BRfooter.removeClass('js-menu-hide');
1967
- }
1968
-
1969
- this.refs.$BRtoolbar.animate(
1970
- { top: toolbarHeight },
1971
- animationLength,
1972
- animationType,
1973
- resizePageContainer
1974
- );
1975
- this.refs.$BRfooter.animate(
1976
- { bottom: navbarHeight },
1977
- animationLength,
1978
- animationType,
1979
- resizePageContainer
1980
- );
1981
- };
1982
- /**
1983
- * Hide navigation elements, if visible
1984
- */
1985
- BookReader.prototype.hideNavigation = function() {
1986
- // Check if navigation is showing
1987
- if (this.navigationIsVisible()) {
1988
- var hide = true;
1989
- this.setNavigationView(hide);
1990
- }
1991
- };
1992
-
1993
- /**
1994
- * Show navigation elements
1995
- */
1996
- BookReader.prototype.showNavigation = function() {
1997
- // Check if navigation is hidden
1998
- if (!this.navigationIsVisible()) {
1999
- this.setNavigationView();
2000
- }
2001
- };
2002
-
2003
- /**
2004
- * Returns the index of the first visible page, dependent on the mode.
2005
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
2006
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
2007
- * @return {number}
2008
- */
2009
- BookReader.prototype.firstDisplayableIndex = function() {
2010
- if (this.mode != this.constMode2up) {
2011
- return 0;
2012
- }
2013
-
2014
- if ('rl' != this.pageProgression) {
2015
- // LTR
2016
- if (this._models.book.getPageSide(0) == 'L') {
2017
- return 0;
2018
- } else {
2019
- return -1;
2020
- }
2021
- } else {
2022
- // RTL
2023
- if (this._models.book.getPageSide(0) == 'R') {
2024
- return 0;
2025
- } else {
2026
- return -1;
2027
- }
2028
- }
2029
- };
2030
-
2031
- /**
2032
- * Returns the index of the last visible page, dependent on the mode.
2033
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
2034
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
2035
- * @return {number}
2036
- */
2037
- BookReader.prototype.lastDisplayableIndex = function() {
2038
-
2039
- var lastIndex = this._models.book.getNumLeafs() - 1;
2040
-
2041
- if (this.mode != this.constMode2up) {
2042
- return lastIndex;
2043
- }
2044
-
2045
- if ('rl' != this.pageProgression) {
2046
- // LTR
2047
- if (this._models.book.getPageSide(lastIndex) == 'R') {
2048
- return lastIndex;
2049
- } else {
2050
- return lastIndex + 1;
2051
- }
2052
- } else {
2053
- // RTL
2054
- if (this._models.book.getPageSide(lastIndex) == 'L') {
2055
- return lastIndex;
2056
- } else {
2057
- return lastIndex + 1;
2058
- }
2059
- }
2060
- };
2061
-
2062
-
2063
- /**************************/
2064
- /** BookModel extensions **/
2065
- /**************************/
2066
- /** @deprecated not used outside */
2067
- BookReader.prototype.getMedianPageSize = BookModel.prototype.getMedianPageSize;
2068
- exposeOverrideableMethod(BookModel, '_models.book', 'getMedianPageSize');
2069
- BookReader.prototype._getPageWidth = BookModel.prototype._getPageWidth;
2070
- exposeOverrideableMethod(BookModel, '_models.book', '_getPageWidth');
2071
- BookReader.prototype._getPageHeight = BookModel.prototype._getPageHeight;
2072
- exposeOverrideableMethod(BookModel, '_models.book', '_getPageHeight');
2073
- BookReader.prototype.getPageIndex = BookModel.prototype.getPageIndex;
2074
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndex');
2075
- /** @deprecated not used outside */
2076
- BookReader.prototype.getPageIndices = BookModel.prototype.getPageIndices;
2077
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndices');
2078
- BookReader.prototype.getPageName = BookModel.prototype.getPageName;
2079
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageName');
2080
- BookReader.prototype.getNumLeafs = BookModel.prototype.getNumLeafs;
2081
- exposeOverrideableMethod(BookModel, '_models.book', 'getNumLeafs');
2082
- BookReader.prototype.getPageWidth = BookModel.prototype.getPageWidth;
2083
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageWidth');
2084
- BookReader.prototype.getPageHeight = BookModel.prototype.getPageHeight;
2085
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageHeight');
2086
- BookReader.prototype.getPageURI = BookModel.prototype.getPageURI;
2087
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageURI');
2088
- BookReader.prototype.getPageSide = BookModel.prototype.getPageSide;
2089
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageSide');
2090
- BookReader.prototype.getPageNum = BookModel.prototype.getPageNum;
2091
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageNum');
2092
- BookReader.prototype.getPageProp = BookModel.prototype.getPageProp;
2093
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageProp');
2094
- BookReader.prototype.getSpreadIndices = BookModel.prototype.getSpreadIndices;
2095
- exposeOverrideableMethod(BookModel, '_models.book', 'getSpreadIndices');
2096
- BookReader.prototype.leafNumToIndex = BookModel.prototype.leafNumToIndex;
2097
- exposeOverrideableMethod(BookModel, '_models.book', 'leafNumToIndex');
2098
- BookReader.prototype.parsePageString = BookModel.prototype.parsePageString;
2099
- exposeOverrideableMethod(BookModel, '_models.book', 'parsePageString');
2100
- /** @deprecated unused */
2101
- BookReader.prototype._getDataFlattened = BookModel.prototype._getDataFlattened;
2102
- exposeOverrideableMethod(BookModel, '_models.book', '_getDataFlattened');
2103
- /** @deprecated unused */
2104
- BookReader.prototype._getDataProp = BookModel.prototype._getDataProp;
2105
- exposeOverrideableMethod(BookModel, '_models.book', '_getDataProp');
2106
-
2107
- // Parameter related functions
2108
-
2109
- /**
2110
- * Update from the params object
2111
- * @param {Object}
2112
- */
2113
- BookReader.prototype.updateFromParams = function(params) {
2114
- // Set init, fragment change options for switchMode()
2115
- const {
2116
- mode = 0,
2117
- init = false,
2118
- fragmentChange = false,
2119
- } = params;
2120
-
2121
- if (mode) {
2122
- this.switchMode(
2123
- mode,
2124
- { init: init, suppressFragmentChange: !fragmentChange }
2125
- );
2126
- }
2127
-
2128
- // $$$ process /zoom
2129
- // We only respect page if index is not set
2130
- if ('undefined' != typeof(params.index)) {
2131
- if (params.index != this.currentIndex()) {
2132
- this.jumpToIndex(params.index);
2133
- }
2134
- } else if ('undefined' != typeof(params.page)) {
2135
- // $$$ this assumes page numbers are unique
2136
- if (params.page != this._models.book.getPageNum(this.currentIndex())) {
2137
- this.jumpToPage(params.page);
2138
- }
2139
- }
2140
-
2141
-
2142
- // process /search
2143
- // @deprecated for urlMode 'history'
2144
- // Continues to work for urlMode 'hash'
2145
- if (this.enableSearch && 'undefined' != typeof(params.search)) {
2146
- if (this.searchTerm !== params.search) {
2147
- this.$('.BRsearchInput').val(params.search);
2148
- }
2149
- }
2150
-
2151
- // $$$ process /region
2152
- // $$$ process /highlight
2153
-
2154
- // $$$ process /theme
2155
- if (this.enableThemesPlugin && 'undefined' != typeof(params.theme)) {
2156
- this.updateTheme(params.theme);
2157
- }
2158
- };
2159
-
2160
- /**
2161
- * Returns true if we can switch to the requested mode
2162
- * @param {number} mode
2163
- * @return {boolean}
2164
- */
2165
- BookReader.prototype.canSwitchToMode = function(mode) {
2166
- if (mode == this.constMode2up || mode == this.constModeThumb) {
2167
- // check there are enough pages to display
2168
- // $$$ this is a workaround for the mis-feature that we can't display
2169
- // short books in 2up mode
2170
- if (this._models.book.getNumLeafs() < 2) {
2171
- return false;
2172
- }
2173
- }
2174
-
2175
- return true;
2176
- };
2177
-
2178
-
2179
- /**
2180
- * @deprecated. Use PageModel.getURISrcSet. Slated for removal in v5.
2181
- * Returns the srcset with correct URIs or void string if out of range
2182
- * Also makes the reduce argument optional
2183
- * @param {number} index
2184
- * @param {number} [reduce]
2185
- * @param {number} [rotate]
2186
- * @return {string}
2187
- */
2188
- BookReader.prototype._getPageURISrcset = function(index, reduce, rotate) {
2189
- const page = this._models.book.getPage(index, false);
2190
- // Synthesize page
2191
- if (!page) return "";
2192
-
2193
- // reduce not passed in
2194
- // $$$ this probably won't work for thumbnail mode
2195
- if ('undefined' == typeof(reduce)) {
2196
- reduce = page.height / this.twoPage.height;
2197
- }
2198
-
2199
- return page.getURISrcSet(reduce, rotate);
2200
- };
2201
-
2202
-
2203
- /**
2204
- * Returns the page URI or transparent image if out of range
2205
- * Also makes the reduce argument optional
2206
- * @param {number} index
2207
- * @param {number} [reduce]
2208
- * @param {number} [rotate]
2209
- * @return {string}
2210
- */
2211
- BookReader.prototype._getPageURI = function(index, reduce, rotate) {
2212
- const page = this._models.book.getPage(index, false);
2213
- // Synthesize page
2214
- if (!page) return this.imagesBaseURL + "transparent.png";
2215
-
2216
- if ('undefined' == typeof(reduce)) {
2217
- // reduce not passed in
2218
- // $$$ this probably won't work for thumbnail mode
2219
- reduce = page.height / this.twoPage.height;
2220
- }
2221
-
2222
- return page.getURI(reduce, rotate);
2223
- };
2224
-
2225
- /**
2226
- * @param {string} msg
2227
- * @param {function|undefined} onCloseCallback
2228
- */
2229
- BookReader.prototype.showProgressPopup = function(msg, onCloseCallback) {
2230
- if (this.popup) return;
2231
-
2232
- this.popup = document.createElement("div");
2233
- $(this.popup).prop('className', 'BRprogresspopup');
2234
-
2235
- if (typeof(onCloseCallback) === 'function') {
2236
- const closeButton = document.createElement('button');
2237
- closeButton.setAttribute('title', 'close');
2238
- closeButton.setAttribute('class', 'close-popup');
2239
- const icon = document.createElement('span');
2240
- icon.setAttribute('class', 'icon icon-close-dark');
2241
- $(closeButton).append(icon);
2242
- closeButton.addEventListener('click', () => {
2243
- onCloseCallback();
2244
- this.removeProgressPopup();
2245
- });
2246
- $(this.popup).append(closeButton);
2247
- }
2248
-
2249
- const bar = document.createElement("div");
2250
- $(bar).css({
2251
- height: '20px'
2252
- }).prop('className', 'BRprogressbar');
2253
- $(this.popup).append(bar);
2254
-
2255
- if (msg) {
2256
- const msgdiv = document.createElement("div");
2257
- msgdiv.innerHTML = msg;
2258
- $(this.popup).append(msgdiv);
2259
- }
2260
-
2261
- $(this.popup).appendTo(this.refs.$br);
2262
- };
2263
-
2264
- BookReader.prototype.removeProgressPopup = function() {
2265
- $(this.popup).remove();
2266
- this.$('.BRprogresspopup').remove();
2267
- this.popup = null;
2268
- };
2269
-
2270
- /**
2271
- * Can be overridden
2272
- */
2273
- BookReader.prototype.initUIStrings = function() {
2274
- // Navigation handlers will be bound after all UI is in place -- makes moving icons between
2275
- // the toolbar and nav bar easier
2276
-
2277
- // Setup tooltips -- later we could load these from a file for i18n
2278
- var titles = {
2279
- '.logo': 'Go to Archive.org', // $$$ update after getting OL record
2280
- '.zoom_in': 'Zoom in',
2281
- '.zoom_out': 'Zoom out',
2282
- '.onepg': 'One-page view',
2283
- '.twopg': 'Two-page view',
2284
- '.thumb': 'Thumbnail view',
2285
- '.print': 'Print this page',
2286
- '.embed': 'Embed BookReader',
2287
- '.link': 'Link to this book (and page)',
2288
- '.bookmark': 'Bookmark this page',
2289
- '.share': 'Share this book',
2290
- '.info': 'About this book',
2291
- '.full': 'Toggle fullscreen',
2292
- '.book_left': 'Flip left',
2293
- '.book_right': 'Flip right',
2294
- '.book_up': 'Page up',
2295
- '.book_down': 'Page down',
2296
- '.play': 'Play',
2297
- '.pause': 'Pause',
2298
- '.BRdn': 'Show/hide nav bar', // Would have to keep updating on state change to have just "Hide nav bar"
2299
- '.BRup': 'Show/hide nav bar',
2300
- '.book_top': 'First page',
2301
- '.book_bottom': 'Last page',
2302
- '.book_leftmost': 'First page',
2303
- '.book_rightmost': 'Last page',
2304
- };
2305
- if ('rl' == this.pageProgression) {
2306
- titles['.book_leftmost'] = 'Last page';
2307
- titles['.book_rightmost'] = 'First page';
2308
- }
2309
-
2310
- for (var icon in titles) {
2311
- this.$(icon).prop('title', titles[icon]);
2312
- }
2313
- };
2314
-
2315
- /**
2316
- * Reloads images. Useful when some images might have failed.
2317
- */
2318
- BookReader.prototype.reloadImages = function() {
2319
- this.refs.$brContainer.find('img').each(function(index, elem) {
2320
- if (!elem.complete || elem.naturalHeight === 0) {
2321
- var src = elem.src;
2322
- elem.src = '';
2323
- setTimeout(function() {
2324
- elem.src = src;
2325
- }, 1000);
2326
- }
2327
- });
2328
- };
2329
-
2330
- /**
2331
- * @param {boolean} ignoreDisplay - bypass the display check
2332
- * @return {number}
2333
- */
2334
- BookReader.prototype.getFooterHeight = function() {
2335
- var $heightEl = this.mode == this.constMode2up ? this.refs.$BRfooter : this.refs.$BRnav;
2336
- if ($heightEl && this.refs.$BRfooter) {
2337
- var outerHeight = $heightEl.outerHeight();
2338
- var bottom = parseInt(this.refs.$BRfooter.css('bottom'));
2339
- if (!isNaN(outerHeight) && !isNaN(bottom)) {
2340
- return outerHeight + bottom;
2341
- }
2342
- }
2343
- return 0;
2344
- };
2345
-
2346
- // Basic Usage built-in Methods (can be overridden through options)
2347
- // This implementation uses options.data value for populating BookReader
2348
-
2349
- /**
2350
- * Create a params object from the current parameters.
2351
- * @return {Object}
2352
- */
2353
- BookReader.prototype.paramsFromCurrent = function() {
2354
- var params = {};
2355
-
2356
- // Path params
2357
- var index = this.currentIndex();
2358
- var pageNum = this._models.book.getPageNum(index);
2359
- if ((pageNum === 0) || pageNum) {
2360
- params.page = pageNum;
2361
- }
2362
-
2363
- params.index = index;
2364
- params.mode = this.mode;
2365
-
2366
- // Unused params
2367
- // $$$ highlight
2368
- // $$$ region
2369
-
2370
- // Querystring params
2371
- // View
2372
- const fullscreenView = 'theater';
2373
- if (this.isFullscreenActive) {
2374
- params.view = fullscreenView;
2375
- }
2376
- // Search
2377
- if (this.enableSearch) {
2378
- params.search = this.searchTerm;
2379
- }
2380
-
2381
- return params;
2382
- };
2383
-
2384
- /**
2385
- * Return an object with configuration parameters from a fragment string.
2386
- *
2387
- * Fragments are formatted as a URL path but may be used outside of URLs as a
2388
- * serialization format for BookReader parameters
2389
- *
2390
- * @see http://openlibrary.org/dev/docs/bookurls for fragment syntax
2391
- *
2392
- * @param {string} fragment initial # is allowed for backwards compatibility
2393
- * but is deprecated
2394
- * @return {Object}
2395
- */
2396
- BookReader.prototype.paramsFromFragment = function(fragment) {
2397
- var params = {};
2398
-
2399
- // For backwards compatibility we allow an initial # character
2400
- // (as from window.location.hash) but don't require it
2401
- if (fragment.substr(0, 1) == '#') {
2402
- fragment = fragment.substr(1);
2403
- }
2404
-
2405
- // Simple #nn syntax
2406
- var oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
2407
- if ( !isNaN(oldStyleLeafNum) ) {
2408
- params.index = oldStyleLeafNum;
2409
-
2410
- // Done processing if using old-style syntax
2411
- return params;
2412
- }
2413
-
2414
- // Split into key-value pairs
2415
- var urlArray = fragment.split('/');
2416
- var urlHash = {};
2417
- for (var i = 0; i < urlArray.length; i += 2) {
2418
- urlHash[urlArray[i]] = urlArray[i + 1];
2419
- }
2420
-
2421
- // Mode
2422
- if ('1up' == urlHash['mode']) {
2423
- params.mode = this.constMode1up;
2424
- } else if ('2up' == urlHash['mode']) {
2425
- params.mode = this.constMode2up;
2426
- } else if ('thumb' == urlHash['mode']) {
2427
- params.mode = this.constModeThumb;
2428
- }
2429
-
2430
- // Index and page
2431
- if ('undefined' != typeof(urlHash['page'])) {
2432
- // page was set -- may not be int
2433
- params.page = urlHash['page'];
2434
- }
2435
-
2436
- // $$$ process /region
2437
- // $$$ process /search
2438
-
2439
- if (urlHash['search'] != undefined) {
2440
- params.search = utils.decodeURIComponentPlus(urlHash['search']);
2441
- }
2442
-
2443
- // $$$ process /highlight
2444
-
2445
- // $$$ process /theme
2446
- if (urlHash['theme'] != undefined) {
2447
- params.theme = urlHash['theme'];
2448
- }
2449
- return params;
2450
- };
2451
-
2452
- /**
2453
- * Create a fragment string from the params object.
2454
- *
2455
- * Fragments are formatted as a URL path but may be used outside of URLs as a
2456
- * serialization format for BookReader parameters
2457
- *
2458
- * @see https://openlibrary.org/dev/docs/bookurls for fragment syntax
2459
- *
2460
- * @param {Object} params
2461
- * @param {string} [urlMode]
2462
- * @return {string}
2463
- */
2464
- BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2465
- const separator = '/';
2466
- const fragments = [];
2467
-
2468
- if ('undefined' != typeof(params.page)) {
2469
- fragments.push('page', params.page);
2470
- } else {
2471
- if ('undefined' != typeof(params.index)) {
2472
- // Don't have page numbering but we do have the index
2473
- fragments.push('page', 'n' + params.index);
2474
- }
2475
- }
2476
-
2477
- // $$$ highlight
2478
- // $$$ region
2479
-
2480
- // mode
2481
- if ('undefined' != typeof(params.mode)) {
2482
- if (params.mode == this.constMode1up) {
2483
- fragments.push('mode', '1up');
2484
- } else if (params.mode == this.constMode2up) {
2485
- fragments.push('mode', '2up');
2486
- } else if (params.mode == this.constModeThumb) {
2487
- fragments.push('mode', 'thumb');
2488
- } else {
2489
- throw 'fragmentFromParams called with unknown mode ' + params.mode;
2490
- }
2491
- }
2492
-
2493
- // search
2494
- if (params.search && urlMode === 'hash') {
2495
- fragments.push('search', params.search);
2496
- }
2497
-
2498
- return utils.encodeURIComponentPlus(fragments.join(separator)).replace(/%2F/g, '/');
2499
- };
2500
-
2501
- /**
2502
- * Create, update querystring from the params object
2503
- *
2504
- * Handles:
2505
- * view=
2506
- * q=
2507
- * @param {Object} params
2508
- * @param {string} currQueryString
2509
- * @param {string} [urlMode]
2510
- * @return {string}
2511
- */
2512
- BookReader.prototype.queryStringFromParams = function(
2513
- params,
2514
- currQueryString,
2515
- urlMode = 'hash'
2516
- ) {
2517
- const newParams = new URLSearchParams(currQueryString);
2518
-
2519
- if (params.view) {
2520
- // Set ?view=theater when fullscreen
2521
- newParams.set('view', params.view);
2522
- } else {
2523
- // Remove
2524
- newParams.delete('view');
2525
- }
2526
-
2527
- if (params.search && urlMode === 'history') {
2528
- newParams.set('q', params.search);
2529
- }
2530
- // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
2531
- // Note: This method returns the query string without the question mark.
2532
- const result = newParams.toString();
2533
- return result ? '?' + result : '';
2534
- };
2535
-
2536
- /**
2537
- * Helper to select within instance's elements
2538
- */
2539
- BookReader.prototype.$ = function(selector) {
2540
- return this.refs.$br.find(selector);
2541
- };
2542
-
2543
- /**
2544
- * Polyfill for deprecated method
2545
- */
2546
- jQuery.curCSS = function(element, prop, val) {
2547
- return jQuery(element).css(prop, val);
2548
- };
2549
-
2550
- window.BookReader = BookReader;