@internetarchive/bookreader 5.0.0-8 → 5.0.0-80

Sign up to get free protection for your applications and to get access to all the features.
Files changed (313) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +73 -10
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +398 -1133
  6. package/BookReader/BookReader.js +1 -1
  7. package/BookReader/BookReader.js.LICENSE.txt +20 -20
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1782 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +7 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/1up.svg +1 -1
  13. package/BookReader/icons/2up.svg +1 -1
  14. package/BookReader/icons/advance.svg +1 -1
  15. package/BookReader/icons/chevron-right.svg +1 -1
  16. package/BookReader/icons/close-circle-dark.svg +1 -1
  17. package/BookReader/icons/close-circle.svg +1 -1
  18. package/BookReader/icons/fullscreen.svg +1 -1
  19. package/BookReader/icons/fullscreen_exit.svg +1 -1
  20. package/BookReader/icons/hamburger.svg +1 -1
  21. package/BookReader/icons/left-arrow.svg +1 -1
  22. package/BookReader/icons/magnify-minus.svg +1 -1
  23. package/BookReader/icons/magnify-plus.svg +1 -1
  24. package/BookReader/icons/magnify.svg +1 -1
  25. package/BookReader/icons/pause.svg +1 -1
  26. package/BookReader/icons/play.svg +1 -1
  27. package/BookReader/icons/playback-speed.svg +1 -1
  28. package/BookReader/icons/read-aloud.svg +1 -1
  29. package/BookReader/icons/review.svg +1 -1
  30. package/BookReader/icons/thumbnails.svg +1 -1
  31. package/BookReader/icons/voice.svg +1 -0
  32. package/BookReader/icons/volume-full.svg +1 -1
  33. package/BookReader/images/BRicons.svg +3 -3
  34. package/BookReader/images/books_graphic.svg +1 -1
  35. package/BookReader/images/icon_book.svg +1 -1
  36. package/BookReader/images/icon_bookmark.svg +1 -1
  37. package/BookReader/images/icon_gear.svg +1 -1
  38. package/BookReader/images/icon_hamburger.svg +1 -1
  39. package/BookReader/images/icon_home.svg +1 -1
  40. package/BookReader/images/icon_info.svg +1 -1
  41. package/BookReader/images/icon_one_page.svg +1 -1
  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 -1
  45. package/BookReader/images/icon_search_button.svg +1 -1
  46. package/BookReader/images/icon_share.svg +1 -1
  47. package/BookReader/images/icon_skip-ahead.svg +1 -1
  48. package/BookReader/images/icon_skip-back.svg +1 -1
  49. package/BookReader/images/icon_speaker.svg +1 -1
  50. package/BookReader/images/icon_speaker_open.svg +1 -1
  51. package/BookReader/images/icon_thumbnails.svg +1 -1
  52. package/BookReader/images/icon_toc.svg +1 -1
  53. package/BookReader/images/icon_two_pages.svg +1 -1
  54. package/BookReader/images/marker_chap-off.svg +1 -1
  55. package/BookReader/images/marker_chap-on.svg +1 -1
  56. package/BookReader/images/marker_srch-on.svg +1 -1
  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 -1
  60. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  61. package/BookReader/plugins/plugin.autoplay.js +1 -1
  62. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  63. package/BookReader/plugins/plugin.chapters.js +25 -1
  64. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  65. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  66. package/BookReader/plugins/plugin.iframe.js +1 -1
  67. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  68. package/BookReader/plugins/plugin.iiif.js +2 -0
  69. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  70. package/BookReader/plugins/plugin.resume.js +1 -1
  71. package/BookReader/plugins/plugin.resume.js.map +1 -1
  72. package/BookReader/plugins/plugin.search.js +2 -1
  73. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  74. package/BookReader/plugins/plugin.search.js.map +1 -1
  75. package/BookReader/plugins/plugin.text_selection.js +2 -1
  76. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  77. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  78. package/BookReader/plugins/plugin.tts.js +1 -1
  79. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  80. package/BookReader/plugins/plugin.tts.js.map +1 -1
  81. package/BookReader/plugins/plugin.url.js +1 -1
  82. package/BookReader/plugins/plugin.url.js.map +1 -1
  83. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  84. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  85. package/BookReader/webcomponents-bundle.js +3 -0
  86. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  87. package/BookReader/webcomponents-bundle.js.map +1 -0
  88. package/BookReaderDemo/BookReaderDemo.css +18 -19
  89. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
  90. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  91. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  92. package/BookReaderDemo/IADemoBr.js +147 -0
  93. package/BookReaderDemo/demo-advanced.html +2 -2
  94. package/BookReaderDemo/demo-autoplay.html +2 -3
  95. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  96. package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
  97. package/BookReaderDemo/demo-fullscreen.html +2 -4
  98. package/BookReaderDemo/demo-iiif.html +99 -12
  99. package/BookReaderDemo/demo-internetarchive.html +214 -18
  100. package/BookReaderDemo/demo-multiple.html +2 -1
  101. package/BookReaderDemo/demo-preview-pages.html +2 -1
  102. package/BookReaderDemo/demo-simple.html +2 -1
  103. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -4
  104. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  105. package/BookReaderDemo/immersion-1up.html +2 -2
  106. package/BookReaderDemo/immersion-mode.html +2 -4
  107. package/BookReaderDemo/toggle_controls.html +3 -2
  108. package/BookReaderDemo/view_mode.html +2 -1
  109. package/BookReaderDemo/viewmode-cycle.html +2 -3
  110. package/CHANGELOG.md +536 -33
  111. package/README.md +14 -1
  112. package/babel.config.js +20 -0
  113. package/codecov.yml +6 -0
  114. package/index.html +4 -1
  115. package/jsconfig.json +19 -0
  116. package/netlify.toml +9 -0
  117. package/package.json +70 -60
  118. package/renovate.json +52 -0
  119. package/scripts/preversion.js +0 -1
  120. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  121. package/src/BookNavigator/assets/button-base.js +4 -2
  122. package/src/BookNavigator/assets/ia-logo.js +17 -0
  123. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  124. package/src/BookNavigator/assets/icon_close.js +1 -1
  125. package/src/BookNavigator/book-navigator.js +590 -0
  126. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  127. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  128. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  129. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  130. package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
  131. package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
  132. package/src/BookNavigator/delete-modal-actions.js +1 -1
  133. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  134. package/src/BookNavigator/downloads/downloads.js +41 -25
  135. package/src/BookNavigator/search/search-provider.js +49 -27
  136. package/src/BookNavigator/search/search-results.js +23 -9
  137. package/src/BookNavigator/sharing.js +27 -0
  138. package/src/BookNavigator/viewable-files.js +95 -0
  139. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  140. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  141. package/src/BookReader/BookModel.js +64 -34
  142. package/src/BookReader/DragScrollable.js +233 -0
  143. package/src/BookReader/Mode1Up.js +56 -351
  144. package/src/BookReader/Mode1UpLit.js +388 -0
  145. package/src/BookReader/Mode2Up.js +73 -1318
  146. package/src/BookReader/Mode2UpLit.js +776 -0
  147. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  148. package/src/BookReader/ModeSmoothZoom.js +312 -0
  149. package/src/BookReader/ModeThumb.js +18 -12
  150. package/src/BookReader/Navbar/Navbar.js +14 -40
  151. package/src/BookReader/PageContainer.js +81 -6
  152. package/src/BookReader/ReduceSet.js +1 -1
  153. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  154. package/src/BookReader/events.js +2 -3
  155. package/src/BookReader/options.js +27 -2
  156. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  157. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  158. package/src/BookReader/utils/SelectionObserver.js +45 -0
  159. package/src/BookReader/utils.js +118 -13
  160. package/src/BookReader.js +427 -1061
  161. package/src/assets/icons/magnify-minus.svg +3 -7
  162. package/src/assets/icons/magnify-plus.svg +3 -7
  163. package/src/assets/icons/voice.svg +1 -0
  164. package/src/css/BookReader.scss +1 -5
  165. package/src/css/_BRBookmarks.scss +1 -1
  166. package/src/css/_BRComponent.scss +1 -1
  167. package/src/css/_BRmain.scss +16 -3
  168. package/src/css/_BRnav.scss +12 -39
  169. package/src/css/_BRpages.scss +149 -40
  170. package/src/css/_BRsearch.scss +68 -25
  171. package/src/css/_BRtoolbar.scss +5 -5
  172. package/src/css/_TextSelection.scss +87 -27
  173. package/src/css/_colorbox.scss +2 -2
  174. package/src/css/_controls.scss +20 -7
  175. package/src/css/_icons.scss +1 -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 +5 -11
  179. package/src/plugins/plugin.chapters.js +237 -191
  180. package/src/plugins/plugin.iiif.js +151 -0
  181. package/src/plugins/plugin.resume.js +3 -3
  182. package/src/plugins/plugin.text_selection.js +464 -134
  183. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  184. package/src/plugins/search/plugin.search.js +142 -125
  185. package/src/plugins/search/utils.js +43 -0
  186. package/src/plugins/search/view.js +33 -58
  187. package/src/plugins/tts/AbstractTTSEngine.js +71 -40
  188. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  189. package/src/plugins/tts/PageChunk.js +15 -21
  190. package/src/plugins/tts/PageChunkIterator.js +8 -12
  191. package/src/plugins/tts/WebTTSEngine.js +87 -71
  192. package/src/plugins/tts/plugin.tts.js +96 -127
  193. package/src/plugins/tts/utils.js +15 -25
  194. package/src/plugins/url/UrlPlugin.js +191 -0
  195. package/src/plugins/{plugin.url.js → url/plugin.url.js} +45 -16
  196. package/src/util/browserSniffing.js +22 -0
  197. package/src/util/docCookies.js +21 -2
  198. package/tests/e2e/README.md +37 -0
  199. package/tests/e2e/autoplay.test.js +2 -2
  200. package/tests/e2e/base.test.js +8 -16
  201. package/tests/e2e/helpers/base.js +53 -48
  202. package/tests/e2e/helpers/debug.js +1 -1
  203. package/tests/e2e/helpers/params.js +17 -0
  204. package/tests/e2e/helpers/rightToLeft.js +8 -14
  205. package/tests/e2e/helpers/search.js +73 -0
  206. package/tests/e2e/models/Navigation.js +20 -37
  207. package/tests/e2e/rightToLeft.test.js +4 -5
  208. package/tests/e2e/viewmode.test.js +40 -33
  209. package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
  210. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  211. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  212. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  213. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  214. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  215. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  216. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  217. package/tests/{karma → jest}/BookNavigator/search/search-results.test.js +109 -60
  218. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  219. package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
  220. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  221. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  222. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  223. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  224. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  225. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  226. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  227. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  228. package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
  229. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  230. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +10 -10
  231. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  232. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  233. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  234. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  235. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  236. package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
  237. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  238. package/tests/jest/BookReader/utils.test.js +229 -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/jest/plugins/plugin.chapters.test.js +195 -0
  245. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  246. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  247. package/tests/jest/plugins/plugin.text_selection.test.js +317 -0
  248. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  249. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +25 -47
  250. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +39 -6
  251. package/tests/jest/plugins/search/utils.js +25 -0
  252. package/tests/jest/plugins/search/utils.test.js +29 -0
  253. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +29 -9
  254. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  255. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  256. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  257. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  258. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  259. package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
  260. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +53 -14
  261. package/tests/jest/setup.js +3 -0
  262. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  263. package/tests/jest/util/docCookies.test.js +24 -0
  264. package/tests/{util → jest/util}/strings.test.js +1 -1
  265. package/tests/{utils.js → jest/utils.js} +38 -0
  266. package/webpack.config.js +12 -6
  267. package/.babelrc +0 -12
  268. package/.dependabot/config.yml +0 -6
  269. package/.testcaferc.json +0 -5
  270. package/BookReader/bookreader-component-bundle.js +0 -1450
  271. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  272. package/BookReader/bookreader-component-bundle.js.map +0 -1
  273. package/BookReader/jquery-1.10.1.js +0 -2
  274. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  275. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  276. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  277. package/BookReader/plugins/plugin.mobile_nav.js +0 -2
  278. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  279. package/BookReaderDemo/IIIFBookReader.js +0 -207
  280. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  281. package/BookReaderDemo/demo-iiif.js +0 -26
  282. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  283. package/karma.conf.js +0 -23
  284. package/src/BookNavigator/BookModel.js +0 -14
  285. package/src/BookNavigator/BookNavigator.js +0 -446
  286. package/src/BookNavigator/assets/book-loader.js +0 -27
  287. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  288. package/src/BookNavigator/search/a-search-result.js +0 -55
  289. package/src/BookReader/DebugConsole.js +0 -54
  290. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  291. package/src/ItemNavigator/ItemNavigator.js +0 -376
  292. package/src/ItemNavigator/providers/sharing.js +0 -29
  293. package/src/css/_MobileNav.scss +0 -194
  294. package/src/dragscrollable-br.js +0 -261
  295. package/src/lit-wrapper.js +0 -2
  296. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  297. package/src/plugins/plugin.mobile_nav.js +0 -287
  298. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  299. package/tests/BookReader/DebugConsole.test.js +0 -25
  300. package/tests/BookReader/Mode1Up.test.js +0 -164
  301. package/tests/BookReader/Mode2Up.test.js +0 -247
  302. package/tests/BookReader/utils.test.js +0 -109
  303. package/tests/e2e/helpers/desktopSearch.js +0 -72
  304. package/tests/e2e/helpers/mobileSearch.js +0 -85
  305. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  306. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  307. package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
  308. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  309. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  310. package/tests/plugins/plugin.chapters.test.js +0 -130
  311. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  312. package/tests/plugins/plugin.text_selection.test.js +0 -203
  313. package/tests/util/docCookies.test.js +0 -15
@@ -56,7 +56,7 @@ if (!isMobile()) {
56
56
  $(document.body).append(cboxOverlay).append(cbox);
57
57
  }
58
58
  });
59
- }
59
+ };
60
60
  })(BookReader.prototype.init);
61
61
 
62
62
  /**
@@ -92,7 +92,7 @@ if (!isMobile()) {
92
92
  $(document).off('keyup' + EVENT_NAMESPACE);
93
93
 
94
94
  this.isFullscreenActive = false;
95
- this.updateBrClasses()
95
+ this.updateBrClasses();
96
96
 
97
97
  this.resize();
98
98
  this.refs.$brContainer.animate({ opacity: 1 }, 400, 'linear');
@@ -233,8 +233,8 @@ export function bindFullscreenChangeListener(
233
233
  'moz',
234
234
  'ms'
235
235
  ];
236
- const all_events = $.trim(event + vendor_prefixes.join(event) + event);
237
- $(document).bind(all_events, data, fullscreenchangeListener);
236
+ const all_events = (event + vendor_prefixes.join(event) + event).trim();
237
+ $(document).on(all_events, data, fullscreenchangeListener);
238
238
  }
239
239
 
240
240
  /**
@@ -1,9 +1,7 @@
1
+ // @ts-check
1
2
  /* global BookReader */
2
3
  /**
3
4
  * 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
5
  * Events fired at various points throughout search processing are published
8
6
  * on the document DOM element. These can be subscribed to using jQuery's event
9
7
  * binding method `$.fn.on`. All of the events are prefixed with a BookReader
@@ -23,7 +21,14 @@
23
21
  * @event BookReader:SearchCanceled - When no results found. Receives
24
22
  * `instance`
25
23
  */
24
+ import { poll } from '../../BookReader/utils.js';
25
+ import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.js';
26
26
  import SearchView from './view.js';
27
+ import { marshallSearchResults } from './utils.js';
28
+ /** @typedef {import('../../BookReader/PageContainer').PageContainer} PageContainer */
29
+ /** @typedef {import('../../BookReader/BookModel').PageIndex} PageIndex */
30
+ /** @typedef {import('../../BookReader/BookModel').LeafNum} LeafNum */
31
+ /** @typedef {import('../../BookReader/BookModel').PageNumString} PageNumString */
27
32
 
28
33
  jQuery.extend(BookReader.defaultOptions, {
29
34
  server: 'ia600609.us.archive.org',
@@ -31,7 +36,10 @@ jQuery.extend(BookReader.defaultOptions, {
31
36
  subPrefix: '',
32
37
  bookPath: '',
33
38
  enableSearch: true,
39
+ searchInsideProtocol: 'https',
34
40
  searchInsideUrl: '/fulltext/inside.php',
41
+ searchInsidePreTag: '{{{',
42
+ searchInsidePostTag: '}}}',
35
43
  initialSearchTerm: null,
36
44
  });
37
45
 
@@ -44,7 +52,6 @@ BookReader.prototype.setup = (function (super_) {
44
52
  this.searchResults = null;
45
53
  this.searchInsideUrl = options.searchInsideUrl;
46
54
  this.enableSearch = options.enableSearch;
47
- this.goToFirstResult = false;
48
55
 
49
56
  // Base server used by some api calls
50
57
  this.bookId = options.bookId;
@@ -56,14 +63,10 @@ BookReader.prototype.setup = (function (super_) {
56
63
  this._cancelSearch.bind(this);
57
64
  this.cancelSearchRequest.bind(this);
58
65
 
59
- if (this.searchView) { return; }
60
- this.searchView = new SearchView({
61
- br: this,
62
- searchCancelledCallback: () => {
63
- this._cancelSearch();
64
- this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
65
- }
66
- });
66
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
67
+ this._searchBoxesByIndex = {};
68
+
69
+ this.searchView = undefined;
67
70
  };
68
71
  })(BookReader.prototype.setup);
69
72
 
@@ -71,11 +74,26 @@ BookReader.prototype.setup = (function (super_) {
71
74
  BookReader.prototype.init = (function (super_) {
72
75
  return function () {
73
76
  super_.call(this);
74
-
77
+ // give SearchView the most complete bookreader state
78
+ this.searchView = new SearchView({
79
+ br: this,
80
+ searchCancelledCallback: () => {
81
+ this._cancelSearch();
82
+ this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
83
+ }
84
+ });
75
85
  if (this.options.enableSearch && this.options.initialSearchTerm) {
86
+ /**
87
+ * this.search() take two parameter
88
+ * 1. this.options.initialSearchTerm - search term
89
+ * 2. {
90
+ * goToFirstResult: this.options.goToFirstResult,
91
+ * suppressFragmentChange: false // always want to change fragment in URL
92
+ * }
93
+ */
76
94
  this.search(
77
95
  this.options.initialSearchTerm,
78
- { goToFirstResult: this.goToFirstResult, suppressFragmentChange: true }
96
+ { goToFirstResult: this.options.goToFirstResult, suppressFragmentChange: false }
79
97
  );
80
98
  }
81
99
  };
@@ -93,6 +111,25 @@ BookReader.prototype.buildToolbarElement = (function (super_) {
93
111
  };
94
112
  })(BookReader.prototype.buildToolbarElement);
95
113
 
114
+ /** @override */
115
+ BookReader.prototype._createPageContainer = (function (super_) {
116
+ return function (index) {
117
+ const pageContainer = super_.call(this, index);
118
+ if (this.enableSearch && pageContainer.page && index in this._searchBoxesByIndex) {
119
+ const pageIndex = pageContainer.page.index;
120
+ const boxes = this._searchBoxesByIndex[pageIndex];
121
+ renderBoxesInPageContainerLayer(
122
+ 'searchHiliteLayer',
123
+ boxes,
124
+ pageContainer.page,
125
+ pageContainer.$container[0],
126
+ boxes.map(b => `match-index-${b.matchIndex}`),
127
+ );
128
+ }
129
+ return pageContainer;
130
+ };
131
+ })(BookReader.prototype._createPageContainer);
132
+
96
133
  /**
97
134
  * @typedef {object} SearchOptions
98
135
  * @property {boolean} goToFirstResult
@@ -107,7 +144,7 @@ BookReader.prototype.buildToolbarElement = (function (super_) {
107
144
  * @param {string} term
108
145
  * @param {SearchOptions} overrides
109
146
  */
110
- BookReader.prototype.search = function(term = '', overrides = {}) {
147
+ BookReader.prototype.search = async function(term = '', overrides = {}) {
111
148
  /** @type {SearchOptions} */
112
149
  const defaultOptions = {
113
150
  goToFirstResult: false, /* jump to the first result (default=false) */
@@ -119,6 +156,7 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
119
156
  };
120
157
  const options = jQuery.extend({}, defaultOptions, overrides);
121
158
  this.suppressFragmentChange = options.suppressFragmentChange;
159
+ this.searchCancelled = false;
122
160
 
123
161
  // strip slashes, since this goes in the url
124
162
  this.searchTerm = term.replace(/\//g, ' ');
@@ -133,7 +171,7 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
133
171
 
134
172
  // Remove the port and userdir
135
173
  const serverPath = this.server.replace(/:.+/, '');
136
- const baseUrl = `https://${serverPath}${this.searchInsideUrl}?`;
174
+ const baseUrl = `${this.options.searchInsideProtocol}://${serverPath}${this.searchInsideUrl}?`;
137
175
 
138
176
  // Remove subPrefix from end of path
139
177
  let path = this.bookPath;
@@ -147,6 +185,8 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
147
185
  doc: this.subPrefix,
148
186
  path,
149
187
  q: term,
188
+ pre_tag: this.options.searchInsidePreTag,
189
+ post_tag: this.options.searchInsidePostTag,
150
190
  };
151
191
 
152
192
  // NOTE that the API does not expect / (slashes) to be encoded. (%2F) won't work
@@ -154,13 +194,8 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
154
194
 
155
195
  const url = `${baseUrl}${paramStr}`;
156
196
 
157
- const cleanup = () => {
158
- this.searchXHR = null
159
- window.BRSearchInProgress = () => {};
160
- };
161
-
162
- const processSearchResults = (searchInsideResults) => {
163
- if (!this.searchXHR) {
197
+ const callSearchResultsCallback = (searchInsideResults) => {
198
+ if (this.searchCancelled) {
164
199
  return;
165
200
  }
166
201
  const responseHasError = searchInsideResults.error || !searchInsideResults.matches.length;
@@ -168,6 +203,7 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
168
203
  const hasCustomSuccess = typeof options.success === 'function';
169
204
 
170
205
  if (responseHasError) {
206
+ console.error('Search Inside Response Error', searchInsideResults.error || 'matches.length == 0');
171
207
  hasCustomError
172
208
  ? options.error.call(this, searchInsideResults, options)
173
209
  : this.BRSearchCallbackError(searchInsideResults, options);
@@ -176,21 +212,15 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
176
212
  ? options.success.call(this, searchInsideResults, options)
177
213
  : this.BRSearchCallback(searchInsideResults, options);
178
214
  }
179
- cleanup();
180
- };
181
-
182
- const beforeSend = (xhr) => {
183
- this.searchXHR = xhr;
184
- window.BRSearchInProgress = processSearchResults;
185
215
  };
186
216
 
187
217
  this.trigger('SearchStarted', { term: this.searchTerm, instance: this });
188
- return $.ajax({
218
+ callSearchResultsCallback(await $.ajax({
189
219
  url: url,
190
220
  dataType: 'jsonp',
191
- beforeSend,
192
- jsonpCallback: 'BRSearchInProgress'
193
- }).then(processSearchResults)
221
+ cache: true,
222
+ beforeSend: xhr => { this.searchXHR = xhr; },
223
+ }));
194
224
  };
195
225
 
196
226
  /**
@@ -202,21 +232,22 @@ BookReader.prototype._cancelSearch = function () {
202
232
  this.searchView.clearSearchFieldAndResults(false);
203
233
  this.searchTerm = '';
204
234
  this.searchXHR = null;
235
+ this.searchCancelled = true;
205
236
  this.searchResults = [];
206
- window.BRSearchInProgress = () => {};
207
- }
237
+ };
208
238
 
209
239
  /**
210
240
  * External function to cancel search
211
241
  * checks for term & xhr in flight before running
212
242
  */
213
243
  BookReader.prototype.cancelSearchRequest = function () {
244
+ this.searchCancelled = true;
214
245
  if (this.searchXHR !== null) {
215
246
  this._cancelSearch();
216
247
  this.searchView.toggleSearchPending();
217
248
  this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
218
249
  }
219
- }
250
+ };
220
251
 
221
252
  /**
222
253
  * @typedef {object} SearchInsideMatchBox
@@ -226,10 +257,14 @@ BookReader.prototype.cancelSearchRequest = function () {
226
257
  * @property {number} b
227
258
  * @property {number} t
228
259
  * @property {HTMLDivElement} [div]
260
+ * @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.
229
261
  */
230
262
 
231
263
  /**
232
264
  * @typedef {object} SearchInsideMatch
265
+ * @property {number} matchIndex This is a fake field! Not part of the API response. It is added by the JS.
266
+ * @property {string} displayPageNumber (fake field) The page number as it should be displayed in the UI.
267
+ * @property {string} html (computed field) The html-escaped raw html to display in the UI.
233
268
  * @property {string} text
234
269
  * @property {Array<{ page: number, boxes: SearchInsideMatchBox[] }>} par
235
270
  */
@@ -243,21 +278,26 @@ BookReader.prototype.cancelSearchRequest = function () {
243
278
 
244
279
  /**
245
280
  * Search Results return handler
246
- * @callback
247
281
  * @param {SearchInsideResults} results
248
282
  * @param {object} options
249
283
  * @param {boolean} options.goToFirstResult
250
284
  */
251
285
  BookReader.prototype.BRSearchCallback = function(results, options) {
286
+ marshallSearchResults(
287
+ results,
288
+ pageNum => this.book.getPageNum(this.book.leafNumToIndex(pageNum)),
289
+ this.options.searchInsidePreTag,
290
+ this.options.searchInsidePostTag,
291
+ );
252
292
  this.searchResults = results || [];
253
293
 
254
294
  this.updateSearchHilites();
255
295
  this.removeProgressPopup();
256
296
  if (options.goToFirstResult) {
257
- this._searchPluginGoToResult(results.matches[0].par[0].page);
297
+ this._searchPluginGoToResult(0);
258
298
  }
259
299
  this.trigger('SearchCallback', { results, options, instance: this });
260
- }
300
+ };
261
301
 
262
302
  /**
263
303
  * Main search results error handler
@@ -297,95 +337,41 @@ BookReader.prototype._BRSearchCallbackError = function(results) {
297
337
  * updates search on-page highlights controller
298
338
  */
299
339
  BookReader.prototype.updateSearchHilites = function() {
300
- if (this.constMode2up == this.mode) {
301
- this.updateSearchHilites2UP();
302
- return;
340
+ /** @type {SearchInsideMatch[]} */
341
+ const matches = this.searchResults?.matches || [];
342
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
343
+ const boxesByIndex = {};
344
+
345
+ // Clear any existing svg layers
346
+ this.removeSearchHilites();
347
+
348
+ // Group by pageIndex
349
+ for (const match of matches) {
350
+ for (const box of match.par[0].boxes) {
351
+ const pageIndex = this.book.leafNumToIndex(box.page);
352
+ const pageBoxes = boxesByIndex[pageIndex] || (boxesByIndex[pageIndex] = []);
353
+ pageBoxes.push(box);
354
+ }
303
355
  }
304
- this.updateSearchHilites1UP();
305
- };
306
356
 
307
- /**
308
- * update search on-page highlights in 1up mode
309
- */
310
- BookReader.prototype.updateSearchHilites1UP = function() {
311
- const results = this.searchResults;
312
- if (null == results) return;
313
- results.matches?.forEach(match => {
314
- match.par[0].boxes.forEach(box => {
315
- const pageIndex = this.leafNumToIndex(box.page);
316
- const pageIsInView = jQuery.inArray(pageIndex, this.displayedIndices) >= 0;
317
- if (pageIsInView) {
318
- if (!box.div) {
319
- //create a div for the search highlight, and stash it in the box object
320
- box.div = document.createElement('div');
321
- $(box.div).prop('className', 'BookReaderSearchHilite').appendTo(this.$(`.pagediv${pageIndex}`));
322
- }
323
- const page = this._models.book.getPage(pageIndex);
324
- const highlight = {
325
- width: this._modes.mode1Up.physicalInchesToDisplayPixels((box.r - box.l) / page.ppi),
326
- height: this._modes.mode1Up.physicalInchesToDisplayPixels((box.b - box.t) / page.ppi),
327
- left: this._modes.mode1Up.physicalInchesToDisplayPixels(box.l / page.ppi),
328
- top: this._modes.mode1Up.physicalInchesToDisplayPixels(box.t / page.ppi),
329
- };
330
- $(box.div).css(highlight);
331
- } else {
332
- if (box.div) {
333
- $(box.div).remove();
334
- box.div = null;
335
- }
336
- }
337
- });
338
- });
339
- };
340
-
341
- /**
342
- * update search on-page highlights in 2up mode
343
- */
344
- BookReader.prototype.updateSearchHilites2UP = function() {
345
- const results = this.searchResults;
357
+ // update any already created pages
358
+ for (const [pageIndexString, boxes] of Object.entries(boxesByIndex)) {
359
+ const pageIndex = parseFloat(pageIndexString);
360
+ const page = this.book.getPage(pageIndex);
361
+ const pageContainers = this.getActivePageContainerElementsForIndex(pageIndex);
362
+ for (const container of pageContainers) {
363
+ renderBoxesInPageContainerLayer('searchHiliteLayer', boxes, page, container, boxes.map(b => `match-index-${b.matchIndex}`));
364
+ }
365
+ }
346
366
 
347
- if (results === null) return;
348
-
349
- const { matches = [] } = results;
350
- matches.forEach((match) => {
351
- match.par[0].boxes.forEach(box => {
352
- const pageIndex = this.leafNumToIndex(match.par[0].page);
353
- const pageIsInView = jQuery.inArray(pageIndex, this.displayedIndices) >= 0;
354
- const { isViewable } = this._models.book.getPage(pageIndex);
355
-
356
- if (pageIsInView && isViewable) {
357
- if (!box.div) {
358
- //create a div for the search highlight, and stash it in the box object
359
- box.div = document.createElement('div');
360
- $(box.div).addClass('BookReaderSearchHilite')
361
- .appendTo(this.refs.$brTwoPageView);
362
- }
363
- this.setHilightCss2UP(box.div, pageIndex, box.l, box.r, box.t, box.b);
364
- } else {
365
- // clear stale reference
366
- if (box.div) {
367
- $(box.div).remove();
368
- box.div = null;
369
- }
370
- }
371
- });
372
- });
367
+ this._searchBoxesByIndex = boxesByIndex;
373
368
  };
374
369
 
375
370
  /**
376
371
  * remove search highlights
377
372
  */
378
373
  BookReader.prototype.removeSearchHilites = function() {
379
- const results = this.searchResults;
380
- if (null == results || !results.matches) { return; }
381
- results.matches.forEach(match => {
382
- match.par[0].boxes.forEach(box => {
383
- if (null != box.div) {
384
- $(box.div).remove();
385
- box.div = null;
386
- }
387
- });
388
- });
374
+ $(this.getActivePageContainerElements()).find('.searchHiliteLayer').remove();
389
375
  };
390
376
 
391
377
  /**
@@ -393,11 +379,14 @@ BookReader.prototype.removeSearchHilites = function() {
393
379
  * Goes to the page specified. If the page is not viewable, tries to load the page
394
380
  * FIXME Most of this logic is IA specific, and should be less integrated into here
395
381
  * or at least more configurable.
396
- * @param {PageIndex} pageIndex
382
+ * @param {number} matchIndex
397
383
  */
398
- BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
399
- const { book } = this._models;
384
+ BookReader.prototype._searchPluginGoToResult = async function (matchIndex) {
385
+ const match = this.searchResults?.matches[matchIndex];
386
+ const book = this.book;
387
+ const pageIndex = book.leafNumToIndex(match.par[0].page);
400
388
  const page = book.getPage(pageIndex);
389
+ const onNearbyPage = Math.abs(this.currentIndex() - pageIndex) < 3;
401
390
  let makeUnviewableAtEnd = false;
402
391
  if (!page.isViewable) {
403
392
  const resp = await fetch('/services/bookreader/request_page?' + new URLSearchParams({
@@ -416,15 +405,43 @@ BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
416
405
  book.getPage(pageIndex).makeViewable();
417
406
  makeUnviewableAtEnd = true;
418
407
  }
408
+
409
+ // Trigger an update of book
410
+ this._modes.mode1Up.mode1UpLit.updatePages();
411
+ if (this.activeMode == this._modes.mode1Up) {
412
+ await this._modes.mode1Up.mode1UpLit.updateComplete;
413
+ }
419
414
  }
420
415
  /* this updates the URL */
421
- this.suppressFragmentChange = false;
422
- this.jumpToIndex(pageIndex);
416
+ if (!this._isIndexDisplayed(pageIndex)) {
417
+ this.suppressFragmentChange = false;
418
+ this.jumpToIndex(pageIndex);
419
+ }
423
420
 
424
421
  // Reset it to unviewable if it wasn't resolved
425
422
  if (makeUnviewableAtEnd) {
426
423
  book.getPage(pageIndex).makeViewable(false);
427
424
  }
425
+
426
+ // Scroll/flash in the ui
427
+ const $boxes = await poll(() => $(`rect.match-index-${match.matchIndex}`), { until: result => result.length > 0 });
428
+ if ($boxes.length) {
429
+ $boxes.css('animation', 'none');
430
+ $boxes[0].scrollIntoView({
431
+ // Only vertically center the highlight if we're in 1up or in full screen. In
432
+ // 2up, if we're not fullscreen, the whole body gets scrolled around to try to
433
+ // center the highlight 🙄 See:
434
+ // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move/11041376
435
+ // Note: nearest doesn't quite work great, because the ReadAloud toolbar is now
436
+ // full-width, and covers up the last line of the highlight.
437
+ block: this.constMode1up == this.mode || this.isFullscreenActive ? 'center' : 'nearest',
438
+ inline: 'center',
439
+ behavior: onNearbyPage ? 'smooth' : 'auto',
440
+ });
441
+ // wait for animation to start
442
+ await new Promise(resolve => setTimeout(resolve, 100));
443
+ $boxes.removeAttr("style");
444
+ }
428
445
  };
429
446
 
430
447
  /**
@@ -458,7 +475,7 @@ BookReader.prototype.searchHighlightVisible = function() {
458
475
 
459
476
  results.matches.some(match => {
460
477
  return match.par[0].boxes.some(box => {
461
- const pageIndex = this.leafNumToIndex(box.page);
478
+ const pageIndex = this.book.leafNumToIndex(box.page);
462
479
  if (jQuery.inArray(pageIndex, visiblePages) >= 0) {
463
480
  return true;
464
481
  }
@@ -0,0 +1,43 @@
1
+ import { escapeHTML, escapeRegExp } from '../../BookReader/utils.js';
2
+
3
+ /**
4
+ * @param {string} match
5
+ * @param {string} preTag
6
+ * @param {string} postTag
7
+ * @returns {string}
8
+ */
9
+ export function renderMatch(match, preTag, postTag) {
10
+ // Search results are returned as a text blob with the hits wrapped in
11
+ // triple mustaches. Hits occasionally include text beyond the search
12
+ // term, so everything within the staches is captured and wrapped.
13
+ const preTagRe = escapeRegExp(escapeHTML(preTag));
14
+ const postTagRe = escapeRegExp(escapeHTML(postTag));
15
+ // [^] matches any character, including line breaks
16
+ const regex = new RegExp(`${preTagRe}([^]+?)${postTagRe}`, 'g');
17
+ return escapeHTML(match)
18
+ .replace(regex, '<mark>$1</mark>')
19
+ // Fix trailing hyphens. This over-corrects but is net useful.
20
+ .replace(/(\b)- /g, '$1');
21
+ }
22
+
23
+ /**
24
+ * Attach some fields to search inside results
25
+ * @param {SearchInsideResults} results
26
+ * @param {(pageNum: LeafNum) => PageNumString} displayPageNumberFn
27
+ * @param {string} preTag
28
+ * @param {string} postTag
29
+ */
30
+ export function marshallSearchResults(results, displayPageNumberFn, preTag, postTag) {
31
+ // Attach matchIndex to a few things to make it easier to identify
32
+ // an active/selected match
33
+ for (const [index, match] of results.matches.entries()) {
34
+ match.matchIndex = index;
35
+ match.displayPageNumber = displayPageNumberFn(match.par[0].page);
36
+ match.html = renderMatch(match.text, preTag, postTag);
37
+ for (const par of match.par) {
38
+ for (const box of par.boxes) {
39
+ box.matchIndex = index;
40
+ }
41
+ }
42
+ }
43
+ }
@@ -9,11 +9,6 @@ class SearchView {
9
9
  */
10
10
  constructor({ br, searchCancelledCallback = () => {} }) {
11
11
  this.br = br;
12
-
13
- // Search results are returned as a text blob with the hits wrapped in
14
- // triple mustaches. Hits occasionally include text beyond the search
15
- // term, so everything within the staches is captured and wrapped.
16
- this.matcher = new RegExp('{{{(.+?)}}}', 'g');
17
12
  this.matches = [];
18
13
  this.cacheDOMElements();
19
14
  this.bindEvents();
@@ -142,17 +137,19 @@ class SearchView {
142
137
  const start = pool.slice(0, pool.length / 2);
143
138
  const end = pool.slice(pool.length / 2);
144
139
  return closestTo((comparisonFn(start, end, comparator) ? start : end), comparator);
145
- }
140
+ };
146
141
 
147
142
  const closestPage = closestTo(matchPages, currentPage);
148
143
  return this.matches.indexOf(this.matches.find((m) => m.par[0].page === closestPage));
149
144
  }
150
145
 
151
146
  updateResultsPosition() {
147
+ if (!this.dom.searchNavigation) return;
152
148
  this.dom.searchNavigation.find('[data-id=resultsCount]').text(this.resultsPosition());
153
149
  }
154
150
 
155
151
  updateSearchNavigationButtons() {
152
+ if (!this.dom.searchNavigation) return;
156
153
  this.dom.searchNavigation.find('.prev').attr('disabled', !this.currentMatchIndex);
157
154
  this.dom.searchNavigation.find('.next').attr('disabled', this.currentMatchIndex + 1 === this.matches.length);
158
155
  }
@@ -226,25 +223,20 @@ class SearchView {
226
223
  */
227
224
  renderPins(matches) {
228
225
  matches.forEach((match) => {
229
- const queryString = match.text;
230
- const pageIndex = this.br.leafNumToIndex(match.par[0].page);
231
- const pageNumber = this.br.getPageNum(pageIndex);
226
+ const pageIndex = this.br.book.leafNumToIndex(match.par[0].page);
232
227
  const uiStringSearch = "Search result"; // i18n
233
- const uiStringPage = "Page"; // i18n
234
-
235
- const percentThrough = this.br.constructor.util.cssPercentage(pageIndex, this.br.getNumLeafs() - 1);
236
-
237
- const queryStringWithB = queryString.replace(this.matcher, '<b>$1</b>');
238
-
239
- let queryStringWithBTruncated = '';
240
-
241
- if (queryString.length > 100) {
242
- queryStringWithBTruncated = queryString
243
- .replace(/^(.{100}[^\s]*).*/, "$1")
244
- .replace(this.matcher, '<b>$1</b>')
245
- + '...';
228
+ const percentThrough = this.br.constructor.util.cssPercentage(pageIndex, this.br.book.getNumLeafs() - 1);
229
+
230
+ let html = match.html;
231
+ if (html.length > 200) {
232
+ const start = Math.max(0, html.indexOf('<mark>') - 100);
233
+ if (start != 0) {
234
+ html = '' + match.html
235
+ .substring(start)
236
+ // Make sure at word boundary though
237
+ .replace(/^\S+/, '');
238
+ }
246
239
  }
247
-
248
240
  // draw marker
249
241
  $('<div>')
250
242
  .addClass('BRsearch')
@@ -254,35 +246,27 @@ class SearchView {
254
246
  .attr('title', uiStringSearch)
255
247
  .append(`
256
248
  <div class="BRquery">
257
- <div>${queryStringWithBTruncated || queryStringWithB}</div>
258
- <div>${uiStringPage} ${pageNumber}</div>
249
+ <main>${html}</main>
250
+ <footer>Page ${match.displayPageNumber}</footer>
259
251
  </div>
260
252
  `)
261
- .data({ pageIndex })
262
253
  .appendTo(this.br.$('.BRnavline'))
263
- .hover(
264
- (event) => {
265
- // remove from other markers then turn on just for this
266
- // XXX should be done when nav slider moves
267
- const marker = event.currentTarget;
268
- const tooltip = marker.querySelector('.BRquery');
269
- const tooltipOffset = tooltip.getBoundingClientRect();
270
- const targetOffset = marker.getBoundingClientRect();
271
- const boxSizeAdjust = parseInt(getComputedStyle(tooltip).paddingLeft) * 2;
272
- if (tooltipOffset.x - boxSizeAdjust < 0) {
273
- tooltip.style.setProperty('transform', `translateX(-${targetOffset.left - boxSizeAdjust}px)`);
274
- }
275
- $('.BRsearch,.BRchapter').removeClass('front');
276
- $(event.target).addClass('front');
277
- },
278
- (event) => $(event.target).removeClass('front'))
279
- .click(function (event) {
280
- // closures are nested and deep, using an arrow function breaks references.
281
- // Todo: update to arrow function & clean up closures
282
- // to remove `bind` dependency
283
- this.br._searchPluginGoToResult(+$(event.target).data('pageIndex'));
284
- this.br.updateSearchHilites();
285
- }.bind(this));
254
+ .on("mouseenter", (event) => {
255
+ // remove from other markers then turn on just for this
256
+ // XXX should be done when nav slider moves
257
+ const marker = event.currentTarget;
258
+ const tooltip = marker.querySelector('.BRquery');
259
+ const tooltipOffset = tooltip.getBoundingClientRect();
260
+ const targetOffset = marker.getBoundingClientRect();
261
+ const boxSizeAdjust = parseInt(getComputedStyle(tooltip).paddingLeft) * 2;
262
+ if (tooltipOffset.x - boxSizeAdjust < 0) {
263
+ tooltip.style.setProperty('transform', `translateX(-${targetOffset.left - boxSizeAdjust}px)`);
264
+ }
265
+ $('.BRsearch,.BRchapter').removeClass('front');
266
+ $(event.target).addClass('front');
267
+ })
268
+ .on("mouseleave", (event) => $(event.target).removeClass('front'))
269
+ .on("click", () => { this.br._searchPluginGoToResult(match.matchIndex); });
286
270
  });
287
271
  }
288
272
 
@@ -386,14 +370,6 @@ class SearchView {
386
370
  }
387
371
  }
388
372
 
389
- /**
390
- * @param {Event} e
391
- */
392
- handleNavToggledCallback(e) {
393
- const is_visible = this.br.navigationIsVisible();
394
- this.togglePinsFor(is_visible);
395
- }
396
-
397
373
  handleSearchStarted() {
398
374
  this.emptyMatches();
399
375
  this.br.removeSearchHilites();
@@ -428,7 +404,6 @@ class SearchView {
428
404
 
429
405
  window.addEventListener(`${namespace}SearchCallbackError`, this.handleSearchCallbackError.bind(this));
430
406
  $(document).on(`${namespace}SearchCallback`, this.handleSearchCallback.bind(this))
431
- .on(`${namespace}navToggled`, this.handleNavToggledCallback.bind(this))
432
407
  .on(`${namespace}SearchStarted`, this.handleSearchStarted.bind(this))
433
408
  .on(`${namespace}SearchCallbackBookNotIndexed`, this.handleSearchCallbackBookNotIndexed.bind(this))
434
409
  .on(`${namespace}SearchCallbackEmpty`, this.handleSearchCallbackEmpty.bind(this))