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

Sign up to get free protection for your applications and to get access to all the features.
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
  }