xrange.src.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /**
  2. * @license Highcharts JS v6.1.0 (2018-04-13)
  3. * X-range series
  4. *
  5. * (c) 2010-2017 Torstein Honsi, Lars A. V. Cabrera
  6. *
  7. * License: www.highcharts.com/license
  8. */
  9. 'use strict';
  10. (function (factory) {
  11. if (typeof module === 'object' && module.exports) {
  12. module.exports = factory;
  13. } else {
  14. factory(Highcharts);
  15. }
  16. }(function (Highcharts) {
  17. (function (H) {
  18. /**
  19. * X-range series module
  20. *
  21. * (c) 2010-2017 Torstein Honsi, Lars A. V. Cabrera
  22. *
  23. * License: www.highcharts.com/license
  24. */
  25. var addEvent = H.addEvent,
  26. defined = H.defined,
  27. color = H.Color,
  28. columnType = H.seriesTypes.column,
  29. each = H.each,
  30. isNumber = H.isNumber,
  31. isObject = H.isObject,
  32. merge = H.merge,
  33. pick = H.pick,
  34. seriesType = H.seriesType,
  35. seriesTypes = H.seriesTypes,
  36. Axis = H.Axis,
  37. Point = H.Point,
  38. Series = H.Series;
  39. /**
  40. * The X-range series displays ranges on the X axis, typically time intervals
  41. * with a start and end date.
  42. *
  43. * @extends {plotOptions.column}
  44. * @excluding boostThreshold,crisp,cropThreshold,depth,edgeColor,edgeWidth,
  45. * findNearestPointBy,getExtremesFromAll,grouping,groupPadding,
  46. * negativeColor,pointInterval,pointIntervalUnit,pointPlacement,
  47. * pointRange,pointStart,softThreshold,stacking,threshold,data
  48. * @product highcharts highstock
  49. * @sample {highcharts} highcharts/demo/x-range/
  50. * X-range
  51. * @sample {highcharts} highcharts/css/x-range/
  52. * Styled mode X-range
  53. * @sample {highcharts} highcharts/chart/inverted-xrange/
  54. * Inverted X-range
  55. * @since 6.0.0
  56. * @optionparent plotOptions.xrange
  57. */
  58. seriesType('xrange', 'column', {
  59. /**
  60. * A partial fill for each point, typically used to visualize how much of
  61. * a task is performed. The partial fill object can be set either on series
  62. * or point level.
  63. *
  64. * @sample {highcharts} highcharts/demo/x-range
  65. * X-range with partial fill
  66. * @type {Object}
  67. * @product highcharts highstock
  68. * @apioption plotOptions.xrange.partialFill
  69. */
  70. /**
  71. * The fill color to be used for partial fills. Defaults to a darker shade
  72. * of the point color.
  73. *
  74. * @type {Color}
  75. * @product highcharts highstock
  76. * @apioption plotOptions.xrange.partialFill.fill
  77. */
  78. /**
  79. * In an X-range series, this option makes all points of the same Y-axis
  80. * category the same color.
  81. */
  82. colorByPoint: true,
  83. dataLabels: {
  84. verticalAlign: 'middle',
  85. inside: true,
  86. /**
  87. * The default formatter for X-range data labels displays the percentage
  88. * of the partial fill amount.
  89. */
  90. formatter: function () {
  91. var point = this.point,
  92. amount = point.partialFill;
  93. if (isObject(amount)) {
  94. amount = amount.amount;
  95. }
  96. if (!defined(amount)) {
  97. amount = 0;
  98. }
  99. return (amount * 100) + '%';
  100. }
  101. },
  102. tooltip: {
  103. headerFormat: '<span style="font-size: 0.85em">{point.x} - {point.x2}</span><br/>',
  104. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.yCategory}</b><br/>'
  105. },
  106. borderRadius: 3,
  107. pointRange: 0
  108. }, {
  109. type: 'xrange',
  110. parallelArrays: ['x', 'x2', 'y'],
  111. requireSorting: false,
  112. animate: seriesTypes.line.prototype.animate,
  113. cropShoulder: 1,
  114. getExtremesFromAll: true,
  115. /**
  116. * Borrow the column series metrics, but with swapped axes. This gives free
  117. * access to features like groupPadding, grouping, pointWidth etc.
  118. */
  119. getColumnMetrics: function () {
  120. var metrics,
  121. chart = this.chart;
  122. function swapAxes() {
  123. each(chart.series, function (s) {
  124. var xAxis = s.xAxis;
  125. s.xAxis = s.yAxis;
  126. s.yAxis = xAxis;
  127. });
  128. }
  129. swapAxes();
  130. metrics = columnType.prototype.getColumnMetrics.call(this);
  131. swapAxes();
  132. return metrics;
  133. },
  134. /**
  135. * Override cropData to show a point where x or x2 is outside visible range,
  136. * but one of them is inside.
  137. */
  138. cropData: function (xData, yData, min, max) {
  139. // Replace xData with x2Data to find the appropriate cropStart
  140. var cropData = Series.prototype.cropData,
  141. crop = cropData.call(this, this.x2Data, yData, min, max);
  142. // Re-insert the cropped xData
  143. crop.xData = xData.slice(crop.start, crop.end);
  144. return crop;
  145. },
  146. translatePoint: function (point) {
  147. var series = this,
  148. xAxis = series.xAxis,
  149. metrics = series.columnMetrics,
  150. minPointLength = series.options.minPointLength || 0,
  151. plotX = point.plotX,
  152. posX = pick(point.x2, point.x + (point.len || 0)),
  153. plotX2 = xAxis.translate(posX, 0, 0, 0, 1),
  154. length = plotX2 - plotX,
  155. widthDifference,
  156. shapeArgs,
  157. partialFill,
  158. inverted = this.chart.inverted,
  159. borderWidth = pick(series.options.borderWidth, 1),
  160. crisper = borderWidth % 2 / 2,
  161. dlLeft,
  162. dlRight,
  163. dlWidth;
  164. if (minPointLength) {
  165. widthDifference = minPointLength - length;
  166. if (widthDifference < 0) {
  167. widthDifference = 0;
  168. }
  169. plotX -= widthDifference / 2;
  170. plotX2 += widthDifference / 2;
  171. }
  172. plotX = Math.max(plotX, -10);
  173. plotX2 = Math.min(Math.max(plotX2, -10), xAxis.len + 10);
  174. point.shapeArgs = {
  175. x: Math.floor(Math.min(plotX, plotX2)) + crisper,
  176. y: Math.floor(point.plotY + metrics.offset) + crisper,
  177. width: Math.round(Math.abs(plotX2 - plotX)),
  178. height: Math.round(metrics.width),
  179. r: series.options.borderRadius
  180. };
  181. // Align data labels inside the shape and inside the plot area
  182. dlLeft = point.shapeArgs.x;
  183. dlRight = dlLeft + point.shapeArgs.width;
  184. if (dlLeft < 0 || dlRight > xAxis.len) {
  185. dlLeft = Math.min(xAxis.len, Math.max(0, dlLeft));
  186. dlRight = Math.max(0, Math.min(dlRight, xAxis.len));
  187. dlWidth = dlRight - dlLeft;
  188. point.dlBox = merge(point.shapeArgs, {
  189. x: dlLeft,
  190. width: dlRight - dlLeft,
  191. centerX: dlWidth ? dlWidth / 2 : null
  192. });
  193. } else {
  194. point.dlBox = null;
  195. }
  196. // Tooltip position
  197. point.tooltipPos[0] += inverted ? 0 : length / 2;
  198. point.tooltipPos[1] -= inverted ? length / 2 : metrics.width / 2;
  199. // Add a partShapeArgs to the point, based on the shapeArgs property
  200. partialFill = point.partialFill;
  201. if (partialFill) {
  202. // Get the partial fill amount
  203. if (isObject(partialFill)) {
  204. partialFill = partialFill.amount;
  205. }
  206. // If it was not a number, assume 0
  207. if (!isNumber(partialFill)) {
  208. partialFill = 0;
  209. }
  210. shapeArgs = point.shapeArgs;
  211. point.partShapeArgs = {
  212. x: shapeArgs.x,
  213. y: shapeArgs.y,
  214. width: shapeArgs.width,
  215. height: shapeArgs.height,
  216. r: series.options.borderRadius
  217. };
  218. point.clipRectArgs = {
  219. x: shapeArgs.x,
  220. y: shapeArgs.y,
  221. width: Math.max(
  222. Math.round(
  223. length * partialFill +
  224. (point.plotX - plotX)
  225. ),
  226. 0
  227. ),
  228. height: shapeArgs.height
  229. };
  230. }
  231. },
  232. translate: function () {
  233. columnType.prototype.translate.apply(this, arguments);
  234. each(this.points, function (point) {
  235. this.translatePoint(point);
  236. }, this);
  237. },
  238. /**
  239. * Draws a single point in the series. Needed for partial fill.
  240. *
  241. * This override turns point.graphic into a group containing the original
  242. * graphic and an overlay displaying the partial fill.
  243. *
  244. * @param {Object} point an instance of Point in the series
  245. * @param {string} verb 'animate' (animates changes) or 'attr' (sets
  246. * options)
  247. * @returns {void}
  248. */
  249. drawPoint: function (point, verb) {
  250. var series = this,
  251. seriesOpts = series.options,
  252. renderer = series.chart.renderer,
  253. graphic = point.graphic,
  254. type = point.shapeType,
  255. shapeArgs = point.shapeArgs,
  256. partShapeArgs = point.partShapeArgs,
  257. clipRectArgs = point.clipRectArgs,
  258. pfOptions = point.partialFill,
  259. fill,
  260. state = point.selected && 'select',
  261. cutOff = seriesOpts.stacking && !seriesOpts.borderRadius;
  262. if (!point.isNull) {
  263. // Original graphic
  264. if (graphic) { // update
  265. point.graphicOriginal[verb](
  266. merge(shapeArgs)
  267. );
  268. } else {
  269. point.graphic = graphic = renderer.g('point')
  270. .addClass(point.getClassName())
  271. .add(point.group || series.group);
  272. point.graphicOriginal = renderer[type](shapeArgs)
  273. .addClass(point.getClassName())
  274. .addClass('highcharts-partfill-original')
  275. .add(graphic);
  276. }
  277. // Partial fill graphic
  278. if (partShapeArgs) {
  279. if (point.graphicOverlay) {
  280. point.graphicOverlay[verb](
  281. merge(partShapeArgs)
  282. );
  283. point.clipRect.animate(
  284. merge(clipRectArgs)
  285. );
  286. } else {
  287. point.clipRect = renderer.clipRect(
  288. clipRectArgs.x,
  289. clipRectArgs.y,
  290. clipRectArgs.width,
  291. clipRectArgs.height
  292. );
  293. point.graphicOverlay = renderer[type](partShapeArgs)
  294. .addClass('highcharts-partfill-overlay')
  295. .add(graphic)
  296. .clip(point.clipRect);
  297. }
  298. }
  299. // Presentational
  300. point.graphicOriginal
  301. .attr(series.pointAttribs(point, state))
  302. .shadow(seriesOpts.shadow, null, cutOff);
  303. if (partShapeArgs) {
  304. // Ensure pfOptions is an object
  305. if (!isObject(pfOptions)) {
  306. pfOptions = {};
  307. }
  308. if (isObject(seriesOpts.partialFill)) {
  309. pfOptions = merge(pfOptions, seriesOpts.partialFill);
  310. }
  311. fill = (
  312. pfOptions.fill ||
  313. color(point.color || series.color).brighten(-0.3).get()
  314. );
  315. point.graphicOverlay
  316. .attr(series.pointAttribs(point, state))
  317. .attr({
  318. 'fill': fill
  319. })
  320. .shadow(seriesOpts.shadow, null, cutOff);
  321. }
  322. } else if (graphic) {
  323. point.graphic = graphic.destroy(); // #1269
  324. }
  325. },
  326. drawPoints: function () {
  327. var series = this,
  328. verb = series.getAnimationVerb();
  329. // Draw the columns
  330. each(series.points, function (point) {
  331. series.drawPoint(point, verb);
  332. });
  333. },
  334. /**
  335. * Returns "animate", or "attr" if the number of points is above the
  336. * animation limit.
  337. *
  338. * @returns {String}
  339. */
  340. getAnimationVerb: function () {
  341. return this.chart.pointCount < (this.options.animationLimit || 250) ?
  342. 'animate' : 'attr';
  343. }
  344. /**
  345. * Override to remove stroke from points.
  346. * For partial fill.
  347. * /
  348. pointAttribs: function () {
  349. var series = this,
  350. retVal = columnType.prototype.pointAttribs.apply(series, arguments);
  351. //retVal['stroke-width'] = 0;
  352. return retVal;
  353. }
  354. //*/
  355. // Point class properties
  356. }, {
  357. /**
  358. * Extend init so that `colorByPoint` for x-range means that one color is
  359. * applied per Y axis category.
  360. */
  361. init: function () {
  362. Point.prototype.init.apply(this, arguments);
  363. var colors,
  364. series = this.series,
  365. colorCount = series.chart.options.chart.colorCount;
  366. if (!this.y) {
  367. this.y = 0;
  368. }
  369. if (series.options.colorByPoint) {
  370. colors = series.options.colors || series.chart.options.colors;
  371. colorCount = colors.length;
  372. if (!this.options.color && colors[this.y % colorCount]) {
  373. this.color = colors[this.y % colorCount];
  374. }
  375. }
  376. this.colorIndex = pick(this.options.colorIndex, this.y % colorCount);
  377. return this;
  378. },
  379. setState: function () {
  380. Point.prototype.setState.apply(this, arguments);
  381. this.series.drawPoint(this, this.series.getAnimationVerb());
  382. },
  383. // Add x2 and yCategory to the available properties for tooltip formats
  384. getLabelConfig: function () {
  385. var point = this,
  386. cfg = Point.prototype.getLabelConfig.call(point),
  387. yCats = point.series.yAxis.categories;
  388. cfg.x2 = point.x2;
  389. cfg.yCategory = point.yCategory = yCats && yCats[point.y];
  390. return cfg;
  391. },
  392. tooltipDateKeys: ['x', 'x2'],
  393. isValid: function () {
  394. return typeof this.x === 'number' &&
  395. typeof this.x2 === 'number';
  396. }
  397. });
  398. /**
  399. * Max x2 should be considered in xAxis extremes
  400. */
  401. addEvent(Axis, 'afterGetSeriesExtremes', function () {
  402. var axis = this,
  403. axisSeries = axis.series,
  404. dataMax,
  405. modMax;
  406. if (axis.isXAxis) {
  407. dataMax = pick(axis.dataMax, -Number.MAX_VALUE);
  408. each(axisSeries, function (series) {
  409. if (series.x2Data) {
  410. each(series.x2Data, function (val) {
  411. if (val > dataMax) {
  412. dataMax = val;
  413. modMax = true;
  414. }
  415. });
  416. }
  417. });
  418. if (modMax) {
  419. axis.dataMax = dataMax;
  420. }
  421. }
  422. });
  423. /**
  424. * An `xrange` series. If the [type](#series.xrange.type) option is not
  425. * specified, it is inherited from [chart.type](#chart.type).
  426. *
  427. * @type {Object}
  428. * @extends series,plotOptions.xrange
  429. * @excluding boostThreshold,crisp,cropThreshold,depth,edgeColor,edgeWidth,
  430. * findNearestPointBy,getExtremesFromAll,grouping,groupPadding,
  431. * negativeColor,pointInterval,pointIntervalUnit,pointPlacement,
  432. * pointRange,pointStart,softThreshold,stacking,threshold
  433. * @product highcharts highstock
  434. * @apioption series.xrange
  435. */
  436. /**
  437. * An array of data points for the series. For the `xrange` series type,
  438. * points can be given in the following ways:
  439. *
  440. * 1. An array of objects with named values. The objects are point
  441. * configuration objects as seen below.
  442. *
  443. * ```js
  444. * data: [{
  445. * x: Date.UTC(2017, 0, 1),
  446. * x2: Date.UTC(2017, 0, 3),
  447. * name: "Test",
  448. * y: 0,
  449. * color: "#00FF00"
  450. * }, {
  451. * x: Date.UTC(2017, 0, 4),
  452. * x2: Date.UTC(2017, 0, 5),
  453. * name: "Deploy",
  454. * y: 1,
  455. * color: "#FF0000"
  456. * }]
  457. * ```
  458. *
  459. * @type {Array<Object|Array|Number>}
  460. * @extends series.line.data
  461. * @sample {highcharts} highcharts/chart/reflow-true/
  462. * Numerical values
  463. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  464. * Arrays of numeric x and y
  465. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  466. * Arrays of datetime x and y
  467. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  468. * Arrays of point.name and y
  469. * @sample {highcharts} highcharts/series/data-array-of-objects/
  470. * Config objects
  471. * @product highcharts highstock
  472. * @apioption series.xrange.data
  473. */
  474. /**
  475. * The ending X value of the range point.
  476. *
  477. * @sample {highcharts} highcharts/demo/x-range
  478. * X-range
  479. * @type {Number}
  480. * @product highcharts highstock
  481. * @apioption plotOptions.xrange.data.x2
  482. */
  483. /**
  484. * A partial fill for each point, typically used to visualize how much of
  485. * a task is performed. The partial fill object can be set either on series
  486. * or point level.
  487. *
  488. * @sample {highcharts} highcharts/demo/x-range
  489. * X-range with partial fill
  490. * @type {Object|Number}
  491. * @product highcharts highstock
  492. * @apioption plotOptions.xrange.data.partialFill
  493. */
  494. /**
  495. * The amount of the X-range point to be filled. Values can be 0-1 and are
  496. * converted to percentages in the default data label formatter.
  497. *
  498. * @type {Number}
  499. * @product highcharts highstock
  500. * @apioption plotOptions.xrange.data.partialFill.amount
  501. */
  502. /**
  503. * The fill color to be used for partial fills. Defaults to a darker shade
  504. * of the point color.
  505. *
  506. * @type {Color}
  507. * @product highcharts highstock
  508. * @apioption plotOptions.xrange.data.partialFill.fill
  509. */
  510. }(Highcharts));
  511. }));