@checksub_team/peaks_timeline
Version:
JavaScript UI component for displaying audio waveforms
461 lines (369 loc) • 13.7 kB
JavaScript
/**
* @file
*
* Defines the {@link LineIndicator} class.
*
* @module line-indicator
*/
define([
'konva',
'./utils',
'./svgs'
], function(
Konva,
Utils,
SVGs) {
'use strict';
/**
* Creates a Konva.Stage that displays a representation of each line.
*
* @class
* @alias LineIndicator
*
* @param {Peaks} peaks
* @param {WaveformOverview|WaveformZoomView} view
*/
function LineIndicator(peaks, view, container) {
this._peaks = peaks;
this._view = view;
this._container = container;
this._width = this._peaks.options.lineIndicatorWidth;
this._height = this._view.getHeight();
this._sizes = {
font: this._peaks.options.lineIndicatorFontSize,
icon: {
default: this._peaks.options.lineIndicatorDefaultIconSize,
volume: this._peaks.options.lineIndicatorVolumeIconSize,
noVolume: this._peaks.options.lineIndicatorNoVolumeIconSize,
visibility: this._peaks.options.lineIndicatorVisibilityIconSize,
noVisibility: this._peaks.options.lineIndicatorNoVisibilityIconSize
}
};
this._yPadding = 30;
this._defaultPadding = 5;
this._types = ['default'].concat(Object.keys(SVGs));
this._stage = new Konva.Stage({
container: container,
width: this._width,
height: this._height
});
this._layer = new Konva.Layer();
this._stage.add(this._layer);
this._indicators = {};
this._separatingLine = new Konva.Line({
points: [this._width, 0, this._width, this._height],
stroke: 'gray',
strokeWidth: 1,
listening: false
});
this._layer.add(this._separatingLine);
this._layer.draw();
if (this._peaks.options.enableLineIndicatorContextMenu) {
this._createContextMenu();
}
this._peaks.on('lineIndicator.setType', this._onSetType.bind(this));
this._peaks.on('lineIndicator.setText', this._onSetText.bind(this));
}
LineIndicator.prototype.fitToView = function() {
this._height = this._view.getHeight();
this._stage.height(this._height);
this._separatingLine.points([this._width, 0, this._width, this._height]);
};
LineIndicator.prototype._onSetType = function(lineId, type) {
this.removeIndicator(lineId, true);
type = this._types.includes(type) ? type : 'default';
var indicator = this._createIndicator(
this._indicators[lineId].line,
type,
this._indicators[lineId].text
);
this._layer.add(indicator);
this._indicators[lineId].indicator = indicator;
this._indicators[lineId].type = type;
this.draw();
};
LineIndicator.prototype._onSetText = function(lineId, text) {
this.removeIndicator(lineId, true);
var indicator = this._createIndicator(
this._indicators[lineId].line,
this._indicators[lineId].type,
text
);
this._layer.add(indicator);
this._indicators[lineId].indicator = indicator;
this._indicators[lineId].text = text;
this.draw();
};
LineIndicator.prototype._showMenu = function(menu) {
menu.style.display = 'block';
var containerRect = this._stage.container().getBoundingClientRect();
menu.style.top = containerRect.top
+ this._stage.getPointerPosition().y
- menu.offsetHeight + 'px';
menu.style.left = containerRect.left + this._stage.getPointerPosition().x + 6 + 'px';
};
LineIndicator.prototype._createIndicator = function(line, type, text) {
var indicator = new Konva.Group();
var indicatorHeight = 0;
if (text) {
var textNode = new Konva.Text({
text: text,
fontSize: this._sizes.font,
fontFamily: this._peaks.options.lineIndicatorFont,
fill: this._peaks.options.lineIndicatorTextColor,
align: 'center',
width: this._width,
listening: false
});
textNode.setAttr('defaultColor', this._peaks.options.lineIndicatorTextColor);
textNode.setAttr('selectedColor', this._peaks.options.lineIndicatorSelectedTextColor);
indicator.add(textNode);
indicatorHeight += this._sizes.font;
}
type = this._types.includes(type) ? type : 'default';
var iconNode;
if (type === 'default') {
var padding = text ? this._defaultPadding : 0;
iconNode = new Konva.Circle({
x: this._width / 2,
y: indicatorHeight + this._sizes.icon.default / 2 + padding,
radius: this._sizes.icon.default / 2,
fill: this._peaks.options.lineIndicatorIconColor,
strokeWidth: 0,
lineId: line.getId(),
listening: false
});
indicatorHeight += padding;
}
else {
iconNode = new Konva.Path({
x: (this._width - this._sizes.icon[type]) / 2,
y: indicatorHeight,
data: SVGs[type].path,
fill: this._peaks.options.lineIndicatorIconColor,
scale: {
x: (this._sizes.icon[type]) / SVGs[type].width,
y: (this._sizes.icon[type]) / SVGs[type].height
},
lineId: line.getId(),
listening: false
});
}
iconNode.setAttr('defaultColor', this._peaks.options.lineIndicatorIconColor);
iconNode.setAttr('selectedColor', this._peaks.options.lineIndicatorSelectedIconColor);
indicator.add(iconNode);
indicatorHeight += this._sizes.icon[type];
indicator.setAttr('trueHeight', indicatorHeight);
var backgroundRect = new Konva.Rect({
x: 0,
y: 0,
width: this._width,
height: indicatorHeight,
fill: 'transparent',
listening: true
});
indicator.add(backgroundRect);
backgroundRect.moveToTop();
indicator.y(line.getY() + (line.lineHeight() - indicatorHeight) / 2);
var self = this;
indicator.on('mouseenter', function() {
indicator.getChildren().forEach(function(child) {
child.fill(child.getAttr('selectedColor'));
});
self.draw();
self._stage.container().style.cursor = 'pointer';
});
indicator.on('mouseout', function() {
indicator.getChildren().forEach(function(child) {
child.fill(child.getAttr('defaultColor'));
});
self.draw();
self._stage.container().style.cursor = 'default';
});
indicator.on('click', function(e) {
self._peaks.emit('lineIndicator.click', self._indicators[line.getId()], e.evt);
});
return indicator;
};
LineIndicator.prototype.addIndicator = function(line) {
if (!this._indicators[line.getId()]) {
var indicator = this._createIndicator(line);
this._layer.add(indicator);
this._indicators[line.getId()] = {
indicator: indicator,
line: line,
type: 'default'
};
}
};
LineIndicator.prototype.removeIndicator = function(lineId, keepInList) {
if (this._indicators[lineId]) {
if (this._indicators[lineId].indicator) {
this._indicators[lineId].indicator.destroy();
}
if (!keepInList) {
delete this._indicators[lineId];
}
else {
this._indicators[lineId].indicator = null;
}
}
};
LineIndicator.prototype.updateIndicator = function(lineId) {
if (this._indicators[lineId]) {
var y = this._indicators[lineId].line.getY();
var isVisible = y + this._indicators[lineId].line.lineHeight() + this._yPadding > 0
&& y - this._yPadding < this._height;
var isLineNotEmpty = !this._indicators[lineId].line.isEmpty();
if (isLineNotEmpty && isVisible) {
if (!this._indicators[lineId].indicator) {
this._indicators[lineId].indicator = this._createIndicator(
this._indicators[lineId].line,
this._indicators[lineId].type,
this._indicators[lineId].text
);
this._layer.add(this._indicators[lineId].indicator);
}
else {
this._indicators[lineId].indicator.y(
y + this._indicators[lineId].line.lineHeight() / 2
- this._indicators[lineId].indicator.getAttr('trueHeight') / 2
);
}
}
else {
this.removeIndicator(lineId, true);
}
}
};
LineIndicator.prototype.updateIndicators = function() {
for (var lineId in this._indicators) {
if (Utils.objectHasProperty(this._indicators, lineId)) {
this.updateIndicator(lineId);
}
}
};
LineIndicator.prototype.draw = function() {
this._layer.draw();
};
LineIndicator.prototype._createContextMenu = function() {
var menu = document.createElement('div');
var addLine = document.createElement('button');
var addLineAbove = document.createElement('button');
var addLineBelow = document.createElement('button');
var deleteLine = document.createElement('button');
var currentIndicator = null;
var self = this;
menu.style.display = 'none';
menu.style.position = 'absolute';
menu.style.backgroundColor = 'white';
menu.style.boxShadow = '0 0 5px grey';
menu.style.borderRadius = '3px';
menu.style.zIndex = 2;
addLine.style.display = 'none';
addLine.style.border = 'none';
addLine.style.margin = '0';
addLine.style.width = '100%';
addLine.style.backgroundColor = 'white';
addLine.style.padding = '10px';
addLine.textContent = 'Add first line';
addLine.onmouseover = function() {
this.style.backgroundColor = 'lightgray';
};
addLine.onmouseout = function() {
this.style.backgroundColor = 'white';
};
addLineAbove.style.display = 'none';
addLineAbove.style.border = 'none';
addLineAbove.style.margin = '0';
addLineAbove.style.width = '100%';
addLineAbove.style.backgroundColor = 'white';
addLineAbove.style.padding = '10px';
addLineAbove.textContent = 'Add line above';
addLineAbove.onmouseover = function() {
this.style.backgroundColor = 'lightgray';
};
addLineAbove.onmouseout = function() {
this.style.backgroundColor = 'white';
};
addLineBelow.style.display = 'none';
addLineBelow.style.border = 'none';
addLineBelow.style.margin = '0';
addLineBelow.style.width = '100%';
addLineBelow.style.backgroundColor = 'white';
addLineBelow.style.padding = '10px';
addLineBelow.textContent = 'Add line below';
addLineBelow.onmouseover = function() {
this.style.backgroundColor = 'lightgray';
};
addLineBelow.onmouseout = function() {
this.style.backgroundColor = 'white';
};
deleteLine.style.display = 'none';
deleteLine.style.border = 'none';
deleteLine.style.margin = '0';
deleteLine.style.width = '100%';
deleteLine.style.backgroundColor = 'white';
deleteLine.style.padding = '10px';
deleteLine.textContent = 'Delete line';
deleteLine.onmouseover = function() {
this.style.backgroundColor = 'lightgray';
};
deleteLine.onmouseout = function() {
this.style.backgroundColor = 'white';
};
menu.appendChild(addLine);
menu.appendChild(addLineAbove);
menu.appendChild(addLineBelow);
menu.appendChild(deleteLine);
this._container.appendChild(menu);
addLine.addEventListener('click', function() {
self._peaks.emit('line.add', 0);
self.draw();
});
addLineAbove.addEventListener('click', function() {
var line = self._indicators[currentIndicator.getAttr('lineId')].line;
self._peaks.emit('line.add', Number(line.getPosition()));
self.draw();
});
addLineBelow.addEventListener('click', function() {
var line = self._indicators[currentIndicator.getAttr('lineId')].line;
self._peaks.emit('line.add', Number(line.getPosition()) + 1);
self.draw();
});
deleteLine.addEventListener('click', function() {
var line = self._indicators[currentIndicator.getAttr('lineId')].line;
self._peaks.emit('line.remove', Number(line.getPosition()));
self.draw();
});
window.addEventListener('click', function() {
// hide menu
menu.style.display = 'none';
});
this._stage.on('contextmenu', function(e) {
// prevent default behavior
e.evt.preventDefault();
if (e.target === self._stage && Object.keys(self._indicators).length === 0) {
// if we are on empty place of the stage
// we will show the possibility to add a first line
addLine.style.display = 'block';
addLineAbove.style.display = 'none';
addLineBelow.style.display = 'none';
deleteLine.style.display = 'none';
// show menu
self._showMenu(menu);
}
else if (e.target !== self._stage) {
currentIndicator = e.target;
addLine.style.display = 'none';
addLineAbove.style.display = 'block';
addLineBelow.style.display = 'block';
deleteLine.style.display = 'block';
deleteLine.disabled = !self._indicators[currentIndicator.getAttr('lineId')].line.isEmpty();
// show menu
self._showMenu(menu);
}
});
};
return LineIndicator;
});