wavesurfer.js 7.0.0-alpha.30 → 7.0.0-alpha.32
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/player.js +1 -2
- package/dist/plugins/envelope.d.ts +4 -4
- package/dist/plugins/envelope.js +19 -19
- package/dist/plugins/envelope.min.js +1 -1
- package/dist/plugins/minimap.min.js +1 -1
- package/dist/plugins/multitrack.js +40 -30
- package/dist/plugins/multitrack.min.js +1 -1
- package/dist/plugins/regions.d.ts +62 -40
- package/dist/plugins/regions.js +275 -218
- package/dist/plugins/regions.min.js +1 -1
- package/dist/plugins/spectrogram.d.ts +24 -0
- package/dist/plugins/spectrogram.js +165 -0
- package/dist/renderer.d.ts +5 -2
- package/dist/renderer.js +25 -21
- package/dist/wavesurfer.d.ts +1 -0
- package/dist/wavesurfer.js +13 -2
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +4 -3
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
import BasePlugin from '../base-plugin.js';
|
|
2
|
+
import EventEmitter from '../event-emitter.js';
|
|
2
3
|
import type { WaveSurferPluginParams } from '../wavesurfer.js';
|
|
3
|
-
export type RegionsPluginOptions =
|
|
4
|
-
dragSelection?: boolean;
|
|
5
|
-
draggable?: boolean;
|
|
6
|
-
resizable?: boolean;
|
|
7
|
-
};
|
|
8
|
-
export type Region = {
|
|
9
|
-
startTime: number;
|
|
10
|
-
endTime: number;
|
|
11
|
-
title: string;
|
|
12
|
-
start: number;
|
|
13
|
-
end: number;
|
|
14
|
-
element: HTMLElement;
|
|
15
|
-
};
|
|
4
|
+
export type RegionsPluginOptions = undefined;
|
|
16
5
|
export type RegionsPluginEvents = {
|
|
17
6
|
'region-created': {
|
|
18
7
|
region: Region;
|
|
@@ -24,39 +13,72 @@ export type RegionsPluginEvents = {
|
|
|
24
13
|
region: Region;
|
|
25
14
|
};
|
|
26
15
|
};
|
|
16
|
+
export type RegionEvents = {
|
|
17
|
+
remove: void;
|
|
18
|
+
update: void;
|
|
19
|
+
'update-end': void;
|
|
20
|
+
play: void;
|
|
21
|
+
click: {
|
|
22
|
+
event: MouseEvent;
|
|
23
|
+
};
|
|
24
|
+
dblclick: {
|
|
25
|
+
event: MouseEvent;
|
|
26
|
+
};
|
|
27
|
+
over: {
|
|
28
|
+
event: MouseEvent;
|
|
29
|
+
};
|
|
30
|
+
leave: {
|
|
31
|
+
event: MouseEvent;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export type RegionParams = {
|
|
35
|
+
id?: string;
|
|
36
|
+
start: number;
|
|
37
|
+
end?: number;
|
|
38
|
+
drag?: boolean;
|
|
39
|
+
resize?: boolean;
|
|
40
|
+
color?: string;
|
|
41
|
+
content?: string | HTMLElement;
|
|
42
|
+
};
|
|
43
|
+
declare class Region extends EventEmitter<RegionEvents> {
|
|
44
|
+
private totalDuration;
|
|
45
|
+
element: HTMLElement;
|
|
46
|
+
id: string;
|
|
47
|
+
start: number;
|
|
48
|
+
end: number;
|
|
49
|
+
drag: boolean;
|
|
50
|
+
resize: boolean;
|
|
51
|
+
color: string;
|
|
52
|
+
content?: HTMLElement;
|
|
53
|
+
constructor(params: RegionParams, totalDuration: number);
|
|
54
|
+
private initElement;
|
|
55
|
+
private renderPosition;
|
|
56
|
+
private initMouseEvents;
|
|
57
|
+
private onStartMoving;
|
|
58
|
+
private onEndMoving;
|
|
59
|
+
private onUpdate;
|
|
60
|
+
private onMove;
|
|
61
|
+
private onResize;
|
|
62
|
+
private onEndResizing;
|
|
63
|
+
play(): void;
|
|
64
|
+
setTotalDuration(totalDuration: number): void;
|
|
65
|
+
remove(): void;
|
|
66
|
+
}
|
|
27
67
|
declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions> {
|
|
28
|
-
private dragStart;
|
|
29
|
-
private regionsContainer;
|
|
30
68
|
private regions;
|
|
31
|
-
private
|
|
32
|
-
private modifiedRegion;
|
|
33
|
-
private isResizingLeft;
|
|
34
|
-
private isMoving;
|
|
35
|
-
private wasInteractive;
|
|
69
|
+
private regionsContainer;
|
|
36
70
|
/** Create an instance of RegionsPlugin */
|
|
37
71
|
constructor(options?: RegionsPluginOptions);
|
|
38
72
|
static create(options?: RegionsPluginOptions): RegionsPlugin;
|
|
39
73
|
init(params: WaveSurferPluginParams): void;
|
|
40
|
-
/** Set options */
|
|
41
|
-
setOptions(options: RegionsPluginOptions): void;
|
|
42
|
-
/** Unmount */
|
|
43
|
-
destroy(): void;
|
|
44
74
|
private initRegionsContainer;
|
|
45
|
-
|
|
46
|
-
private
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
/** Create a region at a given start and end time, with an optional title */
|
|
54
|
-
add(startTime: number, endTime: number, title?: string, color?: string): Region;
|
|
55
|
-
/** Remove a region */
|
|
56
|
-
remove(region: Region): void;
|
|
57
|
-
/** Set the background color of a region */
|
|
58
|
-
setRegionColor(region: Region, color: string): void;
|
|
59
|
-
/** Disable or enable mouse events for this region */
|
|
60
|
-
setRegionInteractive(region: Region, isInteractive: boolean): void;
|
|
75
|
+
getRegions(): Region[];
|
|
76
|
+
private avoidOverlapping;
|
|
77
|
+
private saveRegion;
|
|
78
|
+
addRegion(options: RegionParams): Region;
|
|
79
|
+
add(start: number, end: number, content?: string, color?: string): Region;
|
|
80
|
+
enableDragSelection(options: RegionParams): void;
|
|
81
|
+
clearRegions(): void;
|
|
82
|
+
destroy(): void;
|
|
61
83
|
}
|
|
62
84
|
export default RegionsPlugin;
|
package/dist/plugins/regions.js
CHANGED
|
@@ -1,33 +1,192 @@
|
|
|
1
1
|
import BasePlugin from '../base-plugin.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import EventEmitter from '../event-emitter.js';
|
|
3
|
+
function makeDraggable(element, onStart, onMove, onEnd) {
|
|
4
|
+
if (!element)
|
|
5
|
+
return;
|
|
6
|
+
let preventClickPropagation = false;
|
|
7
|
+
element.addEventListener('click', (event) => {
|
|
8
|
+
preventClickPropagation && event.stopPropagation();
|
|
9
|
+
});
|
|
10
|
+
element.addEventListener('mousedown', (e) => {
|
|
11
|
+
e.stopPropagation();
|
|
12
|
+
let x = e.clientX;
|
|
13
|
+
onStart(x);
|
|
14
|
+
const onMouseMove = (e) => {
|
|
15
|
+
const newX = e.clientX;
|
|
16
|
+
const dx = newX - x;
|
|
17
|
+
x = newX;
|
|
18
|
+
preventClickPropagation = true;
|
|
19
|
+
onMove(dx);
|
|
20
|
+
};
|
|
21
|
+
const onMouseUp = () => {
|
|
22
|
+
onEnd();
|
|
23
|
+
setTimeout(() => (preventClickPropagation = false), 10);
|
|
24
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
25
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
26
|
+
};
|
|
27
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
28
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
class Region extends EventEmitter {
|
|
32
|
+
totalDuration;
|
|
33
|
+
element;
|
|
34
|
+
id;
|
|
35
|
+
start;
|
|
36
|
+
end;
|
|
37
|
+
drag;
|
|
38
|
+
resize;
|
|
39
|
+
color;
|
|
40
|
+
content;
|
|
41
|
+
constructor(params, totalDuration) {
|
|
42
|
+
super();
|
|
43
|
+
this.totalDuration = totalDuration;
|
|
44
|
+
this.id = params.id || Math.random().toString(32).slice(2);
|
|
45
|
+
this.start = params.start;
|
|
46
|
+
this.end = params.end ?? params.start;
|
|
47
|
+
this.drag = params.drag ?? true;
|
|
48
|
+
this.resize = params.resize ?? true;
|
|
49
|
+
this.color = params.color ?? 'rgba(0, 0, 0, 0.1)';
|
|
50
|
+
this.element = this.initElement(params.content);
|
|
51
|
+
this.renderPosition();
|
|
52
|
+
this.initMouseEvents();
|
|
53
|
+
}
|
|
54
|
+
initElement(content) {
|
|
55
|
+
const element = document.createElement('div');
|
|
56
|
+
const isMarker = this.start === this.end;
|
|
57
|
+
element.id = this.id;
|
|
58
|
+
element.setAttribute('style', `
|
|
59
|
+
position: absolute;
|
|
60
|
+
height: 100%;
|
|
61
|
+
background-color: ${isMarker ? 'none' : this.color};
|
|
62
|
+
border-left: ${isMarker ? '2px solid ' + this.color : 'none'};
|
|
63
|
+
border-radius: 2px;
|
|
64
|
+
box-sizing: border-box;
|
|
65
|
+
transition: background-color 0.2s ease;
|
|
66
|
+
cursor: ${this.drag ? 'grab' : 'default'};
|
|
67
|
+
pointer-events: all;
|
|
68
|
+
padding: 0.2em ${isMarker ? 0.2 : 0.4}em;
|
|
69
|
+
pointer-events: all;
|
|
70
|
+
`);
|
|
71
|
+
// Init content
|
|
72
|
+
if (content) {
|
|
73
|
+
if (typeof content === 'string') {
|
|
74
|
+
this.content = document.createElement('div');
|
|
75
|
+
this.content.textContent = content;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.content = content;
|
|
79
|
+
}
|
|
80
|
+
element.appendChild(this.content);
|
|
81
|
+
}
|
|
82
|
+
// Add resize handles
|
|
83
|
+
if (!isMarker) {
|
|
84
|
+
const leftHandle = document.createElement('div');
|
|
85
|
+
leftHandle.setAttribute('data-resize', 'left');
|
|
86
|
+
leftHandle.setAttribute('style', `
|
|
87
|
+
position: absolute;
|
|
88
|
+
z-index: 2;
|
|
89
|
+
width: 6px;
|
|
90
|
+
height: 100%;
|
|
91
|
+
top: 0;
|
|
92
|
+
left: 0;
|
|
93
|
+
border-left: 2px solid rgba(0, 0, 0, 0.5);
|
|
94
|
+
border-radius: 2px 0 0 2px;
|
|
95
|
+
cursor: ${this.resize ? 'ew-resize' : 'default'};
|
|
96
|
+
word-break: keep-all;
|
|
97
|
+
`);
|
|
98
|
+
const rightHandle = leftHandle.cloneNode();
|
|
99
|
+
rightHandle.setAttribute('data-resize', 'right');
|
|
100
|
+
rightHandle.style.left = '';
|
|
101
|
+
rightHandle.style.right = '0';
|
|
102
|
+
rightHandle.style.borderRight = rightHandle.style.borderLeft;
|
|
103
|
+
rightHandle.style.borderLeft = '';
|
|
104
|
+
rightHandle.style.borderRadius = '0 2px 2px 0';
|
|
105
|
+
element.appendChild(leftHandle);
|
|
106
|
+
element.appendChild(rightHandle);
|
|
107
|
+
}
|
|
108
|
+
return element;
|
|
109
|
+
}
|
|
110
|
+
renderPosition() {
|
|
111
|
+
const start = this.start / this.totalDuration;
|
|
112
|
+
const end = this.end / this.totalDuration;
|
|
113
|
+
this.element.style.left = `${start * 100}%`;
|
|
114
|
+
this.element.style.width = `${(end - start) * 100}%`;
|
|
115
|
+
}
|
|
116
|
+
initMouseEvents() {
|
|
117
|
+
const { element } = this;
|
|
118
|
+
element.addEventListener('mouseenter', (event) => this.emit('over', { event }));
|
|
119
|
+
element.addEventListener('mouseleave', (event) => this.emit('leave', { event }));
|
|
120
|
+
element.addEventListener('click', (event) => this.emit('click', { event }));
|
|
121
|
+
element.addEventListener('dblclick', (event) => this.emit('dblclick', { event }));
|
|
122
|
+
// Drag
|
|
123
|
+
makeDraggable(element, () => this.onStartMoving(), (dx) => this.onMove(dx), () => this.onEndMoving());
|
|
124
|
+
// Resize
|
|
125
|
+
makeDraggable(element.querySelector('[data-resize="left"]'), () => null, (dx) => this.onResize(dx, 'start'), () => this.onEndResizing());
|
|
126
|
+
makeDraggable(element.querySelector('[data-resize="right"]'), () => null, (dx) => this.onResize(dx, 'end'), () => this.onEndResizing());
|
|
11
127
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
128
|
+
onStartMoving() {
|
|
129
|
+
if (!this.drag)
|
|
130
|
+
return;
|
|
131
|
+
this.element.style.cursor = 'grabbing';
|
|
132
|
+
}
|
|
133
|
+
onEndMoving() {
|
|
134
|
+
if (!this.drag)
|
|
135
|
+
return;
|
|
136
|
+
this.element.style.cursor = 'grab';
|
|
137
|
+
this.emit('update-end');
|
|
138
|
+
}
|
|
139
|
+
onUpdate(dx, sides) {
|
|
140
|
+
if (!this.element.parentElement)
|
|
141
|
+
return;
|
|
142
|
+
const deltaSeconds = (dx / this.element.parentElement.clientWidth) * this.totalDuration;
|
|
143
|
+
sides.forEach((side) => {
|
|
144
|
+
this[side] += deltaSeconds;
|
|
145
|
+
if (side === 'start') {
|
|
146
|
+
this.start = Math.max(0, Math.min(this.start, this.end));
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
this.end = Math.max(this.start, Math.min(this.end, this.totalDuration));
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
this.renderPosition();
|
|
153
|
+
this.emit('update');
|
|
154
|
+
}
|
|
155
|
+
onMove(dx) {
|
|
156
|
+
if (!this.drag)
|
|
157
|
+
return;
|
|
158
|
+
this.onUpdate(dx, ['start', 'end']);
|
|
159
|
+
}
|
|
160
|
+
onResize(dx, side) {
|
|
161
|
+
if (!this.resize)
|
|
162
|
+
return;
|
|
163
|
+
this.onUpdate(dx, [side]);
|
|
164
|
+
}
|
|
165
|
+
onEndResizing() {
|
|
166
|
+
if (!this.resize)
|
|
167
|
+
return;
|
|
168
|
+
this.emit('update-end');
|
|
169
|
+
}
|
|
170
|
+
// Play the region from start to end
|
|
171
|
+
play() {
|
|
172
|
+
this.emit('play');
|
|
173
|
+
}
|
|
174
|
+
setTotalDuration(totalDuration) {
|
|
175
|
+
this.totalDuration = totalDuration;
|
|
176
|
+
this.renderPosition();
|
|
177
|
+
}
|
|
178
|
+
// Remove the region
|
|
179
|
+
remove() {
|
|
180
|
+
this.emit('remove');
|
|
181
|
+
this.element.remove();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
18
184
|
class RegionsPlugin extends BasePlugin {
|
|
19
|
-
dragStart = NaN;
|
|
20
|
-
regionsContainer;
|
|
21
185
|
regions = [];
|
|
22
|
-
|
|
23
|
-
modifiedRegion = null;
|
|
24
|
-
isResizingLeft = false;
|
|
25
|
-
isMoving = false;
|
|
26
|
-
wasInteractive = true;
|
|
186
|
+
regionsContainer;
|
|
27
187
|
/** Create an instance of RegionsPlugin */
|
|
28
188
|
constructor(options) {
|
|
29
|
-
super(options
|
|
30
|
-
this.options = Object.assign({}, defaultOptions, options);
|
|
189
|
+
super(options);
|
|
31
190
|
this.regionsContainer = this.initRegionsContainer();
|
|
32
191
|
}
|
|
33
192
|
static create(options) {
|
|
@@ -38,222 +197,120 @@ class RegionsPlugin extends BasePlugin {
|
|
|
38
197
|
if (!this.wavesurfer || !this.wrapper) {
|
|
39
198
|
throw Error('WaveSurfer is not initialized');
|
|
40
199
|
}
|
|
41
|
-
this.wrapper
|
|
42
|
-
this.wrapper.addEventListener('mousedown', this.handleMouseDown);
|
|
43
|
-
document.addEventListener('mousemove', this.handleMouseMove);
|
|
44
|
-
document.addEventListener('mouseup', this.handleMouseUp);
|
|
45
|
-
}
|
|
46
|
-
/** Set options */
|
|
47
|
-
setOptions(options) {
|
|
48
|
-
this.options = { ...this.options, ...options };
|
|
49
|
-
}
|
|
50
|
-
/** Unmount */
|
|
51
|
-
destroy() {
|
|
52
|
-
this.wrapper?.removeEventListener('mousedown', this.handleMouseDown);
|
|
53
|
-
document.removeEventListener('mousemove', this.handleMouseMove);
|
|
54
|
-
document.removeEventListener('mouseup', this.handleMouseUp, true);
|
|
55
|
-
this.regionsContainer.remove();
|
|
56
|
-
super.destroy();
|
|
200
|
+
this.wrapper.appendChild(this.regionsContainer);
|
|
57
201
|
}
|
|
58
202
|
initRegionsContainer() {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
203
|
+
const div = document.createElement('div');
|
|
204
|
+
div.setAttribute('style', `
|
|
205
|
+
position: absolute;
|
|
206
|
+
top: 0;
|
|
207
|
+
left: 0;
|
|
208
|
+
width: 100%;
|
|
209
|
+
height: 100%;
|
|
210
|
+
z-index: 3;
|
|
211
|
+
pointer-events: none;
|
|
212
|
+
`);
|
|
213
|
+
return div;
|
|
68
214
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
handleMouseMove = (e) => {
|
|
77
|
-
if (!this.wrapper)
|
|
78
|
-
return;
|
|
79
|
-
const box = this.wrapper.getBoundingClientRect();
|
|
80
|
-
const { width } = box;
|
|
81
|
-
const dragEnd = e.clientX - box.left;
|
|
82
|
-
if (this.options.draggable && this.modifiedRegion && this.isMoving) {
|
|
83
|
-
this.moveRegion(this.modifiedRegion, (dragEnd - this.dragStart) / width);
|
|
84
|
-
this.dragStart = dragEnd;
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (this.options.resizable && this.modifiedRegion) {
|
|
88
|
-
this.updateRegion(this.modifiedRegion, this.isResizingLeft ? dragEnd / width : undefined, this.isResizingLeft ? undefined : dragEnd / width);
|
|
215
|
+
getRegions() {
|
|
216
|
+
return this.regions;
|
|
217
|
+
}
|
|
218
|
+
avoidOverlapping(region) {
|
|
219
|
+
if (!region.content)
|
|
89
220
|
return;
|
|
90
|
-
}
|
|
91
|
-
if (this.options.dragSelection && !isNaN(this.dragStart)) {
|
|
92
|
-
const dragEnd = e.clientX - this.regionsContainer.getBoundingClientRect().left;
|
|
93
|
-
if (dragEnd - this.dragStart >= MIN_WIDTH) {
|
|
94
|
-
if (!this.createdRegion) {
|
|
95
|
-
this.wasInteractive = this.wavesurfer?.options.interact || true;
|
|
96
|
-
this.wavesurfer?.toggleInteraction(false);
|
|
97
|
-
this.createdRegion = this.createRegion(this.dragStart / width, dragEnd / width);
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
this.updateRegion(this.createdRegion, this.dragStart / width, dragEnd / width);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
handleMouseUp = () => {
|
|
106
|
-
if (this.createdRegion) {
|
|
107
|
-
this.addRegion(this.createdRegion);
|
|
108
|
-
this.createdRegion = null;
|
|
109
|
-
}
|
|
110
|
-
this.modifiedRegion = null;
|
|
111
|
-
this.isMoving = false;
|
|
112
|
-
this.dragStart = NaN;
|
|
113
|
-
this.wavesurfer?.toggleInteraction(this.wasInteractive);
|
|
114
|
-
};
|
|
115
|
-
createRegionElement(start, end, title = '') {
|
|
116
|
-
const noWidth = start === end;
|
|
117
|
-
const div = el('div', {
|
|
118
|
-
position: 'absolute',
|
|
119
|
-
left: `${start * 100}%`,
|
|
120
|
-
width: `${(end - start) * 100}%`,
|
|
121
|
-
height: '100%',
|
|
122
|
-
backgroundColor: noWidth ? '' : 'rgba(0, 0, 0, 0.1)',
|
|
123
|
-
borderRadius: '2px',
|
|
124
|
-
boxSizing: 'border-box',
|
|
125
|
-
borderLeft: noWidth ? '2px solid rgba(0, 0, 0, 0.5)' : '',
|
|
126
|
-
transition: 'background-color 0.2s ease',
|
|
127
|
-
cursor: this.options.draggable ? 'move' : '',
|
|
128
|
-
pointerEvents: 'all',
|
|
129
|
-
whiteSpace: noWidth ? 'nowrap' : '',
|
|
130
|
-
padding: '0.2em',
|
|
131
|
-
});
|
|
132
|
-
div.textContent = title;
|
|
133
|
-
const leftHandle = el('div', {
|
|
134
|
-
position: 'absolute',
|
|
135
|
-
left: '0',
|
|
136
|
-
top: '0',
|
|
137
|
-
width: '6px',
|
|
138
|
-
height: '100%',
|
|
139
|
-
cursor: this.options.resizable ? 'ew-resize' : '',
|
|
140
|
-
pointerEvents: 'all',
|
|
141
|
-
borderLeft: noWidth ? '' : '2px solid rgba(0, 0, 0, 0.5)',
|
|
142
|
-
borderRadius: '2px 0 0 2px',
|
|
143
|
-
});
|
|
144
|
-
div.appendChild(leftHandle);
|
|
145
|
-
const rightHandle = leftHandle.cloneNode();
|
|
146
|
-
style(rightHandle, {
|
|
147
|
-
left: '',
|
|
148
|
-
right: '0',
|
|
149
|
-
borderLeft: '',
|
|
150
|
-
borderRight: noWidth ? '' : '2px solid rgba(0, 0, 0, 0.5)',
|
|
151
|
-
borderRadius: '0 2px 2px 0',
|
|
152
|
-
});
|
|
153
|
-
div.appendChild(rightHandle);
|
|
154
|
-
leftHandle.addEventListener('mousedown', (e) => {
|
|
155
|
-
if (!this.options.resizable)
|
|
156
|
-
return;
|
|
157
|
-
e.stopPropagation();
|
|
158
|
-
this.modifiedRegion = this.regions.find((r) => r.element === div) || null;
|
|
159
|
-
this.isResizingLeft = true;
|
|
160
|
-
this.isMoving = false;
|
|
161
|
-
});
|
|
162
|
-
rightHandle.addEventListener('mousedown', (e) => {
|
|
163
|
-
if (!this.options.resizable)
|
|
164
|
-
return;
|
|
165
|
-
e.stopPropagation();
|
|
166
|
-
this.modifiedRegion = this.regions.find((r) => r.element === div) || null;
|
|
167
|
-
this.isResizingLeft = false;
|
|
168
|
-
this.isMoving = false;
|
|
169
|
-
});
|
|
170
|
-
div.addEventListener('mousedown', () => {
|
|
171
|
-
if (!this.options.draggable)
|
|
172
|
-
return;
|
|
173
|
-
this.modifiedRegion = this.regions.find((r) => r.element === div) || null;
|
|
174
|
-
this.isMoving = this.modifiedRegion !== null;
|
|
175
|
-
});
|
|
176
|
-
div.addEventListener('click', () => {
|
|
177
|
-
const region = this.regions.find((r) => r.element === div);
|
|
178
|
-
if (region) {
|
|
179
|
-
this.emit('region-clicked', { region });
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
this.regionsContainer.appendChild(div);
|
|
183
221
|
// Check that the label doesn't overlap with other labels
|
|
184
|
-
// If it does, push it down
|
|
222
|
+
// If it does, push it down until it doesn't
|
|
223
|
+
const div = region.content;
|
|
185
224
|
const labelLeft = div.getBoundingClientRect().left;
|
|
186
|
-
const labelWidth =
|
|
225
|
+
const labelWidth = region.element.scrollWidth;
|
|
187
226
|
const overlap = this.regions
|
|
188
227
|
.filter((reg) => {
|
|
189
|
-
|
|
228
|
+
if (reg === region || !reg.content)
|
|
229
|
+
return false;
|
|
230
|
+
const left = reg.content.getBoundingClientRect().left;
|
|
190
231
|
const width = reg.element.scrollWidth;
|
|
191
232
|
return labelLeft < left + width && left < labelLeft + labelWidth;
|
|
192
233
|
})
|
|
193
|
-
.map((reg) =>
|
|
234
|
+
.map((reg) => reg.content?.getBoundingClientRect().height || 0)
|
|
194
235
|
.reduce((sum, val) => sum + val, 0);
|
|
195
|
-
|
|
196
|
-
div.style.paddingTop = `${overlap + 1}em`;
|
|
197
|
-
}
|
|
198
|
-
return div;
|
|
199
|
-
}
|
|
200
|
-
createRegion(start, end, title = '') {
|
|
201
|
-
const duration = this.wavesurfer?.getDuration() || 0;
|
|
202
|
-
return {
|
|
203
|
-
element: this.createRegionElement(start, end, title),
|
|
204
|
-
start,
|
|
205
|
-
end,
|
|
206
|
-
startTime: start * duration,
|
|
207
|
-
endTime: end * duration,
|
|
208
|
-
title,
|
|
209
|
-
};
|
|
236
|
+
div.style.marginTop = `${overlap}px`;
|
|
210
237
|
}
|
|
211
|
-
|
|
238
|
+
saveRegion(region) {
|
|
239
|
+
this.regionsContainer.appendChild(region.element);
|
|
240
|
+
this.avoidOverlapping(region);
|
|
212
241
|
this.regions.push(region);
|
|
213
242
|
this.emit('region-created', { region });
|
|
243
|
+
this.subscriptions.push(region.on('update-end', () => {
|
|
244
|
+
this.avoidOverlapping(region);
|
|
245
|
+
this.emit('region-updated', { region });
|
|
246
|
+
}), region.on('play', () => {
|
|
247
|
+
this.wavesurfer?.play();
|
|
248
|
+
this.wavesurfer?.setTime(region.start);
|
|
249
|
+
}), region.on('click', () => {
|
|
250
|
+
this.emit('region-clicked', { region });
|
|
251
|
+
}));
|
|
214
252
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
region.start = start;
|
|
219
|
-
region.element.style.left = `${region.start * 100}%`;
|
|
220
|
-
region.element.style.width = `${(region.end - region.start) * 100}%`;
|
|
221
|
-
region.startTime = start * duration;
|
|
253
|
+
addRegion(options) {
|
|
254
|
+
if (!this.wavesurfer) {
|
|
255
|
+
throw Error('WaveSurfer is not initialized');
|
|
222
256
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
257
|
+
const duration = this.wavesurfer.getDuration();
|
|
258
|
+
const region = new Region(options, duration);
|
|
259
|
+
if (!duration) {
|
|
260
|
+
this.subscriptions.push(this.wavesurfer.once('canplay', ({ duration }) => {
|
|
261
|
+
region.setTotalDuration(duration);
|
|
262
|
+
this.saveRegion(region);
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
this.saveRegion(region);
|
|
227
267
|
}
|
|
228
|
-
this.emit('region-updated', { region });
|
|
229
|
-
}
|
|
230
|
-
moveRegion(region, delta) {
|
|
231
|
-
this.updateRegion(region, region.start + delta, region.end + delta);
|
|
232
|
-
}
|
|
233
|
-
/** Create a region at a given start and end time, with an optional title */
|
|
234
|
-
add(startTime, endTime, title = '', color = '') {
|
|
235
|
-
const duration = this.wavesurfer?.getDuration() || 0;
|
|
236
|
-
const start = startTime / duration;
|
|
237
|
-
const end = endTime / duration;
|
|
238
|
-
const region = this.createRegion(start, end, title);
|
|
239
|
-
this.addRegion(region);
|
|
240
|
-
if (color)
|
|
241
|
-
this.setRegionColor(region, color);
|
|
242
268
|
return region;
|
|
243
269
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
region.element = null;
|
|
248
|
-
this.regions = this.regions.filter((r) => r !== region);
|
|
270
|
+
// The same as addRegion but with spread params
|
|
271
|
+
add(start, end, content, color) {
|
|
272
|
+
return this.addRegion({ start, end, content, color });
|
|
249
273
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
274
|
+
enableDragSelection(options) {
|
|
275
|
+
if (!this.wrapper)
|
|
276
|
+
return;
|
|
277
|
+
const minWidth = 5; // min 5 pixels
|
|
278
|
+
let region = null;
|
|
279
|
+
let startX = 0;
|
|
280
|
+
let sumDx = 0;
|
|
281
|
+
makeDraggable(this.wrapper,
|
|
282
|
+
// On mousedown
|
|
283
|
+
(x) => (startX = x),
|
|
284
|
+
// On mousemove
|
|
285
|
+
(dx) => {
|
|
286
|
+
sumDx += dx;
|
|
287
|
+
if (!this.wavesurfer || !this.wrapper)
|
|
288
|
+
return;
|
|
289
|
+
if (!region && sumDx > minWidth) {
|
|
290
|
+
const duration = this.wavesurfer.getDuration();
|
|
291
|
+
const box = this.wrapper.getBoundingClientRect();
|
|
292
|
+
const start = ((startX + sumDx - box.left) / box.width) * duration;
|
|
293
|
+
region = new Region({
|
|
294
|
+
...options,
|
|
295
|
+
start,
|
|
296
|
+
end: start,
|
|
297
|
+
}, duration);
|
|
298
|
+
this.regionsContainer.appendChild(region.element);
|
|
299
|
+
}
|
|
300
|
+
if (region) {
|
|
301
|
+
const privateRegion = region;
|
|
302
|
+
privateRegion.onUpdate(dx, ['end']);
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
// On mouseup
|
|
306
|
+
() => region && this.saveRegion(region));
|
|
307
|
+
}
|
|
308
|
+
clearRegions() {
|
|
309
|
+
this.regions.forEach((region) => region.remove());
|
|
253
310
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
311
|
+
destroy() {
|
|
312
|
+
this.clearRegions();
|
|
313
|
+
super.destroy();
|
|
257
314
|
}
|
|
258
315
|
}
|
|
259
316
|
export default RegionsPlugin;
|