drilldown.src.js 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321
  1. /**
  2. * @license Highcharts JS v7.0.2 (2019-01-17)
  3. * Highcharts Drilldown module
  4. *
  5. * Author: Torstein Honsi
  6. * License: www.highcharts.com/license
  7. *
  8. */
  9. 'use strict';
  10. (function (factory) {
  11. if (typeof module === 'object' && module.exports) {
  12. factory['default'] = factory;
  13. module.exports = factory;
  14. } else if (typeof define === 'function' && define.amd) {
  15. define(function () {
  16. return factory;
  17. });
  18. } else {
  19. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  20. }
  21. }(function (Highcharts) {
  22. (function (H) {
  23. /* *
  24. * Highcharts Drilldown module
  25. *
  26. * Author: Torstein Honsi
  27. * License: www.highcharts.com/license
  28. *
  29. */
  30. /**
  31. * Gets fired when a drilldown point is clicked, before the new series is added.
  32. * Note that when clicking a category label to trigger multiple series
  33. * drilldown, one `drilldown` event is triggered per point in the category.
  34. *
  35. * @callback Highcharts.DrilldownCallbackFunction
  36. *
  37. * @param {Highcharts.Chart} this
  38. * The chart where the event occurs.
  39. *
  40. * @param {Highcharts.DrilldownEventObject} e
  41. * The drilldown event.
  42. */
  43. /**
  44. * The event arguments when a drilldown point is clicked.
  45. *
  46. * @interface Highcharts.DrilldownEventObject
  47. *//**
  48. * If a category label was clicked, which index.
  49. * @name Highcharts.DrilldownEventObject#category
  50. * @type {number|undefined}
  51. *//**
  52. * The original browser event (usually click) that triggered the drilldown.
  53. * @name Highcharts.DrilldownEventObject#originalEvent
  54. * @type {global.Event|undefined}
  55. *//**
  56. * Prevents the default behaviour of the event.
  57. * @name Highcharts.DrilldownEventObject#preventDefault
  58. * @type {Function}
  59. *//**
  60. * The originating point.
  61. * @name Highcharts.DrilldownEventObject#point
  62. * @type {Highcharts.Point}
  63. *//**
  64. * If a category label was clicked, this array holds all points corresponing to
  65. * the category. Otherwise it is set to false.
  66. * @name Highcharts.DrilldownEventObject#points
  67. * @type {boolean|Array<Highcharts.Point>|undefined}
  68. *//**
  69. * Options for the new series. If the event is utilized for async drilldown, the
  70. * seriesOptions are not added, but rather loaded async.
  71. * @name Highcharts.DrilldownEventObject#seriesOptions
  72. * @type {Highcharts.SeriesOptionsType|undefined}
  73. *//**
  74. * The event target.
  75. * @name Highcharts.DrilldownEventObject#target
  76. * @type {Highcharts.Chart}
  77. *//**
  78. * The event type.
  79. * @name Highcharts.DrilldownEventObject#type
  80. * @type {"drilldown"}
  81. */
  82. /**
  83. * This gets fired after all the series have been drilled up. This is especially
  84. * usefull in a chart with multiple drilldown series.
  85. *
  86. * @callback Highcharts.DrillupAllCallbackFunction
  87. *
  88. * @param {Highcharts.Chart} this
  89. * The chart where the event occurs.
  90. *
  91. * @param {Highcharts.DrillupAllEventObject} e
  92. * The final drillup event.
  93. */
  94. /**
  95. * The event arguments when all the series have been drilled up.
  96. *
  97. * @interface Highcharts.DrillupAllEventObject
  98. *//**
  99. * Prevents the default behaviour of the event.
  100. * @name Highcharts.DrillupAllEventObject#preventDefault
  101. * @type {Function}
  102. *//**
  103. * The event target.
  104. * @name Highcharts.DrillupAllEventObject#target
  105. * @type {Highcharts.Chart}
  106. *//**
  107. * The event type.
  108. * @name Highcharts.DrillupAllEventObject#type
  109. * @type {"drillupall"}
  110. */
  111. /**
  112. * Gets fired when drilling up from a drilldown series.
  113. *
  114. * @callback Highcharts.DrillupCallbackFunction
  115. *
  116. * @param {Highcharts.Chart} this
  117. * The chart where the event occurs.
  118. *
  119. * @param {Highcharts.DrillupEventObject} e
  120. * The drillup event.
  121. */
  122. /**
  123. * The event arguments when drilling up from a drilldown series.
  124. *
  125. * @interface Highcharts.DrillupEventObject
  126. *//**
  127. * Prevents the default behaviour of the event.
  128. * @name Highcharts.DrillupEventObject#preventDefault
  129. * @type {Function}
  130. *//**
  131. * Options for the new series.
  132. * @name Highcharts.DrillupEventObject#seriesOptions
  133. * @type {Highcharts.SeriesOptionsType|undefined}
  134. *//**
  135. * The event target.
  136. * @name Highcharts.DrillupEventObject#target
  137. * @type {Highcharts.Chart}
  138. *//**
  139. * The event type.
  140. * @name Highcharts.DrillupEventObject#type
  141. * @type {"drillup"}
  142. */
  143. var animObject = H.animObject,
  144. noop = H.noop,
  145. color = H.color,
  146. defaultOptions = H.defaultOptions,
  147. extend = H.extend,
  148. format = H.format,
  149. objectEach = H.objectEach,
  150. pick = H.pick,
  151. Chart = H.Chart,
  152. seriesTypes = H.seriesTypes,
  153. PieSeries = seriesTypes.pie,
  154. ColumnSeries = seriesTypes.column,
  155. Tick = H.Tick,
  156. fireEvent = H.fireEvent,
  157. ddSeriesId = 1;
  158. // Add language
  159. extend(
  160. defaultOptions.lang,
  161. /**
  162. * @optionparent lang
  163. */
  164. {
  165. /**
  166. * The text for the button that appears when drilling down, linking back
  167. * to the parent series. The parent series' name is inserted for
  168. * `{series.name}`.
  169. *
  170. * @since 3.0.8
  171. * @product highcharts highmaps
  172. */
  173. drillUpText: '◁ Back to {series.name}'
  174. }
  175. );
  176. /**
  177. * Options for drill down, the concept of inspecting increasingly high
  178. * resolution data through clicking on chart items like columns or pie slices.
  179. *
  180. * The drilldown feature requires the drilldown.js file to be loaded,
  181. * found in the modules directory of the download package, or online at
  182. * [code.highcharts.com/modules/drilldown.js
  183. * ](code.highcharts.com/modules/drilldown.js).
  184. *
  185. * @product highcharts highstock highmaps
  186. * @optionparent drilldown
  187. */
  188. defaultOptions.drilldown = {
  189. /**
  190. * When this option is false, clicking a single point will drill down
  191. * all points in the same category, equivalent to clicking the X axis
  192. * label.
  193. *
  194. * @sample {highcharts} highcharts/drilldown/allowpointdrilldown-false/
  195. * Don't allow point drilldown
  196. *
  197. * @type {boolean}
  198. * @default true
  199. * @since 4.1.7
  200. * @product highcharts
  201. * @apioption drilldown.allowPointDrilldown
  202. */
  203. /**
  204. * An array of series configurations for the drill down. Each series
  205. * configuration uses the same syntax as the [series](#series) option set.
  206. * These drilldown series are hidden by default. The drilldown series is
  207. * linked to the parent series' point by its `id`.
  208. *
  209. * @type {Array<Highcharts.SeriesOptionsType>}
  210. * @since 3.0.8
  211. * @product highcharts highmaps
  212. * @apioption drilldown.series
  213. */
  214. /**
  215. * Additional styles to apply to the X axis label for a point that
  216. * has drilldown data. By default it is underlined and blue to invite
  217. * to interaction.
  218. *
  219. * In styled mode, active label styles can be set with the
  220. * `.highcharts-drilldown-axis-label` class.
  221. *
  222. * @sample {highcharts} highcharts/drilldown/labels/
  223. * Label styles
  224. *
  225. * @type {Highcharts.CSSObject}
  226. * @default { "cursor": "pointer", "color": "#003399", "fontWeight": "bold", "textDecoration": "underline" }
  227. * @since 3.0.8
  228. * @product highcharts highmaps
  229. */
  230. activeAxisLabelStyle: {
  231. /** @ignore-option */
  232. cursor: 'pointer',
  233. /** @ignore-option */
  234. color: '#003399',
  235. /** @ignore-option */
  236. fontWeight: 'bold',
  237. /** @ignore-option */
  238. textDecoration: 'underline'
  239. },
  240. /**
  241. * Additional styles to apply to the data label of a point that has
  242. * drilldown data. By default it is underlined and blue to invite to
  243. * interaction.
  244. *
  245. * In styled mode, active data label styles can be applied with the
  246. * `.highcharts-drilldown-data-label` class.
  247. *
  248. * @sample {highcharts} highcharts/drilldown/labels/
  249. * Label styles
  250. *
  251. * @type {Highcharts.CSSObject}
  252. * @default { "cursor": "pointer", "color": "#003399", "fontWeight": "bold", "textDecoration": "underline" }
  253. * @since 3.0.8
  254. * @product highcharts highmaps
  255. */
  256. activeDataLabelStyle: {
  257. cursor: 'pointer',
  258. color: '#003399',
  259. fontWeight: 'bold',
  260. textDecoration: 'underline'
  261. },
  262. /**
  263. * Set the animation for all drilldown animations. Animation of a drilldown
  264. * occurs when drilling between a column point and a column series,
  265. * or a pie slice and a full pie series. Drilldown can still be used
  266. * between series and points of different types, but animation will
  267. * not occur.
  268. *
  269. * The animation can either be set as a boolean or a configuration
  270. * object. If `true`, it will use the 'swing' jQuery easing and a duration
  271. * of 500 ms. If used as a configuration object, the following properties
  272. * are supported:
  273. *
  274. * - `duration`: The duration of the animation in milliseconds.
  275. *
  276. * - `easing`: A string reference to an easing function set on the `Math`
  277. * object. See
  278. * [the easing demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-animation-easing/).
  279. *
  280. * @type {boolean|Highcharts.AnimationOptionsObject}
  281. * @default { "duration": 500 }
  282. * @since 3.0.8
  283. * @product highcharts highmaps
  284. */
  285. animation: {
  286. /** @ignore-option */
  287. duration: 500
  288. },
  289. /**
  290. * Options for the drill up button that appears when drilling down on a
  291. * series. The text for the button is defined in
  292. * [lang.drillUpText](#lang.drillUpText).
  293. *
  294. * @sample {highcharts} highcharts/drilldown/drillupbutton/
  295. * Drill up button
  296. * @sample {highmaps} highcharts/drilldown/drillupbutton/
  297. * Drill up button
  298. *
  299. * @since 3.0.8
  300. * @product highcharts highmaps
  301. */
  302. drillUpButton: {
  303. /**
  304. * What box to align the button to. Can be either `plotBox` or
  305. * `spacingBox`.
  306. *
  307. * @type {string}
  308. * @default plotBox
  309. * @since 3.0.8
  310. * @product highcharts highmaps
  311. * @validvalue ["plotBox", "spacingBox"]
  312. * @apioption drilldown.drillUpButton.relativeTo
  313. */
  314. /**
  315. * A collection of attributes for the button. The object takes SVG
  316. * attributes like `fill`, `stroke`, `stroke-width` or `r`, the border
  317. * radius. The theme also supports `style`, a collection of CSS
  318. * properties for the text. Equivalent attributes for the hover state
  319. * are given in `theme.states.hover`.
  320. *
  321. * In styled mode, drill-up button styles can be applied with the
  322. * `.highcharts-drillup-button` class.
  323. *
  324. * @sample {highcharts} highcharts/drilldown/drillupbutton/
  325. * Button theming
  326. * @sample {highmaps} highcharts/drilldown/drillupbutton/
  327. * Button theming
  328. *
  329. * @type {object}
  330. * @since 3.0.8
  331. * @product highcharts highmaps
  332. * @apioption drilldown.drillUpButton.theme
  333. */
  334. /**
  335. * Positioning options for the button within the `relativeTo` box.
  336. * Available properties are `x`, `y`, `align` and `verticalAlign`.
  337. *
  338. * @type {Highcharts.AlignObject}
  339. * @since 3.0.8
  340. * @product highcharts highmaps
  341. */
  342. position: {
  343. /**
  344. * Vertical alignment of the button.
  345. *
  346. * @type {Highcharts.VerticalAlignType}
  347. * @default top
  348. * @product highcharts highmaps
  349. * @apioption drilldown.drillUpButton.position.verticalAlign
  350. */
  351. /**
  352. * Horizontal alignment.
  353. *
  354. * @type {Highcharts.AlignType}
  355. */
  356. align: 'right',
  357. /**
  358. * The X offset of the button.
  359. */
  360. x: -10,
  361. /**
  362. * The Y offset of the button.
  363. */
  364. y: 10
  365. }
  366. }
  367. };
  368. /**
  369. * Fires when a drilldown point is clicked, before the new series is added. This
  370. * event is also utilized for async drilldown, where the seriesOptions are not
  371. * added by option, but rather loaded async. Note that when clicking a category
  372. * label to trigger multiple series drilldown, one `drilldown` event is
  373. * triggered per point in the category.
  374. *
  375. * Event arguments:
  376. *
  377. * - `category`: If a category label was clicked, which index.</dd>
  378. *
  379. * - `originalEvent`: The original browser event (usually click) that triggered
  380. * the drilldown.
  381. *
  382. * - `point`: The originating point.
  383. *
  384. * - `points`: If a category label was clicked, this array holds all points
  385. * corresponing to the category.</dd>
  386. *
  387. * - `seriesOptions`: Options for the new series.
  388. *
  389. * @sample {highcharts} highcharts/drilldown/async/
  390. * Async drilldown
  391. *
  392. * @type {Highcharts.DrilldownCallbackFunction}
  393. * @since 3.0.8
  394. * @product highcharts highmaps
  395. * @context Highcharts.Chart
  396. * @apioption chart.events.drilldown
  397. */
  398. /**
  399. * Fires when drilling up from a drilldown series.
  400. *
  401. * @type {Highcharts.DrillupCallbackFunction}
  402. * @since 3.0.8
  403. * @product highcharts highmaps
  404. * @context Highcharts.Chart
  405. * @apioption chart.events.drillup
  406. */
  407. /**
  408. * In a chart with multiple drilldown series, this event fires after all the
  409. * series have been drilled up.
  410. *
  411. * @type {Highcharts.DrillupAllCallbackFunction}
  412. * @since 4.2.4
  413. * @product highcharts highmaps
  414. * @context Highcharts.Chart
  415. * @apioption chart.events.drillupall
  416. */
  417. /**
  418. * The `id` of a series in the [drilldown.series](#drilldown.series) array to
  419. * use for a drilldown for this point.
  420. *
  421. * @sample {highcharts} highcharts/drilldown/basic/
  422. * Basic drilldown
  423. *
  424. * @type {string}
  425. * @since 3.0.8
  426. * @product highcharts
  427. * @apioption series.line.data.drilldown
  428. */
  429. /**
  430. * A general fadeIn method.
  431. *
  432. * @requires module:modules/drilldown
  433. *
  434. * @function Highcharts.SVGElement#fadeIn
  435. *
  436. * @param {Highcharts.AnimationOptionsObject} [animation]
  437. */
  438. H.SVGRenderer.prototype.Element.prototype.fadeIn = function (animation) {
  439. this
  440. .attr({
  441. opacity: 0.1,
  442. visibility: 'inherit'
  443. })
  444. .animate({
  445. opacity: pick(this.newOpacity, 1) // newOpacity used in maps
  446. }, animation || {
  447. duration: 250
  448. });
  449. };
  450. /**
  451. * Add a series to the chart as drilldown from a specific point in the parent
  452. * series. This method is used for async drilldown, when clicking a point in a
  453. * series should result in loading and displaying a more high-resolution series.
  454. * When not async, the setup is simpler using the
  455. * [drilldown.series](https://api.highcharts.com/highcharts/drilldown.series)
  456. * options structure.
  457. *
  458. * @sample highcharts/drilldown/async/
  459. * Async drilldown
  460. *
  461. * @function Highcharts.Chart#addSeriesAsDrilldown
  462. *
  463. * @param {Highcharts.Point} point
  464. * The point from which the drilldown will start.
  465. *
  466. * @param {Highcharts.SeriesOptionsType} options
  467. * The series options for the new, detailed series.
  468. */
  469. Chart.prototype.addSeriesAsDrilldown = function (point, options) {
  470. this.addSingleSeriesAsDrilldown(point, options);
  471. this.applyDrilldown();
  472. };
  473. Chart.prototype.addSingleSeriesAsDrilldown = function (point, ddOptions) {
  474. var oldSeries = point.series,
  475. xAxis = oldSeries.xAxis,
  476. yAxis = oldSeries.yAxis,
  477. newSeries,
  478. pointIndex,
  479. levelSeries = [],
  480. levelSeriesOptions = [],
  481. level,
  482. levelNumber,
  483. last,
  484. colorProp;
  485. colorProp = this.styledMode ?
  486. { colorIndex: pick(point.colorIndex, oldSeries.colorIndex) } :
  487. { color: point.color || oldSeries.color };
  488. if (!this.drilldownLevels) {
  489. this.drilldownLevels = [];
  490. }
  491. levelNumber = oldSeries.options._levelNumber || 0;
  492. // See if we can reuse the registered series from last run
  493. last = this.drilldownLevels[this.drilldownLevels.length - 1];
  494. if (last && last.levelNumber !== levelNumber) {
  495. last = undefined;
  496. }
  497. ddOptions = extend(extend({
  498. _ddSeriesId: ddSeriesId++
  499. }, colorProp), ddOptions);
  500. pointIndex = oldSeries.points.indexOf(point);
  501. // Record options for all current series
  502. oldSeries.chart.series.forEach(function (series) {
  503. if (series.xAxis === xAxis && !series.isDrilling) {
  504. series.options._ddSeriesId =
  505. series.options._ddSeriesId || ddSeriesId++;
  506. series.options._colorIndex = series.userOptions._colorIndex;
  507. series.options._levelNumber =
  508. series.options._levelNumber || levelNumber; // #3182
  509. if (last) {
  510. levelSeries = last.levelSeries;
  511. levelSeriesOptions = last.levelSeriesOptions;
  512. } else {
  513. levelSeries.push(series);
  514. levelSeriesOptions.push(series.options);
  515. }
  516. }
  517. });
  518. // Add a record of properties for each drilldown level
  519. level = extend({
  520. levelNumber: levelNumber,
  521. seriesOptions: oldSeries.options,
  522. levelSeriesOptions: levelSeriesOptions,
  523. levelSeries: levelSeries,
  524. shapeArgs: point.shapeArgs,
  525. // no graphic in line series with markers disabled
  526. bBox: point.graphic ? point.graphic.getBBox() : {},
  527. color: point.isNull ? new H.Color(color).setOpacity(0).get() : color,
  528. lowerSeriesOptions: ddOptions,
  529. pointOptions: oldSeries.options.data[pointIndex],
  530. pointIndex: pointIndex,
  531. oldExtremes: {
  532. xMin: xAxis && xAxis.userMin,
  533. xMax: xAxis && xAxis.userMax,
  534. yMin: yAxis && yAxis.userMin,
  535. yMax: yAxis && yAxis.userMax
  536. },
  537. resetZoomButton: this.resetZoomButton
  538. }, colorProp);
  539. // Push it to the lookup array
  540. this.drilldownLevels.push(level);
  541. // Reset names to prevent extending (#6704)
  542. if (xAxis && xAxis.names) {
  543. xAxis.names.length = 0;
  544. }
  545. newSeries = level.lowerSeries = this.addSeries(ddOptions, false);
  546. newSeries.options._levelNumber = levelNumber + 1;
  547. if (xAxis) {
  548. xAxis.oldPos = xAxis.pos;
  549. xAxis.userMin = xAxis.userMax = null;
  550. yAxis.userMin = yAxis.userMax = null;
  551. }
  552. // Run fancy cross-animation on supported and equal types
  553. if (oldSeries.type === newSeries.type) {
  554. newSeries.animate = newSeries.animateDrilldown || noop;
  555. newSeries.options.animation = true;
  556. }
  557. };
  558. Chart.prototype.applyDrilldown = function () {
  559. var drilldownLevels = this.drilldownLevels,
  560. levelToRemove;
  561. if (drilldownLevels && drilldownLevels.length > 0) { // #3352, async loading
  562. levelToRemove = drilldownLevels[drilldownLevels.length - 1].levelNumber;
  563. this.drilldownLevels.forEach(function (level) {
  564. if (level.levelNumber === levelToRemove) {
  565. level.levelSeries.forEach(function (series) {
  566. // Not removed, not added as part of a multi-series
  567. // drilldown
  568. if (
  569. series.options &&
  570. series.options._levelNumber === levelToRemove
  571. ) {
  572. series.remove(false);
  573. }
  574. });
  575. }
  576. });
  577. }
  578. // We have a reset zoom button. Hide it and detatch it from the chart. It
  579. // is preserved to the layer config above.
  580. if (this.resetZoomButton) {
  581. this.resetZoomButton.hide();
  582. delete this.resetZoomButton;
  583. }
  584. this.pointer.reset();
  585. this.redraw();
  586. this.showDrillUpButton();
  587. };
  588. Chart.prototype.getDrilldownBackText = function () {
  589. var drilldownLevels = this.drilldownLevels,
  590. lastLevel;
  591. if (drilldownLevels && drilldownLevels.length > 0) { // #3352, async loading
  592. lastLevel = drilldownLevels[drilldownLevels.length - 1];
  593. lastLevel.series = lastLevel.seriesOptions;
  594. return format(this.options.lang.drillUpText, lastLevel);
  595. }
  596. };
  597. Chart.prototype.showDrillUpButton = function () {
  598. var chart = this,
  599. backText = this.getDrilldownBackText(),
  600. buttonOptions = chart.options.drilldown.drillUpButton,
  601. attr,
  602. states;
  603. if (!this.drillUpButton) {
  604. attr = buttonOptions.theme;
  605. states = attr && attr.states;
  606. this.drillUpButton = this.renderer.button(
  607. backText,
  608. null,
  609. null,
  610. function () {
  611. chart.drillUp();
  612. },
  613. attr,
  614. states && states.hover,
  615. states && states.select
  616. )
  617. .addClass('highcharts-drillup-button')
  618. .attr({
  619. align: buttonOptions.position.align,
  620. zIndex: 7
  621. })
  622. .add()
  623. .align(
  624. buttonOptions.position,
  625. false,
  626. buttonOptions.relativeTo || 'plotBox'
  627. );
  628. } else {
  629. this.drillUpButton.attr({
  630. text: backText
  631. })
  632. .align();
  633. }
  634. };
  635. /**
  636. * When the chart is drilled down to a child series, calling `chart.drillUp()`
  637. * will drill up to the parent series. Requires the drilldown module.
  638. *
  639. * @function Highcharts.Chart#drillUp
  640. */
  641. Chart.prototype.drillUp = function () {
  642. if (!this.drilldownLevels || this.drilldownLevels.length === 0) {
  643. return;
  644. }
  645. var chart = this,
  646. drilldownLevels = chart.drilldownLevels,
  647. levelNumber = drilldownLevels[drilldownLevels.length - 1].levelNumber,
  648. i = drilldownLevels.length,
  649. chartSeries = chart.series,
  650. seriesI,
  651. level,
  652. oldSeries,
  653. newSeries,
  654. oldExtremes,
  655. addSeries = function (seriesOptions) {
  656. var addedSeries;
  657. chartSeries.forEach(function (series) {
  658. if (series.options._ddSeriesId === seriesOptions._ddSeriesId) {
  659. addedSeries = series;
  660. }
  661. });
  662. addedSeries = addedSeries || chart.addSeries(seriesOptions, false);
  663. if (
  664. addedSeries.type === oldSeries.type &&
  665. addedSeries.animateDrillupTo
  666. ) {
  667. addedSeries.animate = addedSeries.animateDrillupTo;
  668. }
  669. if (seriesOptions === level.seriesOptions) {
  670. newSeries = addedSeries;
  671. }
  672. };
  673. while (i--) {
  674. level = drilldownLevels[i];
  675. if (level.levelNumber === levelNumber) {
  676. drilldownLevels.pop();
  677. // Get the lower series by reference or id
  678. oldSeries = level.lowerSeries;
  679. if (!oldSeries.chart) { // #2786
  680. seriesI = chartSeries.length; // #2919
  681. while (seriesI--) {
  682. if (
  683. chartSeries[seriesI].options.id ===
  684. level.lowerSeriesOptions.id &&
  685. chartSeries[seriesI].options._levelNumber ===
  686. levelNumber + 1
  687. ) { // #3867
  688. oldSeries = chartSeries[seriesI];
  689. break;
  690. }
  691. }
  692. }
  693. oldSeries.xData = []; // Overcome problems with minRange (#2898)
  694. level.levelSeriesOptions.forEach(addSeries);
  695. fireEvent(chart, 'drillup', { seriesOptions: level.seriesOptions });
  696. if (newSeries.type === oldSeries.type) {
  697. newSeries.drilldownLevel = level;
  698. newSeries.options.animation = chart.options.drilldown.animation;
  699. if (oldSeries.animateDrillupFrom && oldSeries.chart) { // #2919
  700. oldSeries.animateDrillupFrom(level);
  701. }
  702. }
  703. newSeries.options._levelNumber = levelNumber;
  704. oldSeries.remove(false);
  705. // Reset the zoom level of the upper series
  706. if (newSeries.xAxis) {
  707. oldExtremes = level.oldExtremes;
  708. newSeries.xAxis.setExtremes(
  709. oldExtremes.xMin,
  710. oldExtremes.xMax,
  711. false
  712. );
  713. newSeries.yAxis.setExtremes(
  714. oldExtremes.yMin,
  715. oldExtremes.yMax,
  716. false
  717. );
  718. }
  719. // We have a resetZoomButton tucked away for this level. Attatch
  720. // it to the chart and show it.
  721. if (level.resetZoomButton) {
  722. chart.resetZoomButton = level.resetZoomButton;
  723. chart.resetZoomButton.show();
  724. }
  725. }
  726. }
  727. // Fire a once-off event after all series have been drilled up (#5158)
  728. fireEvent(chart, 'drillupall');
  729. this.redraw();
  730. if (this.drilldownLevels.length === 0) {
  731. this.drillUpButton = this.drillUpButton.destroy();
  732. } else {
  733. this.drillUpButton.attr({
  734. text: this.getDrilldownBackText()
  735. })
  736. .align();
  737. }
  738. this.ddDupes.length = []; // #3315
  739. };
  740. // Add update function to be called internally from Chart.update (#7600)
  741. Chart.prototype.callbacks.push(function () {
  742. var chart = this;
  743. chart.drilldown = {
  744. update: function (options, redraw) {
  745. H.merge(true, chart.options.drilldown, options);
  746. if (pick(redraw, true)) {
  747. chart.redraw();
  748. }
  749. }
  750. };
  751. });
  752. // Don't show the reset button if we already are displaying the drillUp button.
  753. H.addEvent(Chart, 'beforeShowResetZoom', function () {
  754. if (this.drillUpButton) {
  755. return false;
  756. }
  757. });
  758. H.addEvent(Chart, 'render', function setDDPoints() {
  759. (this.xAxis || []).forEach(function (axis) {
  760. axis.ddPoints = {};
  761. axis.series.forEach(function (series) {
  762. var i,
  763. xData = series.xData || [],
  764. points = series.points,
  765. p;
  766. for (i = 0; i < xData.length; i++) {
  767. p = series.options.data[i];
  768. // The `drilldown` property can only be set on an array or an
  769. // object
  770. if (typeof p !== 'number') {
  771. // Convert array to object (#8008)
  772. p = series.pointClass.prototype.optionsToObject
  773. .call({ series: series }, p);
  774. if (p.drilldown) {
  775. if (!axis.ddPoints[xData[i]]) {
  776. axis.ddPoints[xData[i]] = [];
  777. }
  778. axis.ddPoints[xData[i]].push(points ? points[i] : true);
  779. }
  780. }
  781. }
  782. });
  783. // Add drillability to ticks, and always keep it drillability updated
  784. // (#3951)
  785. objectEach(axis.ticks, Tick.prototype.drillable);
  786. });
  787. });
  788. /**
  789. * When drilling up, keep the upper series invisible until the lower series has
  790. * moved into place.
  791. *
  792. * @private
  793. * @function Highcharts.ColumnSeries#animateDrillupTo
  794. *
  795. * @param {boolean} [init=false]
  796. */
  797. ColumnSeries.prototype.animateDrillupTo = function (init) {
  798. if (!init) {
  799. var newSeries = this,
  800. level = newSeries.drilldownLevel;
  801. // First hide all items before animating in again
  802. this.points.forEach(function (point) {
  803. var dataLabel = point.dataLabel;
  804. if (point.graphic) { // #3407
  805. point.graphic.hide();
  806. }
  807. if (dataLabel) {
  808. // The data label is initially hidden, make sure it is not faded
  809. // in (#6127)
  810. dataLabel.hidden = dataLabel.attr('visibility') === 'hidden';
  811. if (!dataLabel.hidden) {
  812. dataLabel.hide();
  813. if (point.connector) {
  814. point.connector.hide();
  815. }
  816. }
  817. }
  818. });
  819. // Do dummy animation on first point to get to complete
  820. H.syncTimeout(function () {
  821. if (newSeries.points) { // May be destroyed in the meantime, #3389
  822. newSeries.points.forEach(function (point, i) {
  823. // Fade in other points
  824. var verb =
  825. i === (level && level.pointIndex) ? 'show' : 'fadeIn',
  826. inherit = verb === 'show' ? true : undefined,
  827. dataLabel = point.dataLabel;
  828. if (point.graphic) { // #3407
  829. point.graphic[verb](inherit);
  830. }
  831. if (dataLabel && !dataLabel.hidden) { // #6127
  832. dataLabel.fadeIn(); // #7384
  833. if (point.connector) {
  834. point.connector.fadeIn();
  835. }
  836. }
  837. });
  838. }
  839. }, Math.max(this.chart.options.drilldown.animation.duration - 50, 0));
  840. // Reset
  841. this.animate = noop;
  842. }
  843. };
  844. ColumnSeries.prototype.animateDrilldown = function (init) {
  845. var series = this,
  846. chart = this.chart,
  847. drilldownLevels = chart.drilldownLevels,
  848. animateFrom,
  849. animationOptions = animObject(chart.options.drilldown.animation),
  850. xAxis = this.xAxis,
  851. styledMode = chart.styledMode;
  852. if (!init) {
  853. drilldownLevels.forEach(function (level) {
  854. if (
  855. series.options._ddSeriesId ===
  856. level.lowerSeriesOptions._ddSeriesId
  857. ) {
  858. animateFrom = level.shapeArgs;
  859. if (!styledMode) {
  860. // Add the point colors to animate from
  861. animateFrom.fill = level.color;
  862. }
  863. }
  864. });
  865. animateFrom.x += (pick(xAxis.oldPos, xAxis.pos) - xAxis.pos);
  866. this.points.forEach(function (point) {
  867. var animateTo = point.shapeArgs;
  868. if (!styledMode) {
  869. // Add the point colors to animate to
  870. animateTo.fill = point.color;
  871. }
  872. if (point.graphic) {
  873. point.graphic
  874. .attr(animateFrom)
  875. .animate(
  876. extend(
  877. point.shapeArgs,
  878. { fill: point.color || series.color }
  879. ),
  880. animationOptions
  881. );
  882. }
  883. if (point.dataLabel) {
  884. point.dataLabel.fadeIn(animationOptions);
  885. }
  886. });
  887. this.animate = null;
  888. }
  889. };
  890. /**
  891. * When drilling up, pull out the individual point graphics from the lower
  892. * series and animate them into the origin point in the upper series.
  893. *
  894. * @private
  895. * @function Highcharts.ColumnSeries#animateDrillupFrom
  896. *
  897. * @param {object} level
  898. */
  899. ColumnSeries.prototype.animateDrillupFrom = function (level) {
  900. var animationOptions = animObject(this.chart.options.drilldown.animation),
  901. group = this.group,
  902. // For 3d column series all columns are added to one group
  903. // so we should not delete the whole group. #5297
  904. removeGroup = group !== this.chart.columnGroup,
  905. series = this;
  906. // Cancel mouse events on the series group (#2787)
  907. series.trackerGroups.forEach(function (key) {
  908. if (series[key]) { // we don't always have dataLabelsGroup
  909. series[key].on('mouseover');
  910. }
  911. });
  912. if (removeGroup) {
  913. delete this.group;
  914. }
  915. this.points.forEach(function (point) {
  916. var graphic = point.graphic,
  917. animateTo = level.shapeArgs,
  918. complete = function () {
  919. graphic.destroy();
  920. if (group && removeGroup) {
  921. group = group.destroy();
  922. }
  923. };
  924. if (graphic) {
  925. delete point.graphic;
  926. if (!series.chart.styledMode) {
  927. animateTo.fill = level.color;
  928. }
  929. if (animationOptions.duration) {
  930. graphic.animate(
  931. animateTo,
  932. H.merge(animationOptions, { complete: complete })
  933. );
  934. } else {
  935. graphic.attr(animateTo);
  936. complete();
  937. }
  938. }
  939. });
  940. };
  941. if (PieSeries) {
  942. extend(PieSeries.prototype, {
  943. animateDrillupTo: ColumnSeries.prototype.animateDrillupTo,
  944. animateDrillupFrom: ColumnSeries.prototype.animateDrillupFrom,
  945. animateDrilldown: function (init) {
  946. var level = this.chart.drilldownLevels[
  947. this.chart.drilldownLevels.length - 1
  948. ],
  949. animationOptions = this.chart.options.drilldown.animation,
  950. animateFrom = level.shapeArgs,
  951. start = animateFrom.start,
  952. angle = animateFrom.end - start,
  953. startAngle = angle / this.points.length,
  954. styledMode = this.chart.styledMode;
  955. if (!init) {
  956. this.points.forEach(function (point, i) {
  957. var animateTo = point.shapeArgs;
  958. if (!styledMode) {
  959. animateFrom.fill = level.color;
  960. animateTo.fill = point.color;
  961. }
  962. if (point.graphic) {
  963. point.graphic
  964. .attr(H.merge(animateFrom, {
  965. start: start + i * startAngle,
  966. end: start + (i + 1) * startAngle
  967. }))[animationOptions ? 'animate' : 'attr'](
  968. animateTo,
  969. animationOptions
  970. );
  971. }
  972. });
  973. this.animate = null;
  974. }
  975. }
  976. });
  977. }
  978. H.Point.prototype.doDrilldown = function (
  979. _holdRedraw,
  980. category,
  981. originalEvent
  982. ) {
  983. var series = this.series,
  984. chart = series.chart,
  985. drilldown = chart.options.drilldown,
  986. i = (drilldown.series || []).length,
  987. seriesOptions;
  988. if (!chart.ddDupes) {
  989. chart.ddDupes = [];
  990. }
  991. while (i-- && !seriesOptions) {
  992. if (
  993. drilldown.series[i].id === this.drilldown &&
  994. chart.ddDupes.indexOf(this.drilldown) === -1
  995. ) {
  996. seriesOptions = drilldown.series[i];
  997. chart.ddDupes.push(this.drilldown);
  998. }
  999. }
  1000. // Fire the event. If seriesOptions is undefined, the implementer can check
  1001. // for seriesOptions, and call addSeriesAsDrilldown async if necessary.
  1002. fireEvent(chart, 'drilldown', {
  1003. point: this,
  1004. seriesOptions: seriesOptions,
  1005. category: category,
  1006. originalEvent: originalEvent,
  1007. points: (
  1008. category !== undefined &&
  1009. this.series.xAxis.getDDPoints(category).slice(0)
  1010. )
  1011. }, function (e) {
  1012. var chart = e.point.series && e.point.series.chart,
  1013. seriesOptions = e.seriesOptions;
  1014. if (chart && seriesOptions) {
  1015. if (_holdRedraw) {
  1016. chart.addSingleSeriesAsDrilldown(e.point, seriesOptions);
  1017. } else {
  1018. chart.addSeriesAsDrilldown(e.point, seriesOptions);
  1019. }
  1020. }
  1021. });
  1022. };
  1023. /**
  1024. * Drill down to a given category. This is the same as clicking on an axis
  1025. * label.
  1026. *
  1027. * @private
  1028. * @function Highcharts.Axis#drilldownCategory
  1029. */
  1030. H.Axis.prototype.drilldownCategory = function (x, e) {
  1031. objectEach(this.getDDPoints(x), function (point) {
  1032. if (
  1033. point &&
  1034. point.series &&
  1035. point.series.visible &&
  1036. point.doDrilldown
  1037. ) { // #3197
  1038. point.doDrilldown(true, x, e);
  1039. }
  1040. });
  1041. this.chart.applyDrilldown();
  1042. };
  1043. /**
  1044. * Return drillable points for this specific X value.
  1045. *
  1046. * @private
  1047. * @function Highcharts.Axis#getDDPoints
  1048. */
  1049. H.Axis.prototype.getDDPoints = function (x) {
  1050. return this.ddPoints && this.ddPoints[x];
  1051. };
  1052. /**
  1053. * Make a tick label drillable, or remove drilling on update.
  1054. *
  1055. * @private
  1056. * @function Highcharts.Axis#drillable
  1057. */
  1058. Tick.prototype.drillable = function () {
  1059. var pos = this.pos,
  1060. label = this.label,
  1061. axis = this.axis,
  1062. isDrillable = axis.coll === 'xAxis' && axis.getDDPoints,
  1063. ddPointsX = isDrillable && axis.getDDPoints(pos),
  1064. styledMode = axis.chart.styledMode;
  1065. if (isDrillable) {
  1066. if (label && ddPointsX && ddPointsX.length) {
  1067. label.drillable = true;
  1068. if (!label.basicStyles && !styledMode) {
  1069. label.basicStyles = H.merge(label.styles);
  1070. }
  1071. label
  1072. .addClass('highcharts-drilldown-axis-label')
  1073. .on('click', function (e) {
  1074. axis.drilldownCategory(pos, e);
  1075. });
  1076. if (!styledMode) {
  1077. label.css(axis.chart.options.drilldown.activeAxisLabelStyle);
  1078. }
  1079. } else if (label && label.drillable) {
  1080. if (!styledMode) {
  1081. label.styles = {}; // reset for full overwrite of styles
  1082. label.css(label.basicStyles);
  1083. }
  1084. label.on('click', null); // #3806
  1085. label.removeClass('highcharts-drilldown-axis-label');
  1086. }
  1087. }
  1088. };
  1089. // On initialization of each point, identify its label and make it clickable.
  1090. // Also, provide a list of points associated to that label.
  1091. H.addEvent(H.Point, 'afterInit', function () {
  1092. var point = this,
  1093. series = point.series;
  1094. if (point.drilldown) {
  1095. // Add the click event to the point
  1096. H.addEvent(point, 'click', function (e) {
  1097. if (
  1098. series.xAxis &&
  1099. series.chart.options.drilldown.allowPointDrilldown === false
  1100. ) {
  1101. series.xAxis.drilldownCategory(point.x, e); // #5822, x changed
  1102. } else {
  1103. point.doDrilldown(undefined, undefined, e);
  1104. }
  1105. });
  1106. }
  1107. return point;
  1108. });
  1109. H.addEvent(H.Series, 'afterDrawDataLabels', function () {
  1110. var css = this.chart.options.drilldown.activeDataLabelStyle,
  1111. renderer = this.chart.renderer,
  1112. styledMode = this.chart.styledMode;
  1113. this.points.forEach(function (point) {
  1114. var dataLabelsOptions = point.options.dataLabels,
  1115. pointCSS = pick(
  1116. point.dlOptions,
  1117. dataLabelsOptions && dataLabelsOptions.style,
  1118. {}
  1119. );
  1120. if (point.drilldown && point.dataLabel) {
  1121. if (css.color === 'contrast' && !styledMode) {
  1122. pointCSS.color = renderer.getContrast(
  1123. point.color || this.color
  1124. );
  1125. }
  1126. if (dataLabelsOptions && dataLabelsOptions.color) {
  1127. pointCSS.color = dataLabelsOptions.color;
  1128. }
  1129. point.dataLabel
  1130. .addClass('highcharts-drilldown-data-label');
  1131. if (!styledMode) {
  1132. point.dataLabel
  1133. .css(css)
  1134. .css(pointCSS);
  1135. }
  1136. }
  1137. }, this);
  1138. });
  1139. var applyCursorCSS = function (element, cursor, addClass, styledMode) {
  1140. element[addClass ? 'addClass' : 'removeClass'](
  1141. 'highcharts-drilldown-point'
  1142. );
  1143. if (!styledMode) {
  1144. element.css({ cursor: cursor });
  1145. }
  1146. };
  1147. // Mark the trackers with a pointer
  1148. H.addEvent(H.Series, 'afterDrawTracker', function () {
  1149. var styledMode = this.chart.styledMode;
  1150. this.points.forEach(function (point) {
  1151. if (point.drilldown && point.graphic) {
  1152. applyCursorCSS(point.graphic, 'pointer', true, styledMode);
  1153. }
  1154. });
  1155. });
  1156. H.addEvent(H.Point, 'afterSetState', function () {
  1157. var styledMode = this.series.chart.styledMode;
  1158. if (this.drilldown && this.series.halo && this.state === 'hover') {
  1159. applyCursorCSS(this.series.halo, 'pointer', true, styledMode);
  1160. } else if (this.series.halo) {
  1161. applyCursorCSS(this.series.halo, 'auto', false, styledMode);
  1162. }
  1163. });
  1164. }(Highcharts));
  1165. return (function () {
  1166. }());
  1167. }));