UNPKG

@checksub_team/peaks_timeline

Version:

JavaScript UI component for displaying audio waveforms

1,591 lines (1,319 loc) 44.8 kB
/** * @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; });