@checksub_team/peaks_timeline 2.4.0 → 2.5.0-alpha.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.
@@ -69,6 +69,8 @@ define([
69
69
  // Used to suppress drag lifecycle callbacks when a dragged source is
70
70
  // temporarily stopped/restarted as part of a rebuild.
71
71
  this._suppressDragLifecycleForSourceId = null;
72
+ this._sourceHandleDragSession = null;
73
+ this._segmentDragSession = null;
72
74
 
73
75
  this._peaks.on('handler.sources.add', this._onSourcesAdd.bind(this));
74
76
  this._peaks.on('handler.sources.destroy', this._onSourcesDestroy.bind(this));
@@ -96,6 +98,10 @@ define([
96
98
  if (draggedSourceGroup) {
97
99
  draggedSourceGroup.stopDrag();
98
100
  }
101
+
102
+ if (this._segmentDragSession && this._segmentDragSession.draggedNode) {
103
+ this._segmentDragSession.draggedNode.stopDrag();
104
+ }
99
105
  };
100
106
 
101
107
  SourcesLayer.prototype.fitToView = function() {
@@ -114,6 +120,17 @@ define([
114
120
  return this._lineGroups.getSegmentsGroups();
115
121
  };
116
122
 
123
+ /**
124
+ * Returns all segments in the given segment group or displayed line whose start time is at or after the given time.
125
+ *
126
+ * @param {String|Number} lineId
127
+ * @param {Number} time
128
+ * @returns {Array<Segment>}
129
+ */
130
+ SourcesLayer.prototype.getSegmentsOnLineAfter = function(lineId, time) {
131
+ return this._lineGroups.getSegmentsOnLineAfter(lineId, time);
132
+ };
133
+
117
134
  SourcesLayer.prototype.add = function(element) {
118
135
  this._layer.add(element);
119
136
 
@@ -441,6 +458,341 @@ define([
441
458
  this._draggedElementsData = null;
442
459
  };
443
460
 
461
+ SourcesLayer.prototype.cleanupAfterSourceHandleDrag = function() {
462
+ this._sourceHandleDragSession = null;
463
+ };
464
+
465
+ SourcesLayer.prototype.onSourceHandleDragStart = function(sourceGroup, leftHandle) {
466
+ var source = sourceGroup.getSource();
467
+
468
+ this._sourceHandleDragSession = {
469
+ source: source,
470
+ leftHandle: leftHandle,
471
+ mouseDownX: this._view.getPointerPosition().x,
472
+ initialTimeOffset: this._view.getTimeOffset(),
473
+ initialStartTime: source.startTime,
474
+ initialEndTime: source.endTime
475
+ };
476
+ };
477
+
478
+ SourcesLayer.prototype.onSourceHandleDrag = function(draggedElement) {
479
+ this._view.updateWithAutoScroll(this._dragSourceHandle.bind(this), null, false);
480
+
481
+ return {
482
+ x: draggedElement.absolutePosition().x,
483
+ y: draggedElement.absolutePosition().y
484
+ };
485
+ };
486
+
487
+ SourcesLayer.prototype._dragSourceHandle = function() {
488
+ var session = this._sourceHandleDragSession;
489
+ var pointer = this._view.getPointerPosition();
490
+ var pointerX;
491
+ var diff;
492
+ var timeOffsetDiff;
493
+ var shouldRedraw;
494
+
495
+ if (!session) {
496
+ return;
497
+ }
498
+
499
+ pointerX = pointer ? pointer.x : session.mouseDownX;
500
+ diff = this._view.pixelsToTime(pointerX - session.mouseDownX);
501
+ timeOffsetDiff = this._view.getTimeOffset() - session.initialTimeOffset;
502
+ shouldRedraw = this.manageSourceMovements(
503
+ [session.source],
504
+ session.leftHandle ? session.initialStartTime + diff + timeOffsetDiff : null,
505
+ session.leftHandle ? null : session.initialEndTime + diff + timeOffsetDiff
506
+ );
507
+
508
+ if (shouldRedraw) {
509
+ this.batchDraw();
510
+ }
511
+ };
512
+
513
+ SourcesLayer.prototype.onSourceHandleDragEnd = function(sourceGroup) {
514
+ this.cleanupAfterSourceHandleDrag();
515
+ this.processSourceUpdates([sourceGroup.getSource()]);
516
+ };
517
+
518
+ SourcesLayer.prototype.isSegmentDragInProgress = function() {
519
+ return Boolean(this._segmentDragSession);
520
+ };
521
+
522
+ SourcesLayer.prototype.cleanupAfterSegmentDrag = function() {
523
+ this._segmentDragSession = null;
524
+ };
525
+
526
+ SourcesLayer.prototype._getSelectedSegmentsForDrag = function(draggedSegment) {
527
+ var selectedElements = this._view.getSelectedElements();
528
+ var selectedSegments;
529
+ var draggedSegmentIsSelected;
530
+
531
+ if (!Array.isArray(selectedElements)) {
532
+ selectedElements = Object.values(selectedElements);
533
+ }
534
+
535
+ selectedSegments = selectedElements.filter(function(element) {
536
+ return !Utils.isNullOrUndefined(element.line) && Utils.isNullOrUndefined(element.lineId);
537
+ });
538
+
539
+ draggedSegmentIsSelected = selectedSegments.some(function(segment) {
540
+ return segment.id === draggedSegment.id;
541
+ });
542
+
543
+ if (!draggedSegmentIsSelected) {
544
+ selectedSegments = [draggedSegment];
545
+ this._view.deselectAll();
546
+ }
547
+
548
+ return selectedSegments;
549
+ };
550
+
551
+ SourcesLayer.prototype._buildSegmentDragGroups = function(segments) {
552
+ var segmentsGroups = this._lineGroups.getSegmentsGroups();
553
+ var groupsByLine = {};
554
+
555
+ segments.forEach(function(segment) {
556
+ var groupContext = groupsByLine[segment.line];
557
+
558
+ if (!groupContext) {
559
+ groupContext = {
560
+ group: segmentsGroups[segment.line],
561
+ segments: [],
562
+ initialPositions: {}
563
+ };
564
+ groupsByLine[segment.line] = groupContext;
565
+ }
566
+
567
+ groupContext.segments.push(segment);
568
+ groupContext.initialPositions[segment.id] = {
569
+ startTime: segment.startTime,
570
+ endTime: segment.endTime
571
+ };
572
+ });
573
+
574
+ return Object.keys(groupsByLine).map(function(lineId) {
575
+ groupsByLine[lineId].segments.sort(function(a, b) {
576
+ return a.startTime - b.startTime;
577
+ });
578
+
579
+ return groupsByLine[lineId];
580
+ });
581
+ };
582
+
583
+ SourcesLayer.prototype.onSegmentDragStart = function(segmentShape) {
584
+ var segment = segmentShape.getSegment();
585
+ var selectedSegments = this._getSelectedSegmentsForDrag(segment);
586
+ var groups = this._buildSegmentDragGroups(selectedSegments);
587
+ var minTimeOffset = -Infinity;
588
+ var maxTimeOffset = Infinity;
589
+ var draggable = true;
590
+ var shouldClampToAdjacentGroups = !(groups.length === 1 && groups[0].segments.length === 1);
591
+
592
+ groups.forEach(function(groupContext) {
593
+ if (shouldClampToAdjacentGroups) {
594
+ var limits = groupContext.group.getSegmentsDragTimeLimits(
595
+ groupContext.segments,
596
+ groupContext.initialPositions
597
+ );
598
+
599
+ minTimeOffset = Math.max(minTimeOffset, limits.minTimeOffset);
600
+ maxTimeOffset = Math.min(maxTimeOffset, limits.maxTimeOffset);
601
+ }
602
+
603
+ draggable = draggable && groupContext.segments.every(function(draggedSegment) {
604
+ return draggedSegment.editable;
605
+ });
606
+ });
607
+
608
+ this._segmentDragSession = {
609
+ kind: 'move',
610
+ draggedNode: segmentShape,
611
+ draggedSegment: segment,
612
+ mouseDownX: this._view.getPointerPosition().x,
613
+ initialTimeOffset: this._view.getTimeOffset(),
614
+ groups: groups,
615
+ minTimeOffset: minTimeOffset,
616
+ maxTimeOffset: maxTimeOffset,
617
+ appliedTimeOffset: 0,
618
+ draggable: draggable
619
+ };
620
+
621
+ this._peaks.emit('segments.dragstart', segment);
622
+ };
623
+
624
+ SourcesLayer.prototype.onSegmentDrag = function(draggedElement) {
625
+ this._view.updateWithAutoScroll(this._dragSegments.bind(this), null, false);
626
+
627
+ return {
628
+ x: draggedElement.absolutePosition().x,
629
+ y: draggedElement.absolutePosition().y
630
+ };
631
+ };
632
+
633
+ SourcesLayer.prototype._dragSegments = function() {
634
+ var session = this._segmentDragSession;
635
+ var pointerPos;
636
+ var mousePos;
637
+ var timeDiff;
638
+ var timeOffsetDiff;
639
+ var requestedTimeOffset;
640
+ var appliedTimeOffset;
641
+ var groupContext;
642
+ var segment;
643
+ var initialPosition;
644
+
645
+ if (!session || session.kind !== 'move' || !session.draggable) {
646
+ return;
647
+ }
648
+
649
+ pointerPos = this._view.getPointerPosition();
650
+ mousePos = Math.min(
651
+ this._view.getWidth() - this._peaks.options.autoScrollThreshold * this._view.getWidth(),
652
+ Math.max(0, pointerPos ? pointerPos.x : session.mouseDownX)
653
+ );
654
+ timeDiff = this._view.pixelsToTime(mousePos - session.mouseDownX);
655
+ timeOffsetDiff = this._view.getTimeOffset() - session.initialTimeOffset;
656
+ requestedTimeOffset = Utils.roundTime(timeDiff + timeOffsetDiff);
657
+ appliedTimeOffset = Utils.clamp(
658
+ requestedTimeOffset,
659
+ session.minTimeOffset,
660
+ session.maxTimeOffset
661
+ );
662
+
663
+ if (appliedTimeOffset === session.appliedTimeOffset) {
664
+ return;
665
+ }
666
+
667
+ if (session.groups.length === 1 && session.groups[0].segments.length === 1) {
668
+ groupContext = session.groups[0];
669
+ segment = groupContext.segments[0];
670
+ initialPosition = groupContext.initialPositions[segment.id];
671
+
672
+ groupContext.group.updateSegment(
673
+ segment,
674
+ this._view.timeToPixels(initialPosition.startTime + appliedTimeOffset),
675
+ this._view.timeToPixels(initialPosition.endTime + appliedTimeOffset),
676
+ false
677
+ );
678
+ }
679
+ else {
680
+ session.groups.forEach(function(groupContext) {
681
+ groupContext.group.dragSegmentsByTimeOffset(
682
+ groupContext.segments,
683
+ groupContext.initialPositions,
684
+ appliedTimeOffset
685
+ );
686
+ });
687
+ }
688
+
689
+ session.appliedTimeOffset = appliedTimeOffset;
690
+ this._view.updateTimelineLength();
691
+ this.batchDraw();
692
+ };
693
+
694
+ SourcesLayer.prototype.onSegmentDragEnd = function(segmentShape) {
695
+ var session = this._segmentDragSession;
696
+
697
+ if (session && session.kind === 'move' && session.appliedTimeOffset !== 0) {
698
+ session.groups.forEach(function(groupContext) {
699
+ groupContext.group.markSegmentsUpdated(groupContext.segments);
700
+ });
701
+ this._view.updateTimelineLength();
702
+ }
703
+
704
+ this.cleanupAfterSegmentDrag();
705
+ this._peaks.emit('segments.dragend', segmentShape.getSegment());
706
+ };
707
+
708
+ SourcesLayer.prototype.onSegmentHandleDragStart = function(segmentMarker) {
709
+ this._segmentDragSession = {
710
+ kind: segmentMarker.isStartMarker() ? 'resize-start' : 'resize-end',
711
+ draggedNode: segmentMarker,
712
+ segment: segmentMarker.getSegment(),
713
+ group: this._lineGroups.getSegmentsGroups()[segmentMarker.getSegment().line],
714
+ mouseDownX: this._view.getPointerPosition().x,
715
+ initialTimeOffset: this._view.getTimeOffset(),
716
+ initialStartTime: segmentMarker.getSegment().startTime,
717
+ initialEndTime: segmentMarker.getSegment().endTime,
718
+ startMarker: segmentMarker.isStartMarker()
719
+ };
720
+
721
+ this._peaks.emit('segments.dragstart', segmentMarker.getSegment(), segmentMarker.isStartMarker());
722
+ };
723
+
724
+ SourcesLayer.prototype.onSegmentHandleDrag = function(segmentMarker) {
725
+ this._view.updateWithAutoScroll(this._dragSegmentHandle.bind(this), null, false);
726
+
727
+ return {
728
+ x: segmentMarker.getAbsolutePosition().x,
729
+ y: segmentMarker.getAbsolutePosition().y
730
+ };
731
+ };
732
+
733
+ SourcesLayer.prototype._dragSegmentHandle = function() {
734
+ var session = this._segmentDragSession;
735
+ var pointerPos;
736
+ var pointerX;
737
+ var timeDiff;
738
+ var timeOffsetDiff;
739
+ var newStartTime;
740
+ var newEndTime;
741
+
742
+ if (!session || (session.kind !== 'resize-start' && session.kind !== 'resize-end')) {
743
+ return;
744
+ }
745
+
746
+ pointerPos = this._view.getPointerPosition();
747
+ pointerX = pointerPos ? pointerPos.x : session.mouseDownX;
748
+ timeDiff = this._view.pixelsToTime(pointerX - session.mouseDownX);
749
+ timeOffsetDiff = this._view.getTimeOffset() - session.initialTimeOffset;
750
+
751
+ if (session.startMarker) {
752
+ newStartTime = Utils.roundTime(session.initialStartTime + timeDiff + timeOffsetDiff);
753
+
754
+ if (session.segment.duration) {
755
+ newStartTime = Math.max(
756
+ newStartTime,
757
+ Utils.roundTime(session.segment.endTime - session.segment.duration)
758
+ );
759
+ }
760
+
761
+ session.group.updateSegment(
762
+ session.segment,
763
+ this._view.timeToPixels(newStartTime),
764
+ null,
765
+ false
766
+ );
767
+ }
768
+ else {
769
+ newEndTime = Utils.roundTime(session.initialEndTime + timeDiff + timeOffsetDiff);
770
+
771
+ if (session.segment.duration) {
772
+ newEndTime = Math.min(
773
+ newEndTime,
774
+ Utils.roundTime(session.segment.startTime + session.segment.duration)
775
+ );
776
+ }
777
+
778
+ session.group.updateSegment(
779
+ session.segment,
780
+ null,
781
+ this._view.timeToPixels(newEndTime),
782
+ false
783
+ );
784
+ }
785
+
786
+ this._view.updateTimelineLength();
787
+ this.batchDraw();
788
+ };
789
+
790
+ SourcesLayer.prototype.onSegmentHandleDragEnd = function(segmentMarker) {
791
+ this._view.updateTimelineLength();
792
+ this.cleanupAfterSegmentDrag();
793
+ this._peaks.emit('segments.dragend', segmentMarker.getSegment(), segmentMarker.isStartMarker());
794
+ };
795
+
444
796
  SourcesLayer.prototype.onSourcesGroupDragStart = function(element) {
445
797
  if (this._suppressDragLifecycleForSourceId
446
798
  && element
package/src/main.js CHANGED
@@ -791,16 +791,48 @@ define([
791
791
  .getSourceGroupById(id);
792
792
  };
793
793
 
794
+ /**
795
+ * Selects a source by its identifier.
796
+ *
797
+ * @param {String} sourceId
798
+ */
794
799
  Peaks.prototype.selectSourceById = function(sourceId) {
795
800
  return this.view
796
801
  .selectSourceById(sourceId);
797
802
  };
798
803
 
804
+ /**
805
+ * Selects all sources on a line whose start time is at or after the given time.
806
+ *
807
+ * @param {String} lineId
808
+ * @param {Number} time
809
+ */
799
810
  Peaks.prototype.selectSourcesOnLineAfter = function(lineId, time) {
800
811
  return this.view
801
812
  .selectSourcesOnLineAfter(lineId, time);
802
813
  };
803
814
 
815
+ /**
816
+ * Selects a segment by its identifier.
817
+ *
818
+ * @param {String} segmentId
819
+ */
820
+ Peaks.prototype.selectSegmentById = function(segmentId) {
821
+ return this.view
822
+ .selectSegmentById(segmentId);
823
+ };
824
+
825
+ /**
826
+ * Selects all segments in the given segment group or displayed line whose start time is at or after the given time.
827
+ *
828
+ * @param {String|Number} lineId
829
+ * @param {Number} time
830
+ */
831
+ Peaks.prototype.selectSegmentsOnLineAfter = function(lineId, time) {
832
+ return this.view
833
+ .selectSegmentsOnLineAfter(lineId, time);
834
+ };
835
+
804
836
  Peaks.prototype.deselectAll = function(notify) {
805
837
  return this.view
806
838
  .deselectAll(notify);
@@ -352,6 +352,25 @@ define([
352
352
  this._peaks.emit('model.segment.updated', this);
353
353
  };
354
354
 
355
+ /**
356
+ * Updates segment times without emitting a model update event.
357
+ *
358
+ * This is used by controller-owned drag flows that refresh the visible slice
359
+ * explicitly and emit their consolidated updates on drag end.
360
+ *
361
+ * @param {Number} [newStartTime]
362
+ * @param {Number} [newEndTime]
363
+ */
364
+ Segment.prototype.updateTimes = function(newStartTime, newEndTime) {
365
+ if (!Utils.isNullOrUndefined(newStartTime)) {
366
+ this._startTime = Utils.roundTime(newStartTime);
367
+ }
368
+
369
+ if (!Utils.isNullOrUndefined(newEndTime)) {
370
+ this._endTime = Utils.roundTime(newEndTime);
371
+ }
372
+ };
373
+
355
374
  Segment.prototype.setSelected = function(selected) {
356
375
  this._selected = selected;
357
376
  this._peaks.emit('model.segment.selected', this);
package/src/view.js CHANGED
@@ -525,19 +525,57 @@ define([
525
525
  return this._sourcesLayer.getSourceGroupById(sourceId);
526
526
  };
527
527
 
528
+ /**
529
+ * Selects a source by its identifier.
530
+ *
531
+ * @param {String} sourceId
532
+ */
528
533
  View.prototype.selectSourceById = function(sourceId) {
529
534
  const sourceGroup = this._sourcesLayer.getSourceGroupById(sourceId);
530
535
 
531
536
  if (sourceGroup) {
532
- this._modeLayer.selectElements([sourceGroup.getSource()], false);
537
+ this._modeLayer.selectElements([sourceGroup.getSource()], true);
533
538
  }
534
539
  };
535
540
 
541
+ /**
542
+ * Selects all sources on a line whose start time is at or after the given time.
543
+ *
544
+ * @param {String} lineId
545
+ * @param {Number} time
546
+ */
536
547
  View.prototype.selectSourcesOnLineAfter = function(lineId, time) {
537
548
  const sources = this._sourcesLayer.getSourcesOnLineAfter(lineId, time);
538
549
 
539
- if (sources) {
540
- this._modeLayer.selectElements(sources, false);
550
+ if (sources.length) {
551
+ this._modeLayer.selectElements(sources, true);
552
+ }
553
+ };
554
+
555
+ /**
556
+ * Selects a segment by its identifier.
557
+ *
558
+ * @param {String} segmentId
559
+ */
560
+ View.prototype.selectSegmentById = function(segmentId) {
561
+ const segment = this._peaks.segmentHandler.getSegment(segmentId);
562
+
563
+ if (segment) {
564
+ this._modeLayer.selectElements([segment], true);
565
+ }
566
+ };
567
+
568
+ /**
569
+ * Selects all segments in the given segment group or displayed line whose start time is at or after the given time.
570
+ *
571
+ * @param {String|Number} lineId
572
+ * @param {Number} time
573
+ */
574
+ View.prototype.selectSegmentsOnLineAfter = function(lineId, time) {
575
+ const segments = this._sourcesLayer.getSegmentsOnLineAfter(lineId, time);
576
+
577
+ if (segments.length) {
578
+ this._modeLayer.selectElements(segments, true);
541
579
  }
542
580
  };
543
581