UNPKG

wavesurfer.js

Version:

Interactive navigable audio visualization using Web Audio and Canvas

529 lines (514 loc) 18.3 kB
/*! * wavesurfer.js markers plugin 6.6.3 (2023-04-04) * 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"]["markers"] = factory(); })(self, () => { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./src/plugin/markers/index.js": /*!*************************************!*\ !*** ./src/plugin/markers/index.js ***! \*************************************/ /***/ ((module, exports) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports["default"] = void 0; function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } 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(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /** * @typedef {Object} MarkerParams * @desc The parameters used to describe a marker. * @example wavesurfer.addMarker(regionParams); * @property {number} time The time to set the marker at * @property {?label} string An optional marker label * @property {?tooltip} string An optional marker tooltip * @property {?color} string Background color for marker * @property {?position} string "top" or "bottom", defaults to "bottom" * @property {?markerElement} element An HTML element to display instead of the default marker image * @property {?draggable} boolean Set marker as draggable, defaults to false * @property {?boolean} preventContextMenu Determines whether the context menu * is prevented from being opened, defaults to false */ /** * Markers are points in time in the audio that can be jumped to. * * @implements {PluginClass} * * @example * import MarkersPlugin from 'wavesurfer.markers.js'; * * // if you are using <script> tags * var MarkerPlugin = window.WaveSurfer.markers; * * // ... initialising wavesurfer with the plugin * var wavesurfer = WaveSurfer.create({ * // wavesurfer options ... * plugins: [ * MarkersPlugin.create({ * // plugin options ... * }) * ] * }); */ var DEFAULT_FILL_COLOR = "#D8D8D8"; var DEFAULT_POSITION = "bottom"; var MarkersPlugin = /*#__PURE__*/function () { function MarkersPlugin(params, ws) { var _this = this; _classCallCheck(this, MarkersPlugin); this.params = params; this.wavesurfer = ws; this.util = ws.util; this.style = this.util.style; this.markerLineWidth = 1; this.markerWidth = 11; this.markerHeight = 22; this.dragging = false; this._onResize = function () { _this._updateMarkerPositions(); }; this._onBackendCreated = function () { _this.wrapper = _this.wavesurfer.drawer.wrapper; if (_this.params.markers) { _this.params.markers.forEach(function (marker) { return _this.add(marker); }); } window.addEventListener('resize', _this._onResize, true); window.addEventListener('orientationchange', _this._onResize, true); _this.wavesurfer.on('zoom', _this._onResize); if (!_this.markers.find(function (marker) { return marker.draggable; })) { return; } _this.onMouseMove = function (e) { return _this._onMouseMove(e); }; window.addEventListener('mousemove', _this.onMouseMove); _this.onMouseUp = function (e) { return _this._onMouseUp(e); }; window.addEventListener("mouseup", _this.onMouseUp); }; this.markers = []; this._onReady = function () { _this.wrapper = _this.wavesurfer.drawer.wrapper; _this._updateMarkerPositions(); }; } _createClass(MarkersPlugin, [{ key: "init", value: function init() { // Check if ws is ready if (this.wavesurfer.isReady) { this._onBackendCreated(); this._onReady(); } else { this.wavesurfer.once('ready', this._onReady); this.wavesurfer.once('backend-created', this._onBackendCreated); } } }, { key: "destroy", value: function destroy() { this.wavesurfer.un('ready', this._onReady); this.wavesurfer.un('backend-created', this._onBackendCreated); this.wavesurfer.un('zoom', this._onResize); window.removeEventListener('resize', this._onResize, true); window.removeEventListener('orientationchange', this._onResize, true); if (this.onMouseMove) { window.removeEventListener('mousemove', this.onMouseMove); } if (this.onMouseUp) { window.removeEventListener("mouseup", this.onMouseUp); } this.clear(); } /** * Add a marker * * @param {MarkerParams} params Marker definition * @return {object} The created marker */ }, { key: "add", value: function add(params) { var marker = { time: params.time, label: params.label, tooltip: params.tooltip, color: params.color || DEFAULT_FILL_COLOR, position: params.position || DEFAULT_POSITION, draggable: !!params.draggable, preventContextMenu: !!params.preventContextMenu }; marker.el = this._createMarkerElement(marker, params.markerElement); this.wrapper.appendChild(marker.el); this.markers.push(marker); this._updateMarkerPositions(); this._registerEvents(); return marker; } /** * Remove a marker * * @param {number|Object} indexOrMarker Index of the marker to remove or the marker object itself */ }, { key: "remove", value: function remove(indexOrMarker) { var index = indexOrMarker; if (isNaN(index)) { index = this.markers.findIndex(function (marker) { return marker === indexOrMarker; }); } var marker = this.markers[index]; if (!marker) { return; } var label = marker.el.getElementsByClassName("marker-label")[0]; if (label) { if (label._onContextMenu) { label.removeEventListener("contextmenu", label._onContextMenu); } if (label._onClick) { label.removeEventListener("click", label._onClick); } if (label._onMouseDown) { label.removeEventListener("mousedown", label._onMouseDown); } } this.wrapper.removeChild(marker.el); this.markers.splice(index, 1); this._unregisterEvents(); } }, { key: "_createPointerSVG", value: function _createPointerSVG(color, position) { var svgNS = "http://www.w3.org/2000/svg"; var el = document.createElementNS(svgNS, "svg"); var polygon = document.createElementNS(svgNS, "polygon"); el.setAttribute("viewBox", "0 0 40 80"); polygon.setAttribute("id", "polygon"); polygon.setAttribute("stroke", "#979797"); polygon.setAttribute("fill", color); polygon.setAttribute("points", "20 0 40 30 40 80 0 80 0 30"); if (position == "top") { polygon.setAttribute("transform", "rotate(180, 20 40)"); } el.appendChild(polygon); this.style(el, { width: this.markerWidth + "px", height: this.markerHeight + "px", "min-width": this.markerWidth + "px", "margin-right": "5px", "z-index": 4 }); return el; } }, { key: "_createMarkerElement", value: function _createMarkerElement(marker, markerElement) { var _this2 = this; var label = marker.label; var tooltip = marker.tooltip; var el = document.createElement('marker'); el.className = "wavesurfer-marker"; this.style(el, { position: "absolute", height: "100%", display: "flex", overflow: "hidden", "flex-direction": marker.position == "top" ? "column-reverse" : "column" }); var line = document.createElement('div'); var width = markerElement ? markerElement.width : this.markerWidth; marker.offset = (width - this.markerLineWidth) / 2; this.style(line, { "flex-grow": 1, "margin-left": marker.offset + "px", background: "black", width: this.markerLineWidth + "px", opacity: 0.1 }); el.appendChild(line); var labelDiv = document.createElement('div'); var point = markerElement || this._createPointerSVG(marker.color, marker.position); if (marker.draggable) { point.draggable = false; } labelDiv.appendChild(point); if (label) { var labelEl = document.createElement('span'); labelEl.innerText = label; labelEl.setAttribute('title', tooltip); this.style(labelEl, { "font-family": "inherit", "font-size": "90%" }); labelDiv.appendChild(labelEl); } this.style(labelDiv, { display: "flex", "align-items": "center", cursor: "pointer" }); labelDiv.classList.add("marker-label"); el.appendChild(labelDiv); labelDiv._onClick = function (e) { e.stopPropagation(); // Click event is caught when the marker-drop event was dispatched. // Drop event was dispatched at this moment, but this.dragging // is waiting for the next tick to set as false if (_this2.dragging) { return; } _this2.wavesurfer.setCurrentTime(marker.time); _this2.wavesurfer.fireEvent("marker-click", marker, e); }; labelDiv.addEventListener("click", labelDiv._onClick); labelDiv._onContextMenu = function (e) { if (marker.preventContextMenu) { e.preventDefault(); } _this2.wavesurfer.fireEvent("marker-contextmenu", marker, e); }; labelDiv.addEventListener("contextmenu", labelDiv._onContextMenu); if (marker.draggable) { labelDiv._onMouseDown = function () { _this2.selectedMarker = marker; }; labelDiv.addEventListener("mousedown", labelDiv._onMouseDown); } return el; } }, { key: "_updateMarkerPositions", value: function _updateMarkerPositions() { for (var i = 0; i < this.markers.length; i++) { var marker = this.markers[i]; this._updateMarkerPosition(marker); } } /** * Update a marker position based on its time property. * * @private * @param {MarkerParams} params The marker to update. * @returns {void} */ }, { key: "_updateMarkerPosition", value: function _updateMarkerPosition(params) { var duration = this.wavesurfer.getDuration(); var elementWidth = this.wavesurfer.drawer.width / this.wavesurfer.params.pixelRatio; var positionPct = Math.min(params.time / duration, 1); var leftPx = elementWidth * positionPct - params.offset; this.style(params.el, { "left": leftPx + "px", "max-width": elementWidth - leftPx + "px" }); } /** * Fires `marker-drag` event, update the `time` property for the * selected marker based on the mouse position, and calls to update * its position. * * @private * @param {MouseEvent} event The mouse event. * @returns {void} */ }, { key: "_onMouseMove", value: function _onMouseMove(event) { if (!this.selectedMarker) { return; } if (!this.dragging) { this.dragging = true; this.wavesurfer.fireEvent("marker-drag", this.selectedMarker, event); } this.selectedMarker.time = this.wavesurfer.drawer.handleEvent(event) * this.wavesurfer.getDuration(); this._updateMarkerPositions(); } /** * Fires `marker-drop` event and unselect the dragged marker. * * @private * @param {MouseEvent} event The mouse event. * @returns {void} */ }, { key: "_onMouseUp", value: function _onMouseUp(event) { var _this3 = this; if (this.selectedMarker) { setTimeout(function () { _this3.selectedMarker = false; _this3.dragging = false; }, 0); } if (!this.dragging) { return; } event.stopPropagation(); var duration = this.wavesurfer.getDuration(); this.selectedMarker.time = this.wavesurfer.drawer.handleEvent(event) * duration; this._updateMarkerPositions(); this.wavesurfer.fireEvent("marker-drop", this.selectedMarker, event); } }, { key: "_registerEvents", value: function _registerEvents() { var _this4 = this; if (!this.markers.find(function (marker) { return marker.draggable; })) { return; } //we have some draggable markers, check for listeners if (!this.onMouseMove) { this.onMouseMove = function (e) { return _this4._onMouseMove(e); }; window.addEventListener('mousemove', this.onMouseMove); } if (!this.onMouseUp) { this.onMouseUp = function (e) { return _this4._onMouseUp(e); }; window.addEventListener("mouseup", this.onMouseUp); } } }, { key: "_unregisterEvents", value: function _unregisterEvents() { if (this.markers.find(function (marker) { return marker.draggable; })) { return; } //we don't have any draggable markers, unregister listeners if (this.onMouseMove) { window.removeEventListener('mousemove', this.onMouseMove); this.onMouseMove = null; } if (this.onMouseUp) { window.removeEventListener("mouseup", this.onMouseUp); this.onMouseUp = null; } } /** * Remove all markers */ }, { key: "clear", value: function clear() { while (this.markers.length > 0) { this.remove(0); } } }], [{ key: "create", value: /** * @typedef {Object} MarkersPluginParams * @property {?MarkerParams[]} markers Initial set of markers * @fires MarkersPlugin#marker-click * @fires MarkersPlugin#marker-drag * @fires MarkersPlugin#marker-drop */ /** * Markers 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 {MarkersPluginParams} params parameters use to initialise the plugin * @since 4.6.0 * @return {PluginDefinition} an object representing the plugin */ function create(params) { return { name: 'markers', deferInit: params && params.deferInit ? params.deferInit : false, params: params, staticProps: { addMarker: function addMarker(options) { if (!this.initialisedPluginList.markers) { this.initPlugin('markers'); } return this.markers.add(options); }, getMarkers: function getMarkers() { return this.markers; }, clearMarkers: function clearMarkers() { this.markers && this.markers.clear(); } }, instance: MarkersPlugin }; } }]); return MarkersPlugin; }(); exports["default"] = MarkersPlugin; 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/markers/index.js"); /******/ /******/ return __webpack_exports__; /******/ })() ; }); //# sourceMappingURL=wavesurfer.markers.js.map