@internetarchive/bookreader 5.0.0-12-fixsorting-8 → 5.0.0-121

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 (420) hide show
  1. package/BookReader/474.js +2 -0
  2. package/BookReader/474.js.map +1 -0
  3. package/BookReader/BookReader.css +670 -1190
  4. package/BookReader/bergamot-translator-worker.js +2966 -0
  5. package/BookReader/bergamot-translator-worker.wasm +0 -0
  6. package/BookReader/hypothesis/LICENSE +50 -0
  7. package/BookReader/hypothesis/README.md +55 -0
  8. package/BookReader/hypothesis/build/boot-template.js +1 -0
  9. package/BookReader/hypothesis/build/boot.js +1 -0
  10. package/BookReader/hypothesis/build/manifest.json +20 -0
  11. package/BookReader/hypothesis/build/scripts/annotator.bundle.js +199 -0
  12. package/BookReader/hypothesis/build/scripts/annotator.bundle.js.map +1 -0
  13. package/BookReader/hypothesis/build/scripts/sidebar.bundle.js +811 -0
  14. package/BookReader/hypothesis/build/scripts/sidebar.bundle.js.map +1 -0
  15. package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js +437 -0
  16. package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js.map +1 -0
  17. package/BookReader/hypothesis/build/styles/annotator.css +4 -0
  18. package/BookReader/hypothesis/build/styles/annotator.css.map +1 -0
  19. package/BookReader/hypothesis/build/styles/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  20. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  21. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  22. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  23. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  24. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Bold.woff2 +0 -0
  25. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  26. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Italic.woff2 +0 -0
  27. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Regular.woff2 +0 -0
  28. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  29. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-Italic.woff2 +0 -0
  30. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  31. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  32. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  33. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Script-Regular.woff2 +0 -0
  34. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  35. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  36. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  37. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  38. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  39. package/BookReader/hypothesis/build/styles/highlights.css +2 -0
  40. package/BookReader/hypothesis/build/styles/highlights.css.map +1 -0
  41. package/BookReader/hypothesis/build/styles/katex.min.css +3 -0
  42. package/BookReader/hypothesis/build/styles/katex.min.css.map +1 -0
  43. package/BookReader/hypothesis/build/styles/pdfjs-overrides.css +14 -0
  44. package/BookReader/hypothesis/build/styles/pdfjs-overrides.css.map +1 -0
  45. package/BookReader/hypothesis/build/styles/sidebar.css +4 -0
  46. package/BookReader/hypothesis/build/styles/sidebar.css.map +1 -0
  47. package/BookReader/hypothesis/build/styles/ui-playground.css +4 -0
  48. package/BookReader/hypothesis/build/styles/ui-playground.css.map +1 -0
  49. package/BookReader/hypothesis/package.json +126 -0
  50. package/BookReader/ia-bookreader.js +1886 -0
  51. package/BookReader/{BookReader.js.LICENSE.txt → ia-bookreader.js.LICENSE.txt} +26 -20
  52. package/BookReader/ia-bookreader.js.map +1 -0
  53. package/BookReader/icons/1up.svg +1 -12
  54. package/BookReader/icons/2up.svg +1 -15
  55. package/BookReader/icons/advance.svg +3 -26
  56. package/BookReader/icons/chevron-right.svg +1 -1
  57. package/BookReader/icons/close-circle-dark.svg +1 -1
  58. package/BookReader/icons/close-circle.svg +1 -1
  59. package/BookReader/icons/fullscreen.svg +1 -17
  60. package/BookReader/icons/fullscreen_exit.svg +1 -17
  61. package/BookReader/icons/hamburger.svg +1 -15
  62. package/BookReader/icons/left-arrow.svg +1 -12
  63. package/BookReader/icons/magnify-minus.svg +1 -16
  64. package/BookReader/icons/magnify-plus.svg +1 -17
  65. package/BookReader/icons/magnify.svg +1 -15
  66. package/BookReader/icons/pause.svg +1 -23
  67. package/BookReader/icons/play.svg +1 -22
  68. package/BookReader/icons/playback-speed.svg +1 -34
  69. package/BookReader/icons/read-aloud.svg +1 -22
  70. package/BookReader/icons/review.svg +3 -22
  71. package/BookReader/icons/slider-toggle.svg +1 -0
  72. package/BookReader/icons/thumbnails.svg +1 -17
  73. package/BookReader/icons/voice.svg +1 -0
  74. package/BookReader/icons/volume-full.svg +1 -22
  75. package/BookReader/images/BRicons.svg +5 -94
  76. package/BookReader/images/books_graphic.svg +1 -177
  77. package/BookReader/images/hypothesis.ico +0 -0
  78. package/BookReader/images/icon_book.svg +1 -12
  79. package/BookReader/images/icon_bookmark.svg +1 -12
  80. package/BookReader/images/icon_experiment.svg +1 -0
  81. package/BookReader/images/icon_gear.svg +1 -14
  82. package/BookReader/images/icon_hamburger.svg +1 -20
  83. package/BookReader/images/icon_home.svg +1 -21
  84. package/BookReader/images/icon_info.svg +1 -11
  85. package/BookReader/images/icon_one_page.svg +1 -8
  86. package/BookReader/images/icon_pause.svg +1 -1
  87. package/BookReader/images/icon_play.svg +1 -1
  88. package/BookReader/images/icon_playback-rate.svg +1 -15
  89. package/BookReader/images/icon_search_button.svg +1 -8
  90. package/BookReader/images/icon_share.svg +1 -9
  91. package/BookReader/images/icon_skip-ahead.svg +1 -6
  92. package/BookReader/images/icon_skip-back.svg +2 -13
  93. package/BookReader/images/icon_speaker.svg +1 -18
  94. package/BookReader/images/icon_speaker_open.svg +1 -10
  95. package/BookReader/images/icon_thumbnails.svg +1 -12
  96. package/BookReader/images/icon_toc.svg +1 -5
  97. package/BookReader/images/icon_two_pages.svg +1 -9
  98. package/BookReader/images/marker_chap-off.svg +1 -11
  99. package/BookReader/images/marker_chap-on.svg +1 -11
  100. package/BookReader/images/marker_srch-on.svg +1 -11
  101. package/BookReader/images/translate.svg +1 -0
  102. package/BookReader/images/unviewable_page.png +0 -0
  103. package/BookReader/jquery-3.js +2 -0
  104. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  105. package/BookReader/plugins/plugin.archive_analytics.js +1 -172
  106. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  107. package/BookReader/plugins/plugin.autoplay.js +1 -165
  108. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  109. package/BookReader/plugins/plugin.chapters.js +19 -301
  110. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  111. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  112. package/BookReader/plugins/plugin.experiments.js +3 -0
  113. package/BookReader/plugins/plugin.experiments.js.LICENSE.txt +1 -0
  114. package/BookReader/plugins/plugin.experiments.js.map +1 -0
  115. package/BookReader/plugins/plugin.iframe.js +1 -74
  116. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  117. package/BookReader/plugins/plugin.iiif.js +2 -0
  118. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  119. package/BookReader/plugins/plugin.resume.js +1 -368
  120. package/BookReader/plugins/plugin.resume.js.map +1 -1
  121. package/BookReader/plugins/plugin.search.js +2 -1385
  122. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  123. package/BookReader/plugins/plugin.search.js.map +1 -1
  124. package/BookReader/plugins/plugin.text_selection.js +2 -1080
  125. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  126. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  127. package/BookReader/plugins/plugin.translate.js +3 -0
  128. package/BookReader/plugins/plugin.translate.js.LICENSE.txt +1 -0
  129. package/BookReader/plugins/plugin.translate.js.map +1 -0
  130. package/BookReader/plugins/plugin.tts.js +2 -9193
  131. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  132. package/BookReader/plugins/plugin.tts.js.map +1 -1
  133. package/BookReader/plugins/plugin.url.js +1 -269
  134. package/BookReader/plugins/plugin.url.js.map +1 -1
  135. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  136. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  137. package/BookReader/plugins/translator-worker.js +2 -0
  138. package/BookReader/plugins/translator-worker.js.map +1 -0
  139. package/BookReader/silence.mp3 +0 -0
  140. package/BookReader/translator-worker.js +475 -0
  141. package/BookReader/webcomponents-bundle.js +3 -0
  142. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  143. package/BookReader/webcomponents-bundle.js.map +1 -0
  144. package/README.md +17 -4
  145. package/dist/esm/ia-bookreader.js +3 -0
  146. package/dist/esm/ia-bookreader.js.LICENSE.txt +17 -0
  147. package/dist/esm/ia-bookreader.js.map +1 -0
  148. package/dist/esm/plugins/plugin.archive_analytics.js +2 -0
  149. package/dist/esm/plugins/plugin.archive_analytics.js.map +1 -0
  150. package/dist/esm/plugins/plugin.autoplay.js +2 -0
  151. package/dist/esm/plugins/plugin.autoplay.js.map +1 -0
  152. package/dist/esm/plugins/plugin.chapters.js +2 -0
  153. package/dist/esm/plugins/plugin.chapters.js.map +1 -0
  154. package/dist/esm/plugins/plugin.experiments.js +2 -0
  155. package/dist/esm/plugins/plugin.experiments.js.map +1 -0
  156. package/dist/esm/plugins/plugin.iframe.js +2 -0
  157. package/dist/esm/plugins/plugin.iframe.js.map +1 -0
  158. package/dist/esm/plugins/plugin.iiif.js +2 -0
  159. package/dist/esm/plugins/plugin.iiif.js.map +1 -0
  160. package/dist/esm/plugins/plugin.resume.js +2 -0
  161. package/dist/esm/plugins/plugin.resume.js.map +1 -0
  162. package/dist/esm/plugins/plugin.search.js +2 -0
  163. package/dist/esm/plugins/plugin.search.js.map +1 -0
  164. package/dist/esm/plugins/plugin.text_selection.js +2 -0
  165. package/dist/esm/plugins/plugin.text_selection.js.map +1 -0
  166. package/dist/esm/plugins/plugin.translate.js +2 -0
  167. package/dist/esm/plugins/plugin.translate.js.map +1 -0
  168. package/dist/esm/plugins/plugin.tts.js +2 -0
  169. package/dist/esm/plugins/plugin.tts.js.map +1 -0
  170. package/dist/esm/plugins/plugin.url.js +2 -0
  171. package/dist/esm/plugins/plugin.url.js.map +1 -0
  172. package/dist/esm/plugins/plugin.vendor-fullscreen.js +2 -0
  173. package/dist/esm/plugins/plugin.vendor-fullscreen.js.map +1 -0
  174. package/jsconfig.json +18 -0
  175. package/package.json +113 -71
  176. package/src/BookReader/BookModel.js +92 -46
  177. package/src/BookReader/DragScrollable.js +233 -0
  178. package/src/BookReader/ImageCache.js +49 -16
  179. package/src/BookReader/Mode1Up.js +66 -365
  180. package/src/BookReader/Mode1UpLit.js +392 -0
  181. package/src/BookReader/Mode2Up.js +87 -1315
  182. package/src/BookReader/Mode2UpLit.js +786 -0
  183. package/src/BookReader/ModeAbstract.js +43 -0
  184. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  185. package/src/BookReader/ModeSmoothZoom.js +312 -0
  186. package/src/BookReader/ModeThumb.js +29 -15
  187. package/src/BookReader/Navbar/Navbar.js +204 -84
  188. package/src/BookReader/PageContainer.js +120 -23
  189. package/src/BookReader/ReduceSet.js +2 -2
  190. package/src/BookReader/Toolbar/Toolbar.js +18 -40
  191. package/src/BookReader/events.js +3 -3
  192. package/src/BookReader/options.js +105 -18
  193. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  194. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  195. package/src/BookReader/utils/SelectionObserver.js +72 -0
  196. package/src/BookReader/utils/classes.js +1 -1
  197. package/src/BookReader/utils.js +136 -12
  198. package/src/BookReader.js +732 -1231
  199. package/src/BookReaderPlugin.js +52 -0
  200. package/src/assets/icons/magnify-minus.svg +3 -7
  201. package/src/assets/icons/magnify-plus.svg +3 -7
  202. package/src/assets/icons/slider-toggle.svg +1 -0
  203. package/src/assets/icons/voice.svg +1 -0
  204. package/src/assets/images/hypothesis.ico +0 -0
  205. package/src/assets/images/icon_experiment.svg +1 -0
  206. package/src/assets/images/translate.svg +1 -0
  207. package/src/assets/images/unviewable_page.png +0 -0
  208. package/src/assets/silence.mp3 +0 -0
  209. package/src/css/BookReader.scss +1 -5
  210. package/src/css/_BRBookmarks.scss +1 -1
  211. package/src/css/_BRComponent.scss +1 -1
  212. package/src/css/_BRicon.scss +8 -2
  213. package/src/css/_BRmain.scss +16 -3
  214. package/src/css/_BRnav.scss +74 -70
  215. package/src/css/_BRpages.scss +174 -42
  216. package/src/css/_BRsearch.scss +69 -30
  217. package/src/css/_BRtoolbar.scss +5 -5
  218. package/src/css/_TextSelection.scss +249 -25
  219. package/src/css/_colorbox.scss +2 -2
  220. package/src/css/_controls.scss +24 -7
  221. package/src/css/_icons.scss +8 -1
  222. package/src/{BookNavigator/assets → css}/button-base.js +2 -2
  223. package/src/css/icon_checkmark.js +9 -0
  224. package/src/css/sharedStyles.js +15 -0
  225. package/src/ia-bookreader/downloads/downloads-provider.js +81 -0
  226. package/src/{BookNavigator → ia-bookreader}/downloads/downloads.js +25 -5
  227. package/src/ia-bookreader/ia-bookreader.js +686 -0
  228. package/src/ia-bookreader/sharing.js +27 -0
  229. package/src/ia-bookreader/viewable-files.js +98 -0
  230. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments-provider.js +17 -17
  231. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments.js +75 -67
  232. package/src/{BookNavigator → plugins}/bookmarks/bookmark-button.js +4 -3
  233. package/src/{BookNavigator/assets → plugins/bookmarks}/bookmark-colors.js +1 -1
  234. package/src/{BookNavigator → plugins}/bookmarks/bookmark-edit.js +43 -31
  235. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-list.js +48 -49
  236. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-loginCTA.js +3 -3
  237. package/src/plugins/bookmarks/bookmarks-provider.js +63 -0
  238. package/src/{BookNavigator → plugins/bookmarks}/delete-modal-actions.js +1 -1
  239. package/src/{BookNavigator → plugins}/bookmarks/ia-bookmarks.js +117 -68
  240. package/src/plugins/plugin.archive_analytics.js +84 -78
  241. package/src/plugins/plugin.autoplay.js +99 -104
  242. package/src/plugins/plugin.chapters.js +323 -205
  243. package/src/plugins/plugin.experiments.js +375 -0
  244. package/src/plugins/plugin.iframe.js +36 -37
  245. package/src/plugins/plugin.iiif.js +147 -0
  246. package/src/plugins/plugin.resume.js +54 -51
  247. package/src/plugins/plugin.text_selection.js +464 -219
  248. package/src/plugins/plugin.vendor-fullscreen.js +5 -5
  249. package/src/plugins/search/plugin.search.js +347 -397
  250. package/src/{BookNavigator → plugins}/search/search-provider.js +59 -27
  251. package/src/{BookNavigator → plugins}/search/search-results.js +105 -76
  252. package/src/plugins/search/utils.js +50 -0
  253. package/src/plugins/search/view.js +51 -69
  254. package/src/plugins/translate/TranslationManager.js +164 -0
  255. package/src/plugins/translate/plugin.translate.js +559 -0
  256. package/src/plugins/tts/AbstractTTSEngine.js +78 -49
  257. package/src/plugins/tts/FestivalTTSEngine.js +20 -30
  258. package/src/plugins/tts/PageChunk.js +117 -23
  259. package/src/plugins/tts/PageChunkIterator.js +11 -17
  260. package/src/plugins/tts/WebTTSEngine.js +132 -92
  261. package/src/plugins/tts/plugin.tts.js +341 -351
  262. package/src/plugins/tts/utils.js +76 -67
  263. package/src/plugins/url/UrlPlugin.js +213 -0
  264. package/src/plugins/{plugin.url.js → url/plugin.url.js} +54 -18
  265. package/src/util/TextSelectionManager.js +1471 -0
  266. package/src/util/browserSniffing.js +42 -1
  267. package/src/util/cache.js +20 -0
  268. package/src/util/docCookies.js +21 -2
  269. package/src/util/dom.js +88 -0
  270. package/src/util/generators.js +89 -0
  271. package/src/util/lit.js +15 -0
  272. package/src/util/strings.js +11 -0
  273. package/.babelrc +0 -12
  274. package/.dependabot/config.yml +0 -6
  275. package/.eslintrc.js +0 -50
  276. package/.gitattributes +0 -2
  277. package/.github/ISSUE_TEMPLATE/bug.md +0 -32
  278. package/.github/ISSUE_TEMPLATE/feature-request.md +0 -30
  279. package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -15
  280. package/.github/workflows/node.js.yml +0 -37
  281. package/.github/workflows/npm-publish.yml +0 -47
  282. package/.testcaferc.json +0 -5
  283. package/BookReader/BookReader.js +0 -21613
  284. package/BookReader/BookReader.js.map +0 -1
  285. package/BookReader/bookreader-component-bundle.js +0 -14497
  286. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  287. package/BookReader/bookreader-component-bundle.js.map +0 -1
  288. package/BookReader/jquery-1.10.1.js +0 -108
  289. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  290. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  291. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  292. package/BookReader/plugins/plugin.mobile_nav.js +0 -335
  293. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  294. package/BookReaderDemo/BookReaderDemo.css +0 -41
  295. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -115
  296. package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
  297. package/BookReaderDemo/BookReaderJSSimple.js +0 -55
  298. package/BookReaderDemo/IIIFBookReader.js +0 -207
  299. package/BookReaderDemo/assets/v5/Bookreader-logo-cool-grad.svg +0 -1
  300. package/BookReaderDemo/assets/v5/Bookreader-logo-flat.svg +0 -1
  301. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-cool-grad.png +0 -0
  302. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-flat.png +0 -0
  303. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.png +0 -0
  304. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.svg +0 -1
  305. package/BookReaderDemo/assets/v5/Bookreader-logo-warm.svg +0 -1
  306. package/BookReaderDemo/assets/v5/bookreader-logo-renders@1x.png +0 -0
  307. package/BookReaderDemo/assets/v5/bookreader-logo-renders@2x.png +0 -0
  308. package/BookReaderDemo/assets/v5/bookreader-v5-screenshot.png +0 -0
  309. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  310. package/BookReaderDemo/demo-advanced.html +0 -33
  311. package/BookReaderDemo/demo-autoplay.html +0 -38
  312. package/BookReaderDemo/demo-embed-iframe-src.html +0 -84
  313. package/BookReaderDemo/demo-embed.html +0 -26
  314. package/BookReaderDemo/demo-fullscreen-mobile.html +0 -36
  315. package/BookReaderDemo/demo-fullscreen.html +0 -33
  316. package/BookReaderDemo/demo-iiif.html +0 -34
  317. package/BookReaderDemo/demo-iiif.js +0 -26
  318. package/BookReaderDemo/demo-internetarchive.html +0 -74
  319. package/BookReaderDemo/demo-multiple.html +0 -43
  320. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  321. package/BookReaderDemo/demo-preview-pages.html +0 -1092
  322. package/BookReaderDemo/demo-simple.html +0 -34
  323. package/BookReaderDemo/demo-vendor-fullscreen.html +0 -36
  324. package/BookReaderDemo/immersion-1up.html +0 -64
  325. package/BookReaderDemo/immersion-mode.html +0 -35
  326. package/BookReaderDemo/toggle_controls.html +0 -53
  327. package/BookReaderDemo/view_mode.html +0 -39
  328. package/BookReaderDemo/viewmode-cycle.html +0 -41
  329. package/CHANGELOG.md +0 -499
  330. package/CONTRIBUTING.md +0 -7
  331. package/codecov.yml +0 -17
  332. package/index.html +0 -31
  333. package/karma.conf.js +0 -23
  334. package/screenshot.png +0 -0
  335. package/scripts/postversion.js +0 -10
  336. package/scripts/preversion.js +0 -14
  337. package/scripts/version.js +0 -26
  338. package/src/BookNavigator/BookModel.js +0 -14
  339. package/src/BookNavigator/BookNavigator.js +0 -459
  340. package/src/BookNavigator/assets/book-loader.js +0 -27
  341. package/src/BookNavigator/assets/icon_checkmark.js +0 -6
  342. package/src/BookNavigator/assets/icon_close.js +0 -3
  343. package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
  344. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  345. package/src/BookNavigator/assets/icon_sort_neutral.js +0 -5
  346. package/src/BookNavigator/assets/icon_volumes.js +0 -11
  347. package/src/BookNavigator/bookmarks/bookmarks-provider.js +0 -53
  348. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  349. package/src/BookNavigator/downloads/downloads-provider.js +0 -66
  350. package/src/BookNavigator/search/a-search-result.js +0 -55
  351. package/src/BookNavigator/volumes/volumes-provider.js +0 -83
  352. package/src/BookNavigator/volumes/volumes.js +0 -178
  353. package/src/BookReader/DebugConsole.js +0 -54
  354. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  355. package/src/ItemNavigator/ItemNavigator.js +0 -376
  356. package/src/ItemNavigator/providers/sharing.js +0 -33
  357. package/src/css/_MobileNav.scss +0 -194
  358. package/src/dragscrollable-br.js +0 -261
  359. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  360. package/src/plugins/plugin.mobile_nav.js +0 -287
  361. package/tests/BookReader/BookModel.test.js +0 -312
  362. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  363. package/tests/BookReader/DebugConsole.test.js +0 -25
  364. package/tests/BookReader/ImageCache.test.js +0 -150
  365. package/tests/BookReader/Mode1Up.test.js +0 -164
  366. package/tests/BookReader/Mode2Up.test.js +0 -247
  367. package/tests/BookReader/Navbar/Navbar.test.js +0 -169
  368. package/tests/BookReader/PageContainer.test.js +0 -115
  369. package/tests/BookReader/ReduceSet.test.js +0 -38
  370. package/tests/BookReader/Toolbar/Toolbar.test.js +0 -26
  371. package/tests/BookReader/utils/classes.test.js +0 -88
  372. package/tests/BookReader/utils.test.js +0 -109
  373. package/tests/BookReader.options.test.js +0 -39
  374. package/tests/BookReader.test.js +0 -301
  375. package/tests/e2e/README.md +0 -75
  376. package/tests/e2e/autoplay.test.js +0 -13
  377. package/tests/e2e/base.test.js +0 -35
  378. package/tests/e2e/helpers/base.js +0 -263
  379. package/tests/e2e/helpers/debug.js +0 -13
  380. package/tests/e2e/helpers/desktopSearch.js +0 -72
  381. package/tests/e2e/helpers/mobileSearch.js +0 -85
  382. package/tests/e2e/helpers/mockSearch.js +0 -93
  383. package/tests/e2e/helpers/rightToLeft.js +0 -29
  384. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  385. package/tests/e2e/models/BookReader.js +0 -11
  386. package/tests/e2e/models/Navigation.js +0 -56
  387. package/tests/e2e/rightToLeft.test.js +0 -20
  388. package/tests/e2e/viewmode.test.js +0 -37
  389. package/tests/karma/BookNavigator/book-navigator.test.js +0 -139
  390. package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +0 -133
  391. package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +0 -222
  392. package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
  393. package/tests/karma/BookNavigator/search/search-results.test.js +0 -240
  394. package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +0 -40
  395. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  396. package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +0 -141
  397. package/tests/karma/BookNavigator/volumes/volumes.test.js +0 -98
  398. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  399. package/tests/plugins/plugin.archive_analytics.test.js +0 -23
  400. package/tests/plugins/plugin.autoplay.test.js +0 -52
  401. package/tests/plugins/plugin.chapters.test.js +0 -130
  402. package/tests/plugins/plugin.iframe.test.js +0 -42
  403. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  404. package/tests/plugins/plugin.resume.test.js +0 -98
  405. package/tests/plugins/plugin.text_selection.test.js +0 -203
  406. package/tests/plugins/plugin.url.test.js +0 -129
  407. package/tests/plugins/plugin.vendor-fullscreen.test.js +0 -65
  408. package/tests/plugins/search/plugin.search.test.js +0 -173
  409. package/tests/plugins/search/plugin.search.view.test.js +0 -106
  410. package/tests/plugins/tts/AbstractTTSEngine.test.js +0 -153
  411. package/tests/plugins/tts/FestivalTTSEngine.test.js +0 -59
  412. package/tests/plugins/tts/PageChunk.test.js +0 -57
  413. package/tests/plugins/tts/PageChunkIterator.test.js +0 -179
  414. package/tests/plugins/tts/WebTTSEngine.test.js +0 -126
  415. package/tests/plugins/tts/utils.test.js +0 -133
  416. package/tests/util/browserSniffing.test.js +0 -56
  417. package/tests/util/docCookies.test.js +0 -15
  418. package/tests/util/strings.test.js +0 -63
  419. package/tests/utils.js +0 -42
  420. package/webpack.config.js +0 -86
@@ -0,0 +1,559 @@
1
+ // @ts-check
2
+ import { css, html, LitElement } from 'lit';
3
+ import { BookReaderPlugin } from '../../BookReaderPlugin.js';
4
+ import { customElement, property, query } from 'lit/decorators.js';
5
+ import { TranslationManager } from "./TranslationManager.js";
6
+ import { toISO6391, toNativeName } from '../tts/utils.js';
7
+ import { sortBy } from '../../../src/BookReader/utils.js';
8
+ import { TextSelectionManager } from '../../../src/util/TextSelectionManager.js';
9
+ import '@internetarchive/ia-activity-indicator';
10
+
11
+ // @ts-ignore
12
+ const BookReader = /** @type {typeof import('@/src/BookReader.js').default} */(window.BookReader);
13
+
14
+ export class TranslatePlugin extends BookReaderPlugin {
15
+
16
+ options = {
17
+ enabled: true,
18
+
19
+ /** @type {string | import('lit').TemplateResult} */
20
+ panelDisclaimerText: "Translations are in alpha",
21
+ }
22
+
23
+ /** @type {TranslationManager} */
24
+ translationManager = new TranslationManager();
25
+
26
+ /** @type {Worker}*/
27
+ worker;
28
+
29
+ /**
30
+ * Contains the list of languages available to translate to
31
+ * @type {string[]}
32
+ */
33
+ toLanguages = [];
34
+
35
+ /**
36
+ * Current language code that is being translated From. Defaults to EN currently
37
+ * @type {!string}
38
+ */
39
+ langFromCode;
40
+
41
+ /**
42
+ * Current language code that is being translated To
43
+ * @type {!string}
44
+ */
45
+ langToCode;
46
+ /**
47
+ * @type {BrTranslatePanel} _panel - Represents a panel used in the plugin.
48
+ * The specific type and purpose of this panel should be defined based on its usage.
49
+ */
50
+ _panel;
51
+
52
+ /**
53
+ * @type {boolean} userToggleTranslate - Checks if user has initiated translation
54
+ * Should synchronize with the state of TranslationManager's active state
55
+ */
56
+ userToggleTranslate;
57
+
58
+ /**
59
+ * @type {boolean} loadingModel - Shows loading animation while downloading lang model
60
+ */
61
+ loadingModel = true;
62
+
63
+ textSelectionManager = new TextSelectionManager('.BRtranslateLayer', this.br, {selectionElement: [".BRlineElement"]}, 1);
64
+
65
+ async init() {
66
+ if (!this.options.enabled) return;
67
+
68
+ const bookLanguage = this.br.options.bookLanguage || '';
69
+ const currentLanguage = toISO6391(bookLanguage);
70
+ this.langFromCode = currentLanguage ?? "en";
71
+ this.textSelectionManager.init();
72
+ this.translationManager.publicPath = this.br.options.imagesBaseURL.replace(/\/+$/, '') + '/..';
73
+
74
+ /**
75
+ * @param {*} ev
76
+ * @param {object} eventProps
77
+ */
78
+ this.br.on('textLayerRendered', async (_, {pageIndex, pageContainer}) => {
79
+ // Stops invalid models from running, also prevents translation on page load
80
+ // TODO check if model has finished loading or if it exists
81
+ if (!this.translationManager) {
82
+ return;
83
+ }
84
+ if (this.translationManager.active) {
85
+ const pageElement = pageContainer.$container[0];
86
+ this.translateRenderedLayer(pageElement);
87
+ }
88
+ });
89
+
90
+ /**
91
+ * @param {*} ev
92
+ * @param {object} eventProps
93
+ */
94
+ this.br.on('pageVisible', (_, {pageContainerEl}) => {
95
+ if (!this.translationManager.active) {
96
+ return;
97
+ }
98
+ for (const paragraphEl of pageContainerEl.querySelectorAll('.BRtranslateLayer > .BRparagraphElement')) {
99
+ if (paragraphEl.textContent) {
100
+ this.fitVisiblePage(paragraphEl);
101
+ }
102
+ }
103
+ });
104
+
105
+ await this.translationManager.initWorker();
106
+ // Note await above lets _render function properly, since it gives the browser
107
+ // time to render the rest of bookreader, which _render depends on
108
+ this.langToCode = this.translationManager.toLanguages[0].code;
109
+ this._render();
110
+ }
111
+
112
+ /** @param {HTMLElement} page*/
113
+ getParagraphsOnPage = (page) => {
114
+ return page ? Array.from(page.querySelectorAll(".BRtextLayer > .BRparagraphElement")) : [];
115
+ }
116
+
117
+ translateActivePageContainerElements() {
118
+ const currentlyActiveContainers = this.br.getActivePageContainerElements();
119
+ const visiblePageContainers = currentlyActiveContainers.filter((element) => {
120
+ return element.classList.contains('BRpage-visible');
121
+ });
122
+ const hiddenPageContainers = currentlyActiveContainers.filter((element) => {
123
+ return !element.classList.contains('BRpage-visible');
124
+ });
125
+
126
+ for (const page of visiblePageContainers) {
127
+ this.translateRenderedLayer(page, 0);
128
+ }
129
+ for (const loadingPage of hiddenPageContainers) {
130
+ this.translateRenderedLayer(loadingPage, 1000);
131
+ }
132
+ }
133
+
134
+ /** @param {HTMLElement} page */
135
+ async translateRenderedLayer(page, priority) {
136
+ // Do not run translation if in thumbnail mode or if user did not initiate transations
137
+ if (this.br.mode == this.br.constModeThumb || !this.userToggleTranslate || this.langFromCode == this.langToCode) {
138
+ return;
139
+ }
140
+
141
+ const pageIndex = page.dataset.index;
142
+ let pageTranslationLayer;
143
+ if (!page.querySelector('.BRPageLayer.BRtranslateLayer')) {
144
+ pageTranslationLayer = document.createElement('div');
145
+ pageTranslationLayer.classList.add('BRPageLayer', 'BRtranslateLayer', 'BRtranslateLayerLoading');
146
+ pageTranslationLayer.setAttribute('lang', `${this.langToCode}`);
147
+ page.prepend(pageTranslationLayer);
148
+ } else {
149
+ pageTranslationLayer = page.querySelector('.BRPageLayer.BRtranslateLayer');
150
+ }
151
+ /** @type {HTMLElement} textLayerElement */
152
+ const textLayerElement = page.querySelector('.BRtextLayer');
153
+ // Should use native DOM element.style method instead of $().css method, specific issue with rendering / style calculation in Chrome
154
+ $(pageTranslationLayer).css({
155
+ "width": textLayerElement.style.width,
156
+ "height": textLayerElement.style.height,
157
+ "transform": textLayerElement.style.transform,
158
+ "pointer-events": textLayerElement.style.pointerEvents,
159
+ "z-index": 3,
160
+ });
161
+ textLayerElement.classList.add('showingTranslation');
162
+ const paragraphs = this.getParagraphsOnPage(page);
163
+
164
+ const paragraphTranslationPromises = paragraphs.map(async (paragraph, pidx) => {
165
+ let translatedParagraph = page.querySelector(`[data-translate-index='${pageIndex}-${pidx}']`);
166
+ if (!translatedParagraph) {
167
+ translatedParagraph = document.createElement('p');
168
+ // set data-translate-index on the placeholder
169
+ translatedParagraph.setAttribute('data-translate-index', `${pageIndex}-${pidx}`);
170
+ translatedParagraph.className = 'BRparagraphElement';
171
+ const originalParagraphStyle = paragraphs[pidx];
172
+ // check text selection paragraphs for header/footer roles
173
+ if (paragraph.classList.contains('ocr-role-header-footer')) {
174
+ translatedParagraph.ariaHidden = "true";
175
+ translatedParagraph.classList.add('ocr-role-header-footer');
176
+ }
177
+ const fontSize = `${parseInt($(originalParagraphStyle).css("font-size"))}px`;
178
+
179
+ $(translatedParagraph).css({
180
+ "margin-left": $(originalParagraphStyle).css("margin-left"),
181
+ "margin-top": $(originalParagraphStyle).css("margin-top"),
182
+ "top": $(originalParagraphStyle).css("top"),
183
+ "height": $(originalParagraphStyle).css("height"),
184
+ "width": $(originalParagraphStyle).css("width"),
185
+ "font-size": fontSize,
186
+ });
187
+ pageTranslationLayer.append(translatedParagraph);
188
+ }
189
+
190
+ if (paragraph.textContent.length !== 0) {
191
+ const pagePriority = parseFloat(pageIndex) + priority + pidx;
192
+ this.translationManager.getTranslationModel(this.langFromCode, this.langToCode).then(() => {
193
+ this._panel.loadingModel = false;
194
+ this.loadingModel = false;
195
+ });
196
+ const translatedText = await this.translationManager.getTranslation(this.langFromCode, this.langToCode, pageIndex, pidx, paragraph.textContent, pagePriority);
197
+ // prevent duplicate spans from appearing if exists
198
+ translatedParagraph.firstElementChild?.remove();
199
+
200
+ const firstWordSpacing = paragraphs[pidx]?.firstChild?.firstChild;
201
+ const createSpan = document.createElement('span');
202
+ createSpan.className = 'BRlineElement';
203
+ createSpan.textContent = translatedText;
204
+ translatedParagraph.appendChild(createSpan);
205
+
206
+ $(createSpan).css({
207
+ "text-indent": $(firstWordSpacing).css('padding-left'),
208
+ });
209
+ if (page.classList.contains('BRpage-visible')) {
210
+ this.fitVisiblePage(translatedParagraph);
211
+ }
212
+ }
213
+ });
214
+
215
+ this.textSelectionManager?.stopPageFlip(this.br.refs.$brContainer);
216
+ await Promise.all(paragraphTranslationPromises);
217
+ this.br.trigger('translateLayerRendered', {
218
+ leafIndex: pageIndex,
219
+ translateLayer: pageTranslationLayer,
220
+ });
221
+ }
222
+
223
+ /**
224
+ * Get the translation layers for a specific leaf index.
225
+ * @param {number} leafIndex
226
+ * @returns {Promise<HTMLElement[]>}
227
+ */
228
+ async getTranslateLayers(leafIndex) {
229
+ const pageContainerElements = this.br.getActivePageContainerElementsForIndex(leafIndex);
230
+ const translateLayer = $(pageContainerElements).filter(`[data-index='${leafIndex}']`).find('.BRtranslateLayer');
231
+ if (translateLayer.length) return translateLayer.toArray();
232
+
233
+ return new Promise((res, rej) => {
234
+ const handler = async (_, extraParams) => {
235
+ if (extraParams.leafIndex == leafIndex) {
236
+ this.br.off('translateLayerRendered', handler); // remember to detach translateLayer
237
+ res([extraParams.translateLayer]);
238
+ }
239
+ };
240
+ this.br.on('translateLayerRendered', handler);
241
+ });
242
+ }
243
+
244
+ clearAllTranslations() {
245
+ const brEl = this.br.refs.$br[0];
246
+ brEl.querySelectorAll('.BRtranslateLayer').forEach(el => el.remove());
247
+ brEl.querySelectorAll('.showingTranslation').forEach(el => el.classList.remove('showingTranslation'));
248
+ }
249
+
250
+ /**
251
+ * @param {Element} paragEl
252
+ */
253
+ fitVisiblePage(paragEl) {
254
+ const originalFontSize = parseInt($(paragEl).css("font-size"));
255
+ let adjustedFontSize = originalFontSize;
256
+ while (paragEl.clientHeight < paragEl.scrollHeight && adjustedFontSize > 0) {
257
+ adjustedFontSize--;
258
+ $(paragEl).css({ "font-size": `${adjustedFontSize}px` });
259
+ }
260
+
261
+ const textHeight = paragEl.firstElementChild.clientHeight;
262
+ const scrollHeight = paragEl.scrollHeight;
263
+ const fits = textHeight < scrollHeight;
264
+ if (fits) {
265
+ const lines = textHeight / adjustedFontSize;
266
+ // Line heights for smaller paragraphs occasionally need a minor adjustment
267
+ const newLineHeight = scrollHeight / lines;
268
+ $(paragEl).css({
269
+ "line-height" : `${newLineHeight}px`,
270
+ "overflow": "visible",
271
+ });
272
+ }
273
+ }
274
+
275
+ handleFromLangChange = async (e) => {
276
+ this.clearAllTranslations();
277
+ const selectedLangFrom = e.detail.value;
278
+
279
+ // Update the from language
280
+ this.langFromCode = selectedLangFrom;
281
+ this._panel.requestUpdate();
282
+
283
+ // Add 'From' language to 'To' list if not already present
284
+ if (!this.translationManager.toLanguages.some(lang => lang.code === selectedLangFrom)) {
285
+ this.translationManager.toLanguages.push({
286
+ code: selectedLangFrom,
287
+ name: this.translationManager.fromLanguages.find((entry) => entry.code == selectedLangFrom).name,
288
+ });
289
+ }
290
+
291
+ // Update the 'To' languages list and set the correct 'To' language
292
+ this._panel.toLanguages = this.translationManager.toLanguages;
293
+
294
+ console.log(this.langFromCode, this.langToCode);
295
+ this._render();
296
+ if (this.langFromCode !== this.langToCode) {
297
+ this.translateActivePageContainerElements();
298
+ }
299
+ }
300
+
301
+ handleToLangChange = async (e) => {
302
+ this.clearAllTranslations();
303
+ this.langToCode = e.detail.value;
304
+ this._render();
305
+ this.translateActivePageContainerElements();
306
+ }
307
+
308
+ handleToggleTranslation = async () => {
309
+ const shouldEnableTranslation = !this.userToggleTranslate;
310
+ if (shouldEnableTranslation && !this.isSourceLanguageSupported()) {
311
+ this.userToggleTranslate = false;
312
+ this.translationManager.active = false;
313
+ this._render();
314
+ return;
315
+ }
316
+
317
+ this.userToggleTranslate = !this.userToggleTranslate;
318
+ this.translationManager.active = this.userToggleTranslate;
319
+
320
+ this._render();
321
+ if (!this.userToggleTranslate) {
322
+ this.clearAllTranslations();
323
+ this.br.trigger('translationDisabled', { });
324
+ this.textSelectionManager.detach();
325
+ } else {
326
+ this.br.trigger('translationEnabled', { });
327
+ this.translateActivePageContainerElements();
328
+ this.textSelectionManager.attach();
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Update translation side menu
334
+ */
335
+ isSourceLanguageSupported() {
336
+ return this.translationManager.fromLanguages.some((lang) => lang.code == this.langFromCode);
337
+ }
338
+
339
+ _render() {
340
+ this.br.shell.menuProviders['translate'] = {
341
+ id: 'translate',
342
+ icon: html`<img src='${this.br.options.imagesBaseURL}/translate.svg' width="26"/>`,
343
+ label: 'Translate',
344
+ component: html`<br-translate-panel
345
+ @connected="${e => {
346
+ this._panel = e.target;
347
+ this._panel.fromLanguages = this.translationManager.fromLanguages;
348
+ this._panel.toLanguages = this.translationManager.toLanguages;
349
+ this._panel.userTranslationActive = this.userToggleTranslate;
350
+ this._panel.detectedToLang = this.langToCode;
351
+ this._panel.detectedFromLang = this.langFromCode;
352
+ this._panel.loadingModel = this.loadingModel;
353
+ this._panel.sourceLanguageSupported = this.isSourceLanguageSupported();
354
+ }
355
+ }"
356
+ @langFromChanged="${this.handleFromLangChange}"
357
+ @langToChanged="${this.handleToLangChange}"
358
+ @toggleTranslation="${this.handleToggleTranslation}"
359
+ .fromLanguages="${this.translationManager.fromLanguages}"
360
+ .toLanguages="${this.translationManager.toLanguages}"
361
+ .disclaimerMessage="${this.options.panelDisclaimerText}"
362
+ .userTranslationActive=${this.userToggleTranslate}
363
+ .detectedFromLang=${this.langFromCode}
364
+ .detectedToLang=${this.langToCode}
365
+ .loadingModel=${this.loadingModel}
366
+ .sourceLanguageSupported=${this.isSourceLanguageSupported()}
367
+ class="translate-panel"
368
+ />`,
369
+ };
370
+ this.br.shell.updateMenuContents();
371
+ }
372
+ }
373
+ BookReader?.registerPlugin('translate', TranslatePlugin);
374
+
375
+ @customElement('br-translate-panel')
376
+ export class BrTranslatePanel extends LitElement {
377
+ @property({ type: Array }) fromLanguages = []; // List of obj {code, name}
378
+ @property({ type: Array }) toLanguages = []; // List of obj {code, name}
379
+ @property({ type: String }) prevSelectedLang = ''; // Tracks the previous selected language for the "To" dropdown
380
+ @property({ type: String }) disclaimerMessage = '';
381
+ @property({ type: Boolean }) userTranslationActive = false;
382
+ @property({ type: String }) detectedFromLang = '';
383
+ @property({ type: String }) detectedToLang = '';
384
+ @property({ type: Boolean }) loadingModel;
385
+ @property({ type: Boolean }) sourceLanguageSupported = true;
386
+
387
+ @query('#lang-from') langFromDropdown;
388
+ @query('#lang-to') langToDropdown;
389
+
390
+ static styles = css`
391
+ .disclaimer {
392
+ padding: 10px;
393
+ border-radius: 8px;
394
+ font-size: 12px;
395
+ margin: 0 0 10px;
396
+ background-color: rgba(255, 255, 255, 0.1);
397
+ color: rgba(255, 255, 255, 0.9);
398
+ }
399
+
400
+ .disclaimer--warning {
401
+ margin: 10px 0;
402
+ background-color: rgba(255, 186, 8, 0.18);
403
+ color: #ffe7a3;
404
+ }
405
+ `;
406
+
407
+ connectedCallback() {
408
+ super.connectedCallback();
409
+ this.dispatchEvent(new CustomEvent('connected'));
410
+ }
411
+
412
+ render() {
413
+ const statusWarning = this._statusWarning();
414
+
415
+ return html`<div class="app" style="margin-top: 5%;padding-right: 5px;">
416
+ <div
417
+ class="disclaimer"
418
+ id="disclaimerMessage"
419
+ >${this.disclaimerMessage}</div>
420
+
421
+ <div class="panel panel--to" style="padding: 0 10px;">
422
+ <label>
423
+ <span style="font-size: 12px;color: #ccc;">Translate To</span>
424
+ <select id="lang-to" name="to" class="lang-select" style="display:block; width:100%;" @change="${this._onLangToChange}">
425
+ ${sortBy(this.toLanguages, ((lang) => lang.name.toLowerCase()
426
+ )).map((lang) => {
427
+ return html`<option value="${lang.code}"
428
+ ?selected=${lang.code == this.detectedToLang}
429
+ > ${lang.name ? lang.name : lang.code} </option>`;
430
+ })
431
+ }
432
+ </select>
433
+ </label>
434
+ </div>
435
+
436
+ <div class="panel panel--start" style="text-align: right;padding: 0 10px;/*! font-size: 18px; */margin-top: 10px;">
437
+ <button class="start-translation-brn" @click="${this._toggleTranslation}" ?disabled=${!this.sourceLanguageSupported}>
438
+ ${this.userTranslationActive ? "Stop Translating" : "Translate"}
439
+ </button>
440
+ </div>
441
+
442
+ <div class="panel panel--from" style="font-size: 12px;color: #ccc;text-align: center;padding: 8px 10px;">
443
+ <details style="display: contents">
444
+ <summary style="text-decoration: underline white; cursor:pointer; display:inline-block">
445
+ <i>
446
+ Source: ${this._getLangName(this.detectedFromLang)} ${this.prevSelectedLang ? "" : "(detected)"}
447
+ </i> Change
448
+ </summary>
449
+ <select id="lang-from" name="from" class="lang-select" value=${this.detectedFromLang} @change="${this._onLangFromChange}" style="width:65%; margin-top: 3%; margin-bottom: 3%">
450
+ ${sortBy(this.fromLanguages, ((lang) => lang.name.toLowerCase()
451
+ )).map((lang) => {
452
+ return html`<option value="${lang.code}"
453
+ ?selected=${lang.code == this.detectedFromLang}
454
+ >${lang.name ? lang.name : lang.code} </option>`;
455
+ })
456
+ }
457
+ </select>
458
+ </details>
459
+ </div>
460
+ <div class="footer" id="status" style="margin-top:5%">
461
+ ${
462
+ statusWarning ? html`
463
+ <div class="disclaimer disclaimer--warning">${statusWarning}</div>
464
+ ` : ''
465
+ }
466
+ </div>
467
+
468
+ <div class="lang-models-loading">
469
+ ${this._languageModelStatus()}
470
+ </div>
471
+ </div>`;
472
+ }
473
+ _onLangFromChange(event) {
474
+ const langFromChangedEvent = new CustomEvent('langFromChanged', {
475
+ detail: { value: event.target.value },
476
+ bubbles: true,
477
+ composed: true,
478
+ });
479
+ this.dispatchEvent(langFromChangedEvent);
480
+
481
+ // Update the prevSelectedLang if "To" is different from "From"
482
+ if (this._getSelectedLang('to') !== this._getSelectedLang('from')) {
483
+ this.prevSelectedLang = this._getSelectedLang('from');
484
+ }
485
+ this.loadingModel = true;
486
+ this.detectedFromLang = event.target.value;
487
+ }
488
+
489
+ _onLangToChange(event) {
490
+ const langToChangedEvent = new CustomEvent('langToChanged', {
491
+ detail: { value: event.target.value },
492
+ bubbles: true,
493
+ composed: true,
494
+ });
495
+ this.dispatchEvent(langToChangedEvent);
496
+
497
+ // Update the prevSelectedLang if "To" is different from "From"
498
+ if (this._getSelectedLang('from') !== event.target.value) {
499
+ this.prevSelectedLang = this._getSelectedLang('from');
500
+ }
501
+ this.loadingModel = true;
502
+ this.detectedToLang = event.target.value;
503
+ }
504
+
505
+ _getSelectedLang(type) {
506
+ const dropdown = type === 'from' ? this.langFromDropdown : this.langToDropdown;
507
+ return dropdown ? dropdown.value : '';
508
+ }
509
+
510
+ /**
511
+ * Resolves a human-readable language name for a language code.
512
+ * Prefers names from loaded translation language lists, then falls back to
513
+ * ISO language metadata via toNativeName.
514
+ * @param {string} code
515
+ * @returns {string}
516
+ */
517
+ _getLangName(code) {
518
+ const lang = [...this.fromLanguages, ...this.toLanguages]
519
+ .find(lang => lang.code === code);
520
+ return lang?.name || toNativeName(code) || code;
521
+ }
522
+
523
+ _toggleTranslation(event) {
524
+ if (!this.sourceLanguageSupported) {
525
+ return;
526
+ }
527
+ const toggleTranslateEvent = new CustomEvent('toggleTranslation', {
528
+ detail: {value: event.target.value},
529
+ bubbles: true,
530
+ composed:true,
531
+ });
532
+ this.userTranslationActive = !this.userTranslationActive;
533
+ this.dispatchEvent(toggleTranslateEvent);
534
+ }
535
+
536
+ // TODO: Hardcoded warning message for now but should add more statuses
537
+ _statusWarning() {
538
+ if (!this.sourceLanguageSupported) {
539
+ return `Translating from ${this._getLangName(this.detectedFromLang)} is not supported`;
540
+ }
541
+ if (this.detectedFromLang == this.detectedToLang) {
542
+ return "Translate To language is the same as the Source language";
543
+ }
544
+ return "";
545
+ }
546
+
547
+ _languageModelStatus() {
548
+ if (this.userTranslationActive) {
549
+ if (this.loadingModel) {
550
+ return html`
551
+ <ia-activity-indicator mode="processing" style="display:block; width: 40px; height: 40px; margin: 0 auto;"></ia-activity-indicator>
552
+ <p>Downloading language model</p>
553
+ `;
554
+ }
555
+ return html`<p>Language model loaded</p>`;
556
+ }
557
+ return "";
558
+ }
559
+ }