FlagsSeries.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. /* *
  2. *
  3. * (c) 2010-2019 Torstein Honsi
  4. *
  5. * License: www.highcharts.com/license
  6. *
  7. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8. *
  9. * */
  10. 'use strict';
  11. import H from './Globals.js';
  12. /**
  13. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  14. */
  15. import U from './Utilities.js';
  16. var defined = U.defined, isNumber = U.isNumber, objectEach = U.objectEach, wrap = U.wrap;
  17. import './Series.js';
  18. import './SvgRenderer.js';
  19. import onSeriesMixin from '../mixins/on-series.js';
  20. var addEvent = H.addEvent, merge = H.merge, noop = H.noop, Renderer = H.Renderer, Series = H.Series, seriesType = H.seriesType, SVGRenderer = H.SVGRenderer, TrackerMixin = H.TrackerMixin, VMLRenderer = H.VMLRenderer, symbols = SVGRenderer.prototype.symbols;
  21. /**
  22. * The Flags series.
  23. *
  24. * @private
  25. * @class
  26. * @name Highcharts.seriesTypes.flags
  27. *
  28. * @augments Highcharts.Series
  29. */
  30. seriesType('flags', 'column'
  31. /**
  32. * Flags are used to mark events in stock charts. They can be added on the
  33. * timeline, or attached to a specific series.
  34. *
  35. * @sample stock/demo/flags-general/
  36. * Flags on a line series
  37. *
  38. * @extends plotOptions.column
  39. * @excluding animation, borderColor, borderRadius, borderWidth,
  40. * colorByPoint, dataGrouping, pointPadding, pointWidth,
  41. * turboThreshold
  42. * @product highstock
  43. * @optionparent plotOptions.flags
  44. */
  45. , {
  46. /**
  47. * In case the flag is placed on a series, on what point key to place
  48. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  49. * however, the flag can optionally be placed on the `open`, `high`,
  50. * `low` or `close` key.
  51. *
  52. * @sample {highstock} stock/plotoptions/flags-onkey/
  53. * Range series, flag on high
  54. *
  55. * @type {string}
  56. * @default y
  57. * @since 4.2.2
  58. * @product highstock
  59. * @validvalue ["y", "open", "high", "low", "close"]
  60. * @apioption plotOptions.flags.onKey
  61. */
  62. /**
  63. * The id of the series that the flags should be drawn on. If no id
  64. * is given, the flags are drawn on the x axis.
  65. *
  66. * @sample {highstock} stock/plotoptions/flags/
  67. * Flags on series and on x axis
  68. *
  69. * @type {string}
  70. * @product highstock
  71. * @apioption plotOptions.flags.onSeries
  72. */
  73. pointRange: 0,
  74. /**
  75. * Whether the flags are allowed to overlap sideways. If `false`, the
  76. * flags are moved sideways using an algorithm that seeks to place every
  77. * flag as close as possible to its original position.
  78. *
  79. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  80. * Allow sideways overlap
  81. *
  82. * @since 6.0.4
  83. */
  84. allowOverlapX: false,
  85. /**
  86. * The shape of the marker. Can be one of "flag", "circlepin",
  87. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  88. * Individual shapes can also be set for each point.
  89. *
  90. * @sample {highstock} stock/plotoptions/flags/
  91. * Different shapes
  92. *
  93. * @type {Highcharts.FlagsShapeValue}
  94. * @product highstock
  95. */
  96. shape: 'flag',
  97. /**
  98. * When multiple flags in the same series fall on the same value, this
  99. * number determines the vertical offset between them.
  100. *
  101. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  102. * A greater stack distance
  103. *
  104. * @product highstock
  105. */
  106. stackDistance: 12,
  107. /**
  108. * Text alignment for the text inside the flag.
  109. *
  110. * @since 5.0.0
  111. * @product highstock
  112. * @validvalue ["left", "center", "right"]
  113. */
  114. textAlign: 'center',
  115. /**
  116. * Specific tooltip options for flag series. Flag series tooltips are
  117. * different from most other types in that a flag doesn't have a data
  118. * value, so the tooltip rather displays the `text` option for each
  119. * point.
  120. *
  121. * @extends plotOptions.series.tooltip
  122. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  123. * @product highstock
  124. */
  125. tooltip: {
  126. pointFormat: '{point.text}<br/>'
  127. },
  128. threshold: null,
  129. /**
  130. * The text to display on each flag. This can be defined on series
  131. * level, or individually for each point. Defaults to `"A"`.
  132. *
  133. * @type {string}
  134. * @default A
  135. * @product highstock
  136. * @apioption plotOptions.flags.title
  137. */
  138. /**
  139. * The y position of the top left corner of the flag relative to either
  140. * the series (if onSeries is defined), or the x axis. Defaults to
  141. * `-30`.
  142. *
  143. * @product highstock
  144. */
  145. y: -30,
  146. /**
  147. * Whether to use HTML to render the flag texts. Using HTML allows for
  148. * advanced formatting, images and reliable bi-directional text
  149. * rendering. Note that exported images won't respect the HTML, and that
  150. * HTML won't respect Z-index settings.
  151. *
  152. * @type {boolean}
  153. * @default false
  154. * @since 1.3
  155. * @product highstock
  156. * @apioption plotOptions.flags.useHTML
  157. */
  158. /**
  159. * Fixed width of the flag's shape. By default, width is autocalculated
  160. * according to the flag's title.
  161. *
  162. * @sample {highstock} stock/demo/flags-shapes/
  163. * Flags with fixed width
  164. *
  165. * @type {number}
  166. * @product highstock
  167. * @apioption plotOptions.flags.width
  168. */
  169. /**
  170. * Fixed height of the flag's shape. By default, height is
  171. * autocalculated according to the flag's title.
  172. *
  173. * @type {number}
  174. * @product highstock
  175. * @apioption plotOptions.flags.height
  176. */
  177. /**
  178. * The fill color for the flags.
  179. *
  180. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  181. * @product highstock
  182. */
  183. fillColor: '#ffffff',
  184. /**
  185. * The color of the line/border of the flag.
  186. *
  187. * In styled mode, the stroke is set in the
  188. * `.highcharts-flag-series.highcharts-point` rule.
  189. *
  190. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  191. * @default #000000
  192. * @product highstock
  193. * @apioption plotOptions.flags.lineColor
  194. */
  195. /**
  196. * The pixel width of the flag's line/border.
  197. *
  198. * @product highstock
  199. */
  200. lineWidth: 1,
  201. states: {
  202. /**
  203. * @extends plotOptions.column.states.hover
  204. * @product highstock
  205. */
  206. hover: {
  207. /**
  208. * The color of the line/border of the flag.
  209. *
  210. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  211. * @product highstock
  212. */
  213. lineColor: '#000000',
  214. /**
  215. * The fill or background color of the flag.
  216. *
  217. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  218. * @product highstock
  219. */
  220. fillColor: '#ccd6eb'
  221. }
  222. },
  223. /**
  224. * The text styles of the flag.
  225. *
  226. * In styled mode, the styles are set in the
  227. * `.highcharts-flag-series .highcharts-point` rule.
  228. *
  229. * @type {Highcharts.CSSObject}
  230. * @default {"fontSize": "11px", "fontWeight": "bold"}
  231. * @product highstock
  232. */
  233. style: {
  234. /** @ignore-option */
  235. fontSize: '11px',
  236. /** @ignore-option */
  237. fontWeight: 'bold'
  238. }
  239. },
  240. /**
  241. * @lends seriesTypes.flags.prototype
  242. */
  243. {
  244. sorted: false,
  245. noSharedTooltip: true,
  246. allowDG: false,
  247. takeOrdinalPosition: false,
  248. trackerGroups: ['markerGroup'],
  249. forceCrop: true,
  250. /* eslint-disable no-invalid-this, valid-jsdoc */
  251. /**
  252. * Inherit the initialization from base Series.
  253. *
  254. * @private
  255. * @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
  256. */
  257. init: Series.prototype.init,
  258. /**
  259. * Get presentational attributes
  260. *
  261. * @private
  262. * @function Highcharts.seriesTypes.flags#pointAttribs
  263. *
  264. * @param {Highcharts.Point} point
  265. *
  266. * @param {string} [state]
  267. *
  268. * @return {Highcharts.SVGAttributes}
  269. */
  270. pointAttribs: function (point, state) {
  271. var options = this.options, color = (point && point.color) || this.color, lineColor = options.lineColor, lineWidth = (point && point.lineWidth), fill = (point && point.fillColor) || options.fillColor;
  272. if (state) {
  273. fill = options.states[state].fillColor;
  274. lineColor = options.states[state].lineColor;
  275. lineWidth = options.states[state].lineWidth;
  276. }
  277. return {
  278. fill: fill || color,
  279. stroke: lineColor || color,
  280. 'stroke-width': lineWidth || options.lineWidth || 0
  281. };
  282. },
  283. translate: onSeriesMixin.translate,
  284. getPlotBox: onSeriesMixin.getPlotBox,
  285. /**
  286. * Draw the markers.
  287. *
  288. * @private
  289. * @function Highcharts.seriesTypes.flags#drawPoints
  290. * @return {void}
  291. */
  292. drawPoints: function () {
  293. 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 = [], centered;
  294. i = points.length;
  295. while (i--) {
  296. point = points[i];
  297. outsideRight =
  298. (inverted ? point.plotY : point.plotX) >
  299. series.xAxis.len;
  300. plotX = point.plotX;
  301. stackIndex = point.stackIndex;
  302. shape = point.options.shape || options.shape;
  303. plotY = point.plotY;
  304. if (typeof plotY !== 'undefined') {
  305. plotY = point.plotY + optionsY -
  306. (typeof stackIndex !== 'undefined' &&
  307. (stackIndex * options.stackDistance));
  308. }
  309. // skip connectors for higher level stacked points
  310. point.anchorX = stackIndex ? void 0 : point.plotX;
  311. anchorY = stackIndex ? void 0 : point.plotY;
  312. centered = shape !== 'flag';
  313. graphic = point.graphic;
  314. // Only draw the point if y is defined and the flag is within
  315. // the visible area
  316. if (typeof plotY !== 'undefined' &&
  317. plotX >= 0 &&
  318. !outsideRight) {
  319. // Create the flag
  320. if (!graphic) {
  321. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML);
  322. if (!chart.styledMode) {
  323. graphic
  324. .attr(series.pointAttribs(point))
  325. .css(merge(options.style, point.style));
  326. }
  327. graphic.attr({
  328. align: centered ? 'center' : 'left',
  329. width: options.width,
  330. height: options.height,
  331. 'text-align': options.textAlign
  332. })
  333. .addClass('highcharts-point')
  334. .add(series.markerGroup);
  335. // Add reference to the point for tracker (#6303)
  336. if (point.graphic.div) {
  337. point.graphic.div.point = point;
  338. }
  339. if (!chart.styledMode) {
  340. graphic.shadow(options.shadow);
  341. }
  342. graphic.isNew = true;
  343. }
  344. if (plotX > 0) { // #3119
  345. plotX -= graphic.strokeWidth() % 2; // #4285
  346. }
  347. // Plant the flag
  348. attribs = {
  349. y: plotY,
  350. anchorY: anchorY
  351. };
  352. if (options.allowOverlapX) {
  353. attribs.x = plotX;
  354. attribs.anchorX = point.anchorX;
  355. }
  356. graphic.attr({
  357. text: point.options.title || options.title || 'A'
  358. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  359. // Rig for the distribute function
  360. if (!options.allowOverlapX) {
  361. if (!boxesMap[point.plotX]) {
  362. boxesMap[point.plotX] = {
  363. align: centered ? 0.5 : 0,
  364. size: graphic.width,
  365. target: plotX,
  366. anchorX: plotX
  367. };
  368. }
  369. else {
  370. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  371. }
  372. }
  373. // Set the tooltip anchor position
  374. point.tooltipPos = [
  375. plotX,
  376. plotY + yAxis.pos - chart.plotTop
  377. ]; // #6327
  378. }
  379. else if (graphic) {
  380. point.graphic = graphic.destroy();
  381. }
  382. }
  383. // Handle X-dimension overlapping
  384. if (!options.allowOverlapX) {
  385. objectEach(boxesMap, function (box) {
  386. box.plotX = box.anchorX;
  387. boxes.push(box);
  388. });
  389. H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
  390. points.forEach(function (point) {
  391. var box = point.graphic && boxesMap[point.plotX];
  392. if (box) {
  393. point.graphic[point.graphic.isNew ? 'attr' : 'animate']({
  394. x: box.pos + box.align * box.size,
  395. anchorX: point.anchorX
  396. });
  397. // Hide flag when its box position is not specified
  398. // (#8573, #9299)
  399. if (!defined(box.pos)) {
  400. point.graphic.attr({
  401. x: -9999,
  402. anchorX: -9999
  403. });
  404. point.graphic.isNew = true;
  405. }
  406. else {
  407. point.graphic.isNew = false;
  408. }
  409. }
  410. });
  411. }
  412. // Can be a mix of SVG and HTML and we need events for both (#6303)
  413. if (options.useHTML) {
  414. wrap(series.markerGroup, 'on', function (proceed) {
  415. return H.SVGElement.prototype.on.apply(
  416. // for HTML
  417. proceed.apply(this, [].slice.call(arguments, 1)),
  418. // and for SVG
  419. [].slice.call(arguments, 1));
  420. });
  421. }
  422. },
  423. /**
  424. * Extend the column trackers with listeners to expand and contract
  425. * stacks.
  426. *
  427. * @private
  428. * @function Highcharts.seriesTypes.flags#drawTracker
  429. * @return {void}
  430. */
  431. drawTracker: function () {
  432. var series = this, points = series.points;
  433. TrackerMixin.drawTrackerPoint.apply(this);
  434. /* *
  435. * Bring each stacked flag up on mouse over, this allows readability
  436. * of vertically stacked elements as well as tight points on the x
  437. * axis. #1924.
  438. */
  439. points.forEach(function (point) {
  440. var graphic = point.graphic;
  441. if (graphic) {
  442. addEvent(graphic.element, 'mouseover', function () {
  443. // Raise this point
  444. if (point.stackIndex > 0 &&
  445. !point.raised) {
  446. point._y = graphic.y;
  447. graphic.attr({
  448. y: point._y - 8
  449. });
  450. point.raised = true;
  451. }
  452. // Revert other raised points
  453. points.forEach(function (otherPoint) {
  454. if (otherPoint !== point &&
  455. otherPoint.raised &&
  456. otherPoint.graphic) {
  457. otherPoint.graphic.attr({
  458. y: otherPoint._y
  459. });
  460. otherPoint.raised = false;
  461. }
  462. });
  463. });
  464. }
  465. });
  466. },
  467. /**
  468. * Disable animation, but keep clipping (#8546).
  469. *
  470. * @private
  471. * @function Highcharts.seriesTypes.flags#animate
  472. * @param {boolean} [init]
  473. * @return {void}
  474. */
  475. animate: function (init) {
  476. if (init) {
  477. this.setClip();
  478. }
  479. else {
  480. this.animate = null;
  481. }
  482. },
  483. /**
  484. * @private
  485. * @function Highcharts.seriesTypes.flags#setClip
  486. * @return {void}
  487. */
  488. setClip: function () {
  489. Series.prototype.setClip.apply(this, arguments);
  490. if (this.options.clip !== false && this.sharedClipKey) {
  491. this.markerGroup
  492. .clip(this.chart[this.sharedClipKey]);
  493. }
  494. },
  495. /**
  496. * @private
  497. * @function Highcharts.seriesTypes.flags#buildKDTree
  498. */
  499. buildKDTree: noop,
  500. /**
  501. * Don't invert the flag marker group (#4960).
  502. *
  503. * @private
  504. * @function Highcharts.seriesTypes.flags#invertGroups
  505. */
  506. invertGroups: noop
  507. /* eslint-enable no-invalid-this, valid-jsdoc */
  508. },
  509. /**
  510. * @lends Highcharts.seriesTypes.flag.prototype.pointClass.prototype
  511. */
  512. {
  513. isValid: function () {
  514. // #9233 - Prevent from treating flags as null points (even if
  515. // they have no y values defined).
  516. return isNumber(this.y) || typeof this.y === 'undefined';
  517. }
  518. });
  519. // create the flag icon with anchor
  520. symbols.flag = function (x, y, w, h, options) {
  521. var anchorX = (options && options.anchorX) || x, anchorY = (options && options.anchorY) || y;
  522. return symbols.circle(anchorX - 1, anchorY - 1, 2, 2).concat([
  523. 'M', anchorX, anchorY,
  524. 'L', x, y + h,
  525. x, y,
  526. x + w, y,
  527. x + w, y + h,
  528. x, y + h,
  529. 'Z'
  530. ]);
  531. };
  532. /**
  533. * Create the circlepin and squarepin icons with anchor.
  534. * @private
  535. * @param {string} shape - circle or square
  536. * @return {void}
  537. */
  538. function createPinSymbol(shape) {
  539. symbols[shape + 'pin'] = function (x, y, w, h, options) {
  540. var anchorX = options && options.anchorX, anchorY = options && options.anchorY, path, labelTopOrBottomY;
  541. // For single-letter flags, make sure circular flags are not taller
  542. // than their width
  543. if (shape === 'circle' && h > w) {
  544. x -= Math.round((h - w) / 2);
  545. w = h;
  546. }
  547. path = symbols[shape](x, y, w, h);
  548. if (anchorX && anchorY) {
  549. /**
  550. * If the label is below the anchor, draw the connecting line
  551. * from the top edge of the label
  552. * otherwise start drawing from the bottom edge
  553. */
  554. labelTopOrBottomY = (y > anchorY) ? y : y + h;
  555. path.push('M', shape === 'circle' ?
  556. x + w / 2 :
  557. path[1] + path[4] / 2, labelTopOrBottomY, 'L', anchorX, anchorY);
  558. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  559. }
  560. return path;
  561. };
  562. }
  563. createPinSymbol('circle');
  564. createPinSymbol('square');
  565. /**
  566. * The symbol callbacks are generated on the SVGRenderer object in all browsers.
  567. * Even VML browsers need this in order to generate shapes in export. Now share
  568. * them with the VMLRenderer.
  569. */
  570. if (Renderer === VMLRenderer) {
  571. ['circlepin', 'flag', 'squarepin'].forEach(function (shape) {
  572. VMLRenderer.prototype.symbols[shape] = symbols[shape];
  573. });
  574. }
  575. /**
  576. * A `flags` series. If the [type](#series.flags.type) option is not
  577. * specified, it is inherited from [chart.type](#chart.type).
  578. *
  579. * @extends series,plotOptions.flags
  580. * @excluding dataParser, dataURL
  581. * @product highstock
  582. * @apioption series.flags
  583. */
  584. /**
  585. * An array of data points for the series. For the `flags` series type,
  586. * points can be given in the following ways:
  587. *
  588. * 1. An array of objects with named values. The following snippet shows only a
  589. * few settings, see the complete options set below. If the total number of
  590. * data points exceeds the series'
  591. * [turboThreshold](#series.flags.turboThreshold), this option is not
  592. * available.
  593. * ```js
  594. * data: [{
  595. * x: 1,
  596. * title: "A",
  597. * text: "First event"
  598. * }, {
  599. * x: 1,
  600. * title: "B",
  601. * text: "Second event"
  602. * }]
  603. * ```
  604. *
  605. * @type {Array<*>}
  606. * @extends series.line.data
  607. * @excluding dataLabels, marker, name, y
  608. * @product highstock
  609. * @apioption series.flags.data
  610. */
  611. /**
  612. * The fill color of an individual flag. By default it inherits from
  613. * the series color.
  614. *
  615. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  616. * @product highstock
  617. * @apioption series.flags.data.fillColor
  618. */
  619. /**
  620. * The longer text to be shown in the flag's tooltip.
  621. *
  622. * @type {string}
  623. * @product highstock
  624. * @apioption series.flags.data.text
  625. */
  626. /**
  627. * The short text to be shown on the flag.
  628. *
  629. * @type {string}
  630. * @product highstock
  631. * @apioption series.flags.data.title
  632. */
  633. ''; // adds doclets above to transpiled file