@internetarchive/elements 0.2.14-webdev-9022.0 → 0.3.0

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 (204) hide show
  1. package/dist/src/elements/ia-audio-element/ia-audio-element.d.ts +72 -0
  2. package/dist/src/elements/ia-audio-element/ia-audio-element.d.ts.map +1 -0
  3. package/dist/src/elements/ia-audio-element/ia-audio-element.js +173 -0
  4. package/dist/src/elements/ia-audio-element/ia-audio-element.js.map +1 -0
  5. package/dist/src/elements/ia-audio-element/models.d.ts +13 -0
  6. package/dist/src/elements/ia-audio-element/models.d.ts.map +1 -0
  7. package/dist/src/elements/ia-audio-element/models.js.map +1 -0
  8. package/dist/src/elements/ia-expandable-search-bar/assets/clear-results.d.ts +3 -0
  9. package/dist/src/elements/ia-expandable-search-bar/assets/clear-results.d.ts.map +1 -0
  10. package/dist/src/elements/ia-expandable-search-bar/assets/clear-results.js +28 -0
  11. package/dist/src/elements/ia-expandable-search-bar/assets/clear-results.js.map +1 -0
  12. package/dist/src/elements/ia-expandable-search-bar/assets/disclosure.d.ts +3 -0
  13. package/dist/src/elements/ia-expandable-search-bar/assets/disclosure.d.ts.map +1 -0
  14. package/dist/src/elements/ia-expandable-search-bar/assets/disclosure.js +26 -0
  15. package/dist/src/elements/ia-expandable-search-bar/assets/disclosure.js.map +1 -0
  16. package/dist/src/elements/ia-expandable-search-bar/assets/magnifying-glass.d.ts +3 -0
  17. package/dist/src/elements/ia-expandable-search-bar/assets/magnifying-glass.d.ts.map +1 -0
  18. package/dist/src/elements/ia-expandable-search-bar/assets/magnifying-glass.js +22 -0
  19. package/dist/src/elements/ia-expandable-search-bar/assets/magnifying-glass.js.map +1 -0
  20. package/dist/src/elements/ia-expandable-search-bar/ia-expandable-search-bar.d.ts +53 -0
  21. package/dist/src/elements/ia-expandable-search-bar/ia-expandable-search-bar.d.ts.map +1 -0
  22. package/dist/src/elements/ia-expandable-search-bar/ia-expandable-search-bar.js +352 -0
  23. package/dist/src/elements/ia-expandable-search-bar/ia-expandable-search-bar.js.map +1 -0
  24. package/dist/src/elements/ia-expandable-search-bar/ia-quick-search.d.ts +19 -0
  25. package/dist/src/elements/ia-expandable-search-bar/ia-quick-search.d.ts.map +1 -0
  26. package/dist/src/elements/ia-expandable-search-bar/ia-quick-search.js +115 -0
  27. package/dist/src/elements/ia-expandable-search-bar/ia-quick-search.js.map +1 -0
  28. package/dist/src/elements/ia-expandable-search-bar/models.d.ts +13 -0
  29. package/dist/src/elements/ia-expandable-search-bar/models.d.ts.map +1 -0
  30. package/dist/src/elements/ia-expandable-search-bar/models.js +2 -0
  31. package/dist/src/elements/ia-expandable-search-bar/models.js.map +1 -0
  32. package/dist/src/elements/ia-playback-controls/assets/next-section.d.ts +3 -0
  33. package/dist/src/elements/ia-playback-controls/assets/next-section.d.ts.map +1 -0
  34. package/dist/src/elements/ia-playback-controls/assets/next-section.js +16 -0
  35. package/dist/src/elements/ia-playback-controls/assets/next-section.js.map +1 -0
  36. package/dist/src/elements/ia-playback-controls/assets/pause.d.ts +3 -0
  37. package/dist/src/elements/ia-playback-controls/assets/pause.d.ts.map +1 -0
  38. package/dist/src/elements/ia-playback-controls/assets/pause.js +17 -0
  39. package/dist/src/elements/ia-playback-controls/assets/pause.js.map +1 -0
  40. package/dist/src/elements/ia-playback-controls/assets/play.d.ts +3 -0
  41. package/dist/src/elements/ia-playback-controls/assets/play.d.ts.map +1 -0
  42. package/dist/src/elements/ia-playback-controls/assets/play.js +18 -0
  43. package/dist/src/elements/ia-playback-controls/assets/play.js.map +1 -0
  44. package/dist/src/elements/ia-playback-controls/assets/playback-speed.d.ts +3 -0
  45. package/dist/src/elements/ia-playback-controls/assets/playback-speed.d.ts.map +1 -0
  46. package/dist/src/elements/ia-playback-controls/assets/playback-speed.js +28 -0
  47. package/dist/src/elements/ia-playback-controls/assets/playback-speed.js.map +1 -0
  48. package/dist/src/elements/ia-playback-controls/assets/previous-section.d.ts +3 -0
  49. package/dist/src/elements/ia-playback-controls/assets/previous-section.d.ts.map +1 -0
  50. package/dist/src/elements/ia-playback-controls/assets/previous-section.js +16 -0
  51. package/dist/src/elements/ia-playback-controls/assets/previous-section.js.map +1 -0
  52. package/dist/src/elements/ia-playback-controls/assets/replay.d.ts +3 -0
  53. package/dist/src/elements/ia-playback-controls/assets/replay.d.ts.map +1 -0
  54. package/dist/src/elements/ia-playback-controls/assets/replay.js +28 -0
  55. package/dist/src/elements/ia-playback-controls/assets/replay.js.map +1 -0
  56. package/dist/src/elements/ia-playback-controls/assets/skip-ahead.d.ts +3 -0
  57. package/dist/src/elements/ia-playback-controls/assets/skip-ahead.d.ts.map +1 -0
  58. package/dist/src/elements/ia-playback-controls/assets/skip-ahead.js +29 -0
  59. package/dist/src/elements/ia-playback-controls/assets/skip-ahead.js.map +1 -0
  60. package/dist/src/elements/ia-playback-controls/assets/volume-full.d.ts +3 -0
  61. package/dist/src/elements/ia-playback-controls/assets/volume-full.d.ts.map +1 -0
  62. package/dist/src/elements/ia-playback-controls/assets/volume-full.js +19 -0
  63. package/dist/src/elements/ia-playback-controls/assets/volume-full.js.map +1 -0
  64. package/dist/src/elements/ia-playback-controls/assets/volume-medium.d.ts +3 -0
  65. package/dist/src/elements/ia-playback-controls/assets/volume-medium.d.ts.map +1 -0
  66. package/dist/src/elements/ia-playback-controls/assets/volume-medium.js +24 -0
  67. package/dist/src/elements/ia-playback-controls/assets/volume-medium.js.map +1 -0
  68. package/dist/src/elements/ia-playback-controls/assets/volume-mute.d.ts +3 -0
  69. package/dist/src/elements/ia-playback-controls/assets/volume-mute.d.ts.map +1 -0
  70. package/dist/src/elements/ia-playback-controls/assets/volume-mute.js +35 -0
  71. package/dist/src/elements/ia-playback-controls/assets/volume-mute.js.map +1 -0
  72. package/dist/src/elements/ia-playback-controls/ia-playback-controls.d.ts +66 -0
  73. package/dist/src/elements/ia-playback-controls/ia-playback-controls.d.ts.map +1 -0
  74. package/dist/src/elements/ia-playback-controls/ia-playback-controls.js +337 -0
  75. package/dist/src/elements/ia-playback-controls/ia-playback-controls.js.map +1 -0
  76. package/dist/src/elements/ia-playback-controls/models.d.ts +13 -0
  77. package/dist/src/elements/ia-playback-controls/models.d.ts.map +1 -0
  78. package/dist/src/elements/ia-playback-controls/models.js +12 -0
  79. package/dist/src/elements/ia-playback-controls/models.js.map +1 -0
  80. package/dist/src/elements/ia-radio-player/assets/next-result.d.ts +3 -0
  81. package/dist/src/elements/ia-radio-player/assets/next-result.d.ts.map +1 -0
  82. package/dist/src/elements/ia-radio-player/assets/next-result.js +27 -0
  83. package/dist/src/elements/ia-radio-player/assets/next-result.js.map +1 -0
  84. package/dist/src/elements/ia-radio-player/assets/previous-result.d.ts +3 -0
  85. package/dist/src/elements/ia-radio-player/assets/previous-result.d.ts.map +1 -0
  86. package/dist/src/elements/ia-radio-player/assets/previous-result.js +27 -0
  87. package/dist/src/elements/ia-radio-player/assets/previous-result.js.map +1 -0
  88. package/dist/src/elements/ia-radio-player/ia-radio-player.d.ts +130 -0
  89. package/dist/src/elements/ia-radio-player/ia-radio-player.d.ts.map +1 -0
  90. package/dist/src/elements/ia-radio-player/ia-radio-player.js +748 -0
  91. package/dist/src/elements/ia-radio-player/ia-radio-player.js.map +1 -0
  92. package/dist/src/elements/ia-radio-player/ia-search-results-switcher.d.ts +22 -0
  93. package/dist/src/elements/ia-radio-player/ia-search-results-switcher.d.ts.map +1 -0
  94. package/dist/src/elements/ia-radio-player/ia-search-results-switcher.js +137 -0
  95. package/dist/src/elements/ia-radio-player/ia-search-results-switcher.js.map +1 -0
  96. package/dist/src/elements/ia-radio-player/models.d.ts +29 -0
  97. package/dist/src/elements/ia-radio-player/models.d.ts.map +1 -0
  98. package/dist/src/elements/ia-radio-player/models.js +2 -0
  99. package/dist/src/elements/ia-radio-player/models.js.map +1 -0
  100. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-backend.d.ts +38 -0
  101. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-backend.d.ts.map +1 -0
  102. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-backend.js +64 -0
  103. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-backend.js.map +1 -0
  104. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-response.d.ts +94 -0
  105. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-response.d.ts.map +1 -0
  106. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-response.js +56 -0
  107. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-response.js.map +1 -0
  108. package/dist/src/elements/ia-radio-player/search/backends/local-search-backend.d.ts +15 -0
  109. package/dist/src/elements/ia-radio-player/search/backends/local-search-backend.d.ts.map +1 -0
  110. package/dist/src/elements/ia-radio-player/search/backends/local-search-backend.js +26 -0
  111. package/dist/src/elements/ia-radio-player/search/backends/local-search-backend.js.map +1 -0
  112. package/dist/src/elements/ia-radio-player/search/backends/search-backend.d.ts +12 -0
  113. package/dist/src/elements/ia-radio-player/search/backends/search-backend.d.ts.map +1 -0
  114. package/dist/src/elements/ia-radio-player/search/backends/search-backend.js +2 -0
  115. package/dist/src/elements/ia-radio-player/search/backends/search-backend.js.map +1 -0
  116. package/dist/src/elements/ia-radio-player/search/models.d.ts +38 -0
  117. package/dist/src/elements/ia-radio-player/search/models.d.ts.map +1 -0
  118. package/dist/src/elements/ia-radio-player/search/models.js +17 -0
  119. package/dist/src/elements/ia-radio-player/search/models.js.map +1 -0
  120. package/dist/src/elements/ia-radio-player/search/search-handler.d.ts +49 -0
  121. package/dist/src/elements/ia-radio-player/search/search-handler.d.ts.map +1 -0
  122. package/dist/src/elements/ia-radio-player/search/search-handler.js +128 -0
  123. package/dist/src/elements/ia-radio-player/search/search-handler.js.map +1 -0
  124. package/dist/src/elements/ia-radio-player/search/search-helper.d.ts +9 -0
  125. package/dist/src/elements/ia-radio-player/search/search-helper.d.ts.map +1 -0
  126. package/dist/src/elements/ia-radio-player/search/search-helper.js +15 -0
  127. package/dist/src/elements/ia-radio-player/search/search-helper.js.map +1 -0
  128. package/dist/src/elements/ia-radio-player/search/transcript-index.d.ts +45 -0
  129. package/dist/src/elements/ia-radio-player/search/transcript-index.d.ts.map +1 -0
  130. package/dist/src/elements/ia-radio-player/search/transcript-index.js +48 -0
  131. package/dist/src/elements/ia-radio-player/search/transcript-index.js.map +1 -0
  132. package/dist/src/elements/ia-scrubber-bar/assets/next-section-marker.d.ts +3 -0
  133. package/dist/src/elements/ia-scrubber-bar/assets/next-section-marker.d.ts.map +1 -0
  134. package/dist/src/elements/ia-scrubber-bar/assets/next-section-marker.js +18 -0
  135. package/dist/src/elements/ia-scrubber-bar/assets/next-section-marker.js.map +1 -0
  136. package/dist/src/elements/ia-scrubber-bar/assets/previous-section-marker.d.ts +3 -0
  137. package/dist/src/elements/ia-scrubber-bar/assets/previous-section-marker.d.ts.map +1 -0
  138. package/dist/src/elements/ia-scrubber-bar/assets/previous-section-marker.js +18 -0
  139. package/dist/src/elements/ia-scrubber-bar/assets/previous-section-marker.js.map +1 -0
  140. package/dist/src/elements/ia-scrubber-bar/ia-scrubber-bar.d.ts +82 -0
  141. package/dist/src/elements/ia-scrubber-bar/ia-scrubber-bar.d.ts.map +1 -0
  142. package/dist/src/elements/ia-scrubber-bar/ia-scrubber-bar.js +387 -0
  143. package/dist/src/elements/ia-scrubber-bar/ia-scrubber-bar.js.map +1 -0
  144. package/dist/src/elements/ia-scrubber-bar/ia-section-marker.d.ts +16 -0
  145. package/dist/src/elements/ia-scrubber-bar/ia-section-marker.d.ts.map +1 -0
  146. package/dist/src/elements/ia-scrubber-bar/ia-section-marker.js +109 -0
  147. package/dist/src/elements/ia-scrubber-bar/ia-section-marker.js.map +1 -0
  148. package/dist/src/elements/ia-scrubber-bar/models.d.ts +21 -0
  149. package/dist/src/elements/ia-scrubber-bar/models.d.ts.map +1 -0
  150. package/dist/src/elements/ia-scrubber-bar/models.js +20 -0
  151. package/dist/src/elements/ia-scrubber-bar/models.js.map +1 -0
  152. package/dist/src/elements/ia-transcript-view/duration-formatter.d.ts +13 -0
  153. package/dist/src/elements/ia-transcript-view/duration-formatter.d.ts.map +1 -0
  154. package/dist/src/elements/ia-transcript-view/duration-formatter.js +25 -0
  155. package/dist/src/elements/ia-transcript-view/duration-formatter.js.map +1 -0
  156. package/dist/src/elements/ia-transcript-view/ia-transcript-entry.d.ts +25 -0
  157. package/dist/src/elements/ia-transcript-view/ia-transcript-entry.d.ts.map +1 -0
  158. package/dist/src/elements/ia-transcript-view/ia-transcript-entry.js +51 -0
  159. package/dist/src/elements/ia-transcript-view/ia-transcript-entry.js.map +1 -0
  160. package/dist/src/elements/ia-transcript-view/ia-transcript-view.d.ts +87 -0
  161. package/dist/src/elements/ia-transcript-view/ia-transcript-view.d.ts.map +1 -0
  162. package/dist/src/elements/ia-transcript-view/ia-transcript-view.js +565 -0
  163. package/dist/src/elements/ia-transcript-view/ia-transcript-view.js.map +1 -0
  164. package/dist/src/elements/ia-transcript-view/models.d.ts +34 -0
  165. package/dist/src/elements/ia-transcript-view/models.d.ts.map +1 -0
  166. package/dist/src/elements/ia-transcript-view/models.js +38 -0
  167. package/dist/src/elements/ia-transcript-view/models.js.map +1 -0
  168. package/dist/src/elements/ia-waveform-progress/ia-waveform-progress.d.ts +62 -0
  169. package/dist/src/elements/ia-waveform-progress/ia-waveform-progress.d.ts.map +1 -0
  170. package/dist/src/elements/ia-waveform-progress/ia-waveform-progress.js +217 -0
  171. package/dist/src/elements/ia-waveform-progress/ia-waveform-progress.js.map +1 -0
  172. package/dist/src/elements/ia-waveform-progress/models.d.ts +13 -0
  173. package/dist/src/elements/ia-waveform-progress/models.d.ts.map +1 -0
  174. package/dist/src/elements/ia-waveform-progress/models.js +2 -0
  175. package/dist/src/elements/ia-waveform-progress/models.js.map +1 -0
  176. package/dist/src/elements/index.d.ts +27 -1
  177. package/dist/src/elements/index.d.ts.map +1 -1
  178. package/dist/src/elements/index.js +27 -1
  179. package/dist/src/elements/index.js.map +1 -1
  180. package/package.json +1 -1
  181. package/dist/src/elements/ia-image-viewer/ia-image-viewer.d.ts +0 -147
  182. package/dist/src/elements/ia-image-viewer/ia-image-viewer.d.ts.map +0 -1
  183. package/dist/src/elements/ia-image-viewer/ia-image-viewer.js +0 -533
  184. package/dist/src/elements/ia-image-viewer/ia-image-viewer.js.map +0 -1
  185. package/dist/src/elements/ia-image-viewer/ia-imgview-controls.d.ts +0 -22
  186. package/dist/src/elements/ia-image-viewer/ia-imgview-controls.d.ts.map +0 -1
  187. package/dist/src/elements/ia-image-viewer/ia-imgview-controls.js +0 -145
  188. package/dist/src/elements/ia-image-viewer/ia-imgview-controls.js.map +0 -1
  189. package/dist/src/elements/ia-image-viewer/ia-imgview-slide.d.ts +0 -44
  190. package/dist/src/elements/ia-image-viewer/ia-imgview-slide.d.ts.map +0 -1
  191. package/dist/src/elements/ia-image-viewer/ia-imgview-slide.js +0 -213
  192. package/dist/src/elements/ia-image-viewer/ia-imgview-slide.js.map +0 -1
  193. package/dist/src/elements/ia-image-viewer/image-viewer-gestures.d.ts +0 -79
  194. package/dist/src/elements/ia-image-viewer/image-viewer-gestures.d.ts.map +0 -1
  195. package/dist/src/elements/ia-image-viewer/image-viewer-gestures.js +0 -254
  196. package/dist/src/elements/ia-image-viewer/image-viewer-gestures.js.map +0 -1
  197. package/dist/src/elements/ia-image-viewer/models.d.ts +0 -20
  198. package/dist/src/elements/ia-image-viewer/models.d.ts.map +0 -1
  199. package/dist/src/elements/ia-image-viewer/models.js.map +0 -1
  200. package/dist/src/util/prefers-reduced-motion.d.ts +0 -10
  201. package/dist/src/util/prefers-reduced-motion.d.ts.map +0 -1
  202. package/dist/src/util/prefers-reduced-motion.js +0 -12
  203. package/dist/src/util/prefers-reduced-motion.js.map +0 -1
  204. /package/dist/src/elements/{ia-image-viewer → ia-audio-element}/models.js +0 -0
@@ -0,0 +1,748 @@
1
+ import { __decorate } from "tslib";
2
+ import { css, html, LitElement, nothing, } from 'lit';
3
+ import { customElement, property, query, state } from 'lit/decorators.js';
4
+ import { msg, str } from '@lit/localize';
5
+ import { PlaybackMode } from '../../elements/ia-playback-controls/models.js';
6
+ import '../../elements/ia-audio-element/ia-audio-element.js';
7
+ import '../../elements/ia-expandable-search-bar/ia-expandable-search-bar.js';
8
+ import '../../elements/ia-playback-controls/ia-playback-controls.js';
9
+ import '../../elements/ia-scrubber-bar/ia-scrubber-bar.js';
10
+ import '../../elements/ia-status-indicator/ia-status-indicator.js';
11
+ import '../../elements/ia-transcript-view/ia-transcript-view.js';
12
+ import '../../elements/ia-waveform-progress/ia-waveform-progress.js';
13
+ import './ia-search-results-switcher.js';
14
+ /**
15
+ * Event names emitted by this component
16
+ */
17
+ const Events = {
18
+ CanPlay: 'canplay',
19
+ CurrentTimeChanged: 'currentTimeChanged',
20
+ HighlightedSearchResultChanged: 'highlightedSearchResultChanged',
21
+ JumpBackButtonPressed: 'jumpBackButtonPressed',
22
+ JumpForwardButtonPressed: 'jumpForwardButtonPressed',
23
+ NextSectionButtonPressed: 'nextSectionButtonPressed',
24
+ PlaybackPaused: 'playbackPaused',
25
+ PlaybackRateChanged: 'playbackRateChanged',
26
+ PlaybackStarted: 'playbackStarted',
27
+ PlayPauseButtonPressed: 'playPauseButtonPressed',
28
+ PrevSectionButtonPressed: 'prevSectionButtonPressed',
29
+ SearchCleared: 'searchCleared',
30
+ SearchExecuted: 'searchExecuted',
31
+ SearchFailed: 'searchFailed',
32
+ SearchTermChanged: 'searchTermChanged',
33
+ TimeChangedFromScrub: 'timeChangedFromScrub',
34
+ TranscriptEntrySelected: 'transcriptEntrySelected',
35
+ };
36
+ /** How far the skip buttons move, in seconds */
37
+ const SKIP_SECONDS = 10;
38
+ /** Shortest term worth searching for */
39
+ const MIN_SEARCH_LENGTH = 2;
40
+ /**
41
+ * Nudge past a section boundary when jumping to it, so playback lands inside
42
+ * the next section rather than exactly on its edge.
43
+ */
44
+ const SECTION_SEEK_NUDGE = 0.1;
45
+ /**
46
+ * A player for transcribed radio broadcasts.
47
+ *
48
+ * It owns no playback or search logic of its own. It wires the audio element,
49
+ * transport controls, waveform, scrubber, search bar and transcript together,
50
+ * and hands searching off to whatever `searchHandler` it is given.
51
+ */
52
+ let IARadioPlayer = class IARadioPlayer extends LitElement {
53
+ constructor() {
54
+ super(...arguments);
55
+ /** Playback position, in seconds */
56
+ this.currentTime = 0;
57
+ /** What is currently in the search bar */
58
+ this.searchTerm = '';
59
+ /** Whether to skip past the music sections rather than play them */
60
+ this.skipMusicSections = false;
61
+ this.percentComplete = 0;
62
+ this.isPlaying = false;
63
+ this.duration = 0;
64
+ this.playbackRate = 1;
65
+ this.volume = 1;
66
+ this.isSearching = false;
67
+ /** Numbers each search so a stale one can be told from the current one. */
68
+ this.latestSearchId = 0;
69
+ }
70
+ render() {
71
+ return html `
72
+ ${this.audioElementTemplate}
73
+ <section role="main">
74
+ ${this.titleDateTemplate} ${this.collectionLogoTemplate}
75
+ ${this.playbackControlsTemplate}
76
+ <div class="waveform-scrubber-container">
77
+ ${this.waveformProgressTemplate} ${this.scrubberBarTemplate}
78
+ </div>
79
+ ${this.searchSectionTemplate} ${this.transcriptViewTemplate}
80
+ </section>
81
+ `;
82
+ }
83
+ /** Starts playback */
84
+ play() {
85
+ this.audioElement?.play();
86
+ }
87
+ /** Pauses playback */
88
+ pause() {
89
+ this.audioElement?.pause();
90
+ }
91
+ /** Jumps to a position in the track, in seconds */
92
+ seekTo(seconds) {
93
+ this.audioElement?.seekTo(seconds);
94
+ }
95
+ get audioElementTemplate() {
96
+ return html `
97
+ <ia-audio-element
98
+ .sources=${this.audioSources}
99
+ .playbackRate=${this.playbackRate}
100
+ .volume=${this.volume}
101
+ @timeupdate=${this.handleTimeChange}
102
+ @durationchange=${this.handleDurationChange}
103
+ @playbackStarted=${this.playbackStarted}
104
+ @playbackPaused=${this.playbackPaused}
105
+ @canplay=${this.canPlay}
106
+ ></ia-audio-element>
107
+ `;
108
+ }
109
+ get titleDateTemplate() {
110
+ return html `
111
+ <div class="title-date">
112
+ <div class="title">${this.config?.title ?? ''}</div>
113
+ <div class="date">${this.config?.date ?? ''}</div>
114
+ </div>
115
+ `;
116
+ }
117
+ get collectionLogoTemplate() {
118
+ const logoUrl = this.config?.logoUrl;
119
+ if (!logoUrl)
120
+ return nothing;
121
+ return html `
122
+ <img
123
+ class="collection-logo"
124
+ src=${logoUrl}
125
+ alt=${msg('Collection logo')}
126
+ />
127
+ `;
128
+ }
129
+ get playbackControlsTemplate() {
130
+ return html `
131
+ <ia-playback-controls
132
+ .playbackMode=${this.isPlaying
133
+ ? PlaybackMode.playing
134
+ : PlaybackMode.paused}
135
+ .playbackRate=${this.playbackRate}
136
+ .volume=${this.volume}
137
+ @back-button-pressed=${this.handleBackButton}
138
+ @play-pause-button-pressed=${this.handlePlayPauseButton}
139
+ @forward-button-pressed=${this.handleForwardButton}
140
+ @volumeChange=${this.handleVolumeChange}
141
+ @playbackRateChange=${this.handlePlaybackRateChange}
142
+ @next-section-button-pressed=${this.handleNextSectionButton}
143
+ @prev-section-button-pressed=${this.handlePrevSectionButton}
144
+ ></ia-playback-controls>
145
+ `;
146
+ }
147
+ get waveformProgressTemplate() {
148
+ const waveformUrl = this.config?.waveformUrl;
149
+ if (!waveformUrl)
150
+ return nothing;
151
+ return html `
152
+ <ia-waveform-progress
153
+ interactive
154
+ .waveformUrl=${waveformUrl}
155
+ .percentComplete=${this.percentComplete}
156
+ .zonesOfSilence=${this.zonesOfSilence}
157
+ @valuechange=${this.handleScrub}
158
+ ></ia-waveform-progress>
159
+ `;
160
+ }
161
+ get scrubberBarTemplate() {
162
+ return html `
163
+ <ia-scrubber-bar
164
+ .sectionMarkerPercentages=${this.sectionMarkerPercentages}
165
+ .value=${this.percentComplete}
166
+ @valuechange=${this.handleScrub}
167
+ ></ia-scrubber-bar>
168
+ `;
169
+ }
170
+ get transcriptViewTemplate() {
171
+ return html `
172
+ <div class="transcript-container">
173
+ <ia-transcript-view
174
+ .config=${this.currentTranscript}
175
+ .currentTime=${this.currentTime}
176
+ @transcriptEntrySelected=${this.handleTranscriptEntrySelected}
177
+ ></ia-transcript-view>
178
+ </div>
179
+ `;
180
+ }
181
+ get searchSectionTemplate() {
182
+ return html `
183
+ <div class="search-section">
184
+ <ia-expandable-search-bar
185
+ .searchTerm=${this.searchTerm}
186
+ .quickSearches=${this.quickSearches}
187
+ ?showsDisclosure=${this.quickSearches.length > 0}
188
+ @inputchange=${this.handleSearchInput}
189
+ @enterKeyPressed=${this.handleSearchSubmitted}
190
+ @quickSearchSelected=${this.handleQuickSearchSelected}
191
+ @searchCleared=${this.handleSearchCleared}
192
+ ></ia-expandable-search-bar>
193
+
194
+ <div class="search-results-info">
195
+ <!--
196
+ Always rendered, so a screen reader is already watching it by the
197
+ time the text changes. A live region added to the page with its
198
+ text already in place is not announced.
199
+ -->
200
+ <div class="sr-only" role="status">${this.searchStatusMessage}</div>
201
+
202
+ ${this.isSearching
203
+ ? html `<ia-status-indicator
204
+ mode="loading"
205
+ loadingTitle=${msg('Searching the transcript')}
206
+ ></ia-status-indicator>`
207
+ : this.searchOutcomeTemplate}
208
+ </div>
209
+ </div>
210
+ `;
211
+ }
212
+ /** What to show once a search has come back: a counter, or nothing found. */
213
+ get searchOutcomeTemplate() {
214
+ if (this.searchTerm.length === 0 || !this.searchResultsTranscript) {
215
+ return nothing;
216
+ }
217
+ const resultCount = this.searchResults.length;
218
+ if (resultCount === 0) {
219
+ return html `
220
+ <div class="no-search-results-message" aria-hidden="true">
221
+ ${msg('No search results.')}
222
+ </div>
223
+ `;
224
+ }
225
+ return html `
226
+ <ia-search-results-switcher
227
+ .numberOfResults=${resultCount}
228
+ @searchResultIndexChanged=${this.handleSearchResultIndexChanged}
229
+ ></ia-search-results-switcher>
230
+ `;
231
+ }
232
+ /** What the live region announces as a search runs and comes back. */
233
+ get searchStatusMessage() {
234
+ if (this.isSearching)
235
+ return msg('Searching the transcript');
236
+ if (this.searchTerm.length === 0 || !this.searchResultsTranscript)
237
+ return '';
238
+ const count = this.searchResults.length;
239
+ if (count === 0)
240
+ return msg('No search results.');
241
+ return msg(str `${count} search results`);
242
+ }
243
+ get currentTranscript() {
244
+ return this.searchResultsTranscript ?? this.transcriptConfig;
245
+ }
246
+ get transcriptEntries() {
247
+ return this.currentTranscript?.entries ?? [];
248
+ }
249
+ get searchResults() {
250
+ return this.searchResultsTranscript?.searchResults ?? [];
251
+ }
252
+ get audioSources() {
253
+ return this.config?.audioSources ?? [];
254
+ }
255
+ get quickSearches() {
256
+ return (this.config?.quickSearches ?? []).map((displayText) => ({
257
+ displayText,
258
+ }));
259
+ }
260
+ get musicEntries() {
261
+ return this.transcriptEntries.filter((entry) => entry.isMusic);
262
+ }
263
+ /** The music stretches, as percentages, for marking on the waveform. */
264
+ get zonesOfSilence() {
265
+ if (this.duration === 0)
266
+ return [];
267
+ return this.musicEntries.map((entry) => ({
268
+ startPercent: (entry.start / this.duration) * 100,
269
+ endPercent: (entry.end / this.duration) * 100,
270
+ }));
271
+ }
272
+ /** The music stretches in seconds, for skipping past them. */
273
+ get musicZones() {
274
+ return this.musicEntries.map((entry) => ({
275
+ start: entry.start,
276
+ end: entry.end,
277
+ }));
278
+ }
279
+ /**
280
+ * Every section boundary as a percentage, including the two ends.
281
+ *
282
+ * The section skip buttons step between these, and the scrubber draws a
283
+ * marker at each.
284
+ */
285
+ get sectionMarkerPercentages() {
286
+ const percentages = [0];
287
+ this.zonesOfSilence.forEach((zone) => {
288
+ percentages.push(zone.startPercent, zone.endPercent);
289
+ });
290
+ percentages.push(100);
291
+ return percentages;
292
+ }
293
+ updated(changedProperties) {
294
+ if (changedProperties.has('searchResultsTranscript')) {
295
+ this.resetSearchResultPosition();
296
+ }
297
+ if (changedProperties.has('searchHandler') && this.searchTerm) {
298
+ this.executeSearch(this.searchTerm);
299
+ }
300
+ if (changedProperties.has('currentTime')) {
301
+ this.dispatchEvent(new CustomEvent(Events.CurrentTimeChanged, {
302
+ detail: { currentTime: this.currentTime },
303
+ }));
304
+ if (this.skipMusicSections)
305
+ this.skipMusicZone();
306
+ }
307
+ }
308
+ handleTimeChange(e) {
309
+ const { currentTime } = e.detail;
310
+ if (currentTime === undefined)
311
+ return;
312
+ this.currentTime = currentTime;
313
+ // A duration of 0 means nothing has loaded yet, and dividing by it would
314
+ // put NaN into the scrubber and the waveform.
315
+ this.percentComplete =
316
+ this.duration > 0 ? (currentTime / this.duration) * 100 : 0;
317
+ }
318
+ handleDurationChange(e) {
319
+ const { duration } = e.detail;
320
+ // A stream reports Infinity, and metadata that hasn't loaded reports NaN.
321
+ if (!Number.isFinite(duration))
322
+ return;
323
+ this.duration = duration;
324
+ }
325
+ handlePlaybackRateChange(e) {
326
+ const { playbackRate } = e.detail;
327
+ if (playbackRate === undefined)
328
+ return;
329
+ this.playbackRate = playbackRate;
330
+ this.dispatchEvent(new CustomEvent(Events.PlaybackRateChanged, {
331
+ detail: { playbackRate },
332
+ }));
333
+ }
334
+ handleVolumeChange(e) {
335
+ const { volume } = e.detail;
336
+ if (volume === undefined)
337
+ return;
338
+ this.volume = volume;
339
+ this.dispatchEvent(new CustomEvent('volumeChanged', {
340
+ detail: { volume },
341
+ }));
342
+ }
343
+ handleBackButton() {
344
+ this.audioElement?.seekBy(-SKIP_SECONDS);
345
+ this.dispatchEvent(new Event(Events.JumpBackButtonPressed));
346
+ }
347
+ handleForwardButton() {
348
+ this.audioElement?.seekBy(SKIP_SECONDS);
349
+ this.dispatchEvent(new Event(Events.JumpForwardButtonPressed));
350
+ }
351
+ handlePlayPauseButton() {
352
+ this.isPlaying = !this.isPlaying;
353
+ if (this.isPlaying)
354
+ this.audioElement?.play();
355
+ else
356
+ this.audioElement?.pause();
357
+ this.dispatchEvent(new CustomEvent(Events.PlayPauseButtonPressed, {
358
+ detail: { isPlaying: this.isPlaying },
359
+ }));
360
+ }
361
+ handleNextSectionButton() {
362
+ const ahead = this.sectionMarkerPercentages.filter((percent) => percent > this.percentComplete + SECTION_SEEK_NUDGE);
363
+ this.seekToSectionBoundary(ahead, Math.min, SECTION_SEEK_NUDGE);
364
+ this.dispatchEvent(new Event(Events.NextSectionButtonPressed));
365
+ }
366
+ handlePrevSectionButton() {
367
+ const behind = this.sectionMarkerPercentages.filter((percent) => percent < this.percentComplete - SECTION_SEEK_NUDGE);
368
+ this.seekToSectionBoundary(behind, Math.max, -SECTION_SEEK_NUDGE);
369
+ this.dispatchEvent(new Event(Events.PrevSectionButtonPressed));
370
+ }
371
+ /**
372
+ * Seeks to the nearest of a set of boundaries.
373
+ *
374
+ * Does nothing when the set is empty, since `Math.min` of nothing is
375
+ * `Infinity` and `Math.max` of nothing is `-Infinity`, either of which would
376
+ * be handed straight to the audio element as a position.
377
+ */
378
+ seekToSectionBoundary(percentages, pick, nudge) {
379
+ if (percentages.length === 0)
380
+ return;
381
+ const boundary = pick(...percentages);
382
+ this.audioElement?.seekTo(this.duration * (boundary / 100) + nudge);
383
+ }
384
+ handleScrub(e) {
385
+ const percentage = e.detail.value;
386
+ if (percentage === undefined)
387
+ return;
388
+ const newTime = this.duration * (percentage / 100);
389
+ this.currentTime = newTime;
390
+ this.percentComplete = percentage;
391
+ this.audioElement?.seekTo(newTime);
392
+ this.dispatchEvent(new CustomEvent(Events.TimeChangedFromScrub, {
393
+ detail: { newTime },
394
+ }));
395
+ }
396
+ handleTranscriptEntrySelected(e) {
397
+ const { entry } = e.detail;
398
+ if (!entry)
399
+ return;
400
+ this.currentTime = entry.start;
401
+ this.audioElement?.seekTo(entry.start);
402
+ this.audioElement?.play();
403
+ this.dispatchEvent(new CustomEvent(Events.TranscriptEntrySelected, {
404
+ detail: { newTime: entry.start },
405
+ }));
406
+ }
407
+ handleSearchInput(e) {
408
+ const { value } = e.detail;
409
+ if (value === undefined)
410
+ return;
411
+ this.searchTerm = value;
412
+ // An empty box has nothing to show results for. The counter already hides
413
+ // itself on an empty term, so without dropping the results too the
414
+ // transcript keeps its outlines and offers no way to step between them.
415
+ if (value.length === 0) {
416
+ this.searchResultsTranscript = undefined;
417
+ // Abandons whatever is in flight, so its results can't land afterwards.
418
+ this.latestSearchId += 1;
419
+ this.isSearching = false;
420
+ this.resetSearchResultPosition();
421
+ }
422
+ this.emitSearchTermChanged(value);
423
+ }
424
+ handleSearchSubmitted(e) {
425
+ const { value } = e.detail;
426
+ if (!value)
427
+ return;
428
+ this.executeSearch(value);
429
+ }
430
+ handleQuickSearchSelected(e) {
431
+ const term = e.detail.quickSearchEntry?.displayText;
432
+ if (!term)
433
+ return;
434
+ this.emitSearchTermChanged(term);
435
+ this.executeSearch(term);
436
+ }
437
+ handleSearchCleared() {
438
+ this.searchTerm = '';
439
+ this.searchResultsTranscript = undefined;
440
+ // Abandons whatever is in flight, so its results can't arrive after this.
441
+ this.latestSearchId += 1;
442
+ this.isSearching = false;
443
+ this.dispatchEvent(new Event(Events.SearchCleared));
444
+ this.emitSearchTermChanged('');
445
+ this.resetSearchResultPosition();
446
+ }
447
+ /**
448
+ * Runs a search and shows its results.
449
+ *
450
+ * Each run is numbered, and only the newest one is allowed to report back.
451
+ * Searches go over the network, so a slow one started earlier can land after
452
+ * a fast one started later, and without this the older results would end up
453
+ * on screen under the newer term.
454
+ *
455
+ * A failed search has to put the spinner away as surely as a successful one,
456
+ * hence the `finally`. Leaving it up would hide the search UI for as long as
457
+ * the page lives.
458
+ */
459
+ async executeSearch(term) {
460
+ if (!this.searchHandler || term.length < MIN_SEARCH_LENGTH) {
461
+ this.searchResultsTranscript = undefined;
462
+ return;
463
+ }
464
+ this.searchTerm = term;
465
+ this.isSearching = true;
466
+ this.dispatchEvent(new Event(Events.SearchExecuted));
467
+ this.latestSearchId += 1;
468
+ const searchId = this.latestSearchId;
469
+ try {
470
+ const results = await this.searchHandler.search(term);
471
+ if (searchId !== this.latestSearchId)
472
+ return;
473
+ this.searchResultsTranscript = results;
474
+ }
475
+ catch {
476
+ if (searchId !== this.latestSearchId)
477
+ return;
478
+ this.searchResultsTranscript = undefined;
479
+ this.dispatchEvent(new Event(Events.SearchFailed));
480
+ }
481
+ finally {
482
+ if (searchId === this.latestSearchId)
483
+ this.isSearching = false;
484
+ }
485
+ }
486
+ handleSearchResultIndexChanged(e) {
487
+ const { searchResultIndex } = e.detail;
488
+ if (searchResultIndex === undefined || !this.transcriptView)
489
+ return;
490
+ this.transcriptView.selectedSearchResultIndex = searchResultIndex;
491
+ this.transcriptView.scrollToSelectedSearchResult();
492
+ this.dispatchEvent(new CustomEvent(Events.HighlightedSearchResultChanged, { detail: { searchResultIndex } }));
493
+ }
494
+ /** Puts both the transcript and the switcher back to the first result. */
495
+ resetSearchResultPosition() {
496
+ if (this.transcriptView)
497
+ this.transcriptView.selectedSearchResultIndex = 0;
498
+ if (this.searchResultsSwitcher) {
499
+ this.searchResultsSwitcher.currentResultIndex = 0;
500
+ }
501
+ }
502
+ emitSearchTermChanged(searchTerm) {
503
+ this.dispatchEvent(new CustomEvent(Events.SearchTermChanged, {
504
+ detail: { searchTerm },
505
+ }));
506
+ }
507
+ playbackPaused() {
508
+ this.isPlaying = false;
509
+ this.dispatchEvent(new Event(Events.PlaybackPaused));
510
+ }
511
+ playbackStarted() {
512
+ this.isPlaying = true;
513
+ this.dispatchEvent(new Event(Events.PlaybackStarted));
514
+ }
515
+ canPlay() {
516
+ this.dispatchEvent(new Event(Events.CanPlay));
517
+ }
518
+ /** Jumps past a music stretch if playback has wandered into one. */
519
+ skipMusicZone() {
520
+ const activeZone = this.musicZones.find((zone) => this.currentTime > zone.start && this.currentTime < zone.end);
521
+ if (!activeZone)
522
+ return;
523
+ this.audioElement?.seekTo(activeZone.end + SECTION_SEEK_NUDGE);
524
+ }
525
+ static get styles() {
526
+ return css `
527
+ :host {
528
+ --radio-player-title-color--: var(
529
+ --ia-theme-radio-player-title-color,
530
+ #fff
531
+ );
532
+ --radio-player-title-font--: var(
533
+ --ia-theme-radio-player-title-font,
534
+ 1.5em sans-serif
535
+ );
536
+ --radio-player-date-color--: var(
537
+ --ia-theme-radio-player-date-color,
538
+ #fff
539
+ );
540
+ --radio-player-date-font--: var(
541
+ --ia-theme-radio-player-date-font,
542
+ 1em sans-serif
543
+ );
544
+ --radio-player-logo-max-height--: var(
545
+ --ia-theme-radio-player-logo-max-height,
546
+ 8rem
547
+ );
548
+ --radio-player-waveform-height--: var(
549
+ --ia-theme-radio-player-waveform-height,
550
+ 5rem
551
+ );
552
+ }
553
+
554
+ section[role='main'] {
555
+ display: grid;
556
+ grid-gap: 0.5rem;
557
+ }
558
+
559
+ ia-status-indicator {
560
+ --ia-theme-icon-width: 1.5em;
561
+ --ia-theme-primary-text-color: #999;
562
+
563
+ display: block;
564
+ margin: auto;
565
+ }
566
+
567
+ /* mobile view */
568
+ @media (max-width: 770px) {
569
+ section[role='main'] {
570
+ grid-template-columns: 25% 1fr;
571
+ grid-template-areas:
572
+ 'collection-logo title-date'
573
+ 'waveform-scrubber waveform-scrubber'
574
+ 'playback-controls playback-controls'
575
+ 'search-section search-section'
576
+ 'transcript-container transcript-container';
577
+ }
578
+
579
+ .date {
580
+ text-align: left;
581
+ }
582
+
583
+ ia-transcript-view {
584
+ --ia-theme-transcript-time-display: none;
585
+ }
586
+
587
+ ia-playback-controls {
588
+ width: 75%;
589
+ margin: auto;
590
+ }
591
+
592
+ .search-section {
593
+ width: 75%;
594
+ margin: auto;
595
+ }
596
+ }
597
+
598
+ /* wide view */
599
+ @media (min-width: 770px) {
600
+ section[role='main'] {
601
+ grid-template-columns: 192px 0 250px 1fr;
602
+ grid-template-areas:
603
+ 'title-date title-date title-date title-date'
604
+ 'collection-logo . playback-controls waveform-scrubber'
605
+ 'search-section transcript-container transcript-container transcript-container';
606
+ }
607
+
608
+ .title-date {
609
+ display: flex;
610
+ justify-content: space-between;
611
+ align-items: baseline;
612
+ }
613
+
614
+ ia-transcript-view {
615
+ --ia-theme-transcript-time-display: block;
616
+ }
617
+ }
618
+
619
+ .title-date {
620
+ grid-area: title-date;
621
+ }
622
+
623
+ .title {
624
+ color: var(--radio-player-title-color--);
625
+ font: var(--radio-player-title-font--);
626
+ }
627
+
628
+ .date {
629
+ color: var(--radio-player-date-color--);
630
+ font: var(--radio-player-date-font--);
631
+ }
632
+
633
+ ia-waveform-progress {
634
+ width: 100%;
635
+ height: var(--radio-player-waveform-height--);
636
+ }
637
+
638
+ ia-playback-controls {
639
+ grid-area: playback-controls;
640
+ }
641
+
642
+ .transcript-container {
643
+ grid-area: transcript-container;
644
+ }
645
+
646
+ ia-transcript-view {
647
+ max-width: 600px;
648
+ display: block;
649
+ }
650
+
651
+ .collection-logo {
652
+ width: 100%;
653
+ max-height: var(--radio-player-logo-max-height--);
654
+ object-fit: contain;
655
+ grid-area: collection-logo;
656
+ align-self: center;
657
+ }
658
+
659
+ .waveform-scrubber-container {
660
+ width: 100%;
661
+ height: 100%;
662
+ grid-area: waveform-scrubber;
663
+ }
664
+
665
+ .search-section {
666
+ grid-area: search-section;
667
+ }
668
+
669
+ .search-results-info {
670
+ margin-top: 0.5em;
671
+ }
672
+
673
+ ia-expandable-search-bar {
674
+ display: block;
675
+ margin: auto;
676
+ }
677
+
678
+ .no-search-results-message {
679
+ text-align: center;
680
+ }
681
+
682
+ .sr-only {
683
+ position: absolute;
684
+ width: 1px;
685
+ height: 1px;
686
+ padding: 0;
687
+ margin: -1px;
688
+ overflow: hidden;
689
+ clip: rect(0, 0, 0, 0);
690
+ white-space: nowrap;
691
+ border: 0;
692
+ }
693
+ `;
694
+ }
695
+ };
696
+ __decorate([
697
+ property({ type: Object })
698
+ ], IARadioPlayer.prototype, "config", void 0);
699
+ __decorate([
700
+ property({ type: Object })
701
+ ], IARadioPlayer.prototype, "searchHandler", void 0);
702
+ __decorate([
703
+ property({ type: Object })
704
+ ], IARadioPlayer.prototype, "transcriptConfig", void 0);
705
+ __decorate([
706
+ property({ type: Object })
707
+ ], IARadioPlayer.prototype, "searchResultsTranscript", void 0);
708
+ __decorate([
709
+ property({ type: Number })
710
+ ], IARadioPlayer.prototype, "currentTime", void 0);
711
+ __decorate([
712
+ property({ type: String })
713
+ ], IARadioPlayer.prototype, "searchTerm", void 0);
714
+ __decorate([
715
+ property({ type: Boolean })
716
+ ], IARadioPlayer.prototype, "skipMusicSections", void 0);
717
+ __decorate([
718
+ state()
719
+ ], IARadioPlayer.prototype, "percentComplete", void 0);
720
+ __decorate([
721
+ state()
722
+ ], IARadioPlayer.prototype, "isPlaying", void 0);
723
+ __decorate([
724
+ state()
725
+ ], IARadioPlayer.prototype, "duration", void 0);
726
+ __decorate([
727
+ state()
728
+ ], IARadioPlayer.prototype, "playbackRate", void 0);
729
+ __decorate([
730
+ state()
731
+ ], IARadioPlayer.prototype, "volume", void 0);
732
+ __decorate([
733
+ state()
734
+ ], IARadioPlayer.prototype, "isSearching", void 0);
735
+ __decorate([
736
+ query('ia-audio-element')
737
+ ], IARadioPlayer.prototype, "audioElement", void 0);
738
+ __decorate([
739
+ query('ia-transcript-view')
740
+ ], IARadioPlayer.prototype, "transcriptView", void 0);
741
+ __decorate([
742
+ query('ia-search-results-switcher')
743
+ ], IARadioPlayer.prototype, "searchResultsSwitcher", void 0);
744
+ IARadioPlayer = __decorate([
745
+ customElement('ia-radio-player')
746
+ ], IARadioPlayer);
747
+ export { IARadioPlayer };
748
+ //# sourceMappingURL=ia-radio-player.js.map