wavesurfer-rails 0.1.12 → 0.1.21

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,213 @@
1
+ 'use strict';
2
+
3
+ /* Minimap */
4
+ WaveSurfer.Minimap = WaveSurfer.util.extend({}, WaveSurfer.Drawer, WaveSurfer.Drawer.Canvas, {
5
+ init: function (wavesurfer, params) {
6
+ this.wavesurfer = wavesurfer;
7
+ this.container = this.wavesurfer.drawer.container;
8
+ this.lastPos = this.wavesurfer.drawer.lastPos;
9
+ this.params = wavesurfer.util.extend(
10
+ {}, this.wavesurfer.drawer.params, {
11
+ showRegions: false,
12
+ showOverview: false,
13
+ overviewBorderColor: 'green',
14
+ overviewBorderSize: 2
15
+ }, params, {
16
+ scrollParent: false,
17
+ fillParent: true
18
+ }
19
+ );
20
+
21
+ this.width = 0;
22
+ this.height = this.params.height * this.params.pixelRatio;
23
+
24
+ this.createWrapper();
25
+ this.createElements();
26
+
27
+ if (WaveSurfer.Regions && this.params.showRegions) {
28
+ this.regions();
29
+ }
30
+
31
+ this.bindWaveSurferEvents();
32
+ this.bindMinimapEvents();
33
+ },
34
+ regions: function() {
35
+ var my = this;
36
+ this.regions = {};
37
+
38
+ this.wavesurfer.on('region-created', function(region) {
39
+ my.regions[region.id] = region;
40
+ my.renderRegions();
41
+ });
42
+
43
+ this.wavesurfer.on('region-updated', function(region) {
44
+ my.regions[region.id] = region;
45
+ my.renderRegions();
46
+ });
47
+
48
+ this.wavesurfer.on('region-removed', function(region) {
49
+ delete my.regions[region.id];
50
+ my.renderRegions();
51
+ });
52
+ },
53
+ renderRegions: function() {
54
+ var my = this;
55
+ var regionElements = this.wrapper.querySelectorAll('region');
56
+ for (var i = 0; i < regionElements.length; ++i) {
57
+ this.wrapper.removeChild(regionElements[i]);
58
+ }
59
+
60
+ Object.keys(this.regions).forEach(function(id){
61
+ var region = my.regions[id];
62
+ var width = (my.width * ((region.end - region.start) / my.wavesurfer.getDuration()));
63
+ var left = (my.width * (region.start / my.wavesurfer.getDuration()));
64
+ var regionElement = my.style(document.createElement('region'), {
65
+ height: 'inherit',
66
+ backgroundColor: region.color,
67
+ width: width + 'px',
68
+ left: left + 'px',
69
+ display: 'block',
70
+ position: 'absolute'
71
+ });
72
+ regionElement.classList.add(id);
73
+ my.wrapper.appendChild(regionElement);
74
+ });
75
+ },
76
+ createElements: function() {
77
+ WaveSurfer.Drawer.Canvas.createElements.call(this);
78
+
79
+ if (this.params.showOverview) {
80
+ this.overviewRegion = this.style(document.createElement('overview'), {
81
+ height: (this.wrapper.offsetHeight - (this.params.overviewBorderSize * 2)) + 'px',
82
+ width: '0px',
83
+ display: 'block',
84
+ position: 'absolute',
85
+ cursor: 'move',
86
+ border: this.params.overviewBorderSize + 'px solid ' + this.params.overviewBorderColor,
87
+ zIndex: 2,
88
+ opacity: this.params.overviewOpacity
89
+ });
90
+
91
+ this.wrapper.appendChild(this.overviewRegion);
92
+ }
93
+ },
94
+
95
+ bindWaveSurferEvents: function () {
96
+ var my = this;
97
+ this.wavesurfer.on('ready', this.render.bind(this));
98
+ this.wavesurfer.on('audioprocess', function (currentTime) {
99
+ my.progress(my.wavesurfer.backend.getPlayedPercents());
100
+ });
101
+ this.wavesurfer.on('seek', function(progress) {
102
+ my.progress(my.wavesurfer.backend.getPlayedPercents());
103
+ });
104
+
105
+ if (this.params.showOverview) {
106
+ this.wavesurfer.on('scroll', function(event) {
107
+ if (!my.draggingOverview) {
108
+ my.moveOverviewRegion(event.target.scrollLeft / my.ratio);
109
+ }
110
+ });
111
+
112
+ this.wavesurfer.drawer.wrapper.addEventListener('mouseover', function(event) {
113
+ if (my.draggingOverview) {
114
+ my.draggingOverview = false;
115
+ }
116
+ });
117
+ }
118
+
119
+ var prevWidth = 0;
120
+ var onResize = function () {
121
+ if (prevWidth != my.wrapper.clientWidth) {
122
+ prevWidth = my.wrapper.clientWidth;
123
+ my.render();
124
+ my.progress(my.wavesurfer.backend.getPlayedPercents());
125
+ }
126
+ };
127
+ window.addEventListener('resize', onResize, true);
128
+
129
+ this.wavesurfer.on('destroy', function () {
130
+ my.destroy.bind(this);
131
+ window.removeEventListener('resize', onResize, true);
132
+ });
133
+ },
134
+
135
+ bindMinimapEvents: function () {
136
+ var my = this;
137
+ var relativePositionX = 0;
138
+ var seek = true;
139
+ var positionMouseDown = {
140
+ clientX: 0,
141
+ clientY: 0
142
+ };
143
+
144
+ this.on('click', (function (e, position) {
145
+ if (seek) {
146
+ this.progress(position);
147
+ this.wavesurfer.seekAndCenter(position);
148
+ } else {
149
+ seek = true;
150
+ }
151
+ }).bind(this));
152
+
153
+ if (this.params.showOverview) {
154
+ this.overviewRegion.addEventListener('mousedown', function(event) {
155
+ my.draggingOverview = true;
156
+ relativePositionX = event.layerX;
157
+ positionMouseDown.clientX = event.clientX;
158
+ positionMouseDown.clientY = event.clientY;
159
+ });
160
+
161
+ this.wrapper.addEventListener('mousemove', function(event) {
162
+ if(my.draggingOverview) {
163
+ my.moveOverviewRegion(event.clientX - my.container.getBoundingClientRect().left - relativePositionX);
164
+ }
165
+ });
166
+
167
+ this.wrapper.addEventListener('mouseup', function(event) {
168
+ if (positionMouseDown.clientX - event.clientX === 0 && positionMouseDown.clientX - event.clientX === 0) {
169
+ seek = true;
170
+ my.draggingOverview = false;
171
+ } else if (my.draggingOverview) {
172
+ seek = false;
173
+ my.draggingOverview = false;
174
+ }
175
+ });
176
+ }
177
+ },
178
+
179
+ render: function () {
180
+ var len = this.getWidth();
181
+ var peaks = this.wavesurfer.backend.getPeaks(len);
182
+ this.drawPeaks(peaks, len);
183
+
184
+ if (this.params.showOverview) {
185
+ //get proportional width of overview region considering the respective
186
+ //width of the drawers
187
+ this.ratio = this.wavesurfer.drawer.width / this.width;
188
+ this.waveShowedWidth = this.wavesurfer.drawer.width / this.ratio;
189
+ this.waveWidth = this.wavesurfer.drawer.width;
190
+ this.overviewWidth = (this.width / this.ratio);
191
+ this.overviewPosition = 0;
192
+ this.overviewRegion.style.width = (this.overviewWidth - (this.params.overviewBorderSize * 2)) + 'px';
193
+ }
194
+ },
195
+ moveOverviewRegion: function(pixels) {
196
+ if (pixels < 0) {
197
+ this.overviewPosition = 0;
198
+ } else if (pixels + this.overviewWidth < this.width) {
199
+ this.overviewPosition = pixels;
200
+ } else {
201
+ this.overviewPosition = (this.width - this.overviewWidth);
202
+ }
203
+ this.overviewRegion.style.left = this.overviewPosition + 'px';
204
+ this.wavesurfer.drawer.wrapper.scrollLeft = this.overviewPosition * this.ratio;
205
+ }
206
+ });
207
+
208
+
209
+ WaveSurfer.initMinimap = function (params) {
210
+ var map = Object.create(WaveSurfer.Minimap);
211
+ map.init(this, params);
212
+ return map;
213
+ };
@@ -0,0 +1,416 @@
1
+ 'use strict';
2
+
3
+ /* Regions manager */
4
+ WaveSurfer.Regions = {
5
+ init: function (wavesurfer) {
6
+ this.wavesurfer = wavesurfer;
7
+ this.wrapper = this.wavesurfer.drawer.wrapper;
8
+
9
+ /* Id-based hash of regions. */
10
+ this.list = {};
11
+ },
12
+
13
+ /* Remove a region. */
14
+ add: function (params) {
15
+ var region = Object.create(WaveSurfer.Region);
16
+ region.init(params, this.wavesurfer);
17
+
18
+ this.list[region.id] = region;
19
+
20
+ region.on('remove', (function () {
21
+ delete this.list[region.id];
22
+ }).bind(this));
23
+
24
+ return region;
25
+ },
26
+
27
+ /* Remove all regions. */
28
+ clear: function () {
29
+ Object.keys(this.list).forEach(function (id) {
30
+ this.list[id].remove();
31
+ }, this);
32
+ },
33
+
34
+ enableDragSelection: function (params) {
35
+ var my = this;
36
+ var drag;
37
+ var start;
38
+ var region;
39
+
40
+ this.wrapper.addEventListener('mousedown', function (e) {
41
+ drag = true;
42
+ start = my.wavesurfer.drawer.handleEvent(e);
43
+ region = null;
44
+ });
45
+ this.wrapper.addEventListener('mouseup', function (e) {
46
+ drag = false;
47
+
48
+ if (region) {
49
+ region.fireEvent('update-end', e);
50
+ my.wavesurfer.fireEvent('region-update-end', region, e);
51
+ }
52
+
53
+ region = null;
54
+ });
55
+ this.wrapper.addEventListener('mousemove', function (e) {
56
+ if (!drag) { return; }
57
+
58
+ if (!region) {
59
+ region = my.add(params || {});
60
+ }
61
+
62
+ var duration = my.wavesurfer.getDuration();
63
+ var end = my.wavesurfer.drawer.handleEvent(e);
64
+ region.update({
65
+ start: Math.min(end * duration, start * duration),
66
+ end: Math.max(end * duration, start * duration)
67
+ });
68
+ });
69
+ }
70
+ };
71
+
72
+ WaveSurfer.Region = {
73
+ /* Helper function to assign CSS styles. */
74
+ style: WaveSurfer.Drawer.style,
75
+
76
+ init: function (params, wavesurfer) {
77
+ this.wavesurfer = wavesurfer;
78
+ this.wrapper = wavesurfer.drawer.wrapper;
79
+
80
+ this.id = params.id == null ? WaveSurfer.util.getId() : params.id;
81
+ this.start = Number(params.start) || 0;
82
+ this.end = params.end == null ?
83
+ // small marker-like region
84
+ this.start + (4 / this.wrapper.scrollWidth) * this.wavesurfer.getDuration() :
85
+ Number(params.end);
86
+ this.resize = params.resize === undefined ? true : Boolean(params.resize);
87
+ this.drag = params.drag === undefined ? true : Boolean(params.drag);
88
+ this.loop = Boolean(params.loop);
89
+ this.color = params.color || 'rgba(0, 0, 0, 0.1)';
90
+ this.data = params.data || {};
91
+
92
+ this.maxLength = params.maxLength;
93
+ this.minLength = params.minLength;
94
+
95
+ this.bindInOut();
96
+ this.render();
97
+
98
+ this.wavesurfer.fireEvent('region-created', this);
99
+ },
100
+
101
+ /* Update region params. */
102
+ update: function (params) {
103
+ if (null != params.start) {
104
+ this.start = Number(params.start);
105
+ }
106
+ if (null != params.end) {
107
+ this.end = Number(params.end);
108
+ }
109
+ if (null != params.loop) {
110
+ this.loop = Boolean(params.loop);
111
+ }
112
+ if (null != params.color) {
113
+ this.color = params.color;
114
+ }
115
+ if (null != params.data) {
116
+ this.data = params.data;
117
+ }
118
+ if (null != params.resize) {
119
+ this.resize = Boolean(params.resize);
120
+ }
121
+ if (null != params.drag) {
122
+ this.drag = Boolean(params.drag);
123
+ }
124
+ if (null != params.maxLength) {
125
+ this.maxLength = Number(params.maxLength);
126
+ }
127
+ if (null != params.minLength) {
128
+ this.minLength = Number(params.minLength);
129
+ }
130
+
131
+ this.updateRender();
132
+ this.fireEvent('update');
133
+ this.wavesurfer.fireEvent('region-updated', this);
134
+ },
135
+
136
+ /* Remove a single region. */
137
+ remove: function (region) {
138
+ if (this.element) {
139
+ this.wrapper.removeChild(this.element);
140
+ this.element = null;
141
+ this.fireEvent('remove');
142
+ this.wavesurfer.fireEvent('region-removed', this);
143
+ }
144
+ },
145
+
146
+ /* Play the audio region. */
147
+ play: function () {
148
+ this.wavesurfer.play(this.start, this.end);
149
+ this.fireEvent('play');
150
+ this.wavesurfer.fireEvent('region-play', this);
151
+ },
152
+
153
+ /* Play the region in loop. */
154
+ playLoop: function () {
155
+ this.play();
156
+ this.once('out', this.playLoop.bind(this));
157
+ },
158
+
159
+ /* Render a region as a DOM element. */
160
+ render: function () {
161
+ var regionEl = document.createElement('region');
162
+ regionEl.className = 'wavesurfer-region';
163
+ regionEl.title = this.formatTime(this.start, this.end);
164
+ regionEl.setAttribute('data-id', this.id);
165
+
166
+ var width = this.wrapper.scrollWidth;
167
+ this.style(regionEl, {
168
+ position: 'absolute',
169
+ zIndex: 2,
170
+ height: '100%',
171
+ top: '0px'
172
+ });
173
+
174
+ /* Resize handles */
175
+ if (this.resize) {
176
+ var handleLeft = regionEl.appendChild(document.createElement('handle'));
177
+ var handleRight = regionEl.appendChild(document.createElement('handle'));
178
+ handleLeft.className = 'wavesurfer-handle wavesurfer-handle-start';
179
+ handleRight.className = 'wavesurfer-handle wavesurfer-handle-end';
180
+ var css = {
181
+ cursor: 'col-resize',
182
+ position: 'absolute',
183
+ left: '0px',
184
+ top: '0px',
185
+ width: '1%',
186
+ maxWidth: '4px',
187
+ height: '100%'
188
+ };
189
+ this.style(handleLeft, css);
190
+ this.style(handleRight, css);
191
+ this.style(handleRight, {
192
+ left: '100%'
193
+ });
194
+ }
195
+
196
+ this.element = this.wrapper.appendChild(regionEl);
197
+ this.updateRender();
198
+ this.bindEvents(regionEl);
199
+ },
200
+
201
+ formatTime: function (start, end) {
202
+ return (start == end ? [ start ] : [ start, end ]).map(function (time) {
203
+ return [
204
+ Math.floor((time % 3600) / 60), // minutes
205
+ ('00' + Math.floor(time % 60)).slice(-2) // seconds
206
+ ].join(':');
207
+ }).join('–');
208
+ },
209
+
210
+ /* Update element's position, width, color. */
211
+ updateRender: function () {
212
+ var dur = this.wavesurfer.getDuration();
213
+ var width = this.wrapper.scrollWidth;
214
+
215
+ if (this.start < 0) {
216
+ this.start = 0;
217
+ this.end = this.end - this.start;
218
+ }
219
+ if (this.end > dur) {
220
+ this.end = dur;
221
+ this.start = dur - (this.end - this.start);
222
+ }
223
+
224
+ if (this.minLength != null) {
225
+ this.end = Math.max(this.start + this.minLength, this.end);
226
+ }
227
+
228
+ if (this.maxLength != null) {
229
+ this.end = Math.min(this.start + this.maxLength, this.end);
230
+ }
231
+
232
+ this.style(this.element, {
233
+ left: ~~(this.start / dur * width) + 'px',
234
+ width: ~~((this.end - this.start) / dur * width) + 'px',
235
+ backgroundColor: this.color,
236
+ cursor: this.drag ? 'move' : 'default'
237
+ });
238
+ this.element.title = this.formatTime(this.start, this.end);
239
+ },
240
+
241
+ /* Bind audio events. */
242
+ bindInOut: function () {
243
+ var my = this;
244
+
245
+ var onPlay = function () {
246
+ my.firedIn = false;
247
+ my.firedOut = false;
248
+ };
249
+
250
+ var onProcess = function (time) {
251
+ if (!my.firedIn && my.start <= time && my.end > time) {
252
+ my.firedIn = true;
253
+ my.fireEvent('in');
254
+ my.wavesurfer.fireEvent('region-in', my);
255
+ }
256
+ if (!my.firedOut && my.firedIn && my.end <= Math.round(time * 100) / 100) {
257
+ my.firedOut = true;
258
+ my.fireEvent('out');
259
+ my.wavesurfer.fireEvent('region-out', my);
260
+ }
261
+ };
262
+
263
+ this.wavesurfer.on('play', onPlay);
264
+ this.wavesurfer.backend.on('audioprocess', onProcess);
265
+
266
+ this.on('remove', function () {
267
+ my.wavesurfer.un('play', onPlay);
268
+ my.wavesurfer.backend.un('audioprocess', onProcess);
269
+ });
270
+
271
+ /* Loop playback. */
272
+ this.on('out', function () {
273
+ if (my.loop) {
274
+ my.wavesurfer.play(my.start);
275
+ }
276
+ });
277
+ },
278
+
279
+ /* Bind DOM events. */
280
+ bindEvents: function () {
281
+ var my = this;
282
+
283
+ this.element.addEventListener('mouseenter', function (e) {
284
+ my.fireEvent('mouseenter', e);
285
+ my.wavesurfer.fireEvent('region-mouseenter', my, e);
286
+ });
287
+
288
+ this.element.addEventListener('mouseleave', function (e) {
289
+ my.fireEvent('mouseleave', e);
290
+ my.wavesurfer.fireEvent('region-mouseleave', my, e);
291
+ });
292
+
293
+ this.element.addEventListener('click', function (e) {
294
+ e.preventDefault();
295
+ my.fireEvent('click', e);
296
+ my.wavesurfer.fireEvent('region-click', my, e);
297
+ });
298
+
299
+ this.element.addEventListener('dblclick', function (e) {
300
+ e.stopPropagation();
301
+ e.preventDefault();
302
+ my.fireEvent('dblclick', e);
303
+ my.wavesurfer.fireEvent('region-dblclick', my, e);
304
+ });
305
+
306
+ /* Drag or resize on mousemove. */
307
+ (this.drag || this.resize) && (function () {
308
+ var duration = my.wavesurfer.getDuration();
309
+ var drag;
310
+ var resize;
311
+ var startTime;
312
+
313
+ var onDown = function (e) {
314
+ e.stopPropagation();
315
+ startTime = my.wavesurfer.drawer.handleEvent(e) * duration;
316
+
317
+ if (e.target.tagName.toLowerCase() == 'handle') {
318
+ if (e.target.classList.contains('wavesurfer-handle-start')) {
319
+ resize = 'start';
320
+ } else {
321
+ resize = 'end';
322
+ }
323
+ } else {
324
+ drag = true;
325
+ }
326
+ };
327
+ var onUp = function (e) {
328
+ if (drag || resize) {
329
+ drag = false;
330
+ resize = false;
331
+ e.stopPropagation();
332
+ e.preventDefault();
333
+
334
+ my.fireEvent('update-end', e);
335
+ my.wavesurfer.fireEvent('region-update-end', my, e);
336
+ }
337
+ };
338
+ var onMove = function (e) {
339
+ if (drag || resize) {
340
+ var time = my.wavesurfer.drawer.handleEvent(e) * duration;
341
+ var delta = time - startTime;
342
+ startTime = time;
343
+
344
+ // Drag
345
+ if (my.drag && drag) {
346
+ my.onDrag(delta);
347
+ }
348
+
349
+ // Resize
350
+ if (my.resize && resize) {
351
+ my.onResize(delta, resize);
352
+ }
353
+ }
354
+ };
355
+
356
+ my.element.addEventListener('mousedown', onDown);
357
+ my.wrapper.addEventListener('mousemove', onMove);
358
+ document.body.addEventListener('mouseup', onUp);
359
+
360
+ my.on('remove', function () {
361
+ document.body.removeEventListener('mouseup', onUp);
362
+ my.wrapper.removeEventListener('mousemove', onMove);
363
+ });
364
+
365
+ my.wavesurfer.on('destroy', function () {
366
+ document.body.removeEventListener('mouseup', onUp);
367
+ });
368
+ }());
369
+ },
370
+
371
+ onDrag: function (delta) {
372
+ this.update({
373
+ start: this.start + delta,
374
+ end: this.end + delta
375
+ });
376
+ },
377
+
378
+ onResize: function (delta, direction) {
379
+ if (direction == 'start') {
380
+ this.update({
381
+ start: Math.min(this.start + delta, this.end),
382
+ end: Math.max(this.start + delta, this.end)
383
+ });
384
+ } else {
385
+ this.update({
386
+ start: Math.min(this.end + delta, this.start),
387
+ end: Math.max(this.end + delta, this.start)
388
+ });
389
+ }
390
+ }
391
+ };
392
+
393
+ WaveSurfer.util.extend(WaveSurfer.Region, WaveSurfer.Observer);
394
+
395
+
396
+ /* Augment WaveSurfer with region methods. */
397
+ WaveSurfer.initRegions = function () {
398
+ if (!this.regions) {
399
+ this.regions = Object.create(WaveSurfer.Regions);
400
+ this.regions.init(this);
401
+ }
402
+ };
403
+
404
+ WaveSurfer.addRegion = function (options) {
405
+ this.initRegions();
406
+ return this.regions.add(options);
407
+ };
408
+
409
+ WaveSurfer.clearRegions = function () {
410
+ this.regions && this.regions.clear();
411
+ };
412
+
413
+ WaveSurfer.enableDragSelection = function (options) {
414
+ this.initRegions();
415
+ this.regions.enableDragSelection(options);
416
+ };