wavesurfer.js 7.0.0-alpha.22 → 7.0.0-alpha.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Multitrack=e():t.Multitrack=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={284:(t,e,i)=>{i.d(e,{Z:()=>r});var s=i(139);class n extends s.Z{wavesurfer;container;wrapper;subscriptions=[];options;constructor(t){super(),this.options=t}init(t){this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper}destroy(){this.subscriptions.forEach((t=>t()))}}const r=n},139:(t,e,i)=>{i.d(e,{Z:()=>s});const s=class{eventTarget;constructor(){this.eventTarget=new EventTarget}emit(t,e){const i=new CustomEvent(String(t),{detail:e});this.eventTarget.dispatchEvent(i)}on(t,e,i){const s=t=>{t instanceof CustomEvent&&e(t.detail)},n=String(t);return this.eventTarget.addEventListener(n,s,{once:i}),()=>this.eventTarget.removeEventListener(n,s)}once(t,e){return this.on(t,e,!0)}}},919:(t,e,i)=>{i.d(e,{default:()=>o});var s=i(284);const n={startTime:0,endTime:0,fadeInEnd:0,fadeOutStart:0,lineWidth:4,lineColor:"rgba(0, 0, 255, 0.5)",dragPointSize:10,dragPointFill:"rgba(255, 255, 255, 0.8)",dragPointStroke:"rgba(255, 255, 255, 0.8)"};class r extends s.Z{options;svg=null;audioContext=null;gainNode=null;volume=1;isFadingIn=!1;isFadingOut=!1;constructor(t){super(t),this.options=Object.assign({},n,t),this.options.lineColor=this.options.lineColor||n.lineColor,this.options.dragPointFill=this.options.dragPointFill||n.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||n.dragPointStroke,this.volume=this.options.volume??1}static create(t){return new r(t)}init(t){if(super.init(t),!this.wavesurfer)throw Error("WaveSurfer is not initialized");let e;this.subscriptions.push(this.wavesurfer.once("decode",(({duration:t})=>{this.options.startTime=this.options.startTime||0,this.options.endTime=this.options.endTime||t,this.options.fadeInEnd=this.options.fadeInEnd||this.options.startTime,this.options.fadeOutStart=this.options.fadeOutStart||this.options.endTime,this.initWebAudio(),this.initSvg(),this.initFadeEffects()}))),this.subscriptions.push(this.wavesurfer.on("zoom",(()=>{e&&clearTimeout(e),e=setTimeout((()=>{this.svg?.remove(),this.initSvg()}),100)})))}makeDraggable(t,e){t.addEventListener("mousedown",(t=>{let i=t.clientX,s=t.clientY;const n=this.wavesurfer?.options.interact||!0;let r;this.wavesurfer?.toggleInteractive(!1);const o=t=>{const n=t.clientX-i,r=t.clientY-s;i=t.clientX,s=t.clientY,e(n,r)},a=()=>{document.removeEventListener("mousemove",o),document.removeEventListener("mouseup",a),r&&clearTimeout(r),r=setTimeout((()=>{this.wavesurfer?.toggleInteractive(n)}),100)};document.addEventListener("mousemove",o),document.addEventListener("mouseup",a),t.preventDefault(),t.stopPropagation()}))}renderPolyline(){if(!this.svg||!this.wrapper||!this.wavesurfer)return;const t=this.svg.querySelector("polyline"),e=t.points,i=e.getItem(1).y,s=this.wrapper.clientWidth,n=this.wavesurfer.getDuration();e.getItem(0).x=this.options.startTime/n*s,e.getItem(3).x=this.options.endTime/n*s;const r=this.svg.querySelector("line");r.setAttribute("x1",e.getItem(1).x.toString()),r.setAttribute("x2",e.getItem(2).x.toString()),r.setAttribute("y1",i.toString()),r.setAttribute("y2",i.toString());const o=this.svg.querySelectorAll("circle");for(let e=0;e<o.length;e++){const s=o[e],n=t.points.getItem(e+1);s.setAttribute("cx",n.x.toString()),s.setAttribute("cy",i.toString())}}initSvg(){if(!this.wrapper||!this.wavesurfer)return;const t=this.wrapper.clientWidth,e=this.wrapper.clientHeight,i=this.wavesurfer.getDuration(),s=document.createElementNS("http://www.w3.org/2000/svg","svg");s.setAttribute("width","100%"),s.setAttribute("height","100%"),s.setAttribute("viewBox",`0 0 ${t} ${e}`),s.setAttribute("preserveAspectRatio","none"),s.setAttribute("style","position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;"),this.svg=s;const n=document.createElementNS("http://www.w3.org/2000/svg","polyline");n.setAttribute("points","0,0 0,0 0,0 0,0"),n.setAttribute("stroke",this.options.lineColor),n.setAttribute("stroke-width",this.options.lineWidth),n.setAttribute("fill","none"),n.setAttribute("style","pointer-events: none"),s.appendChild(n);const r=document.createElementNS("http://www.w3.org/2000/svg","line");r.setAttribute("stroke","none"),r.setAttribute("stroke-width",(3*this.options.lineWidth).toString()),r.setAttribute("style","cursor: ns-resize; pointer-events: all;"),s.appendChild(r);const o=n.points,a=this.options.dragPointSize/2,d=e-this.volume*e+a;o.getItem(0).x=this.options.startTime/i*t,o.getItem(0).y=e,o.getItem(1).x=this.options.fadeInEnd/i*t,o.getItem(1).y=d,o.getItem(2).x=this.options.fadeOutStart/i*t,o.getItem(2).y=d,o.getItem(3).x=this.options.endTime/i*t,o.getItem(3).y=e,[1,2].forEach((()=>{const t=document.createElementNS("http://www.w3.org/2000/svg","circle");t.setAttribute("r",(this.options.dragPointSize/2).toString()),t.setAttribute("fill",this.options.dragPointFill),t.setAttribute("stroke",this.options.dragPointStroke||this.options.dragPointFill),t.setAttribute("stroke-width","2"),t.setAttribute("style","cursor: ew-resize; pointer-events: all;"),s.appendChild(t)})),this.wrapper.appendChild(s),this.renderPolyline();const h=t=>{const i=o.getItem(1).y+t-a;if(i<-.5||i>e)return;o.getItem(1).y=i+a,o.getItem(2).y=i+a,this.renderPolyline();const s=Math.min(1,Math.max(0,(e-i)/e));this.onVolumeChange(s),this.renderPolyline()},l=(e,s,r)=>{const o=n.points.getItem(r),a=o.x+e,d=a/t*i;1===r&&d>this.options.fadeOutStart||d<this.options.startTime||2===r&&d<this.options.fadeInEnd||d>this.options.endTime||(o.x=a,1===r?(this.options.fadeInEnd=d,this.emit("fade-in-change",{time:d})):2===r&&(this.options.fadeOutStart=d,this.emit("fade-out-change",{time:d})),s>1||s<-1?h(s):this.renderPolyline())};this.makeDraggable(r,((t,e)=>h(e)));const u=s.querySelectorAll("circle");for(let t=0;t<u.length;t++){const e=t+1;this.makeDraggable(u[t],((t,i)=>l(t,i,e)))}}destroy(){this.svg?.remove(),super.destroy()}initWebAudio(){const t=this.wavesurfer?.getMediaElement();if(!t)return null;this.volume=this.options.volume??t.volume;const e=new window.AudioContext;this.gainNode=e.createGain(),this.gainNode.gain.value=this.volume,e.createMediaElementSource(t).connect(this.gainNode),this.gainNode.connect(e.destination),this.audioContext=e}naturalVolume(t){return 1e-4+.9999*Math.pow(t,3)}onVolumeChange(t){t=this.naturalVolume(t),this.volume=t,this.emit("volume-change",{volume:t}),this.gainNode&&(this.gainNode.gain.value=t)}initFadeEffects(){if(!this.audioContext||!this.wavesurfer)return;const t=this.wavesurfer.on("timeupdate",(({currentTime:t})=>{if(!this.audioContext||!this.gainNode)return;if(!this.wavesurfer?.isPlaying())return;if("suspended"===this.audioContext.state&&this.audioContext.resume(),!this.isFadingIn&&t>=this.options.startTime&&t<=this.options.fadeInEnd)return this.isFadingIn=!0,this.gainNode.gain.setValueAtTime(0,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(this.volume,this.audioContext.currentTime+(this.options.fadeInEnd-t));if(!this.isFadingOut&&t>=this.options.fadeOutStart&&t<=this.options.endTime)return this.isFadingOut=!0,void this.gainNode.gain.linearRampToValueAtTime(0,this.audioContext.currentTime+(this.options.endTime-t));let e=!1;this.isFadingIn&&(t<this.options.startTime||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,e=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.endTime)&&(this.isFadingOut=!1,e=!0),e&&(this.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime),this.gainNode.gain.value=this.volume)}));this.subscriptions.push(t)}getCurrentVolume(){return this.gainNode?this.gainNode.gain.value:this.volume}setStartTime(t){this.options.startTime=t,this.renderPolyline()}setEndTime(t){this.options.endTime=t,this.renderPolyline()}}const o=r},76:(t,e,i)=>{i.d(e,{default:()=>d});var s=i(284);const n={dragSelection:!0,draggable:!0,resizable:!0},r=(t,e)=>{for(const i in e)t.style[i]=e[i]||""},o=(t,e)=>{const i=document.createElement(t);return r(i,e),i};class a extends s.Z{dragStart=NaN;regionsContainer;regions=[];createdRegion=null;modifiedRegion=null;isResizingLeft=!1;isMoving=!1;wasInteractive=!0;constructor(t){super(t||{}),this.options=Object.assign({},n,t),this.regionsContainer=this.initRegionsContainer()}static create(t){return new a(t)}init(t){if(super.init(t),!this.wavesurfer||!this.wrapper)throw Error("WaveSurfer is not initialized");this.subscriptions.push(this.wavesurfer.once("decode",(()=>{this.wrapper?.appendChild(this.regionsContainer)}))),this.wrapper.addEventListener("mousedown",this.handleMouseDown),document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp)}destroy(){this.wrapper?.removeEventListener("mousedown",this.handleMouseDown),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp,!0),this.regionsContainer.remove(),super.destroy()}initRegionsContainer(){return o("div",{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",zIndex:"3",pointerEvents:"none"})}handleMouseDown=t=>{if(this.options.draggable||this.options.resizable||this.options.dragSelection){if(!this.wrapper)return;this.dragStart=t.clientX-this.wrapper.getBoundingClientRect().left}};handleMouseMove=t=>{if(!this.wrapper)return;const e=this.wrapper.getBoundingClientRect(),{width:i}=e,s=t.clientX-e.left;if(this.options.draggable&&this.modifiedRegion&&this.isMoving)return this.moveRegion(this.modifiedRegion,(s-this.dragStart)/i),void(this.dragStart=s);if(this.options.resizable&&this.modifiedRegion)this.updateRegion(this.modifiedRegion,this.isResizingLeft?s/i:void 0,this.isResizingLeft?void 0:s/i);else if(this.options.dragSelection&&!isNaN(this.dragStart)){const e=t.clientX-this.regionsContainer.getBoundingClientRect().left;e-this.dragStart>=10&&(this.createdRegion?this.updateRegion(this.createdRegion,this.dragStart/i,e/i):(this.wasInteractive=this.wavesurfer?.options.interact||!0,this.wavesurfer?.toggleInteractive(!1),this.createdRegion=this.createRegion(this.dragStart/i,e/i)))}};handleMouseUp=()=>{this.createdRegion&&(this.addRegion(this.createdRegion),this.createdRegion=null),this.modifiedRegion=null,this.isMoving=!1,this.dragStart=NaN,this.wavesurfer?.toggleInteractive(this.wasInteractive)};createRegionElement(t,e,i=""){const s=t===e,n=o("div",{position:"absolute",left:100*t+"%",width:100*(e-t)+"%",height:"100%",backgroundColor:s?"":"rgba(0, 0, 0, 0.1)",borderRadius:"2px",boxSizing:"border-box",borderLeft:s?"2px solid rgba(0, 0, 0, 0.5)":"",transition:"background-color 0.2s ease",cursor:this.options.draggable?"move":"",pointerEvents:"all",whiteSpace:s?"nowrap":"",padding:"0.2em"});n.textContent=i;const a=o("div",{position:"absolute",left:"0",top:"0",width:"6px",height:"100%",cursor:this.options.resizable?"ew-resize":"",pointerEvents:"all",borderLeft:s?"":"2px solid rgba(0, 0, 0, 0.5)",borderRadius:"2px 0 0 2px"});n.appendChild(a);const d=a.cloneNode();r(d,{left:"",right:"0",borderLeft:"",borderRight:s?"":"2px solid rgba(0, 0, 0, 0.5)",borderRadius:"0 2px 2px 0"}),n.appendChild(d),a.addEventListener("mousedown",(t=>{this.options.resizable&&(t.stopPropagation(),this.modifiedRegion=this.regions.find((t=>t.element===n))||null,this.isResizingLeft=!0,this.isMoving=!1)})),d.addEventListener("mousedown",(t=>{this.options.resizable&&(t.stopPropagation(),this.modifiedRegion=this.regions.find((t=>t.element===n))||null,this.isResizingLeft=!1,this.isMoving=!1)})),n.addEventListener("mousedown",(()=>{this.options.draggable&&(this.modifiedRegion=this.regions.find((t=>t.element===n))||null,this.isMoving=!0)})),n.addEventListener("click",(()=>{const t=this.regions.find((t=>t.element===n));t&&this.emit("region-clicked",{region:t})})),this.regionsContainer.appendChild(n);const h=n.getBoundingClientRect().left,l=n.scrollWidth,u=this.regions.filter((t=>{const{left:e}=t.element.getBoundingClientRect(),i=t.element.scrollWidth;return h<e+i&&e<h+l})).map((t=>parseFloat(t.element.style.paddingTop))).reduce(((t,e)=>t+e),0);return u>0&&(n.style.paddingTop=`${u+1}em`),n}createRegion(t,e,i=""){const s=this.wavesurfer?.getDuration()||0;return{element:this.createRegionElement(t,e,i),start:t,end:e,startTime:t*s,endTime:e*s,title:i}}addRegion(t){this.regions.push(t),this.emit("region-created",{region:t})}updateRegion(t,e,i){const s=this.wavesurfer?.getDuration()||0;null!=e&&(t.start=e,t.element.style.left=100*t.start+"%",t.element.style.width=100*(t.end-t.start)+"%",t.startTime=e*s),null!=i&&(t.end=i,t.element.style.width=100*(t.end-t.start)+"%",t.endTime=i*s),this.emit("region-updated",{region:t})}moveRegion(t,e){this.updateRegion(t,t.start+e,t.end+e)}add(t,e,i="",s=""){const n=this.wavesurfer?.getDuration()||0,r=t/n,o=e/n,a=this.createRegion(r,o,i);return this.addRegion(a),s&&this.setRegionColor(a,s),a}setRegionColor(t,e){t.element.style[t.startTime===t.endTime?"borderColor":"backgroundColor"]=e}}const d=a},954:(t,e,i)=>{i.d(e,{default:()=>o});var s=i(284);const n={height:20};class r extends s.Z{timelineWrapper;options;constructor(t){super(t),this.options=Object.assign({},n,t),this.timelineWrapper=this.initTimelineWrapper()}static create(t){return new r(t)}init(t){if(super.init(t),!this.wavesurfer||!this.wrapper)throw Error("WaveSurfer is not initialized");(this.options.container??this.wrapper).appendChild(this.timelineWrapper),this.options.duration?this.initTimeline(this.options.duration):this.subscriptions.push(this.wavesurfer.on("decode",(({duration:t})=>{this.initTimeline(t)})))}destroy(){this.timelineWrapper.remove(),super.destroy()}initTimelineWrapper(){return document.createElement("div")}formatTime(t){return t/60>1?`${Math.round(t/60)}:${(t=Math.round(t%60))<10?"0":""}${t}`:""+Math.round(1e3*t)/1e3}defaultTimeInterval(t){return t>=25?1:5*t>=25?5:15*t>=25?15:60*Math.ceil(.5/t)}defaultPrimaryLabelInterval(t){return t>=25?10:5*t>=25?6:4}defaultSecondaryLabelInterval(t){return t>=25?5:2}initTimeline(t){const e=Math.round(this.timelineWrapper.scrollWidth*devicePixelRatio)/t,i=this.options.timeInterval??this.defaultTimeInterval(e),s=this.options.primaryLabelInterval??this.defaultPrimaryLabelInterval(e),n=this.options.secondaryLabelInterval??this.defaultSecondaryLabelInterval(e),r=document.createElement("div");r.setAttribute("style",`\n height: ${this.options.height}px;\n overflow: hidden;\n display: flex;\n justify-content: space-between;\n align-items: flex-end;\n font-size: ${this.options.height/2}px;\n white-space: nowrap;\n `);const o=document.createElement("div");o.setAttribute("style","\n width: 1px;\n height: 50%;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n overflow: visible;\n border-left: 1px solid currentColor;\n opacity: 0.25;\n ");for(let e=0;e<t;e+=i){const t=o.cloneNode(),i=e%s==0;(i||e%n==0)&&(t.style.height="100%",t.style.textIndent="3px",t.textContent=this.formatTime(e),i&&(t.style.opacity="1")),r.appendChild(t)}this.timelineWrapper.appendChild(r),this.emit("ready")}}const o=r}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var r=e[s]={exports:{}};return t[s](r,r.exports,i),r.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var s={};return(()=>{i.d(s,{default:()=>f});var t=i(139);class e extends t.Z{options;container;scrollContainer;wrapper;canvasWrapper;progressWrapper;timeout=null;isScrolling=!1;audioData=null;resizeObserver=null;constructor(t){super(),this.options=t;let e=null;if("string"==typeof this.options.container?e=document.querySelector(this.options.container):this.options.container instanceof HTMLElement&&(e=this.options.container),!e)throw new Error("Container not found");const i=document.createElement("div"),s=i.attachShadow({mode:"open"});s.innerHTML=`\n <style>\n :host {\n user-select: none;\n }\n :host .scroll {\n overflow-x: auto;\n overflow-y: hidden;\n width: 100%;\n position: relative;\n ${this.options.hideScrollbar?"scrollbar-color: transparent;":""}\n }\n :host ::-webkit-scrollbar {\n display: ${this.options.hideScrollbar?"none":"auto"};\n }\n :host .wrapper {\n position: relative;\n min-width: 100%;\n z-index: 2;\n }\n :host .canvases {\n position: relative;\n height: ${this.options.height}px;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n height: ${this.options.height}px;\n image-rendering: pixelated;\n }\n :host .progress {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n width: 0;\n height: ${this.options.height}px;\n overflow: hidden;\n box-sizing: border-box;\n border-right-style: solid;\n border-right-width: ${this.options.cursorWidth}px;\n border-right-color: ${this.options.cursorColor||this.options.progressColor};\n }\n </style>\n\n <div class="scroll">\n <div class="wrapper">\n <div class="canvases"></div>\n <div class="progress"></div>\n </div>\n </div>\n `,this.container=i,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),e.appendChild(i),this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",{relativeX:i})})),this.resizeObserver=new ResizeObserver((()=>{this.delay((()=>this.audioData&&this.zoom(this.audioData,this.options.minPxPerSec)),100)})),this.resizeObserver.observe(this.scrollContainer)}getContainer(){return this.scrollContainer}getWrapper(){return this.wrapper}destroy(){this.container.remove(),this.resizeObserver?.disconnect()}delay(t,e=10){return this.timeout&&clearTimeout(this.timeout),new Promise((i=>{this.timeout=setTimeout((()=>{i(t())}),e)}))}async renderPeaks(t,e,i,s){const n=null!=this.options.barWidth?this.options.barWidth*s:1,r=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?n/2:0,o=this.options.barRadius??0,a=t[0],d=a.length,h=Math.floor(e/(n+r))/d,l=i/2,u=1===t.length,c=u?a:t[1],p=u&&c.some((t=>t<0)),m=(t,i)=>{let u=0,m=0,g=0;const v=document.createElement("canvas");v.width=Math.round(e*(i-t)/d),v.height=this.options.height,v.style.width=`${Math.floor(v.width/s)}px`,v.style.left=`${Math.floor(t*e/s/d)}px`,this.canvasWrapper.appendChild(v);const f=v.getContext("2d",{desynchronized:!0});f.beginPath(),f.fillStyle=this.options.waveColor,f.roundRect||(f.roundRect=f.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*h);if(i>u){const t=Math.round(m*l),e=Math.round(g*l);f.roundRect(u*(n+r),l-t,n,t+e||1,o),u=i,m=0,g=0}const s=p?a[e]:Math.abs(a[e]),d=p?c[e]:Math.abs(c[e]);s>m&&(m=s),(p?d<-g:d>g)&&(g=d<0?-d:d)}f.fill(),f.closePath();const y=v.cloneNode();this.progressWrapper.appendChild(y);const b=y.getContext("2d",{desynchronized:!0});b.drawImage(v,0,0),b.globalCompositeOperation="source-in",b.fillStyle=this.options.progressColor,b.fillRect(0,0,v.width,v.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:g,scrollWidth:v,clientWidth:f}=this.scrollContainer,y=d/v,b=Math.floor(g*y),w=Math.ceil(Math.min(v,g+f)*y);m(b,w);const C=w-b;for(let t=w;t<d;t+=C)await this.delay((()=>{m(t,Math.min(d,t+C))}));for(let t=b-1;t>=0;t-=C)await this.delay((()=>{m(Math.max(0,t-C),t)}))}render(t){const e=window.devicePixelRatio||1,i=this.options.fillParent?this.scrollContainer.clientWidth*e:0,s=Math.max(1,t.duration*this.options.minPxPerSec),n=s>i?s:i,{height:r}=this.options;this.isScrolling=n>i,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.wrapper.style.width=`${Math.floor(n/e)}px`;const o=[t.getChannelData(0)];t.numberOfChannels>1&&o.push(t.getChannelData(1)),this.renderPeaks(o,n,r,e),this.audioData=t}zoom(t,e){const i=this.progressWrapper.clientWidth;this.options.minPxPerSec=e,this.render(t);const s=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=s-i}renderProgress(t,e=!1){if(this.progressWrapper.style.width=100*t+"%",this.isScrolling){const t=this.scrollContainer.clientWidth,i=t/2,s=this.progressWrapper.clientWidth,n=e?i:t;s>this.scrollContainer.scrollLeft+n&&(this.scrollContainer.scrollLeft=s-i)}}}const n=e;class r extends t.Z{media;subscriptions=[];isExternalMedia=!1;hasPlayedOnce=!1;constructor(t){super(),t.media?(this.media=t.media,this.isExternalMedia=!0):this.media=document.createElement("audio"),this.subscriptions.push(this.onceMediaEvent("play",(()=>{this.hasPlayedOnce=!0}))),t.autoplay&&(this.media.autoplay=!0),null!=t.playbackRate&&(this.media.playbackRate=t.playbackRate)}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}onceMediaEvent(t,e){return this.onMediaEvent(t,e,{once:!0})}loadUrl(t){this.media.src=t}destroy(){this.media.pause(),this.subscriptions.forEach((t=>t())),this.isExternalMedia||this.media.remove()}play(){this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}seekTo(t){!this.hasPlayedOnce&&navigator.userAgent.match(/(iPad|iPhone|iPod)/g)&&this.media.play()?.then?.((()=>{setTimeout((()=>this.media.pause()),10)})),this.media.currentTime=t}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}setPlaybackRate(t,e){null!=e&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}}const o=r;class a extends t.Z{unsubscribe=()=>{};start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}}const d=a,h={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoCenter:!0};class l extends o{options;fetcher;decoder;renderer;timer;plugins=[];decodedData=null;canPlay=!1;static create(t){return new l(t)}constructor(t){super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.options=Object.assign({},h,t),this.fetcher=new class{async load(t){return fetch(t).then((t=>t.arrayBuffer()))}},this.decoder=new class{audioCtx=null;initAudioContext(t){this.audioCtx=new AudioContext({latencyHint:"playback",sampleRate:t})}constructor(){try{this.initAudioContext(3e3)}catch(t){this.initAudioContext(8e3)}}async decode(t){if(!this.audioCtx)throw new Error("AudioContext is not initialized");return this.audioCtx.decodeAudioData(t)}destroy(){this.audioCtx?.close(),this.audioCtx=null}},this.timer=new d,this.renderer=new n({container:this.options.container,height:this.options.height,waveColor:this.options.waveColor,progressColor:this.options.progressColor,cursorColor:this.options.cursorColor,cursorWidth:this.options.cursorWidth,minPxPerSec:this.options.minPxPerSec,fillParent:this.options.fillParent,barWidth:this.options.barWidth,barGap:this.options.barGap,barRadius:this.options.barRadius,hideScrollbar:this.options.hideScrollbar}),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent(),this.initPlugins();const e=this.options.url||this.options.media?.src;e&&this.load(e,this.options.peaks,this.options.duration)}initPlayerEvents(){this.subscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.options.autoCenter&&this.isPlaying()),this.emit("timeupdate",{currentTime:t})})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.onMediaEvent("canplay",(()=>{this.canPlay=!0,this.emit("canplay",{duration:this.getDuration()})})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",{currentTime:this.getCurrentTime()})})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(({relativeX:t})=>{if(this.options.interact){const e=this.getDuration()*t;this.seekTo(e),this.emit("seekClick",{currentTime:this.getCurrentTime()})}})))}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",{currentTime:t})})))}initReadyEvent(){const t=()=>{this.decodedData&&this.canPlay&&this.emit("ready",{duration:this.getDuration()})};this.subscriptions.push(this.on("decode",t),this.on("canplay",t))}initPlugins(){this.options.plugins?.length&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}registerPlugin(t){return t.init({wavesurfer:this,container:this.renderer.getContainer(),wrapper:this.renderer.getWrapper()}),this.plugins.push(t),t.once("destroy",(()=>{this.plugins=this.plugins.filter((e=>e!==t))})),t}async load(t,e,i){if(this.decodedData=null,this.canPlay=!1,this.loadUrl(t),null==e){const e=await this.fetcher.load(t),i=await this.decoder.decode(e);this.decodedData=i}else i||(i=await new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getDuration())))}))||0),this.decodedData={duration:i,numberOfChannels:e.length,sampleRate:e[0].length/i,getChannelData:t=>e[t]};this.renderer.render(this.decodedData),this.emit("decode",{duration:this.decodedData.duration})}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(this.decodedData,t),this.emit("zoom",{minPxPerSec:t})}getDecodedData(){return this.decodedData}getDuration(){return this.decodedData?.duration||this.getMediaElement()?.duration||0}toggleInteractive(t){this.options.interact=t}destroy(){this.emit("destroy"),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.decoder.destroy(),this.renderer.destroy(),super.destroy()}}const u=l;var c=i(76),p=i(954),m=i(919);class g extends t.Z{tracks;options;audios=[];wavesurfers=[];durations=[];currentTime=0;maxDuration=0;rendering;isDragging=!1;frameRequest=null;timer=null;subscriptions=[];timeline=null;static create(t,e){return new g(t,e)}constructor(t,e){super(),this.tracks=t.map((t=>({...t,startPosition:t.startPosition||0,peaks:t.peaks||(t.url?void 0:[new Float32Array])}))),this.options=e,this.rendering=function(t,e){let i=0,s=[],n=0;const r=document.createElement("div");r.setAttribute("style","width: 100%; overflow-x: scroll; overflow-y: hidden; user-select: none;");const o=document.createElement("div");o.style.position="relative",r.appendChild(o),e.container.appendChild(r);const a=document.createElement("div");a.setAttribute("style","height: 100%; position: absolute; z-index: 10; top: 0; left: 0"),a.style.backgroundColor=e.cursorColor||"#000",a.style.width=`${e.cursorWidth??1}px`,o.appendChild(a);const{clientWidth:d}=o,h=t.map(((t,i)=>{const s=document.createElement("div");if(s.style.position="relative",e.trackBorderColor&&i>0){const t=document.createElement("div");t.setAttribute("style",`width: 100%; height: 2px; background-color: ${e.trackBorderColor}`),o.appendChild(t)}if(e.trackBackground&&t.url&&(s.style.background=e.trackBackground),!t.url){const t=document.createElement("div");t.setAttribute("style",`position: absolute; z-index: 10; left: 10px; top: 10px; right: 10px; bottom: 10px; border: 2px dashed ${e.trackBorderColor};`),t.addEventListener("dragover",(i=>{i.preventDefault(),t.style.background=e.trackBackground||""})),t.addEventListener("dragleave",(e=>{e.preventDefault(),t.style.background=""})),t.addEventListener("drop",(e=>{e.preventDefault(),t.style.background=""})),s.appendChild(t)}return o.appendChild(s),s})),l=()=>{h.forEach(((e,n)=>{const r=t[n].startPosition*i;s[n]&&(e.style.width=s[n]*i+"px"),e.style.transform=`translateX(${r}px)`}))};return{containers:h,setContainerOffsets:l,setMainWidth:(t,r)=>{s=t,i=Math.max(e.minPxPerSec||0,d/r),n=i*r,o.style.width=`${n}px`,l()},updateCursor:(t,e)=>{a.style.left=`${Math.min(100,100*t)}%`;const{clientWidth:i,scrollLeft:s}=r,o=i/2,d=t*n;(d>s+(e?o:i)||d<s)&&(r.scrollLeft=d-o)},addClickHandler:t=>{o.addEventListener("click",(e=>{const i=o.getBoundingClientRect(),s=(e.clientX-i.left)/o.offsetWidth;t(s)}))},destroy:()=>{r.remove()},addDropHandler:e=>{t.forEach(((t,i)=>{if(!t.url){const s=h[i].querySelector("div");s?.addEventListener("drop",(i=>{i.preventDefault(),e(t.id)}))}}))}}}(this.tracks,this.options),this.rendering.addDropHandler((t=>{this.emit("drop",{id:t})})),this.initAllAudios().then((t=>{this.initDurations(t),this.initAllWavesurfers(),this.rendering.containers.forEach(((t,i)=>{const s=v(t,(t=>this.onDrag(i,t)),e.rightButtonDrag);this.wavesurfers[i].once("destroy",(()=>s?.destroy()))})),this.rendering.addClickHandler((t=>{this.isDragging||this.seekTo(t*this.maxDuration)})),this.emit("canplay")}))}initDurations(t){this.durations=t,this.maxDuration=this.tracks.reduce(((e,i,s)=>Math.max(e,i.startPosition+t[s])),0),this.rendering.setMainWidth(t,this.maxDuration)}initAudio(t){const e=new Audio(t.url);return new Promise((t=>{if(!e.src)return t(e);e.addEventListener("loadedmetadata",(()=>t(e)),{once:!0})}))}async initAllAudios(){return this.audios=await Promise.all(this.tracks.map((t=>this.initAudio(t)))),this.audios.map((t=>t.src?t.duration:0))}initWavesurfer(t,e){const i=this.rendering.containers[e],s=u.create({...t.options,container:i,minPxPerSec:0,media:this.audios[e],peaks:t.peaks,cursorColor:"transparent",cursorWidth:0,interact:!1}),n=s.registerPlugin(c.default.create({draggable:!1,resizable:!0,dragSelection:!1}));this.subscriptions.push(s.once("decode",(()=>{if(null!=t.startCue||null!=t.endCue){const{startCue:i=0,endCue:s=this.durations[e]}=t,r=n.add(0,i,"","rgba(0, 0, 0, 0.7)"),o=n.add(s,s+this.durations[e],"","rgba(0, 0, 0, 0.7)");r.element.firstElementChild?.remove(),o.element.lastChild?.remove(),this.subscriptions.push(n.on("region-updated",(({region:e})=>{this.setIsDragging(),e!==r&&e!==o||(e===r?(t.startCue=e.endTime,this.emit("start-cue-change",{id:t.id,startCue:t.startCue})):(t.endCue=e.startTime,this.emit("end-cue-change",{id:t.id,endCue:t.endCue})))})))}if(t.intro){const e=n.add(0,t.intro.endTime,t.intro.label,this.options.trackBackground);e.element.firstElementChild?.remove(),e.element.style.backgroundColor=this.options.trackBackground||"transparent",e.element.parentElement.style.mixBlendMode="plus-lighter",t.intro.color&&(e.element.lastElementChild.style.borderColor=t.intro.color),this.subscriptions.push(n.on("region-updated",(({region:i})=>{i===e&&this.emit("intro-end-change",{id:t.id,endTime:i.endTime})})))}t.markers&&t.markers.forEach((t=>{n.add(t.time,t.time,t.label,t.color)}))})));const r=s.registerPlugin(m.default.create({...this.options.envelopeOptions,startTime:t.startCue,endTime:t.endCue,fadeInEnd:t.fadeInEnd,fadeOutStart:t.fadeOutStart,volume:t.volume}));return this.subscriptions.push(r.on("volume-change",(({volume:e})=>{this.setIsDragging(),this.emit("volume-change",{id:t.id,volume:e})})),r.on("fade-in-change",(({time:e})=>{this.setIsDragging(),this.emit("fade-in-change",{id:t.id,fadeInEnd:e})})),r.on("fade-out-change",(({time:e})=>{this.setIsDragging(),this.emit("fade-out-change",{id:t.id,fadeOutStart:e})})),this.on("start-cue-change",(({id:e,startCue:i})=>{e===t.id&&r.setStartTime(i)})),this.on("end-cue-change",(({id:e,endCue:i})=>{e===t.id&&r.setEndTime(i)}))),s}initAllWavesurfers(){const t=this.tracks.map(((t,e)=>this.initWavesurfer(t,e)));this.wavesurfers=t,this.initTimeline()}initTimeline(){this.timeline&&this.timeline.destroy(),this.timeline=this.wavesurfers[0].registerPlugin(p.default.create({duration:this.maxDuration,container:this.rendering.containers[0].parentElement}))}updatePosition(t,e=!1){const i=!this.isPlaying();t!==this.currentTime&&(this.currentTime=t,this.rendering.updateCursor(t/this.maxDuration,e)),this.tracks.forEach(((e,s)=>{const n=this.audios[s],r=this.durations[s],o=t-e.startPosition;Math.abs(n.currentTime-o)>.3&&(n.currentTime=o),i||o<0||o>r?!n.paused&&n.pause():i||n.paused&&n.play();const a=o>=(e.startCue||0)&&o<(e.endCue||1/0)?1:0;a!==n.volume&&(n.volume=a)}))}setIsDragging(){this.isDragging=!0,this.timer&&clearTimeout(this.timer),this.timer=setTimeout((()=>this.isDragging=!1),300)}onDrag(t,e){this.setIsDragging();const i=this.tracks[t];if(!i.draggable)return;const s=i.startPosition+e*this.maxDuration,n=this.tracks.findIndex((t=>t.url&&!t.draggable)),r=this.tracks[n],o=(r?r.startPosition:0)-this.durations[t],a=r?r.startPosition+this.durations[n]:this.maxDuration;s>=o&&s<=a&&(i.startPosition=s,this.initDurations(this.durations),this.rendering.setContainerOffsets(),this.updatePosition(this.currentTime),this.emit("start-position-change",{id:i.id,startPosition:s}))}findCurrentTracks(){const t=[];if(this.tracks.forEach(((e,i)=>{e.url&&this.currentTime>=e.startPosition&&this.currentTime<e.startPosition+this.durations[i]&&t.push(i)})),0===t.length){const e=Math.min(...this.tracks.filter((t=>t.url)).map((t=>t.startPosition)));t.push(this.tracks.findIndex((t=>t.startPosition===e)))}return t}startSync(){const t=()=>{const e=this.audios.reduce(((t,e,i)=>(e.paused||(t=Math.max(t,e.currentTime+this.tracks[i].startPosition)),t)),this.currentTime);e>this.currentTime&&this.updatePosition(e,!0),this.frameRequest=requestAnimationFrame(t)};t()}play(){this.startSync(),this.findCurrentTracks().forEach((t=>{this.audios[t]?.play()}))}pause(){this.audios.forEach((t=>t.pause()))}isPlaying(){return this.audios.some((t=>!t.paused))}getCurrentTime(){return this.currentTime}seekTo(t){const e=this.isPlaying();this.updatePosition(t),e&&this.play()}zoom(t){this.options.minPxPerSec=t,this.wavesurfers.forEach(((e,i)=>this.tracks[i].url&&e.zoom(t))),this.rendering.setMainWidth(this.durations,this.maxDuration),this.rendering.setContainerOffsets()}addTrack(t){const e=this.tracks.findIndex((e=>e.id===t.id));-1!==e&&(this.tracks[e]=t,this.initAudio(t).then((i=>{this.audios[e]=i,this.durations[e]=i.duration,this.initDurations(this.durations);const s=this.rendering.containers[e];s.innerHTML="",this.wavesurfers[e].destroy(),this.wavesurfers[e]=this.initWavesurfer(t,e);const n=v(s,(t=>this.onDrag(e,t)),this.options.rightButtonDrag);this.wavesurfers[e].once("destroy",(()=>n?.destroy())),this.initTimeline(),this.emit("canplay")})))}destroy(){this.frameRequest&&cancelAnimationFrame(this.frameRequest),this.rendering.destroy(),this.audios.forEach((t=>{t.pause(),t.src=""})),this.wavesurfers.forEach((t=>{t.destroy()}))}setSinkId(t){return Promise.all(this.audios.map((e=>{const i=e;return i.setSinkId?i.setSinkId(t):Promise.resolve()})))}}function v(t,e,i=!1){const s=t.parentElement;if(!s)return;let n=null;t.addEventListener("contextmenu",(t=>{i&&t.preventDefault()})),t.addEventListener("mousedown",(e=>{if(i&&2!==e.button)return;const r=s.getBoundingClientRect();n=e.clientX-r.left,t.style.cursor="grabbing"}));const r=e=>{null!=n&&(e.stopPropagation(),n=null,t.style.cursor="")},o=t=>{if(null==n)return;const i=s.getBoundingClientRect(),r=t.clientX-i.left,o=r-n;(o>1||o<-1)&&(n=r,e(o/s.offsetWidth))};return document.body.addEventListener("mouseup",r),document.body.addEventListener("mousemove",o),{destroy:()=>{document.body.removeEventListener("mouseup",r),document.body.removeEventListener("mousemove",o)}}}const f=g})(),s.default})()));
@@ -1,5 +1,5 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
- import type { WaveSurferPluginParams } from '../index.js';
2
+ import type { WaveSurferPluginParams } from '../wavesurfer.js';
3
3
  export type RegionsPluginOptions = {
4
4
  dragSelection?: boolean;
5
5
  draggable?: boolean;
@@ -32,8 +32,11 @@ declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPlugi
32
32
  private modifiedRegion;
33
33
  private isResizingLeft;
34
34
  private isMoving;
35
+ private wasInteractive;
35
36
  /** Create an instance of RegionsPlugin */
36
- constructor(params: WaveSurferPluginParams, options: RegionsPluginOptions);
37
+ constructor(options?: RegionsPluginOptions);
38
+ static create(options?: RegionsPluginOptions): RegionsPlugin;
39
+ init(params: WaveSurferPluginParams): void;
37
40
  /** Unmount */
38
41
  destroy(): void;
39
42
  private initRegionsContainer;
@@ -23,13 +23,23 @@ class RegionsPlugin extends BasePlugin {
23
23
  modifiedRegion = null;
24
24
  isResizingLeft = false;
25
25
  isMoving = false;
26
+ wasInteractive = true;
26
27
  /** Create an instance of RegionsPlugin */
27
- constructor(params, options) {
28
- super(params, options);
28
+ constructor(options) {
29
+ super(options || {});
29
30
  this.options = Object.assign({}, defaultOptions, options);
30
31
  this.regionsContainer = this.initRegionsContainer();
32
+ }
33
+ static create(options) {
34
+ return new RegionsPlugin(options);
35
+ }
36
+ init(params) {
37
+ super.init(params);
38
+ if (!this.wavesurfer || !this.wrapper) {
39
+ throw Error('WaveSurfer is not initialized');
40
+ }
31
41
  this.subscriptions.push(this.wavesurfer.once('decode', () => {
32
- this.wrapper.appendChild(this.regionsContainer);
42
+ this.wrapper?.appendChild(this.regionsContainer);
33
43
  }));
34
44
  this.wrapper.addEventListener('mousedown', this.handleMouseDown);
35
45
  document.addEventListener('mousemove', this.handleMouseMove);
@@ -37,7 +47,7 @@ class RegionsPlugin extends BasePlugin {
37
47
  }
38
48
  /** Unmount */
39
49
  destroy() {
40
- this.wrapper.removeEventListener('mousedown', this.handleMouseDown);
50
+ this.wrapper?.removeEventListener('mousedown', this.handleMouseDown);
41
51
  document.removeEventListener('mousemove', this.handleMouseMove);
42
52
  document.removeEventListener('mouseup', this.handleMouseUp, true);
43
53
  this.regionsContainer.remove();
@@ -56,10 +66,14 @@ class RegionsPlugin extends BasePlugin {
56
66
  }
57
67
  handleMouseDown = (e) => {
58
68
  if (this.options.draggable || this.options.resizable || this.options.dragSelection) {
69
+ if (!this.wrapper)
70
+ return;
59
71
  this.dragStart = e.clientX - this.wrapper.getBoundingClientRect().left;
60
72
  }
61
73
  };
62
74
  handleMouseMove = (e) => {
75
+ if (!this.wrapper)
76
+ return;
63
77
  const box = this.wrapper.getBoundingClientRect();
64
78
  const { width } = box;
65
79
  const dragEnd = e.clientX - box.left;
@@ -76,7 +90,8 @@ class RegionsPlugin extends BasePlugin {
76
90
  const dragEnd = e.clientX - this.regionsContainer.getBoundingClientRect().left;
77
91
  if (dragEnd - this.dragStart >= MIN_WIDTH) {
78
92
  if (!this.createdRegion) {
79
- this.wrapper.style.pointerEvents = 'none';
93
+ this.wasInteractive = this.wavesurfer?.options.interact || true;
94
+ this.wavesurfer?.toggleInteractive(false);
80
95
  this.createdRegion = this.createRegion(this.dragStart / width, dragEnd / width);
81
96
  }
82
97
  else {
@@ -93,7 +108,7 @@ class RegionsPlugin extends BasePlugin {
93
108
  this.modifiedRegion = null;
94
109
  this.isMoving = false;
95
110
  this.dragStart = NaN;
96
- this.wrapper.style.pointerEvents = '';
111
+ this.wavesurfer?.toggleInteractive(this.wasInteractive);
97
112
  };
98
113
  createRegionElement(start, end, title = '') {
99
114
  const noWidth = start === end;
@@ -181,7 +196,7 @@ class RegionsPlugin extends BasePlugin {
181
196
  return div;
182
197
  }
183
198
  createRegion(start, end, title = '') {
184
- const duration = this.wavesurfer.getDuration();
199
+ const duration = this.wavesurfer?.getDuration() || 0;
185
200
  return {
186
201
  element: this.createRegionElement(start, end, title),
187
202
  start,
@@ -196,16 +211,17 @@ class RegionsPlugin extends BasePlugin {
196
211
  this.emit('region-created', { region });
197
212
  }
198
213
  updateRegion(region, start, end) {
214
+ const duration = this.wavesurfer?.getDuration() || 0;
199
215
  if (start != null) {
200
216
  region.start = start;
201
217
  region.element.style.left = `${region.start * 100}%`;
202
218
  region.element.style.width = `${(region.end - region.start) * 100}%`;
203
- region.startTime = start * this.wavesurfer.getDuration();
219
+ region.startTime = start * duration;
204
220
  }
205
221
  if (end != null) {
206
222
  region.end = end;
207
223
  region.element.style.width = `${(region.end - region.start) * 100}%`;
208
- region.endTime = end * this.wavesurfer.getDuration();
224
+ region.endTime = end * duration;
209
225
  }
210
226
  this.emit('region-updated', { region });
211
227
  }
@@ -214,7 +230,7 @@ class RegionsPlugin extends BasePlugin {
214
230
  }
215
231
  /** Create a region at a given start and end time, with an optional title */
216
232
  add(startTime, endTime, title = '', color = '') {
217
- const duration = this.wavesurfer.getDuration();
233
+ const duration = this.wavesurfer?.getDuration() || 0;
218
234
  const start = startTime / duration;
219
235
  const end = endTime / duration;
220
236
  const region = this.createRegion(start, end, title);
@@ -0,0 +1 @@
1
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Regions=t():e.Regions=t()}(WaveSurfer,(()=>(()=>{"use strict";var e={284:(e,t,i)=>{i.d(t,{Z:()=>o});var n=i(139);class s extends n.Z{wavesurfer;container;wrapper;subscriptions=[];options;constructor(e){super(),this.options=e}init(e){this.wavesurfer=e.wavesurfer,this.container=e.container,this.wrapper=e.wrapper}destroy(){this.subscriptions.forEach((e=>e()))}}const o=s},139:(e,t,i)=>{i.d(t,{Z:()=>n});const n=class{eventTarget;constructor(){this.eventTarget=new EventTarget}emit(e,t){const i=new CustomEvent(String(e),{detail:t});this.eventTarget.dispatchEvent(i)}on(e,t,i){const n=e=>{e instanceof CustomEvent&&t(e.detail)},s=String(e);return this.eventTarget.addEventListener(s,n,{once:i}),()=>this.eventTarget.removeEventListener(s,n)}once(e,t){return this.on(e,t,!0)}}}},t={};function i(n){var s=t[n];if(void 0!==s)return s.exports;var o=t[n]={exports:{}};return e[n](o,o.exports,i),o.exports}i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{i.d(n,{default:()=>a});var e=i(284);const t={dragSelection:!0,draggable:!0,resizable:!0},s=(e,t)=>{for(const i in t)e.style[i]=t[i]||""},o=(e,t)=>{const i=document.createElement(e);return s(i,t),i};class r extends e.Z{dragStart=NaN;regionsContainer;regions=[];createdRegion=null;modifiedRegion=null;isResizingLeft=!1;isMoving=!1;wasInteractive=!0;constructor(e){super(e||{}),this.options=Object.assign({},t,e),this.regionsContainer=this.initRegionsContainer()}static create(e){return new r(e)}init(e){if(super.init(e),!this.wavesurfer||!this.wrapper)throw Error("WaveSurfer is not initialized");this.subscriptions.push(this.wavesurfer.once("decode",(()=>{this.wrapper?.appendChild(this.regionsContainer)}))),this.wrapper.addEventListener("mousedown",this.handleMouseDown),document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp)}destroy(){this.wrapper?.removeEventListener("mousedown",this.handleMouseDown),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp,!0),this.regionsContainer.remove(),super.destroy()}initRegionsContainer(){return o("div",{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",zIndex:"3",pointerEvents:"none"})}handleMouseDown=e=>{if(this.options.draggable||this.options.resizable||this.options.dragSelection){if(!this.wrapper)return;this.dragStart=e.clientX-this.wrapper.getBoundingClientRect().left}};handleMouseMove=e=>{if(!this.wrapper)return;const t=this.wrapper.getBoundingClientRect(),{width:i}=t,n=e.clientX-t.left;if(this.options.draggable&&this.modifiedRegion&&this.isMoving)return this.moveRegion(this.modifiedRegion,(n-this.dragStart)/i),void(this.dragStart=n);if(this.options.resizable&&this.modifiedRegion)this.updateRegion(this.modifiedRegion,this.isResizingLeft?n/i:void 0,this.isResizingLeft?void 0:n/i);else if(this.options.dragSelection&&!isNaN(this.dragStart)){const t=e.clientX-this.regionsContainer.getBoundingClientRect().left;t-this.dragStart>=10&&(this.createdRegion?this.updateRegion(this.createdRegion,this.dragStart/i,t/i):(this.wasInteractive=this.wavesurfer?.options.interact||!0,this.wavesurfer?.toggleInteractive(!1),this.createdRegion=this.createRegion(this.dragStart/i,t/i)))}};handleMouseUp=()=>{this.createdRegion&&(this.addRegion(this.createdRegion),this.createdRegion=null),this.modifiedRegion=null,this.isMoving=!1,this.dragStart=NaN,this.wavesurfer?.toggleInteractive(this.wasInteractive)};createRegionElement(e,t,i=""){const n=e===t,r=o("div",{position:"absolute",left:100*e+"%",width:100*(t-e)+"%",height:"100%",backgroundColor:n?"":"rgba(0, 0, 0, 0.1)",borderRadius:"2px",boxSizing:"border-box",borderLeft:n?"2px solid rgba(0, 0, 0, 0.5)":"",transition:"background-color 0.2s ease",cursor:this.options.draggable?"move":"",pointerEvents:"all",whiteSpace:n?"nowrap":"",padding:"0.2em"});r.textContent=i;const a=o("div",{position:"absolute",left:"0",top:"0",width:"6px",height:"100%",cursor:this.options.resizable?"ew-resize":"",pointerEvents:"all",borderLeft:n?"":"2px solid rgba(0, 0, 0, 0.5)",borderRadius:"2px 0 0 2px"});r.appendChild(a);const d=a.cloneNode();s(d,{left:"",right:"0",borderLeft:"",borderRight:n?"":"2px solid rgba(0, 0, 0, 0.5)",borderRadius:"0 2px 2px 0"}),r.appendChild(d),a.addEventListener("mousedown",(e=>{this.options.resizable&&(e.stopPropagation(),this.modifiedRegion=this.regions.find((e=>e.element===r))||null,this.isResizingLeft=!0,this.isMoving=!1)})),d.addEventListener("mousedown",(e=>{this.options.resizable&&(e.stopPropagation(),this.modifiedRegion=this.regions.find((e=>e.element===r))||null,this.isResizingLeft=!1,this.isMoving=!1)})),r.addEventListener("mousedown",(()=>{this.options.draggable&&(this.modifiedRegion=this.regions.find((e=>e.element===r))||null,this.isMoving=!0)})),r.addEventListener("click",(()=>{const e=this.regions.find((e=>e.element===r));e&&this.emit("region-clicked",{region:e})})),this.regionsContainer.appendChild(r);const h=r.getBoundingClientRect().left,g=r.scrollWidth,l=this.regions.filter((e=>{const{left:t}=e.element.getBoundingClientRect(),i=e.element.scrollWidth;return h<t+i&&t<h+g})).map((e=>parseFloat(e.element.style.paddingTop))).reduce(((e,t)=>e+t),0);return l>0&&(r.style.paddingTop=`${l+1}em`),r}createRegion(e,t,i=""){const n=this.wavesurfer?.getDuration()||0;return{element:this.createRegionElement(e,t,i),start:e,end:t,startTime:e*n,endTime:t*n,title:i}}addRegion(e){this.regions.push(e),this.emit("region-created",{region:e})}updateRegion(e,t,i){const n=this.wavesurfer?.getDuration()||0;null!=t&&(e.start=t,e.element.style.left=100*e.start+"%",e.element.style.width=100*(e.end-e.start)+"%",e.startTime=t*n),null!=i&&(e.end=i,e.element.style.width=100*(e.end-e.start)+"%",e.endTime=i*n),this.emit("region-updated",{region:e})}moveRegion(e,t){this.updateRegion(e,e.start+t,e.end+t)}add(e,t,i="",n=""){const s=this.wavesurfer?.getDuration()||0,o=e/s,r=t/s,a=this.createRegion(o,r,i);return this.addRegion(a),n&&this.setRegionColor(a,n),a}setRegionColor(e,t){e.element.style[e.startTime===e.endTime?"borderColor":"backgroundColor"]=t}}const a=r})(),n.default})()));
@@ -1,5 +1,5 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
- import type { WaveSurferPluginParams } from '../index.js';
2
+ import type { WaveSurferPluginParams } from '../wavesurfer.js';
3
3
  export type TimelinePluginOptions = {
4
4
  /** The height of the timeline in pixels, defaults to 20 */
5
5
  height?: number;
@@ -23,7 +23,9 @@ export type TimelinePluginEvents = {
23
23
  declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOptions> {
24
24
  private timelineWrapper;
25
25
  protected options: TimelinePluginOptions & typeof defaultOptions;
26
- constructor(params: WaveSurferPluginParams, options: TimelinePluginOptions);
26
+ constructor(options: TimelinePluginOptions);
27
+ static create(options: TimelinePluginOptions): TimelinePlugin;
28
+ init(params: WaveSurferPluginParams): void;
27
29
  /** Unmount */
28
30
  destroy(): void;
29
31
  private initTimelineWrapper;
@@ -5,12 +5,21 @@ const defaultOptions = {
5
5
  class TimelinePlugin extends BasePlugin {
6
6
  timelineWrapper;
7
7
  options;
8
- constructor(params, options) {
9
- super(params, options);
8
+ constructor(options) {
9
+ super(options);
10
10
  this.options = Object.assign({}, defaultOptions, options);
11
- this.container = this.options.container ?? this.wrapper;
12
11
  this.timelineWrapper = this.initTimelineWrapper();
13
- this.container.appendChild(this.timelineWrapper);
12
+ }
13
+ static create(options) {
14
+ return new TimelinePlugin(options);
15
+ }
16
+ init(params) {
17
+ super.init(params);
18
+ if (!this.wavesurfer || !this.wrapper) {
19
+ throw Error('WaveSurfer is not initialized');
20
+ }
21
+ const container = this.options.container ?? this.wrapper;
22
+ container.appendChild(this.timelineWrapper);
14
23
  if (this.options.duration) {
15
24
  this.initTimeline(this.options.duration);
16
25
  }
@@ -0,0 +1 @@
1
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Timeline=t():e.Timeline=t()}(WaveSurfer,(()=>(()=>{"use strict";var e={284:(e,t,i)=>{i.d(t,{Z:()=>s});var n=i(139);class r extends n.Z{wavesurfer;container;wrapper;subscriptions=[];options;constructor(e){super(),this.options=e}init(e){this.wavesurfer=e.wavesurfer,this.container=e.container,this.wrapper=e.wrapper}destroy(){this.subscriptions.forEach((e=>e()))}}const s=r},139:(e,t,i)=>{i.d(t,{Z:()=>n});const n=class{eventTarget;constructor(){this.eventTarget=new EventTarget}emit(e,t){const i=new CustomEvent(String(e),{detail:t});this.eventTarget.dispatchEvent(i)}on(e,t,i){const n=e=>{e instanceof CustomEvent&&t(e.detail)},r=String(e);return this.eventTarget.addEventListener(r,n,{once:i}),()=>this.eventTarget.removeEventListener(r,n)}once(e,t){return this.on(e,t,!0)}}}},t={};function i(n){var r=t[n];if(void 0!==r)return r.exports;var s=t[n]={exports:{}};return e[n](s,s.exports,i),s.exports}i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{i.d(n,{default:()=>s});var e=i(284);const t={height:20};class r extends e.Z{timelineWrapper;options;constructor(e){super(e),this.options=Object.assign({},t,e),this.timelineWrapper=this.initTimelineWrapper()}static create(e){return new r(e)}init(e){if(super.init(e),!this.wavesurfer||!this.wrapper)throw Error("WaveSurfer is not initialized");(this.options.container??this.wrapper).appendChild(this.timelineWrapper),this.options.duration?this.initTimeline(this.options.duration):this.subscriptions.push(this.wavesurfer.on("decode",(({duration:e})=>{this.initTimeline(e)})))}destroy(){this.timelineWrapper.remove(),super.destroy()}initTimelineWrapper(){return document.createElement("div")}formatTime(e){return e/60>1?`${Math.round(e/60)}:${(e=Math.round(e%60))<10?"0":""}${e}`:""+Math.round(1e3*e)/1e3}defaultTimeInterval(e){return e>=25?1:5*e>=25?5:15*e>=25?15:60*Math.ceil(.5/e)}defaultPrimaryLabelInterval(e){return e>=25?10:5*e>=25?6:4}defaultSecondaryLabelInterval(e){return e>=25?5:2}initTimeline(e){const t=Math.round(this.timelineWrapper.scrollWidth*devicePixelRatio)/e,i=this.options.timeInterval??this.defaultTimeInterval(t),n=this.options.primaryLabelInterval??this.defaultPrimaryLabelInterval(t),r=this.options.secondaryLabelInterval??this.defaultSecondaryLabelInterval(t),s=document.createElement("div");s.setAttribute("style",`\n height: ${this.options.height}px;\n overflow: hidden;\n display: flex;\n justify-content: space-between;\n align-items: flex-end;\n font-size: ${this.options.height/2}px;\n white-space: nowrap;\n `);const o=document.createElement("div");o.setAttribute("style","\n width: 1px;\n height: 50%;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n overflow: visible;\n border-left: 1px solid currentColor;\n opacity: 0.25;\n ");for(let t=0;t<e;t+=i){const e=o.cloneNode(),i=t%n==0;(i||t%r==0)&&(e.style.height="100%",e.style.textIndent="3px",e.textContent=this.formatTime(t),i&&(e.style.opacity="1")),s.appendChild(e)}this.timelineWrapper.appendChild(s),this.emit("ready")}}const s=r})(),n.default})()));
@@ -11,7 +11,7 @@ type RendererOptions = {
11
11
  barWidth?: number;
12
12
  barGap?: number;
13
13
  barRadius?: number;
14
- noScrollbar?: boolean;
14
+ hideScrollbar?: boolean;
15
15
  };
16
16
  type RendererEvents = {
17
17
  click: {
@@ -27,6 +27,8 @@ declare class Renderer extends EventEmitter<RendererEvents> {
27
27
  private progressWrapper;
28
28
  private timeout;
29
29
  private isScrolling;
30
+ private audioData;
31
+ private resizeObserver;
30
32
  constructor(options: RendererOptions);
31
33
  getContainer(): HTMLElement;
32
34
  getWrapper(): HTMLElement;
package/dist/renderer.js CHANGED
@@ -8,6 +8,8 @@ class Renderer extends EventEmitter {
8
8
  progressWrapper;
9
9
  timeout = null;
10
10
  isScrolling = false;
11
+ audioData = null;
12
+ resizeObserver = null;
11
13
  constructor(options) {
12
14
  super();
13
15
  this.options = options;
@@ -33,10 +35,10 @@ class Renderer extends EventEmitter {
33
35
  overflow-y: hidden;
34
36
  width: 100%;
35
37
  position: relative;
36
- ${this.options.noScrollbar ? 'scrollbar-color: transparent;' : ''}
38
+ ${this.options.hideScrollbar ? 'scrollbar-color: transparent;' : ''}
37
39
  }
38
40
  :host ::-webkit-scrollbar {
39
- display: ${this.options.noScrollbar ? 'none' : 'auto'};
41
+ display: ${this.options.hideScrollbar ? 'none' : 'auto'};
40
42
  }
41
43
  :host .wrapper {
42
44
  position: relative;
@@ -89,6 +91,11 @@ class Renderer extends EventEmitter {
89
91
  const relativeX = x / rect.width;
90
92
  this.emit('click', { relativeX });
91
93
  });
94
+ // Re-render the waveform on container resize
95
+ this.resizeObserver = new ResizeObserver(() => {
96
+ this.delay(() => this.audioData && this.zoom(this.audioData, this.options.minPxPerSec), 100);
97
+ });
98
+ this.resizeObserver.observe(this.scrollContainer);
92
99
  }
93
100
  getContainer() {
94
101
  return this.scrollContainer;
@@ -98,6 +105,7 @@ class Renderer extends EventEmitter {
98
105
  }
99
106
  destroy() {
100
107
  this.container.remove();
108
+ this.resizeObserver?.disconnect();
101
109
  }
102
110
  delay(fn, delayMs = 10) {
103
111
  if (this.timeout) {
@@ -178,11 +186,12 @@ class Renderer extends EventEmitter {
178
186
  // Clear the canvas
179
187
  this.canvasWrapper.innerHTML = '';
180
188
  this.progressWrapper.innerHTML = '';
181
- // Draw the currently visible part of the waveform
189
+ // Determine the currently visible part of the waveform
182
190
  const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;
183
191
  const scale = len / scrollWidth;
184
192
  const start = Math.floor(scrollLeft * scale);
185
193
  const end = Math.ceil(Math.min(scrollWidth, scrollLeft + clientWidth) * scale);
194
+ // Draw the visible portion of the waveform
186
195
  draw(start, end);
187
196
  // Draw the rest of the waveform with a timeout for better performance
188
197
  const step = end - start;
@@ -198,30 +207,34 @@ class Renderer extends EventEmitter {
198
207
  }
199
208
  }
200
209
  render(audioData) {
201
- // Determine the width of the canvas
210
+ // Determine the width of the waveform
202
211
  const pixelRatio = window.devicePixelRatio || 1;
203
212
  const parentWidth = this.options.fillParent ? this.scrollContainer.clientWidth * pixelRatio : 0;
204
- const scrollWidth = audioData.duration * this.options.minPxPerSec;
205
- this.isScrolling = scrollWidth > parentWidth;
206
- const width = Math.max(1, this.isScrolling ? scrollWidth : parentWidth);
213
+ const scrollWidth = Math.max(1, audioData.duration * this.options.minPxPerSec);
214
+ const width = scrollWidth > parentWidth ? scrollWidth : parentWidth;
207
215
  const { height } = this.options;
208
- // Remember the current cursor position
209
- const oldCursorPosition = this.progressWrapper.clientWidth;
216
+ // Set the width of the container
217
+ this.isScrolling = width > parentWidth;
210
218
  this.scrollContainer.style.overflowX = this.isScrolling ? 'auto' : 'hidden';
211
219
  this.wrapper.style.width = `${Math.floor(width / pixelRatio)}px`;
212
- // Adjust the scroll position so that the cursor stays in the same place
213
- const newCursortPosition = this.progressWrapper.clientWidth;
214
- this.scrollContainer.scrollLeft += newCursortPosition - oldCursorPosition;
215
- // First two channels are used
220
+ // Only the first two channels are used
216
221
  const channelData = [audioData.getChannelData(0)];
217
222
  if (audioData.numberOfChannels > 1) {
218
223
  channelData.push(audioData.getChannelData(1));
219
224
  }
225
+ // Render the waveform
220
226
  this.renderPeaks(channelData, width, height, pixelRatio);
227
+ this.audioData = audioData;
221
228
  }
222
229
  zoom(audioData, minPxPerSec) {
230
+ // Remember the current cursor position
231
+ const oldCursorPosition = this.progressWrapper.clientWidth;
232
+ // Set the new zoom level and re-render the waveform
223
233
  this.options.minPxPerSec = minPxPerSec;
224
234
  this.render(audioData);
235
+ // Adjust the scroll position so that the cursor stays in the same place
236
+ const newCursortPosition = this.progressWrapper.clientWidth;
237
+ this.scrollContainer.scrollLeft += newCursortPosition - oldCursorPosition;
225
238
  }
226
239
  renderProgress(progress, autoCenter = false) {
227
240
  this.progressWrapper.style.width = `${progress * 100}%`;
@@ -1,4 +1,5 @@
1
- import EventEmitter, { type GeneralEventTypes } from './event-emitter.js';
1
+ import Player from './player.js';
2
+ import { type GeneralEventTypes } from './event-emitter.js';
2
3
  import BasePlugin from './base-plugin.js';
3
4
  export type WaveSurferOptions = {
4
5
  /** HTML element or CSS selector */
@@ -34,9 +35,15 @@ export type WaveSurferOptions = {
34
35
  /** Play the audio on load */
35
36
  autoplay?: boolean;
36
37
  /** Is the waveform clickable? */
37
- interactive?: boolean;
38
+ interact?: boolean;
38
39
  /** Hide scrollbar **/
39
- noScrollbar?: boolean;
40
+ hideScrollbar?: boolean;
41
+ /** Audio rate */
42
+ audioRate?: number;
43
+ /** Keep scroll to the center of the waveform during playback */
44
+ autoCenter?: boolean;
45
+ /** Initialize plugins */
46
+ plugins?: BasePlugin<GeneralEventTypes, unknown>[];
40
47
  };
41
48
  declare const defaultOptions: {
42
49
  height: number;
@@ -45,7 +52,8 @@ declare const defaultOptions: {
45
52
  cursorWidth: number;
46
53
  minPxPerSec: number;
47
54
  fillParent: boolean;
48
- interactive: boolean;
55
+ interact: boolean;
56
+ autoCenter: boolean;
49
57
  };
50
58
  export type WaveSurferEvents = {
51
59
  decode: {
@@ -78,16 +86,15 @@ export type WaveSurferPluginParams = {
78
86
  container: HTMLElement;
79
87
  wrapper: HTMLElement;
80
88
  };
81
- declare class WaveSurfer extends EventEmitter<WaveSurferEvents> {
89
+ declare class WaveSurfer extends Player<WaveSurferEvents> {
82
90
  options: WaveSurferOptions & typeof defaultOptions;
83
91
  private fetcher;
84
92
  private decoder;
85
93
  private renderer;
86
- private player;
87
94
  private timer;
88
95
  private plugins;
89
- private subscriptions;
90
96
  private decodedData;
97
+ private canPlay;
91
98
  /** Create a new WaveSurfer instance */
92
99
  static create(options: WaveSurferOptions): WaveSurfer;
93
100
  /** Create a new WaveSurfer instance */
@@ -96,40 +103,16 @@ declare class WaveSurfer extends EventEmitter<WaveSurferEvents> {
96
103
  private initRendererEvents;
97
104
  private initTimerEvents;
98
105
  private initReadyEvent;
106
+ private initPlugins;
107
+ /** Register a wavesurfer.js plugin */
108
+ registerPlugin<T extends BasePlugin<GeneralEventTypes, unknown>>(plugin: T): T;
99
109
  /** Load an audio file by URL, with optional pre-decoded audio data */
100
110
  load(url: string, channelData?: Float32Array[], duration?: number): Promise<void>;
101
111
  /** Zoom in or out */
102
112
  zoom(minPxPerSec: number): void;
103
- /** Start playing the audio */
104
- play(): void;
105
- /** Pause the audio */
106
- pause(): void;
107
- /** Skip to a time position in seconds */
108
- seekTo(time: number): void;
109
- /** Check if the audio is playing */
110
- isPlaying(): boolean;
111
- /** Get the duration of the audio in seconds */
112
- getDuration(): number;
113
- /** Get the current audio position in seconds */
114
- getCurrentTime(): number;
115
- /** Get the audio volume */
116
- getVolume(): number;
117
- /** Set the audio volume */
118
- setVolume(volume: number): void;
119
- /** Get the audio muted state */
120
- getMuted(): boolean;
121
- /** Mute or unmute the audio */
122
- setMuted(muted: boolean): void;
123
- /** Get playback rate */
124
- getPlaybackRate(): number;
125
- /** Set playback rate, with an optional parameter to NOT preserve the pitch if false */
126
- setPlaybackRate(rate: number, preservePitch?: boolean): void;
127
- /** Register and initialize a plugin */
128
- registerPlugin<T extends BasePlugin<GeneralEventTypes, Options>, Options>(CustomPlugin: new (params: WaveSurferPluginParams, options: Options) => T, options: Options): T;
129
113
  /** Get the decoded audio data */
130
114
  getDecodedData(): AudioBuffer | null;
131
- /** Get the raw media element */
132
- getMediaElement(): HTMLMediaElement | null;
115
+ getDuration(): number;
133
116
  /** Toggle if the waveform should react to clicks */
134
117
  toggleInteractive(isInteractive: boolean): void;
135
118
  /** Unmount wavesurfer */