drilldown.src.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. /**
  2. * @license Highcharts JS v5.0.6 (2016-12-07)
  3. * Highcharts Drilldown module
  4. *
  5. * Author: Torstein Honsi
  6. * License: www.highcharts.com/license
  7. *
  8. */
  9. (function(factory) {
  10. if (typeof module === 'object' && module.exports) {
  11. module.exports = factory;
  12. } else {
  13. factory(Highcharts);
  14. }
  15. }(function(Highcharts) {
  16. (function(H) {
  17. /**
  18. * Highcharts Drilldown module
  19. *
  20. * Author: Torstein Honsi
  21. * License: www.highcharts.com/license
  22. *
  23. */
  24. 'use strict';
  25. var noop = H.noop,
  26. color = H.color,
  27. defaultOptions = H.defaultOptions,
  28. each = H.each,
  29. extend = H.extend,
  30. format = H.format,
  31. pick = H.pick,
  32. wrap = H.wrap,
  33. Chart = H.Chart,
  34. seriesTypes = H.seriesTypes,
  35. PieSeries = seriesTypes.pie,
  36. ColumnSeries = seriesTypes.column,
  37. Tick = H.Tick,
  38. fireEvent = H.fireEvent,
  39. inArray = H.inArray,
  40. ddSeriesId = 1;
  41. // Utilities
  42. /*
  43. * Return an intermediate color between two colors, according to pos where 0
  44. * is the from color and 1 is the to color. This method is copied from ColorAxis.js
  45. * and should always be kept updated, until we get AMD support.
  46. */
  47. function tweenColors(from, to, pos) {
  48. // Check for has alpha, because rgba colors perform worse due to lack of
  49. // support in WebKit.
  50. var hasAlpha,
  51. ret;
  52. // Unsupported color, return to-color (#3920)
  53. if (!to.rgba.length || !from.rgba.length) {
  54. ret = to.input || 'none';
  55. // Interpolate
  56. } else {
  57. from = from.rgba;
  58. to = to.rgba;
  59. hasAlpha = (to[3] !== 1 || from[3] !== 1);
  60. ret = (hasAlpha ? 'rgba(' : 'rgb(') +
  61. Math.round(to[0] + (from[0] - to[0]) * (1 - pos)) + ',' +
  62. Math.round(to[1] + (from[1] - to[1]) * (1 - pos)) + ',' +
  63. Math.round(to[2] + (from[2] - to[2]) * (1 - pos)) +
  64. (hasAlpha ? (',' + (to[3] + (from[3] - to[3]) * (1 - pos))) : '') + ')';
  65. }
  66. return ret;
  67. }
  68. /**
  69. * Handle animation of the color attributes directly
  70. */
  71. each(['fill', 'stroke'], function(prop) {
  72. H.Fx.prototype[prop + 'Setter'] = function() {
  73. this.elem.attr(
  74. prop,
  75. tweenColors(color(this.start), color(this.end), this.pos),
  76. null,
  77. true
  78. );
  79. };
  80. });
  81. // Add language
  82. extend(defaultOptions.lang, {
  83. drillUpText: '◁ Back to {series.name}'
  84. });
  85. defaultOptions.drilldown = {
  86. animation: {
  87. duration: 500
  88. },
  89. drillUpButton: {
  90. position: {
  91. align: 'right',
  92. x: -10,
  93. y: 10
  94. }
  95. // relativeTo: 'plotBox'
  96. // theme
  97. }
  98. };
  99. /**
  100. * A general fadeIn method
  101. */
  102. H.SVGRenderer.prototype.Element.prototype.fadeIn = function(animation) {
  103. this
  104. .attr({
  105. opacity: 0.1,
  106. visibility: 'inherit'
  107. })
  108. .animate({
  109. opacity: pick(this.newOpacity, 1) // newOpacity used in maps
  110. }, animation || {
  111. duration: 250
  112. });
  113. };
  114. Chart.prototype.addSeriesAsDrilldown = function(point, ddOptions) {
  115. this.addSingleSeriesAsDrilldown(point, ddOptions);
  116. this.applyDrilldown();
  117. };
  118. Chart.prototype.addSingleSeriesAsDrilldown = function(point, ddOptions) {
  119. var oldSeries = point.series,
  120. xAxis = oldSeries.xAxis,
  121. yAxis = oldSeries.yAxis,
  122. newSeries,
  123. pointIndex,
  124. levelSeries = [],
  125. levelSeriesOptions = [],
  126. level,
  127. levelNumber,
  128. last,
  129. colorProp;
  130. colorProp = {
  131. colorIndex: pick(point.colorIndex, oldSeries.colorIndex)
  132. };
  133. if (!this.drilldownLevels) {
  134. this.drilldownLevels = [];
  135. }
  136. levelNumber = oldSeries.options._levelNumber || 0;
  137. // See if we can reuse the registered series from last run
  138. last = this.drilldownLevels[this.drilldownLevels.length - 1];
  139. if (last && last.levelNumber !== levelNumber) {
  140. last = undefined;
  141. }
  142. ddOptions = extend(extend({
  143. _ddSeriesId: ddSeriesId++
  144. }, colorProp), ddOptions);
  145. pointIndex = inArray(point, oldSeries.points);
  146. // Record options for all current series
  147. each(oldSeries.chart.series, function(series) {
  148. if (series.xAxis === xAxis && !series.isDrilling) {
  149. series.options._ddSeriesId = series.options._ddSeriesId || ddSeriesId++;
  150. series.options._colorIndex = series.userOptions._colorIndex;
  151. series.options._levelNumber = series.options._levelNumber || levelNumber; // #3182
  152. if (last) {
  153. levelSeries = last.levelSeries;
  154. levelSeriesOptions = last.levelSeriesOptions;
  155. } else {
  156. levelSeries.push(series);
  157. levelSeriesOptions.push(series.options);
  158. }
  159. }
  160. });
  161. // Add a record of properties for each drilldown level
  162. level = extend({
  163. levelNumber: levelNumber,
  164. seriesOptions: oldSeries.options,
  165. levelSeriesOptions: levelSeriesOptions,
  166. levelSeries: levelSeries,
  167. shapeArgs: point.shapeArgs,
  168. bBox: point.graphic ? point.graphic.getBBox() : {}, // no graphic in line series with markers disabled
  169. color: point.isNull ? new H.Color(color).setOpacity(0).get() : color,
  170. lowerSeriesOptions: ddOptions,
  171. pointOptions: oldSeries.options.data[pointIndex],
  172. pointIndex: pointIndex,
  173. oldExtremes: {
  174. xMin: xAxis && xAxis.userMin,
  175. xMax: xAxis && xAxis.userMax,
  176. yMin: yAxis && yAxis.userMin,
  177. yMax: yAxis && yAxis.userMax
  178. }
  179. }, colorProp);
  180. // Push it to the lookup array
  181. this.drilldownLevels.push(level);
  182. newSeries = level.lowerSeries = this.addSeries(ddOptions, false);
  183. newSeries.options._levelNumber = levelNumber + 1;
  184. if (xAxis) {
  185. xAxis.oldPos = xAxis.pos;
  186. xAxis.userMin = xAxis.userMax = null;
  187. yAxis.userMin = yAxis.userMax = null;
  188. }
  189. // Run fancy cross-animation on supported and equal types
  190. if (oldSeries.type === newSeries.type) {
  191. newSeries.animate = newSeries.animateDrilldown || noop;
  192. newSeries.options.animation = true;
  193. }
  194. };
  195. Chart.prototype.applyDrilldown = function() {
  196. var drilldownLevels = this.drilldownLevels,
  197. levelToRemove;
  198. if (drilldownLevels && drilldownLevels.length > 0) { // #3352, async loading
  199. levelToRemove = drilldownLevels[drilldownLevels.length - 1].levelNumber;
  200. each(this.drilldownLevels, function(level) {
  201. if (level.levelNumber === levelToRemove) {
  202. each(level.levelSeries, function(series) {
  203. if (series.options && series.options._levelNumber === levelToRemove) { // Not removed, not added as part of a multi-series drilldown
  204. series.remove(false);
  205. }
  206. });
  207. }
  208. });
  209. }
  210. this.redraw();
  211. this.showDrillUpButton();
  212. };
  213. Chart.prototype.getDrilldownBackText = function() {
  214. var drilldownLevels = this.drilldownLevels,
  215. lastLevel;
  216. if (drilldownLevels && drilldownLevels.length > 0) { // #3352, async loading
  217. lastLevel = drilldownLevels[drilldownLevels.length - 1];
  218. lastLevel.series = lastLevel.seriesOptions;
  219. return format(this.options.lang.drillUpText, lastLevel);
  220. }
  221. };
  222. Chart.prototype.showDrillUpButton = function() {
  223. var chart = this,
  224. backText = this.getDrilldownBackText(),
  225. buttonOptions = chart.options.drilldown.drillUpButton,
  226. attr,
  227. states;
  228. if (!this.drillUpButton) {
  229. attr = buttonOptions.theme;
  230. states = attr && attr.states;
  231. this.drillUpButton = this.renderer.button(
  232. backText,
  233. null,
  234. null,
  235. function() {
  236. chart.drillUp();
  237. },
  238. attr,
  239. states && states.hover,
  240. states && states.select
  241. )
  242. .addClass('highcharts-drillup-button')
  243. .attr({
  244. align: buttonOptions.position.align,
  245. zIndex: 7
  246. })
  247. .add()
  248. .align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox');
  249. } else {
  250. this.drillUpButton.attr({
  251. text: backText
  252. })
  253. .align();
  254. }
  255. };
  256. Chart.prototype.drillUp = function() {
  257. var chart = this,
  258. drilldownLevels = chart.drilldownLevels,
  259. levelNumber = drilldownLevels[drilldownLevels.length - 1].levelNumber,
  260. i = drilldownLevels.length,
  261. chartSeries = chart.series,
  262. seriesI,
  263. level,
  264. oldSeries,
  265. newSeries,
  266. oldExtremes,
  267. addSeries = function(seriesOptions) {
  268. var addedSeries;
  269. each(chartSeries, function(series) {
  270. if (series.options._ddSeriesId === seriesOptions._ddSeriesId) {
  271. addedSeries = series;
  272. }
  273. });
  274. addedSeries = addedSeries || chart.addSeries(seriesOptions, false);
  275. if (addedSeries.type === oldSeries.type && addedSeries.animateDrillupTo) {
  276. addedSeries.animate = addedSeries.animateDrillupTo;
  277. }
  278. if (seriesOptions === level.seriesOptions) {
  279. newSeries = addedSeries;
  280. }
  281. };
  282. while (i--) {
  283. level = drilldownLevels[i];
  284. if (level.levelNumber === levelNumber) {
  285. drilldownLevels.pop();
  286. // Get the lower series by reference or id
  287. oldSeries = level.lowerSeries;
  288. if (!oldSeries.chart) { // #2786
  289. seriesI = chartSeries.length; // #2919
  290. while (seriesI--) {
  291. if (chartSeries[seriesI].options.id === level.lowerSeriesOptions.id &&
  292. chartSeries[seriesI].options._levelNumber === levelNumber + 1) { // #3867
  293. oldSeries = chartSeries[seriesI];
  294. break;
  295. }
  296. }
  297. }
  298. oldSeries.xData = []; // Overcome problems with minRange (#2898)
  299. each(level.levelSeriesOptions, addSeries);
  300. fireEvent(chart, 'drillup', {
  301. seriesOptions: level.seriesOptions
  302. });
  303. if (newSeries.type === oldSeries.type) {
  304. newSeries.drilldownLevel = level;
  305. newSeries.options.animation = chart.options.drilldown.animation;
  306. if (oldSeries.animateDrillupFrom && oldSeries.chart) { // #2919
  307. oldSeries.animateDrillupFrom(level);
  308. }
  309. }
  310. newSeries.options._levelNumber = levelNumber;
  311. oldSeries.remove(false);
  312. // Reset the zoom level of the upper series
  313. if (newSeries.xAxis) {
  314. oldExtremes = level.oldExtremes;
  315. newSeries.xAxis.setExtremes(oldExtremes.xMin, oldExtremes.xMax, false);
  316. newSeries.yAxis.setExtremes(oldExtremes.yMin, oldExtremes.yMax, false);
  317. }
  318. }
  319. }
  320. // Fire a once-off event after all series have been drilled up (#5158)
  321. fireEvent(chart, 'drillupall');
  322. this.redraw();
  323. if (this.drilldownLevels.length === 0) {
  324. this.drillUpButton = this.drillUpButton.destroy();
  325. } else {
  326. this.drillUpButton.attr({
  327. text: this.getDrilldownBackText()
  328. })
  329. .align();
  330. }
  331. this.ddDupes.length = []; // #3315
  332. };
  333. ColumnSeries.prototype.supportsDrilldown = true;
  334. /**
  335. * When drilling up, keep the upper series invisible until the lower series has
  336. * moved into place
  337. */
  338. ColumnSeries.prototype.animateDrillupTo = function(init) {
  339. if (!init) {
  340. var newSeries = this,
  341. level = newSeries.drilldownLevel;
  342. each(this.points, function(point) {
  343. if (point.graphic) { // #3407
  344. point.graphic.hide();
  345. }
  346. if (point.dataLabel) {
  347. point.dataLabel.hide();
  348. }
  349. if (point.connector) {
  350. point.connector.hide();
  351. }
  352. });
  353. // Do dummy animation on first point to get to complete
  354. setTimeout(function() {
  355. if (newSeries.points) { // May be destroyed in the meantime, #3389
  356. each(newSeries.points, function(point, i) {
  357. // Fade in other points
  358. var verb = i === (level && level.pointIndex) ? 'show' : 'fadeIn',
  359. inherit = verb === 'show' ? true : undefined;
  360. if (point.graphic) { // #3407
  361. point.graphic[verb](inherit);
  362. }
  363. if (point.dataLabel) {
  364. point.dataLabel[verb](inherit);
  365. }
  366. if (point.connector) {
  367. point.connector[verb](inherit);
  368. }
  369. });
  370. }
  371. }, Math.max(this.chart.options.drilldown.animation.duration - 50, 0));
  372. // Reset
  373. this.animate = noop;
  374. }
  375. };
  376. ColumnSeries.prototype.animateDrilldown = function(init) {
  377. var series = this,
  378. drilldownLevels = this.chart.drilldownLevels,
  379. animateFrom,
  380. animationOptions = this.chart.options.drilldown.animation,
  381. xAxis = this.xAxis;
  382. if (!init) {
  383. each(drilldownLevels, function(level) {
  384. if (series.options._ddSeriesId === level.lowerSeriesOptions._ddSeriesId) {
  385. animateFrom = level.shapeArgs;
  386. }
  387. });
  388. animateFrom.x += (pick(xAxis.oldPos, xAxis.pos) - xAxis.pos);
  389. each(this.points, function(point) {
  390. var animateTo = point.shapeArgs;
  391. if (point.graphic) {
  392. point.graphic
  393. .attr(animateFrom)
  394. .animate(
  395. extend(point.shapeArgs, {
  396. fill: point.color || series.color
  397. }),
  398. animationOptions
  399. );
  400. }
  401. if (point.dataLabel) {
  402. point.dataLabel.fadeIn(animationOptions);
  403. }
  404. });
  405. this.animate = null;
  406. }
  407. };
  408. /**
  409. * When drilling up, pull out the individual point graphics from the lower series
  410. * and animate them into the origin point in the upper series.
  411. */
  412. ColumnSeries.prototype.animateDrillupFrom = function(level) {
  413. var animationOptions = this.chart.options.drilldown.animation,
  414. group = this.group,
  415. series = this;
  416. // Cancel mouse events on the series group (#2787)
  417. each(series.trackerGroups, function(key) {
  418. if (series[key]) { // we don't always have dataLabelsGroup
  419. series[key].on('mouseover');
  420. }
  421. });
  422. delete this.group;
  423. each(this.points, function(point) {
  424. var graphic = point.graphic,
  425. animateTo = level.shapeArgs,
  426. complete = function() {
  427. graphic.destroy();
  428. if (group) {
  429. group = group.destroy();
  430. }
  431. };
  432. if (graphic) {
  433. delete point.graphic;
  434. if (animationOptions) {
  435. graphic.animate(
  436. animateTo,
  437. H.merge(animationOptions, {
  438. complete: complete
  439. })
  440. );
  441. } else {
  442. graphic.attr(animateTo);
  443. complete();
  444. }
  445. }
  446. });
  447. };
  448. if (PieSeries) {
  449. extend(PieSeries.prototype, {
  450. supportsDrilldown: true,
  451. animateDrillupTo: ColumnSeries.prototype.animateDrillupTo,
  452. animateDrillupFrom: ColumnSeries.prototype.animateDrillupFrom,
  453. animateDrilldown: function(init) {
  454. var level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1],
  455. animationOptions = this.chart.options.drilldown.animation,
  456. animateFrom = level.shapeArgs,
  457. start = animateFrom.start,
  458. angle = animateFrom.end - start,
  459. startAngle = angle / this.points.length;
  460. if (!init) {
  461. each(this.points, function(point, i) {
  462. var animateTo = point.shapeArgs;
  463. if (point.graphic) {
  464. point.graphic
  465. .attr(H.merge(animateFrom, {
  466. start: start + i * startAngle,
  467. end: start + (i + 1) * startAngle
  468. }))[animationOptions ? 'animate' : 'attr'](
  469. animateTo,
  470. animationOptions
  471. );
  472. }
  473. });
  474. this.animate = null;
  475. }
  476. }
  477. });
  478. }
  479. H.Point.prototype.doDrilldown = function(_holdRedraw, category, originalEvent) {
  480. var series = this.series,
  481. chart = series.chart,
  482. drilldown = chart.options.drilldown,
  483. i = (drilldown.series || []).length,
  484. seriesOptions;
  485. if (!chart.ddDupes) {
  486. chart.ddDupes = [];
  487. }
  488. while (i-- && !seriesOptions) {
  489. if (drilldown.series[i].id === this.drilldown && inArray(this.drilldown, chart.ddDupes) === -1) {
  490. seriesOptions = drilldown.series[i];
  491. chart.ddDupes.push(this.drilldown);
  492. }
  493. }
  494. // Fire the event. If seriesOptions is undefined, the implementer can check for
  495. // seriesOptions, and call addSeriesAsDrilldown async if necessary.
  496. fireEvent(chart, 'drilldown', {
  497. point: this,
  498. seriesOptions: seriesOptions,
  499. category: category,
  500. originalEvent: originalEvent,
  501. points: category !== undefined && this.series.xAxis.getDDPoints(category).slice(0)
  502. }, function(e) {
  503. var chart = e.point.series && e.point.series.chart,
  504. seriesOptions = e.seriesOptions;
  505. if (chart && seriesOptions) {
  506. if (_holdRedraw) {
  507. chart.addSingleSeriesAsDrilldown(e.point, seriesOptions);
  508. } else {
  509. chart.addSeriesAsDrilldown(e.point, seriesOptions);
  510. }
  511. }
  512. });
  513. };
  514. /**
  515. * Drill down to a given category. This is the same as clicking on an axis label.
  516. */
  517. H.Axis.prototype.drilldownCategory = function(x, e) {
  518. var key,
  519. point,
  520. ddPointsX = this.getDDPoints(x);
  521. for (key in ddPointsX) {
  522. point = ddPointsX[key];
  523. if (point && point.series && point.series.visible && point.doDrilldown) { // #3197
  524. point.doDrilldown(true, x, e);
  525. }
  526. }
  527. this.chart.applyDrilldown();
  528. };
  529. /**
  530. * Return drillable points for this specific X value
  531. */
  532. H.Axis.prototype.getDDPoints = function(x) {
  533. var ret = [];
  534. each(this.series, function(series) {
  535. var i,
  536. xData = series.xData,
  537. points = series.points;
  538. for (i = 0; i < xData.length; i++) {
  539. if (xData[i] === x && series.options.data[i] && series.options.data[i].drilldown) {
  540. ret.push(points ? points[i] : true);
  541. break;
  542. }
  543. }
  544. });
  545. return ret;
  546. };
  547. /**
  548. * Make a tick label drillable, or remove drilling on update
  549. */
  550. Tick.prototype.drillable = function() {
  551. var pos = this.pos,
  552. label = this.label,
  553. axis = this.axis,
  554. isDrillable = axis.coll === 'xAxis' && axis.getDDPoints,
  555. ddPointsX = isDrillable && axis.getDDPoints(pos);
  556. if (isDrillable) {
  557. if (label && ddPointsX.length) {
  558. label.drillable = true;
  559. label
  560. .addClass('highcharts-drilldown-axis-label')
  561. .on('click', function(e) {
  562. axis.drilldownCategory(pos, e);
  563. });
  564. } else if (label && label.drillable) {
  565. label.on('click', null); // #3806
  566. label.removeClass('highcharts-drilldown-axis-label');
  567. }
  568. }
  569. };
  570. /**
  571. * Always keep the drillability updated (#3951)
  572. */
  573. wrap(Tick.prototype, 'addLabel', function(proceed) {
  574. proceed.call(this);
  575. this.drillable();
  576. });
  577. /**
  578. * On initialization of each point, identify its label and make it clickable. Also, provide a
  579. * list of points associated to that label.
  580. */
  581. wrap(H.Point.prototype, 'init', function(proceed, series, options, x) {
  582. var point = proceed.call(this, series, options, x),
  583. xAxis = series.xAxis,
  584. tick = xAxis && xAxis.ticks[x];
  585. if (point.drilldown) {
  586. // Add the click event to the point
  587. H.addEvent(point, 'click', function(e) {
  588. if (series.xAxis && series.chart.options.drilldown.allowPointDrilldown === false) {
  589. series.xAxis.drilldownCategory(point.x, e); // #5822, x changed
  590. } else {
  591. point.doDrilldown(undefined, undefined, e);
  592. }
  593. });
  594. /*wrap(point, 'importEvents', function (proceed) { // wrapping importEvents makes point.click event work
  595. if (!this.hasImportedEvents) {
  596. proceed.call(this);
  597. H.addEvent(this, 'click', function () {
  598. this.doDrilldown();
  599. });
  600. }
  601. });*/
  602. }
  603. // Add or remove click handler and style on the tick label
  604. if (tick) {
  605. tick.drillable();
  606. }
  607. return point;
  608. });
  609. wrap(H.Series.prototype, 'drawDataLabels', function(proceed) {
  610. var css = this.chart.options.drilldown.activeDataLabelStyle,
  611. renderer = this.chart.renderer;
  612. proceed.call(this);
  613. each(this.points, function(point) {
  614. var pointCSS = {};
  615. if (point.drilldown && point.dataLabel) {
  616. if (css.color === 'contrast') {
  617. pointCSS.color = renderer.getContrast(point.color || this.color);
  618. }
  619. point.dataLabel
  620. .addClass('highcharts-drilldown-data-label');
  621. }
  622. }, this);
  623. });
  624. // Mark the trackers with a pointer
  625. var type,
  626. drawTrackerWrapper = function(proceed) {
  627. proceed.call(this);
  628. each(this.points, function(point) {
  629. if (point.drilldown && point.graphic) {
  630. point.graphic.addClass('highcharts-drilldown-point');
  631. }
  632. });
  633. };
  634. for (type in seriesTypes) {
  635. if (seriesTypes[type].prototype.supportsDrilldown) {
  636. wrap(seriesTypes[type].prototype, 'drawTracker', drawTrackerWrapper);
  637. }
  638. }
  639. }(Highcharts));
  640. }));