UNPKG

wavesurfer.js

Version:

Interactive navigable audio visualization using Web Audio and Canvas

242 lines (229 loc) 8.94 kB
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 request 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 = Object.assign( {}, { // 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) { // eslint-disable-next-line no-console 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 = Object.assign( { 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;