UNPKG

wavesurfer.js

Version:

Interactive navigable audio visualization using Web Audio and Canvas

381 lines (368 loc) 14.2 kB
/*! * wavesurfer.js elan 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"]["elan"] = factory(); })(self, () => { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./src/plugin/elan/index.js": /*!**********************************!*\ !*** ./src/plugin/elan/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 _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 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} ElanPluginParams * @property {string|HTMLElement} container CSS selector or HTML element where * the ELAN information should be rendered. * @property {string} url The location of ELAN XML data * @property {?boolean} deferInit Set to true to manually call * @property {?Object} tiers If set only shows the data tiers with the `TIER_ID` * in this map. */ /** * Downloads and renders ELAN audio transcription documents alongside the * waveform. * * @implements {PluginClass} * @extends {Observer} * @example * // es6 * import ElanPlugin from 'wavesurfer.elan.js'; * * // commonjs * var ElanPlugin = require('wavesurfer.elan.js'); * * // if you are using <script> tags * var ElanPlugin = window.WaveSurfer.elan; * * // ... initialising wavesurfer with the plugin * var wavesurfer = WaveSurfer.create({ * // wavesurfer options ... * plugins: [ * ElanPlugin.create({ * // plugin options ... * }) * ] * }); */ var ElanPlugin = /*#__PURE__*/function () { function ElanPlugin(params, ws) { _classCallCheck(this, ElanPlugin); _defineProperty(this, "Types", { ALIGNABLE_ANNOTATION: 'ALIGNABLE_ANNOTATION', REF_ANNOTATION: 'REF_ANNOTATION' }); this.data = null; this.params = params; this.container = 'string' == typeof params.container ? document.querySelector(params.container) : params.container; if (!this.container) { throw Error('No container for ELAN'); } } _createClass(ElanPlugin, [{ key: "init", value: function init() { this.bindClick(); if (this.params.url) { this.load(this.params.url); } } }, { key: "destroy", value: function destroy() { this.container.removeEventListener('click', this._onClick); this.container.removeChild(this.table); } }, { key: "load", value: function load(url) { var _this = this; this.loadXML(url, function (xml) { _this.data = _this.parseElan(xml); _this.render(); _this.fireEvent('ready', _this.data); }); } }, { key: "loadXML", value: function loadXML(url, callback) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'document'; xhr.send(); xhr.addEventListener('load', function (e) { callback && callback(e.target.responseXML); }); } }, { key: "parseElan", value: function parseElan(xml) { var _this2 = this; var _forEach = Array.prototype.forEach; var _map = Array.prototype.map; var data = { media: {}, timeOrder: {}, tiers: [], annotations: {}, alignableAnnotations: [] }; var header = xml.querySelector('HEADER'); var inMilliseconds = header.getAttribute('TIME_UNITS') == 'milliseconds'; var media = header.querySelector('MEDIA_DESCRIPTOR'); data.media.url = media.getAttribute('MEDIA_URL'); data.media.type = media.getAttribute('MIME_TYPE'); var timeSlots = xml.querySelectorAll('TIME_ORDER TIME_SLOT'); var timeOrder = {}; _forEach.call(timeSlots, function (slot) { var value = parseFloat(slot.getAttribute('TIME_VALUE')); // If in milliseconds, convert to seconds with rounding if (inMilliseconds) { value = Math.round(value * 1e2) / 1e5; } timeOrder[slot.getAttribute('TIME_SLOT_ID')] = value; }); data.tiers = _map.call(xml.querySelectorAll('TIER'), function (tier) { return { id: tier.getAttribute('TIER_ID'), linguisticTypeRef: tier.getAttribute('LINGUISTIC_TYPE_REF'), defaultLocale: tier.getAttribute('DEFAULT_LOCALE'), annotations: _map.call(tier.querySelectorAll('REF_ANNOTATION, ALIGNABLE_ANNOTATION'), function (node) { var annot = { type: node.nodeName, id: node.getAttribute('ANNOTATION_ID'), ref: node.getAttribute('ANNOTATION_REF'), value: node.querySelector('ANNOTATION_VALUE').textContent.trim() }; if (_this2.Types.ALIGNABLE_ANNOTATION == annot.type) { // Add start & end to alignable annotation annot.start = timeOrder[node.getAttribute('TIME_SLOT_REF1')]; annot.end = timeOrder[node.getAttribute('TIME_SLOT_REF2')]; // Add to the list of alignable annotations data.alignableAnnotations.push(annot); } // Additionally, put into the flat map of all annotations data.annotations[annot.id] = annot; return annot; }) }; }); // Create JavaScript references between annotations data.tiers.forEach(function (tier) { tier.annotations.forEach(function (annot) { if (null != annot.ref) { annot.reference = data.annotations[annot.ref]; } }); }); // Sort alignable annotations by start & end data.alignableAnnotations.sort(function (a, b) { var d = a.start - b.start; if (d == 0) { d = b.end - a.end; } return d; }); data.length = data.alignableAnnotations.length; return data; } }, { key: "render", value: function render() { var _this3 = this; // apply tiers filter var tiers = this.data.tiers; if (this.params.tiers) { tiers = tiers.filter(function (tier) { return tier.id in _this3.params.tiers; }); } // denormalize references to alignable annotations var backRefs = {}; var indeces = {}; tiers.forEach(function (tier, index) { tier.annotations.forEach(function (annot) { if (annot.reference && annot.reference.type == _this3.Types.ALIGNABLE_ANNOTATION) { if (!(annot.reference.id in backRefs)) { backRefs[annot.ref] = {}; } backRefs[annot.ref][index] = annot; indeces[index] = true; } }); }); indeces = Object.keys(indeces).sort(); this.renderedAlignable = this.data.alignableAnnotations.filter(function (alignable) { return backRefs[alignable.id]; }); // table var table = this.table = document.createElement('table'); table.className = 'wavesurfer-annotations'; // head var thead = document.createElement('thead'); var headRow = document.createElement('tr'); thead.appendChild(headRow); table.appendChild(thead); var th = document.createElement('th'); th.textContent = 'Time'; th.className = 'wavesurfer-time'; headRow.appendChild(th); indeces.forEach(function (index) { var tier = tiers[index]; var th = document.createElement('th'); th.className = 'wavesurfer-tier-' + tier.id; th.textContent = tier.id; if (_this3.params.tiers) { th.style.width = _this3.params.tiers[tier.id]; } headRow.appendChild(th); }); // body var tbody = document.createElement('tbody'); table.appendChild(tbody); this.renderedAlignable.forEach(function (alignable) { var row = document.createElement('tr'); row.id = 'wavesurfer-alignable-' + alignable.id; tbody.appendChild(row); var td = document.createElement('td'); td.className = 'wavesurfer-time'; td.textContent = alignable.start.toFixed(1) + '–' + alignable.end.toFixed(1); row.appendChild(td); var backRef = backRefs[alignable.id]; indeces.forEach(function (index) { var tier = tiers[index]; var td = document.createElement('td'); var annotation = backRef[index]; if (annotation) { td.id = 'wavesurfer-annotation-' + annotation.id; td.dataset.ref = alignable.id; td.dataset.start = alignable.start; td.dataset.end = alignable.end; td.textContent = annotation.value; } td.className = 'wavesurfer-tier-' + tier.id; row.appendChild(td); }); }); this.container.innerHTML = ''; this.container.appendChild(table); } }, { key: "bindClick", value: function bindClick() { var _this4 = this; this._onClick = function (e) { var ref = e.target.dataset.ref; if (null != ref) { var annot = _this4.data.annotations[ref]; if (annot) { _this4.fireEvent('select', annot.start, annot.end); } } }; this.container.addEventListener('click', this._onClick); } }, { key: "getRenderedAnnotation", value: function getRenderedAnnotation(time) { var result; this.renderedAlignable.some(function (annotation) { if (annotation.start <= time && annotation.end >= time) { result = annotation; return true; } return false; }); return result; } }, { key: "getAnnotationNode", value: function getAnnotationNode(annotation) { return document.getElementById('wavesurfer-alignable-' + annotation.id); } }], [{ key: "create", value: /** * Elan 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 {ElanPluginParams} params parameters use to initialise the plugin * @return {PluginDefinition} an object representing the plugin */ function create(params) { return { name: 'elan', deferInit: params && params.deferInit ? params.deferInit : false, params: params, instance: ElanPlugin }; } }]); return ElanPlugin; }(); exports["default"] = ElanPlugin; 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/elan/index.js"); /******/ /******/ return __webpack_exports__; /******/ })() ; }); //# sourceMappingURL=wavesurfer.elan.js.map