/* * * * (c) 2009-2020 Øystein Moseng * * Sonification functions for chart/series. * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ 'use strict'; import H from '../../parts/Globals.js'; /** * An Earcon configuration, specifying an Earcon and when to play it. * * @requires module:modules/sonification * * @interface Highcharts.EarconConfiguration */ /** * An Earcon instance. * @name Highcharts.EarconConfiguration#earcon * @type {Highcharts.Earcon} */ /** * The ID of the point to play the Earcon on. * @name Highcharts.EarconConfiguration#onPoint * @type {string|undefined} */ /** * A function to determine whether or not to play this earcon on a point. The * function is called for every point, receiving that point as parameter. It * should return either a boolean indicating whether or not to play the earcon, * or a new Earcon instance - in which case the new Earcon will be played. * @name Highcharts.EarconConfiguration#condition * @type {Function|undefined} */ /** * Options for sonifying a series. * * @requires module:modules/sonification * * @interface Highcharts.SonifySeriesOptionsObject */ /** * The duration for playing the points. Note that points might continue to play * after the duration has passed, but no new points will start playing. * @name Highcharts.SonifySeriesOptionsObject#duration * @type {number} */ /** * The axis to use for when to play the points. Can be a string with a data * property (e.g. `x`), or a function. If it is a function, this function * receives the point as argument, and should return a numeric value. The points * with the lowest numeric values are then played first, and the time between * points will be proportional to the distance between the numeric values. * @name Highcharts.SonifySeriesOptionsObject#pointPlayTime * @type {string|Function} */ /** * The instrument definitions for the points in this series. * @name Highcharts.SonifySeriesOptionsObject#instruments * @type {Array} */ /** * Earcons to add to the series. * @name Highcharts.SonifySeriesOptionsObject#earcons * @type {Array|undefined} */ /** * Optionally provide the minimum/maximum data values for the points. If this is * not supplied, it is calculated from all points in the chart on demand. This * option is supplied in the following format, as a map of point data properties * to objects with min/max values: * ```js * dataExtremes: { * y: { * min: 0, * max: 100 * }, * z: { * min: -10, * max: 10 * } * // Properties used and not provided are calculated on demand * } * ``` * @name Highcharts.SonifySeriesOptionsObject#dataExtremes * @type {Highcharts.Dictionary|undefined} */ /** * Callback before a point is played. * @name Highcharts.SonifySeriesOptionsObject#onPointStart * @type {Function|undefined} */ /** * Callback after a point has finished playing. * @name Highcharts.SonifySeriesOptionsObject#onPointEnd * @type {Function|undefined} */ /** * Callback after the series has played. * @name Highcharts.SonifySeriesOptionsObject#onEnd * @type {Function|undefined} */ ''; // detach doclets above import Point from '../../parts/Point.js'; import U from '../../parts/Utilities.js'; var find = U.find, isArray = U.isArray, merge = U.merge, pick = U.pick, splat = U.splat; import utilities from './utilities.js'; /** * Get the relative time value of a point. * @private * @param {Highcharts.Point} point * The point. * @param {Function|string} timeProp * The time axis data prop or the time function. * @return {number} * The time value. */ function getPointTimeValue(point, timeProp) { return typeof timeProp === 'function' ? timeProp(point) : pick(point[timeProp], point.options[timeProp]); } /** * Get the time extremes of this series. This is handled outside of the * dataExtremes, as we always want to just sonify the visible points, and we * always want the extremes to be the extremes of the visible points. * @private * @param {Highcharts.Series} series * The series to compute on. * @param {Function|string} timeProp * The time axis data prop or the time function. * @return {Highcharts.RangeObject} * Object with min/max extremes for the time values. */ function getTimeExtremes(series, timeProp) { // Compute the extremes from the visible points. return series.points.reduce(function (acc, point) { var value = getPointTimeValue(point, timeProp); acc.min = Math.min(acc.min, value); acc.max = Math.max(acc.max, value); return acc; }, { min: Infinity, max: -Infinity }); } /** * Calculate value extremes for used instrument data properties. * @private * @param {Highcharts.Chart} chart * The chart to calculate extremes from. * @param {Array} instruments * The instrument definitions used. * @param {Highcharts.Dictionary} [dataExtremes] * Predefined extremes for each data prop. * @return {Highcharts.Dictionary} * New extremes with data properties mapped to min/max objects. */ function getExtremesForInstrumentProps(chart, instruments, dataExtremes) { return (instruments || []).reduce(function (newExtremes, instrumentDefinition) { Object.keys(instrumentDefinition.instrumentMapping || {}).forEach(function (instrumentParameter) { var value = instrumentDefinition.instrumentMapping[instrumentParameter]; if (typeof value === 'string' && !newExtremes[value]) { // This instrument parameter is mapped to a data prop. // If we don't have predefined data extremes, find them. newExtremes[value] = utilities.calculateDataExtremes(chart, value); } }); return newExtremes; }, merge(dataExtremes)); } /** * Get earcons for the point if there are any. * @private * @param {Highcharts.Point} point * The point to find earcons for. * @param {Array} earconDefinitions * Earcons to check. * @return {Array} * Array of earcons to be played with this point. */ function getPointEarcons(point, earconDefinitions) { return earconDefinitions.reduce(function (earcons, earconDefinition) { var cond, earcon = earconDefinition.earcon; if (earconDefinition.condition) { // We have a condition. This overrides onPoint cond = earconDefinition.condition(point); if (cond instanceof H.sonification.Earcon) { // Condition returned an earcon earcons.push(cond); } else if (cond) { // Condition returned true earcons.push(earcon); } } else if (earconDefinition.onPoint && point.id === earconDefinition.onPoint) { // We have earcon onPoint earcons.push(earcon); } return earcons; }, []); } /** * Utility function to get a new list of instrument options where all the * instrument references are copies. * @private * @param {Array} instruments * The instrument options. * @return {Array} * Array of copied instrument options. */ function makeInstrumentCopies(instruments) { return instruments.map(function (instrumentDef) { var instrument = instrumentDef.instrument, copy = (typeof instrument === 'string' ? H.sonification.instruments[instrument] : instrument).copy(); return merge(instrumentDef, { instrument: copy }); }); } /** * Create a TimelinePath from a series. Takes the same options as seriesSonify. * To intuitively allow multiple series to play simultaneously we make copies of * the instruments for each series. * @private * @param {Highcharts.Series} series * The series to build from. * @param {Highcharts.SonifySeriesOptionsObject} options * The options for building the TimelinePath. * @return {Highcharts.TimelinePath} * A timeline path with events. */ function buildTimelinePathFromSeries(series, options) { // options.timeExtremes is internal and used so that the calculations from // chart.sonify can be reused. var timeExtremes = options.timeExtremes || getTimeExtremes(series, options.pointPlayTime), // Get time offset for a point, relative to duration pointToTime = function (point) { return utilities.virtualAxisTranslate(getPointTimeValue(point, options.pointPlayTime), timeExtremes, { min: 0, max: options.duration }); }, // Compute any data extremes that aren't defined yet dataExtremes = getExtremesForInstrumentProps(series.chart, options.instruments, options.dataExtremes), // Make copies of the instruments used for this series, to allow // multiple series with the same instrument to play together instruments = makeInstrumentCopies(options.instruments), // Go through the points, convert to events, optionally add Earcons timelineEvents = series.points.reduce(function (events, point) { var earcons = getPointEarcons(point, options.earcons || []), time = pointToTime(point); return events.concat( // Event object for point new H.sonification.TimelineEvent({ eventObject: point, time: time, id: point.id, playOptions: { instruments: instruments, dataExtremes: dataExtremes } }), // Earcons earcons.map(function (earcon) { return new H.sonification.TimelineEvent({ eventObject: earcon, time: time }); })); }, []); // Build the timeline path return new H.sonification.TimelinePath({ events: timelineEvents, onStart: function () { if (options.onStart) { options.onStart(series); } }, onEventStart: function (event) { var eventObject = event.options && event.options.eventObject; if (eventObject instanceof Point) { // Check for hidden series if (!eventObject.series.visible && !eventObject.series.chart.series.some(function (series) { return series.visible; })) { // We have no visible series, stop the path. event.timelinePath.timeline.pause(); event.timelinePath.timeline.resetCursor(); return false; } // Emit onPointStart if (options.onPointStart) { options.onPointStart(event, eventObject); } } }, onEventEnd: function (eventData) { var eventObject = eventData.event && eventData.event.options && eventData.event.options.eventObject; if (eventObject instanceof Point && options.onPointEnd) { options.onPointEnd(eventData.event, eventObject); } }, onEnd: function () { if (options.onEnd) { options.onEnd(series); } } }); } /* eslint-disable no-invalid-this, valid-jsdoc */ /** * Sonify a series. * * @sample highcharts/sonification/series-basic/ * Click on series to sonify * @sample highcharts/sonification/series-earcon/ * Series with earcon * @sample highcharts/sonification/point-play-time/ * Play y-axis by time * @sample highcharts/sonification/earcon-on-point/ * Earcon set on point * * @requires module:modules/sonification * * @function Highcharts.Series#sonify * * @param {Highcharts.SonifySeriesOptionsObject} options * The options for sonifying this series. * * @return {void} */ function seriesSonify(options) { var timelinePath = buildTimelinePathFromSeries(this, options), chartSonification = this.chart.sonification; // Only one timeline can play at a time. If we want multiple series playing // at the same time, use chart.sonify. if (chartSonification.timeline) { chartSonification.timeline.pause(); } // Store reference to duration chartSonification.duration = options.duration; // Create new timeline for this series, and play it. chartSonification.timeline = new H.sonification.Timeline({ paths: [timelinePath] }); chartSonification.timeline.play(); } /** * Utility function to assemble options for creating a TimelinePath from a * series when sonifying an entire chart. * @private * @param {Highcharts.Series} series * The series to return options for. * @param {Highcharts.RangeObject} dataExtremes * Pre-calculated data extremes for the chart. * @param {Highcharts.SonificationOptions} chartSonifyOptions * Options passed in to chart.sonify. * @return {Partial} * Options for buildTimelinePathFromSeries. */ function buildSeriesOptions(series, dataExtremes, chartSonifyOptions) { var seriesOptions = chartSonifyOptions.seriesOptions || {}; return merge({ // Calculated dataExtremes for chart dataExtremes: dataExtremes, // We need to get timeExtremes for each series. We pass this // in when building the TimelinePath objects to avoid // calculating twice. timeExtremes: getTimeExtremes(series, chartSonifyOptions.pointPlayTime), // Some options we just pass on instruments: chartSonifyOptions.instruments, onStart: chartSonifyOptions.onSeriesStart, onEnd: chartSonifyOptions.onSeriesEnd, earcons: chartSonifyOptions.earcons }, // Merge in the specific series options by ID isArray(seriesOptions) ? (find(seriesOptions, function (optEntry) { return optEntry.id === pick(series.id, series.options.id); }) || {}) : seriesOptions, { // Forced options pointPlayTime: chartSonifyOptions.pointPlayTime }); } /** * Utility function to normalize the ordering of timeline paths when sonifying * a chart. * @private * @param {string|Array>} orderOptions - * Order options for the sonification. * @param {Highcharts.Chart} chart - The chart we are sonifying. * @param {Function} seriesOptionsCallback * A function that takes a series as argument, and returns the series options * for that series to be used with buildTimelinePathFromSeries. * @return {Array>} If order is * sequential, we return an array of objects to create series paths from. If * order is simultaneous we return an array of an array with the same. If there * is a custom order, we return an array of arrays of either objects (for * series) or TimelinePaths (for earcons and delays). */ function buildPathOrder(orderOptions, chart, seriesOptionsCallback) { var order; if (orderOptions === 'sequential' || orderOptions === 'simultaneous') { // Just add the series from the chart order = chart.series.reduce(function (seriesList, series) { if (series.visible) { seriesList.push({ series: series, seriesOptions: seriesOptionsCallback(series) }); } return seriesList; }, []); // If order is simultaneous, group all series together if (orderOptions === 'simultaneous') { order = [order]; } } else { // We have a specific order, and potentially custom items - like // earcons or silent waits. order = orderOptions.reduce(function (orderList, orderDef) { // Return set of items to play simultaneously. Could be only one. var simulItems = splat(orderDef).reduce(function (items, item) { var itemObject; // Is this item a series ID? if (typeof item === 'string') { var series = chart.get(item); if (series.visible) { itemObject = { series: series, seriesOptions: seriesOptionsCallback(series) }; } // Is it an earcon? If so, just create the path. } else if (item instanceof H.sonification.Earcon) { // Path with a single event itemObject = new H.sonification.TimelinePath({ events: [new H.sonification.TimelineEvent({ eventObject: item })] }); } // Is this item a silent wait? If so, just create the path. if (item.silentWait) { itemObject = new H.sonification.TimelinePath({ silentWait: item.silentWait }); } // Add to items to play simultaneously if (itemObject) { items.push(itemObject); } return items; }, []); // Add to order list if (simulItems.length) { orderList.push(simulItems); } return orderList; }, []); } return order; } /** * Utility function to add a silent wait after all series. * @private * @param {Array>} order * The order of items. * @param {number} wait * The wait in milliseconds to add. * @return {Array>} * The order with waits inserted. */ function addAfterSeriesWaits(order, wait) { if (!wait) { return order; } return order.reduce(function (newOrder, orderDef, i) { var simultaneousPaths = splat(orderDef); newOrder.push(simultaneousPaths); // Go through the simultaneous paths and see if there is a series there if (i < order.length - 1 && // Do not add wait after last series simultaneousPaths.some(function (item) { return item.series; })) { // We have a series, meaning we should add a wait after these // paths have finished. newOrder.push(new H.sonification.TimelinePath({ silentWait: wait })); } return newOrder; }, []); } /** * Utility function to find the total amout of wait time in the TimelinePaths. * @private * @param {Array>} order - The order of * TimelinePaths/items. * @return {number} The total time in ms spent on wait paths between playing. */ function getWaitTime(order) { return order.reduce(function (waitTime, orderDef) { var def = splat(orderDef); return waitTime + (def.length === 1 && def[0].options && def[0].options.silentWait || 0); }, 0); } /** * Utility function to ensure simultaneous paths have start/end events at the * same time, to sync them. * @private * @param {Array} paths - The paths to sync. */ function syncSimultaneousPaths(paths) { // Find the extremes for these paths var extremes = paths.reduce(function (extremes, path) { var events = path.events; if (events && events.length) { extremes.min = Math.min(events[0].time, extremes.min); extremes.max = Math.max(events[events.length - 1].time, extremes.max); } return extremes; }, { min: Infinity, max: -Infinity }); // Go through the paths and add events to make them fit the same timespan paths.forEach(function (path) { var events = path.events, hasEvents = events && events.length, eventsToAdd = []; if (!(hasEvents && events[0].time <= extremes.min)) { eventsToAdd.push(new H.sonification.TimelineEvent({ time: extremes.min })); } if (!(hasEvents && events[events.length - 1].time >= extremes.max)) { eventsToAdd.push(new H.sonification.TimelineEvent({ time: extremes.max })); } if (eventsToAdd.length) { path.addTimelineEvents(eventsToAdd); } }); } /** * Utility function to find the total duration span for all simul path sets * that include series. * @private * @param {Array>} order - The * order of TimelinePaths/items. * @return {number} The total time value span difference for all series. */ function getSimulPathDurationTotal(order) { return order.reduce(function (durationTotal, orderDef) { return durationTotal + splat(orderDef).reduce(function (maxPathDuration, item) { var timeExtremes = (item.series && item.seriesOptions && item.seriesOptions.timeExtremes); return timeExtremes ? Math.max(maxPathDuration, timeExtremes.max - timeExtremes.min) : maxPathDuration; }, 0); }, 0); } /** * Function to calculate the duration in ms for a series. * @private * @param {number} seriesValueDuration - The duration of the series in value * difference. * @param {number} totalValueDuration - The total duration of all (non * simultaneous) series in value difference. * @param {number} totalDurationMs - The desired total duration for all series * in milliseconds. * @return {number} The duration for the series in milliseconds. */ function getSeriesDurationMs(seriesValueDuration, totalValueDuration, totalDurationMs) { // A series spanning the whole chart would get the full duration. return utilities.virtualAxisTranslate(seriesValueDuration, { min: 0, max: totalValueDuration }, { min: 0, max: totalDurationMs }); } /** * Convert series building objects into paths and return a new list of * TimelinePaths. * @private * @param {Array>} order - The * order list. * @param {number} duration - Total duration to aim for in milliseconds. * @return {Array>} Array of TimelinePath objects * to play. */ function buildPathsFromOrder(order, duration) { // Find time used for waits (custom or after series), and subtract it from // available duration. var totalAvailableDurationMs = Math.max(duration - getWaitTime(order), 0), // Add up simultaneous path durations to find total value span duration // of everything totalUsedDuration = getSimulPathDurationTotal(order); // Go through the order list and convert the items return order.reduce(function (allPaths, orderDef) { var simultaneousPaths = splat(orderDef).reduce(function (simulPaths, item) { if (item instanceof H.sonification.TimelinePath) { // This item is already a path object simulPaths.push(item); } else if (item.series) { // We have a series. // We need to set the duration of the series item.seriesOptions.duration = item.seriesOptions.duration || getSeriesDurationMs(item.seriesOptions.timeExtremes.max - item.seriesOptions.timeExtremes.min, totalUsedDuration, totalAvailableDurationMs); // Add the path simulPaths.push(buildTimelinePathFromSeries(item.series, item.seriesOptions)); } return simulPaths; }, []); // Add in the simultaneous paths allPaths.push(simultaneousPaths); return allPaths; }, []); } /** * @private * @param {Highcharts.Chart} chart The chart to get options for. * @param {Highcharts.SonificationOptions} userOptions * Options to merge with options on chart and default options. * @returns {Highcharts.SonificationOptions} The merged options. */ function getChartSonifyOptions(chart, userOptions) { return merge(chart.options.sonification, userOptions); } /** * Options for sonifying a chart. * * @requires module:modules/sonification * * @interface Highcharts.SonificationOptions */ /** * Duration for sonifying the entire chart. The duration is distributed across * the different series intelligently, but does not take earcons into account. * It is also possible to set the duration explicitly per series, using * `seriesOptions`. Note that points may continue to play after the duration has * passed, but no new points will start playing. * @name Highcharts.SonificationOptions#duration * @type {number} */ /** * Define the order to play the series in. This can be given as a string, or an * array specifying a custom ordering. If given as a string, valid values are * `sequential` - where each series is played in order - or `simultaneous`, * where all series are played at once. For custom ordering, supply an array as * the order. Each element in the array can be either a string with a series ID, * an Earcon object, or an object with a numeric `silentWait` property * designating a number of milliseconds to wait before continuing. Each element * of the array will be played in order. To play elements simultaneously, group * the elements in an array. * @name Highcharts.SonificationOptions#order * @type {string|Array>} */ /** * The axis to use for when to play the points. Can be a string with a data * property (e.g. `x`), or a function. If it is a function, this function * receives the point as argument, and should return a numeric value. The points * with the lowest numeric values are then played first, and the time between * points will be proportional to the distance between the numeric values. This * option can not be overridden per series. * @name Highcharts.SonificationOptions#pointPlayTime * @type {string|Function} */ /** * Milliseconds of silent waiting to add between series. Note that waiting time * is considered part of the sonify duration. * @name Highcharts.SonificationOptions#afterSeriesWait * @type {number|undefined} */ /** * Options as given to `series.sonify` to override options per series. If the * option is supplied as an array of options objects, the `id` property of the * object should correspond to the series' id. If the option is supplied as a * single object, the options apply to all series. * @name Highcharts.SonificationOptions#seriesOptions * @type {Object|Array|undefined} */ /** * The instrument definitions for the points in this chart. * @name Highcharts.SonificationOptions#instruments * @type {Array|undefined} */ /** * Earcons to add to the chart. Note that earcons can also be added per series * using `seriesOptions`. * @name Highcharts.SonificationOptions#earcons * @type {Array|undefined} */ /** * Optionally provide the minimum/maximum data values for the points. If this is * not supplied, it is calculated from all points in the chart on demand. This * option is supplied in the following format, as a map of point data properties * to objects with min/max values: * ```js * dataExtremes: { * y: { * min: 0, * max: 100 * }, * z: { * min: -10, * max: 10 * } * // Properties used and not provided are calculated on demand * } * ``` * @name Highcharts.SonificationOptions#dataExtremes * @type {Highcharts.Dictionary|undefined} */ /** * Callback before a series is played. * @name Highcharts.SonificationOptions#onSeriesStart * @type {Function|undefined} */ /** * Callback after a series has finished playing. * @name Highcharts.SonificationOptions#onSeriesEnd * @type {Function|undefined} */ /** * Callback after the chart has played. * @name Highcharts.SonificationOptions#onEnd * @type {Function|undefined} */ /** * Sonify a chart. * * @sample highcharts/sonification/chart-sequential/ * Sonify a basic chart * @sample highcharts/sonification/chart-simultaneous/ * Sonify series simultaneously * @sample highcharts/sonification/chart-custom-order/ * Custom defined order of series * @sample highcharts/sonification/chart-earcon/ * Earcons on chart * @sample highcharts/sonification/chart-events/ * Sonification events on chart * * @requires module:modules/sonification * * @function Highcharts.Chart#sonify * * @param {Highcharts.SonificationOptions} options * The options for sonifying this chart. * * @return {void} */ function chartSonify(options) { var opts = getChartSonifyOptions(this, options); // Only one timeline can play at a time. if (this.sonification.timeline) { this.sonification.timeline.pause(); } // Store reference to duration this.sonification.duration = opts.duration; // Calculate data extremes for the props used var dataExtremes = getExtremesForInstrumentProps(this, opts.instruments, opts.dataExtremes); // Figure out ordering of series and custom paths var order = buildPathOrder(opts.order, this, function (series) { return buildSeriesOptions(series, dataExtremes, opts); }); // Add waits after simultaneous paths with series in them. order = addAfterSeriesWaits(order, opts.afterSeriesWait || 0); // We now have a list of either TimelinePath objects or series that need to // be converted to TimelinePath objects. Convert everything to paths. var paths = buildPathsFromOrder(order, opts.duration); // Sync simultaneous paths paths.forEach(function (simultaneousPaths) { syncSimultaneousPaths(simultaneousPaths); }); // We have a set of paths. Create the timeline, and play it. this.sonification.timeline = new H.sonification.Timeline({ paths: paths, onEnd: opts.onEnd }); this.sonification.timeline.play(); } /** * Get a list of the points currently under cursor. * * @requires module:modules/sonification * * @function Highcharts.Chart#getCurrentSonifyPoints * * @return {Array} * The points currently under the cursor. */ function getCurrentPoints() { var cursorObj; if (this.sonification.timeline) { cursorObj = this.sonification.timeline.getCursor(); // Cursor per pathID return Object.keys(cursorObj).map(function (path) { // Get the event objects under cursor for each path return cursorObj[path].eventObject; }).filter(function (eventObj) { // Return the events that are points return eventObj instanceof Point; }); } return []; } /** * Set the cursor to a point or set of points in different series. * * @requires module:modules/sonification * * @function Highcharts.Chart#setSonifyCursor * * @param {Highcharts.Point|Array} points * The point or points to set the cursor to. If setting multiple points * under the cursor, the points have to be in different series that are * being played simultaneously. */ function setCursor(points) { var timeline = this.sonification.timeline; if (timeline) { splat(points).forEach(function (point) { // We created the events with the ID of the points, which makes // this easy. Just call setCursor for each ID. timeline.setCursor(point.id); }); } } /** * Pause the running sonification. * * @requires module:modules/sonification * * @function Highcharts.Chart#pauseSonify * * @param {boolean} [fadeOut=true] * Fade out as we pause to avoid clicks. * * @return {void} */ function pause(fadeOut) { if (this.sonification.timeline) { this.sonification.timeline.pause(pick(fadeOut, true)); } else if (this.sonification.currentlyPlayingPoint) { this.sonification.currentlyPlayingPoint.cancelSonify(fadeOut); } } /** * Resume the currently running sonification. Requires series.sonify or * chart.sonify to have been played at some point earlier. * * @requires module:modules/sonification * * @function Highcharts.Chart#resumeSonify * * @param {Function} onEnd * Callback to call when play finished. * * @return {void} */ function resume(onEnd) { if (this.sonification.timeline) { this.sonification.timeline.play(onEnd); } } /** * Play backwards from cursor. Requires series.sonify or chart.sonify to have * been played at some point earlier. * * @requires module:modules/sonification * * @function Highcharts.Chart#rewindSonify * * @param {Function} onEnd * Callback to call when play finished. * * @return {void} */ function rewind(onEnd) { if (this.sonification.timeline) { this.sonification.timeline.rewind(onEnd); } } /** * Cancel current sonification and reset cursor. * * @requires module:modules/sonification * * @function Highcharts.Chart#cancelSonify * * @param {boolean} [fadeOut=true] * Fade out as we pause to avoid clicks. * * @return {void} */ function cancel(fadeOut) { this.pauseSonify(fadeOut); this.resetSonifyCursor(); } /** * Reset cursor to start. Requires series.sonify or chart.sonify to have been * played at some point earlier. * * @requires module:modules/sonification * * @function Highcharts.Chart#resetSonifyCursor * * @return {void} */ function resetCursor() { if (this.sonification.timeline) { this.sonification.timeline.resetCursor(); } } /** * Reset cursor to end. Requires series.sonify or chart.sonify to have been * played at some point earlier. * * @requires module:modules/sonification * * @function Highcharts.Chart#resetSonifyCursorEnd * * @return {void} */ function resetCursorEnd() { if (this.sonification.timeline) { this.sonification.timeline.resetCursorEnd(); } } // Export functions var chartSonifyFunctions = { chartSonify: chartSonify, seriesSonify: seriesSonify, pause: pause, resume: resume, rewind: rewind, cancel: cancel, getCurrentPoints: getCurrentPoints, setCursor: setCursor, resetCursor: resetCursor, resetCursorEnd: resetCursorEnd }; export default chartSonifyFunctions;