@internetarchive/bookreader 5.0.0-3 → 5.0.0-30-a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (389) hide show
  1. package/.eslintrc.js +17 -5
  2. package/.github/dependabot.yml +8 -0
  3. package/.github/workflows/node.js.yml +10 -1
  4. package/.husky/_/husky.sh +30 -0
  5. package/.testcaferc.js +10 -0
  6. package/BookReader/BookReader.css +75 -323
  7. package/BookReader/BookReader.js +1 -1
  8. package/BookReader/BookReader.js.LICENSE.txt +45 -0
  9. package/BookReader/BookReader.js.map +1 -1
  10. package/BookReader/ia-bookreader-bundle.js +1458 -0
  11. package/BookReader/{bookreader-component-bundle.js.LICENSE.txt → ia-bookreader-bundle.js.LICENSE.txt} +10 -9
  12. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  13. package/BookReader/icons/close-circle-dark.svg +1 -0
  14. package/BookReader/icons/voice.svg +1 -0
  15. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  16. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  17. package/BookReader/plugins/plugin.autoplay.js +1 -1
  18. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  19. package/BookReader/plugins/plugin.chapters.js +1 -1
  20. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  21. package/BookReader/plugins/plugin.iframe.js +1 -1
  22. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  23. package/BookReader/plugins/plugin.mobile_nav.js +1 -1
  24. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  25. package/BookReader/plugins/plugin.resume.js +1 -1
  26. package/BookReader/plugins/plugin.resume.js.map +1 -1
  27. package/BookReader/plugins/plugin.search.js +1 -1
  28. package/BookReader/plugins/plugin.search.js.map +1 -1
  29. package/BookReader/plugins/plugin.text_selection.js +1 -1
  30. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  31. package/BookReader/plugins/plugin.tts.js +1 -1
  32. package/BookReader/plugins/plugin.tts.js.map +1 -1
  33. package/BookReader/plugins/plugin.url.js +1 -1
  34. package/BookReader/plugins/plugin.url.js.map +1 -1
  35. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  36. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  37. package/BookReader/webcomponents-bundle.js +3 -0
  38. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  39. package/BookReader/webcomponents-bundle.js.map +1 -0
  40. package/BookReaderDemo/BookReaderDemo.css +14 -1
  41. package/BookReaderDemo/IADemoBr.js +107 -0
  42. package/BookReaderDemo/demo-advanced.html +1 -1
  43. package/BookReaderDemo/demo-autoplay.html +1 -0
  44. package/BookReaderDemo/demo-embed-iframe-src.html +1 -0
  45. package/BookReaderDemo/demo-fullscreen-mobile.html +1 -0
  46. package/BookReaderDemo/demo-fullscreen.html +1 -0
  47. package/BookReaderDemo/demo-iiif.html +1 -0
  48. package/BookReaderDemo/demo-internetarchive.html +66 -18
  49. package/BookReaderDemo/demo-multiple.html +1 -0
  50. package/BookReaderDemo/demo-preview-pages.html +1 -0
  51. package/BookReaderDemo/demo-simple.html +1 -0
  52. package/BookReaderDemo/demo-vendor-fullscreen.html +1 -0
  53. package/BookReaderDemo/immersion-1up.html +1 -0
  54. package/BookReaderDemo/immersion-mode.html +1 -0
  55. package/BookReaderDemo/toggle_controls.html +1 -0
  56. package/BookReaderDemo/view_mode.html +1 -0
  57. package/BookReaderDemo/viewmode-cycle.html +1 -2
  58. package/CHANGELOG.md +114 -0
  59. package/babel.config.js +18 -0
  60. package/index.html +3 -0
  61. package/jsconfig.json +19 -0
  62. package/package.json +45 -27
  63. package/src/BookNavigator/assets/button-base.js +8 -1
  64. package/src/BookNavigator/assets/ia-logo.js +17 -0
  65. package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
  66. package/src/BookNavigator/assets/icon_sort_desc.js +5 -0
  67. package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
  68. package/src/BookNavigator/assets/icon_volumes.js +11 -0
  69. package/src/BookNavigator/book-navigator.js +528 -0
  70. package/src/BookNavigator/bookmarks/bookmark-button.js +2 -1
  71. package/src/BookNavigator/bookmarks/bookmark-edit.js +2 -1
  72. package/src/BookNavigator/bookmarks/bookmarks-list.js +1 -0
  73. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +4 -9
  74. package/src/BookNavigator/bookmarks/bookmarks-provider.js +32 -11
  75. package/src/BookNavigator/bookmarks/ia-bookmarks.js +88 -43
  76. package/src/BookNavigator/downloads/downloads-provider.js +22 -16
  77. package/src/BookNavigator/downloads/downloads.js +16 -23
  78. package/src/BookNavigator/search/a-search-result.js +1 -0
  79. package/src/BookNavigator/search/search-provider.js +54 -20
  80. package/src/BookNavigator/search/search-results.js +7 -18
  81. package/src/BookNavigator/sharing.js +27 -0
  82. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +10 -12
  83. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +1 -0
  84. package/src/BookNavigator/volumes/volumes-provider.js +114 -0
  85. package/src/BookNavigator/volumes/volumes.js +189 -0
  86. package/src/BookReader/DebugConsole.js +3 -3
  87. package/src/BookReader/DragScrollable.js +233 -0
  88. package/src/BookReader/Mode1Up.js +50 -351
  89. package/src/BookReader/Mode1UpLit.js +434 -0
  90. package/src/BookReader/Mode2Up.js +94 -72
  91. package/src/BookReader/ModeSmoothZoom.js +177 -0
  92. package/src/BookReader/ModeThumb.js +16 -8
  93. package/src/BookReader/Navbar/Navbar.js +2 -31
  94. package/src/BookReader/PageContainer.js +47 -2
  95. package/src/BookReader/ReduceSet.js +1 -1
  96. package/src/BookReader/Toolbar/Toolbar.js +5 -5
  97. package/src/BookReader/options.js +10 -0
  98. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  99. package/src/BookReader/utils.js +68 -13
  100. package/src/BookReader.js +316 -232
  101. package/src/assets/icons/close-circle-dark.svg +1 -0
  102. package/src/assets/icons/voice.svg +1 -0
  103. package/src/css/BookReader.scss +0 -12
  104. package/src/css/_BRComponent.scss +1 -1
  105. package/src/css/_BRmain.scss +19 -24
  106. package/src/css/_BRnav.scss +4 -26
  107. package/src/css/_BRpages.scss +35 -0
  108. package/src/css/_BRsearch.scss +11 -215
  109. package/src/css/_TextSelection.scss +1 -17
  110. package/src/css/_controls.scss +16 -3
  111. package/src/css/_icons.scss +6 -0
  112. package/src/ia-bookreader/ia-bookreader.js +205 -0
  113. package/src/plugins/plugin.chapters.js +15 -18
  114. package/src/plugins/plugin.mobile_nav.js +11 -10
  115. package/src/plugins/plugin.resume.js +3 -3
  116. package/src/plugins/plugin.text_selection.js +17 -29
  117. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  118. package/src/plugins/search/plugin.search.js +113 -104
  119. package/src/plugins/search/view.js +48 -163
  120. package/src/plugins/tts/AbstractTTSEngine.js +7 -0
  121. package/src/plugins/tts/FestivalTTSEngine.js +2 -2
  122. package/src/plugins/tts/WebTTSEngine.js +5 -0
  123. package/src/plugins/tts/plugin.tts.js +67 -102
  124. package/src/plugins/url/UrlPlugin.js +184 -0
  125. package/src/plugins/url/plugin.url.js +220 -0
  126. package/{src → stat}/BookNavigator/BookModel.js +0 -0
  127. package/{src → stat}/BookNavigator/BookNavigator.js +151 -104
  128. package/stat/BookNavigator/assets/bookmark-colors.js +15 -0
  129. package/stat/BookNavigator/assets/button-base.js +61 -0
  130. package/stat/BookNavigator/assets/ia-logo.js +17 -0
  131. package/stat/BookNavigator/assets/icon_checkmark.js +6 -0
  132. package/stat/BookNavigator/assets/icon_close.js +3 -0
  133. package/stat/BookNavigator/assets/icon_sort_asc.js +5 -0
  134. package/stat/BookNavigator/assets/icon_sort_desc.js +5 -0
  135. package/stat/BookNavigator/assets/icon_sort_neutral.js +5 -0
  136. package/stat/BookNavigator/assets/icon_volumes.js +11 -0
  137. package/stat/BookNavigator/bookmarks/bookmark-button.js +64 -0
  138. package/stat/BookNavigator/bookmarks/bookmark-edit.js +215 -0
  139. package/stat/BookNavigator/bookmarks/bookmarks-list.js +285 -0
  140. package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
  141. package/stat/BookNavigator/bookmarks/bookmarks-provider.js +56 -0
  142. package/stat/BookNavigator/bookmarks/ia-bookmarks.js +523 -0
  143. package/{src → stat}/BookNavigator/br-fullscreen-mgr.js +1 -2
  144. package/stat/BookNavigator/delete-modal-actions.js +49 -0
  145. package/stat/BookNavigator/downloads/downloads-provider.js +72 -0
  146. package/stat/BookNavigator/downloads/downloads.js +139 -0
  147. package/stat/BookNavigator/provider-config.js +0 -0
  148. package/stat/BookNavigator/search/a-search-result.js +55 -0
  149. package/stat/BookNavigator/search/search-provider.js +180 -0
  150. package/stat/BookNavigator/search/search-results.js +360 -0
  151. package/stat/BookNavigator/sharing.js +31 -0
  152. package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +94 -0
  153. package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
  154. package/stat/BookNavigator/volumes/volumes-provider.js +83 -0
  155. package/stat/BookNavigator/volumes/volumes.js +178 -0
  156. package/stat/BookReader/BookModel.js +518 -0
  157. package/stat/BookReader/DebugConsole.js +54 -0
  158. package/stat/BookReader/DragScrollable.js +233 -0
  159. package/stat/BookReader/ImageCache.js +116 -0
  160. package/stat/BookReader/Mode1Up.js +102 -0
  161. package/stat/BookReader/Mode1UpLit.js +434 -0
  162. package/stat/BookReader/Mode2Up.js +1372 -0
  163. package/stat/BookReader/ModeSmoothZoom.js +177 -0
  164. package/stat/BookReader/ModeThumb.js +344 -0
  165. package/stat/BookReader/Navbar/Navbar.js +310 -0
  166. package/stat/BookReader/PageContainer.js +120 -0
  167. package/stat/BookReader/ReduceSet.js +26 -0
  168. package/stat/BookReader/Toolbar/Toolbar.js +384 -0
  169. package/stat/BookReader/events.js +20 -0
  170. package/stat/BookReader/options.js +324 -0
  171. package/stat/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  172. package/stat/BookReader/utils/classes.js +36 -0
  173. package/stat/BookReader/utils.js +240 -0
  174. package/stat/BookReader.js +2550 -0
  175. package/{src → stat}/BookReaderComponent/BookReaderComponent.js +16 -11
  176. package/stat/assets/icons/1up.svg +12 -0
  177. package/stat/assets/icons/2up.svg +15 -0
  178. package/stat/assets/icons/advance.svg +26 -0
  179. package/stat/assets/icons/chevron-right.svg +1 -0
  180. package/stat/assets/icons/close-circle-dark.svg +1 -0
  181. package/stat/assets/icons/close-circle.svg +1 -0
  182. package/stat/assets/icons/fullscreen.svg +17 -0
  183. package/stat/assets/icons/fullscreen_exit.svg +17 -0
  184. package/stat/assets/icons/hamburger.svg +15 -0
  185. package/stat/assets/icons/left-arrow.svg +12 -0
  186. package/stat/assets/icons/magnify-minus.svg +16 -0
  187. package/stat/assets/icons/magnify-plus.svg +17 -0
  188. package/stat/assets/icons/magnify.svg +15 -0
  189. package/stat/assets/icons/pause.svg +23 -0
  190. package/stat/assets/icons/play.svg +22 -0
  191. package/stat/assets/icons/playback-speed.svg +34 -0
  192. package/stat/assets/icons/read-aloud.svg +22 -0
  193. package/stat/assets/icons/review.svg +22 -0
  194. package/stat/assets/icons/thumbnails.svg +17 -0
  195. package/stat/assets/icons/voice.svg +1 -0
  196. package/stat/assets/icons/volume-full.svg +22 -0
  197. package/stat/assets/images/BRicons.png +0 -0
  198. package/stat/assets/images/BRicons.svg +94 -0
  199. package/stat/assets/images/BRicons_ia.png +0 -0
  200. package/stat/assets/images/back_pages.png +0 -0
  201. package/stat/assets/images/book_bottom_icon.png +0 -0
  202. package/stat/assets/images/book_down_icon.png +0 -0
  203. package/stat/assets/images/book_left_icon.png +0 -0
  204. package/stat/assets/images/book_leftmost_icon.png +0 -0
  205. package/stat/assets/images/book_right_icon.png +0 -0
  206. package/stat/assets/images/book_rightmost_icon.png +0 -0
  207. package/stat/assets/images/book_top_icon.png +0 -0
  208. package/stat/assets/images/book_up_icon.png +0 -0
  209. package/stat/assets/images/books_graphic.svg +177 -0
  210. package/stat/assets/images/booksplit.png +0 -0
  211. package/stat/assets/images/control_pause_icon.png +0 -0
  212. package/stat/assets/images/control_play_icon.png +0 -0
  213. package/stat/assets/images/embed_icon.png +0 -0
  214. package/stat/assets/images/icon-home-ia.png +0 -0
  215. package/stat/assets/images/icon_OL-logo-xs.png +0 -0
  216. package/stat/assets/images/icon_alert-xs.png +0 -0
  217. package/stat/assets/images/icon_book.svg +12 -0
  218. package/stat/assets/images/icon_bookmark.svg +12 -0
  219. package/stat/assets/images/icon_close-pop.png +0 -0
  220. package/stat/assets/images/icon_download.png +0 -0
  221. package/stat/assets/images/icon_gear.svg +14 -0
  222. package/stat/assets/images/icon_hamburger.svg +20 -0
  223. package/stat/assets/images/icon_home.png +0 -0
  224. package/stat/assets/images/icon_home.svg +21 -0
  225. package/stat/assets/images/icon_home_ia.png +0 -0
  226. package/stat/assets/images/icon_indicator.png +0 -0
  227. package/stat/assets/images/icon_info.svg +11 -0
  228. package/stat/assets/images/icon_one_page.svg +8 -0
  229. package/stat/assets/images/icon_pause.svg +1 -0
  230. package/stat/assets/images/icon_play.svg +1 -0
  231. package/stat/assets/images/icon_playback-rate.svg +15 -0
  232. package/stat/assets/images/icon_return.png +0 -0
  233. package/stat/assets/images/icon_search_button.svg +8 -0
  234. package/stat/assets/images/icon_share.svg +9 -0
  235. package/stat/assets/images/icon_skip-ahead.svg +6 -0
  236. package/stat/assets/images/icon_skip-back.svg +13 -0
  237. package/stat/assets/images/icon_speaker.svg +18 -0
  238. package/stat/assets/images/icon_speaker_open.svg +10 -0
  239. package/stat/assets/images/icon_thumbnails.svg +12 -0
  240. package/stat/assets/images/icon_toc.svg +5 -0
  241. package/stat/assets/images/icon_two_pages.svg +9 -0
  242. package/stat/assets/images/icon_zoomer.png +0 -0
  243. package/stat/assets/images/loading.gif +0 -0
  244. package/stat/assets/images/logo_icon.png +0 -0
  245. package/stat/assets/images/marker_chap-off.png +0 -0
  246. package/stat/assets/images/marker_chap-off.svg +11 -0
  247. package/stat/assets/images/marker_chap-off_ia.png +0 -0
  248. package/stat/assets/images/marker_chap-on.png +0 -0
  249. package/stat/assets/images/marker_chap-on.svg +11 -0
  250. package/stat/assets/images/marker_srch-on.svg +11 -0
  251. package/stat/assets/images/marker_srchchap-off.png +0 -0
  252. package/stat/assets/images/marker_srchchap-on.png +0 -0
  253. package/stat/assets/images/nav_control-dn.png +0 -0
  254. package/stat/assets/images/nav_control-dn_ia.png +0 -0
  255. package/stat/assets/images/nav_control-up.png +0 -0
  256. package/stat/assets/images/nav_control-up_ia.png +0 -0
  257. package/stat/assets/images/nav_control.png +0 -0
  258. package/stat/assets/images/one_page_mode_icon.png +0 -0
  259. package/stat/assets/images/paper-badge.png +0 -0
  260. package/stat/assets/images/print_icon.png +0 -0
  261. package/stat/assets/images/progressbar.gif +0 -0
  262. package/stat/assets/images/right_edges.png +0 -0
  263. package/stat/assets/images/slider.png +0 -0
  264. package/stat/assets/images/slider_ia.png +0 -0
  265. package/stat/assets/images/thumbnail_mode_icon.png +0 -0
  266. package/stat/assets/images/transparent.png +0 -0
  267. package/stat/assets/images/two_page_mode_icon.png +0 -0
  268. package/stat/assets/images/zoom_in_icon.png +0 -0
  269. package/stat/assets/images/zoom_out_icon.png +0 -0
  270. package/stat/css/BookReader.scss +89 -0
  271. package/stat/css/_BRBookmarks.scss +29 -0
  272. package/stat/css/_BRComponent.scss +13 -0
  273. package/stat/css/_BRfloat.scss +197 -0
  274. package/stat/css/_BRicon.scss +48 -0
  275. package/stat/css/_BRmain.scss +251 -0
  276. package/stat/css/_BRnav.scss +359 -0
  277. package/stat/css/_BRpages.scss +139 -0
  278. package/stat/css/_BRsearch.scss +226 -0
  279. package/stat/css/_BRtoolbar.scss +84 -0
  280. package/stat/css/_BRvendor.scss +5 -0
  281. package/stat/css/_MobileNav.scss +194 -0
  282. package/stat/css/_TextSelection.scss +32 -0
  283. package/stat/css/_colorbox.scss +52 -0
  284. package/stat/css/_controls.scss +253 -0
  285. package/stat/css/_icons.scss +121 -0
  286. package/stat/jquery-wrapper.js +4 -0
  287. package/stat/plugins/plugin.archive_analytics.js +86 -0
  288. package/stat/plugins/plugin.autoplay.js +129 -0
  289. package/stat/plugins/plugin.chapters.js +248 -0
  290. package/stat/plugins/plugin.iframe.js +48 -0
  291. package/stat/plugins/plugin.mobile_nav.js +288 -0
  292. package/stat/plugins/plugin.resume.js +68 -0
  293. package/stat/plugins/plugin.text_selection.js +291 -0
  294. package/{src → stat}/plugins/plugin.url.js +4 -4
  295. package/stat/plugins/plugin.vendor-fullscreen.js +247 -0
  296. package/stat/plugins/search/plugin.search.js +439 -0
  297. package/stat/plugins/search/view.js +439 -0
  298. package/stat/plugins/tts/AbstractTTSEngine.js +249 -0
  299. package/stat/plugins/tts/FestivalTTSEngine.js +169 -0
  300. package/stat/plugins/tts/PageChunk.js +107 -0
  301. package/stat/plugins/tts/PageChunkIterator.js +163 -0
  302. package/stat/plugins/tts/WebTTSEngine.js +357 -0
  303. package/stat/plugins/tts/plugin.tts.js +357 -0
  304. package/stat/plugins/tts/tooltip_dict.js +15 -0
  305. package/stat/plugins/tts/utils.js +91 -0
  306. package/stat/util/browserSniffing.js +30 -0
  307. package/stat/util/debouncer.js +26 -0
  308. package/stat/util/docCookies.js +67 -0
  309. package/stat/util/strings.js +34 -0
  310. package/tests/e2e/README.md +37 -0
  311. package/tests/e2e/autoplay.test.js +2 -2
  312. package/tests/e2e/base.test.js +5 -7
  313. package/tests/e2e/helpers/base.js +8 -3
  314. package/tests/e2e/helpers/debug.js +1 -1
  315. package/tests/e2e/helpers/desktopSearch.js +1 -1
  316. package/tests/e2e/helpers/mobileSearch.js +3 -3
  317. package/tests/e2e/helpers/params.js +17 -0
  318. package/tests/e2e/rightToLeft.test.js +4 -5
  319. package/tests/e2e/viewmode.test.js +30 -31
  320. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +3 -3
  321. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +176 -0
  322. package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
  323. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  324. package/tests/jest/BookReader/Mode1UpLit.test.js +87 -0
  325. package/tests/{BookReader → jest/BookReader}/Mode2Up.test.js +5 -7
  326. package/tests/jest/BookReader/ModeSmoothZoom.test.js +149 -0
  327. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  328. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +7 -7
  329. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +74 -2
  330. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  331. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  332. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  333. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  334. package/tests/jest/BookReader/utils.test.js +136 -0
  335. package/tests/jest/BookReader.keyboard.test.js +190 -0
  336. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  337. package/tests/{BookReader.test.js → jest/BookReader.test.js} +20 -4
  338. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  339. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +2 -2
  340. package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +8 -8
  341. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  342. package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
  343. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  344. package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +14 -24
  345. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  346. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +12 -5
  347. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +6 -6
  348. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +3 -3
  349. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  350. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  351. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  352. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +1 -1
  353. package/tests/{plugins → jest/plugins}/tts/utils.test.js +3 -3
  354. package/tests/jest/plugins/url/UrlPlugin.test.js +175 -0
  355. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +33 -14
  356. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  357. package/tests/{util → jest/util}/docCookies.test.js +1 -1
  358. package/tests/{util → jest/util}/strings.test.js +1 -1
  359. package/tests/{utils.js → jest/utils.js} +38 -0
  360. package/tests/karma/BookNavigator/book-navigator.test.js +485 -0
  361. package/tests/karma/BookNavigator/bookmarks/bookmark-button.test.js +44 -0
  362. package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +1 -3
  363. package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +1 -2
  364. package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +67 -0
  365. package/tests/karma/BookNavigator/downloads/downloads.test.js +54 -0
  366. package/tests/karma/BookNavigator/search/search-provider.test.js +123 -0
  367. package/tests/karma/BookNavigator/{search-results.test.js → search/search-results.test.js} +1 -4
  368. package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +49 -0
  369. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -2
  370. package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +184 -0
  371. package/tests/karma/BookNavigator/volumes/volumes.test.js +98 -0
  372. package/webpack.config.js +10 -4
  373. package/.babelrc +0 -12
  374. package/.dependabot/config.yml +0 -6
  375. package/.testcaferc.json +0 -5
  376. package/BookReader/bookreader-component-bundle.js +0 -1450
  377. package/BookReader/bookreader-component-bundle.js.map +0 -1
  378. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  379. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  380. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  381. package/src/BookNavigator/assets/book-loader.js +0 -27
  382. package/src/ItemNavigator/ItemNavigator.js +0 -372
  383. package/src/ItemNavigator/providers/sharing.js +0 -29
  384. package/src/dragscrollable-br.js +0 -261
  385. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  386. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  387. package/tests/BookReader/Mode1Up.test.js +0 -164
  388. package/tests/BookReader/utils.test.js +0 -109
  389. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
@@ -0,0 +1,177 @@
1
+ // @ts-check
2
+ import Hammer from "hammerjs";
3
+ /** @typedef {import('./utils/HTMLDimensionsCacher.js').HTMLDimensionsCacher} HTMLDimensionsCacher */
4
+
5
+ /**
6
+ * @typedef {object} SmoothZoomable
7
+ * @property {HTMLElement} $container
8
+ * @property {HTMLElement} $visibleWorld
9
+ * @property {number} scale
10
+ * @property {{ x: number, y: number }} scaleCenter
11
+ * @property {HTMLDimensionsCacher} htmlDimensionsCacher
12
+ * @property {function(): void} [attachScrollListeners]
13
+ * @property {function(): void} [detachScrollListeners]
14
+ */
15
+
16
+ /** Manages pinch-zoom, ctrl-wheel, and trackpad pinch smooth zooming. */
17
+ export class ModeSmoothZoom {
18
+ /** @param {SmoothZoomable} mode */
19
+ constructor(mode) {
20
+ /** @type {SmoothZoomable} */
21
+ this.mode = mode;
22
+
23
+ /** Non-null when a scale has been enqueued/is being processed by the buffer function */
24
+ this.pinchMoveFrame = null;
25
+ /** Promise for the current/enqueued pinch move frame. Resolves when it is complete. */
26
+ this.pinchMoveFramePromise = Promise.resolve();
27
+ this.oldScale = 1;
28
+ /** @type {{ scale: number, center: { x: number, y: number }}} */
29
+ this.lastEvent = null;
30
+ this.attached = false;
31
+
32
+ /** @type {function(function(): void): any} */
33
+ this.bufferFn = window.requestAnimationFrame.bind(window);
34
+
35
+ // Hammer.js by default set userSelect to None; we don't want that!
36
+ // TODO: Is there any way to do this not globally on Hammer?
37
+ delete Hammer.defaults.cssProps.userSelect;
38
+ this.hammer = new Hammer.Manager(this.mode.$container, {
39
+ touchAction: "pan-x pan-y",
40
+ });
41
+
42
+ this.hammer.add(new Hammer.Pinch());
43
+ }
44
+
45
+ attach() {
46
+ if (this.attached) return;
47
+
48
+ this.attachCtrlZoom();
49
+
50
+ // GestureEvents work only on Safari; they interfere with Hammer,
51
+ // so block them.
52
+ this.mode.$container.addEventListener('gesturestart', this._preventEvent);
53
+ this.mode.$container.addEventListener('gesturechange', this._preventEvent);
54
+ this.mode.$container.addEventListener('gestureend', this._preventEvent);
55
+
56
+ // The pinch listeners
57
+ this.hammer.on("pinchstart", this._pinchStart);
58
+ this.hammer.on("pinchmove", this._pinchMove);
59
+ this.hammer.on("pinchend", this._pinchEnd);
60
+ this.hammer.on("pinchcancel", this._pinchCancel);
61
+
62
+ this.attached = true;
63
+ }
64
+
65
+ detach() {
66
+ this.detachCtrlZoom();
67
+
68
+ // GestureEvents work only on Safari; they interfere with Hammer,
69
+ // so block them.
70
+ this.mode.$container.removeEventListener('gesturestart', this._preventEvent);
71
+ this.mode.$container.removeEventListener('gesturechange', this._preventEvent);
72
+ this.mode.$container.removeEventListener('gestureend', this._preventEvent);
73
+
74
+ // The pinch listeners
75
+ this.hammer.off("pinchstart", this._pinchStart);
76
+ this.hammer.off("pinchmove", this._pinchMove);
77
+ this.hammer.off("pinchend", this._pinchEnd);
78
+ this.hammer.off("pinchcancel", this._pinchCancel);
79
+
80
+ this.attached = false;
81
+ }
82
+
83
+ /** @param {Event} ev */
84
+ _preventEvent = (ev) => {
85
+ ev.preventDefault();
86
+ return false;
87
+ }
88
+
89
+ _pinchStart = () => {
90
+ // Do this in case the pinchend hasn't fired yet.
91
+ this.oldScale = 1;
92
+ this.mode.$visibleWorld.style.willChange = "transform";
93
+ this.detachCtrlZoom();
94
+ this.mode.detachScrollListeners?.();
95
+ }
96
+
97
+ /** @param {{ scale: number, center: { x: number, y: number }}} e */
98
+ _pinchMove = async (e) => {
99
+ this.lastEvent = e;
100
+ if (!this.pinchMoveFrame) {
101
+ let pinchMoveFramePromiseRes = null;
102
+ this.pinchMoveFramePromise = new Promise(
103
+ (res) => (pinchMoveFramePromiseRes = res)
104
+ );
105
+
106
+ // Buffer these events; only update the scale when request animation fires
107
+ this.pinchMoveFrame = this.bufferFn(() => {
108
+ this.updateScaleCenter({
109
+ clientX: this.lastEvent.center.x,
110
+ clientY: this.lastEvent.center.y,
111
+ });
112
+ this.mode.scale *= this.lastEvent.scale / this.oldScale;
113
+ this.oldScale = this.lastEvent.scale;
114
+ this.pinchMoveFrame = null;
115
+ pinchMoveFramePromiseRes();
116
+ });
117
+ }
118
+ }
119
+
120
+ _pinchEnd = async () => {
121
+ // Want this to happen after the pinchMoveFrame,
122
+ // if one is in progress; otherwise setting oldScale
123
+ // messes up the transform.
124
+ await this.pinchMoveFramePromise;
125
+ this.mode.scaleCenter = { x: 0.5, y: 0.5 };
126
+ this.oldScale = 1;
127
+ this.mode.$visibleWorld.style.willChange = "auto";
128
+ this.attachCtrlZoom();
129
+ this.mode.attachScrollListeners?.();
130
+ }
131
+
132
+ _pinchCancel = async () => {
133
+ // iOS fires pinchcancel ~randomly; it looks like it sometimes
134
+ // thinks the pinch becomes a pan, at which point it cancels?
135
+ await this._pinchEnd();
136
+ }
137
+
138
+ /** @private */
139
+ attachCtrlZoom() {
140
+ window.addEventListener("wheel", this._handleCtrlWheel, { passive: false });
141
+ }
142
+
143
+ /** @private */
144
+ detachCtrlZoom() {
145
+ window.removeEventListener("wheel", this._handleCtrlWheel);
146
+ }
147
+
148
+ /** @param {WheelEvent} ev **/
149
+ _handleCtrlWheel = (ev) => {
150
+ if (!ev.ctrlKey) return;
151
+ ev.preventDefault();
152
+ const zoomMultiplier =
153
+ // Zooming on macs was painfully slow; likely due to their better
154
+ // trackpads. Give them a higher zoom rate.
155
+ /Mac/i.test(navigator.platform)
156
+ ? 0.045
157
+ : // This worked well for me on Windows
158
+ 0.03;
159
+
160
+ // Zoom around the cursor
161
+ this.updateScaleCenter(ev);
162
+ this.mode.scale *= 1 - Math.sign(ev.deltaY) * zoomMultiplier;
163
+ }
164
+
165
+ /**
166
+ * @param {object} param0
167
+ * @param {number} param0.clientX
168
+ * @param {number} param0.clientY
169
+ */
170
+ updateScaleCenter({ clientX, clientY }) {
171
+ const bc = this.mode.htmlDimensionsCacher.boundingClientRect;
172
+ this.mode.scaleCenter = {
173
+ x: (clientX - bc.left) / this.mode.htmlDimensionsCacher.clientWidth,
174
+ y: (clientY - bc.top) / this.mode.htmlDimensionsCacher.clientHeight,
175
+ };
176
+ }
177
+ }
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
  import { notInArray, clamp } from './utils.js';
3
3
  import { EVENTS } from './events.js';
4
+ import { DragScrollable } from './DragScrollable.js';
4
5
  /** @typedef {import('../BookREader.js').default} BookReader */
5
6
  /** @typedef {import('./BookModel.js').PageIndex} PageIndex */
6
7
  /** @typedef {import('./BookModel.js').BookModel} BookModel */
@@ -234,20 +235,27 @@ export class ModeThumb {
234
235
  }
235
236
 
236
237
  /**
237
- * @param {1 | -1} direction
238
+ * @param {'in' | 'out'} direction
238
239
  */
239
240
  zoom(direction) {
240
241
  const oldColumns = this.br.thumbColumns;
241
242
  switch (direction) {
242
- case -1:
243
- this.br.thumbColumns += 1;
244
- break;
245
- case 1:
243
+ case 'in':
246
244
  this.br.thumbColumns -= 1;
247
245
  break;
246
+ case 'out':
247
+ this.br.thumbColumns += 1;
248
+ break;
249
+ default:
250
+ console.error(`Unsupported direction: ${direction}`);
248
251
  }
249
252
 
250
- this.br.thumbColumns = clamp(this.br.thumbColumns, 2, 8);
253
+ // Limit zoom in/out columns
254
+ this.br.thumbColumns = clamp(
255
+ this.br.thumbColumns,
256
+ this.br.options.thumbMinZoomColumns,
257
+ this.br.options.thumbMaxZoomColumns
258
+ );
251
259
 
252
260
  if (this.br.thumbColumns != oldColumns) {
253
261
  this.br.displayedRows = []; /* force a gallery redraw */
@@ -279,7 +287,7 @@ export class ModeThumb {
279
287
 
280
288
  this.br.refs.$brPageViewEl = $("<div class='BRpageview'></div>");
281
289
  this.br.refs.$brContainer.append(this.br.refs.$brPageViewEl);
282
- this.br.refs.$brContainer.dragscrollable({preventDefault:true});
290
+ this.dragScrollable = this.dragScrollable || new DragScrollable(this.br.refs.$brContainer[0], {preventDefault: true});
283
291
 
284
292
  this.br.bindGestures(this.br.refs.$brContainer);
285
293
 
@@ -330,7 +338,7 @@ export class ModeThumb {
330
338
  } else {
331
339
  this.br.animating = true;
332
340
  this.br.refs.$brContainer.stop(true)
333
- .animate({ scrollTop: leafTop }, 'fast', () => { this.br.animating = false });
341
+ .animate({ scrollTop: leafTop }, 'fast', () => { this.br.animating = false; });
334
342
  }
335
343
  }
336
344
  }
@@ -240,35 +240,6 @@ export class Navbar {
240
240
  return this.$nav;
241
241
  }
242
242
 
243
- /**
244
- * Initialize the navigation bar when embedded
245
- */
246
- initEmbed() {
247
- const { br } = this;
248
- // IA-specific
249
- const thisLink = (window.location + '')
250
- .replace('?ui=embed','')
251
- .replace('/stream/', '/details/')
252
- .replace('#', '/');
253
- const logoHtml = br.showLogo ? `<a class="logo" href="${br.logoURL}" target="_blank"></a>` : '';
254
-
255
- br.refs.$BRfooter = this.$root = $('<div class="BRfooter"></div>');
256
- br.refs.$BRnav = this.$nav = $(
257
- `<div class="BRnav BRnavEmbed">
258
- ${logoHtml}
259
- <span class="BRembedreturn">
260
- <a href="${thisLink}" target="_blank">${br.bookTitle}</a>
261
- </span>
262
- <span class="BRtoolbarbuttons">
263
- <button class="BRicon book_left"></button>
264
- <button class="BRicon book_right"></button>
265
- <button class="BRicon full"></button>
266
- </span>
267
- </div>`);
268
- this.$root.append(this.$nav);
269
- br.refs.$br.append(this.$root);
270
- }
271
-
272
243
  /**
273
244
  * Returns the textual representation of the current page for the navbar
274
245
  * @param {number} index
@@ -331,9 +302,9 @@ export function getNavPageNumHtml(index, numLeafs, pageNum, pageType, maxPageNum
331
302
 
332
303
  if (!pageIsAsserted) {
333
304
  const pageIndex = index + 1;
334
- return `Page (${pageIndex} of ${numLeafs})`; // Page (8 of 10)
305
+ return `(${pageIndex} of ${numLeafs})`; // Page (8 of 10)
335
306
  }
336
307
 
337
308
  const bookLengthLabel = maxPageNum ? ` of ${maxPageNum}` : '';
338
- return `Page ${pageNum}${bookLengthLabel}`;
309
+ return `${pageNum}${bookLengthLabel}`;
339
310
  }
@@ -58,14 +58,14 @@ export class PageContainer {
58
58
  backgroundLayers.push(`url("${this.loadingImage}") center/20px no-repeat`);
59
59
  }
60
60
  if (nextBestLoadedReduce) {
61
- backgroundLayers.push(`url("${this.page.getURI(nextBestLoadedReduce, 0)}") center/100% no-repeat`);
61
+ backgroundLayers.push(`url("${this.page.getURI(nextBestLoadedReduce, 0)}") center/100% 100% no-repeat`);
62
62
  }
63
63
 
64
64
  if (!alreadyLoaded) {
65
65
  this.$img
66
66
  .css('background', backgroundLayers.join(','))
67
67
  .one('loadend', async (ev) => {
68
- $(ev.target).css({ 'background': '' })
68
+ $(ev.target).css({ 'background': '' });
69
69
  $(ev.target).parent().removeClass('BRpageloading');
70
70
  });
71
71
  }
@@ -73,3 +73,48 @@ export class PageContainer {
73
73
  return this;
74
74
  }
75
75
  }
76
+
77
+
78
+ /**
79
+ * @param {PageModel} page
80
+ * @param {string} className
81
+ */
82
+ export function createSVGPageLayer(page, className) {
83
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
84
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
85
+ svg.setAttribute("viewBox", `0 0 ${page.width} ${page.height}`);
86
+ svg.setAttribute('class', `BRPageLayer ${className}`);
87
+ svg.setAttribute('preserveAspectRatio', 'none');
88
+ return svg;
89
+ }
90
+
91
+ /**
92
+ * @param {{ l: number, r: number, b: number, t: number }} box
93
+ */
94
+ export function boxToSVGRect({ l: left, r: right, b: bottom, t: top }) {
95
+ const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
96
+ rect.setAttribute("x", left.toString());
97
+ rect.setAttribute("y", top.toString());
98
+ rect.setAttribute("width", (right - left).toString());
99
+ rect.setAttribute("height", (bottom - top).toString());
100
+ return rect;
101
+ }
102
+
103
+ /**
104
+ * @param {string} layerClass
105
+ * @param {Array<{ l: number, r: number, b: number, t: number }>} boxes
106
+ * @param {PageModel} page
107
+ * @param {HTMLElement} containerEl
108
+ */
109
+ export function renderBoxesInPageContainerLayer(layerClass, boxes, page, containerEl) {
110
+ const mountedSvg = containerEl.querySelector(`.${layerClass}`);
111
+ // Create the layer if it's not there
112
+ const svg = mountedSvg || createSVGPageLayer(page, layerClass);
113
+ if (!mountedSvg) {
114
+ // Insert after the image if the image is already loaded.
115
+ const imgEl = containerEl.querySelector('.BRpageimage');
116
+ if (imgEl) $(svg).insertAfter(imgEl);
117
+ else $(svg).prependTo(containerEl);
118
+ }
119
+ boxes.forEach(box => svg.appendChild(boxToSVGRect(box)));
120
+ }
@@ -18,7 +18,7 @@ export const Pow2ReduceSet = {
18
18
  decr(n) {
19
19
  return 2 ** (Math.log2(n) - 1);
20
20
  }
21
- }
21
+ };
22
22
 
23
23
  export const NAMED_REDUCE_SETS = {
24
24
  pow2: Pow2ReduceSet,
@@ -54,7 +54,7 @@ export class Toolbar {
54
54
  .attr({href: br.bookUrl, title: br.bookUrlTitle})
55
55
  .addClass('BRreturn')
56
56
  .html(br.bookUrlText || br.bookTitle)
57
- )
57
+ );
58
58
  } else if (br.bookTitle) {
59
59
  $titleSectionEl.append(br.bookUrlText || br.bookTitle);
60
60
  }
@@ -212,7 +212,7 @@ export class Toolbar {
212
212
  $form.appendTo($shareDiv);
213
213
 
214
214
  $form.find('.fieldset-embed input').on('change', event => {
215
- const form = $(event.target).parents('form:first');
215
+ const form = $(event.target).parents('form').first();
216
216
  const params = {};
217
217
  params.mode = $(form.find('.fieldset-embed input[name=pages]:checked')).val();
218
218
  if (form.find('.fieldset-embed input[name=thispage]').prop('checked')) {
@@ -232,12 +232,12 @@ export class Toolbar {
232
232
  // Bind share buttons
233
233
 
234
234
  // Use url without hashes
235
- $form.find('.facebook-share-button').click(() => {
235
+ $form.find('.facebook-share-button').on("click", () => {
236
236
  const params = $.param({ u: this._getSocialShareUrl() });
237
237
  const url = 'https://www.facebook.com/sharer.php?' + params;
238
238
  createPopup(url, 600, 400, 'Share');
239
239
  });
240
- $form.find('.twitter-share-button').click(() => {
240
+ $form.find('.twitter-share-button').on("click", () => {
241
241
  const params = $.param({
242
242
  url: this._getSocialShareUrl(),
243
243
  text: br.bookTitle
@@ -245,7 +245,7 @@ export class Toolbar {
245
245
  const url = 'https://twitter.com/intent/tweet?' + params;
246
246
  createPopup(url, 600, 400, 'Share');
247
247
  });
248
- $form.find('.email-share-button').click(() => {
248
+ $form.find('.email-share-button').on("click", () => {
249
249
  const body = `${br.bookTitle}\n\n${this._getSocialShareUrl()}`;
250
250
  window.location.href = `mailto:?subject=${encodeURI(br.bookTitle)}&body=${encodeURI(body)}`;
251
251
  });
@@ -25,6 +25,10 @@ export const DEFAULT_OPTIONS = {
25
25
  thumbMaxLoading: 4,
26
26
  /** spacing between thumbnails */
27
27
  thumbPadding: 10,
28
+ /** min zoom in columns */
29
+ thumbMinZoomColumns: 2,
30
+ /** max zoom out columns */
31
+ thumbMaxZoomColumns: 8,
28
32
 
29
33
  /** @type {number | 'fast' | 'slow'} speed for flip animation */
30
34
  flipSpeed: 'fast',
@@ -275,6 +279,12 @@ export const DEFAULT_OPTIONS = {
275
279
  */
276
280
  startFullscreen: false,
277
281
 
282
+ /**
283
+ * @type {Boolean}
284
+ * will show logo at fullscreen mode
285
+ */
286
+ enableFSLogoShortcut: false,
287
+
278
288
  /**
279
289
  * @type {Boolean}
280
290
  * On init, by default, we want to handle resizing bookreader
@@ -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
+ }
@@ -40,6 +40,24 @@ export function notInArray(value, array) {
40
40
  return !array.includes(value);
41
41
  }
42
42
 
43
+ /**
44
+ * Determines the active element, going into shadow doms.
45
+ * @return {Element}
46
+ */
47
+ export function getActiveElement(doc = document, recurseShadowDom = true) {
48
+ const activeElement = doc.activeElement;
49
+ if (recurseShadowDom && activeElement?.shadowRoot) {
50
+ return getActiveElement(activeElement.shadowRoot, true);
51
+ }
52
+ return activeElement;
53
+ }
54
+
55
+ /** Check if an input field/textarea is active. Also checks shadow DOMs. */
56
+ export function isInputActive(doc = document) {
57
+ const activeEl = getActiveElement(doc);
58
+ return activeEl?.tagName == "INPUT" || activeEl?.tagName == "TEXTAREA";
59
+ }
60
+
43
61
  /**
44
62
  * @param {HTMLIFrameElement} iframe
45
63
  * @return {Document}
@@ -80,16 +98,17 @@ export function encodeURIComponentPlus(value) {
80
98
  }
81
99
 
82
100
  /**
101
+ * @template {Function} T
83
102
  * Returns a function, that, as long as it continues to be invoked, will not
84
103
  * be triggered. The function will be called after it stops being called for
85
104
  * N milliseconds. If `immediate` is passed, trigger the function on the
86
105
  * leading edge, instead of the trailing.
87
106
  * @see https://davidwalsh.name/javascript-debounce-function
88
107
  *
89
- * @param {Function} func
108
+ * @param {T} func
90
109
  * @param {number} wait
91
110
  * @param {boolean} immediate
92
- * @return {Function}
111
+ * @return {T}
93
112
  */
94
113
  export function debounce(func, wait, immediate) {
95
114
  let timeout;
@@ -108,12 +127,13 @@ export function debounce(func, wait, immediate) {
108
127
  }
109
128
 
110
129
  /**
130
+ * @template T
111
131
  * Throttle function
112
132
  * @see https://remysharp.com/2010/07/21/throttling-function-calls
113
- * @param {Function} fn
133
+ * @param {T} fn
114
134
  * @param {number} threshold
115
135
  * @param {boolean} delay
116
- * @return {Function}
136
+ * @return {T}
117
137
  */
118
138
  export function throttle(fn, threshold, delay) {
119
139
  threshold || (threshold = 250);
@@ -160,14 +180,6 @@ export function PolyfilledCustomEvent(eventName, {bubbles = false, cancelable =
160
180
  return event;
161
181
  }
162
182
 
163
- /**
164
- * Promise based sleep - resolves at default 500ms
165
- * @param {Number} wait time in milliseconds
166
- */
167
- export function sleep(ms = 500) {
168
- return new Promise(res => setTimeout(res(true), ms));
169
- }
170
-
171
183
  /*
172
184
  * Returns the number pixels something should be rendered at to be ~1n on the users
173
185
  * screen when measured with a ruler.
@@ -179,7 +191,50 @@ export function calcScreenDPI() {
179
191
  const dpi = el.offsetWidth;
180
192
  document.body.removeChild(el);
181
193
 
182
- const screenDPI = dpi * devicePixelRatio;
194
+ // Do you believe in magic... numbers? We tested on some devices, and the displayed
195
+ // size of `width: 1in` was less than desired. On @pezvi's mac, it was ~75% ; on
196
+ // @cdrini's laptop it was ~85%. Since we want to avoid things appearing too small,
197
+ // let's just use a multiplier of 1.25
198
+ const screenDPI = dpi * 1.25;
183
199
  // This will return 0 in testing; never want it to be 0!
184
200
  return screenDPI == 0 ? 100 : screenDPI;
185
201
  }
202
+
203
+ /**
204
+ * @param {number[]} nums
205
+ * @returns {number}
206
+ */
207
+ export function sum(nums) {
208
+ return nums.reduce((cur, acc) => cur + acc, 0);
209
+ }
210
+
211
+ /**
212
+ * @template T
213
+ * @param {Generator<T>} gen
214
+ * @returns {T[]}
215
+ */
216
+ export function genToArray(gen) {
217
+ const result = [];
218
+ for (const item of gen) {
219
+ result.push(item);
220
+ }
221
+ return result;
222
+ }
223
+
224
+ /**
225
+ * Check if arrays contain the same elements. Does reference comparison.
226
+ * @param {Array} arr1
227
+ * @param {Array} arr2
228
+ */
229
+ export function arrEquals(arr1, arr2) {
230
+ return arr1.length == arr2.length && arr1.every((x, i) => x == arr2[i]);
231
+ }
232
+
233
+ /**
234
+ * Check if array has changed; namely to be used with lit's property.hasChanged
235
+ * @param {Array} [arr1]
236
+ * @param {Array} [arr2]
237
+ */
238
+ export function arrChanged(arr1, arr2) {
239
+ return arr1 && arr2 && !arrEquals(arr1, arr2);
240
+ }