@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,75 +1,26 @@
1
1
  class SearchView {
2
2
  /**
3
3
  * @param {object} params
4
- * @param {string} params.selector A selector for the element that the search tray will be rendered in
5
- * @param {string} params.query An existing query string
6
- * @param {object} params.br The BookReader instance
4
+ * @param {object} params.br The BookReader instance
5
+ * @param {function} params.cancelSearch callback when a user wants to cancel search
7
6
  *
8
7
  * @event BookReader:SearchResultsCleared - when the search results nav gets cleared
9
8
  * @event BookReader:ToggleSearchMenu - when search results menu should toggle
10
9
  */
11
- constructor(params) {
12
- if (!params.selector) {
13
- console.warn('BookReader::Search - SearchView must be passed a valid CSS selector');
14
- return;
15
- }
16
-
17
- this.br = params.br;
18
-
19
- // Search results are returned as a text blob with the hits wrapped in
20
- // triple mustaches. Hits occasionally include text beyond the search
21
- // term, so everything within the staches is captured and wrapped.
22
- this.matcher = new RegExp('{{{(.+?)}}}', 'g');
10
+ constructor({ br, searchCancelledCallback = () => {} }) {
11
+ this.br = br;
23
12
  this.matches = [];
24
- this.cacheDOMElements(params.selector);
13
+ this.cacheDOMElements();
25
14
  this.bindEvents();
15
+ this.cancelSearch = searchCancelledCallback;
26
16
  }
27
17
 
28
- /**
29
- * @param {string} selector A selector for the element that the search tray will be rendered in
30
- */
31
- cacheDOMElements(selector) {
18
+ cacheDOMElements() {
32
19
  this.dom = {};
33
-
34
- // The parent search tray in mobile menu
35
- this.dom.searchTray = this.renderSearchTray(selector);
36
- // Container for rendered search results
37
- this.dom.results = this.dom.searchTray.querySelector('[data-id="results"]');
38
- // Element used to display number of results
39
- this.dom.resultsCount = this.dom.searchTray.querySelector('[data-id="results_count"]');
40
- // Search input within the mobile search tray
41
- this.dom.searchField = this.dom.searchTray.querySelector('[name="query"]');
42
- // Waiting indicator displayed while waiting for a search request
43
- this.dom.searchPending = this.dom.searchTray.querySelector('[data-id="searchPending"]');
44
- // The element added to the mobile menu that is animated into view when
45
- // the "search" nav item is clicked
46
- this.dom.mobileSearch = this.buildMobileDrawer();
47
20
  // Search input within the top toolbar. Will be removed once the mobile menu is replaced.
48
21
  this.dom.toolbarSearch = this.buildToolbarSearch();
49
22
  }
50
23
 
51
- /**
52
- * @param {boolean} bool
53
- */
54
- toggleSearchTray(bool = this.dom.searchTray.classList.contains('hidden')) {
55
- this.dom.searchTray.classList.toggle('hidden', !bool);
56
- }
57
-
58
- /**
59
- * @param {boolean} bool
60
- */
61
- toggleResultsCount(bool) {
62
- this.dom.resultsCount.classList.toggle('visible', bool);
63
- }
64
-
65
- /**
66
- * @param {SearchInsideResults} results
67
- */
68
- updateResultsCount(results) {
69
- this.dom.resultsCount.innerText = `(${results} result${results != 1 ? 's' : ''})`;
70
- this.toggleResultsCount(true);
71
- }
72
-
73
24
  /**
74
25
  * @param {string} query
75
26
  */
@@ -78,7 +29,6 @@ class SearchView {
78
29
  }
79
30
 
80
31
  emptyMatches() {
81
- this.dom.results.innerHTML = '';
82
32
  this.matches = [];
83
33
  }
84
34
 
@@ -86,48 +36,20 @@ class SearchView {
86
36
  this.br.$('.BRnavpos .BRsearch').remove();
87
37
  }
88
38
 
89
- clearSearchFieldAndResults() {
39
+ clearSearchFieldAndResults(dispatchEventWhenComplete = true) {
90
40
  this.br.removeSearchResults();
91
- this.toggleResultsCount(false);
92
41
  this.removeResultPins();
93
42
  this.emptyMatches();
94
43
  this.setQuery('');
95
44
  this.teardownSearchNavigation();
96
- this.br.trigger('SearchResultsCleared');
45
+ if (dispatchEventWhenComplete) {
46
+ this.br.trigger('SearchResultsCleared');
47
+ }
97
48
  }
98
49
 
99
50
  toggleSidebar() {
100
51
  this.br.trigger('ToggleSearchMenu');
101
52
  }
102
- /**
103
- * @param {string} selector The ID attribute to be used for the search tray
104
- */
105
- renderSearchTray(selector) {
106
- const searchTray = document.createElement('div');
107
- searchTray.setAttribute('id', selector.replace(/^#/, ''));
108
- searchTray.innerHTML = `
109
- <header>
110
- <div>
111
- <h3>Search inside</h3>
112
- <p data-id="results_count"></p>
113
- </div>
114
- <a href="#" class="close"></a>
115
- </header>
116
- <form action="" method="get">
117
- <fieldset>
118
- <input name="all_files" id="all_files" type="checkbox" />
119
- <label class="checkbox" for="all_files">Search all files</label>
120
- <input type="search" name="query" placeholder="Enter a search term" />
121
- </fieldset>
122
- </form>
123
- <div data-id="searchPending" id="search_pending">
124
- <p>Your search results will appear below</p>
125
- <div class="loader tc mt20"></div>
126
- </div>
127
- <ul data-id="results"></ul>
128
- `;
129
- return searchTray;
130
- }
131
53
 
132
54
  renderSearchNavigation() {
133
55
  const selector = 'BRsearch-navigation';
@@ -215,17 +137,19 @@ class SearchView {
215
137
  const start = pool.slice(0, pool.length / 2);
216
138
  const end = pool.slice(pool.length / 2);
217
139
  return closestTo((comparisonFn(start, end, comparator) ? start : end), comparator);
218
- }
140
+ };
219
141
 
220
142
  const closestPage = closestTo(matchPages, currentPage);
221
143
  return this.matches.indexOf(this.matches.find((m) => m.par[0].page === closestPage));
222
144
  }
223
145
 
224
146
  updateResultsPosition() {
147
+ if (!this.dom.searchNavigation) return;
225
148
  this.dom.searchNavigation.find('[data-id=resultsCount]').text(this.resultsPosition());
226
149
  }
227
150
 
228
151
  updateSearchNavigationButtons() {
152
+ if (!this.dom.searchNavigation) return;
229
153
  this.dom.searchNavigation.find('.prev').attr('disabled', !this.currentMatchIndex);
230
154
  this.dom.searchNavigation.find('.next').attr('disabled', this.currentMatchIndex + 1 === this.matches.length);
231
155
  }
@@ -272,19 +196,6 @@ class SearchView {
272
196
  this.updateSearchNavigationButtons();
273
197
  }
274
198
 
275
- /**
276
- * @param {array} matches
277
- */
278
- renderMatches(matches) {
279
- const items = matches.map((match) => `
280
- <li data-page="${match.par[0].page}" data-page-index="${this.br.leafNumToIndex(match.par[0].page)}">
281
- <h4>Page ${match.par[0].page}</h4>
282
- <p>${match.text.replace(this.matcher, '<mark>$1</mark>')}</p>
283
- </li>
284
- `);
285
- this.dom.results.innerHTML = items.join('');
286
- }
287
-
288
199
  /**
289
200
  * @param {boolean} bool
290
201
  */
@@ -293,23 +204,6 @@ class SearchView {
293
204
  this.br.refs.$BRfooter.find('.BRsearch').css({ visibility: pinsVisibleState });
294
205
  }
295
206
 
296
- buildMobileDrawer() {
297
- const mobileSearch = document.createElement('li');
298
- mobileSearch.innerHTML = `
299
- <span>
300
- <span class="DrawerIconWrapper">
301
- <img class="DrawerIcon" src="${this.br.imagesBaseURL}icon_search_button.svg" />
302
- </span>
303
- Search
304
- </span>
305
- <div data-id="search_slot">
306
- </div>
307
- `;
308
- mobileSearch.querySelector('[data-id="search_slot"]').appendChild(this.dom.searchTray);
309
- mobileSearch.classList.add('BRmobileMenu__search');
310
- return mobileSearch;
311
- }
312
-
313
207
  buildToolbarSearch() {
314
208
  const toolbarSearch = document.createElement('span');
315
209
  toolbarSearch.classList.add('BRtoolbarSection', 'BRtoolbarSectionSearch');
@@ -329,25 +223,20 @@ class SearchView {
329
223
  */
330
224
  renderPins(matches) {
331
225
  matches.forEach((match) => {
332
- const queryString = match.text;
333
- const pageIndex = this.br.leafNumToIndex(match.par[0].page);
334
- const pageNumber = this.br.getPageNum(pageIndex);
226
+ const pageIndex = this.br.book.leafNumToIndex(match.par[0].page);
335
227
  const uiStringSearch = "Search result"; // i18n
336
- const uiStringPage = "Page"; // i18n
337
-
338
- const percentThrough = this.br.constructor.util.cssPercentage(pageIndex, this.br.getNumLeafs() - 1);
339
-
340
- const queryStringWithB = queryString.replace(this.matcher, '<b>$1</b>');
341
-
342
- let queryStringWithBTruncated = '';
343
-
344
- if (queryString.length > 100) {
345
- queryStringWithBTruncated = queryString
346
- .replace(/^(.{100}[^\s]*).*/, "$1")
347
- .replace(this.matcher, '<b>$1</b>')
348
- + '...';
228
+ const percentThrough = this.br.constructor.util.cssPercentage(pageIndex, this.br.book.getNumLeafs() - 1);
229
+
230
+ let html = match.html;
231
+ if (html.length > 200) {
232
+ const start = Math.max(0, html.indexOf('<mark>') - 100);
233
+ if (start != 0) {
234
+ html = '' + match.html
235
+ .substring(start)
236
+ // Make sure at word boundary though
237
+ .replace(/^\S+/, '');
238
+ }
349
239
  }
350
-
351
240
  // draw marker
352
241
  $('<div>')
353
242
  .addClass('BRsearch')
@@ -357,35 +246,27 @@ class SearchView {
357
246
  .attr('title', uiStringSearch)
358
247
  .append(`
359
248
  <div class="BRquery">
360
- <div>${queryStringWithBTruncated || queryStringWithB}</div>
361
- <div>${uiStringPage} ${pageNumber}</div>
249
+ <main>${html}</main>
250
+ <footer>Page ${match.displayPageNumber}</footer>
362
251
  </div>
363
252
  `)
364
- .data({ pageIndex })
365
253
  .appendTo(this.br.$('.BRnavline'))
366
- .hover(
367
- (event) => {
368
- // remove from other markers then turn on just for this
369
- // XXX should be done when nav slider moves
370
- const marker = event.currentTarget;
371
- const tooltip = marker.querySelector('.BRquery');
372
- const tooltipOffset = tooltip.getBoundingClientRect();
373
- const targetOffset = marker.getBoundingClientRect();
374
- const boxSizeAdjust = parseInt(getComputedStyle(tooltip).paddingLeft) * 2;
375
- if (tooltipOffset.x - boxSizeAdjust < 0) {
376
- tooltip.style.setProperty('transform', `translateX(-${targetOffset.left - boxSizeAdjust}px)`);
377
- }
378
- $('.BRsearch,.BRchapter').removeClass('front');
379
- $(event.target).addClass('front');
380
- },
381
- (event) => $(event.target).removeClass('front'))
382
- .click(function (event) {
383
- // closures are nested and deep, using an arrow function breaks references.
384
- // Todo: update to arrow function & clean up closures
385
- // to remove `bind` dependency
386
- this.br._searchPluginGoToResult(+$(event.target).data('pageIndex'));
387
- this.br.updateSearchHilites();
388
- }.bind(this));
254
+ .on("mouseenter", (event) => {
255
+ // remove from other markers then turn on just for this
256
+ // XXX should be done when nav slider moves
257
+ const marker = event.currentTarget;
258
+ const tooltip = marker.querySelector('.BRquery');
259
+ const tooltipOffset = tooltip.getBoundingClientRect();
260
+ const targetOffset = marker.getBoundingClientRect();
261
+ const boxSizeAdjust = parseInt(getComputedStyle(tooltip).paddingLeft) * 2;
262
+ if (tooltipOffset.x - boxSizeAdjust < 0) {
263
+ tooltip.style.setProperty('transform', `translateX(-${targetOffset.left - boxSizeAdjust}px)`);
264
+ }
265
+ $('.BRsearch,.BRchapter').removeClass('front');
266
+ $(event.target).addClass('front');
267
+ })
268
+ .on("mouseleave", (event) => $(event.target).removeClass('front'))
269
+ .on("click", () => { this.br._searchPluginGoToResult(match.matchIndex); });
389
270
  });
390
271
  }
391
272
 
@@ -393,20 +274,28 @@ class SearchView {
393
274
  * @param {boolean} bool
394
275
  */
395
276
  toggleSearchPending(bool) {
396
- this.dom.searchPending.classList.toggle('visible', bool);
397
277
  if (bool) {
398
- this.br.showProgressPopup("Search results will appear below...");
278
+ this.br.showProgressPopup("Search results will appear below...", () => this.progressPopupClosed());
399
279
  }
400
280
  else {
401
281
  this.br.removeProgressPopup();
402
282
  }
403
283
  }
404
284
 
405
- renderErrorModal() {
285
+ /**
286
+ * Primary callback when user cancels search popup
287
+ */
288
+ progressPopupClosed() {
289
+ this.toggleSearchPending();
290
+ this.cancelSearch();
291
+ }
292
+
293
+ renderErrorModal(textIsProcessing = false) {
294
+ const errorDetails = `${!textIsProcessing ? 'The text may still be processing. ' : ''}Please try again.`;
406
295
  this.renderModalMessage(`
407
296
  Sorry, there was an error with your search.
408
297
  <br />
409
- The text may still be processing.
298
+ ${errorDetails}
410
299
  `);
411
300
  this.delayModalRemovalFor(4000);
412
301
  }
@@ -445,14 +334,6 @@ class SearchView {
445
334
  setTimeout(this.br.removeProgressPopup.bind(this.br), timeoutMS);
446
335
  }
447
336
 
448
- openMobileMenu() {
449
- this.br.refs.$mmenu.data('mmenu').open();
450
- }
451
-
452
- closeMobileMenu() {
453
- this.br.refs.$mmenu.data('mmenu').close();
454
- }
455
-
456
337
  /**
457
338
  * @param {Event} e
458
339
  */
@@ -461,7 +342,6 @@ class SearchView {
461
342
  const query = e.target.querySelector('[name="query"]').value;
462
343
  if (!query.length) { return false; }
463
344
  this.br.search(query);
464
- this.dom.searchField.blur();
465
345
  this.emptyMatches();
466
346
  this.toggleSearchPending(true);
467
347
  return false;
@@ -479,9 +359,7 @@ class SearchView {
479
359
  this.teardownSearchNavigation();
480
360
  this.renderSearchNavigation();
481
361
  this.bindSearchNavigationEvents();
482
- this.renderMatches(results.matches);
483
362
  this.renderPins(results.matches);
484
- this.updateResultsCount(results.matches.length);
485
363
  this.toggleSearchPending(false);
486
364
  if (options.goToFirstResult) {
487
365
  $(document).one('BookReader:pageChanged', () => {
@@ -492,15 +370,6 @@ class SearchView {
492
370
  }
493
371
  }
494
372
 
495
- /**
496
- * @param {Event} e
497
- */
498
- handleNavToggledCallback(e) {
499
- const is_visible = this.br.navigationIsVisible();
500
- this.togglePinsFor(is_visible);
501
- this.toggleSearchTray(is_visible ? !!this.dom.results.querySelector('li') : false);
502
- }
503
-
504
373
  handleSearchStarted() {
505
374
  this.emptyMatches();
506
375
  this.br.removeSearchHilites();
@@ -510,9 +379,14 @@ class SearchView {
510
379
  this.setQuery(this.br.searchTerm);
511
380
  }
512
381
 
513
- handleSearchCallbackError() {
382
+ /**
383
+ * Event listener for: `BookReader:SearchCallbackError`
384
+ * @param {CustomEvent} event
385
+ */
386
+ handleSearchCallbackError(event = {}) {
514
387
  this.toggleSearchPending(false);
515
- this.renderErrorModal();
388
+ const isIndexed = event?.detail?.props?.results?.indexed;
389
+ this.renderErrorModal(isIndexed);
516
390
  }
517
391
 
518
392
  handleSearchCallbackBookNotIndexed() {
@@ -528,26 +402,14 @@ class SearchView {
528
402
  bindEvents() {
529
403
  const namespace = 'BookReader:';
530
404
 
405
+ window.addEventListener(`${namespace}SearchCallbackError`, this.handleSearchCallbackError.bind(this));
531
406
  $(document).on(`${namespace}SearchCallback`, this.handleSearchCallback.bind(this))
532
- .on(`${namespace}navToggled`, this.handleNavToggledCallback.bind(this))
533
407
  .on(`${namespace}SearchStarted`, this.handleSearchStarted.bind(this))
534
- .on(`${namespace}SearchCallbackError`, this.handleSearchCallbackError.bind(this))
535
408
  .on(`${namespace}SearchCallbackBookNotIndexed`, this.handleSearchCallbackBookNotIndexed.bind(this))
536
409
  .on(`${namespace}SearchCallbackEmpty`, this.handleSearchCallbackEmpty.bind(this))
537
410
  .on(`${namespace}pageChanged`, this.updateSearchNavigation.bind(this));
538
411
 
539
- this.dom.searchTray.addEventListener('submit', this.submitHandler.bind(this));
540
412
  this.dom.toolbarSearch.querySelector('form').addEventListener('submit', this.submitHandler.bind(this));
541
- this.dom.searchField.addEventListener('search', () => {
542
- if (this.dom.searchField.value) { return; }
543
- this.clearSearchFieldAndResults();
544
- });
545
-
546
- $(this.dom.results).on('click', 'li', (e) => {
547
- this.br._searchPluginGoToResult(+e.currentTarget.dataset.pageIndex);
548
- this.br.updateSearchHilites();
549
- this.closeMobileMenu();
550
- });
551
413
  }
552
414
  }
553
415
 
@@ -1,4 +1,5 @@
1
1
  import PageChunkIterator from './PageChunkIterator.js';
2
+ import { hasLocalStorage } from './utils.js';
2
3
  /** @typedef {import('./utils.js').ISO6391} ISO6391 */
3
4
  /** @typedef {import('./PageChunk.js')} PageChunk */
4
5
 
@@ -28,6 +29,7 @@ import PageChunkIterator from './PageChunkIterator.js';
28
29
  * @property {() => void} resume
29
30
  * @property {() => void} finish force the sound to 'finish'
30
31
  * @property {number => void} setPlaybackRate
32
+ * @property {SpeechSynthesisVoice => void} setVoice
31
33
  **/
32
34
 
33
35
  /** Handling bookreader's text-to-speech */
@@ -50,9 +52,7 @@ export default class AbstractTTSEngine {
50
52
  /** @type {SpeechSynthesisVoice} */
51
53
  this.voice = null;
52
54
  // Listen for voice changes (fired by subclasses)
53
- this.events.on('voiceschanged', () => {
54
- this.voice = AbstractTTSEngine.getBestBookVoice(this.getVoices(), this.opts.bookLanguage);
55
- });
55
+ this.events.on('voiceschanged', this.updateBestVoice);
56
56
  this.events.trigger('voiceschanged');
57
57
  }
58
58
 
@@ -71,12 +71,17 @@ export default class AbstractTTSEngine {
71
71
  /** @abstract */
72
72
  init() { return null; }
73
73
 
74
+ updateBestVoice = () => {
75
+ this.voice = AbstractTTSEngine.getBestBookVoice(this.getVoices(), this.opts.bookLanguage);
76
+ }
77
+
74
78
  /**
75
79
  * @param {number} leafIndex
76
80
  * @param {number} numLeafs total number of leafs in the current book
77
81
  */
78
82
  start(leafIndex, numLeafs) {
79
83
  this.playing = true;
84
+ this.paused = false;
80
85
  this.opts.onLoadingStart();
81
86
 
82
87
  this._chunkIterator = new PageChunkIterator(numLeafs, leafIndex, {
@@ -92,6 +97,7 @@ export default class AbstractTTSEngine {
92
97
  stop() {
93
98
  if (this.activeSound) this.activeSound.stop();
94
99
  this.playing = false;
100
+ this.paused = true;
95
101
  this._chunkIterator = null;
96
102
  this.activeSound = null;
97
103
  this.events.trigger('stop');
@@ -124,13 +130,26 @@ export default class AbstractTTSEngine {
124
130
  }
125
131
 
126
132
  /** @public */
127
- jumpBackward() {
128
- Promise.all([
133
+ async jumpBackward() {
134
+ await Promise.all([
129
135
  this.activeSound.stop(),
130
136
  this._chunkIterator.decrement()
131
137
  .then(() => this._chunkIterator.decrement())
132
- ])
133
- .then(() => this.step());
138
+ ]);
139
+ this.step();
140
+ }
141
+
142
+ /** @param {string} voiceURI */
143
+ setVoice(voiceURI) {
144
+ // if the user actively selects a voice, don't re-choose best voice anymore
145
+ // MS Edge fires voices changed randomly very often
146
+ this.events.off('voiceschanged', this.updateBestVoice);
147
+ this.voice = this.getVoices().find(voice => voice.voiceURI === voiceURI);
148
+ // if the current book has a language set, store the selected voice with the book language as a suffix
149
+ if (this.opts.bookLanguage && hasLocalStorage()) {
150
+ localStorage.setItem(`BRtts-voice-${this.opts.bookLanguage}`, this.voice.voiceURI);
151
+ }
152
+ if (this.activeSound) this.activeSound.setVoice(this.voice);
134
153
  }
135
154
 
136
155
  /** @param {number} newRate */
@@ -140,36 +159,33 @@ export default class AbstractTTSEngine {
140
159
  }
141
160
 
142
161
  /** @private */
143
- step() {
144
- this._chunkIterator.next()
145
- .then(chunk => {
146
- if (chunk == PageChunkIterator.AT_END) {
147
- this.stop();
148
- this.opts.onDone();
149
- return;
150
- }
151
-
152
- this.opts.onLoadingStart();
153
- const sound = this.createSound(chunk);
154
- sound.chunk = chunk;
155
- sound.rate = this.playbackRate;
156
- sound.voice = this.voice;
157
- sound.load(() => this.opts.onLoadingComplete());
158
-
159
- this.opts.onLoadingComplete();
160
- return this.opts.beforeChunkPlay(chunk).then(() => sound);
161
- })
162
- .then(sound => {
163
- if (!this.playing) return;
164
-
165
- const playPromise = this.playSound(sound)
166
- .then(() => this.opts.afterChunkPlay(sound.chunk));
167
- if (this.paused) this.pause();
168
- return playPromise;
169
- })
170
- .then(() => {
171
- if (this.playing) return this.step();
172
- });
162
+ async step() {
163
+ const chunk = await this._chunkIterator.next();
164
+ if (chunk == PageChunkIterator.AT_END) {
165
+ this.stop();
166
+ this.opts.onDone();
167
+ return;
168
+ }
169
+ this.opts.onLoadingStart();
170
+ const sound = this.createSound(chunk);
171
+ sound.chunk = chunk;
172
+ sound.rate = this.playbackRate;
173
+ sound.voice = this.voice;
174
+ sound.load(() => this.opts.onLoadingComplete());
175
+
176
+ this.opts.onLoadingComplete();
177
+
178
+ await this.opts.beforeChunkPlay(chunk);
179
+
180
+ if (!this.playing) return;
181
+
182
+ const playPromise = await this.playSound(sound)
183
+ .then(()=> this.opts.afterChunkPlay(sound.chunk));
184
+
185
+ if (this.paused) this.pause();
186
+ await playPromise;
187
+
188
+ if (this.playing) return this.step();
173
189
  }
174
190
 
175
191
  /**
@@ -212,10 +228,12 @@ export default class AbstractTTSEngine {
212
228
  // user languages that match the book language
213
229
  const matchingUserLangs = userLanguages.filter(lang => lang.startsWith(bookLanguage));
214
230
 
215
- // Try to find voices that intersect these two sets
216
- return AbstractTTSEngine.getMatchingVoice(matchingUserLangs, bookLangVoices) ||
231
+ // First try to find the last chosen voice from localStorage for the current book language
232
+ return AbstractTTSEngine.getMatchingStoredVoice(bookLangVoices, bookLanguage)
233
+ // Try to find voices that intersect these two sets
234
+ || AbstractTTSEngine.getMatchingVoice(matchingUserLangs, bookLangVoices)
217
235
  // no user languages match the books; let's return the best voice for the book language
218
- (bookLangVoices.find(v => v.default) || bookLangVoices[0])
236
+ || (bookLangVoices.find(v => v.default) || bookLangVoices[0])
219
237
  // No voices match the book language? let's find a voice in the user's language
220
238
  // and ignore book lang
221
239
  || AbstractTTSEngine.getMatchingVoice(userLanguages, voices)
@@ -223,6 +241,19 @@ export default class AbstractTTSEngine {
223
241
  || (voices.find(v => v.default) || voices[0]);
224
242
  }
225
243
 
244
+ /**
245
+ * @private
246
+ * Get the voice last selected by the user for the book language from localStorage.
247
+ * Returns undefined if no voice is stored or found.
248
+ * @param {SpeechSynthesisVoice[]} voices browser voices to choose from
249
+ * @param {ISO6391} bookLanguage book language to look for
250
+ * @return {SpeechSynthesisVoice | undefined}
251
+ */
252
+ static getMatchingStoredVoice(voices, bookLanguage) {
253
+ const storedVoice = hasLocalStorage() && localStorage.getItem(`BRtts-voice-${bookLanguage}`);
254
+ return (storedVoice ? voices.find(v => v.voiceURI === storedVoice) : undefined);
255
+ }
256
+
226
257
  /**
227
258
  * @private
228
259
  * Get the best voice that matches one of the BCP47 languages (order by preference)
@@ -1,5 +1,5 @@
1
1
  import AbstractTTSEngine from './AbstractTTSEngine.js';
2
- import { sleep } from './utils.js';
2
+ import { sleep } from '../../BookReader/utils.js';
3
3
  /* global soundManager */
4
4
  import 'soundmanager2';
5
5
  import 'jquery.browser';
@@ -23,7 +23,7 @@ export default class FestivalTTSEngine extends AbstractTTSEngine {
23
23
  // $.browsers is sometimes undefined on some Android browsers :/
24
24
  // Likely related to when $.browser was moved to npm
25
25
  /** @type {'mp3' | 'ogg'} format of audio to get */
26
- this.audioFormat = $.browser?.mozilla ? 'ogg' : 'mp3';
26
+ this.audioFormat = $.browser?.mozilla ? 'ogg' : 'mp3'; //eslint-disable-line no-jquery/no-browser
27
27
  }
28
28
 
29
29
  /** @override */
@@ -91,10 +91,10 @@ export default class FestivalTTSEngine extends AbstractTTSEngine {
91
91
  * See https://stackoverflow.com/questions/12206631/html5-audio-cant-play-through-javascript-unless-triggered-manually-once
92
92
  * @return {PromiseLike}
93
93
  */
94
- iOSCaptureUserIntentHack() {
94
+ async iOSCaptureUserIntentHack() {
95
95
  const sound = soundManager.createSound({ url: SILENCE_1MS[this.audioFormat] });
96
- return new Promise(res => sound.play({onfinish: res}))
97
- .then(() => sound.destruct());
96
+ await new Promise(res => sound.play({onfinish: res}));
97
+ sound.destruct();
98
98
  }
99
99
  }
100
100
 
@@ -107,7 +107,7 @@ class FestivalTTSSound {
107
107
  this.sound = null;
108
108
  this.rate = 1;
109
109
  /** @type {function} calling this resolves the "play" promise */
110
- this._finishResolver = null
110
+ this._finishResolver = null;
111
111
  }
112
112
 
113
113
  get loaded() {
@@ -122,21 +122,20 @@ class FestivalTTSSound {
122
122
  if (this.rate != 1) this.sound.setPlaybackRate(this.rate);
123
123
  onload();
124
124
  },
125
- onresume: () => {
126
- sleep(25).then(() => {
127
- if (this.rate != 1) this.sound.setPlaybackRate(this.rate);
128
- });
125
+ onresume: async () => {
126
+ await sleep(25);
127
+ if (this.rate != 1) this.sound.setPlaybackRate(this.rate);
129
128
  }
130
129
  });
131
130
  return this.sound.load();
132
131
  }
133
132
 
134
- play() {
135
- return new Promise(res => {
133
+ async play() {
134
+ await new Promise(res => {
136
135
  this._finishResolver = res;
137
136
  this.sound.play({ onfinish: res });
138
- })
139
- .then(() => this.sound.destruct());
137
+ });
138
+ this.sound.destruct();
140
139
  }
141
140
 
142
141
  /** @override */