@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/CHANGELOG.md +530 -0
- package/COPYING +165 -0
- package/README.md +1184 -0
- package/package.json +88 -0
- package/peaks.js +20174 -0
- package/peaks.js.d.ts +332 -0
- package/src/data-retriever.js +90 -0
- package/src/data.js +56 -0
- package/src/default-segment-marker.js +132 -0
- package/src/keyboard-handler.js +112 -0
- package/src/line-indicator.js +312 -0
- package/src/line.js +629 -0
- package/src/lines.js +356 -0
- package/src/main.js +663 -0
- package/src/marker-factories.js +91 -0
- package/src/mode-layer.js +361 -0
- package/src/mouse-drag-handler.js +207 -0
- package/src/player.js +178 -0
- package/src/playhead-layer.js +413 -0
- package/src/segment-marker.js +155 -0
- package/src/segment-shape.js +345 -0
- package/src/segment.js +229 -0
- package/src/segments-group.js +697 -0
- package/src/source-group.js +975 -0
- package/src/source.js +688 -0
- package/src/sources-layer.js +509 -0
- package/src/timeline-axis.js +238 -0
- package/src/timeline-segments.js +389 -0
- package/src/timeline-sources.js +431 -0
- package/src/timeline-zoomview.js +866 -0
- package/src/utils.js +339 -0
- package/src/waveform-builder.js +458 -0
- package/src/waveform-shape.js +223 -0
|
@@ -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
|
+
});
|