@checksub_team/peaks_timeline
Version:
JavaScript UI component for displaying audio waveforms
1,591 lines (1,319 loc) • 44.8 kB
JavaScript
/**
* @file
*
* Defines the {@link SourceGroup} class.
*
* @module source-group
*/
define([
'./waveform-builder',
'./waveform-shape',
'./utils',
'./loader',
'konva'
], function(
WaveformBuilder,
WaveformShape,
Utils,
Loader,
Konva) {
'use strict';
var SPACING_BETWEEN_PREVIEW_AND_BORDER_RATIO = 0.15;
var SPACING_BETWEEN_PREVIEWS = 1.5;
var CORNER_RADIUS = 8;
var INDICATOR_RADIUS = 4; // px
/**
* Creates a source group for the given source.
*
* @class
* @alias SourceGroup
*
* @param {Source} source
* @param {Peaks} peaks
* @param {SourcesLayer} layer
* @param {WaveformOverview|WaveformZoomView} view
*/
function SourceGroup(source, peaks, layer, view) {
this._source = source;
this._peaks = peaks;
this._layer = layer;
this._view = view;
this._indicators = {};
var self = this;
this._x = this._view.timeToPixels(source.startTime);
this._width = this._view.timeToPixels(source.endTime - source.startTime);
this._unwrappedHeight = source.binaryHeight && source.previewHeight ?
source.binaryHeight + source.previewHeight :
this._peaks.options.lineHeight;
this._wrappedHeight = this._peaks.options.wrappedLineHeight;
this._borderWidth = this._source.borderWidth || 0;
this._height = this._unwrappedHeight;
this._currentTimeToPixelsScaleUsed = this._view.getTimeToPixelsScale();
this._selected = this._source.selected;
this._previewList = [];
this._markersGroup = this._createMarkers();
this._group = new Konva.Group({
x: this._x,
sourceId: this._source.id,
draggable: this._source.draggable,
dragBoundFunc: function() {
return self._layer.onSourcesGroupDrag(this);
},
clipFunc: function(ctx) {
self.drawSourceShape(ctx, null);
}
});
this._group.on('dragstart', this._layer.onSourcesGroupDragStart.bind(this._layer));
this._group.on('dragend', this._layer.onSourcesGroupDragEnd.bind(this._layer));
this._cursor = null;
this._group.on('mouseenter', function() {
self._view.setHoveredElement(self);
if (!self._source.loading) {
self._showButtons();
}
});
this._group.on('mouseleave', function() {
self._view.setHoveredElement(null);
self._hideButtons();
});
this._group.on('mouseover', function() {
if (self._source.draggable) {
self._view.setCursor(self._cursor || 'pointer');
}
if (self._view.getCurrentMode() === 'cut') {
self.toggleDragging(false);
self.toggleResizing(false);
}
});
this._group.on('mouseout', function() {
self._view.setCursor('default');
if (self._view.getCurrentMode() === 'cut') {
self.toggleDragging(true);
self.toggleResizing(true);
}
});
this._group.add(new Konva.Group());
if (this._borderWidth) {
this._border = new Konva.Shape({
fill: this._source.borderColor,
sceneFunc: function(ctx, shape) {
self.drawSourceShape(ctx, shape);
}
});
this._group.getChildren()[0].add(this._border);
}
this._addHandles();
this.setWrapping(source.wrapped);
this.setSelected();
this._indicatorsGroup = new Konva.Group();
this.addToContent(this._indicatorsGroup);
this.createIndicators();
this.setLoadingState(this._source.loading);
}
SourceGroup.prototype.addToContent = function(newChild) {
if (this._source.wrapped) {
this._wrap.add(newChild);
}
else {
this._unwrap.add(newChild);
}
};
SourceGroup.prototype.prepareDragEnd = function() {
var handleWidth = Math.min(this._peaks.options.sourceHandleWidth, this._width / 2);
this._leftHandle.width(handleWidth);
this._rightHandle.width(handleWidth);
this._rightHandle.x(this._width - handleWidth);
};
SourceGroup.prototype._onSourceGroupHandleDrag = function(draggedElement, dragPos, leftHandle) {
this._view.updateWithAutoScroll(
function() {
if (this._layer.updateSource(
this._source,
leftHandle ? dragPos.x + this._view.getFrameOffset() : null,
leftHandle ? null : dragPos.x + draggedElement.width() + this._view.getFrameOffset()
)) {
this._layer.draw();
}
}.bind(this)
);
this._view.setTimelineLength(
this._view.timeToPixels(this._source.endTime) + this._view.getWidth()
);
return {
x: draggedElement.absolutePosition().x,
y: draggedElement.absolutePosition().y
};
};
SourceGroup.prototype.update = function() {
const startPixel = this._view.timeToPixels(this._source.startTime);
const endPixel = this._view.timeToPixels(this._source.endTime);
const frameOffset = this._view.timeToPixels(this._view.getTimeOffset());
const newTimeToPixelsScale = this._view.getTimeToPixelsScale();
this._group.x(startPixel - frameOffset);
this._x = startPixel;
const newWidth = endPixel - startPixel;
if (newWidth !== this._width) {
this._width = newWidth;
// the zoom was changed
if (newTimeToPixelsScale !== this._currentTimeToPixelsScaleUsed) {
this._currentTimeToPixelsScaleUsed = newTimeToPixelsScale;
this._updateMarkers();
this._rightHandle.x(this._width - this._rightHandle.width());
}
else {
// the zoom was not changed, but the source was resized
const newTitle = Utils.removeLineBreaks(this._source.getVisibleTitle());
if (this._wrappedTitle && this._wrappedTitle.text() !== newTitle) {
this._wrap.add(this._createTitle(true));
}
if (this._unwrappedTitle && this._unwrappedTitle.text() !== newTitle) {
this._unwrap.add(this._createTitle(false));
}
}
this._updateVolumeSlider();
this._updateButtons();
this._updateLoadingOverlay();
// update unwrap
this.updatePreviews();
}
};
SourceGroup.prototype.setWrapping = function(wrap, forceCreate, notify) {
if (wrap) {
this._removeUnwrap();
this._height = this._wrappedHeight;
this._addWrap(forceCreate);
}
else {
this._removeWrap();
this._height = this._unwrappedHeight;
this._addUnwrap(forceCreate);
}
this.setHandlesWrapping(wrap);
if (notify) {
this._peaks.emit('source.wrappingChanged', this);
}
};
SourceGroup.prototype.setHandlesWrapping = function(wrap) {
if (wrap) {
this._leftHandle.height(this._wrappedHeight);
this._rightHandle.height(this._wrappedHeight);
}
else {
this._leftHandle.height(this._unwrappedHeight);
this._rightHandle.height(this._unwrappedHeight);
}
};
SourceGroup.prototype._addHandles = function(forceCreate) {
var self = this;
var handleWidth = Math.min(this._peaks.options.sourceHandleWidth, this._width / 2);
if (!this._leftHandle || forceCreate) {
this._leftHandle = new Konva.Rect({
x: 0,
width: handleWidth,
height: this._unwrappedHeight,
visible: true,
draggable: this._source.resizable,
dragBoundFunc: function(pos) {
return self._onSourceGroupHandleDrag(this, pos, true);
}
});
this._leftHandle.on('dragstart', function() {
self._hideButtons();
});
this._leftHandle.on('dragend', function() {
self._showButtons();
});
if (this._source.resizable) {
this._leftHandle.on('mouseover', function() {
self._cursor = 'ew-resize';
});
this._leftHandle.on('mouseout', function() {
self._cursor = null;
});
}
}
if (!this._rightHandle || forceCreate) {
this._rightHandle = new Konva.Rect({
x: this._width - handleWidth,
width: handleWidth,
height: this._unwrappedHeight,
visible: true,
draggable: this._source.resizable,
dragBoundFunc: function(pos) {
return self._onSourceGroupHandleDrag(this, pos, false);
}
});
this._rightHandle.on('dragstart', function() {
self._hideButtons();
});
this._rightHandle.on('dragend', function() {
self._showButtons();
});
if (this._source.resizable) {
this._rightHandle.on('mouseover', function() {
self._cursor = 'ew-resize';
});
this._rightHandle.on('mouseout', function() {
self._cursor = null;
});
}
}
this._group.add(this._leftHandle);
this._group.add(this._rightHandle);
};
SourceGroup.prototype.toggleDragging = function(bool) {
var background;
if (this._wrap) {
background = this._wrap.getChildren(function(node) {
return node.getClassName() === 'Shape';
})[0];
if (background) {
background.draggable(bool);
}
}
if (this._unwrap) {
background = this._unwrap.getChildren(function(node) {
return node.getClassName() === 'Shape';
})[0];
if (background) {
background.draggable(bool);
}
}
};
SourceGroup.prototype.toggleResizing = function(bool) {
if (this._leftHandle) {
this._leftHandle.draggable(bool);
}
if (this._rightHandle) {
this._rightHandle.draggable(bool);
}
};
SourceGroup.prototype.drawSourceShape = function(ctx, shape, addBorderWidth, fill) {
var offset = addBorderWidth ? this._borderWidth : 0;
var radius = this._source.borderRadius !== undefined && this._source.borderRadius !== null ?
this._source.borderRadius :
Math.max(
1,
Math.min(
this._width / 2,
Math.min(
CORNER_RADIUS,
this._height / 2
)
)
);
var x = Math.max(
0,
this._view.getFrameOffset() - this._x - radius
);
var width = Math.min(
this._width - x,
this._view.getWidth() + radius - Math.max(
0,
this._x - this._view.getFrameOffset()
)
);
var xWidth = x + width;
ctx.beginPath();
ctx.moveTo(x + radius, offset);
ctx.lineTo(xWidth - radius, offset);
ctx.quadraticCurveTo(xWidth - offset, offset, xWidth - offset, radius);
ctx.lineTo(xWidth - offset, this._height - radius);
ctx.quadraticCurveTo(
xWidth - offset,
this._height - offset,
xWidth - radius,
this._height - offset
);
ctx.lineTo(x + radius, this._height - offset);
ctx.quadraticCurveTo(x + offset, this._height - offset, x + offset, this._height - radius);
ctx.lineTo(x + offset, radius);
ctx.quadraticCurveTo(x + offset, offset, x + radius, offset);
ctx.closePath();
if (fill) {
if (this._source.shouldShowWarning()) {
var gradient = ctx.createLinearGradient(0, 0, this._width, 0);
if (this._source.mediaEndTime < this._source.duration) {
var rightStopPosition = Math.max(1 - (this._source.warningWidth / this._width), 0.5);
gradient.addColorStop(rightStopPosition, this._source.backgroundColor);
gradient.addColorStop(1, this._source.warningColor);
}
if (this._source.mediaStartTime > 0) {
var leftStopPosition = Math.min(this._source.warningWidth / this._width, 0.5);
gradient.addColorStop(0, this._source.warningColor);
gradient.addColorStop(leftStopPosition, this._source.backgroundColor);
}
ctx.fillStyle = gradient;
ctx.fill();
}
else {
ctx.fillStyle = this._source.backgroundColor;
ctx.fill();
}
}
if (shape) {
ctx.fillStrokeShape(shape);
}
};
SourceGroup.prototype._addUnwrap = function(forceCreate) {
if (!this._unwrap || forceCreate) {
this._unwrap = this._createUnwrap();
}
this._group.getChildren()[0].add(this._unwrap);
};
SourceGroup.prototype._createUnwrap = function() {
var self = this;
var unwrap = new Konva.Group({
width: this._width,
height: this._unwrappedHeight,
clipFunc: function(ctx) {
self.drawSourceShape(ctx, null, true);
}
});
var background = new Konva.Group();
background.add(new Konva.Shape({
sceneFunc: function(ctx, shape) {
self.drawSourceShape(ctx, shape, true, true);
}
}));
unwrap.add(background);
unwrap.add(this._markersGroup);
if (this._source.volumeRange && this._source.volume !== undefined) {
unwrap.add(this._getVolumeSlider());
}
if (this._source.buttons.length > 0) {
unwrap.add(this._getButtons());
}
unwrap.add(this._createTitle(false));
return unwrap;
};
SourceGroup.prototype._addToUnwrap = function(element, inForeground) {
if (inForeground) {
this._unwrap.add(element);
}
else {
this._unwrap.getChildren()[0].add(element);
}
};
SourceGroup.prototype._removeUnwrap = function() {
if (this._unwrap) {
this._unwrap.remove();
}
};
SourceGroup.prototype._addWrap = function(forceCreate) {
if (!this._wrap || forceCreate) {
this._wrap = this._createWrap();
}
this._group.getChildren()[0].add(this._wrap);
};
SourceGroup.prototype._createWrap = function() {
var self = this;
var wrap = new Konva.Group({
width: this._width,
height: this._wrappedHeight,
clipFunc: function(ctx) {
self.drawSourceShape(ctx, null, true);
}
});
var background = new Konva.Group();
background.add(new Konva.Shape({
sceneFunc: function(ctx, shape) {
self.drawSourceShape(ctx, shape, true, true);
}
}));
wrap.add(background);
wrap.add(this._markersGroup);
if (this._source.volumeRange && this._source.volume !== undefined) {
wrap.add(this._getVolumeSlider());
}
if (this._source.buttons.length > 0) {
wrap.add(this._getButtons());
}
wrap.add(this._createTitle(true));
return wrap;
};
SourceGroup.prototype._addToWrap = function(element, inForeground) {
if (inForeground) {
this._wrap.add(element);
}
else {
this._wrap.getChildren()[0].add(element);
}
};
SourceGroup.prototype._removeWrap = function() {
if (this._wrap) {
this._wrap.remove();
}
};
SourceGroup.prototype._createTitle = function(isWrap) {
var self = this;
var defaultWidth;
var y = (this._source.textPosition === 'bottom') ?
Math.max(
(isWrap ? this._wrappedHeight : this._unwrappedHeight)
- this._source.textFontSize - this._peaks.options.sourceTextYOffset,
this._peaks.options.sourceTextYOffset
) : this._peaks.options.sourceTextYOffset;
var defaultXOffset = this._peaks.options.sourceTextXOffset;
var maxXOffset = this._width - 2 * defaultXOffset;
if (isWrap) {
if (this._wrappedTitle) {
this._wrappedTitle.destroy();
this._wrappedTitle = null;
}
}
else {
if (this._unwrappedTitle) {
this._unwrappedTitle.destroy();
this._unwrappedTitle = null;
}
}
var title = new Konva.Text({
x: defaultXOffset,
y: y,
text: Utils.removeLineBreaks(this._source.getVisibleTitle()),
textAlign: 'left',
verticalAlign: 'middle',
fontSize: this._source.textFontSize,
fontFamily: this._source.textFont,
fill: this._source.textColor,
wrap: 'none',
ellipsis: true,
listening: false,
sceneFunc: function(context, shape) {
var absX = this.absolutePosition().x;
if (self._source.textAutoScroll && absX < defaultXOffset) {
this.offsetX(Math.max(Math.min(0, absX - defaultXOffset), -(maxXOffset - shape.width())));
}
defaultWidth = defaultWidth ? defaultWidth : shape.width();
shape.width(Math.min(self._width - 10, defaultWidth));
if (self._source.textBackgroundColor) {
context.fillStyle = self._source.textBackgroundColor;
context.fillRect(-5, -5, shape.width() + 10, shape.height() ? shape.height() + 10 : 0);
}
shape._sceneFunc(context);
}
});
if (isWrap) {
this._wrappedTitle = title;
}
else {
this._unwrappedTitle = title;
}
return title;
};
SourceGroup.prototype.getWidth = function() {
return this._width;
};
SourceGroup.prototype.getX = function() {
return this._x;
};
SourceGroup.prototype.getY = function() {
return this._group.absolutePosition().y;
};
SourceGroup.prototype.x = function(value) {
if (value) {
return this._group.x(value);
}
else {
return this._group.x();
}
};
SourceGroup.prototype.y = function(value) {
if (value) {
return this._group.y(value);
}
else {
return this._group.y();
}
};
SourceGroup.prototype.getSource = function() {
return this._source;
};
SourceGroup.prototype.startDrag = function() {
return this._group.startDrag();
};
SourceGroup.prototype.stopDrag = function() {
return this._group.stopDrag();
};
SourceGroup.prototype.addToGroup = function(group) {
group.add(this._group);
};
SourceGroup.prototype.isDescendantOf = function(group) {
group.isAncestorOf(this._group);
};
SourceGroup.prototype.moveTo = function(group) {
this._group.moveTo(group);
};
SourceGroup.prototype.getParent = function() {
return this._group.getParent();
};
SourceGroup.prototype.remove = function() {
this._group.remove();
};
SourceGroup.prototype.addImagePreview = function(content, url, redraw) {
var preview = {
type: 'image',
group: new Konva.Group({
height: this._unwrappedHeight,
listening: false
})
};
var imageData = this._layer.getLoadedData(url);
if (!imageData) {
imageData = new Image();
var self = this;
imageData.onload = function() {
self._layer.setLoadedData(url, this);
preview.loaded = true;
self._createImagePreview(preview, imageData, redraw);
};
imageData.src = content;
}
else {
preview.loaded = true;
this._createImagePreview(preview, imageData, redraw);
}
};
SourceGroup.prototype.addVideoPreview = function(content, url, redraw) {
var preview = {
type: 'video',
group: new Konva.Group({
y: this._source.binaryUrl && this._source.previewUrl ? this._source.binaryHeight : 0,
height: this._source.binaryUrl && this._source.previewUrl ?
this._source.previewHeight : this._unwrappedHeight,
listening: false
})
};
var imageData = this._layer.getLoadedData(url);
if (!imageData) {
var video = document.createElement('video');
var self = this;
video.onloadeddata = function() {
this.currentTime = this.duration / 2;
var canvas = document.createElement('canvas');
canvas.width = this.videoWidth;
canvas.height = this.videoHeight;
canvas.getContext('2d').drawImage(this, 0, 0, canvas.width, canvas.height);
imageData = new Image();
imageData.onload = function() {
self._layer.setLoadedData(url, this);
preview.loaded = true;
self._createImagePreview(preview, imageData, redraw);
};
imageData.src = canvas.toDataURL();
};
video.src = content;
}
else {
preview.loaded = true;
this._createImagePreview(preview, imageData, redraw);
}
};
SourceGroup.prototype.addAudioPreview = function(type, content, url, redraw) {
var preview = {
type: 'audio',
group: new Konva.Group({
height: this._source.binaryUrl && this._source.previewUrl ?
this._source.binaryHeight : this._unwrappedHeight,
listening: false
}),
url: url
};
var self = this;
var audioData = this._layer.getLoadedData(url);
if (!audioData) {
var waveformBuilder = new WaveformBuilder(this._peaks);
var options = Object.assign({},this._peaks.options);
if (type === 'audio') {
options.objectUrl = content;
}
else {
options.dataUri = content;
}
waveformBuilder.init(options, function(err, originalWaveformData) {
if (err) {
throw err;
}
originalWaveformData.hasAudio = self._hasAudio(originalWaveformData);
if (originalWaveformData.hasAudio) {
var newScale = originalWaveformData.sample_rate / self._view.getTimeToPixelsMaxZoom();
if (newScale > originalWaveformData.scale) {
self._minScale = newScale;
}
else {
self._minScale = originalWaveformData.scale;
}
self._view.setTimeToPixelsMaxZoom(originalWaveformData.sample_rate / self._minScale);
}
self._layer.setLoadedData(url, originalWaveformData);
self._layer.setLoadedData(
url + '-scaled',
{ data: originalWaveformData, scale: originalWaveformData.sample_rate / self._minScale }
);
preview.loaded = true;
self._createAudioPreview(preview, originalWaveformData, redraw);
});
}
else {
preview.loaded = true;
this._createAudioPreview(preview, audioData, redraw);
}
};
SourceGroup.prototype._hasAudio = function(waveformData) {
var channels = waveformData.channels;
var channel, someIsNotZero = false;
for (var i = 0; i < channels; i++) {
channel = waveformData.channel(i);
someIsNotZero = channel.min_array().some(function(item) {
return item !== 0;
});
if (!someIsNotZero) {
someIsNotZero = channel.max_array().some(function(item) {
return item !== 0;
});
}
if (someIsNotZero) {
break;
}
}
return someIsNotZero;
};
SourceGroup.prototype._createAudioPreview = function(preview, waveformData, redraw) {
if (waveformData.hasAudio) {
var waveform = new WaveformShape({
layer: this._layer,
view: this._view,
source: this._source,
height: preview.group.height(),
url: preview.url
});
preview.group.add(waveform);
this._addToUnwrap(preview.group);
if (redraw) {
this._layer.rescale(true);
}
this._previewList.push(preview);
}
};
SourceGroup.prototype.getAudioPreview = function() {
return this._previewList.filter(function(preview) {
return preview.type === 'audio';
});
};
SourceGroup.prototype.setSelected = function() {
this._selected = this._source.selected;
if (this._border) {
if (this._selected) {
this._border.fill(this._source.selectedColor);
this._borderWidth = this._peaks.options.sourceSelectedBorderWidth;
}
else {
this._border.fill(this._source.borderColor);
this._borderWidth = this._source.borderWidth;
}
}
else {
if (this._unwrap) {
// update unwrap
var unwrap_background = this._unwrap.getChildren(function(node) {
return node.getClassName() === 'Shape';
})[0];
if (unwrap_background) {
if (this._selected) {
unwrap_background.stroke(this._source.selectedColor);
unwrap_background.strokeWidth(this._peaks.options.sourceSelectedBorderWidth);
}
else {
unwrap_background.strokeWidth(0);
}
}
}
if (this._wrap) {
// update wrap
var wrap_background = this._wrap.getChildren(function(node) {
return node.getClassName() === 'Shape';
})[0];
if (wrap_background) {
if (this._selected) {
wrap_background.stroke(this._source.selectedColor);
wrap_background.strokeWidth(this._peaks.options.sourceSelectedBorderWidth);
}
else {
wrap_background.strokeWidth(0);
}
}
}
}
};
SourceGroup.prototype.updatePreviews = function() {
var self = this;
this._previewList.forEach(function(preview) {
if (preview.loaded) {
switch (preview.type) {
case 'video':
case 'image':
// image or video preview
if (self._unwrappedHeight !== preview.imageData.referenceHeight) {
preview.imageData.referenceHeight = preview.group.height();
preview.imageData.borderSpacing = SPACING_BETWEEN_PREVIEW_AND_BORDER_RATIO
* preview.imageData.referenceHeight;
preview.imageData.height = preview.imageData.referenceHeight
- (2 * preview.imageData.borderSpacing);
preview.imageData.width = preview.imageData.height
* preview.imageData.dimRatio;
preview.imageData.imageSpacing = preview.imageData.width
* SPACING_BETWEEN_PREVIEWS;
}
var interImageSpacing = preview.imageData.width + preview.imageData.imageSpacing;
var imageNumber;
if (self._width > preview.imageData.borderSpacing) {
imageNumber = Math.trunc(
(self._width - preview.imageData.borderSpacing)
/ interImageSpacing
) + 1;
}
else {
imageNumber = 0;
}
var imageList = preview.group.getChildren();
var i = 0;
for (i = 0; i < imageNumber; i++) {
if (imageList.length > i) {
imageList[i].visible(true);
}
else {
var imagePreview = new Konva.Image({
x: preview.imageData.borderSpacing + i * interImageSpacing,
y: preview.imageData.borderSpacing,
image: preview.imageData.image,
width: preview.imageData.width,
height: preview.imageData.height,
listening: false,
visible: true
});
preview.group.add(imagePreview);
}
}
for (i = imageNumber; i < imageList.length; i++) {
imageList[i].visible(false);
}
}
}
});
};
SourceGroup.prototype._createImagePreview = function(preview, image, redraw) {
preview.imageData = {
image: image,
referenceHeight: null,
dimRatio: null,
borderSpacing: null,
height: null,
width: null,
imageSpacing: null
};
preview.imageData.referenceHeight = preview.group.height();
preview.imageData.dimRatio = image.width / image.height;
preview.imageData.borderSpacing = SPACING_BETWEEN_PREVIEW_AND_BORDER_RATIO
* preview.imageData.referenceHeight;
preview.imageData.height = preview.imageData.referenceHeight
- (2 * preview.imageData.borderSpacing);
preview.imageData.width = preview.imageData.height * preview.imageData.dimRatio;
preview.imageData.imageSpacing = preview.imageData.width * SPACING_BETWEEN_PREVIEWS;
var currentX = preview.imageData.borderSpacing;
while (currentX < this._width) {
var imagePreview = new Konva.Image({
x: currentX,
y: preview.imageData.borderSpacing,
image: image,
width: preview.imageData.width,
height: preview.imageData.height,
listening: false
});
preview.group.add(imagePreview);
currentX += preview.imageData.width + preview.imageData.imageSpacing;
}
this._addToUnwrap(preview.group);
if (redraw) {
this._group.draw();
}
this._previewList.push(preview);
};
SourceGroup.prototype.setLoadingState = function(isLoading) {
if (isLoading && !this._loadingOverlay) {
this._createLoadingOverlay();
}
else if (!isLoading && this._loadingOverlay) {
this._removeLoadingOverlay();
}
if (this._loadingOverlay) {
this._loadingOverlay.visible(isLoading);
}
};
SourceGroup.prototype._createLoadingOverlay = function() {
this._loadingOverlay = new Konva.Group({
x: 0,
y: 0,
width: this._width,
height: this._height,
listening: false
});
// Semi-transparent background
var loadingBackground = new Konva.Rect({
x: 0,
y: 0,
width: this._width,
height: this._height,
fill: 'rgba(0, 0, 0, 0.7)'
});
this._loader = new Loader();
this._loader.x(this._width / 2);
this._loader.y(this._height / 2);
// Add overlay to the main group
this._loadingOverlay.add(loadingBackground);
this._loader.addTo(this._loadingOverlay);
this.addToContent(this._loadingOverlay);
};
SourceGroup.prototype._removeLoadingOverlay = function() {
if (this._loadingOverlay) {
if (this._loader) {
this._loader.destroy();
this._loader = null;
}
this._loadingOverlay.destroy();
this._loadingOverlay = null;
}
};
SourceGroup.prototype._updateLoadingOverlay = function() {
if (this._loadingOverlay) {
var self = this;
this._loadingOverlay.width(self._width);
this._loadingOverlay.height(self._height);
this._loadingOverlay.getChildren().forEach(function(child) {
if (child instanceof Konva.Rect) {
child.width(self._width);
child.height(self._height);
}
else {
child.x(self._width / 2);
}
});
}
};
SourceGroup.prototype.isWrapped = function() {
return this._source.wrapped;
};
SourceGroup.prototype.getCurrentHeight = function() {
return this._height;
};
SourceGroup.prototype.setVisible = function(boolean) {
this._group.visible(boolean);
};
SourceGroup.prototype.setListening = function(boolean) {
this._group.listening(boolean);
};
SourceGroup.prototype.isVisible = function() {
return this._group.visible();
};
SourceGroup.prototype.isCuttable = function() {
return this._source.cuttable;
};
SourceGroup.prototype.isDeletable = function() {
return this._source.deletable;
};
SourceGroup.prototype.getLine = function() {
return this._source.position;
};
SourceGroup.prototype.getAbsoluteBoundingBox = function() {
var stageContainer = this._group.getStage().container();
var containerRect = stageContainer.getBoundingClientRect();
var elementPos = this._group.getAbsolutePosition();
return {
left: containerRect.left + elementPos.x,
top: containerRect.top + elementPos.y,
width: this._width,
height: this._height
};
};
SourceGroup.prototype.getButtonBoundingBox = function(buttonId) {
if (!this._buttonsGroup) {
return null;
}
var buttonIdx = this._source.buttons.findIndex(function(button) {
return button.id === buttonId;
});
if (buttonIdx === -1) {
return null;
}
var button = this._source.buttons[buttonIdx];
var buttonGroup = this._buttonsGroup.getChildren()[buttonIdx];
var buttonPos = buttonGroup.getAbsolutePosition(this._group);
return {
left: buttonPos.x,
top: buttonPos.y,
width: button.width,
height: button.height
};
};
SourceGroup.prototype.createIndicators = function() {
var newIndicatorsColors = this._source.indicators;
var oldIndicators = this._indicators;
var newIndicators = {};
if (newIndicatorsColors) {
newIndicatorsColors.forEach(function(indicatorColor) {
var oldIndicator = oldIndicators[indicatorColor];
if (oldIndicator) {
newIndicators[indicatorColor] = oldIndicator;
delete oldIndicators[indicatorColor];
}
else {
newIndicators[indicatorColor] = null;
}
});
for (var color in oldIndicators) {
if (Utils.objectHasProperty(oldIndicators, color)) {
oldIndicators[color].destroy();
}
}
}
this._indicators = Object.keys(newIndicators)
.sort()
.reverse()
.reduce(function(objEntries, key) {
objEntries[key] = newIndicators[key];
return objEntries;
}, {}
);
this._createIndicators();
};
SourceGroup.prototype._createIndicators = function() {
var currentX = 0;
var zIndex = 0;
for (var color in this._indicators) {
if (Utils.objectHasProperty(this._indicators, color)) {
if (!this._indicators[color]) {
this._indicators[color] = new Konva.Circle({
radius: INDICATOR_RADIUS,
fill: color,
strokeEnabled: false
});
this._indicatorsGroup.add(this._indicators[color]);
}
this._indicators[color].x(currentX);
this._indicators[color].zIndex(zIndex);
currentX -= INDICATOR_RADIUS;
zIndex += 1;
}
}
this._indicatorsGroup.offsetX(currentX - this._peaks.options.sourceIndicatorsXOffset);
this._indicatorsGroup.offsetY(-this._peaks.options.sourceIndicatorsYOffset);
};
SourceGroup.prototype._createMarkers = function() {
const markersGroup = new Konva.Group({
listening: false
});
this._source.markers.forEach(function(marker) {
const markerX = this._view.timeToPixels(marker - this._source.mediaStartTime);
var markerLine = new Konva.Line({
points: [markerX, 0, markerX, this._unwrappedHeight],
stroke: this._source.markerColor,
strokeWidth: this._source.markerWidth
});
markersGroup.add(markerLine);
}.bind(this));
return markersGroup;
};
SourceGroup.prototype._createButtons = function() {
var buttonsGroup = new Konva.Group({
listening: true,
x: this._width,
visible: false,
opacity: 0
});
var buttonsGroupWidth = 0;
var buttonsGap = this._peaks.options.sourceButtonsGap;
var self = this;
this._source.buttons.forEach(function(button) {
const {
id,
width,
height,
cornerRadius,
color,
hoverColor,
borderColor,
borderWidth,
svg,
image
} = button;
if (buttonsGroupWidth > 0) {
buttonsGroupWidth += buttonsGap;
}
var buttonGroup = new Konva.Group({
x: buttonsGroupWidth,
y: 0,
listening: true
});
var buttonRect = new Konva.Rect({
width: width,
height: height,
fill: color,
stroke: borderColor,
strokeWidth: borderWidth,
cornerRadius: cornerRadius
});
buttonsGroupWidth += width;
buttonGroup.add(buttonRect);
if (svg) {
var svgIcon = new Konva.Path({
x: width / 2,
y: height / 2,
data: svg.path,
fill: svg.color,
offsetX: svg.width / 2,
offsetY: svg.height / 2,
listening: false
});
buttonGroup.add(svgIcon);
}
else if (image) {
var imageObj = new Image();
imageObj.onload = function() {
var imageIcon = new Konva.Image({
x: width / 2,
y: height / 2,
image: imageObj,
offsetX: image.width / 2,
offsetY: image.height / 2,
listening: false
});
buttonGroup.add(imageIcon);
};
imageObj.src = image.data;
}
buttonGroup.on('mouseover', function() {
self._view.setClickable(false);
if (hoverColor) {
buttonRect.fill(hoverColor);
}
self._peaks.emit('source.buttonEnter', self._source, id);
});
buttonGroup.on('mouseout', function() {
self._view.setClickable(true);
if (buttonRect.fill() !== color) {
buttonRect.fill(color);
}
self._peaks.emit('source.buttonLeave', self._source, id);
});
buttonGroup.on('click', function() {
self._peaks.emit('source.buttonClicked', self._source, id);
});
buttonsGroup.add(buttonGroup);
});
buttonsGroup.offsetX(
buttonsGroupWidth
+ this._borderWidth
+ this._peaks.options.sourceButtonsPadding
+ this._peaks.options.sourceButtonsXOffset
);
buttonsGroup.offsetY(
-this._borderWidth
- this._peaks.options.sourceButtonsPadding
- this._peaks.options.sourceButtonsYOffset
);
return buttonsGroup;
};
SourceGroup.prototype._updateMarkers = function() {
const self = this;
if (this._markersGroup) {
this._markersGroup.getChildren().forEach(function(markerLine, index) {
const marker = self._source.markers[index];
const markerX = self._view.timeToPixels(marker - self._source.mediaStartTime);
markerLine.points([markerX, 0, markerX, self._unwrappedHeight]);
});
}
};
SourceGroup.prototype._updateButtons = function() {
if (this._buttonsGroup) {
this._buttonsGroup.x(this._width);
}
};
SourceGroup.prototype._getButtons = function() {
if (!this._buttonsGroup) {
this._buttonsGroup = this._createButtons();
}
return this._buttonsGroup;
};
SourceGroup.prototype._showButtons = function() {
if (this._buttonsGroup) {
if (this._buttonsAnimation) {
this._buttonsAnimation.destroy();
this._buttonsAnimation = null;
}
var self = this;
this._buttonsGroup.visible(true);
this._buttonsAnimation = new Konva.Tween({
node: this._buttonsGroup,
opacity: 1,
duration: 0.2,
easing: Konva.Easings.EaseOut,
onFinish: function() {
self._buttonsAnimation.destroy();
self._buttonsAnimation = null;
}
});
this._buttonsAnimation.play();
}
};
SourceGroup.prototype._hideButtons = function() {
if (this._buttonsGroup) {
if (this._buttonsAnimation) {
this._buttonsAnimation.destroy();
this._buttonsAnimation = null;
}
var self = this;
this._buttonsAnimation = new Konva.Tween({
node: this._buttonsGroup,
opacity: 0,
duration: 0.2,
easing: Konva.Easings.EaseOut,
onFinish: function() {
self._buttonsGroup.visible(false);
self._buttonsAnimation.destroy();
self._buttonsAnimation = null;
}
});
this._buttonsAnimation.play();
}
};
SourceGroup.prototype._getYFromVolume = function(volume) {
return this._borderWidth + (this._height - 2 * this._borderWidth) * (
this._source.volumeRange[1] - volume
) / (
this._source.volumeRange[1] - this._source.volumeRange[0]
);
};
SourceGroup.prototype._getVolumeFromY = function(y) {
return this._source.volumeRange[1] - (
(y - this._borderWidth) / (this._height - 2 * this._borderWidth)
) * (
this._source.volumeRange[1] - this._source.volumeRange[0]
);
};
SourceGroup.prototype._updateVolumeSlider = function() {
const width = this._width;
if (this._volumeSliderGroup) {
this._volumeSliderGroup.getChildren().forEach(function(child) {
if (child instanceof Konva.Group) {
child.width(width);
child.getChildren().forEach(function(node) {
if (node instanceof Konva.Line) {
node.points([0, 0, width, 0]);
}
});
}
});
}
};
SourceGroup.prototype._getVolumeSlider = function() {
if (!this._volumeSliderGroup) {
this._volumeSliderGroup = this._createVolumeSlider();
}
return this._volumeSliderGroup;
};
SourceGroup.prototype._createVolumeSlider = function() {
var self = this;
var volumeY = this._getYFromVolume(this._source.volume);
var volumeGroup = new Konva.Group({
x: 0,
y: 0
});
var volumeText = new Konva.Text({
x: 0,
y: volumeY - 20,
text: '100%',
fontSize: 12,
fill: this._source.volumeSliderColor,
visible: false
});
var maxTextWidth = volumeText.width();
var maxTextHeight = volumeText.height();
var volumeSliderGroup = new Konva.Group({
x: 0,
y: volumeY,
draggable: true,
dragBoundFunc: function(pos) {
var y = Math.min(
volumeGroup.absolutePosition().y + self._height - self._borderWidth,
Math.max(
volumeGroup.absolutePosition().y + self._borderWidth,
pos.y
)
);
var textX = Math.min(
volumeGroup.absolutePosition().x + self._width - maxTextWidth - self._borderWidth,
Math.max(
volumeGroup.absolutePosition().x + self._borderWidth,
self._view.getPointerPosition().x - maxTextWidth
)
);
var textY = y - (self._source.volumeSliderWidth / 2) - maxTextHeight;
volumeText.absolutePosition({
x: textX,
y: textY < volumeGroup.absolutePosition().y + self._borderWidth ?
y + self._source.volumeSliderWidth :
textY
});
return { x: this.absolutePosition().x, y: y };
}
});
var volumeSliderLine = new Konva.Line({
points: [0, 0, this._width, 0],
stroke: this._source.volumeSliderColor,
strokeWidth: this._source.volumeSliderWidth
});
var volumeSliderRect = new Konva.Rect({
x: 0,
y: -this._source.volumeSliderDraggingWidth / 2,
width: this._width,
height: this._source.volumeSliderDraggingWidth,
opacity: 0
});
volumeSliderGroup.add(volumeSliderRect);
volumeSliderGroup.add(volumeSliderLine);
volumeSliderGroup.on('dragstart', function() {
volumeText.visible(true);
});
volumeSliderGroup.on('dragmove', function() {
var volume = self._getVolumeFromY(volumeSliderGroup.y());
volumeText.text((volume * 100).toFixed(0) + '%');
self._source.volume = Math.max(self._source.volumeRange[0], Math.min(volume, self._source.volumeRange[1]));
self._peaks.emit('source.volumeChanged', self._source);
self._group.draw();
});
volumeSliderGroup.on('dragend', function() {
volumeText.visible(false);
});
volumeSliderGroup.on('mouseover', function() {
self._cursor = 'ns-resize';
});
volumeSliderGroup.on('mouseout', function() {
self._cursor = null;
});
volumeGroup.add(volumeSliderGroup);
volumeGroup.add(volumeText);
return volumeGroup;
};
SourceGroup.prototype.destroy = function() {
if (this._buttonsAnimation) {
this._buttonsAnimation.destroy();
this._buttonsAnimation = null;
}
if (this._loader) {
this._loader.destroy();
this._loader = null;
}
this._group.destroy();
};
return SourceGroup;
});