diff --git a/README.md b/README.md index 54e084a..3fd2849 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ https://github.com/katspaugh/wavesurfer.js démo du rendu recherché: https://dev.to/jamland/audio-player-with-wavesurfer-js-react-1g3b +https://codesandbox.io/s/audio-player-with-wavesurferjs-react-bd499?from-embed marqueurs pour chapitres https://wavesurfer-js.org/plugins/markers.html diff --git a/index.html b/index.html index f1d7cb5..474c988 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,93 @@ - + + + + +

Example de lecteur audio enrichi

@@ -25,14 +111,41 @@
- - -
+
+

Rendu enrichi

+ +
+ - + + + + +

Sections

+ +
diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..e69de29 diff --git a/js/regions.min.js b/js/regions.min.js new file mode 100644 index 0000000..e8b4b60 --- /dev/null +++ b/js/regions.min.js @@ -0,0 +1,7 @@ +/*! + * wavesurfer.js regions plugin 5.1.0 (2021-06-20) + * https://wavesurfer-js.org + * @license BSD-3-Clause + */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("WaveSurfer",[],t):"object"==typeof exports?exports.WaveSurfer=t():(e.WaveSurfer=e.WaveSurfer||{},e.WaveSurfer.regions=t())}(this,(function(){return(()=>{"use strict";var e={23:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(638);function n(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,i)}return r}function s(e){for(var t=1;t=this.maxRegions}},{key:"add",value:function(e){var t=this;if(this.wouldExceedMaxRegions())return null;!e.minLength&&this.regionsMinLength&&(e=s(s({},e),{},{minLength:this.regionsMinLength}));var r=new this.wavesurfer.Region(e,this.util,this.wavesurfer);return this.list[r.id]=r,r.on("remove",(function(){delete t.list[r.id]})),r}},{key:"clear",value:function(){var e=this;Object.keys(this.list).forEach((function(t){e.list[t].remove()}))}},{key:"enableDragSelection",value:function(e){var t=this;this.disableDragSelection();var r,i,n,s,a,o,l,h=e.slop||2,u=this.wavesurfer.drawer.container,d=!1!==e.scroll&&this.wavesurfer.params.scrollParent,c=e.scrollSpeed||1,v=e.scrollThreshold||10,f=this.wavesurfer.getDuration(),g=0,p=function e(r){if(s&&o){var a=t.wrapper.scrollLeft+c*o;t.wrapper.scrollLeft=a=Math.min(i,Math.max(0,a));var l=t.wavesurfer.drawer.handleEvent(r);s.update({start:Math.min(l*f,n*f),end:Math.max(l*f,n*f)}),a0&&window.requestAnimationFrame((function(){e(r)}))}},m=function(e){e.touches&&e.touches.length>1||(f=t.wavesurfer.getDuration(),a=e.targetTouches?e.targetTouches[0].identifier:null,i=t.wrapper.scrollWidth-t.wrapper.clientWidth,l=t.util.withOrientation(t.wrapper.getBoundingClientRect(),t.vertical),r=!0,n=t.wavesurfer.drawer.handleEvent(e,!0),s=null,o=null)};this.wrapper.addEventListener("mousedown",m),this.wrapper.addEventListener("touchstart",m),this.on("disable-drag-selection",(function(){t.wrapper.removeEventListener("touchstart",m),t.wrapper.removeEventListener("mousedown",m)}));var w=function(e){e.touches&&e.touches.length>1||(r=!1,g=0,o=null,s&&(t.util.preventClick(),s.fireEvent("update-end",e),t.wavesurfer.fireEvent("region-update-end",s,e)),s=null)};this.wrapper.addEventListener("mouseleave",w),this.wrapper.addEventListener("mouseup",w),this.wrapper.addEventListener("touchend",w),document.body.addEventListener("mouseup",w),document.body.addEventListener("touchend",w),this.on("disable-drag-selection",(function(){document.body.removeEventListener("mouseup",w),document.body.removeEventListener("touchend",w),t.wrapper.removeEventListener("touchend",w),t.wrapper.removeEventListener("mouseup",w),t.wrapper.removeEventListener("mouseleave",w)}));var b=function(i){if(r&&!(++g<=h)&&!(i.touches&&i.touches.length>1)&&(!i.targetTouches||i.targetTouches[0].identifier==a)&&(s||(s=t.add(e||{})))){var c=t.wavesurfer.drawer.handleEvent(i),m=t.wavesurfer.regions.util.getRegionSnapToGridValue(n*f),w=t.wavesurfer.regions.util.getRegionSnapToGridValue(c*f);s.update({start:Math.min(w,m),end:Math.max(w,m)});var b=t.util.withOrientation(i,t.vertical);if(d&&u.clientWidth=l.right-v?1:null)&&p(i)}}};this.wrapper.addEventListener("mousemove",b),this.wrapper.addEventListener("touchmove",b),this.on("disable-drag-selection",(function(){t.wrapper.removeEventListener("touchmove",b),t.wrapper.removeEventListener("mousemove",b)})),this.wavesurfer.on("region-created",(function(e){t.regionsMinLength&&(e.minLength=t.regionsMinLength)}))}},{key:"disableDragSelection",value:function(){this.fireEvent("disable-drag-selection")}},{key:"getCurrentRegion",value:function(){var e=this,t=this.wavesurfer.getCurrentTime(),r=null;return Object.keys(this.list).forEach((function(i){var n=e.list[i];n.start<=t&&n.end>=t&&(!r||n.end-n.start{function r(e,t){for(var r=0;r=0&&ae&&(r=e-((i=e)-r)),null!=this.minLength&&(i=Math.max(r+this.minLength,i)),null!=this.maxLength&&(i=Math.min(r+this.maxLength,i)),null!=this.element){var n=Math.round(r/e*t),s=Math.round(i/e*t)-n;for(var a in this.style(this.element,{left:n+"px",width:s+"px",backgroundColor:this.color,cursor:this.drag?"move":"default"}),this.attributes)this.element.setAttribute("data-region-"+a,this.attributes[a]);this.showTooltip&&(this.element.title=this.formatTime(this.start,this.end))}}},{key:"bindInOut",value:function(){var e=this;this.firedIn=!1,this.firedOut=!1;var t=function(t){var r=Math.round(10*e.start)/10,i=Math.round(10*e.end)/10;t=Math.round(10*t)/10,!e.firedOut&&e.firedIn&&(r>t||i<=t)&&(e.firedOut=!0,e.firedIn=!1,e.fireEvent("out"),e.wavesurfer.fireEvent("region-out",e)),!e.firedIn&&r<=t&&i>t&&(e.firedIn=!0,e.firedOut=!1,e.fireEvent("in"),e.wavesurfer.fireEvent("region-in",e))};this.wavesurfer.backend.on("audioprocess",t),this.on("remove",(function(){e.wavesurfer.backend.un("audioprocess",t)})),this.on("out",(function(){if(e.loop){var t=e.wavesurfer.getCurrentTime();t>=e.start&&t<=e.end&&e.wavesurfer.play(e.start)}}))}},{key:"bindEvents",value:function(){var e=this,t=this.preventContextMenu;this.element.addEventListener("mouseenter",(function(t){e.fireEvent("mouseenter",t),e.wavesurfer.fireEvent("region-mouseenter",e,t)})),this.element.addEventListener("mouseleave",(function(t){e.fireEvent("mouseleave",t),e.wavesurfer.fireEvent("region-mouseleave",e,t)})),this.element.addEventListener("click",(function(t){t.preventDefault(),e.fireEvent("click",t),e.wavesurfer.fireEvent("region-click",e,t)})),this.element.addEventListener("dblclick",(function(t){t.stopPropagation(),t.preventDefault(),e.fireEvent("dblclick",t),e.wavesurfer.fireEvent("region-dblclick",e,t)})),this.element.addEventListener("contextmenu",(function(r){t&&r.preventDefault(),e.fireEvent("contextmenu",r),e.wavesurfer.fireEvent("region-contextmenu",e,r)})),(this.drag||this.resize)&&this.bindDragEvents()}},{key:"bindDragEvents",value:function(){var e,t,r,i,n,s,a,o,l,h=this,u=this.wavesurfer.drawer.container,d=this.scrollSpeed,c=(this.scrollThreshold,!1),v=function t(u){var c=h.util.withOrientation(u,h.vertical),v=h.wavesurfer.getDuration();if(s&&(r||n)){var f=c.clientX,g=0,p=0,m=0,w=h.regionsUtil.getRegionSnapToGridValue(h.wavesurfer.drawer.handleEvent(u)*v);if(r)-1===s?(p=o*h.wavesurfer.params.minPxPerSec,g=f-a.left):(p=l*h.wavesurfer.params.minPxPerSec,g=a.right-f);else{var b=h.minLength;b||(b=0),"start"===n?(w>h.end-b&&(w=h.end-b,m=d*s),w<0&&(w=0)):"end"===n&&(wv&&(w=v))}var y=h.wrapper.scrollLeft;if(-1===s){if(0===Math.round(y))return;if(Math.round(y-p+g)<=0)return}else{if(Math.round(y)===i)return;if(Math.round(y+p-g)>=i)return}var E=y-m+d*s;if(-1===s){var L=Math.max(0+p-g,E);h.wrapper.scrollLeft=E=L}else{var k=Math.min(i-p+g,E);h.wrapper.scrollLeft=E=k}var R=w-e;e=w,r?h.onDrag(R):h.onResize(R,n),window.requestAnimationFrame((function(){t(u)}))}},f=function(s){var u=h.wavesurfer.getDuration();s.touches&&s.touches.length>1||(t=s.targetTouches?s.targetTouches[0].identifier:null,(h.drag||h.resize)&&s.stopPropagation(),e=h.regionsUtil.getRegionSnapToGridValue(h.wavesurfer.drawer.handleEvent(s,!0)*u),o=e-h.start,l=h.end-e,i=h.wrapper.scrollWidth-h.wrapper.clientWidth,a=h.util.withOrientation(h.wrapper.getBoundingClientRect(),h.vertical),h.isResizing=!1,h.isDragging=!1,"handle"===s.target.tagName.toLowerCase()?(h.isResizing=!0,n=s.target.classList.contains("wavesurfer-handle-start")?"start":"end"):(h.isDragging=!0,r=!0,n=!1))},g=function(e){e.touches&&e.touches.length>1||((r||n)&&(h.isDragging=!1,h.isResizing=!1,r=!1,s=null,n=!1),c&&(c=!1,h.util.preventClick(),h.fireEvent("update-end",e),h.wavesurfer.fireEvent("region-update-end",h,e)))},p=function(i){var d=h.wavesurfer.getDuration(),f=h.util.withOrientation(i,h.vertical);if(!(i.touches&&i.touches.length>1)&&(!i.targetTouches||i.targetTouches[0].identifier==t)&&(r||n)){var g=h.regionsUtil.getRegionSnapToGridValue(h.wavesurfer.drawer.handleEvent(i)*d);if(r){var p=h.wavesurfer.getDuration();g>p-l&&(g=p-l),g-o<0&&(g=o)}if(n){var m=h.minLength;m||(m=0),"start"===n?(g>h.end-m&&(g=h.end-m),g<0&&(g=0)):"end"===n&&(gd&&(g=d))}var w=g-e;if(e=g,h.drag&&r&&(c=c||!!w,h.onDrag(w)),h.resize&&n&&(c=c||!!w,h.onResize(w,n)),h.scroll&&u.clientWidtha.right-h.edgeScrollWidth?1:null)&&v(i)}}};this.element.addEventListener("mousedown",f),this.element.addEventListener("touchstart",f),document.body.addEventListener("mousemove",p),document.body.addEventListener("touchmove",p,{passive:!1}),document.addEventListener("mouseup",g),document.body.addEventListener("touchend",g),this.on("remove",(function(){document.removeEventListener("mouseup",g),document.body.removeEventListener("touchend",g),document.body.removeEventListener("mousemove",p),document.body.removeEventListener("touchmove",p)})),this.wavesurfer.on("destroy",(function(){document.removeEventListener("mouseup",g),document.body.removeEventListener("touchend",g)}))}},{key:"onDrag",value:function(e){var t=this.wavesurfer.getDuration();this.end+e>t&&(e=t-this.end),this.start+e<0&&(e=-1*this.start),this.update({start:this.start+e,end:this.end+e})}},{key:"onResize",value:function(e,t){var r=this.wavesurfer.getDuration();"start"===t?(e>0&&this.end-(this.start+e)0&&this.end+e>r&&(e=r-this.end),this.update({start:Math.min(this.end+e,this.start),end:Math.max(this.end+e,this.start)}))}},{key:"updateHandlesResize",value:function(e){var t;t=e?this.vertical?"row-resize":"col-resize":"auto",this.handleLeftEl&&this.style(this.handleLeftEl,{cursor:t}),this.handleRightEl&&this.style(this.handleRightEl,{cursor:t})}}])&&r(t.prototype,i),n&&r(t,n),e}();t.Region=i}},t={};return function r(i){var n=t[i];if(void 0!==n)return n.exports;var s=t[i]={exports:{}};return e[i](s,s.exports,r),s.exports}(23)})()})); +//# sourceMappingURL=wavesurfer.regions.min.js.map \ No newline at end of file diff --git a/js/wavesurfer.js b/js/wavesurfer.js new file mode 100644 index 0000000..299cbd8 --- /dev/null +++ b/js/wavesurfer.js @@ -0,0 +1,6463 @@ +/*! + * wavesurfer.js 5.1.0 (2021-08-06) + * https://wavesurfer-js.org + * @license BSD-3-Clause + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define("WaveSurfer", [], factory); + else if(typeof exports === 'object') + exports["WaveSurfer"] = factory(); + else + root["WaveSurfer"] = factory(); +})(this, function() { +return /******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "./src/drawer.canvasentry.js": +/*!***********************************!*\ + !*** ./src/drawer.canvasentry.js ***! + \***********************************/ +/***/ ((module, exports, __webpack_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.default = void 0; + +var _style = _interopRequireDefault(__webpack_require__(/*! ./util/style */ "./src/util/style.js")); + +var _getId = _interopRequireDefault(__webpack_require__(/*! ./util/get-id */ "./src/util/get-id.js")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +/** + * The `CanvasEntry` class represents an element consisting of a wave `canvas` + * and an (optional) progress wave `canvas`. + * + * The `MultiCanvas` renderer uses one or more `CanvasEntry` instances to + * render a waveform, depending on the zoom level. + */ +var CanvasEntry = /*#__PURE__*/function () { + function CanvasEntry() { + _classCallCheck(this, CanvasEntry); + + /** + * The wave node + * + * @type {HTMLCanvasElement} + */ + this.wave = null; + /** + * The wave canvas rendering context + * + * @type {CanvasRenderingContext2D} + */ + + this.waveCtx = null; + /** + * The (optional) progress wave node + * + * @type {HTMLCanvasElement} + */ + + this.progress = null; + /** + * The (optional) progress wave canvas rendering context + * + * @type {CanvasRenderingContext2D} + */ + + this.progressCtx = null; + /** + * Start of the area the canvas should render, between 0 and 1 + * + * @type {number} + */ + + this.start = 0; + /** + * End of the area the canvas should render, between 0 and 1 + * + * @type {number} + */ + + this.end = 1; + /** + * Unique identifier for this entry + * + * @type {string} + */ + + this.id = (0, _getId.default)(typeof this.constructor.name !== 'undefined' ? this.constructor.name.toLowerCase() + '_' : 'canvasentry_'); + /** + * Canvas 2d context attributes + * + * @type {object} + */ + + this.canvasContextAttributes = {}; + } + /** + * Store the wave canvas element and create the 2D rendering context + * + * @param {HTMLCanvasElement} element The wave `canvas` element. + */ + + + _createClass(CanvasEntry, [{ + key: "initWave", + value: function initWave(element) { + this.wave = element; + this.waveCtx = this.wave.getContext('2d', this.canvasContextAttributes); + } + /** + * Store the progress wave canvas element and create the 2D rendering + * context + * + * @param {HTMLCanvasElement} element The progress wave `canvas` element. + */ + + }, { + key: "initProgress", + value: function initProgress(element) { + this.progress = element; + this.progressCtx = this.progress.getContext('2d', this.canvasContextAttributes); + } + /** + * Update the dimensions + * + * @param {number} elementWidth Width of the entry + * @param {number} totalWidth Total width of the multi canvas renderer + * @param {number} width The new width of the element + * @param {number} height The new height of the element + */ + + }, { + key: "updateDimensions", + value: function updateDimensions(elementWidth, totalWidth, width, height) { + // where the canvas starts and ends in the waveform, represented as a + // decimal between 0 and 1 + this.start = this.wave.offsetLeft / totalWidth || 0; + this.end = this.start + elementWidth / totalWidth; // set wave canvas dimensions + + this.wave.width = width; + this.wave.height = height; + var elementSize = { + width: elementWidth + 'px' + }; + (0, _style.default)(this.wave, elementSize); + + if (this.hasProgressCanvas) { + // set progress canvas dimensions + this.progress.width = width; + this.progress.height = height; + (0, _style.default)(this.progress, elementSize); + } + } + /** + * Clear the wave and progress rendering contexts + */ + + }, { + key: "clearWave", + value: function clearWave() { + // wave + this.waveCtx.clearRect(0, 0, this.waveCtx.canvas.width, this.waveCtx.canvas.height); // progress + + if (this.hasProgressCanvas) { + this.progressCtx.clearRect(0, 0, this.progressCtx.canvas.width, this.progressCtx.canvas.height); + } + } + /** + * Set the fill styles for wave and progress + * + * @param {string} waveColor Fill color for the wave canvas + * @param {?string} progressColor Fill color for the progress canvas + */ + + }, { + key: "setFillStyles", + value: function setFillStyles(waveColor, progressColor) { + this.waveCtx.fillStyle = waveColor; + + if (this.hasProgressCanvas) { + this.progressCtx.fillStyle = progressColor; + } + } + /** + * Set the canvas transforms for wave and progress + * + * @param {boolean} vertical Whether to render vertically + */ + + }, { + key: "applyCanvasTransforms", + value: function applyCanvasTransforms(vertical) { + if (vertical) { + // Reflect the waveform across the line y = -x + this.waveCtx.setTransform(0, 1, 1, 0, 0, 0); + + if (this.hasProgressCanvas) { + this.progressCtx.setTransform(0, 1, 1, 0, 0, 0); + } + } + } + /** + * Draw a rectangle for wave and progress + * + * @param {number} x X start position + * @param {number} y Y start position + * @param {number} width Width of the rectangle + * @param {number} height Height of the rectangle + * @param {number} radius Radius of the rectangle + */ + + }, { + key: "fillRects", + value: function fillRects(x, y, width, height, radius) { + this.fillRectToContext(this.waveCtx, x, y, width, height, radius); + + if (this.hasProgressCanvas) { + this.fillRectToContext(this.progressCtx, x, y, width, height, radius); + } + } + /** + * Draw the actual rectangle on a `canvas` element + * + * @param {CanvasRenderingContext2D} ctx Rendering context of target canvas + * @param {number} x X start position + * @param {number} y Y start position + * @param {number} width Width of the rectangle + * @param {number} height Height of the rectangle + * @param {number} radius Radius of the rectangle + */ + + }, { + key: "fillRectToContext", + value: function fillRectToContext(ctx, x, y, width, height, radius) { + if (!ctx) { + return; + } + + if (radius) { + this.drawRoundedRect(ctx, x, y, width, height, radius); + } else { + ctx.fillRect(x, y, width, height); + } + } + /** + * Draw a rounded rectangle on Canvas + * + * @param {CanvasRenderingContext2D} ctx Canvas context + * @param {number} x X-position of the rectangle + * @param {number} y Y-position of the rectangle + * @param {number} width Width of the rectangle + * @param {number} height Height of the rectangle + * @param {number} radius Radius of the rectangle + * + * @return {void} + * @example drawRoundedRect(ctx, 50, 50, 5, 10, 3) + */ + + }, { + key: "drawRoundedRect", + value: function drawRoundedRect(ctx, x, y, width, height, radius) { + if (height === 0) { + return; + } // peaks are float values from -1 to 1. Use absolute height values in + // order to correctly calculate rounded rectangle coordinates + + + if (height < 0) { + height *= -1; + y -= height; + } + + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + ctx.fill(); + } + /** + * Render the actual wave and progress lines + * + * @param {number[]} peaks Array with peaks data + * @param {number} absmax Maximum peak value (absolute) + * @param {number} halfH Half the height of the waveform + * @param {number} offsetY Offset to the top + * @param {number} start The x-offset of the beginning of the area that + * should be rendered + * @param {number} end The x-offset of the end of the area that + * should be rendered + */ + + }, { + key: "drawLines", + value: function drawLines(peaks, absmax, halfH, offsetY, start, end) { + this.drawLineToContext(this.waveCtx, peaks, absmax, halfH, offsetY, start, end); + + if (this.hasProgressCanvas) { + this.drawLineToContext(this.progressCtx, peaks, absmax, halfH, offsetY, start, end); + } + } + /** + * Render the actual waveform line on a `canvas` element + * + * @param {CanvasRenderingContext2D} ctx Rendering context of target canvas + * @param {number[]} peaks Array with peaks data + * @param {number} absmax Maximum peak value (absolute) + * @param {number} halfH Half the height of the waveform + * @param {number} offsetY Offset to the top + * @param {number} start The x-offset of the beginning of the area that + * should be rendered + * @param {number} end The x-offset of the end of the area that + * should be rendered + */ + + }, { + key: "drawLineToContext", + value: function drawLineToContext(ctx, peaks, absmax, halfH, offsetY, start, end) { + if (!ctx) { + return; + } + + var length = peaks.length / 2; + var first = Math.round(length * this.start); // use one more peak value to make sure we join peaks at ends -- unless, + // of course, this is the last canvas + + var last = Math.round(length * this.end) + 1; + var canvasStart = first; + var canvasEnd = last; + var scale = this.wave.width / (canvasEnd - canvasStart - 1); // optimization + + var halfOffset = halfH + offsetY; + var absmaxHalf = absmax / halfH; + ctx.beginPath(); + ctx.moveTo((canvasStart - first) * scale, halfOffset); + ctx.lineTo((canvasStart - first) * scale, halfOffset - Math.round((peaks[2 * canvasStart] || 0) / absmaxHalf)); + var i, peak, h; + + for (i = canvasStart; i < canvasEnd; i++) { + peak = peaks[2 * i] || 0; + h = Math.round(peak / absmaxHalf); + ctx.lineTo((i - first) * scale + this.halfPixel, halfOffset - h); + } // draw the bottom edge going backwards, to make a single + // closed hull to fill + + + var j = canvasEnd - 1; + + for (j; j >= canvasStart; j--) { + peak = peaks[2 * j + 1] || 0; + h = Math.round(peak / absmaxHalf); + ctx.lineTo((j - first) * scale + this.halfPixel, halfOffset - h); + } + + ctx.lineTo((canvasStart - first) * scale, halfOffset - Math.round((peaks[2 * canvasStart + 1] || 0) / absmaxHalf)); + ctx.closePath(); + ctx.fill(); + } + /** + * Destroys this entry + */ + + }, { + key: "destroy", + value: function destroy() { + this.waveCtx = null; + this.wave = null; + this.progressCtx = null; + this.progress = null; + } + /** + * Return image data of the wave `canvas` element + * + * When using a `type` of `'blob'`, this will return a `Promise` that + * resolves with a `Blob` instance. + * + * @param {string} format='image/png' An optional value of a format type. + * @param {number} quality=0.92 An optional value between 0 and 1. + * @param {string} type='dataURL' Either 'dataURL' or 'blob'. + * @return {string|Promise} When using the default `'dataURL'` `type` this + * returns a data URL. When using the `'blob'` `type` this returns a + * `Promise` that resolves with a `Blob` instance. + */ + + }, { + key: "getImage", + value: function getImage(format, quality, type) { + var _this = this; + + if (type === 'blob') { + return new Promise(function (resolve) { + _this.wave.toBlob(resolve, format, quality); + }); + } else if (type === 'dataURL') { + return this.wave.toDataURL(format, quality); + } + } + }]); + + return CanvasEntry; +}(); + +exports.default = CanvasEntry; +module.exports = exports.default; + +/***/ }), + +/***/ "./src/drawer.js": +/*!***********************!*\ + !*** ./src/drawer.js ***! + \***********************/ +/***/ ((module, exports, __webpack_require__) => { + +"use strict"; + + +function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.default = void 0; + +var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js")); + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + +function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } + +/** + * Parent class for renderers + * + * @extends {Observer} + */ +var Drawer = /*#__PURE__*/function (_util$Observer) { + _inherits(Drawer, _util$Observer); + + var _super = _createSuper(Drawer); + + /** + * @param {HTMLElement} container The container node of the wavesurfer instance + * @param {WavesurferParams} params The wavesurfer initialisation options + */ + function Drawer(container, params) { + var _this; + + _classCallCheck(this, Drawer); + + _this = _super.call(this); + _this.container = util.withOrientation(container, params.vertical); + /** + * @type {WavesurferParams} + */ + + _this.params = params; + /** + * The width of the renderer + * @type {number} + */ + + _this.width = 0; + /** + * The height of the renderer + * @type {number} + */ + + _this.height = params.height * _this.params.pixelRatio; + _this.lastPos = 0; + /** + * The `` element which is added to the container + * @type {HTMLElement} + */ + + _this.wrapper = null; + return _this; + } + /** + * Alias of `util.style` + * + * @param {HTMLElement} el The element that the styles will be applied to + * @param {Object} styles The map of propName: attribute, both are used as-is + * @return {HTMLElement} el + */ + + + _createClass(Drawer, [{ + key: "style", + value: function style(el, styles) { + return util.style(el, styles); + } + /** + * Create the wrapper `` element, style it and set up the events for + * interaction + */ + + }, { + key: "createWrapper", + value: function createWrapper() { + this.wrapper = util.withOrientation(this.container.appendChild(document.createElement('wave')), this.params.vertical); + this.style(this.wrapper, { + display: 'block', + position: 'relative', + userSelect: 'none', + webkitUserSelect: 'none', + height: this.params.height + 'px' + }); + + if (this.params.fillParent || this.params.scrollParent) { + this.style(this.wrapper, { + width: '100%', + overflowX: this.params.hideScrollbar ? 'hidden' : 'auto', + overflowY: 'hidden' + }); + } + + this.setupWrapperEvents(); + } + /** + * Handle click event + * + * @param {Event} e Click event + * @param {?boolean} noPrevent Set to true to not call `e.preventDefault()` + * @return {number} Playback position from 0 to 1 + */ + + }, { + key: "handleEvent", + value: function handleEvent(e, noPrevent) { + !noPrevent && e.preventDefault(); + var clientX = util.withOrientation(e.targetTouches ? e.targetTouches[0] : e, this.params.vertical).clientX; + var bbox = this.wrapper.getBoundingClientRect(); + var nominalWidth = this.width; + var parentWidth = this.getWidth(); + var progressPixels = this.getProgressPixels(bbox, clientX); + var progress; + + if (!this.params.fillParent && nominalWidth < parentWidth) { + progress = progressPixels * (this.params.pixelRatio / nominalWidth) || 0; + } else { + progress = (progressPixels + this.wrapper.scrollLeft) / this.wrapper.scrollWidth || 0; + } + + return util.clamp(progress, 0, 1); + } + }, { + key: "getProgressPixels", + value: function getProgressPixels(wrapperBbox, clientX) { + if (this.params.rtl) { + return wrapperBbox.right - clientX; + } else { + return clientX - wrapperBbox.left; + } + } + }, { + key: "setupWrapperEvents", + value: function setupWrapperEvents() { + var _this2 = this; + + this.wrapper.addEventListener('click', function (e) { + var orientedEvent = util.withOrientation(e, _this2.params.vertical); + var scrollbarHeight = _this2.wrapper.offsetHeight - _this2.wrapper.clientHeight; + + if (scrollbarHeight !== 0) { + // scrollbar is visible. Check if click was on it + var bbox = _this2.wrapper.getBoundingClientRect(); + + if (orientedEvent.clientY >= bbox.bottom - scrollbarHeight) { + // ignore mousedown as it was on the scrollbar + return; + } + } + + if (_this2.params.interact) { + _this2.fireEvent('click', e, _this2.handleEvent(e)); + } + }); + this.wrapper.addEventListener('dblclick', function (e) { + if (_this2.params.interact) { + _this2.fireEvent('dblclick', e, _this2.handleEvent(e)); + } + }); + this.wrapper.addEventListener('scroll', function (e) { + return _this2.fireEvent('scroll', e); + }); + } + /** + * Draw peaks on the canvas + * + * @param {number[]|Number.} peaks Can also be an array of arrays + * for split channel rendering + * @param {number} length The width of the area that should be drawn + * @param {number} start The x-offset of the beginning of the area that + * should be rendered + * @param {number} end The x-offset of the end of the area that should be + * rendered + */ + + }, { + key: "drawPeaks", + value: function drawPeaks(peaks, length, start, end) { + if (!this.setWidth(length)) { + this.clearWave(); + } + + this.params.barWidth ? this.drawBars(peaks, 0, start, end) : this.drawWave(peaks, 0, start, end); + } + /** + * Scroll to the beginning + */ + + }, { + key: "resetScroll", + value: function resetScroll() { + if (this.wrapper !== null) { + this.wrapper.scrollLeft = 0; + } + } + /** + * Recenter the view-port at a certain percent of the waveform + * + * @param {number} percent Value from 0 to 1 on the waveform + */ + + }, { + key: "recenter", + value: function recenter(percent) { + var position = this.wrapper.scrollWidth * percent; + this.recenterOnPosition(position, true); + } + /** + * Recenter the view-port on a position, either scroll there immediately or + * in steps of 5 pixels + * + * @param {number} position X-offset in pixels + * @param {boolean} immediate Set to true to immediately scroll somewhere + */ + + }, { + key: "recenterOnPosition", + value: function recenterOnPosition(position, immediate) { + var scrollLeft = this.wrapper.scrollLeft; + var half = ~~(this.wrapper.clientWidth / 2); + var maxScroll = this.wrapper.scrollWidth - this.wrapper.clientWidth; + var target = position - half; + var offset = target - scrollLeft; + + if (maxScroll == 0) { + // no need to continue if scrollbar is not there + return; + } // if the cursor is currently visible... + + + if (!immediate && -half <= offset && offset < half) { + // set rate at which waveform is centered + var rate = this.params.autoCenterRate; // make rate depend on width of view and length of waveform + + rate /= half; + rate *= maxScroll; + offset = Math.max(-rate, Math.min(rate, offset)); + target = scrollLeft + offset; + } // limit target to valid range (0 to maxScroll) + + + target = Math.max(0, Math.min(maxScroll, target)); // no use attempting to scroll if we're not moving + + if (target != scrollLeft) { + this.wrapper.scrollLeft = target; + } + } + /** + * Get the current scroll position in pixels + * + * @return {number} Horizontal scroll position in pixels + */ + + }, { + key: "getScrollX", + value: function getScrollX() { + var x = 0; + + if (this.wrapper) { + var pixelRatio = this.params.pixelRatio; + x = Math.round(this.wrapper.scrollLeft * pixelRatio); // In cases of elastic scroll (safari with mouse wheel) you can + // scroll beyond the limits of the container + // Calculate and floor the scrollable extent to make sure an out + // of bounds value is not returned + // Ticket #1312 + + if (this.params.scrollParent) { + var maxScroll = ~~(this.wrapper.scrollWidth * pixelRatio - this.getWidth()); + x = Math.min(maxScroll, Math.max(0, x)); + } + } + + return x; + } + /** + * Get the width of the container + * + * @return {number} The width of the container + */ + + }, { + key: "getWidth", + value: function getWidth() { + return Math.round(this.container.clientWidth * this.params.pixelRatio); + } + /** + * Set the width of the container + * + * @param {number} width The new width of the container + * @return {boolean} Whether the width of the container was updated or not + */ + + }, { + key: "setWidth", + value: function setWidth(width) { + if (this.width == width) { + return false; + } + + this.width = width; + + if (this.params.fillParent || this.params.scrollParent) { + this.style(this.wrapper, { + width: '' + }); + } else { + var newWidth = ~~(this.width / this.params.pixelRatio) + 'px'; + this.style(this.wrapper, { + width: newWidth + }); + } + + this.updateSize(); + return true; + } + /** + * Set the height of the container + * + * @param {number} height The new height of the container. + * @return {boolean} Whether the height of the container was updated or not + */ + + }, { + key: "setHeight", + value: function setHeight(height) { + if (height == this.height) { + return false; + } + + this.height = height; + this.style(this.wrapper, { + height: ~~(this.height / this.params.pixelRatio) + 'px' + }); + this.updateSize(); + return true; + } + /** + * Called by wavesurfer when progress should be rendered + * + * @param {number} progress From 0 to 1 + */ + + }, { + key: "progress", + value: function progress(_progress) { + var minPxDelta = 1 / this.params.pixelRatio; + var pos = Math.round(_progress * this.width) * minPxDelta; + + if (pos < this.lastPos || pos - this.lastPos >= minPxDelta) { + this.lastPos = pos; + + if (this.params.scrollParent && this.params.autoCenter) { + var newPos = ~~(this.wrapper.scrollWidth * _progress); + this.recenterOnPosition(newPos, this.params.autoCenterImmediately); + } + + this.updateProgress(pos); + } + } + /** + * This is called when wavesurfer is destroyed + */ + + }, { + key: "destroy", + value: function destroy() { + this.unAll(); + + if (this.wrapper) { + if (this.wrapper.parentNode == this.container.domElement) { + this.container.removeChild(this.wrapper.domElement); + } + + this.wrapper = null; + } + } + /* Renderer-specific methods */ + + /** + * Called after cursor related params have changed. + * + * @abstract + */ + + }, { + key: "updateCursor", + value: function updateCursor() {} + /** + * Called when the size of the container changes so the renderer can adjust + * + * @abstract + */ + + }, { + key: "updateSize", + value: function updateSize() {} + /** + * Draw a waveform with bars + * + * @abstract + * @param {number[]|Number.} peaks Can also be an array of arrays for split channel + * rendering + * @param {number} channelIndex The index of the current channel. Normally + * should be 0 + * @param {number} start The x-offset of the beginning of the area that + * should be rendered + * @param {number} end The x-offset of the end of the area that should be + * rendered + */ + + }, { + key: "drawBars", + value: function drawBars(peaks, channelIndex, start, end) {} + /** + * Draw a waveform + * + * @abstract + * @param {number[]|Number.} peaks Can also be an array of arrays for split channel + * rendering + * @param {number} channelIndex The index of the current channel. Normally + * should be 0 + * @param {number} start The x-offset of the beginning of the area that + * should be rendered + * @param {number} end The x-offset of the end of the area that should be + * rendered + */ + + }, { + key: "drawWave", + value: function drawWave(peaks, channelIndex, start, end) {} + /** + * Clear the waveform + * + * @abstract + */ + + }, { + key: "clearWave", + value: function clearWave() {} + /** + * Render the new progress + * + * @abstract + * @param {number} position X-Offset of progress position in pixels + */ + + }, { + key: "updateProgress", + value: function updateProgress(position) {} + }]); + + return Drawer; +}(util.Observer); + +exports.default = Drawer; +module.exports = exports.default; + +/***/ }), + +/***/ "./src/drawer.multicanvas.js": +/*!***********************************!*\ + !*** ./src/drawer.multicanvas.js ***! + \***********************************/ +/***/ ((module, exports, __webpack_require__) => { + +"use strict"; + + +function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.default = void 0; + +var _drawer = _interopRequireDefault(__webpack_require__(/*! ./drawer */ "./src/drawer.js")); + +var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js")); + +var _drawer2 = _interopRequireDefault(__webpack_require__(/*! ./drawer.canvasentry */ "./src/drawer.canvasentry.js")); + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + +function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } + +/** + * MultiCanvas renderer for wavesurfer. Is currently the default and sole + * builtin renderer. + * + * A `MultiCanvas` consists of one or more `CanvasEntry` instances, depending + * on the zoom level. + */ +var MultiCanvas = /*#__PURE__*/function (_Drawer) { + _inherits(MultiCanvas, _Drawer); + + var _super = _createSuper(MultiCanvas); + + /** + * @param {HTMLElement} container The container node of the wavesurfer instance + * @param {WavesurferParams} params The wavesurfer initialisation options + */ + function MultiCanvas(container, params) { + var _this; + + _classCallCheck(this, MultiCanvas); + + _this = _super.call(this, container, params); + /** + * @type {number} + */ + + _this.maxCanvasWidth = params.maxCanvasWidth; + /** + * @type {number} + */ + + _this.maxCanvasElementWidth = Math.round(params.maxCanvasWidth / params.pixelRatio); + /** + * Whether or not the progress wave is rendered. If the `waveColor` + * and `progressColor` are the same color it is not. + * + * @type {boolean} + */ + + _this.hasProgressCanvas = params.waveColor != params.progressColor; + /** + * @type {number} + */ + + _this.halfPixel = 0.5 / params.pixelRatio; + /** + * List of `CanvasEntry` instances. + * + * @type {Array} + */ + + _this.canvases = []; + /** + * @type {HTMLElement} + */ + + _this.progressWave = null; + /** + * Class used to generate entries. + * + * @type {function} + */ + + _this.EntryClass = _drawer2.default; + /** + * Canvas 2d context attributes. + * + * @type {object} + */ + + _this.canvasContextAttributes = params.drawingContextAttributes; + /** + * Overlap added between entries to prevent vertical white stripes + * between `canvas` elements. + * + * @type {number} + */ + + _this.overlap = 2 * Math.ceil(params.pixelRatio / 2); + /** + * The radius of the wave bars. Makes bars rounded + * + * @type {number} + */ + + _this.barRadius = params.barRadius || 0; + /** + * Whether to render the waveform vertically. Defaults to false. + * + * @type {boolean} + */ + + _this.vertical = params.vertical; + return _this; + } + /** + * Initialize the drawer + */ + + + _createClass(MultiCanvas, [{ + key: "init", + value: function init() { + this.createWrapper(); + this.createElements(); + } + /** + * Create the canvas elements and style them + * + */ + + }, { + key: "createElements", + value: function createElements() { + this.progressWave = util.withOrientation(this.wrapper.appendChild(document.createElement('wave')), this.params.vertical); + this.style(this.progressWave, { + position: 'absolute', + zIndex: 3, + left: 0, + top: 0, + bottom: 0, + overflow: 'hidden', + width: '0', + display: 'none', + boxSizing: 'border-box', + borderRightStyle: 'solid', + pointerEvents: 'none' + }); + this.addCanvas(); + this.updateCursor(); + } + /** + * Update cursor style + */ + + }, { + key: "updateCursor", + value: function updateCursor() { + this.style(this.progressWave, { + borderRightWidth: this.params.cursorWidth + 'px', + borderRightColor: this.params.cursorColor + }); + } + /** + * Adjust to the updated size by adding or removing canvases + */ + + }, { + key: "updateSize", + value: function updateSize() { + var _this2 = this; + + var totalWidth = Math.round(this.width / this.params.pixelRatio); + var requiredCanvases = Math.ceil(totalWidth / (this.maxCanvasElementWidth + this.overlap)); // add required canvases + + while (this.canvases.length < requiredCanvases) { + this.addCanvas(); + } // remove older existing canvases, if any + + + while (this.canvases.length > requiredCanvases) { + this.removeCanvas(); + } + + var canvasWidth = this.maxCanvasWidth + this.overlap; + var lastCanvas = this.canvases.length - 1; + this.canvases.forEach(function (entry, i) { + if (i == lastCanvas) { + canvasWidth = _this2.width - _this2.maxCanvasWidth * lastCanvas; + } + + _this2.updateDimensions(entry, canvasWidth, _this2.height); + + entry.clearWave(); + }); + } + /** + * Add a canvas to the canvas list + * + */ + + }, { + key: "addCanvas", + value: function addCanvas() { + var entry = new this.EntryClass(); + entry.canvasContextAttributes = this.canvasContextAttributes; + entry.hasProgressCanvas = this.hasProgressCanvas; + entry.halfPixel = this.halfPixel; + var leftOffset = this.maxCanvasElementWidth * this.canvases.length; // wave + + var wave = util.withOrientation(this.wrapper.appendChild(document.createElement('canvas')), this.params.vertical); + this.style(wave, { + position: 'absolute', + zIndex: 2, + left: leftOffset + 'px', + top: 0, + bottom: 0, + height: '100%', + pointerEvents: 'none' + }); + entry.initWave(wave); // progress + + if (this.hasProgressCanvas) { + var progress = util.withOrientation(this.progressWave.appendChild(document.createElement('canvas')), this.params.vertical); + this.style(progress, { + position: 'absolute', + left: leftOffset + 'px', + top: 0, + bottom: 0, + height: '100%' + }); + entry.initProgress(progress); + } + + this.canvases.push(entry); + } + /** + * Pop single canvas from the list + * + */ + + }, { + key: "removeCanvas", + value: function removeCanvas() { + var lastEntry = this.canvases[this.canvases.length - 1]; // wave + + lastEntry.wave.parentElement.removeChild(lastEntry.wave.domElement); // progress + + if (this.hasProgressCanvas) { + lastEntry.progress.parentElement.removeChild(lastEntry.progress.domElement); + } // cleanup + + + if (lastEntry) { + lastEntry.destroy(); + lastEntry = null; + } + + this.canvases.pop(); + } + /** + * Update the dimensions of a canvas element + * + * @param {CanvasEntry} entry Target entry + * @param {number} width The new width of the element + * @param {number} height The new height of the element + */ + + }, { + key: "updateDimensions", + value: function updateDimensions(entry, width, height) { + var elementWidth = Math.round(width / this.params.pixelRatio); + var totalWidth = Math.round(this.width / this.params.pixelRatio); // update canvas dimensions + + entry.updateDimensions(elementWidth, totalWidth, width, height); // style element + + this.style(this.progressWave, { + display: 'block' + }); + } + /** + * Clear the whole multi-canvas + */ + + }, { + key: "clearWave", + value: function clearWave() { + var _this3 = this; + + util.frame(function () { + _this3.canvases.forEach(function (entry) { + return entry.clearWave(); + }); + })(); + } + /** + * Draw a waveform with bars + * + * @param {number[]|Number.} peaks Can also be an array of arrays + * for split channel rendering + * @param {number} channelIndex The index of the current channel. Normally + * should be 0. Must be an integer. + * @param {number} start The x-offset of the beginning of the area that + * should be rendered + * @param {number} end The x-offset of the end of the area that should be + * rendered + * @returns {void} + */ + + }, { + key: "drawBars", + value: function drawBars(peaks, channelIndex, start, end) { + var _this4 = this; + + return this.prepareDraw(peaks, channelIndex, start, end, function (_ref) { + var absmax = _ref.absmax, + hasMinVals = _ref.hasMinVals, + height = _ref.height, + offsetY = _ref.offsetY, + halfH = _ref.halfH, + peaks = _ref.peaks, + ch = _ref.channelIndex; + + // if drawBars was called within ws.empty we don't pass a start and + // don't want anything to happen + if (start === undefined) { + return; + } // Skip every other value if there are negatives. + + + var peakIndexScale = hasMinVals ? 2 : 1; + var length = peaks.length / peakIndexScale; + var bar = _this4.params.barWidth * _this4.params.pixelRatio; + var gap = _this4.params.barGap === null ? Math.max(_this4.params.pixelRatio, ~~(bar / 2)) : Math.max(_this4.params.pixelRatio, _this4.params.barGap * _this4.params.pixelRatio); + var step = bar + gap; + var scale = length / _this4.width; + var first = start; + var last = end; + var i = first; + + for (i; i < last; i += step) { + var peak = peaks[Math.floor(i * scale * peakIndexScale)] || 0; + var h = Math.round(peak / absmax * halfH); + /* in case of silences, allow the user to specify that we + * always draw *something* (normally a 1px high bar) */ + + if (h == 0 && _this4.params.barMinHeight) { + h = _this4.params.barMinHeight; + } + + _this4.fillRect(i + _this4.halfPixel, halfH - h + offsetY, bar + _this4.halfPixel, h * 2, _this4.barRadius, ch); + } + }); + } + /** + * Draw a waveform + * + * @param {number[]|Number.} peaks Can also be an array of arrays + * for split channel rendering + * @param {number} channelIndex The index of the current channel. Normally + * should be 0 + * @param {number?} start The x-offset of the beginning of the area that + * should be rendered (If this isn't set only a flat line is rendered) + * @param {number?} end The x-offset of the end of the area that should be + * rendered + * @returns {void} + */ + + }, { + key: "drawWave", + value: function drawWave(peaks, channelIndex, start, end) { + var _this5 = this; + + return this.prepareDraw(peaks, channelIndex, start, end, function (_ref2) { + var absmax = _ref2.absmax, + hasMinVals = _ref2.hasMinVals, + height = _ref2.height, + offsetY = _ref2.offsetY, + halfH = _ref2.halfH, + peaks = _ref2.peaks, + channelIndex = _ref2.channelIndex; + + if (!hasMinVals) { + var reflectedPeaks = []; + var len = peaks.length; + var i = 0; + + for (i; i < len; i++) { + reflectedPeaks[2 * i] = peaks[i]; + reflectedPeaks[2 * i + 1] = -peaks[i]; + } + + peaks = reflectedPeaks; + } // if drawWave was called within ws.empty we don't pass a start and + // end and simply want a flat line + + + if (start !== undefined) { + _this5.drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex); + } // always draw a median line + + + _this5.fillRect(0, halfH + offsetY - _this5.halfPixel, _this5.width, _this5.halfPixel, _this5.barRadius, channelIndex); + }); + } + /** + * Tell the canvas entries to render their portion of the waveform + * + * @param {number[]} peaks Peaks data + * @param {number} absmax Maximum peak value (absolute) + * @param {number} halfH Half the height of the waveform + * @param {number} offsetY Offset to the top + * @param {number} start The x-offset of the beginning of the area that + * should be rendered + * @param {number} end The x-offset of the end of the area that + * should be rendered + * @param {channelIndex} channelIndex The channel index of the line drawn + */ + + }, { + key: "drawLine", + value: function drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex) { + var _this6 = this; + + var _ref3 = this.params.splitChannelsOptions.channelColors[channelIndex] || {}, + waveColor = _ref3.waveColor, + progressColor = _ref3.progressColor; + + this.canvases.forEach(function (entry, i) { + _this6.setFillStyles(entry, waveColor, progressColor); + + _this6.applyCanvasTransforms(entry, _this6.params.vertical); + + entry.drawLines(peaks, absmax, halfH, offsetY, start, end); + }); + } + /** + * Draw a rectangle on the multi-canvas + * + * @param {number} x X-position of the rectangle + * @param {number} y Y-position of the rectangle + * @param {number} width Width of the rectangle + * @param {number} height Height of the rectangle + * @param {number} radius Radius of the rectangle + * @param {channelIndex} channelIndex The channel index of the bar drawn + */ + + }, { + key: "fillRect", + value: function fillRect(x, y, width, height, radius, channelIndex) { + var startCanvas = Math.floor(x / this.maxCanvasWidth); + var endCanvas = Math.min(Math.ceil((x + width) / this.maxCanvasWidth) + 1, this.canvases.length); + var i = startCanvas; + + for (i; i < endCanvas; i++) { + var entry = this.canvases[i]; + var leftOffset = i * this.maxCanvasWidth; + var intersection = { + x1: Math.max(x, i * this.maxCanvasWidth), + y1: y, + x2: Math.min(x + width, i * this.maxCanvasWidth + entry.wave.width), + y2: y + height + }; + + if (intersection.x1 < intersection.x2) { + var _ref4 = this.params.splitChannelsOptions.channelColors[channelIndex] || {}, + waveColor = _ref4.waveColor, + progressColor = _ref4.progressColor; + + this.setFillStyles(entry, waveColor, progressColor); + this.applyCanvasTransforms(entry, this.params.vertical); + entry.fillRects(intersection.x1 - leftOffset, intersection.y1, intersection.x2 - intersection.x1, intersection.y2 - intersection.y1, radius); + } + } + } + /** + * Returns whether to hide the channel from being drawn based on params. + * + * @param {number} channelIndex The index of the current channel. + * @returns {bool} True to hide the channel, false to draw. + */ + + }, { + key: "hideChannel", + value: function hideChannel(channelIndex) { + return this.params.splitChannels && this.params.splitChannelsOptions.filterChannels.includes(channelIndex); + } + /** + * Performs preparation tasks and calculations which are shared by `drawBars` + * and `drawWave` + * + * @param {number[]|Number.} peaks Can also be an array of arrays for + * split channel rendering + * @param {number} channelIndex The index of the current channel. Normally + * should be 0 + * @param {number?} start The x-offset of the beginning of the area that + * should be rendered. If this isn't set only a flat line is rendered + * @param {number?} end The x-offset of the end of the area that should be + * rendered + * @param {function} fn The render function to call, e.g. `drawWave` + * @param {number} drawIndex The index of the current channel after filtering. + * @param {number?} normalizedMax Maximum modulation value across channels for use with relativeNormalization. Ignored when undefined + * @returns {void} + */ + + }, { + key: "prepareDraw", + value: function prepareDraw(peaks, channelIndex, start, end, fn, drawIndex, normalizedMax) { + var _this7 = this; + + return util.frame(function () { + // Split channels and call this function with the channelIndex set + if (peaks[0] instanceof Array) { + var channels = peaks; + + if (_this7.params.splitChannels) { + var filteredChannels = channels.filter(function (c, i) { + return !_this7.hideChannel(i); + }); + + if (!_this7.params.splitChannelsOptions.overlay) { + _this7.setHeight(Math.max(filteredChannels.length, 1) * _this7.params.height * _this7.params.pixelRatio); + } + + var overallAbsMax; + + if (_this7.params.splitChannelsOptions && _this7.params.splitChannelsOptions.relativeNormalization) { + // calculate maximum peak across channels to use for normalization + overallAbsMax = util.max(channels.map(function (channelPeaks) { + return util.absMax(channelPeaks); + })); + } + + return channels.forEach(function (channelPeaks, i) { + return _this7.prepareDraw(channelPeaks, i, start, end, fn, filteredChannels.indexOf(channelPeaks), overallAbsMax); + }); + } + + peaks = channels[0]; + } // Return and do not draw channel peaks if hidden. + + + if (_this7.hideChannel(channelIndex)) { + return; + } // calculate maximum modulation value, either from the barHeight + // parameter or if normalize=true from the largest value in the peak + // set + + + var absmax = 1 / _this7.params.barHeight; + + if (_this7.params.normalize) { + absmax = normalizedMax === undefined ? util.absMax(peaks) : normalizedMax; + } // Bar wave draws the bottom only as a reflection of the top, + // so we don't need negative values + + + var hasMinVals = [].some.call(peaks, function (val) { + return val < 0; + }); + var height = _this7.params.height * _this7.params.pixelRatio; + var halfH = height / 2; + var offsetY = height * drawIndex || 0; // Override offsetY if overlay is true + + if (_this7.params.splitChannelsOptions && _this7.params.splitChannelsOptions.overlay) { + offsetY = 0; + } + + return fn({ + absmax: absmax, + hasMinVals: hasMinVals, + height: height, + offsetY: offsetY, + halfH: halfH, + peaks: peaks, + channelIndex: channelIndex + }); + })(); + } + /** + * Set the fill styles for a certain entry (wave and progress) + * + * @param {CanvasEntry} entry Target entry + * @param {string} waveColor Wave color to draw this entry + * @param {string} progressColor Progress color to draw this entry + */ + + }, { + key: "setFillStyles", + value: function setFillStyles(entry) { + var waveColor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.params.waveColor; + var progressColor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.params.progressColor; + entry.setFillStyles(waveColor, progressColor); + } + /** + * Set the canvas transforms for a certain entry (wave and progress) + * + * @param {CanvasEntry} entry Target entry + * @param {boolean} vertical Whether to render the waveform vertically + */ + + }, { + key: "applyCanvasTransforms", + value: function applyCanvasTransforms(entry) { + var vertical = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + entry.applyCanvasTransforms(vertical); + } + /** + * Return image data of the multi-canvas + * + * When using a `type` of `'blob'`, this will return a `Promise`. + * + * @param {string} format='image/png' An optional value of a format type. + * @param {number} quality=0.92 An optional value between 0 and 1. + * @param {string} type='dataURL' Either 'dataURL' or 'blob'. + * @return {string|string[]|Promise} When using the default `'dataURL'` + * `type` this returns a single data URL or an array of data URLs, + * one for each canvas. When using the `'blob'` `type` this returns a + * `Promise` that resolves with an array of `Blob` instances, one for each + * canvas. + */ + + }, { + key: "getImage", + value: function getImage(format, quality, type) { + if (type === 'blob') { + return Promise.all(this.canvases.map(function (entry) { + return entry.getImage(format, quality, type); + })); + } else if (type === 'dataURL') { + var images = this.canvases.map(function (entry) { + return entry.getImage(format, quality, type); + }); + return images.length > 1 ? images : images[0]; + } + } + /** + * Render the new progress + * + * @param {number} position X-offset of progress position in pixels + */ + + }, { + key: "updateProgress", + value: function updateProgress(position) { + this.style(this.progressWave, { + width: position + 'px' + }); + } + }]); + + return MultiCanvas; +}(_drawer.default); + +exports.default = MultiCanvas; +module.exports = exports.default; + +/***/ }), + +/***/ "./src/mediaelement-webaudio.js": +/*!**************************************!*\ + !*** ./src/mediaelement-webaudio.js ***! + \**************************************/ +/***/ ((module, exports, __webpack_require__) => { + +"use strict"; + + +function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports.default = void 0; + +var _mediaelement = _interopRequireDefault(__webpack_require__(/*! ./mediaelement */ "./src/mediaelement.js")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } + +function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + +function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } + +/** + * MediaElementWebAudio backend: load audio via an HTML5 audio tag, but playback with the WebAudio API. + * The advantage here is that the html5