@internetarchive/bookreader 5.0.0-26 → 5.0.0-29

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 (236) hide show
  1. package/.husky/_/husky.sh +30 -0
  2. package/BookReader/BookReader.css +1 -1
  3. package/BookReader/BookReader.js +1 -1
  4. package/BookReader/BookReader.js.map +1 -1
  5. package/BookReader/bookreader-component-bundle.js +570 -542
  6. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +23 -0
  7. package/BookReader/bookreader-component-bundle.js.map +1 -1
  8. package/BookReader/plugins/plugin.search.js +1 -1
  9. package/BookReader/plugins/plugin.search.js.map +1 -1
  10. package/BookReader/plugins/plugin.tts.js.map +1 -1
  11. package/BookReader/plugins/plugin.url.js +1 -1
  12. package/BookReader/plugins/plugin.url.js.map +1 -1
  13. package/BookReaderDemo/BookReaderDemo.css +14 -1
  14. package/BookReaderDemo/IADemoBr.js +104 -0
  15. package/BookReaderDemo/demo-internetarchive.html +65 -98
  16. package/CHANGELOG.md +10 -0
  17. package/package.json +9 -6
  18. package/src/BookNavigator/assets/ia-logo.js +17 -0
  19. package/src/BookNavigator/book-navigator.js +521 -0
  20. package/src/BookNavigator/bookmarks/bookmark-button.js +2 -1
  21. package/src/BookNavigator/bookmarks/bookmarks-provider.js +20 -8
  22. package/src/BookNavigator/bookmarks/ia-bookmarks.js +84 -51
  23. package/src/BookNavigator/downloads/downloads-provider.js +5 -9
  24. package/src/BookNavigator/downloads/downloads.js +1 -0
  25. package/src/BookNavigator/search/search-provider.js +15 -8
  26. package/src/BookNavigator/sharing.js +27 -0
  27. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +9 -8
  28. package/src/BookNavigator/volumes/volumes-provider.js +44 -13
  29. package/src/BookNavigator/volumes/volumes.js +14 -3
  30. package/src/BookReader/options.js +6 -0
  31. package/src/BookReader.js +20 -8
  32. package/src/BookReaderComponent/BookReaderComponent.js +53 -32
  33. package/src/css/_BRComponent.scss +1 -1
  34. package/src/plugins/search/plugin.search.js +10 -9
  35. package/src/plugins/tts/FestivalTTSEngine.js +1 -1
  36. package/src/plugins/url/UrlPlugin.js +184 -0
  37. package/src/plugins/url/plugin.url.js +220 -0
  38. package/{src → stat}/BookNavigator/BookModel.js +0 -0
  39. package/{src → stat}/BookNavigator/BookNavigator.js +109 -102
  40. package/stat/BookNavigator/assets/bookmark-colors.js +15 -0
  41. package/stat/BookNavigator/assets/button-base.js +61 -0
  42. package/stat/BookNavigator/assets/ia-logo.js +17 -0
  43. package/stat/BookNavigator/assets/icon_checkmark.js +6 -0
  44. package/stat/BookNavigator/assets/icon_close.js +3 -0
  45. package/stat/BookNavigator/assets/icon_sort_asc.js +5 -0
  46. package/stat/BookNavigator/assets/icon_sort_desc.js +5 -0
  47. package/stat/BookNavigator/assets/icon_sort_neutral.js +5 -0
  48. package/stat/BookNavigator/assets/icon_volumes.js +11 -0
  49. package/stat/BookNavigator/bookmarks/bookmark-button.js +64 -0
  50. package/stat/BookNavigator/bookmarks/bookmark-edit.js +215 -0
  51. package/stat/BookNavigator/bookmarks/bookmarks-list.js +285 -0
  52. package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
  53. package/stat/BookNavigator/bookmarks/bookmarks-provider.js +56 -0
  54. package/stat/BookNavigator/bookmarks/ia-bookmarks.js +523 -0
  55. package/{src → stat}/BookNavigator/br-fullscreen-mgr.js +1 -2
  56. package/stat/BookNavigator/delete-modal-actions.js +49 -0
  57. package/stat/BookNavigator/downloads/downloads-provider.js +72 -0
  58. package/stat/BookNavigator/downloads/downloads.js +139 -0
  59. package/stat/BookNavigator/provider-config.js +0 -0
  60. package/stat/BookNavigator/search/a-search-result.js +55 -0
  61. package/stat/BookNavigator/search/search-provider.js +180 -0
  62. package/stat/BookNavigator/search/search-results.js +360 -0
  63. package/{src/ItemNavigator/providers → stat/BookNavigator}/sharing.js +3 -5
  64. package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +94 -0
  65. package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
  66. package/stat/BookNavigator/volumes/volumes-provider.js +83 -0
  67. package/stat/BookNavigator/volumes/volumes.js +178 -0
  68. package/stat/BookReader/BookModel.js +518 -0
  69. package/stat/BookReader/DebugConsole.js +54 -0
  70. package/stat/BookReader/DragScrollable.js +233 -0
  71. package/stat/BookReader/ImageCache.js +116 -0
  72. package/stat/BookReader/Mode1Up.js +102 -0
  73. package/stat/BookReader/Mode1UpLit.js +434 -0
  74. package/stat/BookReader/Mode2Up.js +1372 -0
  75. package/stat/BookReader/ModeSmoothZoom.js +177 -0
  76. package/stat/BookReader/ModeThumb.js +344 -0
  77. package/stat/BookReader/Navbar/Navbar.js +310 -0
  78. package/stat/BookReader/PageContainer.js +120 -0
  79. package/stat/BookReader/ReduceSet.js +26 -0
  80. package/stat/BookReader/Toolbar/Toolbar.js +384 -0
  81. package/stat/BookReader/events.js +20 -0
  82. package/stat/BookReader/options.js +324 -0
  83. package/stat/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  84. package/stat/BookReader/utils/classes.js +36 -0
  85. package/stat/BookReader/utils.js +240 -0
  86. package/stat/BookReader.js +2550 -0
  87. package/stat/BookReaderComponent/BookReaderComponent.js +117 -0
  88. package/stat/assets/icons/1up.svg +12 -0
  89. package/stat/assets/icons/2up.svg +15 -0
  90. package/stat/assets/icons/advance.svg +26 -0
  91. package/stat/assets/icons/chevron-right.svg +1 -0
  92. package/stat/assets/icons/close-circle-dark.svg +1 -0
  93. package/stat/assets/icons/close-circle.svg +1 -0
  94. package/stat/assets/icons/fullscreen.svg +17 -0
  95. package/stat/assets/icons/fullscreen_exit.svg +17 -0
  96. package/stat/assets/icons/hamburger.svg +15 -0
  97. package/stat/assets/icons/left-arrow.svg +12 -0
  98. package/stat/assets/icons/magnify-minus.svg +16 -0
  99. package/stat/assets/icons/magnify-plus.svg +17 -0
  100. package/stat/assets/icons/magnify.svg +15 -0
  101. package/stat/assets/icons/pause.svg +23 -0
  102. package/stat/assets/icons/play.svg +22 -0
  103. package/stat/assets/icons/playback-speed.svg +34 -0
  104. package/stat/assets/icons/read-aloud.svg +22 -0
  105. package/stat/assets/icons/review.svg +22 -0
  106. package/stat/assets/icons/thumbnails.svg +17 -0
  107. package/stat/assets/icons/voice.svg +1 -0
  108. package/stat/assets/icons/volume-full.svg +22 -0
  109. package/stat/assets/images/BRicons.png +0 -0
  110. package/stat/assets/images/BRicons.svg +94 -0
  111. package/stat/assets/images/BRicons_ia.png +0 -0
  112. package/stat/assets/images/back_pages.png +0 -0
  113. package/stat/assets/images/book_bottom_icon.png +0 -0
  114. package/stat/assets/images/book_down_icon.png +0 -0
  115. package/stat/assets/images/book_left_icon.png +0 -0
  116. package/stat/assets/images/book_leftmost_icon.png +0 -0
  117. package/stat/assets/images/book_right_icon.png +0 -0
  118. package/stat/assets/images/book_rightmost_icon.png +0 -0
  119. package/stat/assets/images/book_top_icon.png +0 -0
  120. package/stat/assets/images/book_up_icon.png +0 -0
  121. package/stat/assets/images/books_graphic.svg +177 -0
  122. package/stat/assets/images/booksplit.png +0 -0
  123. package/stat/assets/images/control_pause_icon.png +0 -0
  124. package/stat/assets/images/control_play_icon.png +0 -0
  125. package/stat/assets/images/embed_icon.png +0 -0
  126. package/stat/assets/images/icon-home-ia.png +0 -0
  127. package/stat/assets/images/icon_OL-logo-xs.png +0 -0
  128. package/stat/assets/images/icon_alert-xs.png +0 -0
  129. package/stat/assets/images/icon_book.svg +12 -0
  130. package/stat/assets/images/icon_bookmark.svg +12 -0
  131. package/stat/assets/images/icon_close-pop.png +0 -0
  132. package/stat/assets/images/icon_download.png +0 -0
  133. package/stat/assets/images/icon_gear.svg +14 -0
  134. package/stat/assets/images/icon_hamburger.svg +20 -0
  135. package/stat/assets/images/icon_home.png +0 -0
  136. package/stat/assets/images/icon_home.svg +21 -0
  137. package/stat/assets/images/icon_home_ia.png +0 -0
  138. package/stat/assets/images/icon_indicator.png +0 -0
  139. package/stat/assets/images/icon_info.svg +11 -0
  140. package/stat/assets/images/icon_one_page.svg +8 -0
  141. package/stat/assets/images/icon_pause.svg +1 -0
  142. package/stat/assets/images/icon_play.svg +1 -0
  143. package/stat/assets/images/icon_playback-rate.svg +15 -0
  144. package/stat/assets/images/icon_return.png +0 -0
  145. package/stat/assets/images/icon_search_button.svg +8 -0
  146. package/stat/assets/images/icon_share.svg +9 -0
  147. package/stat/assets/images/icon_skip-ahead.svg +6 -0
  148. package/stat/assets/images/icon_skip-back.svg +13 -0
  149. package/stat/assets/images/icon_speaker.svg +18 -0
  150. package/stat/assets/images/icon_speaker_open.svg +10 -0
  151. package/stat/assets/images/icon_thumbnails.svg +12 -0
  152. package/stat/assets/images/icon_toc.svg +5 -0
  153. package/stat/assets/images/icon_two_pages.svg +9 -0
  154. package/stat/assets/images/icon_zoomer.png +0 -0
  155. package/stat/assets/images/loading.gif +0 -0
  156. package/stat/assets/images/logo_icon.png +0 -0
  157. package/stat/assets/images/marker_chap-off.png +0 -0
  158. package/stat/assets/images/marker_chap-off.svg +11 -0
  159. package/stat/assets/images/marker_chap-off_ia.png +0 -0
  160. package/stat/assets/images/marker_chap-on.png +0 -0
  161. package/stat/assets/images/marker_chap-on.svg +11 -0
  162. package/stat/assets/images/marker_srch-on.svg +11 -0
  163. package/stat/assets/images/marker_srchchap-off.png +0 -0
  164. package/stat/assets/images/marker_srchchap-on.png +0 -0
  165. package/stat/assets/images/nav_control-dn.png +0 -0
  166. package/stat/assets/images/nav_control-dn_ia.png +0 -0
  167. package/stat/assets/images/nav_control-up.png +0 -0
  168. package/stat/assets/images/nav_control-up_ia.png +0 -0
  169. package/stat/assets/images/nav_control.png +0 -0
  170. package/stat/assets/images/one_page_mode_icon.png +0 -0
  171. package/stat/assets/images/paper-badge.png +0 -0
  172. package/stat/assets/images/print_icon.png +0 -0
  173. package/stat/assets/images/progressbar.gif +0 -0
  174. package/stat/assets/images/right_edges.png +0 -0
  175. package/stat/assets/images/slider.png +0 -0
  176. package/stat/assets/images/slider_ia.png +0 -0
  177. package/stat/assets/images/thumbnail_mode_icon.png +0 -0
  178. package/stat/assets/images/transparent.png +0 -0
  179. package/stat/assets/images/two_page_mode_icon.png +0 -0
  180. package/stat/assets/images/zoom_in_icon.png +0 -0
  181. package/stat/assets/images/zoom_out_icon.png +0 -0
  182. package/stat/css/BookReader.scss +89 -0
  183. package/stat/css/_BRBookmarks.scss +29 -0
  184. package/stat/css/_BRComponent.scss +13 -0
  185. package/stat/css/_BRfloat.scss +197 -0
  186. package/stat/css/_BRicon.scss +48 -0
  187. package/stat/css/_BRmain.scss +251 -0
  188. package/stat/css/_BRnav.scss +359 -0
  189. package/stat/css/_BRpages.scss +139 -0
  190. package/stat/css/_BRsearch.scss +226 -0
  191. package/stat/css/_BRtoolbar.scss +84 -0
  192. package/stat/css/_BRvendor.scss +5 -0
  193. package/stat/css/_MobileNav.scss +194 -0
  194. package/stat/css/_TextSelection.scss +32 -0
  195. package/stat/css/_colorbox.scss +52 -0
  196. package/stat/css/_controls.scss +253 -0
  197. package/stat/css/_icons.scss +121 -0
  198. package/stat/jquery-wrapper.js +4 -0
  199. package/stat/plugins/plugin.archive_analytics.js +86 -0
  200. package/stat/plugins/plugin.autoplay.js +129 -0
  201. package/stat/plugins/plugin.chapters.js +248 -0
  202. package/stat/plugins/plugin.iframe.js +48 -0
  203. package/stat/plugins/plugin.mobile_nav.js +288 -0
  204. package/stat/plugins/plugin.resume.js +68 -0
  205. package/stat/plugins/plugin.text_selection.js +291 -0
  206. package/{src → stat}/plugins/plugin.url.js +0 -0
  207. package/stat/plugins/plugin.vendor-fullscreen.js +247 -0
  208. package/stat/plugins/search/plugin.search.js +439 -0
  209. package/stat/plugins/search/view.js +439 -0
  210. package/stat/plugins/tts/AbstractTTSEngine.js +249 -0
  211. package/stat/plugins/tts/FestivalTTSEngine.js +169 -0
  212. package/stat/plugins/tts/PageChunk.js +107 -0
  213. package/stat/plugins/tts/PageChunkIterator.js +163 -0
  214. package/stat/plugins/tts/WebTTSEngine.js +357 -0
  215. package/stat/plugins/tts/plugin.tts.js +357 -0
  216. package/stat/plugins/tts/tooltip_dict.js +15 -0
  217. package/stat/plugins/tts/utils.js +91 -0
  218. package/stat/util/browserSniffing.js +30 -0
  219. package/stat/util/debouncer.js +26 -0
  220. package/stat/util/docCookies.js +67 -0
  221. package/stat/util/strings.js +34 -0
  222. package/tests/e2e/viewmode.test.js +30 -30
  223. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +64 -52
  224. package/tests/jest/BookReader.test.js +1 -1
  225. package/tests/jest/plugins/url/UrlPlugin.test.js +175 -0
  226. package/tests/jest/plugins/{plugin.url.test.js → url/plugin.url.test.js} +3 -2
  227. package/tests/karma/BookNavigator/book-navigator.test.js +413 -108
  228. package/tests/karma/BookNavigator/bookmarks/bookmark-button.test.js +44 -0
  229. package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +6 -3
  230. package/tests/karma/BookNavigator/search/search-provider.test.js +106 -6
  231. package/tests/karma/BookNavigator/search/search-results.test.js +0 -2
  232. package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +29 -20
  233. package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +46 -22
  234. package/webpack.config.js +1 -1
  235. package/src/BookNavigator/assets/book-loader.js +0 -27
  236. package/src/ItemNavigator/ItemNavigator.js +0 -377
@@ -0,0 +1,439 @@
1
+ /* global BookReader */
2
+ /**
3
+ * Plugin for Archive.org book search
4
+ * NOTE: This script must be loaded AFTER `plugin.mobile_nav.js`
5
+ * as it mutates mobile nav drawer
6
+ *
7
+ * Events fired at various points throughout search processing are published
8
+ * on the document DOM element. These can be subscribed to using jQuery's event
9
+ * binding method `$.fn.on`. All of the events are prefixed with a BookReader
10
+ * namespace. The events are:
11
+ *
12
+ * @event BookReader:SearchStarted - When a search form is submitted, immediately
13
+ * before an AJAX call is made to request search results
14
+ * @event BookReader:SearchCallback - When the search AJAX call is returned and at
15
+ * least one result is returned. The event callback receives an object
16
+ * with the `results`, plugin `options`, and the BookReader `instance`
17
+ * @event BookReader:SearchCallbackError - When the AJAX request returns an error.
18
+ * Receives the `results` and `instance`
19
+ * @event BookReader:SearchCallbackNotIndexed - When a message is received that
20
+ * the book has not had OCR text indexed yet. Receives `instance`
21
+ * @event BookReader:SearchCallbackEmpty - When no results found. Receives
22
+ * `instance`
23
+ * @event BookReader:SearchCanceled - When no results found. Receives
24
+ * `instance`
25
+ */
26
+ import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.js';
27
+ import SearchView from './view.js';
28
+ /** @typedef {import('../../BookReader/PageContainer').PageContainer} PageContainer */
29
+ /** @typedef {import('../../BookReader/BookModel').PageIndex} PageIndex */
30
+
31
+ jQuery.extend(BookReader.defaultOptions, {
32
+ server: 'ia600609.us.archive.org',
33
+ bookId: '',
34
+ subPrefix: '',
35
+ bookPath: '',
36
+ enableSearch: true,
37
+ searchInsideUrl: '/fulltext/inside.php',
38
+ initialSearchTerm: null,
39
+ });
40
+
41
+ /** @override */
42
+ BookReader.prototype.setup = (function (super_) {
43
+ return function (options) {
44
+ super_.call(this, options);
45
+
46
+ this.searchTerm = '';
47
+ this.searchResults = null;
48
+ this.searchInsideUrl = options.searchInsideUrl;
49
+ this.enableSearch = options.enableSearch;
50
+
51
+ // Base server used by some api calls
52
+ this.bookId = options.bookId;
53
+ this.server = options.server;
54
+ this.subPrefix = options.subPrefix;
55
+ this.bookPath = options.bookPath;
56
+
57
+ this.searchXHR = null;
58
+ this._cancelSearch.bind(this);
59
+ this.cancelSearchRequest.bind(this);
60
+
61
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
62
+ this._searchBoxesByIndex = {};
63
+
64
+ if (this.searchView) { return; }
65
+ this.searchView = new SearchView({
66
+ br: this,
67
+ searchCancelledCallback: () => {
68
+ this._cancelSearch();
69
+ this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
70
+ }
71
+ });
72
+ };
73
+ })(BookReader.prototype.setup);
74
+
75
+ /** @override */
76
+ BookReader.prototype.init = (function (super_) {
77
+ return function () {
78
+ super_.call(this);
79
+
80
+ if (this.options.enableSearch && this.options.initialSearchTerm) {
81
+ /**
82
+ * this.search() take two parameter
83
+ * 1. this.options.initialSearchTerm - search term
84
+ * 2. {
85
+ * goToFirstResult: this.options.goToFirstResult,
86
+ * suppressFragmentChange: false // always want to change fragment in URL
87
+ * }
88
+ */
89
+ this.search(
90
+ this.options.initialSearchTerm,
91
+ { goToFirstResult: this.options.goToFirstResult, suppressFragmentChange: false }
92
+ );
93
+ }
94
+ };
95
+ })(BookReader.prototype.init);
96
+
97
+ /** @override */
98
+ BookReader.prototype.buildToolbarElement = (function (super_) {
99
+ return function () {
100
+ const $el = super_.call(this);
101
+ if (!this.enableSearch) { return; }
102
+ if (this.searchView.dom.toolbarSearch) {
103
+ $el.find('.BRtoolbarSectionInfo').after(this.searchView.dom.toolbarSearch);
104
+ }
105
+ return $el;
106
+ };
107
+ })(BookReader.prototype.buildToolbarElement);
108
+
109
+ /** @override */
110
+ BookReader.prototype._createPageContainer = (function (super_) {
111
+ return function (index) {
112
+ const pageContainer = super_.call(this, index);
113
+ if (this.enableSearch && pageContainer.page && index in this._searchBoxesByIndex) {
114
+ const pageIndex = pageContainer.page.index;
115
+ renderBoxesInPageContainerLayer('searchHiliteLayer', this._searchBoxesByIndex[pageIndex], pageContainer.page, pageContainer.$container[0]);
116
+ }
117
+ return pageContainer;
118
+ };
119
+ })(BookReader.prototype._createPageContainer);
120
+
121
+ /**
122
+ * @typedef {object} SearchOptions
123
+ * @property {boolean} goToFirstResult
124
+ * @property {boolean} disablePopup
125
+ * @property {(null|function)} error - @deprecated at v.5.0
126
+ * @property {(null|function)} success - @deprecated at v.5.0
127
+ */
128
+
129
+ /**
130
+ * Submits search request
131
+ *
132
+ * @param {string} term
133
+ * @param {SearchOptions} overrides
134
+ */
135
+ BookReader.prototype.search = function(term = '', overrides = {}) {
136
+ /** @type {SearchOptions} */
137
+ const defaultOptions = {
138
+ goToFirstResult: false, /* jump to the first result (default=false) */
139
+ disablePopup: false, /* don't show the modal progress (default=false) */
140
+ suppressFragmentChange: false, /* don't change the URL on initial load */
141
+ error: null, /* optional error handler (default=null) */
142
+ success: null, /* optional success handler (default=null) */
143
+
144
+ };
145
+ const options = jQuery.extend({}, defaultOptions, overrides);
146
+ this.suppressFragmentChange = options.suppressFragmentChange;
147
+
148
+ // strip slashes, since this goes in the url
149
+ this.searchTerm = term.replace(/\//g, ' ');
150
+
151
+ if (!options.suppressFragmentChange) {
152
+ this.trigger(BookReader.eventNames.fragmentChange);
153
+ }
154
+
155
+ // Add quotes to the term. This is to compenstate for the backends default OR query
156
+ // term = term.replace(/['"]+/g, '');
157
+ // term = '"' + term + '"';
158
+
159
+ // Remove the port and userdir
160
+ const serverPath = this.server.replace(/:.+/, '');
161
+ const baseUrl = `https://${serverPath}${this.searchInsideUrl}?`;
162
+
163
+ // Remove subPrefix from end of path
164
+ let path = this.bookPath;
165
+ const subPrefixWithSlash = `/${this.subPrefix}`;
166
+ if (this.bookPath.length - this.bookPath.lastIndexOf(subPrefixWithSlash) == subPrefixWithSlash.length) {
167
+ path = this.bookPath.substr(0, this.bookPath.length - subPrefixWithSlash.length);
168
+ }
169
+
170
+ const urlParams = {
171
+ item_id: this.bookId,
172
+ doc: this.subPrefix,
173
+ path,
174
+ q: term,
175
+ };
176
+
177
+ // NOTE that the API does not expect / (slashes) to be encoded. (%2F) won't work
178
+ const paramStr = $.param(urlParams).replace(/%2F/g, '/');
179
+
180
+ const url = `${baseUrl}${paramStr}`;
181
+
182
+ const cleanup = () => {
183
+ this.searchXHR = null;
184
+ window.BRSearchInProgress = () => {};
185
+ };
186
+
187
+ const processSearchResults = (searchInsideResults) => {
188
+ if (!this.searchXHR) {
189
+ return;
190
+ }
191
+ const responseHasError = searchInsideResults.error || !searchInsideResults.matches.length;
192
+ const hasCustomError = typeof options.error === 'function';
193
+ const hasCustomSuccess = typeof options.success === 'function';
194
+
195
+ if (responseHasError) {
196
+ hasCustomError
197
+ ? options.error.call(this, searchInsideResults, options)
198
+ : this.BRSearchCallbackError(searchInsideResults, options);
199
+ } else {
200
+ hasCustomSuccess
201
+ ? options.success.call(this, searchInsideResults, options)
202
+ : this.BRSearchCallback(searchInsideResults, options);
203
+ }
204
+ cleanup();
205
+ };
206
+
207
+ const beforeSend = (xhr) => {
208
+ this.searchXHR = xhr;
209
+ window.BRSearchInProgress = processSearchResults;
210
+ };
211
+
212
+ this.trigger('SearchStarted', { term: this.searchTerm, instance: this });
213
+ return $.ajax({
214
+ url: url,
215
+ dataType: 'jsonp',
216
+ beforeSend,
217
+ jsonpCallback: 'BRSearchInProgress'
218
+ }).then(processSearchResults);
219
+ };
220
+
221
+ /**
222
+ * cancels AJAX Call
223
+ * emits custom event
224
+ */
225
+ BookReader.prototype._cancelSearch = function () {
226
+ this.searchXHR?.abort();
227
+ this.searchView.clearSearchFieldAndResults(false);
228
+ this.searchTerm = '';
229
+ this.searchXHR = null;
230
+ this.searchResults = [];
231
+ window.BRSearchInProgress = () => {};
232
+ };
233
+
234
+ /**
235
+ * External function to cancel search
236
+ * checks for term & xhr in flight before running
237
+ */
238
+ BookReader.prototype.cancelSearchRequest = function () {
239
+ if (this.searchXHR !== null) {
240
+ this._cancelSearch();
241
+ this.searchView.toggleSearchPending();
242
+ this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
243
+ }
244
+ };
245
+
246
+ /**
247
+ * @typedef {object} SearchInsideMatchBox
248
+ * @property {number} page
249
+ * @property {number} r
250
+ * @property {number} l
251
+ * @property {number} b
252
+ * @property {number} t
253
+ * @property {HTMLDivElement} [div]
254
+ */
255
+
256
+ /**
257
+ * @typedef {object} SearchInsideMatch
258
+ * @property {string} text
259
+ * @property {Array<{ page: number, boxes: SearchInsideMatchBox[] }>} par
260
+ */
261
+
262
+ /**
263
+ * @typedef {object} SearchInsideResults
264
+ * @property {string} error
265
+ * @property {SearchInsideMatch[]} matches
266
+ * @property {boolean} indexed
267
+ */
268
+
269
+ /**
270
+ * Search Results return handler
271
+ * @callback
272
+ * @param {SearchInsideResults} results
273
+ * @param {object} options
274
+ * @param {boolean} options.goToFirstResult
275
+ */
276
+ BookReader.prototype.BRSearchCallback = function(results, options) {
277
+ this.searchResults = results || [];
278
+
279
+ this.updateSearchHilites();
280
+ this.removeProgressPopup();
281
+ if (options.goToFirstResult) {
282
+ const pageIndex = this._models.book.leafNumToIndex(results.matches[0].par[0].page);
283
+ this._searchPluginGoToResult(pageIndex);
284
+ }
285
+ this.trigger('SearchCallback', { results, options, instance: this });
286
+ };
287
+
288
+ /**
289
+ * Main search results error handler
290
+ * @callback
291
+ * @param {SearchInsideResults} results
292
+ */
293
+ BookReader.prototype.BRSearchCallbackError = function(results) {
294
+ this._BRSearchCallbackError(results);
295
+ };
296
+
297
+ /**
298
+ * @private draws search results error
299
+ * @callback
300
+ * @param {SearchInsideResults} results
301
+ * @param {jQuery} $el
302
+ * @param {boolean} fade
303
+ */
304
+ BookReader.prototype._BRSearchCallbackError = function(results) {
305
+ this.searchResults = results;
306
+ const basePayload = {
307
+ term: this.searchTerm,
308
+ instance: this,
309
+ };
310
+ if (results.error) {
311
+ const payload = Object.assign({}, basePayload, { results });
312
+ this.trigger('SearchCallbackError', payload);
313
+ } else if (0 == results.matches.length) {
314
+ if (false === results.indexed) {
315
+ this.trigger('SearchCallbackBookNotIndexed', basePayload);
316
+ return;
317
+ }
318
+ this.trigger('SearchCallbackEmpty', basePayload);
319
+ }
320
+ };
321
+
322
+ /**
323
+ * updates search on-page highlights controller
324
+ */
325
+ BookReader.prototype.updateSearchHilites = function() {
326
+ /** @type {SearchInsideMatch[]} */
327
+ const matches = this.searchResults?.matches || [];
328
+ /** @type { {[pageIndex: number]: SearchInsideMatch[]} } */
329
+ const boxesByIndex = {};
330
+
331
+ // Clear any existing svg layers
332
+ this.removeSearchHilites();
333
+
334
+ // Group by pageIndex
335
+ for (const match of matches) {
336
+ for (const box of match.par[0].boxes) {
337
+ const pageIndex = this.leafNumToIndex(box.page);
338
+ const pageMatches = boxesByIndex[pageIndex] || (boxesByIndex[pageIndex] = []);
339
+ pageMatches.push(box);
340
+ }
341
+ }
342
+
343
+ // update any already created pages
344
+ for (const [pageIndexString, boxes] of Object.entries(boxesByIndex)) {
345
+ const pageIndex = parseFloat(pageIndexString);
346
+ const page = this._models.book.getPage(pageIndex);
347
+ const pageContainers = this.getActivePageContainerElementsForIndex(pageIndex);
348
+ pageContainers.forEach(container => renderBoxesInPageContainerLayer('searchHiliteLayer', boxes, page, container));
349
+ }
350
+
351
+ this._searchBoxesByIndex = boxesByIndex;
352
+ };
353
+
354
+ /**
355
+ * remove search highlights
356
+ */
357
+ BookReader.prototype.removeSearchHilites = function() {
358
+ $(this.getActivePageContainerElements()).find('.searchHiliteLayer').remove();
359
+ };
360
+
361
+ /**
362
+ * @private
363
+ * Goes to the page specified. If the page is not viewable, tries to load the page
364
+ * FIXME Most of this logic is IA specific, and should be less integrated into here
365
+ * or at least more configurable.
366
+ * @param {PageIndex} pageIndex
367
+ */
368
+ BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
369
+ const { book } = this._models;
370
+ const page = book.getPage(pageIndex);
371
+ let makeUnviewableAtEnd = false;
372
+ if (!page.isViewable) {
373
+ const resp = await fetch('/services/bookreader/request_page?' + new URLSearchParams({
374
+ id: this.options.bookId,
375
+ subprefix: this.options.subPrefix,
376
+ leafNum: page.leafNum,
377
+ })).then(r => r.json());
378
+
379
+ for (const leafNum of resp.value) {
380
+ book.getPage(book.leafNumToIndex(leafNum)).makeViewable();
381
+ }
382
+
383
+ // not able to show page; make the page viewable anyways so that it can
384
+ // actually open. On IA, it has a fallback to a special error page.
385
+ if (!resp.value.length) {
386
+ book.getPage(pageIndex).makeViewable();
387
+ makeUnviewableAtEnd = true;
388
+ }
389
+ }
390
+ /* this updates the URL */
391
+ this.suppressFragmentChange = false;
392
+ this.jumpToIndex(pageIndex);
393
+
394
+ // Reset it to unviewable if it wasn't resolved
395
+ if (makeUnviewableAtEnd) {
396
+ book.getPage(pageIndex).makeViewable(false);
397
+ }
398
+ };
399
+
400
+ /**
401
+ * Removes all search pins
402
+ */
403
+ BookReader.prototype.removeSearchResults = function(suppressFragmentChange = false) {
404
+ this.removeSearchHilites(); //be sure to set all box.divs to null
405
+ this.searchTerm = null;
406
+ this.searchResults = null;
407
+ if (!suppressFragmentChange) {
408
+ this.trigger(BookReader.eventNames.fragmentChange);
409
+ }
410
+ };
411
+
412
+ /**
413
+ * Returns true if a search highlight is currently being displayed
414
+ * @returns {boolean}
415
+ */
416
+ BookReader.prototype.searchHighlightVisible = function() {
417
+ const results = this.searchResults;
418
+ let visiblePages = [];
419
+ if (null == results) return false;
420
+
421
+ if (this.constMode2up == this.mode) {
422
+ visiblePages = [this.twoPage.currentIndexL, this.twoPage.currentIndexR];
423
+ } else if (this.constMode1up == this.mode) {
424
+ visiblePages = [this.currentIndex()];
425
+ } else {
426
+ return false;
427
+ }
428
+
429
+ results.matches.some(match => {
430
+ return match.par[0].boxes.some(box => {
431
+ const pageIndex = this.leafNumToIndex(box.page);
432
+ if (jQuery.inArray(pageIndex, visiblePages) >= 0) {
433
+ return true;
434
+ }
435
+ });
436
+ });
437
+
438
+ return false;
439
+ };