wavesurfer.js 7.11.1 → 7.12.1

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.
Files changed (86) hide show
  1. package/README.md +1 -1
  2. package/dist/__tests__/memory-leaks.test.d.ts +7 -0
  3. package/dist/__tests__/memory-leaks.test.js +281 -0
  4. package/dist/__tests__/player.test.js +107 -0
  5. package/dist/__tests__/renderer-utils.test.js +55 -2
  6. package/dist/draggable.d.ts +4 -0
  7. package/dist/draggable.js +4 -0
  8. package/dist/player.d.ts +21 -0
  9. package/dist/player.js +86 -0
  10. package/dist/plugins/envelope.cjs +1 -1
  11. package/dist/plugins/envelope.esm.js +1 -1
  12. package/dist/plugins/envelope.js +1 -1
  13. package/dist/plugins/envelope.min.js +1 -1
  14. package/dist/plugins/hover.cjs +1 -1
  15. package/dist/plugins/hover.d.ts +0 -3
  16. package/dist/plugins/hover.esm.js +1 -1
  17. package/dist/plugins/hover.js +1 -1
  18. package/dist/plugins/hover.min.js +1 -1
  19. package/dist/plugins/minimap.cjs +1 -1
  20. package/dist/plugins/minimap.esm.js +1 -1
  21. package/dist/plugins/minimap.js +1 -1
  22. package/dist/plugins/minimap.min.js +1 -1
  23. package/dist/plugins/record.cjs +1 -1
  24. package/dist/plugins/record.esm.js +1 -1
  25. package/dist/plugins/record.js +1 -1
  26. package/dist/plugins/record.min.js +1 -1
  27. package/dist/plugins/regions.cjs +1 -1
  28. package/dist/plugins/regions.esm.js +1 -1
  29. package/dist/plugins/regions.js +1 -1
  30. package/dist/plugins/regions.min.js +1 -1
  31. package/dist/plugins/timeline.cjs +1 -1
  32. package/dist/plugins/timeline.d.ts +1 -1
  33. package/dist/plugins/timeline.esm.js +1 -1
  34. package/dist/plugins/timeline.js +1 -1
  35. package/dist/plugins/timeline.min.js +1 -1
  36. package/dist/plugins/zoom.cjs +1 -1
  37. package/dist/plugins/zoom.esm.js +1 -1
  38. package/dist/plugins/zoom.js +1 -1
  39. package/dist/plugins/zoom.min.js +1 -1
  40. package/dist/reactive/__tests__/drag-stream.test.d.ts +1 -0
  41. package/dist/reactive/__tests__/drag-stream.test.js +199 -0
  42. package/dist/reactive/__tests__/event-stream-emitter.test.d.ts +1 -0
  43. package/dist/reactive/__tests__/event-stream-emitter.test.js +230 -0
  44. package/dist/reactive/__tests__/event-streams.test.d.ts +1 -0
  45. package/dist/reactive/__tests__/event-streams.test.js +278 -0
  46. package/dist/reactive/__tests__/media-event-bridge.test.d.ts +1 -0
  47. package/dist/reactive/__tests__/media-event-bridge.test.js +202 -0
  48. package/dist/reactive/__tests__/render-scheduler.test.d.ts +1 -0
  49. package/dist/reactive/__tests__/render-scheduler.test.js +216 -0
  50. package/dist/reactive/__tests__/scroll-stream.test.d.ts +1 -0
  51. package/dist/reactive/__tests__/scroll-stream.test.js +191 -0
  52. package/dist/reactive/__tests__/state-event-emitter.test.d.ts +1 -0
  53. package/dist/reactive/__tests__/state-event-emitter.test.js +234 -0
  54. package/dist/reactive/__tests__/store.test.d.ts +1 -0
  55. package/dist/reactive/__tests__/store.test.js +271 -0
  56. package/dist/reactive/drag-stream.d.ts +48 -0
  57. package/dist/reactive/drag-stream.js +146 -0
  58. package/dist/reactive/event-stream-emitter.d.ts +104 -0
  59. package/dist/reactive/event-stream-emitter.js +151 -0
  60. package/dist/reactive/event-streams.d.ts +65 -0
  61. package/dist/reactive/event-streams.js +146 -0
  62. package/dist/reactive/media-event-bridge.d.ts +51 -0
  63. package/dist/reactive/media-event-bridge.js +152 -0
  64. package/dist/reactive/render-scheduler.d.ts +43 -0
  65. package/dist/reactive/render-scheduler.js +75 -0
  66. package/dist/reactive/scroll-stream.d.ts +93 -0
  67. package/dist/reactive/scroll-stream.js +131 -0
  68. package/dist/reactive/state-event-emitter.d.ts +105 -0
  69. package/dist/reactive/state-event-emitter.js +213 -0
  70. package/dist/reactive/store.d.ts +66 -0
  71. package/dist/reactive/store.js +115 -0
  72. package/dist/renderer-utils.d.ts +13 -4
  73. package/dist/renderer-utils.js +35 -15
  74. package/dist/renderer.d.ts +2 -1
  75. package/dist/renderer.js +51 -48
  76. package/dist/state/__tests__/wavesurfer-state.test.d.ts +1 -0
  77. package/dist/state/__tests__/wavesurfer-state.test.js +268 -0
  78. package/dist/state/wavesurfer-state.d.ts +90 -0
  79. package/dist/state/wavesurfer-state.js +124 -0
  80. package/dist/types.d.ts +165 -1
  81. package/dist/wavesurfer.cjs +1 -1
  82. package/dist/wavesurfer.d.ts +16 -0
  83. package/dist/wavesurfer.esm.js +1 -1
  84. package/dist/wavesurfer.js +32 -0
  85. package/dist/wavesurfer.min.js +1 -1
  86. package/package.json +2 -3
@@ -1 +1 @@
1
- class t{constructor(){this.listeners={}}on(t,i,e){if(this.listeners[t]||(this.listeners[t]=new Set),null==e?void 0:e.once){const e=(...s)=>{this.un(t,e),i(...s)};return this.listeners[t].add(e),()=>this.un(t,e)}return this.listeners[t].add(i),()=>this.un(t,i)}un(t,i){var e;null===(e=this.listeners[t])||void 0===e||e.delete(i)}once(t,i){return this.on(t,i,{once:!0})}unAll(){this.listeners={}}emit(t,...i){this.listeners[t]&&this.listeners[t].forEach((t=>t(...i)))}}class i extends t{constructor(t){super(),this.subscriptions=[],this.isDestroyed=!1,this.options=t}onInit(){}_init(t){this.isDestroyed&&(this.subscriptions=[],this.isDestroyed=!1),this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.subscriptions=[],this.isDestroyed=!0,this.wavesurfer=void 0}}const e={scale:.5,deltaThreshold:5,exponentialZooming:!1,iterations:20};class s extends i{constructor(t){super(t||{}),this.wrapper=void 0,this.container=null,this.accumulatedDelta=0,this.pointerTime=0,this.oldX=0,this.endZoom=0,this.startZoom=0,this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0,this.onWheel=t=>{if(this.wavesurfer&&this.container&&!(Math.abs(t.deltaX)>=Math.abs(t.deltaY))&&(t.preventDefault(),this.accumulatedDelta+=-t.deltaY,0===this.startZoom&&this.options.exponentialZooming&&(this.startZoom=this.wavesurfer.getWrapper().clientWidth/this.wavesurfer.getDuration()),0===this.options.deltaThreshold||Math.abs(this.accumulatedDelta)>=this.options.deltaThreshold)){const i=this.wavesurfer.getDuration(),e=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec,s=t.clientX-this.container.getBoundingClientRect().left,n=this.container.clientWidth,o=this.wavesurfer.getScroll();s===this.oldX&&0!==this.oldX||(this.pointerTime=(o+s)/e),this.oldX=s;const h=this.calculateNewZoom(e,this.accumulatedDelta),r=n/h*(s/n);h*i<n?(this.wavesurfer.zoom(n/i),this.container.scrollLeft=0):(this.wavesurfer.zoom(h),this.container.scrollLeft=(this.pointerTime-r)*h),this.accumulatedDelta=0}},this.calculateNewZoom=(t,i)=>{let e;if(this.options.exponentialZooming){const s=i>0?Math.pow(this.endZoom/this.startZoom,1/(this.options.iterations-1)):Math.pow(this.startZoom/this.endZoom,1/(this.options.iterations-1));e=Math.max(0,t*s)}else e=Math.max(0,t+i*this.options.scale);return Math.min(e,this.options.maxZoom)},this.onTouchStart=t=>{if(this.wavesurfer&&this.container&&2===t.touches.length){t.preventDefault(),this.isPinching=!0,this.initialPinchDistance=this.getTouchDistance(t);const i=this.wavesurfer.getDuration();this.initialZoom=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec;const e=this.getTouchCenterX(t)-this.container.getBoundingClientRect().left,s=this.wavesurfer.getScroll();this.pointerTime=(s+e)/this.initialZoom,this.oldX=e}},this.onTouchMove=t=>{if(!this.isPinching||2!==t.touches.length||!this.wavesurfer||!this.container)return;t.preventDefault();const i=this.getTouchDistance(t)/this.initialPinchDistance;let e=this.initialZoom*i;e=Math.min(e,this.options.maxZoom);const s=this.wavesurfer.getDuration(),n=this.container.clientWidth,o=n/s;e<o&&(e=o);const h=n/e*(this.oldX/n);e===o?(this.wavesurfer.zoom(o),this.container.scrollLeft=0):(this.wavesurfer.zoom(e),this.container.scrollLeft=(this.pointerTime-h)*e)},this.onTouchEnd=t=>{this.isPinching&&t.touches.length<2&&(this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0)},this.options=Object.assign({},e,t)}static create(t){return new s(t)}onInit(){var t;this.wrapper=null===(t=this.wavesurfer)||void 0===t?void 0:t.getWrapper(),this.wrapper&&(this.container=this.wrapper.parentElement,this.container.addEventListener("wheel",this.onWheel),this.container.addEventListener("touchstart",this.onTouchStart,{passive:!1,capture:!0}),this.container.addEventListener("touchmove",this.onTouchMove,{passive:!1,capture:!0}),this.container.addEventListener("touchend",this.onTouchEnd,{passive:!1,capture:!0}),this.container.addEventListener("touchcancel",this.onTouchEnd,{passive:!1,capture:!0}),void 0===this.options.maxZoom&&(this.options.maxZoom=this.container.clientWidth),this.endZoom=this.options.maxZoom)}getTouchDistance(t){const i=t.touches[0],e=t.touches[1];return Math.sqrt(Math.pow(e.clientX-i.clientX,2)+Math.pow(e.clientY-i.clientY,2))}getTouchCenterX(t){const i=t.touches[0],e=t.touches[1];return(i.clientX+e.clientX)/2}destroy(){this.container&&(this.container.removeEventListener("wheel",this.onWheel),this.container.removeEventListener("touchstart",this.onTouchStart),this.container.removeEventListener("touchmove",this.onTouchMove),this.container.removeEventListener("touchend",this.onTouchEnd),this.container.removeEventListener("touchcancel",this.onTouchEnd)),super.destroy()}}export{s as default};
1
+ class t{constructor(){this.listeners={}}on(t,i,s){if(this.listeners[t]||(this.listeners[t]=new Set),null==s?void 0:s.once){const s=(...e)=>{this.un(t,s),i(...e)};return this.listeners[t].add(s),()=>this.un(t,s)}return this.listeners[t].add(i),()=>this.un(t,i)}un(t,i){var s;null===(s=this.listeners[t])||void 0===s||s.delete(i)}once(t,i){return this.on(t,i,{once:!0})}unAll(){this.listeners={}}emit(t,...i){this.listeners[t]&&this.listeners[t].forEach((t=>t(...i)))}}class i extends t{constructor(t){super(),this.subscriptions=[],this.isDestroyed=!1,this.options=t}onInit(){}_init(t){this.isDestroyed&&(this.subscriptions=[],this.isDestroyed=!1),this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.subscriptions=[],this.isDestroyed=!0,this.wavesurfer=void 0}}function s(t,i){let s;const e=()=>{s&&(s(),s=void 0),s=t()},o=i.map((t=>t.subscribe(e)));return e(),()=>{s&&(s(),s=void 0),o.forEach((t=>t()))}}function e(t,i){const s=function(t){let i=t;const s=new Set;return{get value(){return i},set(t){Object.is(i,t)||(i=t,s.forEach((t=>t(i))))},update(t){this.set(t(i))},subscribe:t=>(s.add(t),()=>s.delete(t))}}(null),e=t=>{s.set(t)};return t.addEventListener(i,e),s._cleanup=()=>{t.removeEventListener(i,e)},s}const o={scale:.5,deltaThreshold:5,exponentialZooming:!1,iterations:20};class n extends i{constructor(t){super(t||{}),this.wrapper=void 0,this.container=null,this.accumulatedDelta=0,this.pointerTime=0,this.oldX=0,this.endZoom=0,this.startZoom=0,this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0,this.onWheel=t=>{if(this.wavesurfer&&this.container&&!(Math.abs(t.deltaX)>=Math.abs(t.deltaY))&&(t.preventDefault(),this.accumulatedDelta+=-t.deltaY,0===this.startZoom&&this.options.exponentialZooming&&(this.startZoom=this.wavesurfer.getWrapper().clientWidth/this.wavesurfer.getDuration()),0===this.options.deltaThreshold||Math.abs(this.accumulatedDelta)>=this.options.deltaThreshold)){const i=this.wavesurfer.getDuration(),s=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec,e=t.clientX-this.container.getBoundingClientRect().left,o=this.container.clientWidth,n=this.wavesurfer.getScroll();e===this.oldX&&0!==this.oldX||(this.pointerTime=(n+e)/s),this.oldX=e;const h=this.calculateNewZoom(s,this.accumulatedDelta),r=o/h*(e/o);h*i<o?(this.wavesurfer.zoom(o/i),this.container.scrollLeft=0):(this.wavesurfer.zoom(h),this.container.scrollLeft=(this.pointerTime-r)*h),this.accumulatedDelta=0}},this.calculateNewZoom=(t,i)=>{let s;if(this.options.exponentialZooming){const e=i>0?Math.pow(this.endZoom/this.startZoom,1/(this.options.iterations-1)):Math.pow(this.startZoom/this.endZoom,1/(this.options.iterations-1));s=Math.max(0,t*e)}else s=Math.max(0,t+i*this.options.scale);return Math.min(s,this.options.maxZoom)},this.onTouchStart=t=>{if(this.wavesurfer&&this.container&&2===t.touches.length){t.preventDefault(),this.isPinching=!0,this.initialPinchDistance=this.getTouchDistance(t);const i=this.wavesurfer.getDuration();this.initialZoom=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec;const s=this.getTouchCenterX(t)-this.container.getBoundingClientRect().left,e=this.wavesurfer.getScroll();this.pointerTime=(e+s)/this.initialZoom,this.oldX=s}},this.onTouchMove=t=>{if(!this.isPinching||2!==t.touches.length||!this.wavesurfer||!this.container)return;t.preventDefault();const i=this.getTouchDistance(t)/this.initialPinchDistance;let s=this.initialZoom*i;s=Math.min(s,this.options.maxZoom);const e=this.wavesurfer.getDuration(),o=this.container.clientWidth,n=o/e;s<n&&(s=n);const h=o/s*(this.oldX/o);s===n?(this.wavesurfer.zoom(n),this.container.scrollLeft=0):(this.wavesurfer.zoom(s),this.container.scrollLeft=(this.pointerTime-h)*s)},this.onTouchEnd=t=>{this.isPinching&&t.touches.length<2&&(this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0)},this.options=Object.assign({},o,t)}static create(t){return new n(t)}onInit(){var t,i;if(this.wrapper=null===(t=this.wavesurfer)||void 0===t?void 0:t.getWrapper(),!this.wrapper)return;this.container=this.wrapper.parentElement,void 0===this.options.maxZoom&&(this.options.maxZoom=this.container.clientWidth),this.endZoom=this.options.maxZoom;const o=null===(i=this.wavesurfer)||void 0===i?void 0:i.getState();o&&this.subscriptions.push(s((()=>{if(o.zoom.value>0&&0===this.startZoom&&this.options.exponentialZooming){const t=o.duration.value;t>0&&this.container&&(this.startZoom=this.container.clientWidth/t)}}),[o.zoom,o.duration]));const n=e(this.container,"wheel"),h=e(this.container,"touchstart"),r=e(this.container,"touchmove"),a=e(this.container,"touchend"),c=e(this.container,"touchcancel");this.subscriptions.push(s((()=>{const t=n.value;t&&this.onWheel(t)}),[n])),this.subscriptions.push(s((()=>{const t=h.value;t&&this.onTouchStart(t)}),[h])),this.subscriptions.push(s((()=>{const t=r.value;t&&this.onTouchMove(t)}),[r])),this.subscriptions.push(s((()=>{const t=a.value;t&&this.onTouchEnd(t)}),[a])),this.subscriptions.push(s((()=>{const t=c.value;t&&this.onTouchEnd(t)}),[c]))}getTouchDistance(t){const i=t.touches[0],s=t.touches[1];return Math.sqrt(Math.pow(s.clientX-i.clientX,2)+Math.pow(s.clientY-i.clientY,2))}getTouchCenterX(t){const i=t.touches[0],s=t.touches[1];return(i.clientX+s.clientX)/2}destroy(){super.destroy()}}export{n as default};
@@ -1 +1 @@
1
- class t{constructor(){this.listeners={}}on(t,i,e){if(this.listeners[t]||(this.listeners[t]=new Set),null==e?void 0:e.once){const e=(...s)=>{this.un(t,e),i(...s)};return this.listeners[t].add(e),()=>this.un(t,e)}return this.listeners[t].add(i),()=>this.un(t,i)}un(t,i){var e;null===(e=this.listeners[t])||void 0===e||e.delete(i)}once(t,i){return this.on(t,i,{once:!0})}unAll(){this.listeners={}}emit(t,...i){this.listeners[t]&&this.listeners[t].forEach((t=>t(...i)))}}class i extends t{constructor(t){super(),this.subscriptions=[],this.isDestroyed=!1,this.options=t}onInit(){}_init(t){this.isDestroyed&&(this.subscriptions=[],this.isDestroyed=!1),this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.subscriptions=[],this.isDestroyed=!0,this.wavesurfer=void 0}}const e={scale:.5,deltaThreshold:5,exponentialZooming:!1,iterations:20};class s extends i{constructor(t){super(t||{}),this.wrapper=void 0,this.container=null,this.accumulatedDelta=0,this.pointerTime=0,this.oldX=0,this.endZoom=0,this.startZoom=0,this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0,this.onWheel=t=>{if(this.wavesurfer&&this.container&&!(Math.abs(t.deltaX)>=Math.abs(t.deltaY))&&(t.preventDefault(),this.accumulatedDelta+=-t.deltaY,0===this.startZoom&&this.options.exponentialZooming&&(this.startZoom=this.wavesurfer.getWrapper().clientWidth/this.wavesurfer.getDuration()),0===this.options.deltaThreshold||Math.abs(this.accumulatedDelta)>=this.options.deltaThreshold)){const i=this.wavesurfer.getDuration(),e=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec,s=t.clientX-this.container.getBoundingClientRect().left,n=this.container.clientWidth,o=this.wavesurfer.getScroll();s===this.oldX&&0!==this.oldX||(this.pointerTime=(o+s)/e),this.oldX=s;const h=this.calculateNewZoom(e,this.accumulatedDelta),r=n/h*(s/n);h*i<n?(this.wavesurfer.zoom(n/i),this.container.scrollLeft=0):(this.wavesurfer.zoom(h),this.container.scrollLeft=(this.pointerTime-r)*h),this.accumulatedDelta=0}},this.calculateNewZoom=(t,i)=>{let e;if(this.options.exponentialZooming){const s=i>0?Math.pow(this.endZoom/this.startZoom,1/(this.options.iterations-1)):Math.pow(this.startZoom/this.endZoom,1/(this.options.iterations-1));e=Math.max(0,t*s)}else e=Math.max(0,t+i*this.options.scale);return Math.min(e,this.options.maxZoom)},this.onTouchStart=t=>{if(this.wavesurfer&&this.container&&2===t.touches.length){t.preventDefault(),this.isPinching=!0,this.initialPinchDistance=this.getTouchDistance(t);const i=this.wavesurfer.getDuration();this.initialZoom=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec;const e=this.getTouchCenterX(t)-this.container.getBoundingClientRect().left,s=this.wavesurfer.getScroll();this.pointerTime=(s+e)/this.initialZoom,this.oldX=e}},this.onTouchMove=t=>{if(!this.isPinching||2!==t.touches.length||!this.wavesurfer||!this.container)return;t.preventDefault();const i=this.getTouchDistance(t)/this.initialPinchDistance;let e=this.initialZoom*i;e=Math.min(e,this.options.maxZoom);const s=this.wavesurfer.getDuration(),n=this.container.clientWidth,o=n/s;e<o&&(e=o);const h=n/e*(this.oldX/n);e===o?(this.wavesurfer.zoom(o),this.container.scrollLeft=0):(this.wavesurfer.zoom(e),this.container.scrollLeft=(this.pointerTime-h)*e)},this.onTouchEnd=t=>{this.isPinching&&t.touches.length<2&&(this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0)},this.options=Object.assign({},e,t)}static create(t){return new s(t)}onInit(){var t;this.wrapper=null===(t=this.wavesurfer)||void 0===t?void 0:t.getWrapper(),this.wrapper&&(this.container=this.wrapper.parentElement,this.container.addEventListener("wheel",this.onWheel),this.container.addEventListener("touchstart",this.onTouchStart,{passive:!1,capture:!0}),this.container.addEventListener("touchmove",this.onTouchMove,{passive:!1,capture:!0}),this.container.addEventListener("touchend",this.onTouchEnd,{passive:!1,capture:!0}),this.container.addEventListener("touchcancel",this.onTouchEnd,{passive:!1,capture:!0}),void 0===this.options.maxZoom&&(this.options.maxZoom=this.container.clientWidth),this.endZoom=this.options.maxZoom)}getTouchDistance(t){const i=t.touches[0],e=t.touches[1];return Math.sqrt(Math.pow(e.clientX-i.clientX,2)+Math.pow(e.clientY-i.clientY,2))}getTouchCenterX(t){const i=t.touches[0],e=t.touches[1];return(i.clientX+e.clientX)/2}destroy(){this.container&&(this.container.removeEventListener("wheel",this.onWheel),this.container.removeEventListener("touchstart",this.onTouchStart),this.container.removeEventListener("touchmove",this.onTouchMove),this.container.removeEventListener("touchend",this.onTouchEnd),this.container.removeEventListener("touchcancel",this.onTouchEnd)),super.destroy()}}export{s as default};
1
+ class t{constructor(){this.listeners={}}on(t,i,s){if(this.listeners[t]||(this.listeners[t]=new Set),null==s?void 0:s.once){const s=(...e)=>{this.un(t,s),i(...e)};return this.listeners[t].add(s),()=>this.un(t,s)}return this.listeners[t].add(i),()=>this.un(t,i)}un(t,i){var s;null===(s=this.listeners[t])||void 0===s||s.delete(i)}once(t,i){return this.on(t,i,{once:!0})}unAll(){this.listeners={}}emit(t,...i){this.listeners[t]&&this.listeners[t].forEach((t=>t(...i)))}}class i extends t{constructor(t){super(),this.subscriptions=[],this.isDestroyed=!1,this.options=t}onInit(){}_init(t){this.isDestroyed&&(this.subscriptions=[],this.isDestroyed=!1),this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.subscriptions=[],this.isDestroyed=!0,this.wavesurfer=void 0}}function s(t,i){let s;const e=()=>{s&&(s(),s=void 0),s=t()},o=i.map((t=>t.subscribe(e)));return e(),()=>{s&&(s(),s=void 0),o.forEach((t=>t()))}}function e(t,i){const s=function(t){let i=t;const s=new Set;return{get value(){return i},set(t){Object.is(i,t)||(i=t,s.forEach((t=>t(i))))},update(t){this.set(t(i))},subscribe:t=>(s.add(t),()=>s.delete(t))}}(null),e=t=>{s.set(t)};return t.addEventListener(i,e),s._cleanup=()=>{t.removeEventListener(i,e)},s}const o={scale:.5,deltaThreshold:5,exponentialZooming:!1,iterations:20};class n extends i{constructor(t){super(t||{}),this.wrapper=void 0,this.container=null,this.accumulatedDelta=0,this.pointerTime=0,this.oldX=0,this.endZoom=0,this.startZoom=0,this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0,this.onWheel=t=>{if(this.wavesurfer&&this.container&&!(Math.abs(t.deltaX)>=Math.abs(t.deltaY))&&(t.preventDefault(),this.accumulatedDelta+=-t.deltaY,0===this.startZoom&&this.options.exponentialZooming&&(this.startZoom=this.wavesurfer.getWrapper().clientWidth/this.wavesurfer.getDuration()),0===this.options.deltaThreshold||Math.abs(this.accumulatedDelta)>=this.options.deltaThreshold)){const i=this.wavesurfer.getDuration(),s=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec,e=t.clientX-this.container.getBoundingClientRect().left,o=this.container.clientWidth,n=this.wavesurfer.getScroll();e===this.oldX&&0!==this.oldX||(this.pointerTime=(n+e)/s),this.oldX=e;const h=this.calculateNewZoom(s,this.accumulatedDelta),r=o/h*(e/o);h*i<o?(this.wavesurfer.zoom(o/i),this.container.scrollLeft=0):(this.wavesurfer.zoom(h),this.container.scrollLeft=(this.pointerTime-r)*h),this.accumulatedDelta=0}},this.calculateNewZoom=(t,i)=>{let s;if(this.options.exponentialZooming){const e=i>0?Math.pow(this.endZoom/this.startZoom,1/(this.options.iterations-1)):Math.pow(this.startZoom/this.endZoom,1/(this.options.iterations-1));s=Math.max(0,t*e)}else s=Math.max(0,t+i*this.options.scale);return Math.min(s,this.options.maxZoom)},this.onTouchStart=t=>{if(this.wavesurfer&&this.container&&2===t.touches.length){t.preventDefault(),this.isPinching=!0,this.initialPinchDistance=this.getTouchDistance(t);const i=this.wavesurfer.getDuration();this.initialZoom=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec;const s=this.getTouchCenterX(t)-this.container.getBoundingClientRect().left,e=this.wavesurfer.getScroll();this.pointerTime=(e+s)/this.initialZoom,this.oldX=s}},this.onTouchMove=t=>{if(!this.isPinching||2!==t.touches.length||!this.wavesurfer||!this.container)return;t.preventDefault();const i=this.getTouchDistance(t)/this.initialPinchDistance;let s=this.initialZoom*i;s=Math.min(s,this.options.maxZoom);const e=this.wavesurfer.getDuration(),o=this.container.clientWidth,n=o/e;s<n&&(s=n);const h=o/s*(this.oldX/o);s===n?(this.wavesurfer.zoom(n),this.container.scrollLeft=0):(this.wavesurfer.zoom(s),this.container.scrollLeft=(this.pointerTime-h)*s)},this.onTouchEnd=t=>{this.isPinching&&t.touches.length<2&&(this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0)},this.options=Object.assign({},o,t)}static create(t){return new n(t)}onInit(){var t,i;if(this.wrapper=null===(t=this.wavesurfer)||void 0===t?void 0:t.getWrapper(),!this.wrapper)return;this.container=this.wrapper.parentElement,void 0===this.options.maxZoom&&(this.options.maxZoom=this.container.clientWidth),this.endZoom=this.options.maxZoom;const o=null===(i=this.wavesurfer)||void 0===i?void 0:i.getState();o&&this.subscriptions.push(s((()=>{if(o.zoom.value>0&&0===this.startZoom&&this.options.exponentialZooming){const t=o.duration.value;t>0&&this.container&&(this.startZoom=this.container.clientWidth/t)}}),[o.zoom,o.duration]));const n=e(this.container,"wheel"),h=e(this.container,"touchstart"),r=e(this.container,"touchmove"),a=e(this.container,"touchend"),c=e(this.container,"touchcancel");this.subscriptions.push(s((()=>{const t=n.value;t&&this.onWheel(t)}),[n])),this.subscriptions.push(s((()=>{const t=h.value;t&&this.onTouchStart(t)}),[h])),this.subscriptions.push(s((()=>{const t=r.value;t&&this.onTouchMove(t)}),[r])),this.subscriptions.push(s((()=>{const t=a.value;t&&this.onTouchEnd(t)}),[a])),this.subscriptions.push(s((()=>{const t=c.value;t&&this.onTouchEnd(t)}),[c]))}getTouchDistance(t){const i=t.touches[0],s=t.touches[1];return Math.sqrt(Math.pow(s.clientX-i.clientX,2)+Math.pow(s.clientY-i.clientY,2))}getTouchCenterX(t){const i=t.touches[0],s=t.touches[1];return(i.clientX+s.clientX)/2}destroy(){super.destroy()}}export{n as default};
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):((t="undefined"!=typeof globalThis?globalThis:t||self).WaveSurfer=t.WaveSurfer||{},t.WaveSurfer.Zoom=e())}(this,(function(){"use strict";class t{constructor(){this.listeners={}}on(t,e,i){if(this.listeners[t]||(this.listeners[t]=new Set),null==i?void 0:i.once){const i=(...s)=>{this.un(t,i),e(...s)};return this.listeners[t].add(i),()=>this.un(t,i)}return this.listeners[t].add(e),()=>this.un(t,e)}un(t,e){var i;null===(i=this.listeners[t])||void 0===i||i.delete(e)}once(t,e){return this.on(t,e,{once:!0})}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.isDestroyed=!1,this.options=t}onInit(){}_init(t){this.isDestroyed&&(this.subscriptions=[],this.isDestroyed=!1),this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.subscriptions=[],this.isDestroyed=!0,this.wavesurfer=void 0}}const i={scale:.5,deltaThreshold:5,exponentialZooming:!1,iterations:20};class s extends e{constructor(t){super(t||{}),this.wrapper=void 0,this.container=null,this.accumulatedDelta=0,this.pointerTime=0,this.oldX=0,this.endZoom=0,this.startZoom=0,this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0,this.onWheel=t=>{if(this.wavesurfer&&this.container&&!(Math.abs(t.deltaX)>=Math.abs(t.deltaY))&&(t.preventDefault(),this.accumulatedDelta+=-t.deltaY,0===this.startZoom&&this.options.exponentialZooming&&(this.startZoom=this.wavesurfer.getWrapper().clientWidth/this.wavesurfer.getDuration()),0===this.options.deltaThreshold||Math.abs(this.accumulatedDelta)>=this.options.deltaThreshold)){const e=this.wavesurfer.getDuration(),i=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/e:this.wavesurfer.options.minPxPerSec,s=t.clientX-this.container.getBoundingClientRect().left,n=this.container.clientWidth,o=this.wavesurfer.getScroll();s===this.oldX&&0!==this.oldX||(this.pointerTime=(o+s)/i),this.oldX=s;const h=this.calculateNewZoom(i,this.accumulatedDelta),r=n/h*(s/n);h*e<n?(this.wavesurfer.zoom(n/e),this.container.scrollLeft=0):(this.wavesurfer.zoom(h),this.container.scrollLeft=(this.pointerTime-r)*h),this.accumulatedDelta=0}},this.calculateNewZoom=(t,e)=>{let i;if(this.options.exponentialZooming){const s=e>0?Math.pow(this.endZoom/this.startZoom,1/(this.options.iterations-1)):Math.pow(this.startZoom/this.endZoom,1/(this.options.iterations-1));i=Math.max(0,t*s)}else i=Math.max(0,t+e*this.options.scale);return Math.min(i,this.options.maxZoom)},this.onTouchStart=t=>{if(this.wavesurfer&&this.container&&2===t.touches.length){t.preventDefault(),this.isPinching=!0,this.initialPinchDistance=this.getTouchDistance(t);const e=this.wavesurfer.getDuration();this.initialZoom=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/e:this.wavesurfer.options.minPxPerSec;const i=this.getTouchCenterX(t)-this.container.getBoundingClientRect().left,s=this.wavesurfer.getScroll();this.pointerTime=(s+i)/this.initialZoom,this.oldX=i}},this.onTouchMove=t=>{if(!this.isPinching||2!==t.touches.length||!this.wavesurfer||!this.container)return;t.preventDefault();const e=this.getTouchDistance(t)/this.initialPinchDistance;let i=this.initialZoom*e;i=Math.min(i,this.options.maxZoom);const s=this.wavesurfer.getDuration(),n=this.container.clientWidth,o=n/s;i<o&&(i=o);const h=n/i*(this.oldX/n);i===o?(this.wavesurfer.zoom(o),this.container.scrollLeft=0):(this.wavesurfer.zoom(i),this.container.scrollLeft=(this.pointerTime-h)*i)},this.onTouchEnd=t=>{this.isPinching&&t.touches.length<2&&(this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0)},this.options=Object.assign({},i,t)}static create(t){return new s(t)}onInit(){var t;this.wrapper=null===(t=this.wavesurfer)||void 0===t?void 0:t.getWrapper(),this.wrapper&&(this.container=this.wrapper.parentElement,this.container.addEventListener("wheel",this.onWheel),this.container.addEventListener("touchstart",this.onTouchStart,{passive:!1,capture:!0}),this.container.addEventListener("touchmove",this.onTouchMove,{passive:!1,capture:!0}),this.container.addEventListener("touchend",this.onTouchEnd,{passive:!1,capture:!0}),this.container.addEventListener("touchcancel",this.onTouchEnd,{passive:!1,capture:!0}),void 0===this.options.maxZoom&&(this.options.maxZoom=this.container.clientWidth),this.endZoom=this.options.maxZoom)}getTouchDistance(t){const e=t.touches[0],i=t.touches[1];return Math.sqrt(Math.pow(i.clientX-e.clientX,2)+Math.pow(i.clientY-e.clientY,2))}getTouchCenterX(t){const e=t.touches[0],i=t.touches[1];return(e.clientX+i.clientX)/2}destroy(){this.container&&(this.container.removeEventListener("wheel",this.onWheel),this.container.removeEventListener("touchstart",this.onTouchStart),this.container.removeEventListener("touchmove",this.onTouchMove),this.container.removeEventListener("touchend",this.onTouchEnd),this.container.removeEventListener("touchcancel",this.onTouchEnd)),super.destroy()}}return s}));
1
+ !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):((t="undefined"!=typeof globalThis?globalThis:t||self).WaveSurfer=t.WaveSurfer||{},t.WaveSurfer.Zoom=i())}(this,(function(){"use strict";class t{constructor(){this.listeners={}}on(t,i,s){if(this.listeners[t]||(this.listeners[t]=new Set),null==s?void 0:s.once){const s=(...e)=>{this.un(t,s),i(...e)};return this.listeners[t].add(s),()=>this.un(t,s)}return this.listeners[t].add(i),()=>this.un(t,i)}un(t,i){var s;null===(s=this.listeners[t])||void 0===s||s.delete(i)}once(t,i){return this.on(t,i,{once:!0})}unAll(){this.listeners={}}emit(t,...i){this.listeners[t]&&this.listeners[t].forEach((t=>t(...i)))}}class i extends t{constructor(t){super(),this.subscriptions=[],this.isDestroyed=!1,this.options=t}onInit(){}_init(t){this.isDestroyed&&(this.subscriptions=[],this.isDestroyed=!1),this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.subscriptions=[],this.isDestroyed=!0,this.wavesurfer=void 0}}function s(t,i){let s;const e=()=>{s&&(s(),s=void 0),s=t()},o=i.map((t=>t.subscribe(e)));return e(),()=>{s&&(s(),s=void 0),o.forEach((t=>t()))}}function e(t,i){const s=function(t){let i=t;const s=new Set;return{get value(){return i},set(t){Object.is(i,t)||(i=t,s.forEach((t=>t(i))))},update(t){this.set(t(i))},subscribe:t=>(s.add(t),()=>s.delete(t))}}(null),e=t=>{s.set(t)};return t.addEventListener(i,e),s._cleanup=()=>{t.removeEventListener(i,e)},s}const o={scale:.5,deltaThreshold:5,exponentialZooming:!1,iterations:20};class n extends i{constructor(t){super(t||{}),this.wrapper=void 0,this.container=null,this.accumulatedDelta=0,this.pointerTime=0,this.oldX=0,this.endZoom=0,this.startZoom=0,this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0,this.onWheel=t=>{if(this.wavesurfer&&this.container&&!(Math.abs(t.deltaX)>=Math.abs(t.deltaY))&&(t.preventDefault(),this.accumulatedDelta+=-t.deltaY,0===this.startZoom&&this.options.exponentialZooming&&(this.startZoom=this.wavesurfer.getWrapper().clientWidth/this.wavesurfer.getDuration()),0===this.options.deltaThreshold||Math.abs(this.accumulatedDelta)>=this.options.deltaThreshold)){const i=this.wavesurfer.getDuration(),s=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec,e=t.clientX-this.container.getBoundingClientRect().left,o=this.container.clientWidth,n=this.wavesurfer.getScroll();e===this.oldX&&0!==this.oldX||(this.pointerTime=(n+e)/s),this.oldX=e;const h=this.calculateNewZoom(s,this.accumulatedDelta),r=o/h*(e/o);h*i<o?(this.wavesurfer.zoom(o/i),this.container.scrollLeft=0):(this.wavesurfer.zoom(h),this.container.scrollLeft=(this.pointerTime-r)*h),this.accumulatedDelta=0}},this.calculateNewZoom=(t,i)=>{let s;if(this.options.exponentialZooming){const e=i>0?Math.pow(this.endZoom/this.startZoom,1/(this.options.iterations-1)):Math.pow(this.startZoom/this.endZoom,1/(this.options.iterations-1));s=Math.max(0,t*e)}else s=Math.max(0,t+i*this.options.scale);return Math.min(s,this.options.maxZoom)},this.onTouchStart=t=>{if(this.wavesurfer&&this.container&&2===t.touches.length){t.preventDefault(),this.isPinching=!0,this.initialPinchDistance=this.getTouchDistance(t);const i=this.wavesurfer.getDuration();this.initialZoom=0===this.wavesurfer.options.minPxPerSec?this.wavesurfer.getWrapper().scrollWidth/i:this.wavesurfer.options.minPxPerSec;const s=this.getTouchCenterX(t)-this.container.getBoundingClientRect().left,e=this.wavesurfer.getScroll();this.pointerTime=(e+s)/this.initialZoom,this.oldX=s}},this.onTouchMove=t=>{if(!this.isPinching||2!==t.touches.length||!this.wavesurfer||!this.container)return;t.preventDefault();const i=this.getTouchDistance(t)/this.initialPinchDistance;let s=this.initialZoom*i;s=Math.min(s,this.options.maxZoom);const e=this.wavesurfer.getDuration(),o=this.container.clientWidth,n=o/e;s<n&&(s=n);const h=o/s*(this.oldX/o);s===n?(this.wavesurfer.zoom(n),this.container.scrollLeft=0):(this.wavesurfer.zoom(s),this.container.scrollLeft=(this.pointerTime-h)*s)},this.onTouchEnd=t=>{this.isPinching&&t.touches.length<2&&(this.isPinching=!1,this.initialPinchDistance=0,this.initialZoom=0)},this.options=Object.assign({},o,t)}static create(t){return new n(t)}onInit(){var t,i;if(this.wrapper=null===(t=this.wavesurfer)||void 0===t?void 0:t.getWrapper(),!this.wrapper)return;this.container=this.wrapper.parentElement,void 0===this.options.maxZoom&&(this.options.maxZoom=this.container.clientWidth),this.endZoom=this.options.maxZoom;const o=null===(i=this.wavesurfer)||void 0===i?void 0:i.getState();o&&this.subscriptions.push(s((()=>{if(o.zoom.value>0&&0===this.startZoom&&this.options.exponentialZooming){const t=o.duration.value;t>0&&this.container&&(this.startZoom=this.container.clientWidth/t)}}),[o.zoom,o.duration]));const n=e(this.container,"wheel"),h=e(this.container,"touchstart"),r=e(this.container,"touchmove"),a=e(this.container,"touchend"),c=e(this.container,"touchcancel");this.subscriptions.push(s((()=>{const t=n.value;t&&this.onWheel(t)}),[n])),this.subscriptions.push(s((()=>{const t=h.value;t&&this.onTouchStart(t)}),[h])),this.subscriptions.push(s((()=>{const t=r.value;t&&this.onTouchMove(t)}),[r])),this.subscriptions.push(s((()=>{const t=a.value;t&&this.onTouchEnd(t)}),[a])),this.subscriptions.push(s((()=>{const t=c.value;t&&this.onTouchEnd(t)}),[c]))}getTouchDistance(t){const i=t.touches[0],s=t.touches[1];return Math.sqrt(Math.pow(s.clientX-i.clientX,2)+Math.pow(s.clientY-i.clientY,2))}getTouchCenterX(t){const i=t.touches[0],s=t.touches[1];return(i.clientX+s.clientX)/2}destroy(){super.destroy()}}return n}));
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,199 @@
1
+ import { createDragStream } from '../drag-stream';
2
+ describe('createDragStream', () => {
3
+ beforeAll(() => {
4
+ // Mock matchMedia
5
+ Object.defineProperty(window, 'matchMedia', {
6
+ writable: true,
7
+ value: jest.fn().mockReturnValue({
8
+ matches: false,
9
+ addListener: jest.fn(),
10
+ removeListener: jest.fn(),
11
+ }),
12
+ });
13
+ // Polyfill PointerEvent for jsdom
14
+ if (typeof window.PointerEvent === 'undefined') {
15
+ class FakePointerEvent extends MouseEvent {
16
+ constructor(type, props) {
17
+ super(type, props);
18
+ }
19
+ }
20
+ // @ts-expect-error - Polyfill PointerEvent for jsdom test environment
21
+ window.PointerEvent = FakePointerEvent;
22
+ // @ts-expect-error - Polyfill PointerEvent for jsdom test environment
23
+ global.PointerEvent = FakePointerEvent;
24
+ }
25
+ });
26
+ let element;
27
+ let events;
28
+ beforeEach(() => {
29
+ element = document.createElement('div');
30
+ document.body.appendChild(element);
31
+ element.getBoundingClientRect = jest.fn(() => ({
32
+ left: 0,
33
+ top: 0,
34
+ right: 100,
35
+ bottom: 100,
36
+ width: 100,
37
+ height: 100,
38
+ x: 0,
39
+ y: 0,
40
+ toJSON: () => ({}),
41
+ }));
42
+ events = [];
43
+ });
44
+ afterEach(() => {
45
+ document.body.removeChild(element);
46
+ });
47
+ it('should create a drag signal', () => {
48
+ const { signal, cleanup } = createDragStream(element);
49
+ expect(signal).toBeDefined();
50
+ expect(signal.value).toBeNull();
51
+ cleanup();
52
+ });
53
+ it('should emit start event on drag', () => {
54
+ var _a;
55
+ const { signal, cleanup } = createDragStream(element, { threshold: 0 });
56
+ signal.subscribe((event) => {
57
+ if (event)
58
+ events.push(event);
59
+ });
60
+ // Simulate drag
61
+ const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
62
+ element.dispatchEvent(pointerDown);
63
+ const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20 });
64
+ document.dispatchEvent(pointerMove);
65
+ expect(events.length).toBeGreaterThan(0);
66
+ expect((_a = events[0]) === null || _a === void 0 ? void 0 : _a.type).toBe('start');
67
+ cleanup();
68
+ });
69
+ it('should emit move events with deltas', () => {
70
+ const { signal, cleanup } = createDragStream(element, { threshold: 0 });
71
+ signal.subscribe((event) => {
72
+ if (event)
73
+ events.push(event);
74
+ });
75
+ // Simulate drag
76
+ const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
77
+ element.dispatchEvent(pointerDown);
78
+ const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 30 });
79
+ document.dispatchEvent(pointerMove);
80
+ const moveEvent = events.find((e) => e.type === 'move');
81
+ expect(moveEvent).toBeDefined();
82
+ expect(moveEvent === null || moveEvent === void 0 ? void 0 : moveEvent.deltaX).toBe(10);
83
+ expect(moveEvent === null || moveEvent === void 0 ? void 0 : moveEvent.deltaY).toBe(20);
84
+ cleanup();
85
+ });
86
+ it('should emit end event on pointer up', () => {
87
+ const { signal, cleanup } = createDragStream(element, { threshold: 0 });
88
+ signal.subscribe((event) => {
89
+ if (event)
90
+ events.push(event);
91
+ });
92
+ // Simulate drag
93
+ const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
94
+ element.dispatchEvent(pointerDown);
95
+ const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20 });
96
+ document.dispatchEvent(pointerMove);
97
+ const pointerUp = new PointerEvent('pointerup', { clientX: 20, clientY: 20 });
98
+ document.dispatchEvent(pointerUp);
99
+ expect(events.some((e) => e.type === 'end')).toBe(true);
100
+ cleanup();
101
+ });
102
+ it('should respect threshold', () => {
103
+ const { signal, cleanup } = createDragStream(element, { threshold: 10 });
104
+ signal.subscribe((event) => {
105
+ if (event)
106
+ events.push(event);
107
+ });
108
+ // Simulate small drag (below threshold)
109
+ const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
110
+ element.dispatchEvent(pointerDown);
111
+ const pointerMove = new PointerEvent('pointermove', { clientX: 15, clientY: 15 });
112
+ document.dispatchEvent(pointerMove);
113
+ // Should not emit events yet
114
+ expect(events.length).toBe(0);
115
+ // Simulate larger drag (above threshold)
116
+ const pointerMove2 = new PointerEvent('pointermove', { clientX: 25, clientY: 25 });
117
+ document.dispatchEvent(pointerMove2);
118
+ // Should now emit events
119
+ expect(events.length).toBeGreaterThan(0);
120
+ cleanup();
121
+ });
122
+ it('should cleanup event listeners', () => {
123
+ const { cleanup } = createDragStream(element);
124
+ const addEventListenerSpy = jest.spyOn(document, 'addEventListener');
125
+ const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener');
126
+ // Trigger drag to attach document listeners
127
+ const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
128
+ element.dispatchEvent(pointerDown);
129
+ expect(addEventListenerSpy).toHaveBeenCalled();
130
+ cleanup();
131
+ expect(removeEventListenerSpy).toHaveBeenCalled();
132
+ addEventListenerSpy.mockRestore();
133
+ removeEventListenerSpy.mockRestore();
134
+ });
135
+ it('should ignore non-primary mouse buttons', () => {
136
+ const { signal, cleanup } = createDragStream(element);
137
+ signal.subscribe((event) => {
138
+ if (event)
139
+ events.push(event);
140
+ });
141
+ // Simulate right-click drag
142
+ const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 2 });
143
+ element.dispatchEvent(pointerDown);
144
+ const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20 });
145
+ document.dispatchEvent(pointerMove);
146
+ // Should not emit any events
147
+ expect(events.length).toBe(0);
148
+ cleanup();
149
+ });
150
+ it('should stop propagation during drag', () => {
151
+ const { signal, cleanup } = createDragStream(element, { threshold: 0 });
152
+ signal.subscribe((event) => {
153
+ if (event)
154
+ events.push(event);
155
+ });
156
+ // Start drag
157
+ const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0 });
158
+ element.dispatchEvent(pointerDown);
159
+ // Move - should prevent click
160
+ const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20 });
161
+ document.dispatchEvent(pointerMove);
162
+ // End drag
163
+ const pointerUp = new PointerEvent('pointerup', { clientX: 20, clientY: 20 });
164
+ document.dispatchEvent(pointerUp);
165
+ // Simulate click after drag
166
+ const clickHandler = jest.fn();
167
+ document.addEventListener('click', clickHandler, { capture: true });
168
+ const click = new MouseEvent('click', { bubbles: true });
169
+ Object.defineProperty(click, 'stopPropagation', { value: jest.fn() });
170
+ Object.defineProperty(click, 'preventDefault', { value: jest.fn() });
171
+ document.dispatchEvent(click);
172
+ document.removeEventListener('click', clickHandler, { capture: true });
173
+ cleanup();
174
+ });
175
+ it('should handle pointer leave events', () => {
176
+ const { signal, cleanup } = createDragStream(element, { threshold: 0 });
177
+ signal.subscribe((event) => {
178
+ if (event)
179
+ events.push(event);
180
+ });
181
+ // Start drag
182
+ const pointerDown = new PointerEvent('pointerdown', { clientX: 10, clientY: 10, button: 0, pointerId: 1 });
183
+ element.dispatchEvent(pointerDown);
184
+ // Move to start dragging
185
+ const pointerMove = new PointerEvent('pointermove', { clientX: 20, clientY: 20, pointerId: 1 });
186
+ document.dispatchEvent(pointerMove);
187
+ // Pointer leaves document
188
+ const pointerOut = new PointerEvent('pointerout', {
189
+ clientX: 20,
190
+ clientY: 20,
191
+ pointerId: 1,
192
+ relatedTarget: document.documentElement,
193
+ });
194
+ document.dispatchEvent(pointerOut);
195
+ // Should emit end event
196
+ expect(events.some((e) => e.type === 'end')).toBe(true);
197
+ cleanup();
198
+ });
199
+ });
@@ -0,0 +1,230 @@
1
+ import { toStream, toStreams, mergeStreams, mapStream, filterStream } from '../event-stream-emitter';
2
+ import EventEmitter from '../../event-emitter';
3
+ // Test class that exposes emit for testing
4
+ class TestEmitter extends EventEmitter {
5
+ emit(event, ...args) {
6
+ super.emit(event, ...args);
7
+ }
8
+ }
9
+ describe('event-stream-emitter', () => {
10
+ let emitter;
11
+ beforeEach(() => {
12
+ emitter = new TestEmitter();
13
+ });
14
+ describe('toStream', () => {
15
+ it('should create a stream from event', () => {
16
+ const { stream, cleanup } = toStream(emitter, 'play');
17
+ expect(stream.value).toBeNull();
18
+ const values = [];
19
+ stream.subscribe((value) => values.push(value));
20
+ emitter.emit('play');
21
+ expect(stream.value).toEqual([]);
22
+ // Values includes initial null and then the event
23
+ expect(values.length).toBeGreaterThanOrEqual(1);
24
+ expect(values[values.length - 1]).toEqual([]);
25
+ cleanup();
26
+ });
27
+ it('should update stream with event arguments', () => {
28
+ const { stream, cleanup } = toStream(emitter, 'timeupdate');
29
+ emitter.emit('timeupdate', 42);
30
+ expect(stream.value).toEqual([42]);
31
+ emitter.emit('timeupdate', 100);
32
+ expect(stream.value).toEqual([100]);
33
+ cleanup();
34
+ });
35
+ it('should handle multiple arguments', () => {
36
+ const { stream, cleanup } = toStream(emitter, 'customEvent');
37
+ emitter.emit('customEvent', 'hello', 5);
38
+ expect(stream.value).toEqual(['hello', 5]);
39
+ cleanup();
40
+ });
41
+ it('should cleanup event listener', () => {
42
+ const { stream, cleanup } = toStream(emitter, 'play');
43
+ emitter.emit('play');
44
+ expect(stream.value).toEqual([]);
45
+ cleanup();
46
+ // After cleanup, event should not update stream
47
+ const oldValue = stream.value;
48
+ emitter.emit('play');
49
+ expect(stream.value).toBe(oldValue); // Value should not change after cleanup
50
+ });
51
+ it('should allow multiple subscriptions to stream', () => {
52
+ const { stream, cleanup } = toStream(emitter, 'timeupdate');
53
+ const values1 = [];
54
+ const values2 = [];
55
+ stream.subscribe((value) => values1.push(value));
56
+ stream.subscribe((value) => values2.push(value));
57
+ emitter.emit('timeupdate', 10);
58
+ emitter.emit('timeupdate', 20);
59
+ expect(values1).toContainEqual([10]);
60
+ expect(values1).toContainEqual([20]);
61
+ expect(values2).toContainEqual([10]);
62
+ expect(values2).toContainEqual([20]);
63
+ cleanup();
64
+ });
65
+ });
66
+ describe('toStreams', () => {
67
+ it('should create multiple streams', () => {
68
+ const streams = toStreams(emitter, ['play', 'pause', 'timeupdate']);
69
+ expect(streams.play).toBeDefined();
70
+ expect(streams.pause).toBeDefined();
71
+ expect(streams.timeupdate).toBeDefined();
72
+ emitter.emit('play');
73
+ expect(streams.play.value).toEqual([]);
74
+ emitter.emit('pause');
75
+ expect(streams.pause.value).toEqual([]);
76
+ emitter.emit('timeupdate', 50);
77
+ expect(streams.timeupdate.value).toEqual([50]);
78
+ streams.cleanup();
79
+ });
80
+ it('should cleanup all streams', () => {
81
+ const streams = toStreams(emitter, ['play', 'pause']);
82
+ streams.cleanup();
83
+ emitter.emit('play');
84
+ emitter.emit('pause');
85
+ // Streams should not update after cleanup
86
+ expect(streams.play.value).toBeNull();
87
+ expect(streams.pause.value).toBeNull();
88
+ });
89
+ it('should handle empty event list', () => {
90
+ const streams = toStreams(emitter, []);
91
+ expect(streams.cleanup).toBeDefined();
92
+ expect(typeof streams.cleanup).toBe('function');
93
+ streams.cleanup(); // Should not throw
94
+ });
95
+ });
96
+ describe('mergeStreams', () => {
97
+ it('should merge multiple events into one stream', () => {
98
+ const { stream, cleanup } = mergeStreams(emitter, ['play', 'pause']);
99
+ expect(stream.value).toBeNull();
100
+ const values = [];
101
+ stream.subscribe((value) => values.push(value));
102
+ emitter.emit('play');
103
+ expect(stream.value).toEqual({ event: 'play', args: [] });
104
+ emitter.emit('pause');
105
+ expect(stream.value).toEqual({ event: 'pause', args: [] });
106
+ expect(values).toContainEqual({ event: 'play', args: [] });
107
+ expect(values).toContainEqual({ event: 'pause', args: [] });
108
+ cleanup();
109
+ });
110
+ it('should include event arguments in merged stream', () => {
111
+ const { stream, cleanup } = mergeStreams(emitter, ['timeupdate', 'error']);
112
+ emitter.emit('timeupdate', 42);
113
+ expect(stream.value).toEqual({ event: 'timeupdate', args: [42] });
114
+ const error = new Error('Test error');
115
+ emitter.emit('error', error);
116
+ expect(stream.value).toEqual({ event: 'error', args: [error] });
117
+ cleanup();
118
+ });
119
+ it('should cleanup all merged event listeners', () => {
120
+ const { stream, cleanup } = mergeStreams(emitter, ['play', 'pause']);
121
+ cleanup();
122
+ emitter.emit('play');
123
+ emitter.emit('pause');
124
+ // Stream should not update after cleanup
125
+ expect(stream.value).toBeNull();
126
+ });
127
+ it('should handle single event', () => {
128
+ const { stream, cleanup } = mergeStreams(emitter, ['play']);
129
+ emitter.emit('play');
130
+ expect(stream.value).toEqual({ event: 'play', args: [] });
131
+ cleanup();
132
+ });
133
+ });
134
+ describe('mapStream', () => {
135
+ it('should transform stream values', () => {
136
+ const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
137
+ const seconds = mapStream(timeStream, (value) => (value ? Math.floor(value[0]) : 0));
138
+ expect(seconds.value).toBe(0);
139
+ emitter.emit('timeupdate', 42.7);
140
+ expect(seconds.value).toBe(42);
141
+ emitter.emit('timeupdate', 99.9);
142
+ expect(seconds.value).toBe(99);
143
+ cleanup();
144
+ });
145
+ it('should work with multiple subscriptions', () => {
146
+ const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
147
+ const doubled = mapStream(timeStream, (value) => (value ? value[0] * 2 : 0));
148
+ const values = [];
149
+ doubled.subscribe((value) => values.push(value));
150
+ emitter.emit('timeupdate', 5);
151
+ emitter.emit('timeupdate', 10);
152
+ expect(values).toContain(10);
153
+ expect(values).toContain(20);
154
+ cleanup();
155
+ });
156
+ it('should handle null values', () => {
157
+ const { stream, cleanup } = toStream(emitter, 'play');
158
+ const mapped = mapStream(stream, (value) => (value ? 'playing' : 'not playing'));
159
+ expect(mapped.value).toBe('not playing');
160
+ emitter.emit('play');
161
+ expect(mapped.value).toBe('playing');
162
+ cleanup();
163
+ });
164
+ });
165
+ describe('filterStream', () => {
166
+ it('should filter stream values', () => {
167
+ const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
168
+ const afterTen = filterStream(timeStream, (value) => (value ? value[0] > 10 : false));
169
+ expect(afterTen.value).toBeNull();
170
+ emitter.emit('timeupdate', 5);
171
+ expect(afterTen.value).toBeNull();
172
+ emitter.emit('timeupdate', 15);
173
+ expect(afterTen.value).toEqual([15]);
174
+ emitter.emit('timeupdate', 7);
175
+ expect(afterTen.value).toBeNull();
176
+ cleanup();
177
+ });
178
+ it('should work with subscriptions', () => {
179
+ const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
180
+ const evenSeconds = filterStream(timeStream, (value) => value !== null && Math.floor(value[0]) % 2 === 0);
181
+ const values = [];
182
+ evenSeconds.subscribe((value) => values.push(value));
183
+ emitter.emit('timeupdate', 1);
184
+ emitter.emit('timeupdate', 2);
185
+ emitter.emit('timeupdate', 3);
186
+ emitter.emit('timeupdate', 4);
187
+ expect(values.filter((v) => v !== null)).toEqual([[2], [4]]);
188
+ cleanup();
189
+ });
190
+ it('should pass through values that match predicate', () => {
191
+ const { stream, cleanup } = toStream(emitter, 'customEvent');
192
+ const onlyHello = filterStream(stream, (value) => value !== null && value[0] === 'hello');
193
+ emitter.emit('customEvent', 'hello', 1);
194
+ expect(onlyHello.value).toEqual(['hello', 1]);
195
+ emitter.emit('customEvent', 'world', 2);
196
+ expect(onlyHello.value).toBeNull();
197
+ cleanup();
198
+ });
199
+ });
200
+ describe('integration', () => {
201
+ it('should allow chaining stream operations', () => {
202
+ const { stream: timeStream, cleanup } = toStream(emitter, 'timeupdate');
203
+ // Filter for times > 10, then map to seconds
204
+ const filtered = filterStream(timeStream, (value) => (value ? value[0] > 10 : false));
205
+ const seconds = mapStream(filtered, (value) => (value ? Math.floor(value[0]) : 0));
206
+ const values = [];
207
+ seconds.subscribe((value) => values.push(value));
208
+ emitter.emit('timeupdate', 5);
209
+ emitter.emit('timeupdate', 15.7);
210
+ emitter.emit('timeupdate', 20.3);
211
+ expect(values).toContain(15);
212
+ expect(values).toContain(20);
213
+ expect(values).not.toContain(5);
214
+ cleanup();
215
+ });
216
+ it('should work with merged streams', () => {
217
+ const { stream: mergedStream, cleanup } = mergeStreams(emitter, ['play', 'pause']);
218
+ const eventNames = mapStream(mergedStream, (value) => (value ? value.event : null));
219
+ const names = [];
220
+ eventNames.subscribe((name) => names.push(name));
221
+ emitter.emit('play');
222
+ emitter.emit('pause');
223
+ emitter.emit('play');
224
+ expect(names).toContain('play');
225
+ expect(names).toContain('pause');
226
+ expect(names.filter((n) => n === 'play')).toHaveLength(2);
227
+ cleanup();
228
+ });
229
+ });
230
+ });
@@ -0,0 +1 @@
1
+ export {};