@checksub_team/peaks_timeline 2.3.0-alpha.1 → 2.3.0-alpha.2

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.
@@ -98,7 +98,7 @@ define([
98
98
  this.refreshIndicators();
99
99
  };
100
100
 
101
- LineIndicator.prototype._createIndicator = function(lineGroup, type, text, subText) {
101
+ LineIndicator.prototype._createIndicator = function(lineGroup, type, text, subText, defaultStyle = {}) {
102
102
  const indicator = new Konva.Group();
103
103
  let indicatorHeight = 0;
104
104
  var self = this;
@@ -106,13 +106,13 @@ define([
106
106
  var textNode, iconNode, subTextNode;
107
107
 
108
108
  if (text) {
109
- [textGroup, textNode] = this._createIndicatorText(text);
109
+ [textGroup, textNode] = this._createIndicatorText(text, 'line-indicator-text', defaultStyle);
110
110
 
111
111
  indicator.add(textGroup);
112
112
  indicatorHeight += textGroup.getAttr('trueHeight') + this._topPadding;
113
113
  }
114
114
 
115
- [iconGroup, iconNode] = this._createIndicatorIcon(type);
115
+ [iconGroup, iconNode] = this._createIndicatorIcon(type, 'line-indicator-icon', defaultStyle);
116
116
 
117
117
  iconGroup.y(indicatorHeight);
118
118
  indicator.add(iconGroup);
@@ -121,7 +121,7 @@ define([
121
121
  if (subText) {
122
122
  indicatorHeight += this._bottomPadding;
123
123
 
124
- [subTextGroup, subTextNode] = this._createIndicatorText(subText);
124
+ [subTextGroup, subTextNode] = this._createIndicatorText(subText, 'line-indicator-subText', defaultStyle);
125
125
 
126
126
  subTextGroup.y(indicatorHeight);
127
127
  indicator.add(subTextGroup);
@@ -217,17 +217,19 @@ define([
217
217
  return indicator;
218
218
  };
219
219
 
220
- LineIndicator.prototype._createIndicatorText = function(text) {
220
+ LineIndicator.prototype._createIndicatorText = function(text, role, defaultStyle = {}) {
221
+ defaultStyle = defaultStyle[role] || {};
221
222
  const textGroup = new Konva.Group();
222
223
 
223
224
  const textNode = new Konva.Text({
224
225
  text: text,
225
226
  fontSize: this._sizes.font,
226
227
  fontFamily: this._peaks.options.lineIndicatorFont,
227
- fill: this._peaks.options.lineIndicatorTextColor,
228
+ fill: defaultStyle.fill || this._peaks.options.lineIndicatorTextColor,
228
229
  align: 'center',
229
230
  width: this._width,
230
- listening: false
231
+ listening: false,
232
+ name: role
231
233
  });
232
234
 
233
235
  textNode.setAttr('defaultColor', this._peaks.options.lineIndicatorTextColor);
@@ -251,7 +253,8 @@ define([
251
253
  return [textGroup, textNode];
252
254
  };
253
255
 
254
- LineIndicator.prototype._createIndicatorIcon = function(type) {
256
+ LineIndicator.prototype._createIndicatorIcon = function(type, role, defaultStyle = {}) {
257
+ defaultStyle = defaultStyle[role] || {};
255
258
  type = this._types.includes(type) ? type : 'default';
256
259
 
257
260
  const iconGroup = new Konva.Group();
@@ -264,9 +267,10 @@ define([
264
267
  x: this._width / 2,
265
268
  y: this._sizes.icon.default / 2,
266
269
  radius: this._sizes.icon.default / 2,
267
- fill: this._peaks.options.lineIndicatorIconColor,
270
+ fill: defaultStyle.fill || this._peaks.options.lineIndicatorIconColor,
268
271
  strokeWidth: 0,
269
- listening: false
272
+ listening: false,
273
+ name: role
270
274
  });
271
275
  }
272
276
  else {
@@ -274,12 +278,13 @@ define([
274
278
  x: (this._width - this._sizes.icon[type]) / 2,
275
279
  y: 0,
276
280
  data: SVGs[type].path,
277
- fill: this._peaks.options.lineIndicatorIconColor,
281
+ fill: defaultStyle.fill || this._peaks.options.lineIndicatorIconColor,
278
282
  scale: {
279
283
  x: (this._sizes.icon[type]) / SVGs[type].width,
280
284
  y: (this._sizes.icon[type]) / SVGs[type].height
281
285
  },
282
- listening: false
286
+ listening: false,
287
+ name: role
283
288
  });
284
289
  }
285
290
 
@@ -335,17 +340,20 @@ define([
335
340
  return;
336
341
  }
337
342
 
338
- if (indicatorData.type === line.indicatorType && indicatorData.text === line.indicatorText) {
343
+ if (indicatorData.type === line.indicatorType
344
+ && indicatorData.text === line.indicatorText
345
+ && indicatorData.subText === line.indicatorSubText) {
339
346
  return;
340
347
  }
341
348
 
342
- this.removeIndicator(line.id, true);
349
+ const styleData = this.removeIndicator(line.id, true);
343
350
 
344
351
  var indicator = this._createIndicator(
345
352
  indicatorData.lineGroup,
346
353
  line.indicatorType,
347
354
  line.indicatorText,
348
- line.indicatorSubText
355
+ line.indicatorSubText,
356
+ styleData
349
357
  );
350
358
 
351
359
  this._layer.add(indicator);
@@ -358,18 +366,51 @@ define([
358
366
  this.batchDraw();
359
367
  };
360
368
 
369
+ LineIndicator.prototype._getStyleData = function(konvaItem) {
370
+ if (!konvaItem) {
371
+ return {};
372
+ }
373
+
374
+ var styleData = {};
375
+ const name = konvaItem.name();
376
+
377
+ if (name) {
378
+ styleData[name] = {
379
+ fill: konvaItem.fill()
380
+ };
381
+ }
382
+
383
+ if (typeof konvaItem.getChildren === 'function') {
384
+ const children = konvaItem.getChildren();
385
+
386
+ if (children && children.length > 0) {
387
+ children.forEach(function(child) {
388
+ styleData = Object.assign(styleData, this._getStyleData(child));
389
+ }.bind(this));
390
+ }
391
+ }
392
+
393
+ return styleData;
394
+ };
395
+
361
396
  LineIndicator.prototype.removeIndicator = function(lineId, keepInList) {
397
+ var styleData = {};
398
+
362
399
  if (this._indicators[lineId]) {
363
- if (this._indicators[lineId].indicator) {
364
- this._indicators[lineId].indicator.destroy();
365
- }
400
+ const indicator = this._indicators[lineId].indicator;
401
+
366
402
  if (!keepInList) {
367
403
  delete this._indicators[lineId];
368
404
  }
369
405
  else {
370
406
  this._indicators[lineId].indicator = null;
407
+ styleData = this._getStyleData(indicator);
408
+ }
409
+ if (indicator) {
410
+ indicator.destroy();
371
411
  }
372
412
  }
413
+ return styleData;
373
414
  };
374
415
 
375
416
  LineIndicator.prototype.refreshIndicator = function(lineId) {
@@ -355,6 +355,15 @@ define([
355
355
  ModeLayer.prototype._onMouseMoveInCutMode = function() {
356
356
  var mousePosition = this._stage.getPointerPosition();
357
357
 
358
+ if (!mousePosition) {
359
+ return;
360
+ }
361
+
362
+ // In cut mode, cutting can replace Konva nodes under the pointer.
363
+ // Konva doesn't always emit enter/leave reliably across those updates,
364
+ // so keep the hovered element in sync as the mouse moves.
365
+ this._syncHoveredElementFromPointer();
366
+
358
367
  mousePosition.x = this._view.timeToPixels(this._view.pixelsToTime(mousePosition.x));
359
368
 
360
369
  var cuttingPosition = mousePosition;
@@ -458,8 +467,13 @@ define([
458
467
  if (node.attrs && node.attrs.sourceId) {
459
468
  var sourceGroup = this._view.getSourceGroupById(node.attrs.sourceId);
460
469
 
461
- if (sourceGroup && !sourceGroup.isHovered()) {
462
- sourceGroup.startHover();
470
+ if (sourceGroup) {
471
+ // Ensure the view state matches what is actually under the pointer.
472
+ this._view.setHoveredElement(sourceGroup);
473
+
474
+ if (!sourceGroup.isHovered()) {
475
+ sourceGroup.startHover();
476
+ }
463
477
  }
464
478
 
465
479
  return;
@@ -467,6 +481,9 @@ define([
467
481
 
468
482
  node = node.getParent ? node.getParent() : null;
469
483
  }
484
+
485
+ // Pointer isn't over a source; clear any stale hovered element.
486
+ this._view.setHoveredElement(null);
470
487
  };
471
488
 
472
489
  ModeLayer.prototype.setMode = function(mode) {
@@ -63,7 +63,7 @@ define([
63
63
  this._selected = this._source.selected;
64
64
  this._hovered = false;
65
65
  this._isDragged = false;
66
- this._isHandleDragging = false;
66
+ this._isHandleDragged = false;
67
67
  this._destroyed = false;
68
68
 
69
69
  // Optional Konva element used as a drag "ghost" preview.
@@ -238,7 +238,11 @@ define([
238
238
  };
239
239
 
240
240
  SourceGroup.prototype.isActive = function() {
241
- return this._isDragged || this._isHandleDragging;
241
+ return this._isDragged || this._isHandleDragged;
242
+ };
243
+
244
+ SourceGroup.prototype.isDragged = function() {
245
+ return this._isDragged;
242
246
  };
243
247
 
244
248
  SourceGroup.prototype.addToContent = function(newChild) {
@@ -250,7 +254,7 @@ define([
250
254
  }
251
255
  };
252
256
 
253
- SourceGroup.prototype.prepareDragEnd = function() {
257
+ SourceGroup.prototype._updateHandles = function() {
254
258
  var handleWidth = Math.min(this._peaks.options.sourceHandleWidth, this._width / 2);
255
259
 
256
260
  this._leftHandle.width(handleWidth);
@@ -309,8 +313,6 @@ define([
309
313
  this._currentTimeToPixelsScaleUsed = newTimeToPixelsScale;
310
314
 
311
315
  this._updateMarkers();
312
-
313
- this._rightHandle.x(this._width - this._rightHandle.width());
314
316
  }
315
317
  else {
316
318
  // the zoom was not changed, but the source was resized
@@ -324,6 +326,7 @@ define([
324
326
  }
325
327
  }
326
328
 
329
+ this._updateHandles();
327
330
  this._updateVolumeSlider();
328
331
  this._updateButtons();
329
332
  this._updateLoadingOverlay();
@@ -377,20 +380,14 @@ define([
377
380
  start: this._source.startTime,
378
381
  end: this._source.endTime
379
382
  };
380
- this._isHandleDragging = true;
381
-
383
+ this._isHandleDragged = true;
382
384
  this._hideButtons();
383
385
  };
384
386
 
385
387
  SourceGroup.prototype._onHandleDragEnd = function() {
386
- this._isHandleDragging = false;
388
+ this._isHandleDragged = false;
387
389
  this._showButtons();
388
-
389
- // When resizing via handles, drag events no longer bubble to the parent
390
- // group, so we won't hit the move-drag end path that calls prepareDragEnd.
391
- // Normalize handle geometry here so handles snap to the updated width.
392
- this.update();
393
- this.prepareDragEnd();
390
+ this._layer.processSourceUpdates([this._source]);
394
391
  };
395
392
 
396
393
  SourceGroup.prototype._addHandles = function(forceCreate) {
@@ -409,9 +406,6 @@ define([
409
406
  }
410
407
  });
411
408
 
412
- // Prevent handle drag events from bubbling to the parent group.
413
- // Otherwise the parent SourceGroup dragstart/dragend handlers run and
414
- // the sources-layer creates move-drag ghosts during resize.
415
409
  this._leftHandle.on('dragstart', function(event) {
416
410
  event.cancelBubble = true;
417
411
  self._onHandleDragStart(event);
@@ -445,9 +439,6 @@ define([
445
439
  }
446
440
  });
447
441
 
448
- // Prevent handle drag events from bubbling to the parent group.
449
- // Otherwise the parent SourceGroup dragstart/dragend handlers run and
450
- // the sources-layer creates move-drag ghosts during resize.
451
442
  this._rightHandle.on('dragstart', function(event) {
452
443
  event.cancelBubble = true;
453
444
  self._onHandleDragStart(event);
@@ -520,16 +511,15 @@ define([
520
511
  )
521
512
  );
522
513
 
523
- var actualX = this._group.x() + this._view.getFrameOffset();
524
514
  var x = Math.max(
525
515
  0,
526
- this._view.getFrameOffset() - actualX - 2 * radius
516
+ -(this._group.x() + 2 * radius)
527
517
  );
528
518
  var width = Math.min(
529
519
  this._width - x,
530
520
  this._view.getWidth() + 4 * radius - Math.max(
531
521
  0,
532
- actualX - this._view.getFrameOffset()
522
+ this._group.x()
533
523
  )
534
524
  );
535
525
 
@@ -559,7 +549,7 @@ define([
559
549
  if (this._selected) {
560
550
  backgroundColor = this._source.selectedBackgroundColor;
561
551
  }
562
- else if (this._hovered) {
552
+ else if (this._hovered && this._view.getCurrentMode() !== 'cut') {
563
553
  backgroundColor = this._source.hoverBackgroundColor;
564
554
  }
565
555
  else {
@@ -1010,12 +1000,15 @@ define([
1010
1000
  }
1011
1001
  }
1012
1002
 
1003
+ var self = this;
1004
+
1013
1005
  var waveform = new WaveformShape({
1014
- layer: this._layer,
1015
1006
  view: this._view,
1016
- source: this._source,
1007
+ color: this._source.color,
1017
1008
  height: preview.group.height(),
1018
- url: url
1009
+ waveformDataFunc: function() {
1010
+ return self._createWaveformPointsIterator(url);
1011
+ }
1019
1012
  });
1020
1013
 
1021
1014
  preview.group.add(waveform);
@@ -1028,6 +1021,101 @@ define([
1028
1021
  this._previewList.push(preview);
1029
1022
  };
1030
1023
 
1024
+ SourceGroup.prototype._createWaveformPointsIterator = function(url) {
1025
+ var loaded = this._layer.getLoadedData(url + '-scaled');
1026
+ var waveformData = loaded && loaded.data;
1027
+
1028
+ if (!waveformData) {
1029
+ return {
1030
+ next: function() {
1031
+ return { done: true };
1032
+ }
1033
+ };
1034
+ }
1035
+
1036
+ var view = this._view;
1037
+ var source = this._source;
1038
+
1039
+ var groupX = this._group && typeof this._group.x === 'function' ? this._group.x() : 0;
1040
+ var groupWidth = this._width;
1041
+ var viewWidth = view.getWidth();
1042
+
1043
+ var startPixel = 0;
1044
+ var startOffset = 0;
1045
+ var endPixel = Math.min(viewWidth, waveformData.length);
1046
+ var targetSpeed = 1.0;
1047
+
1048
+ if (source) {
1049
+ targetSpeed = source.targetSpeed || 1.0;
1050
+
1051
+ // Determine which part of the source is visible in the view based on
1052
+ // its current on-canvas geometry (supports dragging without relying on
1053
+ // startTime/endTime).
1054
+ var hiddenLeftPixels = Math.max(-groupX, 0);
1055
+ var hiddenRightPixels = Math.max(groupX + groupWidth - viewWidth, 0);
1056
+
1057
+ startPixel = Math.floor(
1058
+ (view.timeToPixels(source.mediaStartTime) + hiddenLeftPixels) * targetSpeed
1059
+ );
1060
+
1061
+ startOffset = view.timeToPixels(source.mediaStartTime);
1062
+
1063
+ endPixel = Math.min(
1064
+ Math.ceil(
1065
+ (view.timeToPixels(source.mediaEndTime) - hiddenRightPixels) * targetSpeed
1066
+ ),
1067
+ waveformData.length
1068
+ );
1069
+ }
1070
+
1071
+ if (startPixel < 0) {
1072
+ startPixel = 0;
1073
+ }
1074
+
1075
+ if (endPixel < startPixel) {
1076
+ endPixel = startPixel;
1077
+ }
1078
+
1079
+ var channels = waveformData.channels;
1080
+
1081
+ var channelData = new Array(channels);
1082
+
1083
+ for (var c = 0; c < channels; c++) {
1084
+ channelData[c] = waveformData.channel(c);
1085
+ }
1086
+
1087
+ var x = startPixel;
1088
+
1089
+ return {
1090
+ next: function() {
1091
+ if (x >= endPixel) {
1092
+ return { done: true };
1093
+ }
1094
+
1095
+ var min = new Array(channels);
1096
+ var max = new Array(channels);
1097
+
1098
+ for (var i = 0; i < channels; i++) {
1099
+ min[i] = channelData[i].min_sample(x);
1100
+ max[i] = channelData[i].max_sample(x);
1101
+ }
1102
+
1103
+ var value = {
1104
+ x: x / targetSpeed - startOffset + 0.5,
1105
+ min: min,
1106
+ max: max
1107
+ };
1108
+
1109
+ x++;
1110
+
1111
+ return {
1112
+ done: false,
1113
+ value: value
1114
+ };
1115
+ }
1116
+ };
1117
+ };
1118
+
1031
1119
  SourceGroup.prototype.getAudioPreview = function() {
1032
1120
  return this._previewList.filter(function(preview) {
1033
1121
  return preview.type === 'audio';
@@ -1892,12 +1980,14 @@ define([
1892
1980
  volumeSliderGroup.add(volumeSliderRect);
1893
1981
  volumeSliderGroup.add(volumeSliderLine);
1894
1982
 
1895
- volumeSliderGroup.on('dragstart', function() {
1983
+ volumeSliderGroup.on('dragstart', function(evt) {
1984
+ evt.cancelBubble = true;
1896
1985
  volumeText.visible(true);
1897
1986
  self._peaks.emit('source.startVolumeChange', self._source);
1898
1987
  });
1899
1988
 
1900
- volumeSliderGroup.on('dragmove', function() {
1989
+ volumeSliderGroup.on('dragmove', function(evt) {
1990
+ evt.cancelBubble = true;
1901
1991
  var volume = self._getVolumeFromY(volumeSliderGroup.y());
1902
1992
 
1903
1993
  volumeText.text((volume * 100).toFixed(0) + '%');
@@ -1908,7 +1998,8 @@ define([
1908
1998
  self._scheduleBatchDraw();
1909
1999
  });
1910
2000
 
1911
- volumeSliderGroup.on('dragend', function() {
2001
+ volumeSliderGroup.on('dragend', function(evt) {
2002
+ evt.cancelBubble = true;
1912
2003
  volumeText.visible(false);
1913
2004
  self._peaks.emit('source.endVolumeChange', self._source);
1914
2005
  });
@@ -155,22 +155,22 @@ define([
155
155
  const frameEndTime = this._view.pixelsToTime(frameOffset + width);
156
156
  var redraw = false;
157
157
  var isSourceGroupHovered = false;
158
- var isSourceGroupActive = false;
158
+ var isSourceGroupDragged = false;
159
159
 
160
160
  if (sourceGroup) {
161
161
  isSourceGroupHovered = sourceGroup.isHovered();
162
- isSourceGroupActive = sourceGroup.isActive();
162
+ isSourceGroupDragged = sourceGroup.isDragged();
163
163
  this._destroySourceGroup(source);
164
164
  redraw = true;
165
165
  }
166
166
 
167
- if (source.isVisible(frameStartTime, frameEndTime) || isSourceGroupActive) {
167
+ if (source.isVisible(frameStartTime, frameEndTime) || isSourceGroupDragged) {
168
168
  const newSourceGroup = this._addSourceGroup(source);
169
169
 
170
170
  if (isSourceGroupHovered) {
171
171
  newSourceGroup.startHover();
172
172
  }
173
- if (isSourceGroupActive) {
173
+ if (isSourceGroupDragged) {
174
174
  newSourceGroup.startDrag();
175
175
  }
176
176
  redraw = true;
@@ -429,7 +429,6 @@ define([
429
429
  if (sourceGroup) {
430
430
  // Clear dragging state before moving back to line group
431
431
  sourceGroup.setDragging(false);
432
- sourceGroup.prepareDragEnd();
433
432
  // Move source back to its line group (it was moved to layer during drag)
434
433
  self._lineGroups.addSource(source, sourceGroup);
435
434
  // Reset Y position to 0 relative to parent line group
@@ -444,10 +443,7 @@ define([
444
443
  this._draggedElementId = null;
445
444
 
446
445
  this.refresh();
447
- this._view.batchDrawSourcesLayer();
448
- this._view.updateTimelineLength();
449
-
450
- this._peaks.emit('sources.updated', updatedSources);
446
+ this.processSourceUpdates(updatedSources);
451
447
  };
452
448
 
453
449
  SourcesLayer.prototype.onSourcesGroupDrag = function(draggedElement) {
@@ -558,6 +554,13 @@ define([
558
554
  }
559
555
  };
560
556
 
557
+ SourcesLayer.prototype.processSourceUpdates = function(updatedSources) {
558
+ this._view.batchDrawSourcesLayer();
559
+ this._view.updateTimelineLength();
560
+
561
+ this._peaks.emit('sources.updated', updatedSources);
562
+ };
563
+
561
564
  SourcesLayer.prototype.findSources = function(startTime, endTime) {
562
565
  var sources = this._peaks.sourceHandler.find(startTime, endTime);
563
566
  var lineIds = this._lineGroups.getVisibleLines();
@@ -660,7 +663,7 @@ define([
660
663
  this.refresh();
661
664
  };
662
665
 
663
- // TODO: This assumes that no sources between the start and the end are unselected
666
+ // WARNING: This assumes that no sources between the start and the end are unselected
664
667
  SourcesLayer.prototype.manageSourceMovements = function(sources, newStartTime, newEndTime, orderable, mouseX,
665
668
  mouseY
666
669
  ) {
@@ -756,7 +759,10 @@ define([
756
759
  if (Utils.objectHasProperty(this._sourcesGroup, sourceId)) {
757
760
  var sourceGroup = this._sourcesGroup[sourceId];
758
761
 
759
- if (!sourceGroup.isActive()) {
762
+ if (sourceGroup.isActive()) {
763
+ sourceGroup.update();
764
+ }
765
+ else {
760
766
  var source = this._sourcesGroup[sourceId].getSource();
761
767
 
762
768
  if (!this._isSourceVisible(source, startTime, endTime)) {