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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +78 -6
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +442 -1393
  6. package/BookReader/BookReader.js +2 -21564
  7. package/BookReader/BookReader.js.LICENSE.txt +20 -20
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1782 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +7 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/1up.svg +1 -12
  13. package/BookReader/icons/2up.svg +1 -15
  14. package/BookReader/icons/advance.svg +3 -26
  15. package/BookReader/icons/chevron-right.svg +1 -1
  16. package/BookReader/icons/close-circle-dark.svg +1 -0
  17. package/BookReader/icons/close-circle.svg +1 -1
  18. package/BookReader/icons/fullscreen.svg +1 -17
  19. package/BookReader/icons/fullscreen_exit.svg +1 -17
  20. package/BookReader/icons/hamburger.svg +1 -15
  21. package/BookReader/icons/left-arrow.svg +1 -12
  22. package/BookReader/icons/magnify-minus.svg +1 -16
  23. package/BookReader/icons/magnify-plus.svg +1 -17
  24. package/BookReader/icons/magnify.svg +1 -15
  25. package/BookReader/icons/pause.svg +1 -23
  26. package/BookReader/icons/play.svg +1 -22
  27. package/BookReader/icons/playback-speed.svg +1 -34
  28. package/BookReader/icons/read-aloud.svg +1 -22
  29. package/BookReader/icons/review.svg +3 -22
  30. package/BookReader/icons/thumbnails.svg +1 -17
  31. package/BookReader/icons/voice.svg +1 -0
  32. package/BookReader/icons/volume-full.svg +1 -22
  33. package/BookReader/images/BRicons.svg +5 -94
  34. package/BookReader/images/books_graphic.svg +1 -177
  35. package/BookReader/images/icon_book.svg +1 -12
  36. package/BookReader/images/icon_bookmark.svg +1 -12
  37. package/BookReader/images/icon_gear.svg +1 -14
  38. package/BookReader/images/icon_hamburger.svg +1 -20
  39. package/BookReader/images/icon_home.svg +1 -21
  40. package/BookReader/images/icon_info.svg +1 -11
  41. package/BookReader/images/icon_one_page.svg +1 -8
  42. package/BookReader/images/icon_pause.svg +1 -1
  43. package/BookReader/images/icon_play.svg +1 -1
  44. package/BookReader/images/icon_playback-rate.svg +1 -15
  45. package/BookReader/images/icon_search_button.svg +1 -8
  46. package/BookReader/images/icon_share.svg +1 -9
  47. package/BookReader/images/icon_skip-ahead.svg +1 -6
  48. package/BookReader/images/icon_skip-back.svg +2 -13
  49. package/BookReader/images/icon_speaker.svg +1 -18
  50. package/BookReader/images/icon_speaker_open.svg +1 -10
  51. package/BookReader/images/icon_thumbnails.svg +1 -12
  52. package/BookReader/images/icon_toc.svg +1 -5
  53. package/BookReader/images/icon_two_pages.svg +1 -9
  54. package/BookReader/images/marker_chap-off.svg +1 -11
  55. package/BookReader/images/marker_chap-on.svg +1 -11
  56. package/BookReader/images/marker_srch-on.svg +1 -11
  57. package/BookReader/jquery-3.js +2 -0
  58. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  59. package/BookReader/plugins/plugin.archive_analytics.js +1 -172
  60. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  61. package/BookReader/plugins/plugin.autoplay.js +1 -165
  62. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  63. package/BookReader/plugins/plugin.chapters.js +22 -301
  64. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  65. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  66. package/BookReader/plugins/plugin.iframe.js +1 -74
  67. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  68. package/BookReader/plugins/plugin.iiif.js +2 -0
  69. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  70. package/BookReader/plugins/plugin.resume.js +1 -368
  71. package/BookReader/plugins/plugin.resume.js.map +1 -1
  72. package/BookReader/plugins/plugin.search.js +2 -1420
  73. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  74. package/BookReader/plugins/plugin.search.js.map +1 -1
  75. package/BookReader/plugins/plugin.text_selection.js +2 -1080
  76. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  77. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  78. package/BookReader/plugins/plugin.tts.js +2 -9193
  79. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  80. package/BookReader/plugins/plugin.tts.js.map +1 -1
  81. package/BookReader/plugins/plugin.url.js +1 -269
  82. package/BookReader/plugins/plugin.url.js.map +1 -1
  83. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  84. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  85. package/BookReader/webcomponents-bundle.js +3 -0
  86. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  87. package/BookReader/webcomponents-bundle.js.map +1 -0
  88. package/BookReaderDemo/BookReaderDemo.css +18 -19
  89. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
  90. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  91. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  92. package/BookReaderDemo/IADemoBr.js +147 -0
  93. package/BookReaderDemo/demo-advanced.html +2 -2
  94. package/BookReaderDemo/demo-autoplay.html +2 -3
  95. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  96. package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
  97. package/BookReaderDemo/demo-fullscreen.html +2 -4
  98. package/BookReaderDemo/demo-iiif.html +99 -12
  99. package/BookReaderDemo/demo-internetarchive.html +214 -18
  100. package/BookReaderDemo/demo-multiple.html +2 -1
  101. package/BookReaderDemo/demo-preview-pages.html +2 -1
  102. package/BookReaderDemo/demo-simple.html +2 -1
  103. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -4
  104. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  105. package/BookReaderDemo/immersion-1up.html +2 -2
  106. package/BookReaderDemo/immersion-mode.html +2 -4
  107. package/BookReaderDemo/toggle_controls.html +3 -2
  108. package/BookReaderDemo/view_mode.html +2 -1
  109. package/BookReaderDemo/viewmode-cycle.html +2 -3
  110. package/CHANGELOG.md +545 -33
  111. package/README.md +14 -1
  112. package/babel.config.js +20 -0
  113. package/codecov.yml +6 -0
  114. package/index.html +4 -1
  115. package/jsconfig.json +19 -0
  116. package/netlify.toml +9 -0
  117. package/package.json +70 -60
  118. package/renovate.json +52 -0
  119. package/scripts/preversion.js +0 -1
  120. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  121. package/src/BookNavigator/assets/button-base.js +9 -2
  122. package/src/BookNavigator/assets/ia-logo.js +17 -0
  123. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  124. package/src/BookNavigator/assets/icon_close.js +1 -1
  125. package/src/BookNavigator/book-navigator.js +590 -0
  126. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  127. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  128. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  129. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  130. package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
  131. package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
  132. package/src/BookNavigator/delete-modal-actions.js +1 -1
  133. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  134. package/src/BookNavigator/downloads/downloads.js +41 -25
  135. package/src/BookNavigator/search/search-provider.js +80 -28
  136. package/src/BookNavigator/search/search-results.js +28 -25
  137. package/src/BookNavigator/sharing.js +27 -0
  138. package/src/BookNavigator/viewable-files.js +95 -0
  139. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  140. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  141. package/src/BookReader/BookModel.js +64 -34
  142. package/src/BookReader/DragScrollable.js +233 -0
  143. package/src/BookReader/Mode1Up.js +56 -351
  144. package/src/BookReader/Mode1UpLit.js +388 -0
  145. package/src/BookReader/Mode2Up.js +73 -1318
  146. package/src/BookReader/Mode2UpLit.js +776 -0
  147. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  148. package/src/BookReader/ModeSmoothZoom.js +312 -0
  149. package/src/BookReader/ModeThumb.js +18 -12
  150. package/src/BookReader/Navbar/Navbar.js +14 -40
  151. package/src/BookReader/PageContainer.js +81 -6
  152. package/src/BookReader/ReduceSet.js +1 -1
  153. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  154. package/src/BookReader/events.js +2 -3
  155. package/src/BookReader/options.js +27 -2
  156. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  157. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  158. package/src/BookReader/utils/SelectionObserver.js +45 -0
  159. package/src/BookReader/utils.js +118 -13
  160. package/src/BookReader.js +446 -1062
  161. package/src/assets/icons/close-circle-dark.svg +1 -0
  162. package/src/assets/icons/magnify-minus.svg +3 -7
  163. package/src/assets/icons/magnify-plus.svg +3 -7
  164. package/src/assets/icons/voice.svg +1 -0
  165. package/src/css/BookReader.scss +1 -17
  166. package/src/css/_BRBookmarks.scss +1 -1
  167. package/src/css/_BRComponent.scss +1 -1
  168. package/src/css/_BRmain.scss +33 -27
  169. package/src/css/_BRnav.scss +12 -39
  170. package/src/css/_BRpages.scss +149 -40
  171. package/src/css/_BRsearch.scss +68 -230
  172. package/src/css/_BRtoolbar.scss +5 -5
  173. package/src/css/_TextSelection.scss +87 -27
  174. package/src/css/_colorbox.scss +2 -2
  175. package/src/css/_controls.scss +20 -7
  176. package/src/css/_icons.scss +7 -1
  177. package/src/ia-bookreader/ia-bookreader.js +224 -0
  178. package/src/plugins/plugin.archive_analytics.js +3 -3
  179. package/src/plugins/plugin.autoplay.js +5 -11
  180. package/src/plugins/plugin.chapters.js +237 -191
  181. package/src/plugins/plugin.iiif.js +151 -0
  182. package/src/plugins/plugin.resume.js +3 -3
  183. package/src/plugins/plugin.text_selection.js +464 -134
  184. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  185. package/src/plugins/search/plugin.search.js +175 -120
  186. package/src/plugins/search/utils.js +43 -0
  187. package/src/plugins/search/view.js +64 -202
  188. package/src/plugins/tts/AbstractTTSEngine.js +71 -40
  189. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  190. package/src/plugins/tts/PageChunk.js +15 -21
  191. package/src/plugins/tts/PageChunkIterator.js +8 -12
  192. package/src/plugins/tts/WebTTSEngine.js +87 -71
  193. package/src/plugins/tts/plugin.tts.js +96 -127
  194. package/src/plugins/tts/utils.js +15 -25
  195. package/src/plugins/url/UrlPlugin.js +191 -0
  196. package/src/plugins/{plugin.url.js → url/plugin.url.js} +45 -16
  197. package/src/util/browserSniffing.js +22 -0
  198. package/src/util/docCookies.js +21 -2
  199. package/tests/e2e/README.md +37 -0
  200. package/tests/e2e/autoplay.test.js +2 -2
  201. package/tests/e2e/base.test.js +8 -16
  202. package/tests/e2e/helpers/base.js +53 -48
  203. package/tests/e2e/helpers/debug.js +1 -1
  204. package/tests/e2e/helpers/params.js +17 -0
  205. package/tests/e2e/helpers/rightToLeft.js +8 -14
  206. package/tests/e2e/helpers/search.js +73 -0
  207. package/tests/e2e/models/Navigation.js +20 -37
  208. package/tests/e2e/rightToLeft.test.js +4 -5
  209. package/tests/e2e/viewmode.test.js +40 -33
  210. package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
  211. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  212. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  213. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  214. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  215. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  216. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  217. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  218. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +109 -60
  219. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  220. package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
  221. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  222. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  223. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  224. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  225. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  226. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  227. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  228. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  229. package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
  230. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  231. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +10 -10
  232. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  233. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  234. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  235. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  236. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  237. package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
  238. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  239. package/tests/jest/BookReader/utils.test.js +229 -0
  240. package/tests/jest/BookReader.keyboard.test.js +190 -0
  241. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  242. package/tests/{BookReader.test.js → jest/BookReader.test.js} +26 -37
  243. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  244. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  245. package/tests/jest/plugins/plugin.chapters.test.js +195 -0
  246. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  247. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  248. package/tests/jest/plugins/plugin.text_selection.test.js +317 -0
  249. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  250. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +26 -47
  251. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +39 -6
  252. package/tests/jest/plugins/search/utils.js +25 -0
  253. package/tests/jest/plugins/search/utils.test.js +29 -0
  254. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +29 -9
  255. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  256. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  257. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  258. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  259. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  260. package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
  261. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +53 -14
  262. package/tests/jest/setup.js +3 -0
  263. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  264. package/tests/jest/util/docCookies.test.js +24 -0
  265. package/tests/{util → jest/util}/strings.test.js +1 -1
  266. package/tests/{utils.js → jest/utils.js} +38 -0
  267. package/webpack.config.js +12 -6
  268. package/.babelrc +0 -12
  269. package/.dependabot/config.yml +0 -6
  270. package/.testcaferc.json +0 -5
  271. package/BookReader/bookreader-component-bundle.js +0 -14312
  272. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  273. package/BookReader/bookreader-component-bundle.js.map +0 -1
  274. package/BookReader/icons/sort-ascending.svg +0 -1
  275. package/BookReader/icons/sort-descending.svg +0 -1
  276. package/BookReader/jquery-1.10.1.js +0 -108
  277. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  278. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  279. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  280. package/BookReader/plugins/plugin.mobile_nav.js +0 -335
  281. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  282. package/BookReaderDemo/IIIFBookReader.js +0 -207
  283. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  284. package/BookReaderDemo/demo-iiif.js +0 -26
  285. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  286. package/karma.conf.js +0 -23
  287. package/src/BookNavigator/BookModel.js +0 -14
  288. package/src/BookNavigator/BookNavigator.js +0 -452
  289. package/src/BookNavigator/assets/book-loader.js +0 -27
  290. package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
  291. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  292. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  293. package/src/BookNavigator/search/a-search-result.js +0 -55
  294. package/src/BookNavigator/volumes/volumes-provider.js +0 -76
  295. package/src/BookNavigator/volumes/volumes.js +0 -161
  296. package/src/BookReader/DebugConsole.js +0 -54
  297. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  298. package/src/ItemNavigator/ItemNavigator.js +0 -372
  299. package/src/ItemNavigator/providers/sharing.js +0 -29
  300. package/src/assets/icons/sort-ascending.svg +0 -1
  301. package/src/assets/icons/sort-descending.svg +0 -1
  302. package/src/css/_MobileNav.scss +0 -194
  303. package/src/dragscrollable-br.js +0 -261
  304. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  305. package/src/plugins/plugin.mobile_nav.js +0 -287
  306. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  307. package/tests/BookReader/DebugConsole.test.js +0 -25
  308. package/tests/BookReader/Mode1Up.test.js +0 -164
  309. package/tests/BookReader/Mode2Up.test.js +0 -247
  310. package/tests/BookReader/utils.test.js +0 -109
  311. package/tests/e2e/helpers/desktopSearch.js +0 -72
  312. package/tests/e2e/helpers/mobileSearch.js +0 -85
  313. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  314. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  315. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  316. package/tests/karma/BookNavigator/volumes.test.js +0 -101
  317. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  318. package/tests/plugins/plugin.chapters.test.js +0 -130
  319. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  320. package/tests/plugins/plugin.text_selection.test.js +0 -203
  321. package/tests/util/docCookies.test.js +0 -15
@@ -1,4 +1,4 @@
1
- import {PageContainer} from '../../src/BookReader/PageContainer.js';
1
+ import {PageContainer, boxToSVGRect, createSVGPageLayer, renderBoxesInPageContainerLayer} from '@/src/BookReader/PageContainer.js';
2
2
 
3
3
  describe('constructor', () => {
4
4
  test('protected books', () => {
@@ -31,7 +31,7 @@ describe('constructor', () => {
31
31
  });
32
32
  });
33
33
 
34
- describe('update', async () => {
34
+ describe('update', () => {
35
35
  test('dimensions sets CSS', () => {
36
36
  const pc = new PageContainer(null, {});
37
37
  pc.update({ dimensions: { left: 20 } });
@@ -62,17 +62,18 @@ describe('update', async () => {
62
62
  expect(pc.$img[0].style.background).toBe('');
63
63
  });
64
64
 
65
- test('removes image between updates', () => {
65
+ test('removes image between updates only if changed', () => {
66
66
  const fakeImageCache = {
67
67
  imageLoaded: () => true,
68
- image: () => $('<img/>'),
68
+ image: (index, reduce) => $(`<img src="page${index}-${reduce}.jpg" />`),
69
69
  };
70
70
  const pc = new PageContainer({index: 12}, {imageCache: fakeImageCache});
71
71
  pc.update({ reduce: 7 });
72
72
  const $im1 = pc.$img;
73
73
  pc.update({ reduce: 7 });
74
- const $im2 = pc.$img;
75
- expect($im1).not.toBe($im2);
74
+ expect(pc.$img).toBe($im1);
75
+ pc.update({ reduce: 16 });
76
+ expect(pc.$img).not.toBe($im1);
76
77
  expect($im1.parent().length).toBe(0);
77
78
  });
78
79
 
@@ -113,3 +114,84 @@ describe('update', async () => {
113
114
  expect(pc.$img.css('background')).toBeFalsy();
114
115
  });
115
116
  });
117
+
118
+ describe('createSVGPageLayer', () => {
119
+ test('Does what it says', () => {
120
+ const svg = createSVGPageLayer({ width: 100, height: 200}, 'myClass');
121
+ expect(svg.getAttribute('viewBox')).toBe('0 0 100 200');
122
+ expect(svg.getAttribute('class')).toContain('myClass');
123
+ });
124
+ });
125
+
126
+ describe('boxToSVGRect', () => {
127
+ test('Does what it says', () => {
128
+ const rect = boxToSVGRect({ l: 100, r: 200, t: 300, b: 500 });
129
+ expect(rect.getAttribute('x')).toBe('100');
130
+ expect(rect.getAttribute('y')).toBe('300');
131
+ expect(rect.getAttribute('width')).toBe('100');
132
+ expect(rect.getAttribute('height')).toBe('200');
133
+ });
134
+ });
135
+
136
+ describe('renderBoxesInPageContainerLayer', () => {
137
+ test('Handles missing layer', () => {
138
+ const container = document.createElement('div');
139
+ const page = { width: 100, height: 200 };
140
+ const boxes = [{l: 1, r: 2, t: 3, b: 4}];
141
+ renderBoxesInPageContainerLayer('foo', boxes, page, container);
142
+ expect(container.querySelector('.foo')).toBeTruthy();
143
+ expect(container.querySelectorAll('.foo rect').length).toBe(1);
144
+ });
145
+
146
+ test('Handles existing layer', () => {
147
+ const container = document.createElement('div');
148
+ const layer = document.createElement('svg');
149
+ layer.classList.add('foo');
150
+ container.append(layer);
151
+
152
+ const page = { width: 100, height: 200 };
153
+ const boxes = [{l: 1, r: 2, t: 3, b: 4}];
154
+ renderBoxesInPageContainerLayer('foo', boxes, page, container);
155
+ expect(container.querySelector('.foo')).toBe(layer);
156
+ expect(container.querySelectorAll('.foo rect').length).toBe(1);
157
+ });
158
+
159
+ test('Adds layer after image if it exists', () => {
160
+ const container = document.createElement('div');
161
+ const img = document.createElement('img');
162
+ img.classList.add('BRpageimage');
163
+ container.append(img);
164
+
165
+ const page = { width: 100, height: 200 };
166
+ const boxes = [{l: 1, r: 2, t: 3, b: 4}];
167
+ renderBoxesInPageContainerLayer('foo', boxes, page, container);
168
+ expect(container.querySelector('.foo')).toBeTruthy();
169
+ expect(container.children[0].getAttribute('class')).toBe('BRpageimage');
170
+ expect(container.children[1].getAttribute('class')).toBe('BRPageLayer foo');
171
+ });
172
+
173
+ test('Renders all boxes', () => {
174
+ const container = document.createElement('div');
175
+ const page = { width: 100, height: 200 };
176
+ const boxes = [{l: 1, r: 2, t: 3, b: 4}, {l: 1, r: 2, t: 3, b: 4}, {l: 1, r: 2, t: 3, b: 4}];
177
+ renderBoxesInPageContainerLayer('foo', boxes, page, container);
178
+ expect(container.querySelectorAll('.foo rect').length).toBe(3);
179
+ });
180
+
181
+ test('Adds optional classes', () => {
182
+ const container = document.createElement('div');
183
+ const page = { width: 100, height: 200 };
184
+ const boxes = [{l: 1, r: 2, t: 3, b: 4}, {l: 1, r: 2, t: 3, b: 4}, {l: 1, r: 2, t: 3, b: 4}];
185
+ renderBoxesInPageContainerLayer('foo', boxes, page, container, ['match-1', 'match-2', 'match-3']);
186
+ const rects = Array.from(container.querySelectorAll('.foo rect'));
187
+ expect(rects.map(r => r.getAttribute('class'))).toEqual(['match-1', 'match-2', 'match-3']);
188
+ });
189
+
190
+ test('Handles no boxes', () => {
191
+ const container = document.createElement('div');
192
+ const page = { width: 100, height: 200 };
193
+ const boxes = [];
194
+ renderBoxesInPageContainerLayer('foo', boxes, page, container);
195
+ expect(container.querySelectorAll('.foo rect').length).toBe(0);
196
+ });
197
+ });
@@ -1,4 +1,4 @@
1
- import {IntegerReduceSet, Pow2ReduceSet} from '../../src/BookReader/ReduceSet.js';
1
+ import {IntegerReduceSet, Pow2ReduceSet} from '@/src/BookReader/ReduceSet.js';
2
2
 
3
3
  describe('IntegerReduceSet', () => {
4
4
  test('floor', () => {
@@ -1,10 +1,10 @@
1
1
  import sinon from 'sinon';
2
2
 
3
- import { createPopup } from '../../../src/BookReader/Toolbar/Toolbar.js';
3
+ import { createPopup } from '@/src/BookReader/Toolbar/Toolbar.js';
4
4
 
5
5
  afterEach(() => {
6
6
  sinon.restore();
7
- })
7
+ });
8
8
 
9
9
  describe('createPopup', () => {
10
10
  test('calls window.open', () => {
@@ -0,0 +1,59 @@
1
+ // @ts-check
2
+ import sinon from 'sinon';
3
+ import { HTMLDimensionsCacher } from '@/src/BookReader/utils/HTMLDimensionsCacher';
4
+
5
+ describe('HTMLDimensionsCacher', () => {
6
+ test('Does not read from element directly', () => {
7
+ const element = document.createElement('div');
8
+ const getBoundingClientRectStub = element.getBoundingClientRect = sinon.stub().returns({ top: 10, left: 20 });
9
+ const clientWidthStub = sinon.stub().returns(300);
10
+ const clientHeightStub = sinon.stub().returns(500);
11
+ Object.defineProperty(element, 'clientWidth', { get: clientWidthStub });
12
+ Object.defineProperty(element, 'clientHeight', { get: clientHeightStub });
13
+
14
+ const hdc = new HTMLDimensionsCacher(element);
15
+ hdc.clientHeight;
16
+ expect(getBoundingClientRectStub.callCount).toBe(0);
17
+ expect(clientWidthStub.callCount).toBe(0);
18
+ expect(clientHeightStub.callCount).toBe(0);
19
+ });
20
+
21
+ test('Read from element when calling update', () => {
22
+ const element = document.createElement('div');
23
+ const getBoundingClientRectStub = element.getBoundingClientRect = sinon.stub().returns({ top: 10, left: 20 });
24
+ const clientWidthStub = sinon.stub().returns(300);
25
+ const clientHeightStub = sinon.stub().returns(500);
26
+ Object.defineProperty(element, 'clientWidth', { get: clientWidthStub });
27
+ Object.defineProperty(element, 'clientHeight', { get: clientHeightStub });
28
+
29
+ const hdc = new HTMLDimensionsCacher(element);
30
+ hdc.updateClientSizes();
31
+ expect(getBoundingClientRectStub.callCount).toBe(1);
32
+ expect(clientWidthStub.callCount).toBe(1);
33
+ expect(clientHeightStub.callCount).toBe(1);
34
+
35
+ expect(hdc.boundingClientRect).toEqual({ top: 10, left: 20 });
36
+ expect(hdc.clientWidth).toBe(300);
37
+ expect(hdc.clientHeight).toBe(500);
38
+ });
39
+
40
+ test('Does not listen for window resizes when not attached', () => {
41
+ const element = document.createElement('div');
42
+ const hdc = new HTMLDimensionsCacher(element);
43
+ const dummyWindow = new EventTarget();
44
+ const debouncedUpdateSpy = sinon.spy(hdc, 'debouncedUpdateClientSizes');
45
+
46
+ dummyWindow.dispatchEvent(new Event('resize', {}));
47
+ expect(debouncedUpdateSpy.callCount).toBe(0);
48
+
49
+ hdc.attachResizeListener(dummyWindow);
50
+
51
+ dummyWindow.dispatchEvent(new Event('resize', {}));
52
+ expect(debouncedUpdateSpy.callCount).toBe(1);
53
+
54
+ hdc.detachResizeListener(dummyWindow);
55
+
56
+ dummyWindow.dispatchEvent(new Event('resize', {}));
57
+ expect(debouncedUpdateSpy.callCount).toBe(1);
58
+ });
59
+ });
@@ -0,0 +1,49 @@
1
+ // @ts-check
2
+ import sinon from 'sinon';
3
+ import { ScrollClassAdder } from '@/src/BookReader/utils/ScrollClassAdder';
4
+
5
+ describe('ScrollClassAdder', () => {
6
+ test('Does not attach during construction', () => {
7
+ const element = document.createElement('div');
8
+ const attachSpy = sinon.spy(ScrollClassAdder.prototype, 'attach');
9
+ new ScrollClassAdder(element, 'foo');
10
+ expect(attachSpy.callCount).toBe(0);
11
+ });
12
+
13
+ test('Attach/detach call correct methods', () => {
14
+ const el = document.createElement('div');
15
+ const addEventListenerSpy = sinon.spy(el, 'addEventListener');
16
+ const removeEventListenerSpy = sinon.spy(el, 'removeEventListener');
17
+ const sca = new ScrollClassAdder(el, 'foo');
18
+ expect(addEventListenerSpy.callCount).toBe(0);
19
+ sca.attach();
20
+ expect(addEventListenerSpy.callCount).toBe(1);
21
+ sca.detach();
22
+ expect(removeEventListenerSpy.callCount).toBe(1);
23
+ });
24
+
25
+ test('onScroll adds class at right time', () => {
26
+ const clock = sinon.useFakeTimers();
27
+ const el = document.createElement('div');
28
+ const sca = new ScrollClassAdder(el, 'foo');
29
+ expect(el.getAttribute('class')).toBeFalsy();
30
+ sca.onScroll();
31
+ expect(el.getAttribute('class')).toBe('foo');
32
+ clock.tick(600);
33
+ expect(el.getAttribute('class')).toBeFalsy();
34
+
35
+ sca.onScroll();
36
+ expect(el.getAttribute('class')).toBe('foo');
37
+ clock.tick(500);
38
+ expect(el.getAttribute('class')).toBe('foo');
39
+ sca.onScroll();
40
+ expect(el.getAttribute('class')).toBe('foo');
41
+ clock.tick(100);
42
+ expect(el.getAttribute('class')).toBe('foo');
43
+ clock.tick(499);
44
+ expect(el.getAttribute('class')).toBe('foo');
45
+ clock.tick(1);
46
+ expect(el.getAttribute('class')).toBeFalsy();
47
+ clock.restore();
48
+ });
49
+ });
@@ -0,0 +1,57 @@
1
+ // @ts-check
2
+ import sinon from "sinon";
3
+ import { SelectionObserver } from "@/src/BookReader/utils/SelectionObserver";
4
+
5
+ afterEach(() => {
6
+ sinon.restore();
7
+ });
8
+
9
+ describe("SelectionObserver", () => {
10
+ test("_onSelectionChange", () => {
11
+ const handler = sinon.spy();
12
+ const observer = new SelectionObserver(".text-layer", handler);
13
+ const target = document.createElement("div");
14
+ target.classList.add("text-layer");
15
+
16
+ // stub window.getSelection
17
+ const getSelectionStub = sinon.stub(window, "getSelection");
18
+ getSelectionStub.returns({ toString: () => "test", anchorNode: target });
19
+ observer._onSelectionChange();
20
+ expect(handler.callCount).toBe(1);
21
+ expect(handler.calledWith("started", target)).toBe(true);
22
+ expect(observer.selecting).toBe(true);
23
+
24
+ // Calling it again does not call the handler again
25
+ observer._onSelectionChange();
26
+ expect(handler.callCount).toBe(1);
27
+
28
+ // Until the selection is cleared
29
+ getSelectionStub.returns({ toString: () => "", anchorNode: null });
30
+ expect(observer.selecting).toBe(true);
31
+ expect(handler.callCount).toBe(1);
32
+
33
+ observer._onSelectionChange();
34
+ expect(handler.callCount).toBe(2);
35
+ expect(handler.calledWith("cleared", target)).toBe(true);
36
+
37
+ // Calling it again does not call the handler again
38
+ sinon.restore();
39
+ sinon.stub(window, "getSelection").returns({ toString: () => "" });
40
+ observer._onSelectionChange();
41
+ expect(handler.callCount).toBe(2);
42
+ });
43
+
44
+ test('Only fires when selection started in selector', () => {
45
+ const handler = sinon.spy();
46
+ const observer = new SelectionObserver(".text-layer", handler);
47
+ const target = document.createElement("div");
48
+ target.classList.add("text-layer");
49
+
50
+ // stub window.getSelection
51
+ const getSelectionStub = sinon.stub(window, "getSelection");
52
+ getSelectionStub.returns({ toString: () => "test", anchorNode: document.body });
53
+ observer._onSelectionChange();
54
+ expect(handler.callCount).toBe(0);
55
+ expect(observer.selecting).toBe(false);
56
+ });
57
+ });
@@ -1,4 +1,4 @@
1
- import { exposeOverrideable } from '../../../src/BookReader/utils/classes.js';
1
+ import { exposeOverrideable } from '@/src/BookReader/utils/classes.js';
2
2
 
3
3
  describe('exposeOverrideable', () => {
4
4
  test('adds method to To class', () => {
@@ -0,0 +1,229 @@
1
+ import sinon from 'sinon';
2
+ import { afterEventLoop, eventTargetMixin } from '../utils.js';
3
+ import {
4
+ clamp,
5
+ cssPercentage,
6
+ debounce,
7
+ decodeURIComponentPlus,
8
+ encodeURIComponentPlus,
9
+ escapeHTML,
10
+ escapeRegExp,
11
+ getActiveElement,
12
+ isInputActive,
13
+ poll,
14
+ polyfillCustomEvent,
15
+ PolyfilledCustomEvent,
16
+ promisifyEvent,
17
+ sleep,
18
+ } from '@/src/BookReader/utils.js';
19
+
20
+ test('clamp function returns Math.min(Math.max(value, min), max)', () => {
21
+ expect(clamp(2,1,3)).toEqual(2);
22
+ });
23
+
24
+ test('calculate a percentage suitable for CSS', () => {
25
+ expect(cssPercentage(2,1)).toEqual('200%');
26
+ });
27
+
28
+ test('escapeHTML function which replaces the string', () => {
29
+ expect(escapeHTML('Me & You')).toEqual('Me &amp; You');
30
+ expect(escapeHTML('Me > You')).toEqual('Me &gt; You');
31
+ expect(escapeHTML('Me < You')).toEqual('Me &lt; You');
32
+ expect(escapeHTML('Me " You')).toEqual('Me &quot; You');
33
+ });
34
+
35
+ test('Decodes a URI component and converts + to emptyStr', () => {
36
+ expect(decodeURIComponentPlus("https%3A%2F%2Farchive.org%2Fskr+")).toEqual("https://archive.org/skr ");
37
+ expect(decodeURIComponentPlus("%3Fx%3D+test")).toEqual("?x= test");
38
+ });
39
+
40
+ test('Encodes a URI component and converts emptyStr to +', () => {
41
+ expect(encodeURIComponentPlus("?x=test ")).toEqual("%3Fx%3Dtest+");
42
+ expect(encodeURIComponentPlus("ABC abc 123")).toEqual("ABC+abc+123");
43
+ });
44
+
45
+ describe('getActiveElement', () => {
46
+ test('Can ignore shadow DOM', () => {
47
+ const doc = {activeElement: { shadowRoot: {activeElement: {}}}};
48
+ expect(getActiveElement(doc, false)).toBe(doc.activeElement);
49
+ });
50
+
51
+ test('Can traverse shadow DOM', () => {
52
+ const doc = {activeElement: { shadowRoot: {activeElement: {}}}};
53
+ expect(getActiveElement(doc, true)).toBe(doc.activeElement.shadowRoot.activeElement);
54
+ });
55
+
56
+ test('Handles non-shadow elements', () => {
57
+ const doc = {activeElement: {}};
58
+ expect(getActiveElement(doc, true)).toBe(doc.activeElement);
59
+ });
60
+
61
+ test('Handles no active element', () => {
62
+ const doc = {activeElement: null};
63
+ expect(getActiveElement(doc, true)).toBe(null);
64
+ });
65
+ });
66
+
67
+ describe('isInputActive', () => {
68
+ test('Handles no activeElement', () => {
69
+ expect(isInputActive({activeElement: null})).toBe(false);
70
+ });
71
+
72
+ test('Handles deep input activeElement', () => {
73
+ const doc = {activeElement: { shadowRoot: {activeElement: { tagName: 'INPUT' }}}};
74
+ expect(isInputActive(doc)).toBe(true);
75
+ });
76
+
77
+ test('Handles deep non-input activeElement', () => {
78
+ const doc = {activeElement: { shadowRoot: {activeElement: { tagName: 'A' }}}};
79
+ expect(isInputActive(doc)).toBe(false);
80
+ });
81
+
82
+ test('Handles textarea activeElement', () => {
83
+ const doc = {activeElement: { tagName: 'TEXTAREA' }};
84
+ expect(isInputActive(doc)).toBe(true);
85
+ });
86
+ });
87
+
88
+ describe('debounce', () => {
89
+ /** @type {sinon.SinonFakeTimers} */
90
+ let clock;
91
+ beforeEach(() => clock = sinon.useFakeTimers());
92
+ afterEach(() => clock.restore());
93
+
94
+ test('testing debounce', () => {
95
+ const func = jest.fn();
96
+ const debouncedFunc = debounce(func, 1000);
97
+ // Call it immediately
98
+ debouncedFunc();
99
+ expect(func).toHaveBeenCalledTimes(0); // func not called
100
+
101
+ // Call it several times with 500ms between each call
102
+ for (let i = 0; i < 10; i++) {
103
+ clock.tick(500);
104
+ debouncedFunc();
105
+ }
106
+ expect(func).toHaveBeenCalledTimes(0); // func not called
107
+
108
+ // wait 1000ms
109
+ clock.tick(1000);
110
+ expect(func).toHaveBeenCalledTimes(1); // func called
111
+ });
112
+ });
113
+
114
+
115
+ describe('polyfillCustomEvent', () => {
116
+ test('Overrides when missing', () => {
117
+ const win = {};
118
+ polyfillCustomEvent(win);
119
+ expect(win).toHaveProperty('CustomEvent');
120
+ });
121
+
122
+ test('Overrides when not a function', () => {
123
+ const win = { CustomEvent: {} };
124
+ polyfillCustomEvent(win);
125
+ expect(typeof win.CustomEvent).toBe('function');
126
+ });
127
+ });
128
+
129
+ describe('PolyfilledCustomEvent', () => {
130
+ test('Can be called as a constructor', () => {
131
+ new PolyfilledCustomEvent('foo');
132
+ });
133
+
134
+ test('Calls deprecated browser methods', () => {
135
+ const createEventSpy = sinon.spy(document, 'createEvent');
136
+ const initCustomEventSpy = sinon.spy(CustomEvent.prototype, 'initCustomEvent');
137
+ new PolyfilledCustomEvent('foo');
138
+ expect(createEventSpy.callCount).toBe(1);
139
+ expect(initCustomEventSpy.callCount).toBe(1);
140
+ });
141
+ });
142
+
143
+ describe('poll', () => {
144
+ beforeEach(() => jest.useFakeTimers());
145
+ afterEach(() => jest.useRealTimers());
146
+ test('polls until condition is true', async () => {
147
+ const fakeSleep = sinon.spy((ms) => jest.advanceTimersByTime(ms));
148
+
149
+ const returns = [null, null, 'foo'];
150
+ const check = sinon.spy(() => returns.shift());
151
+ const result = await poll(check, {_sleep: fakeSleep});
152
+ expect(fakeSleep.callCount).toBe(2);
153
+ expect(result).toBe('foo');
154
+ expect(check.callCount).toBe(3);
155
+ });
156
+
157
+ test('times out eventually', async () => {
158
+ const fakeSleep = sinon.spy((ms) => jest.advanceTimersByTime(ms));
159
+
160
+ const check = sinon.stub().returns(null);
161
+ const result = await poll(check, {_sleep: fakeSleep});
162
+ expect(result).toBeUndefined();
163
+ expect(check.callCount).toBe(10);
164
+ });
165
+ });
166
+
167
+ describe('sleep', () => {
168
+ test('Sleep 0 doest not called immediately', async () => {
169
+ const spy = sinon.spy();
170
+ sleep(0).then(spy);
171
+ expect(spy.callCount).toBe(0);
172
+ await afterEventLoop();
173
+ expect(spy.callCount).toBe(1);
174
+ });
175
+
176
+ test('Waits the appropriate ms', async () => {
177
+ const clock = sinon.useFakeTimers();
178
+ const spy = sinon.spy();
179
+ sleep(10).then(spy);
180
+ expect(spy.callCount).toBe(0);
181
+ clock.tick(10);
182
+ expect(spy.callCount).toBe(0);
183
+ clock.restore();
184
+
185
+ await afterEventLoop();
186
+ expect(spy.callCount).toBe(1);
187
+ });
188
+ });
189
+
190
+ describe('promisifyEvent', () => {
191
+ test('Resolves once event fires', async () => {
192
+ const fakeTarget = eventTargetMixin();
193
+ const resolveSpy = sinon.spy();
194
+ promisifyEvent(fakeTarget, 'pause').then(resolveSpy);
195
+
196
+ await afterEventLoop();
197
+ expect(resolveSpy.callCount).toBe(0);
198
+ fakeTarget.dispatchEvent('pause', {});
199
+ await afterEventLoop();
200
+ expect(resolveSpy.callCount).toBe(1);
201
+ });
202
+
203
+ test('Only resolves once', async () => {
204
+ const fakeTarget = eventTargetMixin();
205
+ const resolveSpy = sinon.spy();
206
+ promisifyEvent(fakeTarget, 'pause').then(resolveSpy);
207
+
208
+ await afterEventLoop();
209
+ expect(resolveSpy.callCount).toBe(0);
210
+ fakeTarget.dispatchEvent('pause', {});
211
+ fakeTarget.dispatchEvent('pause', {});
212
+ fakeTarget.dispatchEvent('pause', {});
213
+ fakeTarget.dispatchEvent('pause', {});
214
+
215
+ await afterEventLoop();
216
+ expect(resolveSpy.callCount).toBe(1);
217
+ });
218
+ });
219
+
220
+ describe('escapeRegex', () => {
221
+ test('Escapes regex', () => {
222
+ expect(escapeRegExp('.*')).toBe('\\.\\*');
223
+ expect(escapeRegExp('foo')).toBe('foo');
224
+ expect(escapeRegExp('foo.bar')).toBe('foo\\.bar');
225
+ expect(escapeRegExp('{{{')).toBe('\\{\\{\\{');
226
+ expect(escapeRegExp('')).toBe('');
227
+ expect(escapeRegExp('https://example.com')).toBe('https://example\\.com');
228
+ });
229
+ });