123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- 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 initialise 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 overriden 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();
- }
- /**
- * Initialise 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);
- }
- });
- });
- }
- /**
- * Initialise 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 initialise with
- * @return {Object} Wavesurfer instance
- */
- initEl(el, plugins = []) {
- const jsonRegex = /^[[|{]/;
- // initialise 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';
- // initialise 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;
|