@cloudnest/redxplyr 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +10 -0
- package/.gitpod.yml +6 -0
- package/.node-version +1 -0
- package/.prettierrc +7 -0
- package/.stickler.yml +5 -0
- package/.stylelintrc.json +26 -0
- package/CHANGELOG.md +16 -0
- package/CONTRIBUTING.md +34 -0
- package/CONTROLS.md +49 -0
- package/Dockerfile +32 -0
- package/LICENSE.md +22 -0
- package/README.md +194 -0
- package/cspell.json +48 -0
- package/dist/redxplyr.css +1 -0
- package/dist/redxplyr.js +8801 -0
- package/dist/redxplyr.min.js +2 -0
- package/dist/redxplyr.min.js.map +1 -0
- package/dist/redxplyr.min.mjs +1 -0
- package/dist/redxplyr.min.mjs.map +1 -0
- package/dist/redxplyr.mjs +8793 -0
- package/dist/redxplyr.polyfilled.js +9294 -0
- package/dist/redxplyr.polyfilled.min.js +2 -0
- package/dist/redxplyr.polyfilled.min.js.map +1 -0
- package/dist/redxplyr.polyfilled.min.mjs +1 -0
- package/dist/redxplyr.polyfilled.min.mjs.map +1 -0
- package/dist/redxplyr.polyfilled.mjs +9286 -0
- package/dist/redxplyr.svg +1 -0
- package/eslint.config.mjs +39 -0
- package/gulpfile.js +8 -0
- package/package.json +114 -0
- package/pnpm-workspace.yaml +8 -0
- package/src/js/captions.js +411 -0
- package/src/js/config/defaults.js +459 -0
- package/src/js/config/states.js +10 -0
- package/src/js/config/types.js +34 -0
- package/src/js/console.js +28 -0
- package/src/js/controls.js +1870 -0
- package/src/js/fullscreen.js +305 -0
- package/src/js/html5.js +148 -0
- package/src/js/listeners.js +854 -0
- package/src/js/media.js +61 -0
- package/src/js/plugins/ads.js +647 -0
- package/src/js/plugins/preview-thumbnails.js +706 -0
- package/src/js/plugins/vimeo.js +443 -0
- package/src/js/plugins/youtube.js +451 -0
- package/src/js/plyr.d.ts +729 -0
- package/src/js/plyr.js +1291 -0
- package/src/js/plyr.polyfilled.js +13 -0
- package/src/js/source.js +155 -0
- package/src/js/storage.js +70 -0
- package/src/js/support.js +100 -0
- package/src/js/ui.js +297 -0
- package/src/js/utils/animation.js +33 -0
- package/src/js/utils/arrays.js +23 -0
- package/src/js/utils/browser.js +21 -0
- package/src/js/utils/elements.js +263 -0
- package/src/js/utils/events.js +116 -0
- package/src/js/utils/fetch.js +45 -0
- package/src/js/utils/i18n.js +47 -0
- package/src/js/utils/is.js +81 -0
- package/src/js/utils/load-image.js +19 -0
- package/src/js/utils/load-script.js +14 -0
- package/src/js/utils/load-sprite.js +77 -0
- package/src/js/utils/numbers.js +17 -0
- package/src/js/utils/objects.js +43 -0
- package/src/js/utils/promise.js +14 -0
- package/src/js/utils/strings.js +80 -0
- package/src/js/utils/style.js +148 -0
- package/src/js/utils/time.js +36 -0
- package/src/js/utils/urls.js +40 -0
- package/src/sass/base.scss +69 -0
- package/src/sass/components/badges.scss +12 -0
- package/src/sass/components/captions.scss +58 -0
- package/src/sass/components/control.scss +52 -0
- package/src/sass/components/controls.scss +65 -0
- package/src/sass/components/menus.scss +205 -0
- package/src/sass/components/poster.scss +27 -0
- package/src/sass/components/progress.scss +107 -0
- package/src/sass/components/sliders.scss +99 -0
- package/src/sass/components/times.scss +20 -0
- package/src/sass/components/tooltips.scss +91 -0
- package/src/sass/components/volume.scss +18 -0
- package/src/sass/lib/animation.scss +31 -0
- package/src/sass/lib/css-vars.scss +103 -0
- package/src/sass/lib/functions.scss +3 -0
- package/src/sass/lib/mixins.scss +82 -0
- package/src/sass/plugins/ads.scss +53 -0
- package/src/sass/plugins/preview-thumbnails/index.scss +121 -0
- package/src/sass/plugins/preview-thumbnails/settings.scss +17 -0
- package/src/sass/plyr.scss +46 -0
- package/src/sass/settings/badges.scss +7 -0
- package/src/sass/settings/breakpoints.scss +9 -0
- package/src/sass/settings/captions.scss +10 -0
- package/src/sass/settings/colors.scss +18 -0
- package/src/sass/settings/controls.scss +30 -0
- package/src/sass/settings/cosmetics.scss +5 -0
- package/src/sass/settings/helpers.scss +7 -0
- package/src/sass/settings/menus.scss +13 -0
- package/src/sass/settings/progress.scss +18 -0
- package/src/sass/settings/sliders.scss +39 -0
- package/src/sass/settings/tooltips.scss +11 -0
- package/src/sass/settings/type.scss +16 -0
- package/src/sass/states/fullscreen.scss +15 -0
- package/src/sass/types/audio.scss +61 -0
- package/src/sass/types/video.scss +170 -0
- package/src/sass/utils/animation.scss +7 -0
- package/src/sass/utils/hidden.scss +28 -0
- package/src/sprite/plyr-airplay.svg +8 -0
- package/src/sprite/plyr-captions-off.svg +7 -0
- package/src/sprite/plyr-captions-on.svg +7 -0
- package/src/sprite/plyr-download.svg +8 -0
- package/src/sprite/plyr-enter-fullscreen.svg +4 -0
- package/src/sprite/plyr-exit-fullscreen.svg +4 -0
- package/src/sprite/plyr-fast-forward.svg +3 -0
- package/src/sprite/plyr-logo-vimeo.svg +6 -0
- package/src/sprite/plyr-logo-youtube.svg +6 -0
- package/src/sprite/plyr-muted.svg +8 -0
- package/src/sprite/plyr-pause.svg +8 -0
- package/src/sprite/plyr-pip.svg +6 -0
- package/src/sprite/plyr-play.svg +5 -0
- package/src/sprite/plyr-restart.svg +5 -0
- package/src/sprite/plyr-rewind.svg +3 -0
- package/src/sprite/plyr-settings.svg +5 -0
- package/src/sprite/plyr-volume.svg +11 -0
- package/tasks/build.js +226 -0
- package/tasks/deploy.js +216 -0
- package/tasks/utils/publish.js +34 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Browser sniffing
|
|
3
|
+
// Unfortunately, due to mixed support, UA sniffing is required
|
|
4
|
+
// ==========================================================================
|
|
5
|
+
|
|
6
|
+
const isIE = Boolean(window.document.documentMode);
|
|
7
|
+
const isEdge = /Edge/.test(navigator.userAgent);
|
|
8
|
+
const isWebKit = 'WebkitAppearance' in document.documentElement.style && !/Edge/.test(navigator.userAgent);
|
|
9
|
+
const isIPhone = /iPhone|iPod/i.test(navigator.userAgent) && navigator.maxTouchPoints > 1;
|
|
10
|
+
// navigator.platform may be deprecated but this check is still required
|
|
11
|
+
const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1;
|
|
12
|
+
const isIos = /iPad|iPhone|iPod/i.test(navigator.userAgent) && navigator.maxTouchPoints > 1;
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
isIE,
|
|
16
|
+
isEdge,
|
|
17
|
+
isWebKit,
|
|
18
|
+
isIPhone,
|
|
19
|
+
isIPadOS,
|
|
20
|
+
isIos,
|
|
21
|
+
};
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Element utils
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
import is from './is';
|
|
6
|
+
import { extend } from './objects';
|
|
7
|
+
|
|
8
|
+
// Wrap an element
|
|
9
|
+
export function wrap(elements, wrapper) {
|
|
10
|
+
// Convert `elements` to an array, if necessary.
|
|
11
|
+
const targets = elements.length ? elements : [elements];
|
|
12
|
+
|
|
13
|
+
// Loops backwards to prevent having to clone the wrapper on the
|
|
14
|
+
// first element (see `child` below).
|
|
15
|
+
Array.from(targets)
|
|
16
|
+
.reverse()
|
|
17
|
+
.forEach((element, index) => {
|
|
18
|
+
const child = index > 0 ? wrapper.cloneNode(true) : wrapper;
|
|
19
|
+
// Cache the current parent and sibling.
|
|
20
|
+
const parent = element.parentNode;
|
|
21
|
+
const sibling = element.nextSibling;
|
|
22
|
+
|
|
23
|
+
// Wrap the element (is automatically removed from its current
|
|
24
|
+
// parent).
|
|
25
|
+
child.appendChild(element);
|
|
26
|
+
|
|
27
|
+
// If the element had a sibling, insert the wrapper before
|
|
28
|
+
// the sibling to maintain the HTML structure; otherwise, just
|
|
29
|
+
// append it to the parent.
|
|
30
|
+
if (sibling) {
|
|
31
|
+
parent.insertBefore(child, sibling);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
parent.appendChild(child);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Set attributes
|
|
40
|
+
export function setAttributes(element, attributes) {
|
|
41
|
+
if (!is.element(element) || is.empty(attributes)) return;
|
|
42
|
+
|
|
43
|
+
// Assume null and undefined attributes should be left out,
|
|
44
|
+
// Setting them would otherwise convert them to "null" and "undefined"
|
|
45
|
+
Object.entries(attributes)
|
|
46
|
+
.filter(([, value]) => !is.nullOrUndefined(value))
|
|
47
|
+
.forEach(([key, value]) => element.setAttribute(key, value));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Create a DocumentFragment
|
|
51
|
+
export function createElement(type, attributes, text) {
|
|
52
|
+
// Create a new <element>
|
|
53
|
+
const element = document.createElement(type);
|
|
54
|
+
|
|
55
|
+
// Set all passed attributes
|
|
56
|
+
if (is.object(attributes)) {
|
|
57
|
+
setAttributes(element, attributes);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Add text node
|
|
61
|
+
if (is.string(text)) {
|
|
62
|
+
element.textContent = text;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Return built element
|
|
66
|
+
return element;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Insert an element after another
|
|
70
|
+
export function insertAfter(element, target) {
|
|
71
|
+
if (!is.element(element) || !is.element(target)) return;
|
|
72
|
+
|
|
73
|
+
target.parentNode.insertBefore(element, target.nextSibling);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Insert a DocumentFragment
|
|
77
|
+
export function insertElement(type, parent, attributes, text) {
|
|
78
|
+
if (!is.element(parent)) return;
|
|
79
|
+
|
|
80
|
+
parent.appendChild(createElement(type, attributes, text));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Remove element(s)
|
|
84
|
+
export function removeElement(element) {
|
|
85
|
+
if (is.nodeList(element) || is.array(element)) {
|
|
86
|
+
Array.from(element).forEach(removeElement);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!is.element(element) || !is.element(element.parentNode)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
element.parentNode.removeChild(element);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Remove all child elements
|
|
98
|
+
export function emptyElement(element) {
|
|
99
|
+
if (!is.element(element)) return;
|
|
100
|
+
|
|
101
|
+
let { length } = element.childNodes;
|
|
102
|
+
|
|
103
|
+
while (length > 0) {
|
|
104
|
+
element.removeChild(element.lastChild);
|
|
105
|
+
length -= 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Replace element
|
|
110
|
+
export function replaceElement(newChild, oldChild) {
|
|
111
|
+
if (!is.element(oldChild) || !is.element(oldChild.parentNode) || !is.element(newChild)) return null;
|
|
112
|
+
|
|
113
|
+
oldChild.parentNode.replaceChild(newChild, oldChild);
|
|
114
|
+
|
|
115
|
+
return newChild;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Get an attribute object from a string selector
|
|
119
|
+
export function getAttributesFromSelector(sel, existingAttributes) {
|
|
120
|
+
// For example:
|
|
121
|
+
// '.test' to { class: 'test' }
|
|
122
|
+
// '#test' to { id: 'test' }
|
|
123
|
+
// '[data-test="test"]' to { 'data-test': 'test' }
|
|
124
|
+
|
|
125
|
+
if (!is.string(sel) || is.empty(sel)) return {};
|
|
126
|
+
|
|
127
|
+
const attributes = {};
|
|
128
|
+
const existing = extend({}, existingAttributes);
|
|
129
|
+
|
|
130
|
+
sel.split(',').forEach((s) => {
|
|
131
|
+
// Remove whitespace
|
|
132
|
+
const selector = s.trim();
|
|
133
|
+
const className = selector.replace('.', '');
|
|
134
|
+
const stripped = selector.replace(/[[\]]/g, '');
|
|
135
|
+
// Get the parts and value
|
|
136
|
+
const parts = stripped.split('=');
|
|
137
|
+
const [key] = parts;
|
|
138
|
+
const value = parts.length > 1 ? parts[1].replace(/["']/g, '') : '';
|
|
139
|
+
// Get the first character
|
|
140
|
+
const start = selector.charAt(0);
|
|
141
|
+
|
|
142
|
+
switch (start) {
|
|
143
|
+
case '.':
|
|
144
|
+
// Add to existing classname
|
|
145
|
+
if (is.string(existing.class)) {
|
|
146
|
+
attributes.class = `${existing.class} ${className}`;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
attributes.class = className;
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case '#':
|
|
154
|
+
// ID selector
|
|
155
|
+
attributes.id = selector.replace('#', '');
|
|
156
|
+
break;
|
|
157
|
+
|
|
158
|
+
case '[':
|
|
159
|
+
// Attribute selector
|
|
160
|
+
attributes[key] = value;
|
|
161
|
+
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
default:
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return extend(existing, attributes);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Toggle hidden
|
|
173
|
+
export function toggleHidden(element, hidden) {
|
|
174
|
+
if (!is.element(element)) return;
|
|
175
|
+
|
|
176
|
+
let hide = hidden;
|
|
177
|
+
|
|
178
|
+
if (!is.boolean(hide)) {
|
|
179
|
+
hide = !element.hidden;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
element.hidden = hide;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Mirror Element.classList.toggle, with IE compatibility for "force" argument
|
|
186
|
+
export function toggleClass(element, className, force) {
|
|
187
|
+
if (is.nodeList(element)) {
|
|
188
|
+
return Array.from(element).map(e => toggleClass(e, className, force));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (is.element(element)) {
|
|
192
|
+
let method = 'toggle';
|
|
193
|
+
if (typeof force !== 'undefined') {
|
|
194
|
+
method = force ? 'add' : 'remove';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
element.classList[method](className);
|
|
198
|
+
return element.classList.contains(className);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Has class name
|
|
205
|
+
export function hasClass(element, className) {
|
|
206
|
+
return is.element(element) && element.classList.contains(className);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Element matches selector
|
|
210
|
+
export function matches(element, selector) {
|
|
211
|
+
const { prototype } = Element;
|
|
212
|
+
|
|
213
|
+
function match() {
|
|
214
|
+
return Array.from(document.querySelectorAll(selector)).includes(this);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const method
|
|
218
|
+
= prototype.matches
|
|
219
|
+
|| prototype.webkitMatchesSelector
|
|
220
|
+
|| prototype.mozMatchesSelector
|
|
221
|
+
|| prototype.msMatchesSelector
|
|
222
|
+
|| match;
|
|
223
|
+
|
|
224
|
+
return method.call(element, selector);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Closest ancestor element matching selector (also tests element itself)
|
|
228
|
+
export function closest(element, selector) {
|
|
229
|
+
const { prototype } = Element;
|
|
230
|
+
|
|
231
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
|
|
232
|
+
function closestElement() {
|
|
233
|
+
let el = this;
|
|
234
|
+
|
|
235
|
+
do {
|
|
236
|
+
if (matches.matches(el, selector)) return el;
|
|
237
|
+
el = el.parentElement || el.parentNode;
|
|
238
|
+
} while (el !== null && el.nodeType === 1);
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const method = prototype.closest || closestElement;
|
|
243
|
+
|
|
244
|
+
return method.call(element, selector);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Find all elements
|
|
248
|
+
export function getElements(selector) {
|
|
249
|
+
return this.elements.container.querySelectorAll(selector);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Find a single element
|
|
253
|
+
export function getElement(selector) {
|
|
254
|
+
return this.elements.container.querySelector(selector);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Set focus and tab focus class
|
|
258
|
+
export function setFocus(element = null, focusVisible = false) {
|
|
259
|
+
if (!is.element(element)) return;
|
|
260
|
+
|
|
261
|
+
// Set regular focus
|
|
262
|
+
element.focus({ preventScroll: true, focusVisible });
|
|
263
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Event utils
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
import is from './is';
|
|
6
|
+
|
|
7
|
+
// Check for passive event listener support
|
|
8
|
+
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
|
|
9
|
+
// https://www.youtube.com/watch?v=NPM6172J22g
|
|
10
|
+
const supportsPassiveListeners = (() => {
|
|
11
|
+
// Test via a getter in the options object to see if the passive property is accessed
|
|
12
|
+
let supported = false;
|
|
13
|
+
try {
|
|
14
|
+
const options = Object.defineProperty({}, 'passive', {
|
|
15
|
+
get() {
|
|
16
|
+
supported = true;
|
|
17
|
+
return null;
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
window.addEventListener('test', null, options);
|
|
21
|
+
window.removeEventListener('test', null, options);
|
|
22
|
+
}
|
|
23
|
+
catch {}
|
|
24
|
+
|
|
25
|
+
return supported;
|
|
26
|
+
})();
|
|
27
|
+
|
|
28
|
+
// Toggle event listener
|
|
29
|
+
export function toggleListener(element, event, callback, toggle = false, passive = true, capture = false) {
|
|
30
|
+
// Bail if no element, event, or callback
|
|
31
|
+
if (!element || !('addEventListener' in element) || is.empty(event) || !is.function(callback)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Allow multiple events
|
|
36
|
+
const events = event.split(' ');
|
|
37
|
+
// Build options
|
|
38
|
+
// Default to just the capture boolean for browsers with no passive listener support
|
|
39
|
+
let options = capture;
|
|
40
|
+
|
|
41
|
+
// If passive events listeners are supported
|
|
42
|
+
if (supportsPassiveListeners) {
|
|
43
|
+
options = {
|
|
44
|
+
// Whether the listener can be passive (i.e. default never prevented)
|
|
45
|
+
passive,
|
|
46
|
+
// Whether the listener is a capturing listener or not
|
|
47
|
+
capture,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// If a single node is passed, bind the event listener
|
|
52
|
+
events.forEach((type) => {
|
|
53
|
+
if (this && this.eventListeners && toggle) {
|
|
54
|
+
// Cache event listener
|
|
55
|
+
this.eventListeners.push({ element, type, callback, options });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Bind event handler
|
|
63
|
+
export function on(element, events = '', callback, passive = true, capture = false) {
|
|
64
|
+
toggleListener.call(this, element, events, callback, true, passive, capture);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Unbind event handler
|
|
68
|
+
export function off(element, events = '', callback, passive = true, capture = false) {
|
|
69
|
+
toggleListener.call(this, element, events, callback, false, passive, capture);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Bind once-only event handler
|
|
73
|
+
export function once(element, events = '', callback, passive = true, capture = false) {
|
|
74
|
+
const onceCallback = (...args) => {
|
|
75
|
+
off(element, events, onceCallback, passive, capture);
|
|
76
|
+
callback.apply(this, args);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
toggleListener.call(this, element, events, onceCallback, true, passive, capture);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Trigger event
|
|
83
|
+
export function triggerEvent(element, type = '', bubbles = false, detail = {}) {
|
|
84
|
+
// Bail if no element
|
|
85
|
+
if (!is.element(element) || is.empty(type)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Create and dispatch the event
|
|
90
|
+
const event = new CustomEvent(type, {
|
|
91
|
+
bubbles,
|
|
92
|
+
detail: { ...detail, plyr: this },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Dispatch the event
|
|
96
|
+
element.dispatchEvent(event);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Unbind all cached event listeners
|
|
100
|
+
export function unbindListeners() {
|
|
101
|
+
if (this && this.eventListeners) {
|
|
102
|
+
this.eventListeners.forEach((item) => {
|
|
103
|
+
const { element, type, callback, options } = item;
|
|
104
|
+
element.removeEventListener(type, callback, options);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.eventListeners = [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Run method when / if player is ready
|
|
112
|
+
export function ready() {
|
|
113
|
+
return new Promise(resolve =>
|
|
114
|
+
this.ready ? setTimeout(resolve, 0) : on.call(this, this.elements.container, 'ready', resolve),
|
|
115
|
+
).then(() => {});
|
|
116
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Fetch wrapper
|
|
3
|
+
// Using XHR to avoid issues with older browsers
|
|
4
|
+
// ==========================================================================
|
|
5
|
+
|
|
6
|
+
export default function fetch(url, responseType = 'text', withCredentials = false) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
try {
|
|
9
|
+
const request = new XMLHttpRequest();
|
|
10
|
+
|
|
11
|
+
// Check for CORS support
|
|
12
|
+
if (!('withCredentials' in request)) return;
|
|
13
|
+
|
|
14
|
+
// Set to true if needed for CORS
|
|
15
|
+
if (withCredentials) {
|
|
16
|
+
request.withCredentials = true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
request.addEventListener('load', () => {
|
|
20
|
+
if (responseType === 'text') {
|
|
21
|
+
try {
|
|
22
|
+
resolve(JSON.parse(request.responseText));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
resolve(request.responseText);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
resolve(request.response);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
request.addEventListener('error', () => {
|
|
34
|
+
throw new Error(request.status);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
request.open('GET', url, true);
|
|
38
|
+
request.responseType = responseType;
|
|
39
|
+
request.send();
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
reject(error);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Plyr internationalization
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
import is from './is';
|
|
6
|
+
import { getDeep } from './objects';
|
|
7
|
+
import { replaceAll } from './strings';
|
|
8
|
+
|
|
9
|
+
// Skip i18n for abbreviations and brand names
|
|
10
|
+
const resources = {
|
|
11
|
+
pip: 'PIP',
|
|
12
|
+
airplay: 'AirPlay',
|
|
13
|
+
html5: 'HTML5',
|
|
14
|
+
vimeo: 'Vimeo',
|
|
15
|
+
youtube: 'YouTube',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const i18n = {
|
|
19
|
+
get(key = '', config = {}) {
|
|
20
|
+
if (is.empty(key) || is.empty(config)) {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let string = getDeep(config.i18n, key);
|
|
25
|
+
|
|
26
|
+
if (is.empty(string)) {
|
|
27
|
+
if (Object.keys(resources).includes(key)) {
|
|
28
|
+
return resources[key];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const replace = {
|
|
35
|
+
'{seektime}': config.seekTime,
|
|
36
|
+
'{title}': config.title,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
Object.entries(replace).forEach(([k, v]) => {
|
|
40
|
+
string = replaceAll(string, k, v);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return string;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default i18n;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Type checking utils
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
const getConstructor = input => (input !== null && typeof input !== 'undefined' ? input.constructor : null);
|
|
6
|
+
const instanceOf = (input, constructor) => Boolean(input && constructor && input instanceof constructor);
|
|
7
|
+
const isNullOrUndefined = input => input === null || typeof input === 'undefined';
|
|
8
|
+
const isObject = input => getConstructor(input) === Object;
|
|
9
|
+
const isNumber = input => getConstructor(input) === Number && !Number.isNaN(input);
|
|
10
|
+
const isString = input => getConstructor(input) === String;
|
|
11
|
+
const isBoolean = input => getConstructor(input) === Boolean;
|
|
12
|
+
const isFunction = input => typeof input === 'function';
|
|
13
|
+
const isArray = input => Array.isArray(input);
|
|
14
|
+
const isWeakMap = input => instanceOf(input, WeakMap);
|
|
15
|
+
const isNodeList = input => instanceOf(input, NodeList);
|
|
16
|
+
const isTextNode = input => getConstructor(input) === Text;
|
|
17
|
+
const isEvent = input => instanceOf(input, Event);
|
|
18
|
+
const isKeyboardEvent = input => instanceOf(input, KeyboardEvent);
|
|
19
|
+
const isCue = input => instanceOf(input, window.TextTrackCue) || instanceOf(input, window.VTTCue);
|
|
20
|
+
const isTrack = input => instanceOf(input, TextTrack) || (!isNullOrUndefined(input) && isString(input.kind));
|
|
21
|
+
const isPromise = input => instanceOf(input, Promise) && isFunction(input.then);
|
|
22
|
+
|
|
23
|
+
function isElement(input) {
|
|
24
|
+
return input !== null
|
|
25
|
+
&& typeof input === 'object'
|
|
26
|
+
&& input.nodeType === 1
|
|
27
|
+
&& typeof input.style === 'object'
|
|
28
|
+
&& typeof input.ownerDocument === 'object';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isEmpty(input) {
|
|
32
|
+
return isNullOrUndefined(input)
|
|
33
|
+
|| ((isString(input) || isArray(input) || isNodeList(input)) && !input.length)
|
|
34
|
+
|| (isObject(input) && !Object.keys(input).length);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isUrl(input) {
|
|
38
|
+
// Accept a URL object
|
|
39
|
+
if (instanceOf(input, window.URL)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Must be string from here
|
|
44
|
+
if (!isString(input)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Add the protocol if required
|
|
49
|
+
let string = input;
|
|
50
|
+
if (!input.startsWith('http://') || !input.startsWith('https://')) {
|
|
51
|
+
string = `http://${input}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
return !isEmpty(new URL(string).hostname);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default {
|
|
63
|
+
nullOrUndefined: isNullOrUndefined,
|
|
64
|
+
object: isObject,
|
|
65
|
+
number: isNumber,
|
|
66
|
+
string: isString,
|
|
67
|
+
boolean: isBoolean,
|
|
68
|
+
function: isFunction,
|
|
69
|
+
array: isArray,
|
|
70
|
+
weakMap: isWeakMap,
|
|
71
|
+
nodeList: isNodeList,
|
|
72
|
+
element: isElement,
|
|
73
|
+
textNode: isTextNode,
|
|
74
|
+
event: isEvent,
|
|
75
|
+
keyboardEvent: isKeyboardEvent,
|
|
76
|
+
cue: isCue,
|
|
77
|
+
track: isTrack,
|
|
78
|
+
promise: isPromise,
|
|
79
|
+
url: isUrl,
|
|
80
|
+
empty: isEmpty,
|
|
81
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Load image avoiding xhr/fetch CORS issues
|
|
3
|
+
// Server status can't be obtained this way unfortunately, so this uses "naturalWidth" to determine if the image has loaded
|
|
4
|
+
// By default it checks if it is at least 1px, but you can add a second argument to change this
|
|
5
|
+
// ==========================================================================
|
|
6
|
+
|
|
7
|
+
export default function loadImage(src, minWidth = 1) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const image = new Image();
|
|
10
|
+
|
|
11
|
+
const handler = () => {
|
|
12
|
+
delete image.onload;
|
|
13
|
+
delete image.onerror;
|
|
14
|
+
(image.naturalWidth >= minWidth ? resolve : reject)(image);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
Object.assign(image, { onload: handler, onerror: handler, src });
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Load an external script
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
import loadjs from 'loadjs';
|
|
6
|
+
|
|
7
|
+
export default function loadScript(url) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
loadjs(url, {
|
|
10
|
+
success: resolve,
|
|
11
|
+
error: reject,
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Sprite loader
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
import Storage from '../storage';
|
|
6
|
+
import fetch from './fetch';
|
|
7
|
+
import is from './is';
|
|
8
|
+
|
|
9
|
+
// Load an external SVG sprite
|
|
10
|
+
export default function loadSprite(url, id) {
|
|
11
|
+
if (!is.string(url)) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const prefix = 'cache';
|
|
16
|
+
const hasId = is.string(id);
|
|
17
|
+
let isCached = false;
|
|
18
|
+
const exists = () => document.getElementById(id) !== null;
|
|
19
|
+
|
|
20
|
+
const update = (container, data) => {
|
|
21
|
+
container.innerHTML = data;
|
|
22
|
+
|
|
23
|
+
// Check again incase of race condition
|
|
24
|
+
if (hasId && exists()) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Inject the SVG to the body
|
|
29
|
+
document.body.insertAdjacentElement('afterbegin', container);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Only load once if ID set
|
|
33
|
+
if (!hasId || !exists()) {
|
|
34
|
+
const useStorage = Storage.supported;
|
|
35
|
+
// Create container
|
|
36
|
+
const container = document.createElement('div');
|
|
37
|
+
container.setAttribute('hidden', '');
|
|
38
|
+
|
|
39
|
+
if (hasId) {
|
|
40
|
+
container.setAttribute('id', id);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check in cache
|
|
44
|
+
if (useStorage) {
|
|
45
|
+
const cached = window.localStorage.getItem(`${prefix}-${id}`);
|
|
46
|
+
isCached = cached !== null;
|
|
47
|
+
|
|
48
|
+
if (isCached) {
|
|
49
|
+
const data = JSON.parse(cached);
|
|
50
|
+
update(container, data.content);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Get the sprite
|
|
55
|
+
fetch(url)
|
|
56
|
+
.then((result) => {
|
|
57
|
+
if (is.empty(result)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (useStorage) {
|
|
62
|
+
try {
|
|
63
|
+
window.localStorage.setItem(
|
|
64
|
+
`${prefix}-${id}`,
|
|
65
|
+
JSON.stringify({
|
|
66
|
+
content: result,
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
catch {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
update(container, result);
|
|
74
|
+
})
|
|
75
|
+
.catch(() => {});
|
|
76
|
+
}
|
|
77
|
+
}
|