wavesurfer.js 5.2.0 → 6.0.0

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 (58) hide show
  1. package/CHANGES.md +22 -0
  2. package/README.md +10 -0
  3. package/dist/plugin/wavesurfer.cursor.js +52 -50
  4. package/dist/plugin/wavesurfer.cursor.js.map +1 -1
  5. package/dist/plugin/wavesurfer.cursor.min.js +2 -2
  6. package/dist/plugin/wavesurfer.cursor.min.js.map +1 -1
  7. package/dist/plugin/wavesurfer.elan.js +9 -6
  8. package/dist/plugin/wavesurfer.elan.js.map +1 -1
  9. package/dist/plugin/wavesurfer.elan.min.js +2 -2
  10. package/dist/plugin/wavesurfer.elan.min.js.map +1 -1
  11. package/dist/plugin/wavesurfer.markers.js +141 -25
  12. package/dist/plugin/wavesurfer.markers.js.map +1 -1
  13. package/dist/plugin/wavesurfer.markers.min.js +2 -2
  14. package/dist/plugin/wavesurfer.markers.min.js.map +1 -1
  15. package/dist/plugin/wavesurfer.mediasession.js +5 -5
  16. package/dist/plugin/wavesurfer.mediasession.min.js +2 -2
  17. package/dist/plugin/wavesurfer.mediasession.min.js.map +1 -1
  18. package/dist/plugin/wavesurfer.microphone.js +4 -4
  19. package/dist/plugin/wavesurfer.microphone.min.js +2 -2
  20. package/dist/plugin/wavesurfer.microphone.min.js.map +1 -1
  21. package/dist/plugin/wavesurfer.minimap.js +25 -22
  22. package/dist/plugin/wavesurfer.minimap.js.map +1 -1
  23. package/dist/plugin/wavesurfer.minimap.min.js +2 -2
  24. package/dist/plugin/wavesurfer.minimap.min.js.map +1 -1
  25. package/dist/plugin/wavesurfer.playhead.js +4 -4
  26. package/dist/plugin/wavesurfer.playhead.min.js +2 -2
  27. package/dist/plugin/wavesurfer.playhead.min.js.map +1 -1
  28. package/dist/plugin/wavesurfer.regions.js +53 -16
  29. package/dist/plugin/wavesurfer.regions.js.map +1 -1
  30. package/dist/plugin/wavesurfer.regions.min.js +2 -2
  31. package/dist/plugin/wavesurfer.regions.min.js.map +1 -1
  32. package/dist/plugin/wavesurfer.spectrogram.js +70 -57
  33. package/dist/plugin/wavesurfer.spectrogram.js.map +1 -1
  34. package/dist/plugin/wavesurfer.spectrogram.min.js +2 -2
  35. package/dist/plugin/wavesurfer.spectrogram.min.js.map +1 -1
  36. package/dist/plugin/wavesurfer.timeline.js +39 -44
  37. package/dist/plugin/wavesurfer.timeline.js.map +1 -1
  38. package/dist/plugin/wavesurfer.timeline.min.js +2 -2
  39. package/dist/plugin/wavesurfer.timeline.min.js.map +1 -1
  40. package/dist/wavesurfer-html-init.js +5 -5
  41. package/dist/wavesurfer-html-init.min.js +2 -2
  42. package/dist/wavesurfer-html-init.min.js.map +1 -1
  43. package/dist/wavesurfer.js +209 -121
  44. package/dist/wavesurfer.js.map +1 -1
  45. package/dist/wavesurfer.min.js +2 -2
  46. package/dist/wavesurfer.min.js.map +1 -1
  47. package/package.json +23 -23
  48. package/src/drawer.canvasentry.js +30 -6
  49. package/src/drawer.js +1 -0
  50. package/src/drawer.multicanvas.js +19 -8
  51. package/src/mediaelement.js +1 -1
  52. package/src/plugin/cursor/index.js +68 -70
  53. package/src/plugin/markers/index.js +112 -23
  54. package/src/plugin/minimap/index.js +24 -21
  55. package/src/plugin/regions/index.js +30 -3
  56. package/src/plugin/regions/region.js +13 -4
  57. package/src/plugin/spectrogram/index.js +65 -50
  58. package/src/wavesurfer.js +31 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wavesurfer.js",
3
- "version": "5.2.0",
3
+ "version": "6.0.0",
4
4
  "description": "Interactive navigable audio visualization using Web Audio and Canvas",
5
5
  "main": "dist/wavesurfer.js",
6
6
  "directories": {
@@ -55,17 +55,17 @@
55
55
  "visualization"
56
56
  ],
57
57
  "devDependencies": {
58
- "@babel/core": "^7.15.0",
59
- "@babel/preset-env": "^7.15.0",
60
- "@babel/register": "^7.15.3",
61
- "@chiragrupani/karma-chromium-edge-launcher": "^2.1.1",
62
- "babel-eslint": "^10.1.0",
63
- "babel-loader": "^8.2.2",
58
+ "@babel/core": "^7.17.0",
59
+ "@babel/eslint-parser": "^7.17.0",
60
+ "@babel/plugin-proposal-class-properties": "^7.16.7",
61
+ "@babel/preset-env": "^7.16.11",
62
+ "@babel/register": "^7.17.0",
63
+ "@chiragrupani/karma-chromium-edge-launcher": "^2.2.1",
64
+ "babel-loader": "^8.2.3",
64
65
  "babel-plugin-add-module-exports": "^1.0.4",
65
- "babel-plugin-istanbul": "^6.0.0",
66
+ "babel-plugin-istanbul": "^6.1.1",
66
67
  "babel-plugin-proxy": "^1.1.0",
67
- "babel-plugin-transform-class-properties": "^6.24.1",
68
- "date-fns": "^2.23.0",
68
+ "date-fns": "^2.28.0",
69
69
  "debounce": "^1.2.1",
70
70
  "esdoc": "^1.1.0",
71
71
  "esdoc-accessor-plugin": "^1.0.0",
@@ -74,24 +74,24 @@
74
74
  "esdoc-ecmascript-proposal-plugin": "^1.0.0",
75
75
  "esdoc-integrate-test-plugin": "^1.0.0",
76
76
  "esdoc-standard-plugin": "^1.0.0",
77
- "eslint": "^7.32.0",
78
- "htmlhint": "^0.15.1",
77
+ "eslint": "^8.8.0",
78
+ "htmlhint": "^1.1.2",
79
79
  "in-publish": "^2.0.1",
80
- "jasmine-core": "^3.8.0",
81
- "karma": "^6.3.4",
80
+ "jasmine-core": "^4.0.0",
81
+ "karma": "^6.3.15",
82
82
  "karma-chrome-launcher": "3.1.0",
83
- "karma-coverage": "^2.0.3",
84
- "karma-firefox-launcher": "^2.1.1",
83
+ "karma-coverage": "^2.1.1",
84
+ "karma-firefox-launcher": "^2.1.2",
85
85
  "karma-jasmine": "4.0.1",
86
- "karma-jasmine-matchers": "4.0.2",
87
- "karma-verbose-reporter": "0.0.6",
86
+ "karma-jasmine-matchers": "5.0.0",
87
+ "karma-verbose-reporter": "0.0.8",
88
88
  "karma-webpack": "^5.0.0",
89
- "lint-staged": "^11.1.2",
90
- "load-script": "^1.0.0",
89
+ "lint-staged": "^12.3.3",
90
+ "load-script": "^2.0.0",
91
91
  "pre-commit": "^1.2.2",
92
- "webpack": "^5.50.0",
93
- "webpack-cli": "^4.8.0",
94
- "webpack-dev-server": "^4.0.0-rc.0",
92
+ "webpack": "^5.68.0",
93
+ "webpack-cli": "^4.9.2",
94
+ "webpack-dev-server": "^4.7.4",
95
95
  "webpack-merge": "^5.8.0"
96
96
  },
97
97
  "homepage": "https://wavesurfer-js.org",
@@ -145,18 +145,42 @@ export default class CanvasEntry {
145
145
 
146
146
  /**
147
147
  * Set the fill styles for wave and progress
148
- *
149
- * @param {string} waveColor Fill color for the wave canvas
150
- * @param {?string} progressColor Fill color for the progress canvas
148
+ * @param {string|string[]} waveColor Fill color for the wave canvas,
149
+ * or an array of colors to apply as a gradient
150
+ * @param {?string|string[]} progressColor Fill color for the progress canvas,
151
+ * or an array of colors to apply as a gradient
151
152
  */
152
153
  setFillStyles(waveColor, progressColor) {
153
- this.waveCtx.fillStyle = waveColor;
154
+ this.waveCtx.fillStyle = this.getFillStyle(this.waveCtx, waveColor);
154
155
 
155
156
  if (this.hasProgressCanvas) {
156
- this.progressCtx.fillStyle = progressColor;
157
+ this.progressCtx.fillStyle = this.getFillStyle(this.progressCtx, progressColor);
157
158
  }
158
159
  }
159
160
 
161
+ /**
162
+ * Utility function to handle wave color arguments
163
+ *
164
+ * When the color argument type is a string, it will be returned as is.
165
+ * Otherwise, it will be treated as an array, and a canvas gradient will
166
+ * be returned
167
+ *
168
+ * @since 5.3.0
169
+ * @param {CanvasRenderingContext2D} ctx Rendering context of target canvas
170
+ * @param {string|string[]} color Fill color for the wave canvas, or an array of colors to apply as a gradient
171
+ * @returns {string|CanvasGradient} Returns a string fillstyle value, or a canvas gradient
172
+ */
173
+ getFillStyle(ctx, color) {
174
+ if (typeof color == 'string') {
175
+ return color;
176
+ }
177
+
178
+ const waveGradient = ctx.createLinearGradient(0, 0, 0, ctx.canvas.height);
179
+ color.forEach((value, index) => waveGradient.addColorStop((index / color.length), value));
180
+
181
+ return waveGradient;
182
+ }
183
+
160
184
  /**
161
185
  * Set the canvas transforms for wave and progress
162
186
  *
@@ -357,7 +381,7 @@ export default class CanvasEntry {
357
381
  ctx.lineTo(
358
382
  (canvasStart - first) * scale,
359
383
  halfOffset -
360
- Math.round((peaks[2 * canvasStart + 1] || 0) / absmaxHalf)
384
+ Math.round((peaks[2 * canvasStart + 1] || 0) / absmaxHalf)
361
385
  );
362
386
 
363
387
  ctx.closePath();
package/src/drawer.js CHANGED
@@ -69,6 +69,7 @@ export default class Drawer extends util.Observer {
69
69
  if (this.params.fillParent || this.params.scrollParent) {
70
70
  this.style(this.wrapper, {
71
71
  width: '100%',
72
+ cursor: this.params.hideCursor ? 'none' : 'auto',
72
73
  overflowX: this.params.hideScrollbar ? 'hidden' : 'auto',
73
74
  overflowY: 'hidden'
74
75
  });
@@ -305,21 +305,32 @@ export default class MultiCanvas extends Drawer {
305
305
  const scale = length / this.width;
306
306
  const first = start;
307
307
  const last = end;
308
- let i = first;
309
-
310
- for (i; i < last; i += step) {
311
- const peak =
312
- peaks[Math.floor(i * scale * peakIndexScale)] || 0;
308
+ let peakIndex = first;
309
+ for (peakIndex; peakIndex < last; peakIndex += step) {
310
+
311
+ // search for the highest peak in the range this bar falls into
312
+ let peak = 0;
313
+ let peakIndexRange = Math.floor(peakIndex * scale) * peakIndexScale; // start index
314
+ const peakIndexEnd = Math.floor((peakIndex + step) * scale) * peakIndexScale;
315
+ do { // do..while makes sure at least one peak is always evaluated
316
+ const newPeak = peaks[peakIndexRange];
317
+ if (newPeak > peak) {
318
+ peak = newPeak; // higher
319
+ }
320
+ peakIndexRange += peakIndexScale; // skip every other value for negatives
321
+ } while (peakIndexRange < peakIndexEnd);
322
+
323
+ // calculate the height of this bar according to the highest peak found
313
324
  let h = Math.round((peak / absmax) * halfH);
314
325
 
315
- /* in case of silences, allow the user to specify that we
316
- * always draw *something* (normally a 1px high bar) */
326
+ // in case of silences, allow the user to specify that we
327
+ // always draw *something* (normally a 1px high bar)
317
328
  if (h == 0 && this.params.barMinHeight) {
318
329
  h = this.params.barMinHeight;
319
330
  }
320
331
 
321
332
  this.fillRect(
322
- i + this.halfPixel,
333
+ peakIndex + this.halfPixel,
323
334
  halfH - h + offsetY,
324
335
  bar + this.halfPixel,
325
336
  h * 2,
@@ -275,7 +275,7 @@ export default class MediaElement extends WebAudio {
275
275
  * @param {number} start Position to start at in seconds
276
276
  */
277
277
  seekTo(start) {
278
- if (start != null) {
278
+ if (start != null && !isNaN(start)) {
279
279
  this.media.currentTime = start;
280
280
  }
281
281
  this.clearPlayEnd();
@@ -86,14 +86,15 @@ export default class CursorPlugin {
86
86
  * @param {object} e Mouse move event
87
87
  */
88
88
  _onMousemove = e => {
89
- const bbox = this.wavesurfer.container.getBoundingClientRect();
89
+ const event = this.util.withOrientation(e, this.wavesurfer.params.vertical);
90
+ const bbox = this.wrapper.getBoundingClientRect();
90
91
  let y = 0;
91
- let x = e.clientX - bbox.left;
92
- let flip = bbox.right < e.clientX + this.outerWidth(this.displayTime);
92
+ let x = this.wrapper.scrollLeft + event.clientX - bbox.left;
93
+ let flip = bbox.right < event.clientX + this.displayTime.getBoundingClientRect().width;
93
94
 
94
95
  if (this.params.showTime && this.params.followCursorY) {
95
96
  // follow y-position of the mouse
96
- y = e.clientY - (bbox.top + bbox.height / 2);
97
+ y = event.clientY - (bbox.top + bbox.height / 2);
97
98
  }
98
99
 
99
100
  this.updateCursorPosition(x, y, flip);
@@ -119,6 +120,7 @@ export default class CursorPlugin {
119
120
  constructor(params, ws) {
120
121
  this.wavesurfer = ws;
121
122
  this.style = ws.util.style;
123
+ this.util = ws.util;
122
124
  /**
123
125
  * The cursor HTML element
124
126
  *
@@ -141,14 +143,38 @@ export default class CursorPlugin {
141
143
  this.params = Object.assign({}, this.defaultParams, params);
142
144
  }
143
145
 
144
- /**
145
- * Initialise the plugin (used by the Plugin API)
146
- */
147
- init() {
148
- this.wrapper = this.wavesurfer.container;
149
- this.cursor = this.wrapper.appendChild(
146
+ _onReady() {
147
+ this.wrapper = this.wavesurfer.drawer.wrapper;
148
+ this.cursor = this.util.withOrientation(this.wrapper.appendChild(
149
+ document.createElement('cursor'),
150
+ ), this.wavesurfer.params.vertical);
151
+
152
+ this.style(this.cursor,
153
+ Object.assign(
154
+ {
155
+ position: 'absolute',
156
+ zIndex: this.params.zIndex,
157
+ left: 0,
158
+ top: 0,
159
+ bottom: 0,
160
+ width: '0',
161
+ display: 'flex',
162
+ borderRightStyle: this.params.style,
163
+ borderRightWidth: this.params.width,
164
+ borderRightColor: this.params.color,
165
+ opacity: this.params.opacity,
166
+ pointerEvents: 'none'
167
+ },
168
+ this.params.customStyle
169
+ )
170
+ );
171
+
172
+ if (this.params.showTime) {
173
+ this.showTime = this.util.withOrientation(this.wrapper.appendChild(
174
+ document.createElement('showTitle')
175
+ ), this.wavesurfer.params.vertical);
150
176
  this.style(
151
- document.createElement('cursor'),
177
+ this.showTime,
152
178
  Object.assign(
153
179
  {
154
180
  position: 'absolute',
@@ -156,53 +182,32 @@ export default class CursorPlugin {
156
182
  left: 0,
157
183
  top: 0,
158
184
  bottom: 0,
159
- width: '0',
185
+ width: 'auto',
160
186
  display: 'flex',
161
- borderRightStyle: this.params.style,
162
- borderRightWidth: this.params.width,
163
- borderRightColor: this.params.color,
164
187
  opacity: this.params.opacity,
165
- pointerEvents: 'none'
188
+ pointerEvents: 'none',
189
+ height: '100%'
166
190
  },
167
191
  this.params.customStyle
168
192
  )
169
- )
170
- );
171
- if (this.params.showTime) {
172
- this.showTime = this.wrapper.appendChild(
173
- this.style(
174
- document.createElement('showTitle'),
175
- Object.assign(
176
- {
177
- position: 'absolute',
178
- zIndex: this.params.zIndex,
179
- left: 0,
180
- top: 0,
181
- bottom: 0,
182
- width: 'auto',
183
- display: 'flex',
184
- opacity: this.params.opacity,
185
- pointerEvents: 'none',
186
- height: '100%'
187
- },
188
- this.params.customStyle
189
- )
190
- )
191
193
  );
192
- this.displayTime = this.showTime.appendChild(
193
- this.style(
194
- document.createElement('div'),
195
- Object.assign(
196
- {
197
- display: 'inline',
198
- pointerEvents: 'none',
199
- margin: 'auto',
200
- visibility: 'hidden' // initial value will be hidden just for measuring purpose
201
- },
202
- this.params.customShowTimeStyle
203
- )
194
+
195
+ this.displayTime = this.util.withOrientation(this.showTime.appendChild(
196
+ document.createElement('div'),
197
+ ), this.wavesurfer.params.vertical);
198
+
199
+ this.style(this.displayTime,
200
+ Object.assign(
201
+ {
202
+ display: 'inline',
203
+ pointerEvents: 'none',
204
+ margin: 'auto',
205
+ visibility: 'hidden' // initial value will be hidden just for measuring purpose
206
+ },
207
+ this.params.customShowTimeStyle
204
208
  )
205
209
  );
210
+
206
211
  // initial value to measure display width
207
212
  this.displayTime.innerHTML = this.formatTime(0);
208
213
  }
@@ -216,6 +221,17 @@ export default class CursorPlugin {
216
221
  }
217
222
  }
218
223
 
224
+ /**
225
+ * Initialise the plugin (used by the Plugin API)
226
+ */
227
+ init() {
228
+ if (this.wavesurfer.isReady) {
229
+ this._onReady();
230
+ } else {
231
+ this.wavesurfer.once('ready', () => this._onReady());
232
+ }
233
+ }
234
+
219
235
  /**
220
236
  * Destroy the plugin (used by the Plugin API)
221
237
  */
@@ -253,10 +269,10 @@ export default class CursorPlugin {
253
269
  (duration / this.wavesurfer.drawer.width) * scrollWidth;
254
270
 
255
271
  const timeValue =
256
- Math.max(0, (xpos / elementWidth) * duration) + scrollTime;
272
+ Math.max(0, ((xpos - this.wrapper.scrollLeft) / elementWidth) * duration) + scrollTime;
257
273
  const formatValue = this.formatTime(timeValue);
258
274
  if (flip) {
259
- const textOffset = this.outerWidth(this.displayTime);
275
+ const textOffset = this.displayTime.getBoundingClientRect().width;
260
276
  xpos -= textOffset;
261
277
  }
262
278
  this.style(this.showTime, {
@@ -318,22 +334,4 @@ export default class CursorPlugin {
318
334
  ].join(':')
319
335
  );
320
336
  }
321
-
322
- /**
323
- * Get outer width of given element.
324
- *
325
- * @param {DOM} element DOM Element
326
- * @returns {number} outer width
327
- */
328
- outerWidth(element) {
329
- if (!element) {
330
- return 0;
331
- }
332
-
333
- let width = element.offsetWidth;
334
- let style = getComputedStyle(element);
335
-
336
- width += parseInt(style.marginLeft + style.marginRight);
337
- return width;
338
- }
339
337
  }
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * @typedef {Object} MarkerParams
4
3
  * @desc The parameters used to describe a marker.
@@ -8,6 +7,7 @@
8
7
  * @property {?color} string Background color for marker
9
8
  * @property {?position} string "top" or "bottom", defaults to "bottom"
10
9
  * @property {?markerElement} element An HTML element to display instead of the default marker image
10
+ * @property {?draggable} boolean Set marker as draggable, defaults to false
11
11
  */
12
12
 
13
13
 
@@ -41,6 +41,8 @@ export default class MarkersPlugin {
41
41
  * @typedef {Object} MarkersPluginParams
42
42
  * @property {?MarkerParams[]} markers Initial set of markers
43
43
  * @fires MarkersPlugin#marker-click
44
+ * @fires MarkersPlugin#marker-drag
45
+ * @fires MarkersPlugin#marker-drop
44
46
  */
45
47
 
46
48
  /**
@@ -78,8 +80,10 @@ export default class MarkersPlugin {
78
80
  this.wavesurfer = ws;
79
81
  this.util = ws.util;
80
82
  this.style = this.util.style;
83
+ this.markerLineWidth = 1;
81
84
  this.markerWidth = 11;
82
85
  this.markerHeight = 22;
86
+ this.dragging = false;
83
87
 
84
88
 
85
89
  this._onResize = () => {
@@ -94,6 +98,16 @@ export default class MarkersPlugin {
94
98
  window.addEventListener('resize', this._onResize, true);
95
99
  window.addEventListener('orientationchange', this._onResize, true);
96
100
  this.wavesurfer.on('zoom', this._onResize);
101
+
102
+ if (!this.markers.find(marker => marker.draggable)){
103
+ return;
104
+ }
105
+
106
+ this.onMouseMove = (e) => this._onMouseMove(e);
107
+ window.addEventListener('mousemove', this.onMouseMove);
108
+
109
+ this.onMouseUp = (e) => this._onMouseUp(e);
110
+ window.addEventListener("mouseup", this.onMouseUp);
97
111
  };
98
112
 
99
113
  this.markers = [];
@@ -123,6 +137,13 @@ export default class MarkersPlugin {
123
137
  window.removeEventListener('resize', this._onResize, true);
124
138
  window.removeEventListener('orientationchange', this._onResize, true);
125
139
 
140
+ if (this.onMouseMove) {
141
+ window.removeEventListener('mousemove', this.onMouseMove);
142
+ }
143
+ if (this.onMouseUp) {
144
+ window.removeEventListener("mouseup", this.onMouseUp);
145
+ }
146
+
126
147
  this.clear();
127
148
  }
128
149
 
@@ -137,14 +158,10 @@ export default class MarkersPlugin {
137
158
  time: params.time,
138
159
  label: params.label,
139
160
  color: params.color || DEFAULT_FILL_COLOR,
140
- position: params.position || DEFAULT_POSITION
161
+ position: params.position || DEFAULT_POSITION,
162
+ draggable: !!params.draggable
141
163
  };
142
164
 
143
- if (params.markerElement) {
144
- this.markerWidth = params.markerElement.width;
145
- this.markerHeight = params.markerElement.height;
146
- }
147
-
148
165
  marker.el = this._createMarkerElement(marker, params.markerElement);
149
166
 
150
167
  this.wrapper.appendChild(marker.el);
@@ -199,7 +216,6 @@ export default class MarkersPlugin {
199
216
 
200
217
  _createMarkerElement(marker, markerElement) {
201
218
  let label = marker.label;
202
- let time = marker.time;
203
219
 
204
220
  const el = document.createElement('marker');
205
221
  el.className = "wavesurfer-marker";
@@ -213,17 +229,22 @@ export default class MarkersPlugin {
213
229
  });
214
230
 
215
231
  const line = document.createElement('div');
232
+ const width = markerElement ? markerElement.width : this.markerWidth;
233
+ marker.offset = (width - this.markerLineWidth) / 2;
216
234
  this.style(line, {
217
235
  "flex-grow": 1,
218
- "margin-left": (this.markerWidth / 2 - 0.5) + "px",
236
+ "margin-left": marker.offset + "px",
219
237
  background: "black",
220
- width: "1px",
238
+ width: this.markerLineWidth + "px",
221
239
  opacity: 0.1
222
240
  });
223
241
  el.appendChild(line);
224
242
 
225
243
  const labelDiv = document.createElement('div');
226
244
  const point = markerElement || this._createPointerSVG(marker.color, marker.position);
245
+ if (marker.draggable){
246
+ point.draggable = false;
247
+ }
227
248
  labelDiv.appendChild(point);
228
249
 
229
250
  if ( label ) {
@@ -246,31 +267,99 @@ export default class MarkersPlugin {
246
267
 
247
268
  labelDiv.addEventListener("click", e => {
248
269
  e.stopPropagation();
249
- this.wavesurfer.setCurrentTime(time);
270
+ // Click event is caught when the marker-drop event was dispatched.
271
+ // Drop event was dispatched at this moment, but this.dragging
272
+ // is waiting for the next tick to set as false
273
+ if (this.dragging){
274
+ return;
275
+ }
276
+ this.wavesurfer.setCurrentTime(marker.time);
250
277
  this.wavesurfer.fireEvent("marker-click", marker, e);
251
278
  });
252
279
 
280
+ if (marker.draggable) {
281
+ labelDiv.addEventListener("mousedown", e => {
282
+ this.selectedMarker = marker;
283
+ });
284
+ }
253
285
  return el;
254
286
  }
255
287
 
256
288
  _updateMarkerPositions() {
257
- const duration = this.wavesurfer.getDuration();
258
-
259
289
  for ( let i = 0 ; i < this.markers.length; i++ ) {
260
290
  let marker = this.markers[i];
261
- const elementWidth =
262
- this.wavesurfer.drawer.width /
263
- this.wavesurfer.params.pixelRatio;
264
-
265
- const positionPct = Math.min(marker.time / duration, 1);
266
- const leftPx = ((elementWidth * positionPct) - (this.markerWidth / 2));
267
- this.style(marker.el, {
268
- "left": leftPx + "px",
269
- "max-width": (elementWidth - leftPx) + "px"
270
- });
291
+ this._updateMarkerPosition(marker);
271
292
  }
272
293
  }
273
294
 
295
+ /**
296
+ * Update a marker position based on its time property.
297
+ *
298
+ * @private
299
+ * @param {MarkerParams} params The marker to update.
300
+ * @returns {void}
301
+ */
302
+ _updateMarkerPosition(params) {
303
+ const duration = this.wavesurfer.getDuration();
304
+ const elementWidth =
305
+ this.wavesurfer.drawer.width /
306
+ this.wavesurfer.params.pixelRatio;
307
+
308
+ const positionPct = Math.min(params.time / duration, 1);
309
+ const leftPx = ((elementWidth * positionPct) - params.offset);
310
+ this.style(params.el, {
311
+ "left": leftPx + "px",
312
+ "max-width": (elementWidth - leftPx) + "px"
313
+ });
314
+ }
315
+
316
+ /**
317
+ * Fires `marker-drag` event, update the `time` property for the
318
+ * selected marker based on the mouse position, and calls to update
319
+ * its position.
320
+ *
321
+ * @private
322
+ * @param {MouseEvent} event The mouse event.
323
+ * @returns {void}
324
+ */
325
+ _onMouseMove(event) {
326
+ if (!this.selectedMarker){
327
+ return;
328
+ }
329
+ if (!this.dragging){
330
+ this.dragging = true;
331
+ this.wavesurfer.fireEvent("marker-drag", this.selectedMarker, event);
332
+ }
333
+ this.selectedMarker.time = this.wavesurfer.drawer.handleEvent(event) * this.wavesurfer.getDuration();
334
+ this._updateMarkerPositions();
335
+ }
336
+
337
+ /**
338
+ * Fires `marker-drop` event and unselect the dragged marker.
339
+ *
340
+ * @private
341
+ * @param {MouseEvent} event The mouse event.
342
+ * @returns {void}
343
+ */
344
+ _onMouseUp(event) {
345
+ if (this.selectedMarker) {
346
+ setTimeout(() => {
347
+ this.selectedMarker = false;
348
+ this.dragging = false;
349
+ }, 0);
350
+ }
351
+
352
+ if (!this.dragging) {
353
+ return;
354
+ }
355
+
356
+ event.stopPropagation();
357
+ const duration = this.wavesurfer.getDuration();
358
+ this.selectedMarker.time = this.wavesurfer.drawer.handleEvent(event) * duration;
359
+ this._updateMarkerPositions();
360
+ this.wavesurfer.fireEvent("marker-drop", this.selectedMarker, event);
361
+ }
362
+
274
363
  /**
275
364
  * Remove all markers
276
365
  */