broken-axis.src.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. /**
  2. * @license Highcharts JS v8.2.0 (2020-08-20)
  3. *
  4. * (c) 2009-2019 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. 'use strict';
  9. (function (factory) {
  10. if (typeof module === 'object' && module.exports) {
  11. factory['default'] = factory;
  12. module.exports = factory;
  13. } else if (typeof define === 'function' && define.amd) {
  14. define('highcharts/modules/broken-axis', ['highcharts'], function (Highcharts) {
  15. factory(Highcharts);
  16. factory.Highcharts = Highcharts;
  17. return factory;
  18. });
  19. } else {
  20. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  21. }
  22. }(function (Highcharts) {
  23. var _modules = Highcharts ? Highcharts._modules : {};
  24. function _registerModule(obj, path, args, fn) {
  25. if (!obj.hasOwnProperty(path)) {
  26. obj[path] = fn.apply(null, args);
  27. }
  28. }
  29. _registerModule(_modules, 'Core/Axis/BrokenAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Extensions/Stacking.js']], function (Axis, H, U, StackItem) {
  30. /* *
  31. *
  32. * (c) 2009-2020 Torstein Honsi
  33. *
  34. * License: www.highcharts.com/license
  35. *
  36. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37. *
  38. * */
  39. var addEvent = U.addEvent,
  40. find = U.find,
  41. fireEvent = U.fireEvent,
  42. isArray = U.isArray,
  43. isNumber = U.isNumber,
  44. pick = U.pick;
  45. var Series = H.Series;
  46. /* eslint-disable valid-jsdoc */
  47. /**
  48. * Provides support for broken axes.
  49. * @private
  50. * @class
  51. */
  52. var BrokenAxisAdditions = /** @class */ (function () {
  53. /* *
  54. *
  55. * Constructors
  56. *
  57. * */
  58. function BrokenAxisAdditions(axis) {
  59. this.hasBreaks = false;
  60. this.axis = axis;
  61. }
  62. /* *
  63. *
  64. * Static Functions
  65. *
  66. * */
  67. /**
  68. * @private
  69. */
  70. BrokenAxisAdditions.isInBreak = function (brk, val) {
  71. var ret,
  72. repeat = brk.repeat || Infinity,
  73. from = brk.from,
  74. length = brk.to - brk.from,
  75. test = (val >= from ?
  76. (val - from) % repeat :
  77. repeat - ((from - val) % repeat));
  78. if (!brk.inclusive) {
  79. ret = test < length && test !== 0;
  80. }
  81. else {
  82. ret = test <= length;
  83. }
  84. return ret;
  85. };
  86. /**
  87. * @private
  88. */
  89. BrokenAxisAdditions.lin2Val = function (val) {
  90. var axis = this;
  91. var brokenAxis = axis.brokenAxis;
  92. var breakArray = brokenAxis && brokenAxis.breakArray;
  93. if (!breakArray) {
  94. return val;
  95. }
  96. var nval = val,
  97. brk,
  98. i;
  99. for (i = 0; i < breakArray.length; i++) {
  100. brk = breakArray[i];
  101. if (brk.from >= nval) {
  102. break;
  103. }
  104. else if (brk.to < nval) {
  105. nval += brk.len;
  106. }
  107. else if (BrokenAxisAdditions.isInBreak(brk, nval)) {
  108. nval += brk.len;
  109. }
  110. }
  111. return nval;
  112. };
  113. /**
  114. * @private
  115. */
  116. BrokenAxisAdditions.val2Lin = function (val) {
  117. var axis = this;
  118. var brokenAxis = axis.brokenAxis;
  119. var breakArray = brokenAxis && brokenAxis.breakArray;
  120. if (!breakArray) {
  121. return val;
  122. }
  123. var nval = val,
  124. brk,
  125. i;
  126. for (i = 0; i < breakArray.length; i++) {
  127. brk = breakArray[i];
  128. if (brk.to <= val) {
  129. nval -= brk.len;
  130. }
  131. else if (brk.from >= val) {
  132. break;
  133. }
  134. else if (BrokenAxisAdditions.isInBreak(brk, val)) {
  135. nval -= (val - brk.from);
  136. break;
  137. }
  138. }
  139. return nval;
  140. };
  141. /* *
  142. *
  143. * Functions
  144. *
  145. * */
  146. /**
  147. * Returns the first break found where the x is larger then break.from and
  148. * smaller then break.to.
  149. *
  150. * @param {number} x
  151. * The number which should be within a break.
  152. *
  153. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  154. * The array of breaks to search within.
  155. *
  156. * @return {Highcharts.XAxisBreaksOptions|undefined}
  157. * Returns the first break found that matches, returns false if no break is
  158. * found.
  159. */
  160. BrokenAxisAdditions.prototype.findBreakAt = function (x, breaks) {
  161. return find(breaks, function (b) {
  162. return b.from < x && x < b.to;
  163. });
  164. };
  165. /**
  166. * @private
  167. */
  168. BrokenAxisAdditions.prototype.isInAnyBreak = function (val, testKeep) {
  169. var brokenAxis = this;
  170. var axis = brokenAxis.axis;
  171. var breaks = axis.options.breaks,
  172. i = breaks && breaks.length,
  173. inbrk,
  174. keep,
  175. ret;
  176. if (i) {
  177. while (i--) {
  178. if (BrokenAxisAdditions.isInBreak(breaks[i], val)) {
  179. inbrk = true;
  180. if (!keep) {
  181. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  182. }
  183. }
  184. }
  185. if (inbrk && testKeep) {
  186. ret = inbrk && !keep;
  187. }
  188. else {
  189. ret = inbrk;
  190. }
  191. }
  192. return ret;
  193. };
  194. /**
  195. * Dynamically set or unset breaks in an axis. This function in lighter than
  196. * usin Axis.update, and it also preserves animation.
  197. *
  198. * @private
  199. * @function Highcharts.Axis#setBreaks
  200. *
  201. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  202. * The breaks to add. When `undefined` it removes existing breaks.
  203. *
  204. * @param {boolean} [redraw=true]
  205. * Whether to redraw the chart immediately.
  206. *
  207. * @return {void}
  208. */
  209. BrokenAxisAdditions.prototype.setBreaks = function (breaks, redraw) {
  210. var brokenAxis = this;
  211. var axis = brokenAxis.axis;
  212. var hasBreaks = (isArray(breaks) && !!breaks.length);
  213. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  214. brokenAxis.hasBreaks = hasBreaks;
  215. axis.options.breaks = axis.userOptions.breaks = breaks;
  216. axis.forceRedraw = true; // Force recalculation in setScale
  217. // Recalculate series related to the axis.
  218. axis.series.forEach(function (series) {
  219. series.isDirty = true;
  220. });
  221. if (!hasBreaks && axis.val2lin === BrokenAxisAdditions.val2Lin) {
  222. // Revert to prototype functions
  223. delete axis.val2lin;
  224. delete axis.lin2val;
  225. }
  226. if (hasBreaks) {
  227. axis.userOptions.ordinal = false;
  228. axis.lin2val = BrokenAxisAdditions.lin2Val;
  229. axis.val2lin = BrokenAxisAdditions.val2Lin;
  230. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  231. // If trying to set extremes inside a break, extend min to
  232. // after, and max to before the break ( #3857 )
  233. if (brokenAxis.hasBreaks) {
  234. var axisBreak,
  235. breaks = this.options.breaks;
  236. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks))) {
  237. newMin = axisBreak.to;
  238. }
  239. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks))) {
  240. newMax = axisBreak.from;
  241. }
  242. // If both min and max is within the same break.
  243. if (newMax < newMin) {
  244. newMax = newMin;
  245. }
  246. }
  247. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  248. };
  249. axis.setAxisTranslation = function (saveOld) {
  250. Axis.prototype.setAxisTranslation.call(this, saveOld);
  251. brokenAxis.unitLength = null;
  252. if (brokenAxis.hasBreaks) {
  253. var breaks = axis.options.breaks || [],
  254. // Temporary one:
  255. breakArrayT = [],
  256. breakArray = [],
  257. length = 0,
  258. inBrk,
  259. repeat,
  260. min = axis.userMin || axis.min,
  261. max = axis.userMax || axis.max,
  262. pointRangePadding = pick(axis.pointRangePadding, 0),
  263. start,
  264. i;
  265. // Min & max check (#4247)
  266. breaks.forEach(function (brk) {
  267. repeat = brk.repeat || Infinity;
  268. if (BrokenAxisAdditions.isInBreak(brk, min)) {
  269. min +=
  270. (brk.to % repeat) -
  271. (min % repeat);
  272. }
  273. if (BrokenAxisAdditions.isInBreak(brk, max)) {
  274. max -=
  275. (max % repeat) -
  276. (brk.from % repeat);
  277. }
  278. });
  279. // Construct an array holding all breaks in the axis
  280. breaks.forEach(function (brk) {
  281. start = brk.from;
  282. repeat = brk.repeat || Infinity;
  283. while (start - repeat > min) {
  284. start -= repeat;
  285. }
  286. while (start < min) {
  287. start += repeat;
  288. }
  289. for (i = start; i < max; i += repeat) {
  290. breakArrayT.push({
  291. value: i,
  292. move: 'in'
  293. });
  294. breakArrayT.push({
  295. value: i + (brk.to - brk.from),
  296. move: 'out',
  297. size: brk.breakSize
  298. });
  299. }
  300. });
  301. breakArrayT.sort(function (a, b) {
  302. return ((a.value === b.value) ?
  303. ((a.move === 'in' ? 0 : 1) -
  304. (b.move === 'in' ? 0 : 1)) :
  305. a.value - b.value);
  306. });
  307. // Simplify the breaks
  308. inBrk = 0;
  309. start = min;
  310. breakArrayT.forEach(function (brk) {
  311. inBrk += (brk.move === 'in' ? 1 : -1);
  312. if (inBrk === 1 && brk.move === 'in') {
  313. start = brk.value;
  314. }
  315. if (inBrk === 0) {
  316. breakArray.push({
  317. from: start,
  318. to: brk.value,
  319. len: brk.value - start - (brk.size || 0)
  320. });
  321. length += brk.value - start - (brk.size || 0);
  322. }
  323. });
  324. /**
  325. * HC <= 8 backwards compatibility, used by demo samples.
  326. * @deprecated
  327. * @private
  328. * @requires modules/broken-axis
  329. */
  330. axis.breakArray = brokenAxis.breakArray = breakArray;
  331. // Used with staticScale, and below the actual axis length,
  332. // when breaks are substracted.
  333. brokenAxis.unitLength = max - min - length + pointRangePadding;
  334. fireEvent(axis, 'afterBreaks');
  335. if (axis.staticScale) {
  336. axis.transA = axis.staticScale;
  337. }
  338. else if (brokenAxis.unitLength) {
  339. axis.transA *=
  340. (max - axis.min + pointRangePadding) /
  341. brokenAxis.unitLength;
  342. }
  343. if (pointRangePadding) {
  344. axis.minPixelPadding =
  345. axis.transA * axis.minPointOffset;
  346. }
  347. axis.min = min;
  348. axis.max = max;
  349. }
  350. };
  351. }
  352. if (pick(redraw, true)) {
  353. axis.chart.redraw();
  354. }
  355. };
  356. return BrokenAxisAdditions;
  357. }());
  358. /**
  359. * Axis with support of broken data rows.
  360. * @private
  361. * @class
  362. */
  363. var BrokenAxis = /** @class */ (function () {
  364. function BrokenAxis() {
  365. }
  366. /**
  367. * Adds support for broken axes.
  368. * @private
  369. */
  370. BrokenAxis.compose = function (AxisClass, SeriesClass) {
  371. AxisClass.keepProps.push('brokenAxis');
  372. var seriesProto = Series.prototype;
  373. /**
  374. * @private
  375. */
  376. seriesProto.drawBreaks = function (axis, keys) {
  377. var series = this,
  378. points = series.points,
  379. breaks,
  380. threshold,
  381. eventName,
  382. y;
  383. if (axis && // #5950
  384. axis.brokenAxis &&
  385. axis.brokenAxis.hasBreaks) {
  386. var brokenAxis_1 = axis.brokenAxis;
  387. keys.forEach(function (key) {
  388. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  389. threshold = axis.isXAxis ?
  390. axis.min :
  391. pick(series.options.threshold, axis.min);
  392. points.forEach(function (point) {
  393. y = pick(point['stack' + key.toUpperCase()], point[key]);
  394. breaks.forEach(function (brk) {
  395. if (isNumber(threshold) && isNumber(y)) {
  396. eventName = false;
  397. if ((threshold < brk.from && y > brk.to) ||
  398. (threshold > brk.from && y < brk.from)) {
  399. eventName = 'pointBreak';
  400. }
  401. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  402. (threshold > brk.from && y > brk.to && y < brk.from)) {
  403. eventName = 'pointInBreak';
  404. }
  405. if (eventName) {
  406. fireEvent(axis, eventName, { point: point, brk: brk });
  407. }
  408. }
  409. });
  410. });
  411. });
  412. }
  413. };
  414. /**
  415. * Extend getGraphPath by identifying gaps in the data so that we can
  416. * draw a gap in the line or area. This was moved from ordinal axis
  417. * module to broken axis module as of #5045.
  418. *
  419. * @private
  420. * @function Highcharts.Series#gappedPath
  421. *
  422. * @return {Highcharts.SVGPathArray}
  423. * Gapped path
  424. */
  425. seriesProto.gappedPath = function () {
  426. var currentDataGrouping = this.currentDataGrouping,
  427. groupingSize = currentDataGrouping && currentDataGrouping.gapSize,
  428. gapSize = this.options.gapSize,
  429. points = this.points.slice(),
  430. i = points.length - 1,
  431. yAxis = this.yAxis,
  432. stack;
  433. /**
  434. * Defines when to display a gap in the graph, together with the
  435. * [gapUnit](plotOptions.series.gapUnit) option.
  436. *
  437. * In case when `dataGrouping` is enabled, points can be grouped
  438. * into a larger time span. This can make the grouped points to have
  439. * a greater distance than the absolute value of `gapSize` property,
  440. * which will result in disappearing graph completely. To prevent
  441. * this situation the mentioned distance between grouped points is
  442. * used instead of previously defined `gapSize`.
  443. *
  444. * In practice, this option is most often used to visualize gaps in
  445. * time series. In a stock chart, intraday data is available for
  446. * daytime hours, while gaps will appear in nights and weekends.
  447. *
  448. * @see [gapUnit](plotOptions.series.gapUnit)
  449. * @see [xAxis.breaks](#xAxis.breaks)
  450. *
  451. * @sample {highstock} stock/plotoptions/series-gapsize/
  452. * Setting the gap size to 2 introduces gaps for weekends
  453. * in daily datasets.
  454. *
  455. * @type {number}
  456. * @default 0
  457. * @product highstock
  458. * @requires modules/broken-axis
  459. * @apioption plotOptions.series.gapSize
  460. */
  461. /**
  462. * Together with [gapSize](plotOptions.series.gapSize), this option
  463. * defines where to draw gaps in the graph.
  464. *
  465. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  466. * means that if the distance between two points is greater than
  467. * 5 times that of the two closest points, the graph will be broken.
  468. *
  469. * When the `gapUnit` is `"value"`, the gap is based on absolute
  470. * axis values, which on a datetime axis is milliseconds. This also
  471. * applies to the navigator series that inherits gap options from
  472. * the base series.
  473. *
  474. * @see [gapSize](plotOptions.series.gapSize)
  475. *
  476. * @type {string}
  477. * @default relative
  478. * @since 5.0.13
  479. * @product highstock
  480. * @validvalue ["relative", "value"]
  481. * @requires modules/broken-axis
  482. * @apioption plotOptions.series.gapUnit
  483. */
  484. if (gapSize && i > 0) { // #5008
  485. // Gap unit is relative
  486. if (this.options.gapUnit !== 'value') {
  487. gapSize *= this.basePointRange;
  488. }
  489. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  490. if (groupingSize &&
  491. groupingSize > gapSize &&
  492. // Except when DG is forced (e.g. from other series)
  493. // and has lower granularity than actual points (#11351)
  494. groupingSize >= this.basePointRange) {
  495. gapSize = groupingSize;
  496. }
  497. // extension for ordinal breaks
  498. var current = void 0,
  499. next = void 0;
  500. while (i--) {
  501. // Reassign next if it is not visible
  502. if (!(next && next.visible !== false)) {
  503. next = points[i + 1];
  504. }
  505. current = points[i];
  506. // Skip iteration if one of the points is not visible
  507. if (next.visible === false || current.visible === false) {
  508. continue;
  509. }
  510. if (next.x - current.x > gapSize) {
  511. var xRange = (current.x + next.x) / 2;
  512. points.splice(// insert after this one
  513. i + 1, 0, {
  514. isNull: true,
  515. x: xRange
  516. });
  517. // For stacked chart generate empty stack items, #6546
  518. if (yAxis.stacking && this.options.stacking) {
  519. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  520. new StackItem(yAxis, yAxis.options
  521. .stackLabels, false, xRange, this.stack);
  522. stack.total = 0;
  523. }
  524. }
  525. // Assign current to next for the upcoming iteration
  526. next = current;
  527. }
  528. }
  529. // Call base method
  530. return this.getGraphPath(points);
  531. };
  532. /* eslint-disable no-invalid-this */
  533. addEvent(AxisClass, 'init', function () {
  534. var axis = this;
  535. if (!axis.brokenAxis) {
  536. axis.brokenAxis = new BrokenAxisAdditions(axis);
  537. }
  538. });
  539. addEvent(AxisClass, 'afterInit', function () {
  540. if (typeof this.brokenAxis !== 'undefined') {
  541. this.brokenAxis.setBreaks(this.options.breaks, false);
  542. }
  543. });
  544. addEvent(AxisClass, 'afterSetTickPositions', function () {
  545. var axis = this;
  546. var brokenAxis = axis.brokenAxis;
  547. if (brokenAxis &&
  548. brokenAxis.hasBreaks) {
  549. var tickPositions = this.tickPositions,
  550. info = this.tickPositions.info,
  551. newPositions = [],
  552. i;
  553. for (i = 0; i < tickPositions.length; i++) {
  554. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  555. newPositions.push(tickPositions[i]);
  556. }
  557. }
  558. this.tickPositions = newPositions;
  559. this.tickPositions.info = info;
  560. }
  561. });
  562. // Force Axis to be not-ordinal when breaks are defined
  563. addEvent(AxisClass, 'afterSetOptions', function () {
  564. if (this.brokenAxis && this.brokenAxis.hasBreaks) {
  565. this.options.ordinal = false;
  566. }
  567. });
  568. addEvent(SeriesClass, 'afterGeneratePoints', function () {
  569. var _a = this,
  570. isDirty = _a.isDirty,
  571. connectNulls = _a.options.connectNulls,
  572. points = _a.points,
  573. xAxis = _a.xAxis,
  574. yAxis = _a.yAxis;
  575. // Set, or reset visibility of the points. Axis.setBreaks marks the
  576. // series as isDirty
  577. if (isDirty) {
  578. var i = points.length;
  579. while (i--) {
  580. var point = points[i];
  581. // Respect nulls inside the break (#4275)
  582. var nullGap = point.y === null && connectNulls === false;
  583. var isPointInBreak = (!nullGap && ((xAxis &&
  584. xAxis.brokenAxis &&
  585. xAxis.brokenAxis.isInAnyBreak(point.x,
  586. true)) || (yAxis &&
  587. yAxis.brokenAxis &&
  588. yAxis.brokenAxis.isInAnyBreak(point.y,
  589. true))));
  590. // Set point.visible if in any break.
  591. // If not in break, reset visible to original value.
  592. point.visible = isPointInBreak ?
  593. false :
  594. point.options.visible !== false;
  595. }
  596. }
  597. });
  598. addEvent(SeriesClass, 'afterRender', function drawPointsWrapped() {
  599. this.drawBreaks(this.xAxis, ['x']);
  600. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  601. });
  602. };
  603. return BrokenAxis;
  604. }());
  605. BrokenAxis.compose(Axis, Series); // @todo remove automatism
  606. return BrokenAxis;
  607. });
  608. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [], function () {
  609. });
  610. }));