broken-axis.src.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /**
  2. * @license Highcharts JS v7.0.2 (2019-01-17)
  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(function () {
  15. return factory;
  16. });
  17. } else {
  18. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  19. }
  20. }(function (Highcharts) {
  21. (function (H) {
  22. /**
  23. * (c) 2009-2019 Torstein Honsi
  24. *
  25. * License: www.highcharts.com/license
  26. */
  27. var addEvent = H.addEvent,
  28. pick = H.pick,
  29. extend = H.extend,
  30. isArray = H.isArray,
  31. fireEvent = H.fireEvent,
  32. Axis = H.Axis,
  33. Series = H.Series;
  34. extend(Axis.prototype, {
  35. isInBreak: function (brk, val) {
  36. var ret,
  37. repeat = brk.repeat || Infinity,
  38. from = brk.from,
  39. length = brk.to - brk.from,
  40. test = (
  41. val >= from ?
  42. (val - from) % repeat :
  43. repeat - ((from - val) % repeat)
  44. );
  45. if (!brk.inclusive) {
  46. ret = test < length && test !== 0;
  47. } else {
  48. ret = test <= length;
  49. }
  50. return ret;
  51. },
  52. isInAnyBreak: function (val, testKeep) {
  53. var breaks = this.options.breaks,
  54. i = breaks && breaks.length,
  55. inbrk,
  56. keep,
  57. ret;
  58. if (i) {
  59. while (i--) {
  60. if (this.isInBreak(breaks[i], val)) {
  61. inbrk = true;
  62. if (!keep) {
  63. keep = pick(
  64. breaks[i].showPoints,
  65. !this.isXAxis
  66. );
  67. }
  68. }
  69. }
  70. if (inbrk && testKeep) {
  71. ret = inbrk && !keep;
  72. } else {
  73. ret = inbrk;
  74. }
  75. }
  76. return ret;
  77. }
  78. });
  79. addEvent(Axis, 'afterInit', function () {
  80. if (typeof this.setBreaks === 'function') {
  81. this.setBreaks(this.options.breaks, false);
  82. }
  83. });
  84. addEvent(Axis, 'afterSetTickPositions', function () {
  85. if (this.isBroken) {
  86. var axis = this,
  87. tickPositions = this.tickPositions,
  88. info = this.tickPositions.info,
  89. newPositions = [],
  90. i;
  91. for (i = 0; i < tickPositions.length; i++) {
  92. if (!axis.isInAnyBreak(tickPositions[i])) {
  93. newPositions.push(tickPositions[i]);
  94. }
  95. }
  96. this.tickPositions = newPositions;
  97. this.tickPositions.info = info;
  98. }
  99. });
  100. // Force Axis to be not-ordinal when breaks are defined
  101. addEvent(Axis, 'afterSetOptions', function () {
  102. if (this.isBroken) {
  103. this.options.ordinal = false;
  104. }
  105. });
  106. /**
  107. * Dynamically set or unset breaks in an axis. This function in lighter than
  108. * usin Axis.update, and it also preserves animation.
  109. *
  110. * @private
  111. * @function Highcharts.Axis#setBreaks
  112. *
  113. * @param {Array<*>} [breaks]
  114. * The breaks to add. When `undefined` it removes existing breaks.
  115. *
  116. * @param {boolean} [redraw=true]
  117. * Whether to redraw the chart immediately.
  118. */
  119. Axis.prototype.setBreaks = function (breaks, redraw) {
  120. var axis = this,
  121. isBroken = (isArray(breaks) && !!breaks.length);
  122. function breakVal2Lin(val) {
  123. var nval = val,
  124. brk,
  125. i;
  126. for (i = 0; i < axis.breakArray.length; i++) {
  127. brk = axis.breakArray[i];
  128. if (brk.to <= val) {
  129. nval -= brk.len;
  130. } else if (brk.from >= val) {
  131. break;
  132. } else if (axis.isInBreak(brk, val)) {
  133. nval -= (val - brk.from);
  134. break;
  135. }
  136. }
  137. return nval;
  138. }
  139. function breakLin2Val(val) {
  140. var nval = val,
  141. brk,
  142. i;
  143. for (i = 0; i < axis.breakArray.length; i++) {
  144. brk = axis.breakArray[i];
  145. if (brk.from >= nval) {
  146. break;
  147. } else if (brk.to < nval) {
  148. nval += brk.len;
  149. } else if (axis.isInBreak(brk, nval)) {
  150. nval += brk.len;
  151. }
  152. }
  153. return nval;
  154. }
  155. axis.isDirty = axis.isBroken !== isBroken;
  156. axis.isBroken = isBroken;
  157. axis.options.breaks = axis.userOptions.breaks = breaks;
  158. axis.forceRedraw = true; // Force recalculation in setScale
  159. if (!isBroken && axis.val2lin === breakVal2Lin) {
  160. // Revert to prototype functions
  161. delete axis.val2lin;
  162. delete axis.lin2val;
  163. }
  164. if (isBroken) {
  165. axis.userOptions.ordinal = false;
  166. axis.val2lin = breakVal2Lin;
  167. axis.lin2val = breakLin2Val;
  168. axis.setExtremes = function (
  169. newMin,
  170. newMax,
  171. redraw,
  172. animation,
  173. eventArguments
  174. ) {
  175. // If trying to set extremes inside a break, extend it to before and
  176. // after the break ( #3857 )
  177. if (this.isBroken) {
  178. while (this.isInAnyBreak(newMin)) {
  179. newMin -= this.closestPointRange;
  180. }
  181. while (this.isInAnyBreak(newMax)) {
  182. newMax -= this.closestPointRange;
  183. }
  184. }
  185. Axis.prototype.setExtremes.call(
  186. this,
  187. newMin,
  188. newMax,
  189. redraw,
  190. animation,
  191. eventArguments
  192. );
  193. };
  194. axis.setAxisTranslation = function (saveOld) {
  195. Axis.prototype.setAxisTranslation.call(this, saveOld);
  196. this.unitLength = null;
  197. if (this.isBroken) {
  198. var breaks = axis.options.breaks,
  199. breakArrayT = [], // Temporary one
  200. breakArray = [],
  201. length = 0,
  202. inBrk,
  203. repeat,
  204. min = axis.userMin || axis.min,
  205. max = axis.userMax || axis.max,
  206. pointRangePadding = pick(axis.pointRangePadding, 0),
  207. start,
  208. i;
  209. // Min & max check (#4247)
  210. breaks.forEach(function (brk) {
  211. repeat = brk.repeat || Infinity;
  212. if (axis.isInBreak(brk, min)) {
  213. min += (brk.to % repeat) - (min % repeat);
  214. }
  215. if (axis.isInBreak(brk, max)) {
  216. max -= (max % repeat) - (brk.from % repeat);
  217. }
  218. });
  219. // Construct an array holding all breaks in the axis
  220. breaks.forEach(function (brk) {
  221. start = brk.from;
  222. repeat = brk.repeat || Infinity;
  223. while (start - repeat > min) {
  224. start -= repeat;
  225. }
  226. while (start < min) {
  227. start += repeat;
  228. }
  229. for (i = start; i < max; i += repeat) {
  230. breakArrayT.push({
  231. value: i,
  232. move: 'in'
  233. });
  234. breakArrayT.push({
  235. value: i + (brk.to - brk.from),
  236. move: 'out',
  237. size: brk.breakSize
  238. });
  239. }
  240. });
  241. breakArrayT.sort(function (a, b) {
  242. return (
  243. (a.value === b.value) ?
  244. (
  245. (a.move === 'in' ? 0 : 1) -
  246. (b.move === 'in' ? 0 : 1)
  247. ) :
  248. a.value - b.value
  249. );
  250. });
  251. // Simplify the breaks
  252. inBrk = 0;
  253. start = min;
  254. breakArrayT.forEach(function (brk) {
  255. inBrk += (brk.move === 'in' ? 1 : -1);
  256. if (inBrk === 1 && brk.move === 'in') {
  257. start = brk.value;
  258. }
  259. if (inBrk === 0) {
  260. breakArray.push({
  261. from: start,
  262. to: brk.value,
  263. len: brk.value - start - (brk.size || 0)
  264. });
  265. length += brk.value - start - (brk.size || 0);
  266. }
  267. });
  268. axis.breakArray = breakArray;
  269. // Used with staticScale, and below, the actual axis length when
  270. // breaks are substracted.
  271. axis.unitLength = max - min - length + pointRangePadding;
  272. fireEvent(axis, 'afterBreaks');
  273. if (axis.staticScale) {
  274. axis.transA = axis.staticScale;
  275. } else if (axis.unitLength) {
  276. axis.transA *= (max - axis.min + pointRangePadding) /
  277. axis.unitLength;
  278. }
  279. if (pointRangePadding) {
  280. axis.minPixelPadding = axis.transA * axis.minPointOffset;
  281. }
  282. axis.min = min;
  283. axis.max = max;
  284. }
  285. };
  286. }
  287. if (pick(redraw, true)) {
  288. this.chart.redraw();
  289. }
  290. };
  291. addEvent(Series, 'afterGeneratePoints', function () {
  292. var series = this,
  293. xAxis = series.xAxis,
  294. yAxis = series.yAxis,
  295. points = series.points,
  296. point,
  297. i = points.length,
  298. connectNulls = series.options.connectNulls,
  299. nullGap;
  300. if (xAxis && yAxis && (xAxis.options.breaks || yAxis.options.breaks)) {
  301. while (i--) {
  302. point = points[i];
  303. // Respect nulls inside the break (#4275)
  304. nullGap = point.y === null && connectNulls === false;
  305. if (
  306. !nullGap &&
  307. (
  308. xAxis.isInAnyBreak(point.x, true) ||
  309. yAxis.isInAnyBreak(point.y, true)
  310. )
  311. ) {
  312. points.splice(i, 1);
  313. if (this.data[i]) {
  314. // Removes the graphics for this point if they exist
  315. this.data[i].destroyElements();
  316. }
  317. }
  318. }
  319. }
  320. });
  321. addEvent(Series, 'afterRender', function drawPointsWrapped() {
  322. this.drawBreaks(this.xAxis, ['x']);
  323. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  324. });
  325. H.Series.prototype.drawBreaks = function (axis, keys) {
  326. var series = this,
  327. points = series.points,
  328. breaks,
  329. threshold,
  330. eventName,
  331. y;
  332. if (!axis) {
  333. return; // #5950
  334. }
  335. keys.forEach(function (key) {
  336. breaks = axis.breakArray || [];
  337. threshold = axis.isXAxis ?
  338. axis.min :
  339. pick(series.options.threshold, axis.min);
  340. points.forEach(function (point) {
  341. y = pick(point['stack' + key.toUpperCase()], point[key]);
  342. breaks.forEach(function (brk) {
  343. eventName = false;
  344. if (
  345. (threshold < brk.from && y > brk.to) ||
  346. (threshold > brk.from && y < brk.from)
  347. ) {
  348. eventName = 'pointBreak';
  349. } else if (
  350. (threshold < brk.from && y > brk.from && y < brk.to) ||
  351. (threshold > brk.from && y > brk.to && y < brk.from)
  352. ) {
  353. eventName = 'pointInBreak';
  354. }
  355. if (eventName) {
  356. fireEvent(axis, eventName, { point: point, brk: brk });
  357. }
  358. });
  359. });
  360. });
  361. };
  362. /**
  363. * Extend getGraphPath by identifying gaps in the data so that we can draw a gap
  364. * in the line or area. This was moved from ordinal axis module to broken axis
  365. * module as of #5045.
  366. *
  367. * @private
  368. * @function Highcharts.Series#gappedPath
  369. */
  370. H.Series.prototype.gappedPath = function () {
  371. var currentDataGrouping = this.currentDataGrouping,
  372. groupingSize = currentDataGrouping && currentDataGrouping.totalRange,
  373. gapSize = this.options.gapSize,
  374. points = this.points.slice(),
  375. i = points.length - 1,
  376. yAxis = this.yAxis,
  377. xRange,
  378. stack;
  379. /**
  380. * Defines when to display a gap in the graph, together with the
  381. * [gapUnit](plotOptions.series.gapUnit) option.
  382. *
  383. * In case when `dataGrouping` is enabled, points can be grouped into a
  384. * larger time span. This can make the grouped points to have a greater
  385. * distance than the absolute value of `gapSize` property, which will result
  386. * in disappearing graph completely. To prevent this situation the mentioned
  387. * distance between grouped points is used instead of previously defined
  388. * `gapSize`.
  389. *
  390. * In practice, this option is most often used to visualize gaps in
  391. * time series. In a stock chart, intraday data is available for daytime
  392. * hours, while gaps will appear in nights and weekends.
  393. *
  394. * @see [gapUnit](plotOptions.series.gapUnit)
  395. * @see [xAxis.breaks](#xAxis.breaks)
  396. *
  397. * @sample {highstock} stock/plotoptions/series-gapsize/
  398. * Setting the gap size to 2 introduces gaps for weekends in daily
  399. * datasets.
  400. *
  401. * @type {number}
  402. * @default 0
  403. * @product highstock
  404. * @apioption plotOptions.series.gapSize
  405. */
  406. /**
  407. * Together with [gapSize](plotOptions.series.gapSize), this option defines
  408. * where to draw gaps in the graph.
  409. *
  410. * When the `gapUnit` is `relative` (default), a gap size of 5 means
  411. * that if the distance between two points is greater than five times
  412. * that of the two closest points, the graph will be broken.
  413. *
  414. * When the `gapUnit` is `value`, the gap is based on absolute axis values,
  415. * which on a datetime axis is milliseconds. This also applies to the
  416. * navigator series that inherits gap options from the base series.
  417. *
  418. * @see [gapSize](plotOptions.series.gapSize)
  419. *
  420. * @type {string}
  421. * @default relative
  422. * @since 5.0.13
  423. * @product highstock
  424. * @validvalue ["relative", "value"]
  425. * @apioption plotOptions.series.gapUnit
  426. */
  427. if (gapSize && i > 0) { // #5008
  428. // Gap unit is relative
  429. if (this.options.gapUnit !== 'value') {
  430. gapSize *= this.closestPointRange;
  431. }
  432. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  433. if (groupingSize && groupingSize > gapSize) {
  434. gapSize = groupingSize;
  435. }
  436. // extension for ordinal breaks
  437. while (i--) {
  438. if (points[i + 1].x - points[i].x > gapSize) {
  439. xRange = (points[i].x + points[i + 1].x) / 2;
  440. points.splice( // insert after this one
  441. i + 1,
  442. 0,
  443. {
  444. isNull: true,
  445. x: xRange
  446. }
  447. );
  448. // For stacked chart generate empty stack items, #6546
  449. if (this.options.stacking) {
  450. stack = yAxis.stacks[this.stackKey][xRange] =
  451. new H.StackItem(
  452. yAxis,
  453. yAxis.options.stackLabels,
  454. false,
  455. xRange,
  456. this.stack
  457. );
  458. stack.total = 0;
  459. }
  460. }
  461. }
  462. }
  463. // Call base method
  464. return this.getGraphPath(points);
  465. };
  466. }(Highcharts));
  467. return (function () {
  468. }());
  469. }));