@checksub_team/peaks_timeline 1.4.17

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.
package/src/line.js ADDED
@@ -0,0 +1,629 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Defines the {@link line} class.
5
+ *
6
+ * @module line
7
+ */
8
+
9
+ define([
10
+ 'konva',
11
+ './utils'
12
+ ], function(Konva, Utils) {
13
+ 'use strict';
14
+
15
+ function Line(peaks, view, y, id, position) {
16
+ this._peaks = peaks;
17
+ this._view = view;
18
+
19
+ this._id = id;
20
+ this._position = position;
21
+
22
+ this._firstSourceId = null;
23
+ this._sources = {};
24
+
25
+ this._sourcesGroup = {};
26
+ this._wrapped = false;
27
+ this._y = y;
28
+
29
+ this._group = new Konva.Group({
30
+ x: 0,
31
+ y: y - this._view.getFrameOffsetY(),
32
+ draggable: true,
33
+ dragBoundFunc: function() {
34
+ return {
35
+ x: this.absolutePosition().x,
36
+ y: this.absolutePosition().y
37
+ };
38
+ }
39
+ });
40
+
41
+ this._sourceHeights = {};
42
+ this._height = this._peaks.options.emptyLineHeight;
43
+ this._unwrappedCount = 0;
44
+ }
45
+
46
+ Line.prototype.getPosition = function() {
47
+ return this._position;
48
+ };
49
+
50
+ Line.prototype.getId = function() {
51
+ return this._id;
52
+ };
53
+
54
+ Line.prototype.isEmpty = function() {
55
+ return Object.keys(this._sources).length === 0;
56
+ };
57
+
58
+ Line.prototype.isSegmentsLine = function() {
59
+ return Boolean(this._segmentsGroup);
60
+ };
61
+
62
+ Line.prototype.updateSegments = function(frameStartTime, frameEndTime) {
63
+ if (this.isSegmentsLine()) {
64
+ this._segmentsGroup.updateSegments(frameStartTime, frameEndTime);
65
+ }
66
+ };
67
+
68
+ Line.prototype.lineLength = function() {
69
+ var length = 0;
70
+
71
+ if (this.isSegmentsLine()) {
72
+ return this._segmentsGroup.getSegmentsGroupLength();
73
+ }
74
+
75
+ for (var sourceId in this._sources) {
76
+ if (Utils.objectHasProperty(this._sources, sourceId)) {
77
+ var sourceGroupLength = this._view.timeToPixels(
78
+ this._sources[sourceId].source.endTime
79
+ );
80
+
81
+ if (sourceGroupLength > length) {
82
+ length = sourceGroupLength;
83
+ }
84
+ }
85
+ }
86
+
87
+ return length;
88
+ };
89
+
90
+ Line.prototype.lineHeight = function() {
91
+ return this._height;
92
+ };
93
+
94
+ Line.prototype.updateLineHeight = function(sourceGroup, action) {
95
+ var oldHeight = this._height;
96
+ var sourceGroupHeight;
97
+
98
+ switch (action) {
99
+ case 'add':
100
+ sourceGroupHeight = sourceGroup.getCurrentHeight();
101
+
102
+ if (this._sourceHeights[sourceGroupHeight]) {
103
+ this._sourceHeights[sourceGroupHeight]++;
104
+ }
105
+ else {
106
+ this._sourceHeights[sourceGroupHeight] = 1;
107
+ if (sourceGroupHeight > this._height) {
108
+ this._height = sourceGroupHeight;
109
+ }
110
+ }
111
+ break;
112
+ case 'remove':
113
+ if (Object.keys(this._sources).length === 0) {
114
+ this._height = this._peaks.options.emptyLineHeight;
115
+ this._sourceHeights = {};
116
+ }
117
+ else {
118
+ sourceGroupHeight = sourceGroup.getCurrentHeight();
119
+
120
+ this._sourceHeights[sourceGroupHeight]--;
121
+
122
+ if (this._sourceHeights[sourceGroupHeight] === 0
123
+ && sourceGroupHeight === this._height) {
124
+ delete this._sourceHeights[sourceGroupHeight];
125
+ this._height = 0;
126
+ for (var height in this._sourceHeights) {
127
+ if (Utils.objectHasProperty(this._sourceHeights, height)) {
128
+ var parsedHeight = parseInt(height, 10);
129
+
130
+ if (parseInt(height, 10) > this._height) {
131
+ this._height = parsedHeight;
132
+ }
133
+ }
134
+ }
135
+ }
136
+ }
137
+ break;
138
+ }
139
+
140
+ if (this._height !== oldHeight) {
141
+ this._peaks.emit('line.heightChanged', this._position);
142
+ }
143
+ };
144
+
145
+ Line.prototype.isVisible = function() {
146
+ return this._y < this._view.getFrameOffsetY() + this._view.getHeight()
147
+ && this._view.getFrameOffsetY() < this._y + this._height;
148
+ };
149
+
150
+ Line.prototype.addToLayer = function(layer) {
151
+ layer.add(this._group);
152
+ };
153
+
154
+ Line.prototype.addSourceGroup = function(sourceGroup) {
155
+ var source = sourceGroup.getSource();
156
+
157
+ this._sourcesGroup[source.id] = sourceGroup;
158
+
159
+ if (!this._sources[source.id]) {
160
+ var newSource = {
161
+ source: source,
162
+ prevSourceId: null,
163
+ nextSourceId: null
164
+ };
165
+
166
+ if (this._firstSourceId) {
167
+ var currentSource = null;
168
+
169
+ do {
170
+ if (!currentSource) {
171
+ currentSource = this._sources[this._firstSourceId];
172
+ }
173
+ else {
174
+ currentSource = this._sources[currentSource.nextSourceId];
175
+ }
176
+
177
+ if (source.endTime <= currentSource.source.startTime) {
178
+ var startLimit = currentSource.prevSourceId
179
+ ? this._sources[currentSource.prevSourceId].source.endTime
180
+ : 0;
181
+
182
+ var newTimes = this._canBePlacedBetween(
183
+ source.startTime,
184
+ source.endTime,
185
+ startLimit,
186
+ currentSource.source.startTime
187
+ );
188
+
189
+ if (newTimes) {
190
+ source.updateTimes(newTimes.startTime, newTimes.endTime);
191
+
192
+ if (currentSource.prevSourceId) {
193
+ this._sources[currentSource.prevSourceId].nextSourceId = source.id;
194
+ newSource.prevSourceId = currentSource.prevSourceId;
195
+ }
196
+ else {
197
+ this._firstSourceId = source.id;
198
+ }
199
+
200
+ currentSource.prevSourceId = source.id;
201
+ newSource.nextSourceId = currentSource.source.id;
202
+
203
+ this._sources[source.id] = newSource;
204
+ break;
205
+ }
206
+ }
207
+ } while (currentSource.nextSourceId);
208
+
209
+ if (!newSource.prevSourceId && !newSource.nextSourceId) {
210
+ if (source.startTime < currentSource.source.endTime) {
211
+ // Overlapping with last source
212
+ var timeWidth = source.endTime - source.startTime;
213
+
214
+ source.updateTimes(
215
+ currentSource.source.endTime,
216
+ currentSource.source.endTime + timeWidth
217
+ );
218
+ }
219
+ currentSource.nextSourceId = source.id;
220
+ newSource.prevSourceId = currentSource.source.id;
221
+ this._sources[source.id] = newSource;
222
+ }
223
+ }
224
+ else {
225
+ this._firstSourceId = source.id;
226
+ this._sources[source.id] = newSource;
227
+ }
228
+
229
+ this.updateLineHeight(sourceGroup, 'add');
230
+ }
231
+
232
+ if (!sourceGroup.getParent() || !sourceGroup.isDescendantOf(this._group)) {
233
+ sourceGroup.addToGroup(this._group);
234
+ }
235
+ };
236
+
237
+ Line.prototype.addSegments = function(segmentsGroup) {
238
+ this._segmentsGroup = segmentsGroup;
239
+
240
+ this._height = this._segmentsGroup.getCurrentHeight();
241
+
242
+ segmentsGroup.addToGroup(this._group);
243
+ };
244
+
245
+ Line.prototype._canBePlacedBetween = function(startTime, endTime, startLimit, endLimit) {
246
+ var timeWidth = endTime - startTime;
247
+ var newTimes = null;
248
+
249
+ if ((!endLimit && startTime > startLimit) || (startTime > startLimit && endTime < endLimit)) {
250
+ // Can be placed at its wanted position with wanted start/end time
251
+ newTimes = {
252
+ startTime: startTime,
253
+ endTime: endTime
254
+ };
255
+ }
256
+ else if (endLimit - startLimit >= timeWidth) {
257
+ // Can be placed at its wanted position but not with its wanted start/end time
258
+ if (startTime > startLimit) {
259
+ newTimes = {
260
+ startTime: endLimit - timeWidth,
261
+ endTime: endLimit
262
+ };
263
+ }
264
+ else {
265
+ newTimes = {
266
+ startTime: startLimit,
267
+ endTime: startLimit + timeWidth
268
+ };
269
+ }
270
+ }
271
+
272
+ return newTimes;
273
+ };
274
+
275
+ Line.prototype.removeSourceGroup = function(source, isPermanent) {
276
+ var sourceGroup = this._sourcesGroup[source.id];
277
+
278
+ delete this._sourcesGroup[source.id];
279
+
280
+ if (isPermanent) {
281
+ if (this._sources[source.id].prevSourceId) {
282
+ this._sources[this._sources[source.id].prevSourceId].nextSourceId
283
+ = this._sources[source.id].nextSourceId;
284
+ }
285
+
286
+ if (this._sources[source.id].nextSourceId) {
287
+ this._sources[this._sources[source.id].nextSourceId].prevSourceId
288
+ = this._sources[source.id].prevSourceId;
289
+ }
290
+
291
+ if (this._firstSourceId === source.id) {
292
+ this._firstSourceId = this._sources[source.id].nextSourceId;
293
+ }
294
+
295
+ delete this._sources[source.id];
296
+
297
+ this.updateLineHeight(sourceGroup, 'remove');
298
+ }
299
+
300
+ return sourceGroup;
301
+ };
302
+
303
+ Line.prototype.getKonvaGroup = function() {
304
+ return this._group;
305
+ };
306
+
307
+ Line.prototype.getY = function() {
308
+ return this._group.y();
309
+ };
310
+
311
+ Line.prototype.getInitialY = function() {
312
+ return this._y;
313
+ };
314
+
315
+ Line.prototype.y = function(value, changeInitialY) {
316
+ this._group.y(value);
317
+ if (changeInitialY) {
318
+ this._y = value;
319
+ }
320
+ };
321
+
322
+ Line.prototype.moveOnY = function(dy) {
323
+ this._y += dy;
324
+ this._group.y(this._group.y() + dy);
325
+ };
326
+
327
+ Line.prototype.manageSourceOrder = function(source, newStartX, newEndX) {
328
+ var cursorX = this._view.getPointerPosition().x;
329
+ var tmpXs;
330
+
331
+ var newXs = {
332
+ startX: newStartX,
333
+ endX: newEndX
334
+ };
335
+
336
+ var sourceWidth = this._view.timeToPixels(source.endTime - source.startTime);
337
+
338
+ if (newStartX !== null && newEndX !== null) {
339
+ if (this._sources[source.id].prevSourceId) {
340
+ // there is another source to the left
341
+ var previousStartX = this._view.timeToPixels(
342
+ this._sources[this._sources[source.id].prevSourceId].source.startTime
343
+ );
344
+
345
+ if (cursorX + this._view.getFrameOffset() < previousStartX) {
346
+ // we want to change order
347
+ tmpXs = this._changeSourcePosition(
348
+ source,
349
+ sourceWidth,
350
+ cursorX + this._view.getFrameOffset()
351
+ );
352
+
353
+ if (tmpXs) {
354
+ newXs.startX = tmpXs.startTime;
355
+ newXs.endX = tmpXs.endTime;
356
+ }
357
+ }
358
+ }
359
+
360
+ if (this._sources[source.id].nextSourceId) {
361
+ // there is another source to the right
362
+ var nextEndX = this._view.timeToPixels(
363
+ this._sources[this._sources[source.id].nextSourceId].source.endTime
364
+ );
365
+
366
+ if (cursorX + this._view.getFrameOffset() > nextEndX) {
367
+ // we want to change order
368
+ tmpXs = this._changeSourcePosition(
369
+ source,
370
+ sourceWidth,
371
+ cursorX + this._view.getFrameOffset()
372
+ );
373
+
374
+ if (tmpXs) {
375
+ newXs.startX = tmpXs.startTime;
376
+ newXs.endX = tmpXs.endTime;
377
+ }
378
+ }
379
+ }
380
+ }
381
+
382
+ return newXs;
383
+ };
384
+
385
+ Line.prototype._changeSourcePosition = function(source, sourceWidth, x) {
386
+ if (this._firstSourceId) {
387
+ var currentRange = {
388
+ start: null,
389
+ end: null
390
+ };
391
+ var startLimit = null;
392
+ var endLimit = null;
393
+
394
+ do {
395
+ if (!currentRange.end) {
396
+ currentRange.end = this._sources[this._firstSourceId];
397
+ }
398
+ else {
399
+ currentRange.start = currentRange.end;
400
+ currentRange.end = this._sources[currentRange.start.nextSourceId];
401
+ }
402
+
403
+ if (currentRange.start) {
404
+ startLimit = this._view.timeToPixels(
405
+ currentRange.start.source.endTime
406
+ );
407
+ }
408
+ else {
409
+ startLimit = 0;
410
+ }
411
+
412
+ if (currentRange.end) {
413
+ endLimit = this._view.timeToPixels(
414
+ currentRange.end.source.startTime
415
+ );
416
+ }
417
+ else {
418
+ endLimit = null;
419
+ }
420
+
421
+ if (x > startLimit && (endLimit === null || x < endLimit)) {
422
+ var newTimes = this._canBePlacedBetween(
423
+ x,
424
+ x + sourceWidth,
425
+ startLimit,
426
+ endLimit
427
+ );
428
+
429
+ if (newTimes) {
430
+ source.updateTimes(
431
+ this._view.pixelsToTime(newTimes.startTime),
432
+ this._view.pixelsToTime(newTimes.endTime)
433
+ );
434
+ var prevSourceId = currentRange.start
435
+ ? currentRange.start.source.id
436
+ : null;
437
+
438
+ this._moveSource(this._sources[source.id].source, prevSourceId);
439
+ return newTimes;
440
+ }
441
+ }
442
+ } while (currentRange.end);
443
+ }
444
+
445
+ return null;
446
+ };
447
+
448
+ Line.prototype._moveSource = function(source, prevSourceId) {
449
+ // Remove source from the list
450
+ var sourceObj = this._sources[source.id];
451
+ var prevSource = this._sources[sourceObj.prevSourceId];
452
+ var nextSource = this._sources[sourceObj.nextSourceId];
453
+
454
+ if (prevSource) {
455
+ this._sources[sourceObj.prevSourceId].nextSourceId = sourceObj.nextSourceId;
456
+ }
457
+ else {
458
+ this._firstSourceId = sourceObj.nextSourceId;
459
+ }
460
+
461
+ if (nextSource) {
462
+ this._sources[sourceObj.nextSourceId].prevSourceId = sourceObj.prevSourceId;
463
+ }
464
+
465
+ delete this._sources[source.id];
466
+
467
+ // Add source back to the list
468
+ sourceObj.prevSourceId = prevSourceId;
469
+
470
+ if (prevSourceId) {
471
+ sourceObj.nextSourceId = this._sources[prevSourceId].nextSourceId;
472
+ this._sources[prevSourceId].nextSourceId = source.id;
473
+ }
474
+ else {
475
+ sourceObj.nextSourceId = this._firstSourceId;
476
+ this._firstSourceId = source.id;
477
+ }
478
+
479
+ if (sourceObj.nextSourceId) {
480
+ this._sources[sourceObj.nextSourceId].prevSourceId = source.id;
481
+ }
482
+
483
+ this._sources[source.id] = sourceObj;
484
+ };
485
+
486
+ Line.prototype.manageCollision = function(source, newStartX, newEndX) {
487
+ var newStartTime = null;
488
+ var newEndTime = null;
489
+ var startLimited = false;
490
+ var endLimited = false;
491
+ var timeWidth = this._view.pixelsToTime(newEndX - newStartX);
492
+
493
+ var newXs = {
494
+ startX: newStartX,
495
+ endX: newEndX,
496
+ updateWidth: false,
497
+ updateTimelineLength: false
498
+ };
499
+
500
+ if (newStartX !== null) {
501
+ // startMarker changed
502
+ newStartTime = this._view.pixelsToTime(newStartX);
503
+
504
+ if (source.startTime > newStartTime) {
505
+ // startMarker moved to the left
506
+ if (this._sources[source.id].prevSourceId) {
507
+ // there is another source to the left
508
+ var previousSource = this._sources[this._sources[source.id].prevSourceId]
509
+ .source;
510
+
511
+ if (newStartTime < previousSource.endTime) {
512
+ // there is collision
513
+ newStartTime = previousSource.endTime;
514
+ startLimited = true;
515
+ }
516
+ }
517
+ else {
518
+ if (newStartTime < 0) {
519
+ newStartTime = 0;
520
+ startLimited = true;
521
+ }
522
+ }
523
+ }
524
+ }
525
+
526
+ if (newEndX !== null) {
527
+ // endMarker changed
528
+ newEndTime = this._view.pixelsToTime(newEndX);
529
+
530
+ if (source.endTime < newEndTime) {
531
+ // endMarker moved to the right
532
+ if (this._sources[source.id].nextSourceId) {
533
+ // there is another source to the right
534
+ var nextSource = this._sources[this._sources[source.id].nextSourceId]
535
+ .source;
536
+
537
+ if (newEndTime > nextSource.startTime) {
538
+ // there is collision
539
+ newEndTime = nextSource.startTime;
540
+ endLimited = true;
541
+ }
542
+ }
543
+ }
544
+ }
545
+
546
+ // Update the other edge if dragging and collision
547
+ if (newStartTime !== null && newEndTime !== null) {
548
+ if (startLimited) {
549
+ newEndTime = newStartTime + timeWidth;
550
+ }
551
+
552
+ if (endLimited) {
553
+ newStartTime = newEndTime - timeWidth;
554
+ }
555
+ }
556
+
557
+ // Check for minimal size of source
558
+ if (newStartTime !== null && newEndTime === null) {
559
+ if (source.endTime - newStartTime < source.minSize) {
560
+ newStartTime = source.endTime - source.minSize;
561
+ }
562
+ }
563
+ else if (newEndTime !== null && newStartTime === null) {
564
+ if (newEndTime - source.startTime < source.minSize) {
565
+ newEndTime = source.startTime + source.minSize;
566
+ }
567
+ }
568
+ else {
569
+ if (newEndTime - newStartTime < source.minSize) {
570
+ if (source.endTime !== newEndTime) {
571
+ newEndTime = newStartTime + source.minSize;
572
+ }
573
+ if (source.startTime !== newStartTime) {
574
+ newStartTime = newEndTime - source.minSize;
575
+ }
576
+ }
577
+ }
578
+
579
+ if (newStartTime !== null) {
580
+ newXs.startX = this._view.timeToPixels(newStartTime);
581
+ }
582
+
583
+ if (newEndTime !== null) {
584
+ newXs.endX = this._view.timeToPixels(newEndTime);
585
+
586
+ if (this._sources[source.id].nextSourceId === null) {
587
+ newXs.updateTimelineLength = true;
588
+ }
589
+ }
590
+
591
+ if ((newXs.startX !== null && newXs.endX === null)
592
+ || (newXs.startX === null && newXs.endX !== null)) {
593
+ newXs.updateWidth = true;
594
+ }
595
+
596
+ return newXs;
597
+ };
598
+
599
+ Line.prototype.rescale = function() {
600
+ for (var sourceId in this._sourcesGroup) {
601
+ if (Utils.objectHasProperty(this._sourcesGroup, sourceId)) {
602
+ this._sourcesGroup[sourceId].rescale();
603
+ }
604
+ }
605
+ };
606
+
607
+ Line.prototype.updatePosition = function(pos) {
608
+ for (var sourceId in this._sources) {
609
+ if (Utils.objectHasProperty(this._sources, sourceId)) {
610
+ this._sources[sourceId].source.position = pos;
611
+ }
612
+ }
613
+
614
+ this._position = pos;
615
+ };
616
+
617
+ Line.prototype.destroy = function() {
618
+ this._firstSourceId = null;
619
+ this._sources = {};
620
+ this._sourcesGroup = {};
621
+ this._group.destroy();
622
+ };
623
+
624
+ Line.prototype.allowInteractions = function(bool) {
625
+ this._group.listening(bool);
626
+ };
627
+
628
+ return Line;
629
+ });