sintonia/legacy/public/js/wavesurfer/timeline.js

641 lines
22 KiB
JavaScript

/*!
* wavesurfer.js timeline plugin 4.6.0 (2024-02-05)
* https://wavesurfer-js.org
* @license BSD-3-Clause
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define("WaveSurfer", [], factory);
else if(typeof exports === 'object')
exports["WaveSurfer"] = factory();
else
root["WaveSurfer"] = root["WaveSurfer"] || {}, root["WaveSurfer"]["timeline"] = factory();
})(this, () => {
return /******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./src/plugin/timeline/index.js":
/*!**************************************!*\
!*** ./src/plugin/timeline/index.js ***!
\**************************************/
/***/ ((module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports["default"] = void 0;
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* @typedef {Object} TimelinePluginParams
* @desc Extends the `WavesurferParams` wavesurfer was initialised with
* @property {!string|HTMLElement} container CSS selector or HTML element where
* the timeline should be drawn. This is the only required parameter.
* @property {number} notchPercentHeight=90 Height of notches in percent
* @property {string} unlabeledNotchColor='#c0c0c0' The colour of the notches
* that do not have labels
* @property {string} primaryColor='#000' The colour of the main notches
* @property {string} secondaryColor='#c0c0c0' The colour of the secondary
* notches
* @property {string} primaryFontColor='#000' The colour of the labels next to
* the main notches
* @property {string} secondaryFontColor='#000' The colour of the labels next to
* the secondary notches
* @property {number} labelPadding=5 The padding between the label and the notch
* @property {?number} zoomDebounce A debounce timeout to increase rendering
* performance for large files
* @property {string} fontFamily='Arial'
* @property {number} fontSize=10 Font size of labels in pixels
* @property {?number} duration Length of the track in seconds. Overrides
* getDuration() for setting length of timeline
* @property {function} formatTimeCallback (sec, pxPerSec) -> label
* @property {function} timeInterval (pxPerSec) -> seconds between notches
* @property {function} primaryLabelInterval (pxPerSec) -> cadence between
* labels in primary color
* @property {function} secondaryLabelInterval (pxPerSec) -> cadence between
* labels in secondary color
* @property {?number} offset Offset for the timeline start in seconds. May also be
* negative.
* @property {?boolean} deferInit Set to true to manually call
* `initPlugin('timeline')`
*/
/**
* Adds a timeline to the waveform.
*
* @implements {PluginClass}
* @extends {Observer}
* @example
* // es6
* import TimelinePlugin from 'wavesurfer.timeline.js';
*
* // commonjs
* var TimelinePlugin = require('wavesurfer.timeline.js');
*
* // if you are using <script> tags
* var TimelinePlugin = window.WaveSurfer.timeline;
*
* // ... initialising wavesurfer with the plugin
* var wavesurfer = WaveSurfer.create({
* // wavesurfer options ...
* plugins: [
* TimelinePlugin.create({
* // plugin options ...
* })
* ]
* });
*/
var TimelinePlugin = exports["default"] = /*#__PURE__*/function () {
/**
* Creates an instance of TimelinePlugin.
*
* You probably want to use TimelinePlugin.create()
*
* @param {TimelinePluginParams} params Plugin parameters
* @param {object} ws Wavesurfer instance
*/
function TimelinePlugin(params, ws) {
var _this = this;
_classCallCheck(this, TimelinePlugin);
_initialiseProps.call(this);
this.container = 'string' == typeof params.container ? document.querySelector(params.container) : params.container;
if (!this.container) {
throw new Error('No container for wavesurfer timeline');
}
this.wavesurfer = ws;
this.util = ws.util;
this.params = Object.assign({}, {
height: 20,
notchPercentHeight: 90,
labelPadding: 5,
unlabeledNotchColor: '#c0c0c0',
primaryColor: '#000',
secondaryColor: '#c0c0c0',
primaryFontColor: '#000',
secondaryFontColor: '#000',
fontFamily: 'Arial',
fontSize: 10,
duration: null,
zoomDebounce: false,
formatTimeCallback: this.defaultFormatTimeCallback,
timeInterval: this.defaultTimeInterval,
primaryLabelInterval: this.defaultPrimaryLabelInterval,
secondaryLabelInterval: this.defaultSecondaryLabelInterval,
offset: 0
}, params);
this.canvases = [];
this.wrapper = null;
this.drawer = null;
this.pixelRatio = null;
this.maxCanvasWidth = null;
this.maxCanvasElementWidth = null;
/**
* This event handler has to be in the constructor function because it
* relies on the debounce function which is only available after
* instantiation
*
* Use a debounced function if `params.zoomDebounce` is defined
*
* @returns {void}
*/
this._onZoom = this.params.zoomDebounce ? this.wavesurfer.util.debounce(function () {
return _this.render();
}, this.params.zoomDebounce) : function () {
return _this.render();
};
}
/**
* Initialisation function used by the plugin API
*/
_createClass(TimelinePlugin, [{
key: "init",
value: function init() {
// Check if ws is ready
if (this.wavesurfer.isReady) {
this._onReady();
} else {
this.wavesurfer.once('ready', this._onReady);
}
}
/**
* Destroy function used by the plugin API
*/
}, {
key: "destroy",
value: function destroy() {
this.unAll();
this.wavesurfer.un('redraw', this._onRedraw);
this.wavesurfer.un('zoom', this._onZoom);
this.wavesurfer.un('ready', this._onReady);
this.wavesurfer.drawer.wrapper.removeEventListener('scroll', this._onScroll);
if (this.wrapper && this.wrapper.parentNode) {
this.wrapper.removeEventListener('click', this._onWrapperClick);
this.wrapper.parentNode.removeChild(this.wrapper);
this.wrapper = null;
}
}
/**
* Create a timeline element to wrap the canvases drawn by this plugin
*
*/
}, {
key: "createWrapper",
value: function createWrapper() {
var wsParams = this.wavesurfer.params;
this.container.innerHTML = '';
this.wrapper = this.container.appendChild(document.createElement('timeline'));
this.util.style(this.wrapper, {
display: 'block',
position: 'relative',
userSelect: 'none',
webkitUserSelect: 'none',
height: "".concat(this.params.height, "px")
});
if (wsParams.fillParent || wsParams.scrollParent) {
this.util.style(this.wrapper, {
width: '100%',
overflowX: 'hidden',
overflowY: 'hidden'
});
}
this.wrapper.addEventListener('click', this._onWrapperClick);
}
/**
* Render the timeline (also updates the already rendered timeline)
*
*/
}, {
key: "render",
value: function render() {
if (!this.wrapper) {
this.createWrapper();
}
this.updateCanvases();
this.updateCanvasesPositioning();
this.renderCanvases();
}
/**
* Add new timeline canvas
*
*/
}, {
key: "addCanvas",
value: function addCanvas() {
var canvas = this.wrapper.appendChild(document.createElement('canvas'));
this.canvases.push(canvas);
this.util.style(canvas, {
position: 'absolute',
zIndex: 4
});
}
/**
* Remove timeline canvas
*
*/
}, {
key: "removeCanvas",
value: function removeCanvas() {
var canvas = this.canvases.pop();
canvas.parentElement.removeChild(canvas);
}
/**
* Make sure the correct of timeline canvas elements exist and are cached in
* this.canvases
*
*/
}, {
key: "updateCanvases",
value: function updateCanvases() {
var totalWidth = Math.round(this.drawer.wrapper.scrollWidth);
var requiredCanvases = Math.ceil(totalWidth / this.maxCanvasElementWidth);
while (this.canvases.length < requiredCanvases) {
this.addCanvas();
}
while (this.canvases.length > requiredCanvases) {
this.removeCanvas();
}
}
/**
* Update the dimensions and positioning style for all the timeline canvases
*
*/
}, {
key: "updateCanvasesPositioning",
value: function updateCanvasesPositioning() {
var _this2 = this;
// cache length for performance
var canvasesLength = this.canvases.length;
this.canvases.forEach(function (canvas, i) {
// canvas width is the max element width, or if it is the last the
// required width
var canvasWidth = i === canvasesLength - 1 ? _this2.drawer.wrapper.scrollWidth - _this2.maxCanvasElementWidth * (canvasesLength - 1) : _this2.maxCanvasElementWidth;
// set dimensions and style
canvas.width = canvasWidth * _this2.pixelRatio;
// on certain pixel ratios the canvas appears cut off at the bottom,
// therefore leave 1px extra
canvas.height = (_this2.params.height + 1) * _this2.pixelRatio;
_this2.util.style(canvas, {
width: "".concat(canvasWidth, "px"),
height: "".concat(_this2.params.height, "px"),
left: "".concat(i * _this2.maxCanvasElementWidth, "px")
});
});
}
/**
* Render the timeline labels and notches
*
*/
}, {
key: "renderCanvases",
value: function renderCanvases() {
var _this3 = this;
var duration = this.params.duration || this.wavesurfer.backend.getDuration();
if (duration <= 0) {
return;
}
var wsParams = this.wavesurfer.params;
var fontSize = this.params.fontSize * wsParams.pixelRatio;
var totalSeconds = parseInt(duration, 10) + 1;
var width = wsParams.fillParent && !wsParams.scrollParent ? this.drawer.getWidth() : this.drawer.wrapper.scrollWidth * wsParams.pixelRatio;
var height1 = this.params.height * this.pixelRatio;
var height2 = this.params.height * (this.params.notchPercentHeight / 100) * this.pixelRatio;
var pixelsPerSecond = width / duration;
var formatTime = this.params.formatTimeCallback;
// if parameter is function, call the function with
// pixelsPerSecond, otherwise simply take the value as-is
var intervalFnOrVal = function intervalFnOrVal(option) {
return typeof option === 'function' ? option(pixelsPerSecond) : option;
};
var timeInterval = intervalFnOrVal(this.params.timeInterval);
var primaryLabelInterval = intervalFnOrVal(this.params.primaryLabelInterval);
var secondaryLabelInterval = intervalFnOrVal(this.params.secondaryLabelInterval);
var curPixel = pixelsPerSecond * this.params.offset;
var curSeconds = 0;
var i;
// build an array of position data with index, second and pixel data,
// this is then used multiple times below
var positioning = [];
for (i = 0; i < totalSeconds / timeInterval; i++) {
positioning.push([i, curSeconds, curPixel]);
curSeconds += timeInterval;
curPixel += pixelsPerSecond * timeInterval;
}
// iterate over each position
var renderPositions = function renderPositions(cb) {
positioning.forEach(function (pos) {
cb(pos[0], pos[1], pos[2]);
});
};
// render primary labels
this.setFillStyles(this.params.primaryColor);
this.setFonts("".concat(fontSize, "px ").concat(this.params.fontFamily));
this.setFillStyles(this.params.primaryFontColor);
renderPositions(function (i, curSeconds, curPixel) {
if (i % primaryLabelInterval === 0) {
_this3.fillRect(curPixel, 0, 1, height1);
_this3.fillText(formatTime(curSeconds, pixelsPerSecond), curPixel + _this3.params.labelPadding * _this3.pixelRatio, height1);
}
});
// render secondary labels
this.setFillStyles(this.params.secondaryColor);
this.setFonts("".concat(fontSize, "px ").concat(this.params.fontFamily));
this.setFillStyles(this.params.secondaryFontColor);
renderPositions(function (i, curSeconds, curPixel) {
if (i % secondaryLabelInterval === 0) {
_this3.fillRect(curPixel, 0, 1, height1);
_this3.fillText(formatTime(curSeconds, pixelsPerSecond), curPixel + _this3.params.labelPadding * _this3.pixelRatio, height1);
}
});
// render the actual notches (when no labels are used)
this.setFillStyles(this.params.unlabeledNotchColor);
renderPositions(function (i, curSeconds, curPixel) {
if (i % secondaryLabelInterval !== 0 && i % primaryLabelInterval !== 0) {
_this3.fillRect(curPixel, 0, 1, height2);
}
});
}
/**
* Set the canvas fill style
*
* @param {DOMString|CanvasGradient|CanvasPattern} fillStyle Fill style to
* use
*/
}, {
key: "setFillStyles",
value: function setFillStyles(fillStyle) {
this.canvases.forEach(function (canvas) {
canvas.getContext('2d').fillStyle = fillStyle;
});
}
/**
* Set the canvas font
*
* @param {DOMString} font Font to use
*/
}, {
key: "setFonts",
value: function setFonts(font) {
this.canvases.forEach(function (canvas) {
canvas.getContext('2d').font = font;
});
}
/**
* Draw a rectangle on the canvases
*
* (it figures out the offset for each canvas)
*
* @param {number} x X-position
* @param {number} y Y-position
* @param {number} width Width
* @param {number} height Height
*/
}, {
key: "fillRect",
value: function fillRect(x, y, width, height) {
var _this4 = this;
this.canvases.forEach(function (canvas, i) {
var leftOffset = i * _this4.maxCanvasWidth;
var intersection = {
x1: Math.max(x, i * _this4.maxCanvasWidth),
y1: y,
x2: Math.min(x + width, i * _this4.maxCanvasWidth + canvas.width),
y2: y + height
};
if (intersection.x1 < intersection.x2) {
canvas.getContext('2d').fillRect(intersection.x1 - leftOffset, intersection.y1, intersection.x2 - intersection.x1, intersection.y2 - intersection.y1);
}
});
}
/**
* Fill a given text on the canvases
*
* @param {string} text Text to render
* @param {number} x X-position
* @param {number} y Y-position
*/
}, {
key: "fillText",
value: function fillText(text, x, y) {
var textWidth;
var xOffset = 0;
this.canvases.forEach(function (canvas) {
var context = canvas.getContext('2d');
var canvasWidth = context.canvas.width;
if (xOffset > x + textWidth) {
return;
}
if (xOffset + canvasWidth > x) {
textWidth = context.measureText(text).width;
context.fillText(text, x - xOffset, y);
}
xOffset += canvasWidth;
});
}
/**
* Turn the time into a suitable label for the time.
*
* @param {number} seconds Seconds to format
* @param {number} pxPerSec Pixels per second
* @returns {number} Time
*/
}, {
key: "defaultFormatTimeCallback",
value: function defaultFormatTimeCallback(seconds, pxPerSec) {
if (seconds / 60 > 1) {
// calculate minutes and seconds from seconds count
var minutes = parseInt(seconds / 60, 10);
seconds = parseInt(seconds % 60, 10);
// fill up seconds with zeroes
seconds = seconds < 10 ? '0' + seconds : seconds;
return "".concat(minutes, ":").concat(seconds);
}
return Math.round(seconds * 1000) / 1000;
}
/**
* Return how many seconds should be between each notch
*
* @param {number} pxPerSec Pixels per second
* @returns {number} Time
*/
}, {
key: "defaultTimeInterval",
value: function defaultTimeInterval(pxPerSec) {
if (pxPerSec >= 25) {
return 1;
} else if (pxPerSec * 5 >= 25) {
return 5;
} else if (pxPerSec * 15 >= 25) {
return 15;
}
return Math.ceil(0.5 / pxPerSec) * 60;
}
/**
* Return the cadence of notches that get labels in the primary color.
*
* @param {number} pxPerSec Pixels per second
* @returns {number} Cadence
*/
}, {
key: "defaultPrimaryLabelInterval",
value: function defaultPrimaryLabelInterval(pxPerSec) {
if (pxPerSec >= 25) {
return 10;
} else if (pxPerSec * 5 >= 25) {
return 6;
} else if (pxPerSec * 15 >= 25) {
return 4;
}
return 4;
}
/**
* Return the cadence of notches that get labels in the secondary color.
*
* @param {number} pxPerSec Pixels per second
* @returns {number} Cadence
*/
}, {
key: "defaultSecondaryLabelInterval",
value: function defaultSecondaryLabelInterval(pxPerSec) {
if (pxPerSec >= 25) {
return 5;
} else if (pxPerSec * 5 >= 25) {
return 2;
} else if (pxPerSec * 15 >= 25) {
return 2;
}
return 2;
}
}], [{
key: "create",
value:
/**
* Timeline plugin definition factory
*
* This function must be used to create a plugin definition which can be
* used by wavesurfer to correctly instantiate the plugin.
*
* @param {TimelinePluginParams} params parameters use to initialise the plugin
* @return {PluginDefinition} an object representing the plugin
*/
function create(params) {
return {
name: 'timeline',
deferInit: params && params.deferInit ? params.deferInit : false,
params: params,
instance: TimelinePlugin
};
}
// event handlers
/**
* @returns {void}
*/
/**
* @param {object} e Click event
*/
}]);
return TimelinePlugin;
}();
var _initialiseProps = function _initialiseProps() {
var _this5 = this;
this._onScroll = function () {
if (_this5.wrapper && _this5.drawer.wrapper) {
_this5.wrapper.scrollLeft = _this5.drawer.wrapper.scrollLeft;
}
};
this._onRedraw = function () {
return _this5.render();
};
this._onReady = function () {
var ws = _this5.wavesurfer;
_this5.drawer = ws.drawer;
_this5.pixelRatio = ws.drawer.params.pixelRatio;
_this5.maxCanvasWidth = ws.drawer.maxCanvasWidth || ws.drawer.width;
_this5.maxCanvasElementWidth = ws.drawer.maxCanvasElementWidth || Math.round(_this5.maxCanvasWidth / _this5.pixelRatio);
// add listeners
ws.drawer.wrapper.addEventListener('scroll', _this5._onScroll);
ws.on('redraw', _this5._onRedraw);
ws.on('zoom', _this5._onZoom);
_this5.render();
};
this._onWrapperClick = function (e) {
e.preventDefault();
var relX = 'offsetX' in e ? e.offsetX : e.layerX;
_this5.fireEvent('click', relX / _this5.wrapper.scrollWidth || 0);
};
};
module.exports = exports.default;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module is referenced by other modules so it can't be inlined
/******/ var __webpack_exports__ = __webpack_require__("./src/plugin/timeline/index.js");
/******/
/******/ return __webpack_exports__;
/******/ })()
;
});
//# sourceMappingURL=wavesurfer.timeline.js.map