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,201 @@
1
+ import BaseExtension from './base';
2
+
3
+ import { createElement, addEvent, addClass, removeClass, each } from '../utils';
4
+
5
+ // import popover from './helpers/popover';
6
+ import createSvg, { HELP } from '../svgs';
7
+
8
+ const CN = 'rmp-ext-help';
9
+
10
+ const BUTTON_CN = `${CN}__button`;
11
+ const CANVAS_CN = `${CN}__canvas`;
12
+ const CANVAS_VISIBLE_CN = `${CANVAS_CN}--visible`;
13
+
14
+ const START_POSITION = 11;
15
+ const STEP = 36;
16
+
17
+ const RECT_WIDTH = 32;
18
+ const BUTTON_WIDTH = 26;
19
+ const BUTTON_MIDDLE = BUTTON_WIDTH / 2;
20
+ const THINKNESS = 2;
21
+ const TEXT_HEIGHT = 14;
22
+
23
+ export default class HelpExtension extends BaseExtension {
24
+ /** @type {HTMLElement} */
25
+ $outer;
26
+ /** @type {HTMLElement} */
27
+ $button;
28
+ /** @type {HTMLCanvasElement} */
29
+ $outer;
30
+
31
+ constructor(player, state, options) {
32
+ super(player, state, options, ['play']);
33
+ this.logger.debug('help:extension:creating');
34
+
35
+ this.showHelp = this.showHelp.bind(this);
36
+ this.hideHelp = this.hideHelp.bind(this);
37
+
38
+ this.render();
39
+ this.init();
40
+
41
+ this.logger.debug('help:extension:created');
42
+ }
43
+
44
+ init() {
45
+ // setTimeout(() => {
46
+ // popover(this, { $button: this.$button, text: this.$t('help') });
47
+ // });
48
+ addEvent(this.$button, 'click', this.showHelp);
49
+ addEvent(this.$canvas, 'click', this.hideHelp);
50
+ addEvent(this.$canvas, 'mouseleave', this.hideHelp);
51
+ }
52
+
53
+ render() {
54
+ this.$outer = createElement('div', { class: CN });
55
+
56
+ this.$canvas = createElement('canvas', { class: CANVAS_CN, tabindex: -1 }, this.$outer);
57
+ this.$button = createElement('button', { type: 'button', class: BUTTON_CN }, this.$outer);
58
+ this.$button.setAttribute('aria-label', this.$t('help'));
59
+ createSvg(this.$button, HELP);
60
+ }
61
+
62
+ onPlay() {
63
+ this.hideHelp();
64
+ }
65
+
66
+ showHelp(e) {
67
+ e.stopPropagation();
68
+ this.player.pause();
69
+ addClass(this.$outer, CANVAS_VISIBLE_CN);
70
+ /** @type {HTMLElement} */
71
+ const parent = this.$outer.parentNode;
72
+ const rect = parent.getBoundingClientRect();
73
+
74
+ this.$canvas.setAttribute('width', rect.width);
75
+ this.$canvas.setAttribute('height', rect.height);
76
+
77
+ const ctx = this.$canvas.getContext('2d');
78
+ ctx.globalCompositeOperation = 'xor';
79
+ ctx.font = rect.width < 500 ? '12px Verdana' : '14px Verdana';
80
+
81
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.75)';
82
+ ctx.beginPath();
83
+ ctx.fillRect(0, 0, rect.width, rect.height);
84
+ ctx.fill();
85
+
86
+ const recttop = rect.height - RECT_WIDTH;
87
+
88
+ let top = TEXT_HEIGHT;
89
+ const linebottom = rect.height - RECT_WIDTH - THINKNESS;
90
+
91
+ let pointPosion = START_POSITION;
92
+
93
+ const points = [];
94
+
95
+ if (this.options.showForward) {
96
+ points.push(['help-bacward', pointPosion]);
97
+ pointPosion += STEP;
98
+ }
99
+
100
+ points.push(['help-play', pointPosion]);
101
+ pointPosion += STEP;
102
+
103
+ if (this.options.showBackward) {
104
+ points.push(['help-skip', pointPosion]);
105
+ pointPosion += STEP;
106
+ }
107
+
108
+ points.push(['help-volume', pointPosion]);
109
+ // pointPosion += STEP;
110
+
111
+ each(points, ([key, position], idx) => {
112
+ if (idx) {
113
+ top += 2 * TEXT_HEIGHT;
114
+ }
115
+ _rect(ctx, position, recttop);
116
+ _line(ctx, position, top, linebottom);
117
+ let left = position + BUTTON_MIDDLE;
118
+ _circle(ctx, left, top, THINKNESS);
119
+
120
+ left += 10;
121
+ top = wrapText(ctx, this.$t(key), left, top + 6, rect.width - left);
122
+ });
123
+
124
+ if (this.options.showFullScreen) {
125
+ top += 2 * TEXT_HEIGHT;
126
+ const fullscreenPosition = rect.width - START_POSITION - BUTTON_WIDTH;
127
+ _rect(ctx, fullscreenPosition, recttop);
128
+ _line(ctx, fullscreenPosition, top, linebottom);
129
+ _circle(ctx, fullscreenPosition + BUTTON_MIDDLE, top, THINKNESS);
130
+ ctx.textAlign = 'right';
131
+ ctx.fillText(this.$t('help-esc'), fullscreenPosition + BUTTON_MIDDLE - 10, top + 6);
132
+ }
133
+ }
134
+
135
+ hideHelp() {
136
+ removeClass(this.$outer, CANVAS_VISIBLE_CN);
137
+ const parent = this.$outer.parentNode;
138
+ const rect = parent.getBoundingClientRect();
139
+ const ctx = this.$canvas.getContext('2d');
140
+ ctx.fillRect(0, 0, rect.width, rect.height);
141
+ }
142
+
143
+ getOuter() {
144
+ return this.$outer;
145
+ }
146
+
147
+ destroy() {
148
+ super.destroy();
149
+
150
+ this.$outer.parentNode.removeChild(this.$outer);
151
+
152
+ this.$button = null;
153
+ this.$canvas = null;
154
+ this.$outer = null;
155
+ }
156
+ }
157
+
158
+ // eslint-disable-next-line
159
+ function _rect(ctx, x, y) {
160
+ ctx.clearRect(x, y, BUTTON_WIDTH, BUTTON_WIDTH);
161
+ ctx.globalCompositeOperation = 'source-over';
162
+ ctx.strokeStyle = '#FFF';
163
+ ctx.lineWidth = THINKNESS;
164
+ const width = BUTTON_WIDTH + THINKNESS + THINKNESS;
165
+ const height = width;
166
+ ctx.strokeRect(x - THINKNESS, y - THINKNESS, width, height);
167
+ }
168
+
169
+ function _line(ctx, x, y1, y2) {
170
+ ctx.fillStyle = '#FFFFFF';
171
+ ctx.globalCompositeOperation = 'source-over';
172
+ ctx.lineWidth = 3;
173
+ ctx.strokeStyle = '#FFFFFF';
174
+ ctx.moveTo(x + BUTTON_MIDDLE, y1);
175
+ ctx.lineTo(x + BUTTON_MIDDLE, y2);
176
+ ctx.stroke();
177
+ }
178
+
179
+ function _circle(ctx, x, y, r) {
180
+ ctx.beginPath();
181
+ ctx.arc(x, y, r, 0, 2 * Math.PI);
182
+ ctx.stroke();
183
+ }
184
+
185
+ function wrapText(context, text, left, top, maxWidth) {
186
+ const lineHeight = 18;
187
+ let line = '';
188
+ each(text.split(' '), word => {
189
+ var testLine = line + word + ' ';
190
+ var testWidth = context.measureText(testLine).width;
191
+ if (testWidth > maxWidth) {
192
+ context.fillText(line, left, top);
193
+ line = word + ' ';
194
+ top += lineHeight;
195
+ } else {
196
+ line = testLine;
197
+ }
198
+ });
199
+ context.fillText(line, left, top);
200
+ return top;
201
+ }
@@ -0,0 +1,157 @@
1
+ // The one and only way of getting global scope in all environments
2
+ // https://stackoverflow.com/q/3277182/1008999
3
+ var _global = typeof window === 'object' && window.window === window
4
+ ? window
5
+ : typeof self === 'object' && self.self === self
6
+ ? self
7
+ : typeof global === 'object' && global.global === global
8
+ ? global
9
+ : this;
10
+
11
+ function bom (blob, opts) {
12
+ if (typeof opts === 'undefined') opts = { autoBom: false };
13
+ else if (typeof opts !== 'object') {
14
+ console.warn('Deprecated: Expected third argument to be a object');
15
+ opts = { autoBom: !opts };
16
+ }
17
+
18
+ // prepend BOM for UTF-8 XML and text/* types (including HTML)
19
+ // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
20
+ if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
21
+ return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type });
22
+ }
23
+ return blob;
24
+ }
25
+
26
+ function download (url, name, opts) {
27
+ var xhr = new XMLHttpRequest();
28
+ xhr.open('GET', url);
29
+ xhr.responseType = 'blob';
30
+ xhr.onload = function () {
31
+ saveAs(xhr.response, name, opts);
32
+ };
33
+ xhr.onerror = function () {
34
+ console.error('could not download file');
35
+ };
36
+ xhr.send();
37
+ }
38
+
39
+ function corsEnabled (url) {
40
+ var xhr = new XMLHttpRequest();
41
+ // use sync to avoid popup blocker
42
+ xhr.open('HEAD', url, false);
43
+ try {
44
+ xhr.send();
45
+ } catch (e) {}
46
+ return xhr.status >= 200 && xhr.status <= 299;
47
+ }
48
+
49
+ // `a.click()` doesn't work for all browsers (#465)
50
+ function click (node) {
51
+ try {
52
+ node.dispatchEvent(new MouseEvent('click'));
53
+ } catch (e) {
54
+ var evt = document.createEvent('MouseEvents');
55
+ evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
56
+ 20, false, false, false, false, 0, null);
57
+ node.dispatchEvent(evt);
58
+ }
59
+ }
60
+
61
+ // Detect WebView inside a native macOS app by ruling out all browsers
62
+ // We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
63
+ // https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
64
+ var isMacOSWebView = /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent);
65
+
66
+ export var saveAs = _global.saveAs || (
67
+ // probably in some web worker
68
+ (typeof window !== 'object' || window !== _global)
69
+ ? function saveAs () { /* noop */ }
70
+
71
+ // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
72
+ : ('download' in HTMLAnchorElement.prototype && !isMacOSWebView)
73
+ ? function saveAs (blob, name, opts) {
74
+ var URL = _global.URL || _global.webkitURL;
75
+ var a = document.createElement('a');
76
+ name = name || blob.name || 'download';
77
+
78
+ a.download = name;
79
+ a.rel = 'noopener'; // tabnabbing
80
+
81
+ // TODO: detect chrome extensions & packaged apps
82
+ // a.target = '_blank'
83
+
84
+ if (typeof blob === 'string') {
85
+ // Support regular links
86
+ a.href = blob;
87
+ if (a.origin !== location.origin) {
88
+ corsEnabled(a.href)
89
+ ? download(blob, name, opts)
90
+ : click(a, a.target = '_blank');
91
+ } else {
92
+ click(a);
93
+ }
94
+ } else {
95
+ // Support blobs
96
+ a.href = URL.createObjectURL(blob);
97
+ setTimeout(function () { URL.revokeObjectURL(a.href); }, 4E4); // 40s
98
+ setTimeout(function () { click(a); }, 0);
99
+ }
100
+ }
101
+
102
+ // Use msSaveOrOpenBlob as a second approach
103
+ : 'msSaveOrOpenBlob' in navigator
104
+ ? function saveAs (blob, name, opts) {
105
+ name = name || blob.name || 'download';
106
+
107
+ if (typeof blob === 'string') {
108
+ if (corsEnabled(blob)) {
109
+ download(blob, name, opts);
110
+ } else {
111
+ var a = document.createElement('a');
112
+ a.href = blob;
113
+ a.target = '_blank';
114
+ setTimeout(function () { click(a); });
115
+ }
116
+ } else {
117
+ navigator.msSaveOrOpenBlob(bom(blob, opts), name);
118
+ }
119
+ }
120
+
121
+ // Fallback to using FileReader and a popup
122
+ : function saveAs (blob, name, opts, popup) {
123
+ // Open a popup immediately do go around popup blocker
124
+ // Mostly only available on user interaction and the fileReader is async so...
125
+ popup = popup || open('', '_blank');
126
+ if (popup) {
127
+ popup.document.title =
128
+ popup.document.body.innerText = 'downloading...';
129
+ }
130
+
131
+ if (typeof blob === 'string') return download(blob, name, opts);
132
+
133
+ var force = blob.type === 'application/octet-stream';
134
+ var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
135
+ var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
136
+
137
+ if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== 'undefined') {
138
+ // Safari doesn't allow downloading of blob URLs
139
+ var reader = new FileReader();
140
+ reader.onloadend = function () {
141
+ var url = reader.result;
142
+ url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;');
143
+ if (popup) popup.location.href = url;
144
+ else location = url;
145
+ popup = null; // reverse-tabnabbing #460
146
+ };
147
+ reader.readAsDataURL(blob);
148
+ } else {
149
+ var URL = _global.URL || _global.webkitURL;
150
+ var url = URL.createObjectURL(blob);
151
+ if (popup) popup.location = url;
152
+ else location.href = url;
153
+ popup = null; // reverse-tabnabbing #460
154
+ setTimeout(function () { URL.revokeObjectURL(url); }, 4E4); // 40s
155
+ }
156
+ }
157
+ );
@@ -0,0 +1,180 @@
1
+ import { removeClass, addClass, removeEvent, addEvent, existy } from '../../utils';
2
+
3
+ import popoverPosition from './popoverPosition';
4
+ import openerHeightChecker from './openerHeightChecker';
5
+
6
+ const UP_CODE = 38;
7
+ const DOWN_CODE = 40;
8
+ const LEFT_CODE = 37;
9
+ const RIGHT_CODE = 39;
10
+ const ESC_CODE = 27;
11
+
12
+ export default function handle(extension, {
13
+ $button,
14
+ $selector,
15
+ SELECTOR_OPEN_CN,
16
+ reposition = true,
17
+ closeOnClick = false,
18
+ heightControl = false,
19
+ firstFocusItemSelector = '',
20
+ toMainFn = () => {}
21
+ }) {
22
+ let outerClickToken = null;
23
+
24
+ extension.addEvent($button, 'click', toggle);
25
+
26
+ if (!closeOnClick) {
27
+ extension.addEvent($selector, 'click', e => e.stopPropagation() );
28
+ }
29
+ extension.addEvent(extension.player.getElement(), 'mouseleave', hide);
30
+ extension.addEvent(extension.$selector, 'keydown', handleKeydown);
31
+ extension.bus.on('resizeplayer', hide);
32
+
33
+ function toggle() {
34
+ if (existy(outerClickToken)) {
35
+ hide();
36
+ } else {
37
+ show();
38
+ }
39
+ }
40
+
41
+ // Accessability: Handle arrow keys press
42
+ // TODO: This is not generic way, we can find better one
43
+ // To be able to improve this we should refactor the way we rendering our extensions
44
+ // Ex.: Remove hidden elements from DOM, fix the order of element rendering, etc.
45
+ function handleKeydown(e) {
46
+ e.stopPropagation();
47
+
48
+ const keyCode = e.keyCode;
49
+ const activeElement = document.activeElement;
50
+
51
+ if (keyCode === ESC_CODE) {
52
+ e.preventDefault();
53
+ hide();
54
+ extension.$button.focus();
55
+
56
+ return;
57
+ }
58
+
59
+ // If nested menu, move to next step
60
+ if (keyCode === RIGHT_CODE) {
61
+ e.preventDefault();
62
+
63
+ if (activeElement.classList.value.includes('__more')) {
64
+ activeElement.click();
65
+ }
66
+
67
+ return;
68
+ }
69
+
70
+ // If nested menu, move to prev step
71
+ if (keyCode === LEFT_CODE) {
72
+ e.preventDefault();
73
+
74
+ const allBackButtons = extension.$selector.querySelectorAll('button[class$="__back"]');
75
+ const visibleBackButton = Array.from(allBackButtons).find((btn) => btn.offsetWidth > 0 || btn.offsetHeight > 0);
76
+
77
+ if (visibleBackButton) visibleBackButton.click();
78
+
79
+ return;
80
+ }
81
+
82
+ if (keyCode === DOWN_CODE) {
83
+ e.preventDefault();
84
+ let nextFocus = activeElement.nextElementSibling;
85
+ nextFocus = checkVisibility(nextFocus, 'nextElementSibling');
86
+ nextFocus = handleWrapperSibling(nextFocus, 'firstElementChild');
87
+
88
+ if (!nextFocus) {
89
+ nextFocus = handleWrapperParent(nextFocus, 'nextElementSibling');
90
+ }
91
+
92
+ if (nextFocus) nextFocus.focus();
93
+
94
+ return;
95
+ }
96
+
97
+ if (keyCode === UP_CODE) {
98
+ e.preventDefault();
99
+ let prevFocus = activeElement.previousElementSibling;
100
+ prevFocus = handleWrapperParent(prevFocus, 'previousElementSibling');
101
+ prevFocus = checkVisibility(prevFocus, 'previousElementSibling');
102
+ prevFocus = handleWrapperSibling(prevFocus, 'lastElementChild');
103
+
104
+ if (prevFocus) prevFocus.focus();
105
+
106
+ }
107
+ }
108
+
109
+ function handleWrapperParent(el, sibling) {
110
+ const activeElement = document.activeElement;
111
+ let element = el;
112
+ // check if there is no next element and parent is wrapper
113
+ const isInsideWrapper = !element && activeElement.parentElement.classList.value.includes('tab-content');
114
+
115
+ if (isInsideWrapper) {
116
+ element = activeElement.parentElement[sibling];
117
+ }
118
+
119
+ return element;
120
+ }
121
+
122
+ function handleWrapperSibling(el, child) {
123
+ let element = el;
124
+ // check if nextElement is wrapper element
125
+ const isWrapper = element && element.classList.value.includes('tab-content');
126
+
127
+ if (isWrapper) {
128
+ element = element[child];
129
+ }
130
+
131
+ return element;
132
+ }
133
+
134
+ function checkVisibility(el, direction) {
135
+ let element = el;
136
+ let visible = el && (element.offsetWidth > 0 || element.offsetHeight > 0);
137
+
138
+ while (!visible && element) {
139
+ element = element[direction];
140
+ visible = el && (element.offsetWidth > 0 || element.offsetHeight > 0);
141
+ }
142
+
143
+ return element;
144
+ }
145
+
146
+ function hide() {
147
+ toMainFn();
148
+ extension.bus.emit('hidepopup');
149
+ removeClass($selector, SELECTOR_OPEN_CN);
150
+ if (existy(outerClickToken)) {
151
+ removeEvent(outerClickToken);
152
+ outerClickToken = null;
153
+ }
154
+ }
155
+
156
+ function show() {
157
+ addClass($selector, SELECTOR_OPEN_CN);
158
+
159
+ if (reposition) {
160
+ popoverPosition(extension.player, $button, $selector);
161
+ }
162
+
163
+ setTimeout(() => {
164
+ extension.bus.emit('showpopup', $selector);
165
+ outerClickToken = addEvent(document, 'click', hide);
166
+ });
167
+
168
+ if (heightControl) {
169
+ openerHeightChecker(extension.player, $selector);
170
+ }
171
+
172
+ // Accessability: If presented, focus on the first menu item
173
+ if (firstFocusItemSelector) {
174
+ setTimeout(() => {
175
+ const firstFocusItem = $selector.querySelector(firstFocusItemSelector);
176
+ firstFocusItem.focus();
177
+ });
178
+ }
179
+ }
180
+ }
@@ -0,0 +1,30 @@
1
+ import { toggleClass, removeClass, addClass } from '../../utils';
2
+
3
+ import popoverPosition from './popoverPosition';
4
+
5
+ const DELAY = 200;
6
+
7
+ export default function handle(extension, { $button, $selector, SELECTOR_OPEN_CN }) {
8
+ extension.addEvent($button, 'click', toggle);
9
+
10
+ let timerId = 0;
11
+
12
+ extension.addEvent(extension.getElement(), 'mouseleave', hide);
13
+
14
+ extension.addEvent($button, 'mouseenter', () => timerId = setTimeout(show, DELAY) );
15
+
16
+ function toggle() {
17
+ toggleClass($selector, SELECTOR_OPEN_CN);
18
+ popoverPosition(extension.player, $button, $selector);
19
+ }
20
+
21
+ function hide() {
22
+ clearTimeout(timerId);
23
+ removeClass($selector, SELECTOR_OPEN_CN);
24
+ }
25
+
26
+ function show() {
27
+ addClass($selector, SELECTOR_OPEN_CN);
28
+ popoverPosition(extension.player, $button, $selector);
29
+ }
30
+ }
@@ -0,0 +1,13 @@
1
+
2
+ const OPENER_MARGIN = 100;
3
+ const MAX_MAX_HEIGHT = 300;
4
+
5
+ export default function openerHeightChecker(player, $selector) {
6
+ if (!player || !player.getElement()) return;
7
+
8
+ $selector.style.maxHeight = '';
9
+ const rect = player.getElement().parentNode.getBoundingClientRect();
10
+ const mh = (rect.height || rect.bottom - rect.top) - OPENER_MARGIN;
11
+
12
+ $selector.style.maxHeight = `${Math.min(mh, MAX_MAX_HEIGHT)}px`;
13
+ }
@@ -0,0 +1,33 @@
1
+ import { createElement, append, removeClass, addClass, isFunction } from '../../utils';
2
+
3
+ import popoverPosition from './popoverPosition';
4
+
5
+ const CN = 'rmp-ext-popover';
6
+ const VISIBLE_CN = `${CN}--visible`;
7
+
8
+ const DELAY = 500;
9
+
10
+ export default function popover(extension, { $button, text }) {
11
+ const parent = $button.parentNode;
12
+ const span = createElement('span', { class: CN, tabindex: 0 });
13
+ append(parent, span, $button);
14
+ $button.setAttribute('aria-label', isFunction(text) ? text() : text);
15
+
16
+ let timerId = 0;
17
+
18
+ extension.addEvent($button, 'mouseenter', () => timerId = setTimeout(show, DELAY) );
19
+ extension.addEvent($button, 'mouseleave', hide);
20
+ extension.addEvent($button, 'click', hide);
21
+
22
+ function show() {
23
+ span.textContent = isFunction(text) ? text() : text;
24
+ span.style.left = '0';
25
+ popoverPosition(extension.player, $button, span);
26
+ addClass(span, VISIBLE_CN);
27
+ }
28
+
29
+ function hide() {
30
+ clearTimeout(timerId);
31
+ removeClass(span, VISIBLE_CN);
32
+ }
33
+ }
@@ -0,0 +1,30 @@
1
+ import isElement from '../../utils/isElement';
2
+
3
+ const HALF = 2;
4
+
5
+ const PADDING = 3;
6
+
7
+ export default function popoverPosition(player, position, $popover) {
8
+ $popover.style.display = 'block';
9
+ let element = player.getElement();
10
+ const mediaWidth = element ? element.offsetWidth : 0;
11
+ if ( isElement(position) ) {
12
+ const parent = mediaWidth ? element : element.parentNode;
13
+ const parentRect = parent.getBoundingClientRect();
14
+ const rect = position.getBoundingClientRect();
15
+ position = _half(rect.left + rect.right) - parentRect.left;
16
+ }
17
+ const w = $popover.offsetWidth;
18
+ let l = Math.max(PADDING, position - _half(w));
19
+ // native audio element doesn't have any width
20
+ if (mediaWidth) {
21
+ l = Math.min(l, mediaWidth - w - PADDING);
22
+ }
23
+
24
+ $popover.style.left = `${l}px`;
25
+ $popover.style.display = '';
26
+ }
27
+
28
+ function _half(num) {
29
+ return Math.floor(num / HALF);
30
+ }
@@ -0,0 +1,9 @@
1
+ export default function scrollIntoView(el, parent) {
2
+ const { top } = el.getBoundingClientRect();
3
+ const rect = parent.getBoundingClientRect();
4
+
5
+ const startPosition = parent.scrollTop;
6
+ const delta = Math.floor(top - rect.top);
7
+
8
+ parent.scrollTop = startPosition + delta;
9
+ }
@@ -0,0 +1,20 @@
1
+ import existy from '../../utils/existy';
2
+
3
+ const PREFIX = 'rmp-';
4
+
5
+ export function storeData(key, value) {
6
+ try {
7
+ localStorage.setItem(PREFIX + key, value);
8
+ } catch (e) {
9
+ //
10
+ }
11
+ }
12
+
13
+ export function getStoredData(key, defaultValue) {
14
+ try {
15
+ const res = localStorage.getItem(PREFIX + key);
16
+ return existy(res) ? res : defaultValue;
17
+ } catch (e) {
18
+ return defaultValue;
19
+ }
20
+ }
@@ -0,0 +1,6 @@
1
+ const textEl = document.createElement('span');
2
+
3
+ export default function textContent(html) {
4
+ textEl.innerHTML = html;
5
+ return textEl.textContent;
6
+ }