@internetarchive/bookreader 5.0.0-9-multiple-files → 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 (333) hide show
  1. package/.eslintrc.js +21 -19
  2. package/.github/workflows/node.js.yml +81 -7
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +505 -1442
  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/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 -172
  61. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  62. package/BookReader/plugins/plugin.autoplay.js +1 -165
  63. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  64. package/BookReader/plugins/plugin.chapters.js +22 -301
  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 -74
  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 -368
  72. package/BookReader/plugins/plugin.resume.js.map +1 -1
  73. package/BookReader/plugins/plugin.search.js +2 -1420
  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 -1080
  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 +2 -9193
  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 -269
  83. package/BookReader/plugins/plugin.url.js.map +1 -1
  84. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  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 +595 -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 +10 -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 +80 -28
  135. package/src/BookNavigator/search/search-results.js +29 -26
  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 +562 -1078
  162. package/src/BookReaderPlugin.js +44 -0
  163. package/src/assets/icons/close-circle-dark.svg +1 -0
  164. package/src/assets/icons/magnify-minus.svg +3 -7
  165. package/src/assets/icons/magnify-plus.svg +3 -7
  166. package/src/assets/icons/voice.svg +1 -0
  167. package/src/assets/images/unviewable_page.png +0 -0
  168. package/src/css/BookReader.scss +1 -17
  169. package/src/css/_BRBookmarks.scss +1 -1
  170. package/src/css/_BRComponent.scss +1 -1
  171. package/src/css/_BRicon.scss +8 -2
  172. package/src/css/_BRmain.scss +33 -27
  173. package/src/css/_BRnav.scss +12 -42
  174. package/src/css/_BRpages.scss +170 -42
  175. package/src/css/_BRsearch.scss +68 -230
  176. package/src/css/_BRtoolbar.scss +5 -5
  177. package/src/css/_TextSelection.scss +87 -27
  178. package/src/css/_colorbox.scss +2 -2
  179. package/src/css/_controls.scss +24 -7
  180. package/src/css/_icons.scss +7 -1
  181. package/src/ia-bookreader/ia-bookreader.js +224 -0
  182. package/src/plugins/plugin.archive_analytics.js +84 -78
  183. package/src/plugins/plugin.autoplay.js +99 -104
  184. package/src/plugins/plugin.chapters.js +237 -191
  185. package/src/plugins/plugin.iframe.js +1 -1
  186. package/src/plugins/plugin.iiif.js +141 -0
  187. package/src/plugins/plugin.resume.js +53 -50
  188. package/src/plugins/plugin.text_selection.js +503 -175
  189. package/src/plugins/plugin.vendor-fullscreen.js +7 -7
  190. package/src/plugins/search/plugin.search.js +183 -121
  191. package/src/plugins/search/utils.js +43 -0
  192. package/src/plugins/search/view.js +67 -202
  193. package/src/plugins/tts/AbstractTTSEngine.js +75 -45
  194. package/src/plugins/tts/FestivalTTSEngine.js +21 -31
  195. package/src/plugins/tts/PageChunk.js +16 -23
  196. package/src/plugins/tts/PageChunkIterator.js +11 -17
  197. package/src/plugins/tts/WebTTSEngine.js +88 -72
  198. package/src/plugins/tts/plugin.tts.js +310 -350
  199. package/src/plugins/tts/utils.js +16 -26
  200. package/src/plugins/url/UrlPlugin.js +191 -0
  201. package/src/plugins/{plugin.url.js → url/plugin.url.js} +47 -18
  202. package/src/util/browserSniffing.js +22 -0
  203. package/src/util/docCookies.js +21 -2
  204. package/src/util/strings.js +1 -0
  205. package/tests/e2e/README.md +37 -0
  206. package/tests/e2e/autoplay.test.js +9 -6
  207. package/tests/e2e/base.test.js +8 -16
  208. package/tests/e2e/helpers/base.js +55 -50
  209. package/tests/e2e/helpers/debug.js +1 -1
  210. package/tests/e2e/helpers/mockSearch.js +19 -22
  211. package/tests/e2e/helpers/params.js +17 -0
  212. package/tests/e2e/helpers/rightToLeft.js +8 -14
  213. package/tests/e2e/helpers/search.js +73 -0
  214. package/tests/e2e/models/Navigation.js +20 -37
  215. package/tests/e2e/rightToLeft.test.js +4 -5
  216. package/tests/e2e/viewmode.test.js +40 -33
  217. package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
  218. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  219. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  220. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  221. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  222. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  223. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  224. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  225. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +109 -60
  226. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  227. package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
  228. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  229. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  230. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  231. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  232. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  233. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  234. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  235. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  236. package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
  237. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  238. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +42 -29
  239. package/tests/jest/BookReader/PageContainer.test.js +238 -0
  240. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  241. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +3 -3
  242. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  243. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  244. package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
  245. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  246. package/tests/jest/BookReader/utils.test.js +250 -0
  247. package/tests/jest/BookReader.keyboard.test.js +190 -0
  248. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +10 -2
  249. package/tests/{BookReader.test.js → jest/BookReader.test.js} +43 -53
  250. package/tests/jest/plugins/plugin.archive_analytics.test.js +20 -0
  251. package/tests/jest/plugins/plugin.autoplay.test.js +35 -0
  252. package/tests/jest/plugins/plugin.chapters.test.js +195 -0
  253. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +4 -4
  254. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +22 -35
  255. package/tests/jest/plugins/plugin.text_selection.test.js +316 -0
  256. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  257. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +26 -47
  258. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +42 -9
  259. package/tests/jest/plugins/search/utils.js +25 -0
  260. package/tests/jest/plugins/search/utils.test.js +29 -0
  261. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +30 -10
  262. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  263. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  264. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  265. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  266. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  267. package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
  268. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +57 -18
  269. package/tests/jest/setup.js +3 -0
  270. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  271. package/tests/jest/util/docCookies.test.js +24 -0
  272. package/tests/{util → jest/util}/strings.test.js +1 -1
  273. package/tests/{utils.js → jest/utils.js} +38 -0
  274. package/webpack.config.js +16 -10
  275. package/.babelrc +0 -12
  276. package/.dependabot/config.yml +0 -6
  277. package/.testcaferc.json +0 -5
  278. package/BookReader/bookreader-component-bundle.js +0 -14330
  279. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  280. package/BookReader/bookreader-component-bundle.js.map +0 -1
  281. package/BookReader/icons/sort-ascending.svg +0 -1
  282. package/BookReader/icons/sort-descending.svg +0 -1
  283. package/BookReader/jquery-1.10.1.js +0 -108
  284. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  285. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  286. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  287. package/BookReader/plugins/plugin.mobile_nav.js +0 -335
  288. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  289. package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
  290. package/BookReaderDemo/IIIFBookReader.js +0 -207
  291. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  292. package/BookReaderDemo/demo-autoplay.html +0 -38
  293. package/BookReaderDemo/demo-iiif.js +0 -26
  294. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  295. package/karma.conf.js +0 -23
  296. package/src/BookNavigator/BookModel.js +0 -14
  297. package/src/BookNavigator/BookNavigator.js +0 -452
  298. package/src/BookNavigator/assets/book-loader.js +0 -27
  299. package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
  300. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  301. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  302. package/src/BookNavigator/search/a-search-result.js +0 -55
  303. package/src/BookNavigator/volumes/volumes-provider.js +0 -108
  304. package/src/BookNavigator/volumes/volumes.js +0 -162
  305. package/src/BookReader/DebugConsole.js +0 -54
  306. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  307. package/src/ItemNavigator/ItemNavigator.js +0 -372
  308. package/src/ItemNavigator/providers/sharing.js +0 -29
  309. package/src/assets/icons/sort-ascending.svg +0 -1
  310. package/src/assets/icons/sort-descending.svg +0 -1
  311. package/src/css/_MobileNav.scss +0 -194
  312. package/src/dragscrollable-br.js +0 -261
  313. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  314. package/src/plugins/plugin.mobile_nav.js +0 -287
  315. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  316. package/tests/BookReader/DebugConsole.test.js +0 -25
  317. package/tests/BookReader/Mode1Up.test.js +0 -164
  318. package/tests/BookReader/Mode2Up.test.js +0 -247
  319. package/tests/BookReader/PageContainer.test.js +0 -115
  320. package/tests/BookReader/utils.test.js +0 -109
  321. package/tests/e2e/helpers/desktopSearch.js +0 -72
  322. package/tests/e2e/helpers/mobileSearch.js +0 -85
  323. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  324. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  325. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  326. package/tests/karma/BookNavigator/volumes.test.js +0 -133
  327. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  328. package/tests/plugins/plugin.archive_analytics.test.js +0 -23
  329. package/tests/plugins/plugin.autoplay.test.js +0 -52
  330. package/tests/plugins/plugin.chapters.test.js +0 -130
  331. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  332. package/tests/plugins/plugin.text_selection.test.js +0 -203
  333. package/tests/util/docCookies.test.js +0 -15
@@ -2,6 +2,8 @@
2
2
  /** @typedef {import('./BookModel.js').PageModel} PageModel */
3
3
  /** @typedef {import('./ImageCache.js').ImageCache} ImageCache */
4
4
 
5
+ import { sleep } from './utils.js';
6
+
5
7
 
6
8
  export class PageContainer {
7
9
  /**
@@ -9,12 +11,10 @@ export class PageContainer {
9
11
  * @param {object} opts
10
12
  * @param {boolean} opts.isProtected Whether we're in a protected book
11
13
  * @param {ImageCache} opts.imageCache
12
- * @param {string} opts.loadingImage
13
14
  */
14
- constructor(page, {isProtected, imageCache, loadingImage}) {
15
+ constructor(page, {isProtected, imageCache}) {
15
16
  this.page = page;
16
17
  this.imageCache = imageCache;
17
- this.loadingImage = loadingImage;
18
18
  this.$container = $('<div />', {
19
19
  'class': `BRpagecontainer ${page ? `pagediv${page.index}` : 'BRemptypage'}`,
20
20
  css: { position: 'absolute' },
@@ -43,33 +43,127 @@ export class PageContainer {
43
43
  return;
44
44
  }
45
45
 
46
- const alreadyLoaded = this.imageCache.imageLoaded(this.page.index, reduce);
47
- const nextBestLoadedReduce = !alreadyLoaded && this.imageCache.getBestLoadedReduce(this.page.index, reduce);
46
+ const finalReduce = this.imageCache.getFinalReduce(this.page.index, reduce);
47
+ const newImageURI = this.page.getURI(finalReduce, 0);
48
48
 
49
- // Add the actual, highres image
50
- this.$img?.remove();
51
- this.$img = this.imageCache
52
- .image(this.page.index, reduce)
53
- .prependTo(this.$container);
49
+ // Note: These must be computed _before_ we call .image()
50
+ const alreadyLoaded = this.imageCache.imageLoaded(this.page.index, finalReduce);
51
+ const nextBestLoadedReduce = this.imageCache.getBestLoadedReduce(this.page.index, reduce);
54
52
 
55
- const backgroundLayers = [];
56
- if (!alreadyLoaded) {
57
- this.$container.addClass('BRpageloading');
58
- backgroundLayers.push(`url("${this.loadingImage}") center/20px no-repeat`);
53
+ // Avoid removing/re-adding the image if it's already there
54
+ // This can be called quite a bit, so we need to be fast
55
+ if (this.$img?.data('src') == newImageURI) {
56
+ return this;
59
57
  }
60
- if (nextBestLoadedReduce) {
61
- backgroundLayers.push(`url("${this.page.getURI(nextBestLoadedReduce, 0)}") center/100% no-repeat`);
58
+
59
+ let $oldImg = this.$img;
60
+ this.$img = this.imageCache.image(this.page.index, finalReduce);
61
+ if ($oldImg) {
62
+ this.$img.insertAfter($oldImg);
63
+ } else {
64
+ this.$img.prependTo(this.$container);
62
65
  }
63
66
 
64
67
  if (!alreadyLoaded) {
65
- this.$img
66
- .css('background', backgroundLayers.join(','))
67
- .one('loadend', async (ev) => {
68
- $(ev.target).css({ 'background': '' })
69
- $(ev.target).parent().removeClass('BRpageloading');
70
- });
68
+ this.$container.addClass('BRpageloading');
71
69
  }
72
70
 
71
+ if (!alreadyLoaded && nextBestLoadedReduce) {
72
+ // If we have a slightly lower quality image loaded, use that as the background
73
+ // while the higher res one loads
74
+ const nextBestUri = this.page.getURI(nextBestLoadedReduce, 0);
75
+ if ($oldImg) {
76
+ if ($oldImg.data('src') == nextBestUri) {
77
+ // Do nothing! It's already showing the right thing
78
+ } else {
79
+ // We have a different src, need to update the src
80
+ this.imageCache.image(this.page.index, nextBestLoadedReduce, $oldImg[0]);
81
+ }
82
+ } else {
83
+ // We don't have an old <img>, so we need to create a new one
84
+ $oldImg = this.imageCache.image(this.page.index, nextBestLoadedReduce);
85
+ $oldImg.prependTo(this.$container);
86
+ }
87
+ }
88
+
89
+ this.$img
90
+ .one('load', async (ev) => {
91
+ this.$container.removeClass('BRpageloading');
92
+ // `load` can fire a little early, so wait a spell before removing the old image
93
+ // to avoid flicker
94
+ await sleep(100);
95
+ $oldImg?.remove();
96
+ });
97
+
73
98
  return this;
74
99
  }
75
100
  }
101
+
102
+
103
+ /**
104
+ * @param {PageModel} page
105
+ * @param {string} className
106
+ */
107
+ export function createSVGPageLayer(page, className) {
108
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
109
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
110
+ svg.setAttribute("viewBox", `0 0 ${page.width} ${page.height}`);
111
+ svg.setAttribute('class', `BRPageLayer ${className}`);
112
+ svg.setAttribute('preserveAspectRatio', 'none');
113
+ return svg;
114
+ }
115
+
116
+ /**
117
+ * @param {PageModel} page
118
+ * @param {string} className
119
+ */
120
+ export function createDIVPageLayer(page, className) {
121
+ const div = document.createElement("div");
122
+ div.style.width = `${page.width}px`;
123
+ div.style.height = `${page.height}px`;
124
+ div.setAttribute('class', `BRPageLayer ${className}`);
125
+ return div;
126
+ }
127
+
128
+ /**
129
+ * @param {{ l: number, r: number, b: number, t: number }} box
130
+ */
131
+ export function boxToSVGRect({ l: left, r: right, b: bottom, t: top }) {
132
+ const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
133
+ rect.setAttribute("x", left.toString());
134
+ rect.setAttribute("y", top.toString());
135
+ rect.setAttribute("width", (right - left).toString());
136
+ rect.setAttribute("height", (bottom - top).toString());
137
+
138
+ // Some style; corner radius 4px. Can't set this in CSS yet
139
+ rect.setAttribute("rx", "4");
140
+ rect.setAttribute("ry", "4");
141
+ return rect;
142
+ }
143
+
144
+ /**
145
+ * @param {string} layerClass
146
+ * @param {Array<{ l: number, r: number, b: number, t: number }>} boxes
147
+ * @param {PageModel} page
148
+ * @param {HTMLElement} containerEl
149
+ * @param {string[]} [rectClasses] CSS classes to add to the rects
150
+ */
151
+ export function renderBoxesInPageContainerLayer(layerClass, boxes, page, containerEl, rectClasses = null) {
152
+ const mountedSvg = containerEl.querySelector(`.${layerClass}`);
153
+ // Create the layer if it's not there
154
+ const svg = mountedSvg || createSVGPageLayer(page, layerClass);
155
+ if (!mountedSvg) {
156
+ // Insert after the image if the image is already loaded.
157
+ const imgEl = containerEl.querySelector('.BRpageimage');
158
+ if (imgEl) $(svg).insertAfter(imgEl);
159
+ else $(svg).prependTo(containerEl);
160
+ }
161
+
162
+ for (const [i, box] of boxes.entries()) {
163
+ const rect = boxToSVGRect(box);
164
+ if (rectClasses) {
165
+ rect.setAttribute('class', rectClasses[i]);
166
+ }
167
+ svg.appendChild(rect);
168
+ }
169
+ }
@@ -7,7 +7,7 @@
7
7
  /** @type {ReduceSet} */
8
8
  export const IntegerReduceSet = {
9
9
  floor: Math.floor,
10
- decr(n) { return n - 1; }
10
+ decr(n) { return n - 1; },
11
11
  };
12
12
 
13
13
  /** @type {ReduceSet} */
@@ -17,8 +17,8 @@ export const Pow2ReduceSet = {
17
17
  },
18
18
  decr(n) {
19
19
  return 2 ** (Math.log2(n) - 1);
20
- }
21
- }
20
+ },
21
+ };
22
22
 
23
23
  export const NAMED_REDUCE_SETS = {
24
24
  pow2: Pow2ReduceSet,
@@ -53,8 +53,8 @@ export class Toolbar {
53
53
  $('<a>')
54
54
  .attr({href: br.bookUrl, title: br.bookUrlTitle})
55
55
  .addClass('BRreturn')
56
- .html(br.bookUrlText || br.bookTitle)
57
- )
56
+ .html(br.bookUrlText || br.bookTitle),
57
+ );
58
58
  } else if (br.bookTitle) {
59
59
  $titleSectionEl.append(br.bookUrlText || br.bookTitle);
60
60
  }
@@ -75,8 +75,6 @@ export class Toolbar {
75
75
  br.$('.BRnavCntl').addClass('BRup');
76
76
  br.$('.pause').hide();
77
77
 
78
- this.updateToolbarZoom(br.reduce); // Pretty format
79
-
80
78
  // We build in mode 2
81
79
  br.refs.$BRtoolbar.append();
82
80
 
@@ -115,7 +113,7 @@ export class Toolbar {
115
113
  onLoad: () => {
116
114
  br.trigger(EVENTS.stop);
117
115
  br.$('.BRpageviewValue').val(window.location.href);
118
- }
116
+ },
119
117
  });
120
118
  br.$('.info').colorbox({
121
119
  inline: true,
@@ -123,35 +121,10 @@ export class Toolbar {
123
121
  href: br.$('.BRinfo'),
124
122
  onLoad: () => {
125
123
  br.trigger(EVENTS.stop);
126
- }
124
+ },
127
125
  });
128
126
  }
129
127
 
130
- /**
131
- * @deprecated
132
- * @todo .BRzoom doesn't exist anywhere, so this is likely dead code
133
- * Update the displayed zoom factor based on reduction factor
134
- * @param {number} reduce
135
- */
136
- updateToolbarZoom(reduce) {
137
- const { br } = this;
138
- // $$$ TODO preserve zoom/fit for each mode
139
- const autofit = br.mode == br.constMode2up ? br.twoPage.autofit : br.onePage.autofit;
140
- /** @type {string} */
141
- let value;
142
- if (autofit) {
143
- value = autofit.slice(0,1).toUpperCase() + autofit.slice(1);
144
- } else {
145
- value = (100 / reduce)
146
- .toFixed(2)
147
- // Strip trailing zeroes and decimal if all zeroes
148
- .replace(/0+$/,'')
149
- .replace(/\.$/,'')
150
- + '%';
151
- }
152
- br.$('.BRzoom').text(value);
153
- }
154
-
155
128
  /**
156
129
  * @param {JQuery} $shareDiv
157
130
  */
@@ -212,11 +185,11 @@ export class Toolbar {
212
185
  $form.appendTo($shareDiv);
213
186
 
214
187
  $form.find('.fieldset-embed input').on('change', event => {
215
- const form = $(event.target).parents('form:first');
188
+ const form = $(event.target).parents('form').first();
216
189
  const params = {};
217
190
  params.mode = $(form.find('.fieldset-embed input[name=pages]:checked')).val();
218
191
  if (form.find('.fieldset-embed input[name=thispage]').prop('checked')) {
219
- params.page = br.getPageNum(br.currentIndex());
192
+ params.page = br.book.getPageNum(br.currentIndex());
220
193
  }
221
194
 
222
195
  if (br.getEmbedCode) {
@@ -232,20 +205,20 @@ export class Toolbar {
232
205
  // Bind share buttons
233
206
 
234
207
  // Use url without hashes
235
- $form.find('.facebook-share-button').click(() => {
208
+ $form.find('.facebook-share-button').on("click", () => {
236
209
  const params = $.param({ u: this._getSocialShareUrl() });
237
210
  const url = 'https://www.facebook.com/sharer.php?' + params;
238
211
  createPopup(url, 600, 400, 'Share');
239
212
  });
240
- $form.find('.twitter-share-button').click(() => {
213
+ $form.find('.twitter-share-button').on("click", () => {
241
214
  const params = $.param({
242
215
  url: this._getSocialShareUrl(),
243
- text: br.bookTitle
216
+ text: br.bookTitle,
244
217
  });
245
218
  const url = 'https://twitter.com/intent/tweet?' + params;
246
219
  createPopup(url, 600, 400, 'Share');
247
220
  });
248
- $form.find('.email-share-button').click(() => {
221
+ $form.find('.email-share-button').on("click", () => {
249
222
  const body = `${br.bookTitle}\n\n${this._getSocialShareUrl()}`;
250
223
  window.location.href = `mailto:?subject=${encodeURI(br.bookTitle)}&body=${encodeURI(body)}`;
251
224
  });
@@ -331,11 +304,11 @@ export class Toolbar {
331
304
  }
332
305
  }
333
306
 
334
- export function blankInfoDiv() {
307
+ function blankInfoDiv() {
335
308
  return $(`
336
309
  <div class="BRfloat BRinfo">
337
310
  <div class="BRfloatHead">About this book
338
- <button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></button>
311
+ <button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="br-colorbox-shift">Close</span></button>
339
312
  </div>
340
313
  <div class="BRfloatBody">
341
314
  <div class="BRfloatCover"></div>
@@ -351,12 +324,12 @@ export function blankInfoDiv() {
351
324
  </div>`);
352
325
  }
353
326
 
354
- export function blankShareDiv() {
327
+ function blankShareDiv() {
355
328
  return $(`
356
329
  <div class="BRfloat BRshare">
357
330
  <div class="BRfloatHead">
358
331
  Share
359
- <button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></button>
332
+ <button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="br-colorbox-shift">Close</span></button>
360
333
  </div>
361
334
  </div>`);
362
335
  }
@@ -3,11 +3,11 @@ export const EVENTS = {
3
3
  /** Indicates that the fragment (a serialization of the reader
4
4
  * state) has changed. */
5
5
  fragmentChange: 'fragmentChange',
6
+ pageChanged: 'pageChanged',
6
7
  PostInit: 'PostInit',
7
8
  stop: 'stop',
8
9
  resize: 'resize',
9
- // nav events:
10
- navToggled: 'navToggled',
10
+ userAction: 'userAction', // event to know if user is actively reading
11
11
  // menu click events
12
12
  fullscreenToggled: 'fullscreenToggled',
13
13
  zoomOut: 'zoomOut',
@@ -16,5 +16,4 @@ export const EVENTS = {
16
16
  '2PageViewSelected': '2PageViewSelected',
17
17
  /* currently 3 represents thumbnail view */
18
18
  '3PageViewSelected': '3PageViewSelected',
19
- mobileNavOpen: 'mobileNavOpen',
20
19
  };
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  /** @typedef {import('./BookModel.js').PageNumString} PageNumString */
2
3
  /** @typedef {import('./BookModel.js').LeafNum} LeafNum */
3
4
 
@@ -25,9 +26,13 @@ export const DEFAULT_OPTIONS = {
25
26
  thumbMaxLoading: 4,
26
27
  /** spacing between thumbnails */
27
28
  thumbPadding: 10,
29
+ /** min zoom in columns */
30
+ thumbMinZoomColumns: 2,
31
+ /** max zoom out columns */
32
+ thumbMaxZoomColumns: 8,
28
33
 
29
34
  /** @type {number | 'fast' | 'slow'} speed for flip animation */
30
- flipSpeed: 'fast',
35
+ flipSpeed: 400,
31
36
 
32
37
  showToolbar: true,
33
38
  showNavbar: true,
@@ -61,7 +66,7 @@ export const DEFAULT_OPTIONS = {
61
66
  {reduce: 2, autofit: null},
62
67
  {reduce: 3, autofit: null},
63
68
  {reduce: 4, autofit: null},
64
- {reduce: 6, autofit: null}
69
+ {reduce: 6, autofit: null},
65
70
  ],
66
71
 
67
72
  /** Object to hold parameters related to 1up mode */
@@ -79,7 +84,7 @@ export const DEFAULT_OPTIONS = {
79
84
  /** Width of book spine $$$ consider sizing based on book length */
80
85
  bookSpineDivWidth: 64,
81
86
  /** @type {AutoFitValues} */
82
- autofit: 'auto'
87
+ autofit: 'auto',
83
88
  },
84
89
 
85
90
  onePageMinBreakpoint: 800,
@@ -136,8 +141,18 @@ export const DEFAULT_OPTIONS = {
136
141
  * but going forward we'll keep them here.
137
142
  **/
138
143
  plugins: {
139
- /** @type {import('../plugins/plugin.text_selection.js').TextSelectionPluginOptions} */
144
+ /** @type {import('../plugins/plugin.archive_analytics.js').ArchiveAnalyticsPlugin['options']}*/
145
+ archiveAnalytics: null,
146
+ /** @type {import('../plugins/plugin.autoplay.js').AutoplayPlugin['options']}*/
147
+ autoplay: null,
148
+ /** @type {import('../plugins/plugin.iiif.js').IiifPlugin['options']} */
149
+ iiif: null,
150
+ /** @type {import('../plugins/plugin.resume.js').ResumePlugin['options']} */
151
+ resume: null,
152
+ /** @type {import('../plugins/plugin.text_selection.js').TextSelectionPlugin['options']} */
140
153
  textSelection: null,
154
+ /** @type {import('../plugins/tts/plugin.tts.js').TtsPlugin['options']} */
155
+ tts: null,
141
156
  },
142
157
 
143
158
  /**
@@ -176,16 +191,32 @@ export const DEFAULT_OPTIONS = {
176
191
  */
177
192
  data: [],
178
193
 
179
- /** Advanced methods for page rendering */
194
+ /** @type {import('../plugins/plugin.chapters.js').TocEntry[]} */
195
+ table_of_contents: null,
196
+
197
+ /**
198
+ * Advanced methods for page rendering.
199
+ * All option functions have their `this` object set to the BookReader instance.
200
+ **/
201
+
180
202
  /** @type {() => number} */
181
203
  getNumLeafs: null,
182
204
  /** @type {(index: number) => number} */
183
205
  getPageWidth: null,
184
206
  /** @type {(index: number) => number} */
185
207
  getPageHeight: null,
186
- /** @type {(index: number, reduce: number, rotate: number) => *} */
208
+ /** @type {(index: number, reduce: number, rotate: number) => string} */
187
209
  getPageURI: null,
188
210
 
211
+ /**
212
+ * @type {(img: HTMLImageElement, uri: string) => Promise<void>}
213
+ * Render the page URI into the image element. Perform any necessary preloading,
214
+ * authentication, etc.
215
+ */
216
+ renderPageURI(img, uri) {
217
+ img.src = uri;
218
+ },
219
+
189
220
  /**
190
221
  * @type {(index: number) => 'L' | 'R'}
191
222
  * Return which side, left or right, that a given page should be displayed on
@@ -218,31 +249,31 @@ export const DEFAULT_OPTIONS = {
218
249
  visible: true,
219
250
  label: 'Flip left',
220
251
  className: 'book_left',
221
- iconClassName: 'left-arrow'
252
+ iconClassName: 'left-arrow',
222
253
  },
223
254
  bookRight: {
224
255
  visible: true,
225
256
  label: 'Flip right',
226
257
  className: 'book_right',
227
- iconClassName: 'left-arrow hflip'
258
+ iconClassName: 'left-arrow hflip',
228
259
  },
229
260
  onePage: {
230
261
  visible: true,
231
262
  label: 'One-page view',
232
263
  className: 'onepg',
233
- iconClassName: 'onepg'
264
+ iconClassName: 'onepg',
234
265
  },
235
266
  twoPage: {
236
267
  visible: true,
237
268
  label: 'Two-page view',
238
269
  className: 'twopg',
239
- iconClassName: 'twopg'
270
+ iconClassName: 'twopg',
240
271
  },
241
272
  thumbnail: {
242
273
  visible: true,
243
274
  label: 'Thumbnail view',
244
275
  className: 'thumb',
245
- iconClassName: 'thumb'
276
+ iconClassName: 'thumb',
246
277
  },
247
278
  viewmode: {
248
279
  visible: true,
@@ -253,19 +284,19 @@ export const DEFAULT_OPTIONS = {
253
284
  visible: true,
254
285
  label: 'Zoom out',
255
286
  className: 'zoom_out',
256
- iconClassName: 'magnify'
287
+ iconClassName: 'magnify',
257
288
  },
258
289
  zoomIn: {
259
290
  visible: true,
260
291
  label: 'Zoom in',
261
292
  className: 'zoom_in',
262
- iconClassName: 'magnify plus'
293
+ iconClassName: 'magnify plus',
263
294
  },
264
295
  fullScreen: {
265
296
  visible: true,
266
297
  label: 'Toggle fullscreen',
267
298
  className: 'full',
268
- iconClassName: 'fullscreen'
299
+ iconClassName: 'fullscreen',
269
300
  },
270
301
  },
271
302
 
@@ -275,6 +306,12 @@ export const DEFAULT_OPTIONS = {
275
306
  */
276
307
  startFullscreen: false,
277
308
 
309
+ /**
310
+ * @type {Boolean}
311
+ * will show logo at fullscreen mode
312
+ */
313
+ enableFSLogoShortcut: false,
314
+
278
315
  /**
279
316
  * @type {Boolean}
280
317
  * On init, by default, we want to handle resizing bookreader
@@ -288,9 +325,24 @@ export const DEFAULT_OPTIONS = {
288
325
  * On init, by default, we want to use srcSet for images
289
326
  */
290
327
  useSrcSet: false,
328
+
329
+ /**
330
+ * @type {string}
331
+ * Path to the image to display when a page is unviewable (i.e. when
332
+ * displaying a preview of a book).
333
+ *
334
+ * Relative to the imagesBaseURL if a relative path is specified.
335
+ */
336
+ unviewablePageURI: './unviewable_page.png',
291
337
  };
292
338
 
293
- /** @typedef {'width' | 'height' | 'auto' | 'none'} AutoFitValues */
339
+ /**
340
+ * @typedef {'width' | 'height' | 'auto' | 'none'} AutoFitValues
341
+ * - width: fill the width of the container
342
+ * - height: fill the height of the container
343
+ * - auto: fill the width or height of the container, whichever is smaller
344
+ * - none: do not autofit
345
+ **/
294
346
 
295
347
  /**
296
348
  * @typedef {object} ReductionFactor
@@ -318,3 +370,9 @@ export const DEFAULT_OPTIONS = {
318
370
 
319
371
  /** @typedef {typeof DEFAULT_OPTIONS} BookReaderOptions */
320
372
 
373
+ /**
374
+ * Thrown when an error occurs while parsing options.
375
+ * Potentially recoverable and non-halting.
376
+ */
377
+ export class OptionsParseError extends Error {
378
+ }
@@ -0,0 +1,44 @@
1
+ // @ts-check
2
+ import { debounce } from '../utils';
3
+
4
+ /**
5
+ * Computing these things repeatedly is expensive (the browser needs to
6
+ * do a lot of computations/redrawing to make sure these are correct),
7
+ * so we store them here, and only recompute them when necessary:
8
+ * - window resize could have cause the container to change size
9
+ * - zoom could have cause scrollbars to appear/disappear, changing
10
+ * the client size.
11
+ */
12
+ export class HTMLDimensionsCacher {
13
+ clientWidth = 100;
14
+ clientHeight = 100;
15
+
16
+ boundingClientRect = { top: 0, left: 0 };
17
+
18
+ /**
19
+ * @param {HTMLElement} element
20
+ */
21
+ constructor(element) {
22
+ /** @type {HTMLElement} */
23
+ this.element = element;
24
+ }
25
+
26
+ updateClientSizes = () => {
27
+ const bc = this.element.getBoundingClientRect();
28
+ this.clientWidth = this.element.clientWidth;
29
+ this.clientHeight = this.element.clientHeight;
30
+ this.boundingClientRect.top = bc.top;
31
+ this.boundingClientRect.left = bc.left;
32
+ }
33
+ debouncedUpdateClientSizes = debounce(this.updateClientSizes, 150, false);
34
+
35
+ /** @param {EventTarget} win */
36
+ attachResizeListener(win = window) {
37
+ win.addEventListener('resize', this.debouncedUpdateClientSizes);
38
+ }
39
+
40
+ /** @param {EventTarget} win */
41
+ detachResizeListener(win = window) {
42
+ win.removeEventListener('resize', this.debouncedUpdateClientSizes);
43
+ }
44
+ }
@@ -0,0 +1,31 @@
1
+ /** Adds a class while the given element is experiencing scrolling */
2
+ export class ScrollClassAdder {
3
+ /**
4
+ * @param {HTMLElement} element
5
+ * @param {string} className
6
+ */
7
+ constructor(element, className) {
8
+ /** @type {HTMLElement} */
9
+ this.element = element;
10
+ /** @type {string} */
11
+ this.className = className;
12
+ this.timeout = null;
13
+ }
14
+
15
+ attach() {
16
+ this.element.addEventListener('scroll', this.onScroll);
17
+ }
18
+
19
+ detach() {
20
+ this.element.removeEventListener('scroll', this.onScroll);
21
+ }
22
+
23
+ onScroll = () => {
24
+ this.element.classList.add(this.className);
25
+ clearTimeout(this.timeout);
26
+ // TODO: Also remove class on mousemove, touch, click, etc.
27
+ this.timeout = setTimeout(() => {
28
+ this.element.classList.remove(this.className);
29
+ }, 600);
30
+ }
31
+ }
@@ -0,0 +1,45 @@
1
+ // @ts-check
2
+ export class SelectionObserver {
3
+ selecting = false;
4
+ startedInSelector = false;
5
+ /** @type {HTMLElement} */
6
+ target = null;
7
+
8
+ /**
9
+ * @param {string} selector
10
+ * @param {function('started' | 'cleared', HTMLElement): any} handler
11
+ */
12
+ constructor(selector, handler) {
13
+ this.selector = selector;
14
+ this.handler = handler;
15
+ }
16
+
17
+ attach() {
18
+ // We can't just use selectstart, because safari on iOS just
19
+ // randomly decides when to fire it 😤
20
+ // document.addEventListener("selectstart", this._onSelectStart);
21
+ // This has to be on document :/
22
+ document.addEventListener("selectionchange", this._onSelectionChange);
23
+ }
24
+
25
+ detach() {
26
+ document.removeEventListener("selectionchange", this._onSelectionChange);
27
+ }
28
+
29
+ _onSelectionChange = () => {
30
+ const sel = window.getSelection();
31
+
32
+ if (!this.selecting && sel.toString()) {
33
+ const target = $(sel.anchorNode).closest(this.selector)[0];
34
+ if (!target) return;
35
+ this.target = target;
36
+ this.selecting = true;
37
+ this.handler('started', this.target);
38
+ }
39
+
40
+ if (this.selecting && (sel.isCollapsed || !sel.toString() || !$(sel.anchorNode).closest(this.selector)[0])) {
41
+ this.selecting = false;
42
+ this.handler('cleared', this.target);
43
+ }
44
+ };
45
+ }
@@ -31,6 +31,6 @@ export function exposeOverrideable(FromClass, fromMethod, fromTransform, ToClass
31
31
  return overrideFn.apply(newThis, arguments);
32
32
  };
33
33
  wrapper = overrideFn;
34
- }
34
+ },
35
35
  });
36
36
  }