UNPKG

highcharts

Version:
1,164 lines (1,162 loc) 489 kB
/** * @license Highstock JS v9.0.1 (2021-02-16) * * Highstock as a plugin for Highcharts * * (c) 2010-2021 Torstein Honsi * * License: www.highcharts.com/license */ 'use strict'; (function (factory) { if (typeof module === 'object' && module.exports) { factory['default'] = factory; module.exports = factory; } else if (typeof define === 'function' && define.amd) { define('highcharts/modules/stock', ['highcharts'], function (Highcharts) { factory(Highcharts); factory.Highcharts = Highcharts; return factory; }); } else { factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined); } }(function (Highcharts) { var _modules = Highcharts ? Highcharts._modules : {}; function _registerModule(obj, path, args, fn) { if (!obj.hasOwnProperty(path)) { obj[path] = fn.apply(null, args); } } _registerModule(_modules, 'Core/Axis/NavigatorAxis.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) { /* * * * (c) 2010-2021 Torstein Honsi * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var isTouchDevice = H.isTouchDevice; var addEvent = U.addEvent, correctFloat = U.correctFloat, defined = U.defined, isNumber = U.isNumber, pick = U.pick; /* eslint-disable valid-jsdoc */ /** * @private * @class */ var NavigatorAxisAdditions = /** @class */ (function () { /* * * * Constructors * * */ function NavigatorAxisAdditions(axis) { this.axis = axis; } /* * * * Functions * * */ /** * @private */ NavigatorAxisAdditions.prototype.destroy = function () { this.axis = void 0; }; /** * Add logic to normalize the zoomed range in order to preserve the pressed * state of range selector buttons * * @private * @function Highcharts.Axis#toFixedRange * @param {number} [pxMin] * @param {number} [pxMax] * @param {number} [fixedMin] * @param {number} [fixedMax] * @return {*} */ NavigatorAxisAdditions.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) { var navigator = this; var axis = navigator.axis; var chart = axis.chart; var fixedRange = chart && chart.fixedRange, halfPointRange = (axis.pointRange || 0) / 2, newMin = pick(fixedMin, axis.translate(pxMin, true, !axis.horiz)), newMax = pick(fixedMax, axis.translate(pxMax, true, !axis.horiz)), changeRatio = fixedRange && (newMax - newMin) / fixedRange; // Add/remove half point range to/from the extremes (#1172) if (!defined(fixedMin)) { newMin = correctFloat(newMin + halfPointRange); } if (!defined(fixedMax)) { newMax = correctFloat(newMax - halfPointRange); } // If the difference between the fixed range and the actual requested // range is too great, the user is dragging across an ordinal gap, and // we need to release the range selector button. if (changeRatio > 0.7 && changeRatio < 1.3) { if (fixedMax) { newMin = newMax - fixedRange; } else { newMax = newMin + fixedRange; } } if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411 newMin = newMax = void 0; } return { min: newMin, max: newMax }; }; return NavigatorAxisAdditions; }()); /** * @private * @class */ var NavigatorAxis = /** @class */ (function () { function NavigatorAxis() { } /* * * * Static Functions * * */ /** * @private */ NavigatorAxis.compose = function (AxisClass) { AxisClass.keepProps.push('navigatorAxis'); /* eslint-disable no-invalid-this */ addEvent(AxisClass, 'init', function () { var axis = this; if (!axis.navigatorAxis) { axis.navigatorAxis = new NavigatorAxisAdditions(axis); } }); // For Stock charts, override selection zooming with some special // features because X axis zooming is already allowed by the Navigator // and Range selector. addEvent(AxisClass, 'zoom', function (e) { var axis = this; var chart = axis.chart; var chartOptions = chart.options; var navigator = chartOptions.navigator; var navigatorAxis = axis.navigatorAxis; var pinchType = chartOptions.chart.pinchType; var rangeSelector = chartOptions.rangeSelector; var zoomType = chartOptions.chart.zoomType; var previousZoom; if (axis.isXAxis && ((navigator && navigator.enabled) || (rangeSelector && rangeSelector.enabled))) { // For y only zooming, ignore the X axis completely if (zoomType === 'y') { e.zoomed = false; // For xy zooming, record the state of the zoom before zoom // selection, then when the reset button is pressed, revert to // this state. This should apply only if the chart is // initialized with a range (#6612), otherwise zoom all the way // out. } else if (((!isTouchDevice && zoomType === 'xy') || (isTouchDevice && pinchType === 'xy')) && axis.options.range) { previousZoom = navigatorAxis.previousZoom; if (defined(e.newMin)) { navigatorAxis.previousZoom = [axis.min, axis.max]; } else if (previousZoom) { e.newMin = previousZoom[0]; e.newMax = previousZoom[1]; navigatorAxis.previousZoom = void 0; } } } if (typeof e.zoomed !== 'undefined') { e.preventDefault(); } }); /* eslint-enable no-invalid-this */ }; /* * * * Static Properties * * */ /** * @private */ NavigatorAxis.AdditionsClass = NavigatorAxisAdditions; return NavigatorAxis; }()); return NavigatorAxis; }); _registerModule(_modules, 'Core/Axis/ScrollbarAxis.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) { /* * * * (c) 2010-2021 Torstein Honsi * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var addEvent = U.addEvent, defined = U.defined, pick = U.pick; /* eslint-disable no-invalid-this, valid-jsdoc */ /** * Creates scrollbars if enabled. * * @private */ var ScrollbarAxis = /** @class */ (function () { function ScrollbarAxis() { } /** * Attaches to axis events to create scrollbars if enabled. * * @private * * @param AxisClass * Axis class to extend. * * @param ScrollbarClass * Scrollbar class to use. */ ScrollbarAxis.compose = function (AxisClass, ScrollbarClass) { var getExtremes = function (axis) { var axisMin = pick(axis.options && axis.options.min, axis.min); var axisMax = pick(axis.options && axis.options.max, axis.max); return { axisMin: axisMin, axisMax: axisMax, scrollMin: defined(axis.dataMin) ? Math.min(axisMin, axis.min, axis.dataMin, pick(axis.threshold, Infinity)) : axisMin, scrollMax: defined(axis.dataMax) ? Math.max(axisMax, axis.max, axis.dataMax, pick(axis.threshold, -Infinity)) : axisMax }; }; // Wrap axis initialization and create scrollbar if enabled: addEvent(AxisClass, 'afterInit', function () { var axis = this; if (axis.options && axis.options.scrollbar && axis.options.scrollbar.enabled) { // Predefined options: axis.options.scrollbar.vertical = !axis.horiz; axis.options.startOnTick = axis.options.endOnTick = false; axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart); addEvent(axis.scrollbar, 'changed', function (e) { var _a = getExtremes(axis), axisMin = _a.axisMin, axisMax = _a.axisMax, unitedMin = _a.scrollMin, unitedMax = _a.scrollMax, range = unitedMax - unitedMin, to, from; // #12834, scroll when show/hide series, wrong extremes if (!defined(axisMin) || !defined(axisMax)) { return; } if ((axis.horiz && !axis.reversed) || (!axis.horiz && axis.reversed)) { to = unitedMin + range * this.to; from = unitedMin + range * this.from; } else { // y-values in browser are reversed, but this also // applies for reversed horizontal axis: to = unitedMin + range * (1 - this.from); from = unitedMin + range * (1 - this.to); } if (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) || // Mouseup always should change extremes e.DOMType === 'mouseup' || e.DOMType === 'touchend' || // Internal events !defined(e.DOMType)) { axis.setExtremes(from, to, true, e.DOMType !== 'mousemove' && e.DOMType !== 'touchmove', e); } else { // When live redraw is disabled, don't change extremes // Only change the position of the scollbar thumb this.setRange(this.from, this.to); } }); } }); // Wrap rendering axis, and update scrollbar if one is created: addEvent(AxisClass, 'afterRender', function () { var axis = this, _a = getExtremes(axis), scrollMin = _a.scrollMin, scrollMax = _a.scrollMax, scrollbar = axis.scrollbar, offset = axis.axisTitleMargin + (axis.titleOffset || 0), scrollbarsOffsets = axis.chart.scrollbarsOffsets, axisMargin = axis.options.margin || 0, offsetsIndex, from, to; if (scrollbar) { if (axis.horiz) { // Reserve space for labels/title if (!axis.opposite) { scrollbarsOffsets[1] += offset; } scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] - (axis.opposite ? axisMargin : 0), axis.width, axis.height); // Next scrollbar should reserve space for margin (if set) if (!axis.opposite) { scrollbarsOffsets[1] += axisMargin; } offsetsIndex = 1; } else { // Reserve space for labels/title if (axis.opposite) { scrollbarsOffsets[0] += offset; } scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] - (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height); // Next scrollbar should reserve space for margin (if set) if (axis.opposite) { scrollbarsOffsets[0] += axisMargin; } offsetsIndex = 0; } scrollbarsOffsets[offsetsIndex] += scrollbar.size + scrollbar.options.margin; if (isNaN(scrollMin) || isNaN(scrollMax) || !defined(axis.min) || !defined(axis.max) || axis.min === axis.max // #10733 ) { // default action: when extremes are the same or there is // not extremes on the axis, but scrollbar exists, make it // full size scrollbar.setRange(0, 1); } else { from = (axis.min - scrollMin) / (scrollMax - scrollMin); to = (axis.max - scrollMin) / (scrollMax - scrollMin); if ((axis.horiz && !axis.reversed) || (!axis.horiz && axis.reversed)) { scrollbar.setRange(from, to); } else { // inverse vertical axis scrollbar.setRange(1 - to, 1 - from); } } } }); // Make space for a scrollbar: addEvent(AxisClass, 'afterGetOffset', function () { var axis = this, index = axis.horiz ? 2 : 1, scrollbar = axis.scrollbar; if (scrollbar) { axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets axis.chart.axisOffset[index] += scrollbar.size + scrollbar.options.margin; } }); }; return ScrollbarAxis; }()); return ScrollbarAxis; }); _registerModule(_modules, 'Core/Scrollbar.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Color/Palette.js'], _modules['Core/Axis/ScrollbarAxis.js'], _modules['Core/Utilities.js'], _modules['Core/Options.js']], function (Axis, H, palette, ScrollbarAxis, U, O) { /* * * * (c) 2010-2021 Torstein Honsi * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var addEvent = U.addEvent, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, fireEvent = U.fireEvent, merge = U.merge, pick = U.pick, removeEvent = U.removeEvent; var defaultOptions = O.defaultOptions; var isTouchDevice = H.isTouchDevice; /** * When we have vertical scrollbar, rifles and arrow in buttons should be * rotated. The same method is used in Navigator's handles, to rotate them. * * @function Highcharts.swapXY * * @param {Highcharts.SVGPathArray} path * Path to be rotated. * * @param {boolean} [vertical] * If vertical scrollbar, swap x-y values. * * @return {Highcharts.SVGPathArray} * Rotated path. * * @requires modules/stock */ var swapXY = H.swapXY = function (path, vertical) { if (vertical) { path.forEach(function (seg) { var len = seg.length; var temp; for (var i = 0; i < len; i += 2) { temp = seg[i + 1]; if (typeof temp === 'number') { seg[i + 1] = seg[i + 2]; seg[i + 2] = temp; } } }); } return path; }; /* eslint-disable no-invalid-this, valid-jsdoc */ /** * A reusable scrollbar, internally used in Highstock's navigator and optionally * on individual axes. * * @private * @class * @name Highcharts.Scrollbar * @param {Highcharts.SVGRenderer} renderer * @param {Highcharts.ScrollbarOptions} options * @param {Highcharts.Chart} chart */ var Scrollbar = /** @class */ (function () { /* * * * Constructors * * */ function Scrollbar(renderer, options, chart) { /* * * * Properties * * */ this._events = []; this.chartX = 0; this.chartY = 0; this.from = 0; this.group = void 0; this.scrollbar = void 0; this.scrollbarButtons = []; this.scrollbarGroup = void 0; this.scrollbarLeft = 0; this.scrollbarRifles = void 0; this.scrollbarStrokeWidth = 1; this.scrollbarTop = 0; this.size = 0; this.to = 0; this.track = void 0; this.trackBorderWidth = 1; this.userOptions = {}; this.x = 0; this.y = 0; this.chart = chart; this.options = options; this.renderer = chart.renderer; this.init(renderer, options, chart); } /* * * * Functions * * */ /** * Set up the mouse and touch events for the Scrollbar * * @private * @function Highcharts.Scrollbar#addEvents * @return {void} */ Scrollbar.prototype.addEvents = function () { var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1], buttons = this.scrollbarButtons, bar = this.scrollbarGroup.element, track = this.track.element, mouseDownHandler = this.mouseDownHandler.bind(this), mouseMoveHandler = this.mouseMoveHandler.bind(this), mouseUpHandler = this.mouseUpHandler.bind(this), _events; // Mouse events _events = [ [buttons[buttonsOrder[0]].element, 'click', this.buttonToMinClick.bind(this)], [buttons[buttonsOrder[1]].element, 'click', this.buttonToMaxClick.bind(this)], [track, 'click', this.trackClick.bind(this)], [bar, 'mousedown', mouseDownHandler], [bar.ownerDocument, 'mousemove', mouseMoveHandler], [bar.ownerDocument, 'mouseup', mouseUpHandler] ]; // Touch events if (H.hasTouch) { _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]); } // Add them all _events.forEach(function (args) { addEvent.apply(null, args); }); this._events = _events; }; Scrollbar.prototype.buttonToMaxClick = function (e) { var scroller = this; var range = (scroller.to - scroller.from) * pick(scroller.options.step, 0.2); scroller.updatePosition(scroller.from + range, scroller.to + range); fireEvent(scroller, 'changed', { from: scroller.from, to: scroller.to, trigger: 'scrollbar', DOMEvent: e }); }; Scrollbar.prototype.buttonToMinClick = function (e) { var scroller = this; var range = correctFloat(scroller.to - scroller.from) * pick(scroller.options.step, 0.2); scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range)); fireEvent(scroller, 'changed', { from: scroller.from, to: scroller.to, trigger: 'scrollbar', DOMEvent: e }); }; /** * Get normalized (0-1) cursor position over the scrollbar * * @private * @function Highcharts.Scrollbar#cursorToScrollbarPosition * * @param {*} normalizedEvent * normalized event, with chartX and chartY values * * @return {Highcharts.Dictionary<number>} * Local position {chartX, chartY} */ Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) { var scroller = this, options = scroller.options, minWidthDifference = options.minWidth > scroller.calculatedWidth ? options.minWidth : 0; // minWidth distorts translation return { chartX: (normalizedEvent.chartX - scroller.x - scroller.xOffset) / (scroller.barWidth - minWidthDifference), chartY: (normalizedEvent.chartY - scroller.y - scroller.yOffset) / (scroller.barWidth - minWidthDifference) }; }; /** * Destroys allocated elements. * * @private * @function Highcharts.Scrollbar#destroy * @return {void} */ Scrollbar.prototype.destroy = function () { var scroller = this.chart.scroller; // Disconnect events added in addEvents this.removeEvents(); // Destroy properties [ 'track', 'scrollbarRifles', 'scrollbar', 'scrollbarGroup', 'group' ].forEach(function (prop) { if (this[prop] && this[prop].destroy) { this[prop] = this[prop].destroy(); } }, this); // #6421, chart may have more scrollbars if (scroller && this === scroller.scrollbar) { scroller.scrollbar = null; // Destroy elements in collection destroyObjectProperties(scroller.scrollbarButtons); } }; /** * Draw the scrollbar buttons with arrows * * @private * @function Highcharts.Scrollbar#drawScrollbarButton * @param {number} index * 0 is left, 1 is right * @return {void} */ Scrollbar.prototype.drawScrollbarButton = function (index) { var scroller = this, renderer = scroller.renderer, scrollbarButtons = scroller.scrollbarButtons, options = scroller.options, size = scroller.size, group, tempElem; group = renderer.g().add(scroller.group); scrollbarButtons.push(group); // Create a rectangle for the scrollbar button tempElem = renderer.rect() .addClass('highcharts-scrollbar-button') .add(group); // Presentational attributes if (!this.chart.styledMode) { tempElem.attr({ stroke: options.buttonBorderColor, 'stroke-width': options.buttonBorderWidth, fill: options.buttonBackgroundColor }); } // Place the rectangle based on the rendered stroke width tempElem.attr(tempElem.crisp({ x: -0.5, y: -0.5, width: size + 1, height: size + 1, r: options.buttonBorderRadius }, tempElem.strokeWidth())); // Button arrow tempElem = renderer .path(swapXY([[ 'M', size / 2 + (index ? -1 : 1), size / 2 - 3 ], [ 'L', size / 2 + (index ? -1 : 1), size / 2 + 3 ], [ 'L', size / 2 + (index ? 2 : -2), size / 2 ]], options.vertical)) .addClass('highcharts-scrollbar-arrow') .add(scrollbarButtons[index]); if (!this.chart.styledMode) { tempElem.attr({ fill: options.buttonArrowColor }); } }; /** * @private * @function Highcharts.Scrollbar#init * @param {Highcharts.SVGRenderer} renderer * @param {Highcharts.ScrollbarOptions} options * @param {Highcharts.Chart} chart */ Scrollbar.prototype.init = function (renderer, options, chart) { this.scrollbarButtons = []; this.renderer = renderer; this.userOptions = options; this.options = merge(Scrollbar.defaultOptions, options); this.chart = chart; // backward compatibility this.size = pick(this.options.size, this.options.height); // Init if (options.enabled) { this.render(); this.addEvents(); } }; Scrollbar.prototype.mouseDownHandler = function (e) { var scroller = this; var normalizedEvent = scroller.chart.pointer.normalize(e), mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent); scroller.chartX = mousePosition.chartX; scroller.chartY = mousePosition.chartY; scroller.initPositions = [scroller.from, scroller.to]; scroller.grabbedCenter = true; }; /** * Event handler for the mouse move event. * @private */ Scrollbar.prototype.mouseMoveHandler = function (e) { var scroller = this; var normalizedEvent = scroller.chart.pointer.normalize(e), options = scroller.options, direction = options.vertical ? 'chartY' : 'chartX', initPositions = scroller.initPositions || [], scrollPosition, chartPosition, change; // In iOS, a mousemove event with e.pageX === 0 is fired when // holding the finger down in the center of the scrollbar. This // should be ignored. if (scroller.grabbedCenter && // #4696, scrollbar failed on Android (!e.touches || e.touches[0][direction] !== 0)) { chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction]; scrollPosition = scroller[direction]; change = chartPosition - scrollPosition; scroller.hasDragged = true; scroller.updatePosition(initPositions[0] + change, initPositions[1] + change); if (scroller.hasDragged) { fireEvent(scroller, 'changed', { from: scroller.from, to: scroller.to, trigger: 'scrollbar', DOMType: e.type, DOMEvent: e }); } } }; /** * Event handler for the mouse up event. * @private */ Scrollbar.prototype.mouseUpHandler = function (e) { var scroller = this; if (scroller.hasDragged) { fireEvent(scroller, 'changed', { from: scroller.from, to: scroller.to, trigger: 'scrollbar', DOMType: e.type, DOMEvent: e }); } scroller.grabbedCenter = scroller.hasDragged = scroller.chartX = scroller.chartY = null; }; /** * Position the scrollbar, method called from a parent with defined * dimensions. * * @private * @function Highcharts.Scrollbar#position * @param {number} x * x-position on the chart * @param {number} y * y-position on the chart * @param {number} width * width of the scrollbar * @param {number} height * height of the scorllbar * @return {void} */ Scrollbar.prototype.position = function (x, y, width, height) { var scroller = this, options = scroller.options, vertical = options.vertical, xOffset = height, yOffset = 0, method = scroller.rendered ? 'animate' : 'attr'; scroller.x = x; scroller.y = y + this.trackBorderWidth; scroller.width = width; // width with buttons scroller.height = height; scroller.xOffset = xOffset; scroller.yOffset = yOffset; // If Scrollbar is a vertical type, swap options: if (vertical) { scroller.width = scroller.yOffset = width = yOffset = scroller.size; scroller.xOffset = xOffset = 0; scroller.barWidth = height - width * 2; // width without buttons scroller.x = x = x + scroller.options.margin; } else { scroller.height = scroller.xOffset = height = xOffset = scroller.size; scroller.barWidth = width - height * 2; // width without buttons scroller.y = scroller.y + scroller.options.margin; } // Set general position for a group: scroller.group[method]({ translateX: x, translateY: scroller.y }); // Resize background/track: scroller.track[method]({ width: width, height: height }); // Move right/bottom button ot it's place: scroller.scrollbarButtons[1][method]({ translateX: vertical ? 0 : width - xOffset, translateY: vertical ? height - yOffset : 0 }); }; /** * Removes the event handlers attached previously with addEvents. * * @private * @function Highcharts.Scrollbar#removeEvents * @return {void} */ Scrollbar.prototype.removeEvents = function () { this._events.forEach(function (args) { removeEvent.apply(null, args); }); this._events.length = 0; }; /** * Render scrollbar with all required items. * * @private * @function Highcharts.Scrollbar#render */ Scrollbar.prototype.render = function () { var scroller = this, renderer = scroller.renderer, options = scroller.options, size = scroller.size, styledMode = this.chart.styledMode, group; // Draw the scrollbar group scroller.group = group = renderer.g('scrollbar').attr({ zIndex: options.zIndex, translateY: -99999 }).add(); // Draw the scrollbar track: scroller.track = renderer.rect() .addClass('highcharts-scrollbar-track') .attr({ x: 0, r: options.trackBorderRadius || 0, height: size, width: size }).add(group); if (!styledMode) { scroller.track.attr({ fill: options.trackBackgroundColor, stroke: options.trackBorderColor, 'stroke-width': options.trackBorderWidth }); } this.trackBorderWidth = scroller.track.strokeWidth(); scroller.track.attr({ y: -this.trackBorderWidth % 2 / 2 }); // Draw the scrollbar itself scroller.scrollbarGroup = renderer.g().add(group); scroller.scrollbar = renderer.rect() .addClass('highcharts-scrollbar-thumb') .attr({ height: size, width: size, r: options.barBorderRadius || 0 }).add(scroller.scrollbarGroup); scroller.scrollbarRifles = renderer .path(swapXY([ ['M', -3, size / 4], ['L', -3, 2 * size / 3], ['M', 0, size / 4], ['L', 0, 2 * size / 3], ['M', 3, size / 4], ['L', 3, 2 * size / 3] ], options.vertical)) .addClass('highcharts-scrollbar-rifles') .add(scroller.scrollbarGroup); if (!styledMode) { scroller.scrollbar.attr({ fill: options.barBackgroundColor, stroke: options.barBorderColor, 'stroke-width': options.barBorderWidth }); scroller.scrollbarRifles.attr({ stroke: options.rifleColor, 'stroke-width': 1 }); } scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth(); scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2); // Draw the buttons: scroller.drawScrollbarButton(0); scroller.drawScrollbarButton(1); }; /** * Set scrollbar size, with a given scale. * * @private * @function Highcharts.Scrollbar#setRange * @param {number} from * scale (0-1) where bar should start * @param {number} to * scale (0-1) where bar should end * @return {void} */ Scrollbar.prototype.setRange = function (from, to) { var scroller = this, options = scroller.options, vertical = options.vertical, minWidth = options.minWidth, fullWidth = scroller.barWidth, fromPX, toPX, newPos, newSize, newRiflesPos, method = (this.rendered && !this.hasDragged && !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr'; if (!defined(fullWidth)) { return; } from = Math.max(from, 0); fromPX = Math.ceil(fullWidth * from); toPX = fullWidth * Math.min(to, 1); scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX); // We need to recalculate position, if minWidth is used if (newSize < minWidth) { fromPX = (fullWidth - minWidth + newSize) * from; newSize = minWidth; } newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset); newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2 // Store current position: scroller.from = from; scroller.to = to; if (!vertical) { scroller.scrollbarGroup[method]({ translateX: newPos }); scroller.scrollbar[method]({ width: newSize }); scroller.scrollbarRifles[method]({ translateX: newRiflesPos }); scroller.scrollbarLeft = newPos; scroller.scrollbarTop = 0; } else { scroller.scrollbarGroup[method]({ translateY: newPos }); scroller.scrollbar[method]({ height: newSize }); scroller.scrollbarRifles[method]({ translateY: newRiflesPos }); scroller.scrollbarTop = newPos; scroller.scrollbarLeft = 0; } if (newSize <= 12) { scroller.scrollbarRifles.hide(); } else { scroller.scrollbarRifles.show(true); } // Show or hide the scrollbar based on the showFull setting if (options.showFull === false) { if (from <= 0 && to >= 1) { scroller.group.hide(); } else { scroller.group.show(); } } scroller.rendered = true; }; Scrollbar.prototype.trackClick = function (e) { var scroller = this; var normalizedEvent = scroller.chart.pointer.normalize(e), range = scroller.to - scroller.from, top = scroller.y + scroller.scrollbarTop, left = scroller.x + scroller.scrollbarLeft; if ((scroller.options.vertical && normalizedEvent.chartY > top) || (!scroller.options.vertical && normalizedEvent.chartX > left)) { // On the top or on the left side of the track: scroller.updatePosition(scroller.from + range, scroller.to + range); } else { // On the bottom or the right side of the track: scroller.updatePosition(scroller.from - range, scroller.to - range); } fireEvent(scroller, 'changed', { from: scroller.from, to: scroller.to, trigger: 'scrollbar', DOMEvent: e }); }; /** * Update the scrollbar with new options * * @private * @function Highcharts.Scrollbar#update * @param {Highcharts.ScrollbarOptions} options * @return {void} */ Scrollbar.prototype.update = function (options) { this.destroy(); this.init(this.chart.renderer, merge(true, this.options, options), this.chart); }; /** * Update position option in the Scrollbar, with normalized 0-1 scale * * @private * @function Highcharts.Scrollbar#updatePosition * @param {number} from * @param {number} to * @return {void} */ Scrollbar.prototype.updatePosition = function (from, to) { if (to > 1) { from = correctFloat(1 - correctFloat(to - from)); to = 1; } if (from < 0) { to = correctFloat(to - from); from = 0; } this.from = from; this.to = to; }; /* * * * Static Properties * * */ /** * * The scrollbar is a means of panning over the X axis of a stock chart. * Scrollbars can also be applied to other types of axes. * * Another approach to scrollable charts is the [chart.scrollablePlotArea]( * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that * is especially suitable for simpler cartesian charts on mobile. * * In styled mode, all the presentational options for the * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`, * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`, * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`. * * @sample stock/yaxis/inverted-bar-scrollbar/ * A scrollbar on a simple bar chart * * @product highstock gantt * @optionparent scrollbar * * @private */ Scrollbar.defaultOptions = { /** * The height of the scrollbar. The height also applies to the width * of the scroll arrows so that they are always squares. Defaults to * 20 for touch devices and 14 for mouse devices. * * @sample stock/scrollbar/height/ * A 30px scrollbar * * @type {number} * @default 20/14 */ height: isTouchDevice ? 20 : 14, /** * The border rounding radius of the bar. * * @sample stock/scrollbar/style/ * Scrollbar styling */ barBorderRadius: 0, /** * The corner radius of the scrollbar buttons. * * @sample stock/scrollbar/style/ * Scrollbar styling */ buttonBorderRadius: 0, /** * Enable or disable the scrollbar. * * @sample stock/scrollbar/enabled/ * Disable the scrollbar, only use navigator * * @type {boolean} * @default true * @apioption scrollbar.enabled */ /** * Whether to redraw the main chart as the scrollbar or the navigator * zoomed window is moved. Defaults to `true` for modern browsers and * `false` for legacy IE browsers as well as mobile devices. * * @sample stock/scrollbar/liveredraw * Setting live redraw to false * * @type {boolean} * @since 1.3 */ liveRedraw: void 0, /** * The margin between the scrollbar and its axis when the scrollbar is * applied directly to an axis. */ margin: 10, /** * The minimum width of the scrollbar. * * @since 1.2.5 */ minWidth: 6, /** * Whether to show or hide the scrollbar when the scrolled content is * zoomed out to it full extent. * * @type {boolean} * @default true * @apioption scrollbar.showFull */ step: 0.2, /** * The z index of the scrollbar group. */ zIndex: 3, /** * The background color of the scrollbar itself. * * @sample stock/scrollbar/style/ * Scrollbar styling * * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} */ barBackgroundColor: palette.neutralColor20, /** * The width of the bar's border. * * @sample stock/scrollbar/style/ * Scrollbar styling */ barBorderWidth: 1, /** * The color of the scrollbar's border. * * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.P