wavesurfer.js 4.5.0 → 5.1.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 (65) hide show
  1. package/CHANGES.md +36 -1
  2. package/README.md +6 -3
  3. package/dist/plugin/wavesurfer.cursor.js +39 -34
  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 +29 -27
  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 +381 -0
  12. package/dist/plugin/wavesurfer.markers.js.map +1 -0
  13. package/dist/plugin/wavesurfer.markers.min.js +7 -0
  14. package/dist/plugin/wavesurfer.markers.min.js.map +1 -0
  15. package/dist/plugin/wavesurfer.mediasession.js +29 -27
  16. package/dist/plugin/wavesurfer.mediasession.js.map +1 -1
  17. package/dist/plugin/wavesurfer.mediasession.min.js +2 -2
  18. package/dist/plugin/wavesurfer.mediasession.min.js.map +1 -1
  19. package/dist/plugin/wavesurfer.microphone.js +29 -27
  20. package/dist/plugin/wavesurfer.microphone.js.map +1 -1
  21. package/dist/plugin/wavesurfer.microphone.min.js +2 -2
  22. package/dist/plugin/wavesurfer.microphone.min.js.map +1 -1
  23. package/dist/plugin/wavesurfer.minimap.js +30 -28
  24. package/dist/plugin/wavesurfer.minimap.js.map +1 -1
  25. package/dist/plugin/wavesurfer.minimap.min.js +2 -2
  26. package/dist/plugin/wavesurfer.minimap.min.js.map +1 -1
  27. package/dist/plugin/wavesurfer.playhead.js +324 -0
  28. package/dist/plugin/wavesurfer.playhead.js.map +1 -0
  29. package/dist/plugin/wavesurfer.playhead.min.js +7 -0
  30. package/dist/plugin/wavesurfer.playhead.min.js.map +1 -0
  31. package/dist/plugin/wavesurfer.regions.js +152 -126
  32. package/dist/plugin/wavesurfer.regions.js.map +1 -1
  33. package/dist/plugin/wavesurfer.regions.min.js +2 -2
  34. package/dist/plugin/wavesurfer.regions.min.js.map +1 -1
  35. package/dist/plugin/wavesurfer.spectrogram.js +97 -89
  36. package/dist/plugin/wavesurfer.spectrogram.js.map +1 -1
  37. package/dist/plugin/wavesurfer.spectrogram.min.js +2 -2
  38. package/dist/plugin/wavesurfer.spectrogram.min.js.map +1 -1
  39. package/dist/plugin/wavesurfer.timeline.js +46 -32
  40. package/dist/plugin/wavesurfer.timeline.js.map +1 -1
  41. package/dist/plugin/wavesurfer.timeline.min.js +2 -2
  42. package/dist/plugin/wavesurfer.timeline.min.js.map +1 -1
  43. package/dist/wavesurfer-html-init.js +9 -5
  44. package/dist/wavesurfer-html-init.js.map +1 -1
  45. package/dist/wavesurfer-html-init.min.js +2 -2
  46. package/dist/wavesurfer-html-init.min.js.map +1 -1
  47. package/dist/wavesurfer.js +350 -166
  48. package/dist/wavesurfer.js.map +1 -1
  49. package/dist/wavesurfer.min.js +2 -2
  50. package/dist/wavesurfer.min.js.map +1 -1
  51. package/package.json +19 -16
  52. package/src/drawer.canvasentry.js +16 -0
  53. package/src/drawer.js +30 -21
  54. package/src/drawer.multicanvas.js +63 -39
  55. package/src/plugin/cursor/index.js +3 -1
  56. package/src/plugin/markers/index.js +282 -0
  57. package/src/plugin/playhead/index.js +226 -0
  58. package/src/plugin/regions/index.js +28 -11
  59. package/src/plugin/regions/region.js +83 -70
  60. package/src/plugin/spectrogram/index.js +65 -61
  61. package/src/plugin/timeline/index.js +20 -11
  62. package/src/util/index.js +1 -0
  63. package/src/util/orientation.js +98 -0
  64. package/src/wavesurfer.js +18 -9
  65. package/src/webaudio.js +10 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wavesurfer.js",
3
- "version": "4.5.0",
3
+ "version": "5.1.0",
4
4
  "description": "Interactive navigable audio visualization using Web Audio and Canvas",
5
5
  "main": "dist/wavesurfer.js",
6
6
  "directories": {
@@ -29,6 +29,7 @@
29
29
  "lint-js-html": "lint-staged",
30
30
  "doc": "esdoc",
31
31
  "test": "karma start karma.conf.js",
32
+ "test:chrome": "karma start karma.conf.js --browsers=Chrome --single-run=false",
32
33
  "prepublishOnly": "not-in-install && npm run build || in-install",
33
34
  "prepare": "npm run build"
34
35
  },
@@ -54,16 +55,18 @@
54
55
  "visualization"
55
56
  ],
56
57
  "devDependencies": {
57
- "@babel/core": "^7.12.16",
58
- "@babel/preset-env": "^7.12.16",
59
- "@babel/register": "^7.12.13",
58
+ "@babel/core": "^7.14.6",
59
+ "@babel/preset-env": "^7.14.5",
60
+ "@babel/register": "^7.14.5",
61
+ "@chiragrupani/karma-chromium-edge-launcher": "^2.1.1",
60
62
  "babel-eslint": "^10.1.0",
61
63
  "babel-loader": "^8.2.2",
62
64
  "babel-plugin-add-module-exports": "^1.0.4",
63
65
  "babel-plugin-istanbul": "^6.0.0",
66
+ "babel-plugin-proxy": "^1.1.0",
64
67
  "babel-plugin-transform-class-properties": "^6.24.1",
65
- "date-fns": "^2.17.0",
66
- "debounce": "^1.2.0",
68
+ "date-fns": "^2.22.1",
69
+ "debounce": "^1.2.1",
67
70
  "esdoc": "^1.1.0",
68
71
  "esdoc-accessor-plugin": "^1.0.0",
69
72
  "esdoc-brand-plugin": "^1.0.1",
@@ -71,25 +74,25 @@
71
74
  "esdoc-ecmascript-proposal-plugin": "^1.0.0",
72
75
  "esdoc-integrate-test-plugin": "^1.0.0",
73
76
  "esdoc-standard-plugin": "^1.0.0",
74
- "eslint": "^7.20.0",
75
- "htmlhint": "^0.14.2",
77
+ "eslint": "^7.29.0",
78
+ "htmlhint": "^0.15.1",
76
79
  "in-publish": "^2.0.1",
77
- "jasmine-core": "^3.6.0",
78
- "karma": "^5.2.3",
80
+ "jasmine-core": "^3.7.1",
81
+ "karma": "^6.3.4",
79
82
  "karma-chrome-launcher": "3.1.0",
80
83
  "karma-coverage": "^2.0.3",
81
- "karma-firefox-launcher": "^2.1.0",
84
+ "karma-firefox-launcher": "^2.1.1",
82
85
  "karma-jasmine": "4.0.1",
83
86
  "karma-jasmine-matchers": "4.0.2",
84
87
  "karma-verbose-reporter": "0.0.6",
85
88
  "karma-webpack": "^5.0.0",
86
- "lint-staged": "^10.5.4",
89
+ "lint-staged": "^11.0.0",
87
90
  "load-script": "^1.0.0",
88
91
  "pre-commit": "^1.2.2",
89
- "webpack": "^5.21.2",
90
- "webpack-cli": "^4.5.0",
91
- "webpack-dev-server": "^4.0.0-beta.0",
92
- "webpack-merge": "^5.7.3"
92
+ "webpack": "^5.39.1",
93
+ "webpack-cli": "^4.7.2",
94
+ "webpack-dev-server": "^4.0.0-beta.3",
95
+ "webpack-merge": "^5.8.0"
93
96
  },
94
97
  "homepage": "https://wavesurfer-js.org",
95
98
  "pre-commit": "lint-js-html",
@@ -157,6 +157,22 @@ export default class CanvasEntry {
157
157
  }
158
158
  }
159
159
 
160
+ /**
161
+ * Set the canvas transforms for wave and progress
162
+ *
163
+ * @param {boolean} vertical Whether to render vertically
164
+ */
165
+ applyCanvasTransforms(vertical) {
166
+ if (vertical) {
167
+ // Reflect the waveform across the line y = -x
168
+ this.waveCtx.setTransform(0, 1, 1, 0, 0, 0);
169
+
170
+ if (this.hasProgressCanvas) {
171
+ this.progressCtx.setTransform(0, 1, 1, 0, 0, 0);
172
+ }
173
+ }
174
+ }
175
+
160
176
  /**
161
177
  * Draw a rectangle for wave and progress
162
178
  *
package/src/drawer.js CHANGED
@@ -13,7 +13,7 @@ export default class Drawer extends util.Observer {
13
13
  constructor(container, params) {
14
14
  super();
15
15
 
16
- this.container = container;
16
+ this.container = util.withOrientation(container, params.vertical);
17
17
  /**
18
18
  * @type {WavesurferParams}
19
19
  */
@@ -53,8 +53,9 @@ export default class Drawer extends util.Observer {
53
53
  * interaction
54
54
  */
55
55
  createWrapper() {
56
- this.wrapper = this.container.appendChild(
57
- document.createElement('wave')
56
+ this.wrapper = util.withOrientation(
57
+ this.container.appendChild(document.createElement('wave')),
58
+ this.params.vertical
58
59
  );
59
60
 
60
61
  this.style(this.wrapper, {
@@ -86,39 +87,46 @@ export default class Drawer extends util.Observer {
86
87
  handleEvent(e, noPrevent) {
87
88
  !noPrevent && e.preventDefault();
88
89
 
89
- const clientX = e.targetTouches
90
- ? e.targetTouches[0].clientX
91
- : e.clientX;
90
+ const clientX = util.withOrientation(
91
+ e.targetTouches ? e.targetTouches[0] : e,
92
+ this.params.vertical
93
+ ).clientX;
92
94
  const bbox = this.wrapper.getBoundingClientRect();
93
95
 
94
96
  const nominalWidth = this.width;
95
97
  const parentWidth = this.getWidth();
98
+ const progressPixels = this.getProgressPixels(bbox, clientX);
96
99
 
97
100
  let progress;
98
101
  if (!this.params.fillParent && nominalWidth < parentWidth) {
99
- progress =
100
- (this.params.rtl ? bbox.right - clientX : clientX - bbox.left) *
101
- (this.params.pixelRatio / nominalWidth) || 0;
102
+ progress = progressPixels *
103
+ (this.params.pixelRatio / nominalWidth) || 0;
102
104
  } else {
103
- progress =
104
- ((this.params.rtl
105
- ? bbox.right - clientX
106
- : clientX - bbox.left) +
107
- this.wrapper.scrollLeft) /
108
- this.wrapper.scrollWidth || 0;
105
+ progress = (progressPixels + this.wrapper.scrollLeft) /
106
+ this.wrapper.scrollWidth || 0;
109
107
  }
110
108
 
111
109
  return util.clamp(progress, 0, 1);
112
110
  }
113
111
 
112
+ getProgressPixels(wrapperBbox, clientX) {
113
+ if (this.params.rtl) {
114
+ return wrapperBbox.right - clientX;
115
+ } else {
116
+ return clientX - wrapperBbox.left;
117
+ }
118
+ }
119
+
114
120
  setupWrapperEvents() {
115
121
  this.wrapper.addEventListener('click', e => {
116
- const scrollbarHeight =
117
- this.wrapper.offsetHeight - this.wrapper.clientHeight;
122
+ const orientedEvent = util.withOrientation(e, this.params.vertical);
123
+ const scrollbarHeight = this.wrapper.offsetHeight -
124
+ this.wrapper.clientHeight;
125
+
118
126
  if (scrollbarHeight !== 0) {
119
127
  // scrollbar is visible. Check if click was on it
120
128
  const bbox = this.wrapper.getBoundingClientRect();
121
- if (e.clientY >= bbox.bottom - scrollbarHeight) {
129
+ if (orientedEvent.clientY >= bbox.bottom - scrollbarHeight) {
122
130
  // ignore mousedown as it was on the scrollbar
123
131
  return;
124
132
  }
@@ -274,8 +282,9 @@ export default class Drawer extends util.Observer {
274
282
  width: ''
275
283
  });
276
284
  } else {
285
+ const newWidth = ~~(this.width / this.params.pixelRatio) + 'px';
277
286
  this.style(this.wrapper, {
278
- width: ~~(this.width / this.params.pixelRatio) + 'px'
287
+ width: newWidth
279
288
  });
280
289
  }
281
290
 
@@ -333,8 +342,8 @@ export default class Drawer extends util.Observer {
333
342
  destroy() {
334
343
  this.unAll();
335
344
  if (this.wrapper) {
336
- if (this.wrapper.parentNode == this.container) {
337
- this.container.removeChild(this.wrapper);
345
+ if (this.wrapper.parentNode == this.container.domElement) {
346
+ this.container.removeChild(this.wrapper.domElement);
338
347
  }
339
348
  this.wrapper = null;
340
349
  }
@@ -82,6 +82,13 @@ export default class MultiCanvas extends Drawer {
82
82
  * @type {number}
83
83
  */
84
84
  this.barRadius = params.barRadius || 0;
85
+
86
+ /**
87
+ * Whether to render the waveform vertically. Defaults to false.
88
+ *
89
+ * @type {boolean}
90
+ */
91
+ this.vertical = params.vertical;
85
92
  }
86
93
 
87
94
  /**
@@ -97,21 +104,23 @@ export default class MultiCanvas extends Drawer {
97
104
  *
98
105
  */
99
106
  createElements() {
100
- this.progressWave = this.wrapper.appendChild(
101
- this.style(document.createElement('wave'), {
102
- position: 'absolute',
103
- zIndex: 3,
104
- left: 0,
105
- top: 0,
106
- bottom: 0,
107
- overflow: 'hidden',
108
- width: '0',
109
- display: 'none',
110
- boxSizing: 'border-box',
111
- borderRightStyle: 'solid',
112
- pointerEvents: 'none'
113
- })
107
+ this.progressWave = util.withOrientation(
108
+ this.wrapper.appendChild(document.createElement('wave')),
109
+ this.params.vertical
114
110
  );
111
+ this.style(this.progressWave, {
112
+ position: 'absolute',
113
+ zIndex: 3,
114
+ left: 0,
115
+ top: 0,
116
+ bottom: 0,
117
+ overflow: 'hidden',
118
+ width: '0',
119
+ display: 'none',
120
+ boxSizing: 'border-box',
121
+ borderRightStyle: 'solid',
122
+ pointerEvents: 'none'
123
+ });
115
124
 
116
125
  this.addCanvas();
117
126
  this.updateCursor();
@@ -170,33 +179,35 @@ export default class MultiCanvas extends Drawer {
170
179
  const leftOffset = this.maxCanvasElementWidth * this.canvases.length;
171
180
 
172
181
  // wave
173
- entry.initWave(
174
- this.wrapper.appendChild(
175
- this.style(document.createElement('canvas'), {
176
- position: 'absolute',
177
- zIndex: 2,
178
- left: leftOffset + 'px',
179
- top: 0,
180
- bottom: 0,
181
- height: '100%',
182
- pointerEvents: 'none'
183
- })
184
- )
182
+ let wave = util.withOrientation(
183
+ this.wrapper.appendChild(document.createElement('canvas')),
184
+ this.params.vertical
185
185
  );
186
+ this.style(wave, {
187
+ position: 'absolute',
188
+ zIndex: 2,
189
+ left: leftOffset + 'px',
190
+ top: 0,
191
+ bottom: 0,
192
+ height: '100%',
193
+ pointerEvents: 'none'
194
+ });
195
+ entry.initWave(wave);
186
196
 
187
197
  // progress
188
198
  if (this.hasProgressCanvas) {
189
- entry.initProgress(
190
- this.progressWave.appendChild(
191
- this.style(document.createElement('canvas'), {
192
- position: 'absolute',
193
- left: leftOffset + 'px',
194
- top: 0,
195
- bottom: 0,
196
- height: '100%'
197
- })
198
- )
199
+ let progress = util.withOrientation(
200
+ this.progressWave.appendChild(document.createElement('canvas')),
201
+ this.params.vertical
199
202
  );
203
+ this.style(progress, {
204
+ position: 'absolute',
205
+ left: leftOffset + 'px',
206
+ top: 0,
207
+ bottom: 0,
208
+ height: '100%'
209
+ });
210
+ entry.initProgress(progress);
200
211
  }
201
212
 
202
213
  this.canvases.push(entry);
@@ -210,11 +221,11 @@ export default class MultiCanvas extends Drawer {
210
221
  let lastEntry = this.canvases[this.canvases.length - 1];
211
222
 
212
223
  // wave
213
- lastEntry.wave.parentElement.removeChild(lastEntry.wave);
224
+ lastEntry.wave.parentElement.removeChild(lastEntry.wave.domElement);
214
225
 
215
226
  // progress
216
227
  if (this.hasProgressCanvas) {
217
- lastEntry.progress.parentElement.removeChild(lastEntry.progress);
228
+ lastEntry.progress.parentElement.removeChild(lastEntry.progress.domElement);
218
229
  }
219
230
 
220
231
  // cleanup
@@ -303,8 +314,9 @@ export default class MultiCanvas extends Drawer {
303
314
 
304
315
  /* in case of silences, allow the user to specify that we
305
316
  * always draw *something* (normally a 1px high bar) */
306
- if (h == 0 && this.params.barMinHeight)
317
+ if (h == 0 && this.params.barMinHeight) {
307
318
  h = this.params.barMinHeight;
319
+ }
308
320
 
309
321
  this.fillRect(
310
322
  i + this.halfPixel,
@@ -386,6 +398,7 @@ export default class MultiCanvas extends Drawer {
386
398
  const { waveColor, progressColor } = this.params.splitChannelsOptions.channelColors[channelIndex] || {};
387
399
  this.canvases.forEach((entry, i) => {
388
400
  this.setFillStyles(entry, waveColor, progressColor);
401
+ this.applyCanvasTransforms(entry, this.params.vertical);
389
402
  entry.drawLines(peaks, absmax, halfH, offsetY, start, end);
390
403
  });
391
404
  }
@@ -424,6 +437,7 @@ export default class MultiCanvas extends Drawer {
424
437
  if (intersection.x1 < intersection.x2) {
425
438
  const { waveColor, progressColor } = this.params.splitChannelsOptions.channelColors[channelIndex] || {};
426
439
  this.setFillStyles(entry, waveColor, progressColor);
440
+ this.applyCanvasTransforms(entry, this.params.vertical);
427
441
 
428
442
  entry.fillRects(
429
443
  intersection.x1 - leftOffset,
@@ -542,6 +556,16 @@ export default class MultiCanvas extends Drawer {
542
556
  entry.setFillStyles(waveColor, progressColor);
543
557
  }
544
558
 
559
+ /**
560
+ * Set the canvas transforms for a certain entry (wave and progress)
561
+ *
562
+ * @param {CanvasEntry} entry Target entry
563
+ * @param {boolean} vertical Whether to render the waveform vertically
564
+ */
565
+ applyCanvasTransforms(entry, vertical = false) {
566
+ entry.applyCanvasTransforms(vertical);
567
+ }
568
+
545
569
  /**
546
570
  * Return image data of the multi-canvas
547
571
  *
@@ -326,7 +326,9 @@ export default class CursorPlugin {
326
326
  * @returns {number} outer width
327
327
  */
328
328
  outerWidth(element) {
329
- if (!element) return 0;
329
+ if (!element) {
330
+ return 0;
331
+ }
330
332
 
331
333
  let width = element.offsetWidth;
332
334
  let style = getComputedStyle(element);
@@ -0,0 +1,282 @@
1
+
2
+ /**
3
+ * @typedef {Object} MarkerParams
4
+ * @desc The parameters used to describe a marker.
5
+ * @example wavesurfer.addMarker(regionParams);
6
+ * @property {number} time The time to set the marker at
7
+ * @property {?label} string An optional marker label
8
+ * @property {?color} string Background color for marker
9
+ * @property {?position} string "top" or "bottom", defaults to "bottom"
10
+ * @property {?markerElement} element An HTML element to display instead of the default marker image
11
+ */
12
+
13
+
14
+ /**
15
+ * Markers are points in time in the audio that can be jumped to.
16
+ *
17
+ * @implements {PluginClass}
18
+ *
19
+ * @example
20
+ * import MarkersPlugin from 'wavesurfer.markers.js';
21
+ *
22
+ * // if you are using <script> tags
23
+ * var MarkerPlugin = window.WaveSurfer.markers;
24
+ *
25
+ * // ... initialising wavesurfer with the plugin
26
+ * var wavesurfer = WaveSurfer.create({
27
+ * // wavesurfer options ...
28
+ * plugins: [
29
+ * MarkersPlugin.create({
30
+ * // plugin options ...
31
+ * })
32
+ * ]
33
+ * });
34
+ */
35
+
36
+ const DEFAULT_FILL_COLOR = "#D8D8D8";
37
+ const DEFAULT_POSITION = "bottom";
38
+
39
+ export default class MarkersPlugin {
40
+ /**
41
+ * @typedef {Object} MarkersPluginParams
42
+ * @property {?MarkerParams[]} markers Initial set of markers
43
+ * @fires MarkersPlugin#marker-click
44
+ */
45
+
46
+ /**
47
+ * Markers plugin definition factory
48
+ *
49
+ * This function must be used to create a plugin definition which can be
50
+ * used by wavesurfer to correctly instantiate the plugin.
51
+ *
52
+ * @param {MarkersPluginParams} params parameters use to initialise the plugin
53
+ * @since 4.6.0
54
+ * @return {PluginDefinition} an object representing the plugin
55
+ */
56
+ static create(params) {
57
+ return {
58
+ name: 'markers',
59
+ deferInit: params && params.deferInit ? params.deferInit : false,
60
+ params: params,
61
+ staticProps: {
62
+ addMarker(options) {
63
+ if (!this.initialisedPluginList.markers) {
64
+ this.initPlugin('markers');
65
+ }
66
+ return this.markers.add(options);
67
+ },
68
+ clearMarkers() {
69
+ this.markers && this.markers.clear();
70
+ }
71
+ },
72
+ instance: MarkersPlugin
73
+ };
74
+ }
75
+
76
+ constructor(params, ws) {
77
+ this.params = params;
78
+ this.wavesurfer = ws;
79
+ this.util = ws.util;
80
+ this.style = this.util.style;
81
+ this.markerWidth = 11;
82
+ this.markerHeight = 22;
83
+
84
+
85
+ this._onResize = () => {
86
+ this._updateMarkerPositions();
87
+ };
88
+
89
+ this._onBackendCreated = () => {
90
+ this.wrapper = this.wavesurfer.drawer.wrapper;
91
+ if (this.params.markers) {
92
+ this.params.markers.forEach(marker => this.add(marker));
93
+ }
94
+ window.addEventListener('resize', this._onResize, true);
95
+ window.addEventListener('orientationchange', this._onResize, true);
96
+ this.wavesurfer.on('zoom', this._onResize);
97
+ };
98
+
99
+ this.markers = [];
100
+ this._onReady = () => {
101
+ this.wrapper = this.wavesurfer.drawer.wrapper;
102
+ this._updateMarkerPositions();
103
+ };
104
+ }
105
+
106
+ init() {
107
+ // Check if ws is ready
108
+ if (this.wavesurfer.isReady) {
109
+ this._onBackendCreated();
110
+ this._onReady();
111
+ } else {
112
+ this.wavesurfer.once('ready', this._onReady);
113
+ this.wavesurfer.once('backend-created', this._onBackendCreated);
114
+ }
115
+ }
116
+
117
+ destroy() {
118
+ this.wavesurfer.un('ready', this._onReady);
119
+ this.wavesurfer.un('backend-created', this._onBackendCreated);
120
+
121
+ this.wavesurfer.un('zoom', this._onResize);
122
+
123
+ window.removeEventListener('resize', this._onResize, true);
124
+ window.removeEventListener('orientationchange', this._onResize, true);
125
+
126
+ this.clear();
127
+ }
128
+
129
+ /**
130
+ * Add a marker
131
+ *
132
+ * @param {MarkerParams} params Marker definition
133
+ * @return {object} The created marker
134
+ */
135
+ add(params) {
136
+ let marker = {
137
+ time: params.time,
138
+ label: params.label,
139
+ color: params.color || DEFAULT_FILL_COLOR,
140
+ position: params.position || DEFAULT_POSITION
141
+ };
142
+
143
+ if (params.markerElement) {
144
+ this.markerWidth = params.markerElement.width;
145
+ this.markerHeight = params.markerElement.height;
146
+ }
147
+
148
+ marker.el = this._createMarkerElement(marker, params.markerElement);
149
+
150
+ this.wrapper.appendChild(marker.el);
151
+ this.markers.push(marker);
152
+ this._updateMarkerPositions();
153
+
154
+ return marker;
155
+ }
156
+
157
+ /**
158
+ * Remove a marker
159
+ *
160
+ * @param {number} index Index of the marker to remove
161
+ */
162
+ remove(index) {
163
+ let marker = this.markers[index];
164
+ if (!marker) {
165
+ return;
166
+ }
167
+
168
+ this.wrapper.removeChild(marker.el);
169
+ this.markers.splice(index, 1);
170
+ }
171
+
172
+ _createPointerSVG(color, position) {
173
+ const svgNS = "http://www.w3.org/2000/svg";
174
+
175
+ const el = document.createElementNS(svgNS, "svg");
176
+ const polygon = document.createElementNS(svgNS, "polygon");
177
+
178
+ el.setAttribute("viewBox", "0 0 40 80");
179
+
180
+ polygon.setAttribute("id", "polygon");
181
+ polygon.setAttribute("stroke", "#979797");
182
+ polygon.setAttribute("fill", color);
183
+ polygon.setAttribute("points", "20 0 40 30 40 80 0 80 0 30");
184
+ if ( position == "top" ) {
185
+ polygon.setAttribute("transform", "rotate(180, 20 40)");
186
+ }
187
+
188
+ el.appendChild(polygon);
189
+
190
+ this.style(el, {
191
+ width: this.markerWidth + "px",
192
+ height: this.markerHeight + "px",
193
+ "min-width": this.markerWidth + "px",
194
+ "margin-right": "5px",
195
+ "z-index": 4
196
+ });
197
+ return el;
198
+ }
199
+
200
+ _createMarkerElement(marker, markerElement) {
201
+ let label = marker.label;
202
+ let time = marker.time;
203
+
204
+ const el = document.createElement('marker');
205
+ el.className = "wavesurfer-marker";
206
+
207
+ this.style(el, {
208
+ position: "absolute",
209
+ height: "100%",
210
+ display: "flex",
211
+ overflow: "hidden",
212
+ "flex-direction": (marker.position == "top" ? "column-reverse" : "column")
213
+ });
214
+
215
+ const line = document.createElement('div');
216
+ this.style(line, {
217
+ "flex-grow": 1,
218
+ "margin-left": (this.markerWidth / 2 - 0.5) + "px",
219
+ background: "black",
220
+ width: "1px",
221
+ opacity: 0.1
222
+ });
223
+ el.appendChild(line);
224
+
225
+ const labelDiv = document.createElement('div');
226
+ const point = markerElement || this._createPointerSVG(marker.color, marker.position);
227
+ labelDiv.appendChild(point);
228
+
229
+ if ( label ) {
230
+ const labelEl = document.createElement('span');
231
+ labelEl.innerText = label;
232
+ this.style(labelEl, {
233
+ "font-family": "monospace",
234
+ "font-size": "90%"
235
+ });
236
+ labelDiv.appendChild(labelEl);
237
+ }
238
+
239
+ this.style(labelDiv, {
240
+ display: "flex",
241
+ "align-items": "center",
242
+ cursor: "pointer"
243
+ });
244
+
245
+ el.appendChild(labelDiv);
246
+
247
+ labelDiv.addEventListener("click", e => {
248
+ e.stopPropagation();
249
+ this.wavesurfer.setCurrentTime(time);
250
+ this.wavesurfer.fireEvent("marker-click", marker, e);
251
+ });
252
+
253
+ return el;
254
+ }
255
+
256
+ _updateMarkerPositions() {
257
+ const duration = this.wavesurfer.getDuration();
258
+
259
+ for ( let i = 0 ; i < this.markers.length; i++ ) {
260
+ 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
+ });
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Remove all markers
276
+ */
277
+ clear() {
278
+ while ( this.markers.length > 0 ) {
279
+ this.remove(0);
280
+ }
281
+ }
282
+ }