wavesurfer.js 7.12.3 → 7.12.5
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.
- package/dist/__tests__/minimap.test.js +38 -2
- package/dist/__tests__/renderer.test.js +33 -0
- package/dist/plugins/envelope.cjs +1 -0
- package/dist/plugins/envelope.esm.js +1 -0
- package/dist/plugins/envelope.js +1 -426
- package/dist/plugins/envelope.min.js +1 -0
- package/dist/plugins/hover.cjs +1 -0
- package/dist/plugins/hover.esm.js +1 -0
- package/dist/plugins/hover.js +1 -131
- package/dist/plugins/hover.min.js +1 -0
- package/dist/plugins/minimap.cjs +1 -0
- package/dist/plugins/minimap.d.ts +1 -1
- package/dist/plugins/minimap.esm.js +1 -0
- package/dist/plugins/minimap.js +1 -229
- package/dist/plugins/minimap.min.js +1 -0
- package/dist/plugins/record.cjs +1 -0
- package/dist/plugins/record.esm.js +1 -0
- package/dist/plugins/record.js +1 -319
- package/dist/plugins/record.min.js +1 -0
- package/dist/plugins/regions.cjs +1 -0
- package/dist/plugins/regions.esm.js +1 -0
- package/dist/plugins/regions.js +1 -699
- package/dist/plugins/regions.min.js +1 -0
- package/dist/plugins/spectrogram-windowed.cjs +1 -0
- package/dist/plugins/spectrogram-windowed.esm.js +1 -0
- package/dist/plugins/spectrogram-windowed.js +1 -967
- package/dist/plugins/spectrogram-windowed.min.js +1 -0
- package/dist/plugins/spectrogram.cjs +1 -0
- package/dist/plugins/spectrogram.esm.js +1 -0
- package/dist/plugins/spectrogram.min.js +1 -0
- package/dist/renderer.js +16 -1
- package/dist/wavesurfer.cjs +1 -1
- package/dist/wavesurfer.esm.js +1 -1
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +4 -2
package/dist/plugins/regions.js
CHANGED
|
@@ -1,699 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Regions are visual overlays on the waveform that can be used to mark segments of audio.
|
|
3
|
-
* Regions can be clicked on, dragged and resized.
|
|
4
|
-
* You can set the color and content of each region, as well as their HTML content.
|
|
5
|
-
*/
|
|
6
|
-
import BasePlugin from '../base-plugin.js';
|
|
7
|
-
import EventEmitter from '../event-emitter.js';
|
|
8
|
-
import createElement from '../dom.js';
|
|
9
|
-
import { createDragStream } from '../reactive/drag-stream.js';
|
|
10
|
-
import { effect } from '../reactive/store.js';
|
|
11
|
-
import { fromEvent, cleanup as cleanupStream } from '../reactive/event-streams.js';
|
|
12
|
-
class SingleRegion extends EventEmitter {
|
|
13
|
-
constructor(params, totalDuration, numberOfChannels = 0) {
|
|
14
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
15
|
-
super();
|
|
16
|
-
this.totalDuration = totalDuration;
|
|
17
|
-
this.numberOfChannels = numberOfChannels;
|
|
18
|
-
this.element = null; // Element is created on init
|
|
19
|
-
this.minLength = 0;
|
|
20
|
-
this.maxLength = Infinity;
|
|
21
|
-
this.contentEditable = false;
|
|
22
|
-
this.subscriptions = [];
|
|
23
|
-
this.updatingSide = undefined;
|
|
24
|
-
this.isRemoved = false;
|
|
25
|
-
this.subscriptions = [];
|
|
26
|
-
this.id = params.id || `region-${Math.random().toString(32).slice(2)}`;
|
|
27
|
-
this.start = this.clampPosition(params.start);
|
|
28
|
-
this.end = this.clampPosition((_a = params.end) !== null && _a !== void 0 ? _a : params.start);
|
|
29
|
-
this.drag = (_b = params.drag) !== null && _b !== void 0 ? _b : true;
|
|
30
|
-
this.resize = (_c = params.resize) !== null && _c !== void 0 ? _c : true;
|
|
31
|
-
this.resizeStart = (_d = params.resizeStart) !== null && _d !== void 0 ? _d : true;
|
|
32
|
-
this.resizeEnd = (_e = params.resizeEnd) !== null && _e !== void 0 ? _e : true;
|
|
33
|
-
this.color = (_f = params.color) !== null && _f !== void 0 ? _f : 'rgba(0, 0, 0, 0.1)';
|
|
34
|
-
this.minLength = (_g = params.minLength) !== null && _g !== void 0 ? _g : this.minLength;
|
|
35
|
-
this.maxLength = (_h = params.maxLength) !== null && _h !== void 0 ? _h : this.maxLength;
|
|
36
|
-
this.channelIdx = (_j = params.channelIdx) !== null && _j !== void 0 ? _j : -1;
|
|
37
|
-
this.contentEditable = (_k = params.contentEditable) !== null && _k !== void 0 ? _k : this.contentEditable;
|
|
38
|
-
this.element = this.initElement();
|
|
39
|
-
this.setContent(params.content);
|
|
40
|
-
this.setPart();
|
|
41
|
-
this.renderPosition();
|
|
42
|
-
this.initMouseEvents();
|
|
43
|
-
}
|
|
44
|
-
clampPosition(time) {
|
|
45
|
-
return Math.max(0, Math.min(this.totalDuration, time));
|
|
46
|
-
}
|
|
47
|
-
setPart() {
|
|
48
|
-
var _a;
|
|
49
|
-
const isMarker = this.start === this.end;
|
|
50
|
-
(_a = this.element) === null || _a === void 0 ? void 0 : _a.setAttribute('part', `${isMarker ? 'marker' : 'region'} ${this.id}`);
|
|
51
|
-
}
|
|
52
|
-
addResizeHandles(element) {
|
|
53
|
-
const handleStyle = {
|
|
54
|
-
position: 'absolute',
|
|
55
|
-
zIndex: '2',
|
|
56
|
-
width: '6px',
|
|
57
|
-
height: '100%',
|
|
58
|
-
top: '0',
|
|
59
|
-
cursor: 'ew-resize',
|
|
60
|
-
wordBreak: 'keep-all',
|
|
61
|
-
};
|
|
62
|
-
const leftHandle = createElement('div', {
|
|
63
|
-
part: 'region-handle region-handle-left',
|
|
64
|
-
style: Object.assign(Object.assign({}, handleStyle), { left: '0', borderLeft: '2px solid rgba(0, 0, 0, 0.5)', borderRadius: '2px 0 0 2px' }),
|
|
65
|
-
}, element);
|
|
66
|
-
const rightHandle = createElement('div', {
|
|
67
|
-
part: 'region-handle region-handle-right',
|
|
68
|
-
style: Object.assign(Object.assign({}, handleStyle), { right: '0', borderRight: '2px solid rgba(0, 0, 0, 0.5)', borderRadius: '0 2px 2px 0' }),
|
|
69
|
-
}, element);
|
|
70
|
-
// Resize
|
|
71
|
-
const resizeThreshold = 1;
|
|
72
|
-
const leftDragStream = createDragStream(leftHandle, { threshold: resizeThreshold });
|
|
73
|
-
const rightDragStream = createDragStream(rightHandle, { threshold: resizeThreshold });
|
|
74
|
-
const unsubscribeLeft = effect(() => {
|
|
75
|
-
const drag = leftDragStream.signal.value;
|
|
76
|
-
if (!drag)
|
|
77
|
-
return;
|
|
78
|
-
if (drag.type === 'move' && drag.deltaX !== undefined) {
|
|
79
|
-
this.onResize(drag.deltaX, 'start');
|
|
80
|
-
}
|
|
81
|
-
else if (drag.type === 'end') {
|
|
82
|
-
this.onEndResizing('start');
|
|
83
|
-
}
|
|
84
|
-
}, [leftDragStream.signal]);
|
|
85
|
-
const unsubscribeRight = effect(() => {
|
|
86
|
-
const drag = rightDragStream.signal.value;
|
|
87
|
-
if (!drag)
|
|
88
|
-
return;
|
|
89
|
-
if (drag.type === 'move' && drag.deltaX !== undefined) {
|
|
90
|
-
this.onResize(drag.deltaX, 'end');
|
|
91
|
-
}
|
|
92
|
-
else if (drag.type === 'end') {
|
|
93
|
-
this.onEndResizing('end');
|
|
94
|
-
}
|
|
95
|
-
}, [rightDragStream.signal]);
|
|
96
|
-
this.subscriptions.push(() => {
|
|
97
|
-
unsubscribeLeft();
|
|
98
|
-
unsubscribeRight();
|
|
99
|
-
leftDragStream.cleanup();
|
|
100
|
-
rightDragStream.cleanup();
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
removeResizeHandles(element) {
|
|
104
|
-
const leftHandle = element.querySelector('[part*="region-handle-left"]');
|
|
105
|
-
const rightHandle = element.querySelector('[part*="region-handle-right"]');
|
|
106
|
-
if (leftHandle) {
|
|
107
|
-
element.removeChild(leftHandle);
|
|
108
|
-
}
|
|
109
|
-
if (rightHandle) {
|
|
110
|
-
element.removeChild(rightHandle);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
initElement() {
|
|
114
|
-
if (this.isRemoved)
|
|
115
|
-
return null;
|
|
116
|
-
const isMarker = this.start === this.end;
|
|
117
|
-
let elementTop = 0;
|
|
118
|
-
let elementHeight = 100;
|
|
119
|
-
if (this.channelIdx >= 0 && this.numberOfChannels > 0 && this.channelIdx < this.numberOfChannels) {
|
|
120
|
-
elementHeight = 100 / this.numberOfChannels;
|
|
121
|
-
elementTop = elementHeight * this.channelIdx;
|
|
122
|
-
}
|
|
123
|
-
const element = createElement('div', {
|
|
124
|
-
style: {
|
|
125
|
-
position: 'absolute',
|
|
126
|
-
top: `${elementTop}%`,
|
|
127
|
-
height: `${elementHeight}%`,
|
|
128
|
-
backgroundColor: isMarker ? 'none' : this.color,
|
|
129
|
-
borderLeft: isMarker ? '2px solid ' + this.color : 'none',
|
|
130
|
-
borderRadius: '2px',
|
|
131
|
-
boxSizing: 'border-box',
|
|
132
|
-
transition: 'background-color 0.2s ease',
|
|
133
|
-
cursor: this.drag ? 'grab' : 'default',
|
|
134
|
-
pointerEvents: 'all',
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
// Add resize handles
|
|
138
|
-
if (!isMarker && this.resize) {
|
|
139
|
-
this.addResizeHandles(element);
|
|
140
|
-
}
|
|
141
|
-
return element;
|
|
142
|
-
}
|
|
143
|
-
renderPosition() {
|
|
144
|
-
if (!this.element)
|
|
145
|
-
return;
|
|
146
|
-
const start = this.start / this.totalDuration;
|
|
147
|
-
const end = (this.totalDuration - this.end) / this.totalDuration;
|
|
148
|
-
this.element.style.left = `${start * 100}%`;
|
|
149
|
-
this.element.style.right = `${end * 100}%`;
|
|
150
|
-
}
|
|
151
|
-
toggleCursor(toggle) {
|
|
152
|
-
var _a;
|
|
153
|
-
if (!this.drag || !((_a = this.element) === null || _a === void 0 ? void 0 : _a.style))
|
|
154
|
-
return;
|
|
155
|
-
this.element.style.cursor = toggle ? 'grabbing' : 'grab';
|
|
156
|
-
}
|
|
157
|
-
initMouseEvents() {
|
|
158
|
-
const { element } = this;
|
|
159
|
-
if (!element)
|
|
160
|
-
return;
|
|
161
|
-
// Create event streams
|
|
162
|
-
const clicks = fromEvent(element, 'click');
|
|
163
|
-
const mouseenters = fromEvent(element, 'mouseenter');
|
|
164
|
-
const mouseleaves = fromEvent(element, 'mouseleave');
|
|
165
|
-
const dblclicks = fromEvent(element, 'dblclick');
|
|
166
|
-
const pointerdowns = fromEvent(element, 'pointerdown');
|
|
167
|
-
const pointerups = fromEvent(element, 'pointerup');
|
|
168
|
-
// Subscribe to streams
|
|
169
|
-
const unsubscribeClick = clicks.subscribe((e) => e && this.emit('click', e));
|
|
170
|
-
const unsubscribeMouseenter = mouseenters.subscribe((e) => e && this.emit('over', e));
|
|
171
|
-
const unsubscribeMouseleave = mouseleaves.subscribe((e) => e && this.emit('leave', e));
|
|
172
|
-
const unsubscribeDblclick = dblclicks.subscribe((e) => e && this.emit('dblclick', e));
|
|
173
|
-
const unsubscribePointerdown = pointerdowns.subscribe((e) => e && this.toggleCursor(true));
|
|
174
|
-
const unsubscribePointerup = pointerups.subscribe((e) => e && this.toggleCursor(false));
|
|
175
|
-
// Store cleanup
|
|
176
|
-
this.subscriptions.push(() => {
|
|
177
|
-
unsubscribeClick();
|
|
178
|
-
unsubscribeMouseenter();
|
|
179
|
-
unsubscribeMouseleave();
|
|
180
|
-
unsubscribeDblclick();
|
|
181
|
-
unsubscribePointerdown();
|
|
182
|
-
unsubscribePointerup();
|
|
183
|
-
cleanupStream(clicks);
|
|
184
|
-
cleanupStream(mouseenters);
|
|
185
|
-
cleanupStream(mouseleaves);
|
|
186
|
-
cleanupStream(dblclicks);
|
|
187
|
-
cleanupStream(pointerdowns);
|
|
188
|
-
cleanupStream(pointerups);
|
|
189
|
-
});
|
|
190
|
-
// Drag
|
|
191
|
-
const dragStream = createDragStream(element);
|
|
192
|
-
const unsubscribeDrag = effect(() => {
|
|
193
|
-
const drag = dragStream.signal.value;
|
|
194
|
-
if (!drag)
|
|
195
|
-
return;
|
|
196
|
-
if (drag.type === 'start') {
|
|
197
|
-
this.toggleCursor(true);
|
|
198
|
-
}
|
|
199
|
-
else if (drag.type === 'move' && drag.deltaX !== undefined) {
|
|
200
|
-
this.onMove(drag.deltaX);
|
|
201
|
-
}
|
|
202
|
-
else if (drag.type === 'end') {
|
|
203
|
-
this.toggleCursor(false);
|
|
204
|
-
if (this.drag)
|
|
205
|
-
this.emit('update-end');
|
|
206
|
-
}
|
|
207
|
-
}, [dragStream.signal]);
|
|
208
|
-
this.subscriptions.push(() => {
|
|
209
|
-
unsubscribeDrag();
|
|
210
|
-
dragStream.cleanup();
|
|
211
|
-
});
|
|
212
|
-
if (this.contentEditable && this.content) {
|
|
213
|
-
this.contentClickListener = (e) => this.onContentClick(e);
|
|
214
|
-
this.contentBlurListener = () => this.onContentBlur();
|
|
215
|
-
this.content.addEventListener('click', this.contentClickListener);
|
|
216
|
-
this.content.addEventListener('blur', this.contentBlurListener);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
_onUpdate(dx, side, startTime) {
|
|
220
|
-
var _a;
|
|
221
|
-
if (!((_a = this.element) === null || _a === void 0 ? void 0 : _a.parentElement))
|
|
222
|
-
return;
|
|
223
|
-
const { width } = this.element.parentElement.getBoundingClientRect();
|
|
224
|
-
const deltaSeconds = (dx / width) * this.totalDuration;
|
|
225
|
-
let newStart = !side || side === 'start' ? this.start + deltaSeconds : this.start;
|
|
226
|
-
let newEnd = !side || side === 'end' ? this.end + deltaSeconds : this.end;
|
|
227
|
-
const isRegionCreating = startTime !== undefined; // startTime is passed when the region is being created.
|
|
228
|
-
if (isRegionCreating) {
|
|
229
|
-
if (this.updatingSide && this.updatingSide !== side) {
|
|
230
|
-
if (this.updatingSide === 'start') {
|
|
231
|
-
newStart = startTime;
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
newEnd = startTime;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
newStart = Math.max(0, newStart);
|
|
239
|
-
newEnd = Math.min(this.totalDuration, newEnd);
|
|
240
|
-
const length = newEnd - newStart;
|
|
241
|
-
this.updatingSide = side;
|
|
242
|
-
const resizeValid = length >= this.minLength && length <= this.maxLength;
|
|
243
|
-
if (newStart <= newEnd && (resizeValid || isRegionCreating)) {
|
|
244
|
-
this.start = newStart;
|
|
245
|
-
this.end = newEnd;
|
|
246
|
-
this.renderPosition();
|
|
247
|
-
this.emit('update', side);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
onMove(dx) {
|
|
251
|
-
if (!this.drag)
|
|
252
|
-
return;
|
|
253
|
-
this._onUpdate(dx);
|
|
254
|
-
}
|
|
255
|
-
onResize(dx, side) {
|
|
256
|
-
if (!this.resize)
|
|
257
|
-
return;
|
|
258
|
-
if (!this.resizeStart && side === 'start')
|
|
259
|
-
return;
|
|
260
|
-
if (!this.resizeEnd && side === 'end')
|
|
261
|
-
return;
|
|
262
|
-
this._onUpdate(dx, side);
|
|
263
|
-
}
|
|
264
|
-
onEndResizing(side) {
|
|
265
|
-
if (!this.resize)
|
|
266
|
-
return;
|
|
267
|
-
this.emit('update-end', side);
|
|
268
|
-
this.updatingSide = undefined;
|
|
269
|
-
}
|
|
270
|
-
onContentClick(event) {
|
|
271
|
-
event.stopPropagation();
|
|
272
|
-
const contentContainer = event.target;
|
|
273
|
-
contentContainer.focus();
|
|
274
|
-
this.emit('click', event);
|
|
275
|
-
}
|
|
276
|
-
onContentBlur() {
|
|
277
|
-
this.emit('update-end');
|
|
278
|
-
}
|
|
279
|
-
_setTotalDuration(totalDuration) {
|
|
280
|
-
this.totalDuration = totalDuration;
|
|
281
|
-
this.renderPosition();
|
|
282
|
-
}
|
|
283
|
-
/** Play the region from the start, pass `true` to stop at region end */
|
|
284
|
-
play(stopAtEnd) {
|
|
285
|
-
this.emit('play', stopAtEnd && this.end !== this.start ? this.end : undefined);
|
|
286
|
-
}
|
|
287
|
-
/** Get Content as html or string */
|
|
288
|
-
getContent(asHTML = false) {
|
|
289
|
-
var _a;
|
|
290
|
-
if (asHTML) {
|
|
291
|
-
return this.content || undefined;
|
|
292
|
-
}
|
|
293
|
-
if (this.element instanceof HTMLElement) {
|
|
294
|
-
return ((_a = this.content) === null || _a === void 0 ? void 0 : _a.innerHTML) || undefined;
|
|
295
|
-
}
|
|
296
|
-
return '';
|
|
297
|
-
}
|
|
298
|
-
/** Set the HTML content of the region */
|
|
299
|
-
setContent(content) {
|
|
300
|
-
var _a;
|
|
301
|
-
if (!this.element)
|
|
302
|
-
return;
|
|
303
|
-
// Remove event listeners from old content before removing it
|
|
304
|
-
if (this.content && this.contentEditable) {
|
|
305
|
-
if (this.contentClickListener) {
|
|
306
|
-
this.content.removeEventListener('click', this.contentClickListener);
|
|
307
|
-
}
|
|
308
|
-
if (this.contentBlurListener) {
|
|
309
|
-
this.content.removeEventListener('blur', this.contentBlurListener);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
(_a = this.content) === null || _a === void 0 ? void 0 : _a.remove();
|
|
313
|
-
if (!content) {
|
|
314
|
-
this.content = undefined;
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
if (typeof content === 'string') {
|
|
318
|
-
const isMarker = this.start === this.end;
|
|
319
|
-
this.content = createElement('div', {
|
|
320
|
-
style: {
|
|
321
|
-
padding: `0.2em ${isMarker ? 0.2 : 0.4}em`,
|
|
322
|
-
display: 'inline-block',
|
|
323
|
-
},
|
|
324
|
-
textContent: content,
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
328
|
-
this.content = content;
|
|
329
|
-
}
|
|
330
|
-
if (this.contentEditable) {
|
|
331
|
-
this.content.contentEditable = 'true';
|
|
332
|
-
// Re-add event listeners to new content
|
|
333
|
-
this.contentClickListener = (e) => this.onContentClick(e);
|
|
334
|
-
this.contentBlurListener = () => this.onContentBlur();
|
|
335
|
-
this.content.addEventListener('click', this.contentClickListener);
|
|
336
|
-
this.content.addEventListener('blur', this.contentBlurListener);
|
|
337
|
-
}
|
|
338
|
-
this.content.setAttribute('part', 'region-content');
|
|
339
|
-
this.element.appendChild(this.content);
|
|
340
|
-
this.emit('content-changed');
|
|
341
|
-
}
|
|
342
|
-
/** Update the region's options */
|
|
343
|
-
setOptions(options) {
|
|
344
|
-
var _a, _b;
|
|
345
|
-
if (!this.element)
|
|
346
|
-
return;
|
|
347
|
-
if (options.color) {
|
|
348
|
-
this.color = options.color;
|
|
349
|
-
this.element.style.backgroundColor = this.color;
|
|
350
|
-
}
|
|
351
|
-
if (options.drag !== undefined) {
|
|
352
|
-
this.drag = options.drag;
|
|
353
|
-
this.element.style.cursor = this.drag ? 'grab' : 'default';
|
|
354
|
-
}
|
|
355
|
-
if (options.start !== undefined || options.end !== undefined) {
|
|
356
|
-
const isMarker = this.start === this.end;
|
|
357
|
-
this.start = this.clampPosition((_a = options.start) !== null && _a !== void 0 ? _a : this.start);
|
|
358
|
-
this.end = this.clampPosition((_b = options.end) !== null && _b !== void 0 ? _b : (isMarker ? this.start : this.end));
|
|
359
|
-
this.renderPosition();
|
|
360
|
-
this.setPart();
|
|
361
|
-
this.emit('render');
|
|
362
|
-
}
|
|
363
|
-
if (options.content) {
|
|
364
|
-
this.setContent(options.content);
|
|
365
|
-
}
|
|
366
|
-
if (options.id) {
|
|
367
|
-
this.id = options.id;
|
|
368
|
-
this.setPart();
|
|
369
|
-
}
|
|
370
|
-
if (options.resize !== undefined && options.resize !== this.resize) {
|
|
371
|
-
const isMarker = this.start === this.end;
|
|
372
|
-
this.resize = options.resize;
|
|
373
|
-
if (this.resize && !isMarker) {
|
|
374
|
-
this.addResizeHandles(this.element);
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
this.removeResizeHandles(this.element);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
if (options.resizeStart !== undefined) {
|
|
381
|
-
this.resizeStart = options.resizeStart;
|
|
382
|
-
}
|
|
383
|
-
if (options.resizeEnd !== undefined) {
|
|
384
|
-
this.resizeEnd = options.resizeEnd;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
/** Remove the region */
|
|
388
|
-
remove() {
|
|
389
|
-
this.isRemoved = true;
|
|
390
|
-
this.emit('remove');
|
|
391
|
-
// Clean up all subscriptions (drag streams, event listeners, etc.)
|
|
392
|
-
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
393
|
-
this.subscriptions = [];
|
|
394
|
-
// Clean up content event listeners
|
|
395
|
-
if (this.content && this.contentEditable) {
|
|
396
|
-
if (this.contentClickListener) {
|
|
397
|
-
this.content.removeEventListener('click', this.contentClickListener);
|
|
398
|
-
this.contentClickListener = undefined;
|
|
399
|
-
}
|
|
400
|
-
if (this.contentBlurListener) {
|
|
401
|
-
this.content.removeEventListener('blur', this.contentBlurListener);
|
|
402
|
-
this.contentBlurListener = undefined;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
// Remove DOM element
|
|
406
|
-
if (this.element) {
|
|
407
|
-
this.element.remove();
|
|
408
|
-
this.element = null;
|
|
409
|
-
}
|
|
410
|
-
// Clear all event listeners from the EventEmitter
|
|
411
|
-
this.unAll();
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
class RegionsPlugin extends BasePlugin {
|
|
415
|
-
/** Create an instance of RegionsPlugin */
|
|
416
|
-
constructor(options) {
|
|
417
|
-
super(options);
|
|
418
|
-
this.regions = [];
|
|
419
|
-
this.regionsContainer = this.initRegionsContainer();
|
|
420
|
-
}
|
|
421
|
-
/** Create an instance of RegionsPlugin */
|
|
422
|
-
static create(options) {
|
|
423
|
-
return new RegionsPlugin(options);
|
|
424
|
-
}
|
|
425
|
-
/** Called by wavesurfer, don't call manually */
|
|
426
|
-
onInit() {
|
|
427
|
-
if (!this.wavesurfer) {
|
|
428
|
-
throw Error('WaveSurfer is not initialized');
|
|
429
|
-
}
|
|
430
|
-
this.wavesurfer.getWrapper().appendChild(this.regionsContainer);
|
|
431
|
-
// Update region durations when a new audio file is loaded
|
|
432
|
-
this.subscriptions.push(this.wavesurfer.on('ready', (duration) => {
|
|
433
|
-
this.regions.forEach((region) => region._setTotalDuration(duration));
|
|
434
|
-
}));
|
|
435
|
-
let activeRegions = [];
|
|
436
|
-
this.subscriptions.push(this.wavesurfer.on('timeupdate', (currentTime) => {
|
|
437
|
-
// Detect when regions are being played
|
|
438
|
-
const playedRegions = this.regions.filter((region) => region.start <= currentTime &&
|
|
439
|
-
(region.end === region.start ? region.start + 0.05 : region.end) >= currentTime);
|
|
440
|
-
// Trigger region-in when activeRegions doesn't include a played regions
|
|
441
|
-
playedRegions.forEach((region) => {
|
|
442
|
-
if (!activeRegions.includes(region)) {
|
|
443
|
-
this.emit('region-in', region);
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
// Trigger region-out when activeRegions include a un-played regions
|
|
447
|
-
activeRegions.forEach((region) => {
|
|
448
|
-
if (!playedRegions.includes(region)) {
|
|
449
|
-
this.emit('region-out', region);
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
// Update activeRegions only played regions
|
|
453
|
-
activeRegions = playedRegions;
|
|
454
|
-
}));
|
|
455
|
-
}
|
|
456
|
-
initRegionsContainer() {
|
|
457
|
-
return createElement('div', {
|
|
458
|
-
part: 'regions-container',
|
|
459
|
-
style: {
|
|
460
|
-
position: 'absolute',
|
|
461
|
-
top: '0',
|
|
462
|
-
left: '0',
|
|
463
|
-
width: '100%',
|
|
464
|
-
height: '100%',
|
|
465
|
-
zIndex: '5',
|
|
466
|
-
pointerEvents: 'none',
|
|
467
|
-
},
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
/** Get all created regions */
|
|
471
|
-
getRegions() {
|
|
472
|
-
return this.regions;
|
|
473
|
-
}
|
|
474
|
-
avoidOverlapping(region) {
|
|
475
|
-
if (!region.content || region.isRemoved)
|
|
476
|
-
return;
|
|
477
|
-
setTimeout(() => {
|
|
478
|
-
// Check that the label doesn't overlap with other labels
|
|
479
|
-
// If it does, push it down until it doesn't
|
|
480
|
-
// only check regions that are before us in the list -- otherwise
|
|
481
|
-
// both overlapping regions will try to move down away from each other.
|
|
482
|
-
const div = region.content;
|
|
483
|
-
const box = div.getBoundingClientRect();
|
|
484
|
-
const regionIndex = this.regions.indexOf(region);
|
|
485
|
-
const overlap = this.regions
|
|
486
|
-
.slice(0, regionIndex)
|
|
487
|
-
.filter((reg) => !reg.isRemoved)
|
|
488
|
-
.map((reg) => {
|
|
489
|
-
if (reg === region || !reg.content)
|
|
490
|
-
return 0;
|
|
491
|
-
const otherBox = reg.content.getBoundingClientRect();
|
|
492
|
-
if (box.left < otherBox.left + otherBox.width && otherBox.left < box.left + box.width) {
|
|
493
|
-
return otherBox.height + 2;
|
|
494
|
-
}
|
|
495
|
-
return 0;
|
|
496
|
-
})
|
|
497
|
-
.reduce((sum, val) => sum + val, 0);
|
|
498
|
-
div.style.marginTop = `${overlap}px`;
|
|
499
|
-
}, 10);
|
|
500
|
-
}
|
|
501
|
-
adjustScroll(region) {
|
|
502
|
-
var _a, _b;
|
|
503
|
-
if (!region.element)
|
|
504
|
-
return;
|
|
505
|
-
const scrollContainer = (_b = (_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getWrapper()) === null || _b === void 0 ? void 0 : _b.parentElement;
|
|
506
|
-
if (!scrollContainer)
|
|
507
|
-
return;
|
|
508
|
-
const { clientWidth, scrollWidth } = scrollContainer;
|
|
509
|
-
if (scrollWidth <= clientWidth)
|
|
510
|
-
return;
|
|
511
|
-
const scrollBbox = scrollContainer.getBoundingClientRect();
|
|
512
|
-
const bbox = region.element.getBoundingClientRect();
|
|
513
|
-
const left = bbox.left - scrollBbox.left;
|
|
514
|
-
const right = bbox.right - scrollBbox.left;
|
|
515
|
-
if (left < 0) {
|
|
516
|
-
scrollContainer.scrollLeft += left;
|
|
517
|
-
}
|
|
518
|
-
else if (right > clientWidth) {
|
|
519
|
-
scrollContainer.scrollLeft += right - clientWidth;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
virtualAppend(region, container, element) {
|
|
523
|
-
const renderIfVisible = () => {
|
|
524
|
-
if (!this.wavesurfer)
|
|
525
|
-
return;
|
|
526
|
-
const clientWidth = this.wavesurfer.getWidth();
|
|
527
|
-
const scrollLeft = this.wavesurfer.getScroll();
|
|
528
|
-
const scrollWidth = container.clientWidth;
|
|
529
|
-
const duration = this.wavesurfer.getDuration();
|
|
530
|
-
const start = Math.round((region.start / duration) * scrollWidth);
|
|
531
|
-
const width = Math.round(((region.end - region.start) / duration) * scrollWidth) || 1;
|
|
532
|
-
// Check if the region is between the scrollLeft and scrollLeft + clientWidth
|
|
533
|
-
const isVisible = start + width > scrollLeft && start < scrollLeft + clientWidth;
|
|
534
|
-
if (isVisible && !element.parentElement) {
|
|
535
|
-
container.appendChild(element);
|
|
536
|
-
}
|
|
537
|
-
else if (!isVisible && element.parentElement) {
|
|
538
|
-
element.remove();
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
setTimeout(() => {
|
|
542
|
-
// Check if region was removed before setTimeout executed
|
|
543
|
-
if (!this.wavesurfer || !region.element)
|
|
544
|
-
return;
|
|
545
|
-
renderIfVisible();
|
|
546
|
-
const unsubscribeScroll = this.wavesurfer.on('scroll', renderIfVisible);
|
|
547
|
-
const unsubscribeZoom = this.wavesurfer.on('zoom', renderIfVisible);
|
|
548
|
-
const unsubscribeResize = this.wavesurfer.on('resize', renderIfVisible);
|
|
549
|
-
const unsubscribeRender = region.on('render', renderIfVisible);
|
|
550
|
-
// Only push the unsubscribe functions, not the once() return values
|
|
551
|
-
this.subscriptions.push(unsubscribeScroll, unsubscribeZoom, unsubscribeResize, unsubscribeRender);
|
|
552
|
-
// Clean up subscriptions when region is removed
|
|
553
|
-
region.once('remove', () => {
|
|
554
|
-
unsubscribeScroll();
|
|
555
|
-
unsubscribeZoom();
|
|
556
|
-
unsubscribeResize();
|
|
557
|
-
unsubscribeRender();
|
|
558
|
-
});
|
|
559
|
-
}, 0);
|
|
560
|
-
}
|
|
561
|
-
saveRegion(region) {
|
|
562
|
-
if (!region.element)
|
|
563
|
-
return;
|
|
564
|
-
this.virtualAppend(region, this.regionsContainer, region.element);
|
|
565
|
-
this.avoidOverlapping(region);
|
|
566
|
-
this.regions.push(region);
|
|
567
|
-
const regionSubscriptions = [
|
|
568
|
-
region.on('update', (side) => {
|
|
569
|
-
// Undefined side indicates that we are dragging not resizing
|
|
570
|
-
if (!side) {
|
|
571
|
-
this.adjustScroll(region);
|
|
572
|
-
}
|
|
573
|
-
this.emit('region-update', region, side);
|
|
574
|
-
}),
|
|
575
|
-
region.on('update-end', (side) => {
|
|
576
|
-
this.avoidOverlapping(region);
|
|
577
|
-
this.emit('region-updated', region, side);
|
|
578
|
-
}),
|
|
579
|
-
region.on('play', (end) => {
|
|
580
|
-
var _a;
|
|
581
|
-
(_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.play(region.start, end);
|
|
582
|
-
}),
|
|
583
|
-
region.on('click', (e) => {
|
|
584
|
-
this.emit('region-clicked', region, e);
|
|
585
|
-
}),
|
|
586
|
-
region.on('dblclick', (e) => {
|
|
587
|
-
this.emit('region-double-clicked', region, e);
|
|
588
|
-
}),
|
|
589
|
-
region.on('content-changed', () => {
|
|
590
|
-
this.emit('region-content-changed', region);
|
|
591
|
-
}),
|
|
592
|
-
// Remove the region from the list when it's removed
|
|
593
|
-
region.once('remove', () => {
|
|
594
|
-
regionSubscriptions.forEach((unsubscribe) => unsubscribe());
|
|
595
|
-
this.regions = this.regions.filter((reg) => reg !== region);
|
|
596
|
-
this.emit('region-removed', region);
|
|
597
|
-
}),
|
|
598
|
-
];
|
|
599
|
-
this.subscriptions.push(...regionSubscriptions);
|
|
600
|
-
this.emit('region-created', region);
|
|
601
|
-
}
|
|
602
|
-
/** Create a region with given parameters */
|
|
603
|
-
addRegion(options) {
|
|
604
|
-
var _a, _b;
|
|
605
|
-
if (!this.wavesurfer) {
|
|
606
|
-
throw Error('WaveSurfer is not initialized');
|
|
607
|
-
}
|
|
608
|
-
const duration = this.wavesurfer.getDuration();
|
|
609
|
-
const numberOfChannels = (_b = (_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getDecodedData()) === null || _b === void 0 ? void 0 : _b.numberOfChannels;
|
|
610
|
-
const region = new SingleRegion(options, duration, numberOfChannels);
|
|
611
|
-
this.emit('region-initialized', region);
|
|
612
|
-
if (!duration) {
|
|
613
|
-
this.subscriptions.push(this.wavesurfer.once('ready', (duration) => {
|
|
614
|
-
region._setTotalDuration(duration);
|
|
615
|
-
this.saveRegion(region);
|
|
616
|
-
}));
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
this.saveRegion(region);
|
|
620
|
-
}
|
|
621
|
-
return region;
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Enable creation of regions by dragging on an empty space on the waveform.
|
|
625
|
-
* Returns a function to disable the drag selection.
|
|
626
|
-
*/
|
|
627
|
-
enableDragSelection(options, threshold = 3) {
|
|
628
|
-
var _a;
|
|
629
|
-
const wrapper = (_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getWrapper();
|
|
630
|
-
if (!wrapper || !(wrapper instanceof HTMLElement))
|
|
631
|
-
return () => undefined;
|
|
632
|
-
const initialSize = 5;
|
|
633
|
-
let region = null;
|
|
634
|
-
let startX = 0;
|
|
635
|
-
let startTime = 0;
|
|
636
|
-
const dragStream = createDragStream(wrapper, { threshold });
|
|
637
|
-
const unsubscribe = effect(() => {
|
|
638
|
-
var _a, _b;
|
|
639
|
-
const drag = dragStream.signal.value;
|
|
640
|
-
if (!drag)
|
|
641
|
-
return;
|
|
642
|
-
if (drag.type === 'start') {
|
|
643
|
-
// On drag start
|
|
644
|
-
startX = drag.x;
|
|
645
|
-
if (!this.wavesurfer)
|
|
646
|
-
return;
|
|
647
|
-
const duration = this.wavesurfer.getDuration();
|
|
648
|
-
const numberOfChannels = (_b = (_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getDecodedData()) === null || _b === void 0 ? void 0 : _b.numberOfChannels;
|
|
649
|
-
const { width } = this.wavesurfer.getWrapper().getBoundingClientRect();
|
|
650
|
-
startTime = (startX / width) * duration;
|
|
651
|
-
// Calculate the start time of the region
|
|
652
|
-
const start = (drag.x / width) * duration;
|
|
653
|
-
// Give the region a small initial size
|
|
654
|
-
const end = ((drag.x + initialSize) / width) * duration;
|
|
655
|
-
// Create a region but don't save it until the drag ends
|
|
656
|
-
region = new SingleRegion(Object.assign(Object.assign({}, options), { start,
|
|
657
|
-
end }), duration, numberOfChannels);
|
|
658
|
-
this.emit('region-initialized', region);
|
|
659
|
-
// Just add it to the DOM for now
|
|
660
|
-
if (region.element) {
|
|
661
|
-
this.regionsContainer.appendChild(region.element);
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
else if (drag.type === 'move' && drag.deltaX !== undefined) {
|
|
665
|
-
// On drag move
|
|
666
|
-
if (region) {
|
|
667
|
-
// Update the end position of the region
|
|
668
|
-
// If we're dragging to the left, we need to update the start instead
|
|
669
|
-
region._onUpdate(drag.deltaX, drag.x > startX ? 'end' : 'start', startTime);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
else if (drag.type === 'end') {
|
|
673
|
-
// On drag end
|
|
674
|
-
if (region) {
|
|
675
|
-
this.saveRegion(region);
|
|
676
|
-
region.updatingSide = undefined;
|
|
677
|
-
region = null;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}, [dragStream.signal]);
|
|
681
|
-
return () => {
|
|
682
|
-
unsubscribe();
|
|
683
|
-
dragStream.cleanup();
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
/** Remove all regions */
|
|
687
|
-
clearRegions() {
|
|
688
|
-
const regions = this.regions.slice();
|
|
689
|
-
regions.forEach((region) => region.remove());
|
|
690
|
-
this.regions = [];
|
|
691
|
-
}
|
|
692
|
-
/** Destroy the plugin and clean up */
|
|
693
|
-
destroy() {
|
|
694
|
-
this.clearRegions();
|
|
695
|
-
super.destroy();
|
|
696
|
-
this.regionsContainer.remove();
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
export default RegionsPlugin;
|
|
1
|
+
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=(...n)=>{this.un(t,i),e(...n)};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}}function i(t,e){const n=e.xmlns?document.createElementNS(e.xmlns,t):document.createElement(t);for(const[t,s]of Object.entries(e))if("children"===t&&s)for(const[t,e]of Object.entries(s))e instanceof Node?n.appendChild(e):"string"==typeof e?n.appendChild(document.createTextNode(e)):n.appendChild(i(t,e));else"style"===t?Object.assign(n.style,s):"textContent"===t?n.textContent=s:n.setAttribute(t,s.toString());return n}function n(t,e,n){const s=i(t,e||{});return null==n||n.appendChild(s),s}function s(t){let e=t;const i=new Set;return{get value(){return e},set(t){Object.is(e,t)||(e=t,i.forEach((t=>t(e))))},update(t){this.set(t(e))},subscribe:t=>(i.add(t),()=>i.delete(t))}}function r(t,e){let i;const n=()=>{i&&(i(),i=void 0),i=t()},s=e.map((t=>t.subscribe(n)));return n(),()=>{i&&(i(),i=void 0),s.forEach((t=>t()))}}function o(t,e){const i=s(null),n=t=>{i.set(t)};return t.addEventListener(e,n),i._cleanup=()=>{t.removeEventListener(e,n)},i}function l(t){const e=t._cleanup;"function"==typeof e&&e()}function h(t,e={}){const{threshold:i=3,mouseButton:n=0,touchDelay:r=100}=e,o=s(null),h=new Map,a=matchMedia("(pointer: coarse)").matches;let d=()=>{};const c=e=>{if(e.button!==n)return;if(h.set(e.pointerId,e),h.size>1)return;let s=e.clientX,l=e.clientY,c=!1;const u=Date.now(),v=t.getBoundingClientRect(),{left:p,top:g}=v,m=t=>{if(t.defaultPrevented||h.size>1)return;if(a&&Date.now()-u<r)return;const e=t.clientX,n=t.clientY,d=e-s,v=n-l;(c||Math.abs(d)>i||Math.abs(v)>i)&&(t.preventDefault(),t.stopPropagation(),c||(o.set({type:"start",x:s-p,y:l-g}),c=!0),o.set({type:"move",x:e-p,y:n-g,deltaX:d,deltaY:v}),s=e,l=n)},f=t=>{if(h.delete(t.pointerId),c){const e=t.clientX,i=t.clientY;o.set({type:"end",x:e-p,y:i-g})}d()},b=t=>{h.delete(t.pointerId),t.relatedTarget&&t.relatedTarget!==document.documentElement||f(t)},E=t=>{c&&(t.stopPropagation(),t.preventDefault())},C=t=>{t.defaultPrevented||h.size>1||c&&t.preventDefault()};document.addEventListener("pointermove",m),document.addEventListener("pointerup",f),document.addEventListener("pointerout",b),document.addEventListener("pointercancel",b),document.addEventListener("touchmove",C,{passive:!1}),document.addEventListener("click",E,{capture:!0}),d=()=>{document.removeEventListener("pointermove",m),document.removeEventListener("pointerup",f),document.removeEventListener("pointerout",b),document.removeEventListener("pointercancel",b),document.removeEventListener("touchmove",C),setTimeout((()=>{document.removeEventListener("click",E,{capture:!0})}),10)}};t.addEventListener("pointerdown",c);return{signal:o,cleanup:()=>{d(),t.removeEventListener("pointerdown",c),h.clear(),l(o)}}}class a extends t{constructor(t,e,i=0){var n,s,r,o,l,h,a,d,c,u;super(),this.totalDuration=e,this.numberOfChannels=i,this.element=null,this.minLength=0,this.maxLength=1/0,this.contentEditable=!1,this.subscriptions=[],this.updatingSide=void 0,this.isRemoved=!1,this.subscriptions=[],this.id=t.id||`region-${Math.random().toString(32).slice(2)}`,this.start=this.clampPosition(t.start),this.end=this.clampPosition(null!==(n=t.end)&&void 0!==n?n:t.start),this.drag=null===(s=t.drag)||void 0===s||s,this.resize=null===(r=t.resize)||void 0===r||r,this.resizeStart=null===(o=t.resizeStart)||void 0===o||o,this.resizeEnd=null===(l=t.resizeEnd)||void 0===l||l,this.color=null!==(h=t.color)&&void 0!==h?h:"rgba(0, 0, 0, 0.1)",this.minLength=null!==(a=t.minLength)&&void 0!==a?a:this.minLength,this.maxLength=null!==(d=t.maxLength)&&void 0!==d?d:this.maxLength,this.channelIdx=null!==(c=t.channelIdx)&&void 0!==c?c:-1,this.contentEditable=null!==(u=t.contentEditable)&&void 0!==u?u:this.contentEditable,this.element=this.initElement(),this.setContent(t.content),this.setPart(),this.renderPosition(),this.initMouseEvents()}clampPosition(t){return Math.max(0,Math.min(this.totalDuration,t))}setPart(){var t;const e=this.start===this.end;null===(t=this.element)||void 0===t||t.setAttribute("part",`${e?"marker":"region"} ${this.id}`)}addResizeHandles(t){const e={position:"absolute",zIndex:"2",width:"6px",height:"100%",top:"0",cursor:"ew-resize",wordBreak:"keep-all"},i=n("div",{part:"region-handle region-handle-left",style:Object.assign(Object.assign({},e),{left:"0",borderLeft:"2px solid rgba(0, 0, 0, 0.5)",borderRadius:"2px 0 0 2px"})},t),s=n("div",{part:"region-handle region-handle-right",style:Object.assign(Object.assign({},e),{right:"0",borderRight:"2px solid rgba(0, 0, 0, 0.5)",borderRadius:"0 2px 2px 0"})},t),o=h(i,{threshold:1}),l=h(s,{threshold:1}),a=r((()=>{const t=o.signal.value;t&&("move"===t.type&&void 0!==t.deltaX?this.onResize(t.deltaX,"start"):"end"===t.type&&this.onEndResizing("start"))}),[o.signal]),d=r((()=>{const t=l.signal.value;t&&("move"===t.type&&void 0!==t.deltaX?this.onResize(t.deltaX,"end"):"end"===t.type&&this.onEndResizing("end"))}),[l.signal]);this.subscriptions.push((()=>{a(),d(),o.cleanup(),l.cleanup()}))}removeResizeHandles(t){const e=t.querySelector('[part*="region-handle-left"]'),i=t.querySelector('[part*="region-handle-right"]');e&&t.removeChild(e),i&&t.removeChild(i)}initElement(){if(this.isRemoved)return null;const t=this.start===this.end;let e=0,i=100;this.channelIdx>=0&&this.numberOfChannels>0&&this.channelIdx<this.numberOfChannels&&(i=100/this.numberOfChannels,e=i*this.channelIdx);const s=n("div",{style:{position:"absolute",top:`${e}%`,height:`${i}%`,backgroundColor:t?"none":this.color,borderLeft:t?"2px solid "+this.color:"none",borderRadius:"2px",boxSizing:"border-box",transition:"background-color 0.2s ease",cursor:this.drag?"grab":"default",pointerEvents:"all"}});return!t&&this.resize&&this.addResizeHandles(s),s}renderPosition(){if(!this.element)return;const t=this.start/this.totalDuration,e=(this.totalDuration-this.end)/this.totalDuration;this.element.style.left=100*t+"%",this.element.style.right=100*e+"%"}toggleCursor(t){var e;this.drag&&(null===(e=this.element)||void 0===e?void 0:e.style)&&(this.element.style.cursor=t?"grabbing":"grab")}initMouseEvents(){const{element:t}=this;if(!t)return;const e=o(t,"click"),i=o(t,"mouseenter"),n=o(t,"mouseleave"),s=o(t,"dblclick"),a=o(t,"pointerdown"),d=o(t,"pointerup"),c=e.subscribe((t=>t&&this.emit("click",t))),u=i.subscribe((t=>t&&this.emit("over",t))),v=n.subscribe((t=>t&&this.emit("leave",t))),p=s.subscribe((t=>t&&this.emit("dblclick",t))),g=a.subscribe((t=>t&&this.toggleCursor(!0))),m=d.subscribe((t=>t&&this.toggleCursor(!1)));this.subscriptions.push((()=>{c(),u(),v(),p(),g(),m(),l(e),l(i),l(n),l(s),l(a),l(d)}));const f=h(t),b=r((()=>{const t=f.signal.value;t&&("start"===t.type?this.toggleCursor(!0):"move"===t.type&&void 0!==t.deltaX?this.onMove(t.deltaX):"end"===t.type&&(this.toggleCursor(!1),this.drag&&this.emit("update-end")))}),[f.signal]);this.subscriptions.push((()=>{b(),f.cleanup()})),this.contentEditable&&this.content&&(this.contentClickListener=t=>this.onContentClick(t),this.contentBlurListener=()=>this.onContentBlur(),this.content.addEventListener("click",this.contentClickListener),this.content.addEventListener("blur",this.contentBlurListener))}_onUpdate(t,e,i){var n;if(!(null===(n=this.element)||void 0===n?void 0:n.parentElement))return;const{width:s}=this.element.parentElement.getBoundingClientRect(),r=t/s*this.totalDuration;let o=e&&"start"!==e?this.start:this.start+r,l=e&&"end"!==e?this.end:this.end+r;const h=void 0!==i;h&&this.updatingSide&&this.updatingSide!==e&&("start"===this.updatingSide?o=i:l=i),o=Math.max(0,o),l=Math.min(this.totalDuration,l);const a=l-o;this.updatingSide=e;const d=a>=this.minLength&&a<=this.maxLength;o<=l&&(d||h)&&(this.start=o,this.end=l,this.renderPosition(),this.emit("update",e))}onMove(t){this.drag&&this._onUpdate(t)}onResize(t,e){this.resize&&(this.resizeStart||"start"!==e)&&(this.resizeEnd||"end"!==e)&&this._onUpdate(t,e)}onEndResizing(t){this.resize&&(this.emit("update-end",t),this.updatingSide=void 0)}onContentClick(t){t.stopPropagation();t.target.focus(),this.emit("click",t)}onContentBlur(){this.emit("update-end")}_setTotalDuration(t){this.totalDuration=t,this.renderPosition()}play(t){this.emit("play",t&&this.end!==this.start?this.end:void 0)}getContent(t=!1){var e;return t?this.content||void 0:this.element instanceof HTMLElement?(null===(e=this.content)||void 0===e?void 0:e.innerHTML)||void 0:""}setContent(t){var e;if(this.element)if(this.content&&this.contentEditable&&(this.contentClickListener&&this.content.removeEventListener("click",this.contentClickListener),this.contentBlurListener&&this.content.removeEventListener("blur",this.contentBlurListener)),null===(e=this.content)||void 0===e||e.remove(),t){if("string"==typeof t){const e=this.start===this.end;this.content=n("div",{style:{padding:`0.2em ${e?.2:.4}em`,display:"inline-block"},textContent:t})}else this.content=t;this.contentEditable&&(this.content.contentEditable="true",this.contentClickListener=t=>this.onContentClick(t),this.contentBlurListener=()=>this.onContentBlur(),this.content.addEventListener("click",this.contentClickListener),this.content.addEventListener("blur",this.contentBlurListener)),this.content.setAttribute("part","region-content"),this.element.appendChild(this.content),this.emit("content-changed")}else this.content=void 0}setOptions(t){var e,i;if(this.element){if(t.color&&(this.color=t.color,this.element.style.backgroundColor=this.color),void 0!==t.drag&&(this.drag=t.drag,this.element.style.cursor=this.drag?"grab":"default"),void 0!==t.start||void 0!==t.end){const n=this.start===this.end;this.start=this.clampPosition(null!==(e=t.start)&&void 0!==e?e:this.start),this.end=this.clampPosition(null!==(i=t.end)&&void 0!==i?i:n?this.start:this.end),this.renderPosition(),this.setPart(),this.emit("render")}if(t.content&&this.setContent(t.content),t.id&&(this.id=t.id,this.setPart()),void 0!==t.resize&&t.resize!==this.resize){const e=this.start===this.end;this.resize=t.resize,this.resize&&!e?this.addResizeHandles(this.element):this.removeResizeHandles(this.element)}void 0!==t.resizeStart&&(this.resizeStart=t.resizeStart),void 0!==t.resizeEnd&&(this.resizeEnd=t.resizeEnd)}}remove(){this.isRemoved=!0,this.emit("remove"),this.subscriptions.forEach((t=>t())),this.subscriptions=[],this.content&&this.contentEditable&&(this.contentClickListener&&(this.content.removeEventListener("click",this.contentClickListener),this.contentClickListener=void 0),this.contentBlurListener&&(this.content.removeEventListener("blur",this.contentBlurListener),this.contentBlurListener=void 0)),this.element&&(this.element.remove(),this.element=null),this.unAll()}}class d extends e{constructor(t){super(t),this.regions=[],this.regionsContainer=this.initRegionsContainer()}static create(t){return new d(t)}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.wavesurfer.getWrapper().appendChild(this.regionsContainer),this.subscriptions.push(this.wavesurfer.on("ready",(t=>{this.regions.forEach((e=>e._setTotalDuration(t)))})));let t=[];this.subscriptions.push(this.wavesurfer.on("timeupdate",(e=>{const i=this.regions.filter((t=>t.start<=e&&(t.end===t.start?t.start+.05:t.end)>=e));i.forEach((e=>{t.includes(e)||this.emit("region-in",e)})),t.forEach((t=>{i.includes(t)||this.emit("region-out",t)})),t=i})))}initRegionsContainer(){return n("div",{part:"regions-container",style:{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",zIndex:"5",pointerEvents:"none"}})}getRegions(){return this.regions}avoidOverlapping(t){t.content&&!t.isRemoved&&setTimeout((()=>{const e=t.content,i=e.getBoundingClientRect(),n=this.regions.indexOf(t),s=this.regions.slice(0,n).filter((t=>!t.isRemoved)).map((e=>{if(e===t||!e.content)return 0;const n=e.content.getBoundingClientRect();return i.left<n.left+n.width&&n.left<i.left+i.width?n.height+2:0})).reduce(((t,e)=>t+e),0);e.style.marginTop=`${s}px`}),10)}adjustScroll(t){var e,i;if(!t.element)return;const n=null===(i=null===(e=this.wavesurfer)||void 0===e?void 0:e.getWrapper())||void 0===i?void 0:i.parentElement;if(!n)return;const{clientWidth:s,scrollWidth:r}=n;if(r<=s)return;const o=n.getBoundingClientRect(),l=t.element.getBoundingClientRect(),h=l.left-o.left,a=l.right-o.left;h<0?n.scrollLeft+=h:a>s&&(n.scrollLeft+=a-s)}virtualAppend(t,e,i){const n=()=>{if(!this.wavesurfer)return;const n=this.wavesurfer.getWidth(),s=this.wavesurfer.getScroll(),r=e.clientWidth,o=this.wavesurfer.getDuration(),l=Math.round(t.start/o*r),h=l+(Math.round((t.end-t.start)/o*r)||1)>s&&l<s+n;h&&!i.parentElement?e.appendChild(i):!h&&i.parentElement&&i.remove()};setTimeout((()=>{if(!this.wavesurfer||!t.element)return;n();const e=this.wavesurfer.on("scroll",n),i=this.wavesurfer.on("zoom",n),s=this.wavesurfer.on("resize",n),r=t.on("render",n);this.subscriptions.push(e,i,s,r),t.once("remove",(()=>{e(),i(),s(),r()}))}),0)}saveRegion(t){if(!t.element)return;this.virtualAppend(t,this.regionsContainer,t.element),this.avoidOverlapping(t),this.regions.push(t);const e=[t.on("update",(e=>{e||this.adjustScroll(t),this.emit("region-update",t,e)})),t.on("update-end",(e=>{this.avoidOverlapping(t),this.emit("region-updated",t,e)})),t.on("play",(e=>{var i;null===(i=this.wavesurfer)||void 0===i||i.play(t.start,e)})),t.on("click",(e=>{this.emit("region-clicked",t,e)})),t.on("dblclick",(e=>{this.emit("region-double-clicked",t,e)})),t.on("content-changed",(()=>{this.emit("region-content-changed",t)})),t.once("remove",(()=>{e.forEach((t=>t())),this.regions=this.regions.filter((e=>e!==t)),this.emit("region-removed",t)}))];this.subscriptions.push(...e),this.emit("region-created",t)}addRegion(t){var e,i;if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const n=this.wavesurfer.getDuration(),s=null===(i=null===(e=this.wavesurfer)||void 0===e?void 0:e.getDecodedData())||void 0===i?void 0:i.numberOfChannels,r=new a(t,n,s);return this.emit("region-initialized",r),n?this.saveRegion(r):this.subscriptions.push(this.wavesurfer.once("ready",(t=>{r._setTotalDuration(t),this.saveRegion(r)}))),r}enableDragSelection(t,e=3){var i;const n=null===(i=this.wavesurfer)||void 0===i?void 0:i.getWrapper();if(!(n&&n instanceof HTMLElement))return()=>{};let s=null,o=0,l=0;const d=h(n,{threshold:e}),c=r((()=>{var e,i;const n=d.signal.value;if(n)if("start"===n.type){if(o=n.x,!this.wavesurfer)return;const r=this.wavesurfer.getDuration(),h=null===(i=null===(e=this.wavesurfer)||void 0===e?void 0:e.getDecodedData())||void 0===i?void 0:i.numberOfChannels,{width:d}=this.wavesurfer.getWrapper().getBoundingClientRect();l=o/d*r;const c=n.x/d*r,u=(n.x+5)/d*r;s=new a(Object.assign(Object.assign({},t),{start:c,end:u}),r,h),this.emit("region-initialized",s),s.element&&this.regionsContainer.appendChild(s.element)}else"move"===n.type&&void 0!==n.deltaX?s&&s._onUpdate(n.deltaX,n.x>o?"end":"start",l):"end"===n.type&&s&&(this.saveRegion(s),s.updatingSide=void 0,s=null)}),[d.signal]);return()=>{c(),d.cleanup()}}clearRegions(){this.regions.slice().forEach((t=>t.remove())),this.regions=[]}destroy(){this.clearRegions(),super.destroy(),this.regionsContainer.remove()}}export{d as default};
|