@internetarchive/bookreader 5.0.0-88-alpha.8 → 5.0.0-88-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/dist/esm/BookNavigator/assets/bookmark-colors.js +4 -0
  2. package/dist/esm/BookNavigator/assets/button-base.js +4 -0
  3. package/dist/esm/BookNavigator/assets/ia-logo.js +4 -0
  4. package/dist/esm/BookNavigator/assets/icon_checkmark.js +8 -0
  5. package/dist/esm/BookNavigator/assets/icon_close.js +4 -0
  6. package/dist/esm/BookNavigator/book-navigator.js +612 -0
  7. package/dist/esm/BookNavigator/bookmarks/bookmark-button.js +35 -0
  8. package/dist/esm/BookNavigator/bookmarks/bookmark-edit.js +78 -0
  9. package/dist/esm/BookNavigator/bookmarks/bookmarks-list.js +160 -0
  10. package/dist/esm/BookNavigator/bookmarks/bookmarks-loginCTA.js +24 -0
  11. package/dist/esm/BookNavigator/bookmarks/bookmarks-provider.js +55 -0
  12. package/dist/esm/BookNavigator/bookmarks/ia-bookmarks.js +521 -0
  13. package/dist/esm/BookNavigator/delete-modal-actions.js +29 -0
  14. package/dist/esm/BookNavigator/downloads/downloads-provider.js +84 -0
  15. package/dist/esm/BookNavigator/downloads/downloads.js +69 -0
  16. package/dist/esm/BookNavigator/search/search-provider.js +238 -0
  17. package/dist/esm/BookNavigator/search/search-results.js +161 -0
  18. package/dist/esm/BookNavigator/sharing.js +26 -0
  19. package/dist/esm/BookNavigator/viewable-files.js +94 -0
  20. package/dist/esm/BookNavigator/visual-adjustments/visual-adjustments-provider.js +83 -0
  21. package/dist/esm/BookNavigator/visual-adjustments/visual-adjustments.js +131 -0
  22. package/dist/esm/BookReader/BookModel.js +575 -0
  23. package/dist/esm/BookReader/DragScrollable.js +224 -0
  24. package/dist/esm/BookReader/ImageCache.js +122 -0
  25. package/dist/esm/BookReader/Mode1Up.js +114 -0
  26. package/dist/esm/BookReader/Mode1UpLit.js +579 -0
  27. package/dist/esm/BookReader/Mode2Up.js +106 -0
  28. package/dist/esm/BookReader/Mode2UpLit.js +1020 -0
  29. package/dist/esm/BookReader/ModeCoordinateSpace.js +28 -0
  30. package/dist/esm/BookReader/ModeSmoothZoom.js +318 -0
  31. package/dist/esm/BookReader/ModeThumb.js +366 -0
  32. package/dist/esm/BookReader/Navbar/Navbar.js +253 -0
  33. package/dist/esm/BookReader/PageContainer.js +165 -0
  34. package/dist/esm/BookReader/ReduceSet.js +27 -0
  35. package/dist/esm/BookReader/Toolbar/Toolbar.js +242 -0
  36. package/dist/esm/BookReader/events.js +20 -0
  37. package/dist/esm/BookReader/options.js +331 -0
  38. package/dist/esm/BookReader/utils/HTMLDimensionsCacher.js +48 -0
  39. package/dist/esm/BookReader/utils/ScrollClassAdder.js +31 -0
  40. package/dist/esm/BookReader/utils/SelectionObserver.js +42 -0
  41. package/dist/esm/BookReader/utils/classes.js +37 -0
  42. package/dist/esm/BookReader/utils.js +315 -0
  43. package/dist/esm/BookReader.js +1827 -0
  44. package/dist/esm/assets/icons/1up.svg +12 -0
  45. package/dist/esm/assets/icons/2up.svg +15 -0
  46. package/dist/esm/assets/icons/advance.svg +26 -0
  47. package/dist/esm/assets/icons/chevron-right.svg +1 -0
  48. package/dist/esm/assets/icons/close-circle-dark.svg +1 -0
  49. package/dist/esm/assets/icons/close-circle.svg +1 -0
  50. package/dist/esm/assets/icons/fullscreen.svg +17 -0
  51. package/dist/esm/assets/icons/fullscreen_exit.svg +17 -0
  52. package/dist/esm/assets/icons/hamburger.svg +15 -0
  53. package/dist/esm/assets/icons/left-arrow.svg +12 -0
  54. package/dist/esm/assets/icons/magnify-minus.svg +12 -0
  55. package/dist/esm/assets/icons/magnify-plus.svg +13 -0
  56. package/dist/esm/assets/icons/magnify.svg +15 -0
  57. package/dist/esm/assets/icons/pause.svg +23 -0
  58. package/dist/esm/assets/icons/play.svg +22 -0
  59. package/dist/esm/assets/icons/playback-speed.svg +34 -0
  60. package/dist/esm/assets/icons/read-aloud.svg +22 -0
  61. package/dist/esm/assets/icons/review.svg +22 -0
  62. package/dist/esm/assets/icons/thumbnails.svg +17 -0
  63. package/dist/esm/assets/icons/voice.svg +1 -0
  64. package/dist/esm/assets/icons/volume-full.svg +22 -0
  65. package/dist/esm/assets/images/BRicons.png +0 -0
  66. package/dist/esm/assets/images/BRicons.svg +94 -0
  67. package/dist/esm/assets/images/BRicons_ia.png +0 -0
  68. package/dist/esm/assets/images/back_pages.png +0 -0
  69. package/dist/esm/assets/images/book_bottom_icon.png +0 -0
  70. package/dist/esm/assets/images/book_down_icon.png +0 -0
  71. package/dist/esm/assets/images/book_left_icon.png +0 -0
  72. package/dist/esm/assets/images/book_leftmost_icon.png +0 -0
  73. package/dist/esm/assets/images/book_right_icon.png +0 -0
  74. package/dist/esm/assets/images/book_rightmost_icon.png +0 -0
  75. package/dist/esm/assets/images/book_top_icon.png +0 -0
  76. package/dist/esm/assets/images/book_up_icon.png +0 -0
  77. package/dist/esm/assets/images/books_graphic.svg +177 -0
  78. package/dist/esm/assets/images/booksplit.png +0 -0
  79. package/dist/esm/assets/images/control_pause_icon.png +0 -0
  80. package/dist/esm/assets/images/control_play_icon.png +0 -0
  81. package/dist/esm/assets/images/embed_icon.png +0 -0
  82. package/dist/esm/assets/images/icon-home-ia.png +0 -0
  83. package/dist/esm/assets/images/icon_OL-logo-xs.png +0 -0
  84. package/dist/esm/assets/images/icon_alert-xs.png +0 -0
  85. package/dist/esm/assets/images/icon_book.svg +12 -0
  86. package/dist/esm/assets/images/icon_bookmark.svg +12 -0
  87. package/dist/esm/assets/images/icon_close-pop.png +0 -0
  88. package/dist/esm/assets/images/icon_download.png +0 -0
  89. package/dist/esm/assets/images/icon_gear.svg +14 -0
  90. package/dist/esm/assets/images/icon_hamburger.svg +20 -0
  91. package/dist/esm/assets/images/icon_home.png +0 -0
  92. package/dist/esm/assets/images/icon_home.svg +21 -0
  93. package/dist/esm/assets/images/icon_home_ia.png +0 -0
  94. package/dist/esm/assets/images/icon_indicator.png +0 -0
  95. package/dist/esm/assets/images/icon_info.svg +11 -0
  96. package/dist/esm/assets/images/icon_one_page.svg +8 -0
  97. package/dist/esm/assets/images/icon_pause.svg +1 -0
  98. package/dist/esm/assets/images/icon_play.svg +1 -0
  99. package/dist/esm/assets/images/icon_playback-rate.svg +15 -0
  100. package/dist/esm/assets/images/icon_return.png +0 -0
  101. package/dist/esm/assets/images/icon_search_button.svg +8 -0
  102. package/dist/esm/assets/images/icon_share.svg +9 -0
  103. package/dist/esm/assets/images/icon_skip-ahead.svg +6 -0
  104. package/dist/esm/assets/images/icon_skip-back.svg +13 -0
  105. package/dist/esm/assets/images/icon_speaker.svg +18 -0
  106. package/dist/esm/assets/images/icon_speaker_open.svg +10 -0
  107. package/dist/esm/assets/images/icon_thumbnails.svg +12 -0
  108. package/dist/esm/assets/images/icon_toc.svg +5 -0
  109. package/dist/esm/assets/images/icon_two_pages.svg +9 -0
  110. package/dist/esm/assets/images/icon_zoomer.png +0 -0
  111. package/dist/esm/assets/images/loading.gif +0 -0
  112. package/dist/esm/assets/images/logo_icon.png +0 -0
  113. package/dist/esm/assets/images/marker_chap-off.png +0 -0
  114. package/dist/esm/assets/images/marker_chap-off.svg +11 -0
  115. package/dist/esm/assets/images/marker_chap-off_ia.png +0 -0
  116. package/dist/esm/assets/images/marker_chap-on.png +0 -0
  117. package/dist/esm/assets/images/marker_chap-on.svg +11 -0
  118. package/dist/esm/assets/images/marker_srch-on.svg +11 -0
  119. package/dist/esm/assets/images/marker_srchchap-off.png +0 -0
  120. package/dist/esm/assets/images/marker_srchchap-on.png +0 -0
  121. package/dist/esm/assets/images/nav_control-dn.png +0 -0
  122. package/dist/esm/assets/images/nav_control-dn_ia.png +0 -0
  123. package/dist/esm/assets/images/nav_control-up.png +0 -0
  124. package/dist/esm/assets/images/nav_control-up_ia.png +0 -0
  125. package/dist/esm/assets/images/nav_control.png +0 -0
  126. package/dist/esm/assets/images/one_page_mode_icon.png +0 -0
  127. package/dist/esm/assets/images/paper-badge.png +0 -0
  128. package/dist/esm/assets/images/print_icon.png +0 -0
  129. package/dist/esm/assets/images/progressbar.gif +0 -0
  130. package/dist/esm/assets/images/right_edges.png +0 -0
  131. package/dist/esm/assets/images/slider.png +0 -0
  132. package/dist/esm/assets/images/slider_ia.png +0 -0
  133. package/dist/esm/assets/images/thumbnail_mode_icon.png +0 -0
  134. package/dist/esm/assets/images/transparent.png +0 -0
  135. package/dist/esm/assets/images/two_page_mode_icon.png +0 -0
  136. package/dist/esm/assets/images/unviewable_page.png +0 -0
  137. package/dist/esm/assets/images/zoom_in_icon.png +0 -0
  138. package/dist/esm/assets/images/zoom_out_icon.png +0 -0
  139. package/dist/esm/css/BookReader.scss +85 -0
  140. package/dist/esm/css/_BRBookmarks.scss +29 -0
  141. package/dist/esm/css/_BRComponent.scss +13 -0
  142. package/dist/esm/css/_BRfloat.scss +197 -0
  143. package/dist/esm/css/_BRicon.scss +54 -0
  144. package/dist/esm/css/_BRmain.scss +262 -0
  145. package/dist/esm/css/_BRnav.scss +354 -0
  146. package/dist/esm/css/_BRpages.scss +213 -0
  147. package/dist/esm/css/_BRsearch.scss +268 -0
  148. package/dist/esm/css/_BRtoolbar.scss +84 -0
  149. package/dist/esm/css/_BRvendor.scss +5 -0
  150. package/dist/esm/css/_TextSelection.scss +108 -0
  151. package/dist/esm/css/_colorbox.scss +52 -0
  152. package/dist/esm/css/_controls.scss +257 -0
  153. package/dist/esm/css/_icons.scss +121 -0
  154. package/dist/esm/ia-bookreader/ia-bookreader.js +141 -0
  155. package/dist/esm/jquery-wrapper.js +3 -0
  156. package/dist/esm/plugins/plugin.archive_analytics.js +72 -0
  157. package/dist/esm/plugins/plugin.autoplay.js +119 -0
  158. package/dist/esm/plugins/plugin.chapters.js +288 -0
  159. package/dist/esm/plugins/plugin.iframe.js +44 -0
  160. package/dist/esm/plugins/plugin.iiif.js +146 -0
  161. package/dist/esm/plugins/plugin.resume.js +66 -0
  162. package/dist/esm/plugins/plugin.text_selection.js +621 -0
  163. package/dist/esm/plugins/plugin.vendor-fullscreen.js +227 -0
  164. package/dist/esm/plugins/search/plugin.search.js +499 -0
  165. package/dist/esm/plugins/search/utils.js +42 -0
  166. package/dist/esm/plugins/search/view.js +360 -0
  167. package/dist/esm/plugins/tts/AbstractTTSEngine.js +282 -0
  168. package/dist/esm/plugins/tts/FestivalTTSEngine.js +192 -0
  169. package/dist/esm/plugins/tts/PageChunk.js +105 -0
  170. package/dist/esm/plugins/tts/PageChunkIterator.js +155 -0
  171. package/dist/esm/plugins/tts/WebTTSEngine.js +364 -0
  172. package/dist/esm/plugins/tts/plugin.tts.js +315 -0
  173. package/dist/esm/plugins/tts/tooltip_dict.js +14 -0
  174. package/dist/esm/plugins/tts/utils.js +79 -0
  175. package/dist/esm/plugins/url/UrlPlugin.js +197 -0
  176. package/dist/esm/plugins/url/plugin.url.js +212 -0
  177. package/dist/esm/util/browserSniffing.js +56 -0
  178. package/dist/esm/util/debouncer.js +25 -0
  179. package/dist/esm/util/docCookies.js +75 -0
  180. package/dist/esm/util/strings.js +34 -0
  181. package/jsconfig.json +1 -0
  182. package/package.json +1 -7
@@ -0,0 +1,1827 @@
1
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
2
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
3
+ /*
4
+ Copyright(c)2008-2019 Internet Archive. Software license AGPL version 3.
5
+
6
+ This file is part of BookReader.
7
+
8
+ BookReader is free software: you can redistribute it and/or modify
9
+ it under the terms of the GNU Affero General Public License as published by
10
+ the Free Software Foundation, either version 3 of the License, or
11
+ (at your option) any later version.
12
+
13
+ BookReader is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU Affero General Public License for more details.
17
+
18
+ You should have received a copy of the GNU Affero General Public License
19
+ along with BookReader. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ The BookReader source is hosted at http://github.com/internetarchive/bookreader/
22
+
23
+ */
24
+ // Needed by touch-punch
25
+ import 'jquery-ui/ui/widget.js';
26
+ import 'jquery-ui/ui/widgets/mouse.js';
27
+ import 'jquery-ui-touch-punch';
28
+ import jQuery from 'jquery';
29
+ import PACKAGE_JSON from '../package.json';
30
+ import * as utils from './BookReader/utils.js';
31
+ import { exposeOverrideable } from './BookReader/utils/classes.js';
32
+ import { Navbar } from './BookReader/Navbar/Navbar.js';
33
+ import { DEFAULT_OPTIONS, OptionsParseError } from './BookReader/options.js';
34
+ /** @typedef {import('./BookReader/options.js').BookReaderOptions} BookReaderOptions */
35
+ /** @typedef {import('./BookReader/options.js').ReductionFactor} ReductionFactor */
36
+ /** @typedef {import('./BookReader/BookModel.js').PageIndex} PageIndex */
37
+ import { EVENTS } from './BookReader/events.js';
38
+ import { Toolbar } from './BookReader/Toolbar/Toolbar.js';
39
+ import { BookModel } from './BookReader/BookModel.js';
40
+ import { Mode1Up } from './BookReader/Mode1Up.js';
41
+ import { Mode2Up } from './BookReader/Mode2Up.js';
42
+ import { ModeThumb } from './BookReader/ModeThumb';
43
+ import { ImageCache } from './BookReader/ImageCache.js';
44
+ import { PageContainer } from './BookReader/PageContainer.js';
45
+ import { NAMED_REDUCE_SETS } from './BookReader/ReduceSet';
46
+
47
+ /**
48
+ * BookReader
49
+ * @param {BookReaderOptions} options
50
+ * TODO document all options properties
51
+ * @constructor
52
+ */
53
+ export default function BookReader() {
54
+ var overrides = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
55
+ var options = jQuery.extend(true, {}, BookReader.defaultOptions, overrides, BookReader.optionOverrides);
56
+ this.setup(options);
57
+ }
58
+ BookReader.version = PACKAGE_JSON.version;
59
+
60
+ // Mode constants
61
+ /** 1 page view */
62
+ BookReader.constMode1up = 1;
63
+ /** 2 pages view */
64
+ BookReader.constMode2up = 2;
65
+ /** thumbnails view */
66
+ BookReader.constModeThumb = 3;
67
+ /** image cache */
68
+ BookReader.imageCache = null;
69
+
70
+ // Animation constants
71
+ BookReader.constNavAnimationDuration = 300;
72
+ BookReader.constResizeAnimationDuration = 100;
73
+
74
+ // Names of events that can be triggered via BookReader.prototype.trigger()
75
+ BookReader.eventNames = EVENTS;
76
+ BookReader.defaultOptions = DEFAULT_OPTIONS;
77
+
78
+ /**
79
+ * @type {BookReaderOptions}
80
+ * This is here, just in case you need to absolutely override an option.
81
+ */
82
+ BookReader.optionOverrides = {};
83
+
84
+ /**
85
+ * Setup
86
+ * It is separate from the constructor, so plugins can extend.
87
+ * @param {BookReaderOptions} options
88
+ */
89
+ BookReader.prototype.setup = function (options) {
90
+ // Store the options used to setup bookreader
91
+ this.options = options;
92
+
93
+ /** @type {number} @deprecated some past iterations set this */
94
+ this.numLeafs = undefined;
95
+
96
+ /** Overridden by plugin.search.js */
97
+ this.enableSearch = false;
98
+
99
+ /**
100
+ * Store viewModeOrder states
101
+ * @var {boolean}
102
+ */
103
+ this.viewModeOrder = [];
104
+
105
+ /**
106
+ * Used to suppress fragment change for init with canonical URLs
107
+ * @var {boolean}
108
+ */
109
+ this.suppressFragmentChange = false;
110
+
111
+ /** @type {function(): void} */
112
+ this.animationFinishedCallback = null;
113
+
114
+ // @deprecated: Instance constants. Use Class constants instead
115
+ /** 1 page view */
116
+ this.constMode1up = BookReader.constMode1up;
117
+ /** 2 pages view */
118
+ this.constMode2up = BookReader.constMode2up;
119
+ /** thumbnails view */
120
+ this.constModeThumb = BookReader.constModeThumb;
121
+
122
+ // Private properties below. Configuration should be done with options.
123
+ /** @type {number} TODO: Make private */
124
+ this.reduce = 8; /* start very small */
125
+ this.defaults = options.defaults;
126
+ this.padding = options.padding;
127
+ this.reduceSet = NAMED_REDUCE_SETS[options.reduceSet];
128
+ if (!this.reduceSet) {
129
+ console.warn("Invalid reduceSet ".concat(options.reduceSet, ". Ignoring."));
130
+ this.reduceSet = NAMED_REDUCE_SETS[DEFAULT_OPTIONS.reduceSet];
131
+ }
132
+
133
+ /** @type {number}
134
+ * can be 1 or 2 or 3 based on the display mode const value
135
+ */
136
+ this.mode = null;
137
+ this.prevReadMode = null;
138
+ this.ui = options.ui;
139
+ this.uiAutoHide = options.uiAutoHide;
140
+ this.thumbWidth = 100; // will be overridden during this._modes.modeThumb.prepare();
141
+ this.thumbRowBuffer = options.thumbRowBuffer;
142
+ this.thumbColumns = options.thumbColumns;
143
+ this.thumbMaxLoading = options.thumbMaxLoading;
144
+ this.thumbPadding = options.thumbPadding;
145
+ this.displayedRows = [];
146
+ this.displayedIndices = [];
147
+ this.animating = false;
148
+ this.flipSpeed = typeof options.flipSpeed === 'number' ? options.flipSpeed : {
149
+ 'fast': 200,
150
+ 'slow': 600
151
+ }[options.flipSpeed] || 400;
152
+ this.flipDelay = options.flipDelay;
153
+
154
+ /**
155
+ * Represents the first displayed index
156
+ * In 2up mode it will be the left page
157
+ * In 1 up mode it is the highest page
158
+ * @property {number|null} firstIndex
159
+ */
160
+ this.firstIndex = null;
161
+ this.isFullscreenActive = options.startFullscreen || false;
162
+ this.lastScroll = null;
163
+ this.showLogo = options.showLogo;
164
+ this.logoURL = options.logoURL;
165
+ this.imagesBaseURL = options.imagesBaseURL;
166
+ this.reductionFactors = options.reductionFactors;
167
+ this.onePage = options.onePage;
168
+ /** @type {import('./BookReader/Mode2Up').TwoPageState} */
169
+ this.twoPage = options.twoPage;
170
+ this.onePageMinBreakpoint = options.onePageMinBreakpoint;
171
+ this.bookTitle = options.bookTitle;
172
+ this.bookUrl = options.bookUrl;
173
+ this.bookUrlText = options.bookUrlText;
174
+ this.bookUrlTitle = options.bookUrlTitle;
175
+ this.titleLeaf = options.titleLeaf;
176
+ this.metadata = options.metadata;
177
+ this.thumbnail = options.thumbnail;
178
+ this.bookUrlMoreInfo = options.bookUrlMoreInfo;
179
+ this.enableExperimentalControls = options.enableExperimentalControls;
180
+ this.el = options.el;
181
+ this.pageProgression = options.pageProgression;
182
+ this.protected = options.protected;
183
+ this.getEmbedCode = options.getEmbedCode;
184
+ this.popup = null;
185
+
186
+ // Assign the data methods
187
+ this.data = options.data;
188
+
189
+ /** @type {{[name: string]: JQuery}} */
190
+ this.refs = {};
191
+
192
+ /** The book being displayed in BookReader*/
193
+ this.book = new BookModel(this);
194
+ if (options.getNumLeafs) this.book.getNumLeafs = options.getNumLeafs.bind(this);
195
+ if (options.getPageWidth) this.book.getPageWidth = options.getPageWidth.bind(this);
196
+ if (options.getPageHeight) this.book.getPageHeight = options.getPageHeight.bind(this);
197
+ if (options.getPageURI) this.book.getPageURI = options.getPageURI.bind(this);
198
+ if (options.getPageSide) this.book.getPageSide = options.getPageSide.bind(this);
199
+ if (options.getPageNum) this.book.getPageNum = options.getPageNum.bind(this);
200
+ if (options.getPageProp) this.book.getPageProp = options.getPageProp.bind(this);
201
+ if (options.getSpreadIndices) this.book.getSpreadIndices = options.getSpreadIndices.bind(this);
202
+ if (options.leafNumToIndex) this.book.leafNumToIndex = options.leafNumToIndex.bind(this);
203
+
204
+ /**
205
+ * @private Components are 'subchunks' of bookreader functionality, usually UI related
206
+ * They should be relatively decoupled from each other/bookreader.
207
+ * Note there are no hooks right now; components just provide methods that bookreader
208
+ * calls at the correct moments.
209
+ **/
210
+ this._components = {
211
+ navbar: new Navbar(this),
212
+ toolbar: new Toolbar(this)
213
+ };
214
+ this._modes = {
215
+ mode1Up: new Mode1Up(this, this.book),
216
+ mode2Up: new Mode2Up(this, this.book),
217
+ modeThumb: new ModeThumb(this, this.book)
218
+ };
219
+
220
+ /** Stores classes which we want to expose (selectively) some methods as overridable */
221
+ this._overrideable = {
222
+ 'book': this.book,
223
+ '_components.navbar': this._components.navbar,
224
+ '_components.toolbar': this._components.toolbar,
225
+ '_modes.mode1Up': this._modes.mode1Up,
226
+ '_modes.mode2Up': this._modes.mode2Up,
227
+ '_modes.modeThumb': this._modes.modeThumb
228
+ };
229
+
230
+ /** Image cache for general image fetching */
231
+ this.imageCache = new ImageCache(this.book, {
232
+ useSrcSet: this.options.useSrcSet,
233
+ reduceSet: this.reduceSet
234
+ });
235
+
236
+ /**
237
+ * Flag if BookReader has "focus" for keyboard shortcuts
238
+ * Initially true, set to false when:
239
+ * - BookReader scrolled out of view
240
+ * Set to true when:
241
+ * - BookReader scrolled into view
242
+ */
243
+ this.hasKeyFocus = true;
244
+ };
245
+
246
+ /**
247
+ * Get all the HTML Elements that are being/can be rendered.
248
+ * Includes cached elements which might be rendered again.
249
+ */
250
+ BookReader.prototype.getActivePageContainerElements = function () {
251
+ var containerEls = Object.values(this._modes.mode2Up.mode2UpLit.pageContainerCache).map(pc => pc.$container[0]).concat(Object.values(this._modes.mode1Up.mode1UpLit.pageContainerCache).map(pc => pc.$container[0]));
252
+ if (this.mode == this.constModeThumb) {
253
+ containerEls = containerEls.concat(this.$('.BRpagecontainer').toArray());
254
+ }
255
+ return containerEls;
256
+ };
257
+
258
+ /**
259
+ * Get the HTML Elements for the rendered page. Note there can be more than one, since
260
+ * (at least as of writing) different modes can maintain different caches.
261
+ * @param {PageIndex} pageIndex
262
+ */
263
+ BookReader.prototype.getActivePageContainerElementsForIndex = function (pageIndex) {
264
+ var _this$_modes$mode2Up$, _this$_modes$mode1Up$;
265
+ return [(_this$_modes$mode2Up$ = this._modes.mode2Up.mode2UpLit.pageContainerCache[pageIndex]) === null || _this$_modes$mode2Up$ === void 0 || (_this$_modes$mode2Up$ = _this$_modes$mode2Up$.$container) === null || _this$_modes$mode2Up$ === void 0 ? void 0 : _this$_modes$mode2Up$[0], (_this$_modes$mode1Up$ = this._modes.mode1Up.mode1UpLit.pageContainerCache[pageIndex]) === null || _this$_modes$mode1Up$ === void 0 || (_this$_modes$mode1Up$ = _this$_modes$mode1Up$.$container) === null || _this$_modes$mode1Up$ === void 0 ? void 0 : _this$_modes$mode1Up$[0], ...(this.mode == this.constModeThumb ? this.$(".pagediv".concat(pageIndex)).toArray() : [])].filter(x => x);
266
+ };
267
+ Object.defineProperty(BookReader.prototype, 'activeMode', {
268
+ /** @return {Mode1Up | Mode2Up | ModeThumb} */
269
+ get() {
270
+ return {
271
+ 1: this._modes.mode1Up,
272
+ 2: this._modes.mode2Up,
273
+ 3: this._modes.modeThumb
274
+ }[this.mode];
275
+ }
276
+ });
277
+
278
+ /**
279
+ * BookReader.util are static library functions
280
+ * At top of file so they can be used below
281
+ */
282
+ BookReader.util = utils;
283
+
284
+ /**
285
+ * Helper to merge in params in to a params object.
286
+ * It normalizes "page" into the "index" field to disambiguate and prevent concflicts
287
+ * @private
288
+ */
289
+ BookReader.prototype.extendParams = function (params, newParams) {
290
+ var modifiedNewParams = $.extend({}, newParams);
291
+ if ('undefined' != typeof modifiedNewParams.page) {
292
+ var pageIndex = this.book.parsePageString(modifiedNewParams.page);
293
+ if (!isNaN(pageIndex)) modifiedNewParams.index = pageIndex;
294
+ delete modifiedNewParams.page;
295
+ }
296
+ $.extend(params, modifiedNewParams);
297
+ };
298
+
299
+ /**
300
+ * Parses params from from various initialization contexts (url, cookie, options)
301
+ * @private
302
+ * @return {object} the parsed params
303
+ */
304
+ BookReader.prototype.initParams = function () {
305
+ var params = {};
306
+ // Flag initializing for updateFromParams()
307
+ params.init = true;
308
+
309
+ // Flag if page given in defaults or URL (not cookie)
310
+ // Used for overriding goToFirstResult in plugin.search.js
311
+ // Note: extendParams() converts params.page to index and gets rid of page
312
+ // so check and set before extendParams()
313
+ params.pageFound = false;
314
+
315
+ // True if changing the URL
316
+ params.fragmentChange = false;
317
+
318
+ // This is ordered from lowest to highest priority
319
+
320
+ // If we have a title leaf, use that as the default instead of index 0,
321
+ // but only use as default if book has a few pages
322
+ if ('undefined' != typeof this.titleLeaf && this.book.getNumLeafs() > 2) {
323
+ params.index = this.book.leafNumToIndex(this.titleLeaf);
324
+ } else {
325
+ params.index = 0;
326
+ }
327
+
328
+ // this.defaults is a string passed in the url format. eg "page/1/mode/1up"
329
+ if (this.defaults) {
330
+ var defaultParams = this.paramsFromFragment(this.defaults);
331
+ if ('undefined' != typeof defaultParams.page) {
332
+ params.pageFound = true;
333
+ }
334
+ this.extendParams(params, defaultParams);
335
+ }
336
+
337
+ // Check for Resume plugin
338
+ if (this.options.enablePageResume) {
339
+ // Check cookies
340
+ var val = this.getResumeValue();
341
+ if (val !== null) {
342
+ // If page index different from default
343
+ if (params.index !== val) {
344
+ // Show in URL
345
+ params.fragmentChange = true;
346
+ }
347
+ params.index = val;
348
+ }
349
+ }
350
+
351
+ // Check for URL plugin
352
+ if (this.options.enableUrlPlugin) {
353
+ // Params explicitly set in URL take precedence over all other methods
354
+ var urlParams = this.paramsFromFragment(this.urlReadFragment());
355
+
356
+ // Get params if hash fragment available with 'history' urlMode
357
+ var hasHashURL = !Object.keys(urlParams).length && this.urlReadHashFragment();
358
+ if (hasHashURL && this.options.urlMode === 'history') {
359
+ urlParams = this.paramsFromFragment(this.urlReadHashFragment());
360
+ }
361
+
362
+ // If there were any parameters
363
+ if (Object.keys(urlParams).length) {
364
+ if ('undefined' != typeof urlParams.page) {
365
+ params.pageFound = true;
366
+ }
367
+ this.extendParams(params, urlParams);
368
+ // Show in URL
369
+ params.fragmentChange = true;
370
+ }
371
+ }
372
+
373
+ // Check for Search plugin
374
+ if (this.options.enableSearch) {
375
+ // Go to first result only if no default or URL page
376
+ this.options.goToFirstResult = !params.pageFound;
377
+
378
+ // If initialSearchTerm not set
379
+ if (!this.options.initialSearchTerm) {
380
+ // Look for any term in URL
381
+ if (params.search) {
382
+ // Old style: /search/[term]
383
+ this.options.initialSearchTerm = params.search;
384
+ this.searchTerm = params.search;
385
+ } else {
386
+ // If we have a query string: q=[term]
387
+ var searchParams = new URLSearchParams(this.readQueryString());
388
+ var searchTerm = searchParams.get('q');
389
+ if (searchTerm) {
390
+ this.options.initialSearchTerm = utils.decodeURIComponentPlus(searchTerm);
391
+ }
392
+ }
393
+ }
394
+ }
395
+
396
+ // Set for init process, return to false at end of init()
397
+ this.suppressFragmentChange = !params.fragmentChange;
398
+ return params;
399
+ };
400
+
401
+ /**
402
+ * Allow mocking of window.location.search
403
+ */
404
+ BookReader.prototype.getLocationSearch = function () {
405
+ return window.location.search;
406
+ };
407
+
408
+ /**
409
+ * Allow mocking of window.location.hash
410
+ */
411
+ BookReader.prototype.getLocationHash = function () {
412
+ return window.location.hash;
413
+ };
414
+
415
+ /**
416
+ * Return URL or fragment querystring
417
+ */
418
+ BookReader.prototype.readQueryString = function () {
419
+ var queryString = this.getLocationSearch();
420
+ if (queryString) {
421
+ return queryString;
422
+ }
423
+ var hash = this.getLocationHash();
424
+ var found = hash.search(/\?\w+=/);
425
+ return found > -1 ? hash.slice(found) : '';
426
+ };
427
+
428
+ /**
429
+ * Determines the initial mode for starting if a mode is not already
430
+ * present in the params argument
431
+ * @param {object} params
432
+ * @return {1 | 2 | 3} the initial mode
433
+ */
434
+ BookReader.prototype.getInitialMode = function (params) {
435
+ // if mobile breakpoint, we always show this.constMode1up mode
436
+ var windowWidth = $(window).width();
437
+ var isMobile = windowWidth && windowWidth <= this.onePageMinBreakpoint;
438
+ var initialMode;
439
+ if (params.mode) {
440
+ initialMode = params.mode;
441
+ } else if (isMobile) {
442
+ initialMode = this.constMode1up;
443
+ } else {
444
+ initialMode = this.constMode2up;
445
+ }
446
+ if (!this.canSwitchToMode(initialMode)) {
447
+ initialMode = this.constMode1up;
448
+ }
449
+
450
+ // override defaults mode via `options.defaults` metadata
451
+ if (this.options.defaults) {
452
+ try {
453
+ initialMode = _modeStringToNumber(this.options.defaults);
454
+ } catch (e) {
455
+ // Can ignore this error
456
+ }
457
+ }
458
+ return initialMode;
459
+ };
460
+
461
+ /**
462
+ * Converts a mode string to a the mode numeric constant
463
+ * @param {'mode/1up'|'mode/2up'|'mode/thumb'} modeString
464
+ * @return {1 | 2 | 3}
465
+ */
466
+ export function _modeStringToNumber(modeString) {
467
+ var MAPPING = {
468
+ 'mode/1up': 1,
469
+ 'mode/2up': 2,
470
+ 'mode/thumb': 3
471
+ };
472
+ if (!(modeString in MAPPING)) {
473
+ throw new OptionsParseError("Invalid mode string: ".concat(modeString));
474
+ }
475
+ return MAPPING[modeString];
476
+ }
477
+
478
+ /**
479
+ * This is called by the client to initialize BookReader.
480
+ * It renders onto the DOM. It should only be called once.
481
+ */
482
+ BookReader.prototype.init = function () {
483
+ this.init.initComplete = false;
484
+ this.pageScale = this.reduce; // preserve current reduce
485
+
486
+ var params = this.initParams();
487
+ this.firstIndex = params.index ? params.index : 0;
488
+
489
+ // Setup Navbars and other UI
490
+ this.isTouchDevice = !!('ontouchstart' in window) || !!('msmaxtouchpoints' in window.navigator);
491
+ this.refs.$br = $(this.el).empty().removeClass().addClass("ui-" + this.ui).addClass("br-ui-" + this.ui).addClass('BookReader');
492
+
493
+ // Add a class if this is a touch enabled device
494
+ if (this.isTouchDevice) {
495
+ this.refs.$br.addClass("touch");
496
+ } else {
497
+ this.refs.$br.addClass("no-touch");
498
+ }
499
+ this.refs.$brContainer = $("<div class='BRcontainer' dir='ltr'></div>");
500
+ this.refs.$br.append(this.refs.$brContainer);
501
+
502
+ // Explicitly ensure params.mode exists for updateFromParams() below
503
+ params.mode = this.getInitialMode(params);
504
+
505
+ // Explicitly ensure this.mode exists for initNavbar() below
506
+ this.mode = params.mode;
507
+
508
+ // Display Navigation
509
+ if (this.options.showToolbar) {
510
+ this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
511
+ }
512
+ if (this.options.showNavbar) {
513
+ // default navigation
514
+ this.initNavbar();
515
+ }
516
+
517
+ // Switch navbar controls on mobile/desktop
518
+ this._components.navbar.switchNavbarControls();
519
+ this.resizeBRcontainer();
520
+ this.updateFromParams(params);
521
+ this.initUIStrings();
522
+
523
+ // Bind to events
524
+
525
+ this.bindNavigationHandlers();
526
+ this.setupKeyListeners();
527
+ this.lastScroll = new Date().getTime();
528
+ this.refs.$brContainer.on('scroll', this, function (e) {
529
+ // Note, this scroll event fires for both user, and js generated calls
530
+ // It is functioning in some cases as the primary triggerer for rendering
531
+ e.data.lastScroll = new Date().getTime();
532
+ if (e.data.constModeThumb == e.data.mode) {
533
+ e.data.drawLeafsThrottled();
534
+ }
535
+ });
536
+ if (this.options.autoResize) {
537
+ $(window).on('resize', this, function (e) {
538
+ e.data.resize();
539
+ });
540
+ $(window).on("orientationchange", this, function (e) {
541
+ e.data.resize();
542
+ }.bind(this));
543
+ }
544
+ if (this.protected) {
545
+ this.$('.BRicon.share').hide();
546
+ }
547
+
548
+ // If not searching, set to allow on-going fragment changes
549
+ if (!this.options.initialSearchTerm) {
550
+ this.suppressFragmentChange = false;
551
+ }
552
+ if (this.options.startFullscreen) {
553
+ this.enterFullscreen(true);
554
+ }
555
+ this.init.initComplete = true;
556
+ this.trigger(BookReader.eventNames.PostInit);
557
+
558
+ // Must be called after this.init.initComplete set to true to allow
559
+ // BookReader.prototype.resize to run.
560
+ };
561
+
562
+ /**
563
+ * @param {EVENTS} name
564
+ * @param {array | object} [props]
565
+ */
566
+ BookReader.prototype.trigger = function (name) {
567
+ var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this;
568
+ var eventName = 'BookReader:' + name;
569
+ utils.polyfillCustomEvent(window);
570
+ window.dispatchEvent(new CustomEvent(eventName, {
571
+ bubbles: true,
572
+ composed: true,
573
+ detail: {
574
+ props
575
+ }
576
+ }));
577
+ $(document).trigger(eventName, props);
578
+ };
579
+ BookReader.prototype.bind = function (name, callback) {
580
+ $(document).on('BookReader:' + name, callback);
581
+ };
582
+ BookReader.prototype.unbind = function (name, callback) {
583
+ $(document).off('BookReader:' + name, callback);
584
+ };
585
+
586
+ /**
587
+ * Resizes based on the container width and height
588
+ */
589
+ BookReader.prototype.resize = function () {
590
+ if (!this.init.initComplete) return;
591
+ this.resizeBRcontainer();
592
+
593
+ // Switch navbar controls on mobile/desktop
594
+ this._components.navbar.switchNavbarControls();
595
+ if (this.constMode1up == this.mode) {
596
+ if (this.onePage.autofit != 'none') {
597
+ this._modes.mode1Up.resizePageView();
598
+ this.centerPageView();
599
+ } else {
600
+ this.centerPageView();
601
+ this.displayedIndices = [];
602
+ this.drawLeafsThrottled();
603
+ }
604
+ } else if (this.constModeThumb == this.mode) {
605
+ this._modes.modeThumb.prepare();
606
+ } else {
607
+ this._modes.mode2Up.resizePageView();
608
+ }
609
+ this.trigger(BookReader.eventNames.resize);
610
+ };
611
+
612
+ /**
613
+ * Binds keyboard and keyboard focus event listeners
614
+ */
615
+ BookReader.prototype.setupKeyListeners = function () {
616
+ // Keyboard focus by BookReader in viewport
617
+ //
618
+ // Intersection observer and callback sets BookReader keyboard
619
+ // "focus" flag off when the BookReader is not in the viewport.
620
+ if (window.IntersectionObserver) {
621
+ var observer = new IntersectionObserver(entries => {
622
+ entries.forEach(entry => {
623
+ if (entry.intersectionRatio === 0) {
624
+ this.hasKeyFocus = false;
625
+ } else {
626
+ this.hasKeyFocus = true;
627
+ }
628
+ });
629
+ }, {
630
+ root: null,
631
+ rootMargin: '0px',
632
+ threshold: [0, 0.05, 1]
633
+ });
634
+ observer.observe(this.refs.$br[0]);
635
+ }
636
+
637
+ // Keyboard listeners
638
+ document.addEventListener('keydown', e => {
639
+ // Ignore if BookReader "focus" flag not set
640
+ if (!this.hasKeyFocus) {
641
+ return;
642
+ }
643
+
644
+ // Ignore if modifiers are active.
645
+ if (e.getModifierState('Control') || e.getModifierState('Alt') || e.getModifierState('Meta') || e.getModifierState('Win') /* hack for IE */) {
646
+ return;
647
+ }
648
+
649
+ // Ignore in input elements
650
+ if (utils.isInputActive()) {
651
+ return;
652
+ }
653
+
654
+ // KeyboardEvent code values:
655
+ // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values
656
+ switch (e.key) {
657
+ // Page navigation
658
+ case "Home":
659
+ e.preventDefault();
660
+ this.first();
661
+ break;
662
+ case "End":
663
+ e.preventDefault();
664
+ this.last();
665
+ break;
666
+ case "ArrowDown":
667
+ case "PageDown":
668
+ case "Down":
669
+ // hack for IE and old Gecko
670
+ // In 1up and thumb mode page scrolling handled by browser
671
+ if (this.constMode2up === this.mode) {
672
+ e.preventDefault();
673
+ this.next();
674
+ }
675
+ break;
676
+ case "ArrowUp":
677
+ case "PageUp":
678
+ case "Up":
679
+ // hack for IE and old Gecko
680
+ // In 1up and thumb mode page scrolling handled by browser
681
+ if (this.constMode2up === this.mode) {
682
+ e.preventDefault();
683
+ this.prev();
684
+ }
685
+ break;
686
+ case "ArrowLeft":
687
+ case "Left":
688
+ // hack for IE and old Gecko
689
+ // No y-scrolling in thumb mode
690
+ if (this.constModeThumb != this.mode) {
691
+ e.preventDefault();
692
+ this.left();
693
+ }
694
+ break;
695
+ case "ArrowRight":
696
+ case "Right":
697
+ // hack for IE and old Gecko
698
+ // No y-scrolling in thumb mode
699
+ if (this.constModeThumb != this.mode) {
700
+ e.preventDefault();
701
+ this.right();
702
+ }
703
+ break;
704
+ // Zoom
705
+ case '-':
706
+ case 'Subtract':
707
+ e.preventDefault();
708
+ this.zoom(-1);
709
+ break;
710
+ case '+':
711
+ case '=':
712
+ case 'Add':
713
+ e.preventDefault();
714
+ this.zoom(1);
715
+ break;
716
+ // Fullscreen
717
+ case 'F':
718
+ case 'f':
719
+ e.preventDefault();
720
+ this.toggleFullscreen();
721
+ break;
722
+ }
723
+ });
724
+ };
725
+ BookReader.prototype.drawLeafs = function () {
726
+ if (this.constMode1up == this.mode) {
727
+ // Not needed for Mode1Up anymore
728
+ return;
729
+ } else {
730
+ this.activeMode.drawLeafs();
731
+ }
732
+ };
733
+
734
+ /**
735
+ * @protected
736
+ * @param {PageIndex} index
737
+ */
738
+ BookReader.prototype._createPageContainer = function (index) {
739
+ return new PageContainer(this.book.getPage(index, false), {
740
+ isProtected: this.protected,
741
+ imageCache: this.imageCache,
742
+ loadingImage: this.imagesBaseURL + 'loading.gif'
743
+ });
744
+ };
745
+ BookReader.prototype.bindGestures = function (jElement) {
746
+ // TODO support gesture change is only iOS. Support android.
747
+ // HACK(2017-01-20) - Momentum scrolling is causing the scroll position
748
+ // to jump after zooming in on mobile device. I am able to reproduce
749
+ // when you move the book with one finger and then add another
750
+ // finger to pinch. Gestures are aware of scroll state.
751
+
752
+ var self = this;
753
+ var numTouches = 1;
754
+ jElement.unbind('touchmove').bind('touchmove', function (e) {
755
+ if (e.originalEvent.cancelable) numTouches = e.originalEvent.touches.length;
756
+ e.stopPropagation();
757
+ });
758
+ jElement.unbind('gesturechange').bind('gesturechange', function (e) {
759
+ e.preventDefault();
760
+ // These are two very important fixes to adjust for the scroll position
761
+ // issues described below
762
+ if (!(numTouches !== 2 || new Date().getTime() - self.lastScroll < 500)) {
763
+ if (e.originalEvent.scale > 1.5) {
764
+ self.zoom(1);
765
+ } else if (e.originalEvent.scale < 0.6) {
766
+ self.zoom(-1);
767
+ }
768
+ }
769
+ });
770
+ };
771
+
772
+ /**
773
+ * A throttled version of drawLeafs
774
+ */
775
+ BookReader.prototype.drawLeafsThrottled = utils.throttle(BookReader.prototype.drawLeafs, 250 // 250 ms gives quick feedback, but doesn't eat cpu
776
+ );
777
+
778
+ /**
779
+ * @param {number} direction Pass 1 to zoom in, anything else to zoom out
780
+ */
781
+ BookReader.prototype.zoom = function (direction) {
782
+ var _this$textSelectionPl;
783
+ if (direction == 1) {
784
+ this.activeMode.zoom('in');
785
+ } else {
786
+ this.activeMode.zoom('out');
787
+ }
788
+ (_this$textSelectionPl = this.textSelectionPlugin) === null || _this$textSelectionPl === void 0 || _this$textSelectionPl.stopPageFlip(this.refs.$brContainer);
789
+ return;
790
+ };
791
+
792
+ /**
793
+ * Resizes the inner container to fit within the visible space to prevent
794
+ * the top toolbar and bottom navbar from clipping the visible book
795
+ *
796
+ * @param { boolean } animate - optional
797
+ * When used, BookReader will fill the main container with the book's content.
798
+ * This is primarily for 1up view - a follow up animation to the nav animation
799
+ * So resize isn't perceived sharp/jerky
800
+ */
801
+ BookReader.prototype.resizeBRcontainer = function (animate) {
802
+ if (animate) {
803
+ this.refs.$brContainer.animate({
804
+ top: this.getToolBarHeight(),
805
+ bottom: this.getFooterHeight()
806
+ }, this.constResizeAnimationDuration, 'linear');
807
+ } else {
808
+ this.refs.$brContainer.css({
809
+ top: this.getToolBarHeight(),
810
+ bottom: this.getFooterHeight()
811
+ });
812
+ }
813
+ };
814
+ BookReader.prototype.centerPageView = function () {
815
+ var scrollWidth = this.refs.$brContainer.prop('scrollWidth');
816
+ var clientWidth = this.refs.$brContainer.prop('clientWidth');
817
+ if (scrollWidth > clientWidth) {
818
+ this.refs.$brContainer.prop('scrollLeft', (scrollWidth - clientWidth) / 2);
819
+ }
820
+ };
821
+
822
+ /**
823
+ * Quantizes the given reduction factor to closest power of two from set from 12.5% to 200%
824
+ * @param {number} reduce
825
+ * @param {ReductionFactor[]} reductionFactors
826
+ * @return {number}
827
+ */
828
+ BookReader.prototype.quantizeReduce = function (reduce, reductionFactors) {
829
+ var quantized = reductionFactors[0].reduce;
830
+ var distance = Math.abs(reduce - quantized);
831
+ for (var i = 1; i < reductionFactors.length; i++) {
832
+ var newDistance = Math.abs(reduce - reductionFactors[i].reduce);
833
+ if (newDistance < distance) {
834
+ distance = newDistance;
835
+ quantized = reductionFactors[i].reduce;
836
+ }
837
+ }
838
+ return quantized;
839
+ };
840
+
841
+ /**
842
+ * @param {number} currentReduce
843
+ * @param {'in' | 'out' | 'auto' | 'height' | 'width'} direction
844
+ * @param {ReductionFactor[]} reductionFactors Must be sorted
845
+ * @returns {ReductionFactor}
846
+ */
847
+ BookReader.prototype.nextReduce = function (currentReduce, direction, reductionFactors) {
848
+ // XXX add 'closest', to replace quantize function
849
+
850
+ if (direction === 'in') {
851
+ var newReduceIndex = 0;
852
+ for (var i = 1; i < reductionFactors.length; i++) {
853
+ if (reductionFactors[i].reduce < currentReduce) {
854
+ newReduceIndex = i;
855
+ }
856
+ }
857
+ return reductionFactors[newReduceIndex];
858
+ } else if (direction === 'out') {
859
+ var lastIndex = reductionFactors.length - 1;
860
+ var _newReduceIndex = lastIndex;
861
+ for (var _i = lastIndex; _i >= 0; _i--) {
862
+ if (reductionFactors[_i].reduce > currentReduce) {
863
+ _newReduceIndex = _i;
864
+ }
865
+ }
866
+ return reductionFactors[_newReduceIndex];
867
+ } else if (direction === 'auto') {
868
+ // If an 'auto' is specified, use that
869
+ var autoMatch = reductionFactors.find(rf => rf.autofit == 'auto');
870
+ if (autoMatch) return autoMatch;
871
+
872
+ // Otherwise, choose the least reduction from height/width
873
+ var candidates = reductionFactors.filter(_ref => {
874
+ var {
875
+ autofit
876
+ } = _ref;
877
+ return autofit == 'height' || autofit == 'width';
878
+ });
879
+ var choice = null;
880
+ for (var _i2 = 0; _i2 < candidates.length; _i2++) {
881
+ if (choice === null || choice.reduce < candidates[_i2].reduce) {
882
+ choice = candidates[_i2];
883
+ }
884
+ }
885
+ if (choice) return choice;
886
+ } else if (direction === 'height' || direction === 'width') {
887
+ // Asked for specific autofit mode
888
+ var match = reductionFactors.find(rf => rf.autofit == direction);
889
+ if (match) return match;
890
+ }
891
+ return reductionFactors[0];
892
+ };
893
+
894
+ /**
895
+ * @param {ReductionFactor} a
896
+ * @param {ReductionFactor} b
897
+ */
898
+ BookReader.prototype._reduceSort = (a, b) => a.reduce - b.reduce;
899
+
900
+ /**
901
+ * Attempts to jump to page
902
+ * @param {string}
903
+ * @return {boolean} Returns true if page could be found, false otherwise.
904
+ */
905
+ BookReader.prototype.jumpToPage = function (pageNum) {
906
+ var pageIndex = this.book.parsePageString(pageNum);
907
+ if ('undefined' != typeof pageIndex) {
908
+ this.jumpToIndex(pageIndex);
909
+ return true;
910
+ }
911
+
912
+ // Page not found
913
+ return false;
914
+ };
915
+
916
+ /**
917
+ * Check whether the specified index is currently displayed
918
+ * @param {PageIndex} index
919
+ */
920
+ BookReader.prototype._isIndexDisplayed = function (index) {
921
+ return this.displayedIndices ? this.displayedIndices.includes(index) : this.currentIndex() == index;
922
+ };
923
+
924
+ /**
925
+ * Changes the current page
926
+ * @param {PageIndex} index
927
+ * @param {number} [pageX]
928
+ * @param {number} [pageY]
929
+ * @param {boolean} [noAnimate]
930
+ */
931
+ BookReader.prototype.jumpToIndex = function (index, pageX, pageY, noAnimate) {
932
+ // Don't jump into specific unviewable page
933
+ var page = this.book.getPage(index);
934
+ if (!page.isViewable && page.unviewablesStart != page.index) {
935
+ // If already in unviewable range, jump to end of that range
936
+ var alreadyInPreview = this._isIndexDisplayed(page.unviewablesStart);
937
+ var newIndex = alreadyInPreview ? page.findNext({
938
+ combineConsecutiveUnviewables: true
939
+ }).index : page.unviewablesStart;
940
+ return this.jumpToIndex(newIndex, pageX, pageY, noAnimate);
941
+ }
942
+ this.trigger(BookReader.eventNames.stop);
943
+ this.activeMode.jumpToIndex(index, pageX, pageY, noAnimate);
944
+ };
945
+
946
+ /**
947
+ * Return mode or 1up if initial thumb
948
+ * @param {number}
949
+ */
950
+ BookReader.prototype.getPrevReadMode = function (mode) {
951
+ if (mode === BookReader.constMode1up || mode === BookReader.constMode2up) {
952
+ return mode;
953
+ } else if (this.prevReadMode === null) {
954
+ // Initial thumb, return 1up
955
+ return BookReader.constMode1up;
956
+ }
957
+ };
958
+
959
+ /**
960
+ * Switches the mode (eg 1up 2up thumb)
961
+ * @param {number}
962
+ * @param {object} [options]
963
+ * @param {boolean} [options.suppressFragmentChange = false]
964
+ * @param {boolean} [options.onInit = false] - this
965
+ */
966
+ BookReader.prototype.switchMode = function (mode) {
967
+ var _this$textSelectionPl2;
968
+ var {
969
+ suppressFragmentChange = false,
970
+ init = false,
971
+ pageFound = false
972
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
973
+ // Skip checks before init() complete
974
+ if (this.init.initComplete) {
975
+ if (mode === this.mode) {
976
+ return;
977
+ }
978
+ if (!this.canSwitchToMode(mode)) {
979
+ return;
980
+ }
981
+ }
982
+ this.trigger(BookReader.eventNames.stop);
983
+ this.prevReadMode = this.getPrevReadMode(this.mode);
984
+ if (this.mode != mode) {
985
+ var _this$activeMode$unpr, _this$activeMode;
986
+ (_this$activeMode$unpr = (_this$activeMode = this.activeMode).unprepare) === null || _this$activeMode$unpr === void 0 || _this$activeMode$unpr.call(_this$activeMode);
987
+ }
988
+ this.mode = mode;
989
+
990
+ // reinstate scale if moving from thumbnail view
991
+ if (this.pageScale !== this.reduce) {
992
+ this.reduce = this.pageScale;
993
+ }
994
+
995
+ // $$$ TODO preserve center of view when switching between mode
996
+ // See https://bugs.edge.launchpad.net/gnubook/+bug/416682
997
+
998
+ // XXX maybe better to preserve zoom in each mode
999
+ if (this.constMode1up == mode) {
1000
+ this._modes.mode1Up.prepare();
1001
+ } else if (this.constModeThumb == mode) {
1002
+ this.reduce = this.quantizeReduce(this.reduce, this.reductionFactors);
1003
+ this._modes.modeThumb.prepare();
1004
+ } else {
1005
+ this._modes.mode2Up.prepare();
1006
+ }
1007
+ if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1008
+ this.trigger(BookReader.eventNames.fragmentChange);
1009
+ }
1010
+ var eventName = mode + 'PageViewSelected';
1011
+ this.trigger(BookReader.eventNames[eventName]);
1012
+ (_this$textSelectionPl2 = this.textSelectionPlugin) === null || _this$textSelectionPl2 === void 0 || _this$textSelectionPl2.stopPageFlip(this.refs.$brContainer);
1013
+ };
1014
+ BookReader.prototype.updateBrClasses = function () {
1015
+ var modeToClass = {};
1016
+ modeToClass[this.constMode1up] = 'BRmode1up';
1017
+ modeToClass[this.constMode2up] = 'BRmode2up';
1018
+ modeToClass[this.constModeThumb] = 'BRmodeThumb';
1019
+ this.refs.$br.removeClass('BRmode1up BRmode2up BRmodeThumb').addClass(modeToClass[this.mode]);
1020
+ if (this.isFullscreen()) {
1021
+ this.refs.$br.addClass('fullscreenActive');
1022
+ $(document.body).addClass('BRfullscreenActive');
1023
+ } else {
1024
+ this.refs.$br.removeClass('fullscreenActive');
1025
+ $(document.body).removeClass('BRfullscreenActive');
1026
+ }
1027
+ };
1028
+ BookReader.prototype.isFullscreen = function () {
1029
+ return this.isFullscreenActive;
1030
+ };
1031
+
1032
+ /**
1033
+ * Toggles fullscreen
1034
+ * @param { boolean } bindKeyboardControls
1035
+ */
1036
+ BookReader.prototype.toggleFullscreen = /*#__PURE__*/_asyncToGenerator(function* () {
1037
+ var bindKeyboardControls = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
1038
+ if (this.isFullscreen()) {
1039
+ yield this.exitFullScreen();
1040
+ } else {
1041
+ yield this.enterFullscreen(bindKeyboardControls);
1042
+ }
1043
+ });
1044
+
1045
+ /**
1046
+ * Enters fullscreen
1047
+ * including:
1048
+ * - binds keyboard controls
1049
+ * - fires custom event
1050
+ * @param { boolean } bindKeyboardControls
1051
+ */
1052
+ BookReader.prototype.enterFullscreen = /*#__PURE__*/_asyncToGenerator(function* () {
1053
+ var _this$textSelectionPl3;
1054
+ var bindKeyboardControls = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
1055
+ this.refs.$br.addClass('BRfullscreenAnimation');
1056
+ var currentIndex = this.currentIndex();
1057
+ if (bindKeyboardControls) {
1058
+ this._fullscreenCloseHandler = e => {
1059
+ if (e.keyCode === 27) this.toggleFullscreen();
1060
+ };
1061
+ $(document).on("keyup", this._fullscreenCloseHandler);
1062
+ }
1063
+ var windowWidth = $(window).width();
1064
+ if (windowWidth <= this.onePageMinBreakpoint) {
1065
+ this.switchMode(this.constMode1up);
1066
+ }
1067
+ this.isFullscreenActive = true;
1068
+ // prioritize class updates so CSS can propagate
1069
+ this.updateBrClasses();
1070
+ if (this.activeMode instanceof Mode1Up) {
1071
+ this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this.book.getPage(currentIndex));
1072
+ // Need the new scale to be applied before calling jumpToIndex
1073
+ this.activeMode.mode1UpLit.requestUpdate();
1074
+ yield this.activeMode.mode1UpLit.updateComplete;
1075
+ }
1076
+ this.jumpToIndex(currentIndex);
1077
+ (_this$textSelectionPl3 = this.textSelectionPlugin) === null || _this$textSelectionPl3 === void 0 || _this$textSelectionPl3.stopPageFlip(this.refs.$brContainer);
1078
+ // Add "?view=theater"
1079
+ this.trigger(BookReader.eventNames.fragmentChange);
1080
+ // trigger event here, so that animations,
1081
+ // class updates happen before book-nav relays to web components
1082
+ this.trigger(BookReader.eventNames.fullscreenToggled);
1083
+
1084
+ // resize book after all events & css updates
1085
+ yield new Promise(resolve => setTimeout(resolve, 0));
1086
+ this.resize();
1087
+ this.refs.$br.removeClass('BRfullscreenAnimation');
1088
+ });
1089
+
1090
+ /**
1091
+ * Exits fullscreen
1092
+ * - toggles fullscreen
1093
+ * - binds keyboard controls
1094
+ * - fires custom event
1095
+ * @param { boolean } bindKeyboardControls
1096
+ */
1097
+ BookReader.prototype.exitFullScreen = /*#__PURE__*/_asyncToGenerator(function* () {
1098
+ var _this$textSelectionPl4;
1099
+ this.refs.$br.addClass('BRfullscreenAnimation');
1100
+ $(document).off('keyup', this._fullscreenCloseHandler);
1101
+ var windowWidth = $(window).width();
1102
+ var canShow2up = this.options.controls.twoPage.visible;
1103
+ if (canShow2up && windowWidth <= this.onePageMinBreakpoint) {
1104
+ this.switchMode(this.constMode2up);
1105
+ }
1106
+ this.isFullscreenActive = false;
1107
+ // Trigger fullscreen event immediately
1108
+ // so that book-nav can relay to web components
1109
+ this.trigger(BookReader.eventNames.fullscreenToggled);
1110
+ this.updateBrClasses();
1111
+ yield new Promise(resolve => setTimeout(resolve, 0));
1112
+ this.resize();
1113
+ if (this.activeMode instanceof Mode1Up) {
1114
+ this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this.book.getPage(this.currentIndex()));
1115
+ this.activeMode.mode1UpLit.requestUpdate();
1116
+ yield this.activeMode.mode1UpLit.updateComplete;
1117
+ }
1118
+ (_this$textSelectionPl4 = this.textSelectionPlugin) === null || _this$textSelectionPl4 === void 0 || _this$textSelectionPl4.stopPageFlip(this.refs.$brContainer);
1119
+ // Remove "?view=theater"
1120
+ this.trigger(BookReader.eventNames.fragmentChange);
1121
+ this.refs.$br.removeClass('BRfullscreenAnimation');
1122
+ });
1123
+
1124
+ /**
1125
+ * Returns the currently active index
1126
+ * @return {number}
1127
+ * @throws
1128
+ */
1129
+ BookReader.prototype.currentIndex = function () {
1130
+ // $$$ we should be cleaner with our idea of which index is active in 1up/2up
1131
+ if (this.mode == this.constMode1up || this.mode == this.constModeThumb) {
1132
+ return this.firstIndex; // $$$ TODO page in center of view would be better
1133
+ } else if (this.mode == this.constMode2up) {
1134
+ // Only allow indices that are actually present in book
1135
+ return utils.clamp(this.firstIndex, 0, this.book.getNumLeafs() - 1);
1136
+ } else {
1137
+ throw 'currentIndex called for unimplemented mode ' + this.mode;
1138
+ }
1139
+ };
1140
+
1141
+ /**
1142
+ * Setter for this.firstIndex
1143
+ * Also triggers an event and updates the navbar slider position
1144
+ * @param {number} index
1145
+ * @param {object} [options]
1146
+ * @param {boolean} [options.suppressFragmentChange = false]
1147
+ */
1148
+ BookReader.prototype.updateFirstIndex = function (index) {
1149
+ var {
1150
+ suppressFragmentChange = false
1151
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1152
+ // If there's no change, do nothing
1153
+ if (this.firstIndex === index) return;
1154
+ this.firstIndex = index;
1155
+ if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1156
+ this.trigger(BookReader.eventNames.fragmentChange);
1157
+ }
1158
+ // If there's an initial search we stop suppressing global URL changes
1159
+ // when local suppression ends
1160
+ // This seems to correctly handle multiple calls during mode/1up
1161
+ if (this.options.initialSearchTerm && !suppressFragmentChange) {
1162
+ this.suppressFragmentChange = false;
1163
+ }
1164
+ this.trigger(BookReader.eventNames.pageChanged);
1165
+
1166
+ // event to know if user is actively reading
1167
+ this.trigger(BookReader.eventNames.userAction);
1168
+ this._components.navbar.updateNavIndexThrottled(index);
1169
+ };
1170
+
1171
+ /**
1172
+ * Flip the right page over onto the left
1173
+ */
1174
+ BookReader.prototype.right = function () {
1175
+ if ('rl' != this.pageProgression) {
1176
+ this.next();
1177
+ } else {
1178
+ this.prev();
1179
+ }
1180
+ };
1181
+
1182
+ /**
1183
+ * Flip to the rightmost page
1184
+ */
1185
+ BookReader.prototype.rightmost = function () {
1186
+ if ('rl' != this.pageProgression) {
1187
+ this.last();
1188
+ } else {
1189
+ this.first();
1190
+ }
1191
+ };
1192
+
1193
+ /**
1194
+ * Flip the left page over onto the right
1195
+ */
1196
+ BookReader.prototype.left = function () {
1197
+ if ('rl' != this.pageProgression) {
1198
+ this.prev();
1199
+ } else {
1200
+ this.next();
1201
+ }
1202
+ };
1203
+
1204
+ /**
1205
+ * Flip to the leftmost page
1206
+ */
1207
+ BookReader.prototype.leftmost = function () {
1208
+ if ('rl' != this.pageProgression) {
1209
+ this.first();
1210
+ } else {
1211
+ this.last();
1212
+ }
1213
+ };
1214
+ BookReader.prototype.next = function () {
1215
+ var {
1216
+ triggerStop = true
1217
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1218
+ if (this.constMode2up == this.mode) {
1219
+ if (triggerStop) this.trigger(BookReader.eventNames.stop);
1220
+ this._modes.mode2Up.mode2UpLit.flipAnimation('next');
1221
+ } else {
1222
+ if (this.firstIndex < this.book.getNumLeafs() - 1) {
1223
+ this.jumpToIndex(this.firstIndex + 1);
1224
+ }
1225
+ }
1226
+ };
1227
+ BookReader.prototype.prev = function () {
1228
+ var {
1229
+ triggerStop = true
1230
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1231
+ var isOnFrontPage = this.firstIndex < 1;
1232
+ if (isOnFrontPage) return;
1233
+ if (this.constMode2up == this.mode) {
1234
+ if (triggerStop) this.trigger(BookReader.eventNames.stop);
1235
+ this._modes.mode2Up.mode2UpLit.flipAnimation('prev');
1236
+ } else {
1237
+ if (this.firstIndex >= 1) {
1238
+ this.jumpToIndex(this.firstIndex - 1);
1239
+ }
1240
+ }
1241
+ };
1242
+ BookReader.prototype.first = function () {
1243
+ this.jumpToIndex(0);
1244
+ };
1245
+ BookReader.prototype.last = function () {
1246
+ this.jumpToIndex(this.book.getNumLeafs() - 1);
1247
+ };
1248
+
1249
+ /**
1250
+ * @template TClass extends { br: BookReader }
1251
+ * Helper method to expose a method onto BookReader from a composed class.
1252
+ * Only composed classes in BookReader._overridable can be exposed in this
1253
+ * way.
1254
+ * @param {new () => TClass} Class
1255
+ * @param {keyof BookReader['_overrideable']} classKey
1256
+ * @param {keyof TClass} method
1257
+ * @param {string} [brMethod]
1258
+ */
1259
+ function exposeOverrideableMethod(Class, classKey, method) {
1260
+ var brMethod = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : method;
1261
+ /** @type {function(TClass): BookReader} */
1262
+ var classToBr = cls => cls.br;
1263
+ /** @type {function(BookReader): TClass} */
1264
+ var brToClass = br => br._overrideable[classKey];
1265
+ exposeOverrideable(Class, method, classToBr, BookReader, brMethod, brToClass);
1266
+ }
1267
+
1268
+ /***********************/
1269
+ /** Navbar extensions **/
1270
+ /***********************/
1271
+ /** This cannot be removed yet because plugin.tts.js overrides it */
1272
+ BookReader.prototype.initNavbar = Navbar.prototype.init;
1273
+ exposeOverrideableMethod(Navbar, '_components.navbar', 'init', 'initNavbar');
1274
+
1275
+ /************************/
1276
+ /** Toolbar extensions **/
1277
+ /************************/
1278
+ BookReader.prototype.buildToolbarElement = Toolbar.prototype.buildToolbarElement;
1279
+ exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildToolbarElement');
1280
+ BookReader.prototype.initToolbar = Toolbar.prototype.initToolbar;
1281
+ exposeOverrideableMethod(Toolbar, '_components.toolbar', 'initToolbar');
1282
+ BookReader.prototype.buildShareDiv = Toolbar.prototype.buildShareDiv;
1283
+ exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildShareDiv');
1284
+ BookReader.prototype.buildInfoDiv = Toolbar.prototype.buildInfoDiv;
1285
+ exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildInfoDiv');
1286
+ BookReader.prototype.getToolBarHeight = Toolbar.prototype.getToolBarHeight;
1287
+ exposeOverrideableMethod(Toolbar, '_components.toolbar', 'getToolBarHeight');
1288
+
1289
+ /**
1290
+ * Bind navigation handlers
1291
+ */
1292
+ BookReader.prototype.bindNavigationHandlers = function () {
1293
+ var self = this;
1294
+ var jIcons = this.$('.BRicon');
1295
+
1296
+ // Map of jIcon class -> click handler
1297
+ var navigationControls = {
1298
+ book_left: () => {
1299
+ this.trigger(BookReader.eventNames.stop);
1300
+ this.left();
1301
+ },
1302
+ book_right: () => {
1303
+ this.trigger(BookReader.eventNames.stop);
1304
+ this.right();
1305
+ },
1306
+ book_top: this.first.bind(this),
1307
+ book_bottom: this.last.bind(this),
1308
+ book_leftmost: this.leftmost.bind(this),
1309
+ book_rightmost: this.rightmost.bind(this),
1310
+ onepg: () => {
1311
+ this.switchMode(self.constMode1up);
1312
+ },
1313
+ thumb: () => {
1314
+ this.switchMode(self.constModeThumb);
1315
+ },
1316
+ twopg: () => {
1317
+ this.switchMode(self.constMode2up);
1318
+ },
1319
+ zoom_in: () => {
1320
+ this.trigger(BookReader.eventNames.stop);
1321
+ this.zoom(1);
1322
+ this.trigger(BookReader.eventNames.zoomIn);
1323
+ },
1324
+ zoom_out: () => {
1325
+ this.trigger(BookReader.eventNames.stop);
1326
+ this.zoom(-1);
1327
+ this.trigger(BookReader.eventNames.zoomOut);
1328
+ },
1329
+ full: () => {
1330
+ if (this.ui == 'embed') {
1331
+ var url = this.$('.BRembedreturn a').attr('href');
1332
+ window.open(url);
1333
+ } else {
1334
+ this.toggleFullscreen();
1335
+ }
1336
+ }
1337
+ };
1338
+
1339
+ // custom event for auto-loan-renew in ia-book-actions
1340
+ // - to know if user is actively reading
1341
+ this.$('nav.BRcontrols li button').on('click', () => {
1342
+ this.trigger(BookReader.eventNames.userAction);
1343
+ });
1344
+ var _loop = function _loop(control) {
1345
+ jIcons.filter(".".concat(control)).on('click.bindNavigationHandlers', () => {
1346
+ navigationControls[control]();
1347
+ return false;
1348
+ });
1349
+ };
1350
+ for (var control in navigationControls) {
1351
+ _loop(control);
1352
+ }
1353
+ var $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
1354
+ var $brNavCntlTopEl = this.$('.BRnavCntlTop');
1355
+ this.$('.BRnavCntl').click(function () {
1356
+ var promises = [];
1357
+ // TODO don't use magic constants
1358
+ // TODO move this to a function
1359
+ if ($brNavCntlBtmEl.hasClass('BRdn')) {
1360
+ if (self.refs.$BRtoolbar) promises.push(self.refs.$BRtoolbar.animate({
1361
+ top: self.getToolBarHeight() * -1
1362
+ }).promise());
1363
+ promises.push(self.$('.BRfooter').animate({
1364
+ bottom: self.getFooterHeight() * -1
1365
+ }).promise());
1366
+ $brNavCntlBtmEl.addClass('BRup').removeClass('BRdn');
1367
+ $brNavCntlTopEl.addClass('BRdn').removeClass('BRup');
1368
+ self.$('.BRnavCntlBtm.BRnavCntl').animate({
1369
+ height: '45px'
1370
+ });
1371
+ self.$('.BRnavCntl').delay(1000).animate({
1372
+ opacity: .75
1373
+ }, 1000);
1374
+ } else {
1375
+ if (self.refs.$BRtoolbar) promises.push(self.refs.$BRtoolbar.animate({
1376
+ top: 0
1377
+ }).promise());
1378
+ promises.push(self.$('.BRfooter').animate({
1379
+ bottom: 0
1380
+ }).promise());
1381
+ $brNavCntlBtmEl.addClass('BRdn').removeClass('BRup');
1382
+ $brNavCntlTopEl.addClass('BRup').removeClass('BRdn');
1383
+ self.$('.BRnavCntlBtm.BRnavCntl').animate({
1384
+ height: '30px'
1385
+ });
1386
+ self.$('.BRvavCntl').animate({
1387
+ opacity: 1
1388
+ });
1389
+ }
1390
+ $.when.apply($, promises).done(function () {
1391
+ // Only do full resize in auto mode and need to recalc. size
1392
+ if (self.mode == self.constMode2up && self.twoPage.autofit != null && self.twoPage.autofit != 'none') {
1393
+ self.resize();
1394
+ } else if (self.mode == self.constMode1up && self.onePage.autofit != null && self.onePage.autofit != 'none') {
1395
+ self.resize();
1396
+ } else {
1397
+ // Don't do a full resize to avoid redrawing images
1398
+ self.resizeBRcontainer();
1399
+ }
1400
+ });
1401
+ });
1402
+ $brNavCntlBtmEl.on("mouseover", function () {
1403
+ if ($(this).hasClass('BRup')) {
1404
+ self.$('.BRnavCntl').animate({
1405
+ opacity: 1
1406
+ }, 250);
1407
+ }
1408
+ }).on("mouseleave", function () {
1409
+ if ($(this).hasClass('BRup')) {
1410
+ self.$('.BRnavCntl').animate({
1411
+ opacity: .75
1412
+ }, 250);
1413
+ }
1414
+ });
1415
+ $brNavCntlTopEl.on("mouseover", function () {
1416
+ if ($(this).hasClass('BRdn')) {
1417
+ self.$('.BRnavCntl').animate({
1418
+ opacity: 1
1419
+ }, 250);
1420
+ }
1421
+ }).on("mouseleave", function () {
1422
+ if ($(this).hasClass('BRdn')) {
1423
+ self.$('.BRnavCntl').animate({
1424
+ opacity: .75
1425
+ }, 250);
1426
+ }
1427
+ });
1428
+ };
1429
+
1430
+ /**************************/
1431
+ /** BookModel extensions **/
1432
+ /**************************/
1433
+ // Must modify petabox extension, which expects this on the prototype
1434
+ // before removing.
1435
+ BookReader.prototype.getPageURI = BookModel.prototype.getPageURI;
1436
+ exposeOverrideableMethod(BookModel, 'book', 'getPageURI');
1437
+
1438
+ // Parameter related functions
1439
+
1440
+ /**
1441
+ * Update from the params object
1442
+ * @param {Object}
1443
+ */
1444
+ BookReader.prototype.updateFromParams = function (params) {
1445
+ // Set init, fragment change options for switchMode()
1446
+ var {
1447
+ mode = 0,
1448
+ init = false,
1449
+ fragmentChange = false
1450
+ } = params;
1451
+ if (mode) {
1452
+ this.switchMode(mode, {
1453
+ init: init,
1454
+ suppressFragmentChange: !fragmentChange
1455
+ });
1456
+ }
1457
+
1458
+ // $$$ process /zoom
1459
+ // We only respect page if index is not set
1460
+ if ('undefined' != typeof params.index) {
1461
+ if (params.index != this.currentIndex()) {
1462
+ this.jumpToIndex(params.index);
1463
+ }
1464
+ } else if ('undefined' != typeof params.page) {
1465
+ // $$$ this assumes page numbers are unique
1466
+ if (params.page != this.book.getPageNum(this.currentIndex())) {
1467
+ this.jumpToPage(params.page);
1468
+ }
1469
+ }
1470
+
1471
+ // process /search
1472
+ // @deprecated for urlMode 'history'
1473
+ // Continues to work for urlMode 'hash'
1474
+ if (this.enableSearch && 'undefined' != typeof params.search) {
1475
+ if (this.searchTerm !== params.search) {
1476
+ this.$('.BRsearchInput').val(params.search);
1477
+ }
1478
+ }
1479
+
1480
+ // $$$ process /region
1481
+ // $$$ process /highlight
1482
+
1483
+ // $$$ process /theme
1484
+ if (this.enableThemesPlugin && 'undefined' != typeof params.theme) {
1485
+ this.updateTheme(params.theme);
1486
+ }
1487
+ };
1488
+
1489
+ /**
1490
+ * Returns true if we can switch to the requested mode
1491
+ * @param {number} mode
1492
+ * @return {boolean}
1493
+ */
1494
+ BookReader.prototype.canSwitchToMode = function (mode) {
1495
+ if (mode == this.constMode2up || mode == this.constModeThumb) {
1496
+ // check there are enough pages to display
1497
+ // $$$ this is a workaround for the mis-feature that we can't display
1498
+ // short books in 2up mode
1499
+ if (this.book.getNumLeafs() < 2) {
1500
+ return false;
1501
+ }
1502
+ }
1503
+ return true;
1504
+ };
1505
+
1506
+ /**
1507
+ * Returns the page URI or transparent image if out of range
1508
+ * Also makes the reduce argument optional
1509
+ * @param {number} index
1510
+ * @param {number} [reduce]
1511
+ * @param {number} [rotate]
1512
+ * @return {string}
1513
+ */
1514
+ BookReader.prototype._getPageURI = function (index, reduce, rotate) {
1515
+ var page = this.book.getPage(index, false);
1516
+ // Synthesize page
1517
+ if (!page) return this.imagesBaseURL + "transparent.png";
1518
+ if ('undefined' == typeof reduce) {
1519
+ // reduce not passed in
1520
+ // $$$ this probably won't work for thumbnail mode
1521
+ reduce = page.height / this.twoPage.height;
1522
+ }
1523
+ return page.getURI(reduce, rotate);
1524
+ };
1525
+
1526
+ /**
1527
+ * @param {string} msg
1528
+ * @param {function|undefined} onCloseCallback
1529
+ */
1530
+ BookReader.prototype.showProgressPopup = function (msg, onCloseCallback) {
1531
+ if (this.popup) return;
1532
+ this.popup = document.createElement("div");
1533
+ $(this.popup).prop('className', 'BRprogresspopup');
1534
+ if (typeof onCloseCallback === 'function') {
1535
+ var closeButton = document.createElement('button');
1536
+ closeButton.setAttribute('title', 'close');
1537
+ closeButton.setAttribute('class', 'close-popup');
1538
+ var icon = document.createElement('span');
1539
+ icon.setAttribute('class', 'icon icon-close-dark');
1540
+ $(closeButton).append(icon);
1541
+ closeButton.addEventListener('click', () => {
1542
+ onCloseCallback();
1543
+ this.removeProgressPopup();
1544
+ });
1545
+ $(this.popup).append(closeButton);
1546
+ }
1547
+ var bar = document.createElement("div");
1548
+ $(bar).css({
1549
+ height: '20px'
1550
+ }).prop('className', 'BRprogressbar');
1551
+ $(this.popup).append(bar);
1552
+ if (msg) {
1553
+ var msgdiv = document.createElement("div");
1554
+ msgdiv.innerHTML = msg;
1555
+ $(this.popup).append(msgdiv);
1556
+ }
1557
+ $(this.popup).appendTo(this.refs.$br);
1558
+ };
1559
+ BookReader.prototype.removeProgressPopup = function () {
1560
+ $(this.popup).remove();
1561
+ this.$('.BRprogresspopup').remove();
1562
+ this.popup = null;
1563
+ };
1564
+
1565
+ /**
1566
+ * Can be overridden
1567
+ */
1568
+ BookReader.prototype.initUIStrings = function () {
1569
+ // Navigation handlers will be bound after all UI is in place -- makes moving icons between
1570
+ // the toolbar and nav bar easier
1571
+
1572
+ // Setup tooltips -- later we could load these from a file for i18n
1573
+ var titles = {
1574
+ '.logo': 'Go to Archive.org',
1575
+ // $$$ update after getting OL record
1576
+ '.zoom_in': 'Zoom in',
1577
+ '.zoom_out': 'Zoom out',
1578
+ '.onepg': 'One-page view',
1579
+ '.twopg': 'Two-page view',
1580
+ '.thumb': 'Thumbnail view',
1581
+ '.print': 'Print this page',
1582
+ '.embed': 'Embed BookReader',
1583
+ '.link': 'Link to this book (and page)',
1584
+ '.bookmark': 'Bookmark this page',
1585
+ '.share': 'Share this book',
1586
+ '.info': 'About this book',
1587
+ '.full': 'Toggle fullscreen',
1588
+ '.book_left': 'Flip left',
1589
+ '.book_right': 'Flip right',
1590
+ '.play': 'Play',
1591
+ '.pause': 'Pause',
1592
+ '.BRdn': 'Show/hide nav bar',
1593
+ // Would have to keep updating on state change to have just "Hide nav bar"
1594
+ '.BRup': 'Show/hide nav bar',
1595
+ '.book_top': 'First page',
1596
+ '.book_bottom': 'Last page',
1597
+ '.book_leftmost': 'First page',
1598
+ '.book_rightmost': 'Last page'
1599
+ };
1600
+ if ('rl' == this.pageProgression) {
1601
+ titles['.book_leftmost'] = 'Last page';
1602
+ titles['.book_rightmost'] = 'First page';
1603
+ }
1604
+ for (var icon in titles) {
1605
+ this.$(icon).prop('title', titles[icon]);
1606
+ }
1607
+ };
1608
+
1609
+ /**
1610
+ * Reloads images. Useful when some images might have failed.
1611
+ */
1612
+ BookReader.prototype.reloadImages = function () {
1613
+ this.refs.$brContainer.find('img').each(function (index, elem) {
1614
+ if (!elem.complete || elem.naturalHeight === 0) {
1615
+ var src = elem.src;
1616
+ elem.src = '';
1617
+ setTimeout(function () {
1618
+ elem.src = src;
1619
+ }, 1000);
1620
+ }
1621
+ });
1622
+ };
1623
+
1624
+ /**
1625
+ * @param {boolean} ignoreDisplay - bypass the display check
1626
+ * @return {number}
1627
+ */
1628
+ BookReader.prototype.getFooterHeight = function () {
1629
+ var $heightEl = this.mode == this.constMode2up ? this.refs.$BRfooter : this.refs.$BRnav;
1630
+ if ($heightEl && this.refs.$BRfooter) {
1631
+ var outerHeight = $heightEl.outerHeight();
1632
+ var bottom = parseInt(this.refs.$BRfooter.css('bottom'));
1633
+ if (!isNaN(outerHeight) && !isNaN(bottom)) {
1634
+ return outerHeight + bottom;
1635
+ }
1636
+ }
1637
+ return 0;
1638
+ };
1639
+
1640
+ // Basic Usage built-in Methods (can be overridden through options)
1641
+ // This implementation uses options.data value for populating BookReader
1642
+
1643
+ /**
1644
+ * Create a params object from the current parameters.
1645
+ * @return {Object}
1646
+ */
1647
+ BookReader.prototype.paramsFromCurrent = function () {
1648
+ var params = {};
1649
+
1650
+ // Path params
1651
+ var index = this.currentIndex();
1652
+ var pageNum = this.book.getPageNum(index);
1653
+ if (pageNum === 0 || pageNum) {
1654
+ params.page = pageNum;
1655
+ }
1656
+ params.index = index;
1657
+ params.mode = this.mode;
1658
+
1659
+ // Unused params
1660
+ // $$$ highlight
1661
+ // $$$ region
1662
+
1663
+ // Querystring params
1664
+ // View
1665
+ var fullscreenView = 'theater';
1666
+ if (this.isFullscreenActive) {
1667
+ params.view = fullscreenView;
1668
+ }
1669
+ // Search
1670
+ if (this.enableSearch) {
1671
+ params.search = this.searchTerm;
1672
+ }
1673
+ return params;
1674
+ };
1675
+
1676
+ /**
1677
+ * Return an object with configuration parameters from a fragment string.
1678
+ *
1679
+ * Fragments are formatted as a URL path but may be used outside of URLs as a
1680
+ * serialization format for BookReader parameters
1681
+ *
1682
+ * @see http://openlibrary.org/dev/docs/bookurls for fragment syntax
1683
+ *
1684
+ * @param {string} fragment initial # is allowed for backwards compatibility
1685
+ * but is deprecated
1686
+ * @return {Object}
1687
+ */
1688
+ BookReader.prototype.paramsFromFragment = function (fragment) {
1689
+ var params = {};
1690
+
1691
+ // For backwards compatibility we allow an initial # character
1692
+ // (as from window.location.hash) but don't require it
1693
+ if (fragment.substr(0, 1) == '#') {
1694
+ fragment = fragment.substr(1);
1695
+ }
1696
+
1697
+ // Simple #nn syntax
1698
+ var oldStyleLeafNum = parseInt(/^\d+$/.exec(fragment));
1699
+ if (!isNaN(oldStyleLeafNum)) {
1700
+ params.index = oldStyleLeafNum;
1701
+
1702
+ // Done processing if using old-style syntax
1703
+ return params;
1704
+ }
1705
+
1706
+ // Split into key-value pairs
1707
+ var urlArray = fragment.split('/');
1708
+ var urlHash = {};
1709
+ for (var i = 0; i < urlArray.length; i += 2) {
1710
+ urlHash[urlArray[i]] = urlArray[i + 1];
1711
+ }
1712
+
1713
+ // Mode
1714
+ if ('1up' == urlHash['mode']) {
1715
+ params.mode = this.constMode1up;
1716
+ } else if ('2up' == urlHash['mode']) {
1717
+ params.mode = this.constMode2up;
1718
+ } else if ('thumb' == urlHash['mode']) {
1719
+ params.mode = this.constModeThumb;
1720
+ }
1721
+
1722
+ // Index and page
1723
+ if ('undefined' != typeof urlHash['page']) {
1724
+ // page was set -- may not be int
1725
+ params.page = decodeURIComponent(urlHash['page']);
1726
+ }
1727
+
1728
+ // $$$ process /region
1729
+ // $$$ process /search
1730
+
1731
+ if (urlHash['search'] != undefined) {
1732
+ params.search = utils.decodeURIComponentPlus(urlHash['search']);
1733
+ }
1734
+
1735
+ // $$$ process /highlight
1736
+
1737
+ // $$$ process /theme
1738
+ if (urlHash['theme'] != undefined) {
1739
+ params.theme = urlHash['theme'];
1740
+ }
1741
+ return params;
1742
+ };
1743
+
1744
+ /**
1745
+ * Create a fragment string from the params object.
1746
+ *
1747
+ * Fragments are formatted as a URL path but may be used outside of URLs as a
1748
+ * serialization format for BookReader parameters
1749
+ *
1750
+ * @see https://openlibrary.org/dev/docs/bookurls for fragment syntax
1751
+ *
1752
+ * @param {Object} params
1753
+ * @param {string} [urlMode]
1754
+ * @return {string}
1755
+ */
1756
+ BookReader.prototype.fragmentFromParams = function (params) {
1757
+ var urlMode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'hash';
1758
+ var fragments = [];
1759
+ if ('undefined' != typeof params.page) {
1760
+ fragments.push('page', encodeURIComponent(params.page));
1761
+ } else {
1762
+ if ('undefined' != typeof params.index) {
1763
+ // Don't have page numbering but we do have the index
1764
+ fragments.push('page', 'n' + params.index);
1765
+ }
1766
+ }
1767
+
1768
+ // $$$ highlight
1769
+ // $$$ region
1770
+
1771
+ // mode
1772
+ if ('undefined' != typeof params.mode) {
1773
+ if (params.mode == this.constMode1up) {
1774
+ fragments.push('mode', '1up');
1775
+ } else if (params.mode == this.constMode2up) {
1776
+ fragments.push('mode', '2up');
1777
+ } else if (params.mode == this.constModeThumb) {
1778
+ fragments.push('mode', 'thumb');
1779
+ } else {
1780
+ throw 'fragmentFromParams called with unknown mode ' + params.mode;
1781
+ }
1782
+ }
1783
+
1784
+ // search
1785
+ if (params.search && urlMode === 'hash') {
1786
+ fragments.push('search', utils.encodeURIComponentPlus(params.search));
1787
+ }
1788
+ return fragments.join('/');
1789
+ };
1790
+
1791
+ /**
1792
+ * Create, update querystring from the params object
1793
+ *
1794
+ * Handles:
1795
+ * view=
1796
+ * q=
1797
+ * @param {Object} params
1798
+ * @param {string} currQueryString
1799
+ * @param {string} [urlMode]
1800
+ * @return {string}
1801
+ */
1802
+ BookReader.prototype.queryStringFromParams = function (params, currQueryString) {
1803
+ var urlMode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'hash';
1804
+ var newParams = new URLSearchParams(currQueryString);
1805
+ if (params.view) {
1806
+ // Set ?view=theater when fullscreen
1807
+ newParams.set('view', params.view);
1808
+ } else {
1809
+ // Remove
1810
+ newParams.delete('view');
1811
+ }
1812
+ if (params.search && urlMode === 'history') {
1813
+ newParams.set('q', params.search);
1814
+ }
1815
+ // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
1816
+ // Note: This method returns the query string without the question mark.
1817
+ var result = newParams.toString();
1818
+ return result ? '?' + result : '';
1819
+ };
1820
+
1821
+ /**
1822
+ * Helper to select within instance's elements
1823
+ */
1824
+ BookReader.prototype.$ = function (selector) {
1825
+ return this.refs.$br.find(selector);
1826
+ };
1827
+ window.BookReader = BookReader;