@internetarchive/bookreader 5.0.0-8-multiple-files → 5.0.0-80

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 (321) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +78 -6
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +442 -1393
  6. package/BookReader/BookReader.js +2 -21564
  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 -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 +22 -301
  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 -74
  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 -368
  71. package/BookReader/plugins/plugin.resume.js.map +1 -1
  72. package/BookReader/plugins/plugin.search.js +2 -1420
  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 -1080
  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 +2 -9193
  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 -269
  82. package/BookReader/plugins/plugin.url.js.map +1 -1
  83. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  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 +545 -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 +9 -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 +80 -28
  136. package/src/BookNavigator/search/search-results.js +28 -25
  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 +446 -1062
  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 +1 -17
  166. package/src/css/_BRBookmarks.scss +1 -1
  167. package/src/css/_BRComponent.scss +1 -1
  168. package/src/css/_BRmain.scss +33 -27
  169. package/src/css/_BRnav.scss +12 -39
  170. package/src/css/_BRpages.scss +149 -40
  171. package/src/css/_BRsearch.scss +68 -230
  172. package/src/css/_BRtoolbar.scss +5 -5
  173. package/src/css/_TextSelection.scss +87 -27
  174. package/src/css/_colorbox.scss +2 -2
  175. package/src/css/_controls.scss +20 -7
  176. package/src/css/_icons.scss +7 -1
  177. package/src/ia-bookreader/ia-bookreader.js +224 -0
  178. package/src/plugins/plugin.archive_analytics.js +3 -3
  179. package/src/plugins/plugin.autoplay.js +5 -11
  180. package/src/plugins/plugin.chapters.js +237 -191
  181. package/src/plugins/plugin.iiif.js +151 -0
  182. package/src/plugins/plugin.resume.js +3 -3
  183. package/src/plugins/plugin.text_selection.js +464 -134
  184. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  185. package/src/plugins/search/plugin.search.js +175 -120
  186. package/src/plugins/search/utils.js +43 -0
  187. package/src/plugins/search/view.js +64 -202
  188. package/src/plugins/tts/AbstractTTSEngine.js +71 -40
  189. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  190. package/src/plugins/tts/PageChunk.js +15 -21
  191. package/src/plugins/tts/PageChunkIterator.js +8 -12
  192. package/src/plugins/tts/WebTTSEngine.js +87 -71
  193. package/src/plugins/tts/plugin.tts.js +96 -127
  194. package/src/plugins/tts/utils.js +15 -25
  195. package/src/plugins/url/UrlPlugin.js +191 -0
  196. package/src/plugins/{plugin.url.js → url/plugin.url.js} +45 -16
  197. package/src/util/browserSniffing.js +22 -0
  198. package/src/util/docCookies.js +21 -2
  199. package/tests/e2e/README.md +37 -0
  200. package/tests/e2e/autoplay.test.js +2 -2
  201. package/tests/e2e/base.test.js +8 -16
  202. package/tests/e2e/helpers/base.js +53 -48
  203. package/tests/e2e/helpers/debug.js +1 -1
  204. package/tests/e2e/helpers/params.js +17 -0
  205. package/tests/e2e/helpers/rightToLeft.js +8 -14
  206. package/tests/e2e/helpers/search.js +73 -0
  207. package/tests/e2e/models/Navigation.js +20 -37
  208. package/tests/e2e/rightToLeft.test.js +4 -5
  209. package/tests/e2e/viewmode.test.js +40 -33
  210. package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
  211. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  212. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  213. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  214. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  215. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  216. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  217. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  218. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +109 -60
  219. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  220. package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
  221. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  222. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  223. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  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 +218 -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/jest/BookReader/utils/SelectionObserver.test.js +57 -0
  238. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  239. package/tests/jest/BookReader/utils.test.js +229 -0
  240. package/tests/jest/BookReader.keyboard.test.js +190 -0
  241. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  242. package/tests/{BookReader.test.js → jest/BookReader.test.js} +26 -37
  243. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  244. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  245. package/tests/jest/plugins/plugin.chapters.test.js +195 -0
  246. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  247. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  248. package/tests/jest/plugins/plugin.text_selection.test.js +317 -0
  249. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  250. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +26 -47
  251. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +39 -6
  252. package/tests/jest/plugins/search/utils.js +25 -0
  253. package/tests/jest/plugins/search/utils.test.js +29 -0
  254. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +29 -9
  255. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  256. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  257. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  258. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  259. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  260. package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
  261. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +53 -14
  262. package/tests/jest/setup.js +3 -0
  263. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  264. package/tests/jest/util/docCookies.test.js +24 -0
  265. package/tests/{util → jest/util}/strings.test.js +1 -1
  266. package/tests/{utils.js → jest/utils.js} +38 -0
  267. package/webpack.config.js +12 -6
  268. package/.babelrc +0 -12
  269. package/.dependabot/config.yml +0 -6
  270. package/.testcaferc.json +0 -5
  271. package/BookReader/bookreader-component-bundle.js +0 -14312
  272. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  273. package/BookReader/bookreader-component-bundle.js.map +0 -1
  274. package/BookReader/icons/sort-ascending.svg +0 -1
  275. package/BookReader/icons/sort-descending.svg +0 -1
  276. package/BookReader/jquery-1.10.1.js +0 -108
  277. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  278. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  279. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  280. package/BookReader/plugins/plugin.mobile_nav.js +0 -335
  281. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  282. package/BookReaderDemo/IIIFBookReader.js +0 -207
  283. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  284. package/BookReaderDemo/demo-iiif.js +0 -26
  285. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  286. package/karma.conf.js +0 -23
  287. package/src/BookNavigator/BookModel.js +0 -14
  288. package/src/BookNavigator/BookNavigator.js +0 -452
  289. package/src/BookNavigator/assets/book-loader.js +0 -27
  290. package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
  291. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  292. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  293. package/src/BookNavigator/search/a-search-result.js +0 -55
  294. package/src/BookNavigator/volumes/volumes-provider.js +0 -76
  295. package/src/BookNavigator/volumes/volumes.js +0 -161
  296. package/src/BookReader/DebugConsole.js +0 -54
  297. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  298. package/src/ItemNavigator/ItemNavigator.js +0 -372
  299. package/src/ItemNavigator/providers/sharing.js +0 -29
  300. package/src/assets/icons/sort-ascending.svg +0 -1
  301. package/src/assets/icons/sort-descending.svg +0 -1
  302. package/src/css/_MobileNav.scss +0 -194
  303. package/src/dragscrollable-br.js +0 -261
  304. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  305. package/src/plugins/plugin.mobile_nav.js +0 -287
  306. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  307. package/tests/BookReader/DebugConsole.test.js +0 -25
  308. package/tests/BookReader/Mode1Up.test.js +0 -164
  309. package/tests/BookReader/Mode2Up.test.js +0 -247
  310. package/tests/BookReader/utils.test.js +0 -109
  311. package/tests/e2e/helpers/desktopSearch.js +0 -72
  312. package/tests/e2e/helpers/mobileSearch.js +0 -85
  313. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  314. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  315. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  316. package/tests/karma/BookNavigator/volumes.test.js +0 -101
  317. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  318. package/tests/plugins/plugin.chapters.test.js +0 -130
  319. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  320. package/tests/plugins/plugin.text_selection.test.js +0 -203
  321. 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
@@ -20,8 +18,17 @@
20
18
  * the book has not had OCR text indexed yet. Receives `instance`
21
19
  * @event BookReader:SearchCallbackEmpty - When no results found. Receives
22
20
  * `instance`
21
+ * @event BookReader:SearchCanceled - When no results found. Receives
22
+ * `instance`
23
23
  */
24
+ import { poll } from '../../BookReader/utils.js';
25
+ import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.js';
24
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 */
25
32
 
26
33
  jQuery.extend(BookReader.defaultOptions, {
27
34
  server: 'ia600609.us.archive.org',
@@ -29,7 +36,10 @@ jQuery.extend(BookReader.defaultOptions, {
29
36
  subPrefix: '',
30
37
  bookPath: '',
31
38
  enableSearch: true,
39
+ searchInsideProtocol: 'https',
32
40
  searchInsideUrl: '/fulltext/inside.php',
41
+ searchInsidePreTag: '{{{',
42
+ searchInsidePostTag: '}}}',
33
43
  initialSearchTerm: null,
34
44
  });
35
45
 
@@ -42,7 +52,6 @@ BookReader.prototype.setup = (function (super_) {
42
52
  this.searchResults = null;
43
53
  this.searchInsideUrl = options.searchInsideUrl;
44
54
  this.enableSearch = options.enableSearch;
45
- this.goToFirstResult = false;
46
55
 
47
56
  // Base server used by some api calls
48
57
  this.bookId = options.bookId;
@@ -50,11 +59,14 @@ BookReader.prototype.setup = (function (super_) {
50
59
  this.subPrefix = options.subPrefix;
51
60
  this.bookPath = options.bookPath;
52
61
 
53
- if (this.searchView) { return; }
54
- this.searchView = new SearchView({
55
- br: this,
56
- selector: '#BRsearch_tray',
57
- });
62
+ this.searchXHR = null;
63
+ this._cancelSearch.bind(this);
64
+ this.cancelSearchRequest.bind(this);
65
+
66
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
67
+ this._searchBoxesByIndex = {};
68
+
69
+ this.searchView = undefined;
58
70
  };
59
71
  })(BookReader.prototype.setup);
60
72
 
@@ -62,28 +74,31 @@ BookReader.prototype.setup = (function (super_) {
62
74
  BookReader.prototype.init = (function (super_) {
63
75
  return function () {
64
76
  super_.call(this);
65
-
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
+ });
66
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
+ */
67
94
  this.search(
68
95
  this.options.initialSearchTerm,
69
- { goToFirstResult: this.goToFirstResult, suppressFragmentChange: true }
96
+ { goToFirstResult: this.options.goToFirstResult, suppressFragmentChange: false }
70
97
  );
71
98
  }
72
99
  };
73
100
  })(BookReader.prototype.init);
74
101
 
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
102
  /** @override */
88
103
  BookReader.prototype.buildToolbarElement = (function (super_) {
89
104
  return function () {
@@ -96,6 +111,25 @@ BookReader.prototype.buildToolbarElement = (function (super_) {
96
111
  };
97
112
  })(BookReader.prototype.buildToolbarElement);
98
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
+
99
133
  /**
100
134
  * @typedef {object} SearchOptions
101
135
  * @property {boolean} goToFirstResult
@@ -110,7 +144,7 @@ BookReader.prototype.buildToolbarElement = (function (super_) {
110
144
  * @param {string} term
111
145
  * @param {SearchOptions} overrides
112
146
  */
113
- BookReader.prototype.search = function(term = '', overrides = {}) {
147
+ BookReader.prototype.search = async function(term = '', overrides = {}) {
114
148
  /** @type {SearchOptions} */
115
149
  const defaultOptions = {
116
150
  goToFirstResult: false, /* jump to the first result (default=false) */
@@ -122,6 +156,7 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
122
156
  };
123
157
  const options = jQuery.extend({}, defaultOptions, overrides);
124
158
  this.suppressFragmentChange = options.suppressFragmentChange;
159
+ this.searchCancelled = false;
125
160
 
126
161
  // strip slashes, since this goes in the url
127
162
  this.searchTerm = term.replace(/\//g, ' ');
@@ -136,7 +171,7 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
136
171
 
137
172
  // Remove the port and userdir
138
173
  const serverPath = this.server.replace(/:.+/, '');
139
- const baseUrl = `https://${serverPath}${this.searchInsideUrl}?`;
174
+ const baseUrl = `${this.options.searchInsideProtocol}://${serverPath}${this.searchInsideUrl}?`;
140
175
 
141
176
  // Remove subPrefix from end of path
142
177
  let path = this.bookPath;
@@ -150,6 +185,8 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
150
185
  doc: this.subPrefix,
151
186
  path,
152
187
  q: term,
188
+ pre_tag: this.options.searchInsidePreTag,
189
+ post_tag: this.options.searchInsidePostTag,
153
190
  };
154
191
 
155
192
  // NOTE that the API does not expect / (slashes) to be encoded. (%2F) won't work
@@ -157,12 +194,16 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
157
194
 
158
195
  const url = `${baseUrl}${paramStr}`;
159
196
 
160
- const processSearchResults = (searchInsideResults) => {
197
+ const callSearchResultsCallback = (searchInsideResults) => {
198
+ if (this.searchCancelled) {
199
+ return;
200
+ }
161
201
  const responseHasError = searchInsideResults.error || !searchInsideResults.matches.length;
162
202
  const hasCustomError = typeof options.error === 'function';
163
203
  const hasCustomSuccess = typeof options.success === 'function';
164
204
 
165
205
  if (responseHasError) {
206
+ console.error('Search Inside Response Error', searchInsideResults.error || 'matches.length == 0');
166
207
  hasCustomError
167
208
  ? options.error.call(this, searchInsideResults, options)
168
209
  : this.BRSearchCallbackError(searchInsideResults, options);
@@ -173,11 +214,39 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
173
214
  }
174
215
  };
175
216
 
176
- this.trigger('SearchStarted', { term: this.searchTerm });
177
- return $.ajax({
217
+ this.trigger('SearchStarted', { term: this.searchTerm, instance: this });
218
+ callSearchResultsCallback(await $.ajax({
178
219
  url: url,
179
- dataType: 'jsonp'
180
- }).then(processSearchResults);
220
+ dataType: 'jsonp',
221
+ cache: true,
222
+ beforeSend: xhr => { this.searchXHR = xhr; },
223
+ }));
224
+ };
225
+
226
+ /**
227
+ * cancels AJAX Call
228
+ * emits custom event
229
+ */
230
+ BookReader.prototype._cancelSearch = function () {
231
+ this.searchXHR?.abort();
232
+ this.searchView.clearSearchFieldAndResults(false);
233
+ this.searchTerm = '';
234
+ this.searchXHR = null;
235
+ this.searchCancelled = true;
236
+ this.searchResults = [];
237
+ };
238
+
239
+ /**
240
+ * External function to cancel search
241
+ * checks for term & xhr in flight before running
242
+ */
243
+ BookReader.prototype.cancelSearchRequest = function () {
244
+ this.searchCancelled = true;
245
+ if (this.searchXHR !== null) {
246
+ this._cancelSearch();
247
+ this.searchView.toggleSearchPending();
248
+ this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
249
+ }
181
250
  };
182
251
 
183
252
  /**
@@ -188,10 +257,14 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
188
257
  * @property {number} b
189
258
  * @property {number} t
190
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.
191
261
  */
192
262
 
193
263
  /**
194
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.
195
268
  * @property {string} text
196
269
  * @property {Array<{ page: number, boxes: SearchInsideMatchBox[] }>} par
197
270
  */
@@ -205,21 +278,26 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
205
278
 
206
279
  /**
207
280
  * Search Results return handler
208
- * @callback
209
281
  * @param {SearchInsideResults} results
210
282
  * @param {object} options
211
283
  * @param {boolean} options.goToFirstResult
212
284
  */
213
285
  BookReader.prototype.BRSearchCallback = function(results, options) {
214
- this.searchResults = results;
286
+ marshallSearchResults(
287
+ results,
288
+ pageNum => this.book.getPageNum(this.book.leafNumToIndex(pageNum)),
289
+ this.options.searchInsidePreTag,
290
+ this.options.searchInsidePostTag,
291
+ );
292
+ this.searchResults = results || [];
215
293
 
216
294
  this.updateSearchHilites();
217
295
  this.removeProgressPopup();
218
296
  if (options.goToFirstResult) {
219
- this._searchPluginGoToResult(results.matches[0].par[0].page);
297
+ this._searchPluginGoToResult(0);
220
298
  }
221
299
  this.trigger('SearchCallback', { results, options, instance: this });
222
- }
300
+ };
223
301
 
224
302
  /**
225
303
  * Main search results error handler
@@ -259,95 +337,41 @@ BookReader.prototype._BRSearchCallbackError = function(results) {
259
337
  * updates search on-page highlights controller
260
338
  */
261
339
  BookReader.prototype.updateSearchHilites = function() {
262
- if (this.constMode2up == this.mode) {
263
- this.updateSearchHilites2UP();
264
- 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
+ }
265
355
  }
266
- this.updateSearchHilites1UP();
267
- };
268
356
 
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;
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
+ }
308
366
 
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
- });
367
+ this._searchBoxesByIndex = boxesByIndex;
335
368
  };
336
369
 
337
370
  /**
338
371
  * remove search highlights
339
372
  */
340
373
  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
- });
374
+ $(this.getActivePageContainerElements()).find('.searchHiliteLayer').remove();
351
375
  };
352
376
 
353
377
  /**
@@ -355,11 +379,14 @@ BookReader.prototype.removeSearchHilites = function() {
355
379
  * Goes to the page specified. If the page is not viewable, tries to load the page
356
380
  * FIXME Most of this logic is IA specific, and should be less integrated into here
357
381
  * or at least more configurable.
358
- * @param {PageIndex} pageIndex
382
+ * @param {number} matchIndex
359
383
  */
360
- BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
361
- 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);
362
388
  const page = book.getPage(pageIndex);
389
+ const onNearbyPage = Math.abs(this.currentIndex() - pageIndex) < 3;
363
390
  let makeUnviewableAtEnd = false;
364
391
  if (!page.isViewable) {
365
392
  const resp = await fetch('/services/bookreader/request_page?' + new URLSearchParams({
@@ -378,15 +405,43 @@ BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
378
405
  book.getPage(pageIndex).makeViewable();
379
406
  makeUnviewableAtEnd = true;
380
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
+ }
381
414
  }
382
415
  /* this updates the URL */
383
- this.suppressFragmentChange = false;
384
- this.jumpToIndex(pageIndex);
416
+ if (!this._isIndexDisplayed(pageIndex)) {
417
+ this.suppressFragmentChange = false;
418
+ this.jumpToIndex(pageIndex);
419
+ }
385
420
 
386
421
  // Reset it to unviewable if it wasn't resolved
387
422
  if (makeUnviewableAtEnd) {
388
423
  book.getPage(pageIndex).makeViewable(false);
389
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
+ }
390
445
  };
391
446
 
392
447
  /**
@@ -420,7 +475,7 @@ BookReader.prototype.searchHighlightVisible = function() {
420
475
 
421
476
  results.matches.some(match => {
422
477
  return match.par[0].boxes.some(box => {
423
- const pageIndex = this.leafNumToIndex(box.page);
478
+ const pageIndex = this.book.leafNumToIndex(box.page);
424
479
  if (jQuery.inArray(pageIndex, visiblePages) >= 0) {
425
480
  return true;
426
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
+ }