@checksub_team/peaks_timeline 1.4.35 → 1.4.36

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.
@@ -1,765 +1,765 @@
1
- /**
2
- * @file
3
- *
4
- * Defines the {@link SegmentsGroup} class.
5
- *
6
- * @module segments-group
7
- */
8
-
9
- define([
10
- './segment-shape',
11
- './utils',
12
- 'konva'
13
- ], function(
14
- SegmentShape,
15
- Utils,
16
- Konva) {
17
- 'use strict';
18
-
19
- /**
20
- * Creates a Konva.Group that displays segment markers against the audio
21
- * waveform.
22
- *
23
- * @class
24
- * @alias SegmentsGroup
25
- *
26
- * @param {Peaks} peaks
27
- * @param {WaveformOverview|WaveformZoomView} view
28
- * @param {Boolean} allowEditing
29
- */
30
-
31
- function SegmentsGroup(peaks, view, allowEditing) {
32
- this._peaks = peaks;
33
- this._view = view;
34
- this._allowEditing = allowEditing;
35
-
36
- this._firstSegmentId = null;
37
- this._segments = {};
38
- this._lastSegmentId = null;
39
-
40
- this._segmentShapes = {};
41
- this._group = new Konva.Group();
42
-
43
- this._updatedSegments = [];
44
-
45
- this._isMagnetized = false;
46
-
47
- this._peaks.on('segments.setMagnetizing', this.setMagnetizing.bind(this));
48
- }
49
-
50
- /**
51
- * Adds the group to the given {Konva.Group}.
52
- *
53
- * @param {Konva.Group} group
54
- */
55
-
56
- SegmentsGroup.prototype.addToGroup = function(group) {
57
- group.add(this._group);
58
- };
59
-
60
- SegmentsGroup.prototype.moveToTop = function() {
61
- this._group.moveToTop();
62
- };
63
-
64
- SegmentsGroup.prototype.enableEditing = function(enable) {
65
- this._allowEditing = enable;
66
- };
67
-
68
- SegmentsGroup.prototype.isEditingEnabled = function() {
69
- return this._allowEditing;
70
- };
71
-
72
- SegmentsGroup.prototype.y = function(value) {
73
- return this._group.y(value);
74
- };
75
-
76
- SegmentsGroup.prototype.getActiveSegment = function(time) {
77
- var activeSegment = null;
78
- var currentSegment = null;
79
- var nextSegmentId = null;
80
-
81
- do {
82
- if (!currentSegment) {
83
- currentSegment = this._segments[this._firstSegmentId];
84
- }
85
- else {
86
- currentSegment = this._segments[currentSegment.nextSegmentId];
87
- }
88
-
89
- if (currentSegment) {
90
- if (currentSegment.segment.startTime > time) {
91
- // We didn't find an active segment and will not in the remainings segments
92
- break;
93
- }
94
-
95
- if (currentSegment.segment.startTime <= time && currentSegment.segment.endTime > time) {
96
- activeSegment = currentSegment.segment;
97
- break;
98
- }
99
- }
100
- else {
101
- break;
102
- }
103
-
104
- nextSegmentId = currentSegment.nextSegmentId;
105
- } while (nextSegmentId);
106
-
107
- return activeSegment;
108
- };
109
-
110
- SegmentsGroup.prototype.onSegmentsUpdate = function(segment) {
111
- if (this._segments[segment.id]) {
112
- var redraw = false;
113
- var segmentShape = this._segmentShapes[segment.id];
114
- var frameOffset = this._view.getFrameOffset();
115
- var width = this._view.getWidth();
116
- var frameStartTime = this._view.pixelsToTime(frameOffset);
117
- var frameEndTime = this._view.pixelsToTime(frameOffset + width);
118
-
119
- this._deleteSegment(segment);
120
- this._addSegment(segment);
121
-
122
- if (segmentShape) {
123
- this._removeSegment(segment);
124
- redraw = true;
125
- }
126
-
127
- if (segment.isVisible(frameStartTime, frameEndTime)) {
128
- this._addSegmentShape(segment);
129
- redraw = true;
130
- }
131
-
132
- if (redraw) {
133
- this.updateSegments(frameStartTime, frameEndTime);
134
- }
135
- }
136
- };
137
-
138
- SegmentsGroup.prototype.onSegmentUpdated = function() {
139
- this._peaks.emit('segments.updated', this._updatedSegments);
140
- this._updatedSegments = [];
141
- };
142
-
143
- SegmentsGroup.prototype.onSegmentsAdd = function(segments) {
144
- var self = this;
145
-
146
- var frameOffset = self._view.getFrameOffset();
147
- var width = self._view.getWidth();
148
-
149
- var frameStartTime = self._view.pixelsToTime(frameOffset);
150
- var frameEndTime = self._view.pixelsToTime(frameOffset + width);
151
-
152
- segments.forEach(function(segment) {
153
- self._addSegment(segment);
154
- });
155
-
156
- self.updateSegments(frameStartTime, frameEndTime);
157
- };
158
-
159
- SegmentsGroup.prototype.onSegmentsRemove = function(segments) {
160
- var self = this;
161
-
162
- segments.forEach(function(segment) {
163
- var index = self._updatedSegments.indexOf(segment);
164
-
165
- if (index > -1) {
166
- self._updatedSegments.splice(index, 1);
167
- }
168
-
169
- self._removeSegment(segment);
170
- self._deleteSegment(segment);
171
- });
172
-
173
- this._draw();
174
- };
175
-
176
- SegmentsGroup.prototype.onSegmentsRemoveAll = function() {
177
- this._group.removeChildren();
178
- this._firstSegmentId = null;
179
- this._segments = {};
180
- this._lastSegmentId = null;
181
-
182
- this._segmentShapes = {};
183
-
184
- this._draw();
185
- };
186
-
187
- SegmentsGroup.prototype._addSegment = function(segment) {
188
- var newSegment = {
189
- segment: segment,
190
- prevSegmentId: null,
191
- nextSegmentId: null
192
- };
193
-
194
- if (this._firstSegmentId) {
195
- var currentSegment = null;
196
-
197
- do {
198
- if (!currentSegment) {
199
- currentSegment = this._segments[this._firstSegmentId];
200
- }
201
- else {
202
- currentSegment = this._segments[currentSegment.nextSegmentId];
203
- }
204
-
205
- if (segment.startTime <= currentSegment.segment.startTime) {
206
- if (currentSegment.prevSegmentId) {
207
- this._segments[currentSegment.prevSegmentId].nextSegmentId = segment.id;
208
- newSegment.prevSegmentId = currentSegment.prevSegmentId;
209
- }
210
- else {
211
- this._firstSegmentId = segment.id;
212
- }
213
-
214
- currentSegment.prevSegmentId = segment.id;
215
- newSegment.nextSegmentId = currentSegment.segment.id;
216
-
217
- this._segments[segment.id] = newSegment;
218
- break;
219
- }
220
- } while (currentSegment.nextSegmentId);
221
-
222
- if (!newSegment.prevSegmentId && !newSegment.nextSegmentId) {
223
- currentSegment.nextSegmentId = segment.id;
224
- newSegment.prevSegmentId = currentSegment.segment.id;
225
- this._segments[segment.id] = newSegment;
226
- this._lastSegmentId = segment.id;
227
- }
228
- }
229
- else {
230
- this._firstSegmentId = segment.id;
231
- this._segments[segment.id] = newSegment;
232
- this._lastSegmentId = segment.id;
233
- }
234
- };
235
-
236
- SegmentsGroup.prototype._deleteSegment = function(segment) {
237
- if (this._segments[segment.id].prevSegmentId) {
238
- this._segments[this._segments[segment.id].prevSegmentId].nextSegmentId
239
- = this._segments[segment.id].nextSegmentId;
240
- }
241
-
242
- if (this._segments[segment.id].nextSegmentId) {
243
- this._segments[this._segments[segment.id].nextSegmentId].prevSegmentId
244
- = this._segments[segment.id].prevSegmentId;
245
- }
246
-
247
- if (this._firstSegmentId === segment.id) {
248
- this._firstSegmentId = this._segments[segment.id].nextSegmentId;
249
- }
250
-
251
- if (this._lastSegmentId === segment.id) {
252
- this._lastSegmentId = this._segments[segment.id].prevSegmentId;
253
- }
254
-
255
- delete this._segments[segment.id];
256
- };
257
-
258
- SegmentsGroup.prototype.getSegmentsGroupLength = function() {
259
- if (this._segments[this._lastSegmentId]) {
260
- return this._view.timeToPixels(this._segments[this._lastSegmentId].segment.endTime);
261
- }
262
-
263
- return 0;
264
- };
265
-
266
- /**
267
- * Creates the Konva UI objects for a given segment.
268
- *
269
- * @private
270
- * @param {Segment} segment
271
- * @returns {SegmentShape}
272
- */
273
-
274
- SegmentsGroup.prototype._createSegmentShape = function(segment) {
275
- return new SegmentShape(segment, this._peaks, this, this._view);
276
- };
277
-
278
- /**
279
- * Adds a Konva UI object to the group for a given segment.
280
- *
281
- * @private
282
- * @param {Segment} segment
283
- * @returns {SegmentShape}
284
- */
285
-
286
- SegmentsGroup.prototype._addSegmentShape = function(segment) {
287
- var segmentShape = this._createSegmentShape(segment);
288
-
289
- segmentShape.addToGroup(this._group, this);
290
-
291
- this._segmentShapes[segment.id] = segmentShape;
292
-
293
- return segmentShape;
294
- };
295
-
296
- SegmentsGroup.prototype.updateSegmentsOnMove = function(segment, marker) {
297
- this._updateSegments(segment, marker);
298
- };
299
-
300
- /**
301
- * Updates the positions of all displayed segments in the view.
302
- *
303
- * @param {Number} startTime The start of the visible range in the view,
304
- * in seconds.
305
- * @param {Number} endTime The end of the visible range in the view,
306
- * in seconds.
307
- */
308
-
309
- SegmentsGroup.prototype.updateSegments = function(startTime, endTime) {
310
- // Update segments in visible time range.
311
- var segments = this.find(startTime, endTime);
312
-
313
- var count = segments.length;
314
-
315
- segments.forEach(this._updateSegment.bind(this));
316
-
317
- // TODO: in the overview all segments are visible, so no need to check
318
- count += this._removeInvisibleSegments(startTime, endTime);
319
-
320
- if (count > 0) {
321
- this._draw();
322
- }
323
- };
324
-
325
- SegmentsGroup.prototype.find = function(startTime, endTime) {
326
- var currentSegment = null;
327
- var visibleSegments = [];
328
-
329
- if (this._firstSegmentId) {
330
- do {
331
- if (!currentSegment) {
332
- currentSegment = this._segments[this._firstSegmentId];
333
- }
334
- else {
335
- currentSegment = this._segments[currentSegment.nextSegmentId];
336
- }
337
-
338
- if (currentSegment.segment.isVisible(startTime, endTime)) {
339
- visibleSegments.push(currentSegment.segment);
340
- }
341
- else if (visibleSegments.length) {
342
- break;
343
- }
344
- } while (currentSegment.nextSegmentId);
345
- }
346
-
347
- return visibleSegments;
348
- };
349
-
350
- SegmentsGroup.prototype._draw = function() {
351
- this._view.drawSourcesLayer();
352
- };
353
-
354
- /**
355
- * @private
356
- * @param {Segment} segment
357
- */
358
-
359
- SegmentsGroup.prototype._updateSegment = function(segment) {
360
- var segmentShape = this._findOrAddSegmentShape(segment);
361
-
362
- segmentShape.update();
363
- };
364
-
365
- SegmentsGroup.prototype.getCurrentHeight = function() {
366
- var currentHeight = 0;
367
-
368
- for (var id in this._segmentShapes) {
369
- if (Utils.objectHasProperty(this._segmentShapes, id)) {
370
- currentHeight = this._segmentShapes[id].getSegmentHeight();
371
- break;
372
- }
373
- }
374
-
375
- if (!currentHeight) {
376
- if (Object.keys(this._segments).length > 0) {
377
- currentHeight = this._peaks.options.segmentHeight;
378
- }
379
- else {
380
- currentHeight = this._peaks.options.emptyLineHeight;
381
- }
382
- }
383
-
384
- return currentHeight;
385
- };
386
-
387
- /**
388
- * @private
389
- * @param {Segment} segment
390
- */
391
-
392
- SegmentsGroup.prototype._findOrAddSegmentShape = function(segment) {
393
- var segmentShape = this._segmentShapes[segment.id];
394
-
395
- if (!segmentShape) {
396
- segmentShape = this._addSegmentShape(segment);
397
- }
398
-
399
- return segmentShape;
400
- };
401
-
402
- /**
403
- * Removes any segments that are not visible, i.e., are not within and do not
404
- * overlap the given time range.
405
- *
406
- * @private
407
- * @param {Number} startTime The start of the visible time range, in seconds.
408
- * @param {Number} endTime The end of the visible time range, in seconds.
409
- * @returns {Number} The number of segments removed.
410
- */
411
-
412
- SegmentsGroup.prototype._removeInvisibleSegments = function(startTime, endTime) {
413
- var count = 0;
414
-
415
- for (var segmentId in this._segmentShapes) {
416
- if (Utils.objectHasProperty(this._segmentShapes, segmentId)) {
417
- var segment = this._segmentShapes[segmentId].getSegment();
418
-
419
- if (!segment.isVisible(startTime, endTime)) {
420
- this._removeSegment(segment);
421
- count++;
422
- }
423
- }
424
- }
425
-
426
- return count;
427
- };
428
-
429
- SegmentsGroup.prototype.getVisibleSegments = function() {
430
- return this._getVisibleSegments();
431
- };
432
-
433
- SegmentsGroup.prototype._getVisibleSegments = function() {
434
- var frameOffset = this._view.getFrameOffset();
435
- var width = this._view.getWidth();
436
- var frameStartTime = this._view.pixelsToTime(frameOffset);
437
- var frameEndTime = this._view.pixelsToTime(frameOffset + width);
438
-
439
- var visibleSegments = [];
440
-
441
- for (var segmentId in this._segmentShapes) {
442
- if (Utils.objectHasProperty(this._segmentShapes, segmentId)) {
443
- var segment = this._segmentShapes[segmentId]._segment;
444
-
445
- if (segment.isVisible(frameStartTime, frameEndTime)) {
446
- visibleSegments.push(segment);
447
- }
448
- }
449
- }
450
-
451
- return visibleSegments;
452
- };
453
-
454
- SegmentsGroup.prototype.setMagnetizing = function(bool) {
455
- this._isMagnetized = bool;
456
- };
457
-
458
- SegmentsGroup.prototype.isMagnetized = function() {
459
- return this._isMagnetized;
460
- };
461
-
462
- SegmentsGroup.prototype.addToUpdatedSegments = function(segment) {
463
- if (this._updatedSegments.indexOf(segment) === -1) {
464
- this._updatedSegments.push(segment);
465
- }
466
- };
467
-
468
- SegmentsGroup.prototype.updateSegment = function(segment, newStartX, newEndX) {
469
- var newXs = this.manageCollision(segment, newStartX, newEndX);
470
-
471
- if (newXs.startX !== null) {
472
- segment.startTime = this._view.pixelsToTime(newXs.startX);
473
- }
474
-
475
- if (newXs.endX !== null) {
476
- segment.endTime = this._view.pixelsToTime(newXs.endX);
477
- }
478
-
479
- if (newXs) {
480
- this._updateSegment(segment);
481
-
482
- this.addToUpdatedSegments(segment);
483
-
484
- this._draw();
485
- }
486
- };
487
-
488
- SegmentsGroup.prototype.manageCollision = function(segment, newStartX, newEndX) {
489
- var newStartTime = null;
490
- var newEndTime = null;
491
- var startLimited = false;
492
- var endLimited = false;
493
- var segmentMagnetThreshold, width, previousSegment, nextSegment, newXs;
494
-
495
- if (this._isMagnetized) {
496
- segmentMagnetThreshold = this._view.pixelsToTime(
497
- this._peaks.options.segmentMagnetThreshold
498
- );
499
-
500
- if (newStartX !== null && newEndX !== null) {
501
- width = newEndX - newStartX;
502
- }
503
- }
504
-
505
- if (newStartX !== null) {
506
- // startMarker changed
507
- newStartTime = this._view.pixelsToTime(newStartX);
508
-
509
- if (this._segments[segment.id].prevSegmentId) {
510
- // there is another segment to the left
511
- previousSegment = this._segments[this._segments[segment.id].prevSegmentId].segment;
512
-
513
- if (this._isMagnetized) {
514
- if (newStartTime < previousSegment.endTime + segmentMagnetThreshold) {
515
- newStartX = this._view.timeToPixels(previousSegment.endTime);
516
- if (width) {
517
- newEndX = newStartX + width;
518
- }
519
-
520
- return {
521
- startX: newStartX,
522
- endX: newEndX
523
- };
524
- }
525
- }
526
- else if (segment.startTime >= newStartTime) {
527
- // startMarker moved to the left
528
- if (newStartTime < previousSegment.endTime) {
529
- // there is collision
530
- if (previousSegment.editable) {
531
- if (previousSegment.startTime + previousSegment.minSize > newStartTime) {
532
- newStartTime = previousSegment.startTime + previousSegment.minSize;
533
- startLimited = true;
534
- }
535
-
536
- if (previousSegment.endTime !== newStartTime) {
537
- newXs = this.manageCollision(
538
- previousSegment,
539
- this._view.timeToPixels(previousSegment.startTime),
540
- this._view.timeToPixels(newStartTime)
541
- );
542
-
543
- if (newXs.startX !== null) {
544
- previousSegment.startTime = this._view.pixelsToTime(newXs.startX);
545
- }
546
-
547
- if (newXs.endX !== null) {
548
- previousSegment.endTime = this._view.pixelsToTime(newXs.endX);
549
- }
550
-
551
- this._updateSegment(previousSegment);
552
- this.addToUpdatedSegments(previousSegment);
553
- }
554
- }
555
- else {
556
- newStartTime = previousSegment.endTime;
557
- startLimited = true;
558
- }
559
- }
560
- }
561
- }
562
- else {
563
- if (newStartTime < 0) {
564
- newStartTime = 0;
565
- startLimited = true;
566
- }
567
- }
568
- }
569
-
570
- if (newEndX !== null) {
571
- // endMarker changed
572
- newEndTime = this._view.pixelsToTime(newEndX);
573
-
574
- if (this._segments[segment.id].nextSegmentId) {
575
- // there is another segment to the right
576
- nextSegment = this._segments[this._segments[segment.id].nextSegmentId].segment;
577
-
578
- if (this._isMagnetized) {
579
- if (newEndTime > nextSegment.startTime - segmentMagnetThreshold) {
580
- newEndX = this._view.timeToPixels(nextSegment.startTime);
581
- if (width) {
582
- newStartX = newEndX - width;
583
- }
584
-
585
- return {
586
- startX: newStartX,
587
- endX: newEndX
588
- };
589
- }
590
- }
591
- else if (segment.endTime <= newEndTime) {
592
- // endMarker moved to the right
593
- if (newEndTime > nextSegment.startTime) {
594
- // there is collision
595
- if (nextSegment.editable) {
596
- if (nextSegment.endTime - nextSegment.minSize < newEndTime) {
597
- newEndTime = nextSegment.endTime - nextSegment.minSize;
598
- endLimited = true;
599
- }
600
-
601
- if (nextSegment.startTime !== newEndTime) {
602
- newXs = this.manageCollision(
603
- nextSegment,
604
- this._view.timeToPixels(newEndTime),
605
- this._view.timeToPixels(nextSegment.endTime)
606
- );
607
-
608
- if (newXs.startX !== null) {
609
- nextSegment.startTime = this._view.pixelsToTime(newXs.startX);
610
- }
611
-
612
- if (newXs.endX !== null) {
613
- nextSegment.endTime = this._view.pixelsToTime(newXs.endX);
614
- }
615
-
616
- this._updateSegment(nextSegment);
617
- this.addToUpdatedSegments(nextSegment);
618
- }
619
- }
620
- else {
621
- newEndTime = nextSegment.startTime;
622
- endLimited = true;
623
- }
624
- }
625
- }
626
- }
627
- else {
628
- // No limits on the right
629
- }
630
- }
631
-
632
- // Check for minimal size of segment
633
- if (newStartTime !== null && newEndTime !== null) {
634
- if (Utils.roundTime(newEndTime - newStartTime) < segment.minSize) {
635
- if (previousSegment && nextSegment) {
636
- if (Utils.roundTime(nextSegment.startTime - previousSegment.endTime) < segment.minSize) {
637
- return {
638
- startX: null,
639
- endX: null
640
- };
641
- }
642
- }
643
-
644
- if (startLimited) {
645
- newEndTime = newStartTime + segment.minSize;
646
- }
647
- else if (endLimited) {
648
- newStartTime = newEndTime - segment.minSize;
649
- }
650
- }
651
- }
652
- else if (newStartTime !== null) {
653
- if (Utils.roundTime(segment.endTime - newStartTime) < segment.minSize) {
654
- newStartTime = segment.endTime - segment.minSize;
655
- }
656
- }
657
- else if (newEndTime !== null) {
658
- if (Utils.roundTime(newEndTime - segment.startTime) < segment.minSize) {
659
- newEndTime = segment.startTime + segment.minSize;
660
- }
661
- }
662
-
663
- var output = {
664
- startX: null,
665
- endX: null
666
- };
667
-
668
- if (newStartTime !== null) {
669
- output.startX = this._view.timeToPixels(newStartTime);
670
- }
671
-
672
- if (newEndTime !== null) {
673
- output.endX = this._view.timeToPixels(newEndTime);
674
- }
675
-
676
- return output;
677
- };
678
-
679
- SegmentsGroup.prototype._getOverlappedSegments = function() {
680
- var self = this;
681
- var segments = this._getVisibleSegments();
682
-
683
- return segments.reduce(function(result, segment) {
684
- segments.forEach(function(segment2) {
685
- if (self._segmentsOverlapped(segment, segment2)) {
686
- if (!result.includes(segment2.id)) {
687
- result.push(segment2);
688
- }
689
- }
690
- });
691
-
692
- return result;
693
- }, []);
694
- };
695
-
696
- /**
697
- * Removes the given segment from the view.
698
- *
699
- * @param {Segment} segment
700
- */
701
-
702
- SegmentsGroup.prototype._removeSegment = function(segment) {
703
- var segmentShape = this._segmentShapes[segment.id];
704
-
705
- if (segmentShape) {
706
- delete this._segmentShapes[segment.id];
707
- segmentShape.destroy();
708
- }
709
- };
710
-
711
- /**
712
- * Toggles visibility of the segments layer.
713
- *
714
- * @param {Boolean} visible
715
- */
716
-
717
- SegmentsGroup.prototype.setVisible = function(visible) {
718
- this._group.setVisible(visible);
719
- };
720
-
721
- SegmentsGroup.prototype.draw = function() {
722
- this._draw();
723
- };
724
-
725
- SegmentsGroup.prototype._segmentsOverlapped = function(segment1, segment2) {
726
- var endsLater = (segment1.startTime < segment2.startTime)
727
- && (segment1.endTime > segment2.startTime);
728
- var startsEarlier = (segment1.startTime > segment2.startTime)
729
- && (segment1.startTime < segment2.endTime);
730
-
731
- return endsLater || startsEarlier;
732
- };
733
-
734
- SegmentsGroup.prototype.destroy = function() {
735
- this._peaks.off('segments.setMagnetizing', this.setMagnetizing);
736
- };
737
-
738
- SegmentsGroup.prototype.fitToView = function() {
739
- for (var segmentId in this._segmentShapes) {
740
- if (Utils.objectHasProperty(this._segmentShapes, segmentId)) {
741
- var segmentShape = this._segmentShapes[segmentId];
742
-
743
- segmentShape.fitToView();
744
- }
745
- }
746
- };
747
-
748
- SegmentsGroup.prototype.contains = function(segment) {
749
- for (var id in this._segments) {
750
- if (Utils.objectHasProperty(this._segments, id)) {
751
- if (id === segment.id) {
752
- return true;
753
- }
754
- }
755
- }
756
-
757
- return false;
758
- };
759
-
760
- SegmentsGroup.prototype.getHeight = function() {
761
- return this._group.getHeight();
762
- };
763
-
764
- return SegmentsGroup;
765
- });
1
+ /**
2
+ * @file
3
+ *
4
+ * Defines the {@link SegmentsGroup} class.
5
+ *
6
+ * @module segments-group
7
+ */
8
+
9
+ define([
10
+ './segment-shape',
11
+ './utils',
12
+ 'konva'
13
+ ], function(
14
+ SegmentShape,
15
+ Utils,
16
+ Konva) {
17
+ 'use strict';
18
+
19
+ /**
20
+ * Creates a Konva.Group that displays segment markers against the audio
21
+ * waveform.
22
+ *
23
+ * @class
24
+ * @alias SegmentsGroup
25
+ *
26
+ * @param {Peaks} peaks
27
+ * @param {WaveformOverview|WaveformZoomView} view
28
+ * @param {Boolean} allowEditing
29
+ */
30
+
31
+ function SegmentsGroup(peaks, view, allowEditing) {
32
+ this._peaks = peaks;
33
+ this._view = view;
34
+ this._allowEditing = allowEditing;
35
+
36
+ this._firstSegmentId = null;
37
+ this._segments = {};
38
+ this._lastSegmentId = null;
39
+
40
+ this._segmentShapes = {};
41
+ this._group = new Konva.Group();
42
+
43
+ this._updatedSegments = [];
44
+
45
+ this._isMagnetized = false;
46
+
47
+ this._peaks.on('segments.setMagnetizing', this.setMagnetizing.bind(this));
48
+ }
49
+
50
+ /**
51
+ * Adds the group to the given {Konva.Group}.
52
+ *
53
+ * @param {Konva.Group} group
54
+ */
55
+
56
+ SegmentsGroup.prototype.addToGroup = function(group) {
57
+ group.add(this._group);
58
+ };
59
+
60
+ SegmentsGroup.prototype.moveToTop = function() {
61
+ this._group.moveToTop();
62
+ };
63
+
64
+ SegmentsGroup.prototype.enableEditing = function(enable) {
65
+ this._allowEditing = enable;
66
+ };
67
+
68
+ SegmentsGroup.prototype.isEditingEnabled = function() {
69
+ return this._allowEditing;
70
+ };
71
+
72
+ SegmentsGroup.prototype.y = function(value) {
73
+ return this._group.y(value);
74
+ };
75
+
76
+ SegmentsGroup.prototype.getActiveSegment = function(time) {
77
+ var activeSegment = null;
78
+ var currentSegment = null;
79
+ var nextSegmentId = null;
80
+
81
+ do {
82
+ if (!currentSegment) {
83
+ currentSegment = this._segments[this._firstSegmentId];
84
+ }
85
+ else {
86
+ currentSegment = this._segments[currentSegment.nextSegmentId];
87
+ }
88
+
89
+ if (currentSegment) {
90
+ if (currentSegment.segment.startTime > time) {
91
+ // We didn't find an active segment and will not in the remainings segments
92
+ break;
93
+ }
94
+
95
+ if (currentSegment.segment.startTime <= time && currentSegment.segment.endTime > time) {
96
+ activeSegment = currentSegment.segment;
97
+ break;
98
+ }
99
+ }
100
+ else {
101
+ break;
102
+ }
103
+
104
+ nextSegmentId = currentSegment.nextSegmentId;
105
+ } while (nextSegmentId);
106
+
107
+ return activeSegment;
108
+ };
109
+
110
+ SegmentsGroup.prototype.onSegmentsUpdate = function(segment) {
111
+ if (this._segments[segment.id]) {
112
+ var redraw = false;
113
+ var segmentShape = this._segmentShapes[segment.id];
114
+ var frameOffset = this._view.getFrameOffset();
115
+ var width = this._view.getWidth();
116
+ var frameStartTime = this._view.pixelsToTime(frameOffset);
117
+ var frameEndTime = this._view.pixelsToTime(frameOffset + width);
118
+
119
+ this._deleteSegment(segment);
120
+ this._addSegment(segment);
121
+
122
+ if (segmentShape) {
123
+ this._removeSegment(segment);
124
+ redraw = true;
125
+ }
126
+
127
+ if (segment.isVisible(frameStartTime, frameEndTime)) {
128
+ this._addSegmentShape(segment);
129
+ redraw = true;
130
+ }
131
+
132
+ if (redraw) {
133
+ this.updateSegments(frameStartTime, frameEndTime);
134
+ }
135
+ }
136
+ };
137
+
138
+ SegmentsGroup.prototype.onSegmentUpdated = function() {
139
+ this._peaks.emit('segments.updated', this._updatedSegments);
140
+ this._updatedSegments = [];
141
+ };
142
+
143
+ SegmentsGroup.prototype.onSegmentsAdd = function(segments) {
144
+ var self = this;
145
+
146
+ var frameOffset = self._view.getFrameOffset();
147
+ var width = self._view.getWidth();
148
+
149
+ var frameStartTime = self._view.pixelsToTime(frameOffset);
150
+ var frameEndTime = self._view.pixelsToTime(frameOffset + width);
151
+
152
+ segments.forEach(function(segment) {
153
+ self._addSegment(segment);
154
+ });
155
+
156
+ self.updateSegments(frameStartTime, frameEndTime);
157
+ };
158
+
159
+ SegmentsGroup.prototype.onSegmentsRemove = function(segments) {
160
+ var self = this;
161
+
162
+ segments.forEach(function(segment) {
163
+ var index = self._updatedSegments.indexOf(segment);
164
+
165
+ if (index > -1) {
166
+ self._updatedSegments.splice(index, 1);
167
+ }
168
+
169
+ self._removeSegment(segment);
170
+ self._deleteSegment(segment);
171
+ });
172
+
173
+ this._draw();
174
+ };
175
+
176
+ SegmentsGroup.prototype.onSegmentsRemoveAll = function() {
177
+ this._group.removeChildren();
178
+ this._firstSegmentId = null;
179
+ this._segments = {};
180
+ this._lastSegmentId = null;
181
+
182
+ this._segmentShapes = {};
183
+
184
+ this._draw();
185
+ };
186
+
187
+ SegmentsGroup.prototype._addSegment = function(segment) {
188
+ var newSegment = {
189
+ segment: segment,
190
+ prevSegmentId: null,
191
+ nextSegmentId: null
192
+ };
193
+
194
+ if (this._firstSegmentId) {
195
+ var currentSegment = null;
196
+
197
+ do {
198
+ if (!currentSegment) {
199
+ currentSegment = this._segments[this._firstSegmentId];
200
+ }
201
+ else {
202
+ currentSegment = this._segments[currentSegment.nextSegmentId];
203
+ }
204
+
205
+ if (segment.startTime <= currentSegment.segment.startTime) {
206
+ if (currentSegment.prevSegmentId) {
207
+ this._segments[currentSegment.prevSegmentId].nextSegmentId = segment.id;
208
+ newSegment.prevSegmentId = currentSegment.prevSegmentId;
209
+ }
210
+ else {
211
+ this._firstSegmentId = segment.id;
212
+ }
213
+
214
+ currentSegment.prevSegmentId = segment.id;
215
+ newSegment.nextSegmentId = currentSegment.segment.id;
216
+
217
+ this._segments[segment.id] = newSegment;
218
+ break;
219
+ }
220
+ } while (currentSegment.nextSegmentId);
221
+
222
+ if (!newSegment.prevSegmentId && !newSegment.nextSegmentId) {
223
+ currentSegment.nextSegmentId = segment.id;
224
+ newSegment.prevSegmentId = currentSegment.segment.id;
225
+ this._segments[segment.id] = newSegment;
226
+ this._lastSegmentId = segment.id;
227
+ }
228
+ }
229
+ else {
230
+ this._firstSegmentId = segment.id;
231
+ this._segments[segment.id] = newSegment;
232
+ this._lastSegmentId = segment.id;
233
+ }
234
+ };
235
+
236
+ SegmentsGroup.prototype._deleteSegment = function(segment) {
237
+ if (this._segments[segment.id].prevSegmentId) {
238
+ this._segments[this._segments[segment.id].prevSegmentId].nextSegmentId
239
+ = this._segments[segment.id].nextSegmentId;
240
+ }
241
+
242
+ if (this._segments[segment.id].nextSegmentId) {
243
+ this._segments[this._segments[segment.id].nextSegmentId].prevSegmentId
244
+ = this._segments[segment.id].prevSegmentId;
245
+ }
246
+
247
+ if (this._firstSegmentId === segment.id) {
248
+ this._firstSegmentId = this._segments[segment.id].nextSegmentId;
249
+ }
250
+
251
+ if (this._lastSegmentId === segment.id) {
252
+ this._lastSegmentId = this._segments[segment.id].prevSegmentId;
253
+ }
254
+
255
+ delete this._segments[segment.id];
256
+ };
257
+
258
+ SegmentsGroup.prototype.getSegmentsGroupLength = function() {
259
+ if (this._segments[this._lastSegmentId]) {
260
+ return this._view.timeToPixels(this._segments[this._lastSegmentId].segment.endTime);
261
+ }
262
+
263
+ return 0;
264
+ };
265
+
266
+ /**
267
+ * Creates the Konva UI objects for a given segment.
268
+ *
269
+ * @private
270
+ * @param {Segment} segment
271
+ * @returns {SegmentShape}
272
+ */
273
+
274
+ SegmentsGroup.prototype._createSegmentShape = function(segment) {
275
+ return new SegmentShape(segment, this._peaks, this, this._view);
276
+ };
277
+
278
+ /**
279
+ * Adds a Konva UI object to the group for a given segment.
280
+ *
281
+ * @private
282
+ * @param {Segment} segment
283
+ * @returns {SegmentShape}
284
+ */
285
+
286
+ SegmentsGroup.prototype._addSegmentShape = function(segment) {
287
+ var segmentShape = this._createSegmentShape(segment);
288
+
289
+ segmentShape.addToGroup(this._group, this);
290
+
291
+ this._segmentShapes[segment.id] = segmentShape;
292
+
293
+ return segmentShape;
294
+ };
295
+
296
+ SegmentsGroup.prototype.updateSegmentsOnMove = function(segment, marker) {
297
+ this._updateSegments(segment, marker);
298
+ };
299
+
300
+ /**
301
+ * Updates the positions of all displayed segments in the view.
302
+ *
303
+ * @param {Number} startTime The start of the visible range in the view,
304
+ * in seconds.
305
+ * @param {Number} endTime The end of the visible range in the view,
306
+ * in seconds.
307
+ */
308
+
309
+ SegmentsGroup.prototype.updateSegments = function(startTime, endTime) {
310
+ // Update segments in visible time range.
311
+ var segments = this.find(startTime, endTime);
312
+
313
+ var count = segments.length;
314
+
315
+ segments.forEach(this._updateSegment.bind(this));
316
+
317
+ // TODO: in the overview all segments are visible, so no need to check
318
+ count += this._removeInvisibleSegments(startTime, endTime);
319
+
320
+ if (count > 0) {
321
+ this._draw();
322
+ }
323
+ };
324
+
325
+ SegmentsGroup.prototype.find = function(startTime, endTime) {
326
+ var currentSegment = null;
327
+ var visibleSegments = [];
328
+
329
+ if (this._firstSegmentId) {
330
+ do {
331
+ if (!currentSegment) {
332
+ currentSegment = this._segments[this._firstSegmentId];
333
+ }
334
+ else {
335
+ currentSegment = this._segments[currentSegment.nextSegmentId];
336
+ }
337
+
338
+ if (currentSegment.segment.isVisible(startTime, endTime)) {
339
+ visibleSegments.push(currentSegment.segment);
340
+ }
341
+ else if (visibleSegments.length) {
342
+ break;
343
+ }
344
+ } while (currentSegment.nextSegmentId);
345
+ }
346
+
347
+ return visibleSegments;
348
+ };
349
+
350
+ SegmentsGroup.prototype._draw = function() {
351
+ this._view.drawSourcesLayer();
352
+ };
353
+
354
+ /**
355
+ * @private
356
+ * @param {Segment} segment
357
+ */
358
+
359
+ SegmentsGroup.prototype._updateSegment = function(segment) {
360
+ var segmentShape = this._findOrAddSegmentShape(segment);
361
+
362
+ segmentShape.update();
363
+ };
364
+
365
+ SegmentsGroup.prototype.getCurrentHeight = function() {
366
+ var currentHeight = 0;
367
+
368
+ for (var id in this._segmentShapes) {
369
+ if (Utils.objectHasProperty(this._segmentShapes, id)) {
370
+ currentHeight = this._segmentShapes[id].getSegmentHeight();
371
+ break;
372
+ }
373
+ }
374
+
375
+ if (!currentHeight) {
376
+ if (Object.keys(this._segments).length > 0) {
377
+ currentHeight = this._peaks.options.segmentHeight;
378
+ }
379
+ else {
380
+ currentHeight = this._peaks.options.emptyLineHeight;
381
+ }
382
+ }
383
+
384
+ return currentHeight;
385
+ };
386
+
387
+ /**
388
+ * @private
389
+ * @param {Segment} segment
390
+ */
391
+
392
+ SegmentsGroup.prototype._findOrAddSegmentShape = function(segment) {
393
+ var segmentShape = this._segmentShapes[segment.id];
394
+
395
+ if (!segmentShape) {
396
+ segmentShape = this._addSegmentShape(segment);
397
+ }
398
+
399
+ return segmentShape;
400
+ };
401
+
402
+ /**
403
+ * Removes any segments that are not visible, i.e., are not within and do not
404
+ * overlap the given time range.
405
+ *
406
+ * @private
407
+ * @param {Number} startTime The start of the visible time range, in seconds.
408
+ * @param {Number} endTime The end of the visible time range, in seconds.
409
+ * @returns {Number} The number of segments removed.
410
+ */
411
+
412
+ SegmentsGroup.prototype._removeInvisibleSegments = function(startTime, endTime) {
413
+ var count = 0;
414
+
415
+ for (var segmentId in this._segmentShapes) {
416
+ if (Utils.objectHasProperty(this._segmentShapes, segmentId)) {
417
+ var segment = this._segmentShapes[segmentId].getSegment();
418
+
419
+ if (!segment.isVisible(startTime, endTime)) {
420
+ this._removeSegment(segment);
421
+ count++;
422
+ }
423
+ }
424
+ }
425
+
426
+ return count;
427
+ };
428
+
429
+ SegmentsGroup.prototype.getVisibleSegments = function() {
430
+ return this._getVisibleSegments();
431
+ };
432
+
433
+ SegmentsGroup.prototype._getVisibleSegments = function() {
434
+ var frameOffset = this._view.getFrameOffset();
435
+ var width = this._view.getWidth();
436
+ var frameStartTime = this._view.pixelsToTime(frameOffset);
437
+ var frameEndTime = this._view.pixelsToTime(frameOffset + width);
438
+
439
+ var visibleSegments = [];
440
+
441
+ for (var segmentId in this._segmentShapes) {
442
+ if (Utils.objectHasProperty(this._segmentShapes, segmentId)) {
443
+ var segment = this._segmentShapes[segmentId]._segment;
444
+
445
+ if (segment.isVisible(frameStartTime, frameEndTime)) {
446
+ visibleSegments.push(segment);
447
+ }
448
+ }
449
+ }
450
+
451
+ return visibleSegments;
452
+ };
453
+
454
+ SegmentsGroup.prototype.setMagnetizing = function(bool) {
455
+ this._isMagnetized = bool;
456
+ };
457
+
458
+ SegmentsGroup.prototype.isMagnetized = function() {
459
+ return this._isMagnetized;
460
+ };
461
+
462
+ SegmentsGroup.prototype.addToUpdatedSegments = function(segment) {
463
+ if (this._updatedSegments.indexOf(segment) === -1) {
464
+ this._updatedSegments.push(segment);
465
+ }
466
+ };
467
+
468
+ SegmentsGroup.prototype.updateSegment = function(segment, newStartX, newEndX) {
469
+ var newXs = this.manageCollision(segment, newStartX, newEndX);
470
+
471
+ if (newXs.startX !== null) {
472
+ segment.startTime = this._view.pixelsToTime(newXs.startX);
473
+ }
474
+
475
+ if (newXs.endX !== null) {
476
+ segment.endTime = this._view.pixelsToTime(newXs.endX);
477
+ }
478
+
479
+ if (newXs) {
480
+ this._updateSegment(segment);
481
+
482
+ this.addToUpdatedSegments(segment);
483
+
484
+ this._draw();
485
+ }
486
+ };
487
+
488
+ SegmentsGroup.prototype.manageCollision = function(segment, newStartX, newEndX) {
489
+ var newStartTime = null;
490
+ var newEndTime = null;
491
+ var startLimited = false;
492
+ var endLimited = false;
493
+ var segmentMagnetThreshold, width, previousSegment, nextSegment, newXs;
494
+
495
+ if (this._isMagnetized) {
496
+ segmentMagnetThreshold = this._view.pixelsToTime(
497
+ this._peaks.options.segmentMagnetThreshold
498
+ );
499
+
500
+ if (newStartX !== null && newEndX !== null) {
501
+ width = newEndX - newStartX;
502
+ }
503
+ }
504
+
505
+ if (newStartX !== null) {
506
+ // startMarker changed
507
+ newStartTime = this._view.pixelsToTime(newStartX);
508
+
509
+ if (this._segments[segment.id].prevSegmentId) {
510
+ // there is another segment to the left
511
+ previousSegment = this._segments[this._segments[segment.id].prevSegmentId].segment;
512
+
513
+ if (this._isMagnetized) {
514
+ if (newStartTime < previousSegment.endTime + segmentMagnetThreshold) {
515
+ newStartX = this._view.timeToPixels(previousSegment.endTime);
516
+ if (width) {
517
+ newEndX = newStartX + width;
518
+ }
519
+
520
+ return {
521
+ startX: newStartX,
522
+ endX: newEndX
523
+ };
524
+ }
525
+ }
526
+ else if (segment.startTime >= newStartTime) {
527
+ // startMarker moved to the left
528
+ if (newStartTime < previousSegment.endTime) {
529
+ // there is collision
530
+ if (previousSegment.editable) {
531
+ if (previousSegment.startTime + previousSegment.minSize > newStartTime) {
532
+ newStartTime = previousSegment.startTime + previousSegment.minSize;
533
+ startLimited = true;
534
+ }
535
+
536
+ if (previousSegment.endTime !== newStartTime) {
537
+ newXs = this.manageCollision(
538
+ previousSegment,
539
+ this._view.timeToPixels(previousSegment.startTime),
540
+ this._view.timeToPixels(newStartTime)
541
+ );
542
+
543
+ if (newXs.startX !== null) {
544
+ previousSegment.startTime = this._view.pixelsToTime(newXs.startX);
545
+ }
546
+
547
+ if (newXs.endX !== null) {
548
+ previousSegment.endTime = this._view.pixelsToTime(newXs.endX);
549
+ }
550
+
551
+ this._updateSegment(previousSegment);
552
+ this.addToUpdatedSegments(previousSegment);
553
+ }
554
+ }
555
+ else {
556
+ newStartTime = previousSegment.endTime;
557
+ startLimited = true;
558
+ }
559
+ }
560
+ }
561
+ }
562
+ else {
563
+ if (newStartTime < 0) {
564
+ newStartTime = 0;
565
+ startLimited = true;
566
+ }
567
+ }
568
+ }
569
+
570
+ if (newEndX !== null) {
571
+ // endMarker changed
572
+ newEndTime = this._view.pixelsToTime(newEndX);
573
+
574
+ if (this._segments[segment.id].nextSegmentId) {
575
+ // there is another segment to the right
576
+ nextSegment = this._segments[this._segments[segment.id].nextSegmentId].segment;
577
+
578
+ if (this._isMagnetized) {
579
+ if (newEndTime > nextSegment.startTime - segmentMagnetThreshold) {
580
+ newEndX = this._view.timeToPixels(nextSegment.startTime);
581
+ if (width) {
582
+ newStartX = newEndX - width;
583
+ }
584
+
585
+ return {
586
+ startX: newStartX,
587
+ endX: newEndX
588
+ };
589
+ }
590
+ }
591
+ else if (segment.endTime <= newEndTime) {
592
+ // endMarker moved to the right
593
+ if (newEndTime > nextSegment.startTime) {
594
+ // there is collision
595
+ if (nextSegment.editable) {
596
+ if (nextSegment.endTime - nextSegment.minSize < newEndTime) {
597
+ newEndTime = nextSegment.endTime - nextSegment.minSize;
598
+ endLimited = true;
599
+ }
600
+
601
+ if (nextSegment.startTime !== newEndTime) {
602
+ newXs = this.manageCollision(
603
+ nextSegment,
604
+ this._view.timeToPixels(newEndTime),
605
+ this._view.timeToPixels(nextSegment.endTime)
606
+ );
607
+
608
+ if (newXs.startX !== null) {
609
+ nextSegment.startTime = this._view.pixelsToTime(newXs.startX);
610
+ }
611
+
612
+ if (newXs.endX !== null) {
613
+ nextSegment.endTime = this._view.pixelsToTime(newXs.endX);
614
+ }
615
+
616
+ this._updateSegment(nextSegment);
617
+ this.addToUpdatedSegments(nextSegment);
618
+ }
619
+ }
620
+ else {
621
+ newEndTime = nextSegment.startTime;
622
+ endLimited = true;
623
+ }
624
+ }
625
+ }
626
+ }
627
+ else {
628
+ // No limits on the right
629
+ }
630
+ }
631
+
632
+ // Check for minimal size of segment
633
+ if (newStartTime !== null && newEndTime !== null) {
634
+ if (Utils.roundTime(newEndTime - newStartTime) < segment.minSize) {
635
+ if (previousSegment && nextSegment) {
636
+ if (Utils.roundTime(nextSegment.startTime - previousSegment.endTime) < segment.minSize) {
637
+ return {
638
+ startX: null,
639
+ endX: null
640
+ };
641
+ }
642
+ }
643
+
644
+ if (startLimited) {
645
+ newEndTime = newStartTime + segment.minSize;
646
+ }
647
+ else if (endLimited) {
648
+ newStartTime = newEndTime - segment.minSize;
649
+ }
650
+ }
651
+ }
652
+ else if (newStartTime !== null) {
653
+ if (Utils.roundTime(segment.endTime - newStartTime) < segment.minSize) {
654
+ newStartTime = segment.endTime - segment.minSize;
655
+ }
656
+ }
657
+ else if (newEndTime !== null) {
658
+ if (Utils.roundTime(newEndTime - segment.startTime) < segment.minSize) {
659
+ newEndTime = segment.startTime + segment.minSize;
660
+ }
661
+ }
662
+
663
+ var output = {
664
+ startX: null,
665
+ endX: null
666
+ };
667
+
668
+ if (newStartTime !== null) {
669
+ output.startX = this._view.timeToPixels(newStartTime);
670
+ }
671
+
672
+ if (newEndTime !== null) {
673
+ output.endX = this._view.timeToPixels(newEndTime);
674
+ }
675
+
676
+ return output;
677
+ };
678
+
679
+ SegmentsGroup.prototype._getOverlappedSegments = function() {
680
+ var self = this;
681
+ var segments = this._getVisibleSegments();
682
+
683
+ return segments.reduce(function(result, segment) {
684
+ segments.forEach(function(segment2) {
685
+ if (self._segmentsOverlapped(segment, segment2)) {
686
+ if (!result.includes(segment2.id)) {
687
+ result.push(segment2);
688
+ }
689
+ }
690
+ });
691
+
692
+ return result;
693
+ }, []);
694
+ };
695
+
696
+ /**
697
+ * Removes the given segment from the view.
698
+ *
699
+ * @param {Segment} segment
700
+ */
701
+
702
+ SegmentsGroup.prototype._removeSegment = function(segment) {
703
+ var segmentShape = this._segmentShapes[segment.id];
704
+
705
+ if (segmentShape) {
706
+ delete this._segmentShapes[segment.id];
707
+ segmentShape.destroy();
708
+ }
709
+ };
710
+
711
+ /**
712
+ * Toggles visibility of the segments layer.
713
+ *
714
+ * @param {Boolean} visible
715
+ */
716
+
717
+ SegmentsGroup.prototype.setVisible = function(visible) {
718
+ this._group.setVisible(visible);
719
+ };
720
+
721
+ SegmentsGroup.prototype.draw = function() {
722
+ this._draw();
723
+ };
724
+
725
+ SegmentsGroup.prototype._segmentsOverlapped = function(segment1, segment2) {
726
+ var endsLater = (segment1.startTime < segment2.startTime)
727
+ && (segment1.endTime > segment2.startTime);
728
+ var startsEarlier = (segment1.startTime > segment2.startTime)
729
+ && (segment1.startTime < segment2.endTime);
730
+
731
+ return endsLater || startsEarlier;
732
+ };
733
+
734
+ SegmentsGroup.prototype.destroy = function() {
735
+ this._peaks.off('segments.setMagnetizing', this.setMagnetizing);
736
+ };
737
+
738
+ SegmentsGroup.prototype.fitToView = function() {
739
+ for (var segmentId in this._segmentShapes) {
740
+ if (Utils.objectHasProperty(this._segmentShapes, segmentId)) {
741
+ var segmentShape = this._segmentShapes[segmentId];
742
+
743
+ segmentShape.fitToView();
744
+ }
745
+ }
746
+ };
747
+
748
+ SegmentsGroup.prototype.contains = function(segment) {
749
+ for (var id in this._segments) {
750
+ if (Utils.objectHasProperty(this._segments, id)) {
751
+ if (id === segment.id) {
752
+ return true;
753
+ }
754
+ }
755
+ }
756
+
757
+ return false;
758
+ };
759
+
760
+ SegmentsGroup.prototype.getHeight = function() {
761
+ return this._group.getHeight();
762
+ };
763
+
764
+ return SegmentsGroup;
765
+ });