-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
utils.js
183 lines (166 loc) · 5.33 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import React from 'react'
import loadScript from 'load-script'
import merge from 'deepmerge'
/**
* Dynamic import is supported in CJS modules but needs interop require default logic.
*/
export const lazy = (componentImportFn) => React.lazy(async () => {
const obj = await componentImportFn()
return typeof obj.default === 'function' ? obj : obj.default
})
const MATCH_START_QUERY = /[?&#](?:start|t)=([0-9hms]+)/
const MATCH_END_QUERY = /[?&#]end=([0-9hms]+)/
const MATCH_START_STAMP = /(\d+)(h|m|s)/g
const MATCH_NUMERIC = /^\d+$/
// Parse YouTube URL for a start time param, ie ?t=1h14m30s
// and return the start time in seconds
function parseTimeParam (url, pattern) {
if (url instanceof Array) {
return undefined
}
const match = url.match(pattern)
if (match) {
const stamp = match[1]
if (stamp.match(MATCH_START_STAMP)) {
return parseTimeString(stamp)
}
if (MATCH_NUMERIC.test(stamp)) {
return parseInt(stamp)
}
}
return undefined
}
function parseTimeString (stamp) {
let seconds = 0
let array = MATCH_START_STAMP.exec(stamp)
while (array !== null) {
const [, count, period] = array
if (period === 'h') seconds += parseInt(count, 10) * 60 * 60
if (period === 'm') seconds += parseInt(count, 10) * 60
if (period === 's') seconds += parseInt(count, 10)
array = MATCH_START_STAMP.exec(stamp)
}
return seconds
}
export function parseStartTime (url) {
return parseTimeParam(url, MATCH_START_QUERY)
}
export function parseEndTime (url) {
return parseTimeParam(url, MATCH_END_QUERY)
}
// http://stackoverflow.com/a/38622545
export function randomString () {
return Math.random().toString(36).substr(2, 5)
}
export function queryString (object) {
return Object
.keys(object)
.map(key => `${key}=${object[key]}`)
.join('&')
}
function getGlobal (key) {
if (window[key]) {
return window[key]
}
if (window.exports && window.exports[key]) {
return window.exports[key]
}
if (window.module && window.module.exports && window.module.exports[key]) {
return window.module.exports[key]
}
return null
}
// Util function to load an external SDK
// or return the SDK if it is already loaded
const requests = {}
export const getSDK = enableStubOn(function getSDK (url, sdkGlobal, sdkReady = null, isLoaded = () => true, fetchScript = loadScript) {
const existingGlobal = getGlobal(sdkGlobal)
if (existingGlobal && isLoaded(existingGlobal)) {
return Promise.resolve(existingGlobal)
}
return new Promise((resolve, reject) => {
// If we are already loading the SDK, add the resolve and reject
// functions to the existing array of requests
if (requests[url]) {
requests[url].push({ resolve, reject })
return
}
requests[url] = [{ resolve, reject }]
const onLoaded = sdk => {
// When loaded, resolve all pending request promises
requests[url].forEach(request => request.resolve(sdk))
}
if (sdkReady) {
const previousOnReady = window[sdkReady]
window[sdkReady] = function () {
if (previousOnReady) previousOnReady()
onLoaded(getGlobal(sdkGlobal))
}
}
fetchScript(url, err => {
if (err) {
// Loading the SDK failed – reject all requests and
// reset the array of requests for this SDK
requests[url].forEach(request => request.reject(err))
requests[url] = null
} else if (!sdkReady) {
onLoaded(getGlobal(sdkGlobal))
}
})
})
})
export function getConfig (props, defaultProps) {
return merge(defaultProps.config, props.config)
}
export function omit (object, ...arrays) {
const omitKeys = [].concat(...arrays)
const output = {}
const keys = Object.keys(object)
for (const key of keys) {
if (omitKeys.indexOf(key) === -1) {
output[key] = object[key]
}
}
return output
}
export function callPlayer (method, ...args) {
// Util method for calling a method on this.player
// but guard against errors and console.warn instead
if (!this.player || !this.player[method]) {
let message = `ReactPlayer: ${this.constructor.displayName} player could not call %c${method}%c – `
if (!this.player) {
message += 'The player was not available'
} else if (!this.player[method]) {
message += 'The method was not available'
}
console.warn(message, 'font-weight: bold', '')
return null
}
return this.player[method](...args)
}
export function isMediaStream (url) {
return (
typeof window !== 'undefined' &&
typeof window.MediaStream !== 'undefined' &&
url instanceof window.MediaStream
)
}
export function isBlobUrl (url) {
return /^blob:/.test(url)
}
export function supportsWebKitPresentationMode (video = document.createElement('video')) {
// Check if Safari supports PiP, and is not on mobile (other than iPad)
// iPhone safari appears to "support" PiP through the check, however PiP does not function
const notMobile = /iPhone|iPod/.test(navigator.userAgent) === false
return video.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === 'function' && notMobile
}
// Workaround for being able to stub out functions in ESM exports.
// https://github.com/evanw/esbuild/issues/412#issuecomment-723047255
function enableStubOn (fn) {
if (globalThis.__TEST__) {
const wrap = (...args) => wrap.stub(...args)
wrap.stub = fn
return wrap
}
return fn
}