@internetarchive/bookreader 5.0.0-6-multiple-files → 5.0.0-60

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 (303) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +77 -6
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +241 -377
  6. package/BookReader/BookReader.js +2 -21564
  7. package/BookReader/BookReader.js.LICENSE.txt +24 -20
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1519 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +17 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/1up.svg +1 -12
  13. package/BookReader/icons/2up.svg +1 -15
  14. package/BookReader/icons/advance.svg +3 -26
  15. package/BookReader/icons/chevron-right.svg +1 -1
  16. package/BookReader/icons/close-circle-dark.svg +1 -0
  17. package/BookReader/icons/close-circle.svg +1 -1
  18. package/BookReader/icons/fullscreen.svg +1 -17
  19. package/BookReader/icons/fullscreen_exit.svg +1 -17
  20. package/BookReader/icons/hamburger.svg +1 -15
  21. package/BookReader/icons/left-arrow.svg +1 -12
  22. package/BookReader/icons/magnify-minus.svg +1 -16
  23. package/BookReader/icons/magnify-plus.svg +1 -17
  24. package/BookReader/icons/magnify.svg +1 -15
  25. package/BookReader/icons/pause.svg +1 -23
  26. package/BookReader/icons/play.svg +1 -22
  27. package/BookReader/icons/playback-speed.svg +1 -34
  28. package/BookReader/icons/read-aloud.svg +1 -22
  29. package/BookReader/icons/review.svg +3 -22
  30. package/BookReader/icons/thumbnails.svg +1 -17
  31. package/BookReader/icons/voice.svg +1 -0
  32. package/BookReader/icons/volume-full.svg +1 -22
  33. package/BookReader/images/BRicons.svg +5 -94
  34. package/BookReader/images/books_graphic.svg +1 -177
  35. package/BookReader/images/icon_book.svg +1 -12
  36. package/BookReader/images/icon_bookmark.svg +1 -12
  37. package/BookReader/images/icon_gear.svg +1 -14
  38. package/BookReader/images/icon_hamburger.svg +1 -20
  39. package/BookReader/images/icon_home.svg +1 -21
  40. package/BookReader/images/icon_info.svg +1 -11
  41. package/BookReader/images/icon_one_page.svg +1 -8
  42. package/BookReader/images/icon_pause.svg +1 -1
  43. package/BookReader/images/icon_play.svg +1 -1
  44. package/BookReader/images/icon_playback-rate.svg +1 -15
  45. package/BookReader/images/icon_search_button.svg +1 -8
  46. package/BookReader/images/icon_share.svg +1 -9
  47. package/BookReader/images/icon_skip-ahead.svg +1 -6
  48. package/BookReader/images/icon_skip-back.svg +2 -13
  49. package/BookReader/images/icon_speaker.svg +1 -18
  50. package/BookReader/images/icon_speaker_open.svg +1 -10
  51. package/BookReader/images/icon_thumbnails.svg +1 -12
  52. package/BookReader/images/icon_toc.svg +1 -5
  53. package/BookReader/images/icon_two_pages.svg +1 -9
  54. package/BookReader/images/marker_chap-off.svg +1 -11
  55. package/BookReader/images/marker_chap-on.svg +1 -11
  56. package/BookReader/images/marker_srch-on.svg +1 -11
  57. package/BookReader/jquery-3.js +2 -0
  58. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  59. package/BookReader/plugins/plugin.archive_analytics.js +1 -172
  60. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  61. package/BookReader/plugins/plugin.autoplay.js +1 -165
  62. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  63. package/BookReader/plugins/plugin.chapters.js +1 -304
  64. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  65. package/BookReader/plugins/plugin.iframe.js +1 -74
  66. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  67. package/BookReader/plugins/plugin.mobile_nav.js +1 -334
  68. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  69. package/BookReader/plugins/plugin.resume.js +1 -368
  70. package/BookReader/plugins/plugin.resume.js.map +1 -1
  71. package/BookReader/plugins/plugin.search.js +1 -1420
  72. package/BookReader/plugins/plugin.search.js.map +1 -1
  73. package/BookReader/plugins/plugin.text_selection.js +1 -1080
  74. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  75. package/BookReader/plugins/plugin.tts.js +2 -9193
  76. package/BookReader/plugins/plugin.tts.js.map +1 -1
  77. package/BookReader/plugins/plugin.url.js +1 -269
  78. package/BookReader/plugins/plugin.url.js.map +1 -1
  79. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  80. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  81. package/BookReader/webcomponents-bundle.js +3 -0
  82. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  83. package/BookReader/webcomponents-bundle.js.map +1 -0
  84. package/BookReaderDemo/BookReaderDemo.css +14 -1
  85. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  86. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  87. package/BookReaderDemo/IADemoBr.js +147 -0
  88. package/BookReaderDemo/demo-advanced.html +2 -2
  89. package/BookReaderDemo/demo-autoplay.html +2 -1
  90. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  91. package/BookReaderDemo/demo-fullscreen-mobile.html +2 -1
  92. package/BookReaderDemo/demo-fullscreen.html +2 -1
  93. package/BookReaderDemo/demo-iiif.html +2 -1
  94. package/BookReaderDemo/demo-internetarchive.html +84 -17
  95. package/BookReaderDemo/demo-multiple.html +2 -1
  96. package/BookReaderDemo/demo-preview-pages.html +2 -1
  97. package/BookReaderDemo/demo-simple.html +2 -1
  98. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -1
  99. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  100. package/BookReaderDemo/immersion-1up.html +2 -1
  101. package/BookReaderDemo/immersion-mode.html +2 -1
  102. package/BookReaderDemo/toggle_controls.html +2 -1
  103. package/BookReaderDemo/view_mode.html +2 -1
  104. package/BookReaderDemo/viewmode-cycle.html +2 -3
  105. package/CHANGELOG.md +246 -0
  106. package/README.md +14 -1
  107. package/babel.config.js +19 -0
  108. package/codecov.yml +6 -0
  109. package/index.html +3 -0
  110. package/jsconfig.json +19 -0
  111. package/netlify.toml +5 -0
  112. package/package.json +70 -59
  113. package/renovate.json +52 -0
  114. package/scripts/preversion.js +4 -1
  115. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  116. package/src/BookNavigator/assets/button-base.js +9 -2
  117. package/src/BookNavigator/assets/ia-logo.js +17 -0
  118. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  119. package/src/BookNavigator/assets/icon_close.js +1 -1
  120. package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
  121. package/src/BookNavigator/assets/{icon_sort_ascending.js → icon_sort_desc.js} +2 -2
  122. package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
  123. package/src/BookNavigator/assets/icon_volumes.js +11 -0
  124. package/src/BookNavigator/book-navigator.js +585 -0
  125. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  126. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  127. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  128. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  129. package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
  130. package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
  131. package/src/BookNavigator/delete-modal-actions.js +1 -1
  132. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  133. package/src/BookNavigator/downloads/downloads.js +41 -25
  134. package/src/BookNavigator/search/search-provider.js +80 -28
  135. package/src/BookNavigator/search/search-results.js +34 -25
  136. package/src/BookNavigator/sharing.js +27 -0
  137. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  138. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  139. package/src/BookNavigator/volumes/volumes-provider.js +88 -53
  140. package/src/BookNavigator/volumes/volumes.js +41 -14
  141. package/src/BookReader/BookModel.js +59 -30
  142. package/src/BookReader/DebugConsole.js +3 -3
  143. package/src/BookReader/DragScrollable.js +233 -0
  144. package/src/BookReader/Mode1Up.js +56 -351
  145. package/src/BookReader/Mode1UpLit.js +391 -0
  146. package/src/BookReader/Mode2Up.js +73 -1318
  147. package/src/BookReader/Mode2UpLit.js +781 -0
  148. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  149. package/src/BookReader/ModeSmoothZoom.js +211 -0
  150. package/src/BookReader/ModeThumb.js +17 -11
  151. package/src/BookReader/Navbar/Navbar.js +10 -36
  152. package/src/BookReader/PageContainer.js +69 -6
  153. package/src/BookReader/ReduceSet.js +1 -1
  154. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  155. package/src/BookReader/events.js +2 -0
  156. package/src/BookReader/options.js +24 -2
  157. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  158. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  159. package/src/BookReader/utils.js +108 -13
  160. package/src/BookReader.js +480 -825
  161. package/src/assets/icons/close-circle-dark.svg +1 -0
  162. package/src/assets/icons/magnify-minus.svg +3 -7
  163. package/src/assets/icons/magnify-plus.svg +3 -7
  164. package/src/assets/icons/voice.svg +1 -0
  165. package/src/css/BookReader.scss +0 -12
  166. package/src/css/_BRBookmarks.scss +1 -1
  167. package/src/css/_BRComponent.scss +1 -1
  168. package/src/css/_BRmain.scss +33 -24
  169. package/src/css/_BRnav.scss +4 -26
  170. package/src/css/_BRpages.scss +147 -40
  171. package/src/css/_BRsearch.scss +25 -216
  172. package/src/css/_TextSelection.scss +16 -17
  173. package/src/css/_colorbox.scss +2 -2
  174. package/src/css/_controls.scss +17 -5
  175. package/src/css/_icons.scss +7 -1
  176. package/src/ia-bookreader/ia-bookreader.js +224 -0
  177. package/src/plugins/plugin.archive_analytics.js +3 -3
  178. package/src/plugins/plugin.autoplay.js +4 -9
  179. package/src/plugins/plugin.chapters.js +28 -35
  180. package/src/plugins/plugin.mobile_nav.js +11 -10
  181. package/src/plugins/plugin.resume.js +3 -3
  182. package/src/plugins/plugin.text_selection.js +32 -41
  183. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  184. package/src/plugins/search/plugin.search.js +179 -116
  185. package/src/plugins/search/view.js +63 -179
  186. package/src/plugins/tts/AbstractTTSEngine.js +46 -37
  187. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  188. package/src/plugins/tts/PageChunk.js +15 -21
  189. package/src/plugins/tts/PageChunkIterator.js +8 -12
  190. package/src/plugins/tts/WebTTSEngine.js +87 -71
  191. package/src/plugins/tts/plugin.tts.js +94 -125
  192. package/src/plugins/tts/utils.js +0 -25
  193. package/src/plugins/url/UrlPlugin.js +193 -0
  194. package/src/plugins/{plugin.url.js → url/plugin.url.js} +45 -16
  195. package/src/util/docCookies.js +21 -2
  196. package/tests/e2e/README.md +37 -0
  197. package/tests/e2e/autoplay.test.js +2 -2
  198. package/tests/e2e/base.test.js +7 -7
  199. package/tests/e2e/helpers/base.js +28 -23
  200. package/tests/e2e/helpers/debug.js +1 -1
  201. package/tests/e2e/helpers/desktopSearch.js +14 -13
  202. package/tests/e2e/helpers/mobileSearch.js +3 -3
  203. package/tests/e2e/helpers/params.js +17 -0
  204. package/tests/e2e/helpers/rightToLeft.js +4 -10
  205. package/tests/e2e/models/Navigation.js +13 -4
  206. package/tests/e2e/rightToLeft.test.js +4 -5
  207. package/tests/e2e/viewmode.test.js +40 -33
  208. package/tests/jest/BookNavigator/book-navigator.test.js +658 -0
  209. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  210. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  211. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  212. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  213. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  214. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  215. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  216. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +104 -60
  217. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  218. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  219. package/tests/jest/BookNavigator/volumes/volumes-provider.test.js +184 -0
  220. package/tests/jest/BookNavigator/volumes/volumes.test.js +97 -0
  221. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +59 -14
  222. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  223. package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
  224. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  225. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  226. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  227. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  228. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  229. package/tests/jest/BookReader/ModeSmoothZoom.test.js +175 -0
  230. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  231. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +10 -10
  232. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  233. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  234. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  235. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  236. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  237. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  238. package/tests/jest/BookReader/utils.test.js +217 -0
  239. package/tests/jest/BookReader.keyboard.test.js +190 -0
  240. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  241. package/tests/{BookReader.test.js → jest/BookReader.test.js} +26 -37
  242. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  243. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  244. package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +10 -11
  245. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  246. package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
  247. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  248. package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +39 -47
  249. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  250. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +63 -47
  251. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +35 -6
  252. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +9 -9
  253. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  254. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  255. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  256. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  257. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  258. package/tests/jest/plugins/url/UrlPlugin.test.js +190 -0
  259. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +53 -14
  260. package/tests/jest/setup.js +3 -0
  261. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  262. package/tests/jest/util/docCookies.test.js +24 -0
  263. package/tests/{util → jest/util}/strings.test.js +1 -1
  264. package/tests/{utils.js → jest/utils.js} +38 -0
  265. package/webpack.config.js +11 -5
  266. package/.babelrc +0 -12
  267. package/.dependabot/config.yml +0 -6
  268. package/.testcaferc.json +0 -5
  269. package/BookReader/bookreader-component-bundle.js +0 -14312
  270. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  271. package/BookReader/bookreader-component-bundle.js.map +0 -1
  272. package/BookReader/icons/sort-ascending.svg +0 -1
  273. package/BookReader/icons/sort-descending.svg +0 -1
  274. package/BookReader/jquery-1.10.1.js +0 -108
  275. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  276. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  277. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  278. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  279. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  280. package/karma.conf.js +0 -23
  281. package/src/BookNavigator/BookModel.js +0 -14
  282. package/src/BookNavigator/BookNavigator.js +0 -452
  283. package/src/BookNavigator/assets/book-loader.js +0 -27
  284. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  285. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  286. package/src/BookNavigator/search/a-search-result.js +0 -55
  287. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  288. package/src/ItemNavigator/ItemNavigator.js +0 -372
  289. package/src/ItemNavigator/providers/sharing.js +0 -29
  290. package/src/assets/icons/sort-ascending.svg +0 -1
  291. package/src/assets/icons/sort-descending.svg +0 -1
  292. package/src/dragscrollable-br.js +0 -261
  293. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  294. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  295. package/tests/BookReader/Mode1Up.test.js +0 -164
  296. package/tests/BookReader/Mode2Up.test.js +0 -247
  297. package/tests/BookReader/utils.test.js +0 -109
  298. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  299. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  300. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  301. package/tests/karma/BookNavigator/volumes.test.js +0 -101
  302. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  303. package/tests/util/docCookies.test.js +0 -15
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  /* global BookReader */
2
3
  /**
3
4
  * Plugin for Archive.org book search
@@ -20,8 +21,16 @@
20
21
  * the book has not had OCR text indexed yet. Receives `instance`
21
22
  * @event BookReader:SearchCallbackEmpty - When no results found. Receives
22
23
  * `instance`
24
+ * @event BookReader:SearchCanceled - When no results found. Receives
25
+ * `instance`
23
26
  */
27
+ import { poll } from '../../BookReader/utils.js';
28
+ import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.js';
24
29
  import SearchView from './view.js';
30
+ /** @typedef {import('../../BookReader/PageContainer').PageContainer} PageContainer */
31
+ /** @typedef {import('../../BookReader/BookModel').PageIndex} PageIndex */
32
+ /** @typedef {import('../../BookReader/BookModel').LeafNum} LeafNum */
33
+ /** @typedef {import('../../BookReader/BookModel').PageNumString} PageNumString */
25
34
 
26
35
  jQuery.extend(BookReader.defaultOptions, {
27
36
  server: 'ia600609.us.archive.org',
@@ -42,7 +51,6 @@ BookReader.prototype.setup = (function (super_) {
42
51
  this.searchResults = null;
43
52
  this.searchInsideUrl = options.searchInsideUrl;
44
53
  this.enableSearch = options.enableSearch;
45
- this.goToFirstResult = false;
46
54
 
47
55
  // Base server used by some api calls
48
56
  this.bookId = options.bookId;
@@ -50,11 +58,14 @@ BookReader.prototype.setup = (function (super_) {
50
58
  this.subPrefix = options.subPrefix;
51
59
  this.bookPath = options.bookPath;
52
60
 
53
- if (this.searchView) { return; }
54
- this.searchView = new SearchView({
55
- br: this,
56
- selector: '#BRsearch_tray',
57
- });
61
+ this.searchXHR = null;
62
+ this._cancelSearch.bind(this);
63
+ this.cancelSearchRequest.bind(this);
64
+
65
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
66
+ this._searchBoxesByIndex = {};
67
+
68
+ this.searchView = undefined;
58
69
  };
59
70
  })(BookReader.prototype.setup);
60
71
 
@@ -62,28 +73,31 @@ BookReader.prototype.setup = (function (super_) {
62
73
  BookReader.prototype.init = (function (super_) {
63
74
  return function () {
64
75
  super_.call(this);
65
-
76
+ // give SearchView the most complete bookreader state
77
+ this.searchView = new SearchView({
78
+ br: this,
79
+ searchCancelledCallback: () => {
80
+ this._cancelSearch();
81
+ this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
82
+ }
83
+ });
66
84
  if (this.options.enableSearch && this.options.initialSearchTerm) {
85
+ /**
86
+ * this.search() take two parameter
87
+ * 1. this.options.initialSearchTerm - search term
88
+ * 2. {
89
+ * goToFirstResult: this.options.goToFirstResult,
90
+ * suppressFragmentChange: false // always want to change fragment in URL
91
+ * }
92
+ */
67
93
  this.search(
68
94
  this.options.initialSearchTerm,
69
- { goToFirstResult: this.goToFirstResult, suppressFragmentChange: true }
95
+ { goToFirstResult: this.options.goToFirstResult, suppressFragmentChange: false }
70
96
  );
71
97
  }
72
98
  };
73
99
  })(BookReader.prototype.init);
74
100
 
75
- /** @override */
76
- BookReader.prototype.buildMobileDrawerElement = (function (super_) {
77
- return function () {
78
- const $el = super_.call(this);
79
- if (!this.enableSearch) { return; }
80
- if (this.searchView.dom.mobileSearch) {
81
- $el.find('.BRmobileMenu__moreInfoRow').after(this.searchView.dom.mobileSearch);
82
- }
83
- return $el;
84
- };
85
- })(BookReader.prototype.buildMobileDrawerElement);
86
-
87
101
  /** @override */
88
102
  BookReader.prototype.buildToolbarElement = (function (super_) {
89
103
  return function () {
@@ -96,6 +110,25 @@ BookReader.prototype.buildToolbarElement = (function (super_) {
96
110
  };
97
111
  })(BookReader.prototype.buildToolbarElement);
98
112
 
113
+ /** @override */
114
+ BookReader.prototype._createPageContainer = (function (super_) {
115
+ return function (index) {
116
+ const pageContainer = super_.call(this, index);
117
+ if (this.enableSearch && pageContainer.page && index in this._searchBoxesByIndex) {
118
+ const pageIndex = pageContainer.page.index;
119
+ const boxes = this._searchBoxesByIndex[pageIndex];
120
+ renderBoxesInPageContainerLayer(
121
+ 'searchHiliteLayer',
122
+ boxes,
123
+ pageContainer.page,
124
+ pageContainer.$container[0],
125
+ boxes.map(b => `match-index-${b.matchIndex}`),
126
+ );
127
+ }
128
+ return pageContainer;
129
+ };
130
+ })(BookReader.prototype._createPageContainer);
131
+
99
132
  /**
100
133
  * @typedef {object} SearchOptions
101
134
  * @property {boolean} goToFirstResult
@@ -110,7 +143,7 @@ BookReader.prototype.buildToolbarElement = (function (super_) {
110
143
  * @param {string} term
111
144
  * @param {SearchOptions} overrides
112
145
  */
113
- BookReader.prototype.search = function(term = '', overrides = {}) {
146
+ BookReader.prototype.search = async function(term = '', overrides = {}) {
114
147
  /** @type {SearchOptions} */
115
148
  const defaultOptions = {
116
149
  goToFirstResult: false, /* jump to the first result (default=false) */
@@ -122,6 +155,7 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
122
155
  };
123
156
  const options = jQuery.extend({}, defaultOptions, overrides);
124
157
  this.suppressFragmentChange = options.suppressFragmentChange;
158
+ this.searchCancelled = false;
125
159
 
126
160
  // strip slashes, since this goes in the url
127
161
  this.searchTerm = term.replace(/\//g, ' ');
@@ -157,12 +191,16 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
157
191
 
158
192
  const url = `${baseUrl}${paramStr}`;
159
193
 
160
- const processSearchResults = (searchInsideResults) => {
194
+ const callSearchResultsCallback = (searchInsideResults) => {
195
+ if (this.searchCancelled) {
196
+ return;
197
+ }
161
198
  const responseHasError = searchInsideResults.error || !searchInsideResults.matches.length;
162
199
  const hasCustomError = typeof options.error === 'function';
163
200
  const hasCustomSuccess = typeof options.success === 'function';
164
201
 
165
202
  if (responseHasError) {
203
+ console.error('Search Inside Response Error', searchInsideResults.error || 'matches.length == 0');
166
204
  hasCustomError
167
205
  ? options.error.call(this, searchInsideResults, options)
168
206
  : this.BRSearchCallbackError(searchInsideResults, options);
@@ -173,11 +211,39 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
173
211
  }
174
212
  };
175
213
 
176
- this.trigger('SearchStarted', { term: this.searchTerm });
177
- return $.ajax({
214
+ this.trigger('SearchStarted', { term: this.searchTerm, instance: this });
215
+ callSearchResultsCallback(await $.ajax({
178
216
  url: url,
179
- dataType: 'jsonp'
180
- }).then(processSearchResults);
217
+ dataType: 'jsonp',
218
+ cache: true,
219
+ beforeSend: xhr => { this.searchXHR = xhr; },
220
+ }));
221
+ };
222
+
223
+ /**
224
+ * cancels AJAX Call
225
+ * emits custom event
226
+ */
227
+ BookReader.prototype._cancelSearch = function () {
228
+ this.searchXHR?.abort();
229
+ this.searchView.clearSearchFieldAndResults(false);
230
+ this.searchTerm = '';
231
+ this.searchXHR = null;
232
+ this.searchCancelled = true;
233
+ this.searchResults = [];
234
+ };
235
+
236
+ /**
237
+ * External function to cancel search
238
+ * checks for term & xhr in flight before running
239
+ */
240
+ BookReader.prototype.cancelSearchRequest = function () {
241
+ this.searchCancelled = true;
242
+ if (this.searchXHR !== null) {
243
+ this._cancelSearch();
244
+ this.searchView.toggleSearchPending();
245
+ this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
246
+ }
181
247
  };
182
248
 
183
249
  /**
@@ -188,10 +254,13 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
188
254
  * @property {number} b
189
255
  * @property {number} t
190
256
  * @property {HTMLDivElement} [div]
257
+ * @property {number} matchIndex This is a fake field! not part of the API response. The index of the match that contains this box in total search results matches.
191
258
  */
192
259
 
193
260
  /**
194
261
  * @typedef {object} SearchInsideMatch
262
+ * @property {number} matchIndex This is a fake field! Not part of the API response. It is added by the JS.
263
+ * @property {string} displayPageNumber (fake field) The page number as it should be displayed in the UI.
195
264
  * @property {string} text
196
265
  * @property {Array<{ page: number, boxes: SearchInsideMatchBox[] }>} par
197
266
  */
@@ -203,23 +272,42 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
203
272
  * @property {boolean} indexed
204
273
  */
205
274
 
275
+ /**
276
+ * Attach some fields to search inside results
277
+ * @param {SearchInsideResults} results
278
+ * @param {(pageNum: LeafNum) => PageNumString} displayPageNumberFn
279
+ */
280
+ export function marshallSearchResults(results, displayPageNumberFn) {
281
+ // Attach matchIndex to a few things to make it easier to identify
282
+ // an active/selected match
283
+ for (const [index, match] of results.matches.entries()) {
284
+ match.matchIndex = index;
285
+ match.displayPageNumber = displayPageNumberFn(match.par[0].page);
286
+ for (const par of match.par) {
287
+ for (const box of par.boxes) {
288
+ box.matchIndex = index;
289
+ }
290
+ }
291
+ }
292
+ }
293
+
206
294
  /**
207
295
  * Search Results return handler
208
- * @callback
209
296
  * @param {SearchInsideResults} results
210
297
  * @param {object} options
211
298
  * @param {boolean} options.goToFirstResult
212
299
  */
213
300
  BookReader.prototype.BRSearchCallback = function(results, options) {
214
- this.searchResults = results;
301
+ marshallSearchResults(results, pageNum => this.book.getPageNum(this.book.leafNumToIndex(pageNum)));
302
+ this.searchResults = results || [];
215
303
 
216
304
  this.updateSearchHilites();
217
305
  this.removeProgressPopup();
218
306
  if (options.goToFirstResult) {
219
- this._searchPluginGoToResult(results.matches[0].par[0].page);
307
+ this._searchPluginGoToResult(0);
220
308
  }
221
309
  this.trigger('SearchCallback', { results, options, instance: this });
222
- }
310
+ };
223
311
 
224
312
  /**
225
313
  * Main search results error handler
@@ -259,95 +347,41 @@ BookReader.prototype._BRSearchCallbackError = function(results) {
259
347
  * updates search on-page highlights controller
260
348
  */
261
349
  BookReader.prototype.updateSearchHilites = function() {
262
- if (this.constMode2up == this.mode) {
263
- this.updateSearchHilites2UP();
264
- return;
350
+ /** @type {SearchInsideMatch[]} */
351
+ const matches = this.searchResults?.matches || [];
352
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
353
+ const boxesByIndex = {};
354
+
355
+ // Clear any existing svg layers
356
+ this.removeSearchHilites();
357
+
358
+ // Group by pageIndex
359
+ for (const match of matches) {
360
+ for (const box of match.par[0].boxes) {
361
+ const pageIndex = this.book.leafNumToIndex(box.page);
362
+ const pageBoxes = boxesByIndex[pageIndex] || (boxesByIndex[pageIndex] = []);
363
+ pageBoxes.push(box);
364
+ }
265
365
  }
266
- this.updateSearchHilites1UP();
267
- };
268
366
 
269
- /**
270
- * update search on-page highlights in 1up mode
271
- */
272
- BookReader.prototype.updateSearchHilites1UP = function() {
273
- const results = this.searchResults;
274
- if (null == results) return;
275
- results.matches.forEach(match => {
276
- match.par[0].boxes.forEach(box => {
277
- const pageIndex = this.leafNumToIndex(box.page);
278
- const pageIsInView = jQuery.inArray(pageIndex, this.displayedIndices) >= 0;
279
- if (pageIsInView) {
280
- if (!box.div) {
281
- //create a div for the search highlight, and stash it in the box object
282
- box.div = document.createElement('div');
283
- $(box.div).prop('className', 'BookReaderSearchHilite').appendTo(this.$(`.pagediv${pageIndex}`));
284
- }
285
- const page = this._models.book.getPage(pageIndex);
286
- const highlight = {
287
- width: this._modes.mode1Up.physicalInchesToDisplayPixels((box.r - box.l) / page.ppi),
288
- height: this._modes.mode1Up.physicalInchesToDisplayPixels((box.b - box.t) / page.ppi),
289
- left: this._modes.mode1Up.physicalInchesToDisplayPixels(box.l / page.ppi),
290
- top: this._modes.mode1Up.physicalInchesToDisplayPixels(box.t / page.ppi),
291
- };
292
- $(box.div).css(highlight);
293
- } else {
294
- if (box.div) {
295
- $(box.div).remove();
296
- box.div = null;
297
- }
298
- }
299
- });
300
- });
301
- };
302
-
303
- /**
304
- * update search on-page highlights in 2up mode
305
- */
306
- BookReader.prototype.updateSearchHilites2UP = function() {
307
- const results = this.searchResults;
367
+ // update any already created pages
368
+ for (const [pageIndexString, boxes] of Object.entries(boxesByIndex)) {
369
+ const pageIndex = parseFloat(pageIndexString);
370
+ const page = this.book.getPage(pageIndex);
371
+ const pageContainers = this.getActivePageContainerElementsForIndex(pageIndex);
372
+ for (const container of pageContainers) {
373
+ renderBoxesInPageContainerLayer('searchHiliteLayer', boxes, page, container, boxes.map(b => `match-index-${b.matchIndex}`));
374
+ }
375
+ }
308
376
 
309
- if (results === null) return;
310
-
311
- const { matches } = results;
312
- matches.forEach((match) => {
313
- match.par[0].boxes.forEach(box => {
314
- const pageIndex = this.leafNumToIndex(match.par[0].page);
315
- const pageIsInView = jQuery.inArray(pageIndex, this.displayedIndices) >= 0;
316
- const { isViewable } = this._models.book.getPage(pageIndex);
317
-
318
- if (pageIsInView && isViewable) {
319
- if (!box.div) {
320
- //create a div for the search highlight, and stash it in the box object
321
- box.div = document.createElement('div');
322
- $(box.div).addClass('BookReaderSearchHilite')
323
- .appendTo(this.refs.$brTwoPageView);
324
- }
325
- this.setHilightCss2UP(box.div, pageIndex, box.l, box.r, box.t, box.b);
326
- } else {
327
- // clear stale reference
328
- if (box.div) {
329
- $(box.div).remove();
330
- box.div = null;
331
- }
332
- }
333
- });
334
- });
377
+ this._searchBoxesByIndex = boxesByIndex;
335
378
  };
336
379
 
337
380
  /**
338
381
  * remove search highlights
339
382
  */
340
383
  BookReader.prototype.removeSearchHilites = function() {
341
- const results = this.searchResults;
342
- if (null == results || !results.matches) { return; }
343
- results.matches.forEach(match => {
344
- match.par[0].boxes.forEach(box => {
345
- if (null != box.div) {
346
- $(box.div).remove();
347
- box.div = null;
348
- }
349
- });
350
- });
384
+ $(this.getActivePageContainerElements()).find('.searchHiliteLayer').remove();
351
385
  };
352
386
 
353
387
  /**
@@ -355,11 +389,14 @@ BookReader.prototype.removeSearchHilites = function() {
355
389
  * Goes to the page specified. If the page is not viewable, tries to load the page
356
390
  * FIXME Most of this logic is IA specific, and should be less integrated into here
357
391
  * or at least more configurable.
358
- * @param {PageIndex} pageIndex
392
+ * @param {number} matchIndex
359
393
  */
360
- BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
361
- const { book } = this._models;
394
+ BookReader.prototype._searchPluginGoToResult = async function (matchIndex) {
395
+ const match = this.searchResults?.matches[matchIndex];
396
+ const book = this.book;
397
+ const pageIndex = book.leafNumToIndex(match.par[0].page);
362
398
  const page = book.getPage(pageIndex);
399
+ const onNearbyPage = Math.abs(this.currentIndex() - pageIndex) < 3;
363
400
  let makeUnviewableAtEnd = false;
364
401
  if (!page.isViewable) {
365
402
  const resp = await fetch('/services/bookreader/request_page?' + new URLSearchParams({
@@ -378,15 +415,41 @@ BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
378
415
  book.getPage(pageIndex).makeViewable();
379
416
  makeUnviewableAtEnd = true;
380
417
  }
418
+
419
+ // Trigger an update of book
420
+ this._modes.mode1Up.mode1UpLit.updatePages();
421
+ await this._modes.mode1Up.mode1UpLit.updateComplete;
381
422
  }
382
423
  /* this updates the URL */
383
- this.suppressFragmentChange = false;
384
- this.jumpToIndex(pageIndex);
424
+ if (!this._isIndexDisplayed(pageIndex)) {
425
+ this.suppressFragmentChange = false;
426
+ this.jumpToIndex(pageIndex);
427
+ }
385
428
 
386
429
  // Reset it to unviewable if it wasn't resolved
387
430
  if (makeUnviewableAtEnd) {
388
431
  book.getPage(pageIndex).makeViewable(false);
389
432
  }
433
+
434
+ // Scroll/flash in the ui
435
+ const $boxes = await poll(() => $(`rect.match-index-${match.matchIndex}`), { until: result => result.length > 0 });
436
+ if ($boxes.length) {
437
+ $boxes.css('animation', 'none');
438
+ $boxes[0].scrollIntoView({
439
+ // Only vertically center the highlight if we're in 1up or in full screen. In
440
+ // 2up, if we're not fullscreen, the whole body gets scrolled around to try to
441
+ // center the highlight 🙄 See:
442
+ // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move/11041376
443
+ // Note: nearest doesn't quite work great, because the ReadAloud toolbar is now
444
+ // full-width, and covers up the last line of the highlight.
445
+ block: this.constMode1up == this.mode || this.isFullscreenActive ? 'center' : 'nearest',
446
+ inline: 'center',
447
+ behavior: onNearbyPage ? 'smooth' : 'auto',
448
+ });
449
+ // wait for animation to start
450
+ await new Promise(resolve => setTimeout(resolve, 100));
451
+ $boxes.removeAttr("style");
452
+ }
390
453
  };
391
454
 
392
455
  /**
@@ -420,7 +483,7 @@ BookReader.prototype.searchHighlightVisible = function() {
420
483
 
421
484
  results.matches.some(match => {
422
485
  return match.par[0].boxes.some(box => {
423
- const pageIndex = this.leafNumToIndex(box.page);
486
+ const pageIndex = this.book.leafNumToIndex(box.page);
424
487
  if (jQuery.inArray(pageIndex, visiblePages) >= 0) {
425
488
  return true;
426
489
  }