@internetarchive/bookreader 5.0.0-9 → 5.0.0-90

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 (324) hide show
  1. package/.eslintrc.js +21 -19
  2. package/.github/workflows/node.js.yml +76 -11
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +404 -1125
  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/images/unviewable_page.png +0 -0
  58. package/BookReader/jquery-3.js +2 -0
  59. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  60. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  61. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  62. package/BookReader/plugins/plugin.autoplay.js +1 -1
  63. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  64. package/BookReader/plugins/plugin.chapters.js +25 -1
  65. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  66. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  67. package/BookReader/plugins/plugin.iframe.js +1 -1
  68. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  69. package/BookReader/plugins/plugin.iiif.js +2 -0
  70. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  71. package/BookReader/plugins/plugin.resume.js +1 -1
  72. package/BookReader/plugins/plugin.resume.js.map +1 -1
  73. package/BookReader/plugins/plugin.search.js +2 -1
  74. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  75. package/BookReader/plugins/plugin.search.js.map +1 -1
  76. package/BookReader/plugins/plugin.text_selection.js +2 -1
  77. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  78. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  79. package/BookReader/plugins/plugin.tts.js +1 -1
  80. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  81. package/BookReader/plugins/plugin.tts.js.map +1 -1
  82. package/BookReader/plugins/plugin.url.js +1 -1
  83. package/BookReader/plugins/plugin.url.js.map +1 -1
  84. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  85. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  86. package/BookReader/webcomponents-bundle.js +3 -0
  87. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  88. package/BookReader/webcomponents-bundle.js.map +1 -0
  89. package/BookReaderDemo/BookReaderDemo.css +18 -19
  90. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
  91. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  92. package/BookReaderDemo/IADemoBr.js +144 -0
  93. package/BookReaderDemo/demo-advanced.html +2 -2
  94. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  95. package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
  96. package/BookReaderDemo/demo-fullscreen.html +2 -4
  97. package/BookReaderDemo/demo-iiif.html +99 -12
  98. package/BookReaderDemo/demo-internetarchive.html +214 -18
  99. package/BookReaderDemo/demo-multiple.html +2 -1
  100. package/BookReaderDemo/demo-preview-pages.html +526 -525
  101. package/BookReaderDemo/demo-simple.html +2 -1
  102. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -4
  103. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  104. package/BookReaderDemo/immersion-1up.html +2 -2
  105. package/BookReaderDemo/immersion-mode.html +2 -4
  106. package/BookReaderDemo/toggle_controls.html +3 -2
  107. package/BookReaderDemo/view_mode.html +2 -1
  108. package/BookReaderDemo/viewmode-cycle.html +2 -3
  109. package/CHANGELOG.md +584 -33
  110. package/README.md +14 -1
  111. package/babel.config.js +20 -0
  112. package/codecov.yml +6 -0
  113. package/index.html +5 -2
  114. package/jsconfig.json +19 -0
  115. package/netlify.toml +9 -0
  116. package/package.json +70 -62
  117. package/renovate.json +52 -0
  118. package/scripts/preversion.js +0 -1
  119. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  120. package/src/BookNavigator/assets/button-base.js +5 -2
  121. package/src/BookNavigator/assets/ia-logo.js +17 -0
  122. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  123. package/src/BookNavigator/assets/icon_close.js +1 -1
  124. package/src/BookNavigator/book-navigator.js +590 -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 +4 -9
  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 +29 -25
  134. package/src/BookNavigator/search/search-provider.js +50 -28
  135. package/src/BookNavigator/search/search-results.js +24 -10
  136. package/src/BookNavigator/sharing.js +27 -0
  137. package/src/BookNavigator/viewable-files.js +95 -0
  138. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +13 -12
  139. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +7 -7
  140. package/src/BookReader/BookModel.js +76 -41
  141. package/src/BookReader/DragScrollable.js +233 -0
  142. package/src/BookReader/ImageCache.js +48 -15
  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 +777 -0
  147. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  148. package/src/BookReader/ModeSmoothZoom.js +312 -0
  149. package/src/BookReader/ModeThumb.js +19 -13
  150. package/src/BookReader/Navbar/Navbar.js +70 -54
  151. package/src/BookReader/PageContainer.js +116 -22
  152. package/src/BookReader/ReduceSet.js +3 -3
  153. package/src/BookReader/Toolbar/Toolbar.js +14 -41
  154. package/src/BookReader/events.js +2 -3
  155. package/src/BookReader/options.js +73 -15
  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/classes.js +1 -1
  160. package/src/BookReader/utils.js +128 -13
  161. package/src/BookReader.js +544 -1078
  162. package/src/BookReaderPlugin.js +44 -0
  163. package/src/assets/icons/magnify-minus.svg +3 -7
  164. package/src/assets/icons/magnify-plus.svg +3 -7
  165. package/src/assets/icons/voice.svg +1 -0
  166. package/src/assets/images/unviewable_page.png +0 -0
  167. package/src/css/BookReader.scss +1 -5
  168. package/src/css/_BRBookmarks.scss +1 -1
  169. package/src/css/_BRComponent.scss +1 -1
  170. package/src/css/_BRicon.scss +8 -2
  171. package/src/css/_BRmain.scss +16 -3
  172. package/src/css/_BRnav.scss +12 -42
  173. package/src/css/_BRpages.scss +170 -42
  174. package/src/css/_BRsearch.scss +68 -25
  175. package/src/css/_BRtoolbar.scss +5 -5
  176. package/src/css/_TextSelection.scss +87 -27
  177. package/src/css/_colorbox.scss +2 -2
  178. package/src/css/_controls.scss +24 -7
  179. package/src/css/_icons.scss +1 -1
  180. package/src/ia-bookreader/ia-bookreader.js +224 -0
  181. package/src/plugins/plugin.archive_analytics.js +84 -78
  182. package/src/plugins/plugin.autoplay.js +99 -104
  183. package/src/plugins/plugin.chapters.js +237 -191
  184. package/src/plugins/plugin.iframe.js +1 -1
  185. package/src/plugins/plugin.iiif.js +141 -0
  186. package/src/plugins/plugin.resume.js +53 -50
  187. package/src/plugins/plugin.text_selection.js +503 -175
  188. package/src/plugins/plugin.vendor-fullscreen.js +7 -7
  189. package/src/plugins/search/plugin.search.js +151 -127
  190. package/src/plugins/search/utils.js +43 -0
  191. package/src/plugins/search/view.js +37 -59
  192. package/src/plugins/tts/AbstractTTSEngine.js +75 -45
  193. package/src/plugins/tts/FestivalTTSEngine.js +21 -31
  194. package/src/plugins/tts/PageChunk.js +16 -23
  195. package/src/plugins/tts/PageChunkIterator.js +11 -17
  196. package/src/plugins/tts/WebTTSEngine.js +88 -72
  197. package/src/plugins/tts/plugin.tts.js +310 -350
  198. package/src/plugins/tts/utils.js +16 -26
  199. package/src/plugins/url/UrlPlugin.js +191 -0
  200. package/src/plugins/{plugin.url.js → url/plugin.url.js} +47 -18
  201. package/src/util/browserSniffing.js +22 -0
  202. package/src/util/docCookies.js +21 -2
  203. package/src/util/strings.js +1 -0
  204. package/tests/e2e/README.md +37 -0
  205. package/tests/e2e/autoplay.test.js +9 -6
  206. package/tests/e2e/base.test.js +8 -16
  207. package/tests/e2e/helpers/base.js +55 -50
  208. package/tests/e2e/helpers/debug.js +1 -1
  209. package/tests/e2e/helpers/mockSearch.js +19 -22
  210. package/tests/e2e/helpers/params.js +17 -0
  211. package/tests/e2e/helpers/rightToLeft.js +8 -14
  212. package/tests/e2e/helpers/search.js +73 -0
  213. package/tests/e2e/models/Navigation.js +20 -37
  214. package/tests/e2e/rightToLeft.test.js +4 -5
  215. package/tests/e2e/viewmode.test.js +40 -33
  216. package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
  217. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  218. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  219. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  220. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  221. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  222. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  223. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  224. package/tests/{karma → jest}/BookNavigator/search/search-results.test.js +109 -60
  225. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  226. package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
  227. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  228. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  229. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  230. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  231. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  232. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  233. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  234. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  235. package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
  236. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  237. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +42 -29
  238. package/tests/jest/BookReader/PageContainer.test.js +238 -0
  239. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  240. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +3 -3
  241. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  242. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  243. package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
  244. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  245. package/tests/jest/BookReader/utils.test.js +250 -0
  246. package/tests/jest/BookReader.keyboard.test.js +190 -0
  247. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +10 -2
  248. package/tests/{BookReader.test.js → jest/BookReader.test.js} +43 -53
  249. package/tests/jest/plugins/plugin.archive_analytics.test.js +20 -0
  250. package/tests/jest/plugins/plugin.autoplay.test.js +35 -0
  251. package/tests/jest/plugins/plugin.chapters.test.js +195 -0
  252. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +4 -4
  253. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +22 -35
  254. package/tests/jest/plugins/plugin.text_selection.test.js +316 -0
  255. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  256. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +19 -47
  257. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +42 -9
  258. package/tests/jest/plugins/search/utils.js +25 -0
  259. package/tests/jest/plugins/search/utils.test.js +29 -0
  260. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +30 -10
  261. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  262. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  263. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  264. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  265. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  266. package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
  267. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +57 -18
  268. package/tests/jest/setup.js +3 -0
  269. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  270. package/tests/jest/util/docCookies.test.js +24 -0
  271. package/tests/{util → jest/util}/strings.test.js +1 -1
  272. package/tests/{utils.js → jest/utils.js} +38 -0
  273. package/webpack.config.js +16 -10
  274. package/.babelrc +0 -12
  275. package/.dependabot/config.yml +0 -6
  276. package/.testcaferc.json +0 -5
  277. package/BookReader/bookreader-component-bundle.js +0 -1450
  278. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  279. package/BookReader/bookreader-component-bundle.js.map +0 -1
  280. package/BookReader/jquery-1.10.1.js +0 -2
  281. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  282. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  283. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  284. package/BookReader/plugins/plugin.mobile_nav.js +0 -2
  285. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  286. package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
  287. package/BookReaderDemo/IIIFBookReader.js +0 -207
  288. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  289. package/BookReaderDemo/demo-autoplay.html +0 -38
  290. package/BookReaderDemo/demo-iiif.js +0 -26
  291. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  292. package/karma.conf.js +0 -23
  293. package/src/BookNavigator/BookModel.js +0 -14
  294. package/src/BookNavigator/BookNavigator.js +0 -446
  295. package/src/BookNavigator/assets/book-loader.js +0 -27
  296. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  297. package/src/BookNavigator/search/a-search-result.js +0 -55
  298. package/src/BookReader/DebugConsole.js +0 -54
  299. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  300. package/src/ItemNavigator/ItemNavigator.js +0 -376
  301. package/src/ItemNavigator/providers/sharing.js +0 -29
  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/PageContainer.test.js +0 -115
  311. package/tests/BookReader/utils.test.js +0 -109
  312. package/tests/e2e/helpers/desktopSearch.js +0 -72
  313. package/tests/e2e/helpers/mobileSearch.js +0 -85
  314. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  315. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  316. package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
  317. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  318. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  319. package/tests/plugins/plugin.archive_analytics.test.js +0 -23
  320. package/tests/plugins/plugin.autoplay.test.js +0 -52
  321. package/tests/plugins/plugin.chapters.test.js +0 -130
  322. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  323. package/tests/plugins/plugin.text_selection.test.js +0 -203
  324. package/tests/util/docCookies.test.js +0 -15
@@ -8,7 +8,7 @@ if (!isMobile()) {
8
8
 
9
9
  jQuery.extend(BookReader.defaultOptions, {
10
10
  /** @type {boolean} */
11
- enableVendorFullscreenPlugin: true
11
+ enableVendorFullscreenPlugin: true,
12
12
  });
13
13
 
14
14
  /** @override */
@@ -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');
@@ -225,16 +225,16 @@ export function fullscreenAllowed() {
225
225
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/fullscreenchange_event
226
226
  */
227
227
  export function bindFullscreenChangeListener(
228
- data, fullscreenchangeListener
228
+ data, fullscreenchangeListener,
229
229
  ) {
230
230
  const event = 'fullscreenchange ';
231
231
  const vendor_prefixes = [
232
232
  'webkit',
233
233
  'moz',
234
- 'ms'
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
 
@@ -55,14 +63,20 @@ BookReader.prototype.setup = (function (super_) {
55
63
  this._cancelSearch.bind(this);
56
64
  this.cancelSearchRequest.bind(this);
57
65
 
58
- if (this.searchView) { return; }
59
- this.searchView = new SearchView({
60
- br: this,
61
- searchCancelledCallback: () => {
62
- this._cancelSearch();
63
- this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
64
- }
65
- });
66
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
67
+ this._searchBoxesByIndex = {};
68
+
69
+ if (this.enableSearch) {
70
+ this.searchView = new SearchView({
71
+ br: this,
72
+ searchCancelledCallback: () => {
73
+ this._cancelSearch();
74
+ this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
75
+ },
76
+ });
77
+ } else {
78
+ this.searchView = null;
79
+ }
66
80
  };
67
81
  })(BookReader.prototype.setup);
68
82
 
@@ -70,11 +84,21 @@ BookReader.prototype.setup = (function (super_) {
70
84
  BookReader.prototype.init = (function (super_) {
71
85
  return function () {
72
86
  super_.call(this);
73
-
74
- if (this.options.enableSearch && this.options.initialSearchTerm) {
87
+ if (!this.enableSearch) return;
88
+
89
+ this.searchView.init();
90
+ if (this.options.initialSearchTerm) {
91
+ /**
92
+ * this.search() take two parameter
93
+ * 1. this.options.initialSearchTerm - search term
94
+ * 2. {
95
+ * goToFirstResult: this.options.goToFirstResult,
96
+ * suppressFragmentChange: false // always want to change fragment in URL
97
+ * }
98
+ */
75
99
  this.search(
76
100
  this.options.initialSearchTerm,
77
- { goToFirstResult: this.options.goToFirstResult, suppressFragmentChange: true }
101
+ { goToFirstResult: this.options.goToFirstResult, suppressFragmentChange: false },
78
102
  );
79
103
  }
80
104
  };
@@ -92,6 +116,25 @@ BookReader.prototype.buildToolbarElement = (function (super_) {
92
116
  };
93
117
  })(BookReader.prototype.buildToolbarElement);
94
118
 
119
+ /** @override */
120
+ BookReader.prototype._createPageContainer = (function (super_) {
121
+ return function (index) {
122
+ const pageContainer = super_.call(this, index);
123
+ if (this.enableSearch && pageContainer.page && index in this._searchBoxesByIndex) {
124
+ const pageIndex = pageContainer.page.index;
125
+ const boxes = this._searchBoxesByIndex[pageIndex];
126
+ renderBoxesInPageContainerLayer(
127
+ 'searchHiliteLayer',
128
+ boxes,
129
+ pageContainer.page,
130
+ pageContainer.$container[0],
131
+ boxes.map(b => `match-index-${b.matchIndex}`),
132
+ );
133
+ }
134
+ return pageContainer;
135
+ };
136
+ })(BookReader.prototype._createPageContainer);
137
+
95
138
  /**
96
139
  * @typedef {object} SearchOptions
97
140
  * @property {boolean} goToFirstResult
@@ -106,7 +149,7 @@ BookReader.prototype.buildToolbarElement = (function (super_) {
106
149
  * @param {string} term
107
150
  * @param {SearchOptions} overrides
108
151
  */
109
- BookReader.prototype.search = function(term = '', overrides = {}) {
152
+ BookReader.prototype.search = async function(term = '', overrides = {}) {
110
153
  /** @type {SearchOptions} */
111
154
  const defaultOptions = {
112
155
  goToFirstResult: false, /* jump to the first result (default=false) */
@@ -118,6 +161,7 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
118
161
  };
119
162
  const options = jQuery.extend({}, defaultOptions, overrides);
120
163
  this.suppressFragmentChange = options.suppressFragmentChange;
164
+ this.searchCancelled = false;
121
165
 
122
166
  // strip slashes, since this goes in the url
123
167
  this.searchTerm = term.replace(/\//g, ' ');
@@ -132,7 +176,7 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
132
176
 
133
177
  // Remove the port and userdir
134
178
  const serverPath = this.server.replace(/:.+/, '');
135
- const baseUrl = `https://${serverPath}${this.searchInsideUrl}?`;
179
+ const baseUrl = `${this.options.searchInsideProtocol}://${serverPath}${this.searchInsideUrl}?`;
136
180
 
137
181
  // Remove subPrefix from end of path
138
182
  let path = this.bookPath;
@@ -146,6 +190,8 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
146
190
  doc: this.subPrefix,
147
191
  path,
148
192
  q: term,
193
+ pre_tag: this.options.searchInsidePreTag,
194
+ post_tag: this.options.searchInsidePostTag,
149
195
  };
150
196
 
151
197
  // NOTE that the API does not expect / (slashes) to be encoded. (%2F) won't work
@@ -153,13 +199,8 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
153
199
 
154
200
  const url = `${baseUrl}${paramStr}`;
155
201
 
156
- const cleanup = () => {
157
- this.searchXHR = null
158
- window.BRSearchInProgress = () => {};
159
- };
160
-
161
- const processSearchResults = (searchInsideResults) => {
162
- if (!this.searchXHR) {
202
+ const callSearchResultsCallback = (searchInsideResults) => {
203
+ if (this.searchCancelled) {
163
204
  return;
164
205
  }
165
206
  const responseHasError = searchInsideResults.error || !searchInsideResults.matches.length;
@@ -167,6 +208,7 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
167
208
  const hasCustomSuccess = typeof options.success === 'function';
168
209
 
169
210
  if (responseHasError) {
211
+ console.error('Search Inside Response Error', searchInsideResults.error || 'matches.length == 0');
170
212
  hasCustomError
171
213
  ? options.error.call(this, searchInsideResults, options)
172
214
  : this.BRSearchCallbackError(searchInsideResults, options);
@@ -175,21 +217,17 @@ BookReader.prototype.search = function(term = '', overrides = {}) {
175
217
  ? options.success.call(this, searchInsideResults, options)
176
218
  : this.BRSearchCallback(searchInsideResults, options);
177
219
  }
178
- cleanup();
179
- };
180
-
181
- const beforeSend = (xhr) => {
182
- this.searchXHR = xhr;
183
- window.BRSearchInProgress = processSearchResults;
184
220
  };
185
221
 
186
222
  this.trigger('SearchStarted', { term: this.searchTerm, instance: this });
187
- return $.ajax({
223
+ callSearchResultsCallback(await $.ajax({
188
224
  url: url,
189
- dataType: 'jsonp',
190
- beforeSend,
191
- jsonpCallback: 'BRSearchInProgress'
192
- }).then(processSearchResults)
225
+ cache: true,
226
+ xhrFields: {
227
+ withCredentials: this.protected,
228
+ },
229
+ beforeSend: xhr => { this.searchXHR = xhr; },
230
+ }));
193
231
  };
194
232
 
195
233
  /**
@@ -201,21 +239,22 @@ BookReader.prototype._cancelSearch = function () {
201
239
  this.searchView.clearSearchFieldAndResults(false);
202
240
  this.searchTerm = '';
203
241
  this.searchXHR = null;
242
+ this.searchCancelled = true;
204
243
  this.searchResults = [];
205
- window.BRSearchInProgress = () => {};
206
- }
244
+ };
207
245
 
208
246
  /**
209
247
  * External function to cancel search
210
248
  * checks for term & xhr in flight before running
211
249
  */
212
250
  BookReader.prototype.cancelSearchRequest = function () {
251
+ this.searchCancelled = true;
213
252
  if (this.searchXHR !== null) {
214
253
  this._cancelSearch();
215
254
  this.searchView.toggleSearchPending();
216
255
  this.trigger('SearchCanceled', { term: this.searchTerm, instance: this });
217
256
  }
218
- }
257
+ };
219
258
 
220
259
  /**
221
260
  * @typedef {object} SearchInsideMatchBox
@@ -225,10 +264,14 @@ BookReader.prototype.cancelSearchRequest = function () {
225
264
  * @property {number} b
226
265
  * @property {number} t
227
266
  * @property {HTMLDivElement} [div]
267
+ * @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.
228
268
  */
229
269
 
230
270
  /**
231
271
  * @typedef {object} SearchInsideMatch
272
+ * @property {number} matchIndex This is a fake field! Not part of the API response. It is added by the JS.
273
+ * @property {string} displayPageNumber (fake field) The page number as it should be displayed in the UI.
274
+ * @property {string} html (computed field) The html-escaped raw html to display in the UI.
232
275
  * @property {string} text
233
276
  * @property {Array<{ page: number, boxes: SearchInsideMatchBox[] }>} par
234
277
  */
@@ -242,22 +285,26 @@ BookReader.prototype.cancelSearchRequest = function () {
242
285
 
243
286
  /**
244
287
  * Search Results return handler
245
- * @callback
246
288
  * @param {SearchInsideResults} results
247
289
  * @param {object} options
248
290
  * @param {boolean} options.goToFirstResult
249
291
  */
250
292
  BookReader.prototype.BRSearchCallback = function(results, options) {
293
+ marshallSearchResults(
294
+ results,
295
+ pageNum => this.book.getPageNum(this.book.leafNumToIndex(pageNum)),
296
+ this.options.searchInsidePreTag,
297
+ this.options.searchInsidePostTag,
298
+ );
251
299
  this.searchResults = results || [];
252
300
 
253
301
  this.updateSearchHilites();
254
302
  this.removeProgressPopup();
255
303
  if (options.goToFirstResult) {
256
- const pageIndex = this._models.book.leafNumToIndex(results.matches[0].par[0].page);
257
- this._searchPluginGoToResult(pageIndex);
304
+ this._searchPluginGoToResult(0);
258
305
  }
259
306
  this.trigger('SearchCallback', { results, options, instance: this });
260
- }
307
+ };
261
308
 
262
309
  /**
263
310
  * Main search results error handler
@@ -297,95 +344,41 @@ BookReader.prototype._BRSearchCallbackError = function(results) {
297
344
  * updates search on-page highlights controller
298
345
  */
299
346
  BookReader.prototype.updateSearchHilites = function() {
300
- if (this.constMode2up == this.mode) {
301
- this.updateSearchHilites2UP();
302
- return;
347
+ /** @type {SearchInsideMatch[]} */
348
+ const matches = this.searchResults?.matches || [];
349
+ /** @type { {[pageIndex: number]: SearchInsideMatchBox[]} } */
350
+ const boxesByIndex = {};
351
+
352
+ // Clear any existing svg layers
353
+ this.removeSearchHilites();
354
+
355
+ // Group by pageIndex
356
+ for (const match of matches) {
357
+ for (const box of match.par[0].boxes) {
358
+ const pageIndex = this.book.leafNumToIndex(box.page);
359
+ const pageBoxes = boxesByIndex[pageIndex] || (boxesByIndex[pageIndex] = []);
360
+ pageBoxes.push(box);
361
+ }
303
362
  }
304
- this.updateSearchHilites1UP();
305
- };
306
-
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
363
 
341
- /**
342
- * update search on-page highlights in 2up mode
343
- */
344
- BookReader.prototype.updateSearchHilites2UP = function() {
345
- const results = this.searchResults;
364
+ // update any already created pages
365
+ for (const [pageIndexString, boxes] of Object.entries(boxesByIndex)) {
366
+ const pageIndex = parseFloat(pageIndexString);
367
+ const page = this.book.getPage(pageIndex);
368
+ const pageContainers = this.getActivePageContainerElementsForIndex(pageIndex);
369
+ for (const container of pageContainers) {
370
+ renderBoxesInPageContainerLayer('searchHiliteLayer', boxes, page, container, boxes.map(b => `match-index-${b.matchIndex}`));
371
+ }
372
+ }
346
373
 
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
- });
374
+ this._searchBoxesByIndex = boxesByIndex;
373
375
  };
374
376
 
375
377
  /**
376
378
  * remove search highlights
377
379
  */
378
380
  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
- });
381
+ $(this.getActivePageContainerElements()).find('.searchHiliteLayer').remove();
389
382
  };
390
383
 
391
384
  /**
@@ -393,11 +386,14 @@ BookReader.prototype.removeSearchHilites = function() {
393
386
  * Goes to the page specified. If the page is not viewable, tries to load the page
394
387
  * FIXME Most of this logic is IA specific, and should be less integrated into here
395
388
  * or at least more configurable.
396
- * @param {PageIndex} pageIndex
389
+ * @param {number} matchIndex
397
390
  */
398
- BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
399
- const { book } = this._models;
391
+ BookReader.prototype._searchPluginGoToResult = async function (matchIndex) {
392
+ const match = this.searchResults?.matches[matchIndex];
393
+ const book = this.book;
394
+ const pageIndex = book.leafNumToIndex(match.par[0].page);
400
395
  const page = book.getPage(pageIndex);
396
+ const onNearbyPage = Math.abs(this.currentIndex() - pageIndex) < 3;
401
397
  let makeUnviewableAtEnd = false;
402
398
  if (!page.isViewable) {
403
399
  const resp = await fetch('/services/bookreader/request_page?' + new URLSearchParams({
@@ -416,15 +412,43 @@ BookReader.prototype._searchPluginGoToResult = async function (pageIndex) {
416
412
  book.getPage(pageIndex).makeViewable();
417
413
  makeUnviewableAtEnd = true;
418
414
  }
415
+
416
+ // Trigger an update of book
417
+ this._modes.mode1Up.mode1UpLit.updatePages();
418
+ if (this.activeMode == this._modes.mode1Up) {
419
+ await this._modes.mode1Up.mode1UpLit.updateComplete;
420
+ }
419
421
  }
420
422
  /* this updates the URL */
421
- this.suppressFragmentChange = false;
422
- this.jumpToIndex(pageIndex);
423
+ if (!this._isIndexDisplayed(pageIndex)) {
424
+ this.suppressFragmentChange = false;
425
+ this.jumpToIndex(pageIndex);
426
+ }
423
427
 
424
428
  // Reset it to unviewable if it wasn't resolved
425
429
  if (makeUnviewableAtEnd) {
426
430
  book.getPage(pageIndex).makeViewable(false);
427
431
  }
432
+
433
+ // Scroll/flash in the ui
434
+ const $boxes = await poll(() => $(`rect.match-index-${match.matchIndex}`), { until: result => result.length > 0 });
435
+ if ($boxes.length) {
436
+ $boxes.css('animation', 'none');
437
+ $boxes[0].scrollIntoView({
438
+ // Only vertically center the highlight if we're in 1up or in full screen. In
439
+ // 2up, if we're not fullscreen, the whole body gets scrolled around to try to
440
+ // center the highlight 🙄 See:
441
+ // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move/11041376
442
+ // Note: nearest doesn't quite work great, because the ReadAloud toolbar is now
443
+ // full-width, and covers up the last line of the highlight.
444
+ block: this.constMode1up == this.mode || this.isFullscreenActive ? 'center' : 'nearest',
445
+ inline: 'center',
446
+ behavior: onNearbyPage ? 'smooth' : 'auto',
447
+ });
448
+ // wait for animation to start
449
+ await new Promise(resolve => setTimeout(resolve, 100));
450
+ $boxes.removeAttr("style");
451
+ }
428
452
  };
429
453
 
430
454
  /**
@@ -458,7 +482,7 @@ BookReader.prototype.searchHighlightVisible = function() {
458
482
 
459
483
  results.matches.some(match => {
460
484
  return match.par[0].boxes.some(box => {
461
- const pageIndex = this.leafNumToIndex(box.page);
485
+ const pageIndex = this.book.leafNumToIndex(box.page);
462
486
  if (jQuery.inArray(pageIndex, visiblePages) >= 0) {
463
487
  return true;
464
488
  }
@@ -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
+ }