@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.
@@ -0,0 +1,91 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Factory functions for creating point and segment marker handles.
5
+ *
6
+ * @module marker-factories
7
+ */
8
+
9
+ define([
10
+ './default-segment-marker',
11
+ './utils',
12
+ 'konva'
13
+ ], function(
14
+ DefaultSegmentMarker,
15
+ Utils,
16
+ Konva) {
17
+ 'use strict';
18
+
19
+ /**
20
+ * Parameters for the {@link createSegmentMarker} function.
21
+ *
22
+ * @typedef {Object} CreateSegmentMarkerOptions
23
+ * @global
24
+ * @property {Object} peaks
25
+ * @property {Segment} segment
26
+ * @property {Boolean} draggable If true, marker is draggable.
27
+ * @property {Boolean} startMarker
28
+ * @property {String} color
29
+ * @property {Group} group
30
+ * @property {String} view
31
+ */
32
+
33
+ /**
34
+ * Creates a left or right side segment marker handle.
35
+ *
36
+ * @param {CreateSegmentMarkerOptions} options
37
+ * @returns {Marker}
38
+ */
39
+
40
+ function createSegmentMarker(options) {
41
+ if (options.view.getName() === 'zoomview') {
42
+ return new DefaultSegmentMarker(options);
43
+ }
44
+
45
+ return null;
46
+ }
47
+
48
+ /**
49
+ * Parameters for the {@link createSegmentLabel} function.
50
+ *
51
+ * @typedef {Object} SegmentLabelOptions
52
+ * @global
53
+ * @property {Segment} segment The {@link Segment} object associated with this
54
+ * label.
55
+ * @property {String} view The name of the view that the label is being
56
+ * created in, either <code>zoomview</code> or <code>overview</code>.
57
+ * @property {SegmentsLayer} layer
58
+ */
59
+
60
+ /**
61
+ * Creates a Konva object that renders information about a segment, such as
62
+ * its label text.
63
+ *
64
+ * @param {SegmentLabelOptions} options
65
+ * @returns {Konva.Text}
66
+ */
67
+
68
+ function createSegmentLabel(options) {
69
+ return new Konva.Text({
70
+ x: options.x,
71
+ y: options.y,
72
+ width: options.width,
73
+ height: options.height,
74
+ text: Utils.removeLineBreaks(options.segment.labelText),
75
+ textAlign: 'left',
76
+ verticalAlign: 'middle',
77
+ wrap: 'none',
78
+ ellipsis: true,
79
+ padding: 5,
80
+ fontSize: options.fontSize,
81
+ fontFamily: 'Open Sans',
82
+ fill: options.segment.textColor,
83
+ listening: false
84
+ });
85
+ }
86
+
87
+ return {
88
+ createSegmentMarker: createSegmentMarker,
89
+ createSegmentLabel: createSegmentLabel
90
+ };
91
+ });
@@ -0,0 +1,361 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Defines the {@link ModeLayer} class.
5
+ *
6
+ * @module mode-layer
7
+ */
8
+
9
+ define([
10
+ './utils',
11
+ './source-group',
12
+ 'konva'
13
+ ], function(Utils, SourceGroup, Konva) {
14
+ 'use strict';
15
+
16
+ var TIME_X_OFFSET = 20;
17
+ var TIME_Y_OFFSET = 10;
18
+
19
+ /**
20
+ * Creates a Konva.Layer that displays additionnal information for alternative modes.
21
+ *
22
+ * @class
23
+ * @alias ModeLayer
24
+ *
25
+ * @param {Peaks} peaks
26
+ * @param {WaveformOverview|WaveformZoomView} view
27
+ * @param {String} initialMode
28
+ */
29
+
30
+ function ModeLayer(peaks, view, stage, initialMode) {
31
+ this._peaks = peaks;
32
+ this._view = view;
33
+ this._stage = stage;
34
+
35
+ this._layer = new Konva.Layer({
36
+ listening: this._mode !== 'default'
37
+ });
38
+
39
+ this._prepareDefaultMode();
40
+
41
+ this._onMouseClickInDefaultMode = this._onMouseClickInDefaultMode.bind(this);
42
+ this._onKeyboardDelete = this._onKeyboardDelete.bind(this);
43
+
44
+ this._prepareCutMode();
45
+
46
+ this._onMouseEnterInCutMode = this._onMouseEnterInCutMode.bind(this);
47
+ this._onMouseMoveInCutMode = this._onMouseMoveInCutMode.bind(this);
48
+ this._onMouseLeaveInCutMode = this._onMouseLeaveInCutMode.bind(this);
49
+ this._onMouseClickInCutMode = this._onMouseClickInCutMode.bind(this);
50
+
51
+ this.setMode(initialMode);
52
+ }
53
+
54
+ /**
55
+ * Adds the layer to the given {Konva.Stage}.
56
+ *
57
+ * @param {Konva.Stage} stage
58
+ */
59
+
60
+ ModeLayer.prototype.addToStage = function(stage) {
61
+ stage.add(this._layer);
62
+ };
63
+
64
+ ModeLayer.prototype._prepareDefaultMode = function() {
65
+ this._selectedElement = null;
66
+ };
67
+
68
+ ModeLayer.prototype._prepareCutMode = function() {
69
+ this._cutGroup = new Konva.Group({
70
+ width: this._view.getWidth(),
71
+ height: this._view.getHeight(),
72
+ visible: false,
73
+ listening: false
74
+ });
75
+
76
+ this._cursorTime = new Konva.Group({
77
+ listening: false
78
+ });
79
+
80
+ this._timeLabel = new Konva.Text({
81
+ x: 4,
82
+ y: 2,
83
+ text: '00:00:00',
84
+ fontSize: 11,
85
+ fontFamily: 'Open Sans',
86
+ fill: '#999b9d',
87
+ align: 'center',
88
+ listening: false
89
+ });
90
+
91
+ this._timeBackground = new Konva.Rect({
92
+ width: this._timeLabel.width() + 8,
93
+ height: this._timeLabel.height() + 4,
94
+ cornerRadius: 3,
95
+ fill: '#37373c',
96
+ strokeWidth: 0,
97
+ listening: false
98
+ });
99
+
100
+ this._cursorTime.add(this._timeBackground);
101
+ this._cursorTime.add(this._timeLabel);
102
+
103
+ this._cuttingLine = new Konva.Line({
104
+ points: [0, 0, 0, 10],
105
+ stroke: 'red',
106
+ strokeWidth: 1,
107
+ opacity: 0.8,
108
+ listening: false,
109
+ visible: false
110
+ });
111
+
112
+ this._cutGroup.add(this._cursorTime);
113
+ this._cutGroup.add(this._cuttingLine);
114
+
115
+ this._layer.add(this._cutGroup);
116
+ };
117
+
118
+ ModeLayer.prototype._onMouseClickInDefaultMode = function() {
119
+ var hoveredElement = this._view.getHoveredElement();
120
+
121
+ if (hoveredElement) {
122
+ if (this._selectedElement) {
123
+ this._selectedElement.setSelected(false);
124
+ }
125
+
126
+ this._selectedElement = hoveredElement;
127
+
128
+ this._selectedElement.setSelected(true);
129
+ }
130
+ else {
131
+ if (this._selectedElement) {
132
+ this._selectedElement.setSelected(false);
133
+ this._selectedElement = null;
134
+ }
135
+ }
136
+ };
137
+
138
+ ModeLayer.prototype._onKeyboardDelete = function() {
139
+ if (this._selectedElement && this._view.isFocused()) {
140
+ this._peaks.destroySource(this._selectedElement.getSource().id);
141
+ }
142
+ };
143
+
144
+ ModeLayer.prototype._onMouseEnterInCutMode = function() {
145
+ this._cutGroup.visible(true);
146
+
147
+ this._layer.draw();
148
+ };
149
+
150
+ ModeLayer.prototype._updateCursorTime = function(mousePosition) {
151
+ var hoveredElement = this._view.getHoveredElement();
152
+
153
+ if (hoveredElement && hoveredElement instanceof SourceGroup) {
154
+ this._timeLabel.text(
155
+ Utils.formatTime(
156
+ this._view.pixelsToTime(mousePosition.x + this._view.getFrameOffset()),
157
+ false
158
+ )
159
+ );
160
+
161
+ this._timeBackground.width(this._timeLabel.width() + 8);
162
+
163
+ this._cursorTime.x(mousePosition.x + TIME_X_OFFSET);
164
+
165
+ if (mousePosition.y > this._view.getHeight()
166
+ - this._timeBackground.height() - TIME_Y_OFFSET) {
167
+ this._cursorTime.y(mousePosition.y - TIME_Y_OFFSET);
168
+ }
169
+ else {
170
+ this._cursorTime.y(mousePosition.y + TIME_Y_OFFSET);
171
+ }
172
+
173
+ this._cursorTime.visible(true);
174
+ }
175
+ else {
176
+ this._cursorTime.visible(false);
177
+ }
178
+ };
179
+
180
+ ModeLayer.prototype._updateCuttingLine = function(mousePosition) {
181
+ var hoveredElement = this._view.getHoveredElement();
182
+
183
+ if (hoveredElement && hoveredElement instanceof SourceGroup) {
184
+ var minSize = this._view.timeToPixels(hoveredElement.getSource().minSize);
185
+
186
+ if (hoveredElement.getWidth() >= 2 * minSize) {
187
+ var height = hoveredElement.getCurrentHeight();
188
+ var y = hoveredElement.getY();
189
+
190
+ if (mousePosition.x < hoveredElement.x() + minSize) {
191
+ this._cuttingLine.points([
192
+ hoveredElement.x() + minSize,
193
+ y,
194
+ hoveredElement.x() + minSize,
195
+ y + height
196
+ ]);
197
+ }
198
+ else if (mousePosition.x
199
+ > hoveredElement.x() + hoveredElement.getWidth() - minSize) {
200
+ this._cuttingLine.points([
201
+ hoveredElement.x() + hoveredElement.getWidth() - minSize,
202
+ y,
203
+ hoveredElement.x() + hoveredElement.getWidth() - minSize,
204
+ y + height
205
+ ]);
206
+ }
207
+ else {
208
+ this._cuttingLine.points([mousePosition.x, y, mousePosition.x, y + height]);
209
+ }
210
+ this._cuttingLine.visible(true);
211
+ }
212
+ else {
213
+ this._view.setCursor('not-allowed');
214
+ }
215
+ }
216
+ else {
217
+ this._cuttingLine.visible(false);
218
+
219
+ if (this._view.getCursor('not-allowed')) {
220
+ this._view.setCursor('default');
221
+ }
222
+ }
223
+ };
224
+
225
+ ModeLayer.prototype._onMouseMoveInCutMode = function() {
226
+ var mousePosition = this._stage.getPointerPosition();
227
+
228
+ mousePosition.x = this._view.timeToPixels(this._view.pixelsToTime(mousePosition.x));
229
+
230
+ this._updateCursorTime(mousePosition);
231
+ this._updateCuttingLine(mousePosition);
232
+
233
+ this._layer.draw();
234
+ };
235
+
236
+ ModeLayer.prototype._onMouseLeaveInCutMode = function() {
237
+ this._cutGroup.visible(false);
238
+
239
+ this._layer.draw();
240
+ };
241
+
242
+ ModeLayer.prototype._onMouseClickInCutMode = function() {
243
+ var mousePosition = this._stage.getPointerPosition();
244
+
245
+ mousePosition.x = this._view.timeToPixels(this._view.pixelsToTime(mousePosition.x));
246
+
247
+ var hoveredElement = this._view.getHoveredElement();
248
+
249
+ if (hoveredElement && hoveredElement instanceof SourceGroup) {
250
+ var minSize = this._view.timeToPixels(hoveredElement.getSource().minSize);
251
+
252
+ if (hoveredElement.getWidth() >= 2 * minSize) {
253
+ var cuttingPixel;
254
+
255
+ if (mousePosition.x < hoveredElement.x() + minSize) {
256
+ cuttingPixel = hoveredElement.x() + minSize;
257
+ }
258
+ else if (mousePosition.x
259
+ > hoveredElement.x() + hoveredElement.getWidth() - minSize) {
260
+ cuttingPixel = hoveredElement.x()
261
+ + hoveredElement.getWidth() - minSize;
262
+ }
263
+ else {
264
+ cuttingPixel = mousePosition.x;
265
+ }
266
+
267
+ // Relative cuttingPixel
268
+ cuttingPixel -= hoveredElement.x();
269
+
270
+ this._cuttingLine.visible(false);
271
+
272
+ this._peaks.emit(
273
+ 'source.cut',
274
+ hoveredElement.getSource(),
275
+ this._view.pixelsToTime(cuttingPixel)
276
+ );
277
+ }
278
+ }
279
+ };
280
+
281
+ ModeLayer.prototype.setMode = function(mode) {
282
+ if (this._mode === mode) {
283
+ return;
284
+ }
285
+ var hoveredElement = this._view.getHoveredElement();
286
+
287
+ // Clean current mode
288
+ switch (this._mode) {
289
+ case 'cut':
290
+ this._stage.off('mouseover', this._onMouseEnterInCutMode);
291
+ this._stage.off('mousemove', this._onMouseMoveInCutMode);
292
+ this._stage.off('mouseleave', this._onMouseLeaveInCutMode);
293
+ this._stage.off('click', this._onMouseClickInCutMode);
294
+ this._cutGroup.visible(false);
295
+
296
+ if (hoveredElement && hoveredElement instanceof SourceGroup) {
297
+ hoveredElement.toggleDragging(hoveredElement.getSource().draggable);
298
+ hoveredElement.toggleResizing(hoveredElement.getSource().resizable);
299
+ }
300
+
301
+ this._view.toggleMainCursor(false);
302
+ break;
303
+ case 'default':
304
+ this._stage.off('click', this._onMouseClickInDefaultMode);
305
+ this._peaks.off('keyboard.delete', this._onKeyboardDelete);
306
+ if (this._selectedElement) {
307
+ this._selectedElement.setSelected(false);
308
+ }
309
+ break;
310
+ }
311
+
312
+ this._selectedElement = null;
313
+
314
+ // Set new mode
315
+ switch (mode) {
316
+ case 'cut':
317
+ this._stage.on('mouseover', this._onMouseEnterInCutMode);
318
+ this._stage.on('mousemove', this._onMouseMoveInCutMode);
319
+ this._stage.on('mouseleave', this._onMouseLeaveInCutMode);
320
+ this._stage.on('click', this._onMouseClickInCutMode);
321
+
322
+ var mousePosition = this._stage.getPointerPosition();
323
+
324
+ if (mousePosition) {
325
+ mousePosition.x = this._view.timeToPixels(this._view.pixelsToTime(mousePosition.x));
326
+
327
+ if (mousePosition.x > 0 && mousePosition.x < this._view.getWidth()
328
+ && mousePosition.y > 0 && mousePosition.y < this._view.getHeight()) {
329
+ this._cutGroup.visible(true);
330
+
331
+ this._updateCursorTime(mousePosition);
332
+ this._updateCuttingLine(mousePosition);
333
+ }
334
+ }
335
+
336
+ if (hoveredElement && hoveredElement instanceof SourceGroup) {
337
+ hoveredElement.toggleDragging(false);
338
+ hoveredElement.toggleResizing(false);
339
+ }
340
+
341
+ this._view.toggleMainCursor(true, 'default');
342
+ break;
343
+ case 'default':
344
+ this._stage.on('click', this._onMouseClickInDefaultMode);
345
+ this._peaks.on('keyboard.delete', this._onKeyboardDelete);
346
+ break;
347
+ default:
348
+ return;
349
+ }
350
+
351
+ this._mode = mode;
352
+ this._layer.draw();
353
+ this._view.drawSourcesLayer();
354
+ };
355
+
356
+ ModeLayer.prototype.getCurrentMode = function() {
357
+ return this._mode;
358
+ };
359
+
360
+ return ModeLayer;
361
+ });
@@ -0,0 +1,207 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Defines the {@link MouseDragHandler} class.
5
+ *
6
+ * @module mouse-drag-handler
7
+ */
8
+
9
+ define([
10
+ 'konva'
11
+ ], function(Konva) {
12
+ 'use strict';
13
+
14
+ function getMarkerObject(obj) {
15
+ while (obj.parent !== null) {
16
+ if (obj.parent instanceof Konva.Layer) {
17
+ return obj;
18
+ }
19
+
20
+ obj = obj.parent;
21
+ }
22
+
23
+ return null;
24
+ }
25
+
26
+ /**
27
+ * An object to receive callbacks on mouse drag events. Each function is
28
+ * called with the current mouse X position, relative to the stage's
29
+ * container HTML element.
30
+ *
31
+ * @typedef {Object} MouseDragHandlers
32
+ * @global
33
+ * @property {Function} onMouseDown Mouse down event handler.
34
+ * @property {Function} onMouseMove Mouse move event handler.
35
+ * @property {Function} onMouseUp Mouse up event handler.
36
+ */
37
+
38
+ /**
39
+ * Creates a handler for mouse events to allow interaction with the waveform
40
+ * views by clicking and dragging the mouse.
41
+ *
42
+ * @class
43
+ * @alias MouseDragHandler
44
+ *
45
+ * @param {Konva.Stage} stage
46
+ * @param {MouseDragHandlers} handlers
47
+ */
48
+
49
+ function MouseDragHandler(stage, handlers) {
50
+ this._stage = stage;
51
+ this._handlers = handlers;
52
+ this._dragging = false;
53
+ this._mouseDown = this._mouseDown.bind(this);
54
+ this._mouseUp = this._mouseUp.bind(this);
55
+ this._mouseMove = this._mouseMove.bind(this);
56
+
57
+ this._stage.on('mousedown', this._mouseDown);
58
+ this._stage.on('touchstart', this._mouseDown);
59
+
60
+ this._mouseDownClientX = null;
61
+ }
62
+
63
+ /**
64
+ * Mouse down event handler.
65
+ *
66
+ * @param {MouseEvent} event
67
+ */
68
+
69
+ MouseDragHandler.prototype._mouseDown = function(event) {
70
+ var marker = getMarkerObject(event.target);
71
+
72
+ // Avoid interfering with drag/drop of point and segment markers.
73
+ if (marker && marker.attrs.draggable) {
74
+ return;
75
+ }
76
+
77
+ if (event.type === 'touchstart') {
78
+ this._mouseDownClientX = Math.floor(event.evt.touches[0].clientX);
79
+ }
80
+ else {
81
+ this._mouseDownClientX = event.evt.clientX;
82
+ }
83
+
84
+ if (this._handlers.onMouseDown) {
85
+ var mouseDownPosX = this._getMousePosX(this._mouseDownClientX);
86
+ var mouseDownPosY = this._getMousePosY(event.evt.clientY);
87
+
88
+ this._handlers.onMouseDown(mouseDownPosX, mouseDownPosY);
89
+ }
90
+
91
+ // Use the window mousemove and mouseup handlers instead of the
92
+ // Konva.Stage ones so that we still receive events if the user moves the
93
+ // mouse outside the stage.
94
+ window.addEventListener('mousemove', this._mouseMove, false);
95
+ window.addEventListener('touchmove', this._mouseMove, false);
96
+ window.addEventListener('mouseup', this._mouseUp, false);
97
+ window.addEventListener('touchend', this._mouseUp, false);
98
+ window.addEventListener('blur', this._mouseUp, false);
99
+ };
100
+
101
+ /**
102
+ * Mouse move event handler.
103
+ *
104
+ * @param {MouseEvent} event
105
+ */
106
+
107
+ MouseDragHandler.prototype._mouseMove = function(event) {
108
+ var clientX = null;
109
+
110
+ if (event.type === 'touchmove') {
111
+ clientX = Math.floor(event.changedTouches[0].clientX);
112
+ }
113
+ else {
114
+ clientX = event.clientX;
115
+ }
116
+
117
+ // Don't update on vertical mouse movement.
118
+ if (clientX === this._mouseDownClientX) {
119
+ return;
120
+ }
121
+
122
+ this._dragging = true;
123
+
124
+ if (this._handlers.onMouseMove) {
125
+ var mousePosX = this._getMousePosX(clientX);
126
+ var mousePosY = this._getMousePosY(event.clientY);
127
+
128
+ this._handlers.onMouseMove(mousePosX, mousePosY);
129
+ }
130
+ };
131
+
132
+ /**
133
+ * Mouse up event handler.
134
+ *
135
+ * @param {MouseEvent} event
136
+ */
137
+
138
+ MouseDragHandler.prototype._mouseUp = function(event) {
139
+ var clientX = null;
140
+
141
+ if (event.type === 'touchend') {
142
+ clientX = Math.floor(event.changedTouches[0].clientX);
143
+ if (event.cancelable) {
144
+ event.preventDefault();
145
+ }
146
+ }
147
+ else {
148
+ clientX = event.clientX;
149
+ }
150
+
151
+ if (this._handlers.onMouseUp) {
152
+ var mousePosX = this._getMousePosX(clientX);
153
+ var mousePosY = this._getMousePosY(event.clientY);
154
+
155
+ this._handlers.onMouseUp(mousePosX, event, mousePosY);
156
+ }
157
+
158
+ window.removeEventListener('mousemove', this._mouseMove, false);
159
+ window.removeEventListener('touchmove', this._mouseMove, false);
160
+ window.removeEventListener('mouseup', this._mouseUp, false);
161
+ window.removeEventListener('touchend', this._mouseUp, false);
162
+ window.removeEventListener('blur', this._mouseUp, false);
163
+
164
+ this._dragging = false;
165
+ };
166
+
167
+ /**
168
+ * @returns {Number} The mouse X position, relative to the container that
169
+ * received the mouse down event.
170
+ *
171
+ * @private
172
+ * @param {Number} clientX Mouse client X position.
173
+ */
174
+
175
+ MouseDragHandler.prototype._getMousePosX = function(clientX) {
176
+ var containerPos = this._stage.getContainer().getBoundingClientRect();
177
+
178
+ return clientX - containerPos.left;
179
+ };
180
+
181
+ /**
182
+ * @returns {Number} The mouse Y position, relative to the container that
183
+ * received the mouse down event.
184
+ *
185
+ * @private
186
+ * @param {Number} clientY Mouse client X position.
187
+ */
188
+
189
+ MouseDragHandler.prototype._getMousePosY = function(clientY) {
190
+ var containerPos = this._stage.getContainer().getBoundingClientRect();
191
+
192
+ return clientY - containerPos.top;
193
+ };
194
+
195
+ /**
196
+ * Returns <code>true</code> if the mouse is being dragged, i.e., moved with
197
+ * the mouse button held down.
198
+ *
199
+ * @returns {Boolean}
200
+ */
201
+
202
+ MouseDragHandler.prototype.isDragging = function() {
203
+ return this._dragging;
204
+ };
205
+
206
+ return MouseDragHandler;
207
+ });