b4d608f5033193fd833dab06116377db26713644.svn-base 19 KB

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