ravnur-player-public 3.4.2 → 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 (262) 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 +1 -5
  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
  261. package/dist/.DS_Store +0 -0
  262. package/dist/cdn/.DS_Store +0 -0
@@ -0,0 +1,102 @@
1
+ import BaseExtension from './base';
2
+
3
+ import { createElement, removeClass, addClass } from '../utils';
4
+
5
+ import createSvg, { PAUSE, PLAY, REPLAY } from '../svgs';
6
+
7
+ import popover from './helpers/popover';
8
+
9
+ const CN = 'rmp-ext-play';
10
+ const PLAYING_CN = `${CN}--playing`;
11
+ const PAUSED_CN = `${CN}--paused`;
12
+ const ENDED_CN = `${CN}--ended`;
13
+
14
+ export default class PlayExtension extends BaseExtension {
15
+ constructor(player, state, options) {
16
+ super(player, state, options, ['pause', 'play', 'ended', 'seeking']);
17
+ this.logger.debug('play:extension:creating');
18
+
19
+ this.render();
20
+ this.addEvent(this.$el, 'click', this.toggle.bind(this));
21
+
22
+ this.logger.debug('play:extension:created');
23
+ }
24
+
25
+ render() {
26
+ /** @type {HTMLMediaElement} */
27
+ this.$el = createElement('button', { type: 'button', class: `${CN} ${PAUSED_CN}` });
28
+
29
+ setTimeout(() => {
30
+ const text = () => {
31
+ if (this.player.isEnded()) {
32
+ return this.$t('replay');
33
+ } else if (this.player.isPaused()) {
34
+ return this.$t('play');
35
+ }
36
+ return this.$t('pause');
37
+ };
38
+ popover(this, { $button: this.$el, text });
39
+ });
40
+
41
+ createSvg(this.$el, PLAY, {class: 'play'});
42
+ createSvg(this.$el, PAUSE, {class: 'pause'});
43
+ createSvg(this.$el, REPLAY, {class: 'replay'});
44
+ }
45
+
46
+ toggle() {
47
+ if (this.player.isPaused() || this.player.isEnded()) {
48
+ this.player.play();
49
+ } else {
50
+ this.player.pause();
51
+ }
52
+ }
53
+
54
+ onPause() {
55
+ this.$el.setAttribute('aria-label', 'Play');
56
+ removeClass(this.$el, PLAYING_CN);
57
+ removeClass(this.$el, ENDED_CN);
58
+ addClass(this.$el, PAUSED_CN);
59
+ }
60
+
61
+ onPlay() {
62
+ this.$el.setAttribute('aria-label', 'Pause');
63
+ removeClass(this.$el, PAUSED_CN);
64
+ removeClass(this.$el, ENDED_CN);
65
+ addClass(this.$el, PLAYING_CN);
66
+ }
67
+
68
+ onEnded() {
69
+ if (this.options.playLoop) {
70
+ this.player.play();
71
+ } else {
72
+ this.$el.setAttribute('aria-label', 'Reply');
73
+ removeClass(this.$el, PAUSED_CN);
74
+ removeClass(this.$el, PLAYING_CN);
75
+ addClass(this.$el, ENDED_CN);
76
+ }
77
+ }
78
+
79
+ onSeeking() {
80
+ removeClass(this.$el, ENDED_CN);
81
+ removeClass(this.$el, PAUSED_CN);
82
+ removeClass(this.$el, PLAYING_CN);
83
+
84
+ if (this.player.isPaused()) {
85
+ addClass(this.$el, PAUSED_CN);
86
+ } else {
87
+ addClass(this.$el, PLAYING_CN);
88
+ }
89
+ }
90
+
91
+ getElement() {
92
+ return this.$el;
93
+ }
94
+
95
+ destroy() {
96
+ super.destroy();
97
+
98
+ const parent = this.$el.parentNode;
99
+ parent.removeChild(this.$el);
100
+ this.$el = null;
101
+ }
102
+ }
@@ -0,0 +1,47 @@
1
+ import BaseExtension from './base';
2
+
3
+ import { createElement, addClass } from '../utils';
4
+
5
+ const CN = 'rmp-ext-poster';
6
+ const VIDEO_CN = `${CN}--video`;
7
+ const HIDDEN_CN = `${CN}--hidden`;
8
+
9
+ export default class PosterExtension extends BaseExtension {
10
+ constructor(player, state, options) {
11
+ super(player, state, options);
12
+ this.logger.debug('poster:extension:creating');
13
+
14
+ this._hidePoster = this._hidePoster.bind(this);
15
+ this.render();
16
+
17
+ this.logger.debug('poster:extension:created');
18
+ }
19
+
20
+ render() {
21
+ this.$outer = createElement('div', { class: CN, tabindex: -1 });
22
+ const img = createElement('img', { src: this.options.poster }, this.$outer);
23
+ img.setAttribute('aria-label', this.options.title);
24
+ if (!this.options.isAudio) {
25
+ addClass(this.$outer, VIDEO_CN);
26
+ this.bus.on('play', this._hidePoster);
27
+ }
28
+ }
29
+
30
+ _hidePoster() {
31
+ addClass(this.$outer, HIDDEN_CN);
32
+ }
33
+
34
+ getOuter() {
35
+ return this.$outer;
36
+ }
37
+
38
+ destroy() {
39
+ this.bus.off('play', this._hidePoster);
40
+ super.destroy();
41
+ if (this.$outer) {
42
+ const parent = this.$outer.parentNode;
43
+ parent.removeChild(this.$outer);
44
+ this.$outer = null;
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,44 @@
1
+ import BaseExtension from './base';
2
+
3
+ import createElement from '../utils/createElement';
4
+
5
+ import popover from './helpers/popover';
6
+
7
+ import createSvg, { PREV_TRACK } from '../svgs';
8
+
9
+ const CN = 'rmp-ext-prev-frame';
10
+
11
+ export default class PrevFrameExtension extends BaseExtension {
12
+ constructor(player, state, options) {
13
+ super(player, state, options);
14
+ this.logger.debug('prevframe:extension:creating');
15
+
16
+ this.render();
17
+ this.addEvent(this.$el, 'click', this.prevframe.bind(this));
18
+
19
+ this.logger.debug('prevframe:extension:created');
20
+ }
21
+
22
+ render() {
23
+ /** @type {HTMLMediaElement} */
24
+ this.$el = createElement('button', { type: 'button', class: CN });
25
+ setTimeout(() => popover(this, { $button: this.$el, text: this.$t('prevframe') }) );
26
+ createSvg(this.$el, PREV_TRACK);
27
+ }
28
+
29
+ prevframe() {
30
+ this.player.prevFrame();
31
+ }
32
+
33
+ getElement() {
34
+ return this.$el;
35
+ }
36
+
37
+ destroy() {
38
+ super.destroy();
39
+
40
+ const parent = this.$el.parentNode;
41
+ parent.removeChild(this.$el);
42
+ this.$el = null;
43
+ }
44
+ }
@@ -0,0 +1,48 @@
1
+ import BaseExtension from './base';
2
+
3
+ import createElement from '../utils/createElement';
4
+ import createSvg, { PREV_TRACK } from '../svgs';
5
+
6
+ const CN = 'rmp-ext-prev';
7
+ const BUTTON_CN = `${CN}__button`;
8
+ const THUMBNAIL_CN = `${CN}__thumbnail`;
9
+
10
+ export default class PrevExtension extends BaseExtension {
11
+ /** @type {HTMLElement} */
12
+ $outer;
13
+ /** @type {HTMLButtonElement} */
14
+ $button;
15
+
16
+ constructor(player, state, options) {
17
+ super(player, state, options);
18
+ this.logger.debug('prev:extension:creating');
19
+
20
+ this.render();
21
+
22
+ const bus = this.options.bus;
23
+
24
+ this.addEvent(this.$outer, 'click', () => bus.emit('prevtrack') );
25
+
26
+ this.logger.debug('prev:extension:created');
27
+ }
28
+
29
+ render() {
30
+ const title = this.options.prevTrackTitle;
31
+ this.$outer = createElement('div', { class: CN, title });
32
+ this.$button = createElement('button', { type: 'button', class: BUTTON_CN }, this.$outer);
33
+ createSvg(this.$button, PREV_TRACK);
34
+ this.$button.setAttribute('aria-label', 'Go to previous track');
35
+
36
+ createElement('img', { src: this.options.prevTrackThumbnails, class: THUMBNAIL_CN }, this.$outer);
37
+ }
38
+
39
+ getOuter() {
40
+ return this.$outer;
41
+ }
42
+
43
+ destroy() {
44
+ super.destroy();
45
+ this.$outer.parentNode.removeChild(this.$outer);
46
+ this.$outer = null;
47
+ }
48
+ }
@@ -0,0 +1,465 @@
1
+ import BaseExtension from './base';
2
+
3
+ import {
4
+ existy, createElement, last, find, inRange, isEmpty, each, removeEvent, addEvent, isNotEmpty, hasClass
5
+ } from '../utils';
6
+
7
+ import loadVTT from './helpers/vtt-loader';
8
+ import popoverPosition from './helpers/popoverPosition';
9
+
10
+ import type { Player$State } from '../types/State';
11
+ import addClass from '../utils/addClass';
12
+
13
+ const CN = 'rmp-ext-progress';
14
+ const CURRENT_TIME_CN = `${CN}__current`;
15
+ const DURATION_TIME_CN = `${CN}__duration`;
16
+ const BAR_CN = `${CN}__bar`;
17
+ const BAR_PADDING_CN = `${CN}__bar-padding`;
18
+ const HANDLE_CN = `${CN}__handle`;
19
+ const OVERALY_CN = `${CN}__overlay`;
20
+ const PLAY_PROGRESS_CN = `${CN}__play-progress`;
21
+ const PROGRESS_CN = `${CN}__progress`;
22
+ const C2PA_STATUS_CN = `${CN}__c2pa-status`;
23
+ const C2PA_STATUS_INVALID_SEGMENT_CN = `${C2PA_STATUS_CN}--invalid-segment`;
24
+
25
+ const TOOLTIP_CN = `${CN}__tooltip`;
26
+ const TOOLTIP_VISIBLE_CN = `${CN}__tooltip--visible`;
27
+ const THUMBNAIL_CN = `${CN}__thumbmail`;
28
+ const THUMBNAIL_TIME_CN = `${CN}__thumbmail-time`;
29
+ const DISABLE_SLIDE_CN = `${CN}__generic-live-stream`;
30
+ const HIDDEN_CN = `${CN}__hidden`;
31
+
32
+ const CHAPTER_BUBBLE_CN = `${CN}__chapter-bubble`;
33
+
34
+ const SECS_IN_HOUR = 3600; // 60 * 60
35
+ const SECS_IN_MIN = 60;
36
+
37
+ const TEN = 10;
38
+
39
+ export default class ProgressExtension extends BaseExtension {
40
+ thumbnails = [];
41
+ _dragNdropTokens = [];
42
+ _isLoadedMetadata = false;
43
+ _chapters = [];
44
+ _bubbles = [];
45
+
46
+ constructor(player, state, options) {
47
+ super(player, state, options, [
48
+ 'progress', 'timeupdate', 'loadedmetadata', 'canplaythrough', 'statechanged', 'c2pafragment', 'durationchange'
49
+ ]);
50
+ this.logger.debug('progress:extension:creating');
51
+
52
+ this.onLeave = this.onLeave.bind(this);
53
+ this.onHover = this.onHover.bind(this);
54
+ this.onHandleMouseUp = this.onHandleMouseUp.bind(this);
55
+ this.onHandleMouseDown = this.onHandleMouseDown.bind(this);
56
+ this.onHandleMouseMove = this.onHandleMouseMove.bind(this);
57
+ this.onTouchStart = this.onTouchStart.bind(this);
58
+ this.onTouchMove = this.onTouchMove.bind(this);
59
+ this.onTouchEnd = this.onTouchEnd.bind(this);
60
+
61
+ this.render();
62
+
63
+ this.addEvent(this.$bar, 'click', e => {
64
+ const target = e.target || e.srcElement;
65
+ if (target === this.$handle) {
66
+ e.stopPropagation();
67
+ } else {
68
+ const time = this._getTimeByEventPosition(e);
69
+ player.setCurrentTime(time);
70
+ this._moveHandle2time(time);
71
+ }
72
+ });
73
+
74
+ this.addEvent(this.$bar, 'mouseleave', this.onLeave);
75
+ this.addEvent(this.$bar, 'mousemove', this.onHover);
76
+ this.addEvent(this.$handle, 'mousedown', this.onHandleMouseDown);
77
+ this.addEvent(this.$handle, 'touchstart', this.onTouchStart);
78
+
79
+ if (existy(options.preview)) {
80
+ loadVTT(options.preview, resp => this.thumbnails = resp);
81
+ }
82
+
83
+ this.logger.debug('progress:extension:created');
84
+ }
85
+
86
+ render() {
87
+ /** @type {HTMLMediaElement} */
88
+ this.$el = createElement('span', { class: CN });
89
+
90
+ /** @type {HTMLMediaElement} */
91
+ const extensions = this.player.$el.parentNode.querySelector('.rmp-extensions');
92
+
93
+ this.$bar = createElement('span', { class: BAR_CN, tabindex: 0 }, extensions);
94
+ createElement('span', { class: BAR_PADDING_CN }, this.$bar);
95
+ createElement('span', { class: OVERALY_CN }, this.$bar);
96
+
97
+ /** @type {HTMLMediaElement} */
98
+ this.$handle = createElement('span', { class: HANDLE_CN }, this.$bar);
99
+ /** @type {HTMLMediaElement} */
100
+ this.$current = createElement('span', { class: CURRENT_TIME_CN }, this.$el);
101
+ /** @type {HTMLMediaElement} */
102
+ const span = createElement('span', { }, this.$el);
103
+ span.textContent = '/';
104
+ this.$duration = createElement('span', { class: DURATION_TIME_CN }, this.$el);
105
+ this.$c2paStatus = createElement('span', { class: C2PA_STATUS_CN }, this.$bar);
106
+ this.$playProgress = createElement('span', { class: PLAY_PROGRESS_CN }, this.$bar);
107
+ this.$progress = createElement('span', { class: PROGRESS_CN }, this.$bar);
108
+
109
+ this.$tooltip = createElement('div', { class: TOOLTIP_CN }, this.$el);
110
+ this.$thumbnail = createElement('div', { class: THUMBNAIL_CN }, this.$tooltip);
111
+ this.$thumbnailTime = createElement('span', { class: THUMBNAIL_TIME_CN }, this.$tooltip);
112
+
113
+ if (this.options.isProgressLiveStream) {
114
+ addClass(this.$el, DISABLE_SLIDE_CN);
115
+ addClass(this.$bar, DISABLE_SLIDE_CN);
116
+ }
117
+
118
+ if (this.options.isLive) {
119
+ addClass(this.$el, HIDDEN_CN);
120
+ }
121
+
122
+ if ( this.options.isMobile ) {
123
+ this.$tooltip.style.visibility = 'hidden';
124
+ }
125
+ }
126
+
127
+ onTimeupdate() {
128
+ if ( isNotEmpty(this._dragNdropTokens) ) {
129
+ return;
130
+ }
131
+
132
+ if (this.options.isLive && this.player?.isLive()) {
133
+ this.$progress.style.width = '100%';
134
+ this.$handle.style.left = this.$playProgress.style.width = '100%';
135
+ this.$current.textContent = secondsToTimeCode(+this._getCurrentTime());
136
+ // Accessibility: Set correct values for screen readers
137
+ this.$bar.setAttribute('aria-label', this._getAriaLabelValue());
138
+ } else {
139
+ this._moveHandle2time(this._getCurrentTime());
140
+ }
141
+ }
142
+
143
+ _moveHandle2time(time) {
144
+ const duration = this.player.getDuration();
145
+ const { timecode, clip } = this.options;
146
+ if (existy(time) && duration) {
147
+ this.$current.textContent = secondsToTimeCode(+time);
148
+ // Accessibility: Set correct values for screen readers
149
+ this.$bar.setAttribute('aria-label', this._getAriaLabelValue());
150
+ let percent;
151
+
152
+ if ( clip ) {
153
+ let [ start, end ] = clip;
154
+ start = start || 0;
155
+ end = end ? end + timecode : duration + start;
156
+ percent = (time - start - timecode) / (end - start - timecode);
157
+ } else {
158
+ percent = (time - timecode) / (duration - timecode);
159
+ }
160
+
161
+ const position = (100 * percent).toFixed(2) + '%';
162
+ this.$handle.style.left = this.$playProgress.style.width = position;
163
+ }
164
+ }
165
+
166
+ onProgress() {
167
+ this.$duration.textContent = secondsToTimeCode(this._getDuration()); // for live streams
168
+ this.$progress.style.width = 100 * this.player.getBufferedPercent() + '%';
169
+ }
170
+
171
+ onHandleMouseDown() {
172
+ this._dragNdropTokens.push( addEvent(document, 'mouseup', this.onHandleMouseUp) );
173
+ this._dragNdropTokens.push( addEvent(document, 'mousemove', this.onHandleMouseMove) );
174
+ }
175
+
176
+ onTouchStart() {
177
+ this._dragNdropTokens.push( addEvent(document, 'touchend', this.onTouchEnd) );
178
+ this._dragNdropTokens.push( addEvent(document, 'touchmove', this.onTouchMove) );
179
+ }
180
+
181
+ onHandleMouseUp(e) {
182
+ e.stopPropagation();
183
+ e.preventDefault();
184
+ each(this._dragNdropTokens, removeEvent);
185
+ this._dragNdropTokens.length = 0;
186
+ this.player.setCurrentTime(this._getTimeByEventPosition(e));
187
+ }
188
+
189
+ onTouchMove(e) {
190
+ e.stopPropagation();
191
+ e.preventDefault();
192
+ const rect = this.$bar.getBoundingClientRect();
193
+ const touch = e.touches[0];
194
+ let left = Math.max(rect.left, touch.clientX);
195
+ left = Math.min(rect.right, left);
196
+ left -= rect.left;
197
+ left = 100 * (left / this.$bar.offsetWidth) + '%';
198
+ this.$handle.style.left = this.$playProgress.style.width = left;
199
+ const time = this._getTimeByEventPosition(touch);
200
+ this.player.setCurrentTime(time);
201
+ this.bus.emit('heartbeatextensions');
202
+ }
203
+
204
+ onTouchEnd(e) {
205
+ e.stopPropagation();
206
+ e.preventDefault();
207
+ each(this._dragNdropTokens, removeEvent);
208
+ this._dragNdropTokens.length = 0;
209
+ }
210
+
211
+ onHandleMouseMove(e) {
212
+ const rect = this.$bar.getBoundingClientRect();
213
+ // const parentRect = this.player.getElement().parentNode.getBoundingClientRect();
214
+ let left = Math.max(rect.left, e.clientX);
215
+ left = Math.min(rect.right, left);
216
+ left -= rect.left;
217
+ left = 100 * (left / this.$bar.offsetWidth) + '%';
218
+ this.$handle.style.left = this.$playProgress.style.width = left;
219
+ }
220
+
221
+ onDurationchange() {
222
+ if (!this.options.isLive && !this.isProgressLiveStream) {
223
+ this._updateTimes();
224
+ }
225
+ }
226
+
227
+ onCanplaythrough() {
228
+ this._updateTimes();
229
+ }
230
+
231
+ onLoadedmetadata() {
232
+ this._updateTimes();
233
+ }
234
+
235
+ onC2pafragment(fragment) {
236
+ if (fragment.valid) return;
237
+
238
+ setTimeout(() => {
239
+ const duration = this.player.getDuration();
240
+ const start = fragment.start;
241
+ const fragDuration = fragment.duration;
242
+ const startPercent = (start / duration) * 100;
243
+ const widthPercent = (fragDuration / duration) * 100;
244
+
245
+ const $fragment = createElement('span', { class: C2PA_STATUS_INVALID_SEGMENT_CN }, this.$c2paStatus);
246
+
247
+ $fragment.style.left = startPercent + '%';
248
+ $fragment.style.width = widthPercent + '%';
249
+ }, 1000);
250
+ }
251
+
252
+ // Accessibility: Screen reader expect hours to be present in the timecode
253
+ _getAriaLabelValue() {
254
+ const current = this.$current.textContent;
255
+ const duration = this.$duration.textContent;
256
+
257
+ return `Current: ${this._timeCodeToSentence(current)} Total: ${this._timeCodeToSentence(duration)}`;
258
+ }
259
+
260
+ // Accessibility
261
+ _timeCodeToSentence(time) {
262
+ const parts = time.split(':');
263
+ const words = ['minutes', 'seconds'];
264
+
265
+ if (parts.length > 2) words.unshift('hours');
266
+
267
+ const formatted = parts.map((p, idx) => {
268
+ const value = p.charAt(0) === '0' ? p.charAt(1) : p;
269
+ return `${value} ${words[idx]}`;
270
+ });
271
+
272
+ return formatted.join(' ');
273
+ }
274
+
275
+ _getDuration() {
276
+ const { clip, timecode } = this.options;
277
+ const duration = this.player.getDuration();
278
+
279
+ if ( clip ) {
280
+ let [ start, end ] = clip; // eslint-disable-line no-unused-vars
281
+ start = start || 0;
282
+ return end ? end + timecode : duration + start;
283
+ }
284
+
285
+ return duration;
286
+ }
287
+
288
+ _getCurrentTime() {
289
+ const time = this.player.getCurrentTime();
290
+ const { clip, timecode } = this.options;
291
+ const [ start ] = clip || [ 0 ];
292
+ return Math.max(time, start + timecode);
293
+ }
294
+
295
+ onLeave() {
296
+ this.$tooltip.classList.remove(TOOLTIP_VISIBLE_CN);
297
+ }
298
+
299
+ onHover(e) {
300
+ let time;
301
+ const { useOriginTimeForPreview, timecode } = this.options;
302
+ const target = e.target || e.srcElement;
303
+ if ( hasClass(target, CHAPTER_BUBBLE_CN) ) {
304
+ const idx = parseInt(target.getAttribute('data-idx'), 10);
305
+ const chapter = this._chapters[idx];
306
+ time = chapter.from;
307
+ this.$thumbnailTime.innerHTML = chapter.text || chapter.title;
308
+ } else {
309
+ time = this._getTimeByEventPosition(e);
310
+ this.$thumbnailTime.textContent = secondsToTimeCode(time);
311
+ }
312
+
313
+ if (isEmpty(this.thumbnails)) {
314
+ this._popoverPosition(e);
315
+ return;
316
+ }
317
+
318
+ const tt = useOriginTimeForPreview ? time - timecode : time;
319
+ const thumbnail = find(this.thumbnails, ({ from, to }) => inRange(tt, from, to));
320
+ if (thumbnail) {
321
+ const [url, hash] = thumbnail.text.split('#');
322
+
323
+ this.$thumbnail.style.backgroundImage = `url("${url}")`;
324
+
325
+ const xywh = last(hash.split('=')).split(',');
326
+ const h = xywh.pop().trim();
327
+ const w = xywh.pop().trim();
328
+ const y = xywh.pop().trim();
329
+ const x = xywh.pop().trim();
330
+
331
+ this.$thumbnail.style.width = `${w}px`;
332
+ this.$thumbnail.style.height = `${h}px`;
333
+ this.$thumbnail.style.backgroundPosition = `-${x}px -${y}px`;
334
+ this.$thumbnail.style.marginBottom = '7px';
335
+ } else {
336
+ this.$thumbnail.style.height = '';
337
+ this.$thumbnail.style.width = '';
338
+ this.$thumbnail.style.marginBottom = '';
339
+ }
340
+ this._popoverPosition(e);
341
+ }
342
+
343
+ _updateTimes() {
344
+ this.$duration.textContent = secondsToTimeCode(this._getDuration());
345
+ this.$current.textContent = secondsToTimeCode(this._getCurrentTime());
346
+ // Accessibility: Set correct values for screen readers
347
+ this.$bar.setAttribute('aria-label', this._getAriaLabelValue());
348
+ this._isLoadedMetadata = true;
349
+ this._renderChaptersIfNeeded();
350
+ }
351
+
352
+ _popoverPosition(e) {
353
+ popoverPosition(this.player, this._offsetX(e), this.$tooltip);
354
+ this.$tooltip.classList.add(TOOLTIP_VISIBLE_CN);
355
+ }
356
+
357
+ onStatechanged(state: Player$State, old: Player$State) {
358
+ if (state.toc.lang !== old.toc.lang) {
359
+ this._refreshTOC();
360
+ } else if ( state.toc.timedata !== old.toc.timedata ) {
361
+ this._refreshTOC();
362
+ }
363
+ }
364
+
365
+ _refreshTOC() {
366
+ const chapters = this.state.getTOCTimeData();
367
+ const lang = this.state.getTOCLang();
368
+ this._chapters = lang ? chapters : [];
369
+ this._renderChaptersIfNeeded();
370
+ }
371
+
372
+ _renderChaptersIfNeeded() {
373
+ each(this._bubbles, b => this.$bar.removeChild(b));
374
+ this._bubbles.length = 0;
375
+ if ( isEmpty(this._chapters) || !this._isLoadedMetadata ) {
376
+ return;
377
+ }
378
+
379
+ const duration = this.player.getDuration();
380
+
381
+ each(this._chapters, (c, idx) => {
382
+ const bubble = createElement('span', { class: CHAPTER_BUBBLE_CN, 'data-idx': idx }, this.$bar );
383
+ bubble.style.left = (100 * c.from / duration).toFixed(0) + '%';
384
+ this.addEvent(bubble, 'click', e => {
385
+ e.stopPropagation();
386
+ this._moveHandle2time(c.from);
387
+ this.player.setCurrentTime(c.from);
388
+ });
389
+ this._bubbles.push(bubble);
390
+ });
391
+ }
392
+
393
+ _getTimeByEventPosition(e) {
394
+ const duration = this.player.getDuration();
395
+ const { timecode, clip } = this.options;
396
+ const percent = this._getPercentageByPosition(e);
397
+
398
+ if (clip) {
399
+ let [ start, end ] = clip;
400
+ start = start || 0;
401
+ end = end || duration + start - timecode;
402
+ return ((end - start) * percent + start + timecode).toFixed(2);
403
+ }
404
+
405
+ return parseFloat(((duration - timecode) * percent + timecode).toFixed(2));
406
+ }
407
+
408
+ _getPercentageByPosition(e) {
409
+ const rect = this.$bar.getBoundingClientRect();
410
+ let left = Math.max(rect.left, e.clientX);
411
+ left = Math.min(rect.right, left);
412
+ left -= rect.left;
413
+ return (left / this.$bar.offsetWidth).toFixed(2);
414
+ }
415
+
416
+ _offsetX(e) {
417
+ if (!this.player.getElement()) return;
418
+
419
+ const rect = this.$bar.getBoundingClientRect();
420
+ const parentRect = this.player.getElement().parentNode.getBoundingClientRect();
421
+ let left = Math.max(rect.left, e.clientX);
422
+ left = Math.min(rect.right, left);
423
+ left -= parentRect.left;
424
+ return left;
425
+ }
426
+
427
+ getElement() {
428
+ return this.$el;
429
+ }
430
+
431
+ destroy() {
432
+ super.destroy();
433
+
434
+ const parent = this.$el.parentNode;
435
+
436
+ parent.removeChild(this.$el);
437
+
438
+ this.$el = null;
439
+ this.$bar = null;
440
+ this.$handle = null;
441
+ this.$current = null;
442
+ this.$duration = null;
443
+ this.$playProgress = null;
444
+ this.$progress = null;
445
+ this.$tooltip = null;
446
+ this.$thumbnail = null;
447
+ this.$thumbnailTime = null;
448
+ }
449
+ }
450
+
451
+ function secondsToTimeCode(time) {
452
+ if (time === Infinity || isNaN(time)) {
453
+ return '--:--';
454
+ }
455
+
456
+ const hours = Math.floor(time / SECS_IN_HOUR);
457
+ const minutes = Math.floor(time / SECS_IN_MIN) % SECS_IN_MIN;
458
+ const seconds = Math.floor(time % SECS_IN_MIN);
459
+
460
+ return (hours ? _pad(hours) + ':' : '') + _pad(minutes) + ':' + _pad(seconds);
461
+ }
462
+
463
+ function _pad(num) {
464
+ return num < TEN ? `0${num}` : `${num}`;
465
+ }