ravnur-player-public 3.4.3 → 3.4.4

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 (260) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc.js +206 -0
  3. package/.flowconfig +3 -0
  4. package/.vscode/extensions.json +7 -0
  5. package/.vscode/launch.json +37 -0
  6. package/.vscode/settings.json +4 -0
  7. package/README-Private.md +54 -0
  8. package/babel.config.js +28 -0
  9. package/bitbucket-pipelines.yml +61 -0
  10. package/cert.pem +23 -0
  11. package/demo/BaseM.mp4 +0 -0
  12. package/demo/HD.mp4 +0 -0
  13. package/demo/annotations.json +50 -0
  14. package/demo/annotations_ge.json +50 -0
  15. package/demo/base.mp3 +0 -0
  16. package/demo/cc_2125en.vtt +4958 -0
  17. package/demo/cc_en.vtt +171 -0
  18. package/demo/cc_ge.vtt +178 -0
  19. package/demo/chapters_en.vtt +38 -0
  20. package/demo/chapters_ge.vtt +5 -0
  21. package/demo/chapters_ge1.json +23 -0
  22. package/demo/hls/audio/stereo/en/128kbit.m3u8 +912 -0
  23. package/demo/hls/audio/stereo/none/128kbit.m3u8 +912 -0
  24. package/demo/hls/audio/surround/en/320kbit.m3u8 +912 -0
  25. package/demo/hls/playlist.m3u8 +31 -0
  26. package/demo/hls/video/10000kbit.m3u8 +894 -0
  27. package/demo/hls/video/1100kbit.m3u8 +894 -0
  28. package/demo/hls/video/1500kbit.m3u8 +894 -0
  29. package/demo/hls/video/250kbit.m3u8 +894 -0
  30. package/demo/hls/video/4000kbit.m3u8 +894 -0
  31. package/demo/hls/video/500kbit.m3u8 +894 -0
  32. package/demo/hls/video/6000kbit.m3u8 +894 -0
  33. package/demo/hls/video/800kbit.m3u8 +894 -0
  34. package/demo/hls.js +5 -0
  35. package/demo/hls.js.map +1 -0
  36. package/demo/hls.min.js +2 -0
  37. package/demo/hls.min.js.map +1 -0
  38. package/demo/playlist.m3u8 +31 -0
  39. package/demo/ravnur-flash-audio.swf +0 -0
  40. package/demo/ravnur-flash-video-hls.swf +0 -0
  41. package/demo/ravnur-flash-video.swf +0 -0
  42. package/demo/shaka/shaka-player.foo.debug.d.ts +4532 -0
  43. package/demo/shaka/shaka-player.foo.debug.externs.js +3886 -0
  44. package/demo/shaka/shaka-player.foo.debug.js +1746 -0
  45. package/demo/shaka/shaka-player.foo.debug.map +8 -0
  46. package/demo/shaka/wrapper.js +7 -0
  47. package/demo/test.html +458 -0
  48. package/dist/RavnurMediaPlayer.min.js +1 -1
  49. package/dist/cdn/RavnurMediaPlayer.min.js +1 -1
  50. package/jest.config.js +4 -0
  51. package/key.pem +27 -0
  52. package/lib/es5.js +344 -0
  53. package/lib/images/Spinner-small.gif +0 -0
  54. package/lib/images/close.png +0 -0
  55. package/lib/images/ic_check_box_black_24dp_1x.png +0 -0
  56. package/lib/images/ic_check_box_outline_blank_black_24dp_1x.png +0 -0
  57. package/lib/images/ic_chevron_left_white_24dp_1x.png +0 -0
  58. package/lib/images/ic_chevron_right_white_24dp_1x.png +0 -0
  59. package/lib/images/ic_closed_caption_white_24dp_1x.png +0 -0
  60. package/lib/images/ic_fast_forward_white_24dp_1x.png +0 -0
  61. package/lib/images/ic_fast_rewind_white_24dp_1x.png +0 -0
  62. package/lib/images/ic_fullscreen_exit_white_24dp_1x.png +0 -0
  63. package/lib/images/ic_fullscreen_white_24dp_1x.png +0 -0
  64. package/lib/images/ic_hd_white_24dp_1x.png +0 -0
  65. package/lib/images/ic_keyboard_arrow_left_black_24dp_1x.png +0 -0
  66. package/lib/images/ic_keyboard_arrow_right_black_24dp_1x.png +0 -0
  67. package/lib/images/ic_pause_white_24dp_1x.png +0 -0
  68. package/lib/images/ic_photo_white_24dp_1x.png +0 -0
  69. package/lib/images/ic_play_arrow_white_24dp_1x.png +0 -0
  70. package/lib/images/ic_refresh_white_24dp_1x.png +0 -0
  71. package/lib/images/ic_settings_white_24dp_1x.png +0 -0
  72. package/lib/images/ic_skip_next_white_24dp_1x.png +0 -0
  73. package/lib/images/ic_skip_previous_white_24dp_1x.png +0 -0
  74. package/lib/images/ic_toc_white_24dp_1x.png +0 -0
  75. package/lib/images/ic_volume_off_white_24dp_1x.png +0 -0
  76. package/lib/images/ic_volume_up_white_24dp_1x.png +0 -0
  77. package/lib/player4ie8.css +225 -0
  78. package/package.json +3 -7
  79. package/server.js +29 -0
  80. package/src/config/cc.js +56 -0
  81. package/src/config/i18n.js +101 -0
  82. package/src/config/options.js +123 -0
  83. package/src/config/playlist.js +9 -0
  84. package/src/config/source.js +23 -0
  85. package/src/config/statuses.js +8 -0
  86. package/src/config/styles.js +16 -0
  87. package/src/entity.js +27 -0
  88. package/src/events.js +5 -0
  89. package/src/extensions/annotations.js +142 -0
  90. package/src/extensions/audio-tarcks.js +115 -0
  91. package/src/extensions/backward.js +45 -0
  92. package/src/extensions/base.js +73 -0
  93. package/src/extensions/bottom-next.js +50 -0
  94. package/src/extensions/bottom-prev.js +50 -0
  95. package/src/extensions/buffering.js +78 -0
  96. package/src/extensions/c2pa.js +350 -0
  97. package/src/extensions/caption-search.js +230 -0
  98. package/src/extensions/cc.js +874 -0
  99. package/src/extensions/crawl.js +118 -0
  100. package/src/extensions/download.js +411 -0
  101. package/src/extensions/error.js +47 -0
  102. package/src/extensions/forward.js +44 -0
  103. package/src/extensions/fullscreen.js +84 -0
  104. package/src/extensions/help.js +201 -0
  105. package/src/extensions/helpers/FileSaver.js +157 -0
  106. package/src/extensions/helpers/clickOpener.js +180 -0
  107. package/src/extensions/helpers/opener.js +30 -0
  108. package/src/extensions/helpers/openerHeightChecker.js +13 -0
  109. package/src/extensions/helpers/popover.js +33 -0
  110. package/src/extensions/helpers/popoverPosition.js +30 -0
  111. package/src/extensions/helpers/scrollIntoView.js +9 -0
  112. package/src/extensions/helpers/storage.js +20 -0
  113. package/src/extensions/helpers/textContent.js +6 -0
  114. package/src/extensions/helpers/timeCodeToSeconds.js +44 -0
  115. package/src/extensions/helpers/transport.js +43 -0
  116. package/src/extensions/helpers/vtt-loader.js +42 -0
  117. package/src/extensions/index.js +87 -0
  118. package/src/extensions/live.js +76 -0
  119. package/src/extensions/mux.js +57 -0
  120. package/src/extensions/next-frame.js +44 -0
  121. package/src/extensions/next.js +48 -0
  122. package/src/extensions/placeholder.js +241 -0
  123. package/src/extensions/play.js +102 -0
  124. package/src/extensions/poster.js +47 -0
  125. package/src/extensions/prev-frame.js +44 -0
  126. package/src/extensions/prev.js +48 -0
  127. package/src/extensions/progress.js +465 -0
  128. package/src/extensions/resizer.js +37 -0
  129. package/src/extensions/settings.js +367 -0
  130. package/src/extensions/theater.js +56 -0
  131. package/src/extensions/title.js +38 -0
  132. package/src/extensions/toc.js +334 -0
  133. package/src/extensions/volume.js +196 -0
  134. package/src/flash/FlashPlugin.js +301 -0
  135. package/src/flash/MediaElement.js +361 -0
  136. package/src/flash/plugins.js +32 -0
  137. package/src/flash-detector.js +66 -0
  138. package/src/helpers/$t.js +10 -0
  139. package/src/helpers/binder.js +11 -0
  140. package/src/helpers/isAndroid.js +5 -0
  141. package/src/helpers/isBlackBerry.js +5 -0
  142. package/src/helpers/isCanvasSupported.js +6 -0
  143. package/src/helpers/isIE.js +21 -0
  144. package/src/helpers/isMobile.js +10 -0
  145. package/src/helpers/isWindows.js +5 -0
  146. package/src/helpers/isWindowsPhone.js +5 -0
  147. package/src/helpers/isiOS.js +5 -0
  148. package/src/html5media.js +19 -0
  149. package/src/index.js +2 -0
  150. package/src/logger.js +31 -0
  151. package/src/microevent.js +65 -0
  152. package/src/normalize-options.js +139 -0
  153. package/src/player.js +864 -0
  154. package/src/players/base.js +209 -0
  155. package/src/players/flash.js +172 -0
  156. package/src/players/hls.js +278 -0
  157. package/src/players/html.js +205 -0
  158. package/src/players/index.js +59 -0
  159. package/src/players/shaka.js +219 -0
  160. package/src/playlist.js +362 -0
  161. package/src/screenfull.js +121 -0
  162. package/src/state.js +474 -0
  163. package/src/static/es5.js +344 -0
  164. package/src/static/images/Spinner-small.gif +0 -0
  165. package/src/static/images/close.png +0 -0
  166. package/src/static/images/ic_check_box_black_24dp_1x.png +0 -0
  167. package/src/static/images/ic_check_box_outline_blank_black_24dp_1x.png +0 -0
  168. package/src/static/images/ic_chevron_left_white_24dp_1x.png +0 -0
  169. package/src/static/images/ic_chevron_right_white_24dp_1x.png +0 -0
  170. package/src/static/images/ic_closed_caption_white_24dp_1x.png +0 -0
  171. package/src/static/images/ic_fast_forward_white_24dp_1x.png +0 -0
  172. package/src/static/images/ic_fast_rewind_white_24dp_1x.png +0 -0
  173. package/src/static/images/ic_fullscreen_exit_white_24dp_1x.png +0 -0
  174. package/src/static/images/ic_fullscreen_white_24dp_1x.png +0 -0
  175. package/src/static/images/ic_hd_white_24dp_1x.png +0 -0
  176. package/src/static/images/ic_keyboard_arrow_left_black_24dp_1x.png +0 -0
  177. package/src/static/images/ic_keyboard_arrow_right_black_24dp_1x.png +0 -0
  178. package/src/static/images/ic_pause_white_24dp_1x.png +0 -0
  179. package/src/static/images/ic_play_arrow_white_24dp_1x.png +0 -0
  180. package/src/static/images/ic_refresh_white_24dp_1x.png +0 -0
  181. package/src/static/images/ic_settings_white_24dp_1x.png +0 -0
  182. package/src/static/images/ic_skip_next_white_24dp_1x.png +0 -0
  183. package/src/static/images/ic_skip_previous_white_24dp_1x.png +0 -0
  184. package/src/static/images/ic_toc_white_24dp_1x.png +0 -0
  185. package/src/static/images/ic_volume_off_white_24dp_1x.png +0 -0
  186. package/src/static/images/ic_volume_up_white_24dp_1x.png +0 -0
  187. package/src/static/player4ie8.css +225 -0
  188. package/src/styles/bplaylist.css.js +124 -0
  189. package/src/styles/index.js +1966 -0
  190. package/src/styles/playlist.css.js +84 -0
  191. package/src/styles/rplaylist.css.js +98 -0
  192. package/src/svgs.js +111 -0
  193. package/src/types/Logger.js +10 -0
  194. package/src/types/Options.js +179 -0
  195. package/src/types/Playlist.js +3 -0
  196. package/src/types/Source.js +28 -0
  197. package/src/types/State.js +46 -0
  198. package/src/types/Styles.js +11 -0
  199. package/src/types/TimeData.js +8 -0
  200. package/src/types/Translation.js +69 -0
  201. package/src/utils/absolutizeUrl.js +9 -0
  202. package/src/utils/addClass.js +9 -0
  203. package/src/utils/addEvent.js +31 -0
  204. package/src/utils/addProperty.js +65 -0
  205. package/src/utils/appendChild.js +14 -0
  206. package/src/utils/buff2hex.js +5 -0
  207. package/src/utils/contains.js +33 -0
  208. package/src/utils/createElement.js +24 -0
  209. package/src/utils/each.js +34 -0
  210. package/src/utils/escapeHTML.js +8 -0
  211. package/src/utils/existy.js +4 -0
  212. package/src/utils/extend.js +17 -0
  213. package/src/utils/filter.js +16 -0
  214. package/src/utils/find.js +11 -0
  215. package/src/utils/findIndex.js +20 -0
  216. package/src/utils/first.js +5 -0
  217. package/src/utils/get.js +19 -0
  218. package/src/utils/has.js +5 -0
  219. package/src/utils/hasClass.js +6 -0
  220. package/src/utils/head.js +11 -0
  221. package/src/utils/inRange.js +16 -0
  222. package/src/utils/index.js +73 -0
  223. package/src/utils/isArray.js +4 -0
  224. package/src/utils/isBoolean.js +3 -0
  225. package/src/utils/isElement.js +7 -0
  226. package/src/utils/isEmpty.js +6 -0
  227. package/src/utils/isEqual.js +33 -0
  228. package/src/utils/isEqualBuffer.js +13 -0
  229. package/src/utils/isFunction.js +3 -0
  230. package/src/utils/isNotEmpty.js +5 -0
  231. package/src/utils/isObject.js +5 -0
  232. package/src/utils/isString.js +9 -0
  233. package/src/utils/last.js +4 -0
  234. package/src/utils/map.js +11 -0
  235. package/src/utils/negate.js +8 -0
  236. package/src/utils/noop.js +1 -0
  237. package/src/utils/notExisty.js +5 -0
  238. package/src/utils/reduce.js +8 -0
  239. package/src/utils/remove.js +7 -0
  240. package/src/utils/removeClass.js +8 -0
  241. package/src/utils/removeEvent.js +7 -0
  242. package/src/utils/toArray.js +7 -0
  243. package/src/utils/toggleClass.js +16 -0
  244. package/src/utils/uidGenerator.js +8 -0
  245. package/src/utils/upperFirst.js +4 -0
  246. package/tests/extensions/__snapshots__/download.spec.js.snap +226 -0
  247. package/tests/extensions/__snapshots__/fullscreen.spec.js.snap +30 -0
  248. package/tests/extensions/__snapshots__/title.spec.js.snap +16 -0
  249. package/tests/extensions/download.spec.js +111 -0
  250. package/tests/extensions/fullscreen.spec.js +56 -0
  251. package/tests/extensions/title.spec.js +35 -0
  252. package/tests/mocks/assets/BaseM.mp4 +0 -0
  253. package/tests/mocks/assets/hls.min.js +5 -0
  254. package/tests/mocks/assets/styleMock.js +1 -0
  255. package/tests/mocks/base-player-options.js +47 -0
  256. package/tests/mocks/sources.js +58 -0
  257. package/tests/mocks/timedata/cc_en.vtt +171 -0
  258. package/tests/mocks/timedata/cc_ge.vtt +178 -0
  259. package/tests/utils/wait.js +1 -0
  260. package/webpack.config.js +78 -0
@@ -0,0 +1,874 @@
1
+ // @flow
2
+ import BaseExtension from './base';
3
+
4
+ import {
5
+ createElement,
6
+ each,
7
+ find,
8
+ removeClass,
9
+ addClass,
10
+ inRange,
11
+ findIndex,
12
+ isEmpty,
13
+ toArray,
14
+ hasClass,
15
+ filter,
16
+ append
17
+ } from '../utils';
18
+
19
+ import opener from './helpers/clickOpener';
20
+ import popover from './helpers/popover';
21
+ import openerHeightChecker from './helpers/openerHeightChecker';
22
+
23
+ import binder from '../helpers/binder';
24
+
25
+ import BaseController from '../players/base';
26
+ import StateEntity from '../state';
27
+
28
+ import {
29
+ CC_COLORS,
30
+ CC_FONT_FAMILIES,
31
+ CC_FONT_SIZES,
32
+ CC_LOCATIONS
33
+ } from '../config/cc';
34
+
35
+ import {
36
+ UNAPPROVED_TIMEDATA_STATUS,
37
+ UNTRANSLATED_TIMEDATA_STATUS,
38
+ TRANSLATABLE_TIMEDATA_STATUS
39
+ } from '../config/statuses';
40
+
41
+ import createSvg, { CC, BACK, NEXT } from '../svgs';
42
+
43
+ import type { Player$ExtensionOptions } from '../types/Options';
44
+ import type { Player$State } from '../types/State';
45
+ import type { CCOption } from '../config/cc';
46
+ import type { Player$Translation } from '../types/Translation';
47
+ import type { Player$TimeDataSource } from '../types/Source';
48
+
49
+ const CN = 'rmp-ext-cc';
50
+
51
+ const BUTTON_CN = `${CN}__button`;
52
+ const SELECTOR_CN = `${CN}__selector`;
53
+ const SELECTOR_OPEN_CN = `${SELECTOR_CN}--open`;
54
+ const SELECTOR_MAIN_STEP_CN = `${SELECTOR_CN}--main-step`;
55
+ const SELECTOR_SETTINGS_STEP_CN = `${SELECTOR_CN}--settings-step`;
56
+ const SELECTOR_ADDITIONAL_STEP_CN = `${SELECTOR_CN}--additional-step`;
57
+ const SELECTOR_TRANSLATE_STEP_CN = `${SELECTOR_CN}--translate-step`;
58
+
59
+ const MAIN_STEP_CN = `${CN}__main-step`;
60
+ const SETTINGS_STEP_CN = `${CN}__settings-step`;
61
+ const ADDITIONAL_STEP_CN = `${CN}__additional-step`;
62
+
63
+ const ITEM_CN = `${CN}__item`;
64
+ const ITEM_CHECKED_CN = `${ITEM_CN}--checked`;
65
+
66
+ const SETTINGS_CN = `${CN}__settings`;
67
+ const PROGRESS_SETTINGS_CN = `${SETTINGS_CN}--progress`;
68
+ const MORE_CN = `${CN}__more`;
69
+ const CURRENT_VALUE_CN = `${CN}__current-value`;
70
+
71
+ const TEXT_CN = `${CN}__text`;
72
+ const TEXT_VISIBLE_CN = `${TEXT_CN}--visible`;
73
+
74
+ const TEXT_HOLDER_CN = `${CN}__text-holder`;
75
+
76
+ const BACK_BTN_CN = `${CN}__back`;
77
+
78
+ const TRANSLATE_WRAPPER_CN = `${CN}__translate-wrapper`;
79
+ const SETTINGS_WRAPPER_CN = `${CN}__settings-wrapper`;
80
+
81
+ export default class ClosedCaptionsExtension extends BaseExtension {
82
+ $el: HTMLElement;
83
+ $button: HTMLButtonElement;
84
+ $selector: HTMLElement;
85
+ $text: HTMLElement;
86
+ $textHolder: HTMLElement;
87
+ $mainStep: HTMLElement;
88
+ $embedCaptions: HTMLElement;
89
+ $settingsStep: HTMLElement;
90
+ $settings: HTMLElement;
91
+ $toMain: HTMLElement;
92
+ $toSettings: HTMLElement;
93
+ $fontColorSettings: HTMLElement;
94
+ $backgroundColorSettings: HTMLElement;
95
+ $fontSizeSettings: HTMLElement;
96
+ $fontFamilySettings: HTMLElement;
97
+ $locationSettings: ?HTMLElement = null;
98
+ $currentColor: HTMLElement;
99
+ $currentSize: HTMLElement;
100
+ $currentBgColor: HTMLElement;
101
+ $currentLocation: HTMLElement;
102
+ $currentFamily: HTMLElement;
103
+ $additionalStep: HTMLElement;
104
+ $additionalBackText: HTMLElement;
105
+ $additionalStepWrapper: HTMLElement;
106
+ $translate: ?HTMLElement;
107
+ $currentCustomTranslate: ?HTMLElement;
108
+
109
+ $captions: HTMLElement[] = [];
110
+ $translatableCaptions: HTMLElement[] = [];
111
+
112
+ embedCaptionsAutoShow: boolean = true;
113
+
114
+ constructor(
115
+ player: BaseController,
116
+ state: StateEntity,
117
+ options: Player$ExtensionOptions
118
+ ) {
119
+ super(player, state, options, ['timeupdate', 'showpopup', 'statechanged']);
120
+ this.logger.debug('cc:extension:creating');
121
+ this.render();
122
+ this.init();
123
+ this.logger.debug('cc:extension:created');
124
+ }
125
+
126
+ init() {
127
+ binder(this, [
128
+ '_toSettingsPage',
129
+ '_toMainPage',
130
+ '_toColorPage',
131
+ '_toBackgroundColorPage',
132
+ '_toFontFamilyPage',
133
+ '_toSizesPage',
134
+ '_toLocationsPage',
135
+ '_toTranslatePage',
136
+ '_handleEmbedChanged'
137
+ ]);
138
+
139
+ opener(this, {
140
+ $button: this.$button,
141
+ $selector: this.$selector,
142
+ SELECTOR_OPEN_CN,
143
+ heightControl: true,
144
+ firstFocusItemSelector: `.${ITEM_CN}`,
145
+ toMainFn: this._toMainPage.bind(this)
146
+ });
147
+ popover(this, { $button: this.$button, text: this.$t('cc') });
148
+
149
+ this.addEvent(this.$settings, 'click', this._toSettingsPage);
150
+ this.addEvent(this.$toMain, 'click', this._toMainPage);
151
+ this.addEvent(this.$toSettings, 'click', this._toSettingsPage);
152
+
153
+ this.addEvent(this.$fontColorSettings, 'click', this._toColorPage);
154
+ this.addEvent(
155
+ this.$backgroundColorSettings,
156
+ 'click',
157
+ this._toBackgroundColorPage
158
+ );
159
+ this.addEvent(this.$fontFamilySettings, 'click', this._toFontFamilyPage);
160
+ this.addEvent(this.$fontSizeSettings, 'click', this._toSizesPage);
161
+
162
+ if (this.$locationSettings) {
163
+ this.addEvent(this.$locationSettings, 'click', this._toLocationsPage);
164
+ }
165
+
166
+ if (this.$translate) {
167
+ this.addEvent(this.$translate, 'click', this._toTranslatePage);
168
+ }
169
+
170
+ this.player.$el?.textTracks.addEventListener('addtrack', this._handleEmbedChanged);
171
+ this.player.$el?.textTracks.addEventListener('change', this._handleEmbedChanged);
172
+
173
+ this._toMainPage();
174
+ this._refresh();
175
+
176
+ this.changeLang(this.state.getCCLang());
177
+ }
178
+
179
+ render() {
180
+ this.$el = createElement('div', { class: CN });
181
+
182
+ this.$button = createElement(
183
+ 'button',
184
+ { type: 'button', class: BUTTON_CN },
185
+ this.$el
186
+ );
187
+
188
+ this.$selector = createElement('div', { class: SELECTOR_CN }, this.$el);
189
+ this.$text = createElement('span', { class: TEXT_CN });
190
+ this.$textHolder = createElement(
191
+ 'span',
192
+ { class: TEXT_HOLDER_CN },
193
+ this.$text
194
+ );
195
+
196
+ this._renderMainPage();
197
+ this._renderSettingsPage();
198
+ this._renderAdditionalPage();
199
+ }
200
+
201
+ _handleEmbedChanged() {
202
+ const textTracks = this.player.getTextTracks();
203
+ const sources = this.state.getCCSources();
204
+
205
+ this.$el.style.display = !sources.length && !textTracks?.length ? 'none' : 'block';
206
+
207
+ const addOffButton = () => {
208
+ const offItem = createElement(
209
+ 'button',
210
+ { type: 'button', class: ITEM_CN },
211
+ this.$embedCaptions
212
+ );
213
+
214
+ offItem.textContent = 'None';
215
+ this.addEvent(offItem, 'click', () => {
216
+ each(textTracks, (track) => {
217
+ each(this.$captions, (el) => removeClass(el, ITEM_CHECKED_CN));
218
+ track.mode = 'disabled';
219
+ addClass(offItem, ITEM_CHECKED_CN);
220
+ });
221
+ });
222
+ this.$captions.push(offItem);
223
+ };
224
+
225
+ if (!sources.length) {
226
+ this.$settings.style.display = 'none';
227
+ const offItem = this.$embedCaptions.childNodes[0];
228
+ if (!offItem || offItem?.textContent !== 'None') {
229
+ addOffButton();
230
+ }
231
+ } else {
232
+ this.$settings.style.display = 'block';
233
+ }
234
+
235
+ if (!textTracks?.length) {
236
+ return;
237
+ }
238
+
239
+ each(textTracks, (track) => {
240
+ const alreadyExist = find(this.$captions, (el) => el.textContent === track.label);
241
+
242
+ if (alreadyExist) {
243
+ return;
244
+ }
245
+
246
+ const item = createElement(
247
+ 'button',
248
+ { type: 'button', class: ITEM_CN },
249
+ this.$embedCaptions
250
+ );
251
+
252
+ item.textContent = track.label;
253
+ this.addEvent(item, 'click', () => {
254
+ each(this.$captions, (el) => removeClass(el, ITEM_CHECKED_CN));
255
+ each(this.$translatableCaptions, (el) => removeClass(el, ITEM_CHECKED_CN));
256
+ const idx = this.$captions.findIndex((el) => el === item);
257
+ addClass(this.$captions[idx], ITEM_CHECKED_CN);
258
+ track.mode = 'showing';
259
+ });
260
+
261
+ this.$captions.push(item);
262
+
263
+ if (this.embedCaptionsAutoShow) {
264
+ item.click();
265
+ this.embedCaptionsAutoShow = false;
266
+ }
267
+ });
268
+ }
269
+
270
+ _renderMainPage() {
271
+ this.$mainStep = createElement(
272
+ 'div',
273
+ { class: MAIN_STEP_CN, tabindex: -1 },
274
+ this.$selector
275
+ );
276
+
277
+ createSvg(this.$button, CC);
278
+
279
+ const sources = this.state.getCCSources();
280
+
281
+ this.$el.style.display = sources.length ? 'block' : 'none';
282
+
283
+ const alreadyTranslatedSources = filter(
284
+ sources,
285
+ (s) =>
286
+ s.state !== UNTRANSLATED_TIMEDATA_STATUS &&
287
+ s.state !== TRANSLATABLE_TIMEDATA_STATUS
288
+ );
289
+
290
+ each(alreadyTranslatedSources, (data) => {
291
+ const item = createElement(
292
+ 'button',
293
+ { type: 'button', class: ITEM_CN },
294
+ this.$mainStep
295
+ );
296
+
297
+ item.textContent =
298
+ data.state === UNAPPROVED_TIMEDATA_STATUS
299
+ ? `${data.label} ${this.$t('unapproved-source')}`
300
+ : data.label;
301
+ this.addEvent(item, 'click', () => this.bus.emit('cclang', data.srclang));
302
+ this.$captions.push(item);
303
+ });
304
+
305
+ // Create empty container for embed captions (order is important)
306
+ this.$embedCaptions = createElement(
307
+ 'div',
308
+ {},
309
+ this.$mainStep
310
+ );
311
+
312
+ const $translate = createElement(
313
+ 'button',
314
+ { type: 'button', class: SETTINGS_CN },
315
+ this.$mainStep
316
+ );
317
+ this.$translate = $translate;
318
+ let text = createElement('span', {}, $translate);
319
+ text.textContent = this.$t('translate');
320
+ _createMore($translate);
321
+
322
+ this.$currentCustomTranslate = createElement(
323
+ 'span',
324
+ { class: CURRENT_VALUE_CN },
325
+ this.$translate
326
+ );
327
+ const haveUntranslatedSources =
328
+ alreadyTranslatedSources.length !== sources.length;
329
+ $translate.style.display = haveUntranslatedSources ? 'block' : 'none';
330
+
331
+ this.$settings = createElement(
332
+ 'button',
333
+ { type: 'button', class: SETTINGS_CN },
334
+ this.$mainStep
335
+ );
336
+ text = createElement('span', {}, this.$settings);
337
+ text.textContent = this.$t('settings');
338
+ _createMore(this.$settings);
339
+ }
340
+
341
+ _rerenderLanguages(sources: Player$TimeDataSource[]) {
342
+ const { $mainStep, $translate, $settings } = this;
343
+ this.$el.style.display = sources.length ? 'block' : 'none';
344
+
345
+ if (!$mainStep) {
346
+ return;
347
+ }
348
+
349
+ each(this.$captions, (e) => {
350
+ $mainStep.removeChild(e);
351
+ });
352
+ this.$captions.length = 0;
353
+
354
+ const alreadyTranslatedSources = filter(
355
+ sources,
356
+ (s) =>
357
+ s.state !== UNTRANSLATED_TIMEDATA_STATUS &&
358
+ s.state !== TRANSLATABLE_TIMEDATA_STATUS
359
+ );
360
+
361
+ each(alreadyTranslatedSources, (data) => {
362
+ const item = createElement(
363
+ 'button',
364
+ { type: 'button', class: ITEM_CN },
365
+ this.$mainStep
366
+ );
367
+
368
+ item.textContent =
369
+ data.state === UNAPPROVED_TIMEDATA_STATUS
370
+ ? `${data.label} ${this.$t('unapproved-source')}`
371
+ : data.label;
372
+ this.addEvent(item, 'click', () => this.bus.emit('cclang', data.srclang));
373
+ this.$captions.push(item);
374
+ });
375
+
376
+ if ($translate) {
377
+ $mainStep.removeChild($translate);
378
+ const haveUtranslatedSources =
379
+ alreadyTranslatedSources.length !== sources.length;
380
+ $translate.style.display = haveUtranslatedSources ? 'block' : 'none';
381
+ append($mainStep, $translate);
382
+ }
383
+
384
+ if ($settings) {
385
+ $mainStep.removeChild($settings);
386
+ append($mainStep, $settings);
387
+ }
388
+ }
389
+
390
+ _renderSettingsPage() {
391
+ this.$settingsStep = createElement(
392
+ 'div',
393
+ { class: SETTINGS_STEP_CN },
394
+ this.$selector
395
+ );
396
+
397
+ this.$toMain = createElement(
398
+ 'button',
399
+ { type: 'button', class: BACK_BTN_CN },
400
+ this.$settingsStep
401
+ );
402
+
403
+ const $backSpan = createElement('span', { class: 'arrow' }, this.$toMain);
404
+ createSvg($backSpan, BACK, { class: 'arrow' });
405
+
406
+ const $backText = createElement('span', {}, this.$toMain);
407
+ $backText.textContent = this.$t('cc');
408
+
409
+ const $settingsWrapper = createElement(
410
+ 'div',
411
+ { class: `${SETTINGS_WRAPPER_CN} tab-content` },
412
+ this.$settingsStep
413
+ );
414
+
415
+ this.$fontColorSettings = createElement(
416
+ 'button',
417
+ { type: 'button' },
418
+ $settingsWrapper
419
+ );
420
+ let text = createElement('span', {}, this.$fontColorSettings);
421
+ text.textContent = this.$t('settings-fontcolor');
422
+ _createMore(this.$fontColorSettings);
423
+ this.$currentColor = createElement(
424
+ 'span',
425
+ { class: CURRENT_VALUE_CN },
426
+ this.$fontColorSettings
427
+ );
428
+ let color = find(CC_COLORS, (c) => c.value === this.state.getCCTextColor());
429
+ this.$currentColor.textContent =
430
+ color && color.key ? this.$t(color.key) : '';
431
+
432
+ this.$fontSizeSettings = createElement(
433
+ 'button',
434
+ { type: 'button' },
435
+ $settingsWrapper
436
+ );
437
+ text = createElement('span', {}, this.$fontSizeSettings);
438
+ text.textContent = this.$t('settings-fontsize');
439
+ _createMore(this.$fontSizeSettings);
440
+ this.$currentSize = createElement(
441
+ 'span',
442
+ { class: CURRENT_VALUE_CN },
443
+ this.$fontSizeSettings
444
+ );
445
+ this.$currentSize.textContent = this.state.getCCFontSize();
446
+
447
+ this.$fontFamilySettings = createElement(
448
+ 'button',
449
+ { type: 'button' },
450
+ $settingsWrapper
451
+ );
452
+ text = createElement('span', {}, this.$fontFamilySettings);
453
+ text.textContent = this.$t('settings-fontfamily');
454
+ _createMore(this.$fontFamilySettings);
455
+ this.$currentFamily = createElement(
456
+ 'span',
457
+ { class: CURRENT_VALUE_CN },
458
+ this.$fontFamilySettings
459
+ );
460
+ const family = find(
461
+ CC_FONT_FAMILIES,
462
+ (c) => c.value === this.state.getCCFontFamily()
463
+ );
464
+ this.$currentFamily.textContent =
465
+ family && family.key ? this.$t(family.key) : '';
466
+
467
+ this.$backgroundColorSettings = createElement(
468
+ 'button',
469
+ { type: 'button' },
470
+ $settingsWrapper
471
+ );
472
+ text = createElement('span', {}, this.$backgroundColorSettings);
473
+ text.textContent = this.$t('settings-background');
474
+ _createMore(this.$backgroundColorSettings);
475
+ this.$currentBgColor = createElement(
476
+ 'span',
477
+ { class: CURRENT_VALUE_CN },
478
+ this.$backgroundColorSettings
479
+ );
480
+ color = find(CC_COLORS, (c) => c.value === this.state.getCCBgColor());
481
+ this.$currentBgColor.textContent =
482
+ color && color.key ? this.$t(color.key) : '';
483
+
484
+ if (this.options.showCCLayout) {
485
+ const $locationSettings = createElement(
486
+ 'button',
487
+ { type: 'button' },
488
+ $settingsWrapper
489
+ );
490
+ this.$locationSettings = $locationSettings;
491
+ text = createElement('span', {}, $locationSettings);
492
+ text.textContent = this.$t('settings-captionlocations');
493
+ _createMore($locationSettings);
494
+ this.$currentLocation = createElement(
495
+ 'span',
496
+ { class: CURRENT_VALUE_CN },
497
+ this.$locationSettings
498
+ );
499
+ const location = find(
500
+ CC_LOCATIONS,
501
+ (c) => c.value === this.state.getCCLocation()
502
+ );
503
+ this.$currentLocation.textContent =
504
+ location && location.key ? this.$t(location.key) : '';
505
+ }
506
+
507
+ const $translateWrapper = createElement(
508
+ 'div',
509
+ { class: `${TRANSLATE_WRAPPER_CN} tab-content` },
510
+ this.$settingsStep
511
+ );
512
+
513
+ const translatableSources = filter(
514
+ this.state.getCCSources(),
515
+ (s) =>
516
+ s.state === UNTRANSLATED_TIMEDATA_STATUS ||
517
+ s.state === TRANSLATABLE_TIMEDATA_STATUS
518
+ );
519
+
520
+ each(translatableSources, (data) => {
521
+ const item = createElement(
522
+ 'button',
523
+ { type: 'button', class: ITEM_CN },
524
+ $translateWrapper
525
+ );
526
+ item.textContent = data.label;
527
+ this.addEvent(item, 'click', () => {
528
+ this.bus.emit('cclang', data.srclang);
529
+ this._toMainPage();
530
+ });
531
+ this.$translatableCaptions.push(item);
532
+ });
533
+ }
534
+
535
+ _renderAdditionalPage() {
536
+ this.$additionalStep = createElement(
537
+ 'div',
538
+ { class: ADDITIONAL_STEP_CN },
539
+ this.$selector
540
+ );
541
+ this.$toSettings = createElement(
542
+ 'button',
543
+ { type: 'button', class: BACK_BTN_CN },
544
+ this.$additionalStep
545
+ );
546
+ const $backSpan = createElement(
547
+ 'span',
548
+ { class: 'arrow' },
549
+ this.$toSettings
550
+ );
551
+ createSvg($backSpan, BACK, { class: 'arrow' });
552
+ this.$additionalBackText = createElement('span', {}, this.$toSettings);
553
+ this.$additionalStepWrapper = createElement(
554
+ 'div',
555
+ { class: 'tab-content' },
556
+ this.$additionalStep
557
+ );
558
+ }
559
+
560
+ onStatechanged(state: Player$State, old: Player$State) {
561
+ if (state.cc.sources !== old.cc.sources) {
562
+ this._rerenderLanguages(state.cc.sources);
563
+ this.changeLang(state.cc.lang);
564
+ } else if (state.cc.lang !== old.cc.lang) {
565
+ this.changeLang(state.cc.lang);
566
+ } else if (state.cc.timedata !== old.cc.timedata) {
567
+ this.onLoadingTimedata();
568
+ } else if (
569
+ state.cc.location !== old.cc.location ||
570
+ state.cc.fontFamily !== old.cc.fontFamily ||
571
+ state.cc.fontSize !== old.cc.fontSize ||
572
+ state.cc.bgcolor !== old.cc.bgcolor ||
573
+ state.cc.color !== old.cc.color
574
+ ) {
575
+ this._refresh();
576
+ }
577
+ }
578
+
579
+ onLoadingTimedata() {
580
+ const cc = this.state.getSelectedCC();
581
+ const { $currentCustomTranslate, $translate } = this;
582
+ if ($currentCustomTranslate && $translate) {
583
+ removeClass($translate, PROGRESS_SETTINGS_CN);
584
+ $currentCustomTranslate.textContent = '';
585
+ if (cc && !cc.state) {
586
+ $currentCustomTranslate.textContent = cc.label;
587
+ }
588
+ }
589
+ this.onTimeupdate();
590
+ }
591
+
592
+ changeLang(lang: ?string) {
593
+ each(this.$captions, (el) => removeClass(el, ITEM_CHECKED_CN));
594
+ each(this.$translatableCaptions, (el) => removeClass(el, ITEM_CHECKED_CN));
595
+
596
+ const textTracks = this.player.getTextTracks();
597
+
598
+ if (textTracks?.length) {
599
+ each(textTracks, (track) => {
600
+ track.mode = 'disabled';
601
+ });
602
+ }
603
+
604
+ const sources = this.state.getCCSources();
605
+ const alreadyTranslatedSources = filter(
606
+ sources,
607
+ (s) =>
608
+ s.state !== UNTRANSLATED_TIMEDATA_STATUS &&
609
+ s.state !== TRANSLATABLE_TIMEDATA_STATUS
610
+ );
611
+
612
+ const { $currentCustomTranslate, $translate } = this;
613
+ if ($currentCustomTranslate) {
614
+ $currentCustomTranslate.textContent = '';
615
+ }
616
+
617
+ let idx = findIndex(alreadyTranslatedSources, (d) => d.srclang === lang);
618
+
619
+ if (idx === -1) {
620
+ const translatableSources = filter(sources, (s) => !s.state);
621
+ idx = findIndex(translatableSources, (d) => d.srclang === lang);
622
+ if (this.$translatableCaptions[idx]) {
623
+ addClass(this.$translatableCaptions[idx], ITEM_CHECKED_CN);
624
+ }
625
+ if ($currentCustomTranslate && translatableSources[idx]) {
626
+ $currentCustomTranslate.textContent = this.$t('translating');
627
+ }
628
+ if ($translate) {
629
+ addClass($translate, PROGRESS_SETTINGS_CN);
630
+ }
631
+ } else {
632
+ if (this.$captions[idx]) {
633
+ addClass(this.$captions[idx], ITEM_CHECKED_CN);
634
+ }
635
+ if (!lang) {
636
+ removeClass(this.$text, TEXT_VISIBLE_CN);
637
+ }
638
+ }
639
+ }
640
+
641
+ onTimeupdate() {
642
+ const lang = this.state.getCCLang();
643
+ const timedata = this.state.getCCTimeData();
644
+ if (!lang) {
645
+ return;
646
+ }
647
+
648
+ if (isEmpty(timedata)) {
649
+ removeClass(this.$text, TEXT_VISIBLE_CN);
650
+ return;
651
+ }
652
+
653
+ const ct = this.player.getCurrentTime();
654
+ const item = find(timedata, ({ from, to }) => inRange(ct, from, to));
655
+
656
+ if (item) {
657
+ this.$textHolder.innerHTML = item.text.split('\n').join('<br>');
658
+ addClass(this.$text, TEXT_VISIBLE_CN);
659
+ } else {
660
+ removeClass(this.$text, TEXT_VISIBLE_CN);
661
+ }
662
+ }
663
+
664
+ onShowpopup($selector: ?HTMLElement) {
665
+ if ($selector === this.$selector) {
666
+ if (hasClass(this.$selector, SELECTOR_MAIN_STEP_CN)) {
667
+ openerHeightChecker(this.player, this.$mainStep);
668
+ } else if (hasClass(this.$selector, SELECTOR_SETTINGS_STEP_CN)) {
669
+ openerHeightChecker(this.player, this.$settingsStep);
670
+ } else if (hasClass(this.$selector, SELECTOR_ADDITIONAL_STEP_CN)) {
671
+ openerHeightChecker(this.player, this.$additionalStep);
672
+ }
673
+ }
674
+ }
675
+
676
+ _toMainPage() {
677
+ addClass(this.$selector, SELECTOR_MAIN_STEP_CN);
678
+ removeClass(this.$selector, SELECTOR_SETTINGS_STEP_CN);
679
+ removeClass(this.$selector, SELECTOR_ADDITIONAL_STEP_CN);
680
+ removeClass(this.$selector, SELECTOR_TRANSLATE_STEP_CN);
681
+
682
+ openerHeightChecker(this.player, this.$mainStep);
683
+
684
+ // Accessability: Focus on first item of main page
685
+ const mainStepItems = document.querySelectorAll(`.${MAIN_STEP_CN} > .${ITEM_CN}`);
686
+ if (mainStepItems[0]) mainStepItems[0].focus();
687
+ }
688
+
689
+ _toSettingsPage() {
690
+ addClass(this.$selector, SELECTOR_SETTINGS_STEP_CN);
691
+ removeClass(this.$selector, SELECTOR_MAIN_STEP_CN);
692
+ removeClass(this.$selector, SELECTOR_ADDITIONAL_STEP_CN);
693
+ removeClass(this.$selector, SELECTOR_TRANSLATE_STEP_CN);
694
+
695
+ openerHeightChecker(this.player, this.$settingsStep);
696
+
697
+ // Accessability: Focus on first item of settings page
698
+ const backBtn = document.querySelector(`.${BACK_BTN_CN}`);
699
+ backBtn.focus();
700
+ }
701
+
702
+ _toAdditionalPage() {
703
+ addClass(this.$selector, SELECTOR_ADDITIONAL_STEP_CN);
704
+ removeClass(this.$selector, SELECTOR_SETTINGS_STEP_CN);
705
+ removeClass(this.$selector, SELECTOR_MAIN_STEP_CN);
706
+ removeClass(this.$selector, SELECTOR_TRANSLATE_STEP_CN);
707
+
708
+ const items = toArray(this.$additionalStepWrapper.childNodes);
709
+ each(items, (btn) => this.$additionalStepWrapper.removeChild(btn));
710
+
711
+ openerHeightChecker(this.player, this.$additionalStep);
712
+ }
713
+
714
+ _toTranslatePage() {
715
+ if (this.state.isTranslatingCC()) {
716
+ return;
717
+ }
718
+
719
+ addClass(this.$selector, SELECTOR_TRANSLATE_STEP_CN);
720
+ removeClass(this.$selector, SELECTOR_SETTINGS_STEP_CN);
721
+ removeClass(this.$selector, SELECTOR_MAIN_STEP_CN);
722
+ removeClass(this.$selector, SELECTOR_ADDITIONAL_STEP_CN);
723
+
724
+ openerHeightChecker(this.player, this.$settingsStep);
725
+
726
+ // Accessability: Focus on first item of translate page
727
+ const backBtn = document.querySelector(`.${BACK_BTN_CN}`);
728
+ backBtn.focus();
729
+ }
730
+
731
+ _prepareAdditionalPage(
732
+ tkey: $Keys<Player$Translation>,
733
+ fnc: $Keys<StateEntity>,
734
+ event: string,
735
+ options: CCOption<any>[]
736
+ ) {
737
+ this._toAdditionalPage();
738
+ this.$additionalBackText.textContent = this.$t(tkey);
739
+ // Accessability: Focus on additional menu back button
740
+ this.$toSettings.focus();
741
+ const buttons = [];
742
+
743
+ // $FlowFixMe
744
+ const value = this.state[fnc]();
745
+
746
+ each(options, (c) => {
747
+ const button = createElement(
748
+ 'button',
749
+ { type: 'button', class: ITEM_CN },
750
+ this.$additionalStepWrapper
751
+ );
752
+ const key = c.key;
753
+ button.textContent = key ? this.$t(key) : c.label;
754
+
755
+ if (value === c.value) {
756
+ addClass(button, ITEM_CHECKED_CN);
757
+ }
758
+
759
+ this.addEvent(button, 'click', () => {
760
+ each(buttons, (b) => removeClass(b, ITEM_CHECKED_CN));
761
+ addClass(button, ITEM_CHECKED_CN);
762
+ this.bus.emit(event, c.value);
763
+ this._toSettingsPage();
764
+ });
765
+ buttons.push(button);
766
+ });
767
+ }
768
+
769
+ _toColorPage() {
770
+ this._prepareAdditionalPage(
771
+ 'settings-fontcolor',
772
+ 'getCCTextColor',
773
+ 'cccolor',
774
+ CC_COLORS
775
+ );
776
+ }
777
+
778
+ _toBackgroundColorPage() {
779
+ this._prepareAdditionalPage(
780
+ 'settings-background',
781
+ 'getCCBgColor',
782
+ 'ccbgcolor',
783
+ CC_COLORS
784
+ );
785
+ }
786
+
787
+ _toSizesPage() {
788
+ this._prepareAdditionalPage(
789
+ 'settings-fontsize',
790
+ 'getCCFontSize',
791
+ 'ccfontsize',
792
+ CC_FONT_SIZES
793
+ );
794
+ }
795
+
796
+ _toFontFamilyPage() {
797
+ this._prepareAdditionalPage(
798
+ 'settings-fontfamily',
799
+ 'getCCFontFamily',
800
+ 'ccfontfamily',
801
+ CC_FONT_FAMILIES
802
+ );
803
+ }
804
+
805
+ _toLocationsPage() {
806
+ this._prepareAdditionalPage(
807
+ 'settings-captionlocations',
808
+ 'getCCLocation',
809
+ 'cclocation',
810
+ CC_LOCATIONS
811
+ );
812
+ }
813
+
814
+ _detectLabel(options: CCOption<any>[], value: string): string {
815
+ const option = find(options, (c) => c.value === value);
816
+ if (!option) {
817
+ return '';
818
+ }
819
+ const key = option.key;
820
+ return key ? this.$t(key) : option.label || '';
821
+ }
822
+
823
+ _refresh() {
824
+ const color = this.state.getCCTextColor();
825
+ const bgcolor = this.state.getCCBgColor();
826
+ const fontSize = this.state.getCCFontSize();
827
+ const fontFamily = this.state.getCCFontFamily();
828
+
829
+ this.$textHolder.style.color = color;
830
+ this.$textHolder.style.backgroundColor = bgcolor;
831
+ this.$textHolder.style.fontSize = fontSize;
832
+ this.$textHolder.style.fontFamily = fontFamily;
833
+
834
+ this.$currentColor.textContent = this._detectLabel(CC_COLORS, color);
835
+ this.$currentBgColor.textContent = this._detectLabel(CC_COLORS, bgcolor);
836
+ this.$currentSize.textContent = this._detectLabel(CC_FONT_SIZES, fontSize);
837
+ this.$currentFamily.textContent = this._detectLabel(
838
+ CC_FONT_FAMILIES,
839
+ fontFamily
840
+ );
841
+ if (this.$currentLocation) {
842
+ this.$currentLocation.textContent = this._detectLabel(
843
+ CC_LOCATIONS,
844
+ this.state.getCCLocation()
845
+ );
846
+ }
847
+ }
848
+
849
+ getElement() {
850
+ return this.$el;
851
+ }
852
+
853
+ getOuter() {
854
+ return this.$text;
855
+ }
856
+
857
+ destroy() {
858
+ super.destroy();
859
+ if (this.$el.parentNode) {
860
+ this.$el.parentNode.removeChild(this.$el);
861
+ }
862
+ if (this.$text.parentNode) {
863
+ this.$text.parentNode.removeChild(this.$text);
864
+ }
865
+ }
866
+ }
867
+
868
+ function _createMore($parent: HTMLElement) {
869
+ addClass($parent, MORE_CN);
870
+ const span = createElement('span', { class: 'arrow' }, $parent);
871
+ createSvg(span, NEXT);
872
+
873
+ return span;
874
+ }