123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739 |
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- 'use strict';
- import H from './Globals.js';
- import './Utilities.js';
- import './Series.js';
- import './SvgRenderer.js';
- import onSeriesMixin from '../mixins/on-series.js';
- var addEvent = H.addEvent,
- merge = H.merge,
- noop = H.noop,
- defined = H.defined,
- Renderer = H.Renderer,
- Series = H.Series,
- seriesType = H.seriesType,
- SVGRenderer = H.SVGRenderer,
- TrackerMixin = H.TrackerMixin,
- VMLRenderer = H.VMLRenderer,
- symbols = SVGRenderer.prototype.symbols;
- /**
- * The Flags series.
- *
- * @private
- * @class
- * @name Highcharts.seriesTypes.flags
- *
- * @augments Highcharts.Series
- */
- seriesType(
- 'flags',
- 'column'
- /**
- * Flags are used to mark events in stock charts. They can be added on the
- * timeline, or attached to a specific series.
- *
- * @sample stock/demo/flags-general/
- * Flags on a line series
- *
- * @extends plotOptions.column
- * @excluding animation, borderColor, borderRadius, borderWidth,
- * colorByPoint, dataGrouping, pointPadding, pointWidth,
- * turboThreshold
- * @product highstock
- * @optionparent plotOptions.flags
- */
- , {
- /**
- * In case the flag is placed on a series, on what point key to place
- * it. Line and columns have one key, `y`. In range or OHLC-type series,
- * however, the flag can optionally be placed on the `open`, `high`,
- * `low` or `close` key.
- *
- * @sample {highstock} stock/plotoptions/flags-onkey/
- * Range series, flag on high
- *
- * @type {string}
- * @default y
- * @since 4.2.2
- * @product highstock
- * @validvalue ["y", "open", "high", "low", "close"]
- * @apioption plotOptions.flags.onKey
- */
- /**
- * The id of the series that the flags should be drawn on. If no id
- * is given, the flags are drawn on the x axis.
- *
- * @sample {highstock} stock/plotoptions/flags/
- * Flags on series and on x axis
- *
- * @type {string}
- * @product highstock
- * @apioption plotOptions.flags.onSeries
- */
- pointRange: 0, // #673
- /**
- * Whether the flags are allowed to overlap sideways. If `false`, the
- * flags are moved sideways using an algorithm that seeks to place every
- * flag as close as possible to its original position.
- *
- * @sample {highstock} stock/plotoptions/flags-allowoverlapx
- * Allow sideways overlap
- *
- * @since 6.0.4
- */
- allowOverlapX: false,
- /**
- * The shape of the marker. Can be one of "flag", "circlepin",
- * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
- * Individual shapes can also be set for each point.
- *
- * @sample {highstock} stock/plotoptions/flags/
- * Different shapes
- *
- * @product highstock
- * @validvalue ["flag", "circlepin", "squarepin"]
- */
- shape: 'flag',
- /**
- * When multiple flags in the same series fall on the same value, this
- * number determines the vertical offset between them.
- *
- * @sample {highstock} stock/plotoptions/flags-stackdistance/
- * A greater stack distance
- *
- * @product highstock
- */
- stackDistance: 12,
- /**
- * Text alignment for the text inside the flag.
- *
- * @since 5.0.0
- * @product highstock
- * @validvalue ["left", "center", "right"]
- */
- textAlign: 'center',
- /**
- * Specific tooltip options for flag series. Flag series tooltips are
- * different from most other types in that a flag doesn't have a data
- * value, so the tooltip rather displays the `text` option for each
- * point.
- *
- * @extends plotOptions.series.tooltip
- * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
- * @product highstock
- */
- tooltip: {
- pointFormat: '{point.text}<br/>'
- },
- threshold: null,
- /**
- * The text to display on each flag. This can be defined on series
- * level, or individually for each point. Defaults to `"A"`.
- *
- * @type {string}
- * @default A
- * @product highstock
- * @apioption plotOptions.flags.title
- */
- /**
- * The y position of the top left corner of the flag relative to either
- * the series (if onSeries is defined), or the x axis. Defaults to
- * `-30`.
- *
- * @product highstock
- */
- y: -30,
- /**
- * Whether to use HTML to render the flag texts. Using HTML allows for
- * advanced formatting, images and reliable bi-directional text
- * rendering. Note that exported images won't respect the HTML, and that
- * HTML won't respect Z-index settings.
- *
- * @type {boolean}
- * @default false
- * @since 1.3
- * @product highstock
- * @apioption plotOptions.flags.useHTML
- */
- /**
- * Fixed width of the flag's shape. By default, width is autocalculated
- * according to the flag's title.
- *
- * @sample {highstock} stock/demo/flags-shapes/
- * Flags with fixed width
- *
- * @type {number}
- * @product highstock
- * @apioption plotOptions.flags.width
- */
- /**
- * Fixed height of the flag's shape. By default, height is
- * autocalculated according to the flag's title.
- *
- * @type {number}
- * @product highstock
- * @apioption plotOptions.flags.height
- */
- /**
- * The fill color for the flags.
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @product highstock
- */
- fillColor: '#ffffff',
- /**
- * The color of the line/border of the flag.
- *
- * In styled mode, the stroke is set in the
- * `.highcharts-flag-series.highcharts-point` rule.
- *
- * @type {Highcharts.ColorString}
- * @default #000000
- * @product highstock
- * @apioption plotOptions.flags.lineColor
- */
- /**
- * The pixel width of the flag's line/border.
- *
- * @product highstock
- */
- lineWidth: 1,
- states: {
- /**
- * @extends plotOptions.column.states.hover
- * @product highstock
- */
- hover: {
- /**
- * The color of the line/border of the flag.
- *
- * @type {Highcharts.ColorString}
- * @product highstock
- */
- lineColor: '#000000',
- /**
- * The fill or background color of the flag.
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @product highstock
- */
- fillColor: '#ccd6eb'
- }
- },
- /**
- * The text styles of the flag.
- *
- * In styled mode, the styles are set in the
- * `.highcharts-flag-series .highcharts-point` rule.
- *
- * @type {Highcharts.CSSObject}
- * @default {"fontSize": "11px", "fontWeight": "bold"}
- * @product highstock
- */
- style: {
- /** @ignore-option */
- fontSize: '11px',
- /** @ignore-option */
- fontWeight: 'bold'
- }
- }, /** @lends seriesTypes.flags.prototype */ {
- sorted: false,
- noSharedTooltip: true,
- allowDG: false,
- takeOrdinalPosition: false, // #1074
- trackerGroups: ['markerGroup'],
- forceCrop: true,
- /**
- * Inherit the initialization from base Series.
- *
- * @private
- * @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
- */
- init: Series.prototype.init,
- /**
- * Get presentational attributes
- *
- * @private
- * @function Highcharts.seriesTypes.flags#pointAttribs
- *
- * @param {Highcharts.Point} point
- *
- * @param {string} [state]
- *
- * @return {Highcharts.SVGAttributes}
- */
- pointAttribs: function (point, state) {
- var options = this.options,
- color = (point && point.color) || this.color,
- lineColor = options.lineColor,
- lineWidth = (point && point.lineWidth),
- fill = (point && point.fillColor) || options.fillColor;
- if (state) {
- fill = options.states[state].fillColor;
- lineColor = options.states[state].lineColor;
- lineWidth = options.states[state].lineWidth;
- }
- return {
- 'fill': fill || color,
- 'stroke': lineColor || color,
- 'stroke-width': lineWidth || options.lineWidth || 0
- };
- },
- translate: onSeriesMixin.translate,
- getPlotBox: onSeriesMixin.getPlotBox,
- /**
- * Draw the markers.
- *
- * @private
- * @function Highcharts.seriesTypes.flags#drawPoints
- */
- drawPoints: function () {
- var series = this,
- points = series.points,
- chart = series.chart,
- renderer = chart.renderer,
- plotX,
- plotY,
- inverted = chart.inverted,
- options = series.options,
- optionsY = options.y,
- shape,
- i,
- point,
- graphic,
- stackIndex,
- anchorY,
- attribs,
- outsideRight,
- yAxis = series.yAxis,
- boxesMap = {},
- boxes = [];
- i = points.length;
- while (i--) {
- point = points[i];
- outsideRight =
- (inverted ? point.plotY : point.plotX) > series.xAxis.len;
- plotX = point.plotX;
- stackIndex = point.stackIndex;
- shape = point.options.shape || options.shape;
- plotY = point.plotY;
- if (plotY !== undefined) {
- plotY = point.plotY + optionsY -
- (
- stackIndex !== undefined &&
- stackIndex * options.stackDistance
- );
- }
- // skip connectors for higher level stacked points
- point.anchorX = stackIndex ? undefined : point.plotX;
- anchorY = stackIndex ? undefined : point.plotY;
- graphic = point.graphic;
- // Only draw the point if y is defined and the flag is within
- // the visible area
- if (plotY !== undefined && plotX >= 0 && !outsideRight) {
- // Create the flag
- if (!graphic) {
- graphic = point.graphic = renderer.label(
- '',
- null,
- null,
- shape,
- null,
- null,
- options.useHTML
- );
- if (!chart.styledMode) {
- graphic
- .attr(series.pointAttribs(point))
- .css(merge(options.style, point.style));
- }
- graphic.attr({
- align: shape === 'flag' ? 'left' : 'center',
- width: options.width,
- height: options.height,
- 'text-align': options.textAlign
- })
- .addClass('highcharts-point')
- .add(series.markerGroup);
- // Add reference to the point for tracker (#6303)
- if (point.graphic.div) {
- point.graphic.div.point = point;
- }
- if (!chart.styledMode) {
- graphic.shadow(options.shadow);
- }
- graphic.isNew = true;
- }
- if (plotX > 0) { // #3119
- plotX -= graphic.strokeWidth() % 2; // #4285
- }
- // Plant the flag
- attribs = {
- y: plotY,
- anchorY: anchorY
- };
- if (options.allowOverlapX) {
- attribs.x = plotX;
- attribs.anchorX = point.anchorX;
- }
- graphic.attr({
- text: point.options.title || options.title || 'A'
- })[graphic.isNew ? 'attr' : 'animate'](attribs);
- // Rig for the distribute function
- if (!options.allowOverlapX) {
- if (!boxesMap[point.plotX]) {
- boxesMap[point.plotX] = {
- align: 0,
- size: graphic.width,
- target: plotX,
- anchorX: plotX
- };
- } else {
- boxesMap[point.plotX].size = Math.max(
- boxesMap[point.plotX].size,
- graphic.width
- );
- }
- }
- // Set the tooltip anchor position
- point.tooltipPos = [
- plotX,
- plotY + yAxis.pos - chart.plotTop
- ]; // #6327
- } else if (graphic) {
- point.graphic = graphic.destroy();
- }
- }
- // Handle X-dimension overlapping
- if (!options.allowOverlapX) {
- H.objectEach(boxesMap, function (box) {
- box.plotX = box.anchorX;
- boxes.push(box);
- });
- H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
- points.forEach(function (point) {
- var box = point.graphic && boxesMap[point.plotX];
- if (box) {
- point.graphic[
- point.graphic.isNew ? 'attr' : 'animate'
- ]({
- x: box.pos,
- anchorX: point.anchorX
- });
- // Hide flag when its box position is not specified
- // (#8573, #9299)
- if (!defined(box.pos)) {
- point.graphic.attr({
- x: -9999,
- anchorX: -9999
- });
- point.graphic.isNew = true;
- } else {
- point.graphic.isNew = false;
- }
- }
- });
- }
- // Can be a mix of SVG and HTML and we need events for both (#6303)
- if (options.useHTML) {
- H.wrap(series.markerGroup, 'on', function (proceed) {
- return H.SVGElement.prototype.on.apply(
- // for HTML
- proceed.apply(this, [].slice.call(arguments, 1)),
- // and for SVG
- [].slice.call(arguments, 1)
- );
- });
- }
- },
- /**
- * Extend the column trackers with listeners to expand and contract
- * stacks.
- *
- * @private
- * @function Highcharts.seriesTypes.flags#drawTracker
- */
- drawTracker: function () {
- var series = this,
- points = series.points;
- TrackerMixin.drawTrackerPoint.apply(this);
- /* *
- * Bring each stacked flag up on mouse over, this allows readability
- * of vertically stacked elements as well as tight points on the x
- * axis. #1924.
- */
- points.forEach(function (point) {
- var graphic = point.graphic;
- if (graphic) {
- addEvent(graphic.element, 'mouseover', function () {
- // Raise this point
- if (point.stackIndex > 0 && !point.raised) {
- point._y = graphic.y;
- graphic.attr({
- y: point._y - 8
- });
- point.raised = true;
- }
- // Revert other raised points
- points.forEach(function (otherPoint) {
- if (
- otherPoint !== point &&
- otherPoint.raised &&
- otherPoint.graphic
- ) {
- otherPoint.graphic.attr({
- y: otherPoint._y
- });
- otherPoint.raised = false;
- }
- });
- });
- }
- });
- },
- /**
- * Disable animation, but keep clipping (#8546).
- *
- * @private
- * @function Highcharts.seriesTypes.flags#animate
- *
- * @param {boolean} [init]
- */
- animate: function (init) {
- if (init) {
- this.setClip();
- } else {
- this.animate = null;
- }
- },
- /**
- * @private
- * @function Highcharts.seriesTypes.flags#setClip
- */
- setClip: function () {
- Series.prototype.setClip.apply(this, arguments);
- if (this.options.clip !== false && this.sharedClipKey) {
- this.markerGroup.clip(this.chart[this.sharedClipKey]);
- }
- },
- /**
- * @private
- * @function Highcharts.seriesTypes.flags#buildKDTree
- */
- buildKDTree: noop,
- /**
- * Don't invert the flag marker group (#4960).
- *
- * @private
- * @function Highcharts.seriesTypes.flags#invertGroups
- */
- invertGroups: noop
- }
- );
- // create the flag icon with anchor
- symbols.flag = function (x, y, w, h, options) {
- var anchorX = (options && options.anchorX) || x,
- anchorY = (options && options.anchorY) || y;
- return symbols.circle(anchorX - 1, anchorY - 1, 2, 2).concat(
- [
- 'M', anchorX, anchorY,
- 'L', x, y + h,
- x, y,
- x + w, y,
- x + w, y + h,
- x, y + h,
- 'Z'
- ]
- );
- };
- // Create the circlepin and squarepin icons with anchor
- function createPinSymbol(shape) {
- symbols[shape + 'pin'] = function (x, y, w, h, options) {
- var anchorX = options && options.anchorX,
- anchorY = options && options.anchorY,
- path,
- labelTopOrBottomY;
- // For single-letter flags, make sure circular flags are not taller
- // than their width
- if (shape === 'circle' && h > w) {
- x -= Math.round((h - w) / 2);
- w = h;
- }
- path = symbols[shape](x, y, w, h);
- if (anchorX && anchorY) {
- /**
- * If the label is below the anchor, draw the connecting line
- * from the top edge of the label
- * otherwise start drawing from the bottom edge
- */
- labelTopOrBottomY = (y > anchorY) ? y : y + h;
- path.push(
- 'M',
- shape === 'circle' ? path[1] - path[4] : path[1] + path[4] / 2,
- labelTopOrBottomY,
- 'L',
- anchorX,
- anchorY
- );
- path = path.concat(
- symbols.circle(anchorX - 1, anchorY - 1, 2, 2)
- );
- }
- return path;
- };
- }
- createPinSymbol('circle');
- createPinSymbol('square');
- /**
- * The symbol callbacks are generated on the SVGRenderer object in all browsers.
- * Even VML browsers need this in order to generate shapes in export. Now share
- * them with the VMLRenderer.
- */
- if (Renderer === VMLRenderer) {
- ['flag', 'circlepin', 'squarepin'].forEach(function (shape) {
- VMLRenderer.prototype.symbols[shape] = symbols[shape];
- });
- }
- /**
- * A `flags` series. If the [type](#series.flags.type) option is not
- * specified, it is inherited from [chart.type](#chart.type).
- *
- * @extends series,plotOptions.flags
- * @excluding dataParser, dataURL
- * @product highstock
- * @apioption series.flags
- */
- /**
- * An array of data points for the series. For the `flags` series type,
- * points can be given in the following ways:
- *
- * 1. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.flags.turboThreshold), this option is not
- * available.
- * ```js
- * data: [{
- * x: 1,
- * title: "A",
- * text: "First event"
- * }, {
- * x: 1,
- * title: "B",
- * text: "Second event"
- * }]
- * ```
- *
- * @type {Array<*>}
- * @extends series.line.data
- * @excluding dataLabels, marker, name, y
- * @product highstock
- * @apioption series.flags.data
- */
- /**
- * The fill color of an individual flag. By default it inherits from
- * the series color.
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @product highstock
- * @apioption series.flags.data.fillColor
- */
- /**
- * The longer text to be shown in the flag's tooltip.
- *
- * @type {string}
- * @product highstock
- * @apioption series.flags.data.text
- */
- /**
- * The short text to be shown on the flag.
- *
- * @type {string}
- * @product highstock
- * @apioption series.flags.data.title
- */
|