forked from katspaugh/wavesurfer.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
html-init.js
240 lines (228 loc) · 8.69 KB
/
html-init.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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
import loadScript from 'load-script';
/**
* @typedef {Object} InitParams
* @property {WavesurferParams} [defaults={backend: 'MediaElement,
* mediaControls: true}] The default wavesurfer initialisation parameters
* @property {string|NodeList} containers='wavesurfer' Selector or NodeList of
* elements to attach instances to
* @property {string}
* pluginCdnTemplate='//localhost:8080/dist/plugin/wavesurfer.[name].js' URL
* template for the dynamic loading of plugins
* @property {function} loadPlugin If set overwrites the default ajax function,
* can be used to inject plugins differently.
*/
/**
* The HTML initialisation API is not part of the main library bundle file and
* must be additionally included.
*
* The API attaches wavesurfer instances to all `<wavesurfer>` (can be
* customised), parsing their `data-` attributes to construct an options object
* for initialisation. Among other things it can dynamically load plugin code.
*
* The automatic initialisation can be prevented by setting the
* `window.WS_StopAutoInit` flag to true. The `html-init[.min].js` file exports
* the `Init` class, which can be called manually.
*
* Site-wide defaults can be added by setting `window.WS_InitOptions`.
*
* @example
* <!-- with minimap and timeline plugin -->
* <wavesurfer
* data-url="../media/demo.wav"
* data-plugins="minimap,timeline"
* data-minimap-height="30"
* data-minimap-wave-color="#ddd"
* data-minimap-progress-color="#999"
* data-timeline-font-size="13px"
* data-timeline-container="#timeline"
* >
* </wavesurfer>
* <div id="timeline"></div>
*
* <!-- with regions plugin -->
* <wavesurfer
* data-url="../media/demo.wav"
* data-plugins="regions"
* data-regions-regions='[{"start": 1,"end": 3,"color": "hsla(400, 100%, 30%, 0.5)"}]'
* >
* </wavesurfer>
*/
class Init {
/**
* Instantiate Init class and initialize elements
*
* This is done automatically if `window` is defined and
* `window.WS_StopAutoInit` is not set to true
*
* @param {WaveSurfer} WaveSurfer The WaveSurfer library object
* @param {InitParams} params initialisation options
*/
constructor(WaveSurfer, params = {}) {
if (!WaveSurfer) {
throw new Error('WaveSurfer is not available!');
}
/**
* cache WaveSurfer
* @private
*/
this.WaveSurfer = WaveSurfer;
/**
* build parameters, cache them in _params so minified builds are smaller
* @private
*/
const _params = (this.params = WaveSurfer.util.extend(
{},
{
// wavesurfer parameter defaults so by default the audio player is
// usable with native media element controls
defaults: {
backend: 'MediaElement',
mediaControls: true
},
// containers to instantiate on, can be selector string or NodeList
containers: 'wavesurfer',
// @TODO insert plugin CDN URIs
pluginCdnTemplate:
'//localhost:8080/dist/plugin/wavesurfer.[name].js',
// loadPlugin function can be overridden to inject plugin definition
// objects, this default function uses load-script to load a plugin
// and pass it to a callback
loadPlugin(name, cb) {
const src = _params.pluginCdnTemplate.replace(
'[name]',
name
);
loadScript(src, { async: false }, (err, plugin) => {
if (err) {
return console.error(
`WaveSurfer plugin ${name} not found at ${src}`
);
}
cb(window.WaveSurfer[name]);
});
}
},
params
));
/**
* The nodes that should have instances attached to them
* @type {NodeList}
*/
this.containers =
typeof _params.containers == 'string'
? document.querySelectorAll(_params.containers)
: _params.containers;
/** @private */
this.pluginCache = {};
/**
* An array of wavesurfer instances
* @type {Object[]}
*/
this.instances = [];
this.initAllEls();
}
/**
* Initialize all container elements
*/
initAllEls() {
// iterate over all the container elements
Array.prototype.forEach.call(this.containers, el => {
// load the plugins as an array of plugin names
const plugins = el.dataset.plugins
? el.dataset.plugins.split(',')
: [];
// no plugins to be loaded, just render
if (!plugins.length) {
return this.initEl(el);
}
// … or: iterate over all the plugins
plugins.forEach((name, i) => {
// plugin is not cached already, load it
if (!this.pluginCache[name]) {
this.params.loadPlugin(name, lib => {
this.pluginCache[name] = lib;
// plugins were all loaded, render the element
if (i + 1 === plugins.length) {
this.initEl(el, plugins);
}
});
} else if (i === plugins.length) {
// plugin was cached and this plugin was the last
this.initEl(el, plugins);
}
});
});
}
/**
* Initialize a single container element and add to `this.instances`
*
* @param {HTMLElement} el The container to instantiate wavesurfer to
* @param {PluginDefinition[]} plugins An Array of plugin names to initialize with
* @return {Object} Wavesurfer instance
*/
initEl(el, plugins = []) {
const jsonRegex = /^[[|{]/;
// initialize plugins with the correct options
const initialisedPlugins = plugins.map(plugin => {
const options = {};
// the regex to find this plugin attributes
const attrNameRegex = new RegExp('^' + plugin);
let attrName;
// iterate over all the data attributes and find ones for this
// plugin
for (attrName in el.dataset) {
const regexResult = attrNameRegex.exec(attrName);
if (regexResult) {
const attr = el.dataset[attrName];
// if the string begins with a [ or a { parse it as JSON
const prop = jsonRegex.test(attr) ? JSON.parse(attr) : attr;
// this removes the plugin prefix and changes the first letter
// of the resulting string to lower case to follow the naming
// convention of ws params
const unprefixedOptionName =
attrName
.slice(plugin.length, plugin.length + 1)
.toLowerCase() + attrName.slice(plugin.length + 1);
options[unprefixedOptionName] = prop;
}
}
return this.pluginCache[plugin].create(options);
});
// build parameter object for this container
const params = this.WaveSurfer.util.extend(
{ container: el },
this.params.defaults,
el.dataset,
{ plugins: initialisedPlugins }
);
// @TODO make nicer
el.style.display = 'block';
// initialize wavesurfer, load audio (with peaks if provided)
const instance = this.WaveSurfer.create(params);
const peaks = params.peaks ? JSON.parse(params.peaks) : undefined;
instance.load(params.url, peaks);
// push this instance into the instances cache
this.instances.push(instance);
return instance;
}
}
// if window object exists and window.WS_StopAutoInit is not true
if (typeof window === 'object' && !window.WS_StopAutoInit) {
// call init when document is ready, apply any custom default settings
// in window.WS_InitOptions
if (document.readyState === 'complete') {
window.WaveSurferInit = new Init(
window.WaveSurfer,
window.WS_InitOptions
);
} else {
window.addEventListener('load', () => {
window.WaveSurferInit = new Init(
window.WaveSurfer,
window.WS_InitOptions
);
});
}
}
// export init for manual usage
export default Init;