wavesurfer.js 6.3.0 → 6.5.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 (59) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +36 -47
  3. package/dist/plugin/wavesurfer.cursor.js +45 -54
  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 +24 -51
  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 +62 -74
  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 +8 -16
  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 +48 -63
  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 +20 -69
  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 +6 -30
  28. package/dist/plugin/wavesurfer.playhead.js.map +1 -1
  29. package/dist/plugin/wavesurfer.playhead.min.js +2 -2
  30. package/dist/plugin/wavesurfer.playhead.min.js.map +1 -1
  31. package/dist/plugin/wavesurfer.regions.js +203 -260
  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 +55 -147
  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 +53 -88
  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 +34 -54
  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 +490 -1003
  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 +11 -11
  52. package/src/mediaelement.js +3 -0
  53. package/src/plugin/cursor/index.js +15 -3
  54. package/src/plugin/markers/index.js +40 -1
  55. package/src/plugin/regions/index.js +4 -0
  56. package/src/plugin/regions/region.js +76 -1
  57. package/src/util/silence-mode.js +7 -0
  58. package/src/wavesurfer.js +4 -0
  59. package/CHANGES.md +0 -482
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wavesurfer.js",
3
- "version": "6.3.0",
3
+ "version": "6.5.0",
4
4
  "description": "Interactive navigable audio visualization using Web Audio and Canvas",
5
5
  "main": "dist/wavesurfer.js",
6
6
  "directories": {
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "repository": {
37
37
  "type": "git",
38
- "url": "git://github.com/katspaugh/wavesurfer.js.git"
38
+ "url": "git@github.com:wavesurfer-js/wavesurfer.js.git"
39
39
  },
40
40
  "publishConfig": {
41
41
  "registry": "https://registry.npmjs.org"
@@ -43,7 +43,7 @@
43
43
  "author": "",
44
44
  "license": "BSD-3-Clause",
45
45
  "bugs": {
46
- "url": "https://github.com/katspaugh/wavesurfer.js/issues"
46
+ "url": "https://github.com/wavesurfer-js/wavesurfer.js/issues"
47
47
  },
48
48
  "keywords": [
49
49
  "waveform",
@@ -55,13 +55,13 @@
55
55
  "visualization"
56
56
  ],
57
57
  "devDependencies": {
58
- "@babel/core": "^7.19.3",
58
+ "@babel/core": "^7.20.12",
59
59
  "@babel/eslint-parser": "^7.19.1",
60
60
  "@babel/plugin-proposal-class-properties": "^7.18.6",
61
- "@babel/preset-env": "^7.19.3",
61
+ "@babel/preset-env": "^7.20.2",
62
62
  "@babel/register": "^7.18.9",
63
63
  "@chiragrupani/karma-chromium-edge-launcher": "^2.2.2",
64
- "babel-loader": "^8.2.5",
64
+ "babel-loader": "^9.1.2",
65
65
  "babel-plugin-add-module-exports": "^1.0.4",
66
66
  "babel-plugin-istanbul": "^6.1.1",
67
67
  "date-fns": "^2.29.3",
@@ -73,10 +73,10 @@
73
73
  "esdoc-ecmascript-proposal-plugin": "^1.0.0",
74
74
  "esdoc-integrate-test-plugin": "^1.0.0",
75
75
  "esdoc-standard-plugin": "^1.0.0",
76
- "eslint": "^8.24.0",
76
+ "eslint": "^8.26.0",
77
77
  "htmlhint": "^1.1.4",
78
78
  "in-publish": "^2.0.1",
79
- "jasmine-core": "^4.4.0",
79
+ "jasmine-core": "^4.5.0",
80
80
  "karma": "^6.4.1",
81
81
  "karma-chrome-launcher": "3.1.1",
82
82
  "karma-coverage": "^2.2.0",
@@ -85,11 +85,11 @@
85
85
  "karma-jasmine-matchers": "5.0.0",
86
86
  "karma-verbose-reporter": "0.0.8",
87
87
  "karma-webpack": "^5.0.0",
88
- "lint-staged": "^13.0.3",
88
+ "lint-staged": "^13.1.0",
89
89
  "load-script": "^2.0.0",
90
90
  "pre-commit": "^1.2.2",
91
- "webpack": "^5.74.0",
92
- "webpack-cli": "^4.10.0",
91
+ "webpack": "^5.75.0",
92
+ "webpack-cli": "^5.0.1",
93
93
  "webpack-dev-server": "^4.11.1",
94
94
  "webpack-merge": "^5.8.0"
95
95
  },
@@ -65,6 +65,9 @@ export default class MediaElement extends WebAudio {
65
65
  this.mediaListeners.error = () => {
66
66
  this.fireEvent('error', 'Error loading media element');
67
67
  };
68
+ this.mediaListeners.waiting = () => {
69
+ this.fireEvent('waiting');
70
+ };
68
71
  this.mediaListeners.canplay = () => {
69
72
  this.fireEvent('canplay');
70
73
  };
@@ -17,6 +17,7 @@
17
17
  * the cursor follow the x and the y-position of the mouse. Use `false` to make the
18
18
  * it only follow the x-position of the mouse.
19
19
  * @property {function} formatTimeCallback Formats the timestamp on the cursor.
20
+ * @property {boolean} isDestroyCalled true if called destroy before the ready event fired
20
21
  */
21
22
 
22
23
  /**
@@ -140,11 +141,19 @@ export default class CursorPlugin {
140
141
  * @type {?HTMLElement}
141
142
  */
142
143
  this.displayTime = null;
144
+ /**
145
+ * true when `destroy` was called before `ready` event
146
+ * @type {boolean}
147
+ */
148
+ this.isDestroyCalled = false;
143
149
 
144
150
  this.params = Object.assign({}, this.defaultParams, params);
145
151
  }
146
152
 
147
153
  _onReady() {
154
+ if (this.isDestroyCalled) {
155
+ return;
156
+ }
148
157
  this.wrapper = this.wavesurfer.drawer.wrapper;
149
158
  this.cursor = this.util.withOrientation(this.wrapper.appendChild(
150
159
  document.createElement('cursor'),
@@ -237,10 +246,14 @@ export default class CursorPlugin {
237
246
  * Destroy the plugin (used by the Plugin API)
238
247
  */
239
248
  destroy() {
249
+ if (!this.cursorTime || !this.showTime){
250
+ this.isDestroyCalled = true;
251
+ return;
252
+ }
240
253
  if (this.params.showTime) {
241
- this.showTime.remove();
254
+ this.showTime && this.showTime.remove();
242
255
  }
243
- this.cursor.remove();
256
+ this.cursor && this.cursor.remove();
244
257
  this.wrapper.removeEventListener('mousemove', this._onMousemove);
245
258
  if (this.params.hideOnBlur) {
246
259
  this.wrapper.removeEventListener('mouseenter', this._onMouseenter);
@@ -323,7 +336,6 @@ export default class CursorPlugin {
323
336
  */
324
337
  formatTime(cursorTime) {
325
338
  cursorTime = isNaN(cursorTime) ? 0 : cursorTime;
326
-
327
339
  if (this.params.formatTimeCallback) {
328
340
  return this.params.formatTimeCallback(cursorTime);
329
341
  }
@@ -4,6 +4,7 @@
4
4
  * @example wavesurfer.addMarker(regionParams);
5
5
  * @property {number} time The time to set the marker at
6
6
  * @property {?label} string An optional marker label
7
+ * @property {?tooltip} string An optional marker tooltip
7
8
  * @property {?color} string Background color for marker
8
9
  * @property {?position} string "top" or "bottom", defaults to "bottom"
9
10
  * @property {?markerElement} element An HTML element to display instead of the default marker image
@@ -159,6 +160,7 @@ export default class MarkersPlugin {
159
160
  let marker = {
160
161
  time: params.time,
161
162
  label: params.label,
163
+ tooltip: params.tooltip,
162
164
  color: params.color || DEFAULT_FILL_COLOR,
163
165
  position: params.position || DEFAULT_POSITION,
164
166
  draggable: !!params.draggable,
@@ -171,6 +173,8 @@ export default class MarkersPlugin {
171
173
  this.markers.push(marker);
172
174
  this._updateMarkerPositions();
173
175
 
176
+ this._registerEvents();
177
+
174
178
  return marker;
175
179
  }
176
180
 
@@ -199,6 +203,8 @@ export default class MarkersPlugin {
199
203
 
200
204
  this.wrapper.removeChild(marker.el);
201
205
  this.markers.splice(index, 1);
206
+
207
+ this._unregisterEvents();
202
208
  }
203
209
 
204
210
  _createPointerSVG(color, position) {
@@ -231,6 +237,7 @@ export default class MarkersPlugin {
231
237
 
232
238
  _createMarkerElement(marker, markerElement) {
233
239
  let label = marker.label;
240
+ let tooltip = marker.tooltip;
234
241
 
235
242
  const el = document.createElement('marker');
236
243
  el.className = "wavesurfer-marker";
@@ -265,8 +272,9 @@ export default class MarkersPlugin {
265
272
  if ( label ) {
266
273
  const labelEl = document.createElement('span');
267
274
  labelEl.innerText = label;
275
+ labelEl.setAttribute('title', tooltip);
268
276
  this.style(labelEl, {
269
- "font-family": "monospace",
277
+ "font-family": "inherit",
270
278
  "font-size": "90%"
271
279
  });
272
280
  labelDiv.appendChild(labelEl);
@@ -386,6 +394,37 @@ export default class MarkersPlugin {
386
394
  this.wavesurfer.fireEvent("marker-drop", this.selectedMarker, event);
387
395
  }
388
396
 
397
+ _registerEvents() {
398
+ if (!this.markers.find(marker => marker.draggable)) {
399
+ return;
400
+ }
401
+ //we have some draggable markers, check for listeners
402
+ if (!this.onMouseMove) {
403
+ this.onMouseMove = (e) => this._onMouseMove(e);
404
+ window.addEventListener('mousemove', this.onMouseMove);
405
+ }
406
+
407
+ if (!this.onMouseUp) {
408
+ this.onMouseUp = (e) => this._onMouseUp(e);
409
+ window.addEventListener("mouseup", this.onMouseUp);
410
+ }
411
+ }
412
+
413
+ _unregisterEvents() {
414
+ if (this.markers.find(marker => marker.draggable)) {
415
+ return;
416
+ }
417
+ //we don't have any draggable markers, unregister listeners
418
+ if (this.onMouseMove) {
419
+ window.removeEventListener('mousemove', this.onMouseMove);
420
+ this.onMouseMove = null;
421
+ }
422
+ if (this.onMouseUp) {
423
+ window.removeEventListener("mouseup", this.onMouseUp);
424
+ this.onMouseUp = null;
425
+ }
426
+ }
427
+
389
428
  /**
390
429
  * Remove all markers
391
430
  */
@@ -4,6 +4,8 @@
4
4
  * @typedef {Object} RegionsPluginParams
5
5
  * @property {?boolean} dragSelection Enable creating regions by dragging with
6
6
  * the mouse
7
+ * @property {?boolean} contentEditable=false Allow/disallow editing content of the region
8
+ * @property {?boolean} removeButton=false adds remove region button
7
9
  * @property {?RegionParams[]} regions Regions that should be added upon
8
10
  * initialisation
9
11
  * @property {number} slop=2 The sensitivity of the mouse dragging
@@ -203,6 +205,8 @@ export default class RegionsPlugin {
203
205
 
204
206
  params = {
205
207
  edgeScrollWidth: this.params.edgeScrollWidth || this.defaultEdgeScrollWidth,
208
+ contentEditable: this.params.contentEditable,
209
+ removeButton: this.params.removeButton,
206
210
  ...params
207
211
  };
208
212
 
@@ -28,6 +28,8 @@ export class Region {
28
28
  this.resize =
29
29
  params.resize === undefined ? true : Boolean(params.resize);
30
30
  this.drag = params.drag === undefined ? true : Boolean(params.drag);
31
+ this.contentEditable = Boolean(params.contentEditable);
32
+ this.removeButton = Boolean(params.removeButton);
31
33
  // reflect resize and drag state of region for region-updated listener
32
34
  this.isResizing = false;
33
35
  this.isDragging = false;
@@ -196,6 +198,40 @@ export class Region {
196
198
  top: this.marginTop
197
199
  });
198
200
 
201
+ /* Button Remove Region */
202
+ if (this.removeButton){
203
+ const removeButtonEl = document.createElement('div');
204
+ removeButtonEl.className = 'remove-region-button';
205
+ removeButtonEl.innerText = 'x';
206
+ this.removeButtonEl = this.element.appendChild(removeButtonEl);
207
+ const css = {
208
+ zIndex: 4,
209
+ position: 'absolute',
210
+ bottom: 0,
211
+ right: 0,
212
+ cursor:'pointer',
213
+ marginRight: '3px',
214
+ fontSize: '80%',
215
+ color: 'grey',
216
+ height: '14px'
217
+ };
218
+ this.style(this.removeButtonEl, css);
219
+ }
220
+
221
+ /* Edit content */
222
+ if (this.contentEditable){
223
+ const contentEl = document.createElement('div');
224
+ contentEl.className = 'region-content';
225
+ contentEl.contentEditable = true;
226
+ contentEl.innerText = this.data.text || '';
227
+ this.contentEl = this.element.appendChild(contentEl);
228
+ const css = {
229
+ zIndex: 4,
230
+ padding: '2px 5px',
231
+ cursor:'text'};
232
+ this.style(this.contentEl, css);
233
+ }
234
+
199
235
  /* Resize handles */
200
236
  if (this.resize) {
201
237
  this.handleLeftEl = this.util.withOrientation(
@@ -404,6 +440,22 @@ export class Region {
404
440
  if (this.drag || this.resize) {
405
441
  this.bindDragEvents();
406
442
  }
443
+
444
+ /* Edit content */
445
+ if (this.contentEditable){
446
+ this.contentEl.addEventListener('blur', this.onContentBlur.bind(this));
447
+ this.contentEl.addEventListener('click', this.onContentClick.bind(this));
448
+ }
449
+ /* Remove button */
450
+ if (this.removeButton){
451
+ this.removeButtonEl.addEventListener('click', this.onRemove.bind(this));
452
+ }
453
+
454
+ this.on('remove', () => {
455
+ this.contentEl.removeEventListener('blur', this.onContentBlur.bind(this));
456
+ this.contentEl.removeEventListener('click', this.onContentClick.bind(this));
457
+ this.removeButtonEl.removeEventListener('click', this.onRemove.bind(this));
458
+ });
407
459
  }
408
460
 
409
461
  bindDragEvents() {
@@ -586,6 +638,7 @@ export class Region {
586
638
  const onMove = (event) => {
587
639
  const duration = this.wavesurfer.getDuration();
588
640
  let orientedEvent = this.util.withOrientation(event, this.vertical);
641
+ let delta = null;
589
642
 
590
643
  if (event.touches && event.touches.length > 1) {
591
644
  return;
@@ -632,7 +685,9 @@ export class Region {
632
685
  }
633
686
  } else if (resize === 'end') {
634
687
  if (time < this.start + minLength) {
688
+ // Calculate the end time based on the min length of the region.
635
689
  time = this.start + minLength;
690
+ delta = time - (this.end + (time - startTime));
636
691
  }
637
692
 
638
693
  if (time > duration) {
@@ -641,7 +696,10 @@ export class Region {
641
696
  }
642
697
  }
643
698
 
644
- let delta = time - startTime;
699
+ if (!delta) {
700
+ delta = time - startTime;
701
+ }
702
+
645
703
  startTime = time;
646
704
 
647
705
  // Drag
@@ -794,6 +852,23 @@ export class Region {
794
852
  }
795
853
  }
796
854
 
855
+ onContentBlur(event){
856
+ const {text: oldText} = this.data || {};
857
+ const text = event.target.innerText;
858
+ const data = {...this.data, text };
859
+ const eventParams = {action: 'contentEdited', oldText, text};
860
+ this.update({data}, eventParams);
861
+ }
862
+
863
+ onContentClick(event){
864
+ event.stopPropagation();
865
+ }
866
+
867
+ onRemove(event){
868
+ event.stopPropagation();
869
+ this.remove();
870
+ }
871
+
797
872
  updateHandlesResize(resize) {
798
873
  let cursorStyle;
799
874
  if (resize) {
@@ -10,6 +10,13 @@
10
10
  * @since 5.2.0
11
11
  */
12
12
  export default function ignoreSilenceMode() {
13
+ // Set webaudio context with 1 second silent audio 44100 bit rate buffer to allow playing audio even if silent switch is on the device
14
+ const silentAC = new AudioContext();
15
+ const silentBS = silentAC.createBufferSource();
16
+ silentBS.buffer = silentAC.createBuffer(1, 1, 44100);
17
+ silentBS.connect(silentAC.destination);
18
+ silentBS.start();
19
+
13
20
  // Set the src to a short bit of url encoded as a silent mp3
14
21
  // NOTE The silence MP3 must be high quality, when web audio sounds are played
15
22
  // in parallel the web audio sound is mixed to match the bitrate of the html sound
package/src/wavesurfer.js CHANGED
@@ -1004,6 +1004,10 @@ export default class WaveSurfer extends util.Observer {
1004
1004
  * @emits WaveSurfer#volume
1005
1005
  */
1006
1006
  setVolume(newVolume) {
1007
+ if (this.isMuted === true) {
1008
+ this.savedVolume = newVolume;
1009
+ return;
1010
+ }
1007
1011
  this.backend.setVolume(newVolume);
1008
1012
  this.fireEvent('volume', newVolume);
1009
1013
  }